diff options
Diffstat (limited to 'target/linux/ipq806x')
186 files changed, 46248 insertions, 0 deletions
diff --git a/target/linux/ipq806x/Makefile b/target/linux/ipq806x/Makefile new file mode 100644 index 0000000000..7521e971ef --- /dev/null +++ b/target/linux/ipq806x/Makefile @@ -0,0 +1,17 @@ +# 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 + +LINUX_VERSION:=3.14.12 + +KERNELNAME="Image dtbs" + +include $(INCLUDE_DIR)/target.mk + +$(eval $(call BuildTarget)) diff --git a/target/linux/ipq806x/base-files.mk b/target/linux/ipq806x/base-files.mk new file mode 100644 index 0000000000..fdd2c714b2 --- /dev/null +++ b/target/linux/ipq806x/base-files.mk @@ -0,0 +1,3 @@ +define Package/base-files/install-target + rm -f $(1)/etc/config/network +endef diff --git a/target/linux/ipq806x/base-files/etc/inittab b/target/linux/ipq806x/base-files/etc/inittab new file mode 100644 index 0000000000..19a6e11950 --- /dev/null +++ b/target/linux/ipq806x/base-files/etc/inittab @@ -0,0 +1,4 @@ +# 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 diff --git a/target/linux/ipq806x/base-files/lib/ipq806x.sh b/target/linux/ipq806x/base-files/lib/ipq806x.sh new file mode 100644 index 0000000000..7c47fa2a85 --- /dev/null +++ b/target/linux/ipq806x/base-files/lib/ipq806x.sh @@ -0,0 +1,40 @@ +#!/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" + ;; + 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" +} diff --git a/target/linux/ipq806x/base-files/lib/preinit/03_preinit_do_ipq806x.sh b/target/linux/ipq806x/base-files/lib/preinit/03_preinit_do_ipq806x.sh new file mode 100644 index 0000000000..785f1ebb8b --- /dev/null +++ b/target/linux/ipq806x/base-files/lib/preinit/03_preinit_do_ipq806x.sh @@ -0,0 +1,12 @@ +#!/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 diff --git a/target/linux/ipq806x/config-3.14 b/target/linux/ipq806x/config-3.14 new file mode 100644 index 0000000000..876e56d2f7 --- /dev/null +++ b/target/linux/ipq806x/config-3.14 @@ -0,0 +1,356 @@ +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_AMBA_PL08X is not set +# CONFIG_APM_EMULATION is not set +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +CONFIG_ARCH_HAS_TICK_BROADCAST=y +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +# CONFIG_ARCH_MSM is not set +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_NEEDS_CPU_IDLE_COUPLED is not set +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_SUSPEND_POSSIBLE=y +CONFIG_ARCH_USE_BUILTIN_BSWAP=y +CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y +CONFIG_ARCH_WANT_GENERAL_HUGETLB=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_ARM=y +CONFIG_ARM_AMBA=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_ARM_GIC=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_L1_CACHE_SHIFT_6=y +# CONFIG_ARM_LPAE is not set +CONFIG_ARM_NR_BANKS=8 +CONFIG_ARM_PATCH_PHYS_VIRT=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_AUTO_ZRELADDR=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_BOUNCE=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_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_HAS_ASID=y +# CONFIG_CPU_ICACHE_DISABLE is not set +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_PM=y +CONFIG_CPU_RMAP=y +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_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_DW_DMAC_CORE is not set +# CONFIG_DW_DMAC_PCI is not set +CONFIG_DYNAMIC_DEBUG=y +CONFIG_FREEZER=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IO=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_NET_UTILS=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_GPIO_DEVRES=y +# CONFIG_GPIO_MSM_V2 is not set +CONFIG_GPIO_SYSFS=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set +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_KTIME_SCALAR=y +# CONFIG_LEDS_REGULATOR is not set +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_MIGHT_HAVE_PCI=y +CONFIG_MIGRATION=y +# CONFIG_MLX5_CORE is not set +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_SPLIT_SQUASHFS_ROOT=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_RX_BUSY_POLL=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_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_PCI_DOMAINS=y +CONFIG_PCI_MSI=y +CONFIG_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PHYLIB=y +CONFIG_PHY_QCOM_IPQ806X_SATA=y +CONFIG_PINCTRL=y +CONFIG_PINCTRL_APQ8064=y +CONFIG_PINCTRL_IPQ8064=y +CONFIG_PINCTRL_MSM=y +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_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +CONFIG_POWER_RESET=y +# CONFIG_POWER_RESET_GPIO is not set +CONFIG_POWER_RESET_MSM=y +CONFIG_POWER_SUPPLY=y +CONFIG_PREEMPT=y +CONFIG_PREEMPT_COUNT=y +# CONFIG_PREEMPT_NONE is not set +CONFIG_PREEMPT_RCU=y +CONFIG_PRINTK_TIME=y +CONFIG_PROC_DEVICETREE=y +CONFIG_PROC_PAGE_MONITOR=y +# CONFIG_QCOM_ADM is not set +CONFIG_QCOM_BAM_DMA=y +CONFIG_QCOM_GSBI=y +CONFIG_QCOM_HFPLL=y +CONFIG_QCOM_SCM=y +CONFIG_QCOM_TCSR=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_USERSPACE_CONSUMER is not set +CONFIG_RESET_CONTROLLER=y +# CONFIG_RFKILL_REGULATOR is not set +CONFIG_RFS_ACCEL=y +CONFIG_RPS=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_DRV_CMOS is not set +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_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_USE_OF=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_VFIO is not set +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_VM_EVENT_COUNTERS=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_ZBUD is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZONE_DMA_FLAG=0 diff --git a/target/linux/ipq806x/image/Makefile b/target/linux/ipq806x/image/Makefile new file mode 100644 index 0000000000..977e674011 --- /dev/null +++ b/target/linux/ipq806x/image/Makefile @@ -0,0 +1,32 @@ +# 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/BuildKernel + $(CP) $(LINUX_DIR)/vmlinux $(BIN_DIR)/$(IMG_PREFIX)-vmlinux.elf + $(call Image/BuildKernel/FIT,qcom-ipq8064-ap148) +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)) diff --git a/target/linux/ipq806x/patches/0001-ARM-dts-msm-split-out-msm8660-and-msm8960-soc-into-d.patch b/target/linux/ipq806x/patches/0001-ARM-dts-msm-split-out-msm8660-and-msm8960-soc-into-d.patch new file mode 100644 index 0000000000..52d4cf313e --- /dev/null +++ b/target/linux/ipq806x/patches/0001-ARM-dts-msm-split-out-msm8660-and-msm8960-soc-into-d.patch @@ -0,0 +1,313 @@ +From 3cdba35369b404875849008ea97cf1705e6060ed Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 23 Jan 2014 14:09:54 -0600 +Subject: [PATCH 001/182] ARM: dts: msm: split out msm8660 and msm8960 soc + into dts include + +Pull the SoC device tree bits into their own files so other boards based +on these SoCs can include them and reduce duplication across a number of +boards. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-msm8660-surf.dts | 59 +------------------------- + arch/arm/boot/dts/qcom-msm8660.dtsi | 63 ++++++++++++++++++++++++++++ + arch/arm/boot/dts/qcom-msm8960-cdp.dts | 66 +---------------------------- + arch/arm/boot/dts/qcom-msm8960.dtsi | 70 +++++++++++++++++++++++++++++++ + 4 files changed, 135 insertions(+), 123 deletions(-) + create mode 100644 arch/arm/boot/dts/qcom-msm8660.dtsi + create mode 100644 arch/arm/boot/dts/qcom-msm8960.dtsi + +diff --git a/arch/arm/boot/dts/qcom-msm8660-surf.dts b/arch/arm/boot/dts/qcom-msm8660-surf.dts +index 68a72f5..169bad9 100644 +--- a/arch/arm/boot/dts/qcom-msm8660-surf.dts ++++ b/arch/arm/boot/dts/qcom-msm8660-surf.dts +@@ -1,63 +1,6 @@ +-/dts-v1/; +- +-/include/ "skeleton.dtsi" +- +-#include <dt-bindings/clock/qcom,gcc-msm8660.h> ++#include "qcom-msm8660.dtsi" + + / { + model = "Qualcomm MSM8660 SURF"; + compatible = "qcom,msm8660-surf", "qcom,msm8660"; +- interrupt-parent = <&intc>; +- +- intc: interrupt-controller@2080000 { +- compatible = "qcom,msm-8660-qgic"; +- interrupt-controller; +- #interrupt-cells = <3>; +- reg = < 0x02080000 0x1000 >, +- < 0x02081000 0x1000 >; +- }; +- +- timer@2000000 { +- compatible = "qcom,scss-timer", "qcom,msm-timer"; +- interrupts = <1 0 0x301>, +- <1 1 0x301>, +- <1 2 0x301>; +- reg = <0x02000000 0x100>; +- clock-frequency = <27000000>, +- <32768>; +- cpu-offset = <0x40000>; +- }; +- +- msmgpio: gpio@800000 { +- compatible = "qcom,msm-gpio"; +- reg = <0x00800000 0x4000>; +- gpio-controller; +- #gpio-cells = <2>; +- ngpio = <173>; +- interrupts = <0 16 0x4>; +- interrupt-controller; +- #interrupt-cells = <2>; +- }; +- +- gcc: clock-controller@900000 { +- compatible = "qcom,gcc-msm8660"; +- #clock-cells = <1>; +- #reset-cells = <1>; +- reg = <0x900000 0x4000>; +- }; +- +- serial@19c40000 { +- compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; +- reg = <0x19c40000 0x1000>, +- <0x19c00000 0x1000>; +- interrupts = <0 195 0x0>; +- clocks = <&gcc GSBI12_UART_CLK>, <&gcc GSBI12_H_CLK>; +- clock-names = "core", "iface"; +- }; +- +- qcom,ssbi@500000 { +- compatible = "qcom,ssbi"; +- reg = <0x500000 0x1000>; +- qcom,controller-type = "pmic-arbiter"; +- }; + }; +diff --git a/arch/arm/boot/dts/qcom-msm8660.dtsi b/arch/arm/boot/dts/qcom-msm8660.dtsi +new file mode 100644 +index 0000000..69d6c4e +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-msm8660.dtsi +@@ -0,0 +1,63 @@ ++/dts-v1/; ++ ++/include/ "skeleton.dtsi" ++ ++#include <dt-bindings/clock/qcom,gcc-msm8660.h> ++ ++/ { ++ model = "Qualcomm MSM8660"; ++ compatible = "qcom,msm8660"; ++ interrupt-parent = <&intc>; ++ ++ intc: interrupt-controller@2080000 { ++ compatible = "qcom,msm-8660-qgic"; ++ interrupt-controller; ++ #interrupt-cells = <3>; ++ reg = < 0x02080000 0x1000 >, ++ < 0x02081000 0x1000 >; ++ }; ++ ++ timer@2000000 { ++ compatible = "qcom,scss-timer", "qcom,msm-timer"; ++ interrupts = <1 0 0x301>, ++ <1 1 0x301>, ++ <1 2 0x301>; ++ reg = <0x02000000 0x100>; ++ clock-frequency = <27000000>, ++ <32768>; ++ cpu-offset = <0x40000>; ++ }; ++ ++ msmgpio: gpio@800000 { ++ compatible = "qcom,msm-gpio"; ++ reg = <0x00800000 0x4000>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ ngpio = <173>; ++ interrupts = <0 16 0x4>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ gcc: clock-controller@900000 { ++ compatible = "qcom,gcc-msm8660"; ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ reg = <0x900000 0x4000>; ++ }; ++ ++ serial@19c40000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x19c40000 0x1000>, ++ <0x19c00000 0x1000>; ++ interrupts = <0 195 0x0>; ++ clocks = <&gcc GSBI12_UART_CLK>, <&gcc GSBI12_H_CLK>; ++ clock-names = "core", "iface"; ++ }; ++ ++ qcom,ssbi@500000 { ++ compatible = "qcom,ssbi"; ++ reg = <0x500000 0x1000>; ++ qcom,controller-type = "pmic-arbiter"; ++ }; ++}; +diff --git a/arch/arm/boot/dts/qcom-msm8960-cdp.dts b/arch/arm/boot/dts/qcom-msm8960-cdp.dts +index 7c30de4..a58fb88 100644 +--- a/arch/arm/boot/dts/qcom-msm8960-cdp.dts ++++ b/arch/arm/boot/dts/qcom-msm8960-cdp.dts +@@ -1,70 +1,6 @@ +-/dts-v1/; +- +-/include/ "skeleton.dtsi" +- +-#include <dt-bindings/clock/qcom,gcc-msm8960.h> ++#include "qcom-msm8960.dtsi" + + / { + model = "Qualcomm MSM8960 CDP"; + compatible = "qcom,msm8960-cdp", "qcom,msm8960"; +- interrupt-parent = <&intc>; +- +- intc: interrupt-controller@2000000 { +- compatible = "qcom,msm-qgic2"; +- interrupt-controller; +- #interrupt-cells = <3>; +- reg = < 0x02000000 0x1000 >, +- < 0x02002000 0x1000 >; +- }; +- +- timer@200a000 { +- compatible = "qcom,kpss-timer", "qcom,msm-timer"; +- interrupts = <1 1 0x301>, +- <1 2 0x301>, +- <1 3 0x301>; +- reg = <0x0200a000 0x100>; +- clock-frequency = <27000000>, +- <32768>; +- cpu-offset = <0x80000>; +- }; +- +- msmgpio: gpio@800000 { +- compatible = "qcom,msm-gpio"; +- gpio-controller; +- #gpio-cells = <2>; +- ngpio = <150>; +- interrupts = <0 16 0x4>; +- interrupt-controller; +- #interrupt-cells = <2>; +- reg = <0x800000 0x4000>; +- }; +- +- gcc: clock-controller@900000 { +- compatible = "qcom,gcc-msm8960"; +- #clock-cells = <1>; +- #reset-cells = <1>; +- reg = <0x900000 0x4000>; +- }; +- +- clock-controller@4000000 { +- compatible = "qcom,mmcc-msm8960"; +- reg = <0x4000000 0x1000>; +- #clock-cells = <1>; +- #reset-cells = <1>; +- }; +- +- serial@16440000 { +- compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; +- reg = <0x16440000 0x1000>, +- <0x16400000 0x1000>; +- interrupts = <0 154 0x0>; +- clocks = <&gcc GSBI5_UART_CLK>, <&gcc GSBI5_H_CLK>; +- clock-names = "core", "iface"; +- }; +- +- qcom,ssbi@500000 { +- compatible = "qcom,ssbi"; +- reg = <0x500000 0x1000>; +- qcom,controller-type = "pmic-arbiter"; +- }; + }; +diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi +new file mode 100644 +index 0000000..ff00282 +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-msm8960.dtsi +@@ -0,0 +1,70 @@ ++/dts-v1/; ++ ++/include/ "skeleton.dtsi" ++ ++#include <dt-bindings/clock/qcom,gcc-msm8960.h> ++ ++/ { ++ model = "Qualcomm MSM8960"; ++ compatible = "qcom,msm8960"; ++ interrupt-parent = <&intc>; ++ ++ intc: interrupt-controller@2000000 { ++ compatible = "qcom,msm-qgic2"; ++ interrupt-controller; ++ #interrupt-cells = <3>; ++ reg = < 0x02000000 0x1000 >, ++ < 0x02002000 0x1000 >; ++ }; ++ ++ timer@200a000 { ++ compatible = "qcom,kpss-timer", "qcom,msm-timer"; ++ interrupts = <1 1 0x301>, ++ <1 2 0x301>, ++ <1 3 0x301>; ++ reg = <0x0200a000 0x100>; ++ clock-frequency = <27000000>, ++ <32768>; ++ cpu-offset = <0x80000>; ++ }; ++ ++ msmgpio: gpio@800000 { ++ compatible = "qcom,msm-gpio"; ++ gpio-controller; ++ #gpio-cells = <2>; ++ ngpio = <150>; ++ interrupts = <0 16 0x4>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ reg = <0x800000 0x4000>; ++ }; ++ ++ gcc: clock-controller@900000 { ++ compatible = "qcom,gcc-msm8960"; ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ reg = <0x900000 0x4000>; ++ }; ++ ++ clock-controller@4000000 { ++ compatible = "qcom,mmcc-msm8960"; ++ reg = <0x4000000 0x1000>; ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ }; ++ ++ serial@16440000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x16440000 0x1000>, ++ <0x16400000 0x1000>; ++ interrupts = <0 154 0x0>; ++ clocks = <&gcc GSBI5_UART_CLK>, <&gcc GSBI5_H_CLK>; ++ clock-names = "core", "iface"; ++ }; ++ ++ qcom,ssbi@500000 { ++ compatible = "qcom,ssbi"; ++ reg = <0x500000 0x1000>; ++ qcom,controller-type = "pmic-arbiter"; ++ }; ++}; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0002-ARM-msm-Remove-pen_release-usage.patch b/target/linux/ipq806x/patches/0002-ARM-msm-Remove-pen_release-usage.patch new file mode 100644 index 0000000000..72b6618e23 --- /dev/null +++ b/target/linux/ipq806x/patches/0002-ARM-msm-Remove-pen_release-usage.patch @@ -0,0 +1,223 @@ +From 18d53dfa103e63154fb8e548d55016d6ad210d28 Mon Sep 17 00:00:00 2001 +From: Rohit Vaswani <rvaswani@codeaurora.org> +Date: Fri, 21 Jun 2013 12:17:37 -0700 +Subject: [PATCH 002/182] ARM: msm: Remove pen_release usage + +pen_release is no longer required as the synchronization +is now managed by generic arm code. +This is done as suggested in https://lkml.org/lkml/2013/6/4/184 + +Cc: Russell King <linux@arm.linux.org.uk> +Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/mach-msm/Makefile | 2 +- + arch/arm/mach-msm/headsmp.S | 39 --------------------------------------- + arch/arm/mach-msm/hotplug.c | 31 ++++--------------------------- + arch/arm/mach-msm/platsmp.c | 37 +++---------------------------------- + 4 files changed, 8 insertions(+), 101 deletions(-) + delete mode 100644 arch/arm/mach-msm/headsmp.S + +diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile +index 8e307a1..721f27f 100644 +--- a/arch/arm/mach-msm/Makefile ++++ b/arch/arm/mach-msm/Makefile +@@ -19,7 +19,7 @@ obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o + CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) + + obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o +-obj-$(CONFIG_SMP) += headsmp.o platsmp.o ++obj-$(CONFIG_SMP) += platsmp.o + + obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o + obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o +diff --git a/arch/arm/mach-msm/headsmp.S b/arch/arm/mach-msm/headsmp.S +deleted file mode 100644 +index 6c62c3f..0000000 +--- a/arch/arm/mach-msm/headsmp.S ++++ /dev/null +@@ -1,39 +0,0 @@ +-/* +- * linux/arch/arm/mach-realview/headsmp.S +- * +- * Copyright (c) 2003 ARM Limited +- * 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 as +- * published by the Free Software Foundation. +- */ +-#include <linux/linkage.h> +-#include <linux/init.h> +- +-/* +- * MSM specific entry point for secondary CPUs. This provides +- * a "holding pen" into which all secondary cores are held until we're +- * ready for them to initialise. +- */ +-ENTRY(msm_secondary_startup) +- mrc p15, 0, r0, c0, c0, 5 +- and r0, r0, #15 +- adr r4, 1f +- ldmia r4, {r5, r6} +- sub r4, r4, r5 +- add r6, r6, r4 +-pen: ldr r7, [r6] +- cmp r7, r0 +- bne pen +- +- /* +- * we've been released from the holding pen: secondary_stack +- * should now contain the SVC stack for this core +- */ +- b secondary_startup +-ENDPROC(msm_secondary_startup) +- +- .align +-1: .long . +- .long pen_release +diff --git a/arch/arm/mach-msm/hotplug.c b/arch/arm/mach-msm/hotplug.c +index 326a872..cea80fc 100644 +--- a/arch/arm/mach-msm/hotplug.c ++++ b/arch/arm/mach-msm/hotplug.c +@@ -24,33 +24,10 @@ static inline void cpu_leave_lowpower(void) + + static inline void platform_do_lowpower(unsigned int cpu) + { +- /* Just enter wfi for now. TODO: Properly shut off the cpu. */ +- for (;;) { +- /* +- * here's the WFI +- */ +- asm("wfi" +- : +- : +- : "memory", "cc"); +- +- if (pen_release == cpu_logical_map(cpu)) { +- /* +- * OK, proper wakeup, we're done +- */ +- break; +- } +- +- /* +- * getting here, means that we have come out of WFI without +- * having been woken up - this shouldn't happen +- * +- * The trouble is, letting people know about this is not really +- * possible, since we are currently running incoherently, and +- * therefore cannot safely call printk() or anything else +- */ +- pr_debug("CPU%u: spurious wakeup call\n", cpu); +- } ++ asm("wfi" ++ : ++ : ++ : "memory", "cc"); + } + + /* +diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c +index f10a1f5..3721b31 100644 +--- a/arch/arm/mach-msm/platsmp.c ++++ b/arch/arm/mach-msm/platsmp.c +@@ -12,13 +12,10 @@ + #include <linux/errno.h> + #include <linux/delay.h> + #include <linux/device.h> +-#include <linux/jiffies.h> + #include <linux/smp.h> + #include <linux/io.h> + +-#include <asm/cacheflush.h> + #include <asm/cputype.h> +-#include <asm/mach-types.h> + #include <asm/smp_plat.h> + + #include "scm-boot.h" +@@ -28,7 +25,7 @@ + #define SCSS_CPU1CORE_RESET 0xD80 + #define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64 + +-extern void msm_secondary_startup(void); ++extern void secondary_startup(void); + + static DEFINE_SPINLOCK(boot_lock); + +@@ -41,13 +38,6 @@ static inline int get_core_count(void) + static void msm_secondary_init(unsigned int cpu) + { + /* +- * let the primary processor know we're out of the +- * pen, then head off into the C entry point +- */ +- pen_release = -1; +- smp_wmb(); +- +- /* + * Synchronise with the boot thread. + */ + spin_lock(&boot_lock); +@@ -57,7 +47,7 @@ static void msm_secondary_init(unsigned int cpu) + static void prepare_cold_cpu(unsigned int cpu) + { + int ret; +- ret = scm_set_boot_addr(virt_to_phys(msm_secondary_startup), ++ ret = scm_set_boot_addr(virt_to_phys(secondary_startup), + SCM_FLAG_COLDBOOT_CPU1); + if (ret == 0) { + void __iomem *sc1_base_ptr; +@@ -75,7 +65,6 @@ static void prepare_cold_cpu(unsigned int cpu) + + static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle) + { +- unsigned long timeout; + static int cold_boot_done; + + /* Only need to bring cpu out of reset this way once */ +@@ -91,39 +80,19 @@ static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle) + spin_lock(&boot_lock); + + /* +- * The secondary processor is waiting to be released from +- * the holding pen - release it, then wait for it to flag +- * that it has been released by resetting pen_release. +- * +- * Note that "pen_release" is the hardware CPU ID, whereas +- * "cpu" is Linux's internal ID. +- */ +- pen_release = cpu_logical_map(cpu); +- sync_cache_w(&pen_release); +- +- /* + * Send the secondary CPU a soft interrupt, thereby causing + * the boot monitor to read the system wide flags register, + * and branch to the address found there. + */ + arch_send_wakeup_ipi_mask(cpumask_of(cpu)); + +- timeout = jiffies + (1 * HZ); +- while (time_before(jiffies, timeout)) { +- smp_rmb(); +- if (pen_release == -1) +- break; +- +- udelay(10); +- } +- + /* + * now the secondary core is starting up let it run its + * calibrations, then wait for it to finish + */ + spin_unlock(&boot_lock); + +- return pen_release != -1 ? -ENOSYS : 0; ++ return 0; + } + + /* +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0003-ARM-msm-kill-off-hotplug.c.patch b/target/linux/ipq806x/patches/0003-ARM-msm-kill-off-hotplug.c.patch new file mode 100644 index 0000000000..b6e9bbac57 --- /dev/null +++ b/target/linux/ipq806x/patches/0003-ARM-msm-kill-off-hotplug.c.patch @@ -0,0 +1,120 @@ +From b5a3a19e3efa6238c6a00a8f36a8ab2c25eeebc3 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Fri, 31 Jan 2014 13:48:29 -0600 +Subject: [PATCH 003/182] ARM: msm: kill off hotplug.c + +Right now hotplug.c only really implements msm_cpu_die as a wfi. Just +move that implementation into platsmp.c. At the same time we use the +existing wfi() instead of inline asm. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/mach-msm/Makefile | 1 - + arch/arm/mach-msm/common.h | 1 - + arch/arm/mach-msm/hotplug.c | 51 ------------------------------------------- + arch/arm/mach-msm/platsmp.c | 7 ++++++ + 4 files changed, 7 insertions(+), 53 deletions(-) + delete mode 100644 arch/arm/mach-msm/hotplug.c + +diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile +index 721f27f..8327f60 100644 +--- a/arch/arm/mach-msm/Makefile ++++ b/arch/arm/mach-msm/Makefile +@@ -18,7 +18,6 @@ obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o + + CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) + +-obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o + obj-$(CONFIG_SMP) += platsmp.o + + obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o +diff --git a/arch/arm/mach-msm/common.h b/arch/arm/mach-msm/common.h +index 33c7725..0a4899b 100644 +--- a/arch/arm/mach-msm/common.h ++++ b/arch/arm/mach-msm/common.h +@@ -24,7 +24,6 @@ extern void __iomem *__msm_ioremap_caller(phys_addr_t phys_addr, size_t size, + unsigned int mtype, void *caller); + + extern struct smp_operations msm_smp_ops; +-extern void msm_cpu_die(unsigned int cpu); + + struct msm_mmc_platform_data; + +diff --git a/arch/arm/mach-msm/hotplug.c b/arch/arm/mach-msm/hotplug.c +deleted file mode 100644 +index cea80fc..0000000 +--- a/arch/arm/mach-msm/hotplug.c ++++ /dev/null +@@ -1,51 +0,0 @@ +-/* +- * Copyright (C) 2002 ARM Ltd. +- * 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 as +- * published by the Free Software Foundation. +- */ +-#include <linux/kernel.h> +-#include <linux/errno.h> +-#include <linux/smp.h> +- +-#include <asm/smp_plat.h> +- +-#include "common.h" +- +-static inline void cpu_enter_lowpower(void) +-{ +-} +- +-static inline void cpu_leave_lowpower(void) +-{ +-} +- +-static inline void platform_do_lowpower(unsigned int cpu) +-{ +- asm("wfi" +- : +- : +- : "memory", "cc"); +-} +- +-/* +- * platform-specific code to shutdown a CPU +- * +- * Called with IRQs disabled +- */ +-void __ref msm_cpu_die(unsigned int cpu) +-{ +- /* +- * we're ready for shutdown now, so do it +- */ +- cpu_enter_lowpower(); +- platform_do_lowpower(cpu); +- +- /* +- * bring this CPU back into the world of cache +- * coherency, and then restore interrupts +- */ +- cpu_leave_lowpower(); +-} +diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c +index 3721b31..251a91e 100644 +--- a/arch/arm/mach-msm/platsmp.c ++++ b/arch/arm/mach-msm/platsmp.c +@@ -29,6 +29,13 @@ extern void secondary_startup(void); + + static DEFINE_SPINLOCK(boot_lock); + ++#ifdef CONFIG_HOTPLUG_CPU ++static void __ref msm_cpu_die(unsigned int cpu) ++{ ++ wfi(); ++} ++#endif ++ + static inline int get_core_count(void) + { + /* 1 + the PART[1:0] field of MIDR */ +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0004-clocksource-qcom-Move-clocksource-code-out-of-mach-m.patch b/target/linux/ipq806x/patches/0004-clocksource-qcom-Move-clocksource-code-out-of-mach-m.patch new file mode 100644 index 0000000000..fd730c1e47 --- /dev/null +++ b/target/linux/ipq806x/patches/0004-clocksource-qcom-Move-clocksource-code-out-of-mach-m.patch @@ -0,0 +1,788 @@ +From 00009eabeb2074bef5c89e576a7a6d827c12c3d9 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Wed, 29 Jan 2014 16:17:30 -0600 +Subject: [PATCH 004/182] clocksource: qcom: Move clocksource code out of + mach-msm + +We intend to share the clocksource code for MSM platforms between legacy +and multiplatform supported qcom SoCs. + +Acked-by: Olof Johansson <olof@lixom.net> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/mach-msm/Kconfig | 13 +- + arch/arm/mach-msm/Makefile | 1 - + arch/arm/mach-msm/timer.c | 333 -------------------------------------- + drivers/clocksource/Kconfig | 3 + + drivers/clocksource/Makefile | 1 + + drivers/clocksource/qcom-timer.c | 329 +++++++++++++++++++++++++++++++++++++ + 6 files changed, 338 insertions(+), 342 deletions(-) + delete mode 100644 arch/arm/mach-msm/timer.c + create mode 100644 drivers/clocksource/qcom-timer.c + +diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig +index 9625cf3..3c4eca7 100644 +--- a/arch/arm/mach-msm/Kconfig ++++ b/arch/arm/mach-msm/Kconfig +@@ -21,7 +21,7 @@ config ARCH_MSM8X60 + select CPU_V7 + select HAVE_SMP + select MSM_SCM if SMP +- select MSM_TIMER ++ select CLKSRC_QCOM + + config ARCH_MSM8960 + bool "Enable support for MSM8960" +@@ -29,7 +29,7 @@ config ARCH_MSM8960 + select CPU_V7 + select HAVE_SMP + select MSM_SCM if SMP +- select MSM_TIMER ++ select CLKSRC_QCOM + + config ARCH_MSM8974 + bool "Enable support for MSM8974" +@@ -54,7 +54,7 @@ config ARCH_MSM7X00A + select MACH_TROUT if !MACH_HALIBUT + select MSM_PROC_COMM + select MSM_SMD +- select MSM_TIMER ++ select CLKSRC_QCOM + select MSM_SMD_PKG3 + + config ARCH_MSM7X30 +@@ -66,7 +66,7 @@ config ARCH_MSM7X30 + select MSM_GPIOMUX + select MSM_PROC_COMM + select MSM_SMD +- select MSM_TIMER ++ select CLKSRC_QCOM + select MSM_VIC + + config ARCH_QSD8X50 +@@ -78,7 +78,7 @@ config ARCH_QSD8X50 + select MSM_GPIOMUX + select MSM_PROC_COMM + select MSM_SMD +- select MSM_TIMER ++ select CLKSRC_QCOM + select MSM_VIC + + endchoice +@@ -153,7 +153,4 @@ config MSM_GPIOMUX + config MSM_SCM + bool + +-config MSM_TIMER +- bool +- + endif +diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile +index 8327f60..04b1bee 100644 +--- a/arch/arm/mach-msm/Makefile ++++ b/arch/arm/mach-msm/Makefile +@@ -1,4 +1,3 @@ +-obj-$(CONFIG_MSM_TIMER) += timer.o + obj-$(CONFIG_MSM_PROC_COMM) += clock.o + + obj-$(CONFIG_MSM_VIC) += irq-vic.o +diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c +deleted file mode 100644 +index fd16449..0000000 +--- a/arch/arm/mach-msm/timer.c ++++ /dev/null +@@ -1,333 +0,0 @@ +-/* +- * +- * Copyright (C) 2007 Google, Inc. +- * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved. +- * +- * This software is licensed under the terms of the GNU General Public +- * License version 2, as published by the Free Software Foundation, and +- * may be copied, distributed, and modified under those terms. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- */ +- +-#include <linux/clocksource.h> +-#include <linux/clockchips.h> +-#include <linux/cpu.h> +-#include <linux/init.h> +-#include <linux/interrupt.h> +-#include <linux/irq.h> +-#include <linux/io.h> +-#include <linux/of.h> +-#include <linux/of_address.h> +-#include <linux/of_irq.h> +-#include <linux/sched_clock.h> +- +-#include <asm/mach/time.h> +- +-#include "common.h" +- +-#define TIMER_MATCH_VAL 0x0000 +-#define TIMER_COUNT_VAL 0x0004 +-#define TIMER_ENABLE 0x0008 +-#define TIMER_ENABLE_CLR_ON_MATCH_EN BIT(1) +-#define TIMER_ENABLE_EN BIT(0) +-#define TIMER_CLEAR 0x000C +-#define DGT_CLK_CTL 0x10 +-#define DGT_CLK_CTL_DIV_4 0x3 +-#define TIMER_STS_GPT0_CLR_PEND BIT(10) +- +-#define GPT_HZ 32768 +- +-#define MSM_DGT_SHIFT 5 +- +-static void __iomem *event_base; +-static void __iomem *sts_base; +- +-static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) +-{ +- struct clock_event_device *evt = dev_id; +- /* Stop the timer tick */ +- if (evt->mode == CLOCK_EVT_MODE_ONESHOT) { +- u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE); +- ctrl &= ~TIMER_ENABLE_EN; +- writel_relaxed(ctrl, event_base + TIMER_ENABLE); +- } +- evt->event_handler(evt); +- return IRQ_HANDLED; +-} +- +-static int msm_timer_set_next_event(unsigned long cycles, +- struct clock_event_device *evt) +-{ +- u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE); +- +- ctrl &= ~TIMER_ENABLE_EN; +- writel_relaxed(ctrl, event_base + TIMER_ENABLE); +- +- writel_relaxed(ctrl, event_base + TIMER_CLEAR); +- writel_relaxed(cycles, event_base + TIMER_MATCH_VAL); +- +- if (sts_base) +- while (readl_relaxed(sts_base) & TIMER_STS_GPT0_CLR_PEND) +- cpu_relax(); +- +- writel_relaxed(ctrl | TIMER_ENABLE_EN, event_base + TIMER_ENABLE); +- return 0; +-} +- +-static void msm_timer_set_mode(enum clock_event_mode mode, +- struct clock_event_device *evt) +-{ +- u32 ctrl; +- +- ctrl = readl_relaxed(event_base + TIMER_ENABLE); +- ctrl &= ~(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN); +- +- switch (mode) { +- case CLOCK_EVT_MODE_RESUME: +- case CLOCK_EVT_MODE_PERIODIC: +- break; +- case CLOCK_EVT_MODE_ONESHOT: +- /* Timer is enabled in set_next_event */ +- break; +- case CLOCK_EVT_MODE_UNUSED: +- case CLOCK_EVT_MODE_SHUTDOWN: +- break; +- } +- writel_relaxed(ctrl, event_base + TIMER_ENABLE); +-} +- +-static struct clock_event_device __percpu *msm_evt; +- +-static void __iomem *source_base; +- +-static notrace cycle_t msm_read_timer_count(struct clocksource *cs) +-{ +- return readl_relaxed(source_base + TIMER_COUNT_VAL); +-} +- +-static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs) +-{ +- /* +- * Shift timer count down by a constant due to unreliable lower bits +- * on some targets. +- */ +- return msm_read_timer_count(cs) >> MSM_DGT_SHIFT; +-} +- +-static struct clocksource msm_clocksource = { +- .name = "dg_timer", +- .rating = 300, +- .read = msm_read_timer_count, +- .mask = CLOCKSOURCE_MASK(32), +- .flags = CLOCK_SOURCE_IS_CONTINUOUS, +-}; +- +-static int msm_timer_irq; +-static int msm_timer_has_ppi; +- +-static int msm_local_timer_setup(struct clock_event_device *evt) +-{ +- int cpu = smp_processor_id(); +- int err; +- +- evt->irq = msm_timer_irq; +- evt->name = "msm_timer"; +- evt->features = CLOCK_EVT_FEAT_ONESHOT; +- evt->rating = 200; +- evt->set_mode = msm_timer_set_mode; +- evt->set_next_event = msm_timer_set_next_event; +- evt->cpumask = cpumask_of(cpu); +- +- clockevents_config_and_register(evt, GPT_HZ, 4, 0xffffffff); +- +- if (msm_timer_has_ppi) { +- enable_percpu_irq(evt->irq, IRQ_TYPE_EDGE_RISING); +- } else { +- err = request_irq(evt->irq, msm_timer_interrupt, +- IRQF_TIMER | IRQF_NOBALANCING | +- IRQF_TRIGGER_RISING, "gp_timer", evt); +- if (err) +- pr_err("request_irq failed\n"); +- } +- +- return 0; +-} +- +-static void msm_local_timer_stop(struct clock_event_device *evt) +-{ +- evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt); +- disable_percpu_irq(evt->irq); +-} +- +-static int msm_timer_cpu_notify(struct notifier_block *self, +- unsigned long action, void *hcpu) +-{ +- /* +- * Grab cpu pointer in each case to avoid spurious +- * preemptible warnings +- */ +- switch (action & ~CPU_TASKS_FROZEN) { +- case CPU_STARTING: +- msm_local_timer_setup(this_cpu_ptr(msm_evt)); +- break; +- case CPU_DYING: +- msm_local_timer_stop(this_cpu_ptr(msm_evt)); +- break; +- } +- +- return NOTIFY_OK; +-} +- +-static struct notifier_block msm_timer_cpu_nb = { +- .notifier_call = msm_timer_cpu_notify, +-}; +- +-static u64 notrace msm_sched_clock_read(void) +-{ +- return msm_clocksource.read(&msm_clocksource); +-} +- +-static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq, +- bool percpu) +-{ +- struct clocksource *cs = &msm_clocksource; +- int res = 0; +- +- msm_timer_irq = irq; +- msm_timer_has_ppi = percpu; +- +- msm_evt = alloc_percpu(struct clock_event_device); +- if (!msm_evt) { +- pr_err("memory allocation failed for clockevents\n"); +- goto err; +- } +- +- if (percpu) +- res = request_percpu_irq(irq, msm_timer_interrupt, +- "gp_timer", msm_evt); +- +- if (res) { +- pr_err("request_percpu_irq failed\n"); +- } else { +- res = register_cpu_notifier(&msm_timer_cpu_nb); +- if (res) { +- free_percpu_irq(irq, msm_evt); +- goto err; +- } +- +- /* Immediately configure the timer on the boot CPU */ +- msm_local_timer_setup(__this_cpu_ptr(msm_evt)); +- } +- +-err: +- writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE); +- res = clocksource_register_hz(cs, dgt_hz); +- if (res) +- pr_err("clocksource_register failed\n"); +- sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz); +-} +- +-#ifdef CONFIG_OF +-static void __init msm_dt_timer_init(struct device_node *np) +-{ +- u32 freq; +- int irq; +- struct resource res; +- u32 percpu_offset; +- void __iomem *base; +- void __iomem *cpu0_base; +- +- base = of_iomap(np, 0); +- if (!base) { +- pr_err("Failed to map event base\n"); +- return; +- } +- +- /* We use GPT0 for the clockevent */ +- irq = irq_of_parse_and_map(np, 1); +- if (irq <= 0) { +- pr_err("Can't get irq\n"); +- return; +- } +- +- /* We use CPU0's DGT for the clocksource */ +- if (of_property_read_u32(np, "cpu-offset", &percpu_offset)) +- percpu_offset = 0; +- +- if (of_address_to_resource(np, 0, &res)) { +- pr_err("Failed to parse DGT resource\n"); +- return; +- } +- +- cpu0_base = ioremap(res.start + percpu_offset, resource_size(&res)); +- if (!cpu0_base) { +- pr_err("Failed to map source base\n"); +- return; +- } +- +- if (of_property_read_u32(np, "clock-frequency", &freq)) { +- pr_err("Unknown frequency\n"); +- return; +- } +- +- event_base = base + 0x4; +- sts_base = base + 0x88; +- source_base = cpu0_base + 0x24; +- freq /= 4; +- writel_relaxed(DGT_CLK_CTL_DIV_4, source_base + DGT_CLK_CTL); +- +- msm_timer_init(freq, 32, irq, !!percpu_offset); +-} +-CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init); +-CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init); +-#endif +- +-static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source, +- u32 sts) +-{ +- void __iomem *base; +- +- base = ioremap(addr, SZ_256); +- if (!base) { +- pr_err("Failed to map timer base\n"); +- return -ENOMEM; +- } +- event_base = base + event; +- source_base = base + source; +- if (sts) +- sts_base = base + sts; +- +- return 0; +-} +- +-void __init msm7x01_timer_init(void) +-{ +- struct clocksource *cs = &msm_clocksource; +- +- if (msm_timer_map(0xc0100000, 0x0, 0x10, 0x0)) +- return; +- cs->read = msm_read_timer_count_shift; +- cs->mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)); +- /* 600 KHz */ +- msm_timer_init(19200000 >> MSM_DGT_SHIFT, 32 - MSM_DGT_SHIFT, 7, +- false); +-} +- +-void __init msm7x30_timer_init(void) +-{ +- if (msm_timer_map(0xc0100000, 0x4, 0x24, 0x80)) +- return; +- msm_timer_init(24576000 / 4, 32, 1, false); +-} +- +-void __init qsd8x50_timer_init(void) +-{ +- if (msm_timer_map(0xAC100000, 0x0, 0x10, 0x34)) +- return; +- msm_timer_init(19200000 / 4, 32, 7, false); +-} +diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig +index cd6950f..6510ec4 100644 +--- a/drivers/clocksource/Kconfig ++++ b/drivers/clocksource/Kconfig +@@ -140,3 +140,6 @@ config VF_PIT_TIMER + bool + help + Support for Period Interrupt Timer on Freescale Vybrid Family SoCs. ++ ++config CLKSRC_QCOM ++ bool +diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile +index c7ca50a..2e0c0cc 100644 +--- a/drivers/clocksource/Makefile ++++ b/drivers/clocksource/Makefile +@@ -32,6 +32,7 @@ obj-$(CONFIG_CLKSRC_EFM32) += time-efm32.o + obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o + obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o + obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o ++obj-$(CONFIG_CLKSRC_QCOM) += qcom-timer.o + + obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o + obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o +diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c +new file mode 100644 +index 0000000..dca829e +--- /dev/null ++++ b/drivers/clocksource/qcom-timer.c +@@ -0,0 +1,329 @@ ++/* ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * Copyright (c) 2009-2012,2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include <linux/clocksource.h> ++#include <linux/clockchips.h> ++#include <linux/cpu.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/irq.h> ++#include <linux/io.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/of_irq.h> ++#include <linux/sched_clock.h> ++ ++#define TIMER_MATCH_VAL 0x0000 ++#define TIMER_COUNT_VAL 0x0004 ++#define TIMER_ENABLE 0x0008 ++#define TIMER_ENABLE_CLR_ON_MATCH_EN BIT(1) ++#define TIMER_ENABLE_EN BIT(0) ++#define TIMER_CLEAR 0x000C ++#define DGT_CLK_CTL 0x10 ++#define DGT_CLK_CTL_DIV_4 0x3 ++#define TIMER_STS_GPT0_CLR_PEND BIT(10) ++ ++#define GPT_HZ 32768 ++ ++#define MSM_DGT_SHIFT 5 ++ ++static void __iomem *event_base; ++static void __iomem *sts_base; ++ ++static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) ++{ ++ struct clock_event_device *evt = dev_id; ++ /* Stop the timer tick */ ++ if (evt->mode == CLOCK_EVT_MODE_ONESHOT) { ++ u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE); ++ ctrl &= ~TIMER_ENABLE_EN; ++ writel_relaxed(ctrl, event_base + TIMER_ENABLE); ++ } ++ evt->event_handler(evt); ++ return IRQ_HANDLED; ++} ++ ++static int msm_timer_set_next_event(unsigned long cycles, ++ struct clock_event_device *evt) ++{ ++ u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE); ++ ++ ctrl &= ~TIMER_ENABLE_EN; ++ writel_relaxed(ctrl, event_base + TIMER_ENABLE); ++ ++ writel_relaxed(ctrl, event_base + TIMER_CLEAR); ++ writel_relaxed(cycles, event_base + TIMER_MATCH_VAL); ++ ++ if (sts_base) ++ while (readl_relaxed(sts_base) & TIMER_STS_GPT0_CLR_PEND) ++ cpu_relax(); ++ ++ writel_relaxed(ctrl | TIMER_ENABLE_EN, event_base + TIMER_ENABLE); ++ return 0; ++} ++ ++static void msm_timer_set_mode(enum clock_event_mode mode, ++ struct clock_event_device *evt) ++{ ++ u32 ctrl; ++ ++ ctrl = readl_relaxed(event_base + TIMER_ENABLE); ++ ctrl &= ~(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN); ++ ++ switch (mode) { ++ case CLOCK_EVT_MODE_RESUME: ++ case CLOCK_EVT_MODE_PERIODIC: ++ break; ++ case CLOCK_EVT_MODE_ONESHOT: ++ /* Timer is enabled in set_next_event */ ++ break; ++ case CLOCK_EVT_MODE_UNUSED: ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ break; ++ } ++ writel_relaxed(ctrl, event_base + TIMER_ENABLE); ++} ++ ++static struct clock_event_device __percpu *msm_evt; ++ ++static void __iomem *source_base; ++ ++static notrace cycle_t msm_read_timer_count(struct clocksource *cs) ++{ ++ return readl_relaxed(source_base + TIMER_COUNT_VAL); ++} ++ ++static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs) ++{ ++ /* ++ * Shift timer count down by a constant due to unreliable lower bits ++ * on some targets. ++ */ ++ return msm_read_timer_count(cs) >> MSM_DGT_SHIFT; ++} ++ ++static struct clocksource msm_clocksource = { ++ .name = "dg_timer", ++ .rating = 300, ++ .read = msm_read_timer_count, ++ .mask = CLOCKSOURCE_MASK(32), ++ .flags = CLOCK_SOURCE_IS_CONTINUOUS, ++}; ++ ++static int msm_timer_irq; ++static int msm_timer_has_ppi; ++ ++static int msm_local_timer_setup(struct clock_event_device *evt) ++{ ++ int cpu = smp_processor_id(); ++ int err; ++ ++ evt->irq = msm_timer_irq; ++ evt->name = "msm_timer"; ++ evt->features = CLOCK_EVT_FEAT_ONESHOT; ++ evt->rating = 200; ++ evt->set_mode = msm_timer_set_mode; ++ evt->set_next_event = msm_timer_set_next_event; ++ evt->cpumask = cpumask_of(cpu); ++ ++ clockevents_config_and_register(evt, GPT_HZ, 4, 0xffffffff); ++ ++ if (msm_timer_has_ppi) { ++ enable_percpu_irq(evt->irq, IRQ_TYPE_EDGE_RISING); ++ } else { ++ err = request_irq(evt->irq, msm_timer_interrupt, ++ IRQF_TIMER | IRQF_NOBALANCING | ++ IRQF_TRIGGER_RISING, "gp_timer", evt); ++ if (err) ++ pr_err("request_irq failed\n"); ++ } ++ ++ return 0; ++} ++ ++static void msm_local_timer_stop(struct clock_event_device *evt) ++{ ++ evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt); ++ disable_percpu_irq(evt->irq); ++} ++ ++static int msm_timer_cpu_notify(struct notifier_block *self, ++ unsigned long action, void *hcpu) ++{ ++ /* ++ * Grab cpu pointer in each case to avoid spurious ++ * preemptible warnings ++ */ ++ switch (action & ~CPU_TASKS_FROZEN) { ++ case CPU_STARTING: ++ msm_local_timer_setup(this_cpu_ptr(msm_evt)); ++ break; ++ case CPU_DYING: ++ msm_local_timer_stop(this_cpu_ptr(msm_evt)); ++ break; ++ } ++ ++ return NOTIFY_OK; ++} ++ ++static struct notifier_block msm_timer_cpu_nb = { ++ .notifier_call = msm_timer_cpu_notify, ++}; ++ ++static u64 notrace msm_sched_clock_read(void) ++{ ++ return msm_clocksource.read(&msm_clocksource); ++} ++ ++static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq, ++ bool percpu) ++{ ++ struct clocksource *cs = &msm_clocksource; ++ int res = 0; ++ ++ msm_timer_irq = irq; ++ msm_timer_has_ppi = percpu; ++ ++ msm_evt = alloc_percpu(struct clock_event_device); ++ if (!msm_evt) { ++ pr_err("memory allocation failed for clockevents\n"); ++ goto err; ++ } ++ ++ if (percpu) ++ res = request_percpu_irq(irq, msm_timer_interrupt, ++ "gp_timer", msm_evt); ++ ++ if (res) { ++ pr_err("request_percpu_irq failed\n"); ++ } else { ++ res = register_cpu_notifier(&msm_timer_cpu_nb); ++ if (res) { ++ free_percpu_irq(irq, msm_evt); ++ goto err; ++ } ++ ++ /* Immediately configure the timer on the boot CPU */ ++ msm_local_timer_setup(__this_cpu_ptr(msm_evt)); ++ } ++ ++err: ++ writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE); ++ res = clocksource_register_hz(cs, dgt_hz); ++ if (res) ++ pr_err("clocksource_register failed\n"); ++ sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz); ++} ++ ++#ifdef CONFIG_OF ++static void __init msm_dt_timer_init(struct device_node *np) ++{ ++ u32 freq; ++ int irq; ++ struct resource res; ++ u32 percpu_offset; ++ void __iomem *base; ++ void __iomem *cpu0_base; ++ ++ base = of_iomap(np, 0); ++ if (!base) { ++ pr_err("Failed to map event base\n"); ++ return; ++ } ++ ++ /* We use GPT0 for the clockevent */ ++ irq = irq_of_parse_and_map(np, 1); ++ if (irq <= 0) { ++ pr_err("Can't get irq\n"); ++ return; ++ } ++ ++ /* We use CPU0's DGT for the clocksource */ ++ if (of_property_read_u32(np, "cpu-offset", &percpu_offset)) ++ percpu_offset = 0; ++ ++ if (of_address_to_resource(np, 0, &res)) { ++ pr_err("Failed to parse DGT resource\n"); ++ return; ++ } ++ ++ cpu0_base = ioremap(res.start + percpu_offset, resource_size(&res)); ++ if (!cpu0_base) { ++ pr_err("Failed to map source base\n"); ++ return; ++ } ++ ++ if (of_property_read_u32(np, "clock-frequency", &freq)) { ++ pr_err("Unknown frequency\n"); ++ return; ++ } ++ ++ event_base = base + 0x4; ++ sts_base = base + 0x88; ++ source_base = cpu0_base + 0x24; ++ freq /= 4; ++ writel_relaxed(DGT_CLK_CTL_DIV_4, source_base + DGT_CLK_CTL); ++ ++ msm_timer_init(freq, 32, irq, !!percpu_offset); ++} ++CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init); ++CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init); ++#endif ++ ++static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source, ++ u32 sts) ++{ ++ void __iomem *base; ++ ++ base = ioremap(addr, SZ_256); ++ if (!base) { ++ pr_err("Failed to map timer base\n"); ++ return -ENOMEM; ++ } ++ event_base = base + event; ++ source_base = base + source; ++ if (sts) ++ sts_base = base + sts; ++ ++ return 0; ++} ++ ++void __init msm7x01_timer_init(void) ++{ ++ struct clocksource *cs = &msm_clocksource; ++ ++ if (msm_timer_map(0xc0100000, 0x0, 0x10, 0x0)) ++ return; ++ cs->read = msm_read_timer_count_shift; ++ cs->mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)); ++ /* 600 KHz */ ++ msm_timer_init(19200000 >> MSM_DGT_SHIFT, 32 - MSM_DGT_SHIFT, 7, ++ false); ++} ++ ++void __init msm7x30_timer_init(void) ++{ ++ if (msm_timer_map(0xc0100000, 0x4, 0x24, 0x80)) ++ return; ++ msm_timer_init(24576000 / 4, 32, 1, false); ++} ++ ++void __init qsd8x50_timer_init(void) ++{ ++ if (msm_timer_map(0xAC100000, 0x0, 0x10, 0x34)) ++ return; ++ msm_timer_init(19200000 / 4, 32, 7, false); ++} +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0005-ARM-qcom-Split-Qualcomm-support-into-legacy-and-mult.patch b/target/linux/ipq806x/patches/0005-ARM-qcom-Split-Qualcomm-support-into-legacy-and-mult.patch new file mode 100644 index 0000000000..c23faf0640 --- /dev/null +++ b/target/linux/ipq806x/patches/0005-ARM-qcom-Split-Qualcomm-support-into-legacy-and-mult.patch @@ -0,0 +1,1482 @@ +From 8c2a00c0129d6f718245f7a613c2bb28976b7973 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Tue, 21 Jan 2014 17:14:10 -0600 +Subject: [PATCH 005/182] ARM: qcom: Split Qualcomm support into legacy and + multiplatform + +Introduce a new mach-qcom that will support SoCs that intend to be +multiplatform compatible while keeping mach-msm to legacy SoC/board +support that will not transition over to multiplatform. + +As part of this, we move support for MSM8X60, MSM8960 and MSM8974 over +to mach-qcom. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + MAINTAINERS | 8 ++ + arch/arm/Kconfig | 7 +- + arch/arm/Kconfig.debug | 2 +- + arch/arm/Makefile | 1 + + arch/arm/boot/dts/Makefile | 6 +- + arch/arm/mach-msm/Kconfig | 45 +------ + arch/arm/mach-msm/Makefile | 6 - + arch/arm/mach-msm/board-dt.c | 41 ------ + arch/arm/mach-msm/platsmp.c | 137 ------------------- + arch/arm/mach-msm/scm-boot.c | 39 ------ + arch/arm/mach-msm/scm-boot.h | 22 --- + arch/arm/mach-msm/scm.c | 299 ----------------------------------------- + arch/arm/mach-msm/scm.h | 25 ---- + arch/arm/mach-qcom/Kconfig | 33 +++++ + arch/arm/mach-qcom/Makefile | 5 + + arch/arm/mach-qcom/board.c | 40 ++++++ + arch/arm/mach-qcom/platsmp.c | 137 +++++++++++++++++++ + arch/arm/mach-qcom/scm-boot.c | 39 ++++++ + arch/arm/mach-qcom/scm-boot.h | 22 +++ + arch/arm/mach-qcom/scm.c | 299 +++++++++++++++++++++++++++++++++++++++++ + arch/arm/mach-qcom/scm.h | 25 ++++ + 21 files changed, 619 insertions(+), 619 deletions(-) + delete mode 100644 arch/arm/mach-msm/board-dt.c + delete mode 100644 arch/arm/mach-msm/platsmp.c + delete mode 100644 arch/arm/mach-msm/scm-boot.c + delete mode 100644 arch/arm/mach-msm/scm-boot.h + delete mode 100644 arch/arm/mach-msm/scm.c + delete mode 100644 arch/arm/mach-msm/scm.h + create mode 100644 arch/arm/mach-qcom/Kconfig + create mode 100644 arch/arm/mach-qcom/Makefile + create mode 100644 arch/arm/mach-qcom/board.c + create mode 100644 arch/arm/mach-qcom/platsmp.c + create mode 100644 arch/arm/mach-qcom/scm-boot.c + create mode 100644 arch/arm/mach-qcom/scm-boot.h + create mode 100644 arch/arm/mach-qcom/scm.c + create mode 100644 arch/arm/mach-qcom/scm.h + +diff --git a/MAINTAINERS b/MAINTAINERS +index 900d98e..7d23402 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -1168,6 +1168,14 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) + W: http://www.arm.linux.org.uk/ + S: Maintained + ++ARM/QUALCOMM SUPPORT ++M: Kumar Gala <galak@codeaurora.org> ++M: David Brown <davidb@codeaurora.org> ++L: linux-arm-msm@vger.kernel.org ++S: Maintained ++F: arch/arm/mach-qcom/ ++T: git git://git.kernel.org/pub/scm/linux/kernel/git/galak/linux-qcom.git ++ + ARM/RADISYS ENP2611 MACHINE SUPPORT + M: Lennert Buytenhek <kernel@wantstofly.org> + L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 1594945..d02ce70 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -657,9 +657,8 @@ config ARCH_PXA + help + Support for Intel/Marvell's PXA2xx/PXA3xx processor line. + +-config ARCH_MSM_NODT +- bool "Qualcomm MSM" +- select ARCH_MSM ++config ARCH_MSM ++ bool "Qualcomm MSM (non-multiplatform)" + select ARCH_REQUIRE_GPIOLIB + select COMMON_CLK + select GENERIC_CLOCKEVENTS +@@ -1005,6 +1004,8 @@ source "arch/arm/plat-pxa/Kconfig" + + source "arch/arm/mach-mmp/Kconfig" + ++source "arch/arm/mach-qcom/Kconfig" ++ + source "arch/arm/mach-realview/Kconfig" + + source "arch/arm/mach-rockchip/Kconfig" +diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug +index 0531da8..4491c7b 100644 +--- a/arch/arm/Kconfig.debug ++++ b/arch/arm/Kconfig.debug +@@ -956,7 +956,7 @@ config DEBUG_STI_UART + + config DEBUG_MSM_UART + bool +- depends on ARCH_MSM ++ depends on ARCH_MSM || ARCH_QCOM + + config DEBUG_LL_INCLUDE + string +diff --git a/arch/arm/Makefile b/arch/arm/Makefile +index 08a9ef5..51e5bed 100644 +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -180,6 +180,7 @@ machine-$(CONFIG_ARCH_OMAP2PLUS) += omap2 + machine-$(CONFIG_ARCH_ORION5X) += orion5x + machine-$(CONFIG_ARCH_PICOXCELL) += picoxcell + machine-$(CONFIG_ARCH_PXA) += pxa ++machine-$(CONFIG_ARCH_QCOM) += qcom + machine-$(CONFIG_ARCH_REALVIEW) += realview + machine-$(CONFIG_ARCH_ROCKCHIP) += rockchip + machine-$(CONFIG_ARCH_RPC) += rpc +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index 0320303..4a89023 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -119,9 +119,6 @@ dtb-$(CONFIG_ARCH_KIRKWOOD) += kirkwood-cloudbox.dtb \ + kirkwood-ts219-6282.dtb + dtb-$(CONFIG_ARCH_MARCO) += marco-evb.dtb + dtb-$(CONFIG_ARCH_MOXART) += moxart-uc7112lx.dtb +-dtb-$(CONFIG_ARCH_MSM) += qcom-msm8660-surf.dtb \ +- qcom-msm8960-cdp.dtb \ +- qcom-apq8074-dragonboard.dtb + dtb-$(CONFIG_ARCH_MVEBU) += armada-370-db.dtb \ + armada-370-mirabox.dtb \ + armada-370-netgear-rn102.dtb \ +@@ -234,6 +231,9 @@ dtb-$(CONFIG_ARCH_OMAP2PLUS) += omap2420-h4.dtb \ + dra7-evm.dtb + dtb-$(CONFIG_ARCH_ORION5X) += orion5x-lacie-ethernet-disk-mini-v2.dtb + dtb-$(CONFIG_ARCH_PRIMA2) += prima2-evb.dtb ++dtb-$(CONFIG_ARCH_QCOM) += qcom-msm8660-surf.dtb \ ++ qcom-msm8960-cdp.dtb \ ++ qcom-apq8074-dragonboard.dtb + dtb-$(CONFIG_ARCH_U8500) += ste-snowball.dtb \ + ste-hrefprev60-stuib.dtb \ + ste-hrefprev60-tvk.dtb \ +diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig +index 3c4eca7..a7f959e 100644 +--- a/arch/arm/mach-msm/Kconfig ++++ b/arch/arm/mach-msm/Kconfig +@@ -1,50 +1,9 @@ +-config ARCH_MSM +- bool +- +-config ARCH_MSM_DT +- bool "Qualcomm MSM DT Support" if ARCH_MULTI_V7 +- select ARCH_MSM +- select ARCH_REQUIRE_GPIOLIB +- select CLKSRC_OF +- select GENERIC_CLOCKEVENTS +- help +- Support for Qualcomm's devicetree based MSM systems. +- + if ARCH_MSM + +-menu "Qualcomm MSM SoC Selection" +- depends on ARCH_MSM_DT +- +-config ARCH_MSM8X60 +- bool "Enable support for MSM8X60" +- select ARM_GIC +- select CPU_V7 +- select HAVE_SMP +- select MSM_SCM if SMP +- select CLKSRC_QCOM +- +-config ARCH_MSM8960 +- bool "Enable support for MSM8960" +- select ARM_GIC +- select CPU_V7 +- select HAVE_SMP +- select MSM_SCM if SMP +- select CLKSRC_QCOM +- +-config ARCH_MSM8974 +- bool "Enable support for MSM8974" +- select ARM_GIC +- select CPU_V7 +- select HAVE_ARM_ARCH_TIMER +- select HAVE_SMP +- select MSM_SCM if SMP +- +-endmenu +- + choice + prompt "Qualcomm MSM SoC Type" + default ARCH_MSM7X00A +- depends on ARCH_MSM_NODT ++ depends on ARCH_MSM + + config ARCH_MSM7X00A + bool "MSM7x00A / MSM7x01A" +@@ -99,7 +58,7 @@ config MSM_VIC + bool + + menu "Qualcomm MSM Board Type" +- depends on ARCH_MSM_NODT ++ depends on ARCH_MSM + + config MACH_HALIBUT + depends on ARCH_MSM +diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile +index 04b1bee..27c078a 100644 +--- a/arch/arm/mach-msm/Makefile ++++ b/arch/arm/mach-msm/Makefile +@@ -13,17 +13,11 @@ obj-$(CONFIG_ARCH_QSD8X50) += dma.o io.o + + obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o + obj-$(CONFIG_MSM_SMD) += last_radio_log.o +-obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o +- +-CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) +- +-obj-$(CONFIG_SMP) += platsmp.o + + obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o + obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o + obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o + obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o + obj-$(CONFIG_ARCH_QSD8X50) += board-qsd8x50.o devices-qsd8x50.o +-obj-$(CONFIG_ARCH_MSM_DT) += board-dt.o + obj-$(CONFIG_MSM_GPIOMUX) += gpiomux.o + obj-$(CONFIG_ARCH_QSD8X50) += gpiomux-8x50.o +diff --git a/arch/arm/mach-msm/board-dt.c b/arch/arm/mach-msm/board-dt.c +deleted file mode 100644 +index 1f11d93..0000000 +--- a/arch/arm/mach-msm/board-dt.c ++++ /dev/null +@@ -1,41 +0,0 @@ +-/* Copyright (c) 2010-2012,2013 The Linux Foundation. All rights reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 and +- * only version 2 as published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +- +-#include <linux/init.h> +-#include <linux/of.h> +-#include <linux/of_platform.h> +- +-#include <asm/mach/arch.h> +-#include <asm/mach/map.h> +- +-#include "common.h" +- +-static const char * const msm_dt_match[] __initconst = { +- "qcom,msm8660-fluid", +- "qcom,msm8660-surf", +- "qcom,msm8960-cdp", +- NULL +-}; +- +-static const char * const apq8074_dt_match[] __initconst = { +- "qcom,apq8074-dragonboard", +- NULL +-}; +- +-DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)") +- .smp = smp_ops(msm_smp_ops), +- .dt_compat = msm_dt_match, +-MACHINE_END +- +-DT_MACHINE_START(APQ_DT, "Qualcomm MSM (Flattened Device Tree)") +- .dt_compat = apq8074_dt_match, +-MACHINE_END +diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c +deleted file mode 100644 +index 251a91e..0000000 +--- a/arch/arm/mach-msm/platsmp.c ++++ /dev/null +@@ -1,137 +0,0 @@ +-/* +- * Copyright (C) 2002 ARM Ltd. +- * All Rights Reserved +- * Copyright (c) 2010, 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 as +- * published by the Free Software Foundation. +- */ +- +-#include <linux/init.h> +-#include <linux/errno.h> +-#include <linux/delay.h> +-#include <linux/device.h> +-#include <linux/smp.h> +-#include <linux/io.h> +- +-#include <asm/cputype.h> +-#include <asm/smp_plat.h> +- +-#include "scm-boot.h" +-#include "common.h" +- +-#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x15A0 +-#define SCSS_CPU1CORE_RESET 0xD80 +-#define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64 +- +-extern void secondary_startup(void); +- +-static DEFINE_SPINLOCK(boot_lock); +- +-#ifdef CONFIG_HOTPLUG_CPU +-static void __ref msm_cpu_die(unsigned int cpu) +-{ +- wfi(); +-} +-#endif +- +-static inline int get_core_count(void) +-{ +- /* 1 + the PART[1:0] field of MIDR */ +- return ((read_cpuid_id() >> 4) & 3) + 1; +-} +- +-static void msm_secondary_init(unsigned int cpu) +-{ +- /* +- * Synchronise with the boot thread. +- */ +- spin_lock(&boot_lock); +- spin_unlock(&boot_lock); +-} +- +-static void prepare_cold_cpu(unsigned int cpu) +-{ +- int ret; +- ret = scm_set_boot_addr(virt_to_phys(secondary_startup), +- SCM_FLAG_COLDBOOT_CPU1); +- if (ret == 0) { +- void __iomem *sc1_base_ptr; +- sc1_base_ptr = ioremap_nocache(0x00902000, SZ_4K*2); +- if (sc1_base_ptr) { +- writel(0, sc1_base_ptr + VDD_SC1_ARRAY_CLAMP_GFS_CTL); +- writel(0, sc1_base_ptr + SCSS_CPU1CORE_RESET); +- writel(3, sc1_base_ptr + SCSS_DBG_STATUS_CORE_PWRDUP); +- iounmap(sc1_base_ptr); +- } +- } else +- printk(KERN_DEBUG "Failed to set secondary core boot " +- "address\n"); +-} +- +-static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle) +-{ +- static int cold_boot_done; +- +- /* Only need to bring cpu out of reset this way once */ +- if (cold_boot_done == false) { +- prepare_cold_cpu(cpu); +- cold_boot_done = true; +- } +- +- /* +- * set synchronisation state between this boot processor +- * and the secondary one +- */ +- spin_lock(&boot_lock); +- +- /* +- * Send the secondary CPU a soft interrupt, thereby causing +- * the boot monitor to read the system wide flags register, +- * and branch to the address found there. +- */ +- arch_send_wakeup_ipi_mask(cpumask_of(cpu)); +- +- /* +- * now the secondary core is starting up let it run its +- * calibrations, then wait for it to finish +- */ +- spin_unlock(&boot_lock); +- +- return 0; +-} +- +-/* +- * Initialise the CPU possible map early - this describes the CPUs +- * which may be present or become present in the system. The msm8x60 +- * does not support the ARM SCU, so just set the possible cpu mask to +- * NR_CPUS. +- */ +-static void __init msm_smp_init_cpus(void) +-{ +- unsigned int i, ncores = get_core_count(); +- +- if (ncores > nr_cpu_ids) { +- pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", +- ncores, nr_cpu_ids); +- ncores = nr_cpu_ids; +- } +- +- for (i = 0; i < ncores; i++) +- set_cpu_possible(i, true); +-} +- +-static void __init msm_smp_prepare_cpus(unsigned int max_cpus) +-{ +-} +- +-struct smp_operations msm_smp_ops __initdata = { +- .smp_init_cpus = msm_smp_init_cpus, +- .smp_prepare_cpus = msm_smp_prepare_cpus, +- .smp_secondary_init = msm_secondary_init, +- .smp_boot_secondary = msm_boot_secondary, +-#ifdef CONFIG_HOTPLUG_CPU +- .cpu_die = msm_cpu_die, +-#endif +-}; +diff --git a/arch/arm/mach-msm/scm-boot.c b/arch/arm/mach-msm/scm-boot.c +deleted file mode 100644 +index 45cee3e..0000000 +--- a/arch/arm/mach-msm/scm-boot.c ++++ /dev/null +@@ -1,39 +0,0 @@ +-/* Copyright (c) 2010, 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. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA. +- */ +- +-#include <linux/module.h> +-#include <linux/slab.h> +- +-#include "scm.h" +-#include "scm-boot.h" +- +-/* +- * Set the cold/warm boot address for one of the CPU cores. +- */ +-int scm_set_boot_addr(phys_addr_t addr, int flags) +-{ +- struct { +- unsigned int flags; +- phys_addr_t addr; +- } cmd; +- +- cmd.addr = addr; +- cmd.flags = flags; +- return scm_call(SCM_SVC_BOOT, SCM_BOOT_ADDR, +- &cmd, sizeof(cmd), NULL, 0); +-} +-EXPORT_SYMBOL(scm_set_boot_addr); +diff --git a/arch/arm/mach-msm/scm-boot.h b/arch/arm/mach-msm/scm-boot.h +deleted file mode 100644 +index 7be32ff..0000000 +--- a/arch/arm/mach-msm/scm-boot.h ++++ /dev/null +@@ -1,22 +0,0 @@ +-/* Copyright (c) 2010, 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. +- */ +-#ifndef __MACH_SCM_BOOT_H +-#define __MACH_SCM_BOOT_H +- +-#define SCM_BOOT_ADDR 0x1 +-#define SCM_FLAG_COLDBOOT_CPU1 0x1 +-#define SCM_FLAG_WARMBOOT_CPU1 0x2 +-#define SCM_FLAG_WARMBOOT_CPU0 0x4 +- +-int scm_set_boot_addr(phys_addr_t addr, int flags); +- +-#endif +diff --git a/arch/arm/mach-msm/scm.c b/arch/arm/mach-msm/scm.c +deleted file mode 100644 +index c536fd6..0000000 +--- a/arch/arm/mach-msm/scm.c ++++ /dev/null +@@ -1,299 +0,0 @@ +-/* Copyright (c) 2010, 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. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA. +- */ +- +-#include <linux/slab.h> +-#include <linux/io.h> +-#include <linux/module.h> +-#include <linux/mutex.h> +-#include <linux/errno.h> +-#include <linux/err.h> +- +-#include <asm/cacheflush.h> +- +-#include "scm.h" +- +-/* Cache line size for msm8x60 */ +-#define CACHELINESIZE 32 +- +-#define SCM_ENOMEM -5 +-#define SCM_EOPNOTSUPP -4 +-#define SCM_EINVAL_ADDR -3 +-#define SCM_EINVAL_ARG -2 +-#define SCM_ERROR -1 +-#define SCM_INTERRUPTED 1 +- +-static DEFINE_MUTEX(scm_lock); +- +-/** +- * struct scm_command - one SCM command buffer +- * @len: total available memory for command and response +- * @buf_offset: start of command buffer +- * @resp_hdr_offset: start of response buffer +- * @id: command to be executed +- * @buf: buffer returned from scm_get_command_buffer() +- * +- * An SCM command is laid out in memory as follows: +- * +- * ------------------- <--- struct scm_command +- * | command header | +- * ------------------- <--- scm_get_command_buffer() +- * | command buffer | +- * ------------------- <--- struct scm_response and +- * | response header | scm_command_to_response() +- * ------------------- <--- scm_get_response_buffer() +- * | response buffer | +- * ------------------- +- * +- * There can be arbitrary padding between the headers and buffers so +- * you should always use the appropriate scm_get_*_buffer() routines +- * to access the buffers in a safe manner. +- */ +-struct scm_command { +- u32 len; +- u32 buf_offset; +- u32 resp_hdr_offset; +- u32 id; +- u32 buf[0]; +-}; +- +-/** +- * struct scm_response - one SCM response buffer +- * @len: total available memory for response +- * @buf_offset: start of response data relative to start of scm_response +- * @is_complete: indicates if the command has finished processing +- */ +-struct scm_response { +- u32 len; +- u32 buf_offset; +- u32 is_complete; +-}; +- +-/** +- * alloc_scm_command() - Allocate an SCM command +- * @cmd_size: size of the command buffer +- * @resp_size: size of the response buffer +- * +- * Allocate an SCM command, including enough room for the command +- * and response headers as well as the command and response buffers. +- * +- * Returns a valid &scm_command on success or %NULL if the allocation fails. +- */ +-static struct scm_command *alloc_scm_command(size_t cmd_size, size_t resp_size) +-{ +- struct scm_command *cmd; +- size_t len = sizeof(*cmd) + sizeof(struct scm_response) + cmd_size + +- resp_size; +- +- cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL); +- if (cmd) { +- cmd->len = len; +- cmd->buf_offset = offsetof(struct scm_command, buf); +- cmd->resp_hdr_offset = cmd->buf_offset + cmd_size; +- } +- return cmd; +-} +- +-/** +- * free_scm_command() - Free an SCM command +- * @cmd: command to free +- * +- * Free an SCM command. +- */ +-static inline void free_scm_command(struct scm_command *cmd) +-{ +- kfree(cmd); +-} +- +-/** +- * scm_command_to_response() - Get a pointer to a scm_response +- * @cmd: command +- * +- * Returns a pointer to a response for a command. +- */ +-static inline struct scm_response *scm_command_to_response( +- const struct scm_command *cmd) +-{ +- return (void *)cmd + cmd->resp_hdr_offset; +-} +- +-/** +- * scm_get_command_buffer() - Get a pointer to a command buffer +- * @cmd: command +- * +- * Returns a pointer to the command buffer of a command. +- */ +-static inline void *scm_get_command_buffer(const struct scm_command *cmd) +-{ +- return (void *)cmd->buf; +-} +- +-/** +- * scm_get_response_buffer() - Get a pointer to a response buffer +- * @rsp: response +- * +- * Returns a pointer to a response buffer of a response. +- */ +-static inline void *scm_get_response_buffer(const struct scm_response *rsp) +-{ +- return (void *)rsp + rsp->buf_offset; +-} +- +-static int scm_remap_error(int err) +-{ +- switch (err) { +- case SCM_ERROR: +- return -EIO; +- case SCM_EINVAL_ADDR: +- case SCM_EINVAL_ARG: +- return -EINVAL; +- case SCM_EOPNOTSUPP: +- return -EOPNOTSUPP; +- case SCM_ENOMEM: +- return -ENOMEM; +- } +- return -EINVAL; +-} +- +-static u32 smc(u32 cmd_addr) +-{ +- int context_id; +- register u32 r0 asm("r0") = 1; +- register u32 r1 asm("r1") = (u32)&context_id; +- register u32 r2 asm("r2") = cmd_addr; +- do { +- asm volatile( +- __asmeq("%0", "r0") +- __asmeq("%1", "r0") +- __asmeq("%2", "r1") +- __asmeq("%3", "r2") +-#ifdef REQUIRES_SEC +- ".arch_extension sec\n" +-#endif +- "smc #0 @ switch to secure world\n" +- : "=r" (r0) +- : "r" (r0), "r" (r1), "r" (r2) +- : "r3"); +- } while (r0 == SCM_INTERRUPTED); +- +- return r0; +-} +- +-static int __scm_call(const struct scm_command *cmd) +-{ +- int ret; +- u32 cmd_addr = virt_to_phys(cmd); +- +- /* +- * Flush the entire cache here so callers don't have to remember +- * to flush the cache when passing physical addresses to the secure +- * side in the buffer. +- */ +- flush_cache_all(); +- ret = smc(cmd_addr); +- if (ret < 0) +- ret = scm_remap_error(ret); +- +- return ret; +-} +- +-/** +- * scm_call() - Send an SCM command +- * @svc_id: service identifier +- * @cmd_id: command identifier +- * @cmd_buf: command buffer +- * @cmd_len: length of the command buffer +- * @resp_buf: response buffer +- * @resp_len: length of the response buffer +- * +- * Sends a command to the SCM and waits for the command to finish processing. +- */ +-int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, +- void *resp_buf, size_t resp_len) +-{ +- int ret; +- struct scm_command *cmd; +- struct scm_response *rsp; +- +- cmd = alloc_scm_command(cmd_len, resp_len); +- if (!cmd) +- return -ENOMEM; +- +- cmd->id = (svc_id << 10) | cmd_id; +- if (cmd_buf) +- memcpy(scm_get_command_buffer(cmd), cmd_buf, cmd_len); +- +- mutex_lock(&scm_lock); +- ret = __scm_call(cmd); +- mutex_unlock(&scm_lock); +- if (ret) +- goto out; +- +- rsp = scm_command_to_response(cmd); +- do { +- u32 start = (u32)rsp; +- u32 end = (u32)scm_get_response_buffer(rsp) + resp_len; +- start &= ~(CACHELINESIZE - 1); +- while (start < end) { +- asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start) +- : "memory"); +- start += CACHELINESIZE; +- } +- } while (!rsp->is_complete); +- +- if (resp_buf) +- memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len); +-out: +- free_scm_command(cmd); +- return ret; +-} +-EXPORT_SYMBOL(scm_call); +- +-u32 scm_get_version(void) +-{ +- int context_id; +- static u32 version = -1; +- register u32 r0 asm("r0"); +- register u32 r1 asm("r1"); +- +- if (version != -1) +- return version; +- +- mutex_lock(&scm_lock); +- +- r0 = 0x1 << 8; +- r1 = (u32)&context_id; +- do { +- asm volatile( +- __asmeq("%0", "r0") +- __asmeq("%1", "r1") +- __asmeq("%2", "r0") +- __asmeq("%3", "r1") +-#ifdef REQUIRES_SEC +- ".arch_extension sec\n" +-#endif +- "smc #0 @ switch to secure world\n" +- : "=r" (r0), "=r" (r1) +- : "r" (r0), "r" (r1) +- : "r2", "r3"); +- } while (r0 == SCM_INTERRUPTED); +- +- version = r1; +- mutex_unlock(&scm_lock); +- +- return version; +-} +-EXPORT_SYMBOL(scm_get_version); +diff --git a/arch/arm/mach-msm/scm.h b/arch/arm/mach-msm/scm.h +deleted file mode 100644 +index 00b31ea..0000000 +--- a/arch/arm/mach-msm/scm.h ++++ /dev/null +@@ -1,25 +0,0 @@ +-/* Copyright (c) 2010, 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. +- */ +-#ifndef __MACH_SCM_H +-#define __MACH_SCM_H +- +-#define SCM_SVC_BOOT 0x1 +-#define SCM_SVC_PIL 0x2 +- +-extern int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, +- void *resp_buf, size_t resp_len); +- +-#define SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) +- +-extern u32 scm_get_version(void); +- +-#endif +diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig +new file mode 100644 +index 0000000..a028be2 +--- /dev/null ++++ b/arch/arm/mach-qcom/Kconfig +@@ -0,0 +1,33 @@ ++config ARCH_QCOM ++ bool "Qualcomm Support" if ARCH_MULTI_V7 ++ select ARCH_REQUIRE_GPIOLIB ++ select ARM_GIC ++ select CLKSRC_OF ++ select GENERIC_CLOCKEVENTS ++ select HAVE_SMP ++ select QCOM_SCM if SMP ++ help ++ Support for Qualcomm's devicetree based systems. ++ ++if ARCH_QCOM ++ ++menu "Qualcomm SoC Selection" ++ ++config ARCH_MSM8X60 ++ bool "Enable support for MSM8X60" ++ select CLKSRC_QCOM ++ ++config ARCH_MSM8960 ++ bool "Enable support for MSM8960" ++ select CLKSRC_QCOM ++ ++config ARCH_MSM8974 ++ bool "Enable support for MSM8974" ++ select HAVE_ARM_ARCH_TIMER ++ ++endmenu ++ ++config QCOM_SCM ++ bool ++ ++endif +diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile +new file mode 100644 +index 0000000..8f756ae +--- /dev/null ++++ b/arch/arm/mach-qcom/Makefile +@@ -0,0 +1,5 @@ ++obj-y := board.o ++obj-$(CONFIG_SMP) += platsmp.o ++obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o ++ ++CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) +diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c +new file mode 100644 +index 0000000..4529f6b +--- /dev/null ++++ b/arch/arm/mach-qcom/board.c +@@ -0,0 +1,40 @@ ++/* Copyright (c) 2010-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/init.h> ++#include <linux/of.h> ++#include <linux/of_platform.h> ++ ++#include <asm/mach/arch.h> ++#include <asm/mach/map.h> ++ ++extern struct smp_operations msm_smp_ops; ++ ++static const char * const qcom_dt_match[] __initconst = { ++ "qcom,msm8660-surf", ++ "qcom,msm8960-cdp", ++ NULL ++}; ++ ++static const char * const apq8074_dt_match[] __initconst = { ++ "qcom,apq8074-dragonboard", ++ NULL ++}; ++ ++DT_MACHINE_START(QCOM_DT, "Qualcomm (Flattened Device Tree)") ++ .smp = smp_ops(msm_smp_ops), ++ .dt_compat = qcom_dt_match, ++MACHINE_END ++ ++DT_MACHINE_START(APQ_DT, "Qualcomm (Flattened Device Tree)") ++ .dt_compat = apq8074_dt_match, ++MACHINE_END +diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c +new file mode 100644 +index 0000000..67823a7 +--- /dev/null ++++ b/arch/arm/mach-qcom/platsmp.c +@@ -0,0 +1,137 @@ ++/* ++ * Copyright (C) 2002 ARM Ltd. ++ * All Rights Reserved ++ * Copyright (c) 2010, Code Aurora Forum. All rights reserved. ++ * 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 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/init.h> ++#include <linux/errno.h> ++#include <linux/delay.h> ++#include <linux/device.h> ++#include <linux/smp.h> ++#include <linux/io.h> ++ ++#include <asm/cputype.h> ++#include <asm/smp_plat.h> ++ ++#include "scm-boot.h" ++ ++#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x15A0 ++#define SCSS_CPU1CORE_RESET 0xD80 ++#define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64 ++ ++extern void secondary_startup(void); ++ ++static DEFINE_SPINLOCK(boot_lock); ++ ++#ifdef CONFIG_HOTPLUG_CPU ++static void __ref msm_cpu_die(unsigned int cpu) ++{ ++ wfi(); ++} ++#endif ++ ++static inline int get_core_count(void) ++{ ++ /* 1 + the PART[1:0] field of MIDR */ ++ return ((read_cpuid_id() >> 4) & 3) + 1; ++} ++ ++static void msm_secondary_init(unsigned int cpu) ++{ ++ /* ++ * Synchronise with the boot thread. ++ */ ++ spin_lock(&boot_lock); ++ spin_unlock(&boot_lock); ++} ++ ++static void prepare_cold_cpu(unsigned int cpu) ++{ ++ int ret; ++ ret = scm_set_boot_addr(virt_to_phys(secondary_startup), ++ SCM_FLAG_COLDBOOT_CPU1); ++ if (ret == 0) { ++ void __iomem *sc1_base_ptr; ++ sc1_base_ptr = ioremap_nocache(0x00902000, SZ_4K*2); ++ if (sc1_base_ptr) { ++ writel(0, sc1_base_ptr + VDD_SC1_ARRAY_CLAMP_GFS_CTL); ++ writel(0, sc1_base_ptr + SCSS_CPU1CORE_RESET); ++ writel(3, sc1_base_ptr + SCSS_DBG_STATUS_CORE_PWRDUP); ++ iounmap(sc1_base_ptr); ++ } ++ } else ++ printk(KERN_DEBUG "Failed to set secondary core boot " ++ "address\n"); ++} ++ ++static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle) ++{ ++ static int cold_boot_done; ++ ++ /* Only need to bring cpu out of reset this way once */ ++ if (cold_boot_done == false) { ++ prepare_cold_cpu(cpu); ++ cold_boot_done = true; ++ } ++ ++ /* ++ * set synchronisation state between this boot processor ++ * and the secondary one ++ */ ++ spin_lock(&boot_lock); ++ ++ /* ++ * Send the secondary CPU a soft interrupt, thereby causing ++ * the boot monitor to read the system wide flags register, ++ * and branch to the address found there. ++ */ ++ arch_send_wakeup_ipi_mask(cpumask_of(cpu)); ++ ++ /* ++ * now the secondary core is starting up let it run its ++ * calibrations, then wait for it to finish ++ */ ++ spin_unlock(&boot_lock); ++ ++ return 0; ++} ++ ++/* ++ * Initialise the CPU possible map early - this describes the CPUs ++ * which may be present or become present in the system. The msm8x60 ++ * does not support the ARM SCU, so just set the possible cpu mask to ++ * NR_CPUS. ++ */ ++static void __init msm_smp_init_cpus(void) ++{ ++ unsigned int i, ncores = get_core_count(); ++ ++ if (ncores > nr_cpu_ids) { ++ pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", ++ ncores, nr_cpu_ids); ++ ncores = nr_cpu_ids; ++ } ++ ++ for (i = 0; i < ncores; i++) ++ set_cpu_possible(i, true); ++} ++ ++static void __init msm_smp_prepare_cpus(unsigned int max_cpus) ++{ ++} ++ ++struct smp_operations msm_smp_ops __initdata = { ++ .smp_init_cpus = msm_smp_init_cpus, ++ .smp_prepare_cpus = msm_smp_prepare_cpus, ++ .smp_secondary_init = msm_secondary_init, ++ .smp_boot_secondary = msm_boot_secondary, ++#ifdef CONFIG_HOTPLUG_CPU ++ .cpu_die = msm_cpu_die, ++#endif ++}; +diff --git a/arch/arm/mach-qcom/scm-boot.c b/arch/arm/mach-qcom/scm-boot.c +new file mode 100644 +index 0000000..45cee3e +--- /dev/null ++++ b/arch/arm/mach-qcom/scm-boot.c +@@ -0,0 +1,39 @@ ++/* Copyright (c) 2010, 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++#include <linux/module.h> ++#include <linux/slab.h> ++ ++#include "scm.h" ++#include "scm-boot.h" ++ ++/* ++ * Set the cold/warm boot address for one of the CPU cores. ++ */ ++int scm_set_boot_addr(phys_addr_t addr, int flags) ++{ ++ struct { ++ unsigned int flags; ++ phys_addr_t addr; ++ } cmd; ++ ++ cmd.addr = addr; ++ cmd.flags = flags; ++ return scm_call(SCM_SVC_BOOT, SCM_BOOT_ADDR, ++ &cmd, sizeof(cmd), NULL, 0); ++} ++EXPORT_SYMBOL(scm_set_boot_addr); +diff --git a/arch/arm/mach-qcom/scm-boot.h b/arch/arm/mach-qcom/scm-boot.h +new file mode 100644 +index 0000000..7be32ff +--- /dev/null ++++ b/arch/arm/mach-qcom/scm-boot.h +@@ -0,0 +1,22 @@ ++/* Copyright (c) 2010, 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. ++ */ ++#ifndef __MACH_SCM_BOOT_H ++#define __MACH_SCM_BOOT_H ++ ++#define SCM_BOOT_ADDR 0x1 ++#define SCM_FLAG_COLDBOOT_CPU1 0x1 ++#define SCM_FLAG_WARMBOOT_CPU1 0x2 ++#define SCM_FLAG_WARMBOOT_CPU0 0x4 ++ ++int scm_set_boot_addr(phys_addr_t addr, int flags); ++ ++#endif +diff --git a/arch/arm/mach-qcom/scm.c b/arch/arm/mach-qcom/scm.c +new file mode 100644 +index 0000000..c536fd6 +--- /dev/null ++++ b/arch/arm/mach-qcom/scm.c +@@ -0,0 +1,299 @@ ++/* Copyright (c) 2010, 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++#include <linux/slab.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/mutex.h> ++#include <linux/errno.h> ++#include <linux/err.h> ++ ++#include <asm/cacheflush.h> ++ ++#include "scm.h" ++ ++/* Cache line size for msm8x60 */ ++#define CACHELINESIZE 32 ++ ++#define SCM_ENOMEM -5 ++#define SCM_EOPNOTSUPP -4 ++#define SCM_EINVAL_ADDR -3 ++#define SCM_EINVAL_ARG -2 ++#define SCM_ERROR -1 ++#define SCM_INTERRUPTED 1 ++ ++static DEFINE_MUTEX(scm_lock); ++ ++/** ++ * struct scm_command - one SCM command buffer ++ * @len: total available memory for command and response ++ * @buf_offset: start of command buffer ++ * @resp_hdr_offset: start of response buffer ++ * @id: command to be executed ++ * @buf: buffer returned from scm_get_command_buffer() ++ * ++ * An SCM command is laid out in memory as follows: ++ * ++ * ------------------- <--- struct scm_command ++ * | command header | ++ * ------------------- <--- scm_get_command_buffer() ++ * | command buffer | ++ * ------------------- <--- struct scm_response and ++ * | response header | scm_command_to_response() ++ * ------------------- <--- scm_get_response_buffer() ++ * | response buffer | ++ * ------------------- ++ * ++ * There can be arbitrary padding between the headers and buffers so ++ * you should always use the appropriate scm_get_*_buffer() routines ++ * to access the buffers in a safe manner. ++ */ ++struct scm_command { ++ u32 len; ++ u32 buf_offset; ++ u32 resp_hdr_offset; ++ u32 id; ++ u32 buf[0]; ++}; ++ ++/** ++ * struct scm_response - one SCM response buffer ++ * @len: total available memory for response ++ * @buf_offset: start of response data relative to start of scm_response ++ * @is_complete: indicates if the command has finished processing ++ */ ++struct scm_response { ++ u32 len; ++ u32 buf_offset; ++ u32 is_complete; ++}; ++ ++/** ++ * alloc_scm_command() - Allocate an SCM command ++ * @cmd_size: size of the command buffer ++ * @resp_size: size of the response buffer ++ * ++ * Allocate an SCM command, including enough room for the command ++ * and response headers as well as the command and response buffers. ++ * ++ * Returns a valid &scm_command on success or %NULL if the allocation fails. ++ */ ++static struct scm_command *alloc_scm_command(size_t cmd_size, size_t resp_size) ++{ ++ struct scm_command *cmd; ++ size_t len = sizeof(*cmd) + sizeof(struct scm_response) + cmd_size + ++ resp_size; ++ ++ cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL); ++ if (cmd) { ++ cmd->len = len; ++ cmd->buf_offset = offsetof(struct scm_command, buf); ++ cmd->resp_hdr_offset = cmd->buf_offset + cmd_size; ++ } ++ return cmd; ++} ++ ++/** ++ * free_scm_command() - Free an SCM command ++ * @cmd: command to free ++ * ++ * Free an SCM command. ++ */ ++static inline void free_scm_command(struct scm_command *cmd) ++{ ++ kfree(cmd); ++} ++ ++/** ++ * scm_command_to_response() - Get a pointer to a scm_response ++ * @cmd: command ++ * ++ * Returns a pointer to a response for a command. ++ */ ++static inline struct scm_response *scm_command_to_response( ++ const struct scm_command *cmd) ++{ ++ return (void *)cmd + cmd->resp_hdr_offset; ++} ++ ++/** ++ * scm_get_command_buffer() - Get a pointer to a command buffer ++ * @cmd: command ++ * ++ * Returns a pointer to the command buffer of a command. ++ */ ++static inline void *scm_get_command_buffer(const struct scm_command *cmd) ++{ ++ return (void *)cmd->buf; ++} ++ ++/** ++ * scm_get_response_buffer() - Get a pointer to a response buffer ++ * @rsp: response ++ * ++ * Returns a pointer to a response buffer of a response. ++ */ ++static inline void *scm_get_response_buffer(const struct scm_response *rsp) ++{ ++ return (void *)rsp + rsp->buf_offset; ++} ++ ++static int scm_remap_error(int err) ++{ ++ switch (err) { ++ case SCM_ERROR: ++ return -EIO; ++ case SCM_EINVAL_ADDR: ++ case SCM_EINVAL_ARG: ++ return -EINVAL; ++ case SCM_EOPNOTSUPP: ++ return -EOPNOTSUPP; ++ case SCM_ENOMEM: ++ return -ENOMEM; ++ } ++ return -EINVAL; ++} ++ ++static u32 smc(u32 cmd_addr) ++{ ++ int context_id; ++ register u32 r0 asm("r0") = 1; ++ register u32 r1 asm("r1") = (u32)&context_id; ++ register u32 r2 asm("r2") = cmd_addr; ++ do { ++ asm volatile( ++ __asmeq("%0", "r0") ++ __asmeq("%1", "r0") ++ __asmeq("%2", "r1") ++ __asmeq("%3", "r2") ++#ifdef REQUIRES_SEC ++ ".arch_extension sec\n" ++#endif ++ "smc #0 @ switch to secure world\n" ++ : "=r" (r0) ++ : "r" (r0), "r" (r1), "r" (r2) ++ : "r3"); ++ } while (r0 == SCM_INTERRUPTED); ++ ++ return r0; ++} ++ ++static int __scm_call(const struct scm_command *cmd) ++{ ++ int ret; ++ u32 cmd_addr = virt_to_phys(cmd); ++ ++ /* ++ * Flush the entire cache here so callers don't have to remember ++ * to flush the cache when passing physical addresses to the secure ++ * side in the buffer. ++ */ ++ flush_cache_all(); ++ ret = smc(cmd_addr); ++ if (ret < 0) ++ ret = scm_remap_error(ret); ++ ++ return ret; ++} ++ ++/** ++ * scm_call() - Send an SCM command ++ * @svc_id: service identifier ++ * @cmd_id: command identifier ++ * @cmd_buf: command buffer ++ * @cmd_len: length of the command buffer ++ * @resp_buf: response buffer ++ * @resp_len: length of the response buffer ++ * ++ * Sends a command to the SCM and waits for the command to finish processing. ++ */ ++int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, ++ void *resp_buf, size_t resp_len) ++{ ++ int ret; ++ struct scm_command *cmd; ++ struct scm_response *rsp; ++ ++ cmd = alloc_scm_command(cmd_len, resp_len); ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->id = (svc_id << 10) | cmd_id; ++ if (cmd_buf) ++ memcpy(scm_get_command_buffer(cmd), cmd_buf, cmd_len); ++ ++ mutex_lock(&scm_lock); ++ ret = __scm_call(cmd); ++ mutex_unlock(&scm_lock); ++ if (ret) ++ goto out; ++ ++ rsp = scm_command_to_response(cmd); ++ do { ++ u32 start = (u32)rsp; ++ u32 end = (u32)scm_get_response_buffer(rsp) + resp_len; ++ start &= ~(CACHELINESIZE - 1); ++ while (start < end) { ++ asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start) ++ : "memory"); ++ start += CACHELINESIZE; ++ } ++ } while (!rsp->is_complete); ++ ++ if (resp_buf) ++ memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len); ++out: ++ free_scm_command(cmd); ++ return ret; ++} ++EXPORT_SYMBOL(scm_call); ++ ++u32 scm_get_version(void) ++{ ++ int context_id; ++ static u32 version = -1; ++ register u32 r0 asm("r0"); ++ register u32 r1 asm("r1"); ++ ++ if (version != -1) ++ return version; ++ ++ mutex_lock(&scm_lock); ++ ++ r0 = 0x1 << 8; ++ r1 = (u32)&context_id; ++ do { ++ asm volatile( ++ __asmeq("%0", "r0") ++ __asmeq("%1", "r1") ++ __asmeq("%2", "r0") ++ __asmeq("%3", "r1") ++#ifdef REQUIRES_SEC ++ ".arch_extension sec\n" ++#endif ++ "smc #0 @ switch to secure world\n" ++ : "=r" (r0), "=r" (r1) ++ : "r" (r0), "r" (r1) ++ : "r2", "r3"); ++ } while (r0 == SCM_INTERRUPTED); ++ ++ version = r1; ++ mutex_unlock(&scm_lock); ++ ++ return version; ++} ++EXPORT_SYMBOL(scm_get_version); +diff --git a/arch/arm/mach-qcom/scm.h b/arch/arm/mach-qcom/scm.h +new file mode 100644 +index 0000000..00b31ea +--- /dev/null ++++ b/arch/arm/mach-qcom/scm.h +@@ -0,0 +1,25 @@ ++/* Copyright (c) 2010, 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. ++ */ ++#ifndef __MACH_SCM_H ++#define __MACH_SCM_H ++ ++#define SCM_SVC_BOOT 0x1 ++#define SCM_SVC_PIL 0x2 ++ ++extern int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, ++ void *resp_buf, size_t resp_len); ++ ++#define SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) ++ ++extern u32 scm_get_version(void); ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0006-clocksource-qcom-split-building-of-legacy-vs-multipl.patch b/target/linux/ipq806x/patches/0006-clocksource-qcom-split-building-of-legacy-vs-multipl.patch new file mode 100644 index 0000000000..5cb6bf393f --- /dev/null +++ b/target/linux/ipq806x/patches/0006-clocksource-qcom-split-building-of-legacy-vs-multipl.patch @@ -0,0 +1,78 @@ +From 085d4b834dfced8580aab74707e30699b63e7c36 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Wed, 29 Jan 2014 17:01:37 -0600 +Subject: [PATCH 006/182] clocksource: qcom: split building of legacy vs + multiplatform support + +The majority of the clocksource code for the Qualcomm platform is shared +between newer (multiplatform) and older platforms. However there is a bit +of code that isn't, so only build it for the appropriate config. + +Acked-by: Olof Johansson <olof@lixom.net> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/clocksource/qcom-timer.c | 23 ++++++++++++----------- + 1 file changed, 12 insertions(+), 11 deletions(-) + +diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c +index dca829e..e807acf 100644 +--- a/drivers/clocksource/qcom-timer.c ++++ b/drivers/clocksource/qcom-timer.c +@@ -106,15 +106,6 @@ static notrace cycle_t msm_read_timer_count(struct clocksource *cs) + return readl_relaxed(source_base + TIMER_COUNT_VAL); + } + +-static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs) +-{ +- /* +- * Shift timer count down by a constant due to unreliable lower bits +- * on some targets. +- */ +- return msm_read_timer_count(cs) >> MSM_DGT_SHIFT; +-} +- + static struct clocksource msm_clocksource = { + .name = "dg_timer", + .rating = 300, +@@ -228,7 +219,7 @@ err: + sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz); + } + +-#ifdef CONFIG_OF ++#ifdef CONFIG_ARCH_QCOM + static void __init msm_dt_timer_init(struct device_node *np) + { + u32 freq; +@@ -281,7 +272,7 @@ static void __init msm_dt_timer_init(struct device_node *np) + } + CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init); + CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init); +-#endif ++#else + + static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source, + u32 sts) +@@ -301,6 +292,15 @@ static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source, + return 0; + } + ++static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs) ++{ ++ /* ++ * Shift timer count down by a constant due to unreliable lower bits ++ * on some targets. ++ */ ++ return msm_read_timer_count(cs) >> MSM_DGT_SHIFT; ++} ++ + void __init msm7x01_timer_init(void) + { + struct clocksource *cs = &msm_clocksource; +@@ -327,3 +327,4 @@ void __init qsd8x50_timer_init(void) + return; + msm_timer_init(19200000 / 4, 32, 7, false); + } ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0007-ARM-qcom-Rename-various-msm-prefixed-functions-to-qc.patch b/target/linux/ipq806x/patches/0007-ARM-qcom-Rename-various-msm-prefixed-functions-to-qc.patch new file mode 100644 index 0000000000..fc064b27a9 --- /dev/null +++ b/target/linux/ipq806x/patches/0007-ARM-qcom-Rename-various-msm-prefixed-functions-to-qc.patch @@ -0,0 +1,105 @@ +From 35eb6f73546d3b9475652a38fa641bd1a05a1ea1 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Tue, 4 Feb 2014 15:38:45 -0600 +Subject: [PATCH 007/182] ARM: qcom: Rename various msm prefixed functions to + qcom + +As mach-qcom will support a number of different Qualcomm SoC platforms +we replace the msm prefix on function names with qcom to be a bit more +generic. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/mach-qcom/board.c | 4 ++-- + arch/arm/mach-qcom/platsmp.c | 22 +++++++++++----------- + 2 files changed, 13 insertions(+), 13 deletions(-) + +diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c +index 4529f6b..830f69c 100644 +--- a/arch/arm/mach-qcom/board.c ++++ b/arch/arm/mach-qcom/board.c +@@ -17,7 +17,7 @@ + #include <asm/mach/arch.h> + #include <asm/mach/map.h> + +-extern struct smp_operations msm_smp_ops; ++extern struct smp_operations qcom_smp_ops; + + static const char * const qcom_dt_match[] __initconst = { + "qcom,msm8660-surf", +@@ -31,7 +31,7 @@ static const char * const apq8074_dt_match[] __initconst = { + }; + + DT_MACHINE_START(QCOM_DT, "Qualcomm (Flattened Device Tree)") +- .smp = smp_ops(msm_smp_ops), ++ .smp = smp_ops(qcom_smp_ops), + .dt_compat = qcom_dt_match, + MACHINE_END + +diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c +index 67823a7..9c53ea7 100644 +--- a/arch/arm/mach-qcom/platsmp.c ++++ b/arch/arm/mach-qcom/platsmp.c +@@ -30,7 +30,7 @@ extern void secondary_startup(void); + static DEFINE_SPINLOCK(boot_lock); + + #ifdef CONFIG_HOTPLUG_CPU +-static void __ref msm_cpu_die(unsigned int cpu) ++static void __ref qcom_cpu_die(unsigned int cpu) + { + wfi(); + } +@@ -42,7 +42,7 @@ static inline int get_core_count(void) + return ((read_cpuid_id() >> 4) & 3) + 1; + } + +-static void msm_secondary_init(unsigned int cpu) ++static void qcom_secondary_init(unsigned int cpu) + { + /* + * Synchronise with the boot thread. +@@ -70,7 +70,7 @@ static void prepare_cold_cpu(unsigned int cpu) + "address\n"); + } + +-static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle) ++static int qcom_boot_secondary(unsigned int cpu, struct task_struct *idle) + { + static int cold_boot_done; + +@@ -108,7 +108,7 @@ static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle) + * does not support the ARM SCU, so just set the possible cpu mask to + * NR_CPUS. + */ +-static void __init msm_smp_init_cpus(void) ++static void __init qcom_smp_init_cpus(void) + { + unsigned int i, ncores = get_core_count(); + +@@ -122,16 +122,16 @@ static void __init msm_smp_init_cpus(void) + set_cpu_possible(i, true); + } + +-static void __init msm_smp_prepare_cpus(unsigned int max_cpus) ++static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) + { + } + +-struct smp_operations msm_smp_ops __initdata = { +- .smp_init_cpus = msm_smp_init_cpus, +- .smp_prepare_cpus = msm_smp_prepare_cpus, +- .smp_secondary_init = msm_secondary_init, +- .smp_boot_secondary = msm_boot_secondary, ++struct smp_operations qcom_smp_ops __initdata = { ++ .smp_init_cpus = qcom_smp_init_cpus, ++ .smp_prepare_cpus = qcom_smp_prepare_cpus, ++ .smp_secondary_init = qcom_secondary_init, ++ .smp_boot_secondary = qcom_boot_secondary, + #ifdef CONFIG_HOTPLUG_CPU +- .cpu_die = msm_cpu_die, ++ .cpu_die = qcom_cpu_die, + #endif + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0008-ARM-Introduce-CPU_METHOD_OF_DECLARE-for-cpu-hotplug-.patch b/target/linux/ipq806x/patches/0008-ARM-Introduce-CPU_METHOD_OF_DECLARE-for-cpu-hotplug-.patch new file mode 100644 index 0000000000..9579ac2f63 --- /dev/null +++ b/target/linux/ipq806x/patches/0008-ARM-Introduce-CPU_METHOD_OF_DECLARE-for-cpu-hotplug-.patch @@ -0,0 +1,159 @@ +From 48f17325fef9cbebc5cb39aa78f4c1caff5d7b16 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Wed, 30 Oct 2013 18:21:09 -0700 +Subject: [PATCH 008/182] ARM: Introduce CPU_METHOD_OF_DECLARE() for cpu + hotplug/smp + +The goal of multi-platform kernels is to remove the need for mach +directories and machine descriptors. To further that goal, +introduce CPU_METHOD_OF_DECLARE() to allow cpu hotplug/smp +support to be separated from the machine descriptors. +Implementers should specify an enable-method property in their +cpus node and then implement a matching set of smp_ops in their +hotplug/smp code, wiring it up with the CPU_METHOD_OF_DECLARE() +macro. When the kernel is compiled we'll collect all the +enable-method smp_ops into one section for use at boot. + +At boot time we'll look for an enable-method in each cpu node and +try to match that against all known CPU enable methods in the +kernel. If there are no enable-methods in the cpu nodes we +fallback to the cpus node and try to use any enable-method found +there. If that doesn't work we fall back to the old way of using +the machine descriptor. + +Acked-by: Mark Rutland <mark.rutland@arm.com> +Cc: Russell King <linux@arm.linux.org.uk> +Cc: <devicetree@vger.kernel.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/include/asm/smp.h | 9 +++++++++ + arch/arm/kernel/devtree.c | 40 +++++++++++++++++++++++++++++++++++++ + include/asm-generic/vmlinux.lds.h | 10 ++++++++++ + 3 files changed, 59 insertions(+) + +diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h +index 22a3b9b..772435b 100644 +--- a/arch/arm/include/asm/smp.h ++++ b/arch/arm/include/asm/smp.h +@@ -114,6 +114,15 @@ struct smp_operations { + #endif + }; + ++struct of_cpu_method { ++ const char *method; ++ struct smp_operations *ops; ++}; ++ ++#define CPU_METHOD_OF_DECLARE(name, _method, _ops) \ ++ static const struct of_cpu_method __cpu_method_of_table_##name \ ++ __used __section(__cpu_method_of_table) \ ++ = { .method = _method, .ops = _ops } + /* + * set platform specific SMP operations + */ +diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c +index f751714..c7419a5 100644 +--- a/arch/arm/kernel/devtree.c ++++ b/arch/arm/kernel/devtree.c +@@ -18,6 +18,7 @@ + #include <linux/of_fdt.h> + #include <linux/of_irq.h> + #include <linux/of_platform.h> ++#include <linux/smp.h> + + #include <asm/cputype.h> + #include <asm/setup.h> +@@ -63,6 +64,34 @@ void __init arm_dt_memblock_reserve(void) + } + } + ++#ifdef CONFIG_SMP ++extern struct of_cpu_method __cpu_method_of_table_begin[]; ++extern struct of_cpu_method __cpu_method_of_table_end[]; ++ ++static int __init set_smp_ops_by_method(struct device_node *node) ++{ ++ const char *method; ++ struct of_cpu_method *m = __cpu_method_of_table_begin; ++ ++ if (of_property_read_string(node, "enable-method", &method)) ++ return 0; ++ ++ for (; m < __cpu_method_of_table_end; m++) ++ if (!strcmp(m->method, method)) { ++ smp_set_ops(m->ops); ++ return 1; ++ } ++ ++ return 0; ++} ++#else ++static inline int set_smp_ops_by_method(struct device_node *node) ++{ ++ return 1; ++} ++#endif ++ ++ + /* + * arm_dt_init_cpu_maps - Function retrieves cpu nodes from the device tree + * and builds the cpu logical map array containing MPIDR values related to +@@ -79,6 +108,7 @@ void __init arm_dt_init_cpu_maps(void) + * read as 0. + */ + struct device_node *cpu, *cpus; ++ int found_method = 0; + u32 i, j, cpuidx = 1; + u32 mpidr = is_smp() ? read_cpuid_mpidr() & MPIDR_HWID_BITMASK : 0; + +@@ -150,8 +180,18 @@ void __init arm_dt_init_cpu_maps(void) + } + + tmp_map[i] = hwid; ++ ++ if (!found_method) ++ found_method = set_smp_ops_by_method(cpu); + } + ++ /* ++ * Fallback to an enable-method in the cpus node if nothing found in ++ * a cpu node. ++ */ ++ if (!found_method) ++ set_smp_ops_by_method(cpus); ++ + if (!bootcpu_valid) { + pr_warn("DT missing boot CPU MPIDR[23:0], fall back to default cpu_logical_map\n"); + return; +diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h +index bc2121f..bd02ca7 100644 +--- a/include/asm-generic/vmlinux.lds.h ++++ b/include/asm-generic/vmlinux.lds.h +@@ -167,6 +167,15 @@ + #define CLK_OF_TABLES() + #endif + ++#ifdef CONFIG_SMP ++#define CPU_METHOD_OF_TABLES() . = ALIGN(8); \ ++ VMLINUX_SYMBOL(__cpu_method_of_table_begin) = .; \ ++ *(__cpu_method_of_table) \ ++ VMLINUX_SYMBOL(__cpu_method_of_table_end) = .; ++#else ++#define CPU_METHOD_OF_TABLES() ++#endif ++ + #define KERNEL_DTB() \ + STRUCT_ALIGN(); \ + VMLINUX_SYMBOL(__dtb_start) = .; \ +@@ -491,6 +500,7 @@ + MEM_DISCARD(init.rodata) \ + CLK_OF_TABLES() \ + CLKSRC_OF_TABLES() \ ++ CPU_METHOD_OF_TABLES() \ + KERNEL_DTB() \ + IRQCHIP_OF_MATCH_TABLE() + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0009-ARM-qcom-Re-organize-platsmp-to-make-it-extensible.patch b/target/linux/ipq806x/patches/0009-ARM-qcom-Re-organize-platsmp-to-make-it-extensible.patch new file mode 100644 index 0000000000..6601ad4e0d --- /dev/null +++ b/target/linux/ipq806x/patches/0009-ARM-qcom-Re-organize-platsmp-to-make-it-extensible.patch @@ -0,0 +1,249 @@ +From 391848e75f8b0ba14816da1ac8f2d838fd0d5744 Mon Sep 17 00:00:00 2001 +From: Rohit Vaswani <rvaswani@codeaurora.org> +Date: Tue, 21 May 2013 19:13:29 -0700 +Subject: [PATCH 009/182] ARM: qcom: Re-organize platsmp to make it extensible + +This makes it easy to add SMP support for new devices by keying +on a device node for the release sequence. We add the +enable-method property for the cpus property to specify that we +want to use the gcc-msm8660 release sequence (which is going to +look for the global clock controller device node to map some +Scorpion specific power and control registers). We also remove +the nr_cpus detection code as that is done generically in the DT +CPU detection code. + +Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org> +[sboyd: Port to CPU_METHOD_OF_DECLARE] +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/mach-msm/common.h | 2 - + arch/arm/mach-qcom/board.c | 14 ----- + arch/arm/mach-qcom/platsmp.c | 118 +++++++++++++++++++++++------------------- + 3 files changed, 65 insertions(+), 69 deletions(-) + +diff --git a/arch/arm/mach-msm/common.h b/arch/arm/mach-msm/common.h +index 0a4899b..572479a 100644 +--- a/arch/arm/mach-msm/common.h ++++ b/arch/arm/mach-msm/common.h +@@ -23,8 +23,6 @@ extern void msm_map_qsd8x50_io(void); + extern void __iomem *__msm_ioremap_caller(phys_addr_t phys_addr, size_t size, + unsigned int mtype, void *caller); + +-extern struct smp_operations msm_smp_ops; +- + struct msm_mmc_platform_data; + + extern void msm_add_devices(void); +diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c +index 830f69c..bae617e 100644 +--- a/arch/arm/mach-qcom/board.c ++++ b/arch/arm/mach-qcom/board.c +@@ -11,30 +11,16 @@ + */ + + #include <linux/init.h> +-#include <linux/of.h> +-#include <linux/of_platform.h> + + #include <asm/mach/arch.h> +-#include <asm/mach/map.h> +- +-extern struct smp_operations qcom_smp_ops; + + static const char * const qcom_dt_match[] __initconst = { + "qcom,msm8660-surf", + "qcom,msm8960-cdp", +- NULL +-}; +- +-static const char * const apq8074_dt_match[] __initconst = { + "qcom,apq8074-dragonboard", + NULL + }; + + DT_MACHINE_START(QCOM_DT, "Qualcomm (Flattened Device Tree)") +- .smp = smp_ops(qcom_smp_ops), + .dt_compat = qcom_dt_match, + MACHINE_END +- +-DT_MACHINE_START(APQ_DT, "Qualcomm (Flattened Device Tree)") +- .dt_compat = apq8074_dt_match, +-MACHINE_END +diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c +index 9c53ea7..ec8604d 100644 +--- a/arch/arm/mach-qcom/platsmp.c ++++ b/arch/arm/mach-qcom/platsmp.c +@@ -13,17 +13,18 @@ + #include <linux/errno.h> + #include <linux/delay.h> + #include <linux/device.h> ++#include <linux/of.h> ++#include <linux/of_address.h> + #include <linux/smp.h> + #include <linux/io.h> + +-#include <asm/cputype.h> + #include <asm/smp_plat.h> + + #include "scm-boot.h" + +-#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x15A0 +-#define SCSS_CPU1CORE_RESET 0xD80 +-#define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64 ++#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x35a0 ++#define SCSS_CPU1CORE_RESET 0x2d80 ++#define SCSS_DBG_STATUS_CORE_PWRDUP 0x2e64 + + extern void secondary_startup(void); + +@@ -36,12 +37,6 @@ static void __ref qcom_cpu_die(unsigned int cpu) + } + #endif + +-static inline int get_core_count(void) +-{ +- /* 1 + the PART[1:0] field of MIDR */ +- return ((read_cpuid_id() >> 4) & 3) + 1; +-} +- + static void qcom_secondary_init(unsigned int cpu) + { + /* +@@ -51,33 +46,41 @@ static void qcom_secondary_init(unsigned int cpu) + spin_unlock(&boot_lock); + } + +-static void prepare_cold_cpu(unsigned int cpu) ++static int scss_release_secondary(unsigned int cpu) + { +- int ret; +- ret = scm_set_boot_addr(virt_to_phys(secondary_startup), +- SCM_FLAG_COLDBOOT_CPU1); +- if (ret == 0) { +- void __iomem *sc1_base_ptr; +- sc1_base_ptr = ioremap_nocache(0x00902000, SZ_4K*2); +- if (sc1_base_ptr) { +- writel(0, sc1_base_ptr + VDD_SC1_ARRAY_CLAMP_GFS_CTL); +- writel(0, sc1_base_ptr + SCSS_CPU1CORE_RESET); +- writel(3, sc1_base_ptr + SCSS_DBG_STATUS_CORE_PWRDUP); +- iounmap(sc1_base_ptr); +- } +- } else +- printk(KERN_DEBUG "Failed to set secondary core boot " +- "address\n"); ++ struct device_node *node; ++ void __iomem *base; ++ ++ node = of_find_compatible_node(NULL, NULL, "qcom,gcc-msm8660"); ++ if (!node) { ++ pr_err("%s: can't find node\n", __func__); ++ return -ENXIO; ++ } ++ ++ base = of_iomap(node, 0); ++ of_node_put(node); ++ if (!base) ++ return -ENOMEM; ++ ++ writel_relaxed(0, base + VDD_SC1_ARRAY_CLAMP_GFS_CTL); ++ writel_relaxed(0, base + SCSS_CPU1CORE_RESET); ++ writel_relaxed(3, base + SCSS_DBG_STATUS_CORE_PWRDUP); ++ mb(); ++ iounmap(base); ++ ++ return 0; + } + +-static int qcom_boot_secondary(unsigned int cpu, struct task_struct *idle) ++static DEFINE_PER_CPU(int, cold_boot_done); ++ ++static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int)) + { +- static int cold_boot_done; ++ int ret = 0; + +- /* Only need to bring cpu out of reset this way once */ +- if (cold_boot_done == false) { +- prepare_cold_cpu(cpu); +- cold_boot_done = true; ++ if (!per_cpu(cold_boot_done, cpu)) { ++ ret = func(cpu); ++ if (!ret) ++ per_cpu(cold_boot_done, cpu) = true; + } + + /* +@@ -99,39 +102,48 @@ static int qcom_boot_secondary(unsigned int cpu, struct task_struct *idle) + */ + spin_unlock(&boot_lock); + +- return 0; ++ return ret; + } + +-/* +- * Initialise the CPU possible map early - this describes the CPUs +- * which may be present or become present in the system. The msm8x60 +- * does not support the ARM SCU, so just set the possible cpu mask to +- * NR_CPUS. +- */ +-static void __init qcom_smp_init_cpus(void) ++static int msm8660_boot_secondary(unsigned int cpu, struct task_struct *idle) + { +- unsigned int i, ncores = get_core_count(); +- +- if (ncores > nr_cpu_ids) { +- pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", +- ncores, nr_cpu_ids); +- ncores = nr_cpu_ids; +- } +- +- for (i = 0; i < ncores; i++) +- set_cpu_possible(i, true); ++ return qcom_boot_secondary(cpu, scss_release_secondary); + } + + static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) + { ++ int cpu, map; ++ unsigned int flags = 0; ++ static const int cold_boot_flags[] = { ++ 0, ++ SCM_FLAG_COLDBOOT_CPU1, ++ }; ++ ++ for_each_present_cpu(cpu) { ++ map = cpu_logical_map(cpu); ++ if (WARN_ON(map >= ARRAY_SIZE(cold_boot_flags))) { ++ set_cpu_present(cpu, false); ++ continue; ++ } ++ flags |= cold_boot_flags[map]; ++ } ++ ++ if (scm_set_boot_addr(virt_to_phys(secondary_startup), flags)) { ++ for_each_present_cpu(cpu) { ++ if (cpu == smp_processor_id()) ++ continue; ++ set_cpu_present(cpu, false); ++ } ++ pr_warn("Failed to set CPU boot address, disabling SMP\n"); ++ } + } + +-struct smp_operations qcom_smp_ops __initdata = { +- .smp_init_cpus = qcom_smp_init_cpus, ++static struct smp_operations smp_msm8660_ops __initdata = { + .smp_prepare_cpus = qcom_smp_prepare_cpus, + .smp_secondary_init = qcom_secondary_init, +- .smp_boot_secondary = qcom_boot_secondary, ++ .smp_boot_secondary = msm8660_boot_secondary, + #ifdef CONFIG_HOTPLUG_CPU + .cpu_die = qcom_cpu_die, + #endif + }; ++CPU_METHOD_OF_DECLARE(qcom_smp, "qcom,gcc-msm8660", &smp_msm8660_ops); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0010-devicetree-bindings-Document-Krait-Scorpion-cpus-and.patch b/target/linux/ipq806x/patches/0010-devicetree-bindings-Document-Krait-Scorpion-cpus-and.patch new file mode 100644 index 0000000000..0bcd3e873b --- /dev/null +++ b/target/linux/ipq806x/patches/0010-devicetree-bindings-Document-Krait-Scorpion-cpus-and.patch @@ -0,0 +1,70 @@ +From 236d07c7bb0c758ea40ea0110d37306d2e7d9a4b Mon Sep 17 00:00:00 2001 +From: Rohit Vaswani <rvaswani@codeaurora.org> +Date: Thu, 31 Oct 2013 17:26:33 -0700 +Subject: [PATCH 010/182] devicetree: bindings: Document Krait/Scorpion cpus + and enable-method + +Scorpion and Krait don't use the spin-table enable-method. +Instead they rely on mmio register accesses to enable power and +clocks to bring CPUs out of reset. Document their enable-methods. + +Cc: <devicetree@vger.kernel.org> +Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org> +[sboyd: Split off into separate patch, renamed methods to +match compatible nodes] +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + Documentation/devicetree/bindings/arm/cpus.txt | 25 +++++++++++++++++++++++- + 1 file changed, 24 insertions(+), 1 deletion(-) + +diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt +index 9130435..333f4ae 100644 +--- a/Documentation/devicetree/bindings/arm/cpus.txt ++++ b/Documentation/devicetree/bindings/arm/cpus.txt +@@ -180,7 +180,11 @@ nodes to be present and contain the properties described below. + be one of: + "spin-table" + "psci" +- # On ARM 32-bit systems this property is optional. ++ # On ARM 32-bit systems this property is optional and ++ can be one of: ++ "qcom,gcc-msm8660" ++ "qcom,kpss-acc-v1" ++ "qcom,kpss-acc-v2" + + - cpu-release-addr + Usage: required for systems that have an "enable-method" +@@ -191,6 +195,21 @@ nodes to be present and contain the properties described below. + property identifying a 64-bit zero-initialised + memory location. + ++ - qcom,saw ++ Usage: required for systems that have an "enable-method" ++ property value of "qcom,kpss-acc-v1" or ++ "qcom,kpss-acc-v2" ++ Value type: <phandle> ++ Definition: Specifies the SAW[1] node associated with this CPU. ++ ++ - qcom,acc ++ Usage: required for systems that have an "enable-method" ++ property value of "qcom,kpss-acc-v1" or ++ "qcom,kpss-acc-v2" ++ Value type: <phandle> ++ Definition: Specifies the ACC[2] node associated with this CPU. ++ ++ + Example 1 (dual-cluster big.LITTLE system 32-bit): + + cpus { +@@ -382,3 +401,7 @@ cpus { + cpu-release-addr = <0 0x20000000>; + }; + }; ++ ++-- ++[1] arm/msm/qcom,saw2.txt ++[2] arm/msm/qcom,kpss-acc.txt +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0011-devicetree-bindings-Document-qcom-kpss-acc.patch b/target/linux/ipq806x/patches/0011-devicetree-bindings-Document-qcom-kpss-acc.patch new file mode 100644 index 0000000000..fc57727b37 --- /dev/null +++ b/target/linux/ipq806x/patches/0011-devicetree-bindings-Document-qcom-kpss-acc.patch @@ -0,0 +1,55 @@ +From 3a4fe9a3a4aff8b1f1c3685bc9b6adbe739d7367 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 31 Oct 2013 18:20:30 -0700 +Subject: [PATCH 011/182] devicetree: bindings: Document qcom,kpss-acc + +The kpss acc binding describes the clock, reset, and power domain +controller for a Krait CPU. + +Cc: <devicetree@vger.kernel.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + .../devicetree/bindings/arm/msm/qcom,kpss-acc.txt | 30 ++++++++++++++++++++ + 1 file changed, 30 insertions(+) + create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt + +diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt +new file mode 100644 +index 0000000..1333db9 +--- /dev/null ++++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt +@@ -0,0 +1,30 @@ ++Krait Processor Sub-system (KPSS) Application Clock Controller (ACC) ++ ++The KPSS ACC provides clock, power domain, and reset control to a Krait CPU. ++There is one ACC register region per CPU within the KPSS remapped region as ++well as an alias register region that remaps accesses to the ACC associated ++with the CPU accessing the region. ++ ++PROPERTIES ++ ++- compatible: ++ Usage: required ++ Value type: <string> ++ Definition: should be one of: ++ "qcom,kpss-acc-v1" ++ "qcom,kpss-acc-v2" ++ ++- reg: ++ Usage: required ++ Value type: <prop-encoded-array> ++ Definition: the first element specifies the base address and size of ++ the register region. An optional second element specifies ++ the base address and size of the alias register region. ++ ++Example: ++ ++ clock-controller@2088000 { ++ compatible = "qcom,kpss-acc-v2"; ++ reg = <0x02088000 0x1000>, ++ <0x02008000 0x1000>; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0012-devicetree-bindings-Document-qcom-saw2-node.patch b/target/linux/ipq806x/patches/0012-devicetree-bindings-Document-qcom-saw2-node.patch new file mode 100644 index 0000000000..5bf18c1f98 --- /dev/null +++ b/target/linux/ipq806x/patches/0012-devicetree-bindings-Document-qcom-saw2-node.patch @@ -0,0 +1,60 @@ +From 17adce4d3de1fca7761607d572f4979557f0f604 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 31 Oct 2013 18:20:30 -0700 +Subject: [PATCH 012/182] devicetree: bindings: Document qcom,saw2 node + +The saw2 binding describes the SPM/AVS wrapper hardware used to +control the regulator supplying voltage to the Krait CPUs. + +Cc: <devicetree@vger.kernel.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + .../devicetree/bindings/arm/msm/qcom,saw2.txt | 35 ++++++++++++++++++++ + 1 file changed, 35 insertions(+) + create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt + +diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt b/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt +new file mode 100644 +index 0000000..1505fb8 +--- /dev/null ++++ b/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt +@@ -0,0 +1,35 @@ ++SPM AVS Wrapper 2 (SAW2) ++ ++The SAW2 is a wrapper around the Subsystem Power Manager (SPM) and the ++Adaptive Voltage Scaling (AVS) hardware. The SPM is a programmable ++micro-controller that transitions a piece of hardware (like a processor or ++subsystem) into and out of low power modes via a direct connection to ++the PMIC. It can also be wired up to interact with other processors in the ++system, notifying them when a low power state is entered or exited. ++ ++PROPERTIES ++ ++- compatible: ++ Usage: required ++ Value type: <string> ++ Definition: shall contain "qcom,saw2". A more specific value should be ++ one of: ++ "qcom,saw2-v1" ++ "qcom,saw2-v1.1" ++ "qcom,saw2-v2" ++ "qcom,saw2-v2.1" ++ ++- reg: ++ Usage: required ++ Value type: <prop-encoded-array> ++ Definition: the first element specifies the base address and size of ++ the register region. An optional second element specifies ++ the base address and size of the alias register region. ++ ++ ++Example: ++ ++ regulator@2099000 { ++ compatible = "qcom,saw2"; ++ reg = <0x02099000 0x1000>, <0x02009000 0x1000>; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0013-ARM-qcom-Add-SMP-support-for-KPSSv1.patch b/target/linux/ipq806x/patches/0013-ARM-qcom-Add-SMP-support-for-KPSSv1.patch new file mode 100644 index 0000000000..66f667c2a8 --- /dev/null +++ b/target/linux/ipq806x/patches/0013-ARM-qcom-Add-SMP-support-for-KPSSv1.patch @@ -0,0 +1,181 @@ +From 8e843640b3c4a43b963332fdc7b233948ad25a5b Mon Sep 17 00:00:00 2001 +From: Rohit Vaswani <rvaswani@codeaurora.org> +Date: Tue, 21 May 2013 19:13:50 -0700 +Subject: [PATCH 013/182] ARM: qcom: Add SMP support for KPSSv1 + +Implement support for the Krait CPU release sequence when the +CPUs are part of the first version of the krait processor +subsystem. + +Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/mach-qcom/platsmp.c | 106 +++++++++++++++++++++++++++++++++++++++++ + arch/arm/mach-qcom/scm-boot.h | 8 ++-- + 2 files changed, 111 insertions(+), 3 deletions(-) + +diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c +index ec8604d..cb0783f 100644 +--- a/arch/arm/mach-qcom/platsmp.c ++++ b/arch/arm/mach-qcom/platsmp.c +@@ -26,6 +26,16 @@ + #define SCSS_CPU1CORE_RESET 0x2d80 + #define SCSS_DBG_STATUS_CORE_PWRDUP 0x2e64 + ++#define APCS_CPU_PWR_CTL 0x04 ++#define PLL_CLAMP BIT(8) ++#define CORE_PWRD_UP BIT(7) ++#define COREPOR_RST BIT(5) ++#define CORE_RST BIT(4) ++#define L2DT_SLP BIT(3) ++#define CLAMP BIT(0) ++ ++#define APCS_SAW2_VCTL 0x14 ++ + extern void secondary_startup(void); + + static DEFINE_SPINLOCK(boot_lock); +@@ -71,6 +81,85 @@ static int scss_release_secondary(unsigned int cpu) + return 0; + } + ++static int kpssv1_release_secondary(unsigned int cpu) ++{ ++ int ret = 0; ++ void __iomem *reg, *saw_reg; ++ struct device_node *cpu_node, *acc_node, *saw_node; ++ u32 val; ++ ++ cpu_node = of_get_cpu_node(cpu, NULL); ++ if (!cpu_node) ++ return -ENODEV; ++ ++ acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0); ++ if (!acc_node) { ++ ret = -ENODEV; ++ goto out_acc; ++ } ++ ++ saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0); ++ if (!saw_node) { ++ ret = -ENODEV; ++ goto out_saw; ++ } ++ ++ reg = of_iomap(acc_node, 0); ++ if (!reg) { ++ ret = -ENOMEM; ++ goto out_acc_map; ++ } ++ ++ saw_reg = of_iomap(saw_node, 0); ++ if (!saw_reg) { ++ ret = -ENOMEM; ++ goto out_saw_map; ++ } ++ ++ /* Turn on CPU rail */ ++ writel_relaxed(0xA4, saw_reg + APCS_SAW2_VCTL); ++ mb(); ++ udelay(512); ++ ++ /* Krait bring-up sequence */ ++ val = PLL_CLAMP | L2DT_SLP | CLAMP; ++ writel_relaxed(val, reg + APCS_CPU_PWR_CTL); ++ val &= ~L2DT_SLP; ++ writel_relaxed(val, reg + APCS_CPU_PWR_CTL); ++ mb(); ++ ndelay(300); ++ ++ val |= COREPOR_RST; ++ writel_relaxed(val, reg + APCS_CPU_PWR_CTL); ++ mb(); ++ udelay(2); ++ ++ val &= ~CLAMP; ++ writel_relaxed(val, reg + APCS_CPU_PWR_CTL); ++ mb(); ++ udelay(2); ++ ++ val &= ~COREPOR_RST; ++ writel_relaxed(val, reg + APCS_CPU_PWR_CTL); ++ mb(); ++ udelay(100); ++ ++ val |= CORE_PWRD_UP; ++ writel_relaxed(val, reg + APCS_CPU_PWR_CTL); ++ mb(); ++ ++ iounmap(saw_reg); ++out_saw_map: ++ iounmap(reg); ++out_acc_map: ++ of_node_put(saw_node); ++out_saw: ++ of_node_put(acc_node); ++out_acc: ++ of_node_put(cpu_node); ++ return ret; ++} ++ + static DEFINE_PER_CPU(int, cold_boot_done); + + static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int)) +@@ -110,6 +199,11 @@ static int msm8660_boot_secondary(unsigned int cpu, struct task_struct *idle) + return qcom_boot_secondary(cpu, scss_release_secondary); + } + ++static int kpssv1_boot_secondary(unsigned int cpu, struct task_struct *idle) ++{ ++ return qcom_boot_secondary(cpu, kpssv1_release_secondary); ++} ++ + static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) + { + int cpu, map; +@@ -117,6 +211,8 @@ static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) + static const int cold_boot_flags[] = { + 0, + SCM_FLAG_COLDBOOT_CPU1, ++ SCM_FLAG_COLDBOOT_CPU2, ++ SCM_FLAG_COLDBOOT_CPU3, + }; + + for_each_present_cpu(cpu) { +@@ -147,3 +243,13 @@ static struct smp_operations smp_msm8660_ops __initdata = { + #endif + }; + CPU_METHOD_OF_DECLARE(qcom_smp, "qcom,gcc-msm8660", &smp_msm8660_ops); ++ ++static struct smp_operations qcom_smp_kpssv1_ops __initdata = { ++ .smp_prepare_cpus = qcom_smp_prepare_cpus, ++ .smp_secondary_init = qcom_secondary_init, ++ .smp_boot_secondary = kpssv1_boot_secondary, ++#ifdef CONFIG_HOTPLUG_CPU ++ .cpu_die = qcom_cpu_die, ++#endif ++}; ++CPU_METHOD_OF_DECLARE(qcom_smp_kpssv1, "qcom,kpss-acc-v1", &qcom_smp_kpssv1_ops); +diff --git a/arch/arm/mach-qcom/scm-boot.h b/arch/arm/mach-qcom/scm-boot.h +index 7be32ff..6aabb24 100644 +--- a/arch/arm/mach-qcom/scm-boot.h ++++ b/arch/arm/mach-qcom/scm-boot.h +@@ -13,9 +13,11 @@ + #define __MACH_SCM_BOOT_H + + #define SCM_BOOT_ADDR 0x1 +-#define SCM_FLAG_COLDBOOT_CPU1 0x1 +-#define SCM_FLAG_WARMBOOT_CPU1 0x2 +-#define SCM_FLAG_WARMBOOT_CPU0 0x4 ++#define SCM_FLAG_COLDBOOT_CPU1 0x01 ++#define SCM_FLAG_COLDBOOT_CPU2 0x08 ++#define SCM_FLAG_COLDBOOT_CPU3 0x20 ++#define SCM_FLAG_WARMBOOT_CPU0 0x04 ++#define SCM_FLAG_WARMBOOT_CPU1 0x02 + + int scm_set_boot_addr(phys_addr_t addr, int flags); + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0014-ARM-qcom-Add-SMP-support-for-KPSSv2.patch b/target/linux/ipq806x/patches/0014-ARM-qcom-Add-SMP-support-for-KPSSv2.patch new file mode 100644 index 0000000000..03fdaf3eef --- /dev/null +++ b/target/linux/ipq806x/patches/0014-ARM-qcom-Add-SMP-support-for-KPSSv2.patch @@ -0,0 +1,172 @@ +From eb07c23d45ddf10fa89296e6c6c6aed553d8bbf5 Mon Sep 17 00:00:00 2001 +From: Rohit Vaswani <rvaswani@codeaurora.org> +Date: Fri, 21 Jun 2013 17:09:13 -0700 +Subject: [PATCH 014/182] ARM: qcom: Add SMP support for KPSSv2 + +Implement support for the Krait CPU release sequence when the +CPUs are part of the second version of the Krait processor +subsystem. + +Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/mach-qcom/platsmp.c | 123 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 123 insertions(+) + +diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c +index cb0783f..d690856 100644 +--- a/arch/arm/mach-qcom/platsmp.c ++++ b/arch/arm/mach-qcom/platsmp.c +@@ -34,7 +34,15 @@ + #define L2DT_SLP BIT(3) + #define CLAMP BIT(0) + ++#define APC_PWR_GATE_CTL 0x14 ++#define BHS_CNT_SHIFT 24 ++#define LDO_PWR_DWN_SHIFT 16 ++#define LDO_BYP_SHIFT 8 ++#define BHS_SEG_SHIFT 1 ++#define BHS_EN BIT(0) ++ + #define APCS_SAW2_VCTL 0x14 ++#define APCS_SAW2_2_VCTL 0x1c + + extern void secondary_startup(void); + +@@ -160,6 +168,106 @@ out_acc: + return ret; + } + ++static int kpssv2_release_secondary(unsigned int cpu) ++{ ++ void __iomem *reg; ++ struct device_node *cpu_node, *l2_node, *acc_node, *saw_node; ++ void __iomem *l2_saw_base; ++ unsigned reg_val; ++ int ret; ++ ++ cpu_node = of_get_cpu_node(cpu, NULL); ++ if (!cpu_node) ++ return -ENODEV; ++ ++ acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0); ++ if (!acc_node) { ++ ret = -ENODEV; ++ goto out_acc; ++ } ++ ++ l2_node = of_parse_phandle(cpu_node, "next-level-cache", 0); ++ if (!l2_node) { ++ ret = -ENODEV; ++ goto out_l2; ++ } ++ ++ saw_node = of_parse_phandle(l2_node, "qcom,saw", 0); ++ if (!saw_node) { ++ ret = -ENODEV; ++ goto out_saw; ++ } ++ ++ reg = of_iomap(acc_node, 0); ++ if (!reg) { ++ ret = -ENOMEM; ++ goto out_map; ++ } ++ ++ l2_saw_base = of_iomap(saw_node, 0); ++ if (!l2_saw_base) { ++ ret = -ENOMEM; ++ goto out_saw_map; ++ } ++ ++ /* Turn on the BHS, turn off LDO Bypass and power down LDO */ ++ reg_val = (64 << BHS_CNT_SHIFT) | (0x3f << LDO_PWR_DWN_SHIFT) | BHS_EN; ++ writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL); ++ mb(); ++ /* wait for the BHS to settle */ ++ udelay(1); ++ ++ /* Turn on BHS segments */ ++ reg_val |= 0x3f << BHS_SEG_SHIFT; ++ writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL); ++ mb(); ++ /* wait for the BHS to settle */ ++ udelay(1); ++ ++ /* Finally turn on the bypass so that BHS supplies power */ ++ reg_val |= 0x3f << LDO_BYP_SHIFT; ++ writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL); ++ ++ /* enable max phases */ ++ writel_relaxed(0x10003, l2_saw_base + APCS_SAW2_2_VCTL); ++ mb(); ++ udelay(50); ++ ++ reg_val = COREPOR_RST | CLAMP; ++ writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); ++ mb(); ++ udelay(2); ++ ++ reg_val &= ~CLAMP; ++ writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); ++ mb(); ++ udelay(2); ++ ++ reg_val &= ~COREPOR_RST; ++ writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); ++ mb(); ++ ++ reg_val |= CORE_PWRD_UP; ++ writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); ++ mb(); ++ ++ ret = 0; ++ ++ iounmap(l2_saw_base); ++out_saw_map: ++ iounmap(reg); ++out_map: ++ of_node_put(saw_node); ++out_saw: ++ of_node_put(l2_node); ++out_l2: ++ of_node_put(acc_node); ++out_acc: ++ of_node_put(cpu_node); ++ ++ return ret; ++} ++ + static DEFINE_PER_CPU(int, cold_boot_done); + + static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int)) +@@ -204,6 +312,11 @@ static int kpssv1_boot_secondary(unsigned int cpu, struct task_struct *idle) + return qcom_boot_secondary(cpu, kpssv1_release_secondary); + } + ++static int kpssv2_boot_secondary(unsigned int cpu, struct task_struct *idle) ++{ ++ return qcom_boot_secondary(cpu, kpssv2_release_secondary); ++} ++ + static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) + { + int cpu, map; +@@ -253,3 +366,13 @@ static struct smp_operations qcom_smp_kpssv1_ops __initdata = { + #endif + }; + CPU_METHOD_OF_DECLARE(qcom_smp_kpssv1, "qcom,kpss-acc-v1", &qcom_smp_kpssv1_ops); ++ ++static struct smp_operations qcom_smp_kpssv2_ops __initdata = { ++ .smp_prepare_cpus = qcom_smp_prepare_cpus, ++ .smp_secondary_init = qcom_secondary_init, ++ .smp_boot_secondary = kpssv2_boot_secondary, ++#ifdef CONFIG_HOTPLUG_CPU ++ .cpu_die = qcom_cpu_die, ++#endif ++}; ++CPU_METHOD_OF_DECLARE(qcom_smp_kpssv2, "qcom,kpss-acc-v2", &qcom_smp_kpssv2_ops); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0015-tty-serial-msm-Enable-building-msm_serial-for-ARCH_Q.patch b/target/linux/ipq806x/patches/0015-tty-serial-msm-Enable-building-msm_serial-for-ARCH_Q.patch new file mode 100644 index 0000000000..9dbc06dc26 --- /dev/null +++ b/target/linux/ipq806x/patches/0015-tty-serial-msm-Enable-building-msm_serial-for-ARCH_Q.patch @@ -0,0 +1,32 @@ +From 17368813d4182894c6d58b66f9fccd339364de8f Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Wed, 29 Jan 2014 17:23:06 -0600 +Subject: [PATCH 015/182] tty: serial: msm: Enable building msm_serial for + ARCH_QCOM + +We've split Qualcomm MSM support into legacy and multiplatform. So add +the ability to build the serial driver on the newer ARCH_QCOM +multiplatform. + +Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/tty/serial/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig +index a3815ea..ce9b12d 100644 +--- a/drivers/tty/serial/Kconfig ++++ b/drivers/tty/serial/Kconfig +@@ -1024,7 +1024,7 @@ config SERIAL_SGI_IOC3 + + config SERIAL_MSM + bool "MSM on-chip serial port support" +- depends on ARCH_MSM ++ depends on ARCH_MSM || ARCH_QCOM + select SERIAL_CORE + + config SERIAL_MSM_CONSOLE +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0016-drm-msm-drop-ARCH_MSM-Kconfig-depend.patch b/target/linux/ipq806x/patches/0016-drm-msm-drop-ARCH_MSM-Kconfig-depend.patch new file mode 100644 index 0000000000..46e1fb79a2 --- /dev/null +++ b/target/linux/ipq806x/patches/0016-drm-msm-drop-ARCH_MSM-Kconfig-depend.patch @@ -0,0 +1,33 @@ +From aa3cf89746c43172517378224277ba961c46e28c Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 30 Jan 2014 14:45:05 -0600 +Subject: [PATCH 016/182] drm/msm: drop ARCH_MSM Kconfig depend + +The ARCH_MSM depend is redundant with ARCH_MSM8960, so we can remove it. +Additionally, we are splitting Qualcomm MSM support into legacy (ARCH_MSM) +and multiplatform (ARCH_QCOM). The MSM8960 with be ARCH_QCOM going forward +so dropping ARCH_MSM will work properly for the new ARCH_QCOM multiplatform +build. + +Acked-by: Rob Clark <robdclark@gmail.com> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/gpu/drm/msm/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig +index c69d1e0..b698497 100644 +--- a/drivers/gpu/drm/msm/Kconfig ++++ b/drivers/gpu/drm/msm/Kconfig +@@ -3,7 +3,7 @@ config DRM_MSM + tristate "MSM DRM" + depends on DRM + depends on MSM_IOMMU +- depends on (ARCH_MSM && ARCH_MSM8960) || (ARM && COMPILE_TEST) ++ depends on ARCH_MSM8960 || (ARM && COMPILE_TEST) + select DRM_KMS_HELPER + select SHMEM + select TMPFS +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0017-power-reset-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch b/target/linux/ipq806x/patches/0017-power-reset-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch new file mode 100644 index 0000000000..868146c40f --- /dev/null +++ b/target/linux/ipq806x/patches/0017-power-reset-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch @@ -0,0 +1,32 @@ +From 6e8707828be07397ee8ee437a6ef1b8f73f82287 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 30 Jan 2014 14:46:08 -0600 +Subject: [PATCH 017/182] power: reset: msm - switch Kconfig to ARCH_QCOM + depends + +We've split Qualcomm MSM support into legacy and multiplatform. The reset +driver is only relevant on the multiplatform supported SoCs so switch the +Kconfig depends to ARCH_QCOM. + +Acked-by: Dmitry Eremin-Solenikov +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/power/reset/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig +index 6d452a7..fa0e4e0 100644 +--- a/drivers/power/reset/Kconfig ++++ b/drivers/power/reset/Kconfig +@@ -22,7 +22,7 @@ config POWER_RESET_GPIO + + config POWER_RESET_MSM + bool "Qualcomm MSM power-off driver" +- depends on POWER_RESET && ARCH_MSM ++ depends on POWER_RESET && ARCH_QCOM + help + Power off and restart support for Qualcomm boards. + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0018-hwrng-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch b/target/linux/ipq806x/patches/0018-hwrng-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch new file mode 100644 index 0000000000..b375feca2c --- /dev/null +++ b/target/linux/ipq806x/patches/0018-hwrng-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch @@ -0,0 +1,38 @@ +From 4aa784548190c69d946b4dfbc0592a3ed7cd18da Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 30 Jan 2014 14:43:49 -0600 +Subject: [PATCH 018/182] hwrng: msm: switch Kconfig to ARCH_QCOM depends + +We've split Qualcomm MSM support into legacy and multiplatform. The RNG +driver is only relevant on the multiplatform supported SoCs so switch the +Kconfig depends to ARCH_QCOM. + +Acked-by: Herbert Xu <herbert@gondor.apana.org.au> +CC: Stanimir Varbanov <svarbanov@mm-sol.com> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/char/hw_random/Kconfig | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig +index 2f2b084..244759b 100644 +--- a/drivers/char/hw_random/Kconfig ++++ b/drivers/char/hw_random/Kconfig +@@ -342,11 +342,11 @@ config HW_RANDOM_TPM + If unsure, say Y. + + config HW_RANDOM_MSM +- tristate "Qualcomm MSM Random Number Generator support" +- depends on HW_RANDOM && ARCH_MSM ++ tristate "Qualcomm SoCs Random Number Generator support" ++ depends on HW_RANDOM && ARCH_QCOM + ---help--- + This driver provides kernel-side support for the Random Number +- Generator hardware found on Qualcomm MSM SoCs. ++ Generator hardware found on Qualcomm SoCs. + + To compile this driver as a module, choose M here. the + module will be called msm-rng. +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0019-gpio-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch b/target/linux/ipq806x/patches/0019-gpio-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch new file mode 100644 index 0000000000..210f760b22 --- /dev/null +++ b/target/linux/ipq806x/patches/0019-gpio-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch @@ -0,0 +1,32 @@ +From 87d52cc8a390c5b208b6f0bddd90a2d01c906616 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Tue, 11 Feb 2014 14:08:06 -0600 +Subject: [PATCH 019/182] gpio: msm: switch Kconfig to ARCH_QCOM depends + +We've split Qualcomm MSM support into legacy and multiplatform. The gpio +msm-v2 driver is only relevant on the multiplatform supported SoCs so +switch the Kconfig depends to ARCH_QCOM. + +CC: Linus Walleij <linus.walleij@linaro.org> +Acked-by: Alexandre Courbot <gnurou@gmail.com> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/gpio/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig +index 903f24d..2c38d95 100644 +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -192,7 +192,7 @@ config GPIO_MSM_V1 + + config GPIO_MSM_V2 + tristate "Qualcomm MSM GPIO v2" +- depends on GPIOLIB && OF && ARCH_MSM ++ depends on GPIOLIB && OF && ARCH_QCOM + help + Say yes here to support the GPIO interface on ARM v7 based + Qualcomm MSM chips. Most of the pins on the MSM can be +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0020-ARM-qcom-Enable-basic-support-for-Qualcomm-platforms.patch b/target/linux/ipq806x/patches/0020-ARM-qcom-Enable-basic-support-for-Qualcomm-platforms.patch new file mode 100644 index 0000000000..ba25b46e32 --- /dev/null +++ b/target/linux/ipq806x/patches/0020-ARM-qcom-Enable-basic-support-for-Qualcomm-platforms.patch @@ -0,0 +1,54 @@ +From a2f356a6d49f459a2dd681fe4a7c6a55aeb8893f Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Tue, 25 Feb 2014 14:34:05 -0600 +Subject: [PATCH 020/182] ARM: qcom: Enable basic support for Qualcomm + platforms in multi_v7_defconfig + +Enable support for the MSM8x60, MSM8960, and MSM8974 SoCs, clocks and +serial console as part of the standard multi_v7_defconfig. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +[khilman: removed non-qcom changes] +Signed-off-by: Kevin Hilman <khilman@linaro.org> +--- + arch/arm/configs/multi_v7_defconfig | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig +index ee69829..1a61bd8 100644 +--- a/arch/arm/configs/multi_v7_defconfig ++++ b/arch/arm/configs/multi_v7_defconfig +@@ -31,6 +31,10 @@ CONFIG_SOC_OMAP5=y + CONFIG_SOC_AM33XX=y + CONFIG_SOC_DRA7XX=y + CONFIG_SOC_AM43XX=y ++CONFIG_ARCH_QCOM=y ++CONFIG_ARCH_MSM8X60=y ++CONFIG_ARCH_MSM8960=y ++CONFIG_ARCH_MSM8974=y + CONFIG_ARCH_ROCKCHIP=y + CONFIG_ARCH_SOCFPGA=y + CONFIG_PLAT_SPEAR=y +@@ -146,6 +150,8 @@ CONFIG_SERIAL_SIRFSOC_CONSOLE=y + CONFIG_SERIAL_TEGRA=y + CONFIG_SERIAL_IMX=y + CONFIG_SERIAL_IMX_CONSOLE=y ++CONFIG_SERIAL_MSM=y ++CONFIG_SERIAL_MSM_CONSOLE=y + CONFIG_SERIAL_VT8500=y + CONFIG_SERIAL_VT8500_CONSOLE=y + CONFIG_SERIAL_OF_PLATFORM=y +@@ -294,6 +300,10 @@ CONFIG_MFD_NVEC=y + CONFIG_KEYBOARD_NVEC=y + CONFIG_SERIO_NVEC_PS2=y + CONFIG_NVEC_POWER=y ++CONFIG_COMMON_CLK_QCOM=y ++CONFIG_MSM_GCC_8660=y ++CONFIG_MSM_MMCC_8960=y ++CONFIG_MSM_MMCC_8974=y + CONFIG_TEGRA_IOMMU_GART=y + CONFIG_TEGRA_IOMMU_SMMU=y + CONFIG_MEMORY=y +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0021-ARM-dts-qcom-Add-nodes-necessary-for-SMP-boot.patch b/target/linux/ipq806x/patches/0021-ARM-dts-qcom-Add-nodes-necessary-for-SMP-boot.patch new file mode 100644 index 0000000000..b23ee3b154 --- /dev/null +++ b/target/linux/ipq806x/patches/0021-ARM-dts-qcom-Add-nodes-necessary-for-SMP-boot.patch @@ -0,0 +1,214 @@ +From 5a054211d9380cef5a09da7c5e815c827f330a96 Mon Sep 17 00:00:00 2001 +From: Rohit Vaswani <rvaswani@codeaurora.org> +Date: Fri, 1 Nov 2013 10:10:40 -0700 +Subject: [PATCH 021/182] ARM: dts: qcom: Add nodes necessary for SMP boot + +Add the necessary nodes to support SMP on MSM8660, MSM8960, and +MSM8974/APQ8074. While we're here also add in the error +interrupts for the Krait cache error detection. + +Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org> +[sboyd: Split into separate patch, add error interrupts] +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-msm8660.dtsi | 24 ++++++++++++ + arch/arm/boot/dts/qcom-msm8960.dtsi | 52 ++++++++++++++++++++++++++ + arch/arm/boot/dts/qcom-msm8974.dtsi | 69 +++++++++++++++++++++++++++++++++++ + 3 files changed, 145 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-msm8660.dtsi b/arch/arm/boot/dts/qcom-msm8660.dtsi +index 69d6c4e..c52a9e9 100644 +--- a/arch/arm/boot/dts/qcom-msm8660.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8660.dtsi +@@ -9,6 +9,30 @@ + compatible = "qcom,msm8660"; + interrupt-parent = <&intc>; + ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "qcom,scorpion"; ++ enable-method = "qcom,gcc-msm8660"; ++ ++ cpu@0 { ++ device_type = "cpu"; ++ reg = <0>; ++ next-level-cache = <&L2>; ++ }; ++ ++ cpu@1 { ++ device_type = "cpu"; ++ reg = <1>; ++ next-level-cache = <&L2>; ++ }; ++ ++ L2: l2-cache { ++ compatible = "cache"; ++ cache-level = <2>; ++ }; ++ }; ++ + intc: interrupt-controller@2080000 { + compatible = "qcom,msm-8660-qgic"; + interrupt-controller; +diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi +index ff00282..02231a5 100644 +--- a/arch/arm/boot/dts/qcom-msm8960.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8960.dtsi +@@ -9,6 +9,36 @@ + compatible = "qcom,msm8960"; + interrupt-parent = <&intc>; + ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ interrupts = <1 14 0x304>; ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; ++ ++ cpu@0 { ++ device_type = "cpu"; ++ reg = <0>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc0>; ++ qcom,saw = <&saw0>; ++ }; ++ ++ cpu@1 { ++ device_type = "cpu"; ++ reg = <1>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc1>; ++ qcom,saw = <&saw1>; ++ }; ++ ++ L2: l2-cache { ++ compatible = "cache"; ++ cache-level = <2>; ++ interrupts = <0 2 0x4>; ++ }; ++ }; ++ + intc: interrupt-controller@2000000 { + compatible = "qcom,msm-qgic2"; + interrupt-controller; +@@ -53,6 +83,28 @@ + #reset-cells = <1>; + }; + ++ acc0: clock-controller@2088000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x02088000 0x1000>, <0x02008000 0x1000>; ++ }; ++ ++ acc1: clock-controller@2098000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x02098000 0x1000>, <0x02008000 0x1000>; ++ }; ++ ++ saw0: regulator@2089000 { ++ compatible = "qcom,saw2"; ++ reg = <0x02089000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; ++ ++ saw1: regulator@2099000 { ++ compatible = "qcom,saw2"; ++ reg = <0x02099000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; ++ + serial@16440000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x16440000 0x1000>, +diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi +index 9e5dadb..39eebc5 100644 +--- a/arch/arm/boot/dts/qcom-msm8974.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi +@@ -9,6 +9,49 @@ + compatible = "qcom,msm8974"; + interrupt-parent = <&intc>; + ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ interrupts = <1 9 0xf04>; ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v2"; ++ ++ cpu@0 { ++ device_type = "cpu"; ++ reg = <0>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc0>; ++ }; ++ ++ cpu@1 { ++ device_type = "cpu"; ++ reg = <1>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc1>; ++ }; ++ ++ cpu@2 { ++ device_type = "cpu"; ++ reg = <2>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc2>; ++ }; ++ ++ cpu@3 { ++ device_type = "cpu"; ++ reg = <3>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc3>; ++ }; ++ ++ L2: l2-cache { ++ compatible = "cache"; ++ cache-level = <2>; ++ interrupts = <0 2 0x4>; ++ qcom,saw = <&saw_l2>; ++ }; ++ }; ++ + soc: soc { + #address-cells = <1>; + #size-cells = <1>; +@@ -91,6 +134,32 @@ + }; + }; + ++ saw_l2: regulator@f9012000 { ++ compatible = "qcom,saw2"; ++ reg = <0xf9012000 0x1000>; ++ regulator; ++ }; ++ ++ acc0: clock-controller@f9088000 { ++ compatible = "qcom,kpss-acc-v2"; ++ reg = <0xf9088000 0x1000>, <0xf9008000 0x1000>; ++ }; ++ ++ acc1: clock-controller@f9098000 { ++ compatible = "qcom,kpss-acc-v2"; ++ reg = <0xf9098000 0x1000>, <0xf9008000 0x1000>; ++ }; ++ ++ acc2: clock-controller@f90a8000 { ++ compatible = "qcom,kpss-acc-v2"; ++ reg = <0xf90a8000 0x1000>, <0xf9008000 0x1000>; ++ }; ++ ++ acc3: clock-controller@f90b8000 { ++ compatible = "qcom,kpss-acc-v2"; ++ reg = <0xf90b8000 0x1000>, <0xf9008000 0x1000>; ++ }; ++ + restart@fc4ab000 { + compatible = "qcom,pshold"; + reg = <0xfc4ab000 0x4>; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0022-ARM-dts-qcom-Add-RNG-device-tree-node.patch b/target/linux/ipq806x/patches/0022-ARM-dts-qcom-Add-RNG-device-tree-node.patch new file mode 100644 index 0000000000..23edfe7c55 --- /dev/null +++ b/target/linux/ipq806x/patches/0022-ARM-dts-qcom-Add-RNG-device-tree-node.patch @@ -0,0 +1,35 @@ +From b0f93ef8f790e77049b6149416ef0ba05cce7089 Mon Sep 17 00:00:00 2001 +From: Stanimir Varbanov <svarbanov@mm-sol.com> +Date: Fri, 7 Feb 2014 11:23:07 +0200 +Subject: [PATCH 022/182] ARM: dts: qcom: Add RNG device tree node + +Add the necessary DT node to probe the rng driver on +msm8974 platforms. + +Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com> +Acked-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-msm8974.dtsi | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi +index 39eebc5..011eb09 100644 +--- a/arch/arm/boot/dts/qcom-msm8974.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi +@@ -186,5 +186,12 @@ + clocks = <&gcc GCC_BLSP1_UART2_APPS_CLK>, <&gcc GCC_BLSP1_AHB_CLK>; + clock-names = "core", "iface"; + }; ++ ++ rng@f9bff000 { ++ compatible = "qcom,prng"; ++ reg = <0xf9bff000 0x200>; ++ clocks = <&gcc GCC_PRNG_AHB_CLK>; ++ clock-names = "core"; ++ }; + }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0023-ARM-dts-qcom-msm8960-cdp-Add-RNG-device-tree-node.patch b/target/linux/ipq806x/patches/0023-ARM-dts-qcom-msm8960-cdp-Add-RNG-device-tree-node.patch new file mode 100644 index 0000000000..a3e69f3dc6 --- /dev/null +++ b/target/linux/ipq806x/patches/0023-ARM-dts-qcom-msm8960-cdp-Add-RNG-device-tree-node.patch @@ -0,0 +1,34 @@ +From 82bedcd5ad0b3ac8fe78f4be25b2ffa0691d7804 Mon Sep 17 00:00:00 2001 +From: Stanimir Varbanov <svarbanov@mm-sol.com> +Date: Wed, 19 Feb 2014 16:33:06 +0200 +Subject: [PATCH 023/182] ARM: dts: qcom-msm8960-cdp: Add RNG device tree node + +Add the necessary DT node to probe the rng driver on +msm8960-cdp platform. + +Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com> +Tested-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-msm8960.dtsi | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi +index 02231a5..ecfba72 100644 +--- a/arch/arm/boot/dts/qcom-msm8960.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8960.dtsi +@@ -119,4 +119,11 @@ + reg = <0x500000 0x1000>; + qcom,controller-type = "pmic-arbiter"; + }; ++ ++ rng@1a500000 { ++ compatible = "qcom,prng"; ++ reg = <0x1a500000 0x200>; ++ clocks = <&gcc PRNG_CLK>; ++ clock-names = "core"; ++ }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0024-ARM-dts-msm-Add-krait-pmu-to-platforms-with-Krait-CP.patch b/target/linux/ipq806x/patches/0024-ARM-dts-msm-Add-krait-pmu-to-platforms-with-Krait-CP.patch new file mode 100644 index 0000000000..013d45cc41 --- /dev/null +++ b/target/linux/ipq806x/patches/0024-ARM-dts-msm-Add-krait-pmu-to-platforms-with-Krait-CP.patch @@ -0,0 +1,53 @@ +From 02c987fb8a3607ab6e0ead0e5aaa7da753ce9537 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 21 Feb 2014 11:09:50 +0000 +Subject: [PATCH 024/182] ARM: dts: msm: Add krait-pmu to platforms with Krait + CPUs + +Allows us to probe the performance counters on Krait CPUs. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Will Deacon <will.deacon@arm.com> +[olof: Moved 8960 contents to the dtsi instead] +Signed-off-by: Olof Johansson <olof@lixom.net> +--- + arch/arm/boot/dts/qcom-msm8960.dtsi | 6 ++++++ + arch/arm/boot/dts/qcom-msm8974.dtsi | 5 +++++ + 2 files changed, 11 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi +index ecfba72..997b7b9 100644 +--- a/arch/arm/boot/dts/qcom-msm8960.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8960.dtsi +@@ -39,6 +39,12 @@ + }; + }; + ++ cpu-pmu { ++ compatible = "qcom,krait-pmu"; ++ interrupts = <1 10 0x304>; ++ qcom,no-pc-write; ++ }; ++ + intc: interrupt-controller@2000000 { + compatible = "qcom,msm-qgic2"; + interrupt-controller; +diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi +index 011eb09..f687239 100644 +--- a/arch/arm/boot/dts/qcom-msm8974.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi +@@ -52,6 +52,11 @@ + }; + }; + ++ cpu-pmu { ++ compatible = "qcom,krait-pmu"; ++ interrupts = <1 7 0xf04>; ++ }; ++ + soc: soc { + #address-cells = <1>; + #size-cells = <1>; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0025-pinctrl-msm-drop-wake_irqs-bitmap.patch b/target/linux/ipq806x/patches/0025-pinctrl-msm-drop-wake_irqs-bitmap.patch new file mode 100644 index 0000000000..fba5e4f9e3 --- /dev/null +++ b/target/linux/ipq806x/patches/0025-pinctrl-msm-drop-wake_irqs-bitmap.patch @@ -0,0 +1,71 @@ +From ef8eb0991f291df12c12477648235f955cc388b0 Mon Sep 17 00:00:00 2001 +From: Josh Cartwright <joshc@codeaurora.org> +Date: Wed, 5 Mar 2014 13:33:08 -0600 +Subject: [PATCH 025/182] pinctrl: msm: drop wake_irqs bitmap + +Currently, the wake_irqs bitmap is used to track whether there are any +gpio's which are configured as wake irqs, and uses this to determine +whether or not to call enable_irq_wake()/disable_irq_wake() on the +summary interrupt. + +However, the genirq core already handles this case, by maintaining a +'wake_count' per irq_desc, and only calling into the controlling +irq_chip when wake_count transitions 0 <-> 1. + +Drop this bitmap, and unconditionally call irq_set_irq_wake() on the +summary interrupt. + +Signed-off-by: Josh Cartwright <joshc@codeaurora.org> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 14 +------------- + 1 file changed, 1 insertion(+), 13 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index ef2bf31..0e43fdd 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -50,7 +50,6 @@ + * @enabled_irqs: Bitmap of currently enabled irqs. + * @dual_edge_irqs: Bitmap of irqs that need sw emulated dual edge + * detection. +- * @wake_irqs: Bitmap of irqs with requested as wakeup source. + * @soc; Reference to soc_data of platform specific data. + * @regs: Base address for the TLMM register map. + */ +@@ -65,7 +64,6 @@ struct msm_pinctrl { + + DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO); + DECLARE_BITMAP(enabled_irqs, MAX_NR_GPIO); +- DECLARE_BITMAP(wake_irqs, MAX_NR_GPIO); + + const struct msm_pinctrl_soc_data *soc; + void __iomem *regs; +@@ -783,22 +781,12 @@ static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on) + { + struct msm_pinctrl *pctrl; + unsigned long flags; +- unsigned ngpio; + + pctrl = irq_data_get_irq_chip_data(d); +- ngpio = pctrl->chip.ngpio; + + spin_lock_irqsave(&pctrl->lock, flags); + +- if (on) { +- if (bitmap_empty(pctrl->wake_irqs, ngpio)) +- enable_irq_wake(pctrl->irq); +- set_bit(d->hwirq, pctrl->wake_irqs); +- } else { +- clear_bit(d->hwirq, pctrl->wake_irqs); +- if (bitmap_empty(pctrl->wake_irqs, ngpio)) +- disable_irq_wake(pctrl->irq); +- } ++ irq_set_irq_wake(pctrl->irq, on); + + spin_unlock_irqrestore(&pctrl->lock, flags); + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0026-pinctrl-msm-Silence-recursive-lockdep-warning.patch b/target/linux/ipq806x/patches/0026-pinctrl-msm-Silence-recursive-lockdep-warning.patch new file mode 100644 index 0000000000..fd2e10fb0f --- /dev/null +++ b/target/linux/ipq806x/patches/0026-pinctrl-msm-Silence-recursive-lockdep-warning.patch @@ -0,0 +1,73 @@ +From 9588c91936434166007c3a15ad7f4e2f3729c5e7 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 6 Mar 2014 22:44:40 -0800 +Subject: [PATCH 026/182] pinctrl: msm: Silence recursive lockdep warning + +If a driver calls enable_irq_wake() on a gpio turned interrupt +from the msm pinctrl driver we'll get a lockdep warning like so: + +============================================= +[ INFO: possible recursive locking detected ] +3.14.0-rc3 #2 Not tainted +--------------------------------------------- +modprobe/52 is trying to acquire lock: + (&irq_desc_lock_class){-.....}, at: [<c026aea0>] __irq_get_desc_lock+0x48/0x88 + +but task is already holding lock: + (&irq_desc_lock_class){-.....}, at: [<c026aea0>] __irq_get_desc_lock+0x48/0x88 + +other info that might help us debug this: + Possible unsafe locking scenario: + + CPU0 + ---- + lock(&irq_desc_lock_class); + lock(&irq_desc_lock_class); + + *** DEADLOCK *** + + May be due to missing lock nesting notation + +4 locks held by modprobe/52: + #0: (&__lockdep_no_validate__){......}, at: [<c04f2864>] __driver_attach+0x48/0x98 + #1: (&__lockdep_no_validate__){......}, at: [<c04f2874>] __driver_attach+0x58/0x98 + #2: (&irq_desc_lock_class){-.....}, at: [<c026aea0>] __irq_get_desc_lock+0x48/0x88 + #3: (&(&pctrl->lock)->rlock){......}, at: [<c04bb4b8>] msm_gpio_irq_set_wake+0x20/0xa8 + +Silence it by putting the gpios into their own lock class. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index 0e43fdd..e61b30a 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -857,6 +857,12 @@ static void msm_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) + chained_irq_exit(chip, desc); + } + ++/* ++ * This lock class tells lockdep that GPIO irqs are in a different ++ * category than their parents, so it won't report false recursion. ++ */ ++static struct lock_class_key gpio_lock_class; ++ + static int msm_gpio_init(struct msm_pinctrl *pctrl) + { + struct gpio_chip *chip; +@@ -895,6 +901,7 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) + + for (i = 0; i < chip->ngpio; i++) { + irq = irq_create_mapping(pctrl->domain, i); ++ irq_set_lockdep_class(irq, &gpio_lock_class); + irq_set_chip_and_handler(irq, &msm_gpio_irq_chip, handle_edge_irq); + irq_set_chip_data(irq, pctrl); + } +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0027-pinctrl-msm-Check-for-ngpios-MAX_NR_GPIO.patch b/target/linux/ipq806x/patches/0027-pinctrl-msm-Check-for-ngpios-MAX_NR_GPIO.patch new file mode 100644 index 0000000000..c2b7487540 --- /dev/null +++ b/target/linux/ipq806x/patches/0027-pinctrl-msm-Check-for-ngpios-MAX_NR_GPIO.patch @@ -0,0 +1,39 @@ +From 8341db7b05b688e8e5a93acd0e80b40be409d037 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 6 Mar 2014 22:44:41 -0800 +Subject: [PATCH 027/182] pinctrl: msm: Check for ngpios > MAX_NR_GPIO + +Fail the probe and print a warning if SoC specific drivers have +more GPIOs than there can be accounted for in the static bitmaps. +This should avoid silent corruption/failures in the future. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index e61b30a..90ac995 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -870,10 +870,14 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) + int ret; + int i; + int r; ++ unsigned ngpio = pctrl->soc->ngpios; ++ ++ if (WARN_ON(ngpio > MAX_NR_GPIO)) ++ return -EINVAL; + + chip = &pctrl->chip; + chip->base = 0; +- chip->ngpio = pctrl->soc->ngpios; ++ chip->ngpio = ngpio; + chip->label = dev_name(pctrl->dev); + chip->dev = pctrl->dev; + chip->owner = THIS_MODULE; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0028-pinctrl-msm-Drop-unused-includes.patch b/target/linux/ipq806x/patches/0028-pinctrl-msm-Drop-unused-includes.patch new file mode 100644 index 0000000000..8f864f2680 --- /dev/null +++ b/target/linux/ipq806x/patches/0028-pinctrl-msm-Drop-unused-includes.patch @@ -0,0 +1,60 @@ +From d2caa906f865d6919c511a956ae63ab62b240eba Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 6 Mar 2014 22:44:42 -0800 +Subject: [PATCH 028/182] pinctrl: msm: Drop unused includes + +These includes are unused or can be handled via forward +declarations. Remove them. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 1 - + drivers/pinctrl/pinctrl-msm.h | 5 +---- + drivers/pinctrl/pinctrl-msm8x74.c | 1 - + 3 files changed, 1 insertion(+), 6 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index 90ac995..4474e00 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -28,7 +28,6 @@ + #include <linux/interrupt.h> + #include <linux/irq.h> + #include <linux/irqchip/chained_irq.h> +-#include <linux/of_irq.h> + #include <linux/spinlock.h> + + #include "core.h" +diff --git a/drivers/pinctrl/pinctrl-msm.h b/drivers/pinctrl/pinctrl-msm.h +index 206e782..8fbe9fb 100644 +--- a/drivers/pinctrl/pinctrl-msm.h ++++ b/drivers/pinctrl/pinctrl-msm.h +@@ -13,10 +13,7 @@ + #ifndef __PINCTRL_MSM_H__ + #define __PINCTRL_MSM_H__ + +-#include <linux/pinctrl/pinctrl.h> +-#include <linux/pinctrl/pinmux.h> +-#include <linux/pinctrl/pinconf.h> +-#include <linux/pinctrl/machine.h> ++struct pinctrl_pin_desc; + + /** + * struct msm_function - a pinmux function +diff --git a/drivers/pinctrl/pinctrl-msm8x74.c b/drivers/pinctrl/pinctrl-msm8x74.c +index f944bf2..bb5ded69f 100644 +--- a/drivers/pinctrl/pinctrl-msm8x74.c ++++ b/drivers/pinctrl/pinctrl-msm8x74.c +@@ -15,7 +15,6 @@ + #include <linux/of.h> + #include <linux/platform_device.h> + #include <linux/pinctrl/pinctrl.h> +-#include <linux/pinctrl/pinmux.h> + + #include "pinctrl-msm.h" + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0029-pinctrl-msm-Drop-OF_IRQ-dependency.patch b/target/linux/ipq806x/patches/0029-pinctrl-msm-Drop-OF_IRQ-dependency.patch new file mode 100644 index 0000000000..6a33a9d97c --- /dev/null +++ b/target/linux/ipq806x/patches/0029-pinctrl-msm-Drop-OF_IRQ-dependency.patch @@ -0,0 +1,31 @@ +From 2cfbd01bd994bd16dbee28a99553796f12848c4c Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 6 Mar 2014 22:44:43 -0800 +Subject: [PATCH 029/182] pinctrl: msm: Drop OF_IRQ dependency + +This driver doesn't rely on any functionality living in +drivers/of/irq.c to compile. Drop this dependency. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig +index 1e4e693..06cee01 100644 +--- a/drivers/pinctrl/Kconfig ++++ b/drivers/pinctrl/Kconfig +@@ -224,7 +224,7 @@ config PINCTRL_MSM + + config PINCTRL_MSM8X74 + tristate "Qualcomm 8x74 pin controller driver" +- depends on GPIOLIB && OF && OF_IRQ ++ depends on GPIOLIB && OF + select PINCTRL_MSM + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0030-pinctrl-msm-Replace-lookup-tables-with-math.patch b/target/linux/ipq806x/patches/0030-pinctrl-msm-Replace-lookup-tables-with-math.patch new file mode 100644 index 0000000000..975e11a052 --- /dev/null +++ b/target/linux/ipq806x/patches/0030-pinctrl-msm-Replace-lookup-tables-with-math.patch @@ -0,0 +1,66 @@ +From e34d9fdac8182f6ce8933501fea6e84664060bf0 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 6 Mar 2014 22:44:44 -0800 +Subject: [PATCH 030/182] pinctrl: msm: Replace lookup tables with math + +We don't need to waste space with these lookup tables, just do +the math directly. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index 4474e00..87f6c3c 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -258,8 +258,10 @@ static int msm_config_set(struct pinctrl_dev *pctldev, unsigned int pin, + #define MSM_PULL_DOWN 1 + #define MSM_PULL_UP 3 + +-static const unsigned msm_regval_to_drive[] = { 2, 4, 6, 8, 10, 12, 14, 16 }; +-static const unsigned msm_drive_to_regval[] = { -1, -1, 0, -1, 1, -1, 2, -1, 3, -1, 4, -1, 5, -1, 6, -1, 7 }; ++static unsigned msm_regval_to_drive(u32 val) ++{ ++ return (val + 1) * 2; ++} + + static int msm_config_group_get(struct pinctrl_dev *pctldev, + unsigned int group, +@@ -296,7 +298,7 @@ static int msm_config_group_get(struct pinctrl_dev *pctldev, + arg = arg == MSM_PULL_UP; + break; + case PIN_CONFIG_DRIVE_STRENGTH: +- arg = msm_regval_to_drive[arg]; ++ arg = msm_regval_to_drive(arg); + break; + default: + dev_err(pctrl->dev, "Unsupported config parameter: %x\n", +@@ -349,10 +351,10 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev, + break; + case PIN_CONFIG_DRIVE_STRENGTH: + /* Check for invalid values */ +- if (arg >= ARRAY_SIZE(msm_drive_to_regval)) ++ if (arg > 16 || arg < 2 || (arg % 2) != 0) + arg = -1; + else +- arg = msm_drive_to_regval[arg]; ++ arg = (arg / 2) - 1; + break; + default: + dev_err(pctrl->dev, "Unsupported config parameter: %x\n", +@@ -531,7 +533,7 @@ static void msm_gpio_dbg_show_one(struct seq_file *s, + pull = (ctl_reg >> g->pull_bit) & 3; + + seq_printf(s, " %-8s: %-3s %d", g->name, is_out ? "out" : "in", func); +- seq_printf(s, " %dmA", msm_regval_to_drive[drive]); ++ seq_printf(s, " %dmA", msm_regval_to_drive(drive)); + seq_printf(s, " %s", pulls[pull]); + } + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0031-pinctrl-msm-Remove-impossible-WARN_ON-s.patch b/target/linux/ipq806x/patches/0031-pinctrl-msm-Remove-impossible-WARN_ON-s.patch new file mode 100644 index 0000000000..61c8ac1b2a --- /dev/null +++ b/target/linux/ipq806x/patches/0031-pinctrl-msm-Remove-impossible-WARN_ON-s.patch @@ -0,0 +1,95 @@ +From 286113578287b9c7619b4104864cffb91820f49d Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 6 Mar 2014 22:44:45 -0800 +Subject: [PATCH 031/182] pinctrl: msm: Remove impossible WARN_ON()s + +All these functions are limited in what they can pass as the gpio +or irq number to whatever is setup during probe. Remove the +checks. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 16 ---------------- + 1 file changed, 16 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index 87f6c3c..ab46e3a 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -401,8 +401,6 @@ static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset) + u32 val; + + g = &pctrl->soc->groups[offset]; +- if (WARN_ON(g->io_reg < 0)) +- return -EINVAL; + + spin_lock_irqsave(&pctrl->lock, flags); + +@@ -423,8 +421,6 @@ static int msm_gpio_direction_output(struct gpio_chip *chip, unsigned offset, in + u32 val; + + g = &pctrl->soc->groups[offset]; +- if (WARN_ON(g->io_reg < 0)) +- return -EINVAL; + + spin_lock_irqsave(&pctrl->lock, flags); + +@@ -451,8 +447,6 @@ static int msm_gpio_get(struct gpio_chip *chip, unsigned offset) + u32 val; + + g = &pctrl->soc->groups[offset]; +- if (WARN_ON(g->io_reg < 0)) +- return -EINVAL; + + val = readl(pctrl->regs + g->io_reg); + return !!(val & BIT(g->in_bit)); +@@ -466,8 +460,6 @@ static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value) + u32 val; + + g = &pctrl->soc->groups[offset]; +- if (WARN_ON(g->io_reg < 0)) +- return; + + spin_lock_irqsave(&pctrl->lock, flags); + +@@ -616,8 +608,6 @@ static void msm_gpio_irq_mask(struct irq_data *d) + + pctrl = irq_data_get_irq_chip_data(d); + g = &pctrl->soc->groups[d->hwirq]; +- if (WARN_ON(g->intr_cfg_reg < 0)) +- return; + + spin_lock_irqsave(&pctrl->lock, flags); + +@@ -639,8 +629,6 @@ static void msm_gpio_irq_unmask(struct irq_data *d) + + pctrl = irq_data_get_irq_chip_data(d); + g = &pctrl->soc->groups[d->hwirq]; +- if (WARN_ON(g->intr_status_reg < 0)) +- return; + + spin_lock_irqsave(&pctrl->lock, flags); + +@@ -666,8 +654,6 @@ static void msm_gpio_irq_ack(struct irq_data *d) + + pctrl = irq_data_get_irq_chip_data(d); + g = &pctrl->soc->groups[d->hwirq]; +- if (WARN_ON(g->intr_status_reg < 0)) +- return; + + spin_lock_irqsave(&pctrl->lock, flags); + +@@ -692,8 +678,6 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type) + + pctrl = irq_data_get_irq_chip_data(d); + g = &pctrl->soc->groups[d->hwirq]; +- if (WARN_ON(g->intr_cfg_reg < 0)) +- return -EINVAL; + + spin_lock_irqsave(&pctrl->lock, flags); + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0032-pinctrl-msm-Simplify-msm_config_reg-and-callers.patch b/target/linux/ipq806x/patches/0032-pinctrl-msm-Simplify-msm_config_reg-and-callers.patch new file mode 100644 index 0000000000..d656978236 --- /dev/null +++ b/target/linux/ipq806x/patches/0032-pinctrl-msm-Simplify-msm_config_reg-and-callers.patch @@ -0,0 +1,115 @@ +From 2d9ffb1a3f87396c3b792124870ef63fc27c568f Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 6 Mar 2014 22:44:46 -0800 +Subject: [PATCH 032/182] pinctrl: msm: Simplify msm_config_reg() and callers + +We don't need to check for a negative reg here because reg is +always the same and is always non-negative. Also, collapse the +switch statement down for the duplicate cases. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 29 +++++------------------------ + 1 file changed, 5 insertions(+), 24 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index ab46e3a..91de8bc 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -200,28 +200,17 @@ static const struct pinmux_ops msm_pinmux_ops = { + static int msm_config_reg(struct msm_pinctrl *pctrl, + const struct msm_pingroup *g, + unsigned param, +- s16 *reg, + unsigned *mask, + unsigned *bit) + { + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: +- *reg = g->ctl_reg; +- *bit = g->pull_bit; +- *mask = 3; +- break; + case PIN_CONFIG_BIAS_PULL_DOWN: +- *reg = g->ctl_reg; +- *bit = g->pull_bit; +- *mask = 3; +- break; + case PIN_CONFIG_BIAS_PULL_UP: +- *reg = g->ctl_reg; + *bit = g->pull_bit; + *mask = 3; + break; + case PIN_CONFIG_DRIVE_STRENGTH: +- *reg = g->ctl_reg; + *bit = g->drv_bit; + *mask = 7; + break; +@@ -230,12 +219,6 @@ static int msm_config_reg(struct msm_pinctrl *pctrl, + return -ENOTSUPP; + } + +- if (*reg < 0) { +- dev_err(pctrl->dev, "Config param %04x not supported on group %s\n", +- param, g->name); +- return -ENOTSUPP; +- } +- + return 0; + } + +@@ -273,17 +256,16 @@ static int msm_config_group_get(struct pinctrl_dev *pctldev, + unsigned mask; + unsigned arg; + unsigned bit; +- s16 reg; + int ret; + u32 val; + + g = &pctrl->soc->groups[group]; + +- ret = msm_config_reg(pctrl, g, param, ®, &mask, &bit); ++ ret = msm_config_reg(pctrl, g, param, &mask, &bit); + if (ret < 0) + return ret; + +- val = readl(pctrl->regs + reg); ++ val = readl(pctrl->regs + g->ctl_reg); + arg = (val >> bit) & mask; + + /* Convert register value to pinconf value */ +@@ -323,7 +305,6 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev, + unsigned mask; + unsigned arg; + unsigned bit; +- s16 reg; + int ret; + u32 val; + int i; +@@ -334,7 +315,7 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev, + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + +- ret = msm_config_reg(pctrl, g, param, ®, &mask, &bit); ++ ret = msm_config_reg(pctrl, g, param, &mask, &bit); + if (ret < 0) + return ret; + +@@ -369,10 +350,10 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev, + } + + spin_lock_irqsave(&pctrl->lock, flags); +- val = readl(pctrl->regs + reg); ++ val = readl(pctrl->regs + g->ctl_reg); + val &= ~(mask << bit); + val |= arg << bit; +- writel(val, pctrl->regs + reg); ++ writel(val, pctrl->regs + g->ctl_reg); + spin_unlock_irqrestore(&pctrl->lock, flags); + } + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0033-pinctrl-msm-Support-output-high-low-configuration.patch b/target/linux/ipq806x/patches/0033-pinctrl-msm-Support-output-high-low-configuration.patch new file mode 100644 index 0000000000..18c72cc4be --- /dev/null +++ b/target/linux/ipq806x/patches/0033-pinctrl-msm-Support-output-high-low-configuration.patch @@ -0,0 +1,69 @@ +From 469f83e0ed374250be5fd6202ac535276a752fa8 Mon Sep 17 00:00:00 2001 +From: Bjorn Andersson <bjorn@kryo.se> +Date: Tue, 4 Feb 2014 19:55:31 -0800 +Subject: [PATCH 033/182] pinctrl-msm: Support output-{high,low} configuration + +Add support for configuring pins as output with value as from the +pinconf-generic interface. + +Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index 91de8bc..19d2feb 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -214,6 +214,11 @@ static int msm_config_reg(struct msm_pinctrl *pctrl, + *bit = g->drv_bit; + *mask = 7; + break; ++ case PIN_CONFIG_OUTPUT: ++ *reg = g->ctl_reg; ++ *bit = g->oe_bit; ++ *mask = 1; ++ break; + default: + dev_err(pctrl->dev, "Invalid config param %04x\n", param); + return -ENOTSUPP; +@@ -282,6 +287,14 @@ static int msm_config_group_get(struct pinctrl_dev *pctldev, + case PIN_CONFIG_DRIVE_STRENGTH: + arg = msm_regval_to_drive(arg); + break; ++ case PIN_CONFIG_OUTPUT: ++ /* Pin is not output */ ++ if (!arg) ++ return -EINVAL; ++ ++ val = readl(pctrl->regs + g->io_reg); ++ arg = !!(val & BIT(g->in_bit)); ++ break; + default: + dev_err(pctrl->dev, "Unsupported config parameter: %x\n", + param); +@@ -337,6 +350,20 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev, + else + arg = (arg / 2) - 1; + break; ++ case PIN_CONFIG_OUTPUT: ++ /* set output value */ ++ spin_lock_irqsave(&pctrl->lock, flags); ++ val = readl(pctrl->regs + g->io_reg); ++ if (arg) ++ val |= BIT(g->out_bit); ++ else ++ val &= ~BIT(g->out_bit); ++ writel(val, pctrl->regs + g->io_reg); ++ spin_unlock_irqrestore(&pctrl->lock, flags); ++ ++ /* enable output */ ++ arg = 1; ++ break; + default: + dev_err(pctrl->dev, "Unsupported config parameter: %x\n", + param); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0034-pinctrl-msm-Add-SPI8-pin-definitions.patch b/target/linux/ipq806x/patches/0034-pinctrl-msm-Add-SPI8-pin-definitions.patch new file mode 100644 index 0000000000..b8df704ec0 --- /dev/null +++ b/target/linux/ipq806x/patches/0034-pinctrl-msm-Add-SPI8-pin-definitions.patch @@ -0,0 +1,63 @@ +From 77e4b572fcc4015e235f22fd93b8df35e452baf0 Mon Sep 17 00:00:00 2001 +From: "Ivan T. Ivanov" <iivanov@mm-sol.com> +Date: Thu, 6 Feb 2014 17:28:48 +0200 +Subject: [PATCH 034/182] pinctrl-msm: Add SPI8 pin definitions + +Add pin, group and function definitions for SPI#8 +controller. + +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm8x74.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-msm8x74.c b/drivers/pinctrl/pinctrl-msm8x74.c +index bb5ded69f..dde5529 100644 +--- a/drivers/pinctrl/pinctrl-msm8x74.c ++++ b/drivers/pinctrl/pinctrl-msm8x74.c +@@ -405,6 +405,7 @@ enum msm8x74_functions { + MSM_MUX_blsp_i2c6, + MSM_MUX_blsp_i2c11, + MSM_MUX_blsp_spi1, ++ MSM_MUX_blsp_spi8, + MSM_MUX_blsp_uart2, + MSM_MUX_blsp_uart8, + MSM_MUX_slimbus, +@@ -415,6 +416,9 @@ static const char * const blsp_i2c2_groups[] = { "gpio6", "gpio7" }; + static const char * const blsp_i2c6_groups[] = { "gpio29", "gpio30" }; + static const char * const blsp_i2c11_groups[] = { "gpio83", "gpio84" }; + static const char * const blsp_spi1_groups[] = { "gpio0", "gpio1", "gpio2", "gpio3" }; ++static const char * const blsp_spi8_groups[] = { ++ "gpio45", "gpio46", "gpio47", "gpio48" ++}; + static const char * const blsp_uart2_groups[] = { "gpio4", "gpio5" }; + static const char * const blsp_uart8_groups[] = { "gpio45", "gpio46" }; + static const char * const slimbus_groups[] = { "gpio70", "gpio71" }; +@@ -424,6 +428,7 @@ static const struct msm_function msm8x74_functions[] = { + FUNCTION(blsp_i2c6), + FUNCTION(blsp_i2c11), + FUNCTION(blsp_spi1), ++ FUNCTION(blsp_spi8), + FUNCTION(blsp_uart2), + FUNCTION(blsp_uart8), + FUNCTION(slimbus), +@@ -475,10 +480,10 @@ static const struct msm_pingroup msm8x74_groups[] = { + PINGROUP(42, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(43, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(44, NA, NA, NA, NA, NA, NA, NA), +- PINGROUP(45, NA, blsp_uart8, NA, NA, NA, NA, NA), +- PINGROUP(46, NA, blsp_uart8, NA, NA, NA, NA, NA), +- PINGROUP(47, NA, NA, NA, NA, NA, NA, NA), +- PINGROUP(48, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(45, blsp_spi8, blsp_uart8, NA, NA, NA, NA, NA), ++ PINGROUP(46, blsp_spi8, blsp_uart8, NA, NA, NA, NA, NA), ++ PINGROUP(47, blsp_spi8, NA, NA, NA, NA, NA, NA), ++ PINGROUP(48, blsp_spi8, NA, NA, NA, NA, NA, NA), + PINGROUP(49, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(50, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(51, NA, NA, NA, NA, NA, NA, NA), +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0035-pinctrl-msm-fix-up-out-of-order-merge-conflict.patch b/target/linux/ipq806x/patches/0035-pinctrl-msm-fix-up-out-of-order-merge-conflict.patch new file mode 100644 index 0000000000..c141a0b6c0 --- /dev/null +++ b/target/linux/ipq806x/patches/0035-pinctrl-msm-fix-up-out-of-order-merge-conflict.patch @@ -0,0 +1,38 @@ +From c73a138dd24049d06fe7b22518655ed9e7413cd2 Mon Sep 17 00:00:00 2001 +From: Linus Walleij <linus.walleij@linaro.org> +Date: Fri, 14 Mar 2014 07:54:20 +0100 +Subject: [PATCH 035/182] pinctrl: msm: fix up out-of-order merge conflict + +Commit 051a58b4622f0e1b732acb750097c64bc00ddb93 +"pinctrl: msm: Simplify msm_config_reg() and callers" +removed the local "reg" variable in the msm_config_reg() +function, but the earlier +commit ed118a5fd951bd2def8249ee251842c4f81fe4bd +"pinctrl-msm: Support output-{high,low} configuration" +introduced a new switchclause using it. + +Fix this up by removing the offending register assignment. + +Reported-by: Kbuild test robot <fengguang.wu@intel.com> +Cc: Stephen Boyd <sboyd@codeaurora.org> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index 19d2feb..343f421 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -215,7 +215,6 @@ static int msm_config_reg(struct msm_pinctrl *pctrl, + *mask = 7; + break; + case PIN_CONFIG_OUTPUT: +- *reg = g->ctl_reg; + *bit = g->oe_bit; + *mask = 1; + break; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0036-pinctrl-msm-Correct-interrupt-code-for-TLMM-v2.patch b/target/linux/ipq806x/patches/0036-pinctrl-msm-Correct-interrupt-code-for-TLMM-v2.patch new file mode 100644 index 0000000000..b44fb6a0b3 --- /dev/null +++ b/target/linux/ipq806x/patches/0036-pinctrl-msm-Correct-interrupt-code-for-TLMM-v2.patch @@ -0,0 +1,55 @@ +From 32787a9bba5a1ebeea891fd7aab954e6d344892a Mon Sep 17 00:00:00 2001 +From: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Date: Mon, 31 Mar 2014 14:49:54 -0700 +Subject: [PATCH 036/182] pinctrl: msm: Correct interrupt code for TLMM v2 + +Acking interrupts are done differently between on v2 and v3, so add an extra +attribute to the pingroup struct to let the platform definitions control this. +Also make sure to start dual edge detection by detecting the rising edge. + +Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 6 +++++- + drivers/pinctrl/pinctrl-msm.h | 1 + + 2 files changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index 343f421..706809e 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -665,7 +665,10 @@ static void msm_gpio_irq_ack(struct irq_data *d) + spin_lock_irqsave(&pctrl->lock, flags); + + val = readl(pctrl->regs + g->intr_status_reg); +- val &= ~BIT(g->intr_status_bit); ++ if (g->intr_ack_high) ++ val |= BIT(g->intr_status_bit); ++ else ++ val &= ~BIT(g->intr_status_bit); + writel(val, pctrl->regs + g->intr_status_reg); + + if (test_bit(d->hwirq, pctrl->dual_edge_irqs)) +@@ -744,6 +747,7 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type) + break; + case IRQ_TYPE_EDGE_BOTH: + val |= BIT(g->intr_detection_bit); ++ val |= BIT(g->intr_polarity_bit); + break; + case IRQ_TYPE_LEVEL_LOW: + break; +diff --git a/drivers/pinctrl/pinctrl-msm.h b/drivers/pinctrl/pinctrl-msm.h +index 8fbe9fb..6e26f1b 100644 +--- a/drivers/pinctrl/pinctrl-msm.h ++++ b/drivers/pinctrl/pinctrl-msm.h +@@ -84,6 +84,7 @@ struct msm_pingroup { + + unsigned intr_enable_bit:5; + unsigned intr_status_bit:5; ++ unsigned intr_ack_high:1; + + unsigned intr_target_bit:5; + unsigned intr_raw_status_bit:5; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0037-pinctrl-msm-Make-number-of-functions-variable.patch b/target/linux/ipq806x/patches/0037-pinctrl-msm-Make-number-of-functions-variable.patch new file mode 100644 index 0000000000..346dcd0ac6 --- /dev/null +++ b/target/linux/ipq806x/patches/0037-pinctrl-msm-Make-number-of-functions-variable.patch @@ -0,0 +1,74 @@ +From 1c11b14dd6d740e997919f0bf789bf921548dc0f Mon Sep 17 00:00:00 2001 +From: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Date: Mon, 31 Mar 2014 14:49:55 -0700 +Subject: [PATCH 037/182] pinctrl: msm: Make number of functions variable + +The various pins may have different number of functions defined, so make this +number definable per pin instead of just increasing it to the largest one for +all of the platforms. + +Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 4 ++-- + drivers/pinctrl/pinctrl-msm.h | 3 ++- + drivers/pinctrl/pinctrl-msm8x74.c | 3 ++- + 3 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index 706809e..7d67d34 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -145,12 +145,12 @@ static int msm_pinmux_enable(struct pinctrl_dev *pctldev, + if (WARN_ON(g->mux_bit < 0)) + return -EINVAL; + +- for (i = 0; i < ARRAY_SIZE(g->funcs); i++) { ++ for (i = 0; i < g->nfuncs; i++) { + if (g->funcs[i] == function) + break; + } + +- if (WARN_ON(i == ARRAY_SIZE(g->funcs))) ++ if (WARN_ON(i == g->nfuncs)) + return -EINVAL; + + spin_lock_irqsave(&pctrl->lock, flags); +diff --git a/drivers/pinctrl/pinctrl-msm.h b/drivers/pinctrl/pinctrl-msm.h +index 6e26f1b..7b2a227 100644 +--- a/drivers/pinctrl/pinctrl-msm.h ++++ b/drivers/pinctrl/pinctrl-msm.h +@@ -65,7 +65,8 @@ struct msm_pingroup { + const unsigned *pins; + unsigned npins; + +- unsigned funcs[8]; ++ unsigned *funcs; ++ unsigned nfuncs; + + s16 ctl_reg; + s16 io_reg; +diff --git a/drivers/pinctrl/pinctrl-msm8x74.c b/drivers/pinctrl/pinctrl-msm8x74.c +index dde5529..57766d5 100644 +--- a/drivers/pinctrl/pinctrl-msm8x74.c ++++ b/drivers/pinctrl/pinctrl-msm8x74.c +@@ -341,7 +341,7 @@ static const unsigned int sdc2_data_pins[] = { 151 }; + .name = "gpio" #id, \ + .pins = gpio##id##_pins, \ + .npins = ARRAY_SIZE(gpio##id##_pins), \ +- .funcs = { \ ++ .funcs = (int[]){ \ + MSM_MUX_NA, /* gpio mode */ \ + MSM_MUX_##f1, \ + MSM_MUX_##f2, \ +@@ -351,6 +351,7 @@ static const unsigned int sdc2_data_pins[] = { 151 }; + MSM_MUX_##f6, \ + MSM_MUX_##f7 \ + }, \ ++ .nfuncs = 8, \ + .ctl_reg = 0x1000 + 0x10 * id, \ + .io_reg = 0x1004 + 0x10 * id, \ + .intr_cfg_reg = 0x1008 + 0x10 * id, \ +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0038-pinctrl-msm-Add-definitions-for-the-APQ8064-platform.patch b/target/linux/ipq806x/patches/0038-pinctrl-msm-Add-definitions-for-the-APQ8064-platform.patch new file mode 100644 index 0000000000..20e5a1dc83 --- /dev/null +++ b/target/linux/ipq806x/patches/0038-pinctrl-msm-Add-definitions-for-the-APQ8064-platform.patch @@ -0,0 +1,624 @@ +From 247288012122ccfe7d5d9af00a45814c6fdd94c5 Mon Sep 17 00:00:00 2001 +From: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Date: Mon, 31 Mar 2014 14:49:57 -0700 +Subject: [PATCH 038/182] pinctrl: msm: Add definitions for the APQ8064 + platform + +This adds pinctrl definitions for the GPIO pins of the TLMM v2 block in the +Qualcomm APQ8064 platform. + +Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/Kconfig | 8 + + drivers/pinctrl/Makefile | 1 + + drivers/pinctrl/pinctrl-apq8064.c | 566 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 575 insertions(+) + create mode 100644 drivers/pinctrl/pinctrl-apq8064.c + +diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig +index 06cee01..91993a6 100644 +--- a/drivers/pinctrl/Kconfig ++++ b/drivers/pinctrl/Kconfig +@@ -222,6 +222,14 @@ config PINCTRL_MSM + select PINCONF + select GENERIC_PINCONF + ++config PINCTRL_APQ8064 ++ tristate "Qualcomm APQ8064 pin controller driver" ++ depends on GPIOLIB && OF ++ select PINCTRL_MSM ++ help ++ This is the pinctrl, pinmux, pinconf and gpiolib driver for the ++ Qualcomm TLMM block found in the Qualcomm APQ8064 platform. ++ + config PINCTRL_MSM8X74 + tristate "Qualcomm 8x74 pin controller driver" + depends on GPIOLIB && OF +diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile +index 4b83588..9e1fb67 100644 +--- a/drivers/pinctrl/Makefile ++++ b/drivers/pinctrl/Makefile +@@ -38,6 +38,7 @@ obj-$(CONFIG_PINCTRL_IMX23) += pinctrl-imx23.o + obj-$(CONFIG_PINCTRL_IMX25) += pinctrl-imx25.o + obj-$(CONFIG_PINCTRL_IMX28) += pinctrl-imx28.o + obj-$(CONFIG_PINCTRL_MSM) += pinctrl-msm.o ++obj-$(CONFIG_PINCTRL_APQ8064) += pinctrl-apq8064.o + obj-$(CONFIG_PINCTRL_MSM8X74) += pinctrl-msm8x74.o + obj-$(CONFIG_PINCTRL_NOMADIK) += pinctrl-nomadik.o + obj-$(CONFIG_PINCTRL_STN8815) += pinctrl-nomadik-stn8815.o +diff --git a/drivers/pinctrl/pinctrl-apq8064.c b/drivers/pinctrl/pinctrl-apq8064.c +new file mode 100644 +index 0000000..7c2a8ba +--- /dev/null ++++ b/drivers/pinctrl/pinctrl-apq8064.c +@@ -0,0 +1,566 @@ ++/* ++ * Copyright (c) 2014, Sony Mobile Communications AB. ++ * ++ * 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/of.h> ++#include <linux/platform_device.h> ++#include <linux/pinctrl/pinctrl.h> ++ ++#include "pinctrl-msm.h" ++ ++static const struct pinctrl_pin_desc apq8064_pins[] = { ++ PINCTRL_PIN(0, "GPIO_0"), ++ PINCTRL_PIN(1, "GPIO_1"), ++ PINCTRL_PIN(2, "GPIO_2"), ++ PINCTRL_PIN(3, "GPIO_3"), ++ PINCTRL_PIN(4, "GPIO_4"), ++ PINCTRL_PIN(5, "GPIO_5"), ++ PINCTRL_PIN(6, "GPIO_6"), ++ PINCTRL_PIN(7, "GPIO_7"), ++ PINCTRL_PIN(8, "GPIO_8"), ++ PINCTRL_PIN(9, "GPIO_9"), ++ PINCTRL_PIN(10, "GPIO_10"), ++ PINCTRL_PIN(11, "GPIO_11"), ++ PINCTRL_PIN(12, "GPIO_12"), ++ PINCTRL_PIN(13, "GPIO_13"), ++ PINCTRL_PIN(14, "GPIO_14"), ++ PINCTRL_PIN(15, "GPIO_15"), ++ PINCTRL_PIN(16, "GPIO_16"), ++ PINCTRL_PIN(17, "GPIO_17"), ++ PINCTRL_PIN(18, "GPIO_18"), ++ PINCTRL_PIN(19, "GPIO_19"), ++ PINCTRL_PIN(20, "GPIO_20"), ++ PINCTRL_PIN(21, "GPIO_21"), ++ PINCTRL_PIN(22, "GPIO_22"), ++ PINCTRL_PIN(23, "GPIO_23"), ++ PINCTRL_PIN(24, "GPIO_24"), ++ PINCTRL_PIN(25, "GPIO_25"), ++ PINCTRL_PIN(26, "GPIO_26"), ++ PINCTRL_PIN(27, "GPIO_27"), ++ PINCTRL_PIN(28, "GPIO_28"), ++ PINCTRL_PIN(29, "GPIO_29"), ++ PINCTRL_PIN(30, "GPIO_30"), ++ PINCTRL_PIN(31, "GPIO_31"), ++ PINCTRL_PIN(32, "GPIO_32"), ++ PINCTRL_PIN(33, "GPIO_33"), ++ PINCTRL_PIN(34, "GPIO_34"), ++ PINCTRL_PIN(35, "GPIO_35"), ++ PINCTRL_PIN(36, "GPIO_36"), ++ PINCTRL_PIN(37, "GPIO_37"), ++ PINCTRL_PIN(38, "GPIO_38"), ++ PINCTRL_PIN(39, "GPIO_39"), ++ PINCTRL_PIN(40, "GPIO_40"), ++ PINCTRL_PIN(41, "GPIO_41"), ++ PINCTRL_PIN(42, "GPIO_42"), ++ PINCTRL_PIN(43, "GPIO_43"), ++ PINCTRL_PIN(44, "GPIO_44"), ++ PINCTRL_PIN(45, "GPIO_45"), ++ PINCTRL_PIN(46, "GPIO_46"), ++ PINCTRL_PIN(47, "GPIO_47"), ++ PINCTRL_PIN(48, "GPIO_48"), ++ PINCTRL_PIN(49, "GPIO_49"), ++ PINCTRL_PIN(50, "GPIO_50"), ++ PINCTRL_PIN(51, "GPIO_51"), ++ PINCTRL_PIN(52, "GPIO_52"), ++ PINCTRL_PIN(53, "GPIO_53"), ++ PINCTRL_PIN(54, "GPIO_54"), ++ PINCTRL_PIN(55, "GPIO_55"), ++ PINCTRL_PIN(56, "GPIO_56"), ++ PINCTRL_PIN(57, "GPIO_57"), ++ PINCTRL_PIN(58, "GPIO_58"), ++ PINCTRL_PIN(59, "GPIO_59"), ++ PINCTRL_PIN(60, "GPIO_60"), ++ PINCTRL_PIN(61, "GPIO_61"), ++ PINCTRL_PIN(62, "GPIO_62"), ++ PINCTRL_PIN(63, "GPIO_63"), ++ PINCTRL_PIN(64, "GPIO_64"), ++ PINCTRL_PIN(65, "GPIO_65"), ++ PINCTRL_PIN(66, "GPIO_66"), ++ PINCTRL_PIN(67, "GPIO_67"), ++ PINCTRL_PIN(68, "GPIO_68"), ++ PINCTRL_PIN(69, "GPIO_69"), ++ PINCTRL_PIN(70, "GPIO_70"), ++ PINCTRL_PIN(71, "GPIO_71"), ++ PINCTRL_PIN(72, "GPIO_72"), ++ PINCTRL_PIN(73, "GPIO_73"), ++ PINCTRL_PIN(74, "GPIO_74"), ++ PINCTRL_PIN(75, "GPIO_75"), ++ PINCTRL_PIN(76, "GPIO_76"), ++ PINCTRL_PIN(77, "GPIO_77"), ++ PINCTRL_PIN(78, "GPIO_78"), ++ PINCTRL_PIN(79, "GPIO_79"), ++ PINCTRL_PIN(80, "GPIO_80"), ++ PINCTRL_PIN(81, "GPIO_81"), ++ PINCTRL_PIN(82, "GPIO_82"), ++ PINCTRL_PIN(83, "GPIO_83"), ++ PINCTRL_PIN(84, "GPIO_84"), ++ PINCTRL_PIN(85, "GPIO_85"), ++ PINCTRL_PIN(86, "GPIO_86"), ++ PINCTRL_PIN(87, "GPIO_87"), ++ PINCTRL_PIN(88, "GPIO_88"), ++ PINCTRL_PIN(89, "GPIO_89"), ++}; ++ ++#define DECLARE_APQ_GPIO_PINS(pin) static const unsigned int gpio##pin##_pins[] = { pin } ++DECLARE_APQ_GPIO_PINS(0); ++DECLARE_APQ_GPIO_PINS(1); ++DECLARE_APQ_GPIO_PINS(2); ++DECLARE_APQ_GPIO_PINS(3); ++DECLARE_APQ_GPIO_PINS(4); ++DECLARE_APQ_GPIO_PINS(5); ++DECLARE_APQ_GPIO_PINS(6); ++DECLARE_APQ_GPIO_PINS(7); ++DECLARE_APQ_GPIO_PINS(8); ++DECLARE_APQ_GPIO_PINS(9); ++DECLARE_APQ_GPIO_PINS(10); ++DECLARE_APQ_GPIO_PINS(11); ++DECLARE_APQ_GPIO_PINS(12); ++DECLARE_APQ_GPIO_PINS(13); ++DECLARE_APQ_GPIO_PINS(14); ++DECLARE_APQ_GPIO_PINS(15); ++DECLARE_APQ_GPIO_PINS(16); ++DECLARE_APQ_GPIO_PINS(17); ++DECLARE_APQ_GPIO_PINS(18); ++DECLARE_APQ_GPIO_PINS(19); ++DECLARE_APQ_GPIO_PINS(20); ++DECLARE_APQ_GPIO_PINS(21); ++DECLARE_APQ_GPIO_PINS(22); ++DECLARE_APQ_GPIO_PINS(23); ++DECLARE_APQ_GPIO_PINS(24); ++DECLARE_APQ_GPIO_PINS(25); ++DECLARE_APQ_GPIO_PINS(26); ++DECLARE_APQ_GPIO_PINS(27); ++DECLARE_APQ_GPIO_PINS(28); ++DECLARE_APQ_GPIO_PINS(29); ++DECLARE_APQ_GPIO_PINS(30); ++DECLARE_APQ_GPIO_PINS(31); ++DECLARE_APQ_GPIO_PINS(32); ++DECLARE_APQ_GPIO_PINS(33); ++DECLARE_APQ_GPIO_PINS(34); ++DECLARE_APQ_GPIO_PINS(35); ++DECLARE_APQ_GPIO_PINS(36); ++DECLARE_APQ_GPIO_PINS(37); ++DECLARE_APQ_GPIO_PINS(38); ++DECLARE_APQ_GPIO_PINS(39); ++DECLARE_APQ_GPIO_PINS(40); ++DECLARE_APQ_GPIO_PINS(41); ++DECLARE_APQ_GPIO_PINS(42); ++DECLARE_APQ_GPIO_PINS(43); ++DECLARE_APQ_GPIO_PINS(44); ++DECLARE_APQ_GPIO_PINS(45); ++DECLARE_APQ_GPIO_PINS(46); ++DECLARE_APQ_GPIO_PINS(47); ++DECLARE_APQ_GPIO_PINS(48); ++DECLARE_APQ_GPIO_PINS(49); ++DECLARE_APQ_GPIO_PINS(50); ++DECLARE_APQ_GPIO_PINS(51); ++DECLARE_APQ_GPIO_PINS(52); ++DECLARE_APQ_GPIO_PINS(53); ++DECLARE_APQ_GPIO_PINS(54); ++DECLARE_APQ_GPIO_PINS(55); ++DECLARE_APQ_GPIO_PINS(56); ++DECLARE_APQ_GPIO_PINS(57); ++DECLARE_APQ_GPIO_PINS(58); ++DECLARE_APQ_GPIO_PINS(59); ++DECLARE_APQ_GPIO_PINS(60); ++DECLARE_APQ_GPIO_PINS(61); ++DECLARE_APQ_GPIO_PINS(62); ++DECLARE_APQ_GPIO_PINS(63); ++DECLARE_APQ_GPIO_PINS(64); ++DECLARE_APQ_GPIO_PINS(65); ++DECLARE_APQ_GPIO_PINS(66); ++DECLARE_APQ_GPIO_PINS(67); ++DECLARE_APQ_GPIO_PINS(68); ++DECLARE_APQ_GPIO_PINS(69); ++DECLARE_APQ_GPIO_PINS(70); ++DECLARE_APQ_GPIO_PINS(71); ++DECLARE_APQ_GPIO_PINS(72); ++DECLARE_APQ_GPIO_PINS(73); ++DECLARE_APQ_GPIO_PINS(74); ++DECLARE_APQ_GPIO_PINS(75); ++DECLARE_APQ_GPIO_PINS(76); ++DECLARE_APQ_GPIO_PINS(77); ++DECLARE_APQ_GPIO_PINS(78); ++DECLARE_APQ_GPIO_PINS(79); ++DECLARE_APQ_GPIO_PINS(80); ++DECLARE_APQ_GPIO_PINS(81); ++DECLARE_APQ_GPIO_PINS(82); ++DECLARE_APQ_GPIO_PINS(83); ++DECLARE_APQ_GPIO_PINS(84); ++DECLARE_APQ_GPIO_PINS(85); ++DECLARE_APQ_GPIO_PINS(86); ++DECLARE_APQ_GPIO_PINS(87); ++DECLARE_APQ_GPIO_PINS(88); ++DECLARE_APQ_GPIO_PINS(89); ++ ++#define FUNCTION(fname) \ ++ [APQ_MUX_##fname] = { \ ++ .name = #fname, \ ++ .groups = fname##_groups, \ ++ .ngroups = ARRAY_SIZE(fname##_groups), \ ++ } ++ ++#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) \ ++ { \ ++ .name = "gpio" #id, \ ++ .pins = gpio##id##_pins, \ ++ .npins = ARRAY_SIZE(gpio##id##_pins), \ ++ .funcs = (int[]){ \ ++ APQ_MUX_NA, /* gpio mode */ \ ++ APQ_MUX_##f1, \ ++ APQ_MUX_##f2, \ ++ APQ_MUX_##f3, \ ++ APQ_MUX_##f4, \ ++ APQ_MUX_##f5, \ ++ APQ_MUX_##f6, \ ++ APQ_MUX_##f7, \ ++ APQ_MUX_##f8, \ ++ APQ_MUX_##f9, \ ++ APQ_MUX_##f10, \ ++ }, \ ++ .nfuncs = 11, \ ++ .ctl_reg = 0x1000 + 0x10 * id, \ ++ .io_reg = 0x1004 + 0x10 * id, \ ++ .intr_cfg_reg = 0x1008 + 0x10 * id, \ ++ .intr_status_reg = 0x100c + 0x10 * id, \ ++ .intr_target_reg = 0x400 + 0x4 * id, \ ++ .mux_bit = 2, \ ++ .pull_bit = 0, \ ++ .drv_bit = 6, \ ++ .oe_bit = 9, \ ++ .in_bit = 0, \ ++ .out_bit = 1, \ ++ .intr_enable_bit = 0, \ ++ .intr_status_bit = 0, \ ++ .intr_ack_high = 1, \ ++ .intr_target_bit = 0, \ ++ .intr_raw_status_bit = 3, \ ++ .intr_polarity_bit = 1, \ ++ .intr_detection_bit = 2, \ ++ .intr_detection_width = 1, \ ++ } ++ ++enum apq8064_functions { ++ APQ_MUX_cam_mclk, ++ APQ_MUX_codec_mic_i2s, ++ APQ_MUX_codec_spkr_i2s, ++ APQ_MUX_gsbi1, ++ APQ_MUX_gsbi2, ++ APQ_MUX_gsbi3, ++ APQ_MUX_gsbi4, ++ APQ_MUX_gsbi4_cam_i2c, ++ APQ_MUX_gsbi5, ++ APQ_MUX_gsbi5_spi_cs1, ++ APQ_MUX_gsbi5_spi_cs2, ++ APQ_MUX_gsbi5_spi_cs3, ++ APQ_MUX_gsbi6, ++ APQ_MUX_gsbi6_spi_cs1, ++ APQ_MUX_gsbi6_spi_cs2, ++ APQ_MUX_gsbi6_spi_cs3, ++ APQ_MUX_gsbi7, ++ APQ_MUX_gsbi7_spi_cs1, ++ APQ_MUX_gsbi7_spi_cs2, ++ APQ_MUX_gsbi7_spi_cs3, ++ APQ_MUX_gsbi_cam_i2c, ++ APQ_MUX_hdmi, ++ APQ_MUX_mi2s, ++ APQ_MUX_riva_bt, ++ APQ_MUX_riva_fm, ++ APQ_MUX_riva_wlan, ++ APQ_MUX_sdc2, ++ APQ_MUX_sdc4, ++ APQ_MUX_slimbus, ++ APQ_MUX_spkr_i2s, ++ APQ_MUX_tsif1, ++ APQ_MUX_tsif2, ++ APQ_MUX_usb2_hsic, ++ APQ_MUX_NA, ++}; ++ ++static const char * const cam_mclk_groups[] = { ++ "gpio4" "gpio5" ++}; ++static const char * const codec_mic_i2s_groups[] = { ++ "gpio34", "gpio35", "gpio36", "gpio37", "gpio38" ++}; ++static const char * const codec_spkr_i2s_groups[] = { ++ "gpio39", "gpio40", "gpio41", "gpio42" ++}; ++static const char * const gsbi1_groups[] = { ++ "gpio18", "gpio19", "gpio20", "gpio21" ++}; ++static const char * const gsbi2_groups[] = { ++ "gpio22", "gpio23", "gpio24", "gpio25" ++}; ++static const char * const gsbi3_groups[] = { ++ "gpio6", "gpio7", "gpio8", "gpio9" ++}; ++static const char * const gsbi4_groups[] = { ++ "gpio10", "gpio11", "gpio12", "gpio13" ++}; ++static const char * const gsbi4_cam_i2c_groups[] = { ++ "gpio10", "gpio11", "gpio12", "gpio13" ++}; ++static const char * const gsbi5_groups[] = { ++ "gpio51", "gpio52", "gpio53", "gpio54" ++}; ++static const char * const gsbi5_spi_cs1_groups[] = { ++ "gpio47" ++}; ++static const char * const gsbi5_spi_cs2_groups[] = { ++ "gpio31" ++}; ++static const char * const gsbi5_spi_cs3_groups[] = { ++ "gpio32" ++}; ++static const char * const gsbi6_groups[] = { ++ "gpio14", "gpio15", "gpio16", "gpio17" ++}; ++static const char * const gsbi6_spi_cs1_groups[] = { ++ "gpio47" ++}; ++static const char * const gsbi6_spi_cs2_groups[] = { ++ "gpio31" ++}; ++static const char * const gsbi6_spi_cs3_groups[] = { ++ "gpio32" ++}; ++static const char * const gsbi7_groups[] = { ++ "gpio82", "gpio83", "gpio84", "gpio85" ++}; ++static const char * const gsbi7_spi_cs1_groups[] = { ++ "gpio47" ++}; ++static const char * const gsbi7_spi_cs2_groups[] = { ++ "gpio31" ++}; ++static const char * const gsbi7_spi_cs3_groups[] = { ++ "gpio32" ++}; ++static const char * const gsbi_cam_i2c_groups[] = { ++ "gpio10", "gpio11", "gpio12", "gpio13" ++}; ++static const char * const hdmi_groups[] = { ++ "gpio69", "gpio70", "gpio71", "gpio72" ++}; ++static const char * const mi2s_groups[] = { ++ "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", "gpio33" ++}; ++static const char * const riva_bt_groups[] = { ++ "gpio16", "gpio17" ++}; ++static const char * const riva_fm_groups[] = { ++ "gpio14", "gpio15" ++}; ++static const char * const riva_wlan_groups[] = { ++ "gpio64", "gpio65", "gpio66", "gpio67", "gpio68" ++}; ++static const char * const sdc2_groups[] = { ++ "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62" ++}; ++static const char * const sdc4_groups[] = { ++ "gpio63", "gpio64", "gpio65", "gpio66", "gpio67", "gpio68" ++}; ++static const char * const slimbus_groups[] = { ++ "gpio40", "gpio41" ++}; ++static const char * const spkr_i2s_groups[] = { ++ "gpio47", "gpio48", "gpio49", "gpio50" ++}; ++static const char * const tsif1_groups[] = { ++ "gpio55", "gpio56", "gpio57" ++}; ++static const char * const tsif2_groups[] = { ++ "gpio58", "gpio59", "gpio60" ++}; ++static const char * const usb2_hsic_groups[] = { ++ "gpio88", "gpio89" ++}; ++ ++static const struct msm_function apq8064_functions[] = { ++ FUNCTION(cam_mclk), ++ FUNCTION(codec_mic_i2s), ++ FUNCTION(codec_spkr_i2s), ++ FUNCTION(gsbi1), ++ FUNCTION(gsbi2), ++ FUNCTION(gsbi3), ++ FUNCTION(gsbi4), ++ FUNCTION(gsbi4_cam_i2c), ++ FUNCTION(gsbi5), ++ FUNCTION(gsbi5_spi_cs1), ++ FUNCTION(gsbi5_spi_cs2), ++ FUNCTION(gsbi5_spi_cs3), ++ FUNCTION(gsbi6), ++ FUNCTION(gsbi6_spi_cs1), ++ FUNCTION(gsbi6_spi_cs2), ++ FUNCTION(gsbi6_spi_cs3), ++ FUNCTION(gsbi7), ++ FUNCTION(gsbi7_spi_cs1), ++ FUNCTION(gsbi7_spi_cs2), ++ FUNCTION(gsbi7_spi_cs3), ++ FUNCTION(gsbi_cam_i2c), ++ FUNCTION(hdmi), ++ FUNCTION(mi2s), ++ FUNCTION(riva_bt), ++ FUNCTION(riva_fm), ++ FUNCTION(riva_wlan), ++ FUNCTION(sdc2), ++ FUNCTION(sdc4), ++ FUNCTION(slimbus), ++ FUNCTION(spkr_i2s), ++ FUNCTION(tsif1), ++ FUNCTION(tsif2), ++ FUNCTION(usb2_hsic), ++}; ++ ++static const struct msm_pingroup apq8064_groups[] = { ++ PINGROUP(0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(2, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(3, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(4, NA, NA, cam_mclk, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(5, NA, cam_mclk, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(6, gsbi3, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(7, gsbi3, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(8, gsbi3, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(9, gsbi3, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(10, gsbi4, NA, NA, NA, NA, NA, NA, NA, gsbi4_cam_i2c, NA), ++ PINGROUP(11, gsbi4, NA, NA, NA, NA, NA, NA, NA, NA, gsbi4_cam_i2c), ++ PINGROUP(12, gsbi4, NA, NA, NA, NA, gsbi4_cam_i2c, NA, NA, NA, NA), ++ PINGROUP(13, gsbi4, NA, NA, NA, NA, gsbi4_cam_i2c, NA, NA, NA, NA), ++ PINGROUP(14, riva_fm, gsbi6, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(15, riva_fm, gsbi6, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(16, riva_bt, gsbi6, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(17, riva_bt, gsbi6, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(18, gsbi1, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(19, gsbi1, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(20, gsbi1, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(21, gsbi1, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(22, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(23, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(24, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(25, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(26, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(27, mi2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(28, mi2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(29, mi2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(30, mi2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(31, mi2s, NA, gsbi5_spi_cs2, gsbi6_spi_cs2, gsbi7_spi_cs2, NA, NA, NA, NA, NA), ++ PINGROUP(32, mi2s, NA, NA, NA, NA, gsbi5_spi_cs3, gsbi6_spi_cs3, gsbi7_spi_cs3, NA, NA), ++ PINGROUP(33, mi2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(34, codec_mic_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(35, codec_mic_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(36, codec_mic_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(37, codec_mic_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(38, codec_mic_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(39, codec_spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(40, slimbus, codec_spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(41, slimbus, codec_spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(42, codec_spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(43, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(44, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(45, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(46, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(47, spkr_i2s, gsbi5_spi_cs1, gsbi6_spi_cs1, gsbi7_spi_cs1, NA, NA, NA, NA, NA, NA), ++ PINGROUP(48, spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(49, spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(50, spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(51, NA, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(52, NA, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(53, NA, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(54, NA, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(55, tsif1, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(56, tsif1, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(57, tsif1, sdc2, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(58, tsif2, sdc2, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(59, tsif2, sdc2, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(60, tsif2, sdc2, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(61, NA, sdc2, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(62, NA, sdc2, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(63, NA, sdc4, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(64, riva_wlan, sdc4, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(65, riva_wlan, sdc4, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(66, riva_wlan, sdc4, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(67, riva_wlan, sdc4, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(68, riva_wlan, sdc4, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(69, hdmi, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(70, hdmi, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(71, hdmi, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(72, hdmi, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(73, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(74, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(75, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(76, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(77, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(78, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(79, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(80, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(81, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(82, NA, gsbi7, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(83, gsbi7, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(84, NA, gsbi7, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(85, NA, NA, gsbi7, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(86, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(87, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(88, usb2_hsic, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(89, usb2_hsic, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++}; ++ ++#define NUM_GPIO_PINGROUPS 90 ++ ++static const struct msm_pinctrl_soc_data apq8064_pinctrl = { ++ .pins = apq8064_pins, ++ .npins = ARRAY_SIZE(apq8064_pins), ++ .functions = apq8064_functions, ++ .nfunctions = ARRAY_SIZE(apq8064_functions), ++ .groups = apq8064_groups, ++ .ngroups = ARRAY_SIZE(apq8064_groups), ++ .ngpios = NUM_GPIO_PINGROUPS, ++}; ++ ++static int apq8064_pinctrl_probe(struct platform_device *pdev) ++{ ++ return msm_pinctrl_probe(pdev, &apq8064_pinctrl); ++} ++ ++static const struct of_device_id apq8064_pinctrl_of_match[] = { ++ { .compatible = "qcom,apq8064-pinctrl", }, ++ { }, ++}; ++ ++static struct platform_driver apq8064_pinctrl_driver = { ++ .driver = { ++ .name = "apq8064-pinctrl", ++ .owner = THIS_MODULE, ++ .of_match_table = apq8064_pinctrl_of_match, ++ }, ++ .probe = apq8064_pinctrl_probe, ++ .remove = msm_pinctrl_remove, ++}; ++ ++static int __init apq8064_pinctrl_init(void) ++{ ++ return platform_driver_register(&apq8064_pinctrl_driver); ++} ++arch_initcall(apq8064_pinctrl_init); ++ ++static void __exit apq8064_pinctrl_exit(void) ++{ ++ platform_driver_unregister(&apq8064_pinctrl_driver); ++} ++module_exit(apq8064_pinctrl_exit); ++ ++MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>"); ++MODULE_DESCRIPTION("Qualcomm APQ8064 pinctrl driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_DEVICE_TABLE(of, apq8064_pinctrl_of_match); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0039-pinctrl-msm8x74-make-Kconfig-dependency-more-strict.patch b/target/linux/ipq806x/patches/0039-pinctrl-msm8x74-make-Kconfig-dependency-more-strict.patch new file mode 100644 index 0000000000..51ed6d645f --- /dev/null +++ b/target/linux/ipq806x/patches/0039-pinctrl-msm8x74-make-Kconfig-dependency-more-strict.patch @@ -0,0 +1,36 @@ +From 8605197c200786888415bf2e30d1fbde6ba8ba03 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de> +Date: Tue, 1 Apr 2014 22:25:59 +0200 +Subject: [PATCH 039/182] pinctrl: msm8x74: make Kconfig dependency more + strict +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This driver is only useful on MSM8x74, so let the driver depend on +ARCH_QCOM but allow compile coverage testing. +The main benefit is that the driver isn't available to be selected for +machines that don't have the matching hardware. + +Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig +index 91993a6..d34639d 100644 +--- a/drivers/pinctrl/Kconfig ++++ b/drivers/pinctrl/Kconfig +@@ -232,7 +232,7 @@ config PINCTRL_APQ8064 + + config PINCTRL_MSM8X74 + tristate "Qualcomm 8x74 pin controller driver" +- depends on GPIOLIB && OF ++ depends on GPIOLIB && OF && (ARCH_QCOM || COMPILE_TEST) + select PINCTRL_MSM + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0040-pinctrl-qcom-Add-definitions-for-IPQ8064.patch b/target/linux/ipq806x/patches/0040-pinctrl-qcom-Add-definitions-for-IPQ8064.patch new file mode 100644 index 0000000000..6b48085289 --- /dev/null +++ b/target/linux/ipq806x/patches/0040-pinctrl-qcom-Add-definitions-for-IPQ8064.patch @@ -0,0 +1,711 @@ +From 9bbd9d7e40944ca95e07f363b68700225beb9bef Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 14 Apr 2014 22:10:35 -0500 +Subject: [PATCH 040/182] pinctrl: qcom: Add definitions for IPQ8064 + +This adds pinctrl definitions for the GPIO pins of the TLMM v2 block in the +Qualcomm IPQ8064 platform. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Reviewed-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/Kconfig | 8 + + drivers/pinctrl/Makefile | 1 + + drivers/pinctrl/pinctrl-ipq8064.c | 653 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 662 insertions(+) + create mode 100644 drivers/pinctrl/pinctrl-ipq8064.c + +diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig +index d34639d..232e6bc 100644 +--- a/drivers/pinctrl/Kconfig ++++ b/drivers/pinctrl/Kconfig +@@ -230,6 +230,14 @@ config PINCTRL_APQ8064 + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm TLMM block found in the Qualcomm APQ8064 platform. + ++config PINCTRL_IPQ8064 ++ tristate "Qualcomm IPQ8064 pin controller driver" ++ depends on GPIOLIB && OF ++ select PINCTRL_MSM ++ help ++ This is the pinctrl, pinmux, pinconf and gpiolib driver for the ++ Qualcomm TLMM block found in the Qualcomm IPQ8064 platform. ++ + config PINCTRL_MSM8X74 + tristate "Qualcomm 8x74 pin controller driver" + depends on GPIOLIB && OF && (ARCH_QCOM || COMPILE_TEST) +diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile +index 9e1fb67..6b8474a 100644 +--- a/drivers/pinctrl/Makefile ++++ b/drivers/pinctrl/Makefile +@@ -39,6 +39,7 @@ obj-$(CONFIG_PINCTRL_IMX25) += pinctrl-imx25.o + obj-$(CONFIG_PINCTRL_IMX28) += pinctrl-imx28.o + obj-$(CONFIG_PINCTRL_MSM) += pinctrl-msm.o + obj-$(CONFIG_PINCTRL_APQ8064) += pinctrl-apq8064.o ++obj-$(CONFIG_PINCTRL_IPQ8064) += pinctrl-ipq8064.o + obj-$(CONFIG_PINCTRL_MSM8X74) += pinctrl-msm8x74.o + obj-$(CONFIG_PINCTRL_NOMADIK) += pinctrl-nomadik.o + obj-$(CONFIG_PINCTRL_STN8815) += pinctrl-nomadik-stn8815.o +diff --git a/drivers/pinctrl/pinctrl-ipq8064.c b/drivers/pinctrl/pinctrl-ipq8064.c +new file mode 100644 +index 0000000..1700b49 +--- /dev/null ++++ b/drivers/pinctrl/pinctrl-ipq8064.c +@@ -0,0 +1,653 @@ ++/* ++ * 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/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/pinctrl/pinctrl.h> ++ ++#include "pinctrl-msm.h" ++ ++static const struct pinctrl_pin_desc ipq8064_pins[] = { ++ PINCTRL_PIN(0, "GPIO_1"), ++ PINCTRL_PIN(1, "GPIO_1"), ++ PINCTRL_PIN(2, "GPIO_2"), ++ PINCTRL_PIN(3, "GPIO_3"), ++ PINCTRL_PIN(4, "GPIO_4"), ++ PINCTRL_PIN(5, "GPIO_5"), ++ PINCTRL_PIN(6, "GPIO_6"), ++ PINCTRL_PIN(7, "GPIO_7"), ++ PINCTRL_PIN(8, "GPIO_8"), ++ PINCTRL_PIN(9, "GPIO_9"), ++ PINCTRL_PIN(10, "GPIO_10"), ++ PINCTRL_PIN(11, "GPIO_11"), ++ PINCTRL_PIN(12, "GPIO_12"), ++ PINCTRL_PIN(13, "GPIO_13"), ++ PINCTRL_PIN(14, "GPIO_14"), ++ PINCTRL_PIN(15, "GPIO_15"), ++ PINCTRL_PIN(16, "GPIO_16"), ++ PINCTRL_PIN(17, "GPIO_17"), ++ PINCTRL_PIN(18, "GPIO_18"), ++ PINCTRL_PIN(19, "GPIO_19"), ++ PINCTRL_PIN(20, "GPIO_20"), ++ PINCTRL_PIN(21, "GPIO_21"), ++ PINCTRL_PIN(22, "GPIO_22"), ++ PINCTRL_PIN(23, "GPIO_23"), ++ PINCTRL_PIN(24, "GPIO_24"), ++ PINCTRL_PIN(25, "GPIO_25"), ++ PINCTRL_PIN(26, "GPIO_26"), ++ PINCTRL_PIN(27, "GPIO_27"), ++ PINCTRL_PIN(28, "GPIO_28"), ++ PINCTRL_PIN(29, "GPIO_29"), ++ PINCTRL_PIN(30, "GPIO_30"), ++ PINCTRL_PIN(31, "GPIO_31"), ++ PINCTRL_PIN(32, "GPIO_32"), ++ PINCTRL_PIN(33, "GPIO_33"), ++ PINCTRL_PIN(34, "GPIO_34"), ++ PINCTRL_PIN(35, "GPIO_35"), ++ PINCTRL_PIN(36, "GPIO_36"), ++ PINCTRL_PIN(37, "GPIO_37"), ++ PINCTRL_PIN(38, "GPIO_38"), ++ PINCTRL_PIN(39, "GPIO_39"), ++ PINCTRL_PIN(40, "GPIO_40"), ++ PINCTRL_PIN(41, "GPIO_41"), ++ PINCTRL_PIN(42, "GPIO_42"), ++ PINCTRL_PIN(43, "GPIO_43"), ++ PINCTRL_PIN(44, "GPIO_44"), ++ PINCTRL_PIN(45, "GPIO_45"), ++ PINCTRL_PIN(46, "GPIO_46"), ++ PINCTRL_PIN(47, "GPIO_47"), ++ PINCTRL_PIN(48, "GPIO_48"), ++ PINCTRL_PIN(49, "GPIO_49"), ++ PINCTRL_PIN(50, "GPIO_50"), ++ PINCTRL_PIN(51, "GPIO_51"), ++ PINCTRL_PIN(52, "GPIO_52"), ++ PINCTRL_PIN(53, "GPIO_53"), ++ PINCTRL_PIN(54, "GPIO_54"), ++ PINCTRL_PIN(55, "GPIO_55"), ++ PINCTRL_PIN(56, "GPIO_56"), ++ PINCTRL_PIN(57, "GPIO_57"), ++ PINCTRL_PIN(58, "GPIO_58"), ++ PINCTRL_PIN(59, "GPIO_59"), ++ PINCTRL_PIN(60, "GPIO_60"), ++ PINCTRL_PIN(61, "GPIO_61"), ++ PINCTRL_PIN(62, "GPIO_62"), ++ PINCTRL_PIN(63, "GPIO_63"), ++ PINCTRL_PIN(64, "GPIO_64"), ++ PINCTRL_PIN(65, "GPIO_65"), ++ PINCTRL_PIN(66, "GPIO_66"), ++ PINCTRL_PIN(67, "GPIO_67"), ++ PINCTRL_PIN(68, "GPIO_68"), ++ ++ PINCTRL_PIN(69, "SDC3_CLK"), ++ PINCTRL_PIN(70, "SDC3_CMD"), ++ PINCTRL_PIN(71, "SDC3_DATA"), ++}; ++ ++#define DECLARE_IPQ_GPIO_PINS(pin) static const unsigned int gpio##pin##_pins[] = { pin } ++DECLARE_IPQ_GPIO_PINS(0); ++DECLARE_IPQ_GPIO_PINS(1); ++DECLARE_IPQ_GPIO_PINS(2); ++DECLARE_IPQ_GPIO_PINS(3); ++DECLARE_IPQ_GPIO_PINS(4); ++DECLARE_IPQ_GPIO_PINS(5); ++DECLARE_IPQ_GPIO_PINS(6); ++DECLARE_IPQ_GPIO_PINS(7); ++DECLARE_IPQ_GPIO_PINS(8); ++DECLARE_IPQ_GPIO_PINS(9); ++DECLARE_IPQ_GPIO_PINS(10); ++DECLARE_IPQ_GPIO_PINS(11); ++DECLARE_IPQ_GPIO_PINS(12); ++DECLARE_IPQ_GPIO_PINS(13); ++DECLARE_IPQ_GPIO_PINS(14); ++DECLARE_IPQ_GPIO_PINS(15); ++DECLARE_IPQ_GPIO_PINS(16); ++DECLARE_IPQ_GPIO_PINS(17); ++DECLARE_IPQ_GPIO_PINS(18); ++DECLARE_IPQ_GPIO_PINS(19); ++DECLARE_IPQ_GPIO_PINS(20); ++DECLARE_IPQ_GPIO_PINS(21); ++DECLARE_IPQ_GPIO_PINS(22); ++DECLARE_IPQ_GPIO_PINS(23); ++DECLARE_IPQ_GPIO_PINS(24); ++DECLARE_IPQ_GPIO_PINS(25); ++DECLARE_IPQ_GPIO_PINS(26); ++DECLARE_IPQ_GPIO_PINS(27); ++DECLARE_IPQ_GPIO_PINS(28); ++DECLARE_IPQ_GPIO_PINS(29); ++DECLARE_IPQ_GPIO_PINS(30); ++DECLARE_IPQ_GPIO_PINS(31); ++DECLARE_IPQ_GPIO_PINS(32); ++DECLARE_IPQ_GPIO_PINS(33); ++DECLARE_IPQ_GPIO_PINS(34); ++DECLARE_IPQ_GPIO_PINS(35); ++DECLARE_IPQ_GPIO_PINS(36); ++DECLARE_IPQ_GPIO_PINS(37); ++DECLARE_IPQ_GPIO_PINS(38); ++DECLARE_IPQ_GPIO_PINS(39); ++DECLARE_IPQ_GPIO_PINS(40); ++DECLARE_IPQ_GPIO_PINS(41); ++DECLARE_IPQ_GPIO_PINS(42); ++DECLARE_IPQ_GPIO_PINS(43); ++DECLARE_IPQ_GPIO_PINS(44); ++DECLARE_IPQ_GPIO_PINS(45); ++DECLARE_IPQ_GPIO_PINS(46); ++DECLARE_IPQ_GPIO_PINS(47); ++DECLARE_IPQ_GPIO_PINS(48); ++DECLARE_IPQ_GPIO_PINS(49); ++DECLARE_IPQ_GPIO_PINS(50); ++DECLARE_IPQ_GPIO_PINS(51); ++DECLARE_IPQ_GPIO_PINS(52); ++DECLARE_IPQ_GPIO_PINS(53); ++DECLARE_IPQ_GPIO_PINS(54); ++DECLARE_IPQ_GPIO_PINS(55); ++DECLARE_IPQ_GPIO_PINS(56); ++DECLARE_IPQ_GPIO_PINS(57); ++DECLARE_IPQ_GPIO_PINS(58); ++DECLARE_IPQ_GPIO_PINS(59); ++DECLARE_IPQ_GPIO_PINS(60); ++DECLARE_IPQ_GPIO_PINS(61); ++DECLARE_IPQ_GPIO_PINS(62); ++DECLARE_IPQ_GPIO_PINS(63); ++DECLARE_IPQ_GPIO_PINS(64); ++DECLARE_IPQ_GPIO_PINS(65); ++DECLARE_IPQ_GPIO_PINS(66); ++DECLARE_IPQ_GPIO_PINS(67); ++DECLARE_IPQ_GPIO_PINS(68); ++ ++static const unsigned int sdc3_clk_pins[] = { 69 }; ++static const unsigned int sdc3_cmd_pins[] = { 70 }; ++static const unsigned int sdc3_data_pins[] = { 71 }; ++ ++#define FUNCTION(fname) \ ++ [IPQ_MUX_##fname] = { \ ++ .name = #fname, \ ++ .groups = fname##_groups, \ ++ .ngroups = ARRAY_SIZE(fname##_groups), \ ++ } ++ ++#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) \ ++ { \ ++ .name = "gpio" #id, \ ++ .pins = gpio##id##_pins, \ ++ .npins = ARRAY_SIZE(gpio##id##_pins), \ ++ .funcs = (int[]){ \ ++ IPQ_MUX_NA, /* gpio mode */ \ ++ IPQ_MUX_##f1, \ ++ IPQ_MUX_##f2, \ ++ IPQ_MUX_##f3, \ ++ IPQ_MUX_##f4, \ ++ IPQ_MUX_##f5, \ ++ IPQ_MUX_##f6, \ ++ IPQ_MUX_##f7, \ ++ IPQ_MUX_##f8, \ ++ IPQ_MUX_##f9, \ ++ IPQ_MUX_##f10, \ ++ }, \ ++ .nfuncs = 11, \ ++ .ctl_reg = 0x1000 + 0x10 * id, \ ++ .io_reg = 0x1004 + 0x10 * id, \ ++ .intr_cfg_reg = 0x1008 + 0x10 * id, \ ++ .intr_status_reg = 0x100c + 0x10 * id, \ ++ .intr_target_reg = 0x400 + 0x4 * id, \ ++ .mux_bit = 2, \ ++ .pull_bit = 0, \ ++ .drv_bit = 6, \ ++ .oe_bit = 9, \ ++ .in_bit = 0, \ ++ .out_bit = 1, \ ++ .intr_enable_bit = 0, \ ++ .intr_status_bit = 0, \ ++ .intr_ack_high = 1, \ ++ .intr_target_bit = 0, \ ++ .intr_raw_status_bit = 3, \ ++ .intr_polarity_bit = 1, \ ++ .intr_detection_bit = 2, \ ++ .intr_detection_width = 1, \ ++ } ++ ++#define SDC_PINGROUP(pg_name, ctl, pull, drv) \ ++ { \ ++ .name = #pg_name, \ ++ .pins = pg_name##_pins, \ ++ .npins = ARRAY_SIZE(pg_name##_pins), \ ++ .ctl_reg = ctl, \ ++ .io_reg = 0, \ ++ .intr_cfg_reg = 0, \ ++ .intr_status_reg = 0, \ ++ .intr_target_reg = 0, \ ++ .mux_bit = -1, \ ++ .pull_bit = pull, \ ++ .drv_bit = drv, \ ++ .oe_bit = -1, \ ++ .in_bit = -1, \ ++ .out_bit = -1, \ ++ .intr_enable_bit = -1, \ ++ .intr_status_bit = -1, \ ++ .intr_target_bit = -1, \ ++ .intr_raw_status_bit = -1, \ ++ .intr_polarity_bit = -1, \ ++ .intr_detection_bit = -1, \ ++ .intr_detection_width = -1, \ ++ } ++ ++enum ipq8064_functions { ++ IPQ_MUX_mdio, ++ IPQ_MUX_mi2s, ++ IPQ_MUX_pdm, ++ IPQ_MUX_ssbi, ++ IPQ_MUX_spmi, ++ IPQ_MUX_audio_pcm, ++ IPQ_MUX_gsbi1, ++ IPQ_MUX_gsbi2, ++ IPQ_MUX_gsbi4, ++ IPQ_MUX_gsbi5, ++ IPQ_MUX_gsbi5_spi_cs1, ++ IPQ_MUX_gsbi5_spi_cs2, ++ IPQ_MUX_gsbi5_spi_cs3, ++ IPQ_MUX_gsbi6, ++ IPQ_MUX_gsbi7, ++ IPQ_MUX_nss_spi, ++ IPQ_MUX_sdc1, ++ IPQ_MUX_spdif, ++ IPQ_MUX_nand, ++ IPQ_MUX_tsif1, ++ IPQ_MUX_tsif2, ++ IPQ_MUX_usb_fs_n, ++ IPQ_MUX_usb_fs, ++ IPQ_MUX_usb2_hsic, ++ IPQ_MUX_rgmii2, ++ IPQ_MUX_sata, ++ IPQ_MUX_pcie1_rst, ++ IPQ_MUX_pcie1_prsnt, ++ IPQ_MUX_pcie1_pwrflt, ++ IPQ_MUX_pcie1_pwren_n, ++ IPQ_MUX_pcie1_pwren, ++ IPQ_MUX_pcie1_clk_req, ++ IPQ_MUX_pcie2_rst, ++ IPQ_MUX_pcie2_prsnt, ++ IPQ_MUX_pcie2_pwrflt, ++ IPQ_MUX_pcie2_pwren_n, ++ IPQ_MUX_pcie2_pwren, ++ IPQ_MUX_pcie2_clk_req, ++ IPQ_MUX_pcie3_rst, ++ IPQ_MUX_pcie3_prsnt, ++ IPQ_MUX_pcie3_pwrflt, ++ IPQ_MUX_pcie3_pwren_n, ++ IPQ_MUX_pcie3_pwren, ++ IPQ_MUX_pcie3_clk_req, ++ IPQ_MUX_ps_hold, ++ IPQ_MUX_NA, ++}; ++ ++static const char * const mdio_groups[] = { ++ "gpio0", "gpio1", "gpio10", "gpio11", ++}; ++ ++static const char * const mi2s_groups[] = { ++ "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", ++ "gpio33", "gpio55", "gpio56", "gpio57", "gpio58", ++}; ++ ++static const char * const pdm_groups[] = { ++ "gpio3", "gpio16", "gpio17", "gpio22", "gpio30", "gpio31", ++ "gpio34", "gpio35", "gpio52", "gpio55", "gpio56", "gpio58", ++ "gpio59", ++}; ++ ++static const char * const ssbi_groups[] = { ++ "gpio10", "gpio11", ++}; ++ ++static const char * const spmi_groups[] = { ++ "gpio10", "gpio11", ++}; ++ ++static const char * const audio_pcm_groups[] = { ++ "gpio14", "gpio15", "gpio16", "gpio17", ++}; ++ ++static const char * const gsbi1_groups[] = { ++ "gpio51", "gpio52", "gpio53", "gpio54", ++}; ++ ++static const char * const gsbi2_groups[] = { ++ "gpio22", "gpio23", "gpio24", "gpio25", ++}; ++ ++static const char * const gsbi4_groups[] = { ++ "gpio10", "gpio11", "gpio12", "gpio13", ++}; ++ ++static const char * const gsbi5_groups[] = { ++ "gpio18", "gpio19", "gpio20", "gpio21", ++}; ++ ++static const char * const gsbi5_spi_cs1_groups[] = { ++ "gpio6", "gpio61", ++}; ++ ++static const char * const gsbi5_spi_cs2_groups[] = { ++ "gpio7", "gpio62", ++}; ++ ++static const char * const gsbi5_spi_cs3_groups[] = { ++ "gpio2", ++}; ++ ++static const char * const gsbi6_groups[] = { ++ "gpio27", "gpio28", "gpio29", "gpio30", "gpio55", "gpio56", ++ "gpio57", "gpio58", ++}; ++ ++static const char * const gsbi7_groups[] = { ++ "gpio6", "gpio7", "gpio8", "gpio9", ++}; ++ ++static const char * const nss_spi_groups[] = { ++ "gpio14", "gpio15", "gpio16", "gpio17", "gpio55", "gpio56", ++ "gpio57", "gpio58", ++}; ++ ++static const char * const sdc1_groups[] = { ++ "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", "gpio43", ++ "gpio44", "gpio45", "gpio46", "gpio47", ++}; ++ ++static const char * const spdif_groups[] = { ++ "gpio_10", "gpio_48", ++}; ++ ++static const char * const nand_groups[] = { ++ "gpio34", "gpio35", "gpio36", "gpio37", "gpio38", "gpio39", ++ "gpio40", "gpio41", "gpio42", "gpio43", "gpio44", "gpio45", ++ "gpio46", "gpio47", ++}; ++ ++static const char * const tsif1_groups[] = { ++ "gpio55", "gpio56", "gpio57", "gpio58", ++}; ++ ++static const char * const tsif2_groups[] = { ++ "gpio59", "gpio60", "gpio61", "gpio62", ++}; ++ ++static const char * const usb_fs_n_groups[] = { ++ "gpio6", ++}; ++ ++static const char * const usb_fs_groups[] = { ++ "gpio6", "gpio7", "gpio8", ++}; ++ ++static const char * const usb2_hsic_groups[] = { ++ "gpio67", "gpio68", ++}; ++ ++static const char * const rgmii2_groups[] = { ++ "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", ++ "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62", ++}; ++ ++static const char * const sata_groups[] = { ++ "gpio10", ++}; ++ ++static const char * const pcie1_rst_groups[] = { ++ "gpio3", ++}; ++ ++static const char * const pcie1_prsnt_groups[] = { ++ "gpio3", "gpio11", ++}; ++ ++static const char * const pcie1_pwren_n_groups[] = { ++ "gpio4", "gpio12", ++}; ++ ++static const char * const pcie1_pwren_groups[] = { ++ "gpio4", "gpio12", ++}; ++ ++static const char * const pcie1_pwrflt_groups[] = { ++ "gpio5", "gpio13", ++}; ++ ++static const char * const pcie1_clk_req_groups[] = { ++ "gpio5", ++}; ++ ++static const char * const pcie2_rst_groups[] = { ++ "gpio48", ++}; ++ ++static const char * const pcie2_prsnt_groups[] = { ++ "gpio11", "gpio48", ++}; ++ ++static const char * const pcie2_pwren_n_groups[] = { ++ "gpio12", "gpio49", ++}; ++ ++static const char * const pcie2_pwren_groups[] = { ++ "gpio12", "gpio49", ++}; ++ ++static const char * const pcie2_pwrflt_groups[] = { ++ "gpio13", "gpio50", ++}; ++ ++static const char * const pcie2_clk_req_groups[] = { ++ "gpio50", ++}; ++ ++static const char * const pcie3_rst_groups[] = { ++ "gpio63", ++}; ++ ++static const char * const pcie3_prsnt_groups[] = { ++ "gpio11", ++}; ++ ++static const char * const pcie3_pwren_n_groups[] = { ++ "gpio12", ++}; ++ ++static const char * const pcie3_pwren_groups[] = { ++ "gpio12", ++}; ++ ++static const char * const pcie3_pwrflt_groups[] = { ++ "gpio13", ++}; ++ ++static const char * const pcie3_clk_req_groups[] = { ++ "gpio65", ++}; ++ ++static const char * const ps_hold_groups[] = { ++ "gpio26", ++}; ++ ++static const struct msm_function ipq8064_functions[] = { ++ FUNCTION(mdio), ++ FUNCTION(ssbi), ++ FUNCTION(spmi), ++ FUNCTION(mi2s), ++ FUNCTION(pdm), ++ FUNCTION(audio_pcm), ++ FUNCTION(gsbi1), ++ FUNCTION(gsbi2), ++ FUNCTION(gsbi4), ++ FUNCTION(gsbi5), ++ FUNCTION(gsbi5_spi_cs1), ++ FUNCTION(gsbi5_spi_cs2), ++ FUNCTION(gsbi5_spi_cs3), ++ FUNCTION(gsbi6), ++ FUNCTION(gsbi7), ++ FUNCTION(nss_spi), ++ FUNCTION(sdc1), ++ FUNCTION(spdif), ++ FUNCTION(nand), ++ FUNCTION(tsif1), ++ FUNCTION(tsif2), ++ FUNCTION(usb_fs_n), ++ FUNCTION(usb_fs), ++ FUNCTION(usb2_hsic), ++ FUNCTION(rgmii2), ++ FUNCTION(sata), ++ FUNCTION(pcie1_rst), ++ FUNCTION(pcie1_prsnt), ++ FUNCTION(pcie1_pwren_n), ++ FUNCTION(pcie1_pwren), ++ FUNCTION(pcie1_pwrflt), ++ FUNCTION(pcie1_clk_req), ++ FUNCTION(pcie2_rst), ++ FUNCTION(pcie2_prsnt), ++ FUNCTION(pcie2_pwren_n), ++ FUNCTION(pcie2_pwren), ++ FUNCTION(pcie2_pwrflt), ++ FUNCTION(pcie2_clk_req), ++ FUNCTION(pcie3_rst), ++ FUNCTION(pcie3_prsnt), ++ FUNCTION(pcie3_pwren_n), ++ FUNCTION(pcie3_pwren), ++ FUNCTION(pcie3_pwrflt), ++ FUNCTION(pcie3_clk_req), ++ FUNCTION(ps_hold), ++}; ++ ++static const struct msm_pingroup ipq8064_groups[] = { ++ PINGROUP(0, mdio, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(1, mdio, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(2, gsbi5_spi_cs3, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(3, pcie1_rst, pcie1_prsnt, pdm, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(4, pcie1_pwren_n, pcie1_pwren, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(5, pcie1_clk_req, pcie1_pwrflt, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(6, gsbi7, usb_fs, gsbi5_spi_cs1, usb_fs_n, NA, NA, NA, NA, NA, NA), ++ PINGROUP(7, gsbi7, usb_fs, gsbi5_spi_cs2, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(8, gsbi7, usb_fs, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(9, gsbi7, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(10, gsbi4, spdif, sata, ssbi, mdio, spmi, NA, NA, NA, NA), ++ PINGROUP(11, gsbi4, pcie2_prsnt, pcie1_prsnt, pcie3_prsnt, ssbi, mdio, spmi, NA, NA, NA), ++ PINGROUP(12, gsbi4, pcie2_pwren_n, pcie1_pwren_n, pcie3_pwren_n, pcie2_pwren, pcie1_pwren, pcie3_pwren, NA, NA, NA), ++ PINGROUP(13, gsbi4, pcie2_pwrflt, pcie1_pwrflt, pcie3_pwrflt, NA, NA, NA, NA, NA, NA), ++ PINGROUP(14, audio_pcm, nss_spi, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(15, audio_pcm, nss_spi, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(16, audio_pcm, nss_spi, pdm, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(17, audio_pcm, nss_spi, pdm, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(18, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(19, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(20, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(21, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(22, gsbi2, pdm, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(23, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(24, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(25, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(26, ps_hold, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(27, mi2s, rgmii2, gsbi6, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(28, mi2s, rgmii2, gsbi6, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(29, mi2s, rgmii2, gsbi6, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(30, mi2s, rgmii2, gsbi6, pdm, NA, NA, NA, NA, NA, NA), ++ PINGROUP(31, mi2s, rgmii2, pdm, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(32, mi2s, rgmii2, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(33, mi2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(34, nand, pdm, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(35, nand, pdm, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(36, nand, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(37, nand, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(38, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(39, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(40, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(41, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(42, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(43, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(44, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(45, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(46, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(47, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(48, pcie2_rst, spdif, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(49, pcie2_pwren_n, pcie2_pwren, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(50, pcie2_clk_req, pcie2_pwrflt, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(51, gsbi1, rgmii2, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(52, gsbi1, rgmii2, pdm, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(53, gsbi1, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(54, gsbi1, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(55, tsif1, mi2s, gsbi6, pdm, nss_spi, NA, NA, NA, NA, NA), ++ PINGROUP(56, tsif1, mi2s, gsbi6, pdm, nss_spi, NA, NA, NA, NA, NA), ++ PINGROUP(57, tsif1, mi2s, gsbi6, nss_spi, NA, NA, NA, NA, NA, NA), ++ PINGROUP(58, tsif1, mi2s, gsbi6, pdm, nss_spi, NA, NA, NA, NA, NA), ++ PINGROUP(59, tsif2, rgmii2, pdm, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(60, tsif2, rgmii2, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(61, tsif2, rgmii2, gsbi5_spi_cs1, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(62, tsif2, rgmii2, gsbi5_spi_cs2, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(63, pcie3_rst, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(64, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(65, pcie3_clk_req, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(66, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(67, usb2_hsic, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(68, usb2_hsic, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ SDC_PINGROUP(sdc3_clk, 0x204a, 14, 6), ++ SDC_PINGROUP(sdc3_cmd, 0x204a, 11, 3), ++ SDC_PINGROUP(sdc3_data, 0x204a, 9, 0), ++}; ++ ++#define NUM_GPIO_PINGROUPS 69 ++ ++static const struct msm_pinctrl_soc_data ipq8064_pinctrl = { ++ .pins = ipq8064_pins, ++ .npins = ARRAY_SIZE(ipq8064_pins), ++ .functions = ipq8064_functions, ++ .nfunctions = ARRAY_SIZE(ipq8064_functions), ++ .groups = ipq8064_groups, ++ .ngroups = ARRAY_SIZE(ipq8064_groups), ++ .ngpios = NUM_GPIO_PINGROUPS, ++}; ++ ++static int ipq8064_pinctrl_probe(struct platform_device *pdev) ++{ ++ return msm_pinctrl_probe(pdev, &ipq8064_pinctrl); ++} ++ ++static const struct of_device_id ipq8064_pinctrl_of_match[] = { ++ { .compatible = "qcom,ipq8064-pinctrl", }, ++ { }, ++}; ++ ++static struct platform_driver ipq8064_pinctrl_driver = { ++ .driver = { ++ .name = "ipq8064-pinctrl", ++ .owner = THIS_MODULE, ++ .of_match_table = ipq8064_pinctrl_of_match, ++ }, ++ .probe = ipq8064_pinctrl_probe, ++ .remove = msm_pinctrl_remove, ++}; ++ ++static int __init ipq8064_pinctrl_init(void) ++{ ++ return platform_driver_register(&ipq8064_pinctrl_driver); ++} ++arch_initcall(ipq8064_pinctrl_init); ++ ++static void __exit ipq8064_pinctrl_exit(void) ++{ ++ platform_driver_unregister(&ipq8064_pinctrl_driver); ++} ++module_exit(ipq8064_pinctrl_exit); ++ ++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); ++MODULE_DESCRIPTION("Qualcomm IPQ8064 pinctrl driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_DEVICE_TABLE(of, ipq8064_pinctrl_of_match); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0041-dt-Document-Qualcomm-IPQ8064-pinctrl-binding.patch b/target/linux/ipq806x/patches/0041-dt-Document-Qualcomm-IPQ8064-pinctrl-binding.patch new file mode 100644 index 0000000000..2a7c870556 --- /dev/null +++ b/target/linux/ipq806x/patches/0041-dt-Document-Qualcomm-IPQ8064-pinctrl-binding.patch @@ -0,0 +1,120 @@ +From 425015979d3b1600d14403be7d6d64ba1238e58d Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 14 Apr 2014 22:10:36 -0500 +Subject: [PATCH 041/182] dt: Document Qualcomm IPQ8064 pinctrl binding + +Define a new binding for the Qualcomm TLMMv2 based pin controller inside the +IPQ8064. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Reviewed-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + .../bindings/pinctrl/qcom,ipq8064-pinctrl.txt | 95 ++++++++++++++++++++ + 1 file changed, 95 insertions(+) + create mode 100644 Documentation/devicetree/bindings/pinctrl/qcom,ipq8064-pinctrl.txt + +diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,ipq8064-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,ipq8064-pinctrl.txt +new file mode 100644 +index 0000000..e0d35a4 +--- /dev/null ++++ b/Documentation/devicetree/bindings/pinctrl/qcom,ipq8064-pinctrl.txt +@@ -0,0 +1,95 @@ ++Qualcomm IPQ8064 TLMM block ++ ++Required properties: ++- compatible: "qcom,ipq8064-pinctrl" ++- reg: Should be the base address and length of the TLMM block. ++- interrupts: Should be the parent IRQ of the TLMM block. ++- interrupt-controller: Marks the device node as an interrupt controller. ++- #interrupt-cells: Should be two. ++- gpio-controller: Marks the device node as a GPIO controller. ++- #gpio-cells : Should be two. ++ The first cell is the gpio pin number and the ++ second cell is used for optional parameters. ++ ++Please refer to ../gpio/gpio.txt and ../interrupt-controller/interrupts.txt for ++a general description of GPIO and interrupt bindings. ++ ++Please refer to pinctrl-bindings.txt in this directory for details of the ++common pinctrl bindings used by client devices, including the meaning of the ++phrase "pin configuration node". ++ ++Qualcomm's pin configuration nodes act as a container for an abitrary number of ++subnodes. Each of these subnodes represents some desired configuration for a ++pin, a group, or a list of pins or groups. This configuration can include the ++mux function to select on those pin(s)/group(s), and various pin configuration ++parameters, such as pull-up, drive strength, etc. ++ ++The name of each subnode is not important; all subnodes should be enumerated ++and processed purely based on their content. ++ ++Each subnode only affects those parameters that are explicitly listed. In ++other words, a subnode that lists a mux function but no pin configuration ++parameters implies no information about any pin configuration parameters. ++Similarly, a pin subnode that describes a pullup parameter implies no ++information about e.g. the mux function. ++ ++ ++The following generic properties as defined in pinctrl-bindings.txt are valid ++to specify in a pin configuration subnode: ++ ++ pins, function, bias-disable, bias-pull-down, bias-pull,up, drive-strength, ++ output-low, output-high. ++ ++Non-empty subnodes must specify the 'pins' property. ++ ++Valid values for qcom,pins are: ++ gpio0-gpio68 ++ Supports mux, bias, and drive-strength ++ ++ sdc3_clk, sdc3_cmd, sdc3_data ++ Supports bias and drive-strength ++ ++ ++Valid values for function are: ++ mdio, mi2s, pdm, ssbi, spmi, audio_pcm, gsbi1, gsbi2, gsbi4, gsbi5, ++ gsbi5_spi_cs1, gsbi5_spi_cs2, gsbi5_spi_cs3, gsbi6, gsbi7, nss_spi, sdc1, ++ spdif, nand, tsif1, tsif2, usb_fs_n, usb_fs, usb2_hsic, rgmii2, sata, ++ pcie1_rst, pcie1_prsnt, pcie1_pwren_n, pcie1_pwren, pcie1_pwrflt, ++ pcie1_clk_req, pcie2_rst, pcie2_prsnt, pcie2_pwren_n, pcie2_pwren, ++ pcie2_pwrflt, pcie2_clk_req, pcie3_rst, pcie3_prsnt, pcie3_pwren_n, ++ pcie3_pwren, pcie3_pwrflt, pcie3_clk_req, ps_hold ++ ++Example: ++ ++ pinmux: pinctrl@800000 { ++ compatible = "qcom,ipq8064-pinctrl"; ++ reg = <0x800000 0x4000>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ interrupts = <0 32 0x4>; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&gsbi5_uart_default>; ++ ++ gsbi5_uart_default: gsbi5_uart_default { ++ mux { ++ pins = "gpio18", "gpio19"; ++ function = "gsbi5"; ++ }; ++ ++ tx { ++ pins = "gpio18"; ++ drive-strength = <4>; ++ bias-disable; ++ }; ++ ++ rx { ++ pins = "gpio19"; ++ drive-strength = <2>; ++ bias-pull-up; ++ }; ++ }; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0042-ARM-qcom-Select-PINCTRL-by-default-for-ARCH_QCOM.patch b/target/linux/ipq806x/patches/0042-ARM-qcom-Select-PINCTRL-by-default-for-ARCH_QCOM.patch new file mode 100644 index 0000000000..c1c116f0b6 --- /dev/null +++ b/target/linux/ipq806x/patches/0042-ARM-qcom-Select-PINCTRL-by-default-for-ARCH_QCOM.patch @@ -0,0 +1,29 @@ +From add2c1451495ccc4e67ced3dd12d4286500f1672 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 14 Apr 2014 22:10:37 -0500 +Subject: [PATCH 042/182] ARM: qcom: Select PINCTRL by default for ARCH_QCOM + +Add missing PINCTRL selection. This enables selection of pinctrollers for +Qualcomm processors. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Acked-by: Linus Walleij <linus.walleij@linaro.org> +--- + arch/arm/mach-qcom/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig +index a028be2..6440c11 100644 +--- a/arch/arm/mach-qcom/Kconfig ++++ b/arch/arm/mach-qcom/Kconfig +@@ -5,6 +5,7 @@ config ARCH_QCOM + select CLKSRC_OF + select GENERIC_CLOCKEVENTS + select HAVE_SMP ++ select PINCTRL + select QCOM_SCM if SMP + help + Support for Qualcomm's devicetree based systems. +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0043-pinctrl-qcom-Correct-name-for-pin-0.patch b/target/linux/ipq806x/patches/0043-pinctrl-qcom-Correct-name-for-pin-0.patch new file mode 100644 index 0000000000..71e5572b67 --- /dev/null +++ b/target/linux/ipq806x/patches/0043-pinctrl-qcom-Correct-name-for-pin-0.patch @@ -0,0 +1,28 @@ +From 4cf6d61dc3441f50d1d56bfd72280dbcd2b584a6 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Fri, 25 Apr 2014 15:41:55 -0500 +Subject: [PATCH 043/182] pinctrl: qcom: Correct name for pin 0 + +Fix copy/paste error in pinctrl_pin_desc for pin 0. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + drivers/pinctrl/pinctrl-ipq8064.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/pinctrl/pinctrl-ipq8064.c b/drivers/pinctrl/pinctrl-ipq8064.c +index 1700b49..54aba9f 100644 +--- a/drivers/pinctrl/pinctrl-ipq8064.c ++++ b/drivers/pinctrl/pinctrl-ipq8064.c +@@ -20,7 +20,7 @@ + #include "pinctrl-msm.h" + + static const struct pinctrl_pin_desc ipq8064_pins[] = { +- PINCTRL_PIN(0, "GPIO_1"), ++ PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0044-dmaengine-qcom_bam_dma-Add-device-tree-binding.patch b/target/linux/ipq806x/patches/0044-dmaengine-qcom_bam_dma-Add-device-tree-binding.patch new file mode 100644 index 0000000000..148959a9bc --- /dev/null +++ b/target/linux/ipq806x/patches/0044-dmaengine-qcom_bam_dma-Add-device-tree-binding.patch @@ -0,0 +1,65 @@ +From b5e19b657e352d565c5ddeae5f6dfd542de9d7e5 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 10 Mar 2014 16:40:19 -0500 +Subject: [PATCH 044/182] dmaengine: qcom_bam_dma: Add device tree binding + +Add device tree binding support for the QCOM BAM DMA driver. + +Acked-by: Kumar Gala <galak@codeaurora.org> +Signed-off-by: Andy Gross <agross@codeaurora.org> +Signed-off-by: Vinod Koul <vinod.koul@intel.com> +--- + .../devicetree/bindings/dma/qcom_bam_dma.txt | 41 ++++++++++++++++++++ + 1 file changed, 41 insertions(+) + create mode 100644 Documentation/devicetree/bindings/dma/qcom_bam_dma.txt + +diff --git a/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt b/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt +new file mode 100644 +index 0000000..d75a9d7 +--- /dev/null ++++ b/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt +@@ -0,0 +1,41 @@ ++QCOM BAM DMA controller ++ ++Required properties: ++- compatible: must contain "qcom,bam-v1.4.0" for MSM8974 ++- reg: Address range for DMA registers ++- interrupts: Should contain the one interrupt shared by all channels ++- #dma-cells: must be <1>, the cell in the dmas property of the client device ++ represents the channel number ++- clocks: required clock ++- clock-names: must contain "bam_clk" entry ++- qcom,ee : indicates the active Execution Environment identifier (0-7) used in ++ the secure world. ++ ++Example: ++ ++ uart-bam: dma@f9984000 = { ++ compatible = "qcom,bam-v1.4.0"; ++ reg = <0xf9984000 0x15000>; ++ interrupts = <0 94 0>; ++ clocks = <&gcc GCC_BAM_DMA_AHB_CLK>; ++ clock-names = "bam_clk"; ++ #dma-cells = <1>; ++ qcom,ee = <0>; ++ }; ++ ++DMA clients must use the format described in the dma.txt file, using a two cell ++specifier for each channel. ++ ++Example: ++ serial@f991e000 { ++ compatible = "qcom,msm-uart"; ++ reg = <0xf991e000 0x1000> ++ <0xf9944000 0x19000>; ++ interrupts = <0 108 0>; ++ clocks = <&gcc GCC_BLSP1_UART2_APPS_CLK>, ++ <&gcc GCC_BLSP1_AHB_CLK>; ++ clock-names = "core", "iface"; ++ ++ dmas = <&uart-bam 0>, <&uart-bam 1>; ++ dma-names = "rx", "tx"; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0045-dmaengine-add-Qualcomm-BAM-dma-driver.patch b/target/linux/ipq806x/patches/0045-dmaengine-add-Qualcomm-BAM-dma-driver.patch new file mode 100644 index 0000000000..6dcba78ce5 --- /dev/null +++ b/target/linux/ipq806x/patches/0045-dmaengine-add-Qualcomm-BAM-dma-driver.patch @@ -0,0 +1,1174 @@ +From 23dd43da9c885789b3d5aceed3e401345f8e8106 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Sat, 29 Mar 2014 18:53:16 +0530 +Subject: [PATCH 045/182] dmaengine: add Qualcomm BAM dma driver + +Add the DMA engine driver for the QCOM Bus Access Manager (BAM) DMA controller +found in the MSM 8x74 platforms. + +Each BAM DMA device is associated with a specific on-chip peripheral. Each +channel provides a uni-directional data transfer engine that is capable of +transferring data between the peripheral and system memory (System mode), or +between two peripherals (BAM2BAM). + +The initial release of this driver only supports slave transfers between +peripherals and system memory. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Tested-by: Stanimir Varbanov <svarbanov@mm-sol.com> +Signed-off-by: Vinod Koul <vinod.koul@intel.com> +--- + drivers/dma/Kconfig | 9 + + drivers/dma/Makefile | 2 + + drivers/dma/qcom_bam_dma.c | 1111 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 1122 insertions(+) + create mode 100644 drivers/dma/qcom_bam_dma.c + +diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig +index 605b016..f87cef9 100644 +--- a/drivers/dma/Kconfig ++++ b/drivers/dma/Kconfig +@@ -401,4 +401,13 @@ config DMATEST + config DMA_ENGINE_RAID + bool + ++config QCOM_BAM_DMA ++ tristate "QCOM BAM DMA support" ++ depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM) ++ select DMA_ENGINE ++ select DMA_VIRTUAL_CHANNELS ++ ---help--- ++ Enable support for the QCOM BAM DMA controller. This controller ++ provides DMA capabilities for a variety of on-chip devices. ++ + endif +diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile +index a029d0f4..5150c82 100644 +--- a/drivers/dma/Makefile ++++ b/drivers/dma/Makefile +@@ -44,3 +44,5 @@ obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o + obj-$(CONFIG_TI_CPPI41) += cppi41.o + obj-$(CONFIG_K3_DMA) += k3dma.o + obj-$(CONFIG_MOXART_DMA) += moxart-dma.o ++obj-$(CONFIG_FSL_EDMA) += fsl-edma.o ++obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o +diff --git a/drivers/dma/qcom_bam_dma.c b/drivers/dma/qcom_bam_dma.c +new file mode 100644 +index 0000000..82c9231 +--- /dev/null ++++ b/drivers/dma/qcom_bam_dma.c +@@ -0,0 +1,1111 @@ ++/* ++ * 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. ++ * ++ */ ++/* ++ * QCOM BAM DMA engine driver ++ * ++ * QCOM BAM DMA blocks are distributed amongst a number of the on-chip ++ * peripherals on the MSM 8x74. The configuration of the channels are dependent ++ * on the way they are hard wired to that specific peripheral. The peripheral ++ * device tree entries specify the configuration of each channel. ++ * ++ * The DMA controller requires the use of external memory for storage of the ++ * hardware descriptors for each channel. The descriptor FIFO is accessed as a ++ * circular buffer and operations are managed according to the offset within the ++ * FIFO. After pipe/channel reset, all of the pipe registers and internal state ++ * are back to defaults. ++ * ++ * During DMA operations, we write descriptors to the FIFO, being careful to ++ * handle wrapping and then write the last FIFO offset to that channel's ++ * P_EVNT_REG register to kick off the transaction. The P_SW_OFSTS register ++ * indicates the current FIFO offset that is being processed, so there is some ++ * indication of where the hardware is currently working. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/io.h> ++#include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/interrupt.h> ++#include <linux/dma-mapping.h> ++#include <linux/scatterlist.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/of_irq.h> ++#include <linux/of_dma.h> ++#include <linux/clk.h> ++#include <linux/dmaengine.h> ++ ++#include "dmaengine.h" ++#include "virt-dma.h" ++ ++struct bam_desc_hw { ++ u32 addr; /* Buffer physical address */ ++ u16 size; /* Buffer size in bytes */ ++ u16 flags; ++}; ++ ++#define DESC_FLAG_INT BIT(15) ++#define DESC_FLAG_EOT BIT(14) ++#define DESC_FLAG_EOB BIT(13) ++ ++struct bam_async_desc { ++ struct virt_dma_desc vd; ++ ++ u32 num_desc; ++ u32 xfer_len; ++ struct bam_desc_hw *curr_desc; ++ ++ enum dma_transfer_direction dir; ++ size_t length; ++ struct bam_desc_hw desc[0]; ++}; ++ ++#define BAM_CTRL 0x0000 ++#define BAM_REVISION 0x0004 ++#define BAM_SW_REVISION 0x0080 ++#define BAM_NUM_PIPES 0x003C ++#define BAM_TIMER 0x0040 ++#define BAM_TIMER_CTRL 0x0044 ++#define BAM_DESC_CNT_TRSHLD 0x0008 ++#define BAM_IRQ_SRCS 0x000C ++#define BAM_IRQ_SRCS_MSK 0x0010 ++#define BAM_IRQ_SRCS_UNMASKED 0x0030 ++#define BAM_IRQ_STTS 0x0014 ++#define BAM_IRQ_CLR 0x0018 ++#define BAM_IRQ_EN 0x001C ++#define BAM_CNFG_BITS 0x007C ++#define BAM_IRQ_SRCS_EE(ee) (0x0800 + ((ee) * 0x80)) ++#define BAM_IRQ_SRCS_MSK_EE(ee) (0x0804 + ((ee) * 0x80)) ++#define BAM_P_CTRL(pipe) (0x1000 + ((pipe) * 0x1000)) ++#define BAM_P_RST(pipe) (0x1004 + ((pipe) * 0x1000)) ++#define BAM_P_HALT(pipe) (0x1008 + ((pipe) * 0x1000)) ++#define BAM_P_IRQ_STTS(pipe) (0x1010 + ((pipe) * 0x1000)) ++#define BAM_P_IRQ_CLR(pipe) (0x1014 + ((pipe) * 0x1000)) ++#define BAM_P_IRQ_EN(pipe) (0x1018 + ((pipe) * 0x1000)) ++#define BAM_P_EVNT_DEST_ADDR(pipe) (0x182C + ((pipe) * 0x1000)) ++#define BAM_P_EVNT_REG(pipe) (0x1818 + ((pipe) * 0x1000)) ++#define BAM_P_SW_OFSTS(pipe) (0x1800 + ((pipe) * 0x1000)) ++#define BAM_P_DATA_FIFO_ADDR(pipe) (0x1824 + ((pipe) * 0x1000)) ++#define BAM_P_DESC_FIFO_ADDR(pipe) (0x181C + ((pipe) * 0x1000)) ++#define BAM_P_EVNT_TRSHLD(pipe) (0x1828 + ((pipe) * 0x1000)) ++#define BAM_P_FIFO_SIZES(pipe) (0x1820 + ((pipe) * 0x1000)) ++ ++/* BAM CTRL */ ++#define BAM_SW_RST BIT(0) ++#define BAM_EN BIT(1) ++#define BAM_EN_ACCUM BIT(4) ++#define BAM_TESTBUS_SEL_SHIFT 5 ++#define BAM_TESTBUS_SEL_MASK 0x3F ++#define BAM_DESC_CACHE_SEL_SHIFT 13 ++#define BAM_DESC_CACHE_SEL_MASK 0x3 ++#define BAM_CACHED_DESC_STORE BIT(15) ++#define IBC_DISABLE BIT(16) ++ ++/* BAM REVISION */ ++#define REVISION_SHIFT 0 ++#define REVISION_MASK 0xFF ++#define NUM_EES_SHIFT 8 ++#define NUM_EES_MASK 0xF ++#define CE_BUFFER_SIZE BIT(13) ++#define AXI_ACTIVE BIT(14) ++#define USE_VMIDMT BIT(15) ++#define SECURED BIT(16) ++#define BAM_HAS_NO_BYPASS BIT(17) ++#define HIGH_FREQUENCY_BAM BIT(18) ++#define INACTIV_TMRS_EXST BIT(19) ++#define NUM_INACTIV_TMRS BIT(20) ++#define DESC_CACHE_DEPTH_SHIFT 21 ++#define DESC_CACHE_DEPTH_1 (0 << DESC_CACHE_DEPTH_SHIFT) ++#define DESC_CACHE_DEPTH_2 (1 << DESC_CACHE_DEPTH_SHIFT) ++#define DESC_CACHE_DEPTH_3 (2 << DESC_CACHE_DEPTH_SHIFT) ++#define DESC_CACHE_DEPTH_4 (3 << DESC_CACHE_DEPTH_SHIFT) ++#define CMD_DESC_EN BIT(23) ++#define INACTIV_TMR_BASE_SHIFT 24 ++#define INACTIV_TMR_BASE_MASK 0xFF ++ ++/* BAM NUM PIPES */ ++#define BAM_NUM_PIPES_SHIFT 0 ++#define BAM_NUM_PIPES_MASK 0xFF ++#define PERIPH_NON_PIPE_GRP_SHIFT 16 ++#define PERIPH_NON_PIP_GRP_MASK 0xFF ++#define BAM_NON_PIPE_GRP_SHIFT 24 ++#define BAM_NON_PIPE_GRP_MASK 0xFF ++ ++/* BAM CNFG BITS */ ++#define BAM_PIPE_CNFG BIT(2) ++#define BAM_FULL_PIPE BIT(11) ++#define BAM_NO_EXT_P_RST BIT(12) ++#define BAM_IBC_DISABLE BIT(13) ++#define BAM_SB_CLK_REQ BIT(14) ++#define BAM_PSM_CSW_REQ BIT(15) ++#define BAM_PSM_P_RES BIT(16) ++#define BAM_AU_P_RES BIT(17) ++#define BAM_SI_P_RES BIT(18) ++#define BAM_WB_P_RES BIT(19) ++#define BAM_WB_BLK_CSW BIT(20) ++#define BAM_WB_CSW_ACK_IDL BIT(21) ++#define BAM_WB_RETR_SVPNT BIT(22) ++#define BAM_WB_DSC_AVL_P_RST BIT(23) ++#define BAM_REG_P_EN BIT(24) ++#define BAM_PSM_P_HD_DATA BIT(25) ++#define BAM_AU_ACCUMED BIT(26) ++#define BAM_CMD_ENABLE BIT(27) ++ ++#define BAM_CNFG_BITS_DEFAULT (BAM_PIPE_CNFG | \ ++ BAM_NO_EXT_P_RST | \ ++ BAM_IBC_DISABLE | \ ++ BAM_SB_CLK_REQ | \ ++ BAM_PSM_CSW_REQ | \ ++ BAM_PSM_P_RES | \ ++ BAM_AU_P_RES | \ ++ BAM_SI_P_RES | \ ++ BAM_WB_P_RES | \ ++ BAM_WB_BLK_CSW | \ ++ BAM_WB_CSW_ACK_IDL | \ ++ BAM_WB_RETR_SVPNT | \ ++ BAM_WB_DSC_AVL_P_RST | \ ++ BAM_REG_P_EN | \ ++ BAM_PSM_P_HD_DATA | \ ++ BAM_AU_ACCUMED | \ ++ BAM_CMD_ENABLE) ++ ++/* PIPE CTRL */ ++#define P_EN BIT(1) ++#define P_DIRECTION BIT(3) ++#define P_SYS_STRM BIT(4) ++#define P_SYS_MODE BIT(5) ++#define P_AUTO_EOB BIT(6) ++#define P_AUTO_EOB_SEL_SHIFT 7 ++#define P_AUTO_EOB_SEL_512 (0 << P_AUTO_EOB_SEL_SHIFT) ++#define P_AUTO_EOB_SEL_256 (1 << P_AUTO_EOB_SEL_SHIFT) ++#define P_AUTO_EOB_SEL_128 (2 << P_AUTO_EOB_SEL_SHIFT) ++#define P_AUTO_EOB_SEL_64 (3 << P_AUTO_EOB_SEL_SHIFT) ++#define P_PREFETCH_LIMIT_SHIFT 9 ++#define P_PREFETCH_LIMIT_32 (0 << P_PREFETCH_LIMIT_SHIFT) ++#define P_PREFETCH_LIMIT_16 (1 << P_PREFETCH_LIMIT_SHIFT) ++#define P_PREFETCH_LIMIT_4 (2 << P_PREFETCH_LIMIT_SHIFT) ++#define P_WRITE_NWD BIT(11) ++#define P_LOCK_GROUP_SHIFT 16 ++#define P_LOCK_GROUP_MASK 0x1F ++ ++/* BAM_DESC_CNT_TRSHLD */ ++#define CNT_TRSHLD 0xffff ++#define DEFAULT_CNT_THRSHLD 0x4 ++ ++/* BAM_IRQ_SRCS */ ++#define BAM_IRQ BIT(31) ++#define P_IRQ 0x7fffffff ++ ++/* BAM_IRQ_SRCS_MSK */ ++#define BAM_IRQ_MSK BAM_IRQ ++#define P_IRQ_MSK P_IRQ ++ ++/* BAM_IRQ_STTS */ ++#define BAM_TIMER_IRQ BIT(4) ++#define BAM_EMPTY_IRQ BIT(3) ++#define BAM_ERROR_IRQ BIT(2) ++#define BAM_HRESP_ERR_IRQ BIT(1) ++ ++/* BAM_IRQ_CLR */ ++#define BAM_TIMER_CLR BIT(4) ++#define BAM_EMPTY_CLR BIT(3) ++#define BAM_ERROR_CLR BIT(2) ++#define BAM_HRESP_ERR_CLR BIT(1) ++ ++/* BAM_IRQ_EN */ ++#define BAM_TIMER_EN BIT(4) ++#define BAM_EMPTY_EN BIT(3) ++#define BAM_ERROR_EN BIT(2) ++#define BAM_HRESP_ERR_EN BIT(1) ++ ++/* BAM_P_IRQ_EN */ ++#define P_PRCSD_DESC_EN BIT(0) ++#define P_TIMER_EN BIT(1) ++#define P_WAKE_EN BIT(2) ++#define P_OUT_OF_DESC_EN BIT(3) ++#define P_ERR_EN BIT(4) ++#define P_TRNSFR_END_EN BIT(5) ++#define P_DEFAULT_IRQS_EN (P_PRCSD_DESC_EN | P_ERR_EN | P_TRNSFR_END_EN) ++ ++/* BAM_P_SW_OFSTS */ ++#define P_SW_OFSTS_MASK 0xffff ++ ++#define BAM_DESC_FIFO_SIZE SZ_32K ++#define MAX_DESCRIPTORS (BAM_DESC_FIFO_SIZE / sizeof(struct bam_desc_hw) - 1) ++#define BAM_MAX_DATA_SIZE (SZ_32K - 8) ++ ++struct bam_chan { ++ struct virt_dma_chan vc; ++ ++ struct bam_device *bdev; ++ ++ /* configuration from device tree */ ++ u32 id; ++ ++ struct bam_async_desc *curr_txd; /* current running dma */ ++ ++ /* runtime configuration */ ++ struct dma_slave_config slave; ++ ++ /* fifo storage */ ++ struct bam_desc_hw *fifo_virt; ++ dma_addr_t fifo_phys; ++ ++ /* fifo markers */ ++ unsigned short head; /* start of active descriptor entries */ ++ unsigned short tail; /* end of active descriptor entries */ ++ ++ unsigned int initialized; /* is the channel hw initialized? */ ++ unsigned int paused; /* is the channel paused? */ ++ unsigned int reconfigure; /* new slave config? */ ++ ++ struct list_head node; ++}; ++ ++static inline struct bam_chan *to_bam_chan(struct dma_chan *common) ++{ ++ return container_of(common, struct bam_chan, vc.chan); ++} ++ ++struct bam_device { ++ void __iomem *regs; ++ struct device *dev; ++ struct dma_device common; ++ struct device_dma_parameters dma_parms; ++ struct bam_chan *channels; ++ u32 num_channels; ++ ++ /* execution environment ID, from DT */ ++ u32 ee; ++ ++ struct clk *bamclk; ++ int irq; ++ ++ /* dma start transaction tasklet */ ++ struct tasklet_struct task; ++}; ++ ++/** ++ * bam_reset_channel - Reset individual BAM DMA channel ++ * @bchan: bam channel ++ * ++ * This function resets a specific BAM channel ++ */ ++static void bam_reset_channel(struct bam_chan *bchan) ++{ ++ struct bam_device *bdev = bchan->bdev; ++ ++ lockdep_assert_held(&bchan->vc.lock); ++ ++ /* reset channel */ ++ writel_relaxed(1, bdev->regs + BAM_P_RST(bchan->id)); ++ writel_relaxed(0, bdev->regs + BAM_P_RST(bchan->id)); ++ ++ /* don't allow cpu to reorder BAM register accesses done after this */ ++ wmb(); ++ ++ /* make sure hw is initialized when channel is used the first time */ ++ bchan->initialized = 0; ++} ++ ++/** ++ * bam_chan_init_hw - Initialize channel hardware ++ * @bchan: bam channel ++ * ++ * This function resets and initializes the BAM channel ++ */ ++static void bam_chan_init_hw(struct bam_chan *bchan, ++ enum dma_transfer_direction dir) ++{ ++ struct bam_device *bdev = bchan->bdev; ++ u32 val; ++ ++ /* Reset the channel to clear internal state of the FIFO */ ++ bam_reset_channel(bchan); ++ ++ /* ++ * write out 8 byte aligned address. We have enough space for this ++ * because we allocated 1 more descriptor (8 bytes) than we can use ++ */ ++ writel_relaxed(ALIGN(bchan->fifo_phys, sizeof(struct bam_desc_hw)), ++ bdev->regs + BAM_P_DESC_FIFO_ADDR(bchan->id)); ++ writel_relaxed(BAM_DESC_FIFO_SIZE, bdev->regs + ++ BAM_P_FIFO_SIZES(bchan->id)); ++ ++ /* enable the per pipe interrupts, enable EOT, ERR, and INT irqs */ ++ writel_relaxed(P_DEFAULT_IRQS_EN, bdev->regs + BAM_P_IRQ_EN(bchan->id)); ++ ++ /* unmask the specific pipe and EE combo */ ++ val = readl_relaxed(bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee)); ++ val |= BIT(bchan->id); ++ writel_relaxed(val, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee)); ++ ++ /* don't allow cpu to reorder the channel enable done below */ ++ wmb(); ++ ++ /* set fixed direction and mode, then enable channel */ ++ val = P_EN | P_SYS_MODE; ++ if (dir == DMA_DEV_TO_MEM) ++ val |= P_DIRECTION; ++ ++ writel_relaxed(val, bdev->regs + BAM_P_CTRL(bchan->id)); ++ ++ bchan->initialized = 1; ++ ++ /* init FIFO pointers */ ++ bchan->head = 0; ++ bchan->tail = 0; ++} ++ ++/** ++ * bam_alloc_chan - Allocate channel resources for DMA channel. ++ * @chan: specified channel ++ * ++ * This function allocates the FIFO descriptor memory ++ */ ++static int bam_alloc_chan(struct dma_chan *chan) ++{ ++ struct bam_chan *bchan = to_bam_chan(chan); ++ struct bam_device *bdev = bchan->bdev; ++ ++ if (bchan->fifo_virt) ++ return 0; ++ ++ /* allocate FIFO descriptor space, but only if necessary */ ++ bchan->fifo_virt = dma_alloc_writecombine(bdev->dev, BAM_DESC_FIFO_SIZE, ++ &bchan->fifo_phys, GFP_KERNEL); ++ ++ if (!bchan->fifo_virt) { ++ dev_err(bdev->dev, "Failed to allocate desc fifo\n"); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++/** ++ * bam_free_chan - Frees dma resources associated with specific channel ++ * @chan: specified channel ++ * ++ * Free the allocated fifo descriptor memory and channel resources ++ * ++ */ ++static void bam_free_chan(struct dma_chan *chan) ++{ ++ struct bam_chan *bchan = to_bam_chan(chan); ++ struct bam_device *bdev = bchan->bdev; ++ u32 val; ++ unsigned long flags; ++ ++ vchan_free_chan_resources(to_virt_chan(chan)); ++ ++ if (bchan->curr_txd) { ++ dev_err(bchan->bdev->dev, "Cannot free busy channel\n"); ++ return; ++ } ++ ++ spin_lock_irqsave(&bchan->vc.lock, flags); ++ bam_reset_channel(bchan); ++ spin_unlock_irqrestore(&bchan->vc.lock, flags); ++ ++ dma_free_writecombine(bdev->dev, BAM_DESC_FIFO_SIZE, bchan->fifo_virt, ++ bchan->fifo_phys); ++ bchan->fifo_virt = NULL; ++ ++ /* mask irq for pipe/channel */ ++ val = readl_relaxed(bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee)); ++ val &= ~BIT(bchan->id); ++ writel_relaxed(val, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee)); ++ ++ /* disable irq */ ++ writel_relaxed(0, bdev->regs + BAM_P_IRQ_EN(bchan->id)); ++} ++ ++/** ++ * bam_slave_config - set slave configuration for channel ++ * @chan: dma channel ++ * @cfg: slave configuration ++ * ++ * Sets slave configuration for channel ++ * ++ */ ++static void bam_slave_config(struct bam_chan *bchan, ++ struct dma_slave_config *cfg) ++{ ++ memcpy(&bchan->slave, cfg, sizeof(*cfg)); ++ bchan->reconfigure = 1; ++} ++ ++/** ++ * bam_prep_slave_sg - Prep slave sg transaction ++ * ++ * @chan: dma channel ++ * @sgl: scatter gather list ++ * @sg_len: length of sg ++ * @direction: DMA transfer direction ++ * @flags: DMA flags ++ * @context: transfer context (unused) ++ */ ++static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan, ++ struct scatterlist *sgl, unsigned int sg_len, ++ enum dma_transfer_direction direction, unsigned long flags, ++ void *context) ++{ ++ struct bam_chan *bchan = to_bam_chan(chan); ++ struct bam_device *bdev = bchan->bdev; ++ struct bam_async_desc *async_desc; ++ struct scatterlist *sg; ++ u32 i; ++ struct bam_desc_hw *desc; ++ unsigned int num_alloc = 0; ++ ++ ++ if (!is_slave_direction(direction)) { ++ dev_err(bdev->dev, "invalid dma direction\n"); ++ return NULL; ++ } ++ ++ /* calculate number of required entries */ ++ for_each_sg(sgl, sg, sg_len, i) ++ num_alloc += DIV_ROUND_UP(sg_dma_len(sg), BAM_MAX_DATA_SIZE); ++ ++ /* allocate enough room to accomodate the number of entries */ ++ async_desc = kzalloc(sizeof(*async_desc) + ++ (num_alloc * sizeof(struct bam_desc_hw)), GFP_NOWAIT); ++ ++ if (!async_desc) ++ goto err_out; ++ ++ async_desc->num_desc = num_alloc; ++ async_desc->curr_desc = async_desc->desc; ++ async_desc->dir = direction; ++ ++ /* fill in temporary descriptors */ ++ desc = async_desc->desc; ++ for_each_sg(sgl, sg, sg_len, i) { ++ unsigned int remainder = sg_dma_len(sg); ++ unsigned int curr_offset = 0; ++ ++ do { ++ desc->addr = sg_dma_address(sg) + curr_offset; ++ ++ if (remainder > BAM_MAX_DATA_SIZE) { ++ desc->size = BAM_MAX_DATA_SIZE; ++ remainder -= BAM_MAX_DATA_SIZE; ++ curr_offset += BAM_MAX_DATA_SIZE; ++ } else { ++ desc->size = remainder; ++ remainder = 0; ++ } ++ ++ async_desc->length += desc->size; ++ desc++; ++ } while (remainder > 0); ++ } ++ ++ return vchan_tx_prep(&bchan->vc, &async_desc->vd, flags); ++ ++err_out: ++ kfree(async_desc); ++ return NULL; ++} ++ ++/** ++ * bam_dma_terminate_all - terminate all transactions on a channel ++ * @bchan: bam dma channel ++ * ++ * Dequeues and frees all transactions ++ * No callbacks are done ++ * ++ */ ++static void bam_dma_terminate_all(struct bam_chan *bchan) ++{ ++ unsigned long flag; ++ LIST_HEAD(head); ++ ++ /* remove all transactions, including active transaction */ ++ spin_lock_irqsave(&bchan->vc.lock, flag); ++ if (bchan->curr_txd) { ++ list_add(&bchan->curr_txd->vd.node, &bchan->vc.desc_issued); ++ bchan->curr_txd = NULL; ++ } ++ ++ vchan_get_all_descriptors(&bchan->vc, &head); ++ spin_unlock_irqrestore(&bchan->vc.lock, flag); ++ ++ vchan_dma_desc_free_list(&bchan->vc, &head); ++} ++ ++/** ++ * bam_control - DMA device control ++ * @chan: dma channel ++ * @cmd: control cmd ++ * @arg: cmd argument ++ * ++ * Perform DMA control command ++ * ++ */ ++static int bam_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, ++ unsigned long arg) ++{ ++ struct bam_chan *bchan = to_bam_chan(chan); ++ struct bam_device *bdev = bchan->bdev; ++ int ret = 0; ++ unsigned long flag; ++ ++ switch (cmd) { ++ case DMA_PAUSE: ++ spin_lock_irqsave(&bchan->vc.lock, flag); ++ writel_relaxed(1, bdev->regs + BAM_P_HALT(bchan->id)); ++ bchan->paused = 1; ++ spin_unlock_irqrestore(&bchan->vc.lock, flag); ++ break; ++ ++ case DMA_RESUME: ++ spin_lock_irqsave(&bchan->vc.lock, flag); ++ writel_relaxed(0, bdev->regs + BAM_P_HALT(bchan->id)); ++ bchan->paused = 0; ++ spin_unlock_irqrestore(&bchan->vc.lock, flag); ++ break; ++ ++ case DMA_TERMINATE_ALL: ++ bam_dma_terminate_all(bchan); ++ break; ++ ++ case DMA_SLAVE_CONFIG: ++ spin_lock_irqsave(&bchan->vc.lock, flag); ++ bam_slave_config(bchan, (struct dma_slave_config *)arg); ++ spin_unlock_irqrestore(&bchan->vc.lock, flag); ++ break; ++ ++ default: ++ ret = -ENXIO; ++ break; ++ } ++ ++ return ret; ++} ++ ++/** ++ * process_channel_irqs - processes the channel interrupts ++ * @bdev: bam controller ++ * ++ * This function processes the channel interrupts ++ * ++ */ ++static u32 process_channel_irqs(struct bam_device *bdev) ++{ ++ u32 i, srcs, pipe_stts; ++ unsigned long flags; ++ struct bam_async_desc *async_desc; ++ ++ srcs = readl_relaxed(bdev->regs + BAM_IRQ_SRCS_EE(bdev->ee)); ++ ++ /* return early if no pipe/channel interrupts are present */ ++ if (!(srcs & P_IRQ)) ++ return srcs; ++ ++ for (i = 0; i < bdev->num_channels; i++) { ++ struct bam_chan *bchan = &bdev->channels[i]; ++ ++ if (!(srcs & BIT(i))) ++ continue; ++ ++ /* clear pipe irq */ ++ pipe_stts = readl_relaxed(bdev->regs + ++ BAM_P_IRQ_STTS(i)); ++ ++ writel_relaxed(pipe_stts, bdev->regs + ++ BAM_P_IRQ_CLR(i)); ++ ++ spin_lock_irqsave(&bchan->vc.lock, flags); ++ async_desc = bchan->curr_txd; ++ ++ if (async_desc) { ++ async_desc->num_desc -= async_desc->xfer_len; ++ async_desc->curr_desc += async_desc->xfer_len; ++ bchan->curr_txd = NULL; ++ ++ /* manage FIFO */ ++ bchan->head += async_desc->xfer_len; ++ bchan->head %= MAX_DESCRIPTORS; ++ ++ /* ++ * if complete, process cookie. Otherwise ++ * push back to front of desc_issued so that ++ * it gets restarted by the tasklet ++ */ ++ if (!async_desc->num_desc) ++ vchan_cookie_complete(&async_desc->vd); ++ else ++ list_add(&async_desc->vd.node, ++ &bchan->vc.desc_issued); ++ } ++ ++ spin_unlock_irqrestore(&bchan->vc.lock, flags); ++ } ++ ++ return srcs; ++} ++ ++/** ++ * bam_dma_irq - irq handler for bam controller ++ * @irq: IRQ of interrupt ++ * @data: callback data ++ * ++ * IRQ handler for the bam controller ++ */ ++static irqreturn_t bam_dma_irq(int irq, void *data) ++{ ++ struct bam_device *bdev = data; ++ u32 clr_mask = 0, srcs = 0; ++ ++ srcs |= process_channel_irqs(bdev); ++ ++ /* kick off tasklet to start next dma transfer */ ++ if (srcs & P_IRQ) ++ tasklet_schedule(&bdev->task); ++ ++ if (srcs & BAM_IRQ) ++ clr_mask = readl_relaxed(bdev->regs + BAM_IRQ_STTS); ++ ++ /* don't allow reorder of the various accesses to the BAM registers */ ++ mb(); ++ ++ writel_relaxed(clr_mask, bdev->regs + BAM_IRQ_CLR); ++ ++ return IRQ_HANDLED; ++} ++ ++/** ++ * bam_tx_status - returns status of transaction ++ * @chan: dma channel ++ * @cookie: transaction cookie ++ * @txstate: DMA transaction state ++ * ++ * Return status of dma transaction ++ */ ++static enum dma_status bam_tx_status(struct dma_chan *chan, dma_cookie_t cookie, ++ struct dma_tx_state *txstate) ++{ ++ struct bam_chan *bchan = to_bam_chan(chan); ++ struct virt_dma_desc *vd; ++ int ret; ++ size_t residue = 0; ++ unsigned int i; ++ unsigned long flags; ++ ++ ret = dma_cookie_status(chan, cookie, txstate); ++ if (ret == DMA_COMPLETE) ++ return ret; ++ ++ if (!txstate) ++ return bchan->paused ? DMA_PAUSED : ret; ++ ++ spin_lock_irqsave(&bchan->vc.lock, flags); ++ vd = vchan_find_desc(&bchan->vc, cookie); ++ if (vd) ++ residue = container_of(vd, struct bam_async_desc, vd)->length; ++ else if (bchan->curr_txd && bchan->curr_txd->vd.tx.cookie == cookie) ++ for (i = 0; i < bchan->curr_txd->num_desc; i++) ++ residue += bchan->curr_txd->curr_desc[i].size; ++ ++ spin_unlock_irqrestore(&bchan->vc.lock, flags); ++ ++ dma_set_residue(txstate, residue); ++ ++ if (ret == DMA_IN_PROGRESS && bchan->paused) ++ ret = DMA_PAUSED; ++ ++ return ret; ++} ++ ++/** ++ * bam_apply_new_config ++ * @bchan: bam dma channel ++ * @dir: DMA direction ++ */ ++static void bam_apply_new_config(struct bam_chan *bchan, ++ enum dma_transfer_direction dir) ++{ ++ struct bam_device *bdev = bchan->bdev; ++ u32 maxburst; ++ ++ if (dir == DMA_DEV_TO_MEM) ++ maxburst = bchan->slave.src_maxburst; ++ else ++ maxburst = bchan->slave.dst_maxburst; ++ ++ writel_relaxed(maxburst, bdev->regs + BAM_DESC_CNT_TRSHLD); ++ ++ bchan->reconfigure = 0; ++} ++ ++/** ++ * bam_start_dma - start next transaction ++ * @bchan - bam dma channel ++ */ ++static void bam_start_dma(struct bam_chan *bchan) ++{ ++ struct virt_dma_desc *vd = vchan_next_desc(&bchan->vc); ++ struct bam_device *bdev = bchan->bdev; ++ struct bam_async_desc *async_desc; ++ struct bam_desc_hw *desc; ++ struct bam_desc_hw *fifo = PTR_ALIGN(bchan->fifo_virt, ++ sizeof(struct bam_desc_hw)); ++ ++ lockdep_assert_held(&bchan->vc.lock); ++ ++ if (!vd) ++ return; ++ ++ list_del(&vd->node); ++ ++ async_desc = container_of(vd, struct bam_async_desc, vd); ++ bchan->curr_txd = async_desc; ++ ++ /* on first use, initialize the channel hardware */ ++ if (!bchan->initialized) ++ bam_chan_init_hw(bchan, async_desc->dir); ++ ++ /* apply new slave config changes, if necessary */ ++ if (bchan->reconfigure) ++ bam_apply_new_config(bchan, async_desc->dir); ++ ++ desc = bchan->curr_txd->curr_desc; ++ ++ if (async_desc->num_desc > MAX_DESCRIPTORS) ++ async_desc->xfer_len = MAX_DESCRIPTORS; ++ else ++ async_desc->xfer_len = async_desc->num_desc; ++ ++ /* set INT on last descriptor */ ++ desc[async_desc->xfer_len - 1].flags |= DESC_FLAG_INT; ++ ++ if (bchan->tail + async_desc->xfer_len > MAX_DESCRIPTORS) { ++ u32 partial = MAX_DESCRIPTORS - bchan->tail; ++ ++ memcpy(&fifo[bchan->tail], desc, ++ partial * sizeof(struct bam_desc_hw)); ++ memcpy(fifo, &desc[partial], (async_desc->xfer_len - partial) * ++ sizeof(struct bam_desc_hw)); ++ } else { ++ memcpy(&fifo[bchan->tail], desc, ++ async_desc->xfer_len * sizeof(struct bam_desc_hw)); ++ } ++ ++ bchan->tail += async_desc->xfer_len; ++ bchan->tail %= MAX_DESCRIPTORS; ++ ++ /* ensure descriptor writes and dma start not reordered */ ++ wmb(); ++ writel_relaxed(bchan->tail * sizeof(struct bam_desc_hw), ++ bdev->regs + BAM_P_EVNT_REG(bchan->id)); ++} ++ ++/** ++ * dma_tasklet - DMA IRQ tasklet ++ * @data: tasklet argument (bam controller structure) ++ * ++ * Sets up next DMA operation and then processes all completed transactions ++ */ ++static void dma_tasklet(unsigned long data) ++{ ++ struct bam_device *bdev = (struct bam_device *)data; ++ struct bam_chan *bchan; ++ unsigned long flags; ++ unsigned int i; ++ ++ /* go through the channels and kick off transactions */ ++ for (i = 0; i < bdev->num_channels; i++) { ++ bchan = &bdev->channels[i]; ++ spin_lock_irqsave(&bchan->vc.lock, flags); ++ ++ if (!list_empty(&bchan->vc.desc_issued) && !bchan->curr_txd) ++ bam_start_dma(bchan); ++ spin_unlock_irqrestore(&bchan->vc.lock, flags); ++ } ++} ++ ++/** ++ * bam_issue_pending - starts pending transactions ++ * @chan: dma channel ++ * ++ * Calls tasklet directly which in turn starts any pending transactions ++ */ ++static void bam_issue_pending(struct dma_chan *chan) ++{ ++ struct bam_chan *bchan = to_bam_chan(chan); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&bchan->vc.lock, flags); ++ ++ /* if work pending and idle, start a transaction */ ++ if (vchan_issue_pending(&bchan->vc) && !bchan->curr_txd) ++ bam_start_dma(bchan); ++ ++ spin_unlock_irqrestore(&bchan->vc.lock, flags); ++} ++ ++/** ++ * bam_dma_free_desc - free descriptor memory ++ * @vd: virtual descriptor ++ * ++ */ ++static void bam_dma_free_desc(struct virt_dma_desc *vd) ++{ ++ struct bam_async_desc *async_desc = container_of(vd, ++ struct bam_async_desc, vd); ++ ++ kfree(async_desc); ++} ++ ++static struct dma_chan *bam_dma_xlate(struct of_phandle_args *dma_spec, ++ struct of_dma *of) ++{ ++ struct bam_device *bdev = container_of(of->of_dma_data, ++ struct bam_device, common); ++ unsigned int request; ++ ++ if (dma_spec->args_count != 1) ++ return NULL; ++ ++ request = dma_spec->args[0]; ++ if (request >= bdev->num_channels) ++ return NULL; ++ ++ return dma_get_slave_channel(&(bdev->channels[request].vc.chan)); ++} ++ ++/** ++ * bam_init ++ * @bdev: bam device ++ * ++ * Initialization helper for global bam registers ++ */ ++static int bam_init(struct bam_device *bdev) ++{ ++ u32 val; ++ ++ /* read revision and configuration information */ ++ val = readl_relaxed(bdev->regs + BAM_REVISION) >> NUM_EES_SHIFT; ++ val &= NUM_EES_MASK; ++ ++ /* check that configured EE is within range */ ++ if (bdev->ee >= val) ++ return -EINVAL; ++ ++ val = readl_relaxed(bdev->regs + BAM_NUM_PIPES); ++ bdev->num_channels = val & BAM_NUM_PIPES_MASK; ++ ++ /* s/w reset bam */ ++ /* after reset all pipes are disabled and idle */ ++ val = readl_relaxed(bdev->regs + BAM_CTRL); ++ val |= BAM_SW_RST; ++ writel_relaxed(val, bdev->regs + BAM_CTRL); ++ val &= ~BAM_SW_RST; ++ writel_relaxed(val, bdev->regs + BAM_CTRL); ++ ++ /* make sure previous stores are visible before enabling BAM */ ++ wmb(); ++ ++ /* enable bam */ ++ val |= BAM_EN; ++ writel_relaxed(val, bdev->regs + BAM_CTRL); ++ ++ /* set descriptor threshhold, start with 4 bytes */ ++ writel_relaxed(DEFAULT_CNT_THRSHLD, bdev->regs + BAM_DESC_CNT_TRSHLD); ++ ++ /* Enable default set of h/w workarounds, ie all except BAM_FULL_PIPE */ ++ writel_relaxed(BAM_CNFG_BITS_DEFAULT, bdev->regs + BAM_CNFG_BITS); ++ ++ /* enable irqs for errors */ ++ writel_relaxed(BAM_ERROR_EN | BAM_HRESP_ERR_EN, ++ bdev->regs + BAM_IRQ_EN); ++ ++ /* unmask global bam interrupt */ ++ writel_relaxed(BAM_IRQ_MSK, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee)); ++ ++ return 0; ++} ++ ++static void bam_channel_init(struct bam_device *bdev, struct bam_chan *bchan, ++ u32 index) ++{ ++ bchan->id = index; ++ bchan->bdev = bdev; ++ ++ vchan_init(&bchan->vc, &bdev->common); ++ bchan->vc.desc_free = bam_dma_free_desc; ++} ++ ++static int bam_dma_probe(struct platform_device *pdev) ++{ ++ struct bam_device *bdev; ++ struct resource *iores; ++ int ret, i; ++ ++ bdev = devm_kzalloc(&pdev->dev, sizeof(*bdev), GFP_KERNEL); ++ if (!bdev) ++ return -ENOMEM; ++ ++ bdev->dev = &pdev->dev; ++ ++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ bdev->regs = devm_ioremap_resource(&pdev->dev, iores); ++ if (IS_ERR(bdev->regs)) ++ return PTR_ERR(bdev->regs); ++ ++ bdev->irq = platform_get_irq(pdev, 0); ++ if (bdev->irq < 0) ++ return bdev->irq; ++ ++ ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &bdev->ee); ++ if (ret) { ++ dev_err(bdev->dev, "Execution environment unspecified\n"); ++ return ret; ++ } ++ ++ bdev->bamclk = devm_clk_get(bdev->dev, "bam_clk"); ++ if (IS_ERR(bdev->bamclk)) ++ return PTR_ERR(bdev->bamclk); ++ ++ ret = clk_prepare_enable(bdev->bamclk); ++ if (ret) { ++ dev_err(bdev->dev, "failed to prepare/enable clock\n"); ++ return ret; ++ } ++ ++ ret = bam_init(bdev); ++ if (ret) ++ goto err_disable_clk; ++ ++ tasklet_init(&bdev->task, dma_tasklet, (unsigned long)bdev); ++ ++ bdev->channels = devm_kcalloc(bdev->dev, bdev->num_channels, ++ sizeof(*bdev->channels), GFP_KERNEL); ++ ++ if (!bdev->channels) { ++ ret = -ENOMEM; ++ goto err_disable_clk; ++ } ++ ++ /* allocate and initialize channels */ ++ INIT_LIST_HEAD(&bdev->common.channels); ++ ++ for (i = 0; i < bdev->num_channels; i++) ++ bam_channel_init(bdev, &bdev->channels[i], i); ++ ++ ret = devm_request_irq(bdev->dev, bdev->irq, bam_dma_irq, ++ IRQF_TRIGGER_HIGH, "bam_dma", bdev); ++ if (ret) ++ goto err_disable_clk; ++ ++ /* set max dma segment size */ ++ bdev->common.dev = bdev->dev; ++ bdev->common.dev->dma_parms = &bdev->dma_parms; ++ ret = dma_set_max_seg_size(bdev->common.dev, BAM_MAX_DATA_SIZE); ++ if (ret) { ++ dev_err(bdev->dev, "cannot set maximum segment size\n"); ++ goto err_disable_clk; ++ } ++ ++ platform_set_drvdata(pdev, bdev); ++ ++ /* set capabilities */ ++ dma_cap_zero(bdev->common.cap_mask); ++ dma_cap_set(DMA_SLAVE, bdev->common.cap_mask); ++ ++ /* initialize dmaengine apis */ ++ bdev->common.device_alloc_chan_resources = bam_alloc_chan; ++ bdev->common.device_free_chan_resources = bam_free_chan; ++ bdev->common.device_prep_slave_sg = bam_prep_slave_sg; ++ bdev->common.device_control = bam_control; ++ bdev->common.device_issue_pending = bam_issue_pending; ++ bdev->common.device_tx_status = bam_tx_status; ++ bdev->common.dev = bdev->dev; ++ ++ ret = dma_async_device_register(&bdev->common); ++ if (ret) { ++ dev_err(bdev->dev, "failed to register dma async device\n"); ++ goto err_disable_clk; ++ } ++ ++ ret = of_dma_controller_register(pdev->dev.of_node, bam_dma_xlate, ++ &bdev->common); ++ if (ret) ++ goto err_unregister_dma; ++ ++ return 0; ++ ++err_unregister_dma: ++ dma_async_device_unregister(&bdev->common); ++err_disable_clk: ++ clk_disable_unprepare(bdev->bamclk); ++ return ret; ++} ++ ++static int bam_dma_remove(struct platform_device *pdev) ++{ ++ struct bam_device *bdev = platform_get_drvdata(pdev); ++ u32 i; ++ ++ of_dma_controller_free(pdev->dev.of_node); ++ dma_async_device_unregister(&bdev->common); ++ ++ /* mask all interrupts for this execution environment */ ++ writel_relaxed(0, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee)); ++ ++ devm_free_irq(bdev->dev, bdev->irq, bdev); ++ ++ for (i = 0; i < bdev->num_channels; i++) { ++ bam_dma_terminate_all(&bdev->channels[i]); ++ tasklet_kill(&bdev->channels[i].vc.task); ++ ++ dma_free_writecombine(bdev->dev, BAM_DESC_FIFO_SIZE, ++ bdev->channels[i].fifo_virt, ++ bdev->channels[i].fifo_phys); ++ } ++ ++ tasklet_kill(&bdev->task); ++ ++ clk_disable_unprepare(bdev->bamclk); ++ ++ return 0; ++} ++ ++static const struct of_device_id bam_of_match[] = { ++ { .compatible = "qcom,bam-v1.4.0", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, bam_of_match); ++ ++static struct platform_driver bam_dma_driver = { ++ .probe = bam_dma_probe, ++ .remove = bam_dma_remove, ++ .driver = { ++ .name = "bam-dma-engine", ++ .owner = THIS_MODULE, ++ .of_match_table = bam_of_match, ++ }, ++}; ++ ++module_platform_driver(bam_dma_driver); ++ ++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); ++MODULE_DESCRIPTION("QCOM BAM DMA engine driver"); ++MODULE_LICENSE("GPL v2"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0046-mmc-sdhci-msm-Qualcomm-SDHCI-binding-documentation.patch b/target/linux/ipq806x/patches/0046-mmc-sdhci-msm-Qualcomm-SDHCI-binding-documentation.patch new file mode 100644 index 0000000000..b173fcfc1d --- /dev/null +++ b/target/linux/ipq806x/patches/0046-mmc-sdhci-msm-Qualcomm-SDHCI-binding-documentation.patch @@ -0,0 +1,81 @@ +From 8a70c89b2fbb635a8d4fec302165343827aeed9f Mon Sep 17 00:00:00 2001 +From: Georgi Djakov <gdjakov@mm-sol.com> +Date: Mon, 10 Mar 2014 17:37:11 +0200 +Subject: [PATCH 046/182] mmc: sdhci-msm: Qualcomm SDHCI binding documentation + +This patch adds the device-tree binding documentation for Qualcomm +SDHCI driver. It contains the differences between the core properties +in mmc.txt and the properties used by the sdhci-msm driver. + +Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com> +Acked-by: Ulf Hansson <ulf.hansson@linaro.org> +Signed-off-by: Chris Ball <chris@printf.net> +--- + .../devicetree/bindings/mmc/sdhci-msm.txt | 55 ++++++++++++++++++++ + 1 file changed, 55 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-msm.txt + +diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +new file mode 100644 +index 0000000..81b33b5 +--- /dev/null ++++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +@@ -0,0 +1,55 @@ ++* Qualcomm SDHCI controller (sdhci-msm) ++ ++This file documents differences between the core properties in mmc.txt ++and the properties used by the sdhci-msm driver. ++ ++Required properties: ++- compatible: Should contain "qcom,sdhci-msm-v4". ++- reg: Base address and length of the register in the following order: ++ - Host controller register map (required) ++ - SD Core register map (required) ++- interrupts: Should contain an interrupt-specifiers for the interrupts: ++ - Host controller interrupt (required) ++- pinctrl-names: Should contain only one value - "default". ++- pinctrl-0: Should specify pin control groups used for this controller. ++- clocks: A list of phandle + clock-specifier pairs for the clocks listed in clock-names. ++- clock-names: Should contain the following: ++ "iface" - Main peripheral bus clock (PCLK/HCLK - AHB Bus clock) (required) ++ "core" - SDC MMC clock (MCLK) (required) ++ "bus" - SDCC bus voter clock (optional) ++ ++Example: ++ ++ sdhc_1: sdhci@f9824900 { ++ compatible = "qcom,sdhci-msm-v4"; ++ reg = <0xf9824900 0x11c>, <0xf9824000 0x800>; ++ interrupts = <0 123 0>; ++ bus-width = <8>; ++ non-removable; ++ ++ vmmc = <&pm8941_l20>; ++ vqmmc = <&pm8941_s3>; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdc1_clk &sdc1_cmd &sdc1_data>; ++ ++ clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>; ++ clock-names = "core", "iface"; ++ }; ++ ++ sdhc_2: sdhci@f98a4900 { ++ compatible = "qcom,sdhci-msm-v4"; ++ reg = <0xf98a4900 0x11c>, <0xf98a4000 0x800>; ++ interrupts = <0 125 0>; ++ bus-width = <4>; ++ cd-gpios = <&msmgpio 62 0x1>; ++ ++ vmmc = <&pm8941_l21>; ++ vqmmc = <&pm8941_l13>; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdc2_clk &sdc2_cmd &sdc2_data>; ++ ++ clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>; ++ clock-names = "core", "iface"; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0047-mmc-sdhci-msm-Initial-support-for-Qualcomm-chipsets.patch b/target/linux/ipq806x/patches/0047-mmc-sdhci-msm-Initial-support-for-Qualcomm-chipsets.patch new file mode 100644 index 0000000000..7ec97a4687 --- /dev/null +++ b/target/linux/ipq806x/patches/0047-mmc-sdhci-msm-Initial-support-for-Qualcomm-chipsets.patch @@ -0,0 +1,275 @@ +From 5a8f026acb4a7a6c6d0c868cc1790363640b9b8f Mon Sep 17 00:00:00 2001 +From: Georgi Djakov <gdjakov@mm-sol.com> +Date: Mon, 10 Mar 2014 17:37:12 +0200 +Subject: [PATCH 047/182] mmc: sdhci-msm: Initial support for Qualcomm + chipsets + +This platform driver adds the initial support of Secure Digital Host +Controller Interface compliant controller found in Qualcomm chipsets. + +Signed-off-by: Asutosh Das <asutoshd@codeaurora.org> +Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org> +Tested-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com> +Acked-by: Ulf Hansson <ulf.hansson@linaro.org> +Signed-off-by: Chris Ball <chris@printf.net> +--- + drivers/mmc/host/Kconfig | 13 +++ + drivers/mmc/host/Makefile | 1 + + drivers/mmc/host/sdhci-msm.c | 208 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 222 insertions(+) + create mode 100644 drivers/mmc/host/sdhci-msm.c + +diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig +index 1384f67..c0ea72a 100644 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -334,6 +334,19 @@ config MMC_ATMELMCI + + If unsure, say N. + ++config MMC_SDHCI_MSM ++ tristate "Qualcomm SDHCI Controller Support" ++ depends on ARCH_QCOM ++ depends on MMC_SDHCI_PLTFM ++ help ++ This selects the Secure Digital Host Controller Interface (SDHCI) ++ support present in Qualcomm SOCs. The controller supports ++ SD/MMC/SDIO devices. ++ ++ If you have a controller with this interface, say Y or M here. ++ ++ If unsure, say N. ++ + config MMC_MSM + tristate "Qualcomm SDCC Controller Support" + depends on MMC && (ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50) +diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile +index 3483b6b..bbc8445 100644 +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -64,6 +64,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o + obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o + obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o + obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o ++obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o + + ifeq ($(CONFIG_CB710_DEBUG),y) + CFLAGS-cb710-mmc += -DDEBUG +diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c +new file mode 100644 +index 0000000..3b0606f +--- /dev/null ++++ b/drivers/mmc/host/sdhci-msm.c +@@ -0,0 +1,208 @@ ++/* ++ * drivers/mmc/host/sdhci-msm.c - Qualcomm SDHCI Platform driver ++ * ++ * 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/module.h> ++#include <linux/of_device.h> ++#include <linux/regulator/consumer.h> ++#include <linux/delay.h> ++ ++#include "sdhci-pltfm.h" ++ ++#define CORE_HC_MODE 0x78 ++#define HC_MODE_EN 0x1 ++#define CORE_POWER 0x0 ++#define CORE_SW_RST BIT(7) ++ ++ ++struct sdhci_msm_host { ++ struct platform_device *pdev; ++ void __iomem *core_mem; /* MSM SDCC mapped address */ ++ struct clk *clk; /* main SD/MMC bus clock */ ++ struct clk *pclk; /* SDHC peripheral bus clock */ ++ struct clk *bus_clk; /* SDHC bus voter clock */ ++ struct mmc_host *mmc; ++ struct sdhci_pltfm_data sdhci_msm_pdata; ++}; ++ ++/* Platform specific tuning */ ++static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) ++{ ++ /* ++ * Tuning is required for SDR104, HS200 and HS400 cards and if the clock ++ * frequency greater than 100MHz in those modes. The standard tuning ++ * procedure should not be executed, but a custom implementation will be ++ * added here instead. ++ */ ++ return 0; ++} ++ ++static const struct of_device_id sdhci_msm_dt_match[] = { ++ { .compatible = "qcom,sdhci-msm-v4" }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match); ++ ++static struct sdhci_ops sdhci_msm_ops = { ++ .platform_execute_tuning = sdhci_msm_execute_tuning, ++}; ++ ++static int sdhci_msm_probe(struct platform_device *pdev) ++{ ++ struct sdhci_host *host; ++ struct sdhci_pltfm_host *pltfm_host; ++ struct sdhci_msm_host *msm_host; ++ struct resource *core_memres; ++ int ret; ++ u16 host_version; ++ ++ msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL); ++ if (!msm_host) ++ return -ENOMEM; ++ ++ msm_host->sdhci_msm_pdata.ops = &sdhci_msm_ops; ++ host = sdhci_pltfm_init(pdev, &msm_host->sdhci_msm_pdata, 0); ++ if (IS_ERR(host)) ++ return PTR_ERR(host); ++ ++ pltfm_host = sdhci_priv(host); ++ pltfm_host->priv = msm_host; ++ msm_host->mmc = host->mmc; ++ msm_host->pdev = pdev; ++ ++ ret = mmc_of_parse(host->mmc); ++ if (ret) ++ goto pltfm_free; ++ ++ sdhci_get_of_property(pdev); ++ ++ /* Setup SDCC bus voter clock. */ ++ msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus"); ++ if (!IS_ERR(msm_host->bus_clk)) { ++ /* Vote for max. clk rate for max. performance */ ++ ret = clk_set_rate(msm_host->bus_clk, INT_MAX); ++ if (ret) ++ goto pltfm_free; ++ ret = clk_prepare_enable(msm_host->bus_clk); ++ if (ret) ++ goto pltfm_free; ++ } ++ ++ /* Setup main peripheral bus clock */ ++ msm_host->pclk = devm_clk_get(&pdev->dev, "iface"); ++ if (IS_ERR(msm_host->pclk)) { ++ ret = PTR_ERR(msm_host->pclk); ++ dev_err(&pdev->dev, "Perpheral clk setup failed (%d)\n", ret); ++ goto bus_clk_disable; ++ } ++ ++ ret = clk_prepare_enable(msm_host->pclk); ++ if (ret) ++ goto bus_clk_disable; ++ ++ /* Setup SDC MMC clock */ ++ msm_host->clk = devm_clk_get(&pdev->dev, "core"); ++ if (IS_ERR(msm_host->clk)) { ++ ret = PTR_ERR(msm_host->clk); ++ dev_err(&pdev->dev, "SDC MMC clk setup failed (%d)\n", ret); ++ goto pclk_disable; ++ } ++ ++ ret = clk_prepare_enable(msm_host->clk); ++ if (ret) ++ goto pclk_disable; ++ ++ core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ msm_host->core_mem = devm_ioremap_resource(&pdev->dev, core_memres); ++ ++ if (IS_ERR(msm_host->core_mem)) { ++ dev_err(&pdev->dev, "Failed to remap registers\n"); ++ ret = PTR_ERR(msm_host->core_mem); ++ goto clk_disable; ++ } ++ ++ /* Reset the core and Enable SDHC mode */ ++ writel_relaxed(readl_relaxed(msm_host->core_mem + CORE_POWER) | ++ CORE_SW_RST, msm_host->core_mem + CORE_POWER); ++ ++ /* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */ ++ usleep_range(1000, 5000); ++ if (readl(msm_host->core_mem + CORE_POWER) & CORE_SW_RST) { ++ dev_err(&pdev->dev, "Stuck in reset\n"); ++ ret = -ETIMEDOUT; ++ goto clk_disable; ++ } ++ ++ /* Set HC_MODE_EN bit in HC_MODE register */ ++ writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE)); ++ ++ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; ++ host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE; ++ ++ host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION)); ++ dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n", ++ host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >> ++ SDHCI_VENDOR_VER_SHIFT)); ++ ++ ret = sdhci_add_host(host); ++ if (ret) ++ goto clk_disable; ++ ++ return 0; ++ ++clk_disable: ++ clk_disable_unprepare(msm_host->clk); ++pclk_disable: ++ clk_disable_unprepare(msm_host->pclk); ++bus_clk_disable: ++ if (!IS_ERR(msm_host->bus_clk)) ++ clk_disable_unprepare(msm_host->bus_clk); ++pltfm_free: ++ sdhci_pltfm_free(pdev); ++ return ret; ++} ++ ++static int sdhci_msm_remove(struct platform_device *pdev) ++{ ++ struct sdhci_host *host = platform_get_drvdata(pdev); ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct sdhci_msm_host *msm_host = pltfm_host->priv; ++ int dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) == ++ 0xffffffff); ++ ++ sdhci_remove_host(host, dead); ++ sdhci_pltfm_free(pdev); ++ clk_disable_unprepare(msm_host->clk); ++ clk_disable_unprepare(msm_host->pclk); ++ if (!IS_ERR(msm_host->bus_clk)) ++ clk_disable_unprepare(msm_host->bus_clk); ++ return 0; ++} ++ ++static struct platform_driver sdhci_msm_driver = { ++ .probe = sdhci_msm_probe, ++ .remove = sdhci_msm_remove, ++ .driver = { ++ .name = "sdhci_msm", ++ .owner = THIS_MODULE, ++ .of_match_table = sdhci_msm_dt_match, ++ }, ++}; ++ ++module_platform_driver(sdhci_msm_driver); ++ ++MODULE_DESCRIPTION("Qualcomm Secure Digital Host Controller Interface driver"); ++MODULE_LICENSE("GPL v2"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0048-mmc-sdhci-msm-Add-platform_execute_tuning-implementa.patch b/target/linux/ipq806x/patches/0048-mmc-sdhci-msm-Add-platform_execute_tuning-implementa.patch new file mode 100644 index 0000000000..07e122fa64 --- /dev/null +++ b/target/linux/ipq806x/patches/0048-mmc-sdhci-msm-Add-platform_execute_tuning-implementa.patch @@ -0,0 +1,472 @@ +From c2a237b3e467c8bb349c4624b71ec400abaf8ad1 Mon Sep 17 00:00:00 2001 +From: Georgi Djakov <gdjakov@mm-sol.com> +Date: Mon, 10 Mar 2014 17:37:13 +0200 +Subject: [PATCH 048/182] mmc: sdhci-msm: Add platform_execute_tuning + implementation + +This patch adds implementation for platform specific tuning in order +to support HS200 bus speed mode on Qualcomm SDHCI controller. + +Signed-off-by: Asutosh Das <asutoshd@codeaurora.org> +Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org> +Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com> +Acked-by: Ulf Hansson <ulf.hansson@linaro.org> +Signed-off-by: Chris Ball <chris@printf.net> +--- + drivers/mmc/host/sdhci-msm.c | 420 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 415 insertions(+), 5 deletions(-) + +diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c +index 3b0606f..acb0e9e 100644 +--- a/drivers/mmc/host/sdhci-msm.c ++++ b/drivers/mmc/host/sdhci-msm.c +@@ -18,6 +18,8 @@ + #include <linux/of_device.h> + #include <linux/regulator/consumer.h> + #include <linux/delay.h> ++#include <linux/mmc/mmc.h> ++#include <linux/slab.h> + + #include "sdhci-pltfm.h" + +@@ -26,6 +28,42 @@ + #define CORE_POWER 0x0 + #define CORE_SW_RST BIT(7) + ++#define MAX_PHASES 16 ++#define CORE_DLL_LOCK BIT(7) ++#define CORE_DLL_EN BIT(16) ++#define CORE_CDR_EN BIT(17) ++#define CORE_CK_OUT_EN BIT(18) ++#define CORE_CDR_EXT_EN BIT(19) ++#define CORE_DLL_PDN BIT(29) ++#define CORE_DLL_RST BIT(30) ++#define CORE_DLL_CONFIG 0x100 ++#define CORE_DLL_STATUS 0x108 ++ ++#define CORE_VENDOR_SPEC 0x10c ++#define CORE_CLK_PWRSAVE BIT(1) ++ ++#define CDR_SELEXT_SHIFT 20 ++#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT) ++#define CMUX_SHIFT_PHASE_SHIFT 24 ++#define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT) ++ ++static const u32 tuning_block_64[] = { ++ 0x00ff0fff, 0xccc3ccff, 0xffcc3cc3, 0xeffefffe, ++ 0xddffdfff, 0xfbfffbff, 0xff7fffbf, 0xefbdf777, ++ 0xf0fff0ff, 0x3cccfc0f, 0xcfcc33cc, 0xeeffefff, ++ 0xfdfffdff, 0xffbfffdf, 0xfff7ffbb, 0xde7b7ff7 ++}; ++ ++static const u32 tuning_block_128[] = { ++ 0xff00ffff, 0x0000ffff, 0xccccffff, 0xcccc33cc, ++ 0xcc3333cc, 0xffffcccc, 0xffffeeff, 0xffeeeeff, ++ 0xffddffff, 0xddddffff, 0xbbffffff, 0xbbffffff, ++ 0xffffffbb, 0xffffff77, 0x77ff7777, 0xffeeddbb, ++ 0x00ffffff, 0x00ffffff, 0xccffff00, 0xcc33cccc, ++ 0x3333cccc, 0xffcccccc, 0xffeeffff, 0xeeeeffff, ++ 0xddffffff, 0xddffffff, 0xffffffdd, 0xffffffbb, ++ 0xffffbbbb, 0xffff77ff, 0xff7777ff, 0xeeddbb77 ++}; + + struct sdhci_msm_host { + struct platform_device *pdev; +@@ -38,17 +76,389 @@ struct sdhci_msm_host { + }; + + /* Platform specific tuning */ +-static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) ++static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll) ++{ ++ u32 wait_cnt = 50; ++ u8 ck_out_en; ++ struct mmc_host *mmc = host->mmc; ++ ++ /* Poll for CK_OUT_EN bit. max. poll time = 50us */ ++ ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) & ++ CORE_CK_OUT_EN); ++ ++ while (ck_out_en != poll) { ++ if (--wait_cnt == 0) { ++ dev_err(mmc_dev(mmc), "%s: CK_OUT_EN bit is not %d\n", ++ mmc_hostname(mmc), poll); ++ return -ETIMEDOUT; ++ } ++ udelay(1); ++ ++ ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) & ++ CORE_CK_OUT_EN); ++ } ++ ++ return 0; ++} ++ ++static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase) ++{ ++ int rc; ++ static const u8 grey_coded_phase_table[] = { ++ 0x0, 0x1, 0x3, 0x2, 0x6, 0x7, 0x5, 0x4, ++ 0xc, 0xd, 0xf, 0xe, 0xa, 0xb, 0x9, 0x8 ++ }; ++ unsigned long flags; ++ u32 config; ++ struct mmc_host *mmc = host->mmc; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); ++ config &= ~(CORE_CDR_EN | CORE_CK_OUT_EN); ++ config |= (CORE_CDR_EXT_EN | CORE_DLL_EN); ++ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); ++ ++ /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '0' */ ++ rc = msm_dll_poll_ck_out_en(host, 0); ++ if (rc) ++ goto err_out; ++ ++ /* ++ * Write the selected DLL clock output phase (0 ... 15) ++ * to CDR_SELEXT bit field of DLL_CONFIG register. ++ */ ++ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); ++ config &= ~CDR_SELEXT_MASK; ++ config |= grey_coded_phase_table[phase] << CDR_SELEXT_SHIFT; ++ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); ++ ++ /* Set CK_OUT_EN bit of DLL_CONFIG register to 1. */ ++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) ++ | CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG); ++ ++ /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */ ++ rc = msm_dll_poll_ck_out_en(host, 1); ++ if (rc) ++ goto err_out; ++ ++ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); ++ config |= CORE_CDR_EN; ++ config &= ~CORE_CDR_EXT_EN; ++ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); ++ goto out; ++ ++err_out: ++ dev_err(mmc_dev(mmc), "%s: Failed to set DLL phase: %d\n", ++ mmc_hostname(mmc), phase); ++out: ++ spin_unlock_irqrestore(&host->lock, flags); ++ return rc; ++} ++ ++/* ++ * Find out the greatest range of consecuitive selected ++ * DLL clock output phases that can be used as sampling ++ * setting for SD3.0 UHS-I card read operation (in SDR104 ++ * timing mode) or for eMMC4.5 card read operation (in HS200 ++ * timing mode). ++ * Select the 3/4 of the range and configure the DLL with the ++ * selected DLL clock output phase. ++ */ ++ ++static int msm_find_most_appropriate_phase(struct sdhci_host *host, ++ u8 *phase_table, u8 total_phases) ++{ ++ int ret; ++ u8 ranges[MAX_PHASES][MAX_PHASES] = { {0}, {0} }; ++ u8 phases_per_row[MAX_PHASES] = { 0 }; ++ int row_index = 0, col_index = 0, selected_row_index = 0, curr_max = 0; ++ int i, cnt, phase_0_raw_index = 0, phase_15_raw_index = 0; ++ bool phase_0_found = false, phase_15_found = false; ++ struct mmc_host *mmc = host->mmc; ++ ++ if (!total_phases || (total_phases > MAX_PHASES)) { ++ dev_err(mmc_dev(mmc), "%s: Invalid argument: total_phases=%d\n", ++ mmc_hostname(mmc), total_phases); ++ return -EINVAL; ++ } ++ ++ for (cnt = 0; cnt < total_phases; cnt++) { ++ ranges[row_index][col_index] = phase_table[cnt]; ++ phases_per_row[row_index] += 1; ++ col_index++; ++ ++ if ((cnt + 1) == total_phases) { ++ continue; ++ /* check if next phase in phase_table is consecutive or not */ ++ } else if ((phase_table[cnt] + 1) != phase_table[cnt + 1]) { ++ row_index++; ++ col_index = 0; ++ } ++ } ++ ++ if (row_index >= MAX_PHASES) ++ return -EINVAL; ++ ++ /* Check if phase-0 is present in first valid window? */ ++ if (!ranges[0][0]) { ++ phase_0_found = true; ++ phase_0_raw_index = 0; ++ /* Check if cycle exist between 2 valid windows */ ++ for (cnt = 1; cnt <= row_index; cnt++) { ++ if (phases_per_row[cnt]) { ++ for (i = 0; i < phases_per_row[cnt]; i++) { ++ if (ranges[cnt][i] == 15) { ++ phase_15_found = true; ++ phase_15_raw_index = cnt; ++ break; ++ } ++ } ++ } ++ } ++ } ++ ++ /* If 2 valid windows form cycle then merge them as single window */ ++ if (phase_0_found && phase_15_found) { ++ /* number of phases in raw where phase 0 is present */ ++ u8 phases_0 = phases_per_row[phase_0_raw_index]; ++ /* number of phases in raw where phase 15 is present */ ++ u8 phases_15 = phases_per_row[phase_15_raw_index]; ++ ++ if (phases_0 + phases_15 >= MAX_PHASES) ++ /* ++ * If there are more than 1 phase windows then total ++ * number of phases in both the windows should not be ++ * more than or equal to MAX_PHASES. ++ */ ++ return -EINVAL; ++ ++ /* Merge 2 cyclic windows */ ++ i = phases_15; ++ for (cnt = 0; cnt < phases_0; cnt++) { ++ ranges[phase_15_raw_index][i] = ++ ranges[phase_0_raw_index][cnt]; ++ if (++i >= MAX_PHASES) ++ break; ++ } ++ ++ phases_per_row[phase_0_raw_index] = 0; ++ phases_per_row[phase_15_raw_index] = phases_15 + phases_0; ++ } ++ ++ for (cnt = 0; cnt <= row_index; cnt++) { ++ if (phases_per_row[cnt] > curr_max) { ++ curr_max = phases_per_row[cnt]; ++ selected_row_index = cnt; ++ } ++ } ++ ++ i = (curr_max * 3) / 4; ++ if (i) ++ i--; ++ ++ ret = ranges[selected_row_index][i]; ++ ++ if (ret >= MAX_PHASES) { ++ ret = -EINVAL; ++ dev_err(mmc_dev(mmc), "%s: Invalid phase selected=%d\n", ++ mmc_hostname(mmc), ret); ++ } ++ ++ return ret; ++} ++ ++static inline void msm_cm_dll_set_freq(struct sdhci_host *host) ++{ ++ u32 mclk_freq = 0, config; ++ ++ /* Program the MCLK value to MCLK_FREQ bit field */ ++ if (host->clock <= 112000000) ++ mclk_freq = 0; ++ else if (host->clock <= 125000000) ++ mclk_freq = 1; ++ else if (host->clock <= 137000000) ++ mclk_freq = 2; ++ else if (host->clock <= 150000000) ++ mclk_freq = 3; ++ else if (host->clock <= 162000000) ++ mclk_freq = 4; ++ else if (host->clock <= 175000000) ++ mclk_freq = 5; ++ else if (host->clock <= 187000000) ++ mclk_freq = 6; ++ else if (host->clock <= 200000000) ++ mclk_freq = 7; ++ ++ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); ++ config &= ~CMUX_SHIFT_PHASE_MASK; ++ config |= mclk_freq << CMUX_SHIFT_PHASE_SHIFT; ++ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); ++} ++ ++/* Initialize the DLL (Programmable Delay Line) */ ++static int msm_init_cm_dll(struct sdhci_host *host) + { ++ struct mmc_host *mmc = host->mmc; ++ int wait_cnt = 50; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ + /* +- * Tuning is required for SDR104, HS200 and HS400 cards and if the clock +- * frequency greater than 100MHz in those modes. The standard tuning +- * procedure should not be executed, but a custom implementation will be +- * added here instead. ++ * Make sure that clock is always enabled when DLL ++ * tuning is in progress. Keeping PWRSAVE ON may ++ * turn off the clock. + */ ++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) ++ & ~CORE_CLK_PWRSAVE), host->ioaddr + CORE_VENDOR_SPEC); ++ ++ /* Write 1 to DLL_RST bit of DLL_CONFIG register */ ++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) ++ | CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG); ++ ++ /* Write 1 to DLL_PDN bit of DLL_CONFIG register */ ++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) ++ | CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG); ++ msm_cm_dll_set_freq(host); ++ ++ /* Write 0 to DLL_RST bit of DLL_CONFIG register */ ++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) ++ & ~CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG); ++ ++ /* Write 0 to DLL_PDN bit of DLL_CONFIG register */ ++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) ++ & ~CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG); ++ ++ /* Set DLL_EN bit to 1. */ ++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) ++ | CORE_DLL_EN), host->ioaddr + CORE_DLL_CONFIG); ++ ++ /* Set CK_OUT_EN bit to 1. */ ++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) ++ | CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG); ++ ++ /* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */ ++ while (!(readl_relaxed(host->ioaddr + CORE_DLL_STATUS) & ++ CORE_DLL_LOCK)) { ++ /* max. wait for 50us sec for LOCK bit to be set */ ++ if (--wait_cnt == 0) { ++ dev_err(mmc_dev(mmc), "%s: DLL failed to LOCK\n", ++ mmc_hostname(mmc)); ++ spin_unlock_irqrestore(&host->lock, flags); ++ return -ETIMEDOUT; ++ } ++ udelay(1); ++ } ++ ++ spin_unlock_irqrestore(&host->lock, flags); + return 0; + } + ++static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) ++{ ++ int tuning_seq_cnt = 3; ++ u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0; ++ const u32 *tuning_block_pattern = tuning_block_64; ++ int size = sizeof(tuning_block_64); /* Pattern size in bytes */ ++ int rc; ++ struct mmc_host *mmc = host->mmc; ++ struct mmc_ios ios = host->mmc->ios; ++ ++ /* ++ * Tuning is required for SDR104, HS200 and HS400 cards and ++ * if clock frequency is greater than 100MHz in these modes. ++ */ ++ if (host->clock <= 100 * 1000 * 1000 || ++ !((ios.timing == MMC_TIMING_MMC_HS200) || ++ (ios.timing == MMC_TIMING_UHS_SDR104))) ++ return 0; ++ ++ if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) && ++ (mmc->ios.bus_width == MMC_BUS_WIDTH_8)) { ++ tuning_block_pattern = tuning_block_128; ++ size = sizeof(tuning_block_128); ++ } ++ ++ data_buf = kmalloc(size, GFP_KERNEL); ++ if (!data_buf) ++ return -ENOMEM; ++ ++retry: ++ /* First of all reset the tuning block */ ++ rc = msm_init_cm_dll(host); ++ if (rc) ++ goto out; ++ ++ phase = 0; ++ do { ++ struct mmc_command cmd = { 0 }; ++ struct mmc_data data = { 0 }; ++ struct mmc_request mrq = { ++ .cmd = &cmd, ++ .data = &data ++ }; ++ struct scatterlist sg; ++ ++ /* Set the phase in delay line hw block */ ++ rc = msm_config_cm_dll_phase(host, phase); ++ if (rc) ++ goto out; ++ ++ cmd.opcode = opcode; ++ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; ++ ++ data.blksz = size; ++ data.blocks = 1; ++ data.flags = MMC_DATA_READ; ++ data.timeout_ns = NSEC_PER_SEC; /* 1 second */ ++ ++ data.sg = &sg; ++ data.sg_len = 1; ++ sg_init_one(&sg, data_buf, size); ++ memset(data_buf, 0, size); ++ mmc_wait_for_req(mmc, &mrq); ++ ++ if (!cmd.error && !data.error && ++ !memcmp(data_buf, tuning_block_pattern, size)) { ++ /* Tuning is successful at this tuning point */ ++ tuned_phases[tuned_phase_cnt++] = phase; ++ dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n", ++ mmc_hostname(mmc), phase); ++ } ++ } while (++phase < ARRAY_SIZE(tuned_phases)); ++ ++ if (tuned_phase_cnt) { ++ rc = msm_find_most_appropriate_phase(host, tuned_phases, ++ tuned_phase_cnt); ++ if (rc < 0) ++ goto out; ++ else ++ phase = rc; ++ ++ /* ++ * Finally set the selected phase in delay ++ * line hw block. ++ */ ++ rc = msm_config_cm_dll_phase(host, phase); ++ if (rc) ++ goto out; ++ dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n", ++ mmc_hostname(mmc), phase); ++ } else { ++ if (--tuning_seq_cnt) ++ goto retry; ++ /* Tuning failed */ ++ dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n", ++ mmc_hostname(mmc)); ++ rc = -EIO; ++ } ++ ++out: ++ kfree(data_buf); ++ return rc; ++} ++ + static const struct of_device_id sdhci_msm_dt_match[] = { + { .compatible = "qcom,sdhci-msm-v4" }, + {}, +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0049-drivers-of-add-initialization-code-for-static-reserv.patch b/target/linux/ipq806x/patches/0049-drivers-of-add-initialization-code-for-static-reserv.patch new file mode 100644 index 0000000000..f520e6cc7a --- /dev/null +++ b/target/linux/ipq806x/patches/0049-drivers-of-add-initialization-code-for-static-reserv.patch @@ -0,0 +1,216 @@ +From 60ee5b21ce14a7d5c269115b0751b904d0e005ed Mon Sep 17 00:00:00 2001 +From: Marek Szyprowski <m.szyprowski@samsung.com> +Date: Fri, 28 Feb 2014 14:42:47 +0100 +Subject: [PATCH 049/182] drivers: of: add initialization code for static + reserved memory + +This patch adds support for static (defined by 'reg' property) reserved +memory regions declared in device tree. + +Memory blocks can be reliably reserved only during early boot. This must +happen before the whole memory management subsystem is initialized, +because we need to ensure that the given contiguous blocks are not yet +allocated by kernel. Also it must happen before kernel mappings for the +whole low memory are created, to ensure that there will be no mappings +(for reserved blocks). Typically, all this happens before device tree +structures are unflattened, so we need to get reserved memory layout +directly from fdt. + +Based on previous code provided by Josh Cartwright <joshc@codeaurora.org> + +Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> +Signed-off-by: Grant Likely <grant.likely@linaro.org> +--- + drivers/of/fdt.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++ + include/linux/of_fdt.h | 4 ++ + 2 files changed, 135 insertions(+) + +diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c +index 758b4f8..819e112 100644 +--- a/drivers/of/fdt.c ++++ b/drivers/of/fdt.c +@@ -15,6 +15,7 @@ + #include <linux/module.h> + #include <linux/of.h> + #include <linux/of_fdt.h> ++#include <linux/sizes.h> + #include <linux/string.h> + #include <linux/errno.h> + #include <linux/slab.h> +@@ -440,6 +441,118 @@ struct boot_param_header *initial_boot_params; + #ifdef CONFIG_OF_EARLY_FLATTREE + + /** ++ * res_mem_reserve_reg() - reserve all memory described in 'reg' property ++ */ ++static int __init __reserved_mem_reserve_reg(unsigned long node, ++ const char *uname) ++{ ++ int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); ++ phys_addr_t base, size; ++ unsigned long len; ++ __be32 *prop; ++ int nomap; ++ ++ prop = of_get_flat_dt_prop(node, "reg", &len); ++ if (!prop) ++ return -ENOENT; ++ ++ if (len && len % t_len != 0) { ++ pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n", ++ uname); ++ return -EINVAL; ++ } ++ ++ nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; ++ ++ while (len >= t_len) { ++ base = dt_mem_next_cell(dt_root_addr_cells, &prop); ++ size = dt_mem_next_cell(dt_root_size_cells, &prop); ++ ++ if (base && size && ++ early_init_dt_reserve_memory_arch(base, size, nomap) == 0) ++ pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %ld MiB\n", ++ uname, &base, (unsigned long)size / SZ_1M); ++ else ++ pr_info("Reserved memory: failed to reserve memory for node '%s': base %pa, size %ld MiB\n", ++ uname, &base, (unsigned long)size / SZ_1M); ++ ++ len -= t_len; ++ } ++ return 0; ++} ++ ++/** ++ * __reserved_mem_check_root() - check if #size-cells, #address-cells provided ++ * in /reserved-memory matches the values supported by the current implementation, ++ * also check if ranges property has been provided ++ */ ++static int __reserved_mem_check_root(unsigned long node) ++{ ++ __be32 *prop; ++ ++ prop = of_get_flat_dt_prop(node, "#size-cells", NULL); ++ if (!prop || be32_to_cpup(prop) != dt_root_size_cells) ++ return -EINVAL; ++ ++ prop = of_get_flat_dt_prop(node, "#address-cells", NULL); ++ if (!prop || be32_to_cpup(prop) != dt_root_addr_cells) ++ return -EINVAL; ++ ++ prop = of_get_flat_dt_prop(node, "ranges", NULL); ++ if (!prop) ++ return -EINVAL; ++ return 0; ++} ++ ++/** ++ * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory ++ */ ++static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, ++ int depth, void *data) ++{ ++ static int found; ++ const char *status; ++ ++ if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) { ++ if (__reserved_mem_check_root(node) != 0) { ++ pr_err("Reserved memory: unsupported node format, ignoring\n"); ++ /* break scan */ ++ return 1; ++ } ++ found = 1; ++ /* scan next node */ ++ return 0; ++ } else if (!found) { ++ /* scan next node */ ++ return 0; ++ } else if (found && depth < 2) { ++ /* scanning of /reserved-memory has been finished */ ++ return 1; ++ } ++ ++ status = of_get_flat_dt_prop(node, "status", NULL); ++ if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0) ++ return 0; ++ ++ __reserved_mem_reserve_reg(node, uname); ++ ++ /* scan next node */ ++ return 0; ++} ++ ++/** ++ * early_init_fdt_scan_reserved_mem() - create reserved memory regions ++ * ++ * This function grabs memory from early allocator for device exclusive use ++ * defined in device tree structures. It should be called by arch specific code ++ * once the early allocator (i.e. memblock) has been fully activated. ++ */ ++void __init early_init_fdt_scan_reserved_mem(void) ++{ ++ of_scan_flat_dt(__fdt_scan_reserved_mem, NULL); ++} ++ ++/** + * of_scan_flat_dt - scan flattened tree blob and call callback on each. + * @it: callback function + * @data: context data pointer +@@ -856,6 +969,16 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) + memblock_add(base, size); + } + ++int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base, ++ phys_addr_t size, bool nomap) ++{ ++ if (memblock_is_region_reserved(base, size)) ++ return -EBUSY; ++ if (nomap) ++ return memblock_remove(base, size); ++ return memblock_reserve(base, size); ++} ++ + /* + * called from unflatten_device_tree() to bootstrap devicetree itself + * Architectures can override this definition if memblock isn't used +@@ -864,6 +987,14 @@ void * __init __weak early_init_dt_alloc_memory_arch(u64 size, u64 align) + { + return __va(memblock_alloc(size, align)); + } ++#else ++int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base, ++ phys_addr_t size, bool nomap) ++{ ++ pr_err("Reserved memory not supported, ignoring range 0x%llx - 0x%llx%s\n", ++ base, size, nomap ? " (nomap)" : ""); ++ return -ENOSYS; ++} + #endif + + bool __init early_init_dt_scan(void *params) +diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h +index 2b77058..ddd7219 100644 +--- a/include/linux/of_fdt.h ++++ b/include/linux/of_fdt.h +@@ -98,7 +98,10 @@ extern int early_init_dt_scan_chosen(unsigned long node, const char *uname, + int depth, void *data); + extern int early_init_dt_scan_memory(unsigned long node, const char *uname, + int depth, void *data); ++extern void early_init_fdt_scan_reserved_mem(void); + extern void early_init_dt_add_memory_arch(u64 base, u64 size); ++extern int early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size, ++ bool no_map); + extern void * early_init_dt_alloc_memory_arch(u64 size, u64 align); + extern u64 dt_mem_next_cell(int s, __be32 **cellp); + +@@ -118,6 +121,7 @@ extern void unflatten_and_copy_device_tree(void); + extern void early_init_devtree(void *); + extern void early_get_first_memblock_info(void *, phys_addr_t *); + #else /* CONFIG_OF_FLATTREE */ ++static inline void early_init_fdt_scan_reserved_mem(void) {} + static inline const char *of_flat_dt_get_machine_name(void) { return NULL; } + static inline void unflatten_device_tree(void) {} + static inline void unflatten_and_copy_device_tree(void) {} +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0050-drivers-of-add-initialization-code-for-dynamic-reser.patch b/target/linux/ipq806x/patches/0050-drivers-of-add-initialization-code-for-dynamic-reser.patch new file mode 100644 index 0000000000..b23867ba55 --- /dev/null +++ b/target/linux/ipq806x/patches/0050-drivers-of-add-initialization-code-for-dynamic-reser.patch @@ -0,0 +1,331 @@ +From 968b497e1027ec78e986370976c76e1652aa0459 Mon Sep 17 00:00:00 2001 +From: Marek Szyprowski <m.szyprowski@samsung.com> +Date: Fri, 28 Feb 2014 14:42:48 +0100 +Subject: [PATCH 050/182] drivers: of: add initialization code for dynamic + reserved memory + +This patch adds support for dynamically allocated reserved memory regions +declared in device tree. Such regions are defined by 'size', 'alignment' +and 'alloc-ranges' properties. + +Based on previous code provided by Josh Cartwright <joshc@codeaurora.org> + +Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> +Signed-off-by: Grant Likely <grant.likely@linaro.org> +--- + drivers/of/Kconfig | 6 ++ + drivers/of/Makefile | 1 + + drivers/of/fdt.c | 13 ++- + drivers/of/of_reserved_mem.c | 188 +++++++++++++++++++++++++++++++++++++++ + include/linux/of_reserved_mem.h | 21 +++++ + 5 files changed, 227 insertions(+), 2 deletions(-) + create mode 100644 drivers/of/of_reserved_mem.c + create mode 100644 include/linux/of_reserved_mem.h + +diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig +index c6973f1..30a7d87 100644 +--- a/drivers/of/Kconfig ++++ b/drivers/of/Kconfig +@@ -75,4 +75,10 @@ config OF_MTD + depends on MTD + def_bool y + ++config OF_RESERVED_MEM ++ depends on OF_EARLY_FLATTREE ++ bool ++ help ++ Helpers to allow for reservation of memory regions ++ + endmenu # OF +diff --git a/drivers/of/Makefile b/drivers/of/Makefile +index efd0510..ed9660a 100644 +--- a/drivers/of/Makefile ++++ b/drivers/of/Makefile +@@ -9,3 +9,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o + obj-$(CONFIG_OF_PCI) += of_pci.o + obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o + obj-$(CONFIG_OF_MTD) += of_mtd.o ++obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o +diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c +index 819e112..510c0d8 100644 +--- a/drivers/of/fdt.c ++++ b/drivers/of/fdt.c +@@ -15,6 +15,7 @@ + #include <linux/module.h> + #include <linux/of.h> + #include <linux/of_fdt.h> ++#include <linux/of_reserved_mem.h> + #include <linux/sizes.h> + #include <linux/string.h> + #include <linux/errno.h> +@@ -450,7 +451,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, + phys_addr_t base, size; + unsigned long len; + __be32 *prop; +- int nomap; ++ int nomap, first = 1; + + prop = of_get_flat_dt_prop(node, "reg", &len); + if (!prop) +@@ -477,6 +478,10 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, + uname, &base, (unsigned long)size / SZ_1M); + + len -= t_len; ++ if (first) { ++ fdt_reserved_mem_save_node(node, uname, base, size); ++ first = 0; ++ } + } + return 0; + } +@@ -512,6 +517,7 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, + { + static int found; + const char *status; ++ int err; + + if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) { + if (__reserved_mem_check_root(node) != 0) { +@@ -534,7 +540,9 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, + if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0) + return 0; + +- __reserved_mem_reserve_reg(node, uname); ++ err = __reserved_mem_reserve_reg(node, uname); ++ if (err == -ENOENT && of_get_flat_dt_prop(node, "size", NULL)) ++ fdt_reserved_mem_save_node(node, uname, 0, 0); + + /* scan next node */ + return 0; +@@ -550,6 +558,7 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, + void __init early_init_fdt_scan_reserved_mem(void) + { + of_scan_flat_dt(__fdt_scan_reserved_mem, NULL); ++ fdt_init_reserved_mem(); + } + + /** +diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c +new file mode 100644 +index 0000000..69b8117 +--- /dev/null ++++ b/drivers/of/of_reserved_mem.c +@@ -0,0 +1,188 @@ ++/* ++ * Device tree based initialization code for reserved memory. ++ * ++ * Copyright (c) 2013, The Linux Foundation. All Rights Reserved. ++ * Copyright (c) 2013,2014 Samsung Electronics Co., Ltd. ++ * http://www.samsung.com ++ * Author: Marek Szyprowski <m.szyprowski@samsung.com> ++ * Author: Josh Cartwright <joshc@codeaurora.org> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License or (at your optional) any later version of the license. ++ */ ++ ++#include <linux/err.h> ++#include <linux/of.h> ++#include <linux/of_fdt.h> ++#include <linux/of_platform.h> ++#include <linux/mm.h> ++#include <linux/sizes.h> ++#include <linux/of_reserved_mem.h> ++ ++#define MAX_RESERVED_REGIONS 16 ++static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; ++static int reserved_mem_count; ++ ++#if defined(CONFIG_HAVE_MEMBLOCK) ++#include <linux/memblock.h> ++int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, ++ phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap, ++ phys_addr_t *res_base) ++{ ++ /* ++ * We use __memblock_alloc_base() because memblock_alloc_base() ++ * panic()s on allocation failure. ++ */ ++ phys_addr_t base = __memblock_alloc_base(size, align, end); ++ if (!base) ++ return -ENOMEM; ++ ++ /* ++ * Check if the allocated region fits in to start..end window ++ */ ++ if (base < start) { ++ memblock_free(base, size); ++ return -ENOMEM; ++ } ++ ++ *res_base = base; ++ if (nomap) ++ return memblock_remove(base, size); ++ return 0; ++} ++#else ++int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, ++ phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap, ++ phys_addr_t *res_base) ++{ ++ pr_err("Reserved memory not supported, ignoring region 0x%llx%s\n", ++ size, nomap ? " (nomap)" : ""); ++ return -ENOSYS; ++} ++#endif ++ ++/** ++ * res_mem_save_node() - save fdt node for second pass initialization ++ */ ++void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname, ++ phys_addr_t base, phys_addr_t size) ++{ ++ struct reserved_mem *rmem = &reserved_mem[reserved_mem_count]; ++ ++ if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) { ++ pr_err("Reserved memory: not enough space all defined regions.\n"); ++ return; ++ } ++ ++ rmem->fdt_node = node; ++ rmem->name = uname; ++ rmem->base = base; ++ rmem->size = size; ++ ++ reserved_mem_count++; ++ return; ++} ++ ++/** ++ * res_mem_alloc_size() - allocate reserved memory described by 'size', 'align' ++ * and 'alloc-ranges' properties ++ */ ++static int __init __reserved_mem_alloc_size(unsigned long node, ++ const char *uname, phys_addr_t *res_base, phys_addr_t *res_size) ++{ ++ int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); ++ phys_addr_t start = 0, end = 0; ++ phys_addr_t base = 0, align = 0, size; ++ unsigned long len; ++ __be32 *prop; ++ int nomap; ++ int ret; ++ ++ prop = of_get_flat_dt_prop(node, "size", &len); ++ if (!prop) ++ return -EINVAL; ++ ++ if (len != dt_root_size_cells * sizeof(__be32)) { ++ pr_err("Reserved memory: invalid size property in '%s' node.\n", ++ uname); ++ return -EINVAL; ++ } ++ size = dt_mem_next_cell(dt_root_size_cells, &prop); ++ ++ nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; ++ ++ prop = of_get_flat_dt_prop(node, "alignment", &len); ++ if (prop) { ++ if (len != dt_root_addr_cells * sizeof(__be32)) { ++ pr_err("Reserved memory: invalid alignment property in '%s' node.\n", ++ uname); ++ return -EINVAL; ++ } ++ align = dt_mem_next_cell(dt_root_addr_cells, &prop); ++ } ++ ++ prop = of_get_flat_dt_prop(node, "alloc-ranges", &len); ++ if (prop) { ++ ++ if (len % t_len != 0) { ++ pr_err("Reserved memory: invalid alloc-ranges property in '%s', skipping node.\n", ++ uname); ++ return -EINVAL; ++ } ++ ++ base = 0; ++ ++ while (len > 0) { ++ start = dt_mem_next_cell(dt_root_addr_cells, &prop); ++ end = start + dt_mem_next_cell(dt_root_size_cells, ++ &prop); ++ ++ ret = early_init_dt_alloc_reserved_memory_arch(size, ++ align, start, end, nomap, &base); ++ if (ret == 0) { ++ pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n", ++ uname, &base, ++ (unsigned long)size / SZ_1M); ++ break; ++ } ++ len -= t_len; ++ } ++ ++ } else { ++ ret = early_init_dt_alloc_reserved_memory_arch(size, align, ++ 0, 0, nomap, &base); ++ if (ret == 0) ++ pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n", ++ uname, &base, (unsigned long)size / SZ_1M); ++ } ++ ++ if (base == 0) { ++ pr_info("Reserved memory: failed to allocate memory for node '%s'\n", ++ uname); ++ return -ENOMEM; ++ } ++ ++ *res_base = base; ++ *res_size = size; ++ ++ return 0; ++} ++ ++/** ++ * fdt_init_reserved_mem - allocate and init all saved reserved memory regions ++ */ ++void __init fdt_init_reserved_mem(void) ++{ ++ int i; ++ for (i = 0; i < reserved_mem_count; i++) { ++ struct reserved_mem *rmem = &reserved_mem[i]; ++ unsigned long node = rmem->fdt_node; ++ int err = 0; ++ ++ if (rmem->size == 0) ++ err = __reserved_mem_alloc_size(node, rmem->name, ++ &rmem->base, &rmem->size); ++ } ++} +diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h +new file mode 100644 +index 0000000..89226ed +--- /dev/null ++++ b/include/linux/of_reserved_mem.h +@@ -0,0 +1,21 @@ ++#ifndef __OF_RESERVED_MEM_H ++#define __OF_RESERVED_MEM_H ++ ++struct reserved_mem { ++ const char *name; ++ unsigned long fdt_node; ++ phys_addr_t base; ++ phys_addr_t size; ++}; ++ ++#ifdef CONFIG_OF_RESERVED_MEM ++void fdt_init_reserved_mem(void); ++void fdt_reserved_mem_save_node(unsigned long node, const char *uname, ++ phys_addr_t base, phys_addr_t size); ++#else ++static inline void fdt_init_reserved_mem(void) { } ++static inline void fdt_reserved_mem_save_node(unsigned long node, ++ const char *uname, phys_addr_t base, phys_addr_t size) { } ++#endif ++ ++#endif /* __OF_RESERVED_MEM_H */ +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0051-drivers-of-add-support-for-custom-reserved-memory-dr.patch b/target/linux/ipq806x/patches/0051-drivers-of-add-support-for-custom-reserved-memory-dr.patch new file mode 100644 index 0000000000..ce01a8f084 --- /dev/null +++ b/target/linux/ipq806x/patches/0051-drivers-of-add-support-for-custom-reserved-memory-dr.patch @@ -0,0 +1,156 @@ +From fc9e25ed9db2fcfbf16a9cbbf5a1f449da78f1d9 Mon Sep 17 00:00:00 2001 +From: Marek Szyprowski <m.szyprowski@samsung.com> +Date: Fri, 28 Feb 2014 14:42:49 +0100 +Subject: [PATCH 051/182] drivers: of: add support for custom reserved memory + drivers + +Add support for custom reserved memory drivers. Call their init() function +for each reserved region and prepare for using operations provided by them +with by the reserved_mem->ops array. + +Based on previous code provided by Josh Cartwright <joshc@codeaurora.org> + +Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> +Signed-off-by: Grant Likely <grant.likely@linaro.org> +--- + drivers/of/of_reserved_mem.c | 29 +++++++++++++++++++++++++++++ + include/asm-generic/vmlinux.lds.h | 11 +++++++++++ + include/linux/of_reserved_mem.h | 32 ++++++++++++++++++++++++++++++++ + 3 files changed, 72 insertions(+) + +diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c +index 69b8117..daaaf93 100644 +--- a/drivers/of/of_reserved_mem.c ++++ b/drivers/of/of_reserved_mem.c +@@ -170,6 +170,33 @@ static int __init __reserved_mem_alloc_size(unsigned long node, + return 0; + } + ++static const struct of_device_id __rmem_of_table_sentinel ++ __used __section(__reservedmem_of_table_end); ++ ++/** ++ * res_mem_init_node() - call region specific reserved memory init code ++ */ ++static int __init __reserved_mem_init_node(struct reserved_mem *rmem) ++{ ++ extern const struct of_device_id __reservedmem_of_table[]; ++ const struct of_device_id *i; ++ ++ for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) { ++ reservedmem_of_init_fn initfn = i->data; ++ const char *compat = i->compatible; ++ ++ if (!of_flat_dt_is_compatible(rmem->fdt_node, compat)) ++ continue; ++ ++ if (initfn(rmem, rmem->fdt_node, rmem->name) == 0) { ++ pr_info("Reserved memory: initialized node %s, compatible id %s\n", ++ rmem->name, compat); ++ return 0; ++ } ++ } ++ return -ENOENT; ++} ++ + /** + * fdt_init_reserved_mem - allocate and init all saved reserved memory regions + */ +@@ -184,5 +211,7 @@ void __init fdt_init_reserved_mem(void) + if (rmem->size == 0) + err = __reserved_mem_alloc_size(node, rmem->name, + &rmem->base, &rmem->size); ++ if (err == 0) ++ __reserved_mem_init_node(rmem); + } + } +diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h +index bd02ca7..146e4ff 100644 +--- a/include/asm-generic/vmlinux.lds.h ++++ b/include/asm-generic/vmlinux.lds.h +@@ -167,6 +167,16 @@ + #define CLK_OF_TABLES() + #endif + ++#ifdef CONFIG_OF_RESERVED_MEM ++#define RESERVEDMEM_OF_TABLES() \ ++ . = ALIGN(8); \ ++ VMLINUX_SYMBOL(__reservedmem_of_table) = .; \ ++ *(__reservedmem_of_table) \ ++ *(__reservedmem_of_table_end) ++#else ++#define RESERVEDMEM_OF_TABLES() ++#endif ++ + #ifdef CONFIG_SMP + #define CPU_METHOD_OF_TABLES() . = ALIGN(8); \ + VMLINUX_SYMBOL(__cpu_method_of_table_begin) = .; \ +@@ -499,6 +509,7 @@ + TRACE_SYSCALLS() \ + MEM_DISCARD(init.rodata) \ + CLK_OF_TABLES() \ ++ RESERVEDMEM_OF_TABLES() \ + CLKSRC_OF_TABLES() \ + CPU_METHOD_OF_TABLES() \ + KERNEL_DTB() \ +diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h +index 89226ed..9b1fbb7 100644 +--- a/include/linux/of_reserved_mem.h ++++ b/include/linux/of_reserved_mem.h +@@ -1,21 +1,53 @@ + #ifndef __OF_RESERVED_MEM_H + #define __OF_RESERVED_MEM_H + ++struct device; ++struct of_phandle_args; ++struct reserved_mem_ops; ++ + struct reserved_mem { + const char *name; + unsigned long fdt_node; ++ const struct reserved_mem_ops *ops; + phys_addr_t base; + phys_addr_t size; ++ void *priv; ++}; ++ ++struct reserved_mem_ops { ++ void (*device_init)(struct reserved_mem *rmem, ++ struct device *dev); ++ void (*device_release)(struct reserved_mem *rmem, ++ struct device *dev); + }; + ++typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem, ++ unsigned long node, const char *uname); ++ + #ifdef CONFIG_OF_RESERVED_MEM + void fdt_init_reserved_mem(void); + void fdt_reserved_mem_save_node(unsigned long node, const char *uname, + phys_addr_t base, phys_addr_t size); ++ ++#define RESERVEDMEM_OF_DECLARE(name, compat, init) \ ++ static const struct of_device_id __reservedmem_of_table_##name \ ++ __used __section(__reservedmem_of_table) \ ++ = { .compatible = compat, \ ++ .data = (init == (reservedmem_of_init_fn)NULL) ? \ ++ init : init } ++ + #else + static inline void fdt_init_reserved_mem(void) { } + static inline void fdt_reserved_mem_save_node(unsigned long node, + const char *uname, phys_addr_t base, phys_addr_t size) { } ++ ++#define RESERVEDMEM_OF_DECLARE(name, compat, init) \ ++ static const struct of_device_id __reservedmem_of_table_##name \ ++ __attribute__((unused)) \ ++ = { .compatible = compat, \ ++ .data = (init == (reservedmem_of_init_fn)NULL) ? \ ++ init : init } ++ + #endif + + #endif /* __OF_RESERVED_MEM_H */ +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0052-arm-add-support-for-reserved-memory-defined-by-devic.patch b/target/linux/ipq806x/patches/0052-arm-add-support-for-reserved-memory-defined-by-devic.patch new file mode 100644 index 0000000000..8ff56329e0 --- /dev/null +++ b/target/linux/ipq806x/patches/0052-arm-add-support-for-reserved-memory-defined-by-devic.patch @@ -0,0 +1,43 @@ +From 95c831bca9181df62230171fd66286d08ea886a8 Mon Sep 17 00:00:00 2001 +From: Marek Szyprowski <m.szyprowski@samsung.com> +Date: Fri, 28 Feb 2014 14:42:54 +0100 +Subject: [PATCH 052/182] arm: add support for reserved memory defined by + device tree + +Enable reserved memory initialization from device tree. + +Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> +Signed-off-by: Grant Likely <grant.likely@linaro.org> +--- + arch/arm/Kconfig | 1 + + arch/arm/mm/init.c | 2 ++ + 2 files changed, 3 insertions(+) + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index d02ce70..4332e8d 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -1922,6 +1922,7 @@ config USE_OF + select IRQ_DOMAIN + select OF + select OF_EARLY_FLATTREE ++ select OF_RESERVED_MEM + help + Include support for flattened device tree machine descriptions. + +diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c +index 804d615..2a77ba8 100644 +--- a/arch/arm/mm/init.c ++++ b/arch/arm/mm/init.c +@@ -323,6 +323,8 @@ void __init arm_memblock_init(struct meminfo *mi, + if (mdesc->reserve) + mdesc->reserve(); + ++ early_init_fdt_scan_reserved_mem(); ++ + /* + * reserve memory for DMA contigouos allocations, + * must come from DMA area inside low memory +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0053-of-document-bindings-for-reserved-memory-nodes.patch b/target/linux/ipq806x/patches/0053-of-document-bindings-for-reserved-memory-nodes.patch new file mode 100644 index 0000000000..b499e17551 --- /dev/null +++ b/target/linux/ipq806x/patches/0053-of-document-bindings-for-reserved-memory-nodes.patch @@ -0,0 +1,164 @@ +From 060a8e3db75ef98fb296b03b8fb0cdfcbcc76c6e Mon Sep 17 00:00:00 2001 +From: Grant Likely <grant.likely@linaro.org> +Date: Fri, 28 Feb 2014 14:42:46 +0100 +Subject: [PATCH 053/182] of: document bindings for reserved-memory nodes + +Reserved memory nodes allow for the reservation of static (fixed +address) regions, or dynamically allocated regions for a specific +purpose. + +[joshc: Based on binding document proposed (in non-patch form) here: + http://lkml.kernel.org/g/20131030134702.19B57C402A0@trevor.secretlab.ca + adapted to support #memory-region-cells] +Signed-off-by: Josh Cartwright <joshc@codeaurora.org> +[mszyprow: removed #memory-region-cells property] +Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> +[grant.likely: removed residual #memory-region-cells example] +Signed-off-by: Grant Likely <grant.likely@linaro.org> +--- + .../bindings/reserved-memory/reserved-memory.txt | 133 ++++++++++++++++++++ + 1 file changed, 133 insertions(+) + create mode 100644 Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt + +diff --git a/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt +new file mode 100644 +index 0000000..3da0ebd +--- /dev/null ++++ b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt +@@ -0,0 +1,133 @@ ++*** Reserved memory regions *** ++ ++Reserved memory is specified as a node under the /reserved-memory node. ++The operating system shall exclude reserved memory from normal usage ++one can create child nodes describing particular reserved (excluded from ++normal use) memory regions. Such memory regions are usually designed for ++the special usage by various device drivers. ++ ++Parameters for each memory region can be encoded into the device tree ++with the following nodes: ++ ++/reserved-memory node ++--------------------- ++#address-cells, #size-cells (required) - standard definition ++ - Should use the same values as the root node ++ranges (required) - standard definition ++ - Should be empty ++ ++/reserved-memory/ child nodes ++----------------------------- ++Each child of the reserved-memory node specifies one or more regions of ++reserved memory. Each child node may either use a 'reg' property to ++specify a specific range of reserved memory, or a 'size' property with ++optional constraints to request a dynamically allocated block of memory. ++ ++Following the generic-names recommended practice, node names should ++reflect the purpose of the node (ie. "framebuffer" or "dma-pool"). Unit ++address (@<address>) should be appended to the name if the node is a ++static allocation. ++ ++Properties: ++Requires either a) or b) below. ++a) static allocation ++ reg (required) - standard definition ++b) dynamic allocation ++ size (required) - length based on parent's #size-cells ++ - Size in bytes of memory to reserve. ++ alignment (optional) - length based on parent's #size-cells ++ - Address boundary for alignment of allocation. ++ alloc-ranges (optional) - prop-encoded-array (address, length pairs). ++ - Specifies regions of memory that are ++ acceptable to allocate from. ++ ++If both reg and size are present, then the reg property takes precedence ++and size is ignored. ++ ++Additional properties: ++compatible (optional) - standard definition ++ - may contain the following strings: ++ - shared-dma-pool: This indicates a region of memory meant to be ++ used as a shared pool of DMA buffers for a set of devices. It can ++ be used by an operating system to instanciate the necessary pool ++ management subsystem if necessary. ++ - vendor specific string in the form <vendor>,[<device>-]<usage> ++no-map (optional) - empty property ++ - Indicates the operating system must not create a virtual mapping ++ of the region as part of its standard mapping of system memory, ++ nor permit speculative access to it under any circumstances other ++ than under the control of the device driver using the region. ++reusable (optional) - empty property ++ - The operating system can use the memory in this region with the ++ limitation that the device driver(s) owning the region need to be ++ able to reclaim it back. Typically that means that the operating ++ system can use that region to store volatile or cached data that ++ can be otherwise regenerated or migrated elsewhere. ++ ++Linux implementation note: ++- If a "linux,cma-default" property is present, then Linux will use the ++ region for the default pool of the contiguous memory allocator. ++ ++Device node references to reserved memory ++----------------------------------------- ++Regions in the /reserved-memory node may be referenced by other device ++nodes by adding a memory-region property to the device node. ++ ++memory-region (optional) - phandle, specifier pairs to children of /reserved-memory ++ ++Example ++------- ++This example defines 3 contiguous regions are defined for Linux kernel: ++one default of all device drivers (named linux,cma@72000000 and 64MiB in size), ++one dedicated to the framebuffer device (named framebuffer@78000000, 8MiB), and ++one for multimedia processing (named multimedia-memory@77000000, 64MiB). ++ ++/ { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ memory { ++ reg = <0x40000000 0x40000000>; ++ }; ++ ++ reserved-memory { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ /* global autoconfigured region for contiguous allocations */ ++ linux,cma { ++ compatible = "shared-dma-pool"; ++ reusable; ++ size = <0x4000000>; ++ alignment = <0x2000>; ++ linux,cma-default; ++ }; ++ ++ display_reserved: framebuffer@78000000 { ++ reg = <0x78000000 0x800000>; ++ }; ++ ++ multimedia_reserved: multimedia@77000000 { ++ compatible = "acme,multimedia-memory"; ++ reg = <0x77000000 0x4000000>; ++ }; ++ }; ++ ++ /* ... */ ++ ++ fb0: video@12300000 { ++ memory-region = <&display_reserved>; ++ /* ... */ ++ }; ++ ++ scaler: scaler@12500000 { ++ memory-region = <&multimedia_reserved>; ++ /* ... */ ++ }; ++ ++ codec: codec@12600000 { ++ memory-region = <&multimedia_reserved>; ++ /* ... */ ++ }; ++}; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0054-of-only-scan-for-reserved-mem-when-fdt-present.patch b/target/linux/ipq806x/patches/0054-of-only-scan-for-reserved-mem-when-fdt-present.patch new file mode 100644 index 0000000000..a5423d8446 --- /dev/null +++ b/target/linux/ipq806x/patches/0054-of-only-scan-for-reserved-mem-when-fdt-present.patch @@ -0,0 +1,35 @@ +From 884ab569aec3a0847702dff0dad133bfb67e234c Mon Sep 17 00:00:00 2001 +From: Josh Cartwright <joshc@codeaurora.org> +Date: Thu, 13 Mar 2014 16:36:36 -0500 +Subject: [PATCH 054/182] of: only scan for reserved mem when fdt present + +When the reserved memory patches hit -next, several legacy (non-DT) boot +failures were detected and bisected down to that commit. There needs to +be some sanity checking whether a DT is even present before parsing the +reserved ranges. + +Reported-by: Kevin Hilman <khilman@linaro.org> +Signed-off-by: Josh Cartwright <joshc@codeaurora.org> +Tested-by: Kevin Hilman <khilman@linaro.org> +Signed-off-by: Grant Likely <grant.likely@linaro.org> +--- + drivers/of/fdt.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c +index 510c0d8..501bc83 100644 +--- a/drivers/of/fdt.c ++++ b/drivers/of/fdt.c +@@ -557,6 +557,9 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, + */ + void __init early_init_fdt_scan_reserved_mem(void) + { ++ if (!initial_boot_params) ++ return; ++ + of_scan_flat_dt(__fdt_scan_reserved_mem, NULL); + fdt_init_reserved_mem(); + } +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0055-spmi-Linux-driver-framework-for-SPMI.patch b/target/linux/ipq806x/patches/0055-spmi-Linux-driver-framework-for-SPMI.patch new file mode 100644 index 0000000000..8b5943894d --- /dev/null +++ b/target/linux/ipq806x/patches/0055-spmi-Linux-driver-framework-for-SPMI.patch @@ -0,0 +1,943 @@ +From 1b0018dfd6295cbcc87738601b84bf49f3004419 Mon Sep 17 00:00:00 2001 +From: Kenneth Heitke <kheitke@codeaurora.org> +Date: Wed, 12 Feb 2014 13:44:22 -0600 +Subject: [PATCH 055/182] spmi: Linux driver framework for SPMI + +System Power Management Interface (SPMI) is a specification +developed by the MIPI (Mobile Industry Process Interface) Alliance +optimized for the real time control of Power Management ICs (PMIC). + +SPMI is a two-wire serial interface that supports up to 4 master +devices and up to 16 logical slaves. + +The framework supports message APIs, multiple busses (1 controller +per bus) and multiple clients/slave devices per controller. + +Signed-off-by: Kenneth Heitke <kheitke@codeaurora.org> +Signed-off-by: Michael Bohan <mbohan@codeaurora.org> +Signed-off-by: Josh Cartwright <joshc@codeaurora.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/Kconfig | 2 + + drivers/Makefile | 1 + + drivers/spmi/Kconfig | 9 + + drivers/spmi/Makefile | 4 + + drivers/spmi/spmi.c | 609 +++++++++++++++++++++++++++++++++++++++ + include/dt-bindings/spmi/spmi.h | 18 ++ + include/linux/mod_devicetable.h | 8 + + include/linux/spmi.h | 191 ++++++++++++ + 8 files changed, 842 insertions(+) + create mode 100644 drivers/spmi/Kconfig + create mode 100644 drivers/spmi/Makefile + create mode 100644 drivers/spmi/spmi.c + create mode 100644 include/dt-bindings/spmi/spmi.h + create mode 100644 include/linux/spmi.h + +diff --git a/drivers/Kconfig b/drivers/Kconfig +index b3138fb..e0a4ae6 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -52,6 +52,8 @@ source "drivers/i2c/Kconfig" + + source "drivers/spi/Kconfig" + ++source "drivers/spmi/Kconfig" ++ + source "drivers/hsi/Kconfig" + + source "drivers/pps/Kconfig" +diff --git a/drivers/Makefile b/drivers/Makefile +index 8e3b8b0..3d6de8b 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -66,6 +66,7 @@ obj-$(CONFIG_ATA) += ata/ + obj-$(CONFIG_TARGET_CORE) += target/ + obj-$(CONFIG_MTD) += mtd/ + obj-$(CONFIG_SPI) += spi/ ++obj-$(CONFIG_SPMI) += spmi/ + obj-y += hsi/ + obj-y += net/ + obj-$(CONFIG_ATM) += atm/ +diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig +new file mode 100644 +index 0000000..1dbfee0 +--- /dev/null ++++ b/drivers/spmi/Kconfig +@@ -0,0 +1,9 @@ ++# ++# SPMI driver configuration ++# ++menuconfig SPMI ++ tristate "SPMI support" ++ help ++ SPMI (System Power Management Interface) is a two-wire ++ serial interface between baseband and application processors ++ and Power Management Integrated Circuits (PMIC). +diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile +new file mode 100644 +index 0000000..1de1acd +--- /dev/null ++++ b/drivers/spmi/Makefile +@@ -0,0 +1,4 @@ ++# ++# Makefile for kernel SPMI framework. ++# ++obj-$(CONFIG_SPMI) += spmi.o +diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c +new file mode 100644 +index 0000000..6122c8f +--- /dev/null ++++ b/drivers/spmi/spmi.c +@@ -0,0 +1,609 @@ ++/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#include <linux/kernel.h> ++#include <linux/errno.h> ++#include <linux/idr.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_device.h> ++#include <linux/platform_device.h> ++#include <linux/spmi.h> ++#include <linux/module.h> ++#include <linux/pm_runtime.h> ++ ++#include <dt-bindings/spmi/spmi.h> ++ ++static DEFINE_IDA(ctrl_ida); ++ ++static void spmi_dev_release(struct device *dev) ++{ ++ struct spmi_device *sdev = to_spmi_device(dev); ++ kfree(sdev); ++} ++ ++static const struct device_type spmi_dev_type = { ++ .release = spmi_dev_release, ++}; ++ ++static void spmi_ctrl_release(struct device *dev) ++{ ++ struct spmi_controller *ctrl = to_spmi_controller(dev); ++ ida_simple_remove(&ctrl_ida, ctrl->nr); ++ kfree(ctrl); ++} ++ ++static const struct device_type spmi_ctrl_type = { ++ .release = spmi_ctrl_release, ++}; ++ ++#ifdef CONFIG_PM_RUNTIME ++static int spmi_runtime_suspend(struct device *dev) ++{ ++ struct spmi_device *sdev = to_spmi_device(dev); ++ int err; ++ ++ err = pm_generic_runtime_suspend(dev); ++ if (err) ++ return err; ++ ++ return spmi_command_sleep(sdev); ++} ++ ++static int spmi_runtime_resume(struct device *dev) ++{ ++ struct spmi_device *sdev = to_spmi_device(dev); ++ int err; ++ ++ err = spmi_command_wakeup(sdev); ++ if (err) ++ return err; ++ ++ return pm_generic_runtime_resume(dev); ++} ++#endif ++ ++static const struct dev_pm_ops spmi_pm_ops = { ++ SET_RUNTIME_PM_OPS( ++ spmi_runtime_suspend, ++ spmi_runtime_resume, ++ NULL ++ ) ++}; ++ ++static int spmi_device_match(struct device *dev, struct device_driver *drv) ++{ ++ if (of_driver_match_device(dev, drv)) ++ return 1; ++ ++ if (drv->name) ++ return strncmp(dev_name(dev), drv->name, ++ SPMI_NAME_SIZE) == 0; ++ ++ return 0; ++} ++ ++/** ++ * spmi_device_add() - add a device previously constructed via spmi_device_alloc() ++ * @sdev: spmi_device to be added ++ */ ++int spmi_device_add(struct spmi_device *sdev) ++{ ++ struct spmi_controller *ctrl = sdev->ctrl; ++ int err; ++ ++ dev_set_name(&sdev->dev, "%d-%02x", ctrl->nr, sdev->usid); ++ ++ err = device_add(&sdev->dev); ++ if (err < 0) { ++ dev_err(&sdev->dev, "Can't add %s, status %d\n", ++ dev_name(&sdev->dev), err); ++ goto err_device_add; ++ } ++ ++ dev_dbg(&sdev->dev, "device %s registered\n", dev_name(&sdev->dev)); ++ ++err_device_add: ++ return err; ++} ++EXPORT_SYMBOL_GPL(spmi_device_add); ++ ++/** ++ * spmi_device_remove(): remove an SPMI device ++ * @sdev: spmi_device to be removed ++ */ ++void spmi_device_remove(struct spmi_device *sdev) ++{ ++ device_unregister(&sdev->dev); ++} ++EXPORT_SYMBOL_GPL(spmi_device_remove); ++ ++static inline int ++spmi_cmd(struct spmi_controller *ctrl, u8 opcode, u8 sid) ++{ ++ if (!ctrl || !ctrl->cmd || ctrl->dev.type != &spmi_ctrl_type) ++ return -EINVAL; ++ ++ return ctrl->cmd(ctrl, opcode, sid); ++} ++ ++static inline int spmi_read_cmd(struct spmi_controller *ctrl, u8 opcode, ++ u8 sid, u16 addr, u8 *buf, size_t len) ++{ ++ if (!ctrl || !ctrl->read_cmd || ctrl->dev.type != &spmi_ctrl_type) ++ return -EINVAL; ++ ++ return ctrl->read_cmd(ctrl, opcode, sid, addr, buf, len); ++} ++ ++static inline int spmi_write_cmd(struct spmi_controller *ctrl, u8 opcode, ++ u8 sid, u16 addr, const u8 *buf, size_t len) ++{ ++ if (!ctrl || !ctrl->write_cmd || ctrl->dev.type != &spmi_ctrl_type) ++ return -EINVAL; ++ ++ return ctrl->write_cmd(ctrl, opcode, sid, addr, buf, len); ++} ++ ++/** ++ * spmi_register_read() - register read ++ * @sdev: SPMI device. ++ * @addr: slave register address (5-bit address). ++ * @buf: buffer to be populated with data from the Slave. ++ * ++ * Reads 1 byte of data from a Slave device register. ++ */ ++int spmi_register_read(struct spmi_device *sdev, u8 addr, u8 *buf) ++{ ++ /* 5-bit register address */ ++ if (addr > 0x1F) ++ return -EINVAL; ++ ++ return spmi_read_cmd(sdev->ctrl, SPMI_CMD_READ, sdev->usid, addr, ++ buf, 1); ++} ++EXPORT_SYMBOL_GPL(spmi_register_read); ++ ++/** ++ * spmi_ext_register_read() - extended register read ++ * @sdev: SPMI device. ++ * @addr: slave register address (8-bit address). ++ * @buf: buffer to be populated with data from the Slave. ++ * @len: the request number of bytes to read (up to 16 bytes). ++ * ++ * Reads up to 16 bytes of data from the extended register space on a ++ * Slave device. ++ */ ++int spmi_ext_register_read(struct spmi_device *sdev, u8 addr, u8 *buf, ++ size_t len) ++{ ++ /* 8-bit register address, up to 16 bytes */ ++ if (len == 0 || len > 16) ++ return -EINVAL; ++ ++ return spmi_read_cmd(sdev->ctrl, SPMI_CMD_EXT_READ, sdev->usid, addr, ++ buf, len); ++} ++EXPORT_SYMBOL_GPL(spmi_ext_register_read); ++ ++/** ++ * spmi_ext_register_readl() - extended register read long ++ * @sdev: SPMI device. ++ * @addr: slave register address (16-bit address). ++ * @buf: buffer to be populated with data from the Slave. ++ * @len: the request number of bytes to read (up to 8 bytes). ++ * ++ * Reads up to 8 bytes of data from the extended register space on a ++ * Slave device using 16-bit address. ++ */ ++int spmi_ext_register_readl(struct spmi_device *sdev, u16 addr, u8 *buf, ++ size_t len) ++{ ++ /* 16-bit register address, up to 8 bytes */ ++ if (len == 0 || len > 8) ++ return -EINVAL; ++ ++ return spmi_read_cmd(sdev->ctrl, SPMI_CMD_EXT_READL, sdev->usid, addr, ++ buf, len); ++} ++EXPORT_SYMBOL_GPL(spmi_ext_register_readl); ++ ++/** ++ * spmi_register_write() - register write ++ * @sdev: SPMI device ++ * @addr: slave register address (5-bit address). ++ * @data: buffer containing the data to be transferred to the Slave. ++ * ++ * Writes 1 byte of data to a Slave device register. ++ */ ++int spmi_register_write(struct spmi_device *sdev, u8 addr, u8 data) ++{ ++ /* 5-bit register address */ ++ if (addr > 0x1F) ++ return -EINVAL; ++ ++ return spmi_write_cmd(sdev->ctrl, SPMI_CMD_WRITE, sdev->usid, addr, ++ &data, 1); ++} ++EXPORT_SYMBOL_GPL(spmi_register_write); ++ ++/** ++ * spmi_register_zero_write() - register zero write ++ * @sdev: SPMI device. ++ * @data: the data to be written to register 0 (7-bits). ++ * ++ * Writes data to register 0 of the Slave device. ++ */ ++int spmi_register_zero_write(struct spmi_device *sdev, u8 data) ++{ ++ return spmi_write_cmd(sdev->ctrl, SPMI_CMD_ZERO_WRITE, sdev->usid, 0, ++ &data, 1); ++} ++EXPORT_SYMBOL_GPL(spmi_register_zero_write); ++ ++/** ++ * spmi_ext_register_write() - extended register write ++ * @sdev: SPMI device. ++ * @addr: slave register address (8-bit address). ++ * @buf: buffer containing the data to be transferred to the Slave. ++ * @len: the request number of bytes to read (up to 16 bytes). ++ * ++ * Writes up to 16 bytes of data to the extended register space of a ++ * Slave device. ++ */ ++int spmi_ext_register_write(struct spmi_device *sdev, u8 addr, const u8 *buf, ++ size_t len) ++{ ++ /* 8-bit register address, up to 16 bytes */ ++ if (len == 0 || len > 16) ++ return -EINVAL; ++ ++ return spmi_write_cmd(sdev->ctrl, SPMI_CMD_EXT_WRITE, sdev->usid, addr, ++ buf, len); ++} ++EXPORT_SYMBOL_GPL(spmi_ext_register_write); ++ ++/** ++ * spmi_ext_register_writel() - extended register write long ++ * @sdev: SPMI device. ++ * @addr: slave register address (16-bit address). ++ * @buf: buffer containing the data to be transferred to the Slave. ++ * @len: the request number of bytes to read (up to 8 bytes). ++ * ++ * Writes up to 8 bytes of data to the extended register space of a ++ * Slave device using 16-bit address. ++ */ ++int spmi_ext_register_writel(struct spmi_device *sdev, u16 addr, const u8 *buf, ++ size_t len) ++{ ++ /* 4-bit Slave Identifier, 16-bit register address, up to 8 bytes */ ++ if (len == 0 || len > 8) ++ return -EINVAL; ++ ++ return spmi_write_cmd(sdev->ctrl, SPMI_CMD_EXT_WRITEL, sdev->usid, ++ addr, buf, len); ++} ++EXPORT_SYMBOL_GPL(spmi_ext_register_writel); ++ ++/** ++ * spmi_command_reset() - sends RESET command to the specified slave ++ * @sdev: SPMI device. ++ * ++ * The Reset command initializes the Slave and forces all registers to ++ * their reset values. The Slave shall enter the STARTUP state after ++ * receiving a Reset command. ++ */ ++int spmi_command_reset(struct spmi_device *sdev) ++{ ++ return spmi_cmd(sdev->ctrl, SPMI_CMD_RESET, sdev->usid); ++} ++EXPORT_SYMBOL_GPL(spmi_command_reset); ++ ++/** ++ * spmi_command_sleep() - sends SLEEP command to the specified SPMI device ++ * @sdev: SPMI device. ++ * ++ * The Sleep command causes the Slave to enter the user defined SLEEP state. ++ */ ++int spmi_command_sleep(struct spmi_device *sdev) ++{ ++ return spmi_cmd(sdev->ctrl, SPMI_CMD_SLEEP, sdev->usid); ++} ++EXPORT_SYMBOL_GPL(spmi_command_sleep); ++ ++/** ++ * spmi_command_wakeup() - sends WAKEUP command to the specified SPMI device ++ * @sdev: SPMI device. ++ * ++ * The Wakeup command causes the Slave to move from the SLEEP state to ++ * the ACTIVE state. ++ */ ++int spmi_command_wakeup(struct spmi_device *sdev) ++{ ++ return spmi_cmd(sdev->ctrl, SPMI_CMD_WAKEUP, sdev->usid); ++} ++EXPORT_SYMBOL_GPL(spmi_command_wakeup); ++ ++/** ++ * spmi_command_shutdown() - sends SHUTDOWN command to the specified SPMI device ++ * @sdev: SPMI device. ++ * ++ * The Shutdown command causes the Slave to enter the SHUTDOWN state. ++ */ ++int spmi_command_shutdown(struct spmi_device *sdev) ++{ ++ return spmi_cmd(sdev->ctrl, SPMI_CMD_SHUTDOWN, sdev->usid); ++} ++EXPORT_SYMBOL_GPL(spmi_command_shutdown); ++ ++static int spmi_drv_probe(struct device *dev) ++{ ++ const struct spmi_driver *sdrv = to_spmi_driver(dev->driver); ++ struct spmi_device *sdev = to_spmi_device(dev); ++ int err; ++ ++ /* Ensure the slave is in ACTIVE state */ ++ err = spmi_command_wakeup(sdev); ++ if (err) ++ goto fail_wakeup; ++ ++ pm_runtime_get_noresume(dev); ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ ++ err = sdrv->probe(sdev); ++ if (err) ++ goto fail_probe; ++ ++ return 0; ++ ++fail_probe: ++ pm_runtime_disable(dev); ++ pm_runtime_set_suspended(dev); ++ pm_runtime_put_noidle(dev); ++fail_wakeup: ++ return err; ++} ++ ++static int spmi_drv_remove(struct device *dev) ++{ ++ const struct spmi_driver *sdrv = to_spmi_driver(dev->driver); ++ ++ pm_runtime_get_sync(dev); ++ sdrv->remove(to_spmi_device(dev)); ++ pm_runtime_put_noidle(dev); ++ ++ pm_runtime_disable(dev); ++ pm_runtime_set_suspended(dev); ++ pm_runtime_put_noidle(dev); ++ return 0; ++} ++ ++static struct bus_type spmi_bus_type = { ++ .name = "spmi", ++ .match = spmi_device_match, ++ .pm = &spmi_pm_ops, ++ .probe = spmi_drv_probe, ++ .remove = spmi_drv_remove, ++}; ++ ++/** ++ * spmi_controller_alloc() - Allocate a new SPMI device ++ * @ctrl: associated controller ++ * ++ * Caller is responsible for either calling spmi_device_add() to add the ++ * newly allocated controller, or calling spmi_device_put() to discard it. ++ */ ++struct spmi_device *spmi_device_alloc(struct spmi_controller *ctrl) ++{ ++ struct spmi_device *sdev; ++ ++ sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); ++ if (!sdev) ++ return NULL; ++ ++ sdev->ctrl = ctrl; ++ device_initialize(&sdev->dev); ++ sdev->dev.parent = &ctrl->dev; ++ sdev->dev.bus = &spmi_bus_type; ++ sdev->dev.type = &spmi_dev_type; ++ return sdev; ++} ++EXPORT_SYMBOL_GPL(spmi_device_alloc); ++ ++/** ++ * spmi_controller_alloc() - Allocate a new SPMI controller ++ * @parent: parent device ++ * @size: size of private data ++ * ++ * Caller is responsible for either calling spmi_controller_add() to add the ++ * newly allocated controller, or calling spmi_controller_put() to discard it. ++ * The allocated private data region may be accessed via ++ * spmi_controller_get_drvdata() ++ */ ++struct spmi_controller *spmi_controller_alloc(struct device *parent, ++ size_t size) ++{ ++ struct spmi_controller *ctrl; ++ int id; ++ ++ if (WARN_ON(!parent)) ++ return NULL; ++ ++ ctrl = kzalloc(sizeof(*ctrl) + size, GFP_KERNEL); ++ if (!ctrl) ++ return NULL; ++ ++ device_initialize(&ctrl->dev); ++ ctrl->dev.type = &spmi_ctrl_type; ++ ctrl->dev.bus = &spmi_bus_type; ++ ctrl->dev.parent = parent; ++ ctrl->dev.of_node = parent->of_node; ++ spmi_controller_set_drvdata(ctrl, &ctrl[1]); ++ ++ id = ida_simple_get(&ctrl_ida, 0, 0, GFP_KERNEL); ++ if (id < 0) { ++ dev_err(parent, ++ "unable to allocate SPMI controller identifier.\n"); ++ spmi_controller_put(ctrl); ++ return NULL; ++ } ++ ++ ctrl->nr = id; ++ dev_set_name(&ctrl->dev, "spmi-%d", id); ++ ++ dev_dbg(&ctrl->dev, "allocated controller 0x%p id %d\n", ctrl, id); ++ return ctrl; ++} ++EXPORT_SYMBOL_GPL(spmi_controller_alloc); ++ ++static void of_spmi_register_devices(struct spmi_controller *ctrl) ++{ ++ struct device_node *node; ++ int err; ++ ++ if (!ctrl->dev.of_node) ++ return; ++ ++ for_each_available_child_of_node(ctrl->dev.of_node, node) { ++ struct spmi_device *sdev; ++ u32 reg[2]; ++ ++ dev_dbg(&ctrl->dev, "adding child %s\n", node->full_name); ++ ++ err = of_property_read_u32_array(node, "reg", reg, 2); ++ if (err) { ++ dev_err(&ctrl->dev, ++ "node %s err (%d) does not have 'reg' property\n", ++ node->full_name, err); ++ continue; ++ } ++ ++ if (reg[1] != SPMI_USID) { ++ dev_err(&ctrl->dev, ++ "node %s contains unsupported 'reg' entry\n", ++ node->full_name); ++ continue; ++ } ++ ++ if (reg[0] >= SPMI_MAX_SLAVE_ID) { ++ dev_err(&ctrl->dev, ++ "invalid usid on node %s\n", ++ node->full_name); ++ continue; ++ } ++ ++ dev_dbg(&ctrl->dev, "read usid %02x\n", reg[0]); ++ ++ sdev = spmi_device_alloc(ctrl); ++ if (!sdev) ++ continue; ++ ++ sdev->dev.of_node = node; ++ sdev->usid = (u8) reg[0]; ++ ++ err = spmi_device_add(sdev); ++ if (err) { ++ dev_err(&sdev->dev, ++ "failure adding device. status %d\n", err); ++ spmi_device_put(sdev); ++ } ++ } ++} ++ ++/** ++ * spmi_controller_add() - Add an SPMI controller ++ * @ctrl: controller to be registered. ++ * ++ * Register a controller previously allocated via spmi_controller_alloc() with ++ * the SPMI core. ++ */ ++int spmi_controller_add(struct spmi_controller *ctrl) ++{ ++ int ret; ++ ++ /* Can't register until after driver model init */ ++ if (WARN_ON(!spmi_bus_type.p)) ++ return -EAGAIN; ++ ++ ret = device_add(&ctrl->dev); ++ if (ret) ++ return ret; ++ ++ if (IS_ENABLED(CONFIG_OF)) ++ of_spmi_register_devices(ctrl); ++ ++ dev_dbg(&ctrl->dev, "spmi-%d registered: dev:%p\n", ++ ctrl->nr, &ctrl->dev); ++ ++ return 0; ++}; ++EXPORT_SYMBOL_GPL(spmi_controller_add); ++ ++/* Remove a device associated with a controller */ ++static int spmi_ctrl_remove_device(struct device *dev, void *data) ++{ ++ struct spmi_device *spmidev = to_spmi_device(dev); ++ if (dev->type == &spmi_dev_type) ++ spmi_device_remove(spmidev); ++ return 0; ++} ++ ++/** ++ * spmi_controller_remove(): remove an SPMI controller ++ * @ctrl: controller to remove ++ * ++ * Remove a SPMI controller. Caller is responsible for calling ++ * spmi_controller_put() to discard the allocated controller. ++ */ ++void spmi_controller_remove(struct spmi_controller *ctrl) ++{ ++ int dummy; ++ ++ if (!ctrl) ++ return; ++ ++ dummy = device_for_each_child(&ctrl->dev, NULL, ++ spmi_ctrl_remove_device); ++ device_del(&ctrl->dev); ++} ++EXPORT_SYMBOL_GPL(spmi_controller_remove); ++ ++/** ++ * spmi_driver_register() - Register client driver with SPMI core ++ * @sdrv: client driver to be associated with client-device. ++ * ++ * This API will register the client driver with the SPMI framework. ++ * It is typically called from the driver's module-init function. ++ */ ++int spmi_driver_register(struct spmi_driver *sdrv) ++{ ++ sdrv->driver.bus = &spmi_bus_type; ++ return driver_register(&sdrv->driver); ++} ++EXPORT_SYMBOL_GPL(spmi_driver_register); ++ ++static void __exit spmi_exit(void) ++{ ++ bus_unregister(&spmi_bus_type); ++} ++module_exit(spmi_exit); ++ ++static int __init spmi_init(void) ++{ ++ return bus_register(&spmi_bus_type); ++} ++postcore_initcall(spmi_init); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("SPMI module"); ++MODULE_ALIAS("platform:spmi"); +diff --git a/include/dt-bindings/spmi/spmi.h b/include/dt-bindings/spmi/spmi.h +new file mode 100644 +index 0000000..d11e1e5 +--- /dev/null ++++ b/include/dt-bindings/spmi/spmi.h +@@ -0,0 +1,18 @@ ++/* 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 __DT_BINDINGS_SPMI_H ++#define __DT_BINDINGS_SPMI_H ++ ++#define SPMI_USID 0 ++#define SPMI_GSID 1 ++ ++#endif +diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h +index 45e9214..677e474 100644 +--- a/include/linux/mod_devicetable.h ++++ b/include/linux/mod_devicetable.h +@@ -432,6 +432,14 @@ struct spi_device_id { + kernel_ulong_t driver_data; /* Data private to the driver */ + }; + ++#define SPMI_NAME_SIZE 32 ++#define SPMI_MODULE_PREFIX "spmi:" ++ ++struct spmi_device_id { ++ char name[SPMI_NAME_SIZE]; ++ kernel_ulong_t driver_data; /* Data private to the driver */ ++}; ++ + /* dmi */ + enum dmi_field { + DMI_NONE, +diff --git a/include/linux/spmi.h b/include/linux/spmi.h +new file mode 100644 +index 0000000..91f5eab +--- /dev/null ++++ b/include/linux/spmi.h +@@ -0,0 +1,191 @@ ++/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#ifndef _LINUX_SPMI_H ++#define _LINUX_SPMI_H ++ ++#include <linux/types.h> ++#include <linux/device.h> ++#include <linux/mod_devicetable.h> ++ ++/* Maximum slave identifier */ ++#define SPMI_MAX_SLAVE_ID 16 ++ ++/* SPMI Commands */ ++#define SPMI_CMD_EXT_WRITE 0x00 ++#define SPMI_CMD_RESET 0x10 ++#define SPMI_CMD_SLEEP 0x11 ++#define SPMI_CMD_SHUTDOWN 0x12 ++#define SPMI_CMD_WAKEUP 0x13 ++#define SPMI_CMD_AUTHENTICATE 0x14 ++#define SPMI_CMD_MSTR_READ 0x15 ++#define SPMI_CMD_MSTR_WRITE 0x16 ++#define SPMI_CMD_TRANSFER_BUS_OWNERSHIP 0x1A ++#define SPMI_CMD_DDB_MASTER_READ 0x1B ++#define SPMI_CMD_DDB_SLAVE_READ 0x1C ++#define SPMI_CMD_EXT_READ 0x20 ++#define SPMI_CMD_EXT_WRITEL 0x30 ++#define SPMI_CMD_EXT_READL 0x38 ++#define SPMI_CMD_WRITE 0x40 ++#define SPMI_CMD_READ 0x60 ++#define SPMI_CMD_ZERO_WRITE 0x80 ++ ++/** ++ * struct spmi_device - Basic representation of an SPMI device ++ * @dev: Driver model representation of the device. ++ * @ctrl: SPMI controller managing the bus hosting this device. ++ * @usid: This devices' Unique Slave IDentifier. ++ */ ++struct spmi_device { ++ struct device dev; ++ struct spmi_controller *ctrl; ++ u8 usid; ++}; ++ ++static inline struct spmi_device *to_spmi_device(struct device *d) ++{ ++ return container_of(d, struct spmi_device, dev); ++} ++ ++static inline void *spmi_device_get_drvdata(const struct spmi_device *sdev) ++{ ++ return dev_get_drvdata(&sdev->dev); ++} ++ ++static inline void spmi_device_set_drvdata(struct spmi_device *sdev, void *data) ++{ ++ dev_set_drvdata(&sdev->dev, data); ++} ++ ++struct spmi_device *spmi_device_alloc(struct spmi_controller *ctrl); ++ ++static inline void spmi_device_put(struct spmi_device *sdev) ++{ ++ if (sdev) ++ put_device(&sdev->dev); ++} ++ ++int spmi_device_add(struct spmi_device *sdev); ++ ++void spmi_device_remove(struct spmi_device *sdev); ++ ++/** ++ * struct spmi_controller - interface to the SPMI master controller ++ * @dev: Driver model representation of the device. ++ * @nr: board-specific number identifier for this controller/bus ++ * @cmd: sends a non-data command sequence on the SPMI bus. ++ * @read_cmd: sends a register read command sequence on the SPMI bus. ++ * @write_cmd: sends a register write command sequence on the SPMI bus. ++ */ ++struct spmi_controller { ++ struct device dev; ++ unsigned int nr; ++ int (*cmd)(struct spmi_controller *ctrl, u8 opcode, u8 sid); ++ int (*read_cmd)(struct spmi_controller *ctrl, u8 opcode, ++ u8 sid, u16 addr, u8 *buf, size_t len); ++ int (*write_cmd)(struct spmi_controller *ctrl, u8 opcode, ++ u8 sid, u16 addr, const u8 *buf, size_t len); ++}; ++ ++static inline struct spmi_controller *to_spmi_controller(struct device *d) ++{ ++ return container_of(d, struct spmi_controller, dev); ++} ++ ++static inline ++void *spmi_controller_get_drvdata(const struct spmi_controller *ctrl) ++{ ++ return dev_get_drvdata(&ctrl->dev); ++} ++ ++static inline void spmi_controller_set_drvdata(struct spmi_controller *ctrl, ++ void *data) ++{ ++ dev_set_drvdata(&ctrl->dev, data); ++} ++ ++struct spmi_controller *spmi_controller_alloc(struct device *parent, ++ size_t size); ++ ++/** ++ * spmi_controller_put() - decrement controller refcount ++ * @ctrl SPMI controller. ++ */ ++static inline void spmi_controller_put(struct spmi_controller *ctrl) ++{ ++ if (ctrl) ++ put_device(&ctrl->dev); ++} ++ ++int spmi_controller_add(struct spmi_controller *ctrl); ++void spmi_controller_remove(struct spmi_controller *ctrl); ++ ++/** ++ * struct spmi_driver - SPMI slave device driver ++ * @driver: SPMI device drivers should initialize name and owner field of ++ * this structure. ++ * @probe: binds this driver to a SPMI device. ++ * @remove: unbinds this driver from the SPMI device. ++ * @shutdown: standard shutdown callback used during powerdown/halt. ++ * @suspend: standard suspend callback used during system suspend. ++ * @resume: standard resume callback used during system resume. ++ * ++ * If PM runtime support is desired for a slave, a device driver can call ++ * pm_runtime_put() from their probe() routine (and a balancing ++ * pm_runtime_get() in remove()). PM runtime support for a slave is ++ * implemented by issuing a SLEEP command to the slave on runtime_suspend(), ++ * transitioning the slave into the SLEEP state. On runtime_resume(), a WAKEUP ++ * command is sent to the slave to bring it back to ACTIVE. ++ */ ++struct spmi_driver { ++ struct device_driver driver; ++ int (*probe)(struct spmi_device *sdev); ++ void (*remove)(struct spmi_device *sdev); ++}; ++ ++static inline struct spmi_driver *to_spmi_driver(struct device_driver *d) ++{ ++ return container_of(d, struct spmi_driver, driver); ++} ++ ++int spmi_driver_register(struct spmi_driver *sdrv); ++ ++/** ++ * spmi_driver_unregister() - unregister an SPMI client driver ++ * @sdrv: the driver to unregister ++ */ ++static inline void spmi_driver_unregister(struct spmi_driver *sdrv) ++{ ++ if (sdrv) ++ driver_unregister(&sdrv->driver); ++} ++ ++#define module_spmi_driver(__spmi_driver) \ ++ module_driver(__spmi_driver, spmi_driver_register, \ ++ spmi_driver_unregister) ++ ++int spmi_register_read(struct spmi_device *sdev, u8 addr, u8 *buf); ++int spmi_ext_register_read(struct spmi_device *sdev, u8 addr, u8 *buf, ++ size_t len); ++int spmi_ext_register_readl(struct spmi_device *sdev, u16 addr, u8 *buf, ++ size_t len); ++int spmi_register_write(struct spmi_device *sdev, u8 addr, u8 data); ++int spmi_register_zero_write(struct spmi_device *sdev, u8 data); ++int spmi_ext_register_write(struct spmi_device *sdev, u8 addr, ++ const u8 *buf, size_t len); ++int spmi_ext_register_writel(struct spmi_device *sdev, u16 addr, ++ const u8 *buf, size_t len); ++int spmi_command_reset(struct spmi_device *sdev); ++int spmi_command_sleep(struct spmi_device *sdev); ++int spmi_command_wakeup(struct spmi_device *sdev); ++int spmi_command_shutdown(struct spmi_device *sdev); ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0056-spmi-Add-MSM-PMIC-Arbiter-SPMI-controller.patch b/target/linux/ipq806x/patches/0056-spmi-Add-MSM-PMIC-Arbiter-SPMI-controller.patch new file mode 100644 index 0000000000..24f841e589 --- /dev/null +++ b/target/linux/ipq806x/patches/0056-spmi-Add-MSM-PMIC-Arbiter-SPMI-controller.patch @@ -0,0 +1,477 @@ +From 1e1f53fbc2848b23af572c16d19e8004f6a7c9c1 Mon Sep 17 00:00:00 2001 +From: Kenneth Heitke <kheitke@codeaurora.org> +Date: Wed, 12 Feb 2014 13:44:24 -0600 +Subject: [PATCH 056/182] spmi: Add MSM PMIC Arbiter SPMI controller + +Qualcomm's PMIC Arbiter SPMI controller functions as a bus master and +is used to communication with one or more PMIC (slave) devices on the +SPMI bus. The PMIC Arbiter is actually a hardware wrapper around the +SPMI controller that provides concurrent and autonomous PMIC access +to various entities that need to communicate with the PMIC. + +The SPMI controller hardware handles all of the SPMI bus activity (bus +arbitration, sequence start condition, transmission of frames, etc). +This software driver uses the PMIC Arbiter register interface to +initiate command sequences on the SPMI bus. The status register is +read to determine when the command sequence has completed and whether +or not it completed successfully. + +Signed-off-by: Kenneth Heitke <kheitke@codeaurora.org> +Signed-off-by: Josh Cartwright <joshc@codeaurora.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/spmi/Kconfig | 17 ++ + drivers/spmi/Makefile | 2 + + drivers/spmi/spmi-pmic-arb.c | 405 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 424 insertions(+) + create mode 100644 drivers/spmi/spmi-pmic-arb.c + +diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig +index 1dbfee0..80b7901 100644 +--- a/drivers/spmi/Kconfig ++++ b/drivers/spmi/Kconfig +@@ -7,3 +7,20 @@ menuconfig SPMI + SPMI (System Power Management Interface) is a two-wire + serial interface between baseband and application processors + and Power Management Integrated Circuits (PMIC). ++ ++if SPMI ++ ++config SPMI_MSM_PMIC_ARB ++ tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)" ++ depends on ARM ++ depends on ARCH_MSM || COMPILE_TEST ++ default ARCH_MSM ++ help ++ If you say yes to this option, support will be included for the ++ built-in SPMI PMIC Arbiter interface on Qualcomm MSM family ++ processors. ++ ++ This is required for communicating with Qualcomm PMICs and ++ other devices that have the SPMI interface. ++ ++endif +diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile +index 1de1acd..fc75104 100644 +--- a/drivers/spmi/Makefile ++++ b/drivers/spmi/Makefile +@@ -2,3 +2,5 @@ + # Makefile for kernel SPMI framework. + # + obj-$(CONFIG_SPMI) += spmi.o ++ ++obj-$(CONFIG_SPMI_MSM_PMIC_ARB) += spmi-pmic-arb.o +diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c +new file mode 100644 +index 0000000..2dd27e8 +--- /dev/null ++++ b/drivers/spmi/spmi-pmic-arb.c +@@ -0,0 +1,405 @@ ++/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#include <linux/delay.h> ++#include <linux/err.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/slab.h> ++#include <linux/spmi.h> ++ ++/* PMIC Arbiter configuration registers */ ++#define PMIC_ARB_VERSION 0x0000 ++#define PMIC_ARB_INT_EN 0x0004 ++ ++/* PMIC Arbiter channel registers */ ++#define PMIC_ARB_CMD(N) (0x0800 + (0x80 * (N))) ++#define PMIC_ARB_CONFIG(N) (0x0804 + (0x80 * (N))) ++#define PMIC_ARB_STATUS(N) (0x0808 + (0x80 * (N))) ++#define PMIC_ARB_WDATA0(N) (0x0810 + (0x80 * (N))) ++#define PMIC_ARB_WDATA1(N) (0x0814 + (0x80 * (N))) ++#define PMIC_ARB_RDATA0(N) (0x0818 + (0x80 * (N))) ++#define PMIC_ARB_RDATA1(N) (0x081C + (0x80 * (N))) ++ ++/* Interrupt Controller */ ++#define SPMI_PIC_OWNER_ACC_STATUS(M, N) (0x0000 + ((32 * (M)) + (4 * (N)))) ++#define SPMI_PIC_ACC_ENABLE(N) (0x0200 + (4 * (N))) ++#define SPMI_PIC_IRQ_STATUS(N) (0x0600 + (4 * (N))) ++#define SPMI_PIC_IRQ_CLEAR(N) (0x0A00 + (4 * (N))) ++ ++/* Mapping Table */ ++#define SPMI_MAPPING_TABLE_REG(N) (0x0B00 + (4 * (N))) ++#define SPMI_MAPPING_BIT_INDEX(X) (((X) >> 18) & 0xF) ++#define SPMI_MAPPING_BIT_IS_0_FLAG(X) (((X) >> 17) & 0x1) ++#define SPMI_MAPPING_BIT_IS_0_RESULT(X) (((X) >> 9) & 0xFF) ++#define SPMI_MAPPING_BIT_IS_1_FLAG(X) (((X) >> 8) & 0x1) ++#define SPMI_MAPPING_BIT_IS_1_RESULT(X) (((X) >> 0) & 0xFF) ++ ++#define SPMI_MAPPING_TABLE_LEN 255 ++#define SPMI_MAPPING_TABLE_TREE_DEPTH 16 /* Maximum of 16-bits */ ++ ++/* Ownership Table */ ++#define SPMI_OWNERSHIP_TABLE_REG(N) (0x0700 + (4 * (N))) ++#define SPMI_OWNERSHIP_PERIPH2OWNER(X) ((X) & 0x7) ++ ++/* Channel Status fields */ ++enum pmic_arb_chnl_status { ++ PMIC_ARB_STATUS_DONE = (1 << 0), ++ PMIC_ARB_STATUS_FAILURE = (1 << 1), ++ PMIC_ARB_STATUS_DENIED = (1 << 2), ++ PMIC_ARB_STATUS_DROPPED = (1 << 3), ++}; ++ ++/* Command register fields */ ++#define PMIC_ARB_CMD_MAX_BYTE_COUNT 8 ++ ++/* Command Opcodes */ ++enum pmic_arb_cmd_op_code { ++ PMIC_ARB_OP_EXT_WRITEL = 0, ++ PMIC_ARB_OP_EXT_READL = 1, ++ PMIC_ARB_OP_EXT_WRITE = 2, ++ PMIC_ARB_OP_RESET = 3, ++ PMIC_ARB_OP_SLEEP = 4, ++ PMIC_ARB_OP_SHUTDOWN = 5, ++ PMIC_ARB_OP_WAKEUP = 6, ++ PMIC_ARB_OP_AUTHENTICATE = 7, ++ PMIC_ARB_OP_MSTR_READ = 8, ++ PMIC_ARB_OP_MSTR_WRITE = 9, ++ PMIC_ARB_OP_EXT_READ = 13, ++ PMIC_ARB_OP_WRITE = 14, ++ PMIC_ARB_OP_READ = 15, ++ PMIC_ARB_OP_ZERO_WRITE = 16, ++}; ++ ++/* Maximum number of support PMIC peripherals */ ++#define PMIC_ARB_MAX_PERIPHS 256 ++#define PMIC_ARB_PERIPH_ID_VALID (1 << 15) ++#define PMIC_ARB_TIMEOUT_US 100 ++#define PMIC_ARB_MAX_TRANS_BYTES (8) ++ ++#define PMIC_ARB_APID_MASK 0xFF ++#define PMIC_ARB_PPID_MASK 0xFFF ++ ++/* interrupt enable bit */ ++#define SPMI_PIC_ACC_ENABLE_BIT BIT(0) ++ ++/** ++ * spmi_pmic_arb_dev - SPMI PMIC Arbiter object ++ * ++ * @base: address of the PMIC Arbiter core registers. ++ * @intr: address of the SPMI interrupt control registers. ++ * @cnfg: address of the PMIC Arbiter configuration registers. ++ * @lock: lock to synchronize accesses. ++ * @channel: which channel to use for accesses. ++ */ ++struct spmi_pmic_arb_dev { ++ void __iomem *base; ++ void __iomem *intr; ++ void __iomem *cnfg; ++ raw_spinlock_t lock; ++ u8 channel; ++}; ++ ++static inline u32 pmic_arb_base_read(struct spmi_pmic_arb_dev *dev, u32 offset) ++{ ++ return readl_relaxed(dev->base + offset); ++} ++ ++static inline void pmic_arb_base_write(struct spmi_pmic_arb_dev *dev, ++ u32 offset, u32 val) ++{ ++ writel_relaxed(val, dev->base + offset); ++} ++ ++/** ++ * pa_read_data: reads pmic-arb's register and copy 1..4 bytes to buf ++ * @bc: byte count -1. range: 0..3 ++ * @reg: register's address ++ * @buf: output parameter, length must be bc + 1 ++ */ ++static void pa_read_data(struct spmi_pmic_arb_dev *dev, u8 *buf, u32 reg, u8 bc) ++{ ++ u32 data = pmic_arb_base_read(dev, reg); ++ memcpy(buf, &data, (bc & 3) + 1); ++} ++ ++/** ++ * pa_write_data: write 1..4 bytes from buf to pmic-arb's register ++ * @bc: byte-count -1. range: 0..3. ++ * @reg: register's address. ++ * @buf: buffer to write. length must be bc + 1. ++ */ ++static void ++pa_write_data(struct spmi_pmic_arb_dev *dev, const u8 *buf, u32 reg, u8 bc) ++{ ++ u32 data = 0; ++ memcpy(&data, buf, (bc & 3) + 1); ++ pmic_arb_base_write(dev, reg, data); ++} ++ ++static int pmic_arb_wait_for_done(struct spmi_controller *ctrl) ++{ ++ struct spmi_pmic_arb_dev *dev = spmi_controller_get_drvdata(ctrl); ++ u32 status = 0; ++ u32 timeout = PMIC_ARB_TIMEOUT_US; ++ u32 offset = PMIC_ARB_STATUS(dev->channel); ++ ++ while (timeout--) { ++ status = pmic_arb_base_read(dev, offset); ++ ++ if (status & PMIC_ARB_STATUS_DONE) { ++ if (status & PMIC_ARB_STATUS_DENIED) { ++ dev_err(&ctrl->dev, ++ "%s: transaction denied (0x%x)\n", ++ __func__, status); ++ return -EPERM; ++ } ++ ++ if (status & PMIC_ARB_STATUS_FAILURE) { ++ dev_err(&ctrl->dev, ++ "%s: transaction failed (0x%x)\n", ++ __func__, status); ++ return -EIO; ++ } ++ ++ if (status & PMIC_ARB_STATUS_DROPPED) { ++ dev_err(&ctrl->dev, ++ "%s: transaction dropped (0x%x)\n", ++ __func__, status); ++ return -EIO; ++ } ++ ++ return 0; ++ } ++ udelay(1); ++ } ++ ++ dev_err(&ctrl->dev, ++ "%s: timeout, status 0x%x\n", ++ __func__, status); ++ return -ETIMEDOUT; ++} ++ ++/* Non-data command */ ++static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid) ++{ ++ struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl); ++ unsigned long flags; ++ u32 cmd; ++ int rc; ++ ++ /* Check for valid non-data command */ ++ if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP) ++ return -EINVAL; ++ ++ cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20); ++ ++ raw_spin_lock_irqsave(&pmic_arb->lock, flags); ++ pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd); ++ rc = pmic_arb_wait_for_done(ctrl); ++ raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); ++ ++ return rc; ++} ++ ++static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, ++ u16 addr, u8 *buf, size_t len) ++{ ++ struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl); ++ unsigned long flags; ++ u8 bc = len - 1; ++ u32 cmd; ++ int rc; ++ ++ if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { ++ dev_err(&ctrl->dev, ++ "pmic-arb supports 1..%d bytes per trans, but %d requested", ++ PMIC_ARB_MAX_TRANS_BYTES, len); ++ return -EINVAL; ++ } ++ ++ /* Check the opcode */ ++ if (opc >= 0x60 && opc <= 0x7F) ++ opc = PMIC_ARB_OP_READ; ++ else if (opc >= 0x20 && opc <= 0x2F) ++ opc = PMIC_ARB_OP_EXT_READ; ++ else if (opc >= 0x38 && opc <= 0x3F) ++ opc = PMIC_ARB_OP_EXT_READL; ++ else ++ return -EINVAL; ++ ++ cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7); ++ ++ raw_spin_lock_irqsave(&pmic_arb->lock, flags); ++ pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd); ++ rc = pmic_arb_wait_for_done(ctrl); ++ if (rc) ++ goto done; ++ ++ pa_read_data(pmic_arb, buf, PMIC_ARB_RDATA0(pmic_arb->channel), ++ min_t(u8, bc, 3)); ++ ++ if (bc > 3) ++ pa_read_data(pmic_arb, buf + 4, ++ PMIC_ARB_RDATA1(pmic_arb->channel), bc - 4); ++ ++done: ++ raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); ++ return rc; ++} ++ ++static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, ++ u16 addr, const u8 *buf, size_t len) ++{ ++ struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl); ++ unsigned long flags; ++ u8 bc = len - 1; ++ u32 cmd; ++ int rc; ++ ++ if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { ++ dev_err(&ctrl->dev, ++ "pmic-arb supports 1..%d bytes per trans, but:%d requested", ++ PMIC_ARB_MAX_TRANS_BYTES, len); ++ return -EINVAL; ++ } ++ ++ /* Check the opcode */ ++ if (opc >= 0x40 && opc <= 0x5F) ++ opc = PMIC_ARB_OP_WRITE; ++ else if (opc >= 0x00 && opc <= 0x0F) ++ opc = PMIC_ARB_OP_EXT_WRITE; ++ else if (opc >= 0x30 && opc <= 0x37) ++ opc = PMIC_ARB_OP_EXT_WRITEL; ++ else if (opc >= 0x80 && opc <= 0xFF) ++ opc = PMIC_ARB_OP_ZERO_WRITE; ++ else ++ return -EINVAL; ++ ++ cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7); ++ ++ /* Write data to FIFOs */ ++ raw_spin_lock_irqsave(&pmic_arb->lock, flags); ++ pa_write_data(pmic_arb, buf, PMIC_ARB_WDATA0(pmic_arb->channel) ++ , min_t(u8, bc, 3)); ++ if (bc > 3) ++ pa_write_data(pmic_arb, buf + 4, ++ PMIC_ARB_WDATA1(pmic_arb->channel), bc - 4); ++ ++ /* Start the transaction */ ++ pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd); ++ rc = pmic_arb_wait_for_done(ctrl); ++ raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); ++ ++ return rc; ++} ++ ++static int spmi_pmic_arb_probe(struct platform_device *pdev) ++{ ++ struct spmi_pmic_arb_dev *pa; ++ struct spmi_controller *ctrl; ++ struct resource *res; ++ u32 channel; ++ int err, i; ++ ++ ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa)); ++ if (!ctrl) ++ return -ENOMEM; ++ ++ pa = spmi_controller_get_drvdata(ctrl); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); ++ pa->base = devm_ioremap_resource(&ctrl->dev, res); ++ if (IS_ERR(pa->base)) { ++ err = PTR_ERR(pa->base); ++ goto err_put_ctrl; ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr"); ++ pa->intr = devm_ioremap_resource(&ctrl->dev, res); ++ if (IS_ERR(pa->intr)) { ++ err = PTR_ERR(pa->intr); ++ goto err_put_ctrl; ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg"); ++ pa->cnfg = devm_ioremap_resource(&ctrl->dev, res); ++ if (IS_ERR(pa->cnfg)) { ++ err = PTR_ERR(pa->cnfg); ++ goto err_put_ctrl; ++ } ++ ++ err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel); ++ if (err) { ++ dev_err(&pdev->dev, "channel unspecified.\n"); ++ goto err_put_ctrl; ++ } ++ ++ if (channel > 5) { ++ dev_err(&pdev->dev, "invalid channel (%u) specified.\n", ++ channel); ++ goto err_put_ctrl; ++ } ++ ++ pa->channel = channel; ++ ++ platform_set_drvdata(pdev, ctrl); ++ raw_spin_lock_init(&pa->lock); ++ ++ ctrl->cmd = pmic_arb_cmd; ++ ctrl->read_cmd = pmic_arb_read_cmd; ++ ctrl->write_cmd = pmic_arb_write_cmd; ++ ++ err = spmi_controller_add(ctrl); ++ if (err) ++ goto err_put_ctrl; ++ ++ dev_dbg(&ctrl->dev, "PMIC Arb Version 0x%x\n", ++ pmic_arb_base_read(pa, PMIC_ARB_VERSION)); ++ ++ return 0; ++ ++err_put_ctrl: ++ spmi_controller_put(ctrl); ++ return err; ++} ++ ++static int spmi_pmic_arb_remove(struct platform_device *pdev) ++{ ++ struct spmi_controller *ctrl = platform_get_drvdata(pdev); ++ spmi_controller_remove(ctrl); ++ spmi_controller_put(ctrl); ++ return 0; ++} ++ ++static const struct of_device_id spmi_pmic_arb_match_table[] = { ++ { .compatible = "qcom,spmi-pmic-arb", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, spmi_pmic_arb_match_table); ++ ++static struct platform_driver spmi_pmic_arb_driver = { ++ .probe = spmi_pmic_arb_probe, ++ .remove = spmi_pmic_arb_remove, ++ .driver = { ++ .name = "spmi_pmic_arb", ++ .owner = THIS_MODULE, ++ .of_match_table = spmi_pmic_arb_match_table, ++ }, ++}; ++module_platform_driver(spmi_pmic_arb_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:spmi_pmic_arb"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0057-spmi-pmic_arb-add-support-for-interrupt-handling.patch b/target/linux/ipq806x/patches/0057-spmi-pmic_arb-add-support-for-interrupt-handling.patch new file mode 100644 index 0000000000..cbd49b0f76 --- /dev/null +++ b/target/linux/ipq806x/patches/0057-spmi-pmic_arb-add-support-for-interrupt-handling.patch @@ -0,0 +1,494 @@ +From b5bc51d44485c7ce0ca180a8c5de11a206f686e8 Mon Sep 17 00:00:00 2001 +From: Josh Cartwright <joshc@codeaurora.org> +Date: Wed, 12 Feb 2014 13:44:25 -0600 +Subject: [PATCH 057/182] spmi: pmic_arb: add support for interrupt handling + +The Qualcomm PMIC Arbiter, in addition to being a basic SPMI controller, +also implements interrupt handling for slave devices. Note, this is +outside the scope of SPMI, as SPMI leaves interrupt handling completely +unspecified. + +Extend the driver to provide a irq_chip implementation and chained irq +handling which allows for these interrupts to be used. + +Cc: Thomas Gleixner <tglx@linutronix.de> +Signed-off-by: Josh Cartwright <joshc@codeaurora.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/spmi/Kconfig | 1 + + drivers/spmi/spmi-pmic-arb.c | 377 +++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 376 insertions(+), 2 deletions(-) + +diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig +index 80b7901..075bd79 100644 +--- a/drivers/spmi/Kconfig ++++ b/drivers/spmi/Kconfig +@@ -13,6 +13,7 @@ if SPMI + config SPMI_MSM_PMIC_ARB + tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)" + depends on ARM ++ depends on IRQ_DOMAIN + depends on ARCH_MSM || COMPILE_TEST + default ARCH_MSM + help +diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c +index 2dd27e8..246e03a 100644 +--- a/drivers/spmi/spmi-pmic-arb.c ++++ b/drivers/spmi/spmi-pmic-arb.c +@@ -13,6 +13,9 @@ + #include <linux/err.h> + #include <linux/interrupt.h> + #include <linux/io.h> ++#include <linux/irqchip/chained_irq.h> ++#include <linux/irqdomain.h> ++#include <linux/irq.h> + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/of.h> +@@ -103,6 +106,14 @@ enum pmic_arb_cmd_op_code { + * @cnfg: address of the PMIC Arbiter configuration registers. + * @lock: lock to synchronize accesses. + * @channel: which channel to use for accesses. ++ * @irq: PMIC ARB interrupt. ++ * @ee: the current Execution Environment ++ * @min_apid: minimum APID (used for bounding IRQ search) ++ * @max_apid: maximum APID ++ * @mapping_table: in-memory copy of PPID -> APID mapping table. ++ * @domain: irq domain object for PMIC IRQ domain ++ * @spmic: SPMI controller object ++ * @apid_to_ppid: cached mapping from APID to PPID + */ + struct spmi_pmic_arb_dev { + void __iomem *base; +@@ -110,6 +121,14 @@ struct spmi_pmic_arb_dev { + void __iomem *cnfg; + raw_spinlock_t lock; + u8 channel; ++ int irq; ++ u8 ee; ++ u8 min_apid; ++ u8 max_apid; ++ u32 mapping_table[SPMI_MAPPING_TABLE_LEN]; ++ struct irq_domain *domain; ++ struct spmi_controller *spmic; ++ u16 apid_to_ppid[256]; + }; + + static inline u32 pmic_arb_base_read(struct spmi_pmic_arb_dev *dev, u32 offset) +@@ -306,12 +325,316 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, + return rc; + } + ++enum qpnpint_regs { ++ QPNPINT_REG_RT_STS = 0x10, ++ QPNPINT_REG_SET_TYPE = 0x11, ++ QPNPINT_REG_POLARITY_HIGH = 0x12, ++ QPNPINT_REG_POLARITY_LOW = 0x13, ++ QPNPINT_REG_LATCHED_CLR = 0x14, ++ QPNPINT_REG_EN_SET = 0x15, ++ QPNPINT_REG_EN_CLR = 0x16, ++ QPNPINT_REG_LATCHED_STS = 0x18, ++}; ++ ++struct spmi_pmic_arb_qpnpint_type { ++ u8 type; /* 1 -> edge */ ++ u8 polarity_high; ++ u8 polarity_low; ++} __packed; ++ ++/* Simplified accessor functions for irqchip callbacks */ ++static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf, ++ size_t len) ++{ ++ struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); ++ u8 sid = d->hwirq >> 24; ++ u8 per = d->hwirq >> 16; ++ ++ if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid, ++ (per << 8) + reg, buf, len)) ++ dev_err_ratelimited(&pa->spmic->dev, ++ "failed irqchip transaction on %x\n", ++ d->irq); ++} ++ ++static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len) ++{ ++ struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); ++ u8 sid = d->hwirq >> 24; ++ u8 per = d->hwirq >> 16; ++ ++ if (pmic_arb_read_cmd(pa->spmic, SPMI_CMD_EXT_READL, sid, ++ (per << 8) + reg, buf, len)) ++ dev_err_ratelimited(&pa->spmic->dev, ++ "failed irqchip transaction on %x\n", ++ d->irq); ++} ++ ++static void periph_interrupt(struct spmi_pmic_arb_dev *pa, u8 apid) ++{ ++ unsigned int irq; ++ u32 status; ++ int id; ++ ++ status = readl_relaxed(pa->intr + SPMI_PIC_IRQ_STATUS(apid)); ++ while (status) { ++ id = ffs(status) - 1; ++ status &= ~(1 << id); ++ irq = irq_find_mapping(pa->domain, ++ pa->apid_to_ppid[apid] << 16 ++ | id << 8 ++ | apid); ++ generic_handle_irq(irq); ++ } ++} ++ ++static void pmic_arb_chained_irq(unsigned int irq, struct irq_desc *desc) ++{ ++ struct spmi_pmic_arb_dev *pa = irq_get_handler_data(irq); ++ struct irq_chip *chip = irq_get_chip(irq); ++ void __iomem *intr = pa->intr; ++ int first = pa->min_apid >> 5; ++ int last = pa->max_apid >> 5; ++ u32 status; ++ int i, id; ++ ++ chained_irq_enter(chip, desc); ++ ++ for (i = first; i <= last; ++i) { ++ status = readl_relaxed(intr + ++ SPMI_PIC_OWNER_ACC_STATUS(pa->ee, i)); ++ while (status) { ++ id = ffs(status) - 1; ++ status &= ~(1 << id); ++ periph_interrupt(pa, id + i * 32); ++ } ++ } ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static void qpnpint_irq_ack(struct irq_data *d) ++{ ++ struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); ++ u8 irq = d->hwirq >> 8; ++ u8 apid = d->hwirq; ++ unsigned long flags; ++ u8 data; ++ ++ raw_spin_lock_irqsave(&pa->lock, flags); ++ writel_relaxed(1 << irq, pa->intr + SPMI_PIC_IRQ_CLEAR(apid)); ++ raw_spin_unlock_irqrestore(&pa->lock, flags); ++ ++ data = 1 << irq; ++ qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1); ++} ++ ++static void qpnpint_irq_mask(struct irq_data *d) ++{ ++ struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); ++ u8 irq = d->hwirq >> 8; ++ u8 apid = d->hwirq; ++ unsigned long flags; ++ u32 status; ++ u8 data; ++ ++ raw_spin_lock_irqsave(&pa->lock, flags); ++ status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid)); ++ if (status & SPMI_PIC_ACC_ENABLE_BIT) { ++ status = status & ~SPMI_PIC_ACC_ENABLE_BIT; ++ writel_relaxed(status, pa->intr + SPMI_PIC_ACC_ENABLE(apid)); ++ } ++ raw_spin_unlock_irqrestore(&pa->lock, flags); ++ ++ data = 1 << irq; ++ qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1); ++} ++ ++static void qpnpint_irq_unmask(struct irq_data *d) ++{ ++ struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); ++ u8 irq = d->hwirq >> 8; ++ u8 apid = d->hwirq; ++ unsigned long flags; ++ u32 status; ++ u8 data; ++ ++ raw_spin_lock_irqsave(&pa->lock, flags); ++ status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid)); ++ if (!(status & SPMI_PIC_ACC_ENABLE_BIT)) { ++ writel_relaxed(status | SPMI_PIC_ACC_ENABLE_BIT, ++ pa->intr + SPMI_PIC_ACC_ENABLE(apid)); ++ } ++ raw_spin_unlock_irqrestore(&pa->lock, flags); ++ ++ data = 1 << irq; ++ qpnpint_spmi_write(d, QPNPINT_REG_EN_SET, &data, 1); ++} ++ ++static void qpnpint_irq_enable(struct irq_data *d) ++{ ++ u8 irq = d->hwirq >> 8; ++ u8 data; ++ ++ qpnpint_irq_unmask(d); ++ ++ data = 1 << irq; ++ qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1); ++} ++ ++static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type) ++{ ++ struct spmi_pmic_arb_qpnpint_type type; ++ u8 irq = d->hwirq >> 8; ++ ++ qpnpint_spmi_read(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type)); ++ ++ if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { ++ type.type |= 1 << irq; ++ if (flow_type & IRQF_TRIGGER_RISING) ++ type.polarity_high |= 1 << irq; ++ if (flow_type & IRQF_TRIGGER_FALLING) ++ type.polarity_low |= 1 << irq; ++ } else { ++ if ((flow_type & (IRQF_TRIGGER_HIGH)) && ++ (flow_type & (IRQF_TRIGGER_LOW))) ++ return -EINVAL; ++ ++ type.type &= ~(1 << irq); /* level trig */ ++ if (flow_type & IRQF_TRIGGER_HIGH) ++ type.polarity_high |= 1 << irq; ++ else ++ type.polarity_low |= 1 << irq; ++ } ++ ++ qpnpint_spmi_write(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type)); ++ return 0; ++} ++ ++static struct irq_chip pmic_arb_irqchip = { ++ .name = "pmic_arb", ++ .irq_enable = qpnpint_irq_enable, ++ .irq_ack = qpnpint_irq_ack, ++ .irq_mask = qpnpint_irq_mask, ++ .irq_unmask = qpnpint_irq_unmask, ++ .irq_set_type = qpnpint_irq_set_type, ++ .flags = IRQCHIP_MASK_ON_SUSPEND ++ | IRQCHIP_SKIP_SET_WAKE, ++}; ++ ++struct spmi_pmic_arb_irq_spec { ++ unsigned slave:4; ++ unsigned per:8; ++ unsigned irq:3; ++}; ++ ++static int search_mapping_table(struct spmi_pmic_arb_dev *pa, ++ struct spmi_pmic_arb_irq_spec *spec, ++ u8 *apid) ++{ ++ u16 ppid = spec->slave << 8 | spec->per; ++ u32 *mapping_table = pa->mapping_table; ++ int index = 0, i; ++ u32 data; ++ ++ for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) { ++ data = mapping_table[index]; ++ ++ if (ppid & (1 << SPMI_MAPPING_BIT_INDEX(data))) { ++ if (SPMI_MAPPING_BIT_IS_1_FLAG(data)) { ++ index = SPMI_MAPPING_BIT_IS_1_RESULT(data); ++ } else { ++ *apid = SPMI_MAPPING_BIT_IS_1_RESULT(data); ++ return 0; ++ } ++ } else { ++ if (SPMI_MAPPING_BIT_IS_0_FLAG(data)) { ++ index = SPMI_MAPPING_BIT_IS_0_RESULT(data); ++ } else { ++ *apid = SPMI_MAPPING_BIT_IS_0_RESULT(data); ++ return 0; ++ } ++ } ++ } ++ ++ return -ENODEV; ++} ++ ++static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, ++ struct device_node *controller, ++ const u32 *intspec, ++ unsigned int intsize, ++ unsigned long *out_hwirq, ++ unsigned int *out_type) ++{ ++ struct spmi_pmic_arb_dev *pa = d->host_data; ++ struct spmi_pmic_arb_irq_spec spec; ++ int err; ++ u8 apid; ++ ++ dev_dbg(&pa->spmic->dev, ++ "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n", ++ intspec[0], intspec[1], intspec[2]); ++ ++ if (d->of_node != controller) ++ return -EINVAL; ++ if (intsize != 4) ++ return -EINVAL; ++ if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7) ++ return -EINVAL; ++ ++ spec.slave = intspec[0]; ++ spec.per = intspec[1]; ++ spec.irq = intspec[2]; ++ ++ err = search_mapping_table(pa, &spec, &apid); ++ if (err) ++ return err; ++ ++ pa->apid_to_ppid[apid] = spec.slave << 8 | spec.per; ++ ++ /* Keep track of {max,min}_apid for bounding search during interrupt */ ++ if (apid > pa->max_apid) ++ pa->max_apid = apid; ++ if (apid < pa->min_apid) ++ pa->min_apid = apid; ++ ++ *out_hwirq = spec.slave << 24 ++ | spec.per << 16 ++ | spec.irq << 8 ++ | apid; ++ *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK; ++ ++ dev_dbg(&pa->spmic->dev, "out_hwirq = %lu\n", *out_hwirq); ++ ++ return 0; ++} ++ ++static int qpnpint_irq_domain_map(struct irq_domain *d, ++ unsigned int virq, ++ irq_hw_number_t hwirq) ++{ ++ struct spmi_pmic_arb_dev *pa = d->host_data; ++ ++ dev_dbg(&pa->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq); ++ ++ irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq); ++ irq_set_chip_data(virq, d->host_data); ++ irq_set_noprobe(virq); ++ return 0; ++} ++ ++static const struct irq_domain_ops pmic_arb_irq_domain_ops = { ++ .map = qpnpint_irq_domain_map, ++ .xlate = qpnpint_irq_domain_dt_translate, ++}; ++ + static int spmi_pmic_arb_probe(struct platform_device *pdev) + { + struct spmi_pmic_arb_dev *pa; + struct spmi_controller *ctrl; + struct resource *res; +- u32 channel; ++ u32 channel, ee; + int err, i; + + ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa)); +@@ -319,6 +642,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) + return -ENOMEM; + + pa = spmi_controller_get_drvdata(ctrl); ++ pa->spmic = ctrl; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); + pa->base = devm_ioremap_resource(&ctrl->dev, res); +@@ -341,6 +665,12 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) + goto err_put_ctrl; + } + ++ pa->irq = platform_get_irq_byname(pdev, "periph_irq"); ++ if (pa->irq < 0) { ++ err = pa->irq; ++ goto err_put_ctrl; ++ } ++ + err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel); + if (err) { + dev_err(&pdev->dev, "channel unspecified.\n"); +@@ -355,6 +685,29 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) + + pa->channel = channel; + ++ err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee); ++ if (err) { ++ dev_err(&pdev->dev, "EE unspecified.\n"); ++ goto err_put_ctrl; ++ } ++ ++ if (ee > 5) { ++ dev_err(&pdev->dev, "invalid EE (%u) specified\n", ee); ++ err = -EINVAL; ++ goto err_put_ctrl; ++ } ++ ++ pa->ee = ee; ++ ++ for (i = 0; i < ARRAY_SIZE(pa->mapping_table); ++i) ++ pa->mapping_table[i] = readl_relaxed( ++ pa->cnfg + SPMI_MAPPING_TABLE_REG(i)); ++ ++ /* Initialize max_apid/min_apid to the opposite bounds, during ++ * the irq domain translation, we are sure to update these */ ++ pa->max_apid = 0; ++ pa->min_apid = PMIC_ARB_MAX_PERIPHS - 1; ++ + platform_set_drvdata(pdev, ctrl); + raw_spin_lock_init(&pa->lock); + +@@ -362,15 +715,31 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) + ctrl->read_cmd = pmic_arb_read_cmd; + ctrl->write_cmd = pmic_arb_write_cmd; + ++ dev_dbg(&pdev->dev, "adding irq domain\n"); ++ pa->domain = irq_domain_add_tree(pdev->dev.of_node, ++ &pmic_arb_irq_domain_ops, pa); ++ if (!pa->domain) { ++ dev_err(&pdev->dev, "unable to create irq_domain\n"); ++ err = -ENOMEM; ++ goto err_put_ctrl; ++ } ++ ++ irq_set_handler_data(pa->irq, pa); ++ irq_set_chained_handler(pa->irq, pmic_arb_chained_irq); ++ + err = spmi_controller_add(ctrl); + if (err) +- goto err_put_ctrl; ++ goto err_domain_remove; + + dev_dbg(&ctrl->dev, "PMIC Arb Version 0x%x\n", + pmic_arb_base_read(pa, PMIC_ARB_VERSION)); + + return 0; + ++err_domain_remove: ++ irq_set_chained_handler(pa->irq, NULL); ++ irq_set_handler_data(pa->irq, NULL); ++ irq_domain_remove(pa->domain); + err_put_ctrl: + spmi_controller_put(ctrl); + return err; +@@ -379,7 +748,11 @@ err_put_ctrl: + static int spmi_pmic_arb_remove(struct platform_device *pdev) + { + struct spmi_controller *ctrl = platform_get_drvdata(pdev); ++ struct spmi_pmic_arb_dev *pa = spmi_controller_get_drvdata(ctrl); + spmi_controller_remove(ctrl); ++ irq_set_chained_handler(pa->irq, NULL); ++ irq_set_handler_data(pa->irq, NULL); ++ irq_domain_remove(pa->domain); + spmi_controller_put(ctrl); + return 0; + } +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0058-spmi-pmic_arb-make-selectable-on-ARCH_QCOM.patch b/target/linux/ipq806x/patches/0058-spmi-pmic_arb-make-selectable-on-ARCH_QCOM.patch new file mode 100644 index 0000000000..271e8adad4 --- /dev/null +++ b/target/linux/ipq806x/patches/0058-spmi-pmic_arb-make-selectable-on-ARCH_QCOM.patch @@ -0,0 +1,34 @@ +From 840fe915072597b5ba9599c4579f014f47b9638e Mon Sep 17 00:00:00 2001 +From: Josh Cartwright <joshc@codeaurora.org> +Date: Mon, 3 Mar 2014 10:49:43 -0600 +Subject: [PATCH 058/182] spmi: pmic_arb: make selectable on ARCH_QCOM + +With the split of Qualcomm MSM support into legacy and multiplatform, +the SPMI PMIC arb driver is only relevant on the multiplatform supported +SoCs. Switch the Kconfig depends to ARCH_QCOM. + +Acked-by: Kumar Gala <galak@codeaurora.org> +Signed-off-by: Josh Cartwright <joshc@codeaurora.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/spmi/Kconfig | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig +index 075bd79..bf1295e 100644 +--- a/drivers/spmi/Kconfig ++++ b/drivers/spmi/Kconfig +@@ -14,8 +14,8 @@ config SPMI_MSM_PMIC_ARB + tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)" + depends on ARM + depends on IRQ_DOMAIN +- depends on ARCH_MSM || COMPILE_TEST +- default ARCH_MSM ++ depends on ARCH_QCOM || COMPILE_TEST ++ default ARCH_QCOM + help + If you say yes to this option, support will be included for the + built-in SPMI PMIC Arbiter interface on Qualcomm MSM family +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0059-spmi-pm-drop-bus-level-PM-suspend-resume-routines.patch b/target/linux/ipq806x/patches/0059-spmi-pm-drop-bus-level-PM-suspend-resume-routines.patch new file mode 100644 index 0000000000..f252fa055b --- /dev/null +++ b/target/linux/ipq806x/patches/0059-spmi-pm-drop-bus-level-PM-suspend-resume-routines.patch @@ -0,0 +1,74 @@ +From 9d13f01e2ec45253adaae1a330cdc4eb881c7377 Mon Sep 17 00:00:00 2001 +From: Josh Cartwright <joshc@codeaurora.org> +Date: Mon, 3 Mar 2014 10:49:44 -0600 +Subject: [PATCH 059/182] spmi: pm: drop bus-level PM suspend/resume routines + +SPMI defines the behavior of a device in the "SLEEP" state as being +"user-defined or specified by the device manufacturer". Without +clearly-defined bus-level semantics for low-power states, push the +responsibility of transitioning a device into/out of "SLEEP" into SPMI +device drivers. + +Cc: Felipe Balbi <balbi@ti.com> +Signed-off-by: Josh Cartwright <joshc@codeaurora.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/spmi/spmi.c | 35 ----------------------------------- + 1 file changed, 35 deletions(-) + +diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c +index 6122c8f..3b57807 100644 +--- a/drivers/spmi/spmi.c ++++ b/drivers/spmi/spmi.c +@@ -46,40 +46,6 @@ static const struct device_type spmi_ctrl_type = { + .release = spmi_ctrl_release, + }; + +-#ifdef CONFIG_PM_RUNTIME +-static int spmi_runtime_suspend(struct device *dev) +-{ +- struct spmi_device *sdev = to_spmi_device(dev); +- int err; +- +- err = pm_generic_runtime_suspend(dev); +- if (err) +- return err; +- +- return spmi_command_sleep(sdev); +-} +- +-static int spmi_runtime_resume(struct device *dev) +-{ +- struct spmi_device *sdev = to_spmi_device(dev); +- int err; +- +- err = spmi_command_wakeup(sdev); +- if (err) +- return err; +- +- return pm_generic_runtime_resume(dev); +-} +-#endif +- +-static const struct dev_pm_ops spmi_pm_ops = { +- SET_RUNTIME_PM_OPS( +- spmi_runtime_suspend, +- spmi_runtime_resume, +- NULL +- ) +-}; +- + static int spmi_device_match(struct device *dev, struct device_driver *drv) + { + if (of_driver_match_device(dev, drv)) +@@ -391,7 +357,6 @@ static int spmi_drv_remove(struct device *dev) + static struct bus_type spmi_bus_type = { + .name = "spmi", + .match = spmi_device_match, +- .pm = &spmi_pm_ops, + .probe = spmi_drv_probe, + .remove = spmi_drv_remove, + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0060-i2c-qup-New-bus-driver-for-the-Qualcomm-QUP-I2C-cont.patch b/target/linux/ipq806x/patches/0060-i2c-qup-New-bus-driver-for-the-Qualcomm-QUP-I2C-cont.patch new file mode 100644 index 0000000000..64993a3e8c --- /dev/null +++ b/target/linux/ipq806x/patches/0060-i2c-qup-New-bus-driver-for-the-Qualcomm-QUP-I2C-cont.patch @@ -0,0 +1,835 @@ +From 226eff10dc11af5d6b1bc31e5cedc079aa564fb3 Mon Sep 17 00:00:00 2001 +From: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Date: Thu, 13 Mar 2014 19:07:43 -0700 +Subject: [PATCH 060/182] i2c: qup: New bus driver for the Qualcomm QUP I2C + controller + +This bus driver supports the QUP i2c hardware controller in the Qualcomm SOCs. +The Qualcomm Universal Peripheral Engine (QUP) is a general purpose data path +engine with input/output FIFOs and an embedded i2c mini-core. The driver +supports FIFO mode (for low bandwidth applications) and block mode (interrupt +generated for each block-size data transfer). + +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Reviewed-by: Andy Gross <agross@codeaurora.org> +Tested-by: Philip Elcan <pelcan@codeaurora.org> +[wsa: removed needless IS_ERR_VALUE] +Signed-off-by: Wolfram Sang <wsa@the-dreams.de> +--- + drivers/i2c/busses/Kconfig | 10 + + drivers/i2c/busses/Makefile | 1 + + drivers/i2c/busses/i2c-qup.c | 768 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 779 insertions(+) + create mode 100644 drivers/i2c/busses/i2c-qup.c + +diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig +index de17c55..1886315 100644 +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -648,6 +648,16 @@ config I2C_PXA_SLAVE + is necessary for systems where the PXA may be a target on the + I2C bus. + ++config I2C_QUP ++ tristate "Qualcomm QUP based I2C controller" ++ depends on ARCH_QCOM ++ help ++ If you say yes to this option, support will be included for the ++ built-in I2C interface on the Qualcomm SoCs. ++ ++ This driver can also be built as a module. If so, the module ++ will be called i2c-qup. ++ + config I2C_RIIC + tristate "Renesas RIIC adapter" + depends on ARCH_SHMOBILE || COMPILE_TEST +diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile +index a08931f..bf2257b 100644 +--- a/drivers/i2c/busses/Makefile ++++ b/drivers/i2c/busses/Makefile +@@ -63,6 +63,7 @@ obj-$(CONFIG_I2C_PNX) += i2c-pnx.o + obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o + obj-$(CONFIG_I2C_PXA) += i2c-pxa.o + obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o ++obj-$(CONFIG_I2C_QUP) += i2c-qup.o + obj-$(CONFIG_I2C_RIIC) += i2c-riic.o + obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o + obj-$(CONFIG_I2C_S6000) += i2c-s6000.o +diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c +new file mode 100644 +index 0000000..c9d5f78 +--- /dev/null ++++ b/drivers/i2c/busses/i2c-qup.c +@@ -0,0 +1,768 @@ ++/* ++ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved. ++ * Copyright (c) 2014, Sony Mobile Communications AB. ++ * ++ * ++ * 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/err.h> ++#include <linux/i2c.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/pm_runtime.h> ++ ++/* QUP Registers */ ++#define QUP_CONFIG 0x000 ++#define QUP_STATE 0x004 ++#define QUP_IO_MODE 0x008 ++#define QUP_SW_RESET 0x00c ++#define QUP_OPERATIONAL 0x018 ++#define QUP_ERROR_FLAGS 0x01c ++#define QUP_ERROR_FLAGS_EN 0x020 ++#define QUP_HW_VERSION 0x030 ++#define QUP_MX_OUTPUT_CNT 0x100 ++#define QUP_OUT_FIFO_BASE 0x110 ++#define QUP_MX_WRITE_CNT 0x150 ++#define QUP_MX_INPUT_CNT 0x200 ++#define QUP_MX_READ_CNT 0x208 ++#define QUP_IN_FIFO_BASE 0x218 ++#define QUP_I2C_CLK_CTL 0x400 ++#define QUP_I2C_STATUS 0x404 ++ ++/* QUP States and reset values */ ++#define QUP_RESET_STATE 0 ++#define QUP_RUN_STATE 1 ++#define QUP_PAUSE_STATE 3 ++#define QUP_STATE_MASK 3 ++ ++#define QUP_STATE_VALID BIT(2) ++#define QUP_I2C_MAST_GEN BIT(4) ++ ++#define QUP_OPERATIONAL_RESET 0x000ff0 ++#define QUP_I2C_STATUS_RESET 0xfffffc ++ ++/* QUP OPERATIONAL FLAGS */ ++#define QUP_I2C_NACK_FLAG BIT(3) ++#define QUP_OUT_NOT_EMPTY BIT(4) ++#define QUP_IN_NOT_EMPTY BIT(5) ++#define QUP_OUT_FULL BIT(6) ++#define QUP_OUT_SVC_FLAG BIT(8) ++#define QUP_IN_SVC_FLAG BIT(9) ++#define QUP_MX_OUTPUT_DONE BIT(10) ++#define QUP_MX_INPUT_DONE BIT(11) ++ ++/* I2C mini core related values */ ++#define QUP_CLOCK_AUTO_GATE BIT(13) ++#define I2C_MINI_CORE (2 << 8) ++#define I2C_N_VAL 15 ++/* Most significant word offset in FIFO port */ ++#define QUP_MSW_SHIFT (I2C_N_VAL + 1) ++ ++/* Packing/Unpacking words in FIFOs, and IO modes */ ++#define QUP_OUTPUT_BLK_MODE (1 << 10) ++#define QUP_INPUT_BLK_MODE (1 << 12) ++#define QUP_UNPACK_EN BIT(14) ++#define QUP_PACK_EN BIT(15) ++ ++#define QUP_REPACK_EN (QUP_UNPACK_EN | QUP_PACK_EN) ++ ++#define QUP_OUTPUT_BLOCK_SIZE(x)(((x) >> 0) & 0x03) ++#define QUP_OUTPUT_FIFO_SIZE(x) (((x) >> 2) & 0x07) ++#define QUP_INPUT_BLOCK_SIZE(x) (((x) >> 5) & 0x03) ++#define QUP_INPUT_FIFO_SIZE(x) (((x) >> 7) & 0x07) ++ ++/* QUP tags */ ++#define QUP_TAG_START (1 << 8) ++#define QUP_TAG_DATA (2 << 8) ++#define QUP_TAG_STOP (3 << 8) ++#define QUP_TAG_REC (4 << 8) ++ ++/* Status, Error flags */ ++#define I2C_STATUS_WR_BUFFER_FULL BIT(0) ++#define I2C_STATUS_BUS_ACTIVE BIT(8) ++#define I2C_STATUS_ERROR_MASK 0x38000fc ++#define QUP_STATUS_ERROR_FLAGS 0x7c ++ ++#define QUP_READ_LIMIT 256 ++ ++struct qup_i2c_dev { ++ struct device *dev; ++ void __iomem *base; ++ int irq; ++ struct clk *clk; ++ struct clk *pclk; ++ struct i2c_adapter adap; ++ ++ int clk_ctl; ++ int out_fifo_sz; ++ int in_fifo_sz; ++ int out_blk_sz; ++ int in_blk_sz; ++ ++ unsigned long one_byte_t; ++ ++ struct i2c_msg *msg; ++ /* Current posion in user message buffer */ ++ int pos; ++ /* I2C protocol errors */ ++ u32 bus_err; ++ /* QUP core errors */ ++ u32 qup_err; ++ ++ struct completion xfer; ++}; ++ ++static irqreturn_t qup_i2c_interrupt(int irq, void *dev) ++{ ++ struct qup_i2c_dev *qup = dev; ++ u32 bus_err; ++ u32 qup_err; ++ u32 opflags; ++ ++ bus_err = readl(qup->base + QUP_I2C_STATUS); ++ qup_err = readl(qup->base + QUP_ERROR_FLAGS); ++ opflags = readl(qup->base + QUP_OPERATIONAL); ++ ++ if (!qup->msg) { ++ /* Clear Error interrupt */ ++ writel(QUP_RESET_STATE, qup->base + QUP_STATE); ++ return IRQ_HANDLED; ++ } ++ ++ bus_err &= I2C_STATUS_ERROR_MASK; ++ qup_err &= QUP_STATUS_ERROR_FLAGS; ++ ++ if (qup_err) { ++ /* Clear Error interrupt */ ++ writel(qup_err, qup->base + QUP_ERROR_FLAGS); ++ goto done; ++ } ++ ++ if (bus_err) { ++ /* Clear Error interrupt */ ++ writel(QUP_RESET_STATE, qup->base + QUP_STATE); ++ goto done; ++ } ++ ++ if (opflags & QUP_IN_SVC_FLAG) ++ writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL); ++ ++ if (opflags & QUP_OUT_SVC_FLAG) ++ writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL); ++ ++done: ++ qup->qup_err = qup_err; ++ qup->bus_err = bus_err; ++ complete(&qup->xfer); ++ return IRQ_HANDLED; ++} ++ ++static int qup_i2c_poll_state_mask(struct qup_i2c_dev *qup, ++ u32 req_state, u32 req_mask) ++{ ++ int retries = 1; ++ u32 state; ++ ++ /* ++ * State transition takes 3 AHB clocks cycles + 3 I2C master clock ++ * cycles. So retry once after a 1uS delay. ++ */ ++ do { ++ state = readl(qup->base + QUP_STATE); ++ ++ if (state & QUP_STATE_VALID && ++ (state & req_mask) == req_state) ++ return 0; ++ ++ udelay(1); ++ } while (retries--); ++ ++ return -ETIMEDOUT; ++} ++ ++static int qup_i2c_poll_state(struct qup_i2c_dev *qup, u32 req_state) ++{ ++ return qup_i2c_poll_state_mask(qup, req_state, QUP_STATE_MASK); ++} ++ ++static int qup_i2c_poll_state_valid(struct qup_i2c_dev *qup) ++{ ++ return qup_i2c_poll_state_mask(qup, 0, 0); ++} ++ ++static int qup_i2c_poll_state_i2c_master(struct qup_i2c_dev *qup) ++{ ++ return qup_i2c_poll_state_mask(qup, QUP_I2C_MAST_GEN, QUP_I2C_MAST_GEN); ++} ++ ++static int qup_i2c_change_state(struct qup_i2c_dev *qup, u32 state) ++{ ++ if (qup_i2c_poll_state_valid(qup) != 0) ++ return -EIO; ++ ++ writel(state, qup->base + QUP_STATE); ++ ++ if (qup_i2c_poll_state(qup, state) != 0) ++ return -EIO; ++ return 0; ++} ++ ++static int qup_i2c_wait_writeready(struct qup_i2c_dev *qup) ++{ ++ unsigned long timeout; ++ u32 opflags; ++ u32 status; ++ ++ timeout = jiffies + HZ; ++ ++ for (;;) { ++ opflags = readl(qup->base + QUP_OPERATIONAL); ++ status = readl(qup->base + QUP_I2C_STATUS); ++ ++ if (!(opflags & QUP_OUT_NOT_EMPTY) && ++ !(status & I2C_STATUS_BUS_ACTIVE)) ++ return 0; ++ ++ if (time_after(jiffies, timeout)) ++ return -ETIMEDOUT; ++ ++ usleep_range(qup->one_byte_t, qup->one_byte_t * 2); ++ } ++} ++ ++static void qup_i2c_set_write_mode(struct qup_i2c_dev *qup, struct i2c_msg *msg) ++{ ++ /* Number of entries to shift out, including the start */ ++ int total = msg->len + 1; ++ ++ if (total < qup->out_fifo_sz) { ++ /* FIFO mode */ ++ writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE); ++ writel(total, qup->base + QUP_MX_WRITE_CNT); ++ } else { ++ /* BLOCK mode (transfer data on chunks) */ ++ writel(QUP_OUTPUT_BLK_MODE | QUP_REPACK_EN, ++ qup->base + QUP_IO_MODE); ++ writel(total, qup->base + QUP_MX_OUTPUT_CNT); ++ } ++} ++ ++static void qup_i2c_issue_write(struct qup_i2c_dev *qup, struct i2c_msg *msg) ++{ ++ u32 addr = msg->addr << 1; ++ u32 qup_tag; ++ u32 opflags; ++ int idx; ++ u32 val; ++ ++ if (qup->pos == 0) { ++ val = QUP_TAG_START | addr; ++ idx = 1; ++ } else { ++ val = 0; ++ idx = 0; ++ } ++ ++ while (qup->pos < msg->len) { ++ /* Check that there's space in the FIFO for our pair */ ++ opflags = readl(qup->base + QUP_OPERATIONAL); ++ if (opflags & QUP_OUT_FULL) ++ break; ++ ++ if (qup->pos == msg->len - 1) ++ qup_tag = QUP_TAG_STOP; ++ else ++ qup_tag = QUP_TAG_DATA; ++ ++ if (idx & 1) ++ val |= (qup_tag | msg->buf[qup->pos]) << QUP_MSW_SHIFT; ++ else ++ val = qup_tag | msg->buf[qup->pos]; ++ ++ /* Write out the pair and the last odd value */ ++ if (idx & 1 || qup->pos == msg->len - 1) ++ writel(val, qup->base + QUP_OUT_FIFO_BASE); ++ ++ qup->pos++; ++ idx++; ++ } ++} ++ ++static int qup_i2c_write_one(struct qup_i2c_dev *qup, struct i2c_msg *msg) ++{ ++ unsigned long left; ++ int ret; ++ ++ qup->msg = msg; ++ qup->pos = 0; ++ ++ enable_irq(qup->irq); ++ ++ qup_i2c_set_write_mode(qup, msg); ++ ++ ret = qup_i2c_change_state(qup, QUP_RUN_STATE); ++ if (ret) ++ goto err; ++ ++ writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL); ++ ++ do { ++ ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE); ++ if (ret) ++ goto err; ++ ++ qup_i2c_issue_write(qup, msg); ++ ++ ret = qup_i2c_change_state(qup, QUP_RUN_STATE); ++ if (ret) ++ goto err; ++ ++ left = wait_for_completion_timeout(&qup->xfer, HZ); ++ if (!left) { ++ writel(1, qup->base + QUP_SW_RESET); ++ ret = -ETIMEDOUT; ++ goto err; ++ } ++ ++ if (qup->bus_err || qup->qup_err) { ++ if (qup->bus_err & QUP_I2C_NACK_FLAG) ++ dev_err(qup->dev, "NACK from %x\n", msg->addr); ++ ret = -EIO; ++ goto err; ++ } ++ } while (qup->pos < msg->len); ++ ++ /* Wait for the outstanding data in the fifo to drain */ ++ ret = qup_i2c_wait_writeready(qup); ++ ++err: ++ disable_irq(qup->irq); ++ qup->msg = NULL; ++ ++ return ret; ++} ++ ++static void qup_i2c_set_read_mode(struct qup_i2c_dev *qup, int len) ++{ ++ if (len < qup->in_fifo_sz) { ++ /* FIFO mode */ ++ writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE); ++ writel(len, qup->base + QUP_MX_READ_CNT); ++ } else { ++ /* BLOCK mode (transfer data on chunks) */ ++ writel(QUP_INPUT_BLK_MODE | QUP_REPACK_EN, ++ qup->base + QUP_IO_MODE); ++ writel(len, qup->base + QUP_MX_INPUT_CNT); ++ } ++} ++ ++static void qup_i2c_issue_read(struct qup_i2c_dev *qup, struct i2c_msg *msg) ++{ ++ u32 addr, len, val; ++ ++ addr = (msg->addr << 1) | 1; ++ ++ /* 0 is used to specify a length 256 (QUP_READ_LIMIT) */ ++ len = (msg->len == QUP_READ_LIMIT) ? 0 : msg->len; ++ ++ val = ((QUP_TAG_REC | len) << QUP_MSW_SHIFT) | QUP_TAG_START | addr; ++ writel(val, qup->base + QUP_OUT_FIFO_BASE); ++} ++ ++ ++static void qup_i2c_read_fifo(struct qup_i2c_dev *qup, struct i2c_msg *msg) ++{ ++ u32 opflags; ++ u32 val = 0; ++ int idx; ++ ++ for (idx = 0; qup->pos < msg->len; idx++) { ++ if ((idx & 1) == 0) { ++ /* Check that FIFO have data */ ++ opflags = readl(qup->base + QUP_OPERATIONAL); ++ if (!(opflags & QUP_IN_NOT_EMPTY)) ++ break; ++ ++ /* Reading 2 words at time */ ++ val = readl(qup->base + QUP_IN_FIFO_BASE); ++ ++ msg->buf[qup->pos++] = val & 0xFF; ++ } else { ++ msg->buf[qup->pos++] = val >> QUP_MSW_SHIFT; ++ } ++ } ++} ++ ++static int qup_i2c_read_one(struct qup_i2c_dev *qup, struct i2c_msg *msg) ++{ ++ unsigned long left; ++ int ret; ++ ++ /* ++ * The QUP block will issue a NACK and STOP on the bus when reaching ++ * the end of the read, the length of the read is specified as one byte ++ * which limits the possible read to 256 (QUP_READ_LIMIT) bytes. ++ */ ++ if (msg->len > QUP_READ_LIMIT) { ++ dev_err(qup->dev, "HW not capable of reads over %d bytes\n", ++ QUP_READ_LIMIT); ++ return -EINVAL; ++ } ++ ++ qup->msg = msg; ++ qup->pos = 0; ++ ++ enable_irq(qup->irq); ++ ++ qup_i2c_set_read_mode(qup, msg->len); ++ ++ ret = qup_i2c_change_state(qup, QUP_RUN_STATE); ++ if (ret) ++ goto err; ++ ++ writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL); ++ ++ ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE); ++ if (ret) ++ goto err; ++ ++ qup_i2c_issue_read(qup, msg); ++ ++ ret = qup_i2c_change_state(qup, QUP_RUN_STATE); ++ if (ret) ++ goto err; ++ ++ do { ++ left = wait_for_completion_timeout(&qup->xfer, HZ); ++ if (!left) { ++ writel(1, qup->base + QUP_SW_RESET); ++ ret = -ETIMEDOUT; ++ goto err; ++ } ++ ++ if (qup->bus_err || qup->qup_err) { ++ if (qup->bus_err & QUP_I2C_NACK_FLAG) ++ dev_err(qup->dev, "NACK from %x\n", msg->addr); ++ ret = -EIO; ++ goto err; ++ } ++ ++ qup_i2c_read_fifo(qup, msg); ++ } while (qup->pos < msg->len); ++ ++err: ++ disable_irq(qup->irq); ++ qup->msg = NULL; ++ ++ return ret; ++} ++ ++static int qup_i2c_xfer(struct i2c_adapter *adap, ++ struct i2c_msg msgs[], ++ int num) ++{ ++ struct qup_i2c_dev *qup = i2c_get_adapdata(adap); ++ int ret, idx; ++ ++ ret = pm_runtime_get_sync(qup->dev); ++ if (ret) ++ goto out; ++ ++ writel(1, qup->base + QUP_SW_RESET); ++ ret = qup_i2c_poll_state(qup, QUP_RESET_STATE); ++ if (ret) ++ goto out; ++ ++ /* Configure QUP as I2C mini core */ ++ writel(I2C_MINI_CORE | I2C_N_VAL, qup->base + QUP_CONFIG); ++ ++ for (idx = 0; idx < num; idx++) { ++ if (msgs[idx].len == 0) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (qup_i2c_poll_state_i2c_master(qup)) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ if (msgs[idx].flags & I2C_M_RD) ++ ret = qup_i2c_read_one(qup, &msgs[idx]); ++ else ++ ret = qup_i2c_write_one(qup, &msgs[idx]); ++ ++ if (ret) ++ break; ++ ++ ret = qup_i2c_change_state(qup, QUP_RESET_STATE); ++ if (ret) ++ break; ++ } ++ ++ if (ret == 0) ++ ret = num; ++out: ++ ++ pm_runtime_mark_last_busy(qup->dev); ++ pm_runtime_put_autosuspend(qup->dev); ++ ++ return ret; ++} ++ ++static u32 qup_i2c_func(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); ++} ++ ++static const struct i2c_algorithm qup_i2c_algo = { ++ .master_xfer = qup_i2c_xfer, ++ .functionality = qup_i2c_func, ++}; ++ ++static void qup_i2c_enable_clocks(struct qup_i2c_dev *qup) ++{ ++ clk_prepare_enable(qup->clk); ++ clk_prepare_enable(qup->pclk); ++} ++ ++static void qup_i2c_disable_clocks(struct qup_i2c_dev *qup) ++{ ++ u32 config; ++ ++ qup_i2c_change_state(qup, QUP_RESET_STATE); ++ clk_disable_unprepare(qup->clk); ++ config = readl(qup->base + QUP_CONFIG); ++ config |= QUP_CLOCK_AUTO_GATE; ++ writel(config, qup->base + QUP_CONFIG); ++ clk_disable_unprepare(qup->pclk); ++} ++ ++static int qup_i2c_probe(struct platform_device *pdev) ++{ ++ static const int blk_sizes[] = {4, 16, 32}; ++ struct device_node *node = pdev->dev.of_node; ++ struct qup_i2c_dev *qup; ++ unsigned long one_bit_t; ++ struct resource *res; ++ u32 io_mode, hw_ver, size; ++ int ret, fs_div, hs_div; ++ int src_clk_freq; ++ int clk_freq = 100000; ++ ++ qup = devm_kzalloc(&pdev->dev, sizeof(*qup), GFP_KERNEL); ++ if (!qup) ++ return -ENOMEM; ++ ++ qup->dev = &pdev->dev; ++ init_completion(&qup->xfer); ++ platform_set_drvdata(pdev, qup); ++ ++ of_property_read_u32(node, "clock-frequency", &clk_freq); ++ ++ /* We support frequencies up to FAST Mode (400KHz) */ ++ if (!clk_freq || clk_freq > 400000) { ++ dev_err(qup->dev, "clock frequency not supported %d\n", ++ clk_freq); ++ return -EINVAL; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ qup->base = devm_ioremap_resource(qup->dev, res); ++ if (IS_ERR(qup->base)) ++ return PTR_ERR(qup->base); ++ ++ qup->irq = platform_get_irq(pdev, 0); ++ if (qup->irq < 0) { ++ dev_err(qup->dev, "No IRQ defined\n"); ++ return qup->irq; ++ } ++ ++ qup->clk = devm_clk_get(qup->dev, "core"); ++ if (IS_ERR(qup->clk)) { ++ dev_err(qup->dev, "Could not get core clock\n"); ++ return PTR_ERR(qup->clk); ++ } ++ ++ qup->pclk = devm_clk_get(qup->dev, "iface"); ++ if (IS_ERR(qup->pclk)) { ++ dev_err(qup->dev, "Could not get iface clock\n"); ++ return PTR_ERR(qup->pclk); ++ } ++ ++ qup_i2c_enable_clocks(qup); ++ ++ /* ++ * Bootloaders might leave a pending interrupt on certain QUP's, ++ * so we reset the core before registering for interrupts. ++ */ ++ writel(1, qup->base + QUP_SW_RESET); ++ ret = qup_i2c_poll_state_valid(qup); ++ if (ret) ++ goto fail; ++ ++ ret = devm_request_irq(qup->dev, qup->irq, qup_i2c_interrupt, ++ IRQF_TRIGGER_HIGH, "i2c_qup", qup); ++ if (ret) { ++ dev_err(qup->dev, "Request %d IRQ failed\n", qup->irq); ++ goto fail; ++ } ++ disable_irq(qup->irq); ++ ++ hw_ver = readl(qup->base + QUP_HW_VERSION); ++ dev_dbg(qup->dev, "Revision %x\n", hw_ver); ++ ++ io_mode = readl(qup->base + QUP_IO_MODE); ++ ++ /* ++ * The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag' ++ * associated with each byte written/received ++ */ ++ size = QUP_OUTPUT_BLOCK_SIZE(io_mode); ++ if (size > ARRAY_SIZE(blk_sizes)) ++ return -EIO; ++ qup->out_blk_sz = blk_sizes[size] / 2; ++ ++ size = QUP_INPUT_BLOCK_SIZE(io_mode); ++ if (size > ARRAY_SIZE(blk_sizes)) ++ return -EIO; ++ qup->in_blk_sz = blk_sizes[size] / 2; ++ ++ size = QUP_OUTPUT_FIFO_SIZE(io_mode); ++ qup->out_fifo_sz = qup->out_blk_sz * (2 << size); ++ ++ size = QUP_INPUT_FIFO_SIZE(io_mode); ++ qup->in_fifo_sz = qup->in_blk_sz * (2 << size); ++ ++ src_clk_freq = clk_get_rate(qup->clk); ++ fs_div = ((src_clk_freq / clk_freq) / 2) - 3; ++ hs_div = 3; ++ qup->clk_ctl = (hs_div << 8) | (fs_div & 0xff); ++ ++ /* ++ * Time it takes for a byte to be clocked out on the bus. ++ * Each byte takes 9 clock cycles (8 bits + 1 ack). ++ */ ++ one_bit_t = (USEC_PER_SEC / clk_freq) + 1; ++ qup->one_byte_t = one_bit_t * 9; ++ ++ dev_dbg(qup->dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n", ++ qup->in_blk_sz, qup->in_fifo_sz, ++ qup->out_blk_sz, qup->out_fifo_sz); ++ ++ i2c_set_adapdata(&qup->adap, qup); ++ qup->adap.algo = &qup_i2c_algo; ++ qup->adap.dev.parent = qup->dev; ++ qup->adap.dev.of_node = pdev->dev.of_node; ++ strlcpy(qup->adap.name, "QUP I2C adapter", sizeof(qup->adap.name)); ++ ++ ret = i2c_add_adapter(&qup->adap); ++ if (ret) ++ goto fail; ++ ++ pm_runtime_set_autosuspend_delay(qup->dev, MSEC_PER_SEC); ++ pm_runtime_use_autosuspend(qup->dev); ++ pm_runtime_set_active(qup->dev); ++ pm_runtime_enable(qup->dev); ++ return 0; ++ ++fail: ++ qup_i2c_disable_clocks(qup); ++ return ret; ++} ++ ++static int qup_i2c_remove(struct platform_device *pdev) ++{ ++ struct qup_i2c_dev *qup = platform_get_drvdata(pdev); ++ ++ disable_irq(qup->irq); ++ qup_i2c_disable_clocks(qup); ++ i2c_del_adapter(&qup->adap); ++ pm_runtime_disable(qup->dev); ++ pm_runtime_set_suspended(qup->dev); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int qup_i2c_pm_suspend_runtime(struct device *device) ++{ ++ struct qup_i2c_dev *qup = dev_get_drvdata(device); ++ ++ dev_dbg(device, "pm_runtime: suspending...\n"); ++ qup_i2c_disable_clocks(qup); ++ return 0; ++} ++ ++static int qup_i2c_pm_resume_runtime(struct device *device) ++{ ++ struct qup_i2c_dev *qup = dev_get_drvdata(device); ++ ++ dev_dbg(device, "pm_runtime: resuming...\n"); ++ qup_i2c_enable_clocks(qup); ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_PM_SLEEP ++static int qup_i2c_suspend(struct device *device) ++{ ++ qup_i2c_pm_suspend_runtime(device); ++ return 0; ++} ++ ++static int qup_i2c_resume(struct device *device) ++{ ++ qup_i2c_pm_resume_runtime(device); ++ pm_runtime_mark_last_busy(device); ++ pm_request_autosuspend(device); ++ return 0; ++} ++#endif ++ ++static const struct dev_pm_ops qup_i2c_qup_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS( ++ qup_i2c_suspend, ++ qup_i2c_resume) ++ SET_RUNTIME_PM_OPS( ++ qup_i2c_pm_suspend_runtime, ++ qup_i2c_pm_resume_runtime, ++ NULL) ++}; ++ ++static const struct of_device_id qup_i2c_dt_match[] = { ++ { .compatible = "qcom,i2c-qup-v1.1.1" }, ++ { .compatible = "qcom,i2c-qup-v2.1.1" }, ++ { .compatible = "qcom,i2c-qup-v2.2.1" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, qup_i2c_dt_match); ++ ++static struct platform_driver qup_i2c_driver = { ++ .probe = qup_i2c_probe, ++ .remove = qup_i2c_remove, ++ .driver = { ++ .name = "i2c_qup", ++ .owner = THIS_MODULE, ++ .pm = &qup_i2c_qup_pm_ops, ++ .of_match_table = qup_i2c_dt_match, ++ }, ++}; ++ ++module_platform_driver(qup_i2c_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:i2c_qup"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0061-i2c-qup-Add-device-tree-bindings-information.patch b/target/linux/ipq806x/patches/0061-i2c-qup-Add-device-tree-bindings-information.patch new file mode 100644 index 0000000000..6ddae4fe6c --- /dev/null +++ b/target/linux/ipq806x/patches/0061-i2c-qup-Add-device-tree-bindings-information.patch @@ -0,0 +1,71 @@ +From 81480a89c72d811376e9e040729721705b2a984d Mon Sep 17 00:00:00 2001 +From: "Ivan T. Ivanov" <iivanov@mm-sol.com> +Date: Thu, 13 Mar 2014 19:07:42 -0700 +Subject: [PATCH 061/182] i2c: qup: Add device tree bindings information + +The Qualcomm Universal Peripherial (QUP) wraps I2C mini-core and +provide input and output FIFO's for it. I2C controller can operate +as master with supported bus speeds of 100Kbps and 400Kbps. + +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +[bjorn: reformulated part of binding description + added version to compatible + cleaned up example] +Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Acked-by: Rob Herring <robh@kernel.org> +[wsa: removed the dummy child node which was a confusing example] +Signed-off-by: Wolfram Sang <wsa@the-dreams.de> +--- + .../devicetree/bindings/i2c/qcom,i2c-qup.txt | 40 ++++++++++++++++++++ + 1 file changed, 40 insertions(+) + create mode 100644 Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt + +diff --git a/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt b/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt +new file mode 100644 +index 0000000..dc71754 +--- /dev/null ++++ b/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt +@@ -0,0 +1,40 @@ ++Qualcomm Universal Peripheral (QUP) I2C controller ++ ++Required properties: ++ - compatible: Should be: ++ * "qcom,i2c-qup-v1.1.1" for 8660, 8960 and 8064. ++ * "qcom,i2c-qup-v2.1.1" for 8974 v1. ++ * "qcom,i2c-qup-v2.2.1" for 8974 v2 and later. ++ - reg: Should contain QUP register address and length. ++ - interrupts: Should contain I2C interrupt. ++ ++ - clocks: A list of phandles + clock-specifiers, one for each entry in ++ clock-names. ++ - clock-names: Should contain: ++ * "core" for the core clock ++ * "iface" for the AHB clock ++ ++ - #address-cells: Should be <1> Address cells for i2c device address ++ - #size-cells: Should be <0> as i2c addresses have no size component ++ ++Optional properties: ++ - clock-frequency: Should specify the desired i2c bus clock frequency in Hz, ++ defaults to 100kHz if omitted. ++ ++Child nodes should conform to i2c bus binding. ++ ++Example: ++ ++ i2c@f9924000 { ++ compatible = "qcom,i2c-qup-v2.2.1"; ++ reg = <0xf9924000 0x1000>; ++ interrupts = <0 96 0>; ++ ++ clocks = <&gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>, <&gcc GCC_BLSP1_AHB_CLK>; ++ clock-names = "core", "iface"; ++ ++ clock-frequency = <355000>; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0062-i2c-qup-off-by-ones-in-qup_i2c_probe.patch b/target/linux/ipq806x/patches/0062-i2c-qup-off-by-ones-in-qup_i2c_probe.patch new file mode 100644 index 0000000000..a140997b82 --- /dev/null +++ b/target/linux/ipq806x/patches/0062-i2c-qup-off-by-ones-in-qup_i2c_probe.patch @@ -0,0 +1,36 @@ +From aa50d1b0d81e4a9509f15894ec64013aa5190b59 Mon Sep 17 00:00:00 2001 +From: Dan Carpenter <dan.carpenter@oracle.com> +Date: Thu, 3 Apr 2014 10:22:54 +0300 +Subject: [PATCH 062/182] i2c: qup: off by ones in qup_i2c_probe() + +These should ">= ARRAY_SIZE()" instead of "> ARRAY_SIZE()". + +Fixes: 10c5a8425968 ('i2c: qup: New bus driver for the Qualcomm QUP I2C controller') +Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com> +Signed-off-by: Wolfram Sang <wsa@the-dreams.de> +--- + drivers/i2c/busses/i2c-qup.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c +index c9d5f78..ee40980 100644 +--- a/drivers/i2c/busses/i2c-qup.c ++++ b/drivers/i2c/busses/i2c-qup.c +@@ -633,12 +633,12 @@ static int qup_i2c_probe(struct platform_device *pdev) + * associated with each byte written/received + */ + size = QUP_OUTPUT_BLOCK_SIZE(io_mode); +- if (size > ARRAY_SIZE(blk_sizes)) ++ if (size >= ARRAY_SIZE(blk_sizes)) + return -EIO; + qup->out_blk_sz = blk_sizes[size] / 2; + + size = QUP_INPUT_BLOCK_SIZE(io_mode); +- if (size > ARRAY_SIZE(blk_sizes)) ++ if (size >= ARRAY_SIZE(blk_sizes)) + return -EIO; + qup->in_blk_sz = blk_sizes[size] / 2; + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0063-i2c-qup-use-proper-type-fro-clk_freq.patch b/target/linux/ipq806x/patches/0063-i2c-qup-use-proper-type-fro-clk_freq.patch new file mode 100644 index 0000000000..60411abbaa --- /dev/null +++ b/target/linux/ipq806x/patches/0063-i2c-qup-use-proper-type-fro-clk_freq.patch @@ -0,0 +1,30 @@ +From 62bdf6c11c9120543ba4831f0226bf51db1d1fc0 Mon Sep 17 00:00:00 2001 +From: Wolfram Sang <wsa@the-dreams.de> +Date: Thu, 3 Apr 2014 11:30:33 +0200 +Subject: [PATCH 063/182] i2c: qup: use proper type fro clk_freq + +It is used with of_property_read_u32(), so it should be u32. + +Signed-off-by: Wolfram Sang <wsa@the-dreams.de> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Fixes: 10c5a8425968 ('i2c: qup: New bus driver for the Qualcomm QUP I2C controller') +--- + drivers/i2c/busses/i2c-qup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c +index ee40980..1b4cf14 100644 +--- a/drivers/i2c/busses/i2c-qup.c ++++ b/drivers/i2c/busses/i2c-qup.c +@@ -562,7 +562,7 @@ static int qup_i2c_probe(struct platform_device *pdev) + u32 io_mode, hw_ver, size; + int ret, fs_div, hs_div; + int src_clk_freq; +- int clk_freq = 100000; ++ u32 clk_freq = 100000; + + qup = devm_kzalloc(&pdev->dev, sizeof(*qup), GFP_KERNEL); + if (!qup) +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0064-i2c-qup-Fix-pm_runtime_get_sync-usage.patch b/target/linux/ipq806x/patches/0064-i2c-qup-Fix-pm_runtime_get_sync-usage.patch new file mode 100644 index 0000000000..eaec95084e --- /dev/null +++ b/target/linux/ipq806x/patches/0064-i2c-qup-Fix-pm_runtime_get_sync-usage.patch @@ -0,0 +1,31 @@ +From 82f2e53f207019c0d364d92515410be8dffd7524 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Fri, 2 May 2014 20:54:29 -0500 +Subject: [PATCH 064/182] i2c: qup: Fix pm_runtime_get_sync usage + +This patch corrects the error check on the call to pm_runtime_get_sync. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Reviewed-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Wolfram Sang <wsa@the-dreams.de> +--- + drivers/i2c/busses/i2c-qup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c +index 1b4cf14..2a5efb5 100644 +--- a/drivers/i2c/busses/i2c-qup.c ++++ b/drivers/i2c/busses/i2c-qup.c +@@ -479,7 +479,7 @@ static int qup_i2c_xfer(struct i2c_adapter *adap, + int ret, idx; + + ret = pm_runtime_get_sync(qup->dev); +- if (ret) ++ if (ret < 0) + goto out; + + writel(1, qup->base + QUP_SW_RESET); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0065-spi-Add-Qualcomm-QUP-SPI-controller-support.patch b/target/linux/ipq806x/patches/0065-spi-Add-Qualcomm-QUP-SPI-controller-support.patch new file mode 100644 index 0000000000..a392e0473d --- /dev/null +++ b/target/linux/ipq806x/patches/0065-spi-Add-Qualcomm-QUP-SPI-controller-support.patch @@ -0,0 +1,907 @@ +From 24884115a6029995dba2561b1ee810f28a34271a Mon Sep 17 00:00:00 2001 +From: "Ivan T. Ivanov" <iivanov@mm-sol.com> +Date: Thu, 13 Feb 2014 18:21:38 +0200 +Subject: [PATCH 065/182] spi: Add Qualcomm QUP SPI controller support + +Qualcomm Universal Peripheral (QUP) core is an AHB slave that +provides a common data path (an output FIFO and an input FIFO) +for serial peripheral interface (SPI) mini-core. SPI in master +mode supports up to 50MHz, up to four chip selects, programmable +data path from 4 bits to 32 bits and numerous protocol variants. + +Cc: Alok Chauhan <alokc@codeaurora.org> +Cc: Gilad Avidov <gavidov@codeaurora.org> +Cc: Kiran Gunda <kgunda@codeaurora.org> +Cc: Sagar Dharia <sdharia@codeaurora.org> +Cc: dsneddon@codeaurora.org +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/Kconfig | 13 + + drivers/spi/Makefile | 1 + + drivers/spi/spi-qup.c | 837 +++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 851 insertions(+) + create mode 100644 drivers/spi/spi-qup.c + +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 581ee2a..9e9e3ed 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -381,6 +381,19 @@ config SPI_RSPI + help + SPI driver for Renesas RSPI and QSPI blocks. + ++config SPI_QUP ++ tristate "Qualcomm SPI controller with QUP interface" ++ depends on ARCH_MSM_DT ++ help ++ Qualcomm Universal Peripheral (QUP) core is an AHB slave that ++ provides a common data path (an output FIFO and an input FIFO) ++ for serial peripheral interface (SPI) mini-core. SPI in master ++ mode supports up to 50MHz, up to four chip selects, programmable ++ data path from 4 bits to 32 bits and numerous protocol variants. ++ ++ This driver can also be built as a module. If so, the module ++ will be called spi_qup. ++ + config SPI_S3C24XX + tristate "Samsung S3C24XX series SPI" + depends on ARCH_S3C24XX +diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile +index 95af48d..e598147 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -59,6 +59,7 @@ spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_PXADMA) += spi-pxa2xx-pxadma.o + spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o + obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o + obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o ++obj-$(CONFIG_SPI_QUP) += spi-qup.o + obj-$(CONFIG_SPI_RSPI) += spi-rspi.o + obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o + spi-s3c24xx-hw-y := spi-s3c24xx.o +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +new file mode 100644 +index 0000000..b0bcc09 +--- /dev/null ++++ b/drivers/spi/spi-qup.c +@@ -0,0 +1,837 @@ ++/* ++ * Copyright (c) 2008-2014, The Linux foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License rev 2 and ++ * only rev 2 as published by the free Software foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or fITNESS fOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <linux/err.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/list.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/pm_runtime.h> ++#include <linux/spi/spi.h> ++ ++#define QUP_CONFIG 0x0000 ++#define QUP_STATE 0x0004 ++#define QUP_IO_M_MODES 0x0008 ++#define QUP_SW_RESET 0x000c ++#define QUP_OPERATIONAL 0x0018 ++#define QUP_ERROR_FLAGS 0x001c ++#define QUP_ERROR_FLAGS_EN 0x0020 ++#define QUP_OPERATIONAL_MASK 0x0028 ++#define QUP_HW_VERSION 0x0030 ++#define QUP_MX_OUTPUT_CNT 0x0100 ++#define QUP_OUTPUT_FIFO 0x0110 ++#define QUP_MX_WRITE_CNT 0x0150 ++#define QUP_MX_INPUT_CNT 0x0200 ++#define QUP_MX_READ_CNT 0x0208 ++#define QUP_INPUT_FIFO 0x0218 ++ ++#define SPI_CONFIG 0x0300 ++#define SPI_IO_CONTROL 0x0304 ++#define SPI_ERROR_FLAGS 0x0308 ++#define SPI_ERROR_FLAGS_EN 0x030c ++ ++/* QUP_CONFIG fields */ ++#define QUP_CONFIG_SPI_MODE (1 << 8) ++#define QUP_CONFIG_CLOCK_AUTO_GATE BIT(13) ++#define QUP_CONFIG_NO_INPUT BIT(7) ++#define QUP_CONFIG_NO_OUTPUT BIT(6) ++#define QUP_CONFIG_N 0x001f ++ ++/* QUP_STATE fields */ ++#define QUP_STATE_VALID BIT(2) ++#define QUP_STATE_RESET 0 ++#define QUP_STATE_RUN 1 ++#define QUP_STATE_PAUSE 3 ++#define QUP_STATE_MASK 3 ++#define QUP_STATE_CLEAR 2 ++ ++#define QUP_HW_VERSION_2_1_1 0x20010001 ++ ++/* QUP_IO_M_MODES fields */ ++#define QUP_IO_M_PACK_EN BIT(15) ++#define QUP_IO_M_UNPACK_EN BIT(14) ++#define QUP_IO_M_INPUT_MODE_MASK_SHIFT 12 ++#define QUP_IO_M_OUTPUT_MODE_MASK_SHIFT 10 ++#define QUP_IO_M_INPUT_MODE_MASK (3 << QUP_IO_M_INPUT_MODE_MASK_SHIFT) ++#define QUP_IO_M_OUTPUT_MODE_MASK (3 << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT) ++ ++#define QUP_IO_M_OUTPUT_BLOCK_SIZE(x) (((x) & (0x03 << 0)) >> 0) ++#define QUP_IO_M_OUTPUT_FIFO_SIZE(x) (((x) & (0x07 << 2)) >> 2) ++#define QUP_IO_M_INPUT_BLOCK_SIZE(x) (((x) & (0x03 << 5)) >> 5) ++#define QUP_IO_M_INPUT_FIFO_SIZE(x) (((x) & (0x07 << 7)) >> 7) ++ ++#define QUP_IO_M_MODE_FIFO 0 ++#define QUP_IO_M_MODE_BLOCK 1 ++#define QUP_IO_M_MODE_DMOV 2 ++#define QUP_IO_M_MODE_BAM 3 ++ ++/* QUP_OPERATIONAL fields */ ++#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) ++#define QUP_OP_OUT_SERVICE_FLAG BIT(8) ++#define QUP_OP_IN_FIFO_FULL BIT(7) ++#define QUP_OP_OUT_FIFO_FULL BIT(6) ++#define QUP_OP_IN_FIFO_NOT_EMPTY BIT(5) ++#define QUP_OP_OUT_FIFO_NOT_EMPTY BIT(4) ++ ++/* QUP_ERROR_FLAGS and QUP_ERROR_FLAGS_EN fields */ ++#define QUP_ERROR_OUTPUT_OVER_RUN BIT(5) ++#define QUP_ERROR_INPUT_UNDER_RUN BIT(4) ++#define QUP_ERROR_OUTPUT_UNDER_RUN BIT(3) ++#define QUP_ERROR_INPUT_OVER_RUN BIT(2) ++ ++/* SPI_CONFIG fields */ ++#define SPI_CONFIG_HS_MODE BIT(10) ++#define SPI_CONFIG_INPUT_FIRST BIT(9) ++#define SPI_CONFIG_LOOPBACK BIT(8) ++ ++/* SPI_IO_CONTROL fields */ ++#define SPI_IO_C_FORCE_CS BIT(11) ++#define SPI_IO_C_CLK_IDLE_HIGH BIT(10) ++#define SPI_IO_C_MX_CS_MODE BIT(8) ++#define SPI_IO_C_CS_N_POLARITY_0 BIT(4) ++#define SPI_IO_C_CS_SELECT(x) (((x) & 3) << 2) ++#define SPI_IO_C_CS_SELECT_MASK 0x000c ++#define SPI_IO_C_TRISTATE_CS BIT(1) ++#define SPI_IO_C_NO_TRI_STATE BIT(0) ++ ++/* SPI_ERROR_FLAGS and SPI_ERROR_FLAGS_EN fields */ ++#define SPI_ERROR_CLK_OVER_RUN BIT(1) ++#define SPI_ERROR_CLK_UNDER_RUN BIT(0) ++ ++#define SPI_NUM_CHIPSELECTS 4 ++ ++/* high speed mode is when bus rate is greater then 26MHz */ ++#define SPI_HS_MIN_RATE 26000000 ++#define SPI_MAX_RATE 50000000 ++ ++#define SPI_DELAY_THRESHOLD 1 ++#define SPI_DELAY_RETRY 10 ++ ++struct spi_qup_device { ++ int select; ++ u16 mode; ++}; ++ ++struct spi_qup { ++ void __iomem *base; ++ struct device *dev; ++ struct clk *cclk; /* core clock */ ++ struct clk *iclk; /* interface clock */ ++ int irq; ++ u32 max_speed_hz; ++ spinlock_t lock; ++ ++ int in_fifo_sz; ++ int out_fifo_sz; ++ int in_blk_sz; ++ int out_blk_sz; ++ ++ struct spi_transfer *xfer; ++ struct completion done; ++ int error; ++ int w_size; /* bytes per SPI word */ ++ int tx_bytes; ++ int rx_bytes; ++}; ++ ++ ++static inline bool spi_qup_is_valid_state(struct spi_qup *controller) ++{ ++ u32 opstate = readl_relaxed(controller->base + QUP_STATE); ++ ++ return opstate & QUP_STATE_VALID; ++} ++ ++static int spi_qup_set_state(struct spi_qup *controller, u32 state) ++{ ++ unsigned long loop; ++ u32 cur_state; ++ ++ loop = 0; ++ while (!spi_qup_is_valid_state(controller)) { ++ ++ usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD * 2); ++ ++ if (++loop > SPI_DELAY_RETRY) ++ return -EIO; ++ } ++ ++ if (loop) ++ dev_dbg(controller->dev, "invalid state for %ld,us %d\n", ++ loop, state); ++ ++ cur_state = readl_relaxed(controller->base + QUP_STATE); ++ /* ++ * Per spec: for PAUSE_STATE to RESET_STATE, two writes ++ * of (b10) are required ++ */ ++ if (((cur_state & QUP_STATE_MASK) == QUP_STATE_PAUSE) && ++ (state == QUP_STATE_RESET)) { ++ writel_relaxed(QUP_STATE_CLEAR, controller->base + QUP_STATE); ++ writel_relaxed(QUP_STATE_CLEAR, controller->base + QUP_STATE); ++ } else { ++ cur_state &= ~QUP_STATE_MASK; ++ cur_state |= state; ++ writel_relaxed(cur_state, controller->base + QUP_STATE); ++ } ++ ++ loop = 0; ++ while (!spi_qup_is_valid_state(controller)) { ++ ++ usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD * 2); ++ ++ if (++loop > SPI_DELAY_RETRY) ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++ ++static void spi_qup_fifo_read(struct spi_qup *controller, ++ struct spi_transfer *xfer) ++{ ++ 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; ++ } ++ ++ for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) { ++ /* ++ * The data format depends on bytes per SPI word: ++ * 4 bytes: 0x12345678 ++ * 2 bytes: 0x00001234 ++ * 1 byte : 0x00000012 ++ */ ++ shift = BITS_PER_BYTE; ++ shift *= (w_size - idx - 1); ++ rx_buf[controller->rx_bytes] = word >> shift; ++ } ++ } ++} ++ ++static void spi_qup_fifo_write(struct spi_qup *controller, ++ struct spi_transfer *xfer) ++{ ++ const u8 *tx_buf = xfer->tx_buf; ++ u32 word, state, data; ++ int idx, w_size; ++ ++ w_size = controller->w_size; ++ ++ while (controller->tx_bytes < xfer->len) { ++ ++ state = readl_relaxed(controller->base + QUP_OPERATIONAL); ++ if (state & QUP_OP_OUT_FIFO_FULL) ++ break; ++ ++ word = 0; ++ for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) { ++ ++ if (!tx_buf) { ++ controller->tx_bytes += w_size; ++ break; ++ } ++ ++ data = tx_buf[controller->tx_bytes]; ++ word |= data << (BITS_PER_BYTE * (3 - idx)); ++ } ++ ++ writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO); ++ } ++} ++ ++static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) ++{ ++ struct spi_qup *controller = dev_id; ++ struct spi_transfer *xfer; ++ u32 opflags, qup_err, spi_err; ++ unsigned long flags; ++ int error = 0; ++ ++ spin_lock_irqsave(&controller->lock, flags); ++ xfer = controller->xfer; ++ controller->xfer = NULL; ++ spin_unlock_irqrestore(&controller->lock, flags); ++ ++ qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS); ++ spi_err = readl_relaxed(controller->base + SPI_ERROR_FLAGS); ++ opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); ++ ++ writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS); ++ writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS); ++ writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); ++ ++ if (!xfer) { ++ dev_err_ratelimited(controller->dev, "unexpected irq %x08 %x08 %x08\n", ++ qup_err, spi_err, opflags); ++ return IRQ_HANDLED; ++ } ++ ++ if (qup_err) { ++ if (qup_err & QUP_ERROR_OUTPUT_OVER_RUN) ++ dev_warn(controller->dev, "OUTPUT_OVER_RUN\n"); ++ if (qup_err & QUP_ERROR_INPUT_UNDER_RUN) ++ dev_warn(controller->dev, "INPUT_UNDER_RUN\n"); ++ if (qup_err & QUP_ERROR_OUTPUT_UNDER_RUN) ++ dev_warn(controller->dev, "OUTPUT_UNDER_RUN\n"); ++ if (qup_err & QUP_ERROR_INPUT_OVER_RUN) ++ dev_warn(controller->dev, "INPUT_OVER_RUN\n"); ++ ++ error = -EIO; ++ } ++ ++ if (spi_err) { ++ if (spi_err & SPI_ERROR_CLK_OVER_RUN) ++ dev_warn(controller->dev, "CLK_OVER_RUN\n"); ++ if (spi_err & SPI_ERROR_CLK_UNDER_RUN) ++ dev_warn(controller->dev, "CLK_UNDER_RUN\n"); ++ ++ error = -EIO; ++ } ++ ++ 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); ++ ++ spin_lock_irqsave(&controller->lock, flags); ++ controller->error = error; ++ controller->xfer = xfer; ++ spin_unlock_irqrestore(&controller->lock, flags); ++ ++ if (controller->rx_bytes == xfer->len || error) ++ complete(&controller->done); ++ ++ return IRQ_HANDLED; ++} ++ ++ ++/* set clock freq ... bits per word */ ++static int spi_qup_io_config(struct spi_qup *controller, ++ struct spi_qup_device *chip, ++ struct spi_transfer *xfer) ++{ ++ u32 config, iomode, mode; ++ int ret, n_words, w_size; ++ ++ if (chip->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { ++ dev_err(controller->dev, "too big size for loopback %d > %d\n", ++ xfer->len, controller->in_fifo_sz); ++ return -EIO; ++ } ++ ++ ret = clk_set_rate(controller->cclk, xfer->speed_hz); ++ if (ret) { ++ dev_err(controller->dev, "fail to set frequency %d", ++ xfer->speed_hz); ++ return -EIO; ++ } ++ ++ if (spi_qup_set_state(controller, QUP_STATE_RESET)) { ++ dev_err(controller->dev, "cannot set RESET state\n"); ++ return -EIO; ++ } ++ ++ w_size = 4; ++ if (xfer->bits_per_word <= 8) ++ w_size = 1; ++ else if (xfer->bits_per_word <= 16) ++ w_size = 2; ++ ++ n_words = xfer->len / w_size; ++ controller->w_size = w_size; ++ ++ if (n_words <= controller->in_fifo_sz) { ++ mode = QUP_IO_M_MODE_FIFO; ++ writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT); ++ writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT); ++ /* must be zero for FIFO */ ++ writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); ++ writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); ++ } else { ++ mode = QUP_IO_M_MODE_BLOCK; ++ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); ++ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); ++ /* must be zero for BLOCK and BAM */ ++ writel_relaxed(0, controller->base + QUP_MX_READ_CNT); ++ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); ++ } ++ ++ iomode = readl_relaxed(controller->base + QUP_IO_M_MODES); ++ /* Set input and output transfer mode */ ++ iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK); ++ 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); ++ ++ writel_relaxed(iomode, controller->base + QUP_IO_M_MODES); ++ ++ config = readl_relaxed(controller->base + SPI_CONFIG); ++ ++ if (chip->mode & SPI_LOOP) ++ config |= SPI_CONFIG_LOOPBACK; ++ else ++ config &= ~SPI_CONFIG_LOOPBACK; ++ ++ if (chip->mode & SPI_CPHA) ++ config &= ~SPI_CONFIG_INPUT_FIRST; ++ else ++ config |= SPI_CONFIG_INPUT_FIRST; ++ ++ /* ++ * HS_MODE improves signal stability for spi-clk high rates, ++ * but is invalid in loop back mode. ++ */ ++ if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(chip->mode & SPI_LOOP)) ++ config |= SPI_CONFIG_HS_MODE; ++ else ++ config &= ~SPI_CONFIG_HS_MODE; ++ ++ writel_relaxed(config, controller->base + SPI_CONFIG); ++ ++ config = readl_relaxed(controller->base + QUP_CONFIG); ++ config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N); ++ config |= xfer->bits_per_word - 1; ++ config |= QUP_CONFIG_SPI_MODE; ++ writel_relaxed(config, controller->base + QUP_CONFIG); ++ ++ writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK); ++ return 0; ++} ++ ++static void spi_qup_set_cs(struct spi_device *spi, bool enable) ++{ ++ struct spi_qup *controller = spi_master_get_devdata(spi->master); ++ struct spi_qup_device *chip = spi_get_ctldata(spi); ++ ++ u32 iocontol, mask; ++ ++ iocontol = readl_relaxed(controller->base + SPI_IO_CONTROL); ++ ++ /* Disable auto CS toggle and use manual */ ++ iocontol &= ~SPI_IO_C_MX_CS_MODE; ++ iocontol |= SPI_IO_C_FORCE_CS; ++ ++ iocontol &= ~SPI_IO_C_CS_SELECT_MASK; ++ iocontol |= SPI_IO_C_CS_SELECT(chip->select); ++ ++ mask = SPI_IO_C_CS_N_POLARITY_0 << chip->select; ++ ++ if (enable) ++ iocontol |= mask; ++ else ++ iocontol &= ~mask; ++ ++ writel_relaxed(iocontol, controller->base + SPI_IO_CONTROL); ++} ++ ++static int spi_qup_transfer_one(struct spi_master *master, ++ struct spi_device *spi, ++ struct spi_transfer *xfer) ++{ ++ struct spi_qup *controller = spi_master_get_devdata(master); ++ struct spi_qup_device *chip = spi_get_ctldata(spi); ++ unsigned long timeout, flags; ++ int ret = -EIO; ++ ++ ret = spi_qup_io_config(controller, chip, xfer); ++ if (ret) ++ return ret; ++ ++ timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC); ++ timeout = DIV_ROUND_UP(xfer->len * 8, timeout); ++ timeout = 100 * msecs_to_jiffies(timeout); ++ ++ reinit_completion(&controller->done); ++ ++ spin_lock_irqsave(&controller->lock, flags); ++ controller->xfer = xfer; ++ controller->error = 0; ++ controller->rx_bytes = 0; ++ controller->tx_bytes = 0; ++ spin_unlock_irqrestore(&controller->lock, flags); ++ ++ if (spi_qup_set_state(controller, QUP_STATE_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; ++ } ++ ++ 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 (!wait_for_completion_timeout(&controller->done, timeout)) ++ ret = -ETIMEDOUT; ++exit: ++ spi_qup_set_state(controller, QUP_STATE_RESET); ++ spin_lock_irqsave(&controller->lock, flags); ++ controller->xfer = NULL; ++ if (!ret) ++ ret = controller->error; ++ spin_unlock_irqrestore(&controller->lock, flags); ++ return ret; ++} ++ ++static int spi_qup_setup(struct spi_device *spi) ++{ ++ struct spi_qup *controller = spi_master_get_devdata(spi->master); ++ struct spi_qup_device *chip = spi_get_ctldata(spi); ++ ++ if (spi->chip_select >= spi->master->num_chipselect) { ++ dev_err(controller->dev, "invalid chip_select %d\n", ++ spi->chip_select); ++ return -EINVAL; ++ } ++ ++ if (spi->max_speed_hz > controller->max_speed_hz) { ++ dev_err(controller->dev, "invalid max_speed_hz %d\n", ++ spi->max_speed_hz); ++ return -EINVAL; ++ } ++ ++ if (!chip) { ++ /* First setup */ ++ chip = kzalloc(sizeof(*chip), GFP_KERNEL); ++ if (!chip) { ++ dev_err(controller->dev, "no memory for chip data\n"); ++ return -ENOMEM; ++ } ++ ++ chip->mode = spi->mode; ++ chip->select = spi->chip_select; ++ spi_set_ctldata(spi, chip); ++ } ++ ++ return 0; ++} ++ ++static void spi_qup_cleanup(struct spi_device *spi) ++{ ++ struct spi_qup_device *chip = spi_get_ctldata(spi); ++ ++ if (!chip) ++ return; ++ ++ spi_set_ctldata(spi, NULL); ++ kfree(chip); ++} ++ ++static int spi_qup_probe(struct platform_device *pdev) ++{ ++ struct spi_master *master; ++ struct clk *iclk, *cclk; ++ struct spi_qup *controller; ++ struct resource *res; ++ struct device *dev; ++ void __iomem *base; ++ u32 data, max_freq, iomode; ++ int ret, irq, size; ++ ++ dev = &pdev->dev; ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ irq = platform_get_irq(pdev, 0); ++ ++ if (irq < 0) ++ return irq; ++ ++ cclk = devm_clk_get(dev, "core"); ++ if (IS_ERR(cclk)) ++ return PTR_ERR(cclk); ++ ++ iclk = devm_clk_get(dev, "iface"); ++ if (IS_ERR(iclk)) ++ return PTR_ERR(iclk); ++ ++ /* This is optional parameter */ ++ if (of_property_read_u32(dev->of_node, "spi-max-frequency", &max_freq)) ++ max_freq = SPI_MAX_RATE; ++ ++ if (!max_freq || max_freq > SPI_MAX_RATE) { ++ dev_err(dev, "invalid clock frequency %d\n", max_freq); ++ return -ENXIO; ++ } ++ ++ ret = clk_prepare_enable(cclk); ++ if (ret) { ++ dev_err(dev, "cannot enable core clock\n"); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(iclk); ++ if (ret) { ++ clk_disable_unprepare(cclk); ++ dev_err(dev, "cannot enable iface clock\n"); ++ return ret; ++ } ++ ++ data = readl_relaxed(base + QUP_HW_VERSION); ++ ++ if (data < QUP_HW_VERSION_2_1_1) { ++ clk_disable_unprepare(cclk); ++ clk_disable_unprepare(iclk); ++ dev_err(dev, "v.%08x is not supported\n", data); ++ return -ENXIO; ++ } ++ ++ master = spi_alloc_master(dev, sizeof(struct spi_qup)); ++ if (!master) { ++ clk_disable_unprepare(cclk); ++ clk_disable_unprepare(iclk); ++ dev_err(dev, "cannot allocate master\n"); ++ return -ENOMEM; ++ } ++ ++ master->bus_num = pdev->id; ++ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; ++ master->num_chipselect = SPI_NUM_CHIPSELECTS; ++ master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); ++ master->setup = spi_qup_setup; ++ master->cleanup = spi_qup_cleanup; ++ master->set_cs = spi_qup_set_cs; ++ master->transfer_one = spi_qup_transfer_one; ++ master->dev.of_node = pdev->dev.of_node; ++ master->auto_runtime_pm = true; ++ ++ platform_set_drvdata(pdev, master); ++ ++ controller = spi_master_get_devdata(master); ++ ++ controller->dev = dev; ++ controller->base = base; ++ controller->iclk = iclk; ++ controller->cclk = cclk; ++ controller->irq = irq; ++ controller->max_speed_hz = max_freq; ++ ++ spin_lock_init(&controller->lock); ++ init_completion(&controller->done); ++ ++ iomode = readl_relaxed(base + QUP_IO_M_MODES); ++ ++ size = QUP_IO_M_OUTPUT_BLOCK_SIZE(iomode); ++ if (size) ++ controller->out_blk_sz = size * 16; ++ else ++ controller->out_blk_sz = 4; ++ ++ size = QUP_IO_M_INPUT_BLOCK_SIZE(iomode); ++ if (size) ++ controller->in_blk_sz = size * 16; ++ else ++ controller->in_blk_sz = 4; ++ ++ size = QUP_IO_M_OUTPUT_FIFO_SIZE(iomode); ++ controller->out_fifo_sz = controller->out_blk_sz * (2 << size); ++ ++ size = QUP_IO_M_INPUT_FIFO_SIZE(iomode); ++ controller->in_fifo_sz = controller->in_blk_sz * (2 << size); ++ ++ dev_info(dev, "v.%08x IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n", ++ data, controller->in_blk_sz, controller->in_fifo_sz, ++ controller->out_blk_sz, controller->out_fifo_sz); ++ ++ writel_relaxed(1, base + QUP_SW_RESET); ++ ++ ret = spi_qup_set_state(controller, QUP_STATE_RESET); ++ if (ret) { ++ dev_err(dev, "cannot set RESET state\n"); ++ goto error; ++ } ++ ++ writel_relaxed(0, base + QUP_OPERATIONAL); ++ writel_relaxed(0, base + QUP_IO_M_MODES); ++ writel_relaxed(0, base + QUP_OPERATIONAL_MASK); ++ writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN, ++ base + SPI_ERROR_FLAGS_EN); ++ ++ writel_relaxed(0, base + SPI_CONFIG); ++ writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL); ++ ++ ret = devm_request_irq(dev, irq, spi_qup_qup_irq, ++ IRQF_TRIGGER_HIGH, pdev->name, controller); ++ if (ret) ++ goto error; ++ ++ ret = devm_spi_register_master(dev, master); ++ if (ret) ++ goto error; ++ ++ pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC); ++ pm_runtime_use_autosuspend(dev); ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ return 0; ++ ++error: ++ clk_disable_unprepare(cclk); ++ clk_disable_unprepare(iclk); ++ spi_master_put(master); ++ return ret; ++} ++ ++#ifdef CONFIG_PM_RUNTIME ++static int spi_qup_pm_suspend_runtime(struct device *device) ++{ ++ struct spi_master *master = dev_get_drvdata(device); ++ struct spi_qup *controller = spi_master_get_devdata(master); ++ u32 config; ++ ++ /* Enable clocks auto gaiting */ ++ config = readl(controller->base + QUP_CONFIG); ++ config |= QUP_CLOCK_AUTO_GATE; ++ writel_relaxed(config, controller->base + QUP_CONFIG); ++ return 0; ++} ++ ++static int spi_qup_pm_resume_runtime(struct device *device) ++{ ++ struct spi_master *master = dev_get_drvdata(device); ++ struct spi_qup *controller = spi_master_get_devdata(master); ++ u32 config; ++ ++ /* Disable clocks auto gaiting */ ++ config = readl_relaxed(controller->base + QUP_CONFIG); ++ config &= ~QUP_CLOCK_AUTO_GATE; ++ writel_relaxed(config, controller->base + QUP_CONFIG); ++ return 0; ++} ++#endif /* CONFIG_PM_RUNTIME */ ++ ++#ifdef CONFIG_PM_SLEEP ++static int spi_qup_suspend(struct device *device) ++{ ++ struct spi_master *master = dev_get_drvdata(device); ++ struct spi_qup *controller = spi_master_get_devdata(master); ++ int ret; ++ ++ ret = spi_master_suspend(master); ++ if (ret) ++ return ret; ++ ++ ret = spi_qup_set_state(controller, QUP_STATE_RESET); ++ if (ret) ++ return ret; ++ ++ clk_disable_unprepare(controller->cclk); ++ clk_disable_unprepare(controller->iclk); ++ return 0; ++} ++ ++static int spi_qup_resume(struct device *device) ++{ ++ struct spi_master *master = dev_get_drvdata(device); ++ struct spi_qup *controller = spi_master_get_devdata(master); ++ int ret; ++ ++ ret = clk_prepare_enable(controller->iclk); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(controller->cclk); ++ if (ret) ++ return ret; ++ ++ ret = spi_qup_set_state(controller, QUP_STATE_RESET); ++ if (ret) ++ return ret; ++ ++ return spi_master_resume(master); ++} ++#endif /* CONFIG_PM_SLEEP */ ++ ++static int spi_qup_remove(struct platform_device *pdev) ++{ ++ struct spi_master *master = dev_get_drvdata(&pdev->dev); ++ struct spi_qup *controller = spi_master_get_devdata(master); ++ int ret; ++ ++ ret = pm_runtime_get_sync(&pdev->dev); ++ if (ret) ++ return ret; ++ ++ ret = spi_qup_set_state(controller, QUP_STATE_RESET); ++ if (ret) ++ return ret; ++ ++ clk_disable_unprepare(controller->cclk); ++ clk_disable_unprepare(controller->iclk); ++ ++ pm_runtime_put_noidle(&pdev->dev); ++ pm_runtime_disable(&pdev->dev); ++ spi_master_put(master); ++ return 0; ++} ++ ++static struct of_device_id spi_qup_dt_match[] = { ++ { .compatible = "qcom,spi-qup-v2.1.1", }, ++ { .compatible = "qcom,spi-qup-v2.2.1", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, spi_qup_dt_match); ++ ++static const struct dev_pm_ops spi_qup_dev_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(spi_qup_suspend, spi_qup_resume) ++ SET_RUNTIME_PM_OPS(spi_qup_pm_suspend_runtime, ++ spi_qup_pm_resume_runtime, ++ NULL) ++}; ++ ++static struct platform_driver spi_qup_driver = { ++ .driver = { ++ .name = "spi_qup", ++ .owner = THIS_MODULE, ++ .pm = &spi_qup_dev_pm_ops, ++ .of_match_table = spi_qup_dt_match, ++ }, ++ .probe = spi_qup_probe, ++ .remove = spi_qup_remove, ++}; ++module_platform_driver(spi_qup_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_VERSION("0.4"); ++MODULE_ALIAS("platform:spi_qup"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0066-spi-qup-Add-device-tree-bindings-information.patch b/target/linux/ipq806x/patches/0066-spi-qup-Add-device-tree-bindings-information.patch new file mode 100644 index 0000000000..0d8b14c62c --- /dev/null +++ b/target/linux/ipq806x/patches/0066-spi-qup-Add-device-tree-bindings-information.patch @@ -0,0 +1,111 @@ +From a8e8c90a3cc81c6a7a44ff7fb18ceb71978c9155 Mon Sep 17 00:00:00 2001 +From: "Ivan T. Ivanov" <iivanov@mm-sol.com> +Date: Thu, 13 Feb 2014 18:21:23 +0200 +Subject: [PATCH 066/182] spi: qup: Add device tree bindings information + +The Qualcomm Universal Peripheral (QUP) core is an +AHB slave that provides a common data path (an output +FIFO and an input FIFO) for serial peripheral interface +(SPI) mini-core. + +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + .../devicetree/bindings/spi/qcom,spi-qup.txt | 85 ++++++++++++++++++++ + 1 file changed, 85 insertions(+) + create mode 100644 Documentation/devicetree/bindings/spi/qcom,spi-qup.txt + +diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt +new file mode 100644 +index 0000000..b82a268 +--- /dev/null ++++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt +@@ -0,0 +1,85 @@ ++Qualcomm Universal Peripheral (QUP) Serial Peripheral Interface (SPI) ++ ++The QUP core is an AHB slave that provides a common data path (an output FIFO ++and an input FIFO) for serial peripheral interface (SPI) mini-core. ++ ++SPI in master mode supports up to 50MHz, up to four chip selects, programmable ++data path from 4 bits to 32 bits and numerous protocol variants. ++ ++Required properties: ++- compatible: Should contain "qcom,spi-qup-v2.1.1" or "qcom,spi-qup-v2.2.1" ++- reg: Should contain base register location and length ++- interrupts: Interrupt number used by this controller ++ ++- clocks: Should contain the core clock and the AHB clock. ++- clock-names: Should be "core" for the core clock and "iface" for the ++ AHB clock. ++ ++- #address-cells: Number of cells required to define a chip select ++ address on the SPI bus. Should be set to 1. ++- #size-cells: Should be zero. ++ ++Optional properties: ++- spi-max-frequency: Specifies maximum SPI clock frequency, ++ Units - Hz. Definition as per ++ Documentation/devicetree/bindings/spi/spi-bus.txt ++ ++SPI slave nodes must be children of the SPI master node and can contain ++properties described in Documentation/devicetree/bindings/spi/spi-bus.txt ++ ++Example: ++ ++ spi_8: spi@f9964000 { /* BLSP2 QUP2 */ ++ ++ compatible = "qcom,spi-qup-v2"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0xf9964000 0x1000>; ++ interrupts = <0 102 0>; ++ spi-max-frequency = <19200000>; ++ ++ clocks = <&gcc GCC_BLSP2_QUP2_SPI_APPS_CLK>, <&gcc GCC_BLSP2_AHB_CLK>; ++ clock-names = "core", "iface"; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi8_default>; ++ ++ device@0 { ++ compatible = "arm,pl022-dummy"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <0>; /* Chip select 0 */ ++ spi-max-frequency = <19200000>; ++ spi-cpol; ++ }; ++ ++ device@1 { ++ compatible = "arm,pl022-dummy"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <1>; /* Chip select 1 */ ++ spi-max-frequency = <9600000>; ++ spi-cpha; ++ }; ++ ++ device@2 { ++ compatible = "arm,pl022-dummy"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <2>; /* Chip select 2 */ ++ spi-max-frequency = <19200000>; ++ spi-cpol; ++ spi-cpha; ++ }; ++ ++ device@3 { ++ compatible = "arm,pl022-dummy"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <3>; /* Chip select 3 */ ++ spi-max-frequency = <19200000>; ++ spi-cpol; ++ spi-cpha; ++ spi-cs-high; ++ }; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0067-spi-qup-Remove-spi_master_put-in-spi_qup_remove.patch b/target/linux/ipq806x/patches/0067-spi-qup-Remove-spi_master_put-in-spi_qup_remove.patch new file mode 100644 index 0000000000..decf585fd7 --- /dev/null +++ b/target/linux/ipq806x/patches/0067-spi-qup-Remove-spi_master_put-in-spi_qup_remove.patch @@ -0,0 +1,30 @@ +From c6a3f7bda60cfd78c2b05e8b2ec0fbf0d39da9ea Mon Sep 17 00:00:00 2001 +From: Axel Lin <axel.lin@ingics.com> +Date: Fri, 21 Feb 2014 09:33:16 +0800 +Subject: [PATCH 067/182] spi: qup: Remove spi_master_put in spi_qup_remove + +This driver uses devm_spi_register_master() so don't explicitly call +spi_master_put() in spi_qup_remove(). + +Signed-off-by: Axel Lin <axel.lin@ingics.com> +Acked-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/spi-qup.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index b0bcc09..5edc56f 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -802,7 +802,6 @@ static int spi_qup_remove(struct platform_device *pdev) + + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_disable(&pdev->dev); +- spi_master_put(master); + return 0; + } + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0068-spi-qup-Convert-ot-let-spi-core-handle-checking-tran.patch b/target/linux/ipq806x/patches/0068-spi-qup-Convert-ot-let-spi-core-handle-checking-tran.patch new file mode 100644 index 0000000000..6914d44a3a --- /dev/null +++ b/target/linux/ipq806x/patches/0068-spi-qup-Convert-ot-let-spi-core-handle-checking-tran.patch @@ -0,0 +1,69 @@ +From a4b122f945c564e47a42a9665b484785e86648de Mon Sep 17 00:00:00 2001 +From: Axel Lin <axel.lin@ingics.com> +Date: Fri, 21 Feb 2014 09:34:16 +0800 +Subject: [PATCH 068/182] spi: qup: Convert ot let spi core handle checking + transfer speed + +Set master->max_speed_hz then spi core will handle checking transfer speed. +So we can remove the same checking in this driver. + +Also remove checking spi->chip_select in spi_qup_setup(), the checking is done +by spi core. + +Signed-off-by: Axel Lin <axel.lin@ingics.com> +Acked-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/spi-qup.c | 15 +-------------- + 1 file changed, 1 insertion(+), 14 deletions(-) + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index 5edc56f..dec339d 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -134,7 +134,6 @@ struct spi_qup { + struct clk *cclk; /* core clock */ + struct clk *iclk; /* interface clock */ + int irq; +- u32 max_speed_hz; + spinlock_t lock; + + int in_fifo_sz; +@@ -517,18 +516,6 @@ static int spi_qup_setup(struct spi_device *spi) + struct spi_qup *controller = spi_master_get_devdata(spi->master); + struct spi_qup_device *chip = spi_get_ctldata(spi); + +- if (spi->chip_select >= spi->master->num_chipselect) { +- dev_err(controller->dev, "invalid chip_select %d\n", +- spi->chip_select); +- return -EINVAL; +- } +- +- if (spi->max_speed_hz > controller->max_speed_hz) { +- dev_err(controller->dev, "invalid max_speed_hz %d\n", +- spi->max_speed_hz); +- return -EINVAL; +- } +- + if (!chip) { + /* First setup */ + chip = kzalloc(sizeof(*chip), GFP_KERNEL); +@@ -629,6 +616,7 @@ static int spi_qup_probe(struct platform_device *pdev) + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; + master->num_chipselect = SPI_NUM_CHIPSELECTS; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); ++ master->max_speed_hz = max_freq; + master->setup = spi_qup_setup; + master->cleanup = spi_qup_cleanup; + master->set_cs = spi_qup_set_cs; +@@ -645,7 +633,6 @@ static int spi_qup_probe(struct platform_device *pdev) + controller->iclk = iclk; + controller->cclk = cclk; + controller->irq = irq; +- controller->max_speed_hz = max_freq; + + spin_lock_init(&controller->lock); + init_completion(&controller->done); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0069-spi-qup-Fix-build-error-due-to-a-typo.patch b/target/linux/ipq806x/patches/0069-spi-qup-Fix-build-error-due-to-a-typo.patch new file mode 100644 index 0000000000..558f8604f2 --- /dev/null +++ b/target/linux/ipq806x/patches/0069-spi-qup-Fix-build-error-due-to-a-typo.patch @@ -0,0 +1,48 @@ +From df7444fdd808111f7df507b00d357b44d3259376 Mon Sep 17 00:00:00 2001 +From: Axel Lin <axel.lin@ingics.com> +Date: Sun, 23 Feb 2014 13:27:16 +0800 +Subject: [PATCH 069/182] spi: qup: Fix build error due to a typo + +Fix below build error when CONFIG_PM_RUNTIME=y: + +C [M] drivers/spi/spi-qup.o +drivers/spi/spi-qup.c: In function 'spi_qup_pm_suspend_runtime': +drivers/spi/spi-qup.c:712:12: error: 'QUP_CLOCK_AUTO_GATE' undeclared (first use in this function) +drivers/spi/spi-qup.c:712:12: note: each undeclared identifier is reported only once for each function it appears in +drivers/spi/spi-qup.c: In function 'spi_qup_pm_resume_runtime': +drivers/spi/spi-qup.c:725:13: error: 'QUP_CLOCK_AUTO_GATE' undeclared (first use in this function) +make[2]: *** [drivers/spi/spi-qup.o] Error 1 +make[1]: *** [drivers/spi] Error 2 +make: *** [drivers] Error 2 + +Signed-off-by: Axel Lin <axel.lin@ingics.com> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/spi-qup.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index dec339d..886edb4 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -709,7 +709,7 @@ static int spi_qup_pm_suspend_runtime(struct device *device) + + /* Enable clocks auto gaiting */ + config = readl(controller->base + QUP_CONFIG); +- config |= QUP_CLOCK_AUTO_GATE; ++ config |= QUP_CONFIG_CLOCK_AUTO_GATE; + writel_relaxed(config, controller->base + QUP_CONFIG); + return 0; + } +@@ -722,7 +722,7 @@ static int spi_qup_pm_resume_runtime(struct device *device) + + /* Disable clocks auto gaiting */ + config = readl_relaxed(controller->base + QUP_CONFIG); +- config &= ~QUP_CLOCK_AUTO_GATE; ++ config &= ~QUP_CONFIG_CLOCK_AUTO_GATE; + writel_relaxed(config, controller->base + QUP_CONFIG); + return 0; + } +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0070-spi-qup-Enable-driver-compilation-with-COMPILE_TEST.patch b/target/linux/ipq806x/patches/0070-spi-qup-Enable-driver-compilation-with-COMPILE_TEST.patch new file mode 100644 index 0000000000..aee0bf488c --- /dev/null +++ b/target/linux/ipq806x/patches/0070-spi-qup-Enable-driver-compilation-with-COMPILE_TEST.patch @@ -0,0 +1,30 @@ +From 4706cb5844e0f1a08ab25e103f6415fde5328ddc Mon Sep 17 00:00:00 2001 +From: Axel Lin <axel.lin@ingics.com> +Date: Sun, 23 Feb 2014 13:28:33 +0800 +Subject: [PATCH 070/182] spi: qup: Enable driver compilation with + COMPILE_TEST + +This helps increasing build testing coverage. + +Signed-off-by: Axel Lin <axel.lin@ingics.com> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 9e9e3ed..e6a04f8 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -383,7 +383,7 @@ config SPI_RSPI + + config SPI_QUP + tristate "Qualcomm SPI controller with QUP interface" +- depends on ARCH_MSM_DT ++ depends on ARCH_MSM_DT || COMPILE_TEST + help + Qualcomm Universal Peripheral (QUP) core is an AHB slave that + provides a common data path (an output FIFO and an input FIFO) +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0071-spi-qup-Depend-on-ARM-COMPILE_TEST-to-avoid-build-er.patch b/target/linux/ipq806x/patches/0071-spi-qup-Depend-on-ARM-COMPILE_TEST-to-avoid-build-er.patch new file mode 100644 index 0000000000..ce773da713 --- /dev/null +++ b/target/linux/ipq806x/patches/0071-spi-qup-Depend-on-ARM-COMPILE_TEST-to-avoid-build-er.patch @@ -0,0 +1,40 @@ +From 7137376bc6415ab9b2f0c5983245a1273812e8b9 Mon Sep 17 00:00:00 2001 +From: Axel Lin <axel.lin@ingics.com> +Date: Mon, 24 Feb 2014 12:07:51 +0800 +Subject: [PATCH 071/182] spi: qup: Depend on ARM && COMPILE_TEST to avoid + build error + +This driver uses writel_relaxed() which does not exist in x86, ppc, etc. +Make it depend on ARM && COMPILE_TEST to avoid below build error: + + CC [M] drivers/spi/spi-qup.o +drivers/spi/spi-qup.c: In function 'spi_qup_set_state': +drivers/spi/spi-qup.c:180:3: error: implicit declaration of function 'writel_relaxed' [-Werror=implicit-function-declaration] +cc1: some warnings being treated as errors +make[2]: *** [drivers/spi/spi-qup.o] Error 1 +make[1]: *** [drivers/spi] Error 2 +make: *** [drivers] Error 2 + +Reported-by: Stephen Rothwell <sfr@canb.auug.org.au> +Signed-off-by: Axel Lin <axel.lin@ingics.com> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index e6a04f8..2d9111c 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -383,7 +383,7 @@ config SPI_RSPI + + config SPI_QUP + tristate "Qualcomm SPI controller with QUP interface" +- depends on ARCH_MSM_DT || COMPILE_TEST ++ depends on ARCH_MSM_DT || (ARM && COMPILE_TEST) + help + Qualcomm Universal Peripheral (QUP) core is an AHB slave that + provides a common data path (an output FIFO and an input FIFO) +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0072-spi-qup-Remove-module-version.patch b/target/linux/ipq806x/patches/0072-spi-qup-Remove-module-version.patch new file mode 100644 index 0000000000..fcdc517770 --- /dev/null +++ b/target/linux/ipq806x/patches/0072-spi-qup-Remove-module-version.patch @@ -0,0 +1,28 @@ +From 7b8e00404cfc652abbd538df1365a8046ed0d782 Mon Sep 17 00:00:00 2001 +From: Axel Lin <axel.lin@ingics.com> +Date: Mon, 24 Feb 2014 13:48:12 +0800 +Subject: [PATCH 072/182] spi: qup: Remove module version + +The module version is unlikely to be updated, use kernel version should be +enough. + +Signed-off-by: Axel Lin <axel.lin@ingics.com> +Acked-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/spi-qup.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index 886edb4..203f0d4 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -819,5 +819,4 @@ static struct platform_driver spi_qup_driver = { + module_platform_driver(spi_qup_driver); + + MODULE_LICENSE("GPL v2"); +-MODULE_VERSION("0.4"); + MODULE_ALIAS("platform:spi_qup"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0073-spi-qup-Get-rid-of-using-struct-spi_qup_device.patch b/target/linux/ipq806x/patches/0073-spi-qup-Get-rid-of-using-struct-spi_qup_device.patch new file mode 100644 index 0000000000..69b4609a5f --- /dev/null +++ b/target/linux/ipq806x/patches/0073-spi-qup-Get-rid-of-using-struct-spi_qup_device.patch @@ -0,0 +1,167 @@ +From 3027505ab8c8cb17291e53bca1d7bd770539d468 Mon Sep 17 00:00:00 2001 +From: Axel Lin <axel.lin@ingics.com> +Date: Mon, 24 Feb 2014 23:07:36 +0800 +Subject: [PATCH 073/182] spi: qup: Get rid of using struct spi_qup_device + +Current code uses struct spi_qup_device to store spi->mode and spi->chip_select +settings. We can get these settings in spi_qup_transfer_one and spi_qup_set_cs +without using struct spi_qup_device. Refactor the code a bit to remove +spi_qup_setup(), spi_qup_cleanup(), and struct spi_qup_device. + +Signed-off-by: Axel Lin <axel.lin@ingics.com> +Tested-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/spi-qup.c | 61 ++++++++----------------------------------------- + 1 file changed, 9 insertions(+), 52 deletions(-) + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index 203f0d4..b032e88 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -123,11 +123,6 @@ + #define SPI_DELAY_THRESHOLD 1 + #define SPI_DELAY_RETRY 10 + +-struct spi_qup_device { +- int select; +- u16 mode; +-}; +- + struct spi_qup { + void __iomem *base; + struct device *dev; +@@ -338,14 +333,13 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) + + + /* set clock freq ... bits per word */ +-static int spi_qup_io_config(struct spi_qup *controller, +- struct spi_qup_device *chip, +- struct spi_transfer *xfer) ++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; + int ret, n_words, w_size; + +- if (chip->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { ++ if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { + dev_err(controller->dev, "too big size for loopback %d > %d\n", + xfer->len, controller->in_fifo_sz); + return -EIO; +@@ -399,12 +393,12 @@ static int spi_qup_io_config(struct spi_qup *controller, + + config = readl_relaxed(controller->base + SPI_CONFIG); + +- if (chip->mode & SPI_LOOP) ++ if (spi->mode & SPI_LOOP) + config |= SPI_CONFIG_LOOPBACK; + else + config &= ~SPI_CONFIG_LOOPBACK; + +- if (chip->mode & SPI_CPHA) ++ if (spi->mode & SPI_CPHA) + config &= ~SPI_CONFIG_INPUT_FIRST; + else + config |= SPI_CONFIG_INPUT_FIRST; +@@ -413,7 +407,7 @@ static int spi_qup_io_config(struct spi_qup *controller, + * HS_MODE improves signal stability for spi-clk high rates, + * but is invalid in loop back mode. + */ +- if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(chip->mode & SPI_LOOP)) ++ if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(spi->mode & SPI_LOOP)) + config |= SPI_CONFIG_HS_MODE; + else + config &= ~SPI_CONFIG_HS_MODE; +@@ -433,7 +427,6 @@ static int spi_qup_io_config(struct spi_qup *controller, + static void spi_qup_set_cs(struct spi_device *spi, bool enable) + { + struct spi_qup *controller = spi_master_get_devdata(spi->master); +- struct spi_qup_device *chip = spi_get_ctldata(spi); + + u32 iocontol, mask; + +@@ -444,9 +437,9 @@ static void spi_qup_set_cs(struct spi_device *spi, bool enable) + iocontol |= SPI_IO_C_FORCE_CS; + + iocontol &= ~SPI_IO_C_CS_SELECT_MASK; +- iocontol |= SPI_IO_C_CS_SELECT(chip->select); ++ iocontol |= SPI_IO_C_CS_SELECT(spi->chip_select); + +- mask = SPI_IO_C_CS_N_POLARITY_0 << chip->select; ++ mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select; + + if (enable) + iocontol |= mask; +@@ -461,11 +454,10 @@ static int spi_qup_transfer_one(struct spi_master *master, + struct spi_transfer *xfer) + { + struct spi_qup *controller = spi_master_get_devdata(master); +- struct spi_qup_device *chip = spi_get_ctldata(spi); + unsigned long timeout, flags; + int ret = -EIO; + +- ret = spi_qup_io_config(controller, chip, xfer); ++ ret = spi_qup_io_config(spi, xfer); + if (ret) + return ret; + +@@ -511,38 +503,6 @@ exit: + return ret; + } + +-static int spi_qup_setup(struct spi_device *spi) +-{ +- struct spi_qup *controller = spi_master_get_devdata(spi->master); +- struct spi_qup_device *chip = spi_get_ctldata(spi); +- +- if (!chip) { +- /* First setup */ +- chip = kzalloc(sizeof(*chip), GFP_KERNEL); +- if (!chip) { +- dev_err(controller->dev, "no memory for chip data\n"); +- return -ENOMEM; +- } +- +- chip->mode = spi->mode; +- chip->select = spi->chip_select; +- spi_set_ctldata(spi, chip); +- } +- +- return 0; +-} +- +-static void spi_qup_cleanup(struct spi_device *spi) +-{ +- struct spi_qup_device *chip = spi_get_ctldata(spi); +- +- if (!chip) +- return; +- +- spi_set_ctldata(spi, NULL); +- kfree(chip); +-} +- + static int spi_qup_probe(struct platform_device *pdev) + { + struct spi_master *master; +@@ -561,7 +521,6 @@ static int spi_qup_probe(struct platform_device *pdev) + return PTR_ERR(base); + + irq = platform_get_irq(pdev, 0); +- + if (irq < 0) + return irq; + +@@ -617,8 +576,6 @@ static int spi_qup_probe(struct platform_device *pdev) + master->num_chipselect = SPI_NUM_CHIPSELECTS; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + master->max_speed_hz = max_freq; +- master->setup = spi_qup_setup; +- master->cleanup = spi_qup_cleanup; + master->set_cs = spi_qup_set_cs; + master->transfer_one = spi_qup_transfer_one; + master->dev.of_node = pdev->dev.of_node; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0074-spi-qup-Depend-on-ARCH_QCOM.patch b/target/linux/ipq806x/patches/0074-spi-qup-Depend-on-ARCH_QCOM.patch new file mode 100644 index 0000000000..9f279e07a3 --- /dev/null +++ b/target/linux/ipq806x/patches/0074-spi-qup-Depend-on-ARCH_QCOM.patch @@ -0,0 +1,36 @@ +From 5148138d2b879c7b024fa3865fb3980c6dc84fcc Mon Sep 17 00:00:00 2001 +From: Paul Bolle <pebolle@tiscali.nl> +Date: Mon, 7 Apr 2014 16:15:45 +0200 +Subject: [PATCH 074/182] spi: qup: Depend on ARCH_QCOM + +Commit 8fc1b0f87d9f ("ARM: qcom: Split Qualcomm support into legacy and +multiplatform") removed Kconfig symbol ARCH_MSM_DT. But that commit +left one (optional) dependency on ARCH_MSM_DT untouched. + +Three Kconfig symbols used to depend on ARCH_MSM_DT: ARCH_MSM8X60, +ARCH_MSM8960, and ARCH_MSM8974. These three symbols now depend on +ARCH_QCOM. So it appears this driver needs to depend on ARCH_QCOM too. + +Signed-off-by: Paul Bolle <pebolle@tiscali.nl> +Reviewed-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 2d9111c..a2d3570 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -383,7 +383,7 @@ config SPI_RSPI + + config SPI_QUP + tristate "Qualcomm SPI controller with QUP interface" +- depends on ARCH_MSM_DT || (ARM && COMPILE_TEST) ++ depends on ARCH_QCOM || (ARM && COMPILE_TEST) + help + Qualcomm Universal Peripheral (QUP) core is an AHB slave that + provides a common data path (an output FIFO and an input FIFO) +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0075-spi-qup-Correct-selection-of-FIFO-Block-mode.patch b/target/linux/ipq806x/patches/0075-spi-qup-Correct-selection-of-FIFO-Block-mode.patch new file mode 100644 index 0000000000..f1071dbea1 --- /dev/null +++ b/target/linux/ipq806x/patches/0075-spi-qup-Correct-selection-of-FIFO-Block-mode.patch @@ -0,0 +1,39 @@ +From e8e77359524e6629bac68db4183694fccffaed8e Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Tue, 13 May 2014 16:34:42 -0500 +Subject: [PATCH 075/182] spi: qup: Correct selection of FIFO/Block mode + +This patch fixes the calculation for determining whether to use FIFO or BLOCK +mode. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/spi-qup.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index b032e88..65bf18e 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -287,7 +287,7 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) + writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); + + if (!xfer) { +- dev_err_ratelimited(controller->dev, "unexpected irq %x08 %x08 %x08\n", ++ dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n", + qup_err, spi_err, opflags); + return IRQ_HANDLED; + } +@@ -366,7 +366,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + n_words = xfer->len / w_size; + controller->w_size = w_size; + +- if (n_words <= controller->in_fifo_sz) { ++ 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); + writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0078-clk-qcom-Consolidate-common-probe-code.patch b/target/linux/ipq806x/patches/0078-clk-qcom-Consolidate-common-probe-code.patch new file mode 100644 index 0000000000..c801ed5b35 --- /dev/null +++ b/target/linux/ipq806x/patches/0078-clk-qcom-Consolidate-common-probe-code.patch @@ -0,0 +1,755 @@ +From d79cb0c583772f38a9367af106d61bcf8bfa08e4 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 21 Mar 2014 17:59:37 -0700 +Subject: [PATCH 078/182] clk: qcom: Consolidate common probe code + +Most of the probe code is the same between all the different +clock controllers. Consolidate the code into a common.c file. +This makes changes to the common probe parts easier and reduces +chances for bugs. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/common.c | 99 +++++++++++++++++++++++++++++++++++++++ + drivers/clk/qcom/common.h | 34 ++++++++++++++ + drivers/clk/qcom/gcc-msm8660.c | 87 +++++----------------------------- + drivers/clk/qcom/gcc-msm8960.c | 77 +++++------------------------- + drivers/clk/qcom/gcc-msm8974.c | 77 +++++------------------------- + drivers/clk/qcom/mmcc-msm8960.c | 78 +++++------------------------- + drivers/clk/qcom/mmcc-msm8974.c | 80 +++++++------------------------ + 8 files changed, 196 insertions(+), 337 deletions(-) + create mode 100644 drivers/clk/qcom/common.c + create mode 100644 drivers/clk/qcom/common.h + +diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile +index f60db2e..689e05b 100644 +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -1,5 +1,6 @@ + obj-$(CONFIG_COMMON_CLK_QCOM) += clk-qcom.o + ++clk-qcom-y += common.o + clk-qcom-y += clk-regmap.o + clk-qcom-y += clk-pll.o + clk-qcom-y += clk-rcg.o +diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c +new file mode 100644 +index 0000000..86b45fb +--- /dev/null ++++ b/drivers/clk/qcom/common.c +@@ -0,0 +1,99 @@ ++/* ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/export.h> ++#include <linux/regmap.h> ++#include <linux/platform_device.h> ++#include <linux/clk-provider.h> ++#include <linux/reset-controller.h> ++ ++#include "common.h" ++#include "clk-regmap.h" ++#include "reset.h" ++ ++struct qcom_cc { ++ struct qcom_reset_controller reset; ++ struct clk_onecell_data data; ++ struct clk *clks[]; ++}; ++ ++int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc) ++{ ++ void __iomem *base; ++ struct resource *res; ++ int i, ret; ++ struct device *dev = &pdev->dev; ++ struct clk *clk; ++ struct clk_onecell_data *data; ++ struct clk **clks; ++ struct regmap *regmap; ++ struct qcom_reset_controller *reset; ++ struct qcom_cc *cc; ++ size_t num_clks = desc->num_clks; ++ struct clk_regmap **rclks = desc->clks; ++ ++ 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(dev, base, desc->config); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); ++ ++ cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, ++ GFP_KERNEL); ++ if (!cc) ++ return -ENOMEM; ++ ++ clks = cc->clks; ++ data = &cc->data; ++ data->clks = clks; ++ data->clk_num = num_clks; ++ ++ for (i = 0; i < num_clks; i++) { ++ if (!rclks[i]) ++ continue; ++ clk = devm_clk_register_regmap(dev, rclks[i]); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ clks[i] = clk; ++ } ++ ++ ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); ++ if (ret) ++ return ret; ++ ++ reset = &cc->reset; ++ reset->rcdev.of_node = dev->of_node; ++ reset->rcdev.ops = &qcom_reset_ops; ++ reset->rcdev.owner = dev->driver->owner; ++ reset->rcdev.nr_resets = desc->num_resets; ++ reset->regmap = regmap; ++ reset->reset_map = desc->resets; ++ platform_set_drvdata(pdev, &reset->rcdev); ++ ++ ret = reset_controller_register(&reset->rcdev); ++ if (ret) ++ of_clk_del_provider(dev->of_node); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(qcom_cc_probe); ++ ++void qcom_cc_remove(struct platform_device *pdev) ++{ ++ of_clk_del_provider(pdev->dev.of_node); ++ reset_controller_unregister(platform_get_drvdata(pdev)); ++} ++EXPORT_SYMBOL_GPL(qcom_cc_remove); +diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h +new file mode 100644 +index 0000000..2c3cfc8 +--- /dev/null ++++ b/drivers/clk/qcom/common.h +@@ -0,0 +1,34 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#ifndef __QCOM_CLK_COMMON_H__ ++#define __QCOM_CLK_COMMON_H__ ++ ++struct platform_device; ++struct regmap_config; ++struct clk_regmap; ++struct qcom_reset_map; ++ ++struct qcom_cc_desc { ++ const struct regmap_config *config; ++ struct clk_regmap **clks; ++ size_t num_clks; ++ const struct qcom_reset_map *resets; ++ size_t num_resets; ++}; ++ ++extern int qcom_cc_probe(struct platform_device *pdev, ++ const struct qcom_cc_desc *desc); ++ ++extern void qcom_cc_remove(struct platform_device *pdev); ++ ++#endif +diff --git a/drivers/clk/qcom/gcc-msm8660.c b/drivers/clk/qcom/gcc-msm8660.c +index bc0b7f1..44bc6fa 100644 +--- a/drivers/clk/qcom/gcc-msm8660.c ++++ b/drivers/clk/qcom/gcc-msm8660.c +@@ -25,6 +25,7 @@ + #include <dt-bindings/clock/qcom,gcc-msm8660.h> + #include <dt-bindings/reset/qcom,gcc-msm8660.h> + ++#include "common.h" + #include "clk-regmap.h" + #include "clk-pll.h" + #include "clk-rcg.h" +@@ -2701,94 +2702,28 @@ static const struct regmap_config gcc_msm8660_regmap_config = { + .fast_io = true, + }; + ++static const struct qcom_cc_desc gcc_msm8660_desc = { ++ .config = &gcc_msm8660_regmap_config, ++ .clks = gcc_msm8660_clks, ++ .num_clks = ARRAY_SIZE(gcc_msm8660_clks), ++ .resets = gcc_msm8660_resets, ++ .num_resets = ARRAY_SIZE(gcc_msm8660_resets), ++}; ++ + static const struct of_device_id gcc_msm8660_match_table[] = { + { .compatible = "qcom,gcc-msm8660" }, + { } + }; + MODULE_DEVICE_TABLE(of, gcc_msm8660_match_table); + +-struct qcom_cc { +- struct qcom_reset_controller reset; +- struct clk_onecell_data data; +- struct clk *clks[]; +-}; +- + static int gcc_msm8660_probe(struct platform_device *pdev) + { +- void __iomem *base; +- struct resource *res; +- int i, ret; +- struct device *dev = &pdev->dev; +- struct clk *clk; +- struct clk_onecell_data *data; +- struct clk **clks; +- struct regmap *regmap; +- size_t num_clks; +- struct qcom_reset_controller *reset; +- struct qcom_cc *cc; +- +- 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(dev, base, &gcc_msm8660_regmap_config); +- if (IS_ERR(regmap)) +- return PTR_ERR(regmap); +- +- num_clks = ARRAY_SIZE(gcc_msm8660_clks); +- cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, +- GFP_KERNEL); +- if (!cc) +- return -ENOMEM; +- +- clks = cc->clks; +- data = &cc->data; +- data->clks = clks; +- data->clk_num = num_clks; +- +- /* Temporary until RPM clocks supported */ +- clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- +- clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 27000000); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- +- for (i = 0; i < num_clks; i++) { +- if (!gcc_msm8660_clks[i]) +- continue; +- clk = devm_clk_register_regmap(dev, gcc_msm8660_clks[i]); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- clks[i] = clk; +- } +- +- ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); +- if (ret) +- return ret; +- +- reset = &cc->reset; +- reset->rcdev.of_node = dev->of_node; +- reset->rcdev.ops = &qcom_reset_ops, +- reset->rcdev.owner = THIS_MODULE, +- reset->rcdev.nr_resets = ARRAY_SIZE(gcc_msm8660_resets), +- reset->regmap = regmap; +- reset->reset_map = gcc_msm8660_resets, +- platform_set_drvdata(pdev, &reset->rcdev); +- +- ret = reset_controller_register(&reset->rcdev); +- if (ret) +- of_clk_del_provider(dev->of_node); +- +- return ret; ++ return qcom_cc_probe(pdev, &gcc_msm8660_desc); + } + + static int gcc_msm8660_remove(struct platform_device *pdev) + { +- of_clk_del_provider(pdev->dev.of_node); +- reset_controller_unregister(platform_get_drvdata(pdev)); ++ qcom_cc_remove(pdev); + return 0; + } + +diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c +index fd446ab..633b019 100644 +--- a/drivers/clk/qcom/gcc-msm8960.c ++++ b/drivers/clk/qcom/gcc-msm8960.c +@@ -25,6 +25,7 @@ + #include <dt-bindings/clock/qcom,gcc-msm8960.h> + #include <dt-bindings/reset/qcom,gcc-msm8960.h> + ++#include "common.h" + #include "clk-regmap.h" + #include "clk-pll.h" + #include "clk-rcg.h" +@@ -2875,51 +2876,24 @@ static const struct regmap_config gcc_msm8960_regmap_config = { + .fast_io = true, + }; + ++static const struct qcom_cc_desc gcc_msm8960_desc = { ++ .config = &gcc_msm8960_regmap_config, ++ .clks = gcc_msm8960_clks, ++ .num_clks = ARRAY_SIZE(gcc_msm8960_clks), ++ .resets = gcc_msm8960_resets, ++ .num_resets = ARRAY_SIZE(gcc_msm8960_resets), ++}; ++ + static const struct of_device_id gcc_msm8960_match_table[] = { + { .compatible = "qcom,gcc-msm8960" }, + { } + }; + MODULE_DEVICE_TABLE(of, gcc_msm8960_match_table); + +-struct qcom_cc { +- struct qcom_reset_controller reset; +- struct clk_onecell_data data; +- struct clk *clks[]; +-}; +- + static int gcc_msm8960_probe(struct platform_device *pdev) + { +- void __iomem *base; +- struct resource *res; +- int i, ret; +- struct device *dev = &pdev->dev; + struct clk *clk; +- struct clk_onecell_data *data; +- struct clk **clks; +- struct regmap *regmap; +- size_t num_clks; +- struct qcom_reset_controller *reset; +- struct qcom_cc *cc; +- +- 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(dev, base, &gcc_msm8960_regmap_config); +- if (IS_ERR(regmap)) +- return PTR_ERR(regmap); +- +- num_clks = ARRAY_SIZE(gcc_msm8960_clks); +- cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, +- GFP_KERNEL); +- if (!cc) +- return -ENOMEM; +- +- clks = cc->clks; +- data = &cc->data; +- data->clks = clks; +- data->clk_num = num_clks; ++ struct device *dev = &pdev->dev; + + /* Temporary until RPM clocks supported */ + clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000); +@@ -2930,39 +2904,12 @@ static int gcc_msm8960_probe(struct platform_device *pdev) + if (IS_ERR(clk)) + return PTR_ERR(clk); + +- for (i = 0; i < num_clks; i++) { +- if (!gcc_msm8960_clks[i]) +- continue; +- clk = devm_clk_register_regmap(dev, gcc_msm8960_clks[i]); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- clks[i] = clk; +- } +- +- ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); +- if (ret) +- return ret; +- +- reset = &cc->reset; +- reset->rcdev.of_node = dev->of_node; +- reset->rcdev.ops = &qcom_reset_ops, +- reset->rcdev.owner = THIS_MODULE, +- reset->rcdev.nr_resets = ARRAY_SIZE(gcc_msm8960_resets), +- reset->regmap = regmap; +- reset->reset_map = gcc_msm8960_resets, +- platform_set_drvdata(pdev, &reset->rcdev); +- +- ret = reset_controller_register(&reset->rcdev); +- if (ret) +- of_clk_del_provider(dev->of_node); +- +- return ret; ++ return qcom_cc_probe(pdev, &gcc_msm8960_desc); + } + + static int gcc_msm8960_remove(struct platform_device *pdev) + { +- of_clk_del_provider(pdev->dev.of_node); +- reset_controller_unregister(platform_get_drvdata(pdev)); ++ qcom_cc_remove(pdev); + return 0; + } + +diff --git a/drivers/clk/qcom/gcc-msm8974.c b/drivers/clk/qcom/gcc-msm8974.c +index 51d457e..0d1edc1 100644 +--- a/drivers/clk/qcom/gcc-msm8974.c ++++ b/drivers/clk/qcom/gcc-msm8974.c +@@ -25,6 +25,7 @@ + #include <dt-bindings/clock/qcom,gcc-msm8974.h> + #include <dt-bindings/reset/qcom,gcc-msm8974.h> + ++#include "common.h" + #include "clk-regmap.h" + #include "clk-pll.h" + #include "clk-rcg.h" +@@ -2574,51 +2575,24 @@ static const struct regmap_config gcc_msm8974_regmap_config = { + .fast_io = true, + }; + ++static const struct qcom_cc_desc gcc_msm8974_desc = { ++ .config = &gcc_msm8974_regmap_config, ++ .clks = gcc_msm8974_clocks, ++ .num_clks = ARRAY_SIZE(gcc_msm8974_clocks), ++ .resets = gcc_msm8974_resets, ++ .num_resets = ARRAY_SIZE(gcc_msm8974_resets), ++}; ++ + static const struct of_device_id gcc_msm8974_match_table[] = { + { .compatible = "qcom,gcc-msm8974" }, + { } + }; + MODULE_DEVICE_TABLE(of, gcc_msm8974_match_table); + +-struct qcom_cc { +- struct qcom_reset_controller reset; +- struct clk_onecell_data data; +- struct clk *clks[]; +-}; +- + static int gcc_msm8974_probe(struct platform_device *pdev) + { +- void __iomem *base; +- struct resource *res; +- int i, ret; +- struct device *dev = &pdev->dev; + struct clk *clk; +- struct clk_onecell_data *data; +- struct clk **clks; +- struct regmap *regmap; +- size_t num_clks; +- struct qcom_reset_controller *reset; +- struct qcom_cc *cc; +- +- 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(dev, base, &gcc_msm8974_regmap_config); +- if (IS_ERR(regmap)) +- return PTR_ERR(regmap); +- +- num_clks = ARRAY_SIZE(gcc_msm8974_clocks); +- cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, +- GFP_KERNEL); +- if (!cc) +- return -ENOMEM; +- +- clks = cc->clks; +- data = &cc->data; +- data->clks = clks; +- data->clk_num = num_clks; ++ struct device *dev = &pdev->dev; + + /* Temporary until RPM clocks supported */ + clk = clk_register_fixed_rate(dev, "xo", NULL, CLK_IS_ROOT, 19200000); +@@ -2631,39 +2605,12 @@ static int gcc_msm8974_probe(struct platform_device *pdev) + if (IS_ERR(clk)) + return PTR_ERR(clk); + +- for (i = 0; i < num_clks; i++) { +- if (!gcc_msm8974_clocks[i]) +- continue; +- clk = devm_clk_register_regmap(dev, gcc_msm8974_clocks[i]); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- clks[i] = clk; +- } +- +- ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); +- if (ret) +- return ret; +- +- reset = &cc->reset; +- reset->rcdev.of_node = dev->of_node; +- reset->rcdev.ops = &qcom_reset_ops, +- reset->rcdev.owner = THIS_MODULE, +- reset->rcdev.nr_resets = ARRAY_SIZE(gcc_msm8974_resets), +- reset->regmap = regmap; +- reset->reset_map = gcc_msm8974_resets, +- platform_set_drvdata(pdev, &reset->rcdev); +- +- ret = reset_controller_register(&reset->rcdev); +- if (ret) +- of_clk_del_provider(dev->of_node); +- +- return ret; ++ return qcom_cc_probe(pdev, &gcc_msm8974_desc); + } + + static int gcc_msm8974_remove(struct platform_device *pdev) + { +- of_clk_del_provider(pdev->dev.of_node); +- reset_controller_unregister(platform_get_drvdata(pdev)); ++ qcom_cc_remove(pdev); + return 0; + } + +diff --git a/drivers/clk/qcom/mmcc-msm8960.c b/drivers/clk/qcom/mmcc-msm8960.c +index f9b59c7..12f3c0b 100644 +--- a/drivers/clk/qcom/mmcc-msm8960.c ++++ b/drivers/clk/qcom/mmcc-msm8960.c +@@ -26,6 +26,7 @@ + #include <dt-bindings/clock/qcom,mmcc-msm8960.h> + #include <dt-bindings/reset/qcom,mmcc-msm8960.h> + ++#include "common.h" + #include "clk-regmap.h" + #include "clk-pll.h" + #include "clk-rcg.h" +@@ -2222,85 +2223,28 @@ static const struct regmap_config mmcc_msm8960_regmap_config = { + .fast_io = true, + }; + ++static const struct qcom_cc_desc mmcc_msm8960_desc = { ++ .config = &mmcc_msm8960_regmap_config, ++ .clks = mmcc_msm8960_clks, ++ .num_clks = ARRAY_SIZE(mmcc_msm8960_clks), ++ .resets = mmcc_msm8960_resets, ++ .num_resets = ARRAY_SIZE(mmcc_msm8960_resets), ++}; ++ + static const struct of_device_id mmcc_msm8960_match_table[] = { + { .compatible = "qcom,mmcc-msm8960" }, + { } + }; + MODULE_DEVICE_TABLE(of, mmcc_msm8960_match_table); + +-struct qcom_cc { +- struct qcom_reset_controller reset; +- struct clk_onecell_data data; +- struct clk *clks[]; +-}; +- + static int mmcc_msm8960_probe(struct platform_device *pdev) + { +- void __iomem *base; +- struct resource *res; +- int i, ret; +- struct device *dev = &pdev->dev; +- struct clk *clk; +- struct clk_onecell_data *data; +- struct clk **clks; +- struct regmap *regmap; +- size_t num_clks; +- struct qcom_reset_controller *reset; +- struct qcom_cc *cc; +- +- 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(dev, base, &mmcc_msm8960_regmap_config); +- if (IS_ERR(regmap)) +- return PTR_ERR(regmap); +- +- num_clks = ARRAY_SIZE(mmcc_msm8960_clks); +- cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, +- GFP_KERNEL); +- if (!cc) +- return -ENOMEM; +- +- clks = cc->clks; +- data = &cc->data; +- data->clks = clks; +- data->clk_num = num_clks; +- +- for (i = 0; i < num_clks; i++) { +- if (!mmcc_msm8960_clks[i]) +- continue; +- clk = devm_clk_register_regmap(dev, mmcc_msm8960_clks[i]); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- clks[i] = clk; +- } +- +- ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); +- if (ret) +- return ret; +- +- reset = &cc->reset; +- reset->rcdev.of_node = dev->of_node; +- reset->rcdev.ops = &qcom_reset_ops, +- reset->rcdev.owner = THIS_MODULE, +- reset->rcdev.nr_resets = ARRAY_SIZE(mmcc_msm8960_resets), +- reset->regmap = regmap; +- reset->reset_map = mmcc_msm8960_resets, +- platform_set_drvdata(pdev, &reset->rcdev); +- +- ret = reset_controller_register(&reset->rcdev); +- if (ret) +- of_clk_del_provider(dev->of_node); +- +- return ret; ++ return qcom_cc_probe(pdev, &mmcc_msm8960_desc); + } + + static int mmcc_msm8960_remove(struct platform_device *pdev) + { +- of_clk_del_provider(pdev->dev.of_node); +- reset_controller_unregister(platform_get_drvdata(pdev)); ++ qcom_cc_remove(pdev); + return 0; + } + +diff --git a/drivers/clk/qcom/mmcc-msm8974.c b/drivers/clk/qcom/mmcc-msm8974.c +index c957745..60b7c24 100644 +--- a/drivers/clk/qcom/mmcc-msm8974.c ++++ b/drivers/clk/qcom/mmcc-msm8974.c +@@ -25,6 +25,7 @@ + #include <dt-bindings/clock/qcom,mmcc-msm8974.h> + #include <dt-bindings/reset/qcom,mmcc-msm8974.h> + ++#include "common.h" + #include "clk-regmap.h" + #include "clk-pll.h" + #include "clk-rcg.h" +@@ -2524,88 +2525,39 @@ static const struct regmap_config mmcc_msm8974_regmap_config = { + .fast_io = true, + }; + ++static const struct qcom_cc_desc mmcc_msm8974_desc = { ++ .config = &mmcc_msm8974_regmap_config, ++ .clks = mmcc_msm8974_clocks, ++ .num_clks = ARRAY_SIZE(mmcc_msm8974_clocks), ++ .resets = mmcc_msm8974_resets, ++ .num_resets = ARRAY_SIZE(mmcc_msm8974_resets), ++}; ++ + static const struct of_device_id mmcc_msm8974_match_table[] = { + { .compatible = "qcom,mmcc-msm8974" }, + { } + }; + MODULE_DEVICE_TABLE(of, mmcc_msm8974_match_table); + +-struct qcom_cc { +- struct qcom_reset_controller reset; +- struct clk_onecell_data data; +- struct clk *clks[]; +-}; +- + static int mmcc_msm8974_probe(struct platform_device *pdev) + { +- void __iomem *base; +- struct resource *res; +- int i, ret; +- struct device *dev = &pdev->dev; +- struct clk *clk; +- struct clk_onecell_data *data; +- struct clk **clks; ++ int ret; + struct regmap *regmap; +- size_t num_clks; +- struct qcom_reset_controller *reset; +- struct qcom_cc *cc; +- +- 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(dev, base, &mmcc_msm8974_regmap_config); +- if (IS_ERR(regmap)) +- return PTR_ERR(regmap); +- +- num_clks = ARRAY_SIZE(mmcc_msm8974_clocks); +- cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, +- GFP_KERNEL); +- if (!cc) +- return -ENOMEM; +- +- clks = cc->clks; +- data = &cc->data; +- data->clks = clks; +- data->clk_num = num_clks; +- +- clk_pll_configure_sr_hpm_lp(&mmpll1, regmap, &mmpll1_config, true); +- clk_pll_configure_sr_hpm_lp(&mmpll3, regmap, &mmpll3_config, false); + +- for (i = 0; i < num_clks; i++) { +- if (!mmcc_msm8974_clocks[i]) +- continue; +- clk = devm_clk_register_regmap(dev, mmcc_msm8974_clocks[i]); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- clks[i] = clk; +- } +- +- ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); ++ ret = qcom_cc_probe(pdev, &mmcc_msm8974_desc); + if (ret) + return ret; + +- reset = &cc->reset; +- reset->rcdev.of_node = dev->of_node; +- reset->rcdev.ops = &qcom_reset_ops, +- reset->rcdev.owner = THIS_MODULE, +- reset->rcdev.nr_resets = ARRAY_SIZE(mmcc_msm8974_resets), +- reset->regmap = regmap; +- reset->reset_map = mmcc_msm8974_resets, +- platform_set_drvdata(pdev, &reset->rcdev); +- +- ret = reset_controller_register(&reset->rcdev); +- if (ret) +- of_clk_del_provider(dev->of_node); ++ regmap = dev_get_regmap(&pdev->dev, NULL); ++ clk_pll_configure_sr_hpm_lp(&mmpll1, regmap, &mmpll1_config, true); ++ clk_pll_configure_sr_hpm_lp(&mmpll3, regmap, &mmpll3_config, false); + +- return ret; ++ return 0; + } + + static int mmcc_msm8974_remove(struct platform_device *pdev) + { +- of_clk_del_provider(pdev->dev.of_node); +- reset_controller_unregister(platform_get_drvdata(pdev)); ++ qcom_cc_remove(pdev); + return 0; + } + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0079-clk-qcom-Add-basic-support-for-APQ8064-global-clock-.patch b/target/linux/ipq806x/patches/0079-clk-qcom-Add-basic-support-for-APQ8064-global-clock-.patch new file mode 100644 index 0000000000..d83d5d9696 --- /dev/null +++ b/target/linux/ipq806x/patches/0079-clk-qcom-Add-basic-support-for-APQ8064-global-clock-.patch @@ -0,0 +1,123 @@ +From 0f171b8a6e1723f0ce6f98f8b3fba7d2583088c1 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Fri, 4 Apr 2014 11:31:29 -0500 +Subject: [PATCH 079/182] clk: qcom: Add basic support for APQ8064 global + clock controller clocks + +The APQ8064 and MSM8960 share a significant amount of clock data and +code between the two SoCs. Rather than duplicating the data we just add +support for a unqiue APQ8064 clock table into the MSM8960 code. + +For now add just enough clocks to get a basic serial port going on an +APQ8064 device. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +Reviewed-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +[mturquette@linaro.org: trivial conflict due to missing ipq8064 support] +--- + .../devicetree/bindings/clock/qcom,gcc.txt | 1 + + drivers/clk/qcom/Kconfig | 4 +-- + drivers/clk/qcom/gcc-msm8960.c | 30 ++++++++++++++++++-- + 3 files changed, 30 insertions(+), 5 deletions(-) + +diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt +index 767401f..7b7104e 100644 +--- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt ++++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt +@@ -4,6 +4,7 @@ Qualcomm Global Clock & Reset Controller Binding + Required properties : + - compatible : shall contain only one of the following: + ++ "qcom,gcc-apq8064" + "qcom,gcc-msm8660" + "qcom,gcc-msm8960" + "qcom,gcc-msm8974" +diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig +index 995bcfa..7f696b7 100644 +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -13,10 +13,10 @@ config MSM_GCC_8660 + i2c, USB, SD/eMMC, etc. + + config MSM_GCC_8960 +- tristate "MSM8960 Global Clock Controller" ++ tristate "APQ8064/MSM8960 Global Clock Controller" + depends on COMMON_CLK_QCOM + help +- Support for the global clock controller on msm8960 devices. ++ Support for the global clock controller on apq8064/msm8960 devices. + Say Y if you want to use peripheral devices such as UART, SPI, + i2c, USB, SD/eMMC, SATA, PCIe, etc. + +diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c +index 633b019..8e2b6dd 100644 +--- a/drivers/clk/qcom/gcc-msm8960.c ++++ b/drivers/clk/qcom/gcc-msm8960.c +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2013, The Linux Foundation. All rights reserved. ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and +@@ -2868,6 +2868,16 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = { + [RIVA_RESET] = { 0x35e0 }, + }; + ++static struct clk_regmap *gcc_apq8064_clks[] = { ++ [PLL8] = &pll8.clkr, ++ [PLL8_VOTE] = &pll8_vote, ++ [GSBI7_UART_SRC] = &gsbi7_uart_src.clkr, ++ [GSBI7_UART_CLK] = &gsbi7_uart_clk.clkr, ++ [GSBI7_QUP_SRC] = &gsbi7_qup_src.clkr, ++ [GSBI7_QUP_CLK] = &gsbi7_qup_clk.clkr, ++ [GSBI7_H_CLK] = &gsbi7_h_clk.clkr, ++}; ++ + static const struct regmap_config gcc_msm8960_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, +@@ -2884,8 +2894,17 @@ static const struct qcom_cc_desc gcc_msm8960_desc = { + .num_resets = ARRAY_SIZE(gcc_msm8960_resets), + }; + ++static const struct qcom_cc_desc gcc_apq8064_desc = { ++ .config = &gcc_msm8960_regmap_config, ++ .clks = gcc_apq8064_clks, ++ .num_clks = ARRAY_SIZE(gcc_apq8064_clks), ++ .resets = gcc_msm8960_resets, ++ .num_resets = ARRAY_SIZE(gcc_msm8960_resets), ++}; ++ + static const struct of_device_id gcc_msm8960_match_table[] = { +- { .compatible = "qcom,gcc-msm8960" }, ++ { .compatible = "qcom,gcc-msm8960", .data = &gcc_msm8960_desc }, ++ { .compatible = "qcom,gcc-apq8064", .data = &gcc_apq8064_desc }, + { } + }; + MODULE_DEVICE_TABLE(of, gcc_msm8960_match_table); +@@ -2894,6 +2913,11 @@ static int gcc_msm8960_probe(struct platform_device *pdev) + { + struct clk *clk; + struct device *dev = &pdev->dev; ++ const struct of_device_id *match; ++ ++ match = of_match_device(gcc_msm8960_match_table, &pdev->dev); ++ if (!match) ++ return -EINVAL; + + /* Temporary until RPM clocks supported */ + clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000); +@@ -2904,7 +2928,7 @@ static int gcc_msm8960_probe(struct platform_device *pdev) + if (IS_ERR(clk)) + return PTR_ERR(clk); + +- return qcom_cc_probe(pdev, &gcc_msm8960_desc); ++ return qcom_cc_probe(pdev, match->data); + } + + static int gcc_msm8960_remove(struct platform_device *pdev) +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0080-clk-qcom-Various-fixes-for-MSM8960-s-global-clock-co.patch b/target/linux/ipq806x/patches/0080-clk-qcom-Various-fixes-for-MSM8960-s-global-clock-co.patch new file mode 100644 index 0000000000..3cc5bf828a --- /dev/null +++ b/target/linux/ipq806x/patches/0080-clk-qcom-Various-fixes-for-MSM8960-s-global-clock-co.patch @@ -0,0 +1,101 @@ +From 7456451e9df88d4c33479e3d4ea124d8a91ceb57 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Fri, 4 Apr 2014 11:32:56 -0500 +Subject: [PATCH 080/182] clk: qcom: Various fixes for MSM8960's global clock + controller + +* Remove CE2_SLEEP_CLK, doesn't exist on 8960 family SoCs +* Fix incorrect offset for PMIC_SSBI2_RESET +* Fix typo: + SIC_TIC -> SPS_TIC_H + SFAB_ADM0_M2_A_CLK -> SFAB_ADM0_M2_H_CLK +* Fix naming convention: + SFAB_CFPB_S_HCLK -> SFAB_CFPB_S_H_CLK + SATA_SRC_CLK -> SATA_CLK_SRC + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +Reviewed-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +--- + drivers/clk/qcom/gcc-msm8960.c | 4 ++-- + include/dt-bindings/clock/qcom,gcc-msm8960.h | 7 +++---- + include/dt-bindings/reset/qcom,gcc-msm8960.h | 2 +- + 3 files changed, 6 insertions(+), 7 deletions(-) + +diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c +index 8e2b6dd..f4ffd91 100644 +--- a/drivers/clk/qcom/gcc-msm8960.c ++++ b/drivers/clk/qcom/gcc-msm8960.c +@@ -2810,7 +2810,7 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = { + [PPSS_PROC_RESET] = { 0x2594, 1 }, + [PPSS_RESET] = { 0x2594}, + [DMA_BAM_RESET] = { 0x25c0, 7 }, +- [SIC_TIC_RESET] = { 0x2600, 7 }, ++ [SPS_TIC_H_RESET] = { 0x2600, 7 }, + [SLIMBUS_H_RESET] = { 0x2620, 7 }, + [SFAB_CFPB_M_RESET] = { 0x2680, 7 }, + [SFAB_CFPB_S_RESET] = { 0x26c0, 7 }, +@@ -2823,7 +2823,7 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = { + [SFAB_SFPB_M_RESET] = { 0x2780, 7 }, + [SFAB_SFPB_S_RESET] = { 0x27a0, 7 }, + [RPM_PROC_RESET] = { 0x27c0, 7 }, +- [PMIC_SSBI2_RESET] = { 0x270c, 12 }, ++ [PMIC_SSBI2_RESET] = { 0x280c, 12 }, + [SDC1_RESET] = { 0x2830 }, + [SDC2_RESET] = { 0x2850 }, + [SDC3_RESET] = { 0x2870 }, +diff --git a/include/dt-bindings/clock/qcom,gcc-msm8960.h b/include/dt-bindings/clock/qcom,gcc-msm8960.h +index 03bbf49..f9f5471 100644 +--- a/include/dt-bindings/clock/qcom,gcc-msm8960.h ++++ b/include/dt-bindings/clock/qcom,gcc-msm8960.h +@@ -51,7 +51,7 @@ + #define QDSS_TSCTR_CLK 34 + #define SFAB_ADM0_M0_A_CLK 35 + #define SFAB_ADM0_M1_A_CLK 36 +-#define SFAB_ADM0_M2_A_CLK 37 ++#define SFAB_ADM0_M2_H_CLK 37 + #define ADM0_CLK 38 + #define ADM0_PBUS_CLK 39 + #define MSS_XPU_CLK 40 +@@ -99,7 +99,7 @@ + #define CFPB2_H_CLK 82 + #define SFAB_CFPB_M_H_CLK 83 + #define CFPB_MASTER_H_CLK 84 +-#define SFAB_CFPB_S_HCLK 85 ++#define SFAB_CFPB_S_H_CLK 85 + #define CFPB_SPLITTER_H_CLK 86 + #define TSIF_H_CLK 87 + #define TSIF_INACTIVITY_TIMERS_CLK 88 +@@ -110,7 +110,6 @@ + #define CE1_SLEEP_CLK 93 + #define CE2_H_CLK 94 + #define CE2_CORE_CLK 95 +-#define CE2_SLEEP_CLK 96 + #define SFPB_H_CLK_SRC 97 + #define SFPB_H_CLK 98 + #define SFAB_SFPB_M_H_CLK 99 +@@ -252,7 +251,7 @@ + #define MSS_S_H_CLK 235 + #define MSS_CXO_SRC_CLK 236 + #define SATA_H_CLK 237 +-#define SATA_SRC_CLK 238 ++#define SATA_CLK_SRC 238 + #define SATA_RXOOB_CLK 239 + #define SATA_PMALIVE_CLK 240 + #define SATA_PHY_REF_CLK 241 +diff --git a/include/dt-bindings/reset/qcom,gcc-msm8960.h b/include/dt-bindings/reset/qcom,gcc-msm8960.h +index a840e68..07edd0e 100644 +--- a/include/dt-bindings/reset/qcom,gcc-msm8960.h ++++ b/include/dt-bindings/reset/qcom,gcc-msm8960.h +@@ -58,7 +58,7 @@ + #define PPSS_PROC_RESET 41 + #define PPSS_RESET 42 + #define DMA_BAM_RESET 43 +-#define SIC_TIC_RESET 44 ++#define SPS_TIC_H_RESET 44 + #define SLIMBUS_H_RESET 45 + #define SFAB_CFPB_M_RESET 46 + #define SFAB_CFPB_S_RESET 47 +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0081-ARM-config-Add-qcom_defconfig.patch b/target/linux/ipq806x/patches/0081-ARM-config-Add-qcom_defconfig.patch new file mode 100644 index 0000000000..f022463d28 --- /dev/null +++ b/target/linux/ipq806x/patches/0081-ARM-config-Add-qcom_defconfig.patch @@ -0,0 +1,190 @@ +From e1f51c2d41c8936b8383cf4ac53c1ff05b0e2e2f Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 27 Feb 2014 12:54:24 -0800 +Subject: [PATCH 081/182] ARM: config: Add qcom_defconfig + +Add a defconfig for mach-qcom platforms (copied from msm_defconfig). +Although these platforms are part of the multi-platform kernel, it's +useful to have a stripped down version of the defconfig that just +selects the DT based Qualcomm platforms and drivers. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/configs/qcom_defconfig | 164 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 164 insertions(+) + create mode 100644 arch/arm/configs/qcom_defconfig + +diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig +new file mode 100644 +index 0000000..bfed753 +--- /dev/null ++++ b/arch/arm/configs/qcom_defconfig +@@ -0,0 +1,164 @@ ++CONFIG_SYSVIPC=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_IKCONFIG=y ++CONFIG_IKCONFIG_PROC=y ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_SYSCTL_SYSCALL=y ++CONFIG_KALLSYMS_ALL=y ++CONFIG_EMBEDDED=y ++# CONFIG_SLUB_DEBUG is not set ++# CONFIG_COMPAT_BRK is not set ++CONFIG_PROFILING=y ++CONFIG_OPROFILE=y ++CONFIG_KPROBES=y ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_MODULE_FORCE_UNLOAD=y ++CONFIG_MODVERSIONS=y ++CONFIG_PARTITION_ADVANCED=y ++CONFIG_ARCH_QCOM=y ++CONFIG_ARCH_MSM8X60=y ++CONFIG_ARCH_MSM8960=y ++CONFIG_ARCH_MSM8974=y ++CONFIG_SMP=y ++CONFIG_PREEMPT=y ++CONFIG_AEABI=y ++CONFIG_HIGHMEM=y ++CONFIG_HIGHPTE=y ++CONFIG_CLEANCACHE=y ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_VFP=y ++CONFIG_NEON=y ++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_INET=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_IP_MULTIPLE_TABLES=y ++CONFIG_IP_ROUTE_VERBOSE=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++# CONFIG_INET_LRO is not set ++# CONFIG_IPV6 is not set ++CONFIG_CFG80211=y ++CONFIG_RFKILL=y ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_MTD=y ++CONFIG_MTD_BLOCK=y ++CONFIG_MTD_M25P80=y ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_RAM=y ++CONFIG_SCSI=y ++CONFIG_SCSI_TGT=y ++CONFIG_BLK_DEV_SD=y ++CONFIG_CHR_DEV_SG=y ++CONFIG_CHR_DEV_SCH=y ++CONFIG_SCSI_MULTI_LUN=y ++CONFIG_SCSI_CONSTANTS=y ++CONFIG_SCSI_LOGGING=y ++CONFIG_SCSI_SCAN_ASYNC=y ++CONFIG_NETDEVICES=y ++CONFIG_DUMMY=y ++CONFIG_MDIO_BITBANG=y ++CONFIG_MDIO_GPIO=y ++CONFIG_SLIP=y ++CONFIG_SLIP_COMPRESSED=y ++CONFIG_SLIP_MODE_SLIP6=y ++CONFIG_USB_USBNET=y ++# CONFIG_USB_NET_AX8817X is not set ++# CONFIG_USB_NET_ZAURUS is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_KEYBOARD_ATKBD is not set ++# CONFIG_MOUSE_PS2 is not set ++CONFIG_INPUT_JOYSTICK=y ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_UINPUT=y ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_LEGACY_PTYS is not set ++CONFIG_SERIAL_MSM=y ++CONFIG_SERIAL_MSM_CONSOLE=y ++CONFIG_HW_RANDOM=y ++CONFIG_HW_RANDOM_MSM=y ++CONFIG_I2C=y ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_QUP=y ++CONFIG_SPI=y ++CONFIG_SPI_QUP=y ++CONFIG_SPMI=y ++CONFIG_PINCTRL_APQ8064=y ++CONFIG_PINCTRL_IPQ8064=y ++CONFIG_PINCTRL_MSM8X74=y ++CONFIG_DEBUG_GPIO=y ++CONFIG_GPIO_SYSFS=y ++CONFIG_POWER_SUPPLY=y ++CONFIG_POWER_RESET=y ++CONFIG_POWER_RESET_MSM=y ++CONFIG_THERMAL=y ++CONFIG_REGULATOR=y ++CONFIG_MEDIA_SUPPORT=y ++CONFIG_FB=y ++CONFIG_SOUND=y ++CONFIG_SND=y ++CONFIG_SND_DYNAMIC_MINORS=y ++# CONFIG_SND_ARM is not set ++# CONFIG_SND_SPI is not set ++# CONFIG_SND_USB is not set ++CONFIG_SND_SOC=y ++CONFIG_HID_BATTERY_STRENGTH=y ++CONFIG_USB=y ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++CONFIG_USB_MON=y ++CONFIG_USB_EHCI_HCD=y ++CONFIG_USB_ACM=y ++CONFIG_USB_SERIAL=y ++CONFIG_USB_GADGET=y ++CONFIG_USB_GADGET_DEBUG_FILES=y ++CONFIG_USB_GADGET_VBUS_DRAW=500 ++CONFIG_MMC=y ++CONFIG_MMC_BLOCK_MINORS=16 ++CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_PLTFM=y ++CONFIG_MMC_SDHCI_MSM=y ++CONFIG_RTC_CLASS=y ++CONFIG_DMADEVICES=y ++CONFIG_QCOM_BAM_DMA=y ++CONFIG_STAGING=y ++CONFIG_COMMON_CLK_QCOM=y ++CONFIG_MSM_GCC_8660=y ++CONFIG_MSM_MMCC_8960=y ++CONFIG_MSM_MMCC_8974=y ++CONFIG_MSM_IOMMU=y ++CONFIG_GENERIC_PHY=y ++CONFIG_EXT2_FS=y ++CONFIG_EXT2_FS_XATTR=y ++CONFIG_EXT3_FS=y ++# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set ++CONFIG_EXT4_FS=y ++CONFIG_FUSE_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_TMPFS=y ++CONFIG_JFFS2_FS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_CIFS=y ++CONFIG_NLS_CODEPAGE_437=y ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++CONFIG_NLS_UTF8=y ++CONFIG_PRINTK_TIME=y ++CONFIG_DYNAMIC_DEBUG=y ++CONFIG_DEBUG_INFO=y ++CONFIG_MAGIC_SYSRQ=y ++CONFIG_LOCKUP_DETECTOR=y ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_SCHED_DEBUG is not set ++CONFIG_TIMER_STATS=y +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0082-ARM-qcom-Enable-GSBI-driver-in-defconfig.patch b/target/linux/ipq806x/patches/0082-ARM-qcom-Enable-GSBI-driver-in-defconfig.patch new file mode 100644 index 0000000000..7bcd2463da --- /dev/null +++ b/target/linux/ipq806x/patches/0082-ARM-qcom-Enable-GSBI-driver-in-defconfig.patch @@ -0,0 +1,25 @@ +From d247fb8a019e4cc5a607c7fff9922e8e98c2c660 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 29 May 2014 11:25:24 -0500 +Subject: [PATCH 082/182] ARM: qcom: Enable GSBI driver in defconfig + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/configs/qcom_defconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig +index bfed753..42ebd72 100644 +--- a/arch/arm/configs/qcom_defconfig ++++ b/arch/arm/configs/qcom_defconfig +@@ -131,6 +131,7 @@ CONFIG_RTC_CLASS=y + CONFIG_DMADEVICES=y + CONFIG_QCOM_BAM_DMA=y + CONFIG_STAGING=y ++CONFIG_QCOM_GSBI=y + CONFIG_COMMON_CLK_QCOM=y + CONFIG_MSM_GCC_8660=y + CONFIG_MSM_MMCC_8960=y +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0083-soc-Introduce-drivers-soc-place-holder-for-SOC-speci.patch b/target/linux/ipq806x/patches/0083-soc-Introduce-drivers-soc-place-holder-for-SOC-speci.patch new file mode 100644 index 0000000000..837ec9d0c5 --- /dev/null +++ b/target/linux/ipq806x/patches/0083-soc-Introduce-drivers-soc-place-holder-for-SOC-speci.patch @@ -0,0 +1,68 @@ +From 2dac9498966c79a6abb09764419ffd42a44c78cf Mon Sep 17 00:00:00 2001 +From: Santosh Shilimkar <santosh.shilimkar@ti.com> +Date: Wed, 23 Apr 2014 19:46:17 -0400 +Subject: [PATCH 083/182] soc: Introduce drivers/soc place-holder for SOC + specific drivers + +Based on earlier thread "https://lkml.org/lkml/2013/10/7/662" and +discussion at Kernel Summit'2013, it was agreed to create +'driver/soc' for drivers which are quite SOC specific. + +Further discussion on the subject is in response to +the earlier version of the patch is here: + http://lwn.net/Articles/588942/ + +Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Cc: Kumar Gala <galak@codeaurora.org> +Cc: Paul Walmsley <paul@pwsan.com> +Cc: Olof Johansson <olof@lixom.net> +Cc: Arnd Bergmann <arnd@arndb.de> +Signed-off-by: Sandeep Nair <sandeep_n@ti.com> +Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/Kconfig | 2 ++ + drivers/Makefile | 3 +++ + drivers/soc/Kconfig | 3 +++ + 3 files changed, 8 insertions(+) + create mode 100644 drivers/soc/Kconfig + +diff --git a/drivers/Kconfig b/drivers/Kconfig +index e0a4ae6..a299cbd 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -132,6 +132,8 @@ source "drivers/staging/Kconfig" + + source "drivers/platform/Kconfig" + ++source "drivers/soc/Kconfig" ++ + source "drivers/clk/Kconfig" + + source "drivers/hwspinlock/Kconfig" +diff --git a/drivers/Makefile b/drivers/Makefile +index 3d6de8b..4c2bdc1 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -33,6 +33,9 @@ obj-y += amba/ + # really early. + obj-$(CONFIG_DMADEVICES) += dma/ + ++# SOC specific infrastructure drivers. ++obj-y += soc/ ++ + obj-$(CONFIG_VIRTIO) += virtio/ + obj-$(CONFIG_XEN) += xen/ + +diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig +new file mode 100644 +index 0000000..339baa8 +--- /dev/null ++++ b/drivers/soc/Kconfig +@@ -0,0 +1,3 @@ ++menu "SOC (System On Chip) specific Drivers" ++ ++endmenu +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0084-soc-qcom-Add-GSBI-driver.patch b/target/linux/ipq806x/patches/0084-soc-qcom-Add-GSBI-driver.patch new file mode 100644 index 0000000000..f52255826c --- /dev/null +++ b/target/linux/ipq806x/patches/0084-soc-qcom-Add-GSBI-driver.patch @@ -0,0 +1,162 @@ +From a2f0fc20ea49e5dbbdbb21444683ea760fbdd38f Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 24 Apr 2014 11:31:21 -0500 +Subject: [PATCH 084/182] soc: qcom: Add GSBI driver + +The GSBI (General Serial Bus Interface) driver controls the overarching +configuration of the shared serial bus infrastructure on APQ8064, IPQ8064, and +earlier QCOM processors. The GSBI supports UART, I2C, SPI, and UIM +functionality in various combinations. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/soc/Kconfig | 2 + + drivers/soc/Makefile | 5 +++ + drivers/soc/qcom/Kconfig | 11 ++++++ + drivers/soc/qcom/Makefile | 1 + + drivers/soc/qcom/qcom_gsbi.c | 84 ++++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 103 insertions(+) + create mode 100644 drivers/soc/Makefile + create mode 100644 drivers/soc/qcom/Kconfig + create mode 100644 drivers/soc/qcom/Makefile + create mode 100644 drivers/soc/qcom/qcom_gsbi.c + +diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig +index 339baa8..c854385 100644 +--- a/drivers/soc/Kconfig ++++ b/drivers/soc/Kconfig +@@ -1,3 +1,5 @@ + menu "SOC (System On Chip) specific Drivers" + ++source "drivers/soc/qcom/Kconfig" ++ + endmenu +diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile +new file mode 100644 +index 0000000..0f7c447 +--- /dev/null ++++ b/drivers/soc/Makefile +@@ -0,0 +1,5 @@ ++# ++# Makefile for the Linux Kernel SOC specific device drivers. ++# ++ ++obj-$(CONFIG_ARCH_QCOM) += qcom/ +diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig +new file mode 100644 +index 0000000..7bd2c94 +--- /dev/null ++++ b/drivers/soc/qcom/Kconfig +@@ -0,0 +1,11 @@ ++# ++# QCOM Soc drivers ++# ++config QCOM_GSBI ++ tristate "QCOM General Serial Bus Interface" ++ depends on ARCH_QCOM ++ help ++ Say y here to enable GSBI support. The GSBI provides control ++ functions for connecting the underlying serial UART, SPI, and I2C ++ devices to the output pins. ++ +diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile +new file mode 100644 +index 0000000..4389012 +--- /dev/null ++++ b/drivers/soc/qcom/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o +diff --git a/drivers/soc/qcom/qcom_gsbi.c b/drivers/soc/qcom/qcom_gsbi.c +new file mode 100644 +index 0000000..061dd06 +--- /dev/null ++++ b/drivers/soc/qcom/qcom_gsbi.c +@@ -0,0 +1,84 @@ ++/* ++ * Copyright (c) 2014, The Linux foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License rev 2 and ++ * only rev 2 as published by the free Software foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or fITNESS fOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/err.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_platform.h> ++#include <linux/platform_device.h> ++ ++#define GSBI_CTRL_REG 0x0000 ++#define GSBI_PROTOCOL_SHIFT 4 ++ ++static int gsbi_probe(struct platform_device *pdev) ++{ ++ struct device_node *node = pdev->dev.of_node; ++ struct resource *res; ++ void __iomem *base; ++ struct clk *hclk; ++ u32 mode, crci = 0; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ if (of_property_read_u32(node, "qcom,mode", &mode)) { ++ dev_err(&pdev->dev, "missing mode configuration\n"); ++ return -EINVAL; ++ } ++ ++ /* not required, so default to 0 if not present */ ++ of_property_read_u32(node, "qcom,crci", &crci); ++ ++ dev_info(&pdev->dev, "GSBI port protocol: %d crci: %d\n", mode, crci); ++ ++ hclk = devm_clk_get(&pdev->dev, "iface"); ++ if (IS_ERR(hclk)) ++ return PTR_ERR(hclk); ++ ++ clk_prepare_enable(hclk); ++ ++ writel_relaxed((mode << GSBI_PROTOCOL_SHIFT) | crci, ++ base + GSBI_CTRL_REG); ++ ++ /* make sure the gsbi control write is not reordered */ ++ wmb(); ++ ++ clk_disable_unprepare(hclk); ++ ++ return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); ++} ++ ++static const struct of_device_id gsbi_dt_match[] = { ++ { .compatible = "qcom,gsbi-v1.0.0", }, ++}; ++ ++MODULE_DEVICE_TABLE(of, gsbi_dt_match); ++ ++static struct platform_driver gsbi_driver = { ++ .driver = { ++ .name = "gsbi", ++ .owner = THIS_MODULE, ++ .of_match_table = gsbi_dt_match, ++ }, ++ .probe = gsbi_probe, ++}; ++ ++module_platform_driver(gsbi_driver); ++ ++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); ++MODULE_DESCRIPTION("QCOM GSBI driver"); ++MODULE_LICENSE("GPL v2"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0085-soc-qcom-fix-of_device_id-table.patch b/target/linux/ipq806x/patches/0085-soc-qcom-fix-of_device_id-table.patch new file mode 100644 index 0000000000..c619ecef9d --- /dev/null +++ b/target/linux/ipq806x/patches/0085-soc-qcom-fix-of_device_id-table.patch @@ -0,0 +1,28 @@ +From 8efaabc573e58f4b891340f4aedb34f9d0656067 Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann <arnd@arndb.de> +Date: Mon, 26 May 2014 18:07:05 +0200 +Subject: [PATCH 085/182] soc: qcom: fix of_device_id table + +The match tables must be zero-terminated, and Kbuild now helpfully +fails to link the kernel if that isn't the case. + +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +--- + drivers/soc/qcom/qcom_gsbi.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/soc/qcom/qcom_gsbi.c b/drivers/soc/qcom/qcom_gsbi.c +index 061dd06..447458e 100644 +--- a/drivers/soc/qcom/qcom_gsbi.c ++++ b/drivers/soc/qcom/qcom_gsbi.c +@@ -64,6 +64,7 @@ static int gsbi_probe(struct platform_device *pdev) + + static const struct of_device_id gsbi_dt_match[] = { + { .compatible = "qcom,gsbi-v1.0.0", }, ++ { }, + }; + + MODULE_DEVICE_TABLE(of, gsbi_dt_match); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0086-msm_serial-Add-support-for-poll_-get-put-_char.patch b/target/linux/ipq806x/patches/0086-msm_serial-Add-support-for-poll_-get-put-_char.patch new file mode 100644 index 0000000000..afc293bfbd --- /dev/null +++ b/target/linux/ipq806x/patches/0086-msm_serial-Add-support-for-poll_-get-put-_char.patch @@ -0,0 +1,246 @@ +From 48ab619dd6e308c57dac3e5d022a3099806bf79e Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Tue, 14 Jan 2014 12:34:55 -0800 +Subject: [PATCH 086/182] msm_serial: Add support for poll_{get,put}_char() + +Implement the polling functionality for the MSM serial driver. +This allows us to use KGDB on this hardware. + +Cc: David Brown <davidb@codeaurora.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/tty/serial/msm_serial.c | 140 ++++++++++++++++++++++++++++++++++++++- + drivers/tty/serial/msm_serial.h | 9 +++ + 2 files changed, 146 insertions(+), 3 deletions(-) + +diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c +index b5d779c..053b98e 100644 +--- a/drivers/tty/serial/msm_serial.c ++++ b/drivers/tty/serial/msm_serial.c +@@ -39,6 +39,13 @@ + + #include "msm_serial.h" + ++enum { ++ UARTDM_1P1 = 1, ++ UARTDM_1P2, ++ UARTDM_1P3, ++ UARTDM_1P4, ++}; ++ + struct msm_port { + struct uart_port uart; + char name[16]; +@@ -309,6 +316,8 @@ static unsigned int msm_get_mctrl(struct uart_port *port) + + static void msm_reset(struct uart_port *port) + { ++ struct msm_port *msm_port = UART_TO_MSM(port); ++ + /* reset everything */ + msm_write(port, UART_CR_CMD_RESET_RX, UART_CR); + msm_write(port, UART_CR_CMD_RESET_TX, UART_CR); +@@ -316,6 +325,10 @@ static void msm_reset(struct uart_port *port) + msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR); + msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR); + msm_write(port, UART_CR_CMD_SET_RFR, UART_CR); ++ ++ /* Disable DM modes */ ++ if (msm_port->is_uartdm) ++ msm_write(port, 0, UARTDM_DMEN); + } + + static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl) +@@ -711,6 +724,117 @@ static void msm_power(struct uart_port *port, unsigned int state, + } + } + ++#ifdef CONFIG_CONSOLE_POLL ++static int msm_poll_init(struct uart_port *port) ++{ ++ struct msm_port *msm_port = UART_TO_MSM(port); ++ ++ /* Enable single character mode on RX FIFO */ ++ if (msm_port->is_uartdm >= UARTDM_1P4) ++ msm_write(port, UARTDM_DMEN_RX_SC_ENABLE, UARTDM_DMEN); ++ ++ return 0; ++} ++ ++static int msm_poll_get_char_single(struct uart_port *port) ++{ ++ struct msm_port *msm_port = UART_TO_MSM(port); ++ unsigned int rf_reg = msm_port->is_uartdm ? UARTDM_RF : UART_RF; ++ ++ if (!(msm_read(port, UART_SR) & UART_SR_RX_READY)) ++ return NO_POLL_CHAR; ++ else ++ return msm_read(port, rf_reg) & 0xff; ++} ++ ++static int msm_poll_get_char_dm_1p3(struct uart_port *port) ++{ ++ int c; ++ static u32 slop; ++ static int count; ++ unsigned char *sp = (unsigned char *)&slop; ++ ++ /* Check if a previous read had more than one char */ ++ if (count) { ++ c = sp[sizeof(slop) - count]; ++ count--; ++ /* Or if FIFO is empty */ ++ } else if (!(msm_read(port, UART_SR) & UART_SR_RX_READY)) { ++ /* ++ * If RX packing buffer has less than a word, force stale to ++ * push contents into RX FIFO ++ */ ++ count = msm_read(port, UARTDM_RXFS); ++ count = (count >> UARTDM_RXFS_BUF_SHIFT) & UARTDM_RXFS_BUF_MASK; ++ if (count) { ++ msm_write(port, UART_CR_CMD_FORCE_STALE, UART_CR); ++ slop = msm_read(port, UARTDM_RF); ++ c = sp[0]; ++ count--; ++ } else { ++ c = NO_POLL_CHAR; ++ } ++ /* FIFO has a word */ ++ } else { ++ slop = msm_read(port, UARTDM_RF); ++ c = sp[0]; ++ count = sizeof(slop) - 1; ++ } ++ ++ return c; ++} ++ ++static int msm_poll_get_char(struct uart_port *port) ++{ ++ u32 imr; ++ int c; ++ struct msm_port *msm_port = UART_TO_MSM(port); ++ ++ /* Disable all interrupts */ ++ imr = msm_read(port, UART_IMR); ++ msm_write(port, 0, UART_IMR); ++ ++ if (msm_port->is_uartdm == UARTDM_1P3) ++ c = msm_poll_get_char_dm_1p3(port); ++ else ++ c = msm_poll_get_char_single(port); ++ ++ /* Enable interrupts */ ++ msm_write(port, imr, UART_IMR); ++ ++ return c; ++} ++ ++static void msm_poll_put_char(struct uart_port *port, unsigned char c) ++{ ++ u32 imr; ++ struct msm_port *msm_port = UART_TO_MSM(port); ++ ++ /* Disable all interrupts */ ++ imr = msm_read(port, UART_IMR); ++ msm_write(port, 0, UART_IMR); ++ ++ if (msm_port->is_uartdm) ++ reset_dm_count(port, 1); ++ ++ /* Wait until FIFO is empty */ ++ while (!(msm_read(port, UART_SR) & UART_SR_TX_READY)) ++ cpu_relax(); ++ ++ /* Write a character */ ++ msm_write(port, c, msm_port->is_uartdm ? UARTDM_TF : UART_TF); ++ ++ /* Wait until FIFO is empty */ ++ while (!(msm_read(port, UART_SR) & UART_SR_TX_READY)) ++ cpu_relax(); ++ ++ /* Enable interrupts */ ++ msm_write(port, imr, UART_IMR); ++ ++ return; ++} ++#endif ++ + static struct uart_ops msm_uart_pops = { + .tx_empty = msm_tx_empty, + .set_mctrl = msm_set_mctrl, +@@ -729,6 +853,11 @@ static struct uart_ops msm_uart_pops = { + .config_port = msm_config_port, + .verify_port = msm_verify_port, + .pm = msm_power, ++#ifdef CONFIG_CONSOLE_POLL ++ .poll_init = msm_poll_init, ++ .poll_get_char = msm_poll_get_char, ++ .poll_put_char = msm_poll_put_char, ++#endif + }; + + static struct msm_port msm_uart_ports[] = { +@@ -900,7 +1029,10 @@ static struct uart_driver msm_uart_driver = { + static atomic_t msm_uart_next_id = ATOMIC_INIT(0); + + static const struct of_device_id msm_uartdm_table[] = { +- { .compatible = "qcom,msm-uartdm" }, ++ { .compatible = "qcom,msm-uartdm-v1.1", .data = (void *)UARTDM_1P1 }, ++ { .compatible = "qcom,msm-uartdm-v1.2", .data = (void *)UARTDM_1P2 }, ++ { .compatible = "qcom,msm-uartdm-v1.3", .data = (void *)UARTDM_1P3 }, ++ { .compatible = "qcom,msm-uartdm-v1.4", .data = (void *)UARTDM_1P4 }, + { } + }; + +@@ -909,6 +1041,7 @@ static int __init msm_serial_probe(struct platform_device *pdev) + struct msm_port *msm_port; + struct resource *resource; + struct uart_port *port; ++ const struct of_device_id *id; + int irq; + + if (pdev->id == -1) +@@ -923,8 +1056,9 @@ static int __init msm_serial_probe(struct platform_device *pdev) + port->dev = &pdev->dev; + msm_port = UART_TO_MSM(port); + +- if (of_match_device(msm_uartdm_table, &pdev->dev)) +- msm_port->is_uartdm = 1; ++ id = of_match_device(msm_uartdm_table, &pdev->dev); ++ if (id) ++ msm_port->is_uartdm = (unsigned long)id->data; + else + msm_port->is_uartdm = 0; + +diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h +index 469fda5..1e9b68b 100644 +--- a/drivers/tty/serial/msm_serial.h ++++ b/drivers/tty/serial/msm_serial.h +@@ -59,6 +59,7 @@ + #define UART_CR_CMD_RESET_RFR (14 << 4) + #define UART_CR_CMD_PROTECTION_EN (16 << 4) + #define UART_CR_CMD_STALE_EVENT_ENABLE (80 << 4) ++#define UART_CR_CMD_FORCE_STALE (4 << 8) + #define UART_CR_CMD_RESET_TX_READY (3 << 8) + #define UART_CR_TX_DISABLE (1 << 3) + #define UART_CR_TX_ENABLE (1 << 2) +@@ -113,6 +114,14 @@ + #define GSBI_PROTOCOL_UART 0x40 + #define GSBI_PROTOCOL_IDLE 0x0 + ++#define UARTDM_RXFS 0x50 ++#define UARTDM_RXFS_BUF_SHIFT 0x7 ++#define UARTDM_RXFS_BUF_MASK 0x7 ++ ++#define UARTDM_DMEN 0x3C ++#define UARTDM_DMEN_RX_SC_ENABLE BIT(5) ++#define UARTDM_DMEN_TX_SC_ENABLE BIT(4) ++ + #define UARTDM_DMRX 0x34 + #define UARTDM_NCF_TX 0x40 + #define UARTDM_RX_TOTAL_SNAP 0x38 +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0087-tty-serial-msm-Remove-direct-access-to-GSBI.patch b/target/linux/ipq806x/patches/0087-tty-serial-msm-Remove-direct-access-to-GSBI.patch new file mode 100644 index 0000000000..8cbb52a903 --- /dev/null +++ b/target/linux/ipq806x/patches/0087-tty-serial-msm-Remove-direct-access-to-GSBI.patch @@ -0,0 +1,151 @@ +From 025909cbf933cc20c2ff5ea9f87de8e17a739a08 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 24 Apr 2014 11:31:22 -0500 +Subject: [PATCH 087/182] tty: serial: msm: Remove direct access to GSBI + +This patch removes direct access of the GSBI registers. GSBI configuration +should be done through the GSBI driver directly. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/tty/serial/msm_serial.c | 48 ++------------------------------------- + drivers/tty/serial/msm_serial.h | 5 ---- + 2 files changed, 2 insertions(+), 51 deletions(-) + +diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c +index 053b98e..778e376 100644 +--- a/drivers/tty/serial/msm_serial.c ++++ b/drivers/tty/serial/msm_serial.c +@@ -52,7 +52,6 @@ struct msm_port { + struct clk *clk; + struct clk *pclk; + unsigned int imr; +- void __iomem *gsbi_base; + int is_uartdm; + unsigned int old_snap_state; + }; +@@ -599,9 +598,7 @@ static const char *msm_type(struct uart_port *port) + static void msm_release_port(struct uart_port *port) + { + struct platform_device *pdev = to_platform_device(port->dev); +- struct msm_port *msm_port = UART_TO_MSM(port); + struct resource *uart_resource; +- struct resource *gsbi_resource; + resource_size_t size; + + uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); +@@ -612,28 +609,12 @@ static void msm_release_port(struct uart_port *port) + release_mem_region(port->mapbase, size); + iounmap(port->membase); + port->membase = NULL; +- +- if (msm_port->gsbi_base) { +- writel_relaxed(GSBI_PROTOCOL_IDLE, +- msm_port->gsbi_base + GSBI_CONTROL); +- +- gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1); +- if (unlikely(!gsbi_resource)) +- return; +- +- size = resource_size(gsbi_resource); +- release_mem_region(gsbi_resource->start, size); +- iounmap(msm_port->gsbi_base); +- msm_port->gsbi_base = NULL; +- } + } + + static int msm_request_port(struct uart_port *port) + { +- struct msm_port *msm_port = UART_TO_MSM(port); + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *uart_resource; +- struct resource *gsbi_resource; + resource_size_t size; + int ret; + +@@ -652,30 +633,8 @@ static int msm_request_port(struct uart_port *port) + goto fail_release_port; + } + +- gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1); +- /* Is this a GSBI-based port? */ +- if (gsbi_resource) { +- size = resource_size(gsbi_resource); +- +- if (!request_mem_region(gsbi_resource->start, size, +- "msm_serial")) { +- ret = -EBUSY; +- goto fail_release_port_membase; +- } +- +- msm_port->gsbi_base = ioremap(gsbi_resource->start, size); +- if (!msm_port->gsbi_base) { +- ret = -EBUSY; +- goto fail_release_gsbi; +- } +- } +- + return 0; + +-fail_release_gsbi: +- release_mem_region(gsbi_resource->start, size); +-fail_release_port_membase: +- iounmap(port->membase); + fail_release_port: + release_mem_region(port->mapbase, size); + return ret; +@@ -683,7 +642,6 @@ fail_release_port: + + static void msm_config_port(struct uart_port *port, int flags) + { +- struct msm_port *msm_port = UART_TO_MSM(port); + int ret; + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_MSM; +@@ -691,9 +649,6 @@ static void msm_config_port(struct uart_port *port, int flags) + if (ret) + return; + } +- if (msm_port->gsbi_base) +- writel_relaxed(GSBI_PROTOCOL_UART, +- msm_port->gsbi_base + GSBI_CONTROL); + } + + static int msm_verify_port(struct uart_port *port, struct serial_struct *ser) +@@ -1110,6 +1065,7 @@ static struct of_device_id msm_match_table[] = { + + static struct platform_driver msm_platform_driver = { + .remove = msm_serial_remove, ++ .probe = msm_serial_probe, + .driver = { + .name = "msm_serial", + .owner = THIS_MODULE, +@@ -1125,7 +1081,7 @@ static int __init msm_serial_init(void) + if (unlikely(ret)) + return ret; + +- ret = platform_driver_probe(&msm_platform_driver, msm_serial_probe); ++ ret = platform_driver_register(&msm_platform_driver); + if (unlikely(ret)) + uart_unregister_driver(&msm_uart_driver); + +diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h +index 1e9b68b..d98d45e 100644 +--- a/drivers/tty/serial/msm_serial.h ++++ b/drivers/tty/serial/msm_serial.h +@@ -109,11 +109,6 @@ + #define UART_ISR 0x0014 + #define UART_ISR_TX_READY (1 << 7) + +-#define GSBI_CONTROL 0x0 +-#define GSBI_PROTOCOL_CODE 0x30 +-#define GSBI_PROTOCOL_UART 0x40 +-#define GSBI_PROTOCOL_IDLE 0x0 +- + #define UARTDM_RXFS 0x50 + #define UARTDM_RXFS_BUF_SHIFT 0x7 + #define UARTDM_RXFS_BUF_MASK 0x7 +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0088-soc-qcom-Add-device-tree-binding-for-GSBI.patch b/target/linux/ipq806x/patches/0088-soc-qcom-Add-device-tree-binding-for-GSBI.patch new file mode 100644 index 0000000000..39762c1be7 --- /dev/null +++ b/target/linux/ipq806x/patches/0088-soc-qcom-Add-device-tree-binding-for-GSBI.patch @@ -0,0 +1,135 @@ +From 5a58dbf4d82c29f7e6d89abc3520bed1aa2af05c Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 24 Apr 2014 11:31:20 -0500 +Subject: [PATCH 088/182] soc: qcom: Add device tree binding for GSBI + +Add device tree binding support for the QCOM GSBI driver. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + .../devicetree/bindings/soc/qcom/qcom,gsbi.txt | 78 ++++++++++++++++++++ + include/dt-bindings/soc/qcom,gsbi.h | 26 +++++++ + 2 files changed, 104 insertions(+) + create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt + create mode 100644 include/dt-bindings/soc/qcom,gsbi.h + +diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt +new file mode 100644 +index 0000000..4ce24d4 +--- /dev/null ++++ b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt +@@ -0,0 +1,78 @@ ++QCOM GSBI (General Serial Bus Interface) Driver ++ ++The GSBI controller is modeled as a node with zero or more child nodes, each ++representing a serial sub-node device that is mux'd as part of the GSBI ++configuration settings. The mode setting will govern the input/output mode of ++the 4 GSBI IOs. ++ ++Required properties: ++- compatible: must contain "qcom,gsbi-v1.0.0" for APQ8064/IPQ8064 ++- reg: Address range for GSBI registers ++- clocks: required clock ++- clock-names: must contain "iface" entry ++- qcom,mode : indicates MUX value for configuration of the serial interface. ++ Please reference dt-bindings/soc/qcom,gsbi.h for valid mux values. ++ ++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. ++ ++Required properties if child node exists: ++- #address-cells: Must be 1 ++- #size-cells: Must be 1 ++- ranges: Must be present ++ ++Properties for children: ++ ++A GSBI controller node can contain 0 or more child nodes representing serial ++devices. These serial devices can be a QCOM UART, I2C controller, spi ++controller, or some combination of aforementioned devices. ++ ++See the following for child node definitions: ++Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt ++Documentation/devicetree/bindings/spi/qcom,spi-qup.txt ++Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt ++ ++Example for APQ8064: ++ ++#include <dt-bindings/soc/qcom,gsbi.h> ++ ++ gsbi4@16300000 { ++ compatible = "qcom,gsbi-v1.0.0"; ++ reg = <0x16300000 0x100>; ++ clocks = <&gcc GSBI4_H_CLK>; ++ clock-names = "iface"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ qcom,mode = <GSBI_PROT_I2C_UART>; ++ qcom,crci = <GSBI_CRCI_QUP>; ++ ++ /* child nodes go under here */ ++ ++ i2c_qup4: i2c@16380000 { ++ compatible = "qcom,i2c-qup-v1.1.1"; ++ reg = <0x16380000 0x1000>; ++ interrupts = <0 153 0>; ++ ++ clocks = <&gcc GSBI4_QUP_CLK>, <&gcc GSBI4_H_CLK>; ++ clock-names = "core", "iface"; ++ ++ clock-frequency = <200000>; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ }; ++ ++ uart4: serial@16340000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x16340000 0x1000>, ++ <0x16300000 0x1000>; ++ interrupts = <0 152 0x0>; ++ clocks = <&gcc GSBI4_UART_CLK>, <&gcc GSBI4_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "ok"; ++ }; ++ }; ++ +diff --git a/include/dt-bindings/soc/qcom,gsbi.h b/include/dt-bindings/soc/qcom,gsbi.h +new file mode 100644 +index 0000000..7ac4292 +--- /dev/null ++++ b/include/dt-bindings/soc/qcom,gsbi.h +@@ -0,0 +1,26 @@ ++/* 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 __DT_BINDINGS_QCOM_GSBI_H ++#define __DT_BINDINGS_QCOM_GSBI_H ++ ++#define GSBI_PROT_IDLE 0 ++#define GSBI_PROT_I2C_UIM 1 ++#define GSBI_PROT_I2C 2 ++#define GSBI_PROT_SPI 3 ++#define GSBI_PROT_UART_W_FC 4 ++#define GSBI_PROT_UIM 5 ++#define GSBI_PROT_I2C_UART 6 ++ ++#define GSBI_CRCI_QUP 0 ++#define GSBI_CRCI_UART 1 ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0089-ARM-dts-MSM8974-Add-pinctrl-node.patch b/target/linux/ipq806x/patches/0089-ARM-dts-MSM8974-Add-pinctrl-node.patch new file mode 100644 index 0000000000..1663fbe906 --- /dev/null +++ b/target/linux/ipq806x/patches/0089-ARM-dts-MSM8974-Add-pinctrl-node.patch @@ -0,0 +1,57 @@ +From b3a77c7cab10272988231482e2c654c5f10a910c Mon Sep 17 00:00:00 2001 +From: "Ivan T. Ivanov" <iivanov@mm-sol.com> +Date: Thu, 6 Feb 2014 17:28:49 +0200 +Subject: [PATCH 089/182] ARM: dts: MSM8974: Add pinctrl node + +Add the pin control node and pin definitions of SPI8. + +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Reviewed-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Acked-by: Linus Walleij <linus.walleij@linaro.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-msm8974.dtsi | 29 +++++++++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi +index f687239..23aa387 100644 +--- a/arch/arm/boot/dts/qcom-msm8974.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi +@@ -198,5 +198,34 @@ + clocks = <&gcc GCC_PRNG_AHB_CLK>; + clock-names = "core"; + }; ++ ++ msmgpio: pinctrl@fd510000 { ++ compatible = "qcom,msm8974-pinctrl"; ++ reg = <0xfd510000 0x4000>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ interrupts = <0 208 0>; ++ ++ spi8_default: spi8_default { ++ mosi { ++ pins = "gpio45"; ++ function = "blsp_spi8"; ++ }; ++ miso { ++ pins = "gpio46"; ++ function = "blsp_spi8"; ++ }; ++ cs { ++ pins = "gpio47"; ++ function = "blsp_spi8"; ++ }; ++ clk { ++ pins = "gpio48"; ++ function = "blsp_spi8"; ++ }; ++ }; ++ }; + }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0090-ARM-dts-msm-Add-SDHC-controller-nodes-for-MSM8974-an.patch b/target/linux/ipq806x/patches/0090-ARM-dts-msm-Add-SDHC-controller-nodes-for-MSM8974-an.patch new file mode 100644 index 0000000000..c16258ce32 --- /dev/null +++ b/target/linux/ipq806x/patches/0090-ARM-dts-msm-Add-SDHC-controller-nodes-for-MSM8974-an.patch @@ -0,0 +1,75 @@ +From 6632619d49f0f90c4d74caad67749864f154cae4 Mon Sep 17 00:00:00 2001 +From: Georgi Djakov <gdjakov@mm-sol.com> +Date: Fri, 31 Jan 2014 16:21:56 +0200 +Subject: [PATCH 090/182] ARM: dts: msm: Add SDHC controller nodes for MSM8974 + and DB8074 board + +Add support for the 2 SDHC controllers on the DB8074 board. The first +controller (at 0xf9824900) is connected to an on board soldered eMMC. +The second controller (at 0xf98a4900) is connected to a uSD card slot. + +Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-apq8074-dragonboard.dts | 13 +++++++++++++ + arch/arm/boot/dts/qcom-msm8974.dtsi | 22 ++++++++++++++++++++++ + 2 files changed, 35 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts b/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts +index 13ac3e2..92320c4 100644 +--- a/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts ++++ b/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts +@@ -3,4 +3,17 @@ + / { + model = "Qualcomm APQ8074 Dragonboard"; + compatible = "qcom,apq8074-dragonboard", "qcom,apq8074"; ++ ++ soc: soc { ++ sdhci@f9824900 { ++ bus-width = <8>; ++ non-removable; ++ status = "ok"; ++ }; ++ ++ sdhci@f98a4900 { ++ cd-gpios = <&msmgpio 62 0x1>; ++ bus-width = <4>; ++ }; ++ }; + }; +diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi +index 23aa387..c530a33 100644 +--- a/arch/arm/boot/dts/qcom-msm8974.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi +@@ -192,6 +192,28 @@ + clock-names = "core", "iface"; + }; + ++ sdhci@f9824900 { ++ compatible = "qcom,sdhci-msm-v4"; ++ reg = <0xf9824900 0x11c>, <0xf9824000 0x800>; ++ reg-names = "hc_mem", "core_mem"; ++ interrupts = <0 123 0>, <0 138 0>; ++ interrupt-names = "hc_irq", "pwr_irq"; ++ clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ }; ++ ++ sdhci@f98a4900 { ++ compatible = "qcom,sdhci-msm-v4"; ++ reg = <0xf98a4900 0x11c>, <0xf98a4000 0x800>; ++ reg-names = "hc_mem", "core_mem"; ++ interrupts = <0 125 0>, <0 221 0>; ++ interrupt-names = "hc_irq", "pwr_irq"; ++ clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ }; ++ + rng@f9bff000 { + compatible = "qcom,prng"; + reg = <0xf9bff000 0x200>; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0091-ARM-dts-qcom-Update-msm8974-apq8074-device-trees.patch b/target/linux/ipq806x/patches/0091-ARM-dts-qcom-Update-msm8974-apq8074-device-trees.patch new file mode 100644 index 0000000000..dcaaa3df74 --- /dev/null +++ b/target/linux/ipq806x/patches/0091-ARM-dts-qcom-Update-msm8974-apq8074-device-trees.patch @@ -0,0 +1,186 @@ +From 63495b04141e60ceb40d4632a41b7cd4a3d23dd2 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Wed, 28 May 2014 12:01:29 -0500 +Subject: [PATCH 091/182] ARM: dts: qcom: Update msm8974/apq8074 device trees + +* Move SoC peripherals into an SoC container node +* Move serial enabling into board file (qcom-apq8074-dragonboard.dts) +* Move spi pinctrl into board file +* Cleanup cpu node to match binding spec, enable-method and compatible + should be per cpu, not part of the container +* Drop interrupts property from l2-cache node as its not part of the + binding spec +* Move timer node out of SoC container + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-apq8074-dragonboard.dts | 28 +++++++++++++- + arch/arm/boot/dts/qcom-msm8974.dtsi | 49 +++++++++--------------- + 2 files changed, 45 insertions(+), 32 deletions(-) + +diff --git a/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts b/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts +index 92320c4..b4dfb01 100644 +--- a/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts ++++ b/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts +@@ -4,7 +4,11 @@ + model = "Qualcomm APQ8074 Dragonboard"; + compatible = "qcom,apq8074-dragonboard", "qcom,apq8074"; + +- soc: soc { ++ soc { ++ serial@f991e000 { ++ status = "ok"; ++ }; ++ + sdhci@f9824900 { + bus-width = <8>; + non-removable; +@@ -15,5 +19,27 @@ + cd-gpios = <&msmgpio 62 0x1>; + bus-width = <4>; + }; ++ ++ ++ pinctrl@fd510000 { ++ spi8_default: spi8_default { ++ mosi { ++ pins = "gpio45"; ++ function = "blsp_spi8"; ++ }; ++ miso { ++ pins = "gpio46"; ++ function = "blsp_spi8"; ++ }; ++ cs { ++ pins = "gpio47"; ++ function = "blsp_spi8"; ++ }; ++ clk { ++ pins = "gpio48"; ++ function = "blsp_spi8"; ++ }; ++ }; ++ }; + }; + }; +diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi +index c530a33..69dca2a 100644 +--- a/arch/arm/boot/dts/qcom-msm8974.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi +@@ -13,10 +13,10 @@ + #address-cells = <1>; + #size-cells = <0>; + interrupts = <1 9 0xf04>; +- compatible = "qcom,krait"; +- enable-method = "qcom,kpss-acc-v2"; + + cpu@0 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v2"; + device_type = "cpu"; + reg = <0>; + next-level-cache = <&L2>; +@@ -24,6 +24,8 @@ + }; + + cpu@1 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v2"; + device_type = "cpu"; + reg = <1>; + next-level-cache = <&L2>; +@@ -31,6 +33,8 @@ + }; + + cpu@2 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v2"; + device_type = "cpu"; + reg = <2>; + next-level-cache = <&L2>; +@@ -38,6 +42,8 @@ + }; + + cpu@3 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v2"; + device_type = "cpu"; + reg = <3>; + next-level-cache = <&L2>; +@@ -47,7 +53,6 @@ + L2: l2-cache { + compatible = "cache"; + cache-level = <2>; +- interrupts = <0 2 0x4>; + qcom,saw = <&saw_l2>; + }; + }; +@@ -57,6 +62,15 @@ + interrupts = <1 7 0xf04>; + }; + ++ timer { ++ compatible = "arm,armv7-timer"; ++ interrupts = <1 2 0xf08>, ++ <1 3 0xf08>, ++ <1 4 0xf08>, ++ <1 1 0xf08>; ++ clock-frequency = <19200000>; ++ }; ++ + soc: soc { + #address-cells = <1>; + #size-cells = <1>; +@@ -71,15 +85,6 @@ + <0xf9002000 0x1000>; + }; + +- timer { +- compatible = "arm,armv7-timer"; +- interrupts = <1 2 0xf08>, +- <1 3 0xf08>, +- <1 4 0xf08>, +- <1 1 0xf08>; +- clock-frequency = <19200000>; +- }; +- + timer@f9020000 { + #address-cells = <1>; + #size-cells = <1>; +@@ -190,6 +195,7 @@ + interrupts = <0 108 0x0>; + clocks = <&gcc GCC_BLSP1_UART2_APPS_CLK>, <&gcc GCC_BLSP1_AHB_CLK>; + clock-names = "core", "iface"; ++ status = "disabled"; + }; + + sdhci@f9824900 { +@@ -229,25 +235,6 @@ + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <0 208 0>; +- +- spi8_default: spi8_default { +- mosi { +- pins = "gpio45"; +- function = "blsp_spi8"; +- }; +- miso { +- pins = "gpio46"; +- function = "blsp_spi8"; +- }; +- cs { +- pins = "gpio47"; +- function = "blsp_spi8"; +- }; +- clk { +- pins = "gpio48"; +- function = "blsp_spi8"; +- }; +- }; + }; + }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0092-ARM-dts-qcom-Update-msm8960-device-trees.patch b/target/linux/ipq806x/patches/0092-ARM-dts-qcom-Update-msm8960-device-trees.patch new file mode 100644 index 0000000000..5c0c7a5d2b --- /dev/null +++ b/target/linux/ipq806x/patches/0092-ARM-dts-qcom-Update-msm8960-device-trees.patch @@ -0,0 +1,268 @@ +From 881200420e6ece87d9abbb13c0653d26455cdbdd Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Wed, 28 May 2014 12:09:53 -0500 +Subject: [PATCH 092/182] ARM: dts: qcom: Update msm8960 device trees + +* Move SoC peripherals into an SoC container node +* Move serial enabling into board file (qcom-msm8960-cdp.dts) +* Cleanup cpu node to match binding spec, enable-method and compatible + should be per cpu, not part of the container +* Drop interrupts property from l2-cache node as its not part of the + binding spec +* Add GSBI node and configuration of GSBI controller + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-msm8960-cdp.dts | 10 ++ + arch/arm/boot/dts/qcom-msm8960.dtsi | 176 ++++++++++++++++++-------------- + 2 files changed, 108 insertions(+), 78 deletions(-) + +diff --git a/arch/arm/boot/dts/qcom-msm8960-cdp.dts b/arch/arm/boot/dts/qcom-msm8960-cdp.dts +index a58fb88..8f75cc4 100644 +--- a/arch/arm/boot/dts/qcom-msm8960-cdp.dts ++++ b/arch/arm/boot/dts/qcom-msm8960-cdp.dts +@@ -3,4 +3,14 @@ + / { + model = "Qualcomm MSM8960 CDP"; + compatible = "qcom,msm8960-cdp", "qcom,msm8960"; ++ ++ soc { ++ gsbi@16400000 { ++ status = "ok"; ++ qcom,mode = <GSBI_PROT_I2C_UART>; ++ serial@16440000 { ++ status = "ok"; ++ }; ++ }; ++ }; + }; +diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi +index 997b7b9..5303e53 100644 +--- a/arch/arm/boot/dts/qcom-msm8960.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8960.dtsi +@@ -3,6 +3,7 @@ + /include/ "skeleton.dtsi" + + #include <dt-bindings/clock/qcom,gcc-msm8960.h> ++#include <dt-bindings/soc/qcom,gsbi.h> + + / { + model = "Qualcomm MSM8960"; +@@ -13,10 +14,10 @@ + #address-cells = <1>; + #size-cells = <0>; + interrupts = <1 14 0x304>; +- compatible = "qcom,krait"; +- enable-method = "qcom,kpss-acc-v1"; + + cpu@0 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; + device_type = "cpu"; + reg = <0>; + next-level-cache = <&L2>; +@@ -25,6 +26,8 @@ + }; + + cpu@1 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; + device_type = "cpu"; + reg = <1>; + next-level-cache = <&L2>; +@@ -35,7 +38,6 @@ + L2: l2-cache { + compatible = "cache"; + cache-level = <2>; +- interrupts = <0 2 0x4>; + }; + }; + +@@ -45,91 +47,109 @@ + qcom,no-pc-write; + }; + +- intc: interrupt-controller@2000000 { +- compatible = "qcom,msm-qgic2"; +- interrupt-controller; +- #interrupt-cells = <3>; +- reg = < 0x02000000 0x1000 >, +- < 0x02002000 0x1000 >; +- }; ++ soc: soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ compatible = "simple-bus"; ++ ++ intc: interrupt-controller@2000000 { ++ compatible = "qcom,msm-qgic2"; ++ interrupt-controller; ++ #interrupt-cells = <3>; ++ reg = <0x02000000 0x1000>, ++ <0x02002000 0x1000>; ++ }; + +- timer@200a000 { +- compatible = "qcom,kpss-timer", "qcom,msm-timer"; +- interrupts = <1 1 0x301>, +- <1 2 0x301>, +- <1 3 0x301>; +- reg = <0x0200a000 0x100>; +- clock-frequency = <27000000>, +- <32768>; +- cpu-offset = <0x80000>; +- }; ++ timer@200a000 { ++ compatible = "qcom,kpss-timer", "qcom,msm-timer"; ++ interrupts = <1 1 0x301>, ++ <1 2 0x301>, ++ <1 3 0x301>; ++ reg = <0x0200a000 0x100>; ++ clock-frequency = <27000000>, ++ <32768>; ++ cpu-offset = <0x80000>; ++ }; + +- msmgpio: gpio@800000 { +- compatible = "qcom,msm-gpio"; +- gpio-controller; +- #gpio-cells = <2>; +- ngpio = <150>; +- interrupts = <0 16 0x4>; +- interrupt-controller; +- #interrupt-cells = <2>; +- reg = <0x800000 0x4000>; +- }; ++ msmgpio: gpio@800000 { ++ compatible = "qcom,msm-gpio"; ++ gpio-controller; ++ #gpio-cells = <2>; ++ ngpio = <150>; ++ interrupts = <0 16 0x4>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ reg = <0x800000 0x4000>; ++ }; + +- gcc: clock-controller@900000 { +- compatible = "qcom,gcc-msm8960"; +- #clock-cells = <1>; +- #reset-cells = <1>; +- reg = <0x900000 0x4000>; +- }; ++ gcc: clock-controller@900000 { ++ compatible = "qcom,gcc-msm8960"; ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ reg = <0x900000 0x4000>; ++ }; + +- clock-controller@4000000 { +- compatible = "qcom,mmcc-msm8960"; +- reg = <0x4000000 0x1000>; +- #clock-cells = <1>; +- #reset-cells = <1>; +- }; ++ clock-controller@4000000 { ++ compatible = "qcom,mmcc-msm8960"; ++ reg = <0x4000000 0x1000>; ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ }; + +- acc0: clock-controller@2088000 { +- compatible = "qcom,kpss-acc-v1"; +- reg = <0x02088000 0x1000>, <0x02008000 0x1000>; +- }; ++ acc0: clock-controller@2088000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x02088000 0x1000>, <0x02008000 0x1000>; ++ }; + +- acc1: clock-controller@2098000 { +- compatible = "qcom,kpss-acc-v1"; +- reg = <0x02098000 0x1000>, <0x02008000 0x1000>; +- }; ++ acc1: clock-controller@2098000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x02098000 0x1000>, <0x02008000 0x1000>; ++ }; + +- saw0: regulator@2089000 { +- compatible = "qcom,saw2"; +- reg = <0x02089000 0x1000>, <0x02009000 0x1000>; +- regulator; +- }; ++ saw0: regulator@2089000 { ++ compatible = "qcom,saw2"; ++ reg = <0x02089000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; + +- saw1: regulator@2099000 { +- compatible = "qcom,saw2"; +- reg = <0x02099000 0x1000>, <0x02009000 0x1000>; +- regulator; +- }; ++ saw1: regulator@2099000 { ++ compatible = "qcom,saw2"; ++ reg = <0x02099000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; + +- serial@16440000 { +- compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; +- reg = <0x16440000 0x1000>, +- <0x16400000 0x1000>; +- interrupts = <0 154 0x0>; +- clocks = <&gcc GSBI5_UART_CLK>, <&gcc GSBI5_H_CLK>; +- clock-names = "core", "iface"; +- }; ++ gsbi5: gsbi@16400000 { ++ compatible = "qcom,gsbi-v1.0.0"; ++ reg = <0x16400000 0x100>; ++ clocks = <&gcc GSBI5_H_CLK>; ++ clock-names = "iface"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ serial@16440000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x16440000 0x1000>, ++ <0x16400000 0x1000>; ++ interrupts = <0 154 0x0>; ++ clocks = <&gcc GSBI5_UART_CLK>, <&gcc GSBI5_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ }; ++ }; + +- qcom,ssbi@500000 { +- compatible = "qcom,ssbi"; +- reg = <0x500000 0x1000>; +- qcom,controller-type = "pmic-arbiter"; +- }; ++ qcom,ssbi@500000 { ++ compatible = "qcom,ssbi"; ++ reg = <0x500000 0x1000>; ++ qcom,controller-type = "pmic-arbiter"; ++ }; + +- rng@1a500000 { +- compatible = "qcom,prng"; +- reg = <0x1a500000 0x200>; +- clocks = <&gcc PRNG_CLK>; +- clock-names = "core"; ++ rng@1a500000 { ++ compatible = "qcom,prng"; ++ reg = <0x1a500000 0x200>; ++ clocks = <&gcc PRNG_CLK>; ++ clock-names = "core"; ++ }; + }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0093-ARM-dts-qcom-Update-msm8660-device-trees.patch b/target/linux/ipq806x/patches/0093-ARM-dts-qcom-Update-msm8660-device-trees.patch new file mode 100644 index 0000000000..72f9939a08 --- /dev/null +++ b/target/linux/ipq806x/patches/0093-ARM-dts-qcom-Update-msm8660-device-trees.patch @@ -0,0 +1,191 @@ +From 355bf7c6410f5b6e37b5c2b28ebe59bb701c42d6 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Wed, 28 May 2014 12:12:40 -0500 +Subject: [PATCH 093/182] ARM: dts: qcom: Update msm8660 device trees + +* Move SoC peripherals into an SoC container node +* Move serial enabling into board file (qcom-msm8660-surf.dts) +* Cleanup cpu node to match binding spec, enable-method and compatible + should be per cpu, not part of the container +* Add GSBI node and configuration of GSBI controller + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-msm8660-surf.dts | 10 +++ + arch/arm/boot/dts/qcom-msm8660.dtsi | 115 ++++++++++++++++++------------- + 2 files changed, 78 insertions(+), 47 deletions(-) + +diff --git a/arch/arm/boot/dts/qcom-msm8660-surf.dts b/arch/arm/boot/dts/qcom-msm8660-surf.dts +index 169bad9..45180ad 100644 +--- a/arch/arm/boot/dts/qcom-msm8660-surf.dts ++++ b/arch/arm/boot/dts/qcom-msm8660-surf.dts +@@ -3,4 +3,14 @@ + / { + model = "Qualcomm MSM8660 SURF"; + compatible = "qcom,msm8660-surf", "qcom,msm8660"; ++ ++ soc { ++ gsbi@19c00000 { ++ status = "ok"; ++ qcom,mode = <GSBI_PROT_I2C_UART>; ++ serial@19c40000 { ++ status = "ok"; ++ }; ++ }; ++ }; + }; +diff --git a/arch/arm/boot/dts/qcom-msm8660.dtsi b/arch/arm/boot/dts/qcom-msm8660.dtsi +index c52a9e9..53837aaa2f 100644 +--- a/arch/arm/boot/dts/qcom-msm8660.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8660.dtsi +@@ -3,6 +3,7 @@ + /include/ "skeleton.dtsi" + + #include <dt-bindings/clock/qcom,gcc-msm8660.h> ++#include <dt-bindings/soc/qcom,gsbi.h> + + / { + model = "Qualcomm MSM8660"; +@@ -12,16 +13,18 @@ + cpus { + #address-cells = <1>; + #size-cells = <0>; +- compatible = "qcom,scorpion"; +- enable-method = "qcom,gcc-msm8660"; + + cpu@0 { ++ compatible = "qcom,scorpion"; ++ enable-method = "qcom,gcc-msm8660"; + device_type = "cpu"; + reg = <0>; + next-level-cache = <&L2>; + }; + + cpu@1 { ++ compatible = "qcom,scorpion"; ++ enable-method = "qcom,gcc-msm8660"; + device_type = "cpu"; + reg = <1>; + next-level-cache = <&L2>; +@@ -33,55 +36,73 @@ + }; + }; + +- intc: interrupt-controller@2080000 { +- compatible = "qcom,msm-8660-qgic"; +- interrupt-controller; +- #interrupt-cells = <3>; +- reg = < 0x02080000 0x1000 >, +- < 0x02081000 0x1000 >; +- }; ++ soc: soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ compatible = "simple-bus"; + +- timer@2000000 { +- compatible = "qcom,scss-timer", "qcom,msm-timer"; +- interrupts = <1 0 0x301>, +- <1 1 0x301>, +- <1 2 0x301>; +- reg = <0x02000000 0x100>; +- clock-frequency = <27000000>, +- <32768>; +- cpu-offset = <0x40000>; +- }; ++ intc: interrupt-controller@2080000 { ++ compatible = "qcom,msm-8660-qgic"; ++ interrupt-controller; ++ #interrupt-cells = <3>; ++ reg = < 0x02080000 0x1000 >, ++ < 0x02081000 0x1000 >; ++ }; + +- msmgpio: gpio@800000 { +- compatible = "qcom,msm-gpio"; +- reg = <0x00800000 0x4000>; +- gpio-controller; +- #gpio-cells = <2>; +- ngpio = <173>; +- interrupts = <0 16 0x4>; +- interrupt-controller; +- #interrupt-cells = <2>; +- }; ++ timer@2000000 { ++ compatible = "qcom,scss-timer", "qcom,msm-timer"; ++ interrupts = <1 0 0x301>, ++ <1 1 0x301>, ++ <1 2 0x301>; ++ reg = <0x02000000 0x100>; ++ clock-frequency = <27000000>, ++ <32768>; ++ cpu-offset = <0x40000>; ++ }; + +- gcc: clock-controller@900000 { +- compatible = "qcom,gcc-msm8660"; +- #clock-cells = <1>; +- #reset-cells = <1>; +- reg = <0x900000 0x4000>; +- }; ++ msmgpio: gpio@800000 { ++ compatible = "qcom,msm-gpio"; ++ reg = <0x00800000 0x4000>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ ngpio = <173>; ++ interrupts = <0 16 0x4>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; + +- serial@19c40000 { +- compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; +- reg = <0x19c40000 0x1000>, +- <0x19c00000 0x1000>; +- interrupts = <0 195 0x0>; +- clocks = <&gcc GSBI12_UART_CLK>, <&gcc GSBI12_H_CLK>; +- clock-names = "core", "iface"; +- }; ++ gcc: clock-controller@900000 { ++ compatible = "qcom,gcc-msm8660"; ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ reg = <0x900000 0x4000>; ++ }; ++ ++ gsbi12: gsbi@19c00000 { ++ compatible = "qcom,gsbi-v1.0.0"; ++ reg = <0x19c00000 0x100>; ++ clocks = <&gcc GSBI12_H_CLK>; ++ clock-names = "iface"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; + +- qcom,ssbi@500000 { +- compatible = "qcom,ssbi"; +- reg = <0x500000 0x1000>; +- qcom,controller-type = "pmic-arbiter"; ++ serial@19c40000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x19c40000 0x1000>, ++ <0x19c00000 0x1000>; ++ interrupts = <0 195 0x0>; ++ clocks = <&gcc GSBI12_UART_CLK>, <&gcc GSBI12_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ }; ++ }; ++ ++ qcom,ssbi@500000 { ++ compatible = "qcom,ssbi"; ++ reg = <0x500000 0x1000>; ++ qcom,controller-type = "pmic-arbiter"; ++ }; + }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0094-ARM-dts-qcom-Add-initial-APQ8064-SoC-and-IFC6410-boa.patch b/target/linux/ipq806x/patches/0094-ARM-dts-qcom-Add-initial-APQ8064-SoC-and-IFC6410-boa.patch new file mode 100644 index 0000000000..848cbc7834 --- /dev/null +++ b/target/linux/ipq806x/patches/0094-ARM-dts-qcom-Add-initial-APQ8064-SoC-and-IFC6410-boa.patch @@ -0,0 +1,265 @@ +From 07d7d95706c1bf373bd6b30c42f95c7b8dc8b9ce Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 3 Apr 2014 14:48:22 -0500 +Subject: [PATCH 094/182] ARM: dts: qcom: Add initial APQ8064 SoC and IFC6410 + board device trees + +Add basic APQ8064 SoC include device tree and support for basic booting on +the IFC6410 board. Also, keep dtb build list and qcom_dt_match in sorted +order. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/Makefile | 8 +- + arch/arm/boot/dts/qcom-apq8064-ifc6410.dts | 16 +++ + arch/arm/boot/dts/qcom-apq8064-v2.0.dtsi | 1 + + arch/arm/boot/dts/qcom-apq8064.dtsi | 170 ++++++++++++++++++++++++++++ + arch/arm/mach-qcom/board.c | 3 +- + 5 files changed, 194 insertions(+), 4 deletions(-) + create mode 100644 arch/arm/boot/dts/qcom-apq8064-ifc6410.dts + create mode 100644 arch/arm/boot/dts/qcom-apq8064-v2.0.dtsi + create mode 100644 arch/arm/boot/dts/qcom-apq8064.dtsi + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index 4a89023..ee3dfea 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -231,9 +231,11 @@ dtb-$(CONFIG_ARCH_OMAP2PLUS) += omap2420-h4.dtb \ + dra7-evm.dtb + dtb-$(CONFIG_ARCH_ORION5X) += orion5x-lacie-ethernet-disk-mini-v2.dtb + dtb-$(CONFIG_ARCH_PRIMA2) += prima2-evb.dtb +-dtb-$(CONFIG_ARCH_QCOM) += qcom-msm8660-surf.dtb \ +- qcom-msm8960-cdp.dtb \ +- qcom-apq8074-dragonboard.dtb ++dtb-$(CONFIG_ARCH_QCOM) += \ ++ qcom-apq8064-ifc6410.dtb \ ++ qcom-apq8074-dragonboard.dtb \ ++ qcom-msm8660-surf.dtb \ ++ qcom-msm8960-cdp.dtb + dtb-$(CONFIG_ARCH_U8500) += ste-snowball.dtb \ + ste-hrefprev60-stuib.dtb \ + ste-hrefprev60-tvk.dtb \ +diff --git a/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts b/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts +new file mode 100644 +index 0000000..7c2441d +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts +@@ -0,0 +1,16 @@ ++#include "qcom-apq8064-v2.0.dtsi" ++ ++/ { ++ model = "Qualcomm APQ8064/IFC6410"; ++ compatible = "qcom,apq8064-ifc6410", "qcom,apq8064"; ++ ++ soc { ++ gsbi@16600000 { ++ status = "ok"; ++ qcom,mode = <GSBI_PROT_I2C_UART>; ++ serial@16640000 { ++ status = "ok"; ++ }; ++ }; ++ }; ++}; +diff --git a/arch/arm/boot/dts/qcom-apq8064-v2.0.dtsi b/arch/arm/boot/dts/qcom-apq8064-v2.0.dtsi +new file mode 100644 +index 0000000..935c394 +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-apq8064-v2.0.dtsi +@@ -0,0 +1 @@ ++#include "qcom-apq8064.dtsi" +diff --git a/arch/arm/boot/dts/qcom-apq8064.dtsi b/arch/arm/boot/dts/qcom-apq8064.dtsi +new file mode 100644 +index 0000000..92bf793 +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-apq8064.dtsi +@@ -0,0 +1,170 @@ ++/dts-v1/; ++ ++#include "skeleton.dtsi" ++#include <dt-bindings/clock/qcom,gcc-msm8960.h> ++#include <dt-bindings/soc/qcom,gsbi.h> ++ ++/ { ++ model = "Qualcomm APQ8064"; ++ compatible = "qcom,apq8064"; ++ interrupt-parent = <&intc>; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu@0 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; ++ device_type = "cpu"; ++ reg = <0>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc0>; ++ qcom,saw = <&saw0>; ++ }; ++ ++ cpu@1 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; ++ device_type = "cpu"; ++ reg = <1>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc1>; ++ qcom,saw = <&saw1>; ++ }; ++ ++ cpu@2 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; ++ device_type = "cpu"; ++ reg = <2>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc2>; ++ qcom,saw = <&saw2>; ++ }; ++ ++ cpu@3 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; ++ device_type = "cpu"; ++ reg = <3>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc3>; ++ qcom,saw = <&saw3>; ++ }; ++ ++ L2: l2-cache { ++ compatible = "cache"; ++ cache-level = <2>; ++ }; ++ }; ++ ++ cpu-pmu { ++ compatible = "qcom,krait-pmu"; ++ interrupts = <1 10 0x304>; ++ }; ++ ++ soc: soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ compatible = "simple-bus"; ++ ++ intc: interrupt-controller@2000000 { ++ compatible = "qcom,msm-qgic2"; ++ interrupt-controller; ++ #interrupt-cells = <3>; ++ reg = <0x02000000 0x1000>, ++ <0x02002000 0x1000>; ++ }; ++ ++ timer@200a000 { ++ compatible = "qcom,kpss-timer", "qcom,msm-timer"; ++ interrupts = <1 1 0x301>, ++ <1 2 0x301>, ++ <1 3 0x301>; ++ reg = <0x0200a000 0x100>; ++ clock-frequency = <27000000>, ++ <32768>; ++ cpu-offset = <0x80000>; ++ }; ++ ++ acc0: clock-controller@2088000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x02088000 0x1000>, <0x02008000 0x1000>; ++ }; ++ ++ acc1: clock-controller@2098000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x02098000 0x1000>, <0x02008000 0x1000>; ++ }; ++ ++ acc2: clock-controller@20a8000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x020a8000 0x1000>, <0x02008000 0x1000>; ++ }; ++ ++ acc3: clock-controller@20b8000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x020b8000 0x1000>, <0x02008000 0x1000>; ++ }; ++ ++ saw0: regulator@2089000 { ++ compatible = "qcom,saw2"; ++ reg = <0x02089000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; ++ ++ saw1: regulator@2099000 { ++ compatible = "qcom,saw2"; ++ reg = <0x02099000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; ++ ++ saw2: regulator@20a9000 { ++ compatible = "qcom,saw2"; ++ reg = <0x020a9000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; ++ ++ saw3: regulator@20b9000 { ++ compatible = "qcom,saw2"; ++ reg = <0x020b9000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; ++ ++ gsbi7: gsbi@16600000 { ++ status = "disabled"; ++ compatible = "qcom,gsbi-v1.0.0"; ++ reg = <0x16600000 0x100>; ++ clocks = <&gcc GSBI7_H_CLK>; ++ clock-names = "iface"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ serial@16640000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x16640000 0x1000>, ++ <0x16600000 0x1000>; ++ interrupts = <0 158 0x0>; ++ clocks = <&gcc GSBI7_UART_CLK>, <&gcc GSBI7_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ }; ++ }; ++ ++ qcom,ssbi@500000 { ++ compatible = "qcom,ssbi"; ++ reg = <0x00500000 0x1000>; ++ qcom,controller-type = "pmic-arbiter"; ++ }; ++ ++ gcc: clock-controller@900000 { ++ compatible = "qcom,gcc-apq8064"; ++ reg = <0x00900000 0x4000>; ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ }; ++ }; ++}; +diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c +index bae617e..350fa8d 100644 +--- a/arch/arm/mach-qcom/board.c ++++ b/arch/arm/mach-qcom/board.c +@@ -15,9 +15,10 @@ + #include <asm/mach/arch.h> + + static const char * const qcom_dt_match[] __initconst = { ++ "qcom,apq8064", ++ "qcom,apq8074-dragonboard", + "qcom,msm8660-surf", + "qcom,msm8960-cdp", +- "qcom,apq8074-dragonboard", + NULL + }; + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0095-ARM-dts-qcom-Add-APQ8084-MTP-board-support.patch b/target/linux/ipq806x/patches/0095-ARM-dts-qcom-Add-APQ8084-MTP-board-support.patch new file mode 100644 index 0000000000..a797266766 --- /dev/null +++ b/target/linux/ipq806x/patches/0095-ARM-dts-qcom-Add-APQ8084-MTP-board-support.patch @@ -0,0 +1,43 @@ +From 9b06bc14f21463cf687ac6fdd07ca04b99c15466 Mon Sep 17 00:00:00 2001 +From: Georgi Djakov <gdjakov@mm-sol.com> +Date: Fri, 23 May 2014 18:12:30 +0300 +Subject: [PATCH 095/182] ARM: dts: qcom: Add APQ8084-MTP board support + +Add device-tree file for APQ8084-MTP board, which belongs +to the Snapdragon 805 family. + +Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/Makefile | 1 + + arch/arm/boot/dts/qcom-apq8084-mtp.dts | 6 ++++++ + 2 files changed, 7 insertions(+) + create mode 100644 arch/arm/boot/dts/qcom-apq8084-mtp.dts + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index ee3dfea..f2aeb95 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -234,6 +234,7 @@ dtb-$(CONFIG_ARCH_PRIMA2) += prima2-evb.dtb + dtb-$(CONFIG_ARCH_QCOM) += \ + qcom-apq8064-ifc6410.dtb \ + qcom-apq8074-dragonboard.dtb \ ++ qcom-apq8084-mtp.dtb \ + qcom-msm8660-surf.dtb \ + qcom-msm8960-cdp.dtb + dtb-$(CONFIG_ARCH_U8500) += ste-snowball.dtb \ +diff --git a/arch/arm/boot/dts/qcom-apq8084-mtp.dts b/arch/arm/boot/dts/qcom-apq8084-mtp.dts +new file mode 100644 +index 0000000..9dae387 +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-apq8084-mtp.dts +@@ -0,0 +1,6 @@ ++#include "qcom-apq8084.dtsi" ++ ++/ { ++ model = "Qualcomm APQ 8084-MTP"; ++ compatible = "qcom,apq8084-mtp", "qcom,apq8084"; ++}; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0096-ARM-dts-qcom-Add-APQ8084-SoC-support.patch b/target/linux/ipq806x/patches/0096-ARM-dts-qcom-Add-APQ8084-SoC-support.patch new file mode 100644 index 0000000000..87d70a54e5 --- /dev/null +++ b/target/linux/ipq806x/patches/0096-ARM-dts-qcom-Add-APQ8084-SoC-support.patch @@ -0,0 +1,216 @@ +From 8c52931421759b70fc37771be3390813a2a2f9f5 Mon Sep 17 00:00:00 2001 +From: Georgi Djakov <gdjakov@mm-sol.com> +Date: Fri, 23 May 2014 18:12:29 +0300 +Subject: [PATCH 096/182] ARM: dts: qcom: Add APQ8084 SoC support + +Add support for the Qualcomm Snapdragon 805 APQ8084 SoC. It is +used on APQ8084-MTP and other boards. + +Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-apq8084.dtsi | 179 +++++++++++++++++++++++++++++++++++ + arch/arm/mach-qcom/board.c | 1 + + 2 files changed, 180 insertions(+) + create mode 100644 arch/arm/boot/dts/qcom-apq8084.dtsi + +diff --git a/arch/arm/boot/dts/qcom-apq8084.dtsi b/arch/arm/boot/dts/qcom-apq8084.dtsi +new file mode 100644 +index 0000000..e3e009a +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-apq8084.dtsi +@@ -0,0 +1,179 @@ ++/dts-v1/; ++ ++#include "skeleton.dtsi" ++ ++/ { ++ model = "Qualcomm APQ 8084"; ++ compatible = "qcom,apq8084"; ++ interrupt-parent = <&intc>; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu@0 { ++ device_type = "cpu"; ++ compatible = "qcom,krait"; ++ reg = <0>; ++ enable-method = "qcom,kpss-acc-v2"; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc0>; ++ }; ++ ++ cpu@1 { ++ device_type = "cpu"; ++ compatible = "qcom,krait"; ++ reg = <1>; ++ enable-method = "qcom,kpss-acc-v2"; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc1>; ++ }; ++ ++ cpu@2 { ++ device_type = "cpu"; ++ compatible = "qcom,krait"; ++ reg = <2>; ++ enable-method = "qcom,kpss-acc-v2"; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc2>; ++ }; ++ ++ cpu@3 { ++ device_type = "cpu"; ++ compatible = "qcom,krait"; ++ reg = <3>; ++ enable-method = "qcom,kpss-acc-v2"; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc3>; ++ }; ++ ++ L2: l2-cache { ++ compatible = "qcom,arch-cache"; ++ cache-level = <2>; ++ qcom,saw = <&saw_l2>; ++ }; ++ }; ++ ++ cpu-pmu { ++ compatible = "qcom,krait-pmu"; ++ interrupts = <1 7 0xf04>; ++ }; ++ ++ timer { ++ compatible = "arm,armv7-timer"; ++ interrupts = <1 2 0xf08>, ++ <1 3 0xf08>, ++ <1 4 0xf08>, ++ <1 1 0xf08>; ++ clock-frequency = <19200000>; ++ }; ++ ++ soc: soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ compatible = "simple-bus"; ++ ++ intc: interrupt-controller@f9000000 { ++ compatible = "qcom,msm-qgic2"; ++ interrupt-controller; ++ #interrupt-cells = <3>; ++ reg = <0xf9000000 0x1000>, ++ <0xf9002000 0x1000>; ++ }; ++ ++ timer@f9020000 { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ compatible = "arm,armv7-timer-mem"; ++ reg = <0xf9020000 0x1000>; ++ clock-frequency = <19200000>; ++ ++ frame@f9021000 { ++ frame-number = <0>; ++ interrupts = <0 8 0x4>, ++ <0 7 0x4>; ++ reg = <0xf9021000 0x1000>, ++ <0xf9022000 0x1000>; ++ }; ++ ++ frame@f9023000 { ++ frame-number = <1>; ++ interrupts = <0 9 0x4>; ++ reg = <0xf9023000 0x1000>; ++ status = "disabled"; ++ }; ++ ++ frame@f9024000 { ++ frame-number = <2>; ++ interrupts = <0 10 0x4>; ++ reg = <0xf9024000 0x1000>; ++ status = "disabled"; ++ }; ++ ++ frame@f9025000 { ++ frame-number = <3>; ++ interrupts = <0 11 0x4>; ++ reg = <0xf9025000 0x1000>; ++ status = "disabled"; ++ }; ++ ++ frame@f9026000 { ++ frame-number = <4>; ++ interrupts = <0 12 0x4>; ++ reg = <0xf9026000 0x1000>; ++ status = "disabled"; ++ }; ++ ++ frame@f9027000 { ++ frame-number = <5>; ++ interrupts = <0 13 0x4>; ++ reg = <0xf9027000 0x1000>; ++ status = "disabled"; ++ }; ++ ++ frame@f9028000 { ++ frame-number = <6>; ++ interrupts = <0 14 0x4>; ++ reg = <0xf9028000 0x1000>; ++ status = "disabled"; ++ }; ++ }; ++ ++ saw_l2: regulator@f9012000 { ++ compatible = "qcom,saw2"; ++ reg = <0xf9012000 0x1000>; ++ regulator; ++ }; ++ ++ acc0: clock-controller@f9088000 { ++ compatible = "qcom,kpss-acc-v2"; ++ reg = <0xf9088000 0x1000>, ++ <0xf9008000 0x1000>; ++ }; ++ ++ acc1: clock-controller@f9098000 { ++ compatible = "qcom,kpss-acc-v2"; ++ reg = <0xf9098000 0x1000>, ++ <0xf9008000 0x1000>; ++ }; ++ ++ acc2: clock-controller@f90a8000 { ++ compatible = "qcom,kpss-acc-v2"; ++ reg = <0xf90a8000 0x1000>, ++ <0xf9008000 0x1000>; ++ }; ++ ++ acc3: clock-controller@f90b8000 { ++ compatible = "qcom,kpss-acc-v2"; ++ reg = <0xf90b8000 0x1000>, ++ <0xf9008000 0x1000>; ++ }; ++ ++ restart@fc4ab000 { ++ compatible = "qcom,pshold"; ++ reg = <0xfc4ab000 0x4>; ++ }; ++ }; ++}; +diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c +index 350fa8d..c437a99 100644 +--- a/arch/arm/mach-qcom/board.c ++++ b/arch/arm/mach-qcom/board.c +@@ -17,6 +17,7 @@ + static const char * const qcom_dt_match[] __initconst = { + "qcom,apq8064", + "qcom,apq8074-dragonboard", ++ "qcom,apq8084", + "qcom,msm8660-surf", + "qcom,msm8960-cdp", + NULL +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0097-ARM-debug-qcom-make-UART-address-selection-configura.patch b/target/linux/ipq806x/patches/0097-ARM-debug-qcom-make-UART-address-selection-configura.patch new file mode 100644 index 0000000000..2f9cad735e --- /dev/null +++ b/target/linux/ipq806x/patches/0097-ARM-debug-qcom-make-UART-address-selection-configura.patch @@ -0,0 +1,267 @@ +From 48167d4a55890a783cc8b1590bc8071253ae4b83 Mon Sep 17 00:00:00 2001 +From: "Ivan T. Ivanov" <iivanov@mm-sol.com> +Date: Mon, 14 Apr 2014 16:47:34 +0300 +Subject: [PATCH 097/182] ARM: debug: qcom: make UART address selection + configuration option + +Separate Qualcomm low-level debugging UART to two options. + +DEBUG_MSM_UART is used in earlier non-multi platform arches, +like MSM7X00A, QSD8X50 and MSM7X30. + +DEBUG_QCOM_UARTDM is used in multi-plafrom arches and have +embedded data mover. + +Make DEBUG_UART_PHYS and DEBUG_UART_BASE user adjustable by +Kconfig menu. + +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Reviewed-by: Stephen Boyd <sboyd@codeaurora.org> +Tested-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/Kconfig.debug | 81 +++++++++++++++++------------------------- + arch/arm/include/debug/msm.S | 46 +++--------------------- + arch/arm/mach-msm/Kconfig | 3 -- + 3 files changed, 38 insertions(+), 92 deletions(-) + +diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug +index 4491c7b..1a5895d 100644 +--- a/arch/arm/Kconfig.debug ++++ b/arch/arm/Kconfig.debug +@@ -353,56 +353,39 @@ choice + Say Y here if you want kernel low-level debugging support + on MMP UART3. + +- config DEBUG_MSM_UART1 +- bool "Kernel low-level debugging messages via MSM UART1" +- depends on ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50 +- select DEBUG_MSM_UART ++ config DEBUG_MSM_UART ++ bool "Kernel low-level debugging messages via MSM UART" ++ depends on ARCH_MSM + help + Say Y here if you want the debug print routines to direct +- their output to the first serial port on MSM devices. ++ their output to the serial port on MSM devices. + +- config DEBUG_MSM_UART2 +- bool "Kernel low-level debugging messages via MSM UART2" +- depends on ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50 +- select DEBUG_MSM_UART +- help +- Say Y here if you want the debug print routines to direct +- their output to the second serial port on MSM devices. ++ ARCH DEBUG_UART_PHYS DEBUG_UART_BASE # ++ MSM7X00A, QSD8X50 0xa9a00000 0xe1000000 UART1 ++ MSM7X00A, QSD8X50 0xa9b00000 0xe1000000 UART2 ++ MSM7X00A, QSD8X50 0xa9c00000 0xe1000000 UART3 + +- config DEBUG_MSM_UART3 +- bool "Kernel low-level debugging messages via MSM UART3" +- depends on ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50 +- select DEBUG_MSM_UART +- help +- Say Y here if you want the debug print routines to direct +- their output to the third serial port on MSM devices. ++ MSM7X30 0xaca00000 0xe1000000 UART1 ++ MSM7X30 0xacb00000 0xe1000000 UART2 ++ MSM7X30 0xacc00000 0xe1000000 UART3 + +- config DEBUG_MSM8660_UART +- bool "Kernel low-level debugging messages via MSM 8660 UART" +- depends on ARCH_MSM8X60 +- select MSM_HAS_DEBUG_UART_HS +- select DEBUG_MSM_UART +- help +- Say Y here if you want the debug print routines to direct +- their output to the serial port on MSM 8660 devices. ++ Please adjust DEBUG_UART_PHYS and DEBUG_UART_BASE configuration ++ options based on your needs. + +- config DEBUG_MSM8960_UART +- bool "Kernel low-level debugging messages via MSM 8960 UART" +- depends on ARCH_MSM8960 +- select MSM_HAS_DEBUG_UART_HS +- select DEBUG_MSM_UART ++ config DEBUG_QCOM_UARTDM ++ bool "Kernel low-level debugging messages via QCOM UARTDM" ++ depends on ARCH_QCOM + help + Say Y here if you want the debug print routines to direct +- their output to the serial port on MSM 8960 devices. ++ their output to the serial port on Qualcomm devices. + +- config DEBUG_MSM8974_UART +- bool "Kernel low-level debugging messages via MSM 8974 UART" +- depends on ARCH_MSM8974 +- select MSM_HAS_DEBUG_UART_HS +- select DEBUG_MSM_UART +- help +- Say Y here if you want the debug print routines to direct +- their output to the serial port on MSM 8974 devices. ++ ARCH DEBUG_UART_PHYS DEBUG_UART_BASE ++ MSM8X60 0x19c40000 0xf0040000 ++ MSM8960 0x16440000 0xf0040000 ++ MSM8974 0xf991e000 0xfa71e000 ++ ++ Please adjust DEBUG_UART_PHYS and DEBUG_UART_BASE configuration ++ options based on your needs. + + config DEBUG_MVEBU_UART + bool "Kernel low-level debugging messages via MVEBU UART (old bootloaders)" +@@ -954,10 +937,6 @@ config DEBUG_STI_UART + bool + depends on ARCH_STI + +-config DEBUG_MSM_UART +- bool +- depends on ARCH_MSM || ARCH_QCOM +- + config DEBUG_LL_INCLUDE + string + default "debug/8250.S" if DEBUG_LL_UART_8250 || DEBUG_UART_8250 +@@ -975,7 +954,7 @@ config DEBUG_LL_INCLUDE + DEBUG_IMX53_UART ||\ + DEBUG_IMX6Q_UART || \ + DEBUG_IMX6SL_UART +- default "debug/msm.S" if DEBUG_MSM_UART ++ default "debug/msm.S" if DEBUG_MSM_UART || DEBUG_QCOM_UARTDM + default "debug/omap2plus.S" if DEBUG_OMAP2PLUS_UART + default "debug/sirf.S" if DEBUG_SIRFPRIMA2_UART1 || DEBUG_SIRFMARCO_UART1 + default "debug/sti.S" if DEBUG_STI_UART +@@ -1039,6 +1018,7 @@ config DEBUG_UART_PHYS + default 0x80074000 if DEBUG_IMX28_UART + default 0x808c0000 if ARCH_EP93XX + default 0x90020000 if DEBUG_NSPIRE_CLASSIC_UART || DEBUG_NSPIRE_CX_UART ++ default 0xa9a00000 if DEBUG_MSM_UART + default 0xb0090000 if DEBUG_VEXPRESS_UART0_CRX + default 0xc0013000 if DEBUG_U300_UART + default 0xc8000000 if ARCH_IXP4XX && !CPU_BIG_ENDIAN +@@ -1054,6 +1034,7 @@ config DEBUG_UART_PHYS + ARCH_ORION5X + default 0xf7fc9000 if DEBUG_BERLIN_UART + default 0xf8b00000 if DEBUG_HI3716_UART ++ default 0xf991e000 if DEBUG_QCOM_UARTDM + default 0xfcb00000 if DEBUG_HI3620_UART + default 0xfe800000 if ARCH_IOP32X + default 0xffc02000 if DEBUG_SOCFPGA_UART +@@ -1062,11 +1043,13 @@ config DEBUG_UART_PHYS + default 0xfffff700 if ARCH_IOP33X + depends on DEBUG_LL_UART_8250 || DEBUG_LL_UART_PL01X || \ + DEBUG_LL_UART_EFM32 || \ +- DEBUG_UART_8250 || DEBUG_UART_PL01X ++ DEBUG_UART_8250 || DEBUG_UART_PL01X || \ ++ DEBUG_MSM_UART || DEBUG_QCOM_UARTDM + + config DEBUG_UART_VIRT + hex "Virtual base address of debug UART" + default 0xe0010fe0 if ARCH_RPC ++ default 0xe1000000 if DEBUG_MSM_UART + default 0xf0000be0 if ARCH_EBSA110 + default 0xf0009000 if DEBUG_CNS3XXX + default 0xf01fb000 if DEBUG_NOMADIK_UART +@@ -1081,6 +1064,7 @@ config DEBUG_UART_VIRT + default 0xf7fc9000 if DEBUG_BERLIN_UART + default 0xf8009000 if DEBUG_VEXPRESS_UART0_CA9 + default 0xf8090000 if DEBUG_VEXPRESS_UART0_RS1 ++ default 0xfa71e000 if DEBUG_QCOM_UARTDM + default 0xfb009000 if DEBUG_REALVIEW_STD_PORT + default 0xfb10c000 if DEBUG_REALVIEW_PB1176_PORT + default 0xfd000000 if ARCH_SPEAR3XX || ARCH_SPEAR6XX +@@ -1120,7 +1104,8 @@ config DEBUG_UART_VIRT + default 0xff003000 if DEBUG_U300_UART + default DEBUG_UART_PHYS if !MMU + depends on DEBUG_LL_UART_8250 || DEBUG_LL_UART_PL01X || \ +- DEBUG_UART_8250 || DEBUG_UART_PL01X ++ DEBUG_UART_8250 || DEBUG_UART_PL01X || \ ++ DEBUG_MSM_UART || DEBUG_QCOM_UARTDM + + config DEBUG_UART_8250_SHIFT + int "Register offset shift for the 8250 debug UART" +diff --git a/arch/arm/include/debug/msm.S b/arch/arm/include/debug/msm.S +index 9d653d4..9ef5761 100644 +--- a/arch/arm/include/debug/msm.S ++++ b/arch/arm/include/debug/msm.S +@@ -15,51 +15,15 @@ + * + */ + +-#if defined(CONFIG_ARCH_MSM7X00A) || defined(CONFIG_ARCH_QSD8X50) +-#define MSM_UART1_PHYS 0xA9A00000 +-#define MSM_UART2_PHYS 0xA9B00000 +-#define MSM_UART3_PHYS 0xA9C00000 +-#elif defined(CONFIG_ARCH_MSM7X30) +-#define MSM_UART1_PHYS 0xACA00000 +-#define MSM_UART2_PHYS 0xACB00000 +-#define MSM_UART3_PHYS 0xACC00000 +-#endif +- +-#if defined(CONFIG_DEBUG_MSM_UART1) +-#define MSM_DEBUG_UART_BASE 0xE1000000 +-#define MSM_DEBUG_UART_PHYS MSM_UART1_PHYS +-#elif defined(CONFIG_DEBUG_MSM_UART2) +-#define MSM_DEBUG_UART_BASE 0xE1000000 +-#define MSM_DEBUG_UART_PHYS MSM_UART2_PHYS +-#elif defined(CONFIG_DEBUG_MSM_UART3) +-#define MSM_DEBUG_UART_BASE 0xE1000000 +-#define MSM_DEBUG_UART_PHYS MSM_UART3_PHYS +-#endif +- +-#ifdef CONFIG_DEBUG_MSM8660_UART +-#define MSM_DEBUG_UART_BASE 0xF0040000 +-#define MSM_DEBUG_UART_PHYS 0x19C40000 +-#endif +- +-#ifdef CONFIG_DEBUG_MSM8960_UART +-#define MSM_DEBUG_UART_BASE 0xF0040000 +-#define MSM_DEBUG_UART_PHYS 0x16440000 +-#endif +- +-#ifdef CONFIG_DEBUG_MSM8974_UART +-#define MSM_DEBUG_UART_BASE 0xFA71E000 +-#define MSM_DEBUG_UART_PHYS 0xF991E000 +-#endif +- + .macro addruart, rp, rv, tmp +-#ifdef MSM_DEBUG_UART_PHYS +- ldr \rp, =MSM_DEBUG_UART_PHYS +- ldr \rv, =MSM_DEBUG_UART_BASE ++#ifdef CONFIG_DEBUG_UART_PHYS ++ ldr \rp, =CONFIG_DEBUG_UART_PHYS ++ ldr \rv, =CONFIG_DEBUG_UART_VIRT + #endif + .endm + + .macro senduart, rd, rx +-#ifdef CONFIG_MSM_HAS_DEBUG_UART_HS ++#ifdef CONFIG_DEBUG_QCOM_UARTDM + @ Write the 1 character to UARTDM_TF + str \rd, [\rx, #0x70] + #else +@@ -68,7 +32,7 @@ + .endm + + .macro waituart, rd, rx +-#ifdef CONFIG_MSM_HAS_DEBUG_UART_HS ++#ifdef CONFIG_DEBUG_QCOM_UARTDM + @ check for TX_EMT in UARTDM_SR + ldr \rd, [\rx, #0x08] + tst \rd, #0x08 +diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig +index a7f959e..9b26976 100644 +--- a/arch/arm/mach-msm/Kconfig ++++ b/arch/arm/mach-msm/Kconfig +@@ -42,9 +42,6 @@ config ARCH_QSD8X50 + + endchoice + +-config MSM_HAS_DEBUG_UART_HS +- bool +- + config MSM_SOC_REV_A + bool + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0098-ARM-debug-qcom-add-UART-addresses-to-Kconfig-help-fo.patch b/target/linux/ipq806x/patches/0098-ARM-debug-qcom-add-UART-addresses-to-Kconfig-help-fo.patch new file mode 100644 index 0000000000..fdb5c67e22 --- /dev/null +++ b/target/linux/ipq806x/patches/0098-ARM-debug-qcom-add-UART-addresses-to-Kconfig-help-fo.patch @@ -0,0 +1,30 @@ +From 0a551e04e9a36ca1f8332071787b0e26bc5f8ecc Mon Sep 17 00:00:00 2001 +From: Georgi Djakov <gdjakov@mm-sol.com> +Date: Fri, 23 May 2014 18:12:32 +0300 +Subject: [PATCH 098/182] ARM: debug: qcom: add UART addresses to Kconfig help + for APQ8084 + +Add information about the APQ8084 debug UART physical and virtual +addresses in the DEBUG_QCOM_UARTDM Kconfig help section. + +Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/Kconfig.debug | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug +index 1a5895d..7820af1 100644 +--- a/arch/arm/Kconfig.debug ++++ b/arch/arm/Kconfig.debug +@@ -380,6 +380,7 @@ choice + their output to the serial port on Qualcomm devices. + + ARCH DEBUG_UART_PHYS DEBUG_UART_BASE ++ APQ8084 0xf995e000 0xfa75e000 + MSM8X60 0x19c40000 0xf0040000 + MSM8960 0x16440000 0xf0040000 + MSM8974 0xf991e000 0xfa71e000 +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0099-ARM-qcom-Enable-ARM_AMBA-option-for-Qualcomm-SOCs.patch b/target/linux/ipq806x/patches/0099-ARM-qcom-Enable-ARM_AMBA-option-for-Qualcomm-SOCs.patch new file mode 100644 index 0000000000..4e603eaf5e --- /dev/null +++ b/target/linux/ipq806x/patches/0099-ARM-qcom-Enable-ARM_AMBA-option-for-Qualcomm-SOCs.patch @@ -0,0 +1,31 @@ +From b6ee2071ec3af3ec90d93b8b70aa17d741590148 Mon Sep 17 00:00:00 2001 +From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> +Date: Thu, 15 May 2014 11:08:55 +0100 +Subject: [PATCH 099/182] ARM: qcom: Enable ARM_AMBA option for Qualcomm SOCs. + +As some of the IPs on Qualcomm SOCs are based on ARM PrimeCell IPs. +For example SDCC controller is PrimeCell MCI pl180. Adding this option will +give flexibility to reuse the existing drivers as it is without major +modifications. + +Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/mach-qcom/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig +index 6440c11..63502cc 100644 +--- a/arch/arm/mach-qcom/Kconfig ++++ b/arch/arm/mach-qcom/Kconfig +@@ -2,6 +2,7 @@ config ARCH_QCOM + bool "Qualcomm Support" if ARCH_MULTI_V7 + select ARCH_REQUIRE_GPIOLIB + select ARM_GIC ++ select ARM_AMBA + select CLKSRC_OF + select GENERIC_CLOCKEVENTS + select HAVE_SMP +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0100-clk-qcom-Fix-msm8660-GCC-probe.patch b/target/linux/ipq806x/patches/0100-clk-qcom-Fix-msm8660-GCC-probe.patch new file mode 100644 index 0000000000..1fbd80602a --- /dev/null +++ b/target/linux/ipq806x/patches/0100-clk-qcom-Fix-msm8660-GCC-probe.patch @@ -0,0 +1,42 @@ +From d54996ddc69b69fe21f776e1de1eace04bca01c3 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 16 May 2014 10:05:14 -0700 +Subject: [PATCH 100/182] clk: qcom: Fix msm8660 GCC probe + +When consolidating the msm8660 GCC probe code I forgot to keep +around these temporary clock registrations. Put them back so the +clock tree is not entirely orphaned. + +Fixes: 49fc825f0cc2 (clk: qcom: Consolidate common probe code) +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +--- + drivers/clk/qcom/gcc-msm8660.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/drivers/clk/qcom/gcc-msm8660.c b/drivers/clk/qcom/gcc-msm8660.c +index 44bc6fa..0c4b727 100644 +--- a/drivers/clk/qcom/gcc-msm8660.c ++++ b/drivers/clk/qcom/gcc-msm8660.c +@@ -2718,6 +2718,18 @@ MODULE_DEVICE_TABLE(of, gcc_msm8660_match_table); + + static int gcc_msm8660_probe(struct platform_device *pdev) + { ++ struct clk *clk; ++ struct device *dev = &pdev->dev; ++ ++ /* Temporary until RPM clocks supported */ ++ clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 27000000); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ + return qcom_cc_probe(pdev, &gcc_msm8660_desc); + } + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0101-clk-qcom-Fix-blsp2_ahb_clk-register-offset.patch b/target/linux/ipq806x/patches/0101-clk-qcom-Fix-blsp2_ahb_clk-register-offset.patch new file mode 100644 index 0000000000..9555e6b803 --- /dev/null +++ b/target/linux/ipq806x/patches/0101-clk-qcom-Fix-blsp2_ahb_clk-register-offset.patch @@ -0,0 +1,30 @@ +From b11c02becb4dadf5c375fef4b98f92af67106f18 Mon Sep 17 00:00:00 2001 +From: Georgi Djakov <gdjakov@mm-sol.com> +Date: Tue, 20 May 2014 19:50:54 +0300 +Subject: [PATCH 101/182] clk: qcom: Fix blsp2_ahb_clk register offset + +The address of the blsp2_ahb_clk register is incorrect. Fix it. + +Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com> +Reviewed-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +--- + drivers/clk/qcom/gcc-msm8974.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/clk/qcom/gcc-msm8974.c b/drivers/clk/qcom/gcc-msm8974.c +index 0d1edc1..7a420fc 100644 +--- a/drivers/clk/qcom/gcc-msm8974.c ++++ b/drivers/clk/qcom/gcc-msm8974.c +@@ -1341,7 +1341,7 @@ static struct clk_branch gcc_blsp1_uart6_apps_clk = { + }; + + static struct clk_branch gcc_blsp2_ahb_clk = { +- .halt_reg = 0x05c4, ++ .halt_reg = 0x0944, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1484, +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0104-clk-qcom-Return-highest-rate-when-round_rate-exceeds.patch b/target/linux/ipq806x/patches/0104-clk-qcom-Return-highest-rate-when-round_rate-exceeds.patch new file mode 100644 index 0000000000..8c959a5372 --- /dev/null +++ b/target/linux/ipq806x/patches/0104-clk-qcom-Return-highest-rate-when-round_rate-exceeds.patch @@ -0,0 +1,36 @@ +From 6417bb8469eb495d7f4e4b6b0ac08cbc1b8606cb Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 16 May 2014 16:07:10 -0700 +Subject: [PATCH 104/182] clk: qcom: Return highest rate when round_rate() + exceeds plan + +Some drivers may want to call clk_set_rate() with a very large +number to force the clock to go as fast as it possibly can +without having to know the range between the highest rate and +second highest rate. Add support for this by defaulting to the +highest rate in the frequency table if we can't find a frequency +greater than what is requested. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +--- + drivers/clk/qcom/clk-rcg2.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c +index 0996a3a..cbecaec 100644 +--- a/drivers/clk/qcom/clk-rcg2.c ++++ b/drivers/clk/qcom/clk-rcg2.c +@@ -181,7 +181,8 @@ struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate) + if (rate <= f->freq) + return f; + +- return NULL; ++ /* Default to our fastest rate */ ++ return f - 1; + } + + static long _freq_tbl_determine_rate(struct clk_hw *hw, +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0105-clk-qcom-Support-display-RCG-clocks.patch b/target/linux/ipq806x/patches/0105-clk-qcom-Support-display-RCG-clocks.patch new file mode 100644 index 0000000000..b95562956c --- /dev/null +++ b/target/linux/ipq806x/patches/0105-clk-qcom-Support-display-RCG-clocks.patch @@ -0,0 +1,378 @@ +From 3123079878e29eb8c541111e30de4d1bb42ac6f9 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 16 May 2014 16:07:11 -0700 +Subject: [PATCH 105/182] clk: qcom: Support display RCG clocks + +Add support for the DSI/EDP/HDMI RCG clocks. With the proper +display driver in place this should allow us to support display +clocks on msm8974 based devices. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +--- + drivers/clk/qcom/clk-rcg.h | 3 + + drivers/clk/qcom/clk-rcg2.c | 299 ++++++++++++++++++++++++++++++++++++++++--- + 2 files changed, 287 insertions(+), 15 deletions(-) + +diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h +index 1d6b6de..b9ec11d 100644 +--- a/drivers/clk/qcom/clk-rcg.h ++++ b/drivers/clk/qcom/clk-rcg.h +@@ -155,5 +155,8 @@ struct clk_rcg2 { + #define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr) + + extern const struct clk_ops clk_rcg2_ops; ++extern const struct clk_ops clk_edp_pixel_ops; ++extern const struct clk_ops clk_byte_ops; ++extern const struct clk_ops clk_pixel_ops; + + #endif +diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c +index cbecaec..cd185d5 100644 +--- a/drivers/clk/qcom/clk-rcg2.c ++++ b/drivers/clk/qcom/clk-rcg2.c +@@ -19,6 +19,7 @@ + #include <linux/clk-provider.h> + #include <linux/delay.h> + #include <linux/regmap.h> ++#include <linux/math64.h> + + #include <asm/div64.h> + +@@ -225,31 +226,25 @@ static long clk_rcg2_determine_rate(struct clk_hw *hw, unsigned long rate, + return _freq_tbl_determine_rate(hw, rcg->freq_tbl, rate, p_rate, p); + } + +-static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate) ++static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) + { +- struct clk_rcg2 *rcg = to_clk_rcg2(hw); +- const struct freq_tbl *f; + u32 cfg, mask; + int ret; + +- f = find_freq(rcg->freq_tbl, rate); +- if (!f) +- return -EINVAL; +- + if (rcg->mnd_width && f->n) { + mask = BIT(rcg->mnd_width) - 1; +- ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + M_REG, +- mask, f->m); ++ ret = regmap_update_bits(rcg->clkr.regmap, ++ rcg->cmd_rcgr + M_REG, mask, f->m); + if (ret) + return ret; + +- ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + N_REG, +- mask, ~(f->n - f->m)); ++ ret = regmap_update_bits(rcg->clkr.regmap, ++ rcg->cmd_rcgr + N_REG, mask, ~(f->n - f->m)); + if (ret) + return ret; + +- ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + D_REG, +- mask, ~f->n); ++ ret = regmap_update_bits(rcg->clkr.regmap, ++ rcg->cmd_rcgr + D_REG, mask, ~f->n); + if (ret) + return ret; + } +@@ -260,14 +255,26 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate) + cfg |= rcg->parent_map[f->src] << CFG_SRC_SEL_SHIFT; + if (rcg->mnd_width && f->n) + cfg |= CFG_MODE_DUAL_EDGE; +- ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, mask, +- cfg); ++ ret = regmap_update_bits(rcg->clkr.regmap, ++ rcg->cmd_rcgr + CFG_REG, mask, cfg); + if (ret) + return ret; + + return update_config(rcg); + } + ++static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate) ++{ ++ struct clk_rcg2 *rcg = to_clk_rcg2(hw); ++ const struct freq_tbl *f; ++ ++ f = find_freq(rcg->freq_tbl, rate); ++ if (!f) ++ return -EINVAL; ++ ++ return clk_rcg2_configure(rcg, f); ++} ++ + static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) + { +@@ -290,3 +297,265 @@ const struct clk_ops clk_rcg2_ops = { + .set_rate_and_parent = clk_rcg2_set_rate_and_parent, + }; + EXPORT_SYMBOL_GPL(clk_rcg2_ops); ++ ++struct frac_entry { ++ int num; ++ int den; ++}; ++ ++static const struct frac_entry frac_table_675m[] = { /* link rate of 270M */ ++ { 52, 295 }, /* 119 M */ ++ { 11, 57 }, /* 130.25 M */ ++ { 63, 307 }, /* 138.50 M */ ++ { 11, 50 }, /* 148.50 M */ ++ { 47, 206 }, /* 154 M */ ++ { 31, 100 }, /* 205.25 M */ ++ { 107, 269 }, /* 268.50 M */ ++ { }, ++}; ++ ++static struct frac_entry frac_table_810m[] = { /* Link rate of 162M */ ++ { 31, 211 }, /* 119 M */ ++ { 32, 199 }, /* 130.25 M */ ++ { 63, 307 }, /* 138.50 M */ ++ { 11, 60 }, /* 148.50 M */ ++ { 50, 263 }, /* 154 M */ ++ { 31, 120 }, /* 205.25 M */ ++ { 119, 359 }, /* 268.50 M */ ++ { }, ++}; ++ ++static int clk_edp_pixel_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct clk_rcg2 *rcg = to_clk_rcg2(hw); ++ struct freq_tbl f = *rcg->freq_tbl; ++ const struct frac_entry *frac; ++ int delta = 100000; ++ s64 src_rate = parent_rate; ++ s64 request; ++ u32 mask = BIT(rcg->hid_width) - 1; ++ u32 hid_div; ++ ++ if (src_rate == 810000000) ++ frac = frac_table_810m; ++ else ++ frac = frac_table_675m; ++ ++ for (; frac->num; frac++) { ++ request = rate; ++ request *= frac->den; ++ request = div_s64(request, frac->num); ++ if ((src_rate < (request - delta)) || ++ (src_rate > (request + delta))) ++ continue; ++ ++ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, ++ &hid_div); ++ f.pre_div = hid_div; ++ f.pre_div >>= CFG_SRC_DIV_SHIFT; ++ f.pre_div &= mask; ++ f.m = frac->num; ++ f.n = frac->den; ++ ++ return clk_rcg2_configure(rcg, &f); ++ } ++ ++ return -EINVAL; ++} ++ ++static int clk_edp_pixel_set_rate_and_parent(struct clk_hw *hw, ++ unsigned long rate, unsigned long parent_rate, u8 index) ++{ ++ /* Parent index is set statically in frequency table */ ++ return clk_edp_pixel_set_rate(hw, rate, parent_rate); ++} ++ ++static long clk_edp_pixel_determine_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *p_rate, struct clk **p) ++{ ++ struct clk_rcg2 *rcg = to_clk_rcg2(hw); ++ const struct freq_tbl *f = rcg->freq_tbl; ++ const struct frac_entry *frac; ++ int delta = 100000; ++ s64 src_rate = *p_rate; ++ s64 request; ++ u32 mask = BIT(rcg->hid_width) - 1; ++ u32 hid_div; ++ ++ /* Force the correct parent */ ++ *p = clk_get_parent_by_index(hw->clk, f->src); ++ ++ if (src_rate == 810000000) ++ frac = frac_table_810m; ++ else ++ frac = frac_table_675m; ++ ++ for (; frac->num; frac++) { ++ request = rate; ++ request *= frac->den; ++ request = div_s64(request, frac->num); ++ if ((src_rate < (request - delta)) || ++ (src_rate > (request + delta))) ++ continue; ++ ++ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, ++ &hid_div); ++ hid_div >>= CFG_SRC_DIV_SHIFT; ++ hid_div &= mask; ++ ++ return calc_rate(src_rate, frac->num, frac->den, !!frac->den, ++ hid_div); ++ } ++ ++ return -EINVAL; ++} ++ ++const struct clk_ops clk_edp_pixel_ops = { ++ .is_enabled = clk_rcg2_is_enabled, ++ .get_parent = clk_rcg2_get_parent, ++ .set_parent = clk_rcg2_set_parent, ++ .recalc_rate = clk_rcg2_recalc_rate, ++ .set_rate = clk_edp_pixel_set_rate, ++ .set_rate_and_parent = clk_edp_pixel_set_rate_and_parent, ++ .determine_rate = clk_edp_pixel_determine_rate, ++}; ++EXPORT_SYMBOL_GPL(clk_edp_pixel_ops); ++ ++static long clk_byte_determine_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *p_rate, struct clk **p) ++{ ++ struct clk_rcg2 *rcg = to_clk_rcg2(hw); ++ const struct freq_tbl *f = rcg->freq_tbl; ++ unsigned long parent_rate, div; ++ u32 mask = BIT(rcg->hid_width) - 1; ++ ++ if (rate == 0) ++ return -EINVAL; ++ ++ *p = clk_get_parent_by_index(hw->clk, f->src); ++ *p_rate = parent_rate = __clk_round_rate(*p, rate); ++ ++ div = DIV_ROUND_UP((2 * parent_rate), rate) - 1; ++ div = min_t(u32, div, mask); ++ ++ return calc_rate(parent_rate, 0, 0, 0, div); ++} ++ ++static int clk_byte_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct clk_rcg2 *rcg = to_clk_rcg2(hw); ++ struct freq_tbl f = *rcg->freq_tbl; ++ unsigned long div; ++ u32 mask = BIT(rcg->hid_width) - 1; ++ ++ div = DIV_ROUND_UP((2 * parent_rate), rate) - 1; ++ div = min_t(u32, div, mask); ++ ++ f.pre_div = div; ++ ++ return clk_rcg2_configure(rcg, &f); ++} ++ ++static int clk_byte_set_rate_and_parent(struct clk_hw *hw, ++ unsigned long rate, unsigned long parent_rate, u8 index) ++{ ++ /* Parent index is set statically in frequency table */ ++ return clk_byte_set_rate(hw, rate, parent_rate); ++} ++ ++const struct clk_ops clk_byte_ops = { ++ .is_enabled = clk_rcg2_is_enabled, ++ .get_parent = clk_rcg2_get_parent, ++ .set_parent = clk_rcg2_set_parent, ++ .recalc_rate = clk_rcg2_recalc_rate, ++ .set_rate = clk_byte_set_rate, ++ .set_rate_and_parent = clk_byte_set_rate_and_parent, ++ .determine_rate = clk_byte_determine_rate, ++}; ++EXPORT_SYMBOL_GPL(clk_byte_ops); ++ ++static const struct frac_entry frac_table_pixel[] = { ++ { 3, 8 }, ++ { 2, 9 }, ++ { 4, 9 }, ++ { 1, 1 }, ++ { } ++}; ++ ++static long clk_pixel_determine_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *p_rate, struct clk **p) ++{ ++ struct clk_rcg2 *rcg = to_clk_rcg2(hw); ++ unsigned long request, src_rate; ++ int delta = 100000; ++ const struct freq_tbl *f = rcg->freq_tbl; ++ const struct frac_entry *frac = frac_table_pixel; ++ struct clk *parent = *p = clk_get_parent_by_index(hw->clk, f->src); ++ ++ for (; frac->num; frac++) { ++ request = (rate * frac->den) / frac->num; ++ ++ src_rate = __clk_round_rate(parent, request); ++ if ((src_rate < (request - delta)) || ++ (src_rate > (request + delta))) ++ continue; ++ ++ *p_rate = src_rate; ++ return (src_rate * frac->num) / frac->den; ++ } ++ ++ return -EINVAL; ++} ++ ++static int clk_pixel_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct clk_rcg2 *rcg = to_clk_rcg2(hw); ++ struct freq_tbl f = *rcg->freq_tbl; ++ const struct frac_entry *frac = frac_table_pixel; ++ unsigned long request, src_rate; ++ int delta = 100000; ++ u32 mask = BIT(rcg->hid_width) - 1; ++ u32 hid_div; ++ struct clk *parent = clk_get_parent_by_index(hw->clk, f.src); ++ ++ for (; frac->num; frac++) { ++ request = (rate * frac->den) / frac->num; ++ ++ src_rate = __clk_round_rate(parent, request); ++ if ((src_rate < (request - delta)) || ++ (src_rate > (request + delta))) ++ continue; ++ ++ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, ++ &hid_div); ++ f.pre_div = hid_div; ++ f.pre_div >>= CFG_SRC_DIV_SHIFT; ++ f.pre_div &= mask; ++ f.m = frac->num; ++ f.n = frac->den; ++ ++ return clk_rcg2_configure(rcg, &f); ++ } ++ return -EINVAL; ++} ++ ++static int clk_pixel_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate, u8 index) ++{ ++ /* Parent index is set statically in frequency table */ ++ return clk_pixel_set_rate(hw, rate, parent_rate); ++} ++ ++const struct clk_ops clk_pixel_ops = { ++ .is_enabled = clk_rcg2_is_enabled, ++ .get_parent = clk_rcg2_get_parent, ++ .set_parent = clk_rcg2_set_parent, ++ .recalc_rate = clk_rcg2_recalc_rate, ++ .set_rate = clk_pixel_set_rate, ++ .set_rate_and_parent = clk_pixel_set_rate_and_parent, ++ .determine_rate = clk_pixel_determine_rate, ++}; ++EXPORT_SYMBOL_GPL(clk_pixel_ops); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0106-clk-qcom-Properly-support-display-clocks-on-msm8974.patch b/target/linux/ipq806x/patches/0106-clk-qcom-Properly-support-display-clocks-on-msm8974.patch new file mode 100644 index 0000000000..ce9499afb1 --- /dev/null +++ b/target/linux/ipq806x/patches/0106-clk-qcom-Properly-support-display-clocks-on-msm8974.patch @@ -0,0 +1,259 @@ +From 4ccbe584ecb970f86bab58c0ca93998cccc9e810 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 16 May 2014 16:07:12 -0700 +Subject: [PATCH 106/182] clk: qcom: Properly support display clocks on + msm8974 + +The display clocks all source from dedicated phy PLLs within their +respective multimedia hardware block. Hook up these PLLs to the +display clocks with the appropriate parent mappings, clock flags, +and the appropriate clock ops. This should allow the display +clocks to work once the appropriate phy PLL driver registers their +PLL clocks. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +--- + drivers/clk/qcom/mmcc-msm8974.c | 105 ++++++++++++++++++++------------------- + 1 file changed, 54 insertions(+), 51 deletions(-) + +diff --git a/drivers/clk/qcom/mmcc-msm8974.c b/drivers/clk/qcom/mmcc-msm8974.c +index 62200bb..c65b905 100644 +--- a/drivers/clk/qcom/mmcc-msm8974.c ++++ b/drivers/clk/qcom/mmcc-msm8974.c +@@ -41,9 +41,11 @@ + #define P_EDPVCO 3 + #define P_GPLL1 4 + #define P_DSI0PLL 4 ++#define P_DSI0PLL_BYTE 4 + #define P_MMPLL2 4 + #define P_MMPLL3 4 + #define P_DSI1PLL 5 ++#define P_DSI1PLL_BYTE 5 + + static const u8 mmcc_xo_mmpll0_mmpll1_gpll0_map[] = { + [P_XO] = 0, +@@ -161,6 +163,24 @@ static const char *mmcc_xo_dsi_hdmi_edp_gpll0[] = { + "dsi1pll", + }; + ++static const u8 mmcc_xo_dsibyte_hdmi_edp_gpll0_map[] = { ++ [P_XO] = 0, ++ [P_EDPLINK] = 4, ++ [P_HDMIPLL] = 3, ++ [P_GPLL0] = 5, ++ [P_DSI0PLL_BYTE] = 1, ++ [P_DSI1PLL_BYTE] = 2, ++}; ++ ++static const char *mmcc_xo_dsibyte_hdmi_edp_gpll0[] = { ++ "xo", ++ "edp_link_clk", ++ "hdmipll", ++ "gpll0_vote", ++ "dsi0pllbyte", ++ "dsi1pllbyte", ++}; ++ + #define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } + + static struct clk_pll mmpll0 = { +@@ -500,15 +520,8 @@ static struct clk_rcg2 jpeg2_clk_src = { + }, + }; + +-static struct freq_tbl ftbl_mdss_pclk0_clk[] = { +- F(125000000, P_DSI0PLL, 2, 0, 0), +- F(250000000, P_DSI0PLL, 1, 0, 0), +- { } +-}; +- +-static struct freq_tbl ftbl_mdss_pclk1_clk[] = { +- F(125000000, P_DSI1PLL, 2, 0, 0), +- F(250000000, P_DSI1PLL, 1, 0, 0), ++static struct freq_tbl pixel_freq_tbl[] = { ++ { .src = P_DSI0PLL }, + { } + }; + +@@ -517,12 +530,13 @@ static struct clk_rcg2 pclk0_clk_src = { + .mnd_width = 8, + .hid_width = 5, + .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, +- .freq_tbl = ftbl_mdss_pclk0_clk, ++ .freq_tbl = pixel_freq_tbl, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pclk0_clk_src", + .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, + .num_parents = 6, +- .ops = &clk_rcg2_ops, ++ .ops = &clk_pixel_ops, ++ .flags = CLK_SET_RATE_PARENT, + }, + }; + +@@ -531,12 +545,13 @@ static struct clk_rcg2 pclk1_clk_src = { + .mnd_width = 8, + .hid_width = 5, + .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, +- .freq_tbl = ftbl_mdss_pclk1_clk, ++ .freq_tbl = pixel_freq_tbl, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pclk1_clk_src", + .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, + .num_parents = 6, +- .ops = &clk_rcg2_ops, ++ .ops = &clk_pixel_ops, ++ .flags = CLK_SET_RATE_PARENT, + }, + }; + +@@ -754,41 +769,36 @@ static struct clk_rcg2 cpp_clk_src = { + }, + }; + +-static struct freq_tbl ftbl_mdss_byte0_clk[] = { +- F(93750000, P_DSI0PLL, 8, 0, 0), +- F(187500000, P_DSI0PLL, 4, 0, 0), +- { } +-}; +- +-static struct freq_tbl ftbl_mdss_byte1_clk[] = { +- F(93750000, P_DSI1PLL, 8, 0, 0), +- F(187500000, P_DSI1PLL, 4, 0, 0), ++static struct freq_tbl byte_freq_tbl[] = { ++ { .src = P_DSI0PLL_BYTE }, + { } + }; + + static struct clk_rcg2 byte0_clk_src = { + .cmd_rcgr = 0x2120, + .hid_width = 5, +- .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, +- .freq_tbl = ftbl_mdss_byte0_clk, ++ .parent_map = mmcc_xo_dsibyte_hdmi_edp_gpll0_map, ++ .freq_tbl = byte_freq_tbl, + .clkr.hw.init = &(struct clk_init_data){ + .name = "byte0_clk_src", +- .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, ++ .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0, + .num_parents = 6, +- .ops = &clk_rcg2_ops, ++ .ops = &clk_byte_ops, ++ .flags = CLK_SET_RATE_PARENT, + }, + }; + + static struct clk_rcg2 byte1_clk_src = { + .cmd_rcgr = 0x2140, + .hid_width = 5, +- .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, +- .freq_tbl = ftbl_mdss_byte1_clk, ++ .parent_map = mmcc_xo_dsibyte_hdmi_edp_gpll0_map, ++ .freq_tbl = byte_freq_tbl, + .clkr.hw.init = &(struct clk_init_data){ + .name = "byte1_clk_src", +- .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, ++ .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0, + .num_parents = 6, +- .ops = &clk_rcg2_ops, ++ .ops = &clk_byte_ops, ++ .flags = CLK_SET_RATE_PARENT, + }, + }; + +@@ -826,12 +836,12 @@ static struct clk_rcg2 edplink_clk_src = { + .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, + .num_parents = 6, + .ops = &clk_rcg2_ops, ++ .flags = CLK_SET_RATE_PARENT, + }, + }; + +-static struct freq_tbl ftbl_mdss_edppixel_clk[] = { +- F(175000000, P_EDPVCO, 2, 0, 0), +- F(350000000, P_EDPVCO, 11, 0, 0), ++static struct freq_tbl edp_pixel_freq_tbl[] = { ++ { .src = P_EDPVCO }, + { } + }; + +@@ -840,12 +850,12 @@ static struct clk_rcg2 edppixel_clk_src = { + .mnd_width = 8, + .hid_width = 5, + .parent_map = mmcc_xo_dsi_hdmi_edp_map, +- .freq_tbl = ftbl_mdss_edppixel_clk, ++ .freq_tbl = edp_pixel_freq_tbl, + .clkr.hw.init = &(struct clk_init_data){ + .name = "edppixel_clk_src", + .parent_names = mmcc_xo_dsi_hdmi_edp, + .num_parents = 6, +- .ops = &clk_rcg2_ops, ++ .ops = &clk_edp_pixel_ops, + }, + }; + +@@ -857,11 +867,11 @@ static struct freq_tbl ftbl_mdss_esc0_1_clk[] = { + static struct clk_rcg2 esc0_clk_src = { + .cmd_rcgr = 0x2160, + .hid_width = 5, +- .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, ++ .parent_map = mmcc_xo_dsibyte_hdmi_edp_gpll0_map, + .freq_tbl = ftbl_mdss_esc0_1_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "esc0_clk_src", +- .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, ++ .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0, + .num_parents = 6, + .ops = &clk_rcg2_ops, + }, +@@ -870,26 +880,18 @@ static struct clk_rcg2 esc0_clk_src = { + static struct clk_rcg2 esc1_clk_src = { + .cmd_rcgr = 0x2180, + .hid_width = 5, +- .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, ++ .parent_map = mmcc_xo_dsibyte_hdmi_edp_gpll0_map, + .freq_tbl = ftbl_mdss_esc0_1_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "esc1_clk_src", +- .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, ++ .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0, + .num_parents = 6, + .ops = &clk_rcg2_ops, + }, + }; + +-static struct freq_tbl ftbl_mdss_extpclk_clk[] = { +- F(25200000, P_HDMIPLL, 1, 0, 0), +- F(27000000, P_HDMIPLL, 1, 0, 0), +- F(27030000, P_HDMIPLL, 1, 0, 0), +- F(65000000, P_HDMIPLL, 1, 0, 0), +- F(74250000, P_HDMIPLL, 1, 0, 0), +- F(108000000, P_HDMIPLL, 1, 0, 0), +- F(148500000, P_HDMIPLL, 1, 0, 0), +- F(268500000, P_HDMIPLL, 1, 0, 0), +- F(297000000, P_HDMIPLL, 1, 0, 0), ++static struct freq_tbl extpclk_freq_tbl[] = { ++ { .src = P_HDMIPLL }, + { } + }; + +@@ -897,12 +899,13 @@ static struct clk_rcg2 extpclk_clk_src = { + .cmd_rcgr = 0x2060, + .hid_width = 5, + .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, +- .freq_tbl = ftbl_mdss_extpclk_clk, ++ .freq_tbl = extpclk_freq_tbl, + .clkr.hw.init = &(struct clk_init_data){ + .name = "extpclk_clk_src", + .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, + .num_parents = 6, +- .ops = &clk_rcg2_ops, ++ .ops = &clk_byte_ops, ++ .flags = CLK_SET_RATE_PARENT, + }, + }; + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0107-clk-qcom-Support-msm8974pro-global-clock-control-har.patch b/target/linux/ipq806x/patches/0107-clk-qcom-Support-msm8974pro-global-clock-control-har.patch new file mode 100644 index 0000000000..1d0ee4573f --- /dev/null +++ b/target/linux/ipq806x/patches/0107-clk-qcom-Support-msm8974pro-global-clock-control-har.patch @@ -0,0 +1,245 @@ +From a740d2b024c5b71c6f9989976049f03b634bb13d Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 16 May 2014 16:07:13 -0700 +Subject: [PATCH 107/182] clk: qcom: Support msm8974pro global clock control + hardware + +A new PLL (gpll4) is added on msm8974 PRO devices to support a +faster sdc1 clock rate. Add support for this and the two new sdcc +cal clocks. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +--- + .../devicetree/bindings/clock/qcom,gcc.txt | 2 + + drivers/clk/qcom/gcc-msm8974.c | 130 +++++++++++++++++++- + include/dt-bindings/clock/qcom,gcc-msm8974.h | 4 + + 3 files changed, 130 insertions(+), 6 deletions(-) + +diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt +index 7b7104e..9cfcb4f 100644 +--- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt ++++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt +@@ -8,6 +8,8 @@ Required properties : + "qcom,gcc-msm8660" + "qcom,gcc-msm8960" + "qcom,gcc-msm8974" ++ "qcom,gcc-msm8974pro" ++ "qcom,gcc-msm8974pro-ac" + + - reg : shall contain base register location and length + - #clock-cells : shall contain 1 +diff --git a/drivers/clk/qcom/gcc-msm8974.c b/drivers/clk/qcom/gcc-msm8974.c +index 7a420fc..7af7c18 100644 +--- a/drivers/clk/qcom/gcc-msm8974.c ++++ b/drivers/clk/qcom/gcc-msm8974.c +@@ -35,6 +35,7 @@ + #define P_XO 0 + #define P_GPLL0 1 + #define P_GPLL1 1 ++#define P_GPLL4 2 + + static const u8 gcc_xo_gpll0_map[] = { + [P_XO] = 0, +@@ -46,6 +47,18 @@ static const char *gcc_xo_gpll0[] = { + "gpll0_vote", + }; + ++static const u8 gcc_xo_gpll0_gpll4_map[] = { ++ [P_XO] = 0, ++ [P_GPLL0] = 1, ++ [P_GPLL4] = 5, ++}; ++ ++static const char *gcc_xo_gpll0_gpll4[] = { ++ "xo", ++ "gpll0_vote", ++ "gpll4_vote", ++}; ++ + #define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } + + static struct clk_pll gpll0 = { +@@ -138,6 +151,33 @@ static struct clk_regmap gpll1_vote = { + }, + }; + ++static struct clk_pll gpll4 = { ++ .l_reg = 0x1dc4, ++ .m_reg = 0x1dc8, ++ .n_reg = 0x1dcc, ++ .config_reg = 0x1dd4, ++ .mode_reg = 0x1dc0, ++ .status_reg = 0x1ddc, ++ .status_bit = 17, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "gpll4", ++ .parent_names = (const char *[]){ "xo" }, ++ .num_parents = 1, ++ .ops = &clk_pll_ops, ++ }, ++}; ++ ++static struct clk_regmap gpll4_vote = { ++ .enable_reg = 0x1480, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gpll4_vote", ++ .parent_names = (const char *[]){ "gpll4" }, ++ .num_parents = 1, ++ .ops = &clk_pll_vote_ops, ++ }, ++}; ++ + static const struct freq_tbl ftbl_gcc_usb30_master_clk[] = { + F(125000000, P_GPLL0, 1, 5, 24), + { } +@@ -812,18 +852,33 @@ static const struct freq_tbl ftbl_gcc_sdcc1_4_apps_clk[] = { + { } + }; + ++static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk_pro[] = { ++ F(144000, P_XO, 16, 3, 25), ++ F(400000, P_XO, 12, 1, 4), ++ F(20000000, P_GPLL0, 15, 1, 2), ++ F(25000000, P_GPLL0, 12, 1, 2), ++ F(50000000, P_GPLL0, 12, 0, 0), ++ F(100000000, P_GPLL0, 6, 0, 0), ++ F(192000000, P_GPLL4, 4, 0, 0), ++ F(200000000, P_GPLL0, 3, 0, 0), ++ F(384000000, P_GPLL4, 2, 0, 0), ++ { } ++}; ++ ++static struct clk_init_data sdcc1_apps_clk_src_init = { ++ .name = "sdcc1_apps_clk_src", ++ .parent_names = gcc_xo_gpll0, ++ .num_parents = 2, ++ .ops = &clk_rcg2_ops, ++}; ++ + static struct clk_rcg2 sdcc1_apps_clk_src = { + .cmd_rcgr = 0x04d0, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_sdcc1_4_apps_clk, +- .clkr.hw.init = &(struct clk_init_data){ +- .name = "sdcc1_apps_clk_src", +- .parent_names = gcc_xo_gpll0, +- .num_parents = 2, +- .ops = &clk_rcg2_ops, +- }, ++ .clkr.hw.init = &sdcc1_apps_clk_src_init, + }; + + static struct clk_rcg2 sdcc2_apps_clk_src = { +@@ -1995,6 +2050,38 @@ static struct clk_branch gcc_sdcc1_apps_clk = { + }, + }; + ++static struct clk_branch gcc_sdcc1_cdccal_ff_clk = { ++ .halt_reg = 0x04e8, ++ .clkr = { ++ .enable_reg = 0x04e8, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gcc_sdcc1_cdccal_ff_clk", ++ .parent_names = (const char *[]){ ++ "xo" ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch2_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gcc_sdcc1_cdccal_sleep_clk = { ++ .halt_reg = 0x04e4, ++ .clkr = { ++ .enable_reg = 0x04e4, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gcc_sdcc1_cdccal_sleep_clk", ++ .parent_names = (const char *[]){ ++ "sleep_clk_src" ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch2_ops, ++ }, ++ }, ++}; ++ + static struct clk_branch gcc_sdcc2_ahb_clk = { + .halt_reg = 0x0508, + .clkr = { +@@ -2484,6 +2571,10 @@ static struct clk_regmap *gcc_msm8974_clocks[] = { + [GCC_USB_HSIC_IO_CAL_SLEEP_CLK] = &gcc_usb_hsic_io_cal_sleep_clk.clkr, + [GCC_USB_HSIC_SYSTEM_CLK] = &gcc_usb_hsic_system_clk.clkr, + [GCC_MMSS_GPLL0_CLK_SRC] = &gcc_mmss_gpll0_clk_src, ++ [GPLL4] = NULL, ++ [GPLL4_VOTE] = NULL, ++ [GCC_SDCC1_CDCCAL_SLEEP_CLK] = NULL, ++ [GCC_SDCC1_CDCCAL_FF_CLK] = NULL, + }; + + static const struct qcom_reset_map gcc_msm8974_resets[] = { +@@ -2585,14 +2676,41 @@ static const struct qcom_cc_desc gcc_msm8974_desc = { + + static const struct of_device_id gcc_msm8974_match_table[] = { + { .compatible = "qcom,gcc-msm8974" }, ++ { .compatible = "qcom,gcc-msm8974pro" , .data = (void *)1UL }, ++ { .compatible = "qcom,gcc-msm8974pro-ac", .data = (void *)1UL }, + { } + }; + MODULE_DEVICE_TABLE(of, gcc_msm8974_match_table); + ++static void msm8974_pro_clock_override(void) ++{ ++ sdcc1_apps_clk_src_init.parent_names = gcc_xo_gpll0_gpll4; ++ sdcc1_apps_clk_src_init.num_parents = 3; ++ sdcc1_apps_clk_src.freq_tbl = ftbl_gcc_sdcc1_apps_clk_pro; ++ sdcc1_apps_clk_src.parent_map = gcc_xo_gpll0_gpll4_map; ++ ++ gcc_msm8974_clocks[GPLL4] = &gpll4.clkr; ++ gcc_msm8974_clocks[GPLL4_VOTE] = &gpll4_vote; ++ gcc_msm8974_clocks[GCC_SDCC1_CDCCAL_SLEEP_CLK] = ++ &gcc_sdcc1_cdccal_sleep_clk.clkr; ++ gcc_msm8974_clocks[GCC_SDCC1_CDCCAL_FF_CLK] = ++ &gcc_sdcc1_cdccal_ff_clk.clkr; ++} ++ + static int gcc_msm8974_probe(struct platform_device *pdev) + { + struct clk *clk; + struct device *dev = &pdev->dev; ++ bool pro; ++ const struct of_device_id *id; ++ ++ id = of_match_device(gcc_msm8974_match_table, dev); ++ if (!id) ++ return -ENODEV; ++ pro = !!(id->data); ++ ++ if (pro) ++ msm8974_pro_clock_override(); + + /* Temporary until RPM clocks supported */ + clk = clk_register_fixed_rate(dev, "xo", NULL, CLK_IS_ROOT, 19200000); +diff --git a/include/dt-bindings/clock/qcom,gcc-msm8974.h b/include/dt-bindings/clock/qcom,gcc-msm8974.h +index 223ca17..51e51c8 100644 +--- a/include/dt-bindings/clock/qcom,gcc-msm8974.h ++++ b/include/dt-bindings/clock/qcom,gcc-msm8974.h +@@ -316,5 +316,9 @@ + #define GCC_CE2_CLK_SLEEP_ENA 299 + #define GCC_CE2_AXI_CLK_SLEEP_ENA 300 + #define GCC_CE2_AHB_CLK_SLEEP_ENA 301 ++#define GPLL4 302 ++#define GPLL4_VOTE 303 ++#define GCC_SDCC1_CDCCAL_SLEEP_CLK 304 ++#define GCC_SDCC1_CDCCAL_FF_CLK 305 + + #endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0108-clk-qcom-Return-error-pointers-for-unimplemented-clo.patch b/target/linux/ipq806x/patches/0108-clk-qcom-Return-error-pointers-for-unimplemented-clo.patch new file mode 100644 index 0000000000..37806f154b --- /dev/null +++ b/target/linux/ipq806x/patches/0108-clk-qcom-Return-error-pointers-for-unimplemented-clo.patch @@ -0,0 +1,37 @@ +From 77b7f864b03d6b49c143a9c8c19432feef3032b5 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 16 May 2014 16:07:14 -0700 +Subject: [PATCH 108/182] clk: qcom: Return error pointers for unimplemented + clocks + +Not all clocks are implemented but client drivers can still +request them. Currently we will return a NULL pointer to them if +the clock isn't implemented in software but NULL pointers are +valid clock pointers. Return an error pointer so that driver's +don't proceed without a clock they may actually need. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +--- + drivers/clk/qcom/common.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c +index 86b45fb..9b5a1cf 100644 +--- a/drivers/clk/qcom/common.c ++++ b/drivers/clk/qcom/common.c +@@ -62,8 +62,10 @@ int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc) + data->clk_num = num_clks; + + for (i = 0; i < num_clks; i++) { +- if (!rclks[i]) ++ if (!rclks[i]) { ++ clks[i] = ERR_PTR(-ENOENT); + continue; ++ } + clk = devm_clk_register_regmap(dev, rclks[i]); + if (IS_ERR(clk)) + return PTR_ERR(clk); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0109-libahci-Allow-drivers-to-override-start_engine.patch b/target/linux/ipq806x/patches/0109-libahci-Allow-drivers-to-override-start_engine.patch new file mode 100644 index 0000000000..102fd4f6c3 --- /dev/null +++ b/target/linux/ipq806x/patches/0109-libahci-Allow-drivers-to-override-start_engine.patch @@ -0,0 +1,223 @@ +From 10f3c772363e549c3dbd3cc3755d270c5656d5b8 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 22 Feb 2014 16:53:30 +0100 +Subject: [PATCH 109/182] libahci: Allow drivers to override start_engine + +Allwinner A10 and A20 ARM SoCs have an AHCI sata controller which needs a +special register to be poked before starting the DMA engine. + +This register gets reset on an ahci_stop_engine call, so there is no other +place then ahci_start_engine where this poking can be done. + +This commit allows drivers to override ahci_start_engine behavior for use by +the Allwinner AHCI driver (and potentially other drivers in the future). + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci.c | 6 ++++-- + drivers/ata/ahci.h | 6 ++++++ + drivers/ata/libahci.c | 26 +++++++++++++++++++------- + drivers/ata/sata_highbank.c | 3 ++- + 4 files changed, 31 insertions(+), 10 deletions(-) + +diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c +index c81d809..8bfc477 100644 +--- a/drivers/ata/ahci.c ++++ b/drivers/ata/ahci.c +@@ -578,6 +578,7 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) + { + struct ata_port *ap = link->ap; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + bool online; + int rc; + +@@ -588,7 +589,7 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, + rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), + deadline, &online, NULL); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); + +@@ -603,6 +604,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, + { + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + struct ata_taskfile tf; + bool online; +@@ -618,7 +620,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, + rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), + deadline, &online, NULL); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + /* The pseudo configuration device on SIMG4726 attached to + * ASUS P5W-DH Deluxe doesn't send signature FIS after +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index 2289efd..64d1a99 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -323,6 +323,12 @@ struct ahci_host_priv { + u32 em_msg_type; /* EM message type */ + struct clk *clk; /* Only for platforms supporting clk */ + void *plat_data; /* Other platform data */ ++ /* ++ * Optional ahci_start_engine override, if not set this gets set to the ++ * default ahci_start_engine during ahci_save_initial_config, this can ++ * be overridden anytime before the host is activated. ++ */ ++ void (*start_engine)(struct ata_port *ap); + }; + + extern int ahci_ignore_sss; +diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c +index 36605ab..f839bb3 100644 +--- a/drivers/ata/libahci.c ++++ b/drivers/ata/libahci.c +@@ -394,6 +394,9 @@ static ssize_t ahci_show_em_supported(struct device *dev, + * + * If inconsistent, config values are fixed up by this function. + * ++ * If it is not set already this function sets hpriv->start_engine to ++ * ahci_start_engine. ++ * + * LOCKING: + * None. + */ +@@ -500,6 +503,9 @@ void ahci_save_initial_config(struct device *dev, + hpriv->cap = cap; + hpriv->cap2 = cap2; + hpriv->port_map = port_map; ++ ++ if (!hpriv->start_engine) ++ hpriv->start_engine = ahci_start_engine; + } + EXPORT_SYMBOL_GPL(ahci_save_initial_config); + +@@ -766,7 +772,7 @@ static void ahci_start_port(struct ata_port *ap) + + /* enable DMA */ + if (!(hpriv->flags & AHCI_HFLAG_DELAY_ENGINE)) +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + /* turn on LEDs */ + if (ap->flags & ATA_FLAG_EM) { +@@ -1234,7 +1240,7 @@ int ahci_kick_engine(struct ata_port *ap) + + /* restart engine */ + out_restart: +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + return rc; + } + EXPORT_SYMBOL_GPL(ahci_kick_engine); +@@ -1426,6 +1432,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class, + const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + struct ata_taskfile tf; + bool online; +@@ -1443,7 +1450,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class, + rc = sata_link_hardreset(link, timing, deadline, &online, + ahci_check_ready); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + if (online) + *class = ahci_dev_classify(ap); +@@ -2007,10 +2014,12 @@ static void ahci_thaw(struct ata_port *ap) + + void ahci_error_handler(struct ata_port *ap) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; ++ + if (!(ap->pflags & ATA_PFLAG_FROZEN)) { + /* restart engine */ + ahci_stop_engine(ap); +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + } + + sata_pmp_error_handler(ap); +@@ -2031,6 +2040,7 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) + + static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + struct ata_device *dev = ap->link.device; + u32 devslp, dm, dito, mdat, deto; +@@ -2094,7 +2104,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) + PORT_DEVSLP_ADSE); + writel(devslp, port_mmio + PORT_DEVSLP); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + /* enable device sleep feature for the drive */ + err_mask = ata_dev_set_feature(dev, +@@ -2106,6 +2116,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) + + static void ahci_enable_fbs(struct ata_port *ap) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 fbs; +@@ -2134,11 +2145,12 @@ static void ahci_enable_fbs(struct ata_port *ap) + } else + dev_err(ap->host->dev, "Failed to enable FBS\n"); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + } + + static void ahci_disable_fbs(struct ata_port *ap) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 fbs; +@@ -2166,7 +2178,7 @@ static void ahci_disable_fbs(struct ata_port *ap) + pp->fbs_enabled = false; + } + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + } + + static void ahci_pmp_attach(struct ata_port *ap) +diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c +index 870b11e..b3b18d1 100644 +--- a/drivers/ata/sata_highbank.c ++++ b/drivers/ata/sata_highbank.c +@@ -403,6 +403,7 @@ static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class, + static const unsigned long timing[] = { 5, 100, 500}; + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + struct ata_taskfile tf; + bool online; +@@ -431,7 +432,7 @@ static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class, + break; + } while (!online && retry--); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + if (online) + *class = ahci_dev_classify(ap); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0110-ahci-platform-Add-support-for-devices-with-more-then.patch b/target/linux/ipq806x/patches/0110-ahci-platform-Add-support-for-devices-with-more-then.patch new file mode 100644 index 0000000000..09c4f220b4 --- /dev/null +++ b/target/linux/ipq806x/patches/0110-ahci-platform-Add-support-for-devices-with-more-then.patch @@ -0,0 +1,253 @@ +From b7ad421c184b827806c7f65be3a980cfc855b589 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 22 Feb 2014 16:53:31 +0100 +Subject: [PATCH 110/182] ahci-platform: Add support for devices with more + then 1 clock + +The allwinner-sun4i AHCI controller needs 2 clocks to be enabled and the +imx AHCI controller needs 3 clocks to be enabled. + +tj: Minor comment formatting updates. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + .../devicetree/bindings/ata/ahci-platform.txt | 1 + + drivers/ata/ahci.h | 3 +- + drivers/ata/ahci_platform.c | 113 +++++++++++++++----- + include/linux/ahci_platform.h | 4 + + 4 files changed, 93 insertions(+), 28 deletions(-) + +diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt +index 89de156..3ced07d 100644 +--- a/Documentation/devicetree/bindings/ata/ahci-platform.txt ++++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt +@@ -10,6 +10,7 @@ Required properties: + + Optional properties: + - dma-coherent : Present if dma operations are coherent ++- clocks : a list of phandle + clock specifier pairs + + Example: + sata@ffe08000 { +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index 64d1a99..c12862b 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -51,6 +51,7 @@ + + enum { + AHCI_MAX_PORTS = 32, ++ AHCI_MAX_CLKS = 3, + AHCI_MAX_SG = 168, /* hardware max is 64K */ + AHCI_DMA_BOUNDARY = 0xffffffff, + AHCI_MAX_CMDS = 32, +@@ -321,7 +322,7 @@ struct ahci_host_priv { + u32 em_loc; /* enclosure management location */ + u32 em_buf_sz; /* EM buffer size in byte */ + u32 em_msg_type; /* EM message type */ +- struct clk *clk; /* Only for platforms supporting clk */ ++ struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ + void *plat_data; /* Other platform data */ + /* + * Optional ahci_start_engine override, if not set this gets set to the +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 4b231ba..2342a42 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -87,6 +87,60 @@ static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT("ahci_platform"), + }; + ++/** ++ * ahci_platform_enable_clks - Enable platform clocks ++ * @hpriv: host private area to store config values ++ * ++ * This function enables all the clks found in hpriv->clks, starting at ++ * index 0. If any clk fails to enable it disables all the clks already ++ * enabled in reverse order, and then returns an error. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_enable_clks(struct ahci_host_priv *hpriv) ++{ ++ int c, rc; ++ ++ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) { ++ rc = clk_prepare_enable(hpriv->clks[c]); ++ if (rc) ++ goto disable_unprepare_clk; ++ } ++ return 0; ++ ++disable_unprepare_clk: ++ while (--c >= 0) ++ clk_disable_unprepare(hpriv->clks[c]); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); ++ ++/** ++ * ahci_platform_disable_clks - Disable platform clocks ++ * @hpriv: host private area to store config values ++ * ++ * This function disables all the clks found in hpriv->clks, in reverse ++ * order of ahci_platform_enable_clks (starting at the end of the array). ++ */ ++void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) ++{ ++ int c; ++ ++ for (c = AHCI_MAX_CLKS - 1; c >= 0; c--) ++ if (hpriv->clks[c]) ++ clk_disable_unprepare(hpriv->clks[c]); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); ++ ++static void ahci_put_clks(struct ahci_host_priv *hpriv) ++{ ++ int c; ++ ++ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) ++ clk_put(hpriv->clks[c]); ++} ++ + static int ahci_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -97,6 +151,7 @@ static int ahci_probe(struct platform_device *pdev) + struct ahci_host_priv *hpriv; + struct ata_host *host; + struct resource *mem; ++ struct clk *clk; + int irq; + int n_ports; + int i; +@@ -131,17 +186,31 @@ static int ahci_probe(struct platform_device *pdev) + return -ENOMEM; + } + +- hpriv->clk = clk_get(dev, NULL); +- if (IS_ERR(hpriv->clk)) { +- dev_err(dev, "can't get clock\n"); +- } else { +- rc = clk_prepare_enable(hpriv->clk); +- if (rc) { +- dev_err(dev, "clock prepare enable failed"); +- goto free_clk; ++ for (i = 0; i < AHCI_MAX_CLKS; i++) { ++ /* ++ * For now we must use clk_get(dev, NULL) for the first clock, ++ * because some platforms (da850, spear13xx) are not yet ++ * converted to use devicetree for clocks. For new platforms ++ * this is equivalent to of_clk_get(dev->of_node, 0). ++ */ ++ if (i == 0) ++ clk = clk_get(dev, NULL); ++ else ++ clk = of_clk_get(dev->of_node, i); ++ ++ if (IS_ERR(clk)) { ++ rc = PTR_ERR(clk); ++ if (rc == -EPROBE_DEFER) ++ goto free_clk; ++ break; + } ++ hpriv->clks[i] = clk; + } + ++ rc = ahci_enable_clks(dev, hpriv); ++ if (rc) ++ goto free_clk; ++ + /* + * Some platforms might need to prepare for mmio region access, + * which could be done in the following init call. So, the mmio +@@ -222,11 +291,9 @@ pdata_exit: + if (pdata && pdata->exit) + pdata->exit(dev); + disable_unprepare_clk: +- if (!IS_ERR(hpriv->clk)) +- clk_disable_unprepare(hpriv->clk); ++ ahci_disable_clks(hpriv); + free_clk: +- if (!IS_ERR(hpriv->clk)) +- clk_put(hpriv->clk); ++ ahci_put_clks(hpriv); + return rc; + } + +@@ -239,10 +306,8 @@ static void ahci_host_stop(struct ata_host *host) + if (pdata && pdata->exit) + pdata->exit(dev); + +- if (!IS_ERR(hpriv->clk)) { +- clk_disable_unprepare(hpriv->clk); +- clk_put(hpriv->clk); +- } ++ ahci_disable_clks(hpriv); ++ ahci_put_clks(hpriv); + } + + #ifdef CONFIG_PM_SLEEP +@@ -277,8 +342,7 @@ static int ahci_suspend(struct device *dev) + if (pdata && pdata->suspend) + return pdata->suspend(dev); + +- if (!IS_ERR(hpriv->clk)) +- clk_disable_unprepare(hpriv->clk); ++ ahci_disable_clks(hpriv); + + return 0; + } +@@ -290,13 +354,9 @@ static int ahci_resume(struct device *dev) + struct ahci_host_priv *hpriv = host->private_data; + int rc; + +- if (!IS_ERR(hpriv->clk)) { +- rc = clk_prepare_enable(hpriv->clk); +- if (rc) { +- dev_err(dev, "clock prepare enable failed"); +- return rc; +- } +- } ++ rc = ahci_enable_clks(dev, hpriv); ++ if (rc) ++ return rc; + + if (pdata && pdata->resume) { + rc = pdata->resume(dev); +@@ -317,8 +377,7 @@ static int ahci_resume(struct device *dev) + return 0; + + disable_unprepare_clk: +- if (!IS_ERR(hpriv->clk)) +- clk_disable_unprepare(hpriv->clk); ++ ahci_disable_clks(hpriv); + + return rc; + } +diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h +index 73a2500..769d065 100644 +--- a/include/linux/ahci_platform.h ++++ b/include/linux/ahci_platform.h +@@ -19,6 +19,7 @@ + + struct device; + struct ata_port_info; ++struct ahci_host_priv; + + struct ahci_platform_data { + int (*init)(struct device *dev, void __iomem *addr); +@@ -30,4 +31,7 @@ struct ahci_platform_data { + unsigned int mask_port_map; + }; + ++int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); ++void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); ++ + #endif /* _AHCI_PLATFORM_H */ +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0111-ahci-platform-Add-support-for-an-optional-regulator-.patch b/target/linux/ipq806x/patches/0111-ahci-platform-Add-support-for-an-optional-regulator-.patch new file mode 100644 index 0000000000..2d6ac0bd98 --- /dev/null +++ b/target/linux/ipq806x/patches/0111-ahci-platform-Add-support-for-an-optional-regulator-.patch @@ -0,0 +1,142 @@ +From 967f4a7821a3356d9d3c8b641537cd6d5eb15439 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 22 Feb 2014 16:53:32 +0100 +Subject: [PATCH 111/182] ahci-platform: Add support for an optional regulator + for sata-target power + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + .../devicetree/bindings/ata/ahci-platform.txt | 1 + + drivers/ata/ahci.h | 2 ++ + drivers/ata/ahci_platform.c | 36 ++++++++++++++++++-- + 3 files changed, 37 insertions(+), 2 deletions(-) + +diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt +index 3ced07d..1ac807f 100644 +--- a/Documentation/devicetree/bindings/ata/ahci-platform.txt ++++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt +@@ -11,6 +11,7 @@ Required properties: + Optional properties: + - dma-coherent : Present if dma operations are coherent + - clocks : a list of phandle + clock specifier pairs ++- target-supply : regulator for SATA target power + + Example: + sata@ffe08000 { +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index c12862b..bf8100c 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -37,6 +37,7 @@ + + #include <linux/clk.h> + #include <linux/libata.h> ++#include <linux/regulator/consumer.h> + + /* Enclosure Management Control */ + #define EM_CTRL_MSG_TYPE 0x000f0000 +@@ -323,6 +324,7 @@ struct ahci_host_priv { + u32 em_buf_sz; /* EM buffer size in byte */ + u32 em_msg_type; /* EM message type */ + struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ ++ struct regulator *target_pwr; /* Optional */ + void *plat_data; /* Other platform data */ + /* + * Optional ahci_start_engine override, if not set this gets set to the +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 2342a42..8f18ebe 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -186,6 +186,14 @@ static int ahci_probe(struct platform_device *pdev) + return -ENOMEM; + } + ++ hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); ++ if (IS_ERR(hpriv->target_pwr)) { ++ rc = PTR_ERR(hpriv->target_pwr); ++ if (rc == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ hpriv->target_pwr = NULL; ++ } ++ + for (i = 0; i < AHCI_MAX_CLKS; i++) { + /* + * For now we must use clk_get(dev, NULL) for the first clock, +@@ -207,9 +215,15 @@ static int ahci_probe(struct platform_device *pdev) + hpriv->clks[i] = clk; + } + ++ if (hpriv->target_pwr) { ++ rc = regulator_enable(hpriv->target_pwr); ++ if (rc) ++ goto free_clk; ++ } ++ + rc = ahci_enable_clks(dev, hpriv); + if (rc) +- goto free_clk; ++ goto disable_regulator; + + /* + * Some platforms might need to prepare for mmio region access, +@@ -292,6 +306,9 @@ pdata_exit: + pdata->exit(dev); + disable_unprepare_clk: + ahci_disable_clks(hpriv); ++disable_regulator: ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); + free_clk: + ahci_put_clks(hpriv); + return rc; +@@ -308,6 +325,9 @@ static void ahci_host_stop(struct ata_host *host) + + ahci_disable_clks(hpriv); + ahci_put_clks(hpriv); ++ ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); + } + + #ifdef CONFIG_PM_SLEEP +@@ -344,6 +364,9 @@ static int ahci_suspend(struct device *dev) + + ahci_disable_clks(hpriv); + ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++ + return 0; + } + +@@ -354,9 +377,15 @@ static int ahci_resume(struct device *dev) + struct ahci_host_priv *hpriv = host->private_data; + int rc; + ++ if (hpriv->target_pwr) { ++ rc = regulator_enable(hpriv->target_pwr); ++ if (rc) ++ return rc; ++ } ++ + rc = ahci_enable_clks(dev, hpriv); + if (rc) +- return rc; ++ goto disable_regulator; + + if (pdata && pdata->resume) { + rc = pdata->resume(dev); +@@ -378,6 +407,9 @@ static int ahci_resume(struct device *dev) + + disable_unprepare_clk: + ahci_disable_clks(hpriv); ++disable_regulator: ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); + + return rc; + } +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0112-ahci-platform-Add-enable_-disable_resources-helper-f.patch b/target/linux/ipq806x/patches/0112-ahci-platform-Add-enable_-disable_resources-helper-f.patch new file mode 100644 index 0000000000..ad6adbebf5 --- /dev/null +++ b/target/linux/ipq806x/patches/0112-ahci-platform-Add-enable_-disable_resources-helper-f.patch @@ -0,0 +1,208 @@ +From 3b2df84624b38362cb84c2d4c6d1d3540c5069d3 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 22 Feb 2014 16:53:33 +0100 +Subject: [PATCH 112/182] ahci-platform: Add enable_ / disable_resources + helper functions + +tj: Minor comment formatting updates. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci_platform.c | 106 +++++++++++++++++++++++++++-------------- + include/linux/ahci_platform.h | 2 + + 2 files changed, 71 insertions(+), 37 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 8f18ebe..656d285 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -133,6 +133,62 @@ void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) + } + EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); + ++/** ++ * ahci_platform_enable_resources - Enable platform resources ++ * @hpriv: host private area to store config values ++ * ++ * This function enables all ahci_platform managed resources in the ++ * following order: ++ * 1) Regulator ++ * 2) Clocks (through ahci_platform_enable_clks) ++ * ++ * If resource enabling fails at any point the previous enabled resources ++ * are disabled in reverse order. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) ++{ ++ int rc; ++ ++ if (hpriv->target_pwr) { ++ rc = regulator_enable(hpriv->target_pwr); ++ if (rc) ++ return rc; ++ } ++ ++ rc = ahci_platform_enable_clks(hpriv); ++ if (rc) ++ goto disable_regulator; ++ ++ return 0; ++ ++disable_regulator: ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); ++ ++/** ++ * ahci_platform_disable_resources - Disable platform resources ++ * @hpriv: host private area to store config values ++ * ++ * This function disables all ahci_platform managed resources in the ++ * following order: ++ * 1) Clocks (through ahci_platform_disable_clks) ++ * 2) Regulator ++ */ ++void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) ++{ ++ ahci_platform_disable_clks(hpriv); ++ ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); ++ + static void ahci_put_clks(struct ahci_host_priv *hpriv) + { + int c; +@@ -215,15 +271,9 @@ static int ahci_probe(struct platform_device *pdev) + hpriv->clks[i] = clk; + } + +- if (hpriv->target_pwr) { +- rc = regulator_enable(hpriv->target_pwr); +- if (rc) +- goto free_clk; +- } +- +- rc = ahci_enable_clks(dev, hpriv); ++ rc = ahci_platform_enable_resources(hpriv); + if (rc) +- goto disable_regulator; ++ goto free_clk; + + /* + * Some platforms might need to prepare for mmio region access, +@@ -234,7 +284,7 @@ static int ahci_probe(struct platform_device *pdev) + if (pdata && pdata->init) { + rc = pdata->init(dev, hpriv->mmio); + if (rc) +- goto disable_unprepare_clk; ++ goto disable_resources; + } + + ahci_save_initial_config(dev, hpriv, +@@ -304,11 +354,8 @@ static int ahci_probe(struct platform_device *pdev) + pdata_exit: + if (pdata && pdata->exit) + pdata->exit(dev); +-disable_unprepare_clk: +- ahci_disable_clks(hpriv); +-disable_regulator: +- if (hpriv->target_pwr) +- regulator_disable(hpriv->target_pwr); ++disable_resources: ++ ahci_platform_disable_resources(hpriv); + free_clk: + ahci_put_clks(hpriv); + return rc; +@@ -323,11 +370,8 @@ static void ahci_host_stop(struct ata_host *host) + if (pdata && pdata->exit) + pdata->exit(dev); + +- ahci_disable_clks(hpriv); ++ ahci_platform_disable_resources(hpriv); + ahci_put_clks(hpriv); +- +- if (hpriv->target_pwr) +- regulator_disable(hpriv->target_pwr); + } + + #ifdef CONFIG_PM_SLEEP +@@ -362,10 +406,7 @@ static int ahci_suspend(struct device *dev) + if (pdata && pdata->suspend) + return pdata->suspend(dev); + +- ahci_disable_clks(hpriv); +- +- if (hpriv->target_pwr) +- regulator_disable(hpriv->target_pwr); ++ ahci_platform_disable_resources(hpriv); + + return 0; + } +@@ -377,26 +418,20 @@ static int ahci_resume(struct device *dev) + struct ahci_host_priv *hpriv = host->private_data; + int rc; + +- if (hpriv->target_pwr) { +- rc = regulator_enable(hpriv->target_pwr); +- if (rc) +- return rc; +- } +- +- rc = ahci_enable_clks(dev, hpriv); ++ rc = ahci_platform_enable_resources(hpriv); + if (rc) +- goto disable_regulator; ++ return rc; + + if (pdata && pdata->resume) { + rc = pdata->resume(dev); + if (rc) +- goto disable_unprepare_clk; ++ goto disable_resources; + } + + if (dev->power.power_state.event == PM_EVENT_SUSPEND) { + rc = ahci_reset_controller(host); + if (rc) +- goto disable_unprepare_clk; ++ goto disable_resources; + + ahci_init_controller(host); + } +@@ -405,11 +440,8 @@ static int ahci_resume(struct device *dev) + + return 0; + +-disable_unprepare_clk: +- ahci_disable_clks(hpriv); +-disable_regulator: +- if (hpriv->target_pwr) +- regulator_disable(hpriv->target_pwr); ++disable_resources: ++ ahci_platform_disable_resources(hpriv); + + return rc; + } +diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h +index 769d065..b674b01 100644 +--- a/include/linux/ahci_platform.h ++++ b/include/linux/ahci_platform.h +@@ -33,5 +33,7 @@ struct ahci_platform_data { + + int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); + void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); ++int ahci_platform_enable_resources(struct ahci_host_priv *hpriv); ++void ahci_platform_disable_resources(struct ahci_host_priv *hpriv); + + #endif /* _AHCI_PLATFORM_H */ +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0113-ata-delete-non-required-instances-of-include-linux-i.patch b/target/linux/ipq806x/patches/0113-ata-delete-non-required-instances-of-include-linux-i.patch new file mode 100644 index 0000000000..c7db74020d --- /dev/null +++ b/target/linux/ipq806x/patches/0113-ata-delete-non-required-instances-of-include-linux-i.patch @@ -0,0 +1,904 @@ +From 01b380db3c35bbd87cb3765573fd049bce3d7640 Mon Sep 17 00:00:00 2001 +From: Paul Gortmaker <paul.gortmaker@windriver.com> +Date: Tue, 21 Jan 2014 16:22:51 -0500 +Subject: [PATCH 113/182] ata: delete non-required instances of include + <linux/init.h> + +None of these files are actually using any __init type directives +and hence don't need to include <linux/init.h>. Most are just a +left over from __devinit and __cpuinit removal, or simply due to +code getting copied from one driver to the next. + +Cc: linux-ide@vger.kernel.org +Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/acard-ahci.c | 1 - + drivers/ata/ahci.c | 1 - + drivers/ata/ahci_platform.c | 1 - + drivers/ata/ata_generic.c | 1 - + drivers/ata/libahci.c | 1 - + drivers/ata/pata_acpi.c | 1 - + drivers/ata/pata_amd.c | 1 - + drivers/ata/pata_artop.c | 1 - + drivers/ata/pata_at91.c | 1 - + drivers/ata/pata_atiixp.c | 1 - + drivers/ata/pata_atp867x.c | 1 - + drivers/ata/pata_cmd640.c | 1 - + drivers/ata/pata_cmd64x.c | 1 - + drivers/ata/pata_cs5520.c | 1 - + drivers/ata/pata_cs5530.c | 1 - + drivers/ata/pata_cs5535.c | 1 - + drivers/ata/pata_cs5536.c | 1 - + drivers/ata/pata_cypress.c | 1 - + drivers/ata/pata_efar.c | 1 - + drivers/ata/pata_ep93xx.c | 1 - + drivers/ata/pata_hpt366.c | 1 - + drivers/ata/pata_hpt37x.c | 1 - + drivers/ata/pata_hpt3x2n.c | 1 - + drivers/ata/pata_hpt3x3.c | 1 - + drivers/ata/pata_imx.c | 1 - + drivers/ata/pata_it8213.c | 1 - + drivers/ata/pata_it821x.c | 1 - + drivers/ata/pata_jmicron.c | 1 - + drivers/ata/pata_marvell.c | 1 - + drivers/ata/pata_mpiix.c | 1 - + drivers/ata/pata_netcell.c | 1 - + drivers/ata/pata_ninja32.c | 1 - + drivers/ata/pata_ns87410.c | 1 - + drivers/ata/pata_ns87415.c | 1 - + drivers/ata/pata_oldpiix.c | 1 - + drivers/ata/pata_opti.c | 1 - + drivers/ata/pata_optidma.c | 1 - + drivers/ata/pata_pcmcia.c | 1 - + drivers/ata/pata_pdc2027x.c | 1 - + drivers/ata/pata_pdc202xx_old.c | 1 - + drivers/ata/pata_piccolo.c | 1 - + drivers/ata/pata_platform.c | 1 - + drivers/ata/pata_pxa.c | 1 - + drivers/ata/pata_radisys.c | 1 - + drivers/ata/pata_rdc.c | 1 - + drivers/ata/pata_rz1000.c | 1 - + drivers/ata/pata_sc1200.c | 1 - + drivers/ata/pata_scc.c | 1 - + drivers/ata/pata_sch.c | 1 - + drivers/ata/pata_serverworks.c | 1 - + drivers/ata/pata_sil680.c | 1 - + drivers/ata/pata_sis.c | 1 - + drivers/ata/pata_sl82c105.c | 1 - + drivers/ata/pata_triflex.c | 1 - + drivers/ata/pata_via.c | 1 - + drivers/ata/pdc_adma.c | 1 - + drivers/ata/sata_dwc_460ex.c | 1 - + drivers/ata/sata_highbank.c | 1 - + drivers/ata/sata_nv.c | 1 - + drivers/ata/sata_promise.c | 1 - + drivers/ata/sata_qstor.c | 1 - + drivers/ata/sata_sil.c | 1 - + drivers/ata/sata_sis.c | 1 - + drivers/ata/sata_svw.c | 1 - + drivers/ata/sata_sx4.c | 1 - + drivers/ata/sata_uli.c | 1 - + drivers/ata/sata_via.c | 1 - + drivers/ata/sata_vsc.c | 1 - + 68 files changed, 68 deletions(-) + +diff --git a/drivers/ata/acard-ahci.c b/drivers/ata/acard-ahci.c +index fd665d9..b51605a 100644 +--- a/drivers/ata/acard-ahci.c ++++ b/drivers/ata/acard-ahci.c +@@ -36,7 +36,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c +index 8bfc477..a52a5b6 100644 +--- a/drivers/ata/ahci.c ++++ b/drivers/ata/ahci.c +@@ -35,7 +35,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 656d285..a32df31 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -17,7 +17,6 @@ + #include <linux/gfp.h> + #include <linux/module.h> + #include <linux/pm.h> +-#include <linux/init.h> + #include <linux/interrupt.h> + #include <linux/device.h> + #include <linux/platform_device.h> +diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c +index 7d19665..9498a7d 100644 +--- a/drivers/ata/ata_generic.c ++++ b/drivers/ata/ata_generic.c +@@ -19,7 +19,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c +index f839bb3..fa02770 100644 +--- a/drivers/ata/libahci.c ++++ b/drivers/ata/libahci.c +@@ -35,7 +35,6 @@ + #include <linux/kernel.h> + #include <linux/gfp.h> + #include <linux/module.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/pata_acpi.c b/drivers/ata/pata_acpi.c +index 62c9ac8..5108b87 100644 +--- a/drivers/ata/pata_acpi.c ++++ b/drivers/ata/pata_acpi.c +@@ -7,7 +7,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_amd.c b/drivers/ata/pata_amd.c +index d23e2b3..1206fa6 100644 +--- a/drivers/ata/pata_amd.c ++++ b/drivers/ata/pata_amd.c +@@ -17,7 +17,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_artop.c b/drivers/ata/pata_artop.c +index 1581dee..3aa4e65 100644 +--- a/drivers/ata/pata_artop.c ++++ b/drivers/ata/pata_artop.c +@@ -19,7 +19,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_at91.c b/drivers/ata/pata_at91.c +index d63ee8f..e9c8727 100644 +--- a/drivers/ata/pata_at91.c ++++ b/drivers/ata/pata_at91.c +@@ -18,7 +18,6 @@ + + #include <linux/kernel.h> + #include <linux/module.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/gfp.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c +index 24e5105..30fa4ca 100644 +--- a/drivers/ata/pata_atiixp.c ++++ b/drivers/ata/pata_atiixp.c +@@ -15,7 +15,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_atp867x.c b/drivers/ata/pata_atp867x.c +index 2ca5026..7e73a0f 100644 +--- a/drivers/ata/pata_atp867x.c ++++ b/drivers/ata/pata_atp867x.c +@@ -29,7 +29,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_cmd640.c b/drivers/ata/pata_cmd640.c +index 8fb69e5..57f1be6 100644 +--- a/drivers/ata/pata_cmd640.c ++++ b/drivers/ata/pata_cmd640.c +@@ -15,7 +15,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/gfp.h> +diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c +index 1275a8d..6bca350 100644 +--- a/drivers/ata/pata_cmd64x.c ++++ b/drivers/ata/pata_cmd64x.c +@@ -26,7 +26,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_cs5520.c b/drivers/ata/pata_cs5520.c +index f10baab..bcde4b7 100644 +--- a/drivers/ata/pata_cs5520.c ++++ b/drivers/ata/pata_cs5520.c +@@ -34,7 +34,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_cs5530.c b/drivers/ata/pata_cs5530.c +index f07f229..8afe854 100644 +--- a/drivers/ata/pata_cs5530.c ++++ b/drivers/ata/pata_cs5530.c +@@ -26,7 +26,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_cs5535.c b/drivers/ata/pata_cs5535.c +index 997e16a..2c0986f 100644 +--- a/drivers/ata/pata_cs5535.c ++++ b/drivers/ata/pata_cs5535.c +@@ -31,7 +31,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_cs5536.c b/drivers/ata/pata_cs5536.c +index 0448860..32ddcae 100644 +--- a/drivers/ata/pata_cs5536.c ++++ b/drivers/ata/pata_cs5536.c +@@ -33,7 +33,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/libata.h> +diff --git a/drivers/ata/pata_cypress.c b/drivers/ata/pata_cypress.c +index 810bc99..3435bd6 100644 +--- a/drivers/ata/pata_cypress.c ++++ b/drivers/ata/pata_cypress.c +@@ -11,7 +11,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_efar.c b/drivers/ata/pata_efar.c +index 3c12fd7..f440892 100644 +--- a/drivers/ata/pata_efar.c ++++ b/drivers/ata/pata_efar.c +@@ -14,7 +14,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_ep93xx.c b/drivers/ata/pata_ep93xx.c +index 980b88e..cad9d45 100644 +--- a/drivers/ata/pata_ep93xx.c ++++ b/drivers/ata/pata_ep93xx.c +@@ -34,7 +34,6 @@ + #include <linux/err.h> + #include <linux/kernel.h> + #include <linux/module.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <scsi/scsi_host.h> + #include <linux/ata.h> +diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c +index 35b5213..8e76f79 100644 +--- a/drivers/ata/pata_hpt366.c ++++ b/drivers/ata/pata_hpt366.c +@@ -19,7 +19,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c +index a9d74ef..3ba843f 100644 +--- a/drivers/ata/pata_hpt37x.c ++++ b/drivers/ata/pata_hpt37x.c +@@ -19,7 +19,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_hpt3x2n.c b/drivers/ata/pata_hpt3x2n.c +index 4be0398..b93c0f0 100644 +--- a/drivers/ata/pata_hpt3x2n.c ++++ b/drivers/ata/pata_hpt3x2n.c +@@ -20,7 +20,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_hpt3x3.c b/drivers/ata/pata_hpt3x3.c +index 85cf286..255c5aa 100644 +--- a/drivers/ata/pata_hpt3x3.c ++++ b/drivers/ata/pata_hpt3x3.c +@@ -16,7 +16,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_imx.c b/drivers/ata/pata_imx.c +index b0b18ec..7e69797 100644 +--- a/drivers/ata/pata_imx.c ++++ b/drivers/ata/pata_imx.c +@@ -15,7 +15,6 @@ + */ + #include <linux/kernel.h> + #include <linux/module.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <scsi/scsi_host.h> + #include <linux/ata.h> +diff --git a/drivers/ata/pata_it8213.c b/drivers/ata/pata_it8213.c +index 2a8dd95..81369d1 100644 +--- a/drivers/ata/pata_it8213.c ++++ b/drivers/ata/pata_it8213.c +@@ -10,7 +10,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c +index 581e04d..dc3d787 100644 +--- a/drivers/ata/pata_it821x.c ++++ b/drivers/ata/pata_it821x.c +@@ -72,7 +72,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/slab.h> +diff --git a/drivers/ata/pata_jmicron.c b/drivers/ata/pata_jmicron.c +index 76e739b0..b1cfa02 100644 +--- a/drivers/ata/pata_jmicron.c ++++ b/drivers/ata/pata_jmicron.c +@@ -10,7 +10,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_marvell.c b/drivers/ata/pata_marvell.c +index a4f5e78..6bad3df 100644 +--- a/drivers/ata/pata_marvell.c ++++ b/drivers/ata/pata_marvell.c +@@ -11,7 +11,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_mpiix.c b/drivers/ata/pata_mpiix.c +index 1f5f28b..f39a537 100644 +--- a/drivers/ata/pata_mpiix.c ++++ b/drivers/ata/pata_mpiix.c +@@ -28,7 +28,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_netcell.c b/drivers/ata/pata_netcell.c +index ad1a0fe..e3b9709 100644 +--- a/drivers/ata/pata_netcell.c ++++ b/drivers/ata/pata_netcell.c +@@ -7,7 +7,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_ninja32.c b/drivers/ata/pata_ninja32.c +index 9513e07..56201a6 100644 +--- a/drivers/ata/pata_ninja32.c ++++ b/drivers/ata/pata_ninja32.c +@@ -37,7 +37,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_ns87410.c b/drivers/ata/pata_ns87410.c +index 0c424da..6154c3e 100644 +--- a/drivers/ata/pata_ns87410.c ++++ b/drivers/ata/pata_ns87410.c +@@ -20,7 +20,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_ns87415.c b/drivers/ata/pata_ns87415.c +index 16dc3a6..d44df7c 100644 +--- a/drivers/ata/pata_ns87415.c ++++ b/drivers/ata/pata_ns87415.c +@@ -25,7 +25,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c +index d77b2e1..319b644 100644 +--- a/drivers/ata/pata_oldpiix.c ++++ b/drivers/ata/pata_oldpiix.c +@@ -16,7 +16,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_opti.c b/drivers/ata/pata_opti.c +index 4ea70cd..fb042e0 100644 +--- a/drivers/ata/pata_opti.c ++++ b/drivers/ata/pata_opti.c +@@ -26,7 +26,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_optidma.c b/drivers/ata/pata_optidma.c +index 78ede3f..bb71ea2 100644 +--- a/drivers/ata/pata_optidma.c ++++ b/drivers/ata/pata_optidma.c +@@ -25,7 +25,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c +index 40254f4..bcc4b96 100644 +--- a/drivers/ata/pata_pcmcia.c ++++ b/drivers/ata/pata_pcmcia.c +@@ -26,7 +26,6 @@ + + #include <linux/kernel.h> + #include <linux/module.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/slab.h> +diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c +index 9d874c8..1151f23 100644 +--- a/drivers/ata/pata_pdc2027x.c ++++ b/drivers/ata/pata_pdc2027x.c +@@ -25,7 +25,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_pdc202xx_old.c b/drivers/ata/pata_pdc202xx_old.c +index c34fc50..defa050 100644 +--- a/drivers/ata/pata_pdc202xx_old.c ++++ b/drivers/ata/pata_pdc202xx_old.c +@@ -15,7 +15,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_piccolo.c b/drivers/ata/pata_piccolo.c +index 2beb6b5..0b46be1 100644 +--- a/drivers/ata/pata_piccolo.c ++++ b/drivers/ata/pata_piccolo.c +@@ -18,7 +18,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c +index 0279488..a5579b5 100644 +--- a/drivers/ata/pata_platform.c ++++ b/drivers/ata/pata_platform.c +@@ -13,7 +13,6 @@ + */ + #include <linux/kernel.h> + #include <linux/module.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <scsi/scsi_host.h> + #include <linux/ata.h> +diff --git a/drivers/ata/pata_pxa.c b/drivers/ata/pata_pxa.c +index a6f05ac..73259bf 100644 +--- a/drivers/ata/pata_pxa.c ++++ b/drivers/ata/pata_pxa.c +@@ -20,7 +20,6 @@ + + #include <linux/kernel.h> + #include <linux/module.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/ata.h> + #include <linux/libata.h> +diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c +index f582ba1..be3f102 100644 +--- a/drivers/ata/pata_radisys.c ++++ b/drivers/ata/pata_radisys.c +@@ -15,7 +15,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_rdc.c b/drivers/ata/pata_rdc.c +index 79a970f..521b213 100644 +--- a/drivers/ata/pata_rdc.c ++++ b/drivers/ata/pata_rdc.c +@@ -24,7 +24,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_rz1000.c b/drivers/ata/pata_rz1000.c +index 040b093..caedc90 100644 +--- a/drivers/ata/pata_rz1000.c ++++ b/drivers/ata/pata_rz1000.c +@@ -14,7 +14,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_sc1200.c b/drivers/ata/pata_sc1200.c +index ce2f828..96a232f 100644 +--- a/drivers/ata/pata_sc1200.c ++++ b/drivers/ata/pata_sc1200.c +@@ -32,7 +32,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c +index f35f15f..f1f5b5a 100644 +--- a/drivers/ata/pata_scc.c ++++ b/drivers/ata/pata_scc.c +@@ -35,7 +35,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_sch.c b/drivers/ata/pata_sch.c +index d3830c4..5a1cde0 100644 +--- a/drivers/ata/pata_sch.c ++++ b/drivers/ata/pata_sch.c +@@ -27,7 +27,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_serverworks.c b/drivers/ata/pata_serverworks.c +index 96c6a79..e27f31f 100644 +--- a/drivers/ata/pata_serverworks.c ++++ b/drivers/ata/pata_serverworks.c +@@ -34,7 +34,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_sil680.c b/drivers/ata/pata_sil680.c +index c4b0b07..73fe362 100644 +--- a/drivers/ata/pata_sil680.c ++++ b/drivers/ata/pata_sil680.c +@@ -25,7 +25,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c +index 1e83636..78d913a 100644 +--- a/drivers/ata/pata_sis.c ++++ b/drivers/ata/pata_sis.c +@@ -26,7 +26,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c +index 6816911..900f0e4 100644 +--- a/drivers/ata/pata_sl82c105.c ++++ b/drivers/ata/pata_sl82c105.c +@@ -19,7 +19,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_triflex.c b/drivers/ata/pata_triflex.c +index 94473da..7bc78e2 100644 +--- a/drivers/ata/pata_triflex.c ++++ b/drivers/ata/pata_triflex.c +@@ -36,7 +36,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c +index c3ab9a6..f6c9632 100644 +--- a/drivers/ata/pata_via.c ++++ b/drivers/ata/pata_via.c +@@ -55,7 +55,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/gfp.h> +diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c +index 8ea6e6a..f10631b 100644 +--- a/drivers/ata/pdc_adma.c ++++ b/drivers/ata/pdc_adma.c +@@ -36,7 +36,6 @@ + #include <linux/module.h> + #include <linux/gfp.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c +index 523524b..73510d0 100644 +--- a/drivers/ata/sata_dwc_460ex.c ++++ b/drivers/ata/sata_dwc_460ex.c +@@ -29,7 +29,6 @@ + + #include <linux/kernel.h> + #include <linux/module.h> +-#include <linux/init.h> + #include <linux/device.h> + #include <linux/of_address.h> + #include <linux/of_irq.h> +diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c +index b3b18d1..d4df0bf 100644 +--- a/drivers/ata/sata_highbank.c ++++ b/drivers/ata/sata_highbank.c +@@ -19,7 +19,6 @@ + #include <linux/kernel.h> + #include <linux/gfp.h> + #include <linux/module.h> +-#include <linux/init.h> + #include <linux/types.h> + #include <linux/err.h> + #include <linux/io.h> +diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c +index d74def8..ba5f271 100644 +--- a/drivers/ata/sata_nv.c ++++ b/drivers/ata/sata_nv.c +@@ -40,7 +40,6 @@ + #include <linux/module.h> + #include <linux/gfp.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c +index 97f4acb..3638887 100644 +--- a/drivers/ata/sata_promise.c ++++ b/drivers/ata/sata_promise.c +@@ -35,7 +35,6 @@ + #include <linux/module.h> + #include <linux/gfp.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c +index 3b0dd57..9a6bd4c 100644 +--- a/drivers/ata/sata_qstor.c ++++ b/drivers/ata/sata_qstor.c +@@ -31,7 +31,6 @@ + #include <linux/module.h> + #include <linux/gfp.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c +index b7695e8..3062f86 100644 +--- a/drivers/ata/sata_sil.c ++++ b/drivers/ata/sata_sil.c +@@ -37,7 +37,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c +index 1ad2f62..b513428 100644 +--- a/drivers/ata/sata_sis.c ++++ b/drivers/ata/sata_sis.c +@@ -33,7 +33,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c +index dc4f701..c630fa8 100644 +--- a/drivers/ata/sata_svw.c ++++ b/drivers/ata/sata_svw.c +@@ -39,7 +39,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c +index 9947010..6cd0312 100644 +--- a/drivers/ata/sata_sx4.c ++++ b/drivers/ata/sata_sx4.c +@@ -82,7 +82,6 @@ + #include <linux/module.h> + #include <linux/pci.h> + #include <linux/slab.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c +index 6d64891..08f98c3 100644 +--- a/drivers/ata/sata_uli.c ++++ b/drivers/ata/sata_uli.c +@@ -28,7 +28,6 @@ + #include <linux/module.h> + #include <linux/gfp.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c +index 87f056e..f72e842 100644 +--- a/drivers/ata/sata_via.c ++++ b/drivers/ata/sata_via.c +@@ -36,7 +36,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c +index 44f304b..29e847a 100644 +--- a/drivers/ata/sata_vsc.c ++++ b/drivers/ata/sata_vsc.c +@@ -37,7 +37,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0114-ahci-platform-Library-ise-ahci_probe-functionality.patch b/target/linux/ipq806x/patches/0114-ahci-platform-Library-ise-ahci_probe-functionality.patch new file mode 100644 index 0000000000..9b9c7afd47 --- /dev/null +++ b/target/linux/ipq806x/patches/0114-ahci-platform-Library-ise-ahci_probe-functionality.patch @@ -0,0 +1,341 @@ +From 8be4987340a101253fb871556894a002f1afb51f Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 22 Feb 2014 16:53:34 +0100 +Subject: [PATCH 114/182] ahci-platform: "Library-ise" ahci_probe + functionality + +ahci_probe consists of 3 steps: +1) Get resources (get mmio, clks, regulator) +2) Enable resources, handled by ahci_platform_enable_resouces +3) The more or less standard ahci-host controller init sequence + +This commit refactors step 1 and 3 into separate functions, so the platform +drivers for AHCI implementations which need a specific order in step 2, +and / or need to do some custom register poking at some time, can re-use +ahci-platform.c code without needing to copy and paste it. + +Note that ahci_platform_init_host's prototype takes the 3 non function +members of ahci_platform_data as arguments, the idea is that drivers using +the new exported utility functions will not use ahci_platform_data at all, +and hopefully in the future ahci_platform_data can go away entirely. + +tj: Minor comment formatting updates. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci_platform.c | 188 +++++++++++++++++++++++++++-------------- + include/linux/ahci_platform.h | 14 +++ + 2 files changed, 137 insertions(+), 65 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index a32df31..19e9eaa 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -188,64 +188,60 @@ void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) + } + EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); + +-static void ahci_put_clks(struct ahci_host_priv *hpriv) ++static void ahci_platform_put_resources(struct device *dev, void *res) + { ++ struct ahci_host_priv *hpriv = res; + int c; + + for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) + clk_put(hpriv->clks[c]); + } + +-static int ahci_probe(struct platform_device *pdev) ++/** ++ * ahci_platform_get_resources - Get platform resources ++ * @pdev: platform device to get resources for ++ * ++ * This function allocates an ahci_host_priv struct, and gets the following ++ * resources, storing a reference to them inside the returned struct: ++ * ++ * 1) mmio registers (IORESOURCE_MEM 0, mandatory) ++ * 2) regulator for controlling the targets power (optional) ++ * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, ++ * or for non devicetree enabled platforms a single clock ++ * ++ * RETURNS: ++ * The allocated ahci_host_priv on success, otherwise an ERR_PTR value ++ */ ++struct ahci_host_priv *ahci_platform_get_resources( ++ struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +- struct ahci_platform_data *pdata = dev_get_platdata(dev); +- const struct platform_device_id *id = platform_get_device_id(pdev); +- struct ata_port_info pi = ahci_port_info[id ? id->driver_data : 0]; +- const struct ata_port_info *ppi[] = { &pi, NULL }; + struct ahci_host_priv *hpriv; +- struct ata_host *host; +- struct resource *mem; + struct clk *clk; +- int irq; +- int n_ports; +- int i; +- int rc; ++ int i, rc = -ENOMEM; + +- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- if (!mem) { +- dev_err(dev, "no mmio space\n"); +- return -EINVAL; +- } +- +- irq = platform_get_irq(pdev, 0); +- if (irq <= 0) { +- dev_err(dev, "no irq\n"); +- return -EINVAL; +- } ++ if (!devres_open_group(dev, NULL, GFP_KERNEL)) ++ return ERR_PTR(-ENOMEM); + +- if (pdata && pdata->ata_port_info) +- pi = *pdata->ata_port_info; +- +- hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); +- if (!hpriv) { +- dev_err(dev, "can't alloc ahci_host_priv\n"); +- return -ENOMEM; +- } ++ hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv), ++ GFP_KERNEL); ++ if (!hpriv) ++ goto err_out; + +- hpriv->flags |= (unsigned long)pi.private_data; ++ devres_add(dev, hpriv); + +- hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem)); ++ hpriv->mmio = devm_ioremap_resource(dev, ++ platform_get_resource(pdev, IORESOURCE_MEM, 0)); + if (!hpriv->mmio) { +- dev_err(dev, "can't map %pR\n", mem); +- return -ENOMEM; ++ dev_err(dev, "no mmio space\n"); ++ goto err_out; + } + + hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); + if (IS_ERR(hpriv->target_pwr)) { + rc = PTR_ERR(hpriv->target_pwr); + if (rc == -EPROBE_DEFER) +- return -EPROBE_DEFER; ++ goto err_out; + hpriv->target_pwr = NULL; + } + +@@ -264,33 +260,59 @@ static int ahci_probe(struct platform_device *pdev) + if (IS_ERR(clk)) { + rc = PTR_ERR(clk); + if (rc == -EPROBE_DEFER) +- goto free_clk; ++ goto err_out; + break; + } + hpriv->clks[i] = clk; + } + +- rc = ahci_platform_enable_resources(hpriv); +- if (rc) +- goto free_clk; ++ devres_remove_group(dev, NULL); ++ return hpriv; + +- /* +- * Some platforms might need to prepare for mmio region access, +- * which could be done in the following init call. So, the mmio +- * region shouldn't be accessed before init (if provided) has +- * returned successfully. +- */ +- if (pdata && pdata->init) { +- rc = pdata->init(dev, hpriv->mmio); +- if (rc) +- goto disable_resources; +- } ++err_out: ++ devres_release_group(dev, NULL); ++ return ERR_PTR(rc); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_get_resources); ++ ++/** ++ * ahci_platform_init_host - Bring up an ahci-platform host ++ * @pdev: platform device pointer for the host ++ * @hpriv: ahci-host private data for the host ++ * @pi_template: template for the ata_port_info to use ++ * @force_port_map: param passed to ahci_save_initial_config ++ * @mask_port_map: param passed to ahci_save_initial_config ++ * ++ * This function does all the usual steps needed to bring up an ++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.) ++ * must be initialized / enabled before calling this. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_init_host(struct platform_device *pdev, ++ struct ahci_host_priv *hpriv, ++ const struct ata_port_info *pi_template, ++ unsigned int force_port_map, ++ unsigned int mask_port_map) ++{ ++ struct device *dev = &pdev->dev; ++ struct ata_port_info pi = *pi_template; ++ const struct ata_port_info *ppi[] = { &pi, NULL }; ++ struct ata_host *host; ++ int i, irq, n_ports, rc; + +- ahci_save_initial_config(dev, hpriv, +- pdata ? pdata->force_port_map : 0, +- pdata ? pdata->mask_port_map : 0); ++ irq = platform_get_irq(pdev, 0); ++ if (irq <= 0) { ++ dev_err(dev, "no irq\n"); ++ return -EINVAL; ++ } + + /* prepare host */ ++ hpriv->flags |= (unsigned long)pi.private_data; ++ ++ ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map); ++ + if (hpriv->cap & HOST_CAP_NCQ) + pi.flags |= ATA_FLAG_NCQ; + +@@ -307,10 +329,8 @@ static int ahci_probe(struct platform_device *pdev) + n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); + + host = ata_host_alloc_pinfo(dev, ppi, n_ports); +- if (!host) { +- rc = -ENOMEM; +- goto pdata_exit; +- } ++ if (!host) ++ return -ENOMEM; + + host->private_data = hpriv; + +@@ -325,7 +345,8 @@ static int ahci_probe(struct platform_device *pdev) + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + +- ata_port_desc(ap, "mmio %pR", mem); ++ ata_port_desc(ap, "mmio %pR", ++ platform_get_resource(pdev, IORESOURCE_MEM, 0)); + ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); + + /* set enclosure management message type */ +@@ -339,13 +360,53 @@ static int ahci_probe(struct platform_device *pdev) + + rc = ahci_reset_controller(host); + if (rc) +- goto pdata_exit; ++ return rc; + + ahci_init_controller(host); + ahci_print_info(host, "platform"); + +- rc = ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, +- &ahci_platform_sht); ++ return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, ++ &ahci_platform_sht); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_init_host); ++ ++static int ahci_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct ahci_platform_data *pdata = dev_get_platdata(dev); ++ const struct platform_device_id *id = platform_get_device_id(pdev); ++ const struct ata_port_info *pi_template; ++ struct ahci_host_priv *hpriv; ++ int rc; ++ ++ hpriv = ahci_platform_get_resources(pdev); ++ if (IS_ERR(hpriv)) ++ return PTR_ERR(hpriv); ++ ++ rc = ahci_platform_enable_resources(hpriv); ++ if (rc) ++ return rc; ++ ++ /* ++ * Some platforms might need to prepare for mmio region access, ++ * which could be done in the following init call. So, the mmio ++ * region shouldn't be accessed before init (if provided) has ++ * returned successfully. ++ */ ++ if (pdata && pdata->init) { ++ rc = pdata->init(dev, hpriv->mmio); ++ if (rc) ++ goto disable_resources; ++ } ++ ++ if (pdata && pdata->ata_port_info) ++ pi_template = pdata->ata_port_info; ++ else ++ pi_template = &ahci_port_info[id ? id->driver_data : 0]; ++ ++ rc = ahci_platform_init_host(pdev, hpriv, pi_template, ++ pdata ? pdata->force_port_map : 0, ++ pdata ? pdata->mask_port_map : 0); + if (rc) + goto pdata_exit; + +@@ -355,8 +416,6 @@ pdata_exit: + pdata->exit(dev); + disable_resources: + ahci_platform_disable_resources(hpriv); +-free_clk: +- ahci_put_clks(hpriv); + return rc; + } + +@@ -370,7 +429,6 @@ static void ahci_host_stop(struct ata_host *host) + pdata->exit(dev); + + ahci_platform_disable_resources(hpriv); +- ahci_put_clks(hpriv); + } + + #ifdef CONFIG_PM_SLEEP +diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h +index b674b01..b80c51c 100644 +--- a/include/linux/ahci_platform.h ++++ b/include/linux/ahci_platform.h +@@ -20,7 +20,14 @@ + struct device; + struct ata_port_info; + struct ahci_host_priv; ++struct platform_device; + ++/* ++ * Note ahci_platform_data is deprecated, it is only kept around for use ++ * by the old da850 and spear13xx ahci code. ++ * New drivers should instead declare their own platform_driver struct, and ++ * use ahci_platform* functions in their own probe, suspend and resume methods. ++ */ + struct ahci_platform_data { + int (*init)(struct device *dev, void __iomem *addr); + void (*exit)(struct device *dev); +@@ -35,5 +42,12 @@ int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); + void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); + int ahci_platform_enable_resources(struct ahci_host_priv *hpriv); + void ahci_platform_disable_resources(struct ahci_host_priv *hpriv); ++struct ahci_host_priv *ahci_platform_get_resources( ++ struct platform_device *pdev); ++int ahci_platform_init_host(struct platform_device *pdev, ++ struct ahci_host_priv *hpriv, ++ const struct ata_port_info *pi_template, ++ unsigned int force_port_map, ++ unsigned int mask_port_map); + + #endif /* _AHCI_PLATFORM_H */ +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0115-ahci-platform-Library-ise-suspend-resume-functionali.patch b/target/linux/ipq806x/patches/0115-ahci-platform-Library-ise-suspend-resume-functionali.patch new file mode 100644 index 0000000000..9a9cb074e8 --- /dev/null +++ b/target/linux/ipq806x/patches/0115-ahci-platform-Library-ise-suspend-resume-functionali.patch @@ -0,0 +1,180 @@ +From 8121ed9c7ea2d56f5e0a98c2db396214f85d7485 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 22 Feb 2014 16:53:35 +0100 +Subject: [PATCH 115/182] ahci-platform: "Library-ise" suspend / resume + functionality + +Split suspend / resume code into host suspend / resume functionality and +resource enable / disabling phases, and export the new suspend_ / resume_host +functions. + +tj: Minor comment formatting updates. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci_platform.c | 97 ++++++++++++++++++++++++++++++++++------- + include/linux/ahci_platform.h | 5 +++ + 2 files changed, 87 insertions(+), 15 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 19e9eaa..01f7bbe 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -432,14 +432,23 @@ static void ahci_host_stop(struct ata_host *host) + } + + #ifdef CONFIG_PM_SLEEP +-static int ahci_suspend(struct device *dev) ++/** ++ * ahci_platform_suspend_host - Suspend an ahci-platform host ++ * @dev: device pointer for the host ++ * ++ * This function does all the usual steps needed to suspend an ++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.) ++ * must be disabled after calling this. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_suspend_host(struct device *dev) + { +- struct ahci_platform_data *pdata = dev_get_platdata(dev); + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + u32 ctl; +- int rc; + + if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { + dev_err(dev, "firmware update required for suspend/resume\n"); +@@ -456,7 +465,58 @@ static int ahci_suspend(struct device *dev) + writel(ctl, mmio + HOST_CTL); + readl(mmio + HOST_CTL); /* flush */ + +- rc = ata_host_suspend(host, PMSG_SUSPEND); ++ return ata_host_suspend(host, PMSG_SUSPEND); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_suspend_host); ++ ++/** ++ * ahci_platform_resume_host - Resume an ahci-platform host ++ * @dev: device pointer for the host ++ * ++ * This function does all the usual steps needed to resume an ahci-platform ++ * host, note any necessary resources (ie clks, phy, etc.) must be ++ * initialized / enabled before calling this. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_resume_host(struct device *dev) ++{ ++ struct ata_host *host = dev_get_drvdata(dev); ++ int rc; ++ ++ if (dev->power.power_state.event == PM_EVENT_SUSPEND) { ++ rc = ahci_reset_controller(host); ++ if (rc) ++ return rc; ++ ++ ahci_init_controller(host); ++ } ++ ++ ata_host_resume(host); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_resume_host); ++ ++/** ++ * ahci_platform_suspend - Suspend an ahci-platform device ++ * @dev: the platform device to suspend ++ * ++ * This function suspends the host associated with the device, followed by ++ * disabling all the resources of the device. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_suspend(struct device *dev) ++{ ++ struct ahci_platform_data *pdata = dev_get_platdata(dev); ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ int rc; ++ ++ rc = ahci_platform_suspend_host(dev); + if (rc) + return rc; + +@@ -467,8 +527,19 @@ static int ahci_suspend(struct device *dev) + + return 0; + } ++EXPORT_SYMBOL_GPL(ahci_platform_suspend); + +-static int ahci_resume(struct device *dev) ++/** ++ * ahci_platform_resume - Resume an ahci-platform device ++ * @dev: the platform device to resume ++ * ++ * This function enables all the resources of the device followed by ++ * resuming the host associated with the device. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_resume(struct device *dev) + { + struct ahci_platform_data *pdata = dev_get_platdata(dev); + struct ata_host *host = dev_get_drvdata(dev); +@@ -485,15 +556,9 @@ static int ahci_resume(struct device *dev) + goto disable_resources; + } + +- if (dev->power.power_state.event == PM_EVENT_SUSPEND) { +- rc = ahci_reset_controller(host); +- if (rc) +- goto disable_resources; +- +- ahci_init_controller(host); +- } +- +- ata_host_resume(host); ++ rc = ahci_platform_resume_host(dev); ++ if (rc) ++ goto disable_resources; + + return 0; + +@@ -502,9 +567,11 @@ disable_resources: + + return rc; + } ++EXPORT_SYMBOL_GPL(ahci_platform_resume); + #endif + +-static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_suspend, ahci_resume); ++static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend, ++ ahci_platform_resume); + + static const struct of_device_id ahci_of_match[] = { + { .compatible = "snps,spear-ahci", }, +diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h +index b80c51c..542f268 100644 +--- a/include/linux/ahci_platform.h ++++ b/include/linux/ahci_platform.h +@@ -50,4 +50,9 @@ int ahci_platform_init_host(struct platform_device *pdev, + unsigned int force_port_map, + unsigned int mask_port_map); + ++int ahci_platform_suspend_host(struct device *dev); ++int ahci_platform_resume_host(struct device *dev); ++int ahci_platform_suspend(struct device *dev); ++int ahci_platform_resume(struct device *dev); ++ + #endif /* _AHCI_PLATFORM_H */ +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0116-ata-ahci_platform-Add-DT-compatible-for-Synopsis-DWC.patch b/target/linux/ipq806x/patches/0116-ata-ahci_platform-Add-DT-compatible-for-Synopsis-DWC.patch new file mode 100644 index 0000000000..72c2c2e07d --- /dev/null +++ b/target/linux/ipq806x/patches/0116-ata-ahci_platform-Add-DT-compatible-for-Synopsis-DWC.patch @@ -0,0 +1,32 @@ +From bab3492791ade083c172a898b6f7a3692ffb89d5 Mon Sep 17 00:00:00 2001 +From: Roger Quadros <rogerq@ti.com> +Date: Sat, 22 Feb 2014 16:53:38 +0100 +Subject: [PATCH 116/182] ata: ahci_platform: Add DT compatible for Synopsis + DWC AHCI controller + +Add compatible string "snps,dwc-ahci", which should be used +for Synopsis Designware SATA cores. e.g. on TI OMAP5 and DRA7 platforms. + +Signed-off-by: Roger Quadros <rogerq@ti.com> +Reviewed-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci_platform.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 01f7bbe..968e7d9 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -577,6 +577,7 @@ static const struct of_device_id ahci_of_match[] = { + { .compatible = "snps,spear-ahci", }, + { .compatible = "snps,exynos5440-ahci", }, + { .compatible = "ibm,476gtr-ahci", }, ++ { .compatible = "snps,dwc-ahci", }, + {}, + }; + MODULE_DEVICE_TABLE(of, ahci_of_match); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0117-ata-ahci_platform-Manage-SATA-PHY.patch b/target/linux/ipq806x/patches/0117-ata-ahci_platform-Manage-SATA-PHY.patch new file mode 100644 index 0000000000..fab7e57635 --- /dev/null +++ b/target/linux/ipq806x/patches/0117-ata-ahci_platform-Manage-SATA-PHY.patch @@ -0,0 +1,141 @@ +From 05b9b8d1035944af25694d2ebe53077cf1ccb067 Mon Sep 17 00:00:00 2001 +From: Roger Quadros <rogerq@ti.com> +Date: Sat, 22 Feb 2014 16:53:40 +0100 +Subject: [PATCH 117/182] ata: ahci_platform: Manage SATA PHY + +Some platforms have a PHY hooked up to the SATA controller. The PHY +needs to be initialized and powered up for SATA to work. We do that +using the PHY framework. + +tj: Minor comment formatting updates. + +CC: Balaji T K <balajitk@ti.com> +Signed-off-by: Roger Quadros <rogerq@ti.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo<tj@kernel.org> +--- + drivers/ata/ahci.h | 2 ++ + drivers/ata/ahci_platform.c | 47 +++++++++++++++++++++++++++++++++++++++++-- + 2 files changed, 47 insertions(+), 2 deletions(-) + +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index bf8100c..3ab7ac9 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -37,6 +37,7 @@ + + #include <linux/clk.h> + #include <linux/libata.h> ++#include <linux/phy/phy.h> + #include <linux/regulator/consumer.h> + + /* Enclosure Management Control */ +@@ -325,6 +326,7 @@ struct ahci_host_priv { + u32 em_msg_type; /* EM message type */ + struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ + struct regulator *target_pwr; /* Optional */ ++ struct phy *phy; /* If platform uses phy */ + void *plat_data; /* Other platform data */ + /* + * Optional ahci_start_engine override, if not set this gets set to the +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 968e7d9..243dde3 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -22,6 +22,7 @@ + #include <linux/platform_device.h> + #include <linux/libata.h> + #include <linux/ahci_platform.h> ++#include <linux/phy/phy.h> + #include "ahci.h" + + static void ahci_host_stop(struct ata_host *host); +@@ -140,6 +141,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); + * following order: + * 1) Regulator + * 2) Clocks (through ahci_platform_enable_clks) ++ * 3) Phy + * + * If resource enabling fails at any point the previous enabled resources + * are disabled in reverse order. +@@ -161,8 +163,23 @@ int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + if (rc) + goto disable_regulator; + ++ if (hpriv->phy) { ++ rc = phy_init(hpriv->phy); ++ if (rc) ++ goto disable_clks; ++ ++ rc = phy_power_on(hpriv->phy); ++ if (rc) { ++ phy_exit(hpriv->phy); ++ goto disable_clks; ++ } ++ } ++ + return 0; + ++disable_clks: ++ ahci_platform_disable_clks(hpriv); ++ + disable_regulator: + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); +@@ -176,11 +193,17 @@ EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); + * + * This function disables all ahci_platform managed resources in the + * following order: +- * 1) Clocks (through ahci_platform_disable_clks) +- * 2) Regulator ++ * 1) Phy ++ * 2) Clocks (through ahci_platform_disable_clks) ++ * 3) Regulator + */ + void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) + { ++ if (hpriv->phy) { ++ phy_power_off(hpriv->phy); ++ phy_exit(hpriv->phy); ++ } ++ + ahci_platform_disable_clks(hpriv); + + if (hpriv->target_pwr) +@@ -208,6 +231,7 @@ static void ahci_platform_put_resources(struct device *dev, void *res) + * 2) regulator for controlling the targets power (optional) + * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, + * or for non devicetree enabled platforms a single clock ++ * 4) phy (optional) + * + * RETURNS: + * The allocated ahci_host_priv on success, otherwise an ERR_PTR value +@@ -266,6 +290,25 @@ struct ahci_host_priv *ahci_platform_get_resources( + hpriv->clks[i] = clk; + } + ++ hpriv->phy = devm_phy_get(dev, "sata-phy"); ++ if (IS_ERR(hpriv->phy)) { ++ rc = PTR_ERR(hpriv->phy); ++ switch (rc) { ++ case -ENODEV: ++ case -ENOSYS: ++ /* continue normally */ ++ hpriv->phy = NULL; ++ break; ++ ++ case -EPROBE_DEFER: ++ goto err_out; ++ ++ default: ++ dev_err(dev, "couldn't get sata-phy\n"); ++ goto err_out; ++ } ++ } ++ + devres_remove_group(dev, NULL); + return hpriv; + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0118-ata-ahci_platform-runtime-resume-the-device-before-u.patch b/target/linux/ipq806x/patches/0118-ata-ahci_platform-runtime-resume-the-device-before-u.patch new file mode 100644 index 0000000000..2f3e707c31 --- /dev/null +++ b/target/linux/ipq806x/patches/0118-ata-ahci_platform-runtime-resume-the-device-before-u.patch @@ -0,0 +1,86 @@ +From abe309ab531f22b9b89329bd825606f6b68a95a1 Mon Sep 17 00:00:00 2001 +From: Roger Quadros <rogerq@ti.com> +Date: Sat, 22 Feb 2014 16:53:41 +0100 +Subject: [PATCH 118/182] ata: ahci_platform: runtime resume the device before + use + +On OMAP platforms the device needs to be runtime resumed before it can +be accessed. The OMAP HWMOD framework takes care of enabling the +module and its resources based on the device's runtime PM state. + +In this patch we runtime resume during .probe() and runtime suspend +after .remove(). + +We also update the runtime PM state during .resume(). + +CC: Balaji T K <balajitk@ti.com> +Signed-off-by: Roger Quadros <rogerq@ti.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci.h | 1 + + drivers/ata/ahci_platform.c | 15 +++++++++++++++ + 2 files changed, 16 insertions(+) + +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index 3ab7ac9..51af275 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -324,6 +324,7 @@ struct ahci_host_priv { + u32 em_loc; /* enclosure management location */ + u32 em_buf_sz; /* EM buffer size in byte */ + u32 em_msg_type; /* EM message type */ ++ bool got_runtime_pm; /* Did we do pm_runtime_get? */ + struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ + struct regulator *target_pwr; /* Optional */ + struct phy *phy; /* If platform uses phy */ +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 243dde3..fc32863 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -23,6 +23,7 @@ + #include <linux/libata.h> + #include <linux/ahci_platform.h> + #include <linux/phy/phy.h> ++#include <linux/pm_runtime.h> + #include "ahci.h" + + static void ahci_host_stop(struct ata_host *host); +@@ -216,6 +217,11 @@ static void ahci_platform_put_resources(struct device *dev, void *res) + struct ahci_host_priv *hpriv = res; + int c; + ++ if (hpriv->got_runtime_pm) { ++ pm_runtime_put_sync(dev); ++ pm_runtime_disable(dev); ++ } ++ + for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) + clk_put(hpriv->clks[c]); + } +@@ -309,6 +315,10 @@ struct ahci_host_priv *ahci_platform_get_resources( + } + } + ++ pm_runtime_enable(dev); ++ pm_runtime_get_sync(dev); ++ hpriv->got_runtime_pm = true; ++ + devres_remove_group(dev, NULL); + return hpriv; + +@@ -603,6 +613,11 @@ int ahci_platform_resume(struct device *dev) + if (rc) + goto disable_resources; + ++ /* We resumed so update PM runtime state */ ++ pm_runtime_disable(dev); ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ + return 0; + + disable_resources: +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0119-ahci_platform-Drop-support-for-ahci-strict-platform-.patch b/target/linux/ipq806x/patches/0119-ahci_platform-Drop-support-for-ahci-strict-platform-.patch new file mode 100644 index 0000000000..efccae45af --- /dev/null +++ b/target/linux/ipq806x/patches/0119-ahci_platform-Drop-support-for-ahci-strict-platform-.patch @@ -0,0 +1,54 @@ +From 474c436aebe260b0e6e7042ce5291137b7c87b57 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 22 Feb 2014 17:22:53 +0100 +Subject: [PATCH 119/182] ahci_platform: Drop support for ahci-strict platform + device type + +I've done a grep over the entire kernel tree and nothing is using this +(anymore?). + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci_platform.c | 11 ----------- + 1 file changed, 11 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index fc32863..d3d2bad 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -31,7 +31,6 @@ static void ahci_host_stop(struct ata_host *host); + enum ahci_type { + AHCI, /* standard platform ahci */ + IMX53_AHCI, /* ahci on i.mx53 */ +- STRICT_AHCI, /* delayed DMA engine start */ + }; + + static struct platform_device_id ahci_devtype[] = { +@@ -42,9 +41,6 @@ static struct platform_device_id ahci_devtype[] = { + .name = "imx53-ahci", + .driver_data = IMX53_AHCI, + }, { +- .name = "strict-ahci", +- .driver_data = STRICT_AHCI, +- }, { + /* sentinel */ + } + }; +@@ -75,13 +71,6 @@ static const struct ata_port_info ahci_port_info[] = { + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_platform_retry_srst_ops, + }, +- [STRICT_AHCI] = { +- AHCI_HFLAGS (AHCI_HFLAG_DELAY_ENGINE), +- .flags = AHCI_FLAG_COMMON, +- .pio_mask = ATA_PIO4, +- .udma_mask = ATA_UDMA6, +- .port_ops = &ahci_platform_ops, +- }, + }; + + static struct scsi_host_template ahci_platform_sht = { +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0120-ahci_platform-Drop-support-for-imx53-ahci-platform-d.patch b/target/linux/ipq806x/patches/0120-ahci_platform-Drop-support-for-imx53-ahci-platform-d.patch new file mode 100644 index 0000000000..8d0f64ce35 --- /dev/null +++ b/target/linux/ipq806x/patches/0120-ahci_platform-Drop-support-for-imx53-ahci-platform-d.patch @@ -0,0 +1,110 @@ +From 0d153bacb749d9291324ef0282ea9d235baca499 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 22 Feb 2014 17:22:54 +0100 +Subject: [PATCH 120/182] ahci_platform: Drop support for imx53-ahci platform + device type + +Since the 3.13 release the ahci_imx driver has proper devicetree enabled +support for ahci on imx53 and that is used instead of the old board file +created imx53-ahci platform device. + +Note this patch also complete drops the id-table, an id-table is not needed +for a single id platform driver, the name field in the driver struct suffices. + +And the code already has an explicit "MODULE_ALIAS("platform:ahci");" so the +id-table is not needed for that either. + +Cc: Marek Vasut <marex@denx.de> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci_platform.c | 46 ++++++------------------------------------- + 1 file changed, 6 insertions(+), 40 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index d3d2bad..8fab4bf 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -28,49 +28,17 @@ + + static void ahci_host_stop(struct ata_host *host); + +-enum ahci_type { +- AHCI, /* standard platform ahci */ +- IMX53_AHCI, /* ahci on i.mx53 */ +-}; +- +-static struct platform_device_id ahci_devtype[] = { +- { +- .name = "ahci", +- .driver_data = AHCI, +- }, { +- .name = "imx53-ahci", +- .driver_data = IMX53_AHCI, +- }, { +- /* sentinel */ +- } +-}; +-MODULE_DEVICE_TABLE(platform, ahci_devtype); +- + struct ata_port_operations ahci_platform_ops = { + .inherits = &ahci_ops, + .host_stop = ahci_host_stop, + }; + EXPORT_SYMBOL_GPL(ahci_platform_ops); + +-static struct ata_port_operations ahci_platform_retry_srst_ops = { +- .inherits = &ahci_pmp_retry_srst_ops, +- .host_stop = ahci_host_stop, +-}; +- +-static const struct ata_port_info ahci_port_info[] = { +- /* by features */ +- [AHCI] = { +- .flags = AHCI_FLAG_COMMON, +- .pio_mask = ATA_PIO4, +- .udma_mask = ATA_UDMA6, +- .port_ops = &ahci_platform_ops, +- }, +- [IMX53_AHCI] = { +- .flags = AHCI_FLAG_COMMON, +- .pio_mask = ATA_PIO4, +- .udma_mask = ATA_UDMA6, +- .port_ops = &ahci_platform_retry_srst_ops, +- }, ++static const struct ata_port_info ahci_port_info = { ++ .flags = AHCI_FLAG_COMMON, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_platform_ops, + }; + + static struct scsi_host_template ahci_platform_sht = { +@@ -416,7 +384,6 @@ static int ahci_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + struct ahci_platform_data *pdata = dev_get_platdata(dev); +- const struct platform_device_id *id = platform_get_device_id(pdev); + const struct ata_port_info *pi_template; + struct ahci_host_priv *hpriv; + int rc; +@@ -444,7 +411,7 @@ static int ahci_probe(struct platform_device *pdev) + if (pdata && pdata->ata_port_info) + pi_template = pdata->ata_port_info; + else +- pi_template = &ahci_port_info[id ? id->driver_data : 0]; ++ pi_template = &ahci_port_info; + + rc = ahci_platform_init_host(pdev, hpriv, pi_template, + pdata ? pdata->force_port_map : 0, +@@ -638,7 +605,6 @@ static struct platform_driver ahci_driver = { + .of_match_table = ahci_of_match, + .pm = &ahci_pm_ops, + }, +- .id_table = ahci_devtype, + }; + module_platform_driver(ahci_driver); + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0121-ahci_platform-Drop-unused-ahci_platform_data-members.patch b/target/linux/ipq806x/patches/0121-ahci_platform-Drop-unused-ahci_platform_data-members.patch new file mode 100644 index 0000000000..e8f2378b02 --- /dev/null +++ b/target/linux/ipq806x/patches/0121-ahci_platform-Drop-unused-ahci_platform_data-members.patch @@ -0,0 +1,62 @@ +From 438df60c6bff1b12a407fefcfb8a2e5d9b29ac6a Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 22 Feb 2014 17:22:55 +0100 +Subject: [PATCH 121/182] ahci_platform: Drop unused ahci_platform_data + members + +These members are not used anywhere, and in the future we want +ahci_platform_data to go away entirely so there is no reason to keep these +around. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci_platform.c | 10 +--------- + include/linux/ahci_platform.h | 3 --- + 2 files changed, 1 insertion(+), 12 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 8fab4bf..db24d2a 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -384,7 +384,6 @@ static int ahci_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + struct ahci_platform_data *pdata = dev_get_platdata(dev); +- const struct ata_port_info *pi_template; + struct ahci_host_priv *hpriv; + int rc; + +@@ -408,14 +407,7 @@ static int ahci_probe(struct platform_device *pdev) + goto disable_resources; + } + +- if (pdata && pdata->ata_port_info) +- pi_template = pdata->ata_port_info; +- else +- pi_template = &ahci_port_info; +- +- rc = ahci_platform_init_host(pdev, hpriv, pi_template, +- pdata ? pdata->force_port_map : 0, +- pdata ? pdata->mask_port_map : 0); ++ rc = ahci_platform_init_host(pdev, hpriv, &ahci_port_info, 0, 0); + if (rc) + goto pdata_exit; + +diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h +index 542f268..1f16d50 100644 +--- a/include/linux/ahci_platform.h ++++ b/include/linux/ahci_platform.h +@@ -33,9 +33,6 @@ struct ahci_platform_data { + void (*exit)(struct device *dev); + int (*suspend)(struct device *dev); + int (*resume)(struct device *dev); +- const struct ata_port_info *ata_port_info; +- unsigned int force_port_map; +- unsigned int mask_port_map; + }; + + int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0122-ata-ahci_platform-fix-devm_ioremap_resource-return-v.patch b/target/linux/ipq806x/patches/0122-ata-ahci_platform-fix-devm_ioremap_resource-return-v.patch new file mode 100644 index 0000000000..1a5925b48a --- /dev/null +++ b/target/linux/ipq806x/patches/0122-ata-ahci_platform-fix-devm_ioremap_resource-return-v.patch @@ -0,0 +1,46 @@ +From 0db91c83bcb5f7307aaa16c093b8c6bb3f44be52 Mon Sep 17 00:00:00 2001 +From: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> +Date: Fri, 14 Mar 2014 18:22:09 +0100 +Subject: [PATCH 122/182] ata: ahci_platform: fix devm_ioremap_resource() + return value checking + +devm_ioremap_resource() returns a pointer to the remapped memory or +an ERR_PTR() encoded error code on failure. Fix the check inside +ahci_platform_get_resources() accordingly. + +Also while at it remove a needless line break. + +Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci_platform.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index db24d2a..70fbf66 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -199,8 +199,7 @@ static void ahci_platform_put_resources(struct device *dev, void *res) + * RETURNS: + * The allocated ahci_host_priv on success, otherwise an ERR_PTR value + */ +-struct ahci_host_priv *ahci_platform_get_resources( +- struct platform_device *pdev) ++struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + struct ahci_host_priv *hpriv; +@@ -219,8 +218,9 @@ struct ahci_host_priv *ahci_platform_get_resources( + + hpriv->mmio = devm_ioremap_resource(dev, + platform_get_resource(pdev, IORESOURCE_MEM, 0)); +- if (!hpriv->mmio) { ++ if (IS_ERR(hpriv->mmio)) { + dev_err(dev, "no mmio space\n"); ++ rc = PTR_ERR(hpriv->mmio); + goto err_out; + } + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0123-ata-ahci_platform-fix-ahci_platform_data-suspend-met.patch b/target/linux/ipq806x/patches/0123-ata-ahci_platform-fix-ahci_platform_data-suspend-met.patch new file mode 100644 index 0000000000..ce1d840b90 --- /dev/null +++ b/target/linux/ipq806x/patches/0123-ata-ahci_platform-fix-ahci_platform_data-suspend-met.patch @@ -0,0 +1,50 @@ +From bc26554ffa0223015edb474a4877ec544167dfba Mon Sep 17 00:00:00 2001 +From: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> +Date: Tue, 25 Mar 2014 19:51:38 +0100 +Subject: [PATCH 123/182] ata: ahci_platform: fix ahci_platform_data->suspend + method handling + +Looking at ST SPEAr1340 AHCI code (the only user of the deprecated +pdata->suspend and pdata->resume) it is obvious the we should return +after calling pdata->suspend() only if the function have returned +non-zero return value. The code has been broken since commit 1e70c2 +("ata/ahci_platform: Add clock framework support"). Fix it. + +Cc: Viresh Kumar <viresh.linux@gmail.com> +Cc: Shiraz Hashim <shiraz.hashim@st.com> +Acked-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci_platform.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 70fbf66..7bd6adf 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -521,12 +521,19 @@ int ahci_platform_suspend(struct device *dev) + if (rc) + return rc; + +- if (pdata && pdata->suspend) +- return pdata->suspend(dev); ++ if (pdata && pdata->suspend) { ++ rc = pdata->suspend(dev); ++ if (rc) ++ goto resume_host; ++ } + + ahci_platform_disable_resources(hpriv); + + return 0; ++ ++resume_host: ++ ahci_platform_resume_host(dev); ++ return rc; + } + EXPORT_SYMBOL_GPL(ahci_platform_suspend); + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0124-ata-move-library-code-from-ahci_platform.c-to-libahc.patch b/target/linux/ipq806x/patches/0124-ata-move-library-code-from-ahci_platform.c-to-libahc.patch new file mode 100644 index 0000000000..bd45720661 --- /dev/null +++ b/target/linux/ipq806x/patches/0124-ata-move-library-code-from-ahci_platform.c-to-libahc.patch @@ -0,0 +1,1157 @@ +From 04800db1047afbf6701379435bff1a6fa64215f7 Mon Sep 17 00:00:00 2001 +From: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> +Date: Tue, 25 Mar 2014 19:51:39 +0100 +Subject: [PATCH 124/182] ata: move library code from ahci_platform.c to + libahci_platform.c + +Move AHCI platform library code from ahci_platform.c to +libahci_platform.c and fix dependencies for ahci_st, +ahci_imx and ahci_sunxi drivers. + +Acked-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> +Signed-off-by: Tejun Heo <tj@kernel.org> + +Conflicts: + drivers/ata/Kconfig + drivers/ata/Makefile +--- + drivers/ata/Kconfig | 2 +- + drivers/ata/Makefile | 4 +- + drivers/ata/ahci_platform.c | 515 -------------------------------------- + drivers/ata/libahci_platform.c | 541 ++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 544 insertions(+), 518 deletions(-) + create mode 100644 drivers/ata/libahci_platform.c + +diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig +index 868429a..dc950f3 100644 +--- a/drivers/ata/Kconfig ++++ b/drivers/ata/Kconfig +@@ -99,7 +99,7 @@ config SATA_AHCI_PLATFORM + + config AHCI_IMX + tristate "Freescale i.MX AHCI SATA support" +- depends on SATA_AHCI_PLATFORM && MFD_SYSCON ++ depends on MFD_SYSCON + help + This option enables support for the Freescale i.MX SoC's + onboard AHCI SATA. +diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile +index 46518c6..366b743 100644 +--- a/drivers/ata/Makefile ++++ b/drivers/ata/Makefile +@@ -4,13 +4,13 @@ obj-$(CONFIG_ATA) += libata.o + # non-SFF interface + obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o + obj-$(CONFIG_SATA_ACARD_AHCI) += acard-ahci.o libahci.o +-obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o ++obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o libahci_platform.o + obj-$(CONFIG_SATA_FSL) += sata_fsl.o + obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o + obj-$(CONFIG_SATA_SIL24) += sata_sil24.o + obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o + obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o +-obj-$(CONFIG_AHCI_IMX) += ahci_imx.o ++obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o + + # SFF w/ custom DMA + obj-$(CONFIG_PDC_ADMA) += pdc_adma.o +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 7bd6adf..ef67e79 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -12,28 +12,15 @@ + * any later version. + */ + +-#include <linux/clk.h> + #include <linux/kernel.h> +-#include <linux/gfp.h> + #include <linux/module.h> + #include <linux/pm.h> +-#include <linux/interrupt.h> + #include <linux/device.h> + #include <linux/platform_device.h> + #include <linux/libata.h> + #include <linux/ahci_platform.h> +-#include <linux/phy/phy.h> +-#include <linux/pm_runtime.h> + #include "ahci.h" + +-static void ahci_host_stop(struct ata_host *host); +- +-struct ata_port_operations ahci_platform_ops = { +- .inherits = &ahci_ops, +- .host_stop = ahci_host_stop, +-}; +-EXPORT_SYMBOL_GPL(ahci_platform_ops); +- + static const struct ata_port_info ahci_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, +@@ -41,345 +28,6 @@ static const struct ata_port_info ahci_port_info = { + .port_ops = &ahci_platform_ops, + }; + +-static struct scsi_host_template ahci_platform_sht = { +- AHCI_SHT("ahci_platform"), +-}; +- +-/** +- * ahci_platform_enable_clks - Enable platform clocks +- * @hpriv: host private area to store config values +- * +- * This function enables all the clks found in hpriv->clks, starting at +- * index 0. If any clk fails to enable it disables all the clks already +- * enabled in reverse order, and then returns an error. +- * +- * RETURNS: +- * 0 on success otherwise a negative error code +- */ +-int ahci_platform_enable_clks(struct ahci_host_priv *hpriv) +-{ +- int c, rc; +- +- for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) { +- rc = clk_prepare_enable(hpriv->clks[c]); +- if (rc) +- goto disable_unprepare_clk; +- } +- return 0; +- +-disable_unprepare_clk: +- while (--c >= 0) +- clk_disable_unprepare(hpriv->clks[c]); +- return rc; +-} +-EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); +- +-/** +- * ahci_platform_disable_clks - Disable platform clocks +- * @hpriv: host private area to store config values +- * +- * This function disables all the clks found in hpriv->clks, in reverse +- * order of ahci_platform_enable_clks (starting at the end of the array). +- */ +-void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) +-{ +- int c; +- +- for (c = AHCI_MAX_CLKS - 1; c >= 0; c--) +- if (hpriv->clks[c]) +- clk_disable_unprepare(hpriv->clks[c]); +-} +-EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); +- +-/** +- * ahci_platform_enable_resources - Enable platform resources +- * @hpriv: host private area to store config values +- * +- * This function enables all ahci_platform managed resources in the +- * following order: +- * 1) Regulator +- * 2) Clocks (through ahci_platform_enable_clks) +- * 3) Phy +- * +- * If resource enabling fails at any point the previous enabled resources +- * are disabled in reverse order. +- * +- * RETURNS: +- * 0 on success otherwise a negative error code +- */ +-int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) +-{ +- int rc; +- +- if (hpriv->target_pwr) { +- rc = regulator_enable(hpriv->target_pwr); +- if (rc) +- return rc; +- } +- +- rc = ahci_platform_enable_clks(hpriv); +- if (rc) +- goto disable_regulator; +- +- if (hpriv->phy) { +- rc = phy_init(hpriv->phy); +- if (rc) +- goto disable_clks; +- +- rc = phy_power_on(hpriv->phy); +- if (rc) { +- phy_exit(hpriv->phy); +- goto disable_clks; +- } +- } +- +- return 0; +- +-disable_clks: +- ahci_platform_disable_clks(hpriv); +- +-disable_regulator: +- if (hpriv->target_pwr) +- regulator_disable(hpriv->target_pwr); +- return rc; +-} +-EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); +- +-/** +- * ahci_platform_disable_resources - Disable platform resources +- * @hpriv: host private area to store config values +- * +- * This function disables all ahci_platform managed resources in the +- * following order: +- * 1) Phy +- * 2) Clocks (through ahci_platform_disable_clks) +- * 3) Regulator +- */ +-void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) +-{ +- if (hpriv->phy) { +- phy_power_off(hpriv->phy); +- phy_exit(hpriv->phy); +- } +- +- ahci_platform_disable_clks(hpriv); +- +- if (hpriv->target_pwr) +- regulator_disable(hpriv->target_pwr); +-} +-EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); +- +-static void ahci_platform_put_resources(struct device *dev, void *res) +-{ +- struct ahci_host_priv *hpriv = res; +- int c; +- +- if (hpriv->got_runtime_pm) { +- pm_runtime_put_sync(dev); +- pm_runtime_disable(dev); +- } +- +- for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) +- clk_put(hpriv->clks[c]); +-} +- +-/** +- * ahci_platform_get_resources - Get platform resources +- * @pdev: platform device to get resources for +- * +- * This function allocates an ahci_host_priv struct, and gets the following +- * resources, storing a reference to them inside the returned struct: +- * +- * 1) mmio registers (IORESOURCE_MEM 0, mandatory) +- * 2) regulator for controlling the targets power (optional) +- * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, +- * or for non devicetree enabled platforms a single clock +- * 4) phy (optional) +- * +- * RETURNS: +- * The allocated ahci_host_priv on success, otherwise an ERR_PTR value +- */ +-struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct ahci_host_priv *hpriv; +- struct clk *clk; +- int i, rc = -ENOMEM; +- +- if (!devres_open_group(dev, NULL, GFP_KERNEL)) +- return ERR_PTR(-ENOMEM); +- +- hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv), +- GFP_KERNEL); +- if (!hpriv) +- goto err_out; +- +- devres_add(dev, hpriv); +- +- hpriv->mmio = devm_ioremap_resource(dev, +- platform_get_resource(pdev, IORESOURCE_MEM, 0)); +- if (IS_ERR(hpriv->mmio)) { +- dev_err(dev, "no mmio space\n"); +- rc = PTR_ERR(hpriv->mmio); +- goto err_out; +- } +- +- hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); +- if (IS_ERR(hpriv->target_pwr)) { +- rc = PTR_ERR(hpriv->target_pwr); +- if (rc == -EPROBE_DEFER) +- goto err_out; +- hpriv->target_pwr = NULL; +- } +- +- for (i = 0; i < AHCI_MAX_CLKS; i++) { +- /* +- * For now we must use clk_get(dev, NULL) for the first clock, +- * because some platforms (da850, spear13xx) are not yet +- * converted to use devicetree for clocks. For new platforms +- * this is equivalent to of_clk_get(dev->of_node, 0). +- */ +- if (i == 0) +- clk = clk_get(dev, NULL); +- else +- clk = of_clk_get(dev->of_node, i); +- +- if (IS_ERR(clk)) { +- rc = PTR_ERR(clk); +- if (rc == -EPROBE_DEFER) +- goto err_out; +- break; +- } +- hpriv->clks[i] = clk; +- } +- +- hpriv->phy = devm_phy_get(dev, "sata-phy"); +- if (IS_ERR(hpriv->phy)) { +- rc = PTR_ERR(hpriv->phy); +- switch (rc) { +- case -ENODEV: +- case -ENOSYS: +- /* continue normally */ +- hpriv->phy = NULL; +- break; +- +- case -EPROBE_DEFER: +- goto err_out; +- +- default: +- dev_err(dev, "couldn't get sata-phy\n"); +- goto err_out; +- } +- } +- +- pm_runtime_enable(dev); +- pm_runtime_get_sync(dev); +- hpriv->got_runtime_pm = true; +- +- devres_remove_group(dev, NULL); +- return hpriv; +- +-err_out: +- devres_release_group(dev, NULL); +- return ERR_PTR(rc); +-} +-EXPORT_SYMBOL_GPL(ahci_platform_get_resources); +- +-/** +- * ahci_platform_init_host - Bring up an ahci-platform host +- * @pdev: platform device pointer for the host +- * @hpriv: ahci-host private data for the host +- * @pi_template: template for the ata_port_info to use +- * @force_port_map: param passed to ahci_save_initial_config +- * @mask_port_map: param passed to ahci_save_initial_config +- * +- * This function does all the usual steps needed to bring up an +- * ahci-platform host, note any necessary resources (ie clks, phy, etc.) +- * must be initialized / enabled before calling this. +- * +- * RETURNS: +- * 0 on success otherwise a negative error code +- */ +-int ahci_platform_init_host(struct platform_device *pdev, +- struct ahci_host_priv *hpriv, +- const struct ata_port_info *pi_template, +- unsigned int force_port_map, +- unsigned int mask_port_map) +-{ +- struct device *dev = &pdev->dev; +- struct ata_port_info pi = *pi_template; +- const struct ata_port_info *ppi[] = { &pi, NULL }; +- struct ata_host *host; +- int i, irq, n_ports, rc; +- +- irq = platform_get_irq(pdev, 0); +- if (irq <= 0) { +- dev_err(dev, "no irq\n"); +- return -EINVAL; +- } +- +- /* prepare host */ +- hpriv->flags |= (unsigned long)pi.private_data; +- +- ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map); +- +- if (hpriv->cap & HOST_CAP_NCQ) +- pi.flags |= ATA_FLAG_NCQ; +- +- if (hpriv->cap & HOST_CAP_PMP) +- pi.flags |= ATA_FLAG_PMP; +- +- ahci_set_em_messages(hpriv, &pi); +- +- /* CAP.NP sometimes indicate the index of the last enabled +- * port, at other times, that of the last possible port, so +- * determining the maximum port number requires looking at +- * both CAP.NP and port_map. +- */ +- n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); +- +- host = ata_host_alloc_pinfo(dev, ppi, n_ports); +- if (!host) +- return -ENOMEM; +- +- host->private_data = hpriv; +- +- if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) +- host->flags |= ATA_HOST_PARALLEL_SCAN; +- else +- dev_info(dev, "SSS flag set, parallel bus scan disabled\n"); +- +- if (pi.flags & ATA_FLAG_EM) +- ahci_reset_em(host); +- +- for (i = 0; i < host->n_ports; i++) { +- struct ata_port *ap = host->ports[i]; +- +- ata_port_desc(ap, "mmio %pR", +- platform_get_resource(pdev, IORESOURCE_MEM, 0)); +- ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); +- +- /* set enclosure management message type */ +- if (ap->flags & ATA_FLAG_EM) +- ap->em_message_type = hpriv->em_msg_type; +- +- /* disabled/not-implemented port */ +- if (!(hpriv->port_map & (1 << i))) +- ap->ops = &ata_dummy_port_ops; +- } +- +- rc = ahci_reset_controller(host); +- if (rc) +- return rc; +- +- ahci_init_controller(host); +- ahci_print_info(host, "platform"); +- +- return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, +- &ahci_platform_sht); +-} +-EXPORT_SYMBOL_GPL(ahci_platform_init_host); +- + static int ahci_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -420,169 +68,6 @@ disable_resources: + return rc; + } + +-static void ahci_host_stop(struct ata_host *host) +-{ +- struct device *dev = host->dev; +- struct ahci_platform_data *pdata = dev_get_platdata(dev); +- struct ahci_host_priv *hpriv = host->private_data; +- +- if (pdata && pdata->exit) +- pdata->exit(dev); +- +- ahci_platform_disable_resources(hpriv); +-} +- +-#ifdef CONFIG_PM_SLEEP +-/** +- * ahci_platform_suspend_host - Suspend an ahci-platform host +- * @dev: device pointer for the host +- * +- * This function does all the usual steps needed to suspend an +- * ahci-platform host, note any necessary resources (ie clks, phy, etc.) +- * must be disabled after calling this. +- * +- * RETURNS: +- * 0 on success otherwise a negative error code +- */ +-int ahci_platform_suspend_host(struct device *dev) +-{ +- struct ata_host *host = dev_get_drvdata(dev); +- struct ahci_host_priv *hpriv = host->private_data; +- void __iomem *mmio = hpriv->mmio; +- u32 ctl; +- +- if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { +- dev_err(dev, "firmware update required for suspend/resume\n"); +- return -EIO; +- } +- +- /* +- * AHCI spec rev1.1 section 8.3.3: +- * Software must disable interrupts prior to requesting a +- * transition of the HBA to D3 state. +- */ +- ctl = readl(mmio + HOST_CTL); +- ctl &= ~HOST_IRQ_EN; +- writel(ctl, mmio + HOST_CTL); +- readl(mmio + HOST_CTL); /* flush */ +- +- return ata_host_suspend(host, PMSG_SUSPEND); +-} +-EXPORT_SYMBOL_GPL(ahci_platform_suspend_host); +- +-/** +- * ahci_platform_resume_host - Resume an ahci-platform host +- * @dev: device pointer for the host +- * +- * This function does all the usual steps needed to resume an ahci-platform +- * host, note any necessary resources (ie clks, phy, etc.) must be +- * initialized / enabled before calling this. +- * +- * RETURNS: +- * 0 on success otherwise a negative error code +- */ +-int ahci_platform_resume_host(struct device *dev) +-{ +- struct ata_host *host = dev_get_drvdata(dev); +- int rc; +- +- if (dev->power.power_state.event == PM_EVENT_SUSPEND) { +- rc = ahci_reset_controller(host); +- if (rc) +- return rc; +- +- ahci_init_controller(host); +- } +- +- ata_host_resume(host); +- +- return 0; +-} +-EXPORT_SYMBOL_GPL(ahci_platform_resume_host); +- +-/** +- * ahci_platform_suspend - Suspend an ahci-platform device +- * @dev: the platform device to suspend +- * +- * This function suspends the host associated with the device, followed by +- * disabling all the resources of the device. +- * +- * RETURNS: +- * 0 on success otherwise a negative error code +- */ +-int ahci_platform_suspend(struct device *dev) +-{ +- struct ahci_platform_data *pdata = dev_get_platdata(dev); +- struct ata_host *host = dev_get_drvdata(dev); +- struct ahci_host_priv *hpriv = host->private_data; +- int rc; +- +- rc = ahci_platform_suspend_host(dev); +- if (rc) +- return rc; +- +- if (pdata && pdata->suspend) { +- rc = pdata->suspend(dev); +- if (rc) +- goto resume_host; +- } +- +- ahci_platform_disable_resources(hpriv); +- +- return 0; +- +-resume_host: +- ahci_platform_resume_host(dev); +- return rc; +-} +-EXPORT_SYMBOL_GPL(ahci_platform_suspend); +- +-/** +- * ahci_platform_resume - Resume an ahci-platform device +- * @dev: the platform device to resume +- * +- * This function enables all the resources of the device followed by +- * resuming the host associated with the device. +- * +- * RETURNS: +- * 0 on success otherwise a negative error code +- */ +-int ahci_platform_resume(struct device *dev) +-{ +- struct ahci_platform_data *pdata = dev_get_platdata(dev); +- struct ata_host *host = dev_get_drvdata(dev); +- struct ahci_host_priv *hpriv = host->private_data; +- int rc; +- +- rc = ahci_platform_enable_resources(hpriv); +- if (rc) +- return rc; +- +- if (pdata && pdata->resume) { +- rc = pdata->resume(dev); +- if (rc) +- goto disable_resources; +- } +- +- rc = ahci_platform_resume_host(dev); +- if (rc) +- goto disable_resources; +- +- /* We resumed so update PM runtime state */ +- pm_runtime_disable(dev); +- pm_runtime_set_active(dev); +- pm_runtime_enable(dev); +- +- return 0; +- +-disable_resources: +- ahci_platform_disable_resources(hpriv); +- +- return rc; +-} +-EXPORT_SYMBOL_GPL(ahci_platform_resume); +-#endif +- + static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend, + ahci_platform_resume); + +diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c +new file mode 100644 +index 0000000..7cb3a85 +--- /dev/null ++++ b/drivers/ata/libahci_platform.c +@@ -0,0 +1,541 @@ ++/* ++ * AHCI SATA platform library ++ * ++ * Copyright 2004-2005 Red Hat, Inc. ++ * Jeff Garzik <jgarzik@pobox.com> ++ * Copyright 2010 MontaVista Software, LLC. ++ * Anton Vorontsov <avorontsov@ru.mvista.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/kernel.h> ++#include <linux/gfp.h> ++#include <linux/module.h> ++#include <linux/pm.h> ++#include <linux/interrupt.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/libata.h> ++#include <linux/ahci_platform.h> ++#include <linux/phy/phy.h> ++#include <linux/pm_runtime.h> ++#include "ahci.h" ++ ++static void ahci_host_stop(struct ata_host *host); ++ ++struct ata_port_operations ahci_platform_ops = { ++ .inherits = &ahci_ops, ++ .host_stop = ahci_host_stop, ++}; ++EXPORT_SYMBOL_GPL(ahci_platform_ops); ++ ++static struct scsi_host_template ahci_platform_sht = { ++ AHCI_SHT("ahci_platform"), ++}; ++ ++/** ++ * ahci_platform_enable_clks - Enable platform clocks ++ * @hpriv: host private area to store config values ++ * ++ * This function enables all the clks found in hpriv->clks, starting at ++ * index 0. If any clk fails to enable it disables all the clks already ++ * enabled in reverse order, and then returns an error. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_enable_clks(struct ahci_host_priv *hpriv) ++{ ++ int c, rc; ++ ++ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) { ++ rc = clk_prepare_enable(hpriv->clks[c]); ++ if (rc) ++ goto disable_unprepare_clk; ++ } ++ return 0; ++ ++disable_unprepare_clk: ++ while (--c >= 0) ++ clk_disable_unprepare(hpriv->clks[c]); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); ++ ++/** ++ * ahci_platform_disable_clks - Disable platform clocks ++ * @hpriv: host private area to store config values ++ * ++ * This function disables all the clks found in hpriv->clks, in reverse ++ * order of ahci_platform_enable_clks (starting at the end of the array). ++ */ ++void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) ++{ ++ int c; ++ ++ for (c = AHCI_MAX_CLKS - 1; c >= 0; c--) ++ if (hpriv->clks[c]) ++ clk_disable_unprepare(hpriv->clks[c]); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); ++ ++/** ++ * ahci_platform_enable_resources - Enable platform resources ++ * @hpriv: host private area to store config values ++ * ++ * This function enables all ahci_platform managed resources in the ++ * following order: ++ * 1) Regulator ++ * 2) Clocks (through ahci_platform_enable_clks) ++ * 3) Phy ++ * ++ * If resource enabling fails at any point the previous enabled resources ++ * are disabled in reverse order. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) ++{ ++ int rc; ++ ++ if (hpriv->target_pwr) { ++ rc = regulator_enable(hpriv->target_pwr); ++ if (rc) ++ return rc; ++ } ++ ++ rc = ahci_platform_enable_clks(hpriv); ++ if (rc) ++ goto disable_regulator; ++ ++ if (hpriv->phy) { ++ rc = phy_init(hpriv->phy); ++ if (rc) ++ goto disable_clks; ++ ++ rc = phy_power_on(hpriv->phy); ++ if (rc) { ++ phy_exit(hpriv->phy); ++ goto disable_clks; ++ } ++ } ++ ++ return 0; ++ ++disable_clks: ++ ahci_platform_disable_clks(hpriv); ++ ++disable_regulator: ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); ++ ++/** ++ * ahci_platform_disable_resources - Disable platform resources ++ * @hpriv: host private area to store config values ++ * ++ * This function disables all ahci_platform managed resources in the ++ * following order: ++ * 1) Phy ++ * 2) Clocks (through ahci_platform_disable_clks) ++ * 3) Regulator ++ */ ++void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) ++{ ++ if (hpriv->phy) { ++ phy_power_off(hpriv->phy); ++ phy_exit(hpriv->phy); ++ } ++ ++ ahci_platform_disable_clks(hpriv); ++ ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); ++ ++static void ahci_platform_put_resources(struct device *dev, void *res) ++{ ++ struct ahci_host_priv *hpriv = res; ++ int c; ++ ++ if (hpriv->got_runtime_pm) { ++ pm_runtime_put_sync(dev); ++ pm_runtime_disable(dev); ++ } ++ ++ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) ++ clk_put(hpriv->clks[c]); ++} ++ ++/** ++ * ahci_platform_get_resources - Get platform resources ++ * @pdev: platform device to get resources for ++ * ++ * This function allocates an ahci_host_priv struct, and gets the following ++ * resources, storing a reference to them inside the returned struct: ++ * ++ * 1) mmio registers (IORESOURCE_MEM 0, mandatory) ++ * 2) regulator for controlling the targets power (optional) ++ * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, ++ * or for non devicetree enabled platforms a single clock ++ * 4) phy (optional) ++ * ++ * RETURNS: ++ * The allocated ahci_host_priv on success, otherwise an ERR_PTR value ++ */ ++struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct ahci_host_priv *hpriv; ++ struct clk *clk; ++ int i, rc = -ENOMEM; ++ ++ if (!devres_open_group(dev, NULL, GFP_KERNEL)) ++ return ERR_PTR(-ENOMEM); ++ ++ hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv), ++ GFP_KERNEL); ++ if (!hpriv) ++ goto err_out; ++ ++ devres_add(dev, hpriv); ++ ++ hpriv->mmio = devm_ioremap_resource(dev, ++ platform_get_resource(pdev, IORESOURCE_MEM, 0)); ++ if (IS_ERR(hpriv->mmio)) { ++ dev_err(dev, "no mmio space\n"); ++ rc = PTR_ERR(hpriv->mmio); ++ goto err_out; ++ } ++ ++ hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); ++ if (IS_ERR(hpriv->target_pwr)) { ++ rc = PTR_ERR(hpriv->target_pwr); ++ if (rc == -EPROBE_DEFER) ++ goto err_out; ++ hpriv->target_pwr = NULL; ++ } ++ ++ for (i = 0; i < AHCI_MAX_CLKS; i++) { ++ /* ++ * For now we must use clk_get(dev, NULL) for the first clock, ++ * because some platforms (da850, spear13xx) are not yet ++ * converted to use devicetree for clocks. For new platforms ++ * this is equivalent to of_clk_get(dev->of_node, 0). ++ */ ++ if (i == 0) ++ clk = clk_get(dev, NULL); ++ else ++ clk = of_clk_get(dev->of_node, i); ++ ++ if (IS_ERR(clk)) { ++ rc = PTR_ERR(clk); ++ if (rc == -EPROBE_DEFER) ++ goto err_out; ++ break; ++ } ++ hpriv->clks[i] = clk; ++ } ++ ++ hpriv->phy = devm_phy_get(dev, "sata-phy"); ++ if (IS_ERR(hpriv->phy)) { ++ rc = PTR_ERR(hpriv->phy); ++ switch (rc) { ++ case -ENODEV: ++ case -ENOSYS: ++ /* continue normally */ ++ hpriv->phy = NULL; ++ break; ++ ++ case -EPROBE_DEFER: ++ goto err_out; ++ ++ default: ++ dev_err(dev, "couldn't get sata-phy\n"); ++ goto err_out; ++ } ++ } ++ ++ pm_runtime_enable(dev); ++ pm_runtime_get_sync(dev); ++ hpriv->got_runtime_pm = true; ++ ++ devres_remove_group(dev, NULL); ++ return hpriv; ++ ++err_out: ++ devres_release_group(dev, NULL); ++ return ERR_PTR(rc); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_get_resources); ++ ++/** ++ * ahci_platform_init_host - Bring up an ahci-platform host ++ * @pdev: platform device pointer for the host ++ * @hpriv: ahci-host private data for the host ++ * @pi_template: template for the ata_port_info to use ++ * @force_port_map: param passed to ahci_save_initial_config ++ * @mask_port_map: param passed to ahci_save_initial_config ++ * ++ * This function does all the usual steps needed to bring up an ++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.) ++ * must be initialized / enabled before calling this. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_init_host(struct platform_device *pdev, ++ struct ahci_host_priv *hpriv, ++ const struct ata_port_info *pi_template, ++ unsigned int force_port_map, ++ unsigned int mask_port_map) ++{ ++ struct device *dev = &pdev->dev; ++ struct ata_port_info pi = *pi_template; ++ const struct ata_port_info *ppi[] = { &pi, NULL }; ++ struct ata_host *host; ++ int i, irq, n_ports, rc; ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq <= 0) { ++ dev_err(dev, "no irq\n"); ++ return -EINVAL; ++ } ++ ++ /* prepare host */ ++ hpriv->flags |= (unsigned long)pi.private_data; ++ ++ ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map); ++ ++ if (hpriv->cap & HOST_CAP_NCQ) ++ pi.flags |= ATA_FLAG_NCQ; ++ ++ if (hpriv->cap & HOST_CAP_PMP) ++ pi.flags |= ATA_FLAG_PMP; ++ ++ ahci_set_em_messages(hpriv, &pi); ++ ++ /* CAP.NP sometimes indicate the index of the last enabled ++ * port, at other times, that of the last possible port, so ++ * determining the maximum port number requires looking at ++ * both CAP.NP and port_map. ++ */ ++ n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); ++ ++ host = ata_host_alloc_pinfo(dev, ppi, n_ports); ++ if (!host) ++ return -ENOMEM; ++ ++ host->private_data = hpriv; ++ ++ if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) ++ host->flags |= ATA_HOST_PARALLEL_SCAN; ++ else ++ dev_info(dev, "SSS flag set, parallel bus scan disabled\n"); ++ ++ if (pi.flags & ATA_FLAG_EM) ++ ahci_reset_em(host); ++ ++ for (i = 0; i < host->n_ports; i++) { ++ struct ata_port *ap = host->ports[i]; ++ ++ ata_port_desc(ap, "mmio %pR", ++ platform_get_resource(pdev, IORESOURCE_MEM, 0)); ++ ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); ++ ++ /* set enclosure management message type */ ++ if (ap->flags & ATA_FLAG_EM) ++ ap->em_message_type = hpriv->em_msg_type; ++ ++ /* disabled/not-implemented port */ ++ if (!(hpriv->port_map & (1 << i))) ++ ap->ops = &ata_dummy_port_ops; ++ } ++ ++ rc = ahci_reset_controller(host); ++ if (rc) ++ return rc; ++ ++ ahci_init_controller(host); ++ ahci_print_info(host, "platform"); ++ ++ return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, ++ &ahci_platform_sht); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_init_host); ++ ++static void ahci_host_stop(struct ata_host *host) ++{ ++ struct device *dev = host->dev; ++ struct ahci_platform_data *pdata = dev_get_platdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ ++ if (pdata && pdata->exit) ++ pdata->exit(dev); ++ ++ ahci_platform_disable_resources(hpriv); ++} ++ ++#ifdef CONFIG_PM_SLEEP ++/** ++ * ahci_platform_suspend_host - Suspend an ahci-platform host ++ * @dev: device pointer for the host ++ * ++ * This function does all the usual steps needed to suspend an ++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.) ++ * must be disabled after calling this. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_suspend_host(struct device *dev) ++{ ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ void __iomem *mmio = hpriv->mmio; ++ u32 ctl; ++ ++ if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { ++ dev_err(dev, "firmware update required for suspend/resume\n"); ++ return -EIO; ++ } ++ ++ /* ++ * AHCI spec rev1.1 section 8.3.3: ++ * Software must disable interrupts prior to requesting a ++ * transition of the HBA to D3 state. ++ */ ++ ctl = readl(mmio + HOST_CTL); ++ ctl &= ~HOST_IRQ_EN; ++ writel(ctl, mmio + HOST_CTL); ++ readl(mmio + HOST_CTL); /* flush */ ++ ++ return ata_host_suspend(host, PMSG_SUSPEND); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_suspend_host); ++ ++/** ++ * ahci_platform_resume_host - Resume an ahci-platform host ++ * @dev: device pointer for the host ++ * ++ * This function does all the usual steps needed to resume an ahci-platform ++ * host, note any necessary resources (ie clks, phy, etc.) must be ++ * initialized / enabled before calling this. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_resume_host(struct device *dev) ++{ ++ struct ata_host *host = dev_get_drvdata(dev); ++ int rc; ++ ++ if (dev->power.power_state.event == PM_EVENT_SUSPEND) { ++ rc = ahci_reset_controller(host); ++ if (rc) ++ return rc; ++ ++ ahci_init_controller(host); ++ } ++ ++ ata_host_resume(host); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_resume_host); ++ ++/** ++ * ahci_platform_suspend - Suspend an ahci-platform device ++ * @dev: the platform device to suspend ++ * ++ * This function suspends the host associated with the device, followed by ++ * disabling all the resources of the device. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_suspend(struct device *dev) ++{ ++ struct ahci_platform_data *pdata = dev_get_platdata(dev); ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ int rc; ++ ++ rc = ahci_platform_suspend_host(dev); ++ if (rc) ++ return rc; ++ ++ if (pdata && pdata->suspend) { ++ rc = pdata->suspend(dev); ++ if (rc) ++ goto resume_host; ++ } ++ ++ ahci_platform_disable_resources(hpriv); ++ ++ return 0; ++ ++resume_host: ++ ahci_platform_resume_host(dev); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_suspend); ++ ++/** ++ * ahci_platform_resume - Resume an ahci-platform device ++ * @dev: the platform device to resume ++ * ++ * This function enables all the resources of the device followed by ++ * resuming the host associated with the device. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_resume(struct device *dev) ++{ ++ struct ahci_platform_data *pdata = dev_get_platdata(dev); ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ int rc; ++ ++ rc = ahci_platform_enable_resources(hpriv); ++ if (rc) ++ return rc; ++ ++ if (pdata && pdata->resume) { ++ rc = pdata->resume(dev); ++ if (rc) ++ goto disable_resources; ++ } ++ ++ rc = ahci_platform_resume_host(dev); ++ if (rc) ++ goto disable_resources; ++ ++ /* We resumed so update PM runtime state */ ++ pm_runtime_disable(dev); ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ ++ return 0; ++ ++disable_resources: ++ ahci_platform_disable_resources(hpriv); ++ ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_resume); ++#endif ++ ++MODULE_DESCRIPTION("AHCI SATA platform library"); ++MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>"); ++MODULE_LICENSE("GPL"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0125-clk-qcom-Add-support-for-IPQ8064-s-global-clock-cont.patch b/target/linux/ipq806x/patches/0125-clk-qcom-Add-support-for-IPQ8064-s-global-clock-cont.patch new file mode 100644 index 0000000000..250ca6e85d --- /dev/null +++ b/target/linux/ipq806x/patches/0125-clk-qcom-Add-support-for-IPQ8064-s-global-clock-cont.patch @@ -0,0 +1,2939 @@ +From 2e6dfaa714ba4bd70fa5dda07c525b6c15e44552 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 3 Apr 2014 13:47:07 -0500 +Subject: [PATCH 125/182] clk: qcom: Add support for IPQ8064's global clock + controller (GCC) + +Add a driver for the global clock controller found on IPQ8064 based +platforms. This should allow most non-multimedia device drivers to probe +and control their clocks. + +This is currently missing clocks for USB HSIC and networking devices. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + .../devicetree/bindings/clock/qcom,gcc.txt | 1 + + drivers/clk/qcom/Kconfig | 8 + + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/gcc-ipq806x.c | 2424 ++++++++++++++++++++ + include/dt-bindings/clock/qcom,gcc-ipq806x.h | 293 +++ + include/dt-bindings/reset/qcom,gcc-ipq806x.h | 132 ++ + 6 files changed, 2859 insertions(+) + create mode 100644 drivers/clk/qcom/gcc-ipq806x.c + create mode 100644 include/dt-bindings/clock/qcom,gcc-ipq806x.h + create mode 100644 include/dt-bindings/reset/qcom,gcc-ipq806x.h + +diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt +index 9cfcb4f..0171509 100644 +--- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt ++++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt +@@ -5,6 +5,7 @@ Required properties : + - compatible : shall contain only one of the following: + + "qcom,gcc-apq8064" ++ "qcom,gcc-ipq8064" + "qcom,gcc-msm8660" + "qcom,gcc-msm8960" + "qcom,gcc-msm8974" +diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig +index 7f696b7..cfaa54c 100644 +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -4,6 +4,14 @@ config COMMON_CLK_QCOM + select REGMAP_MMIO + select RESET_CONTROLLER + ++config IPQ_GCC_806X ++ tristate "IPQ806x Global Clock Controller" ++ depends on COMMON_CLK_QCOM ++ help ++ Support for the global clock controller on ipq806x devices. ++ Say Y if you want to use peripheral devices such as UART, SPI, ++ i2c, USB, SD/eMMC, etc. ++ + config MSM_GCC_8660 + tristate "MSM8660 Global Clock Controller" + depends on COMMON_CLK_QCOM +diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile +index 689e05b..df2a1b3 100644 +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -8,6 +8,7 @@ clk-qcom-y += clk-rcg2.o + clk-qcom-y += clk-branch.o + clk-qcom-y += reset.o + ++obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o + obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o + obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o + obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o +diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c +new file mode 100644 +index 0000000..278c5fe +--- /dev/null ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -0,0 +1,2424 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/bitops.h> ++#include <linux/err.h> ++#include <linux/platform_device.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_device.h> ++#include <linux/clk-provider.h> ++#include <linux/regmap.h> ++#include <linux/reset-controller.h> ++ ++#include <dt-bindings/clock/qcom,gcc-ipq806x.h> ++#include <dt-bindings/reset/qcom,gcc-ipq806x.h> ++ ++#include "common.h" ++#include "clk-regmap.h" ++#include "clk-pll.h" ++#include "clk-rcg.h" ++#include "clk-branch.h" ++#include "reset.h" ++ ++static struct clk_pll pll3 = { ++ .l_reg = 0x3164, ++ .m_reg = 0x3168, ++ .n_reg = 0x316c, ++ .config_reg = 0x3174, ++ .mode_reg = 0x3160, ++ .status_reg = 0x3178, ++ .status_bit = 16, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "pll3", ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .ops = &clk_pll_ops, ++ }, ++}; ++ ++static struct clk_pll pll8 = { ++ .l_reg = 0x3144, ++ .m_reg = 0x3148, ++ .n_reg = 0x314c, ++ .config_reg = 0x3154, ++ .mode_reg = 0x3140, ++ .status_reg = 0x3158, ++ .status_bit = 16, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "pll8", ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .ops = &clk_pll_ops, ++ }, ++}; ++ ++static struct clk_regmap pll8_vote = { ++ .enable_reg = 0x34c0, ++ .enable_mask = BIT(8), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pll8_vote", ++ .parent_names = (const char *[]){ "pll8" }, ++ .num_parents = 1, ++ .ops = &clk_pll_vote_ops, ++ }, ++}; ++ ++static struct clk_pll pll14 = { ++ .l_reg = 0x31c4, ++ .m_reg = 0x31c8, ++ .n_reg = 0x31cc, ++ .config_reg = 0x31d4, ++ .mode_reg = 0x31c0, ++ .status_reg = 0x31d8, ++ .status_bit = 16, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "pll14", ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .ops = &clk_pll_ops, ++ }, ++}; ++ ++static struct clk_regmap pll14_vote = { ++ .enable_reg = 0x34c0, ++ .enable_mask = BIT(14), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pll14_vote", ++ .parent_names = (const char *[]){ "pll14" }, ++ .num_parents = 1, ++ .ops = &clk_pll_vote_ops, ++ }, ++}; ++ ++#define P_PXO 0 ++#define P_PLL8 1 ++#define P_PLL3 1 ++#define P_PLL0 2 ++#define P_CXO 2 ++ ++static const u8 gcc_pxo_pll8_map[] = { ++ [P_PXO] = 0, ++ [P_PLL8] = 3, ++}; ++ ++static const char *gcc_pxo_pll8[] = { ++ "pxo", ++ "pll8_vote", ++}; ++ ++static const u8 gcc_pxo_pll8_cxo_map[] = { ++ [P_PXO] = 0, ++ [P_PLL8] = 3, ++ [P_CXO] = 5, ++}; ++ ++static const char *gcc_pxo_pll8_cxo[] = { ++ "pxo", ++ "pll8_vote", ++ "cxo", ++}; ++ ++static const u8 gcc_pxo_pll3_map[] = { ++ [P_PXO] = 0, ++ [P_PLL3] = 1, ++}; ++ ++static const u8 gcc_pxo_pll3_sata_map[] = { ++ [P_PXO] = 0, ++ [P_PLL3] = 6, ++}; ++ ++static const char *gcc_pxo_pll3[] = { ++ "pxo", ++ "pll3", ++}; ++ ++static const u8 gcc_pxo_pll8_pll0[] = { ++ [P_PXO] = 0, ++ [P_PLL8] = 3, ++ [P_PLL0] = 2, ++}; ++ ++static const char *gcc_pxo_pll8_pll0_map[] = { ++ "pxo", ++ "pll8_vote", ++ "pll0", ++}; ++ ++static struct freq_tbl clk_tbl_gsbi_uart[] = { ++ { 1843200, P_PLL8, 2, 6, 625 }, ++ { 3686400, P_PLL8, 2, 12, 625 }, ++ { 7372800, P_PLL8, 2, 24, 625 }, ++ { 14745600, P_PLL8, 2, 48, 625 }, ++ { 16000000, P_PLL8, 4, 1, 6 }, ++ { 24000000, P_PLL8, 4, 1, 4 }, ++ { 32000000, P_PLL8, 4, 1, 3 }, ++ { 40000000, P_PLL8, 1, 5, 48 }, ++ { 46400000, P_PLL8, 1, 29, 240 }, ++ { 48000000, P_PLL8, 4, 1, 2 }, ++ { 51200000, P_PLL8, 1, 2, 15 }, ++ { 56000000, P_PLL8, 1, 7, 48 }, ++ { 58982400, P_PLL8, 1, 96, 625 }, ++ { 64000000, P_PLL8, 2, 1, 3 }, ++ { } ++}; ++ ++static struct clk_rcg gsbi1_uart_src = { ++ .ns_reg = 0x29d4, ++ .md_reg = 0x29d0, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 16, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_uart, ++ .clkr = { ++ .enable_reg = 0x29d4, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi1_uart_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi1_uart_clk = { ++ .halt_reg = 0x2fcc, ++ .halt_bit = 12, ++ .clkr = { ++ .enable_reg = 0x29d4, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi1_uart_clk", ++ .parent_names = (const char *[]){ ++ "gsbi1_uart_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi2_uart_src = { ++ .ns_reg = 0x29f4, ++ .md_reg = 0x29f0, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 16, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_uart, ++ .clkr = { ++ .enable_reg = 0x29f4, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi2_uart_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi2_uart_clk = { ++ .halt_reg = 0x2fcc, ++ .halt_bit = 8, ++ .clkr = { ++ .enable_reg = 0x29f4, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi2_uart_clk", ++ .parent_names = (const char *[]){ ++ "gsbi2_uart_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi4_uart_src = { ++ .ns_reg = 0x2a34, ++ .md_reg = 0x2a30, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 16, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_uart, ++ .clkr = { ++ .enable_reg = 0x2a34, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi4_uart_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi4_uart_clk = { ++ .halt_reg = 0x2fd0, ++ .halt_bit = 26, ++ .clkr = { ++ .enable_reg = 0x2a34, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi4_uart_clk", ++ .parent_names = (const char *[]){ ++ "gsbi4_uart_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi5_uart_src = { ++ .ns_reg = 0x2a54, ++ .md_reg = 0x2a50, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 16, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_uart, ++ .clkr = { ++ .enable_reg = 0x2a54, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi5_uart_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi5_uart_clk = { ++ .halt_reg = 0x2fd0, ++ .halt_bit = 22, ++ .clkr = { ++ .enable_reg = 0x2a54, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi5_uart_clk", ++ .parent_names = (const char *[]){ ++ "gsbi5_uart_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi6_uart_src = { ++ .ns_reg = 0x2a74, ++ .md_reg = 0x2a70, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 16, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_uart, ++ .clkr = { ++ .enable_reg = 0x2a74, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi6_uart_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi6_uart_clk = { ++ .halt_reg = 0x2fd0, ++ .halt_bit = 18, ++ .clkr = { ++ .enable_reg = 0x2a74, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi6_uart_clk", ++ .parent_names = (const char *[]){ ++ "gsbi6_uart_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi7_uart_src = { ++ .ns_reg = 0x2a94, ++ .md_reg = 0x2a90, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 16, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_uart, ++ .clkr = { ++ .enable_reg = 0x2a94, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi7_uart_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi7_uart_clk = { ++ .halt_reg = 0x2fd0, ++ .halt_bit = 14, ++ .clkr = { ++ .enable_reg = 0x2a94, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi7_uart_clk", ++ .parent_names = (const char *[]){ ++ "gsbi7_uart_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct freq_tbl clk_tbl_gsbi_qup[] = { ++ { 1100000, P_PXO, 1, 2, 49 }, ++ { 5400000, P_PXO, 1, 1, 5 }, ++ { 10800000, P_PXO, 1, 2, 5 }, ++ { 15060000, P_PLL8, 1, 2, 51 }, ++ { 24000000, P_PLL8, 4, 1, 4 }, ++ { 25600000, P_PLL8, 1, 1, 15 }, ++ { 27000000, P_PXO, 1, 0, 0 }, ++ { 48000000, P_PLL8, 4, 1, 2 }, ++ { 51200000, P_PLL8, 1, 2, 15 }, ++ { } ++}; ++ ++static struct clk_rcg gsbi1_qup_src = { ++ .ns_reg = 0x29cc, ++ .md_reg = 0x29c8, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_qup, ++ .clkr = { ++ .enable_reg = 0x29cc, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi1_qup_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi1_qup_clk = { ++ .halt_reg = 0x2fcc, ++ .halt_bit = 11, ++ .clkr = { ++ .enable_reg = 0x29cc, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi1_qup_clk", ++ .parent_names = (const char *[]){ "gsbi1_qup_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi2_qup_src = { ++ .ns_reg = 0x29ec, ++ .md_reg = 0x29e8, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_qup, ++ .clkr = { ++ .enable_reg = 0x29ec, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi2_qup_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi2_qup_clk = { ++ .halt_reg = 0x2fcc, ++ .halt_bit = 6, ++ .clkr = { ++ .enable_reg = 0x29ec, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi2_qup_clk", ++ .parent_names = (const char *[]){ "gsbi2_qup_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi4_qup_src = { ++ .ns_reg = 0x2a2c, ++ .md_reg = 0x2a28, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_qup, ++ .clkr = { ++ .enable_reg = 0x2a2c, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi4_qup_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi4_qup_clk = { ++ .halt_reg = 0x2fd0, ++ .halt_bit = 24, ++ .clkr = { ++ .enable_reg = 0x2a2c, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi4_qup_clk", ++ .parent_names = (const char *[]){ "gsbi4_qup_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi5_qup_src = { ++ .ns_reg = 0x2a4c, ++ .md_reg = 0x2a48, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_qup, ++ .clkr = { ++ .enable_reg = 0x2a4c, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi5_qup_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi5_qup_clk = { ++ .halt_reg = 0x2fd0, ++ .halt_bit = 20, ++ .clkr = { ++ .enable_reg = 0x2a4c, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi5_qup_clk", ++ .parent_names = (const char *[]){ "gsbi5_qup_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi6_qup_src = { ++ .ns_reg = 0x2a6c, ++ .md_reg = 0x2a68, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_qup, ++ .clkr = { ++ .enable_reg = 0x2a6c, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi6_qup_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi6_qup_clk = { ++ .halt_reg = 0x2fd0, ++ .halt_bit = 16, ++ .clkr = { ++ .enable_reg = 0x2a6c, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi6_qup_clk", ++ .parent_names = (const char *[]){ "gsbi6_qup_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi7_qup_src = { ++ .ns_reg = 0x2a8c, ++ .md_reg = 0x2a88, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_qup, ++ .clkr = { ++ .enable_reg = 0x2a8c, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi7_qup_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi7_qup_clk = { ++ .halt_reg = 0x2fd0, ++ .halt_bit = 12, ++ .clkr = { ++ .enable_reg = 0x2a8c, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi7_qup_clk", ++ .parent_names = (const char *[]){ "gsbi7_qup_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi1_h_clk = { ++ .hwcg_reg = 0x29c0, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fcc, ++ .halt_bit = 13, ++ .clkr = { ++ .enable_reg = 0x29c0, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi1_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi2_h_clk = { ++ .hwcg_reg = 0x29e0, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fcc, ++ .halt_bit = 9, ++ .clkr = { ++ .enable_reg = 0x29e0, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi2_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi4_h_clk = { ++ .hwcg_reg = 0x2a20, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fd0, ++ .halt_bit = 27, ++ .clkr = { ++ .enable_reg = 0x2a20, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi4_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi5_h_clk = { ++ .hwcg_reg = 0x2a40, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fd0, ++ .halt_bit = 23, ++ .clkr = { ++ .enable_reg = 0x2a40, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi5_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi6_h_clk = { ++ .hwcg_reg = 0x2a60, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fd0, ++ .halt_bit = 19, ++ .clkr = { ++ .enable_reg = 0x2a60, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi6_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi7_h_clk = { ++ .hwcg_reg = 0x2a80, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fd0, ++ .halt_bit = 15, ++ .clkr = { ++ .enable_reg = 0x2a80, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi7_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_gp[] = { ++ { 12500000, P_PXO, 2, 0, 0 }, ++ { 25000000, P_PXO, 1, 0, 0 }, ++ { 64000000, P_PLL8, 2, 1, 3 }, ++ { 76800000, P_PLL8, 1, 1, 5 }, ++ { 96000000, P_PLL8, 4, 0, 0 }, ++ { 128000000, P_PLL8, 3, 0, 0 }, ++ { 192000000, P_PLL8, 2, 0, 0 }, ++ { } ++}; ++ ++static struct clk_rcg gp0_src = { ++ .ns_reg = 0x2d24, ++ .md_reg = 0x2d00, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_cxo_map, ++ }, ++ .freq_tbl = clk_tbl_gp, ++ .clkr = { ++ .enable_reg = 0x2d24, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gp0_src", ++ .parent_names = gcc_pxo_pll8_cxo, ++ .num_parents = 3, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ } ++}; ++ ++static struct clk_branch gp0_clk = { ++ .halt_reg = 0x2fd8, ++ .halt_bit = 7, ++ .clkr = { ++ .enable_reg = 0x2d24, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gp0_clk", ++ .parent_names = (const char *[]){ "gp0_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gp1_src = { ++ .ns_reg = 0x2d44, ++ .md_reg = 0x2d40, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_cxo_map, ++ }, ++ .freq_tbl = clk_tbl_gp, ++ .clkr = { ++ .enable_reg = 0x2d44, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gp1_src", ++ .parent_names = gcc_pxo_pll8_cxo, ++ .num_parents = 3, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ } ++}; ++ ++static struct clk_branch gp1_clk = { ++ .halt_reg = 0x2fd8, ++ .halt_bit = 6, ++ .clkr = { ++ .enable_reg = 0x2d44, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gp1_clk", ++ .parent_names = (const char *[]){ "gp1_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gp2_src = { ++ .ns_reg = 0x2d64, ++ .md_reg = 0x2d60, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_cxo_map, ++ }, ++ .freq_tbl = clk_tbl_gp, ++ .clkr = { ++ .enable_reg = 0x2d64, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gp2_src", ++ .parent_names = gcc_pxo_pll8_cxo, ++ .num_parents = 3, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ } ++}; ++ ++static struct clk_branch gp2_clk = { ++ .halt_reg = 0x2fd8, ++ .halt_bit = 5, ++ .clkr = { ++ .enable_reg = 0x2d64, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gp2_clk", ++ .parent_names = (const char *[]){ "gp2_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pmem_clk = { ++ .hwcg_reg = 0x25a0, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fc8, ++ .halt_bit = 20, ++ .clkr = { ++ .enable_reg = 0x25a0, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pmem_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg prng_src = { ++ .ns_reg = 0x2e80, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .clkr = { ++ .hw.init = &(struct clk_init_data){ ++ .name = "prng_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch prng_clk = { ++ .halt_reg = 0x2fd8, ++ .halt_check = BRANCH_HALT_VOTED, ++ .halt_bit = 10, ++ .clkr = { ++ .enable_reg = 0x3080, ++ .enable_mask = BIT(10), ++ .hw.init = &(struct clk_init_data){ ++ .name = "prng_clk", ++ .parent_names = (const char *[]){ "prng_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_sdc[] = { ++ { 144000, P_PXO, 5, 18,625 }, ++ { 400000, P_PLL8, 4, 1, 240 }, ++ { 16000000, P_PLL8, 4, 1, 6 }, ++ { 17070000, P_PLL8, 1, 2, 45 }, ++ { 20210000, P_PLL8, 1, 1, 19 }, ++ { 24000000, P_PLL8, 4, 1, 4 }, ++ { 48000000, P_PLL8, 4, 1, 2 }, ++ { 64000000, P_PLL8, 3, 1, 2 }, ++ { 96000000, P_PLL8, 4, 0, 0 }, ++ { 192000000, P_PLL8, 2, 0, 0 }, ++ { } ++}; ++ ++static struct clk_rcg sdc1_src = { ++ .ns_reg = 0x282c, ++ .md_reg = 0x2828, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_sdc, ++ .clkr = { ++ .enable_reg = 0x282c, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sdc1_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ } ++}; ++ ++static struct clk_branch sdc1_clk = { ++ .halt_reg = 0x2fc8, ++ .halt_bit = 6, ++ .clkr = { ++ .enable_reg = 0x282c, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sdc1_clk", ++ .parent_names = (const char *[]){ "sdc1_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg sdc3_src = { ++ .ns_reg = 0x286c, ++ .md_reg = 0x2868, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_sdc, ++ .clkr = { ++ .enable_reg = 0x286c, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sdc3_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ } ++}; ++ ++static struct clk_branch sdc3_clk = { ++ .halt_reg = 0x2fc8, ++ .halt_bit = 4, ++ .clkr = { ++ .enable_reg = 0x286c, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sdc3_clk", ++ .parent_names = (const char *[]){ "sdc3_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch sdc1_h_clk = { ++ .hwcg_reg = 0x2820, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fc8, ++ .halt_bit = 11, ++ .clkr = { ++ .enable_reg = 0x2820, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sdc1_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch sdc3_h_clk = { ++ .hwcg_reg = 0x2860, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fc8, ++ .halt_bit = 9, ++ .clkr = { ++ .enable_reg = 0x2860, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sdc3_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_tsif_ref[] = { ++ { 105000, P_PXO, 1, 1, 256 }, ++ { } ++}; ++ ++static struct clk_rcg tsif_ref_src = { ++ .ns_reg = 0x2710, ++ .md_reg = 0x270c, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 16, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_tsif_ref, ++ .clkr = { ++ .enable_reg = 0x2710, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "tsif_ref_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ } ++}; ++ ++static struct clk_branch tsif_ref_clk = { ++ .halt_reg = 0x2fd4, ++ .halt_bit = 5, ++ .clkr = { ++ .enable_reg = 0x2710, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "tsif_ref_clk", ++ .parent_names = (const char *[]){ "tsif_ref_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch tsif_h_clk = { ++ .hwcg_reg = 0x2700, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fd4, ++ .halt_bit = 7, ++ .clkr = { ++ .enable_reg = 0x2700, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "tsif_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch dma_bam_h_clk = { ++ .hwcg_reg = 0x25c0, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fc8, ++ .halt_bit = 12, ++ .clkr = { ++ .enable_reg = 0x25c0, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "dma_bam_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch adm0_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_check = BRANCH_HALT_VOTED, ++ .halt_bit = 12, ++ .clkr = { ++ .enable_reg = 0x3080, ++ .enable_mask = BIT(2), ++ .hw.init = &(struct clk_init_data){ ++ .name = "adm0_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch adm0_pbus_clk = { ++ .hwcg_reg = 0x2208, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fdc, ++ .halt_check = BRANCH_HALT_VOTED, ++ .halt_bit = 11, ++ .clkr = { ++ .enable_reg = 0x3080, ++ .enable_mask = BIT(3), ++ .hw.init = &(struct clk_init_data){ ++ .name = "adm0_pbus_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pmic_arb0_h_clk = { ++ .halt_reg = 0x2fd8, ++ .halt_check = BRANCH_HALT_VOTED, ++ .halt_bit = 22, ++ .clkr = { ++ .enable_reg = 0x3080, ++ .enable_mask = BIT(8), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pmic_arb0_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pmic_arb1_h_clk = { ++ .halt_reg = 0x2fd8, ++ .halt_check = BRANCH_HALT_VOTED, ++ .halt_bit = 21, ++ .clkr = { ++ .enable_reg = 0x3080, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pmic_arb1_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pmic_ssbi2_clk = { ++ .halt_reg = 0x2fd8, ++ .halt_check = BRANCH_HALT_VOTED, ++ .halt_bit = 23, ++ .clkr = { ++ .enable_reg = 0x3080, ++ .enable_mask = BIT(7), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pmic_ssbi2_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch rpm_msg_ram_h_clk = { ++ .hwcg_reg = 0x27e0, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fd8, ++ .halt_check = BRANCH_HALT_VOTED, ++ .halt_bit = 12, ++ .clkr = { ++ .enable_reg = 0x3080, ++ .enable_mask = BIT(6), ++ .hw.init = &(struct clk_init_data){ ++ .name = "rpm_msg_ram_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_pcie_ref[] = { ++ { 100000000, P_PLL3, 12, 0, 0 }, ++ { } ++}; ++ ++static struct clk_rcg pcie_ref_src = { ++ .ns_reg = 0x3860, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll3_map, ++ }, ++ .freq_tbl = clk_tbl_pcie_ref, ++ .clkr = { ++ .enable_reg = 0x3860, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie_ref_src", ++ .parent_names = gcc_pxo_pll3, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie_ref_src_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 30, ++ .clkr = { ++ .enable_reg = 0x3860, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie_ref_src_clk", ++ .parent_names = (const char *[]){ "pcie_ref_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie_a_clk = { ++ .halt_reg = 0x2fc0, ++ .halt_bit = 13, ++ .clkr = { ++ .enable_reg = 0x22c0, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie_a_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie_aux_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 31, ++ .clkr = { ++ .enable_reg = 0x22c8, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie_aux_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie_h_clk = { ++ .halt_reg = 0x2fd4, ++ .halt_bit = 8, ++ .clkr = { ++ .enable_reg = 0x22cc, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie_phy_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 29, ++ .clkr = { ++ .enable_reg = 0x22d0, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie_phy_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg pcie1_ref_src = { ++ .ns_reg = 0x3aa0, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll3_map, ++ }, ++ .freq_tbl = clk_tbl_pcie_ref, ++ .clkr = { ++ .enable_reg = 0x3aa0, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie1_ref_src", ++ .parent_names = gcc_pxo_pll3, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie1_ref_src_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 27, ++ .clkr = { ++ .enable_reg = 0x3aa0, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie1_ref_src_clk", ++ .parent_names = (const char *[]){ "pcie1_ref_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie1_a_clk = { ++ .halt_reg = 0x2fc0, ++ .halt_bit = 10, ++ .clkr = { ++ .enable_reg = 0x3a80, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie1_a_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie1_aux_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 28, ++ .clkr = { ++ .enable_reg = 0x3a88, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie1_aux_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie1_h_clk = { ++ .halt_reg = 0x2fd4, ++ .halt_bit = 9, ++ .clkr = { ++ .enable_reg = 0x3a8c, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie1_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie1_phy_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 26, ++ .clkr = { ++ .enable_reg = 0x3a90, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie1_phy_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg pcie2_ref_src = { ++ .ns_reg = 0x3ae0, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll3_map, ++ }, ++ .freq_tbl = clk_tbl_pcie_ref, ++ .clkr = { ++ .enable_reg = 0x3ae0, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie2_ref_src", ++ .parent_names = gcc_pxo_pll3, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie2_ref_src_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 24, ++ .clkr = { ++ .enable_reg = 0x3ae0, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie2_ref_src_clk", ++ .parent_names = (const char *[]){ "pcie2_ref_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie2_a_clk = { ++ .halt_reg = 0x2fc0, ++ .halt_bit = 9, ++ .clkr = { ++ .enable_reg = 0x3ac0, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie2_a_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie2_aux_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 25, ++ .clkr = { ++ .enable_reg = 0x3ac8, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie2_aux_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie2_h_clk = { ++ .halt_reg = 0x2fd4, ++ .halt_bit = 10, ++ .clkr = { ++ .enable_reg = 0x3acc, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie2_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie2_phy_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 23, ++ .clkr = { ++ .enable_reg = 0x3ad0, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie2_phy_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_sata_ref[] = { ++ { 100000000, P_PLL3, 12, 0, 0 }, ++ { } ++}; ++ ++static struct clk_rcg sata_ref_src = { ++ .ns_reg = 0x2c08, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll3_sata_map, ++ }, ++ .freq_tbl = clk_tbl_sata_ref, ++ .clkr = { ++ .enable_reg = 0x2c08, ++ .enable_mask = BIT(7), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sata_ref_src", ++ .parent_names = gcc_pxo_pll3, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch sata_rxoob_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 20, ++ .clkr = { ++ .enable_reg = 0x2c0c, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sata_rxoob_clk", ++ .parent_names = (const char *[]){ "sata_ref_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch sata_pmalive_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 19, ++ .clkr = { ++ .enable_reg = 0x2c10, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sata_pmalive_clk", ++ .parent_names = (const char *[]){ "sata_ref_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch sata_phy_ref_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 18, ++ .clkr = { ++ .enable_reg = 0x2c14, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sata_phy_ref_clk", ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch sata_a_clk = { ++ .halt_reg = 0x2fc0, ++ .halt_bit = 12, ++ .clkr = { ++ .enable_reg = 0x2c20, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sata_a_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch sata_h_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 21, ++ .clkr = { ++ .enable_reg = 0x2c00, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sata_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch sfab_sata_s_h_clk = { ++ .halt_reg = 0x2fc4, ++ .halt_bit = 14, ++ .clkr = { ++ .enable_reg = 0x2480, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sfab_sata_s_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch sata_phy_cfg_clk = { ++ .halt_reg = 0x2fcc, ++ .halt_bit = 14, ++ .clkr = { ++ .enable_reg = 0x2c40, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sata_phy_cfg_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_usb30_master[] = { ++ { 125000000, P_PLL0, 1, 5, 32 }, ++ { } ++}; ++ ++static struct clk_rcg usb30_master_clk_src = { ++ .ns_reg = 0x3b2c, ++ .md_reg = 0x3b28, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll0, ++ }, ++ .freq_tbl = clk_tbl_usb30_master, ++ .clkr = { ++ .enable_reg = 0x3b2c, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb30_master_ref_src", ++ .parent_names = gcc_pxo_pll8_pll0_map, ++ .num_parents = 3, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch usb30_0_branch_clk = { ++ .halt_reg = 0x2fc4, ++ .halt_bit = 22, ++ .clkr = { ++ .enable_reg = 0x3b24, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb30_0_branch_clk", ++ .parent_names = (const char *[]){ "usb30_master_ref_src", }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch usb30_1_branch_clk = { ++ .halt_reg = 0x2fc4, ++ .halt_bit = 17, ++ .clkr = { ++ .enable_reg = 0x3b34, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb30_1_branch_clk", ++ .parent_names = (const char *[]){ "usb30_master_ref_src", }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_usb30_utmi[] = { ++ { 60000000, P_PLL0, 1, 1, 40 }, ++ { } ++}; ++ ++static struct clk_rcg usb30_utmi_clk = { ++ .ns_reg = 0x3b44, ++ .md_reg = 0x3b40, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll0, ++ }, ++ .freq_tbl = clk_tbl_usb30_utmi, ++ .clkr = { ++ .enable_reg = 0x3b44, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb30_utmi_clk", ++ .parent_names = gcc_pxo_pll8_pll0_map, ++ .num_parents = 3, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch usb30_0_utmi_clk_ctl = { ++ .halt_reg = 0x2fc4, ++ .halt_bit = 21, ++ .clkr = { ++ .enable_reg = 0x3b48, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb30_0_utmi_clk_ctl", ++ .parent_names = (const char *[]){ "usb30_utmi_clk", }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch usb30_1_utmi_clk_ctl = { ++ .halt_reg = 0x2fc4, ++ .halt_bit = 15, ++ .clkr = { ++ .enable_reg = 0x3b4c, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb30_1_utmi_clk_ctl", ++ .parent_names = (const char *[]){ "usb30_utmi_clk", }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_usb[] = { ++ { 60000000, P_PLL8, 1, 5, 32 }, ++ { } ++}; ++ ++static struct clk_rcg usb_hs1_xcvr_clk_src = { ++ .ns_reg = 0x290C, ++ .md_reg = 0x2908, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll0, ++ }, ++ .freq_tbl = clk_tbl_usb, ++ .clkr = { ++ .enable_reg = 0x2968, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb_hs1_xcvr_src", ++ .parent_names = gcc_pxo_pll8_pll0_map, ++ .num_parents = 3, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch usb_hs1_xcvr_clk = { ++ .halt_reg = 0x2fcc, ++ .halt_bit = 17, ++ .clkr = { ++ .enable_reg = 0x290c, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb_hs1_xcvr_clk", ++ .parent_names = (const char *[]){ "usb_hs1_xcvr_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch usb_hs1_h_clk = { ++ .hwcg_reg = 0x2900, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fc8, ++ .halt_bit = 1, ++ .clkr = { ++ .enable_reg = 0x2900, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb_hs1_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg usb_fs1_xcvr_clk_src = { ++ .ns_reg = 0x2968, ++ .md_reg = 0x2964, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll0, ++ }, ++ .freq_tbl = clk_tbl_usb, ++ .clkr = { ++ .enable_reg = 0x2968, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb_fs1_xcvr_src", ++ .parent_names = gcc_pxo_pll8_pll0_map, ++ .num_parents = 3, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch usb_fs1_xcvr_clk = { ++ .halt_reg = 0x2fcc, ++ .halt_bit = 17, ++ .clkr = { ++ .enable_reg = 0x2968, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb_fs1_xcvr_clk", ++ .parent_names = (const char *[]){ "usb_fs1_xcvr_src", }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch usb_fs1_sys_clk = { ++ .halt_reg = 0x2fcc, ++ .halt_bit = 18, ++ .clkr = { ++ .enable_reg = 0x296c, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb_fs1_sys_clk", ++ .parent_names = (const char *[]){ "usb_fs1_xcvr_src", }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch usb_fs1_h_clk = { ++ .halt_reg = 0x2fcc, ++ .halt_bit = 19, ++ .clkr = { ++ .enable_reg = 0x2960, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb_fs1_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_regmap *gcc_ipq806x_clks[] = { ++ [PLL3] = &pll3.clkr, ++ [PLL8] = &pll8.clkr, ++ [PLL8_VOTE] = &pll8_vote, ++ [PLL14] = &pll14.clkr, ++ [PLL14_VOTE] = &pll14_vote, ++ [GSBI1_UART_SRC] = &gsbi1_uart_src.clkr, ++ [GSBI1_UART_CLK] = &gsbi1_uart_clk.clkr, ++ [GSBI2_UART_SRC] = &gsbi2_uart_src.clkr, ++ [GSBI2_UART_CLK] = &gsbi2_uart_clk.clkr, ++ [GSBI4_UART_SRC] = &gsbi4_uart_src.clkr, ++ [GSBI4_UART_CLK] = &gsbi4_uart_clk.clkr, ++ [GSBI5_UART_SRC] = &gsbi5_uart_src.clkr, ++ [GSBI5_UART_CLK] = &gsbi5_uart_clk.clkr, ++ [GSBI6_UART_SRC] = &gsbi6_uart_src.clkr, ++ [GSBI6_UART_CLK] = &gsbi6_uart_clk.clkr, ++ [GSBI7_UART_SRC] = &gsbi7_uart_src.clkr, ++ [GSBI7_UART_CLK] = &gsbi7_uart_clk.clkr, ++ [GSBI1_QUP_SRC] = &gsbi1_qup_src.clkr, ++ [GSBI1_QUP_CLK] = &gsbi1_qup_clk.clkr, ++ [GSBI2_QUP_SRC] = &gsbi2_qup_src.clkr, ++ [GSBI2_QUP_CLK] = &gsbi2_qup_clk.clkr, ++ [GSBI4_QUP_SRC] = &gsbi4_qup_src.clkr, ++ [GSBI4_QUP_CLK] = &gsbi4_qup_clk.clkr, ++ [GSBI5_QUP_SRC] = &gsbi5_qup_src.clkr, ++ [GSBI5_QUP_CLK] = &gsbi5_qup_clk.clkr, ++ [GSBI6_QUP_SRC] = &gsbi6_qup_src.clkr, ++ [GSBI6_QUP_CLK] = &gsbi6_qup_clk.clkr, ++ [GSBI7_QUP_SRC] = &gsbi7_qup_src.clkr, ++ [GSBI7_QUP_CLK] = &gsbi7_qup_clk.clkr, ++ [GP0_SRC] = &gp0_src.clkr, ++ [GP0_CLK] = &gp0_clk.clkr, ++ [GP1_SRC] = &gp1_src.clkr, ++ [GP1_CLK] = &gp1_clk.clkr, ++ [GP2_SRC] = &gp2_src.clkr, ++ [GP2_CLK] = &gp2_clk.clkr, ++ [PMEM_A_CLK] = &pmem_clk.clkr, ++ [PRNG_SRC] = &prng_src.clkr, ++ [PRNG_CLK] = &prng_clk.clkr, ++ [SDC1_SRC] = &sdc1_src.clkr, ++ [SDC1_CLK] = &sdc1_clk.clkr, ++ [SDC3_SRC] = &sdc3_src.clkr, ++ [SDC3_CLK] = &sdc3_clk.clkr, ++ [TSIF_REF_SRC] = &tsif_ref_src.clkr, ++ [TSIF_REF_CLK] = &tsif_ref_clk.clkr, ++ [DMA_BAM_H_CLK] = &dma_bam_h_clk.clkr, ++ [GSBI1_H_CLK] = &gsbi1_h_clk.clkr, ++ [GSBI2_H_CLK] = &gsbi2_h_clk.clkr, ++ [GSBI4_H_CLK] = &gsbi4_h_clk.clkr, ++ [GSBI5_H_CLK] = &gsbi5_h_clk.clkr, ++ [GSBI6_H_CLK] = &gsbi6_h_clk.clkr, ++ [GSBI7_H_CLK] = &gsbi7_h_clk.clkr, ++ [TSIF_H_CLK] = &tsif_h_clk.clkr, ++ [SDC1_H_CLK] = &sdc1_h_clk.clkr, ++ [SDC3_H_CLK] = &sdc3_h_clk.clkr, ++ [ADM0_CLK] = &adm0_clk.clkr, ++ [ADM0_PBUS_CLK] = &adm0_pbus_clk.clkr, ++ [PCIE_A_CLK] = &pcie_a_clk.clkr, ++ [PCIE_AUX_CLK] = &pcie_aux_clk.clkr, ++ [PCIE_H_CLK] = &pcie_h_clk.clkr, ++ [PCIE_PHY_CLK] = &pcie_phy_clk.clkr, ++ [SFAB_SATA_S_H_CLK] = &sfab_sata_s_h_clk.clkr, ++ [PMIC_ARB0_H_CLK] = &pmic_arb0_h_clk.clkr, ++ [PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr, ++ [PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr, ++ [RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr, ++ [SATA_H_CLK] = &sata_h_clk.clkr, ++ [SATA_CLK_SRC] = &sata_ref_src.clkr, ++ [SATA_RXOOB_CLK] = &sata_rxoob_clk.clkr, ++ [SATA_PMALIVE_CLK] = &sata_pmalive_clk.clkr, ++ [SATA_PHY_REF_CLK] = &sata_phy_ref_clk.clkr, ++ [SATA_A_CLK] = &sata_a_clk.clkr, ++ [SATA_PHY_CFG_CLK] = &sata_phy_cfg_clk.clkr, ++ [PCIE_ALT_REF_SRC] = &pcie_ref_src.clkr, ++ [PCIE_ALT_REF_CLK] = &pcie_ref_src_clk.clkr, ++ [PCIE_1_A_CLK] = &pcie1_a_clk.clkr, ++ [PCIE_1_AUX_CLK] = &pcie1_aux_clk.clkr, ++ [PCIE_1_H_CLK] = &pcie1_h_clk.clkr, ++ [PCIE_1_PHY_CLK] = &pcie1_phy_clk.clkr, ++ [PCIE_1_ALT_REF_SRC] = &pcie1_ref_src.clkr, ++ [PCIE_1_ALT_REF_CLK] = &pcie1_ref_src_clk.clkr, ++ [PCIE_2_A_CLK] = &pcie2_a_clk.clkr, ++ [PCIE_2_AUX_CLK] = &pcie2_aux_clk.clkr, ++ [PCIE_2_H_CLK] = &pcie2_h_clk.clkr, ++ [PCIE_2_PHY_CLK] = &pcie2_phy_clk.clkr, ++ [PCIE_2_ALT_REF_SRC] = &pcie2_ref_src.clkr, ++ [PCIE_2_ALT_REF_CLK] = &pcie2_ref_src_clk.clkr, ++ [USB30_MASTER_SRC] = &usb30_master_clk_src.clkr, ++ [USB30_0_MASTER_CLK] = &usb30_0_branch_clk.clkr, ++ [USB30_1_MASTER_CLK] = &usb30_1_branch_clk.clkr, ++ [USB30_UTMI_SRC] = &usb30_utmi_clk.clkr, ++ [USB30_0_UTMI_CLK] = &usb30_0_utmi_clk_ctl.clkr, ++ [USB30_1_UTMI_CLK] = &usb30_1_utmi_clk_ctl.clkr, ++ [USB_HS1_H_CLK] = &usb_hs1_h_clk.clkr, ++ [USB_HS1_XCVR_SRC] = &usb_hs1_xcvr_clk_src.clkr, ++ [USB_HS1_XCVR_CLK] = &usb_hs1_xcvr_clk.clkr, ++ [USB_FS1_H_CLK] = &usb_fs1_h_clk.clkr, ++ [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, ++}; ++ ++static const struct qcom_reset_map gcc_ipq806x_resets[] = { ++ [QDSS_STM_RESET] = { 0x2060, 6 }, ++ [AFAB_SMPSS_S_RESET] = { 0x20b8, 2 }, ++ [AFAB_SMPSS_M1_RESET] = { 0x20b8, 1 }, ++ [AFAB_SMPSS_M0_RESET] = { 0x20b8, 0 }, ++ [AFAB_EBI1_CH0_RESET] = { 0x20c0, 7 }, ++ [AFAB_EBI1_CH1_RESET] = { 0x20c4, 7 }, ++ [SFAB_ADM0_M0_RESET] = { 0x21e0, 7 }, ++ [SFAB_ADM0_M1_RESET] = { 0x21e4, 7 }, ++ [SFAB_ADM0_M2_RESET] = { 0x21e8, 7 }, ++ [ADM0_C2_RESET] = { 0x220c, 4 }, ++ [ADM0_C1_RESET] = { 0x220c, 3 }, ++ [ADM0_C0_RESET] = { 0x220c, 2 }, ++ [ADM0_PBUS_RESET] = { 0x220c, 1 }, ++ [ADM0_RESET] = { 0x220c, 0 }, ++ [QDSS_CLKS_SW_RESET] = { 0x2260, 5 }, ++ [QDSS_POR_RESET] = { 0x2260, 4 }, ++ [QDSS_TSCTR_RESET] = { 0x2260, 3 }, ++ [QDSS_HRESET_RESET] = { 0x2260, 2 }, ++ [QDSS_AXI_RESET] = { 0x2260, 1 }, ++ [QDSS_DBG_RESET] = { 0x2260, 0 }, ++ [SFAB_PCIE_M_RESET] = { 0x22d8, 1 }, ++ [SFAB_PCIE_S_RESET] = { 0x22d8, 0 }, ++ [PCIE_EXT_RESET] = { 0x22dc, 6 }, ++ [PCIE_PHY_RESET] = { 0x22dc, 5 }, ++ [PCIE_PCI_RESET] = { 0x22dc, 4 }, ++ [PCIE_POR_RESET] = { 0x22dc, 3 }, ++ [PCIE_HCLK_RESET] = { 0x22dc, 2 }, ++ [PCIE_ACLK_RESET] = { 0x22dc, 0 }, ++ [SFAB_LPASS_RESET] = { 0x23a0, 7 }, ++ [SFAB_AFAB_M_RESET] = { 0x23e0, 7 }, ++ [AFAB_SFAB_M0_RESET] = { 0x2420, 7 }, ++ [AFAB_SFAB_M1_RESET] = { 0x2424, 7 }, ++ [SFAB_SATA_S_RESET] = { 0x2480, 7 }, ++ [SFAB_DFAB_M_RESET] = { 0x2500, 7 }, ++ [DFAB_SFAB_M_RESET] = { 0x2520, 7 }, ++ [DFAB_SWAY0_RESET] = { 0x2540, 7 }, ++ [DFAB_SWAY1_RESET] = { 0x2544, 7 }, ++ [DFAB_ARB0_RESET] = { 0x2560, 7 }, ++ [DFAB_ARB1_RESET] = { 0x2564, 7 }, ++ [PPSS_PROC_RESET] = { 0x2594, 1 }, ++ [PPSS_RESET] = { 0x2594, 0 }, ++ [DMA_BAM_RESET] = { 0x25c0, 7 }, ++ [SPS_TIC_H_RESET] = { 0x2600, 7 }, ++ [SFAB_CFPB_M_RESET] = { 0x2680, 7 }, ++ [SFAB_CFPB_S_RESET] = { 0x26c0, 7 }, ++ [TSIF_H_RESET] = { 0x2700, 7 }, ++ [CE1_H_RESET] = { 0x2720, 7 }, ++ [CE1_CORE_RESET] = { 0x2724, 7 }, ++ [CE1_SLEEP_RESET] = { 0x2728, 7 }, ++ [CE2_H_RESET] = { 0x2740, 7 }, ++ [CE2_CORE_RESET] = { 0x2744, 7 }, ++ [SFAB_SFPB_M_RESET] = { 0x2780, 7 }, ++ [SFAB_SFPB_S_RESET] = { 0x27a0, 7 }, ++ [RPM_PROC_RESET] = { 0x27c0, 7 }, ++ [PMIC_SSBI2_RESET] = { 0x280c, 12 }, ++ [SDC1_RESET] = { 0x2830, 0 }, ++ [SDC2_RESET] = { 0x2850, 0 }, ++ [SDC3_RESET] = { 0x2870, 0 }, ++ [SDC4_RESET] = { 0x2890, 0 }, ++ [USB_HS1_RESET] = { 0x2910, 0 }, ++ [USB_HSIC_RESET] = { 0x2934, 0 }, ++ [USB_FS1_XCVR_RESET] = { 0x2974, 1 }, ++ [USB_FS1_RESET] = { 0x2974, 0 }, ++ [GSBI1_RESET] = { 0x29dc, 0 }, ++ [GSBI2_RESET] = { 0x29fc, 0 }, ++ [GSBI3_RESET] = { 0x2a1c, 0 }, ++ [GSBI4_RESET] = { 0x2a3c, 0 }, ++ [GSBI5_RESET] = { 0x2a5c, 0 }, ++ [GSBI6_RESET] = { 0x2a7c, 0 }, ++ [GSBI7_RESET] = { 0x2a9c, 0 }, ++ [SPDM_RESET] = { 0x2b6c, 0 }, ++ [SEC_CTRL_RESET] = { 0x2b80, 7 }, ++ [TLMM_H_RESET] = { 0x2ba0, 7 }, ++ [SFAB_SATA_M_RESET] = { 0x2c18, 0 }, ++ [SATA_RESET] = { 0x2c1c, 0 }, ++ [TSSC_RESET] = { 0x2ca0, 7 }, ++ [PDM_RESET] = { 0x2cc0, 12 }, ++ [MPM_H_RESET] = { 0x2da0, 7 }, ++ [MPM_RESET] = { 0x2da4, 0 }, ++ [SFAB_SMPSS_S_RESET] = { 0x2e00, 7 }, ++ [PRNG_RESET] = { 0x2e80, 12 }, ++ [SFAB_CE3_M_RESET] = { 0x36c8, 1 }, ++ [SFAB_CE3_S_RESET] = { 0x36c8, 0 }, ++ [CE3_SLEEP_RESET] = { 0x36d0, 7 }, ++ [PCIE_1_M_RESET] = { 0x3a98, 1 }, ++ [PCIE_1_S_RESET] = { 0x3a98, 0 }, ++ [PCIE_1_EXT_RESET] = { 0x3a9c, 6 }, ++ [PCIE_1_PHY_RESET] = { 0x3a9c, 5 }, ++ [PCIE_1_PCI_RESET] = { 0x3a9c, 4 }, ++ [PCIE_1_POR_RESET] = { 0x3a9c, 3 }, ++ [PCIE_1_HCLK_RESET] = { 0x3a9c, 2 }, ++ [PCIE_1_ACLK_RESET] = { 0x3a9c, 0 }, ++ [PCIE_2_M_RESET] = { 0x3ad8, 1 }, ++ [PCIE_2_S_RESET] = { 0x3ad8, 0 }, ++ [PCIE_2_EXT_RESET] = { 0x3adc, 6 }, ++ [PCIE_2_PHY_RESET] = { 0x3adc, 5 }, ++ [PCIE_2_PCI_RESET] = { 0x3adc, 4 }, ++ [PCIE_2_POR_RESET] = { 0x3adc, 3 }, ++ [PCIE_2_HCLK_RESET] = { 0x3adc, 2 }, ++ [PCIE_2_ACLK_RESET] = { 0x3adc, 0 }, ++ [SFAB_USB30_S_RESET] = { 0x3b54, 1 }, ++ [SFAB_USB30_M_RESET] = { 0x3b54, 0 }, ++ [USB30_0_PORT2_HS_PHY_RESET] = { 0x3b50, 5 }, ++ [USB30_0_MASTER_RESET] = { 0x3b50, 4 }, ++ [USB30_0_SLEEP_RESET] = { 0x3b50, 3 }, ++ [USB30_0_UTMI_PHY_RESET] = { 0x3b50, 2 }, ++ [USB30_0_POWERON_RESET] = { 0x3b50, 1 }, ++ [USB30_0_PHY_RESET] = { 0x3b50, 0 }, ++ [USB30_1_MASTER_RESET] = { 0x3b58, 4 }, ++ [USB30_1_SLEEP_RESET] = { 0x3b58, 3 }, ++ [USB30_1_UTMI_PHY_RESET] = { 0x3b58, 2 }, ++ [USB30_1_POWERON_RESET] = { 0x3b58, 1 }, ++ [USB30_1_PHY_RESET] = { 0x3b58, 0 }, ++ [NSSFB0_RESET] = { 0x3b60, 6 }, ++ [NSSFB1_RESET] = { 0x3b60, 7 }, ++}; ++ ++static const struct regmap_config gcc_ipq806x_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = 0x3e40, ++ .fast_io = true, ++}; ++ ++static const struct qcom_cc_desc gcc_ipq806x_desc = { ++ .config = &gcc_ipq806x_regmap_config, ++ .clks = gcc_ipq806x_clks, ++ .num_clks = ARRAY_SIZE(gcc_ipq806x_clks), ++ .resets = gcc_ipq806x_resets, ++ .num_resets = ARRAY_SIZE(gcc_ipq806x_resets), ++}; ++ ++static const struct of_device_id gcc_ipq806x_match_table[] = { ++ { .compatible = "qcom,gcc-ipq8064" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, gcc_ipq806x_match_table); ++ ++static int gcc_ipq806x_probe(struct platform_device *pdev) ++{ ++ struct clk *clk; ++ struct device *dev = &pdev->dev; ++ ++ /* Temporary until RPM clocks supported */ ++ clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 25000000); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 25000000); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ return qcom_cc_probe(pdev, &gcc_ipq806x_desc); ++} ++ ++static int gcc_ipq806x_remove(struct platform_device *pdev) ++{ ++ qcom_cc_remove(pdev); ++ return 0; ++} ++ ++static struct platform_driver gcc_ipq806x_driver = { ++ .probe = gcc_ipq806x_probe, ++ .remove = gcc_ipq806x_remove, ++ .driver = { ++ .name = "gcc-ipq806x", ++ .owner = THIS_MODULE, ++ .of_match_table = gcc_ipq806x_match_table, ++ }, ++}; ++ ++static int __init gcc_ipq806x_init(void) ++{ ++ return platform_driver_register(&gcc_ipq806x_driver); ++} ++core_initcall(gcc_ipq806x_init); ++ ++static void __exit gcc_ipq806x_exit(void) ++{ ++ platform_driver_unregister(&gcc_ipq806x_driver); ++} ++module_exit(gcc_ipq806x_exit); ++ ++MODULE_DESCRIPTION("QCOM GCC IPQ806x Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:gcc-ipq806x"); +diff --git a/include/dt-bindings/clock/qcom,gcc-ipq806x.h b/include/dt-bindings/clock/qcom,gcc-ipq806x.h +new file mode 100644 +index 0000000..3b0f8e7 +--- /dev/null ++++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h +@@ -0,0 +1,293 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef _DT_BINDINGS_CLK_GCC_IPQ806X_H ++#define _DT_BINDINGS_CLK_GCC_IPQ806X_H ++ ++#define AFAB_CLK_SRC 0 ++#define QDSS_STM_CLK 1 ++#define SCSS_A_CLK 2 ++#define SCSS_H_CLK 3 ++#define AFAB_CORE_CLK 4 ++#define SCSS_XO_SRC_CLK 5 ++#define AFAB_EBI1_CH0_A_CLK 6 ++#define AFAB_EBI1_CH1_A_CLK 7 ++#define AFAB_AXI_S0_FCLK 8 ++#define AFAB_AXI_S1_FCLK 9 ++#define AFAB_AXI_S2_FCLK 10 ++#define AFAB_AXI_S3_FCLK 11 ++#define AFAB_AXI_S4_FCLK 12 ++#define SFAB_CORE_CLK 13 ++#define SFAB_AXI_S0_FCLK 14 ++#define SFAB_AXI_S1_FCLK 15 ++#define SFAB_AXI_S2_FCLK 16 ++#define SFAB_AXI_S3_FCLK 17 ++#define SFAB_AXI_S4_FCLK 18 ++#define SFAB_AXI_S5_FCLK 19 ++#define SFAB_AHB_S0_FCLK 20 ++#define SFAB_AHB_S1_FCLK 21 ++#define SFAB_AHB_S2_FCLK 22 ++#define SFAB_AHB_S3_FCLK 23 ++#define SFAB_AHB_S4_FCLK 24 ++#define SFAB_AHB_S5_FCLK 25 ++#define SFAB_AHB_S6_FCLK 26 ++#define SFAB_AHB_S7_FCLK 27 ++#define QDSS_AT_CLK_SRC 28 ++#define QDSS_AT_CLK 29 ++#define QDSS_TRACECLKIN_CLK_SRC 30 ++#define QDSS_TRACECLKIN_CLK 31 ++#define QDSS_TSCTR_CLK_SRC 32 ++#define QDSS_TSCTR_CLK 33 ++#define SFAB_ADM0_M0_A_CLK 34 ++#define SFAB_ADM0_M1_A_CLK 35 ++#define SFAB_ADM0_M2_H_CLK 36 ++#define ADM0_CLK 37 ++#define ADM0_PBUS_CLK 38 ++#define IMEM0_A_CLK 39 ++#define QDSS_H_CLK 40 ++#define PCIE_A_CLK 41 ++#define PCIE_AUX_CLK 42 ++#define PCIE_H_CLK 43 ++#define PCIE_PHY_CLK 44 ++#define SFAB_CLK_SRC 45 ++#define SFAB_LPASS_Q6_A_CLK 46 ++#define SFAB_AFAB_M_A_CLK 47 ++#define AFAB_SFAB_M0_A_CLK 48 ++#define AFAB_SFAB_M1_A_CLK 49 ++#define SFAB_SATA_S_H_CLK 50 ++#define DFAB_CLK_SRC 51 ++#define DFAB_CLK 52 ++#define SFAB_DFAB_M_A_CLK 53 ++#define DFAB_SFAB_M_A_CLK 54 ++#define DFAB_SWAY0_H_CLK 55 ++#define DFAB_SWAY1_H_CLK 56 ++#define DFAB_ARB0_H_CLK 57 ++#define DFAB_ARB1_H_CLK 58 ++#define PPSS_H_CLK 59 ++#define PPSS_PROC_CLK 60 ++#define PPSS_TIMER0_CLK 61 ++#define PPSS_TIMER1_CLK 62 ++#define PMEM_A_CLK 63 ++#define DMA_BAM_H_CLK 64 ++#define SIC_H_CLK 65 ++#define SPS_TIC_H_CLK 66 ++#define CFPB_2X_CLK_SRC 67 ++#define CFPB_CLK 68 ++#define CFPB0_H_CLK 69 ++#define CFPB1_H_CLK 70 ++#define CFPB2_H_CLK 71 ++#define SFAB_CFPB_M_H_CLK 72 ++#define CFPB_MASTER_H_CLK 73 ++#define SFAB_CFPB_S_H_CLK 74 ++#define CFPB_SPLITTER_H_CLK 75 ++#define TSIF_H_CLK 76 ++#define TSIF_INACTIVITY_TIMERS_CLK 77 ++#define TSIF_REF_SRC 78 ++#define TSIF_REF_CLK 79 ++#define CE1_H_CLK 80 ++#define CE1_CORE_CLK 81 ++#define CE1_SLEEP_CLK 82 ++#define CE2_H_CLK 83 ++#define CE2_CORE_CLK 84 ++#define SFPB_H_CLK_SRC 85 ++#define SFPB_H_CLK 86 ++#define SFAB_SFPB_M_H_CLK 87 ++#define SFAB_SFPB_S_H_CLK 88 ++#define RPM_PROC_CLK 89 ++#define RPM_BUS_H_CLK 90 ++#define RPM_SLEEP_CLK 91 ++#define RPM_TIMER_CLK 92 ++#define RPM_MSG_RAM_H_CLK 93 ++#define PMIC_ARB0_H_CLK 94 ++#define PMIC_ARB1_H_CLK 95 ++#define PMIC_SSBI2_SRC 96 ++#define PMIC_SSBI2_CLK 97 ++#define SDC1_H_CLK 98 ++#define SDC2_H_CLK 99 ++#define SDC3_H_CLK 100 ++#define SDC4_H_CLK 101 ++#define SDC1_SRC 102 ++#define SDC1_CLK 103 ++#define SDC2_SRC 104 ++#define SDC2_CLK 105 ++#define SDC3_SRC 106 ++#define SDC3_CLK 107 ++#define SDC4_SRC 108 ++#define SDC4_CLK 109 ++#define USB_HS1_H_CLK 110 ++#define USB_HS1_XCVR_SRC 111 ++#define USB_HS1_XCVR_CLK 112 ++#define USB_HSIC_H_CLK 113 ++#define USB_HSIC_XCVR_SRC 114 ++#define USB_HSIC_XCVR_CLK 115 ++#define USB_HSIC_SYSTEM_CLK_SRC 116 ++#define USB_HSIC_SYSTEM_CLK 117 ++#define CFPB0_C0_H_CLK 118 ++#define CFPB0_D0_H_CLK 119 ++#define CFPB0_C1_H_CLK 120 ++#define CFPB0_D1_H_CLK 121 ++#define USB_FS1_H_CLK 122 ++#define USB_FS1_XCVR_SRC 123 ++#define USB_FS1_XCVR_CLK 124 ++#define USB_FS1_SYSTEM_CLK 125 ++#define GSBI_COMMON_SIM_SRC 126 ++#define GSBI1_H_CLK 127 ++#define GSBI2_H_CLK 128 ++#define GSBI3_H_CLK 129 ++#define GSBI4_H_CLK 130 ++#define GSBI5_H_CLK 131 ++#define GSBI6_H_CLK 132 ++#define GSBI7_H_CLK 133 ++#define GSBI1_QUP_SRC 134 ++#define GSBI1_QUP_CLK 135 ++#define GSBI2_QUP_SRC 136 ++#define GSBI2_QUP_CLK 137 ++#define GSBI3_QUP_SRC 138 ++#define GSBI3_QUP_CLK 139 ++#define GSBI4_QUP_SRC 140 ++#define GSBI4_QUP_CLK 141 ++#define GSBI5_QUP_SRC 142 ++#define GSBI5_QUP_CLK 143 ++#define GSBI6_QUP_SRC 144 ++#define GSBI6_QUP_CLK 145 ++#define GSBI7_QUP_SRC 146 ++#define GSBI7_QUP_CLK 147 ++#define GSBI1_UART_SRC 148 ++#define GSBI1_UART_CLK 149 ++#define GSBI2_UART_SRC 150 ++#define GSBI2_UART_CLK 151 ++#define GSBI3_UART_SRC 152 ++#define GSBI3_UART_CLK 153 ++#define GSBI4_UART_SRC 154 ++#define GSBI4_UART_CLK 155 ++#define GSBI5_UART_SRC 156 ++#define GSBI5_UART_CLK 157 ++#define GSBI6_UART_SRC 158 ++#define GSBI6_UART_CLK 159 ++#define GSBI7_UART_SRC 160 ++#define GSBI7_UART_CLK 161 ++#define GSBI1_SIM_CLK 162 ++#define GSBI2_SIM_CLK 163 ++#define GSBI3_SIM_CLK 164 ++#define GSBI4_SIM_CLK 165 ++#define GSBI5_SIM_CLK 166 ++#define GSBI6_SIM_CLK 167 ++#define GSBI7_SIM_CLK 168 ++#define USB_HSIC_HSIC_CLK_SRC 169 ++#define USB_HSIC_HSIC_CLK 170 ++#define USB_HSIC_HSIO_CAL_CLK 171 ++#define SPDM_CFG_H_CLK 172 ++#define SPDM_MSTR_H_CLK 173 ++#define SPDM_FF_CLK_SRC 174 ++#define SPDM_FF_CLK 175 ++#define SEC_CTRL_CLK 176 ++#define SEC_CTRL_ACC_CLK_SRC 177 ++#define SEC_CTRL_ACC_CLK 178 ++#define TLMM_H_CLK 179 ++#define TLMM_CLK 180 ++#define SATA_H_CLK 181 ++#define SATA_CLK_SRC 182 ++#define SATA_RXOOB_CLK 183 ++#define SATA_PMALIVE_CLK 184 ++#define SATA_PHY_REF_CLK 185 ++#define SATA_A_CLK 186 ++#define SATA_PHY_CFG_CLK 187 ++#define TSSC_CLK_SRC 188 ++#define TSSC_CLK 189 ++#define PDM_SRC 190 ++#define PDM_CLK 191 ++#define GP0_SRC 192 ++#define GP0_CLK 193 ++#define GP1_SRC 194 ++#define GP1_CLK 195 ++#define GP2_SRC 196 ++#define GP2_CLK 197 ++#define MPM_CLK 198 ++#define EBI1_CLK_SRC 199 ++#define EBI1_CH0_CLK 200 ++#define EBI1_CH1_CLK 201 ++#define EBI1_2X_CLK 202 ++#define EBI1_CH0_DQ_CLK 203 ++#define EBI1_CH1_DQ_CLK 204 ++#define EBI1_CH0_CA_CLK 205 ++#define EBI1_CH1_CA_CLK 206 ++#define EBI1_XO_CLK 207 ++#define SFAB_SMPSS_S_H_CLK 208 ++#define PRNG_SRC 209 ++#define PRNG_CLK 210 ++#define PXO_SRC 211 ++#define SPDM_CY_PORT0_CLK 212 ++#define SPDM_CY_PORT1_CLK 213 ++#define SPDM_CY_PORT2_CLK 214 ++#define SPDM_CY_PORT3_CLK 215 ++#define SPDM_CY_PORT4_CLK 216 ++#define SPDM_CY_PORT5_CLK 217 ++#define SPDM_CY_PORT6_CLK 218 ++#define SPDM_CY_PORT7_CLK 219 ++#define PLL0 220 ++#define PLL0_VOTE 221 ++#define PLL3 222 ++#define PLL3_VOTE 223 ++#define PLL4 224 ++#define PLL4_VOTE 225 ++#define PLL8 226 ++#define PLL8_VOTE 227 ++#define PLL9 228 ++#define PLL10 229 ++#define PLL11 230 ++#define PLL12 231 ++#define PLL14 232 ++#define PLL14_VOTE 233 ++#define PLL18 234 ++#define CE5_SRC 235 ++#define CE5_H_CLK 236 ++#define CE5_CORE_CLK 237 ++#define CE3_SLEEP_CLK 238 ++#define SFAB_AHB_S8_FCLK 239 ++#define SPDM_CY_PORT8_CLK 246 ++#define PCIE_ALT_REF_SRC 247 ++#define PCIE_ALT_REF_CLK 248 ++#define PCIE_1_A_CLK 249 ++#define PCIE_1_AUX_CLK 250 ++#define PCIE_1_H_CLK 251 ++#define PCIE_1_PHY_CLK 252 ++#define PCIE_1_ALT_REF_SRC 253 ++#define PCIE_1_ALT_REF_CLK 254 ++#define PCIE_2_A_CLK 255 ++#define PCIE_2_AUX_CLK 256 ++#define PCIE_2_H_CLK 257 ++#define PCIE_2_PHY_CLK 258 ++#define PCIE_2_ALT_REF_SRC 259 ++#define PCIE_2_ALT_REF_CLK 260 ++#define EBI2_CLK 261 ++#define USB30_SLEEP_CLK 262 ++#define USB30_UTMI_SRC 263 ++#define USB30_0_UTMI_CLK 264 ++#define USB30_1_UTMI_CLK 264 ++#define USB30_MASTER_SRC 265 ++#define USB30_0_MASTER_CLK 266 ++#define USB30_1_MASTER_CLK 267 ++#define GMAC_CORE1_CLK_SRC 268 ++#define GMAC_CORE2_CLK_SRC 269 ++#define GMAC_CORE3_CLK_SRC 270 ++#define GMAC_CORE4_CLK_SRC 271 ++#define GMAC_CORE1_CLK 272 ++#define GMAC_CORE2_CLK 273 ++#define GMAC_CORE3_CLK 274 ++#define GMAC_CORE4_CLK 275 ++#define UBI32_CORE1_CLK_SRC 276 ++#define UBI32_CORE2_CLK_SRC 277 ++#define UBI32_CORE1_CLK 278 ++#define UBI32_CORE2_CLK 279 ++ ++#endif +diff --git a/include/dt-bindings/reset/qcom,gcc-ipq806x.h b/include/dt-bindings/reset/qcom,gcc-ipq806x.h +new file mode 100644 +index 0000000..0ad5ef9 +--- /dev/null ++++ b/include/dt-bindings/reset/qcom,gcc-ipq806x.h +@@ -0,0 +1,132 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef _DT_BINDINGS_RESET_IPQ_806X_H ++#define _DT_BINDINGS_RESET_IPQ_806X_H ++ ++#define QDSS_STM_RESET 0 ++#define AFAB_SMPSS_S_RESET 1 ++#define AFAB_SMPSS_M1_RESET 2 ++#define AFAB_SMPSS_M0_RESET 3 ++#define AFAB_EBI1_CH0_RESET 4 ++#define AFAB_EBI1_CH1_RESET 5 ++#define SFAB_ADM0_M0_RESET 6 ++#define SFAB_ADM0_M1_RESET 7 ++#define SFAB_ADM0_M2_RESET 8 ++#define ADM0_C2_RESET 9 ++#define ADM0_C1_RESET 10 ++#define ADM0_C0_RESET 11 ++#define ADM0_PBUS_RESET 12 ++#define ADM0_RESET 13 ++#define QDSS_CLKS_SW_RESET 14 ++#define QDSS_POR_RESET 15 ++#define QDSS_TSCTR_RESET 16 ++#define QDSS_HRESET_RESET 17 ++#define QDSS_AXI_RESET 18 ++#define QDSS_DBG_RESET 19 ++#define SFAB_PCIE_M_RESET 20 ++#define SFAB_PCIE_S_RESET 21 ++#define PCIE_EXT_RESET 22 ++#define PCIE_PHY_RESET 23 ++#define PCIE_PCI_RESET 24 ++#define PCIE_POR_RESET 25 ++#define PCIE_HCLK_RESET 26 ++#define PCIE_ACLK_RESET 27 ++#define SFAB_LPASS_RESET 28 ++#define SFAB_AFAB_M_RESET 29 ++#define AFAB_SFAB_M0_RESET 30 ++#define AFAB_SFAB_M1_RESET 31 ++#define SFAB_SATA_S_RESET 32 ++#define SFAB_DFAB_M_RESET 33 ++#define DFAB_SFAB_M_RESET 34 ++#define DFAB_SWAY0_RESET 35 ++#define DFAB_SWAY1_RESET 36 ++#define DFAB_ARB0_RESET 37 ++#define DFAB_ARB1_RESET 38 ++#define PPSS_PROC_RESET 39 ++#define PPSS_RESET 40 ++#define DMA_BAM_RESET 41 ++#define SPS_TIC_H_RESET 42 ++#define SFAB_CFPB_M_RESET 43 ++#define SFAB_CFPB_S_RESET 44 ++#define TSIF_H_RESET 45 ++#define CE1_H_RESET 46 ++#define CE1_CORE_RESET 47 ++#define CE1_SLEEP_RESET 48 ++#define CE2_H_RESET 49 ++#define CE2_CORE_RESET 50 ++#define SFAB_SFPB_M_RESET 51 ++#define SFAB_SFPB_S_RESET 52 ++#define RPM_PROC_RESET 53 ++#define PMIC_SSBI2_RESET 54 ++#define SDC1_RESET 55 ++#define SDC2_RESET 56 ++#define SDC3_RESET 57 ++#define SDC4_RESET 58 ++#define USB_HS1_RESET 59 ++#define USB_HSIC_RESET 60 ++#define USB_FS1_XCVR_RESET 61 ++#define USB_FS1_RESET 62 ++#define GSBI1_RESET 63 ++#define GSBI2_RESET 64 ++#define GSBI3_RESET 65 ++#define GSBI4_RESET 66 ++#define GSBI5_RESET 67 ++#define GSBI6_RESET 68 ++#define GSBI7_RESET 69 ++#define SPDM_RESET 70 ++#define SEC_CTRL_RESET 71 ++#define TLMM_H_RESET 72 ++#define SFAB_SATA_M_RESET 73 ++#define SATA_RESET 74 ++#define TSSC_RESET 75 ++#define PDM_RESET 76 ++#define MPM_H_RESET 77 ++#define MPM_RESET 78 ++#define SFAB_SMPSS_S_RESET 79 ++#define PRNG_RESET 80 ++#define SFAB_CE3_M_RESET 81 ++#define SFAB_CE3_S_RESET 82 ++#define CE3_SLEEP_RESET 83 ++#define PCIE_1_M_RESET 84 ++#define PCIE_1_S_RESET 85 ++#define PCIE_1_EXT_RESET 86 ++#define PCIE_1_PHY_RESET 87 ++#define PCIE_1_PCI_RESET 88 ++#define PCIE_1_POR_RESET 89 ++#define PCIE_1_HCLK_RESET 90 ++#define PCIE_1_ACLK_RESET 91 ++#define PCIE_2_M_RESET 92 ++#define PCIE_2_S_RESET 93 ++#define PCIE_2_EXT_RESET 94 ++#define PCIE_2_PHY_RESET 95 ++#define PCIE_2_PCI_RESET 96 ++#define PCIE_2_POR_RESET 97 ++#define PCIE_2_HCLK_RESET 98 ++#define PCIE_2_ACLK_RESET 99 ++#define SFAB_USB30_S_RESET 100 ++#define SFAB_USB30_M_RESET 101 ++#define USB30_0_PORT2_HS_PHY_RESET 102 ++#define USB30_0_MASTER_RESET 103 ++#define USB30_0_SLEEP_RESET 104 ++#define USB30_0_UTMI_PHY_RESET 105 ++#define USB30_0_POWERON_RESET 106 ++#define USB30_0_PHY_RESET 107 ++#define USB30_1_MASTER_RESET 108 ++#define USB30_1_SLEEP_RESET 109 ++#define USB30_1_UTMI_PHY_RESET 110 ++#define USB30_1_POWERON_RESET 111 ++#define USB30_1_PHY_RESET 112 ++#define NSSFB0_RESET 113 ++#define NSSFB1_RESET 114 ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0126-clk-Add-safe-switch-hook.patch b/target/linux/ipq806x/patches/0126-clk-Add-safe-switch-hook.patch new file mode 100644 index 0000000000..bda409363e --- /dev/null +++ b/target/linux/ipq806x/patches/0126-clk-Add-safe-switch-hook.patch @@ -0,0 +1,155 @@ +From b7b3ceec506179d59e46e3a8ea8b370872cc7fcb Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Mon, 31 Mar 2014 16:52:29 -0700 +Subject: [PATCH 126/182] clk: Add safe switch hook + +Sometimes clocks can't accept their parent source turning off +while the source is reprogrammed to a different rate. Most +notably CPU clocks require a way to switch away from the current +PLL they're running on, reprogram that PLL to a new rate, and +then switch back to the PLL with the new rate once they're done. +Add a hook that drivers can implement allowing them to return a +'safe parent' that they can switch their parent to while the +upstream source is reprogrammed to support this. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/clk.c | 53 ++++++++++++++++++++++++++++++++++++------ + include/linux/clk-private.h | 2 ++ + include/linux/clk-provider.h | 1 + + 3 files changed, 49 insertions(+), 7 deletions(-) + +diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c +index b94a311..0582068 100644 +--- a/drivers/clk/clk.c ++++ b/drivers/clk/clk.c +@@ -1356,6 +1356,7 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate, + struct clk *new_parent, u8 p_index) + { + struct clk *child; ++ struct clk *parent; + + clk->new_rate = new_rate; + clk->new_parent = new_parent; +@@ -1365,6 +1366,17 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate, + if (new_parent && new_parent != clk->parent) + new_parent->new_child = clk; + ++ if (clk->ops->get_safe_parent) { ++ parent = clk->ops->get_safe_parent(clk->hw); ++ if (parent) { ++ 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) { + if (child->ops->recalc_rate) + child->new_rate = child->ops->recalc_rate(child->hw, new_rate); +@@ -1450,14 +1462,42 @@ out: + 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); ++ 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) ++ ret = __clk_notify(clk, event, clk->rate, ++ clk->new_rate); ++ if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE) + fail_clk = clk; + } + +@@ -1499,7 +1539,8 @@ static void clk_change_rate(struct clk *clk) + else if (clk->parent) + best_parent_rate = clk->parent->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) { +@@ -1522,9 +1563,6 @@ static void clk_change_rate(struct clk *clk) + else + clk->rate = best_parent_rate; + +- if (clk->notifier_count && old_rate != clk->rate) +- __clk_notify(clk, POST_RATE_CHANGE, 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) +@@ -1598,6 +1636,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate) + /* change the rates */ + clk_change_rate(top); + ++ clk_propagate_rate_change(top, POST_RATE_CHANGE); + out: + clk_prepare_unlock(); + +diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h +index efbf70b..f48684a 100644 +--- a/include/linux/clk-private.h ++++ b/include/linux/clk-private.h +@@ -38,8 +38,10 @@ struct clk { + struct clk **parents; + u8 num_parents; + u8 new_parent_index; ++ u8 safe_parent_index; + unsigned long rate; + unsigned long new_rate; ++ struct clk *safe_parent; + struct clk *new_parent; + struct clk *new_child; + unsigned long flags; +diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h +index 939533d..300fcb8 100644 +--- a/include/linux/clk-provider.h ++++ b/include/linux/clk-provider.h +@@ -157,6 +157,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 *(*get_safe_parent)(struct clk_hw *hw); + int (*set_rate)(struct clk_hw *hw, unsigned long, + unsigned long); + int (*set_rate_and_parent)(struct clk_hw *hw, +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0127-clk-qcom-Add-support-for-setting-rates-on-PLLs.patch b/target/linux/ipq806x/patches/0127-clk-qcom-Add-support-for-setting-rates-on-PLLs.patch new file mode 100644 index 0000000000..2983b8444b --- /dev/null +++ b/target/linux/ipq806x/patches/0127-clk-qcom-Add-support-for-setting-rates-on-PLLs.patch @@ -0,0 +1,155 @@ +From fd06a2cc719296f65a280cb1533b125f63cfcb34 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Mon, 28 Apr 2014 15:58:11 -0700 +Subject: [PATCH 127/182] clk: qcom: Add support for setting rates on PLLs + +Some PLLs may require changing their rate at runtime. Add support +for these PLLs. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/clk-pll.c | 68 +++++++++++++++++++++++++++++++++++++++++++- + drivers/clk/qcom/clk-pll.h | 20 +++++++++++++ + 2 files changed, 87 insertions(+), 1 deletion(-) + +diff --git a/drivers/clk/qcom/clk-pll.c b/drivers/clk/qcom/clk-pll.c +index 0f927c5..80c7a76 100644 +--- a/drivers/clk/qcom/clk-pll.c ++++ b/drivers/clk/qcom/clk-pll.c +@@ -97,7 +97,7 @@ static unsigned long + clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) + { + struct clk_pll *pll = to_clk_pll(hw); +- u32 l, m, n; ++ u32 l, m, n, config; + unsigned long rate; + u64 tmp; + +@@ -116,13 +116,79 @@ clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) + do_div(tmp, n); + rate += tmp; + } ++ if (pll->post_div_width) { ++ regmap_read(pll->clkr.regmap, pll->config_reg, &config); ++ config >>= pll->post_div_shift; ++ config &= BIT(pll->post_div_width) - 1; ++ rate /= config + 1; ++ } ++ + return rate; + } + ++static const ++struct pll_freq_tbl *find_freq(const struct pll_freq_tbl *f, unsigned long rate) ++{ ++ if (!f) ++ return NULL; ++ ++ for (; f->freq; f++) ++ if (rate <= f->freq) ++ return f; ++ ++ return NULL; ++} ++ ++static long ++clk_pll_determine_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *p_rate, struct clk **p) ++{ ++ struct clk_pll *pll = to_clk_pll(hw); ++ const struct pll_freq_tbl *f; ++ ++ f = find_freq(pll->freq_tbl, rate); ++ if (!f) ++ return clk_pll_recalc_rate(hw, *p_rate); ++ ++ return f->freq; ++} ++ ++static int ++clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long p_rate) ++{ ++ struct clk_pll *pll = to_clk_pll(hw); ++ const struct pll_freq_tbl *f; ++ bool enabled; ++ u32 mode; ++ u32 enable_mask = PLL_OUTCTRL | PLL_BYPASSNL | PLL_RESET_N; ++ ++ f = find_freq(pll->freq_tbl, rate); ++ if (!f) ++ return -EINVAL; ++ ++ regmap_read(pll->clkr.regmap, pll->mode_reg, &mode); ++ enabled = (mode & enable_mask) == enable_mask; ++ ++ if (enabled) ++ clk_pll_disable(hw); ++ ++ regmap_update_bits(pll->clkr.regmap, pll->l_reg, 0x3ff, f->l); ++ regmap_update_bits(pll->clkr.regmap, pll->m_reg, 0x7ffff, f->m); ++ regmap_update_bits(pll->clkr.regmap, pll->n_reg, 0x7ffff, f->n); ++ regmap_write(pll->clkr.regmap, pll->config_reg, f->ibits); ++ ++ if (enabled) ++ clk_pll_enable(hw); ++ ++ return 0; ++} ++ + const struct clk_ops clk_pll_ops = { + .enable = clk_pll_enable, + .disable = clk_pll_disable, + .recalc_rate = clk_pll_recalc_rate, ++ .determine_rate = clk_pll_determine_rate, ++ .set_rate = clk_pll_set_rate, + }; + EXPORT_SYMBOL_GPL(clk_pll_ops); + +diff --git a/drivers/clk/qcom/clk-pll.h b/drivers/clk/qcom/clk-pll.h +index 0775a99..5f9928b 100644 +--- a/drivers/clk/qcom/clk-pll.h ++++ b/drivers/clk/qcom/clk-pll.h +@@ -18,6 +18,21 @@ + #include "clk-regmap.h" + + /** ++ * struct pll_freq_tbl - PLL frequency table ++ * @l: L value ++ * @m: M value ++ * @n: N value ++ * @ibits: internal values ++ */ ++struct pll_freq_tbl { ++ unsigned long freq; ++ u16 l; ++ u16 m; ++ u16 n; ++ u32 ibits; ++}; ++ ++/** + * struct clk_pll - phase locked loop (PLL) + * @l_reg: L register + * @m_reg: M register +@@ -26,6 +41,7 @@ + * @mode_reg: mode register + * @status_reg: status register + * @status_bit: ANDed with @status_reg to determine if PLL is enabled ++ * @freq_tbl: PLL frequency table + * @hw: handle between common and hardware-specific interfaces + */ + struct clk_pll { +@@ -36,6 +52,10 @@ struct clk_pll { + u32 mode_reg; + u32 status_reg; + u8 status_bit; ++ u8 post_div_width; ++ u8 post_div_shift; ++ ++ const struct pll_freq_tbl *freq_tbl; + + struct clk_regmap clkr; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0128-clk-qcom-Add-support-for-banked-MD-RCGs.patch b/target/linux/ipq806x/patches/0128-clk-qcom-Add-support-for-banked-MD-RCGs.patch new file mode 100644 index 0000000000..0b9e237c71 --- /dev/null +++ b/target/linux/ipq806x/patches/0128-clk-qcom-Add-support-for-banked-MD-RCGs.patch @@ -0,0 +1,317 @@ +From 856324d2daa3246ac62d7920d6a274e1fa35bcf5 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Mon, 28 Apr 2014 15:59:16 -0700 +Subject: [PATCH 128/182] clk: qcom: Add support for banked MD RCGs + +The banked MD RCGs in global clock control have a different +register layout than the ones implemented in multimedia clock +control. Add support for these types of clocks so we can change +the rates of the UBI32 clocks. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/clk-rcg.c | 99 ++++++++++++++++++++------------------- + drivers/clk/qcom/clk-rcg.h | 5 +- + drivers/clk/qcom/mmcc-msm8960.c | 24 +++++++--- + 3 files changed, 73 insertions(+), 55 deletions(-) + +diff --git a/drivers/clk/qcom/clk-rcg.c b/drivers/clk/qcom/clk-rcg.c +index abfc2b6..7bce729 100644 +--- a/drivers/clk/qcom/clk-rcg.c ++++ b/drivers/clk/qcom/clk-rcg.c +@@ -67,16 +67,16 @@ static u8 clk_dyn_rcg_get_parent(struct clk_hw *hw) + { + struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw); + int num_parents = __clk_get_num_parents(hw->clk); +- u32 ns, ctl; ++ u32 ns, reg; + int bank; + int i; + struct src_sel *s; + +- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl); +- bank = reg_to_bank(rcg, ctl); ++ regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); ++ bank = reg_to_bank(rcg, reg); + s = &rcg->s[bank]; + +- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); ++ regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns); + ns = ns_to_src(s, ns); + + for (i = 0; i < num_parents; i++) +@@ -192,90 +192,93 @@ static u32 mn_to_reg(struct mn *mn, u32 m, u32 n, u32 val) + + static void configure_bank(struct clk_dyn_rcg *rcg, const struct freq_tbl *f) + { +- u32 ns, md, ctl, *regp; ++ u32 ns, md, reg; + int bank, new_bank; + struct mn *mn; + struct pre_div *p; + struct src_sel *s; + bool enabled; +- u32 md_reg; +- u32 bank_reg; ++ u32 md_reg, ns_reg; + bool banked_mn = !!rcg->mn[1].width; ++ bool banked_p = !!rcg->p[1].pre_div_width; + struct clk_hw *hw = &rcg->clkr.hw; + + enabled = __clk_is_enabled(hw->clk); + +- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); +- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl); +- +- if (banked_mn) { +- regp = &ctl; +- bank_reg = rcg->clkr.enable_reg; +- } else { +- regp = &ns; +- bank_reg = rcg->ns_reg; +- } +- +- bank = reg_to_bank(rcg, *regp); ++ regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); ++ bank = reg_to_bank(rcg, reg); + new_bank = enabled ? !bank : bank; + ++ ns_reg = rcg->ns_reg[new_bank]; ++ regmap_read(rcg->clkr.regmap, ns_reg, &ns); ++ + if (banked_mn) { + mn = &rcg->mn[new_bank]; + md_reg = rcg->md_reg[new_bank]; + + ns |= BIT(mn->mnctr_reset_bit); +- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); ++ regmap_write(rcg->clkr.regmap, ns_reg, ns); + + regmap_read(rcg->clkr.regmap, md_reg, &md); + md = mn_to_md(mn, f->m, f->n, md); + regmap_write(rcg->clkr.regmap, md_reg, md); + + ns = mn_to_ns(mn, f->m, f->n, ns); +- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); ++ regmap_write(rcg->clkr.regmap, ns_reg, ns); + +- ctl = mn_to_reg(mn, f->m, f->n, ctl); +- regmap_write(rcg->clkr.regmap, rcg->clkr.enable_reg, ctl); ++ /* Two NS registers means mode control is in NS register */ ++ if (rcg->ns_reg[0] != rcg->ns_reg[1]) { ++ ns = mn_to_reg(mn, f->m, f->n, ns); ++ regmap_write(rcg->clkr.regmap, ns_reg, ns); ++ } else { ++ reg = mn_to_reg(mn, f->m, f->n, reg); ++ regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg); ++ } + + ns &= ~BIT(mn->mnctr_reset_bit); +- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); +- } else { ++ regmap_write(rcg->clkr.regmap, ns_reg, ns); ++ } ++ ++ if (banked_p) { + p = &rcg->p[new_bank]; + ns = pre_div_to_ns(p, f->pre_div - 1, ns); + } + + s = &rcg->s[new_bank]; + ns = src_to_ns(s, s->parent_map[f->src], ns); +- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); ++ regmap_write(rcg->clkr.regmap, ns_reg, ns); + + if (enabled) { +- *regp ^= BIT(rcg->mux_sel_bit); +- regmap_write(rcg->clkr.regmap, bank_reg, *regp); ++ regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); ++ reg ^= BIT(rcg->mux_sel_bit); ++ regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg); + } + } + + static int clk_dyn_rcg_set_parent(struct clk_hw *hw, u8 index) + { + struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw); +- u32 ns, ctl, md, reg; ++ u32 ns, md, reg; + int bank; + struct freq_tbl f = { 0 }; + bool banked_mn = !!rcg->mn[1].width; ++ bool banked_p = !!rcg->p[1].pre_div_width; + +- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); +- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl); +- reg = banked_mn ? ctl : ns; +- ++ regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); + bank = reg_to_bank(rcg, reg); + ++ regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns); ++ + if (banked_mn) { + regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md); + f.m = md_to_m(&rcg->mn[bank], md); + f.n = ns_m_to_n(&rcg->mn[bank], ns, f.m); +- } else { +- f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1; + } +- f.src = index; + ++ if (banked_p) ++ f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1; ++ ++ f.src = index; + configure_bank(rcg, &f); + + return 0; +@@ -336,28 +339,30 @@ clk_dyn_rcg_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) + u32 m, n, pre_div, ns, md, mode, reg; + int bank; + struct mn *mn; ++ bool banked_p = !!rcg->p[1].pre_div_width; + bool banked_mn = !!rcg->mn[1].width; + +- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); +- +- if (banked_mn) +- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, ®); +- else +- reg = ns; +- ++ regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); + bank = reg_to_bank(rcg, reg); + ++ regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns); ++ m = n = pre_div = mode = 0; ++ + if (banked_mn) { + mn = &rcg->mn[bank]; + regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md); + m = md_to_m(mn, md); + n = ns_m_to_n(mn, ns, m); ++ /* Two NS registers means mode control is in NS register */ ++ if (rcg->ns_reg[0] != rcg->ns_reg[1]) ++ reg = ns; + mode = reg_to_mnctr_mode(mn, reg); +- return calc_rate(parent_rate, m, n, mode, 0); +- } else { +- pre_div = ns_to_pre_div(&rcg->p[bank], ns); +- return calc_rate(parent_rate, 0, 0, 0, pre_div); + } ++ ++ if (banked_p) ++ pre_div = ns_to_pre_div(&rcg->p[bank], ns); ++ ++ return calc_rate(parent_rate, m, n, mode, pre_div); + } + + static const +diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h +index b9ec11d..5f8b06d 100644 +--- a/drivers/clk/qcom/clk-rcg.h ++++ b/drivers/clk/qcom/clk-rcg.h +@@ -102,7 +102,7 @@ extern const struct clk_ops clk_rcg_ops; + * struct clk_dyn_rcg - root clock generator with glitch free mux + * + * @mux_sel_bit: bit to switch glitch free mux +- * @ns_reg: NS register ++ * @ns_reg: NS0 and NS1 register + * @md_reg: MD0 and MD1 register + * @mn: mn counter (banked) + * @s: source selector (banked) +@@ -112,8 +112,9 @@ extern const struct clk_ops clk_rcg_ops; + * + */ + struct clk_dyn_rcg { +- u32 ns_reg; ++ u32 ns_reg[2]; + u32 md_reg[2]; ++ u32 bank_reg; + + u8 mux_sel_bit; + +diff --git a/drivers/clk/qcom/mmcc-msm8960.c b/drivers/clk/qcom/mmcc-msm8960.c +index 12f3c0b..ce48ad1 100644 +--- a/drivers/clk/qcom/mmcc-msm8960.c ++++ b/drivers/clk/qcom/mmcc-msm8960.c +@@ -726,9 +726,11 @@ static struct freq_tbl clk_tbl_gfx2d[] = { + }; + + static struct clk_dyn_rcg gfx2d0_src = { +- .ns_reg = 0x0070, ++ .ns_reg[0] = 0x0070, ++ .ns_reg[1] = 0x0070, + .md_reg[0] = 0x0064, + .md_reg[1] = 0x0068, ++ .bank_reg = 0x0060, + .mn[0] = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 25, +@@ -784,9 +786,11 @@ static struct clk_branch gfx2d0_clk = { + }; + + static struct clk_dyn_rcg gfx2d1_src = { +- .ns_reg = 0x007c, ++ .ns_reg[0] = 0x007c, ++ .ns_reg[1] = 0x007c, + .md_reg[0] = 0x0078, + .md_reg[1] = 0x006c, ++ .bank_reg = 0x0074, + .mn[0] = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 25, +@@ -862,9 +866,11 @@ static struct freq_tbl clk_tbl_gfx3d[] = { + }; + + static struct clk_dyn_rcg gfx3d_src = { +- .ns_reg = 0x008c, ++ .ns_reg[0] = 0x008c, ++ .ns_reg[1] = 0x008c, + .md_reg[0] = 0x0084, + .md_reg[1] = 0x0088, ++ .bank_reg = 0x0080, + .mn[0] = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 25, +@@ -1051,9 +1057,11 @@ static struct freq_tbl clk_tbl_mdp[] = { + }; + + static struct clk_dyn_rcg mdp_src = { +- .ns_reg = 0x00d0, ++ .ns_reg[0] = 0x00d0, ++ .ns_reg[1] = 0x00d0, + .md_reg[0] = 0x00c4, + .md_reg[1] = 0x00c8, ++ .bank_reg = 0x00c0, + .mn[0] = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 31, +@@ -1158,7 +1166,9 @@ static struct freq_tbl clk_tbl_rot[] = { + }; + + static struct clk_dyn_rcg rot_src = { +- .ns_reg = 0x00e8, ++ .ns_reg[0] = 0x00e8, ++ .ns_reg[1] = 0x00e8, ++ .bank_reg = 0x00e8, + .p[0] = { + .pre_div_shift = 22, + .pre_div_width = 4, +@@ -1355,9 +1365,11 @@ static struct freq_tbl clk_tbl_vcodec[] = { + }; + + static struct clk_dyn_rcg vcodec_src = { +- .ns_reg = 0x0100, ++ .ns_reg[0] = 0x0100, ++ .ns_reg[1] = 0x0100, + .md_reg[0] = 0x00fc, + .md_reg[1] = 0x0128, ++ .bank_reg = 0x00f8, + .mn[0] = { + .mnctr_en_bit = 5, + .mnctr_reset_bit = 31, +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0129-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch b/target/linux/ipq806x/patches/0129-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch new file mode 100644 index 0000000000..4c59580372 --- /dev/null +++ b/target/linux/ipq806x/patches/0129-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch @@ -0,0 +1,869 @@ +From 92fde23153240a6173645dabe4d99f4aa6570bb7 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Mon, 28 Apr 2014 16:01:03 -0700 +Subject: [PATCH 129/182] clk: qcom: Add support for NSS/GMAC clocks and + resets + +The NSS driver expects one virtual clock that actually represents +two clocks (one for each UBI32 core). Register the ubi32 core +clocks and also make a wrapper virtual clock called nss_core_clk +to be used by the driver. This will properly handle switching the +rates of both clocks at the same time like how the NSS driver +expects it. Also add the TCM clock and the NSS resets. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/gcc-ipq806x.c | 710 +++++++++++++++++++++++++- + include/dt-bindings/clock/qcom,gcc-ipq806x.h | 3 + + include/dt-bindings/reset/qcom,gcc-ipq806x.h | 43 ++ + 3 files changed, 755 insertions(+), 1 deletion(-) + +diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c +index 278c5fe..f7916be 100644 +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -32,6 +32,33 @@ + #include "clk-branch.h" + #include "reset.h" + ++static struct clk_pll pll0 = { ++ .l_reg = 0x30c4, ++ .m_reg = 0x30c8, ++ .n_reg = 0x30cc, ++ .config_reg = 0x30d4, ++ .mode_reg = 0x30c0, ++ .status_reg = 0x30d8, ++ .status_bit = 16, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "pll0", ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .ops = &clk_pll_ops, ++ }, ++}; ++ ++static struct clk_regmap pll0_vote = { ++ .enable_reg = 0x34c0, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pll0_vote", ++ .parent_names = (const char *[]){ "pll0" }, ++ .num_parents = 1, ++ .ops = &clk_pll_vote_ops, ++ }, ++}; ++ + static struct clk_pll pll3 = { + .l_reg = 0x3164, + .m_reg = 0x3168, +@@ -102,11 +129,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, +@@ -157,6 +219,22 @@ static const char *gcc_pxo_pll8_pll0_map[] = { + "pll0", + }; + ++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 }, +@@ -2132,12 +2210,567 @@ static struct clk_branch usb_fs1_h_clk = { + }, + }; + ++static const struct freq_tbl clk_tbl_gmac[] = { ++ { 133000000, P_PLL0, 1, 50, 301 }, ++ { } ++}; ++ ++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 int nss_core_clk_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ int ret; ++ ++ ret = clk_dyn_rcg_ops.set_rate(&ubi32_core1_src_clk.clkr.hw, rate, ++ parent_rate); ++ if (ret) ++ return ret; ++ ++ return clk_dyn_rcg_ops.set_rate(&ubi32_core2_src_clk.clkr.hw, rate, ++ parent_rate); ++} ++ ++static int ++nss_core_clk_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate, u8 index) ++{ ++ int ret; ++ ++ ret = clk_dyn_rcg_ops.set_rate_and_parent( ++ &ubi32_core1_src_clk.clkr.hw, rate, parent_rate, index); ++ if (ret) ++ return ret; ++ ++ ret = clk_dyn_rcg_ops.set_rate_and_parent( ++ &ubi32_core2_src_clk.clkr.hw, rate, parent_rate, index); ++ return ret; ++} ++ ++static long nss_core_clk_determine_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *p_rate, struct clk **p) ++{ ++ return clk_dyn_rcg_ops.determine_rate(&ubi32_core1_src_clk.clkr.hw, ++ rate, p_rate, p); ++} ++ ++static unsigned long ++nss_core_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) ++{ ++ return clk_dyn_rcg_ops.recalc_rate(&ubi32_core1_src_clk.clkr.hw, ++ parent_rate); ++} ++ ++static u8 nss_core_clk_get_parent(struct clk_hw *hw) ++{ ++ return clk_dyn_rcg_ops.get_parent(&ubi32_core1_src_clk.clkr.hw); ++} ++ ++static int nss_core_clk_set_parent(struct clk_hw *hw, u8 i) ++{ ++ int ret; ++ ++ ret = clk_dyn_rcg_ops.set_parent(&ubi32_core1_src_clk.clkr.hw, i); ++ if (ret) ++ return ret; ++ ++ return clk_dyn_rcg_ops.set_parent(&ubi32_core2_src_clk.clkr.hw, i); ++} ++ ++static struct clk *nss_core_clk_get_safe_parent(struct clk_hw *hw) ++{ ++ return clk_get_parent_by_index(hw->clk, ++ ubi32_core2_src_clk.s[0].parent_map[P_PLL8]); ++} ++ ++static const struct clk_ops clk_ops_nss_core = { ++ .set_rate = nss_core_clk_set_rate, ++ .set_rate_and_parent = nss_core_clk_set_rate_and_parent, ++ .determine_rate = nss_core_clk_determine_rate, ++ .recalc_rate = nss_core_clk_recalc_rate, ++ .get_parent = nss_core_clk_get_parent, ++ .set_parent = nss_core_clk_set_parent, ++ .get_safe_parent = nss_core_clk_get_safe_parent, ++}; ++ ++/* Virtual clock for nss core clocks */ ++static struct clk_regmap nss_core_clk = { ++ .hw.init = &(struct clk_init_data){ ++ .name = "nss_core_clk", ++ .ops = &clk_ops_nss_core, ++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, ++ .num_parents = 5, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++}; ++ + static struct clk_regmap *gcc_ipq806x_clks[] = { ++ [PLL0] = &pll0.clkr, ++ [PLL0_VOTE] = &pll0_vote, + [PLL3] = &pll3.clkr, + [PLL8] = &pll8.clkr, + [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, +@@ -2232,6 +2865,19 @@ static struct clk_regmap *gcc_ipq806x_clks[] = { + [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, ++ [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, ++ [NSS_CORE_CLK] = &nss_core_clk, + }; + + static const struct qcom_reset_map gcc_ipq806x_resets[] = { +@@ -2350,6 +2996,48 @@ static const struct qcom_reset_map gcc_ipq806x_resets[] = { + [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 = { +@@ -2378,6 +3066,8 @@ static int gcc_ipq806x_probe(struct platform_device *pdev) + { + 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); +@@ -2388,7 +3078,25 @@ static int gcc_ipq806x_probe(struct platform_device *pdev) + 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) +diff --git a/include/dt-bindings/clock/qcom,gcc-ipq806x.h b/include/dt-bindings/clock/qcom,gcc-ipq806x.h +index 3b0f8e7..0fd3e8a 100644 +--- a/include/dt-bindings/clock/qcom,gcc-ipq806x.h ++++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h +@@ -289,5 +289,8 @@ + #define UBI32_CORE2_CLK_SRC 277 + #define UBI32_CORE1_CLK 278 + #define UBI32_CORE2_CLK 279 ++#define NSSTCM_CLK_SRC 280 ++#define NSSTCM_CLK 281 ++#define NSS_CORE_CLK 282 /* Virtual */ + + #endif +diff --git a/include/dt-bindings/reset/qcom,gcc-ipq806x.h b/include/dt-bindings/reset/qcom,gcc-ipq806x.h +index 0ad5ef9..de9c814 100644 +--- 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.7.10.4 + diff --git a/target/linux/ipq806x/patches/0130-ARM-qcom-Add-initial-IPQ8064-SoC-and-AP148-device-tr.patch b/target/linux/ipq806x/patches/0130-ARM-qcom-Add-initial-IPQ8064-SoC-and-AP148-device-tr.patch new file mode 100644 index 0000000000..4c0eed85ca --- /dev/null +++ b/target/linux/ipq806x/patches/0130-ARM-qcom-Add-initial-IPQ8064-SoC-and-AP148-device-tr.patch @@ -0,0 +1,269 @@ +From 1c6e51ffb10f5bf93a3018c7c1e04d7ed93f944e Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Fri, 7 Mar 2014 10:56:59 -0600 +Subject: [PATCH 130/182] ARM: qcom: Add initial IPQ8064 SoC and AP148 device + trees + +Add basic IPQ8064 SoC include device tree and support for basic booting on +the AP148 Reference board. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/Makefile | 1 + + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 25 +++++ + arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi | 1 + + arch/arm/boot/dts/qcom-ipq8064.dtsi | 176 ++++++++++++++++++++++++++++++ + arch/arm/mach-qcom/board.c | 2 + + 5 files changed, 205 insertions(+) + create mode 100644 arch/arm/boot/dts/qcom-ipq8064-ap148.dts + create mode 100644 arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi + create mode 100644 arch/arm/boot/dts/qcom-ipq8064.dtsi + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index f2aeb95..f22c51d 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -235,6 +235,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \ + qcom-apq8064-ifc6410.dtb \ + qcom-apq8074-dragonboard.dtb \ + qcom-apq8084-mtp.dtb \ ++ qcom-ipq8064-ap148.dtb \ + qcom-msm8660-surf.dtb \ + qcom-msm8960-cdp.dtb + dtb-$(CONFIG_ARCH_U8500) += ste-snowball.dtb \ +diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +new file mode 100644 +index 0000000..100b6eb +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -0,0 +1,25 @@ ++#include "qcom-ipq8064-v1.0.dtsi" ++ ++/ { ++ model = "Qualcomm IPQ8064/AP148"; ++ compatible = "qcom,ipq8064-ap148", "qcom,ipq8064"; ++ ++ reserved-memory { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ rsvd@41200000 { ++ reg = <0x41200000 0x300000>; ++ no-map; ++ }; ++ }; ++ ++ soc { ++ gsbi@16300000 { ++ qcom,mode = <GSBI_PROT_I2C_UART>; ++ status = "ok"; ++ serial@16340000 { ++ status = "ok"; ++ }; ++ }; ++ }; ++}; +diff --git a/arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi b/arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi +new file mode 100644 +index 0000000..7093b07 +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi +@@ -0,0 +1 @@ ++#include "qcom-ipq8064.dtsi" +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +new file mode 100644 +index 0000000..952afb7 +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -0,0 +1,176 @@ ++/dts-v1/; ++ ++#include "skeleton.dtsi" ++#include <dt-bindings/clock/qcom,gcc-ipq806x.h> ++#include <dt-bindings/soc/qcom,gsbi.h> ++ ++/ { ++ model = "Qualcomm IPQ8064"; ++ compatible = "qcom,ipq8064"; ++ interrupt-parent = <&intc>; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu@0 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; ++ device_type = "cpu"; ++ reg = <0>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc0>; ++ qcom,saw = <&saw0>; ++ }; ++ ++ cpu@1 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; ++ device_type = "cpu"; ++ reg = <1>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc1>; ++ qcom,saw = <&saw1>; ++ }; ++ ++ L2: l2-cache { ++ compatible = "cache"; ++ cache-level = <2>; ++ }; ++ }; ++ ++ cpu-pmu { ++ compatible = "qcom,krait-pmu"; ++ interrupts = <1 10 0x304>; ++ }; ++ ++ reserved-memory { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ nss@40000000 { ++ reg = <0x40000000 0x1000000>; ++ no-map; ++ }; ++ ++ smem@41000000 { ++ reg = <0x41000000 0x200000>; ++ no-map; ++ }; ++ }; ++ ++ soc: soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ compatible = "simple-bus"; ++ ++ qcom_pinmux: pinmux@800000 { ++ compatible = "qcom,ipq8064-pinctrl"; ++ reg = <0x800000 0x4000>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ interrupts = <0 32 0x4>; ++ }; ++ ++ intc: interrupt-controller@2000000 { ++ compatible = "qcom,msm-qgic2"; ++ interrupt-controller; ++ #interrupt-cells = <3>; ++ reg = <0x02000000 0x1000>, ++ <0x02002000 0x1000>; ++ }; ++ ++ timer@200a000 { ++ compatible = "qcom,kpss-timer", "qcom,msm-timer"; ++ interrupts = <1 1 0x301>, ++ <1 2 0x301>, ++ <1 3 0x301>; ++ reg = <0x0200a000 0x100>; ++ clock-frequency = <25000000>, ++ <32768>; ++ cpu-offset = <0x80000>; ++ }; ++ ++ acc0: clock-controller@2088000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x02088000 0x1000>, <0x02008000 0x1000>; ++ }; ++ ++ acc1: clock-controller@2098000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x02098000 0x1000>, <0x02008000 0x1000>; ++ }; ++ ++ saw0: regulator@2089000 { ++ compatible = "qcom,saw2"; ++ reg = <0x02089000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; ++ ++ saw1: regulator@2099000 { ++ compatible = "qcom,saw2"; ++ reg = <0x02099000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; ++ ++ gsbi2: gsbi@12480000 { ++ compatible = "qcom,gsbi-v1.0.0"; ++ reg = <0x12480000 0x100>; ++ clocks = <&gcc GSBI2_H_CLK>; ++ clock-names = "iface"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ status = "disabled"; ++ ++ serial@12490000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x12490000 0x1000>, ++ <0x12480000 0x1000>; ++ interrupts = <0 195 0x0>; ++ clocks = <&gcc GSBI2_UART_CLK>, <&gcc GSBI2_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ }; ++ }; ++ ++ gsbi4: gsbi@16300000 { ++ compatible = "qcom,gsbi-v1.0.0"; ++ reg = <0x16300000 0x100>; ++ clocks = <&gcc GSBI4_H_CLK>; ++ clock-names = "iface"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ status = "disabled"; ++ ++ serial@16340000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x16340000 0x1000>, ++ <0x16300000 0x1000>; ++ interrupts = <0 152 0x0>; ++ clocks = <&gcc GSBI4_UART_CLK>, <&gcc GSBI4_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ }; ++ }; ++ ++ qcom,ssbi@500000 { ++ compatible = "qcom,ssbi"; ++ reg = <0x00500000 0x1000>; ++ qcom,controller-type = "pmic-arbiter"; ++ }; ++ ++ gcc: clock-controller@900000 { ++ compatible = "qcom,gcc-ipq8064"; ++ reg = <0x00900000 0x4000>; ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ }; ++ }; ++}; +diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c +index c437a99..6d8bbf7 100644 +--- a/arch/arm/mach-qcom/board.c ++++ b/arch/arm/mach-qcom/board.c +@@ -18,6 +18,8 @@ static const char * const qcom_dt_match[] __initconst = { + "qcom,apq8064", + "qcom,apq8074-dragonboard", + "qcom,apq8084", ++ "qcom,ipq8062", ++ "qcom,ipq8064", + "qcom,msm8660-surf", + "qcom,msm8960-cdp", + NULL +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0131-ARM-qcom-config-Enable-IPQ806x-support.patch b/target/linux/ipq806x/patches/0131-ARM-qcom-config-Enable-IPQ806x-support.patch new file mode 100644 index 0000000000..8b20c4b68d --- /dev/null +++ b/target/linux/ipq806x/patches/0131-ARM-qcom-config-Enable-IPQ806x-support.patch @@ -0,0 +1,25 @@ +From df1564829c4abe206d354f7c1b745cc1e864f4bd Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Fri, 11 Apr 2014 14:18:29 -0500 +Subject: [PATCH 131/182] ARM: qcom: config: Enable IPQ806x support + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/configs/qcom_defconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig +index 42ebd72..1752a4d 100644 +--- a/arch/arm/configs/qcom_defconfig ++++ b/arch/arm/configs/qcom_defconfig +@@ -133,6 +133,7 @@ CONFIG_QCOM_BAM_DMA=y + CONFIG_STAGING=y + CONFIG_QCOM_GSBI=y + CONFIG_COMMON_CLK_QCOM=y ++CONFIG_IPQ_GCC_806X=y + CONFIG_MSM_GCC_8660=y + CONFIG_MSM_MMCC_8960=y + CONFIG_MSM_MMCC_8974=y +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0132-XXX-Add-boot-support-for-u-boot.ipq-image.patch b/target/linux/ipq806x/patches/0132-XXX-Add-boot-support-for-u-boot.ipq-image.patch new file mode 100644 index 0000000000..c94c75abe2 --- /dev/null +++ b/target/linux/ipq806x/patches/0132-XXX-Add-boot-support-for-u-boot.ipq-image.patch @@ -0,0 +1,55 @@ +From 776fff11c1bf57e564a386521e56c277287a568c Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 8 May 2014 13:40:16 -0500 +Subject: [PATCH 132/182] XXX: Add boot support for u-boot.ipq image + +--- + arch/arm/Makefile | 2 +- + arch/arm/boot/Makefile | 11 ++++++++++- + 2 files changed, 11 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/Makefile b/arch/arm/Makefile +index 51e5bed..e9fc731 100644 +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -297,7 +297,7 @@ archprepare: + # Convert bzImage to zImage + bzImage: zImage + +-BOOT_TARGETS = zImage Image xipImage bootpImage uImage ++BOOT_TARGETS = zImage Image xipImage bootpImage uImage uImage.ipq Image.gz + INSTALL_TARGETS = zinstall uinstall install + + PHONY += bzImage $(BOOT_TARGETS) $(INSTALL_TARGETS) +diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile +index ec2f806..3cbc83e 100644 +--- a/arch/arm/boot/Makefile ++++ b/arch/arm/boot/Makefile +@@ -25,7 +25,7 @@ INITRD_PHYS := $(initrd_phys-y) + + export ZRELADDR INITRD_PHYS PARAMS_PHYS + +-targets := Image zImage xipImage bootpImage uImage ++targets := Image zImage xipImage bootpImage uImage uImage.ipq Image.gz + + ifeq ($(CONFIG_XIP_KERNEL),y) + +@@ -80,6 +80,15 @@ $(obj)/uImage: $(obj)/zImage FORCE + $(call if_changed,uimage) + @$(kecho) ' Image $@ is ready' + ++$(obj)/uImage.ipq: $(obj)/Image.gz FORCE ++ @$(check_for_multiple_loadaddr) ++ $(call if_changed,uimage,gzip) ++ @$(kecho) ' Image $@ is ready' ++ ++$(obj)/Image.gz: $(obj)/Image FORCE ++ $(call if_changed,gzip) ++ @$(kecho) ' Image $@ is ready' ++ + $(obj)/bootp/bootp: $(obj)/zImage initrd FORCE + $(Q)$(MAKE) $(build)=$(obj)/bootp $@ + @: +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0133-spi-qup-Remove-chip-select-function.patch b/target/linux/ipq806x/patches/0133-spi-qup-Remove-chip-select-function.patch new file mode 100644 index 0000000000..3881064b6b --- /dev/null +++ b/target/linux/ipq806x/patches/0133-spi-qup-Remove-chip-select-function.patch @@ -0,0 +1,90 @@ +From 9bc674f40f22596ef8c2ff6d7f9e53da0baa57e9 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 12 Jun 2014 14:34:10 -0500 +Subject: [PATCH 133/182] spi: qup: Remove chip select function + +This patch removes the chip select function. Chip select should instead be +supported using GPIOs, defining the DT entry "cs-gpios", and letting the SPI +core assert/deassert the chip select as it sees fit. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + .../devicetree/bindings/spi/qcom,spi-qup.txt | 6 ++++ + drivers/spi/spi-qup.c | 33 ++++---------------- + 2 files changed, 12 insertions(+), 27 deletions(-) + +diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt +index b82a268..bee6ff2 100644 +--- a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt ++++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt +@@ -23,6 +23,12 @@ Optional properties: + - spi-max-frequency: Specifies maximum SPI clock frequency, + Units - Hz. Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt ++- 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 ++ nodes. If unspecified, a single SPI device without a chip ++ select can be used. ++ + + SPI slave nodes must be children of the SPI master node and can contain + properties described in Documentation/devicetree/bindings/spi/spi-bus.txt +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index 65bf18e..dc128ac 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -424,31 +424,6 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + return 0; + } + +-static void spi_qup_set_cs(struct spi_device *spi, bool enable) +-{ +- struct spi_qup *controller = spi_master_get_devdata(spi->master); +- +- u32 iocontol, mask; +- +- iocontol = readl_relaxed(controller->base + SPI_IO_CONTROL); +- +- /* Disable auto CS toggle and use manual */ +- iocontol &= ~SPI_IO_C_MX_CS_MODE; +- iocontol |= SPI_IO_C_FORCE_CS; +- +- iocontol &= ~SPI_IO_C_CS_SELECT_MASK; +- iocontol |= SPI_IO_C_CS_SELECT(spi->chip_select); +- +- mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select; +- +- if (enable) +- iocontol |= mask; +- else +- iocontol &= ~mask; +- +- writel_relaxed(iocontol, controller->base + SPI_IO_CONTROL); +-} +- + static int spi_qup_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +@@ -571,12 +546,16 @@ static int spi_qup_probe(struct platform_device *pdev) + return -ENOMEM; + } + ++ /* use num-cs unless not present or out of range */ ++ if (of_property_read_u16(dev->of_node, "num-cs", ++ &master->num_chipselect) || ++ (master->num_chipselect > SPI_NUM_CHIPSELECTS)) ++ master->num_chipselect = SPI_NUM_CHIPSELECTS; ++ + master->bus_num = pdev->id; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; +- master->num_chipselect = SPI_NUM_CHIPSELECTS; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + master->max_speed_hz = max_freq; +- master->set_cs = spi_qup_set_cs; + master->transfer_one = spi_qup_transfer_one; + master->dev.of_node = pdev->dev.of_node; + master->auto_runtime_pm = true; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0134-spi-qup-Fix-order-of-spi_register_master.patch b/target/linux/ipq806x/patches/0134-spi-qup-Fix-order-of-spi_register_master.patch new file mode 100644 index 0000000000..c8738ccac8 --- /dev/null +++ b/target/linux/ipq806x/patches/0134-spi-qup-Fix-order-of-spi_register_master.patch @@ -0,0 +1,46 @@ +From a7357be131e30fd1fb987b2cb343bc81db487f96 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 12 Jun 2014 14:34:11 -0500 +Subject: [PATCH 134/182] spi: qup: Fix order of spi_register_master + +This patch moves the devm_spi_register_master below the initialization of the +runtime_pm. If done in the wrong order, the spi_register_master fails if any +probed slave devices issue SPI transactions. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Acked-by: Ivan T. Ivanov <iivanov@mm-sol.com> +--- + drivers/spi/spi-qup.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index dc128ac..a404298 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -619,16 +619,19 @@ static int spi_qup_probe(struct platform_device *pdev) + if (ret) + goto error; + +- ret = devm_spi_register_master(dev, master); +- if (ret) +- goto error; +- + pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); ++ ++ ret = devm_spi_register_master(dev, master); ++ if (ret) ++ goto disable_pm; ++ + return 0; + ++disable_pm: ++ pm_runtime_disable(&pdev->dev); + error: + clk_disable_unprepare(cclk); + clk_disable_unprepare(iclk); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0135-spi-qup-Add-support-for-v1.1.1.patch b/target/linux/ipq806x/patches/0135-spi-qup-Add-support-for-v1.1.1.patch new file mode 100644 index 0000000000..c1ccc21197 --- /dev/null +++ b/target/linux/ipq806x/patches/0135-spi-qup-Add-support-for-v1.1.1.patch @@ -0,0 +1,132 @@ +From 8b9de04ef3aaa154f30baf1ac703a2d3b474ad4e Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 12 Jun 2014 14:34:12 -0500 +Subject: [PATCH 135/182] spi: qup: Add support for v1.1.1 + +This patch adds support for v1.1.1 of the SPI QUP controller. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + .../devicetree/bindings/spi/qcom,spi-qup.txt | 6 +++- + drivers/spi/spi-qup.c | 36 ++++++++++++-------- + 2 files changed, 27 insertions(+), 15 deletions(-) + +diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt +index bee6ff2..e2c88df 100644 +--- a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt ++++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt +@@ -7,7 +7,11 @@ SPI in master mode supports up to 50MHz, up to four chip selects, programmable + data path from 4 bits to 32 bits and numerous protocol variants. + + Required properties: +-- compatible: Should contain "qcom,spi-qup-v2.1.1" or "qcom,spi-qup-v2.2.1" ++- compatible: Should contain: ++ "qcom,spi-qup-v1.1.1" for 8660, 8960 and 8064. ++ "qcom,spi-qup-v2.1.1" for 8974 and later ++ "qcom,spi-qup-v2.2.1" for 8974 v2 and later. ++ + - reg: Should contain base register location and length + - interrupts: Interrupt number used by this controller + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index a404298..c137226 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -142,6 +142,7 @@ struct spi_qup { + int w_size; /* bytes per SPI word */ + int tx_bytes; + int rx_bytes; ++ int qup_v1; + }; + + +@@ -420,7 +421,9 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + config |= QUP_CONFIG_SPI_MODE; + writel_relaxed(config, controller->base + QUP_CONFIG); + +- writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK); ++ /* only write to OPERATIONAL_MASK when register is present */ ++ if (!controller->qup_v1) ++ writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK); + return 0; + } + +@@ -486,7 +489,7 @@ static int spi_qup_probe(struct platform_device *pdev) + struct resource *res; + struct device *dev; + void __iomem *base; +- u32 data, max_freq, iomode; ++ u32 max_freq, iomode; + int ret, irq, size; + + dev = &pdev->dev; +@@ -529,15 +532,6 @@ static int spi_qup_probe(struct platform_device *pdev) + return ret; + } + +- data = readl_relaxed(base + QUP_HW_VERSION); +- +- if (data < QUP_HW_VERSION_2_1_1) { +- clk_disable_unprepare(cclk); +- clk_disable_unprepare(iclk); +- dev_err(dev, "v.%08x is not supported\n", data); +- return -ENXIO; +- } +- + master = spi_alloc_master(dev, sizeof(struct spi_qup)); + if (!master) { + clk_disable_unprepare(cclk); +@@ -570,6 +564,10 @@ static int spi_qup_probe(struct platform_device *pdev) + controller->cclk = cclk; + controller->irq = irq; + ++ /* set v1 flag if device is version 1 */ ++ if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1")) ++ controller->qup_v1 = 1; ++ + spin_lock_init(&controller->lock); + init_completion(&controller->done); + +@@ -593,8 +591,8 @@ static int spi_qup_probe(struct platform_device *pdev) + size = QUP_IO_M_INPUT_FIFO_SIZE(iomode); + controller->in_fifo_sz = controller->in_blk_sz * (2 << size); + +- dev_info(dev, "v.%08x IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n", +- data, controller->in_blk_sz, controller->in_fifo_sz, ++ dev_info(dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n", ++ controller->in_blk_sz, controller->in_fifo_sz, + controller->out_blk_sz, controller->out_fifo_sz); + + writel_relaxed(1, base + QUP_SW_RESET); +@@ -607,10 +605,19 @@ static int spi_qup_probe(struct platform_device *pdev) + + writel_relaxed(0, base + QUP_OPERATIONAL); + writel_relaxed(0, base + QUP_IO_M_MODES); +- writel_relaxed(0, base + QUP_OPERATIONAL_MASK); ++ ++ if (!controller->qup_v1) ++ writel_relaxed(0, base + QUP_OPERATIONAL_MASK); ++ + writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN, + base + SPI_ERROR_FLAGS_EN); + ++ /* if earlier version of the QUP, disable INPUT_OVERRUN */ ++ if (controller->qup_v1) ++ writel_relaxed(QUP_ERROR_OUTPUT_OVER_RUN | ++ QUP_ERROR_INPUT_UNDER_RUN | QUP_ERROR_OUTPUT_UNDER_RUN, ++ base + QUP_ERROR_FLAGS_EN); ++ + writel_relaxed(0, base + SPI_CONFIG); + writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL); + +@@ -732,6 +739,7 @@ static int spi_qup_remove(struct platform_device *pdev) + } + + static struct of_device_id spi_qup_dt_match[] = { ++ { .compatible = "qcom,spi-qup-v1.1.1", }, + { .compatible = "qcom,spi-qup-v2.1.1", }, + { .compatible = "qcom,spi-qup-v2.2.1", }, + { } +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0136-ARM-ipq8064-ap148-Add-i2c-pinctrl-nodes.patch b/target/linux/ipq806x/patches/0136-ARM-ipq8064-ap148-Add-i2c-pinctrl-nodes.patch new file mode 100644 index 0000000000..7c4757da99 --- /dev/null +++ b/target/linux/ipq806x/patches/0136-ARM-ipq8064-ap148-Add-i2c-pinctrl-nodes.patch @@ -0,0 +1,93 @@ +From e93b9480667cbd0e3a4276e8749279693fe239f4 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Wed, 14 May 2014 22:49:03 -0500 +Subject: [PATCH 136/182] ARM: ipq8064-ap148: Add i2c pinctrl nodes + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 17 +++++++++++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 27 +++++++++++++++++++++++++++ + 2 files changed, 44 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +index 100b6eb..dbb546d 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -14,12 +14,29 @@ + }; + + soc { ++ pinmux@800000 { ++ i2c4_pins: i2c4_pinmux { ++ pins = "gpio12", "gpio13"; ++ function = "gsbi4"; ++ bias-disable; ++ }; ++ }; ++ + gsbi@16300000 { + qcom,mode = <GSBI_PROT_I2C_UART>; + status = "ok"; + serial@16340000 { + status = "ok"; + }; ++ ++ i2c4: i2c@16380000 { ++ status = "ok"; ++ ++ clock-frequency = <200000>; ++ ++ pinctrl-0 = <&i2c4_pins>; ++ pinctrl-names = "default"; ++ }; + }; + }; + }; +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +index 952afb7..b39c1ef 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -137,6 +137,20 @@ + clock-names = "core", "iface"; + status = "disabled"; + }; ++ ++ i2c@124a0000 { ++ compatible = "qcom,i2c-qup-v1.1.1"; ++ reg = <0x124a0000 0x1000>; ++ interrupts = <0 196 0>; ++ ++ clocks = <&gcc GSBI2_QUP_CLK>, <&gcc GSBI2_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ + }; + + gsbi4: gsbi@16300000 { +@@ -158,6 +172,19 @@ + clock-names = "core", "iface"; + status = "disabled"; + }; ++ ++ i2c@16380000 { ++ compatible = "qcom,i2c-qup-v1.1.1"; ++ reg = <0x16380000 0x1000>; ++ interrupts = <0 153 0>; ++ ++ clocks = <&gcc GSBI4_QUP_CLK>, <&gcc GSBI4_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; + }; + + qcom,ssbi@500000 { +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0137-ARM-qcom-ipq8064-ap148-Add-SPI-related-bindings.patch b/target/linux/ipq806x/patches/0137-ARM-qcom-ipq8064-ap148-Add-SPI-related-bindings.patch new file mode 100644 index 0000000000..4a0385db27 --- /dev/null +++ b/target/linux/ipq806x/patches/0137-ARM-qcom-ipq8064-ap148-Add-SPI-related-bindings.patch @@ -0,0 +1,131 @@ +From b9eaa80146abb09bcc7e6d8b33fca476453c839c Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Wed, 14 May 2014 22:01:16 -0500 +Subject: [PATCH 137/182] ARM: qcom-ipq8064-ap148: Add SPI related bindings + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 42 ++++++++++++++++++++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 47 ++++++++++++++++++++++++++++++ + 2 files changed, 89 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +index dbb546d..158a09f 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -20,6 +20,15 @@ + function = "gsbi4"; + bias-disable; + }; ++ ++ spi_pins: spi_pins { ++ mux { ++ pins = "gpio18", "gpio19", "gpio21"; ++ function = "gsbi5"; ++ drive-strength = <10>; ++ bias-none; ++ }; ++ }; + }; + + gsbi@16300000 { +@@ -38,5 +47,38 @@ + pinctrl-names = "default"; + }; + }; ++ ++ 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>; ++ ++ partition@0 { ++ label = "rootfs"; ++ reg = <0x0 0x1000000>; ++ }; ++ ++ partition@1 { ++ label = "scratch"; ++ reg = <0x1000000 0x1000000>; ++ }; ++ }; ++ }; ++ }; + }; + }; +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +index b39c1ef..244f857 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -187,6 +187,53 @@ + }; + }; + ++ gsbi5: gsbi@1a200000 { ++ compatible = "qcom,gsbi-v1.0.0"; ++ reg = <0x1a200000 0x100>; ++ clocks = <&gcc GSBI5_H_CLK>; ++ clock-names = "iface"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ status = "disabled"; ++ ++ serial@1a240000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x1a240000 0x1000>, ++ <0x1a200000 0x1000>; ++ interrupts = <0 154 0x0>; ++ clocks = <&gcc GSBI5_UART_CLK>, <&gcc GSBI5_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ }; ++ ++ i2c@1a280000 { ++ compatible = "qcom,i2c-qup-v1.1.1"; ++ reg = <0x1a280000 0x1000>; ++ interrupts = <0 155 0>; ++ ++ clocks = <&gcc GSBI5_QUP_CLK>, <&gcc GSBI5_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ spi@1a280000 { ++ compatible = "qcom,spi-qup-v1.1.1"; ++ reg = <0x1a280000 0x1000>; ++ interrupts = <0 155 0>; ++ ++ clocks = <&gcc GSBI5_QUP_CLK>, <&gcc GSBI5_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ }; ++ + qcom,ssbi@500000 { + compatible = "qcom,ssbi"; + reg = <0x00500000 0x1000>; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0138-PCI-qcom-Add-support-for-pcie-controllers-on-IPQ8064.patch b/target/linux/ipq806x/patches/0138-PCI-qcom-Add-support-for-pcie-controllers-on-IPQ8064.patch new file mode 100644 index 0000000000..b1ff04b90d --- /dev/null +++ b/target/linux/ipq806x/patches/0138-PCI-qcom-Add-support-for-pcie-controllers-on-IPQ8064.patch @@ -0,0 +1,726 @@ +From 98567c99b4dcd80fc9e5dd97229ebb9a7f6dab03 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Fri, 16 May 2014 11:53:23 -0500 +Subject: [PATCH 138/182] PCI: qcom: Add support for pcie controllers on + IPQ8064 + +--- + arch/arm/mach-qcom/Kconfig | 2 + + drivers/pci/host/Makefile | 1 + + drivers/pci/host/pci-qcom.c | 682 +++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 685 insertions(+) + create mode 100644 drivers/pci/host/pci-qcom.c + +diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig +index 63502cc..4d242e5 100644 +--- a/arch/arm/mach-qcom/Kconfig ++++ b/arch/arm/mach-qcom/Kconfig +@@ -7,6 +7,8 @@ config ARCH_QCOM + select GENERIC_CLOCKEVENTS + select HAVE_SMP + select PINCTRL ++ select MIGHT_HAVE_PCI ++ select PCI_DOMAINS if PCI + select QCOM_SCM if SMP + help + Support for Qualcomm's devicetree based systems. +diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile +index 13fb333..be80744 100644 +--- a/drivers/pci/host/Makefile ++++ b/drivers/pci/host/Makefile +@@ -4,3 +4,4 @@ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o + obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o + obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o + obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o ++obj-$(CONFIG_ARCH_QCOM) += pci-qcom.o +diff --git a/drivers/pci/host/pci-qcom.c b/drivers/pci/host/pci-qcom.c +new file mode 100644 +index 0000000..76d7b88 +--- /dev/null ++++ b/drivers/pci/host/pci-qcom.c +@@ -0,0 +1,682 @@ ++/* Copyright (c) 2012-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. ++ */ ++ ++/* ++ * QCOM MSM PCIe controller driver. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/pci.h> ++#include <linux/gpio.h> ++#include <linux/of_gpio.h> ++#include <linux/platform_device.h> ++#include <linux/of_address.h> ++#include <linux/clk.h> ++#include <linux/reset.h> ++#include <linux/delay.h> ++ ++/* Root Complex Port vendor/device IDs */ ++#define PCIE_VENDOR_ID_RCP 0x17cb ++#define PCIE_DEVICE_ID_RCP 0x0101 ++ ++#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b)) ++ ++#define PCIE20_PARF_PCS_DEEMPH 0x34 ++#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN1(x) __set(x, 21, 16) ++#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x) __set(x, 13, 8) ++#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x) __set(x, 5, 0) ++ ++#define PCIE20_PARF_PCS_SWING 0x38 ++#define PCIE20_PARF_PCS_SWING_TX_SWING_FULL(x) __set(x, 14, 8) ++#define PCIE20_PARF_PCS_SWING_TX_SWING_LOW(x) __set(x, 6, 0) ++ ++#define PCIE20_PARF_PHY_CTRL 0x40 ++#define PCIE20_PARF_PHY_CTRL_PHY_TX0_TERM_OFFST(x) __set(x, 20, 16) ++#define PCIE20_PARF_PHY_CTRL_PHY_LOS_LEVEL(x) __set(x, 12, 8) ++#define PCIE20_PARF_PHY_CTRL_PHY_RTUNE_REQ (1 << 4) ++#define PCIE20_PARF_PHY_CTRL_PHY_TEST_BURNIN (1 << 2) ++#define PCIE20_PARF_PHY_CTRL_PHY_TEST_BYPASS (1 << 1) ++#define PCIE20_PARF_PHY_CTRL_PHY_TEST_PWR_DOWN (1 << 0) ++ ++#define PCIE20_PARF_PHY_REFCLK 0x4C ++#define PCIE20_PARF_CONFIG_BITS 0x50 ++ ++#define PCIE20_ELBI_SYS_CTRL 0x04 ++#define PCIE20_ELBI_SYS_CTRL_LTSSM_EN 0x01 ++ ++#define PCIE20_CAP 0x70 ++#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) ++ ++#define PCIE20_COMMAND_STATUS 0x04 ++#define PCIE20_BUSNUMBERS 0x18 ++#define PCIE20_MEMORY_BASE_LIMIT 0x20 ++ ++#define PCIE20_AXI_MSTR_RESP_COMP_CTRL0 0x818 ++#define PCIE20_AXI_MSTR_RESP_COMP_CTRL1 0x81c ++#define PCIE20_PLR_IATU_VIEWPORT 0x900 ++#define PCIE20_PLR_IATU_CTRL1 0x904 ++#define PCIE20_PLR_IATU_CTRL2 0x908 ++#define PCIE20_PLR_IATU_LBAR 0x90C ++#define PCIE20_PLR_IATU_UBAR 0x910 ++#define PCIE20_PLR_IATU_LAR 0x914 ++#define PCIE20_PLR_IATU_LTAR 0x918 ++#define PCIE20_PLR_IATU_UTAR 0x91c ++ ++#define MSM_PCIE_DEV_CFG_ADDR 0x01000000 ++ ++#define RD 0 ++#define WR 1 ++ ++#define MAX_RC_NUM 3 ++#define PCIE_BUS_PRIV_DATA(pdev) \ ++ (((struct pci_sys_data *)pdev->bus->sysdata)->private_data) ++ ++/* PCIe TLP types that we are interested in */ ++#define PCI_CFG0_RDWR 0x4 ++#define PCI_CFG1_RDWR 0x5 ++ ++#define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \ ++({ \ ++ unsigned long timeout = jiffies + usecs_to_jiffies(timeout_us); \ ++ might_sleep_if(timeout_us); \ ++ for (;;) { \ ++ (val) = readl(addr); \ ++ if (cond) \ ++ break; \ ++ if (timeout_us && time_after(jiffies, timeout)) { \ ++ (val) = readl(addr); \ ++ break; \ ++ } \ ++ if (sleep_us) \ ++ usleep_range(DIV_ROUND_UP(sleep_us, 4), sleep_us); \ ++ } \ ++ (cond) ? 0 : -ETIMEDOUT; \ ++}) ++ ++struct qcom_pcie { ++ void __iomem *elbi_base; ++ void __iomem *parf_base; ++ void __iomem *dwc_base; ++ void __iomem *cfg_base; ++ int reset_gpio; ++ struct clk *iface_clk; ++ struct clk *bus_clk; ++ struct clk *phy_clk; ++ int irq_int[4]; ++ struct reset_control *axi_reset; ++ struct reset_control *ahb_reset; ++ struct reset_control *por_reset; ++ struct reset_control *pci_reset; ++ struct reset_control *phy_reset; ++ ++ struct resource conf; ++ struct resource io; ++ struct resource mem; ++}; ++ ++static int qcom_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin); ++static int qcom_pcie_setup(int nr, struct pci_sys_data *sys); ++static int msm_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, ++ int size, u32 *val); ++static int msm_pcie_wr_conf(struct pci_bus *bus, u32 devfn, ++ int where, int size, u32 val); ++ ++static struct pci_ops qcom_pcie_ops = { ++ .read = msm_pcie_rd_conf, ++ .write = msm_pcie_wr_conf, ++}; ++ ++static struct hw_pci qcom_hw_pci[MAX_RC_NUM] = { ++ { ++#ifdef CONFIG_PCI_DOMAINS ++ .domain = 0, ++#endif ++ .ops = &qcom_pcie_ops, ++ .nr_controllers = 1, ++ .swizzle = pci_common_swizzle, ++ .setup = qcom_pcie_setup, ++ .map_irq = qcom_pcie_map_irq, ++ }, ++ { ++#ifdef CONFIG_PCI_DOMAINS ++ .domain = 1, ++#endif ++ .ops = &qcom_pcie_ops, ++ .nr_controllers = 1, ++ .swizzle = pci_common_swizzle, ++ .setup = qcom_pcie_setup, ++ .map_irq = qcom_pcie_map_irq, ++ }, ++ { ++#ifdef CONFIG_PCI_DOMAINS ++ .domain = 2, ++#endif ++ .ops = &qcom_pcie_ops, ++ .nr_controllers = 1, ++ .swizzle = pci_common_swizzle, ++ .setup = qcom_pcie_setup, ++ .map_irq = qcom_pcie_map_irq, ++ }, ++}; ++ ++static int nr_controllers; ++static DEFINE_SPINLOCK(qcom_hw_pci_lock); ++ ++static inline struct qcom_pcie *sys_to_pcie(struct pci_sys_data *sys) ++{ ++ return sys->private_data; ++} ++ ++inline int is_msm_pcie_rc(struct pci_bus *bus) ++{ ++ return (bus->number == 0); ++} ++ ++static int qcom_pcie_is_link_up(struct qcom_pcie *dev) ++{ ++ return readl_relaxed(dev->dwc_base + PCIE20_CAP_LINKCTRLSTATUS) & BIT(29); ++} ++ ++inline int msm_pcie_get_cfgtype(struct pci_bus *bus) ++{ ++ /* ++ * http://www.tldp.org/LDP/tlk/dd/pci.html ++ * Pass it onto the secondary bus interface unchanged if the ++ * bus number specified is greater than the secondary bus ++ * number and less than or equal to the subordinate bus ++ * number. ++ * ++ * Read/Write to the RC and Device/Switch connected to the RC ++ * are CFG0 type transactions. Rest have to be forwarded ++ * down stream as CFG1 transactions. ++ * ++ */ ++ if (bus->number == 0) ++ return PCI_CFG0_RDWR; ++ ++ return PCI_CFG0_RDWR; ++} ++ ++void msm_pcie_config_cfgtype(struct pci_bus *bus, u32 devfn) ++{ ++ uint32_t bdf, cfgtype; ++ struct qcom_pcie *dev = sys_to_pcie(bus->sysdata); ++ ++ cfgtype = msm_pcie_get_cfgtype(bus); ++ ++ if (cfgtype == PCI_CFG0_RDWR) { ++ bdf = MSM_PCIE_DEV_CFG_ADDR; ++ } else { ++ /* ++ * iATU Lower Target Address Register ++ * Bits Description ++ * *-1:0 Forms bits [*:0] of the ++ * start address of the new ++ * address of the translated ++ * region. The start address ++ * must be aligned to a ++ * CX_ATU_MIN_REGION_SIZE kB ++ * boundary, so these bits are ++ * always 0. A write to this ++ * location is ignored by the ++ * PCIe core. ++ * 31:*1 Forms bits [31:*] of the of ++ * the new address of the ++ * translated region. ++ * ++ * * is log2(CX_ATU_MIN_REGION_SIZE) ++ */ ++ bdf = (((bus->number & 0xff) << 24) & 0xff000000) | ++ (((devfn & 0xff) << 16) & 0x00ff0000); ++ } ++ ++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_VIEWPORT); ++ wmb(); ++ ++ /* Program Bdf Address */ ++ writel_relaxed(bdf, dev->dwc_base + PCIE20_PLR_IATU_LTAR); ++ wmb(); ++ ++ /* Write Config Request Type */ ++ writel_relaxed(cfgtype, dev->dwc_base + PCIE20_PLR_IATU_CTRL1); ++ wmb(); ++} ++ ++static inline int msm_pcie_oper_conf(struct pci_bus *bus, u32 devfn, int oper, ++ int where, int size, u32 *val) ++{ ++ uint32_t word_offset, byte_offset, mask; ++ uint32_t rd_val, wr_val; ++ struct qcom_pcie *dev = sys_to_pcie(bus->sysdata); ++ void __iomem *config_base; ++ int rc; ++ ++ rc = is_msm_pcie_rc(bus); ++ ++ /* ++ * For downstream bus, make sure link is up ++ */ ++ if (rc && (devfn != 0)) { ++ *val = ~0; ++ return PCIBIOS_DEVICE_NOT_FOUND; ++ } else if ((!rc) && (!qcom_pcie_is_link_up(dev))) { ++ *val = ~0; ++ return PCIBIOS_DEVICE_NOT_FOUND; ++ } ++ ++ msm_pcie_config_cfgtype(bus, devfn); ++ ++ word_offset = where & ~0x3; ++ byte_offset = where & 0x3; ++ mask = (~0 >> (8 * (4 - size))) << (8 * byte_offset); ++ ++ config_base = (rc) ? dev->dwc_base : dev->cfg_base; ++ rd_val = readl_relaxed(config_base + word_offset); ++ ++ if (oper == RD) { ++ *val = ((rd_val & mask) >> (8 * byte_offset)); ++ } else { ++ wr_val = (rd_val & ~mask) | ++ ((*val << (8 * byte_offset)) & mask); ++ writel_relaxed(wr_val, config_base + word_offset); ++ wmb(); /* ensure config data is written to hardware register */ ++ } ++ ++ return 0; ++} ++ ++static int msm_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, ++ int size, u32 *val) ++{ ++ return msm_pcie_oper_conf(bus, devfn, RD, where, size, val); ++} ++ ++static int msm_pcie_wr_conf(struct pci_bus *bus, u32 devfn, ++ int where, int size, u32 val) ++{ ++ /* ++ *Attempt to reset secondary bus is causing PCIE core to reset. ++ *Disable secondary bus reset functionality. ++ */ ++ if ((bus->number == 0) && (where == PCI_BRIDGE_CONTROL) && ++ (val & PCI_BRIDGE_CTL_BUS_RESET)) { ++ pr_info("PCIE secondary bus reset not supported\n"); ++ val &= ~PCI_BRIDGE_CTL_BUS_RESET; ++ } ++ ++ return msm_pcie_oper_conf(bus, devfn, WR, where, size, &val); ++} ++ ++static int qcom_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) ++{ ++ struct qcom_pcie *pcie_dev = PCIE_BUS_PRIV_DATA(dev); ++ ++ return pcie_dev->irq_int[pin-1]; ++} ++ ++static int qcom_pcie_setup(int nr, struct pci_sys_data *sys) ++{ ++ struct qcom_pcie *qcom_pcie = sys->private_data; ++ ++ /* ++ * specify linux PCI framework to allocate device memory (BARs) ++ * from msm_pcie_dev.dev_mem_res resource. ++ */ ++ sys->mem_offset = 0; ++ sys->io_offset = 0; ++ ++ pci_add_resource(&sys->resources, &qcom_pcie->mem); ++ pci_add_resource(&sys->resources, &qcom_pcie->io); ++ ++ return 1; ++} ++ ++static inline void qcom_elbi_writel_relaxed(struct qcom_pcie *pcie, u32 val, u32 reg) ++{ ++ writel_relaxed(val, pcie->elbi_base + reg); ++} ++ ++static inline u32 qcom_elbi_readl_relaxed(struct qcom_pcie *pcie, u32 reg) ++{ ++ return readl_relaxed(pcie->elbi_base + reg); ++} ++ ++static inline void qcom_parf_writel_relaxed(struct qcom_pcie *pcie, u32 val, u32 reg) ++{ ++ writel_relaxed(val, pcie->parf_base + reg); ++} ++ ++static inline u32 qcom_parf_readl_relaxed(struct qcom_pcie *pcie, u32 reg) ++{ ++ return readl_relaxed(pcie->parf_base + reg); ++} ++ ++static void msm_pcie_write_mask(void __iomem *addr, ++ uint32_t clear_mask, uint32_t set_mask) ++{ ++ uint32_t val; ++ ++ val = (readl_relaxed(addr) & ~clear_mask) | set_mask; ++ writel_relaxed(val, addr); ++ wmb(); /* ensure data is written to hardware register */ ++} ++ ++static void qcom_pcie_config_controller(struct qcom_pcie *dev) ++{ ++ /* ++ * program and enable address translation region 0 (device config ++ * address space); region type config; ++ * axi config address range to device config address range ++ */ ++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_VIEWPORT); ++ /* ensure that hardware locks the region before programming it */ ++ wmb(); ++ ++ writel_relaxed(4, dev->dwc_base + PCIE20_PLR_IATU_CTRL1); ++ writel_relaxed(BIT(31), dev->dwc_base + PCIE20_PLR_IATU_CTRL2); ++ writel_relaxed(dev->conf.start, dev->dwc_base + PCIE20_PLR_IATU_LBAR); ++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UBAR); ++ writel_relaxed(dev->conf.end, dev->dwc_base + PCIE20_PLR_IATU_LAR); ++ writel_relaxed(MSM_PCIE_DEV_CFG_ADDR, ++ dev->dwc_base + PCIE20_PLR_IATU_LTAR); ++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UTAR); ++ /* ensure that hardware registers the configuration */ ++ wmb(); ++ ++ /* ++ * program and enable address translation region 2 (device resource ++ * address space); region type memory; ++ * axi device bar address range to device bar address range ++ */ ++ writel_relaxed(2, dev->dwc_base + PCIE20_PLR_IATU_VIEWPORT); ++ /* ensure that hardware locks the region before programming it */ ++ wmb(); ++ ++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_CTRL1); ++ writel_relaxed(BIT(31), dev->dwc_base + PCIE20_PLR_IATU_CTRL2); ++ writel_relaxed(dev->mem.start, dev->dwc_base + PCIE20_PLR_IATU_LBAR); ++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UBAR); ++ writel_relaxed(dev->mem.end, dev->dwc_base + PCIE20_PLR_IATU_LAR); ++ writel_relaxed(dev->mem.start, ++ dev->dwc_base + PCIE20_PLR_IATU_LTAR); ++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UTAR); ++ /* ensure that hardware registers the configuration */ ++ wmb(); ++ ++ /* 1K PCIE buffer setting */ ++ writel_relaxed(0x3, dev->dwc_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL0); ++ writel_relaxed(0x1, dev->dwc_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL1); ++ /* ensure that hardware registers the configuration */ ++ wmb(); ++} ++ ++static int qcom_pcie_probe(struct platform_device *pdev) ++{ ++ unsigned long flags; ++ struct qcom_pcie *qcom_pcie; ++ struct device_node *np = pdev->dev.of_node; ++ struct resource *elbi_base, *parf_base, *dwc_base; ++ struct hw_pci *hw; ++ struct of_pci_range range; ++ struct of_pci_range_parser parser; ++ int ret, i; ++ u32 val; ++ ++ qcom_pcie = devm_kzalloc(&pdev->dev, sizeof(*qcom_pcie), GFP_KERNEL); ++ if (!qcom_pcie) { ++ dev_err(&pdev->dev, "no memory for qcom_pcie\n"); ++ return -ENOMEM; ++ } ++ ++ elbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); ++ qcom_pcie->elbi_base = devm_ioremap_resource(&pdev->dev, elbi_base); ++ if (IS_ERR(qcom_pcie->elbi_base)) { ++ dev_err(&pdev->dev, "Failed to ioremap elbi space\n"); ++ return PTR_ERR(qcom_pcie->elbi_base); ++ } ++ ++ parf_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf"); ++ qcom_pcie->parf_base = devm_ioremap_resource(&pdev->dev, parf_base); ++ if (IS_ERR(qcom_pcie->parf_base)) { ++ dev_err(&pdev->dev, "Failed to ioremap parf space\n"); ++ return PTR_ERR(qcom_pcie->parf_base); ++ } ++ ++ dwc_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base"); ++ qcom_pcie->dwc_base = devm_ioremap_resource(&pdev->dev, dwc_base); ++ if (IS_ERR(qcom_pcie->dwc_base)) { ++ dev_err(&pdev->dev, "Failed to ioremap dwc_base space\n"); ++ return PTR_ERR(qcom_pcie->dwc_base); ++ } ++ ++ if (of_pci_range_parser_init(&parser, np)) { ++ dev_err(&pdev->dev, "missing ranges property\n"); ++ return -EINVAL; ++ } ++ ++ /* Get the I/O and memory ranges from DT */ ++ for_each_of_pci_range(&parser, &range) { ++ switch (range.pci_space & 0x3) { ++ case 0: /* cfg */ ++ of_pci_range_to_resource(&range, np, &qcom_pcie->conf); ++ qcom_pcie->conf.flags = IORESOURCE_MEM; ++ break; ++ case 1: /* io */ ++ of_pci_range_to_resource(&range, np, &qcom_pcie->io); ++ break; ++ default: /* mem */ ++ of_pci_range_to_resource(&range, np, &qcom_pcie->mem); ++ break; ++ } ++ } ++ ++ qcom_pcie->cfg_base = devm_ioremap_resource(&pdev->dev, &qcom_pcie->conf); ++ if (IS_ERR(qcom_pcie->cfg_base)) { ++ dev_err(&pdev->dev, "Failed to ioremap PCIe cfg space\n"); ++ return PTR_ERR(qcom_pcie->cfg_base); ++ } ++ ++ qcom_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); ++ if (!gpio_is_valid(qcom_pcie->reset_gpio)) { ++ dev_err(&pdev->dev, "pcie reset gpio is not valid\n"); ++ return -EINVAL; ++ } ++ ++ ret = devm_gpio_request_one(&pdev->dev, qcom_pcie->reset_gpio, ++ GPIOF_DIR_OUT, "pcie_reset"); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to request pcie reset gpio\n"); ++ return ret; ++ } ++ ++ qcom_pcie->iface_clk = devm_clk_get(&pdev->dev, "iface"); ++ if (IS_ERR(qcom_pcie->iface_clk)) { ++ dev_err(&pdev->dev, "Failed to get pcie iface clock\n"); ++ return PTR_ERR(qcom_pcie->iface_clk); ++ } ++ ++ qcom_pcie->phy_clk = devm_clk_get(&pdev->dev, "phy"); ++ if (IS_ERR(qcom_pcie->phy_clk)) { ++ dev_err(&pdev->dev, "Failed to get pcie phy clock\n"); ++ return PTR_ERR(qcom_pcie->phy_clk); ++ } ++ ++ qcom_pcie->bus_clk = devm_clk_get(&pdev->dev, "core"); ++ if (IS_ERR(qcom_pcie->bus_clk)) { ++ dev_err(&pdev->dev, "Failed to get pcie core clock\n"); ++ return PTR_ERR(qcom_pcie->bus_clk); ++ } ++ ++ qcom_pcie->axi_reset = devm_reset_control_get(&pdev->dev, "axi"); ++ if (IS_ERR(qcom_pcie->axi_reset)) { ++ dev_err(&pdev->dev, "Failed to get axi reset\n"); ++ return PTR_ERR(qcom_pcie->axi_reset); ++ } ++ ++ qcom_pcie->ahb_reset = devm_reset_control_get(&pdev->dev, "ahb"); ++ if (IS_ERR(qcom_pcie->ahb_reset)) { ++ dev_err(&pdev->dev, "Failed to get ahb reset\n"); ++ return PTR_ERR(qcom_pcie->ahb_reset); ++ } ++ ++ qcom_pcie->por_reset = devm_reset_control_get(&pdev->dev, "por"); ++ if (IS_ERR(qcom_pcie->por_reset)) { ++ dev_err(&pdev->dev, "Failed to get por reset\n"); ++ return PTR_ERR(qcom_pcie->por_reset); ++ } ++ ++ qcom_pcie->pci_reset = devm_reset_control_get(&pdev->dev, "pci"); ++ if (IS_ERR(qcom_pcie->pci_reset)) { ++ dev_err(&pdev->dev, "Failed to get pci reset\n"); ++ return PTR_ERR(qcom_pcie->pci_reset); ++ } ++ ++ qcom_pcie->phy_reset = devm_reset_control_get(&pdev->dev, "phy"); ++ if (IS_ERR(qcom_pcie->phy_reset)) { ++ dev_err(&pdev->dev, "Failed to get phy reset\n"); ++ return PTR_ERR(qcom_pcie->phy_reset); ++ } ++ ++ for (i = 0; i < 4; i++) { ++ qcom_pcie->irq_int[i] = platform_get_irq(pdev, i+1); ++ if (qcom_pcie->irq_int[i] < 0) { ++ dev_err(&pdev->dev, "failed to get irq resource\n"); ++ return qcom_pcie->irq_int[i]; ++ } ++ } ++ ++ gpio_set_value(qcom_pcie->reset_gpio, 0); ++ usleep_range(10000, 15000); ++ ++ /* assert PCIe PARF reset while powering the core */ ++ reset_control_assert(qcom_pcie->ahb_reset); ++ ++ /* enable clocks */ ++ ret = clk_prepare_enable(qcom_pcie->iface_clk); ++ if (ret) ++ return ret; ++ ret = clk_prepare_enable(qcom_pcie->phy_clk); ++ if (ret) ++ return ret; ++ ret = clk_prepare_enable(qcom_pcie->bus_clk); ++ if (ret) ++ return ret; ++ ++ /* ++ * de-assert PCIe PARF reset; ++ * wait 1us before accessing PARF registers ++ */ ++ reset_control_deassert(qcom_pcie->ahb_reset); ++ udelay(1); ++ ++ /* enable PCIe clocks and resets */ ++ msm_pcie_write_mask(qcom_pcie->parf_base + PCIE20_PARF_PHY_CTRL, BIT(0), 0); ++ ++ /* Set Tx Termination Offset */ ++ val = qcom_parf_readl_relaxed(qcom_pcie, PCIE20_PARF_PHY_CTRL); ++ val |= PCIE20_PARF_PHY_CTRL_PHY_TX0_TERM_OFFST(7); ++ qcom_parf_writel_relaxed(qcom_pcie, val, PCIE20_PARF_PHY_CTRL); ++ ++ /* PARF programming */ ++ qcom_parf_writel_relaxed(qcom_pcie, PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN1(0x18) | ++ PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(0x18) | ++ PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(0x22), ++ PCIE20_PARF_PCS_DEEMPH); ++ qcom_parf_writel_relaxed(qcom_pcie, PCIE20_PARF_PCS_SWING_TX_SWING_FULL(0x78) | ++ PCIE20_PARF_PCS_SWING_TX_SWING_LOW(0x78), ++ PCIE20_PARF_PCS_SWING); ++ qcom_parf_writel_relaxed(qcom_pcie, (4<<24), PCIE20_PARF_CONFIG_BITS); ++ /* ensure that hardware registers the PARF configuration */ ++ wmb(); ++ ++ /* enable reference clock */ ++ msm_pcie_write_mask(qcom_pcie->parf_base + PCIE20_PARF_PHY_REFCLK, BIT(12), BIT(16)); ++ ++ /* ensure that access is enabled before proceeding */ ++ wmb(); ++ ++ /* de-assert PICe PHY, Core, POR and AXI clk domain resets */ ++ reset_control_deassert(qcom_pcie->phy_reset); ++ reset_control_deassert(qcom_pcie->pci_reset); ++ reset_control_deassert(qcom_pcie->por_reset); ++ reset_control_deassert(qcom_pcie->axi_reset); ++ ++ /* wait 150ms for clock acquisition */ ++ usleep_range(10000, 15000); ++ ++ /* de-assert PCIe reset link to bring EP out of reset */ ++ gpio_set_value(qcom_pcie->reset_gpio, 1 - 0); ++ usleep_range(10000, 15000); ++ ++ /* enable link training */ ++ val = qcom_elbi_readl_relaxed(qcom_pcie, PCIE20_ELBI_SYS_CTRL); ++ val |= PCIE20_ELBI_SYS_CTRL_LTSSM_EN; ++ qcom_elbi_writel_relaxed(qcom_pcie, val, PCIE20_ELBI_SYS_CTRL); ++ wmb(); ++ ++ /* poll for link to come up for upto 100ms */ ++ ret = readl_poll_timeout( ++ (qcom_pcie->dwc_base + PCIE20_CAP_LINKCTRLSTATUS), ++ val, (val & BIT(29)), 10000, 100000); ++ ++ printk("link initialized %d\n", ret); ++ ++ qcom_pcie_config_controller(qcom_pcie); ++ ++ platform_set_drvdata(pdev, qcom_pcie); ++ ++ spin_lock_irqsave(&qcom_hw_pci_lock, flags); ++ qcom_hw_pci[nr_controllers].private_data = (void **)&qcom_pcie; ++ hw = &qcom_hw_pci[nr_controllers]; ++ nr_controllers++; ++ spin_unlock_irqrestore(&qcom_hw_pci_lock, flags); ++ ++ pci_common_init(hw); ++ ++ return 0; ++} ++ ++static int __exit qcom_pcie_remove(struct platform_device *pdev) ++{ ++ struct qcom_pcie *qcom_pcie = platform_get_drvdata(pdev); ++ ++ return 0; ++} ++ ++static struct of_device_id qcom_pcie_match[] = { ++ { .compatible = "qcom,pcie-ipq8064", }, ++ {} ++}; ++ ++static struct platform_driver qcom_pcie_driver = { ++ .probe = qcom_pcie_probe, ++ .remove = qcom_pcie_remove, ++ .driver = { ++ .name = "qcom_pcie", ++ .owner = THIS_MODULE, ++ .of_match_table = qcom_pcie_match, ++ }, ++}; ++ ++static int qcom_pcie_init(void) ++{ ++ return platform_driver_register(&qcom_pcie_driver); ++} ++subsys_initcall(qcom_pcie_init); ++ ++/* RC do not represent the right class; set it to PCI_CLASS_BRIDGE_PCI */ ++static void msm_pcie_fixup_early(struct pci_dev *dev) ++{ ++ if (dev->hdr_type == 1) ++ dev->class = (dev->class & 0xff) | (PCI_CLASS_BRIDGE_PCI << 8); ++} ++DECLARE_PCI_FIXUP_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP, msm_pcie_fixup_early); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0139-ARM-dts-msm-Add-PCIe-related-nodes-for-IPQ8064-AP148.patch b/target/linux/ipq806x/patches/0139-ARM-dts-msm-Add-PCIe-related-nodes-for-IPQ8064-AP148.patch new file mode 100644 index 0000000000..1105f140e7 --- /dev/null +++ b/target/linux/ipq806x/patches/0139-ARM-dts-msm-Add-PCIe-related-nodes-for-IPQ8064-AP148.patch @@ -0,0 +1,179 @@ +From 7c6525a0d5cf88f9244187fbe8ee293fa4ee43c1 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Mon, 12 May 2014 19:36:23 -0500 +Subject: [PATCH 139/182] ARM: dts: msm: Add PCIe related nodes for + IPQ8064/AP148 + +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 38 ++++++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 93 ++++++++++++++++++++++++++++++ + 2 files changed, 131 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +index 158a09f..11f7a77 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -21,6 +21,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"; +@@ -80,5 +96,27 @@ + }; + }; + }; ++ ++ pci@1b500000 { ++ status = "ok"; ++ reset-gpio = <&qcom_pinmux 3 0>; ++ pinctrl-0 = <&pcie1_pins>; ++ pinctrl-names = "default"; ++ ++ ranges = <0x00000000 0 0x00000000 0x0ff00000 0 0x00100000 /* configuration space */ ++ 0x81000000 0 0 0x0fe00000 0 0x00100000 /* downstream I/O */ ++ 0x82000000 0 0x00000000 0x08000000 0 0x07e00000>; /* non-prefetchable memory */ ++ }; ++ ++ pci@1b700000 { ++ status = "ok"; ++ reset-gpio = <&qcom_pinmux 48 0>; ++ pinctrl-0 = <&pcie2_pins>; ++ pinctrl-names = "default"; ++ ++ ranges = <0x00000000 0 0x00000000 0x31f00000 0 0x00100000 /* configuration space */ ++ 0x81000000 0 0 0x31e00000 0 0x00100000 /* downstream I/O */ ++ 0x82000000 0 0x00000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */ ++ }; + }; + }; +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +index 244f857..42a651f 100644 +--- 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/reset/qcom,gcc-ipq806x.h> + #include <dt-bindings/soc/qcom,gsbi.h> + + / { +@@ -246,5 +247,97 @@ + #clock-cells = <1>; + #reset-cells = <1>; + }; ++ ++ pci@1b500000 { ++ compatible = "qcom,pcie-ipq8064"; ++ reg = <0x1b500000 0x1000 ++ 0x1b502000 0x80 ++ 0x1b600000 0x100 ++ >; ++ reg-names = "base", "elbi", "parf"; ++ ++ #address-cells = <3>; ++ #size-cells = <2>; ++ device_type = "pci"; ++ interrupts = <0 35 0x0 ++ 0 36 0x0 ++ 0 37 0x0 ++ 0 38 0x0 ++ 0 39 0x0>; ++ 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"; ++ ++ clocks = <&gcc PCIE_A_CLK>, ++ <&gcc PCIE_H_CLK>, ++ <&gcc PCIE_PHY_CLK>; ++ clock-names = "core", "iface", "phy"; ++ status = "disabled"; ++ }; ++ ++ pci@1b700000 { ++ compatible = "qcom,pcie-ipq8064"; ++ reg = <0x1b700000 0x1000 ++ 0x1b702000 0x80 ++ 0x1b800000 0x100 ++ >; ++ reg-names = "base", "elbi", "parf"; ++ ++ #address-cells = <3>; ++ #size-cells = <2>; ++ device_type = "pci"; ++ ++ interrupts = <0 57 0x0 ++ 0 58 0x0 ++ 0 59 0x0 ++ 0 60 0x0 ++ 0 61 0x0>; ++ 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"; ++ ++ clocks = <&gcc PCIE_1_A_CLK>, ++ <&gcc PCIE_1_H_CLK>, ++ <&gcc PCIE_1_PHY_CLK>; ++ clock-names = "core", "iface", "phy"; ++ status = "disabled"; ++ }; ++ ++ pci@1b900000 { ++ compatible = "qcom,pcie-ipq8064"; ++ reg = <0x1b900000 0x1000 ++ 0x1b902000 0x80 ++ 0x1ba00000 0x100 ++ >; ++ reg-names = "base", "elbi", "parf"; ++ ++ #address-cells = <3>; ++ #size-cells = <2>; ++ device_type = "pci"; ++ ++ interrupts = <0 71 0x0 ++ 0 72 0x0 ++ 0 73 0x0 ++ 0 74 0x0 ++ 0 75 0x0>; ++ 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"; ++ ++ clocks = <&gcc PCIE_2_A_CLK>, ++ <&gcc PCIE_2_H_CLK>, ++ <&gcc PCIE_2_PHY_CLK>; ++ clock-names = "core", "iface", "phy"; ++ status = "disabled"; ++ }; + }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0140-ARM-qcom-config-Enable-PCI-support-for-IPQ806x.patch b/target/linux/ipq806x/patches/0140-ARM-qcom-config-Enable-PCI-support-for-IPQ806x.patch new file mode 100644 index 0000000000..e8519e1107 --- /dev/null +++ b/target/linux/ipq806x/patches/0140-ARM-qcom-config-Enable-PCI-support-for-IPQ806x.patch @@ -0,0 +1,33 @@ +From a7e90802be401083098511e72866a0f824c10e15 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Fri, 16 May 2014 16:54:48 -0500 +Subject: [PATCH 140/182] ARM: qcom: config: Enable PCI support for IPQ806x + +--- + arch/arm/configs/qcom_defconfig | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig +index 1752a4d..4866dec 100644 +--- a/arch/arm/configs/qcom_defconfig ++++ b/arch/arm/configs/qcom_defconfig +@@ -21,6 +21,8 @@ CONFIG_ARCH_QCOM=y + CONFIG_ARCH_MSM8X60=y + CONFIG_ARCH_MSM8960=y + CONFIG_ARCH_MSM8974=y ++CONFIG_PCI=y ++CONFIG_PCI_MSI=y + CONFIG_SMP=y + CONFIG_PREEMPT=y + CONFIG_AEABI=y +@@ -83,6 +85,7 @@ CONFIG_INPUT_MISC=y + CONFIG_INPUT_UINPUT=y + CONFIG_SERIO_LIBPS2=y + # CONFIG_LEGACY_PTYS is not set ++CONFIG_SERIAL_8250=y + CONFIG_SERIAL_MSM=y + CONFIG_SERIAL_MSM_CONSOLE=y + CONFIG_HW_RANDOM=y +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0141-ahci-platform-Bump-max-number-of-clocks-to-5.patch b/target/linux/ipq806x/patches/0141-ahci-platform-Bump-max-number-of-clocks-to-5.patch new file mode 100644 index 0000000000..0a41b3e866 --- /dev/null +++ b/target/linux/ipq806x/patches/0141-ahci-platform-Bump-max-number-of-clocks-to-5.patch @@ -0,0 +1,28 @@ +From fd475809eefc0870515d0b04815e2bbae67be906 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Fri, 6 Jun 2014 12:09:01 -0500 +Subject: [PATCH 141/182] ahci-platform: Bump max number of clocks to 5 + +Qualcomm IPQ806x SoCs with SATA controllers need 5 clocks to be enabled. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/ata/ahci.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index 51af275..6357e34 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -53,7 +53,7 @@ + + enum { + AHCI_MAX_PORTS = 32, +- AHCI_MAX_CLKS = 3, ++ AHCI_MAX_CLKS = 5, + AHCI_MAX_SG = 168, /* hardware max is 64K */ + AHCI_DMA_BOUNDARY = 0xffffffff, + AHCI_MAX_CMDS = 32, +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0142-ata-Add-Qualcomm-ARM-SoC-AHCI-SATA-host-controller-d.patch b/target/linux/ipq806x/patches/0142-ata-Add-Qualcomm-ARM-SoC-AHCI-SATA-host-controller-d.patch new file mode 100644 index 0000000000..678a33f472 --- /dev/null +++ b/target/linux/ipq806x/patches/0142-ata-Add-Qualcomm-ARM-SoC-AHCI-SATA-host-controller-d.patch @@ -0,0 +1,146 @@ +From c5ea64dcf7b5d45e402e78b9f291d521669b7b80 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Fri, 30 May 2014 15:35:40 -0500 +Subject: [PATCH 142/182] ata: Add Qualcomm ARM SoC AHCI SATA host controller + driver + +Add support for the Qualcomm AHCI SATA controller that exists on several +SoC and specifically the IPQ806x family of chips. The IPQ806x SATA support +requires the associated IPQ806x SATA PHY Driver to be enabled as well. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/ata/Kconfig | 10 ++++++ + drivers/ata/Makefile | 1 + + drivers/ata/ahci_qcom.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 97 insertions(+) + create mode 100644 drivers/ata/ahci_qcom.c + +diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig +index dc950f3..da05034 100644 +--- a/drivers/ata/Kconfig ++++ b/drivers/ata/Kconfig +@@ -106,6 +106,16 @@ config AHCI_IMX + + If unsure, say N. + ++config AHCI_QCOM ++ tristate "Qualcomm AHCI SATA support" ++ depends on ARCH_QCOM ++ help ++ This option enables support for AHCI SATA controller ++ integrated into Qualcomm ARM SoC chipsets. For more ++ information please refer to http://www.qualcomm.com/chipsets. ++ ++ If unsure, say N. ++ + config SATA_FSL + tristate "Freescale 3.0Gbps SATA support" + depends on FSL_SOC +diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile +index 366b743..e08044f 100644 +--- a/drivers/ata/Makefile ++++ b/drivers/ata/Makefile +@@ -11,6 +11,7 @@ obj-$(CONFIG_SATA_SIL24) += sata_sil24.o + obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o + obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o + obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o ++obj-$(CONFIG_AHCI_QCOM) += ahci_qcom.o libahci.o libahci_platform.o + + # SFF w/ custom DMA + obj-$(CONFIG_PDC_ADMA) += pdc_adma.o +diff --git a/drivers/ata/ahci_qcom.c b/drivers/ata/ahci_qcom.c +new file mode 100644 +index 0000000..bfb7a77 +--- /dev/null ++++ b/drivers/ata/ahci_qcom.c +@@ -0,0 +1,86 @@ ++/* ++ * Qualcomm ARM SoC AHCI SATA platform driver ++ * ++ * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov ++ * ++ * 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/kernel.h> ++#include <linux/module.h> ++#include <linux/pm.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/libata.h> ++#include <linux/ahci_platform.h> ++#include "ahci.h" ++ ++static const struct ata_port_info qcom_ahci_port_info = { ++ .flags = AHCI_FLAG_COMMON, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_platform_ops, ++}; ++ ++static int qcom_ahci_probe(struct platform_device *pdev) ++{ ++ struct ahci_host_priv *hpriv; ++ struct clk *rxoob_clk; ++ int rc; ++ ++ hpriv = ahci_platform_get_resources(pdev); ++ if (IS_ERR(hpriv)) ++ return PTR_ERR(hpriv); ++ ++ /* Try and set the rxoob clk to 100Mhz */ ++ rxoob_clk = of_clk_get_by_name(pdev->dev.of_node, "rxoob"); ++ if (IS_ERR(rxoob_clk)) ++ return PTR_ERR(rxoob_clk); ++ ++ rc = clk_set_rate(rxoob_clk, 100000000); ++ if (rc) ++ return rc; ++ ++ rc = ahci_platform_enable_resources(hpriv); ++ if (rc) ++ return rc; ++ ++ rc = ahci_platform_init_host(pdev, hpriv, &qcom_ahci_port_info, 0, 0); ++ if (rc) ++ goto disable_resources; ++ ++ return 0; ++disable_resources: ++ ahci_platform_disable_resources(hpriv); ++ return rc; ++} ++ ++static const struct of_device_id qcom_ahci_of_match[] = { ++ { .compatible = "qcom,msm-ahci", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, qcom_ahci_of_match); ++ ++static struct platform_driver qcom_ahci_driver = { ++ .probe = qcom_ahci_probe, ++ .remove = ata_platform_remove_one, ++ .driver = { ++ .name = "qcom_ahci_qcom", ++ .owner = THIS_MODULE, ++ .of_match_table = qcom_ahci_of_match, ++ }, ++}; ++module_platform_driver(qcom_ahci_driver); ++ ++MODULE_DESCRIPTION("Qualcomm AHCI SATA platform driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("ahci:qcom"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0143-ata-qcom-Add-device-tree-bindings-information.patch b/target/linux/ipq806x/patches/0143-ata-qcom-Add-device-tree-bindings-information.patch new file mode 100644 index 0000000000..ef795bf054 --- /dev/null +++ b/target/linux/ipq806x/patches/0143-ata-qcom-Add-device-tree-bindings-information.patch @@ -0,0 +1,63 @@ +From dd280a4cf6b826144f74d3fc4285633f1c337a1d Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 12 Jun 2014 11:28:47 -0500 +Subject: [PATCH 143/182] ata: qcom: Add device tree bindings information + +Add device tree binding for Qualcomm AHCI SATA controller and specifically +the sata controller on the IPQ806x family of SoCs. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + .../devicetree/bindings/ata/qcom-sata.txt | 40 ++++++++++++++++++++ + 1 file changed, 40 insertions(+) + create mode 100644 Documentation/devicetree/bindings/ata/qcom-sata.txt + +diff --git a/Documentation/devicetree/bindings/ata/qcom-sata.txt b/Documentation/devicetree/bindings/ata/qcom-sata.txt +new file mode 100644 +index 0000000..5e74e41 +--- /dev/null ++++ b/Documentation/devicetree/bindings/ata/qcom-sata.txt +@@ -0,0 +1,40 @@ ++* Qualcomm AHCI SATA Controller ++ ++SATA nodes are defined to describe on-chip Serial ATA controllers. ++Each SATA controller should have its own node. ++ ++Required properties: ++- compatible : compatible list, contains "qcom,msm-ahci" ++- interrupts : <interrupt mapping for SATA IRQ> ++- reg : <registers mapping> ++- phys : Must contain exactly one entry as specified ++ in phy-bindings.txt ++- phy-names : Must be "sata-phy" ++ ++Required properties for "qcom,ipq806x-ahci" compatible: ++- clocks : Must contain an entry for each entry in clock-names. ++- clock-names : Shall be: ++ "slave_iface" - Fabric port AHB clock for SATA ++ "iface" - AHB clock ++ "core" - core clock ++ "rxoob" - RX out-of-band clock ++ "pmalive" - Power Module Alive clock ++ ++Example: ++ sata@29000000 { ++ compatible = "qcom,ipq806x-ahci", "qcom,msm-ahci"; ++ reg = <0x29000000 0x180>; ++ ++ interrupts = <0 209 0x0>; ++ ++ clocks = <&gcc SFAB_SATA_S_H_CLK>, ++ <&gcc SATA_H_CLK>, ++ <&gcc SATA_A_CLK>, ++ <&gcc SATA_RXOOB_CLK>, ++ <&gcc SATA_PMALIVE_CLK>; ++ clock-names = "slave_iface", "iface", "core", ++ "rxoob", "pmalive"; ++ ++ phys = <&sata_phy>; ++ phy-names = "sata-phy"; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0144-phy-qcom-Add-driver-for-QCOM-IPQ806x-SATA-PHY.patch b/target/linux/ipq806x/patches/0144-phy-qcom-Add-driver-for-QCOM-IPQ806x-SATA-PHY.patch new file mode 100644 index 0000000000..862be0835e --- /dev/null +++ b/target/linux/ipq806x/patches/0144-phy-qcom-Add-driver-for-QCOM-IPQ806x-SATA-PHY.patch @@ -0,0 +1,261 @@ +From 7554dfbbec255e4cd5dd4445841c28c48fd4a855 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Tue, 3 Jun 2014 11:59:09 -0500 +Subject: [PATCH 144/182] phy: qcom: Add driver for QCOM IPQ806x SATA PHY + +Add a PHY driver for uses with AHCI based SATA controller driver on the +IPQ806x family of SoCs. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/phy/Kconfig | 7 ++ + drivers/phy/Makefile | 1 + + drivers/phy/phy-qcom-ipq806x-sata.c | 211 +++++++++++++++++++++++++++++++++++ + 3 files changed, 219 insertions(+) + create mode 100644 drivers/phy/phy-qcom-ipq806x-sata.c + +diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig +index c7a551c..3642a65 100644 +--- a/drivers/phy/Kconfig ++++ b/drivers/phy/Kconfig +@@ -65,4 +65,11 @@ config BCM_KONA_USB2_PHY + help + Enable this to support the Broadcom Kona USB 2.0 PHY. + ++config PHY_QCOM_IPQ806X_SATA ++ tristate "Qualcomm IPQ806x SATA SerDes/PHY driver" ++ depends on ARCH_QCOM ++ depends on HAS_IOMEM ++ depends on OF ++ select GENERIC_PHY ++ + endmenu +diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile +index b57c253..74f78cc 100644 +--- a/drivers/phy/Makefile ++++ b/drivers/phy/Makefile +@@ -9,3 +9,4 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o + obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o + obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o + obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o ++obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o +diff --git a/drivers/phy/phy-qcom-ipq806x-sata.c b/drivers/phy/phy-qcom-ipq806x-sata.c +new file mode 100644 +index 0000000..e931aee +--- /dev/null ++++ b/drivers/phy/phy-qcom-ipq806x-sata.c +@@ -0,0 +1,211 @@ ++/* ++ * 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/io.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/time.h> ++#include <linux/delay.h> ++#include <linux/clk.h> ++#include <linux/slab.h> ++#include <linux/platform_device.h> ++#include <linux/phy/phy.h> ++ ++struct qcom_ipq806x_sata_phy { ++ void __iomem *mmio; ++ struct clk *cfg_clk; ++}; ++ ++#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b)) ++ ++#define SATA_PHY_P0_PARAM0 0x200 ++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x) __set(x, 17, 12) ++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK GENMASK(17, 12) ++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x) __set(x, 11, 6) ++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK GENMASK(11, 6) ++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x) __set(x, 5, 0) ++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK GENMASK(5, 0) ++ ++#define SATA_PHY_P0_PARAM1 0x204 ++#define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x) __set(x, 31, 21) ++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x) __set(x, 20, 14) ++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK GENMASK(20, 14) ++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x) __set(x, 13, 7) ++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK GENMASK(13, 7) ++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x) __set(x, 6, 0) ++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0) ++ ++#define SATA_PHY_P0_PARAM2 0x208 ++#define SATA_PHY_P0_PARAM2_RX_EQ(x) __set(x, 20, 18) ++#define SATA_PHY_P0_PARAM2_RX_EQ_MASK GENMASK(20, 18) ++ ++#define SATA_PHY_P0_PARAM3 0x20C ++#define SATA_PHY_SSC_EN 0x8 ++#define SATA_PHY_P0_PARAM4 0x210 ++#define SATA_PHY_REF_SSP_EN 0x2 ++#define SATA_PHY_RESET 0x1 ++ ++static inline void qcom_ipq806x_sata_delay_us(unsigned int delay) ++{ ++ /* sleep for max. 50us more to combine processor wakeups */ ++ usleep_range(delay, delay + 50); ++} ++ ++static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy) ++{ ++ struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy); ++ u32 reg; ++ ++ /* Setting SSC_EN to 1 */ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3); ++ reg = reg | SATA_PHY_SSC_EN; ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3); ++ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) & ++ ~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK | ++ SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK | ++ SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK); ++ reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf); ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0); ++ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) & ++ ~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK | ++ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK | ++ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK); ++ reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) | ++ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) | ++ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55); ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1); ++ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) & ++ ~SATA_PHY_P0_PARAM2_RX_EQ_MASK; ++ reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3); ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2); ++ ++ /* Setting PHY_RESET to 1 */ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); ++ reg = reg | SATA_PHY_RESET; ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); ++ ++ /* Setting REF_SSP_EN to 1 */ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); ++ reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET; ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); ++ mb(); ++ qcom_ipq806x_sata_delay_us(20); ++ ++ /* Clearing PHY_RESET to 0 */ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); ++ reg = reg & ~SATA_PHY_RESET; ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); ++ ++ return 0; ++} ++ ++static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy) ++{ ++ struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy); ++ u32 reg; ++ ++ /* Setting PHY_RESET to 1 */ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); ++ reg = reg | SATA_PHY_RESET; ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); ++ ++ return 0; ++} ++ ++static struct phy_ops qcom_ipq806x_sata_phy_ops = { ++ .init = qcom_ipq806x_sata_phy_init, ++ .exit = qcom_ipq806x_sata_phy_exit, ++ .owner = THIS_MODULE, ++}; ++ ++static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev) ++{ ++ struct qcom_ipq806x_sata_phy *phy; ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ struct phy_provider *phy_provider; ++ struct phy *generic_phy; ++ int ret; ++ ++ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) { ++ dev_err(dev, "%s: failed to allocate phy\n", __func__); ++ return -ENOMEM; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ phy->mmio = devm_ioremap_resource(dev, res); ++ if (IS_ERR(phy->mmio)) ++ return PTR_ERR(phy->mmio); ++ ++ generic_phy = devm_phy_create(dev, &qcom_ipq806x_sata_phy_ops, NULL); ++ if (IS_ERR(generic_phy)) { ++ dev_err(dev, "%s: failed to create phy\n", __func__); ++ return PTR_ERR(generic_phy); ++ } ++ ++ phy_set_drvdata(generic_phy, phy); ++ ++ phy->cfg_clk = devm_clk_get(dev, "cfg"); ++ if (IS_ERR(phy->cfg_clk)) { ++ dev_err(dev, "Failed to get sata cfg clock\n"); ++ return PTR_ERR(phy->cfg_clk); ++ } ++ ++ ret = clk_prepare_enable(phy->cfg_clk); ++ if (ret) ++ return ret; ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (IS_ERR(phy_provider)) { ++ clk_disable_unprepare(phy->cfg_clk); ++ dev_err(dev, "%s: failed to register phy\n", __func__); ++ return PTR_ERR(phy_provider); ++ } ++ ++ return 0; ++} ++ ++static int qcom_ipq806x_sata_phy_remove(struct platform_device *pdev) ++{ ++ struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev); ++ ++ clk_disable_unprepare(phy->cfg_clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = { ++ { .compatible = "qcom,ipq806x-sata-phy" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match); ++ ++static struct platform_driver qcom_ipq806x_sata_phy_driver = { ++ .probe = qcom_ipq806x_sata_phy_probe, ++ .remove = qcom_ipq806x_sata_phy_remove, ++ .driver = { ++ .name = "qcom-ipq806x-sata-phy", ++ .owner = THIS_MODULE, ++ .of_match_table = qcom_ipq806x_sata_phy_of_match, ++ } ++}; ++module_platform_driver(qcom_ipq806x_sata_phy_driver); ++ ++MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver"); ++MODULE_LICENSE("GPL v2"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0145-phy-qcom-Add-device-tree-bindings-information.patch b/target/linux/ipq806x/patches/0145-phy-qcom-Add-device-tree-bindings-information.patch new file mode 100644 index 0000000000..1791228113 --- /dev/null +++ b/target/linux/ipq806x/patches/0145-phy-qcom-Add-device-tree-bindings-information.patch @@ -0,0 +1,46 @@ +From 37258bc8fe832e4c681593a864686f627f6d3455 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Tue, 10 Jun 2014 13:09:01 -0500 +Subject: [PATCH 145/182] phy: qcom: Add device tree bindings information + +Add binding spec for Qualcomm SoC PHYs, starting with the SATA PHY on +the IPQ806x family of SoCs. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + Documentation/devicetree/bindings/phy/qcom-phy.txt | 23 ++++++++++++++++++++ + 1 file changed, 23 insertions(+) + create mode 100644 Documentation/devicetree/bindings/phy/qcom-phy.txt + +diff --git a/Documentation/devicetree/bindings/phy/qcom-phy.txt b/Documentation/devicetree/bindings/phy/qcom-phy.txt +new file mode 100644 +index 0000000..76bfbd0 +--- /dev/null ++++ b/Documentation/devicetree/bindings/phy/qcom-phy.txt +@@ -0,0 +1,23 @@ ++Qualcomm IPQ806x SATA PHY Controller ++------------------------------------ ++ ++SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers. ++Each SATA PHY controller should have its own node. ++ ++Required properties: ++- compatible: compatible list, contains "qcom,ipq806x-sata-phy" ++- reg: offset and length of the SATA PHY register set; ++- #phy-cells: must be zero ++- clocks: must be exactly one entry ++- clock-names: must be "cfg" ++ ++Example: ++ sata_phy: sata-phy@1b400000 { ++ compatible = "qcom,ipq806x-sata-phy"; ++ reg = <0x1b400000 0x200>; ++ ++ clocks = <&gcc SATA_PHY_CFG_CLK>; ++ clock-names = "cfg"; ++ ++ #phy-cells = <0>; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0146-ARM-dts-qcom-Add-SATA-support-for-IPQ8064-and-AP148-.patch b/target/linux/ipq806x/patches/0146-ARM-dts-qcom-Add-SATA-support-for-IPQ8064-and-AP148-.patch new file mode 100644 index 0000000000..0d196ad212 --- /dev/null +++ b/target/linux/ipq806x/patches/0146-ARM-dts-qcom-Add-SATA-support-for-IPQ8064-and-AP148-.patch @@ -0,0 +1,72 @@ +From 6992cf3e8900d042a845eafc11e7841f32fec0a6 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 12 Jun 2014 10:56:54 -0500 +Subject: [PATCH 146/182] ARM: dts: qcom: Add SATA support for IPQ8064 and + AP148 board + +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 8 ++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 30 ++++++++++++++++++++++++++++++ + 2 files changed, 38 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +index 11f7a77..c752889 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -118,5 +118,13 @@ + 0x81000000 0 0 0x31e00000 0 0x00100000 /* downstream I/O */ + 0x82000000 0 0x00000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */ + }; ++ ++ sata-phy@1b400000 { ++ status = "ok"; ++ }; ++ ++ sata@29000000 { ++ status = "ok"; ++ }; + }; + }; +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +index 42a651f..93c0315 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -339,5 +339,35 @@ + clock-names = "core", "iface", "phy"; + status = "disabled"; + }; ++ ++ sata_phy: sata-phy@1b400000 { ++ compatible = "qcom,ipq806x-sata-phy"; ++ reg = <0x1b400000 0x200>; ++ ++ clocks = <&gcc SATA_PHY_CFG_CLK>; ++ clock-names = "cfg"; ++ ++ #phy-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ sata@29000000 { ++ compatible = "qcom,ipq806x-ahci", "qcom,msm-ahci"; ++ reg = <0x29000000 0x180>; ++ ++ interrupts = <0 209 0x0>; ++ ++ clocks = <&gcc SFAB_SATA_S_H_CLK>, ++ <&gcc SATA_H_CLK>, ++ <&gcc SATA_A_CLK>, ++ <&gcc SATA_RXOOB_CLK>, ++ <&gcc SATA_PMALIVE_CLK>; ++ clock-names = "slave_face", "iface", "core", ++ "rxoob", "pmalive"; ++ ++ phys = <&sata_phy>; ++ phy-names = "sata-phy"; ++ status = "disabled"; ++ }; + }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0147-ARM-qcom-Enable-SATA-SATA-PHY-drivers-in-defconfig.patch b/target/linux/ipq806x/patches/0147-ARM-qcom-Enable-SATA-SATA-PHY-drivers-in-defconfig.patch new file mode 100644 index 0000000000..43e9dff160 --- /dev/null +++ b/target/linux/ipq806x/patches/0147-ARM-qcom-Enable-SATA-SATA-PHY-drivers-in-defconfig.patch @@ -0,0 +1,42 @@ +From 54a945eca8381fa8cda03b20eda3b2f23a88d289 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Tue, 3 Jun 2014 10:36:41 -0500 +Subject: [PATCH 147/182] ARM: qcom: Enable SATA/SATA-PHY drivers in defconfig + +--- + arch/arm/configs/qcom_defconfig | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig +index 4866dec..84c6639 100644 +--- a/arch/arm/configs/qcom_defconfig ++++ b/arch/arm/configs/qcom_defconfig +@@ -57,7 +57,6 @@ CONFIG_MTD_BLOCK=y + CONFIG_MTD_M25P80=y + CONFIG_BLK_DEV_LOOP=y + CONFIG_BLK_DEV_RAM=y +-CONFIG_SCSI=y + CONFIG_SCSI_TGT=y + CONFIG_BLK_DEV_SD=y + CONFIG_CHR_DEV_SG=y +@@ -66,6 +65,8 @@ CONFIG_SCSI_MULTI_LUN=y + CONFIG_SCSI_CONSTANTS=y + CONFIG_SCSI_LOGGING=y + CONFIG_SCSI_SCAN_ASYNC=y ++CONFIG_ATA=y ++CONFIG_AHCI_QCOM=y + CONFIG_NETDEVICES=y + CONFIG_DUMMY=y + CONFIG_MDIO_BITBANG=y +@@ -141,7 +142,7 @@ CONFIG_MSM_GCC_8660=y + CONFIG_MSM_MMCC_8960=y + CONFIG_MSM_MMCC_8974=y + CONFIG_MSM_IOMMU=y +-CONFIG_GENERIC_PHY=y ++CONFIG_PHY_QCOM_IPQ806X_SATA=y + CONFIG_EXT2_FS=y + CONFIG_EXT2_FS_XATTR=y + CONFIG_EXT3_FS=y +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0148-ARM-qcom-enable-default-CPU_IDLE-to-get-wfi-support-.patch b/target/linux/ipq806x/patches/0148-ARM-qcom-enable-default-CPU_IDLE-to-get-wfi-support-.patch new file mode 100644 index 0000000000..29d712679d --- /dev/null +++ b/target/linux/ipq806x/patches/0148-ARM-qcom-enable-default-CPU_IDLE-to-get-wfi-support-.patch @@ -0,0 +1,26 @@ +From 8c9156c67c3e3e37812bb0e4d24263be0e564881 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Mon, 16 Jun 2014 14:40:29 -0500 +Subject: [PATCH 148/182] ARM: qcom: enable default CPU_IDLE to get wfi + support on IPQ806x + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/configs/qcom_defconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig +index 84c6639..85a35af 100644 +--- a/arch/arm/configs/qcom_defconfig ++++ b/arch/arm/configs/qcom_defconfig +@@ -31,6 +31,7 @@ CONFIG_HIGHPTE=y + CONFIG_CLEANCACHE=y + CONFIG_ARM_APPENDED_DTB=y + CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_CPU_IDLE=y + CONFIG_VFP=y + CONFIG_NEON=y + # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0149-pinctrl-qcom-Add-BUS_HOLD-Keeper-bias.patch b/target/linux/ipq806x/patches/0149-pinctrl-qcom-Add-BUS_HOLD-Keeper-bias.patch new file mode 100644 index 0000000000..8e21ee8a97 --- /dev/null +++ b/target/linux/ipq806x/patches/0149-pinctrl-qcom-Add-BUS_HOLD-Keeper-bias.patch @@ -0,0 +1,55 @@ +From b1325bfad3ad0e543ce90a6b08d74500dc96f4b9 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 16 Jun 2014 16:52:55 -0500 +Subject: [PATCH 149/182] pinctrl: qcom: Add BUS_HOLD/Keeper bias + +This patch adds the bus_hold bias option for pins. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + drivers/pinctrl/pinctrl-msm.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index 7d67d34..f054b25 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -207,6 +207,7 @@ static int msm_config_reg(struct msm_pinctrl *pctrl, + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: ++ case PIN_CONFIG_BIAS_BUS_HOLD: + *bit = g->pull_bit; + *mask = 3; + break; +@@ -243,6 +244,7 @@ static int msm_config_set(struct pinctrl_dev *pctldev, unsigned int pin, + + #define MSM_NO_PULL 0 + #define MSM_PULL_DOWN 1 ++#define MSM_KEEPER 2 + #define MSM_PULL_UP 3 + + static unsigned msm_regval_to_drive(u32 val) +@@ -280,6 +282,9 @@ static int msm_config_group_get(struct pinctrl_dev *pctldev, + case PIN_CONFIG_BIAS_PULL_DOWN: + arg = arg == MSM_PULL_DOWN; + break; ++ case PIN_CONFIG_BIAS_BUS_HOLD: ++ arg = arg == MSM_KEEPER; ++ break; + case PIN_CONFIG_BIAS_PULL_UP: + arg = arg == MSM_PULL_UP; + break; +@@ -339,6 +344,9 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev, + case PIN_CONFIG_BIAS_PULL_DOWN: + arg = MSM_PULL_DOWN; + break; ++ case PIN_CONFIG_BIAS_BUS_HOLD: ++ arg = MSM_KEEPER; ++ break; + case PIN_CONFIG_BIAS_PULL_UP: + arg = MSM_PULL_UP; + break; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0150-mtd-nand-Add-Qualcomm-NAND-controller.patch b/target/linux/ipq806x/patches/0150-mtd-nand-Add-Qualcomm-NAND-controller.patch new file mode 100644 index 0000000000..5a9fb51f03 --- /dev/null +++ b/target/linux/ipq806x/patches/0150-mtd-nand-Add-Qualcomm-NAND-controller.patch @@ -0,0 +1,8803 @@ +From d2981ca1343b837fc574c4e46806d041b258720d Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 16 Jun 2014 17:13:22 -0500 +Subject: [PATCH 150/182] mtd: nand: Add Qualcomm NAND controller + +This patch adds the Qualcomm NAND controller and required infrastructure. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + drivers/mtd/nand/Kconfig | 18 + + drivers/mtd/nand/Makefile | 2 + + drivers/mtd/nand/qcom_adm_dma.c | 797 +++++ + drivers/mtd/nand/qcom_adm_dma.h | 268 ++ + drivers/mtd/nand/qcom_nand.c | 7455 +++++++++++++++++++++++++++++++++++++++ + drivers/mtd/nand/qcom_nand.h | 196 + + 6 files changed, 8736 insertions(+) + create mode 100644 drivers/mtd/nand/qcom_adm_dma.c + create mode 100644 drivers/mtd/nand/qcom_adm_dma.h + create mode 100644 drivers/mtd/nand/qcom_nand.c + create mode 100644 drivers/mtd/nand/qcom_nand.h + +diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig +index 90ff447..6e3842f 100644 +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -510,4 +510,22 @@ config MTD_NAND_XWAY + Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached + to the External Bus Unit (EBU). + ++config MTD_QCOM_DMA ++ tristate "QCMO NAND DMA Support" ++ depends on ARCH_QCOM && MTD_QCOM_NAND ++ default n ++ help ++ DMA support for QCOM NAND ++ ++config MTD_QCOM_NAND ++ tristate "QCOM NAND Device Support" ++ depends on MTD && ARCH_QCOM ++ select CRC16 ++ select BITREVERSE ++ select MTD_NAND_IDS ++ select MTD_QCOM_DMA ++ default y ++ help ++ Support for some NAND chips connected to the QCOM NAND controller. ++ + endif # MTD_NAND +diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile +index 542b568..6ef3c02 100644 +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -49,5 +49,7 @@ obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o + obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ + obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o + obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ ++obj-$(CONFIG_MTD_QCOM_NAND) += qcom_nand.o ++obj-$(CONFIG_MTD_QCOM_DMA) += qcom_adm_dma.o + + nand-objs := nand_base.o nand_bbt.o +diff --git a/drivers/mtd/nand/qcom_adm_dma.c b/drivers/mtd/nand/qcom_adm_dma.c +new file mode 100644 +index 0000000..46d8473 +--- /dev/null ++++ b/drivers/mtd/nand/qcom_adm_dma.c +@@ -0,0 +1,797 @@ ++/* * Copyright (c) 2012 The Linux Foundation. All rights reserved.* */ ++/* linux/arch/arm/mach-msm/dma.c ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * Copyright (c) 2008-2010, 2012 The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include <linux/clk.h> ++#include <linux/err.h> ++#include <linux/io.h> ++#include <linux/interrupt.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/spinlock.h> ++#include <linux/pm_runtime.h> ++#include <linux/reset.h> ++#include <linux/reset-controller.h> ++#include "qcom_adm_dma.h" ++ ++#define MODULE_NAME "msm_dmov" ++ ++#define MSM_DMOV_CHANNEL_COUNT 16 ++#define MSM_DMOV_CRCI_COUNT 16 ++ ++enum { ++ CLK_DIS, ++ CLK_TO_BE_DIS, ++ CLK_EN ++}; ++ ++struct msm_dmov_ci_conf { ++ int start; ++ int end; ++ int burst; ++}; ++ ++struct msm_dmov_crci_conf { ++ int sd; ++ int blk_size; ++}; ++ ++struct msm_dmov_chan_conf { ++ int sd; ++ int block; ++ int priority; ++}; ++ ++struct msm_dmov_conf { ++ void *base; ++ struct msm_dmov_crci_conf *crci_conf; ++ struct msm_dmov_chan_conf *chan_conf; ++ int channel_active; ++ int sd; ++ size_t sd_size; ++ struct list_head staged_commands[MSM_DMOV_CHANNEL_COUNT]; ++ struct list_head ready_commands[MSM_DMOV_CHANNEL_COUNT]; ++ struct list_head active_commands[MSM_DMOV_CHANNEL_COUNT]; ++ struct mutex lock; ++ spinlock_t list_lock; ++ unsigned int irq; ++ struct clk *clk; ++ struct clk *pclk; ++ struct clk *ebiclk; ++ unsigned int clk_ctl; ++ struct delayed_work work; ++ struct workqueue_struct *cmd_wq; ++ ++ struct reset_control *adm_reset; ++ struct reset_control *pbus_reset; ++ struct reset_control *c0_reset; ++ struct reset_control *c1_reset; ++ struct reset_control *c2_reset; ++ ++}; ++ ++static void msm_dmov_clock_work(struct work_struct *); ++ ++#define DMOV_CRCI_DEFAULT_CONF { .sd = 0, .blk_size = 0 } ++#define DMOV_CRCI_CONF(secd, blk) { .sd = secd, .blk_size = blk } ++ ++static struct msm_dmov_crci_conf adm_crci_conf[] = { ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_CONF(0, 1), ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++}; ++ ++#define DMOV_CHANNEL_DEFAULT_CONF { .sd = 0, .block = 0, .priority = 1 } ++ ++static struct msm_dmov_chan_conf adm_chan_conf[] = { ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++}; ++ ++#define DMOV_IRQ_TO_ADM(irq) 0 ++ ++static struct msm_dmov_conf dmov_conf[] = { ++ { ++ .crci_conf = adm_crci_conf, ++ .chan_conf = adm_chan_conf, ++ .lock = __MUTEX_INITIALIZER(dmov_conf[0].lock), ++ .list_lock = __SPIN_LOCK_UNLOCKED(dmov_list_lock), ++ .clk_ctl = CLK_EN, ++ .work = __DELAYED_WORK_INITIALIZER(dmov_conf[0].work, ++ msm_dmov_clock_work,0), ++ } ++}; ++ ++#define MSM_DMOV_ID_COUNT (MSM_DMOV_CHANNEL_COUNT * ARRAY_SIZE(dmov_conf)) ++#define DMOV_REG(name, adm) ((name) + (dmov_conf[adm].base) +\ ++ (dmov_conf[adm].sd * dmov_conf[adm].sd_size)) ++#define DMOV_ID_TO_ADM(id) ((id) / MSM_DMOV_CHANNEL_COUNT) ++#define DMOV_ID_TO_CHAN(id) ((id) % MSM_DMOV_CHANNEL_COUNT) ++#define DMOV_CHAN_ADM_TO_ID(ch, adm) ((ch) + (adm) * MSM_DMOV_CHANNEL_COUNT) ++ ++enum { ++ MSM_DMOV_PRINT_ERRORS = 1, ++ MSM_DMOV_PRINT_IO = 2, ++ MSM_DMOV_PRINT_FLOW = 4 ++}; ++ ++unsigned int msm_dmov_print_mask = MSM_DMOV_PRINT_ERRORS; ++ ++#define MSM_DMOV_DPRINTF(mask, format, args...) \ ++ do { \ ++ if ((mask) & msm_dmov_print_mask) \ ++ printk(KERN_ERR format, args); \ ++ } while (0) ++#define PRINT_ERROR(format, args...) \ ++ MSM_DMOV_DPRINTF(MSM_DMOV_PRINT_ERRORS, format, args); ++#define PRINT_IO(format, args...) \ ++ MSM_DMOV_DPRINTF(MSM_DMOV_PRINT_IO, format, args); ++#define PRINT_FLOW(format, args...) \ ++ MSM_DMOV_DPRINTF(MSM_DMOV_PRINT_FLOW, format, args); ++ ++static int msm_dmov_clk_on(int adm) ++{ ++ int ret; ++ ++return 0; ++ ret = clk_prepare_enable(dmov_conf[adm].clk); ++ if (ret) ++ return ret; ++ if (dmov_conf[adm].pclk) { ++ ret = clk_prepare_enable(dmov_conf[adm].pclk); ++ if (ret) { ++ clk_disable_unprepare(dmov_conf[adm].clk); ++ return ret; ++ } ++ } ++ if (dmov_conf[adm].ebiclk) { ++ ret = clk_prepare_enable(dmov_conf[adm].ebiclk); ++ if (ret) { ++ if (dmov_conf[adm].pclk) ++ clk_disable_unprepare(dmov_conf[adm].pclk); ++ clk_disable_unprepare(dmov_conf[adm].clk); ++ } ++ } ++ return ret; ++} ++ ++static void msm_dmov_clk_off(int adm) ++{ ++#if 0 ++ if (dmov_conf[adm].ebiclk) ++ clk_disable_unprepare(dmov_conf[adm].ebiclk); ++ if (dmov_conf[adm].pclk) ++ clk_disable_unprepare(dmov_conf[adm].pclk); ++ clk_disable_unprepare(dmov_conf[adm].clk); ++#endif ++} ++ ++static void msm_dmov_clock_work(struct work_struct *work) ++{ ++ struct msm_dmov_conf *conf = ++ container_of(to_delayed_work(work), struct msm_dmov_conf, work); ++ int adm = DMOV_IRQ_TO_ADM(conf->irq); ++ mutex_lock(&conf->lock); ++ if (conf->clk_ctl == CLK_TO_BE_DIS) { ++ BUG_ON(conf->channel_active); ++ msm_dmov_clk_off(adm); ++ conf->clk_ctl = CLK_DIS; ++ } ++ mutex_unlock(&conf->lock); ++} ++ ++enum { ++ NOFLUSH = 0, ++ GRACEFUL, ++ NONGRACEFUL, ++}; ++ ++/* Caller must hold the list lock */ ++static struct msm_dmov_cmd *start_ready_cmd(unsigned ch, int adm) ++{ ++ struct msm_dmov_cmd *cmd; ++ ++ if (list_empty(&dmov_conf[adm].ready_commands[ch])) { ++ return NULL; ++ } ++ ++ cmd = list_entry(dmov_conf[adm].ready_commands[ch].next, typeof(*cmd), ++ list); ++ list_del(&cmd->list); ++ if (cmd->exec_func) ++ cmd->exec_func(cmd); ++ list_add_tail(&cmd->list, &dmov_conf[adm].active_commands[ch]); ++ if (!dmov_conf[adm].channel_active) { ++ enable_irq(dmov_conf[adm].irq); ++ } ++ dmov_conf[adm].channel_active |= BIT(ch); ++ PRINT_IO("msm dmov enqueue command, %x, ch %d\n", cmd->cmdptr, ch); ++ writel_relaxed(cmd->cmdptr, DMOV_REG(DMOV_CMD_PTR(ch), adm)); ++ ++ return cmd; ++} ++ ++static void msm_dmov_enqueue_cmd_ext_work(struct work_struct *work) ++{ ++ struct msm_dmov_cmd *cmd = ++ container_of(work, struct msm_dmov_cmd, work); ++ unsigned id = cmd->id; ++ unsigned status; ++ unsigned long flags; ++ int adm = DMOV_ID_TO_ADM(id); ++ int ch = DMOV_ID_TO_CHAN(id); ++ ++ mutex_lock(&dmov_conf[adm].lock); ++ if (dmov_conf[adm].clk_ctl == CLK_DIS) { ++ status = msm_dmov_clk_on(adm); ++ if (status != 0) ++ goto error; ++ } ++ dmov_conf[adm].clk_ctl = CLK_EN; ++ ++ spin_lock_irqsave(&dmov_conf[adm].list_lock, flags); ++ ++ cmd = list_entry(dmov_conf[adm].staged_commands[ch].next, typeof(*cmd), ++ list); ++ list_del(&cmd->list); ++ list_add_tail(&cmd->list, &dmov_conf[adm].ready_commands[ch]); ++ status = readl_relaxed(DMOV_REG(DMOV_STATUS(ch), adm)); ++ if (status & DMOV_STATUS_CMD_PTR_RDY) { ++ PRINT_IO("msm_dmov_enqueue_cmd(%d), start command, status %x\n", ++ id, status); ++ cmd = start_ready_cmd(ch, adm); ++ /* ++ * We added something to the ready list, and still hold the ++ * list lock. Thus, no need to check for cmd == NULL ++ */ ++ if (cmd->toflush) { ++ int flush = (cmd->toflush == GRACEFUL) ? 1 << 31 : 0; ++ writel_relaxed(flush, DMOV_REG(DMOV_FLUSH0(ch), adm)); ++ } ++ } else { ++ cmd->toflush = 0; ++ if (list_empty(&dmov_conf[adm].active_commands[ch]) && ++ !list_empty(&dmov_conf[adm].ready_commands[ch])) ++ PRINT_ERROR("msm_dmov_enqueue_cmd_ext(%d), stalled, " ++ "status %x\n", id, status); ++ PRINT_IO("msm_dmov_enqueue_cmd(%d), enqueue command, status " ++ "%x\n", id, status); ++ } ++ if (!dmov_conf[adm].channel_active) { ++ dmov_conf[adm].clk_ctl = CLK_TO_BE_DIS; ++ schedule_delayed_work(&dmov_conf[adm].work, (HZ/10)); ++ } ++ spin_unlock_irqrestore(&dmov_conf[adm].list_lock, flags); ++error: ++ mutex_unlock(&dmov_conf[adm].lock); ++} ++ ++static void __msm_dmov_enqueue_cmd_ext(unsigned id, struct msm_dmov_cmd *cmd) ++{ ++ int adm = DMOV_ID_TO_ADM(id); ++ int ch = DMOV_ID_TO_CHAN(id); ++ unsigned long flags; ++ cmd->id = id; ++ cmd->toflush = 0; ++ ++ spin_lock_irqsave(&dmov_conf[adm].list_lock, flags); ++ list_add_tail(&cmd->list, &dmov_conf[adm].staged_commands[ch]); ++ spin_unlock_irqrestore(&dmov_conf[adm].list_lock, flags); ++ ++ queue_work(dmov_conf[adm].cmd_wq, &cmd->work); ++} ++ ++void msm_dmov_enqueue_cmd_ext(unsigned id, struct msm_dmov_cmd *cmd) ++{ ++ INIT_WORK(&cmd->work, msm_dmov_enqueue_cmd_ext_work); ++ __msm_dmov_enqueue_cmd_ext(id, cmd); ++} ++EXPORT_SYMBOL(msm_dmov_enqueue_cmd_ext); ++ ++void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd) ++{ ++ /* Disable callback function (for backwards compatibility) */ ++ cmd->exec_func = NULL; ++ INIT_WORK(&cmd->work, msm_dmov_enqueue_cmd_ext_work); ++ __msm_dmov_enqueue_cmd_ext(id, cmd); ++} ++EXPORT_SYMBOL(msm_dmov_enqueue_cmd); ++ ++void msm_dmov_flush(unsigned int id, int graceful) ++{ ++ unsigned long irq_flags; ++ int ch = DMOV_ID_TO_CHAN(id); ++ int adm = DMOV_ID_TO_ADM(id); ++ int flush = graceful ? DMOV_FLUSH_TYPE : 0; ++ struct msm_dmov_cmd *cmd; ++ ++ spin_lock_irqsave(&dmov_conf[adm].list_lock, irq_flags); ++ /* XXX not checking if flush cmd sent already */ ++ if (!list_empty(&dmov_conf[adm].active_commands[ch])) { ++ PRINT_IO("msm_dmov_flush(%d), send flush cmd\n", id); ++ writel_relaxed(flush, DMOV_REG(DMOV_FLUSH0(ch), adm)); ++ } ++ list_for_each_entry(cmd, &dmov_conf[adm].staged_commands[ch], list) ++ cmd->toflush = graceful ? GRACEFUL : NONGRACEFUL; ++ /* spin_unlock_irqrestore has the necessary barrier */ ++ spin_unlock_irqrestore(&dmov_conf[adm].list_lock, irq_flags); ++} ++EXPORT_SYMBOL(msm_dmov_flush); ++ ++struct msm_dmov_exec_cmdptr_cmd { ++ struct msm_dmov_cmd dmov_cmd; ++ struct completion complete; ++ unsigned id; ++ unsigned int result; ++ struct msm_dmov_errdata err; ++}; ++ ++static void ++dmov_exec_cmdptr_complete_func(struct msm_dmov_cmd *_cmd, ++ unsigned int result, ++ struct msm_dmov_errdata *err) ++{ ++ struct msm_dmov_exec_cmdptr_cmd *cmd = container_of(_cmd, struct msm_dmov_exec_cmdptr_cmd, dmov_cmd); ++ cmd->result = result; ++ if (result != 0x80000002 && err) ++ memcpy(&cmd->err, err, sizeof(struct msm_dmov_errdata)); ++ ++ complete(&cmd->complete); ++} ++ ++int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr) ++{ ++ struct msm_dmov_exec_cmdptr_cmd cmd; ++ ++ PRINT_FLOW("dmov_exec_cmdptr(%d, %x)\n", id, cmdptr); ++ ++ cmd.dmov_cmd.cmdptr = cmdptr; ++ cmd.dmov_cmd.complete_func = dmov_exec_cmdptr_complete_func; ++ cmd.dmov_cmd.exec_func = NULL; ++ cmd.id = id; ++ cmd.result = 0; ++ INIT_WORK_ONSTACK(&cmd.dmov_cmd.work, msm_dmov_enqueue_cmd_ext_work); ++ init_completion(&cmd.complete); ++ ++ __msm_dmov_enqueue_cmd_ext(id, &cmd.dmov_cmd); ++ wait_for_completion_timeout(&cmd.complete, msecs_to_jiffies(1000)); ++ ++ if (cmd.result != 0x80000002) { ++ PRINT_ERROR("dmov_exec_cmdptr(%d): ERROR, result: %x\n", id, cmd.result); ++ PRINT_ERROR("dmov_exec_cmdptr(%d): flush: %x %x %x %x\n", ++ id, cmd.err.flush[0], cmd.err.flush[1], cmd.err.flush[2], cmd.err.flush[3]); ++ return -EIO; ++ } ++ PRINT_FLOW("dmov_exec_cmdptr(%d, %x) done\n", id, cmdptr); ++ return 0; ++} ++EXPORT_SYMBOL(msm_dmov_exec_cmd); ++ ++static void fill_errdata(struct msm_dmov_errdata *errdata, int ch, int adm) ++{ ++ errdata->flush[0] = readl_relaxed(DMOV_REG(DMOV_FLUSH0(ch), adm)); ++ errdata->flush[1] = readl_relaxed(DMOV_REG(DMOV_FLUSH1(ch), adm)); ++ errdata->flush[2] = 0; ++ errdata->flush[3] = readl_relaxed(DMOV_REG(DMOV_FLUSH3(ch), adm)); ++ errdata->flush[4] = readl_relaxed(DMOV_REG(DMOV_FLUSH4(ch), adm)); ++ errdata->flush[5] = readl_relaxed(DMOV_REG(DMOV_FLUSH5(ch), adm)); ++} ++ ++static irqreturn_t msm_dmov_isr(int irq, void *dev_id) ++{ ++ unsigned int int_status; ++ unsigned int mask; ++ unsigned int id; ++ unsigned int ch; ++ unsigned long irq_flags; ++ unsigned int ch_status; ++ unsigned int ch_result; ++ unsigned int valid = 0; ++ struct msm_dmov_cmd *cmd; ++ int adm = DMOV_IRQ_TO_ADM(irq); ++ ++ mutex_lock(&dmov_conf[adm].lock); ++ /* read and clear isr */ ++ int_status = readl_relaxed(DMOV_REG(DMOV_ISR, adm)); ++ PRINT_FLOW("msm_datamover_irq_handler: DMOV_ISR %x\n", int_status); ++ ++ spin_lock_irqsave(&dmov_conf[adm].list_lock, irq_flags); ++ while (int_status) { ++ mask = int_status & -int_status; ++ ch = fls(mask) - 1; ++ id = DMOV_CHAN_ADM_TO_ID(ch, adm); ++ PRINT_FLOW("msm_datamover_irq_handler %08x %08x id %d\n", int_status, mask, id); ++ int_status &= ~mask; ++ ch_status = readl_relaxed(DMOV_REG(DMOV_STATUS(ch), adm)); ++ if (!(ch_status & DMOV_STATUS_RSLT_VALID)) { ++ PRINT_FLOW("msm_datamover_irq_handler id %d, " ++ "result not valid %x\n", id, ch_status); ++ continue; ++ } ++ do { ++ valid = 1; ++ ch_result = readl_relaxed(DMOV_REG(DMOV_RSLT(ch), adm)); ++ if (list_empty(&dmov_conf[adm].active_commands[ch])) { ++ PRINT_ERROR("msm_datamover_irq_handler id %d, got result " ++ "with no active command, status %x, result %x\n", ++ id, ch_status, ch_result); ++ cmd = NULL; ++ } else { ++ cmd = list_entry(dmov_conf[adm]. ++ active_commands[ch].next, typeof(*cmd), ++ list); ++ } ++ PRINT_FLOW("msm_datamover_irq_handler id %d, status %x, result %x\n", id, ch_status, ch_result); ++ if (ch_result & DMOV_RSLT_DONE) { ++ PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", ++ id, ch_status); ++ PRINT_IO("msm_datamover_irq_handler id %d, got result " ++ "for %p, result %x\n", id, cmd, ch_result); ++ if (cmd) { ++ list_del(&cmd->list); ++ cmd->complete_func(cmd, ch_result, NULL); ++ } ++ } ++ if (ch_result & DMOV_RSLT_FLUSH) { ++ struct msm_dmov_errdata errdata; ++ ++ fill_errdata(&errdata, ch, adm); ++ PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", id, ch_status); ++ PRINT_FLOW("msm_datamover_irq_handler id %d, flush, result %x, flush0 %x\n", id, ch_result, errdata.flush[0]); ++ if (cmd) { ++ list_del(&cmd->list); ++ cmd->complete_func(cmd, ch_result, &errdata); ++ } ++ } ++ if (ch_result & DMOV_RSLT_ERROR) { ++ struct msm_dmov_errdata errdata; ++ ++ fill_errdata(&errdata, ch, adm); ++ ++ PRINT_ERROR("msm_datamover_irq_handler id %d, status %x\n", id, ch_status); ++ PRINT_ERROR("msm_datamover_irq_handler id %d, error, result %x, flush0 %x\n", id, ch_result, errdata.flush[0]); ++ if (cmd) { ++ list_del(&cmd->list); ++ cmd->complete_func(cmd, ch_result, &errdata); ++ } ++ /* this does not seem to work, once we get an error */ ++ /* the datamover will no longer accept commands */ ++ writel_relaxed(0, DMOV_REG(DMOV_FLUSH0(ch), ++ adm)); ++ } ++ rmb(); ++ ch_status = readl_relaxed(DMOV_REG(DMOV_STATUS(ch), ++ adm)); ++ PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", id, ch_status); ++ if (ch_status & DMOV_STATUS_CMD_PTR_RDY) ++ start_ready_cmd(ch, adm); ++ } while (ch_status & DMOV_STATUS_RSLT_VALID); ++ if (list_empty(&dmov_conf[adm].active_commands[ch]) && ++ list_empty(&dmov_conf[adm].ready_commands[ch])) ++ dmov_conf[adm].channel_active &= ~(1U << ch); ++ PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", id, ch_status); ++ } ++ spin_unlock_irqrestore(&dmov_conf[adm].list_lock, irq_flags); ++ ++ if (!dmov_conf[adm].channel_active && valid) { ++ disable_irq_nosync(dmov_conf[adm].irq); ++ dmov_conf[adm].clk_ctl = CLK_TO_BE_DIS; ++ schedule_delayed_work(&dmov_conf[adm].work, (HZ/10)); ++ } ++ ++ mutex_unlock(&dmov_conf[adm].lock); ++ ++ return valid ? IRQ_HANDLED : IRQ_NONE; ++} ++ ++static int msm_dmov_suspend_late(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ int adm = (pdev->id >= 0) ? pdev->id : 0; ++ mutex_lock(&dmov_conf[adm].lock); ++ if (dmov_conf[adm].clk_ctl == CLK_TO_BE_DIS) { ++ BUG_ON(dmov_conf[adm].channel_active); ++ msm_dmov_clk_off(adm); ++ dmov_conf[adm].clk_ctl = CLK_DIS; ++ } ++ mutex_unlock(&dmov_conf[adm].lock); ++ return 0; ++} ++ ++static int msm_dmov_runtime_suspend(struct device *dev) ++{ ++ dev_dbg(dev, "pm_runtime: suspending...\n"); ++ return 0; ++} ++ ++static int msm_dmov_runtime_resume(struct device *dev) ++{ ++ dev_dbg(dev, "pm_runtime: resuming...\n"); ++ return 0; ++} ++ ++static int msm_dmov_runtime_idle(struct device *dev) ++{ ++ dev_dbg(dev, "pm_runtime: idling...\n"); ++ return 0; ++} ++ ++static struct dev_pm_ops msm_dmov_dev_pm_ops = { ++ .runtime_suspend = msm_dmov_runtime_suspend, ++ .runtime_resume = msm_dmov_runtime_resume, ++ .runtime_idle = msm_dmov_runtime_idle, ++ .suspend = msm_dmov_suspend_late, ++}; ++ ++static int msm_dmov_init_clocks(struct platform_device *pdev) ++{ ++ int adm = (pdev->id >= 0) ? pdev->id : 0; ++ int ret; ++ ++ dmov_conf[adm].clk = devm_clk_get(&pdev->dev, "core_clk"); ++ if (IS_ERR(dmov_conf[adm].clk)) { ++ printk(KERN_ERR "%s: Error getting adm_clk\n", __func__); ++ dmov_conf[adm].clk = NULL; ++ return -ENOENT; ++ } ++ ++ dmov_conf[adm].pclk = devm_clk_get(&pdev->dev, "iface_clk"); ++ if (IS_ERR(dmov_conf[adm].pclk)) { ++ dmov_conf[adm].pclk = NULL; ++ /* pclk not present on all SoCs, don't bail on failure */ ++ } ++ ++ dmov_conf[adm].ebiclk = devm_clk_get(&pdev->dev, "mem_clk"); ++ if (IS_ERR(dmov_conf[adm].ebiclk)) { ++ dmov_conf[adm].ebiclk = NULL; ++ /* ebiclk not present on all SoCs, don't bail on failure */ ++ } else { ++ ret = clk_set_rate(dmov_conf[adm].ebiclk, 27000000); ++ if (ret) ++ return -ENOENT; ++ } ++ ++ return 0; ++} ++ ++static void config_datamover(int adm) ++{ ++ int i; ++ ++ /* Reset the ADM */ ++ reset_control_assert(dmov_conf[adm].adm_reset); ++ reset_control_assert(dmov_conf[adm].c0_reset); ++ reset_control_assert(dmov_conf[adm].c1_reset); ++ reset_control_assert(dmov_conf[adm].c2_reset); ++ ++ reset_control_deassert(dmov_conf[adm].c2_reset); ++ reset_control_deassert(dmov_conf[adm].c1_reset); ++ reset_control_deassert(dmov_conf[adm].c0_reset); ++ reset_control_deassert(dmov_conf[adm].adm_reset); ++ ++ for (i = 0; i < MSM_DMOV_CHANNEL_COUNT; i++) { ++ struct msm_dmov_chan_conf *chan_conf = ++ dmov_conf[adm].chan_conf; ++ unsigned conf; ++ /* Only configure scorpion channels */ ++ if (chan_conf[i].sd <= 1) { ++ conf = readl_relaxed(DMOV_REG(DMOV_CONF(i), adm)); ++ conf |= DMOV_CONF_MPU_DISABLE | ++ DMOV_CONF_PERM_MPU_CONF | ++ DMOV_CONF_FLUSH_RSLT_EN | ++ DMOV_CONF_FORCE_RSLT_EN | ++ DMOV_CONF_IRQ_EN | ++ DMOV_CONF_PRIORITY(chan_conf[i].priority); ++ ++ conf &= ~DMOV_CONF_SD(7); ++ conf |= DMOV_CONF_SD(chan_conf[i].sd); ++ writel_relaxed(conf, DMOV_REG(DMOV_CONF(i), adm)); ++ } ++ } ++ ++ for (i = 0; i < MSM_DMOV_CRCI_COUNT; i++) { ++ writel_relaxed(DMOV_CRCI_CTL_RST, ++ DMOV_REG(DMOV_CRCI_CTL(i), adm)); ++ } ++ ++ /* NAND CRCI Enable */ ++ writel_relaxed(0, DMOV_REG(DMOV_CRCI_CTL(DMOV_NAND_CRCI_DATA), adm)); ++ writel_relaxed(0, DMOV_REG(DMOV_CRCI_CTL(DMOV_NAND_CRCI_CMD), adm)); ++ ++ /* GSBI5 CRCI Enable */ ++ writel_relaxed(0, DMOV_REG(DMOV_CRCI_CTL(DMOV_SPI_GSBI5_RX_CRCI), adm)); ++ writel_relaxed(0, DMOV_REG(DMOV_CRCI_CTL(DMOV_SPI_GSBI5_TX_CRCI), adm)); ++ ++ writel_relaxed(DMOV_CI_CONF_RANGE_START(0x40) | /* EBI1 */ ++ DMOV_CI_CONF_RANGE_END(0xb0) | ++ DMOV_CI_CONF_MAX_BURST(0x8), ++ DMOV_REG(DMOV_CI_CONF(0), adm)); ++ ++ writel_relaxed(DMOV_CI_CONF_RANGE_START(0x2a) | /* IMEM */ ++ DMOV_CI_CONF_RANGE_END(0x2c) | ++ DMOV_CI_CONF_MAX_BURST(0x8), ++ DMOV_REG(DMOV_CI_CONF(1), adm)); ++ ++ writel_relaxed(DMOV_CI_CONF_RANGE_START(0x12) | /* CPSS/SPS */ ++ DMOV_CI_CONF_RANGE_END(0x28) | ++ DMOV_CI_CONF_MAX_BURST(0x8), ++ DMOV_REG(DMOV_CI_CONF(2), adm)); ++ ++ writel_relaxed(DMOV_HI_GP_CTL_CORE_CLK_LP_EN | /* will disable LP */ ++ DMOV_HI_GP_CTL_LP_CNT(0xf), ++ DMOV_REG(DMOV_HI_GP_CTL, adm)); ++ ++} ++ ++static int msm_dmov_probe(struct platform_device *pdev) ++{ ++ ++ int adm = (pdev->id >= 0) ? pdev->id : 0; ++ int i; ++ int ret; ++ struct resource *irqres = ++ platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ struct resource *mres = ++ platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ dmov_conf[adm].sd=0; ++ dmov_conf[adm].sd_size=0x800; ++ ++ dmov_conf[adm].irq = irqres->start; ++ ++ dmov_conf[adm].base = devm_ioremap_resource(&pdev->dev, mres); ++ if (!dmov_conf[adm].base) ++ return -ENOMEM; ++ ++ dmov_conf[adm].cmd_wq = alloc_ordered_workqueue("dmov%d_wq", 0, adm); ++ if (!dmov_conf[adm].cmd_wq) { ++ PRINT_ERROR("Couldn't allocate ADM%d workqueue.\n", adm); ++ return -ENOMEM; ++ } ++ ++ /* get resets */ ++ dmov_conf[adm].adm_reset = devm_reset_control_get(&pdev->dev, "adm"); ++ if (IS_ERR(dmov_conf[adm].adm_reset)) { ++ dev_err(&pdev->dev, "failed to get adm reset\n"); ++ ret = PTR_ERR(dmov_conf[adm].adm_reset); ++ goto out_wq; ++ } ++ ++ dmov_conf[adm].pbus_reset = devm_reset_control_get(&pdev->dev, "pbus"); ++ if (IS_ERR(dmov_conf[adm].pbus_reset)) { ++ dev_err(&pdev->dev, "failed to get pbus reset\n"); ++ ret = PTR_ERR(dmov_conf[adm].pbus_reset); ++ goto out_wq; ++ } ++ ++ dmov_conf[adm].c0_reset = devm_reset_control_get(&pdev->dev, "c0"); ++ if (IS_ERR(dmov_conf[adm].c0_reset)) { ++ dev_err(&pdev->dev, "failed to get c0 reset\n"); ++ ret = PTR_ERR(dmov_conf[adm].c0_reset); ++ goto out_wq; ++ } ++ ++ dmov_conf[adm].c1_reset = devm_reset_control_get(&pdev->dev, "c1"); ++ if (IS_ERR(dmov_conf[adm].c1_reset)) { ++ dev_err(&pdev->dev, "failed to get c1 reset\n"); ++ ret = PTR_ERR(dmov_conf[adm].c1_reset); ++ goto out_wq; ++ } ++ ++ dmov_conf[adm].c2_reset = devm_reset_control_get(&pdev->dev, "c2"); ++ if (IS_ERR(dmov_conf[adm].c2_reset)) { ++ dev_err(&pdev->dev, "failed to get c2 reset\n"); ++ ret = PTR_ERR(dmov_conf[adm].c2_reset); ++ goto out_wq; ++ } ++ ++ ret = devm_request_threaded_irq(&pdev->dev, dmov_conf[adm].irq, NULL, ++ msm_dmov_isr, IRQF_ONESHOT, "msmdatamover", NULL); ++ ++ if (ret) { ++ PRINT_ERROR("Requesting ADM%d irq %d failed\n", adm, ++ dmov_conf[adm].irq); ++ goto out_wq; ++ } ++ ++ disable_irq(dmov_conf[adm].irq); ++ ret = msm_dmov_init_clocks(pdev); ++ if (ret) { ++ PRINT_ERROR("Requesting ADM%d clocks failed\n", adm); ++ goto out_wq; ++ } ++ clk_prepare_enable(dmov_conf[adm].clk); ++ clk_prepare_enable(dmov_conf[adm].pclk); ++ ++// ret = msm_dmov_clk_on(adm); ++// if (ret) { ++// PRINT_ERROR("Enabling ADM%d clocks failed\n", adm); ++// goto out_wq; ++// } ++ ++ config_datamover(adm); ++ for (i = 0; i < MSM_DMOV_CHANNEL_COUNT; i++) { ++ INIT_LIST_HEAD(&dmov_conf[adm].staged_commands[i]); ++ INIT_LIST_HEAD(&dmov_conf[adm].ready_commands[i]); ++ INIT_LIST_HEAD(&dmov_conf[adm].active_commands[i]); ++ ++ writel_relaxed(DMOV_RSLT_CONF_IRQ_EN ++ | DMOV_RSLT_CONF_FORCE_FLUSH_RSLT, ++ DMOV_REG(DMOV_RSLT_CONF(i), adm)); ++ } ++ wmb(); ++// msm_dmov_clk_off(adm); ++ return ret; ++out_wq: ++ destroy_workqueue(dmov_conf[adm].cmd_wq); ++ return ret; ++} ++ ++#ifdef CONFIG_OF ++static const struct of_device_id adm_of_match[] = { ++ { .compatible = "qcom,adm", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, adm_of_match); ++#endif ++ ++static struct platform_driver msm_dmov_driver = { ++ .probe = msm_dmov_probe, ++ .driver = { ++ .name = MODULE_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = adm_of_match, ++ .pm = &msm_dmov_dev_pm_ops, ++ }, ++}; ++ ++/* static int __init */ ++static int __init msm_init_datamover(void) ++{ ++ int ret; ++ ret = platform_driver_register(&msm_dmov_driver); ++ if (ret) ++ return ret; ++ return 0; ++} ++arch_initcall(msm_init_datamover); +diff --git a/drivers/mtd/nand/qcom_adm_dma.h b/drivers/mtd/nand/qcom_adm_dma.h +new file mode 100644 +index 0000000..1014d57 +--- /dev/null ++++ b/drivers/mtd/nand/qcom_adm_dma.h +@@ -0,0 +1,268 @@ ++/* * Copyright (c) 2012 The Linux Foundation. All rights reserved.* */ ++/* linux/include/asm-arm/arch-msm/dma.h ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef __ASM_ARCH_MSM_DMA_H ++#define __ASM_ARCH_MSM_DMA_H ++#include <linux/list.h> ++ ++struct msm_dmov_errdata { ++ uint32_t flush[6]; ++}; ++ ++struct msm_dmov_cmd { ++ struct list_head list; ++ unsigned int cmdptr; ++ void (*complete_func)(struct msm_dmov_cmd *cmd, ++ unsigned int result, ++ struct msm_dmov_errdata *err); ++ void (*exec_func)(struct msm_dmov_cmd *cmd); ++ struct work_struct work; ++ unsigned id; /* For internal use */ ++ void *user; /* Pointer for caller's reference */ ++ u8 toflush; ++}; ++ ++struct msm_dmov_pdata { ++ int sd; ++ size_t sd_size; ++}; ++ ++void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd); ++void msm_dmov_enqueue_cmd_ext(unsigned id, struct msm_dmov_cmd *cmd); ++void msm_dmov_flush(unsigned int id, int graceful); ++int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr); ++ ++#define DMOV_CRCIS_PER_CONF 10 ++ ++#define DMOV_ADDR(off, ch) ((off) + ((ch) << 2)) ++ ++#define DMOV_CMD_PTR(ch) DMOV_ADDR(0x000, ch) ++#define DMOV_CMD_LIST (0 << 29) /* does not work */ ++#define DMOV_CMD_PTR_LIST (1 << 29) /* works */ ++#define DMOV_CMD_INPUT_CFG (2 << 29) /* untested */ ++#define DMOV_CMD_OUTPUT_CFG (3 << 29) /* untested */ ++#define DMOV_CMD_ADDR(addr) ((addr) >> 3) ++ ++#define DMOV_RSLT(ch) DMOV_ADDR(0x040, ch) ++#define DMOV_RSLT_VALID (1 << 31) /* 0 == host has empties result fifo */ ++#define DMOV_RSLT_ERROR (1 << 3) ++#define DMOV_RSLT_FLUSH (1 << 2) ++#define DMOV_RSLT_DONE (1 << 1) /* top pointer done */ ++#define DMOV_RSLT_USER (1 << 0) /* command with FR force result */ ++ ++#define DMOV_FLUSH0(ch) DMOV_ADDR(0x080, ch) ++#define DMOV_FLUSH1(ch) DMOV_ADDR(0x0C0, ch) ++#define DMOV_FLUSH2(ch) DMOV_ADDR(0x100, ch) ++#define DMOV_FLUSH3(ch) DMOV_ADDR(0x140, ch) ++#define DMOV_FLUSH4(ch) DMOV_ADDR(0x180, ch) ++#define DMOV_FLUSH5(ch) DMOV_ADDR(0x1C0, ch) ++#define DMOV_FLUSH_TYPE (1 << 31) ++ ++#define DMOV_STATUS(ch) DMOV_ADDR(0x200, ch) ++#define DMOV_STATUS_RSLT_COUNT(n) (((n) >> 29)) ++#define DMOV_STATUS_CMD_COUNT(n) (((n) >> 27) & 3) ++#define DMOV_STATUS_RSLT_VALID (1 << 1) ++#define DMOV_STATUS_CMD_PTR_RDY (1 << 0) ++ ++#define DMOV_CONF(ch) DMOV_ADDR(0x240, ch) ++#define DMOV_CONF_SD(sd) (((sd & 4) << 11) | ((sd & 3) << 4)) ++#define DMOV_CONF_OTHER_CH_BLK_MASK(m) ((m << 0x10) & 0xffff0000) ++#define DMOV_CONF_SHADOW_EN (1 << 12) ++#define DMOV_CONF_MPU_DISABLE (1 << 11) ++#define DMOV_CONF_PERM_MPU_CONF (1 << 9) ++#define DMOV_CONF_FLUSH_RSLT_EN (1 << 8) ++#define DMOV_CONF_IRQ_EN (1 << 6) ++#define DMOV_CONF_FORCE_RSLT_EN (1 << 7) ++#define DMOV_CONF_PRIORITY(n) (n << 0) ++ ++#define DMOV_DBG_ERR(ci) DMOV_ADDR(0x280, ci) ++ ++#define DMOV_RSLT_CONF(ch) DMOV_ADDR(0x300, ch) ++#define DMOV_RSLT_CONF_FORCE_TOP_PTR_RSLT (1 << 2) ++#define DMOV_RSLT_CONF_FORCE_FLUSH_RSLT (1 << 1) ++#define DMOV_RSLT_CONF_IRQ_EN (1 << 0) ++ ++#define DMOV_ISR DMOV_ADDR(0x380, 0) ++ ++#define DMOV_CI_CONF(ci) DMOV_ADDR(0x390, ci) ++#define DMOV_CI_CONF_RANGE_END(n) ((n) << 24) ++#define DMOV_CI_CONF_RANGE_START(n) ((n) << 16) ++#define DMOV_CI_CONF_MAX_BURST(n) ((n) << 0) ++ ++#define DMOV_CI_DBG_ERR(ci) DMOV_ADDR(0x3B0, ci) ++ ++#define DMOV_CRCI_CONF0 DMOV_ADDR(0x3D0, 0) ++#define DMOV_CRCI_CONF0_CRCI9_SD (2 << 0x1b) ++ ++#define DMOV_CRCI_CONF1 DMOV_ADDR(0x3D4, 0) ++#define DMOV_CRCI_CONF0_SD(crci, sd) (sd << (crci*3)) ++#define DMOV_CRCI_CONF1_SD(crci, sd) (sd << ((crci-DMOV_CRCIS_PER_CONF)*3)) ++ ++#define DMOV_HI_GP_CTL DMOV_ADDR(0x3D8, 0) ++#define DMOV_HI_GP_CTL_CORE_CLK_LP_EN (1 << 12) ++#define DMOV_HI_GP_CTL_LP_CNT(x) (((x) & 0xf) << 8) ++#define DMOV_HI_GP_CTL_CI3_CLK_LP_EN (1 << 7) ++#define DMOV_HI_GP_CTL_CI2_CLK_LP_EN (1 << 6) ++#define DMOV_HI_GP_CTL_CI1_CLK_LP_EN (1 << 5) ++#define DMOV_HI_GP_CTL_CI0_CLK_LP_EN (1 << 4) ++ ++#define DMOV_CRCI_CTL(crci) DMOV_ADDR(0x400, crci) ++#define DMOV_CRCI_CTL_BLK_SZ(n) ((n) << 0) ++#define DMOV_CRCI_CTL_RST (1 << 17) ++#define DMOV_CRCI_MUX (1 << 18) ++ ++/* channel assignments */ ++ ++/* ++ * Format of CRCI numbers: crci number + (muxsel << 4) ++ */ ++ ++#define DMOV_GP_CHAN 9 ++ ++#define DMOV_CE_IN_CHAN 0 ++#define DMOV_CE_IN_CRCI 2 ++ ++#define DMOV_CE_OUT_CHAN 1 ++#define DMOV_CE_OUT_CRCI 3 ++ ++#define DMOV_TSIF_CHAN 2 ++#define DMOV_TSIF_CRCI 11 ++ ++#define DMOV_HSUART_GSBI6_TX_CHAN 7 ++#define DMOV_HSUART_GSBI6_TX_CRCI 6 ++ ++#define DMOV_HSUART_GSBI6_RX_CHAN 8 ++#define DMOV_HSUART_GSBI6_RX_CRCI 11 ++ ++#define DMOV_HSUART_GSBI8_TX_CHAN 7 ++#define DMOV_HSUART_GSBI8_TX_CRCI 10 ++ ++#define DMOV_HSUART_GSBI8_RX_CHAN 8 ++#define DMOV_HSUART_GSBI8_RX_CRCI 9 ++ ++#define DMOV_HSUART_GSBI9_TX_CHAN 4 ++#define DMOV_HSUART_GSBI9_TX_CRCI 13 ++ ++#define DMOV_HSUART_GSBI9_RX_CHAN 3 ++#define DMOV_HSUART_GSBI9_RX_CRCI 12 ++ ++#define DMOV_NAND_CHAN 3 ++#define DMOV_NAND_CRCI_CMD 15 ++#define DMOV_NAND_CRCI_DATA 3 ++ ++#define DMOV_SPI_GSBI5_RX_CRCI 9 ++#define DMOV_SPI_GSBI5_TX_CRCI 10 ++#define DMOV_SPI_GSBI5_RX_CHAN 6 ++#define DMOV_SPI_GSBI5_TX_CHAN 5 ++ ++/* channels for APQ8064 */ ++#define DMOV8064_CE_IN_CHAN 0 ++#define DMOV8064_CE_IN_CRCI 14 ++ ++#define DMOV8064_CE_OUT_CHAN 1 ++#define DMOV8064_CE_OUT_CRCI 15 ++ ++#define DMOV8064_TSIF_CHAN 2 ++#define DMOV8064_TSIF_CRCI 1 ++ ++/* channels for APQ8064 SGLTE*/ ++#define DMOV_APQ8064_HSUART_GSBI4_TX_CHAN 11 ++#define DMOV_APQ8064_HSUART_GSBI4_TX_CRCI 8 ++ ++#define DMOV_APQ8064_HSUART_GSBI4_RX_CHAN 10 ++#define DMOV_APQ8064_HSUART_GSBI4_RX_CRCI 7 ++ ++/* channels for MPQ8064 */ ++#define DMOV_MPQ8064_HSUART_GSBI6_TX_CHAN 7 ++#define DMOV_MPQ8064_HSUART_GSBI6_TX_CRCI 6 ++ ++#define DMOV_MPQ8064_HSUART_GSBI6_RX_CHAN 6 ++#define DMOV_MPQ8064_HSUART_GSBI6_RX_CRCI 11 ++ ++#define DMOV_IPQ806X_HSUART_GSBI6_TX_CHAN DMOV_MPQ8064_HSUART_GSBI6_TX_CHAN ++#define DMOV_IPQ806X_HSUART_GSBI6_TX_CRCI DMOV_MPQ8064_HSUART_GSBI6_TX_CRCI ++ ++#define DMOV_IPQ806X_HSUART_GSBI6_RX_CHAN DMOV_MPQ8064_HSUART_GSBI6_RX_CHAN ++#define DMOV_IPQ806X_HSUART_GSBI6_RX_CRCI DMOV_MPQ8064_HSUART_GSBI6_RX_CRCI ++ ++/* no client rate control ifc (eg, ram) */ ++#define DMOV_NONE_CRCI 0 ++ ++ ++/* If the CMD_PTR register has CMD_PTR_LIST selected, the data mover ++ * is going to walk a list of 32bit pointers as described below. Each ++ * pointer points to a *array* of dmov_s, etc structs. The last pointer ++ * in the list is marked with CMD_PTR_LP. The last struct in each array ++ * is marked with CMD_LC (see below). ++ */ ++#define CMD_PTR_ADDR(addr) ((addr) >> 3) ++#define CMD_PTR_LP (1 << 31) /* last pointer */ ++#define CMD_PTR_PT (3 << 29) /* ? */ ++ ++/* Single Item Mode */ ++typedef struct { ++ unsigned cmd; ++ unsigned src; ++ unsigned dst; ++ unsigned len; ++} dmov_s; ++ ++/* Scatter/Gather Mode */ ++typedef struct { ++ unsigned cmd; ++ unsigned src_dscr; ++ unsigned dst_dscr; ++ unsigned _reserved; ++} dmov_sg; ++ ++/* Box mode */ ++typedef struct { ++ uint32_t cmd; ++ uint32_t src_row_addr; ++ uint32_t dst_row_addr; ++ uint32_t src_dst_len; ++ uint32_t num_rows; ++ uint32_t row_offset; ++} dmov_box; ++ ++/* bits for the cmd field of the above structures */ ++ ++#define CMD_LC (1 << 31) /* last command */ ++#define CMD_FR (1 << 22) /* force result -- does not work? */ ++#define CMD_OCU (1 << 21) /* other channel unblock */ ++#define CMD_OCB (1 << 20) /* other channel block */ ++#define CMD_TCB (1 << 19) /* ? */ ++#define CMD_DAH (1 << 18) /* destination address hold -- does not work?*/ ++#define CMD_SAH (1 << 17) /* source address hold -- does not work? */ ++ ++#define CMD_MODE_SINGLE (0 << 0) /* dmov_s structure used */ ++#define CMD_MODE_SG (1 << 0) /* untested */ ++#define CMD_MODE_IND_SG (2 << 0) /* untested */ ++#define CMD_MODE_BOX (3 << 0) /* untested */ ++ ++#define CMD_DST_SWAP_BYTES (1 << 14) /* exchange each byte n with byte n+1 */ ++#define CMD_DST_SWAP_SHORTS (1 << 15) /* exchange each short n with short n+1 */ ++#define CMD_DST_SWAP_WORDS (1 << 16) /* exchange each word n with word n+1 */ ++ ++#define CMD_SRC_SWAP_BYTES (1 << 11) /* exchange each byte n with byte n+1 */ ++#define CMD_SRC_SWAP_SHORTS (1 << 12) /* exchange each short n with short n+1 */ ++#define CMD_SRC_SWAP_WORDS (1 << 13) /* exchange each word n with word n+1 */ ++ ++#define CMD_DST_CRCI(n) (((n) & 15) << 7) ++#define CMD_SRC_CRCI(n) (((n) & 15) << 3) ++ ++#endif +diff --git a/drivers/mtd/nand/qcom_nand.c b/drivers/mtd/nand/qcom_nand.c +new file mode 100644 +index 0000000..9314132 +--- /dev/null ++++ b/drivers/mtd/nand/qcom_nand.c +@@ -0,0 +1,7455 @@ ++/* ++ * Copyright (C) 2007 Google, Inc. ++ * Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include <linux/slab.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/nand.h> ++#include <linux/mtd/partitions.h> ++#include <linux/platform_device.h> ++#include <linux/sched.h> ++#include <linux/dma-mapping.h> ++#include <linux/io.h> ++#include <linux/crc16.h> ++#include <linux/bitrev.h> ++#include <linux/clk.h> ++ ++#include <asm/dma.h> ++#include <asm/mach/flash.h> ++ ++#include "qcom_adm_dma.h" ++ ++#include "qcom_nand.h" ++unsigned long msm_nand_phys = 0; ++unsigned long msm_nandc01_phys = 0; ++unsigned long msm_nandc10_phys = 0; ++unsigned long msm_nandc11_phys = 0; ++unsigned long ebi2_register_base = 0; ++static uint32_t dual_nand_ctlr_present; ++static uint32_t interleave_enable; ++static uint32_t enable_bch_ecc; ++static uint32_t boot_layout; ++ ++ ++#define MSM_NAND_DMA_BUFFER_SIZE SZ_8K ++#define MSM_NAND_DMA_BUFFER_SLOTS \ ++ (MSM_NAND_DMA_BUFFER_SIZE / (sizeof(((atomic_t *)0)->counter) * 8)) ++ ++#define MSM_NAND_CFG0_RAW_ONFI_IDENTIFIER 0x88000800 ++#define MSM_NAND_CFG0_RAW_ONFI_PARAM_INFO 0x88040000 ++#define MSM_NAND_CFG1_RAW_ONFI_IDENTIFIER 0x0005045d ++#define MSM_NAND_CFG1_RAW_ONFI_PARAM_INFO 0x0005045d ++ ++#define ONFI_IDENTIFIER_LENGTH 0x0004 ++#define ONFI_PARAM_INFO_LENGTH 0x0200 ++#define ONFI_PARAM_PAGE_LENGTH 0x0100 ++ ++#define ONFI_PARAMETER_PAGE_SIGNATURE 0x49464E4F ++ ++#define FLASH_READ_ONFI_IDENTIFIER_COMMAND 0x90 ++#define FLASH_READ_ONFI_IDENTIFIER_ADDRESS 0x20 ++#define FLASH_READ_ONFI_PARAMETERS_COMMAND 0xEC ++#define FLASH_READ_ONFI_PARAMETERS_ADDRESS 0x00 ++ ++#define UD_SIZE_BYTES_MASK (0x3FF << 9) ++#define SPARE_SIZE_BYTES_MASK (0xF << 23) ++#define ECC_NUM_DATA_BYTES_MASK (0x3FF << 16) ++ ++#define VERBOSE 0 ++ ++struct msm_nand_chip { ++ struct device *dev; ++ wait_queue_head_t wait_queue; ++ atomic_t dma_buffer_busy; ++ unsigned dma_channel; ++ uint8_t *dma_buffer; ++ dma_addr_t dma_addr; ++ unsigned CFG0, CFG1, CFG0_RAW, CFG1_RAW; ++ uint32_t ecc_buf_cfg; ++ uint32_t ecc_bch_cfg; ++ uint32_t ecc_parity_bytes; ++ unsigned cw_size; ++ unsigned int uncorrectable_bit_mask; ++ unsigned int num_err_mask; ++}; ++ ++#define CFG1_WIDE_FLASH (1U << 1) ++ ++/* TODO: move datamover code out */ ++ ++#define SRC_CRCI_NAND_CMD CMD_SRC_CRCI(DMOV_NAND_CRCI_CMD) ++#define DST_CRCI_NAND_CMD CMD_DST_CRCI(DMOV_NAND_CRCI_CMD) ++#define SRC_CRCI_NAND_DATA CMD_SRC_CRCI(DMOV_NAND_CRCI_DATA) ++#define DST_CRCI_NAND_DATA CMD_DST_CRCI(DMOV_NAND_CRCI_DATA) ++ ++#define msm_virt_to_dma(chip, vaddr) \ ++ ((chip)->dma_addr + \ ++ ((uint8_t *)(vaddr) - (chip)->dma_buffer)) ++ ++/** ++ * msm_nand_oob_64 - oob info for 2KB page ++ */ ++static struct nand_ecclayout msm_nand_oob_64 = { ++ .eccbytes = 40, ++ .eccpos = { ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ++ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ++ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, ++ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, ++ }, ++ .oobavail = 16, ++ .oobfree = { ++ {30, 16}, ++ } ++}; ++ ++/** ++ * msm_nand_oob_128 - oob info for 4KB page ++ */ ++static struct nand_ecclayout msm_nand_oob_128 = { ++ .eccbytes = 80, ++ .eccpos = { ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ++ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ++ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, ++ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, ++ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ++ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, ++ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, ++ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, ++ }, ++ .oobavail = 32, ++ .oobfree = { ++ {70, 32}, ++ } ++}; ++ ++/** ++ * msm_nand_oob_224 - oob info for 4KB page 8Bit interface ++ */ ++static struct nand_ecclayout msm_nand_oob_224_x8 = { ++ .eccbytes = 104, ++ .eccpos = { ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, ++ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, ++ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, ++ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, ++ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, ++ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, ++ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, ++ 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, ++ }, ++ .oobavail = 32, ++ .oobfree = { ++ {91, 32}, ++ } ++}; ++ ++/** ++ * msm_nand_oob_224 - oob info for 4KB page 16Bit interface ++ */ ++static struct nand_ecclayout msm_nand_oob_224_x16 = { ++ .eccbytes = 112, ++ .eccpos = { ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, ++ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, ++ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, ++ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, ++ 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, ++ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, ++ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, ++ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, ++ }, ++ .oobavail = 32, ++ .oobfree = { ++ {98, 32}, ++ } ++}; ++ ++/** ++ * msm_nand_oob_256 - oob info for 8KB page ++ */ ++static struct nand_ecclayout msm_nand_oob_256 = { ++ .eccbytes = 160, ++ .eccpos = { ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ++ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ++ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, ++ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, ++ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ++ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, ++ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, ++ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, ++ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, ++ 90, 91, 92, 93, 94, 96, 97, 98 , 99, 100, ++ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, ++ 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, ++ 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, ++ 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, ++ 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, ++ 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, ++ }, ++ .oobavail = 64, ++ .oobfree = { ++ {151, 64}, ++ } ++}; ++ ++/** ++ * msm_onenand_oob_64 - oob info for large (2KB) page ++ */ ++static struct nand_ecclayout msm_onenand_oob_64 = { ++ .eccbytes = 20, ++ .eccpos = { ++ 8, 9, 10, 11, 12, ++ 24, 25, 26, 27, 28, ++ 40, 41, 42, 43, 44, ++ 56, 57, 58, 59, 60, ++ }, ++ .oobavail = 20, ++ .oobfree = { ++ {2, 3}, {14, 2}, {18, 3}, {30, 2}, ++ {34, 3}, {46, 2}, {50, 3}, {62, 2} ++ } ++}; ++ ++static void *msm_nand_get_dma_buffer(struct msm_nand_chip *chip, size_t size) ++{ ++ unsigned int bitmask, free_bitmask, old_bitmask; ++ unsigned int need_mask, current_need_mask; ++ int free_index; ++ ++ need_mask = (1UL << DIV_ROUND_UP(size, MSM_NAND_DMA_BUFFER_SLOTS)) - 1; ++ bitmask = atomic_read(&chip->dma_buffer_busy); ++ free_bitmask = ~bitmask; ++ while (free_bitmask) { ++ free_index = __ffs(free_bitmask); ++ current_need_mask = need_mask << free_index; ++ ++ if (size + free_index * MSM_NAND_DMA_BUFFER_SLOTS >= ++ MSM_NAND_DMA_BUFFER_SIZE) ++ return NULL; ++ ++ if ((bitmask & current_need_mask) == 0) { ++ old_bitmask = ++ atomic_cmpxchg(&chip->dma_buffer_busy, ++ bitmask, ++ bitmask | current_need_mask); ++ if (old_bitmask == bitmask) ++ return chip->dma_buffer + ++ free_index * MSM_NAND_DMA_BUFFER_SLOTS; ++ free_bitmask = 0; /* force return */ ++ } ++ /* current free range was too small, clear all free bits */ ++ /* below the top busy bit within current_need_mask */ ++ free_bitmask &= ++ ~(~0U >> (32 - fls(bitmask & current_need_mask))); ++ } ++ ++ return NULL; ++} ++ ++static void msm_nand_release_dma_buffer(struct msm_nand_chip *chip, ++ void *buffer, size_t size) ++{ ++ int index; ++ unsigned int used_mask; ++ ++ used_mask = (1UL << DIV_ROUND_UP(size, MSM_NAND_DMA_BUFFER_SLOTS)) - 1; ++ index = ((uint8_t *)buffer - chip->dma_buffer) / ++ MSM_NAND_DMA_BUFFER_SLOTS; ++ atomic_sub(used_mask << index, &chip->dma_buffer_busy); ++ ++ wake_up(&chip->wait_queue); ++} ++ ++ ++unsigned flash_rd_reg(struct msm_nand_chip *chip, unsigned addr) ++{ ++ struct { ++ dmov_s cmd; ++ unsigned cmdptr; ++ unsigned data; ++ } *dma_buffer; ++ unsigned rv; ++ ++ wait_event(chip->wait_queue, ++ (dma_buffer = msm_nand_get_dma_buffer( ++ chip, sizeof(*dma_buffer)))); ++ ++ dma_buffer->cmd.cmd = CMD_LC | CMD_OCB | CMD_OCU; ++ dma_buffer->cmd.src = addr; ++ dma_buffer->cmd.dst = msm_virt_to_dma(chip, &dma_buffer->data); ++ dma_buffer->cmd.len = 4; ++ ++ dma_buffer->cmdptr = ++ (msm_virt_to_dma(chip, &dma_buffer->cmd) >> 3) | CMD_PTR_LP; ++ dma_buffer->data = 0xeeeeeeee; ++ ++ mb(); ++ msm_dmov_exec_cmd( ++ chip->dma_channel, DMOV_CMD_PTR_LIST | ++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); ++ mb(); ++ ++ rv = dma_buffer->data; ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++ return rv; ++} ++ ++void flash_wr_reg(struct msm_nand_chip *chip, unsigned addr, unsigned val) ++{ ++ struct { ++ dmov_s cmd; ++ unsigned cmdptr; ++ unsigned data; ++ } *dma_buffer; ++ ++ wait_event(chip->wait_queue, ++ (dma_buffer = msm_nand_get_dma_buffer( ++ chip, sizeof(*dma_buffer)))); ++ ++ dma_buffer->cmd.cmd = CMD_LC | CMD_OCB | CMD_OCU; ++ dma_buffer->cmd.src = msm_virt_to_dma(chip, &dma_buffer->data); ++ dma_buffer->cmd.dst = addr; ++ dma_buffer->cmd.len = 4; ++ ++ dma_buffer->cmdptr = ++ (msm_virt_to_dma(chip, &dma_buffer->cmd) >> 3) | CMD_PTR_LP; ++ dma_buffer->data = val; ++ ++ mb(); ++ msm_dmov_exec_cmd( ++ chip->dma_channel, DMOV_CMD_PTR_LIST | ++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); ++ mb(); ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++} ++ ++/* ++ * Allocates a bounce buffer, and stores the buffer address in ++ * variable pointed to by bounce_buf. bounce_buf should point to a ++ * stack variable, to avoid SMP issues. ++ */ ++static int msm_nand_alloc_bounce(void *addr, size_t size, ++ enum dma_data_direction dir, ++ uint8_t **bounce_buf) ++{ ++ if (bounce_buf == NULL) { ++ printk(KERN_ERR "not allocating bounce buffer\n"); ++ return -EINVAL; ++ } ++ ++ *bounce_buf = kmalloc(size, GFP_KERNEL | GFP_NOFS | GFP_DMA); ++ if (*bounce_buf == NULL) { ++ printk(KERN_ERR "error alloc bounce buffer %zu\n", size); ++ return -ENOMEM; ++ } ++ ++ if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL) ++ memcpy(*bounce_buf, addr, size); ++ ++ return 0; ++} ++ ++/* ++ * Maps the user buffer for DMA. If the buffer is vmalloced and the ++ * buffer crosses a page boundary, then we kmalloc a bounce buffer and ++ * copy the data into it. The bounce buffer is stored in the variable ++ * pointed to by bounce_buf, for freeing up later on. The bounce_buf ++ * should point to a stack variable, to avoid SMP issues. ++ */ ++static dma_addr_t ++msm_nand_dma_map(struct device *dev, void *addr, size_t size, ++ enum dma_data_direction dir, uint8_t **bounce_buf) ++{ ++ int ret; ++ struct page *page; ++ unsigned long offset = (unsigned long)addr & ~PAGE_MASK; ++ ++ if (virt_addr_valid(addr)) { ++ page = virt_to_page(addr); ++ } else { ++ if (size + offset > PAGE_SIZE) { ++ ret = msm_nand_alloc_bounce(addr, size, dir, bounce_buf); ++ if (ret < 0) ++ return DMA_ERROR_CODE; ++ ++ offset = (unsigned long)*bounce_buf & ~PAGE_MASK; ++ page = virt_to_page(*bounce_buf); ++ } else { ++ page = vmalloc_to_page(addr); ++ } ++ } ++ ++ return dma_map_page(dev, page, offset, size, dir); ++} ++ ++static void msm_nand_dma_unmap(struct device *dev, dma_addr_t addr, size_t size, ++ enum dma_data_direction dir, ++ void *orig_buf, void *bounce_buf) ++{ ++ dma_unmap_page(dev, addr, size, dir); ++ ++ if (bounce_buf != NULL) { ++ if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) ++ memcpy(orig_buf, bounce_buf, size); ++ ++ kfree(bounce_buf); ++ } ++} ++ ++uint32_t flash_read_id(struct msm_nand_chip *chip) ++{ ++ struct { ++ dmov_s cmd[9]; ++ unsigned cmdptr; ++ unsigned data[7]; ++ } *dma_buffer; ++ uint32_t rv; ++ dmov_s *cmd; ++ ++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer ++ (chip, sizeof(*dma_buffer)))); ++ ++ dma_buffer->data[0] = 0 | 4; ++ dma_buffer->data[1] = MSM_NAND_CMD_FETCH_ID; ++ dma_buffer->data[2] = 1; ++ dma_buffer->data[3] = 0xeeeeeeee; ++ dma_buffer->data[4] = 0xeeeeeeee; ++ dma_buffer->data[5] = flash_rd_reg(chip, MSM_NAND_SFLASHC_BURST_CFG); ++ dma_buffer->data[6] = 0x00000000; ++ BUILD_BUG_ON(6 != ARRAY_SIZE(dma_buffer->data) - 1); ++ ++ cmd = dma_buffer->cmd; ++ ++ cmd->cmd = 0 | CMD_OCB; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[6]); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[6]); ++ cmd->dst = MSM_NAND_ADDR0; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[6]); ++ cmd->dst = MSM_NAND_ADDR1; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[0]); ++ cmd->dst = MSM_NAND_FLASH_CHIP_SELECT; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[1]); ++ cmd->dst = MSM_NAND_FLASH_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[2]); ++ cmd->dst = MSM_NAND_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_FLASH_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data[3]); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_READ_ID; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data[4]); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = CMD_OCU | CMD_LC; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[5]); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->cmd) - 1); ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3 ++ ) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST | ++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); ++ mb(); ++ ++ pr_info("status: %x\n", dma_buffer->data[3]); ++ pr_info("nandid: %x maker %02x device %02x\n", ++ dma_buffer->data[4], dma_buffer->data[4] & 0xff, ++ (dma_buffer->data[4] >> 8) & 0xff); ++ rv = dma_buffer->data[4]; ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ return rv; ++} ++ ++struct flash_identification { ++ uint32_t flash_id; ++ uint32_t density; ++ uint32_t widebus; ++ uint32_t pagesize; ++ uint32_t blksize; ++ uint32_t oobsize; ++ uint32_t ecc_correctability; ++} supported_flash; ++ ++uint16_t flash_onfi_crc_check(uint8_t *buffer, uint16_t count) ++{ ++ int i; ++ uint16_t result; ++ ++ for (i = 0; i < count; i++) ++ buffer[i] = bitrev8(buffer[i]); ++ ++ result = bitrev16(crc16(bitrev16(0x4f4e), buffer, count)); ++ ++ for (i = 0; i < count; i++) ++ buffer[i] = bitrev8(buffer[i]); ++ ++ return result; ++} ++ ++static void flash_reset(struct msm_nand_chip *chip) ++{ ++ struct { ++ dmov_s cmd[6]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t exec; ++ uint32_t flash_status; ++ uint32_t sflash_bcfg_orig; ++ uint32_t sflash_bcfg_mod; ++ uint32_t chip_select; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ dma_addr_t dma_cmd; ++ dma_addr_t dma_cmdptr; ++ ++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer ++ (chip, sizeof(*dma_buffer)))); ++ ++ dma_buffer->data.sflash_bcfg_orig ++ = flash_rd_reg(chip, MSM_NAND_SFLASHC_BURST_CFG); ++ dma_buffer->data.sflash_bcfg_mod = 0x00000000; ++ dma_buffer->data.chip_select = 4; ++ dma_buffer->data.cmd = MSM_NAND_CMD_RESET; ++ dma_buffer->data.exec = 1; ++ dma_buffer->data.flash_status = 0xeeeeeeee; ++ ++ cmd = dma_buffer->cmd; ++ ++ /* Put the Nand ctlr in Async mode and disable SFlash ctlr */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sflash_bcfg_mod); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.chip_select); ++ cmd->dst = MSM_NAND_FLASH_CHIP_SELECT; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on cmd ready, & write Reset command */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = MSM_NAND_FLASH_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = MSM_NAND_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_FLASH_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Restore the SFLASH_BURST_CONFIG register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sflash_bcfg_orig); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ BUILD_BUG_ON(6 != ARRAY_SIZE(dma_buffer->cmd)); ++ ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_cmd = msm_virt_to_dma(chip, dma_buffer->cmd); ++ dma_buffer->cmdptr = (dma_cmd >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ dma_cmdptr = msm_virt_to_dma(chip, &dma_buffer->cmdptr); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(dma_cmdptr)); ++ mb(); ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++} ++ ++uint32_t flash_onfi_probe(struct msm_nand_chip *chip) ++{ ++ ++ ++ struct onfi_param_page { ++ uint32_t parameter_page_signature; ++ uint16_t revision_number; ++ uint16_t features_supported; ++ uint16_t optional_commands_supported; ++ uint8_t reserved0[22]; ++ uint8_t device_manufacturer[12]; ++ uint8_t device_model[20]; ++ uint8_t jedec_manufacturer_id; ++ uint16_t date_code; ++ uint8_t reserved1[13]; ++ uint32_t number_of_data_bytes_per_page; ++ uint16_t number_of_spare_bytes_per_page; ++ uint32_t number_of_data_bytes_per_partial_page; ++ uint16_t number_of_spare_bytes_per_partial_page; ++ uint32_t number_of_pages_per_block; ++ uint32_t number_of_blocks_per_logical_unit; ++ uint8_t number_of_logical_units; ++ uint8_t number_of_address_cycles; ++ uint8_t number_of_bits_per_cell; ++ uint16_t maximum_bad_blocks_per_logical_unit; ++ uint16_t block_endurance; ++ uint8_t guaranteed_valid_begin_blocks; ++ uint16_t guaranteed_valid_begin_blocks_endurance; ++ uint8_t number_of_programs_per_page; ++ uint8_t partial_program_attributes; ++ uint8_t number_of_bits_ecc_correctability; ++ uint8_t number_of_interleaved_address_bits; ++ uint8_t interleaved_operation_attributes; ++ uint8_t reserved2[13]; ++ uint8_t io_pin_capacitance; ++ uint16_t timing_mode_support; ++ uint16_t program_cache_timing_mode_support; ++ uint16_t maximum_page_programming_time; ++ uint16_t maximum_block_erase_time; ++ uint16_t maximum_page_read_time; ++ uint16_t maximum_change_column_setup_time; ++ uint8_t reserved3[23]; ++ uint16_t vendor_specific_revision_number; ++ uint8_t vendor_specific[88]; ++ uint16_t integrity_crc; ++ ++ } __attribute__((__packed__)); ++ ++ struct onfi_param_page *onfi_param_page_ptr; ++ uint8_t *onfi_identifier_buf = NULL; ++ uint8_t *onfi_param_info_buf = NULL; ++ ++ struct { ++ dmov_s cmd[12]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t cfg0; ++ uint32_t cfg1; ++ uint32_t exec; ++ uint32_t flash_status; ++ uint32_t devcmd1_orig; ++ uint32_t devcmdvld_orig; ++ uint32_t devcmd1_mod; ++ uint32_t devcmdvld_mod; ++ uint32_t sflash_bcfg_orig; ++ uint32_t sflash_bcfg_mod; ++ uint32_t chip_select; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ ++ unsigned page_address = 0; ++ int err = 0; ++ dma_addr_t dma_addr_param_info = 0; ++ dma_addr_t dma_addr_identifier = 0; ++ unsigned cmd_set_count = 2; ++ unsigned crc_chk_count = 0; ++ ++ /*if (msm_nand_data.nr_parts) { ++ page_address = ((msm_nand_data.parts[0]).offset << 6); ++ } else { ++ pr_err("flash_onfi_probe: " ++ "No partition info available\n"); ++ err = -EIO; ++ return err; ++ }*/ ++ ++ wait_event(chip->wait_queue, (onfi_identifier_buf = ++ msm_nand_get_dma_buffer(chip, ONFI_IDENTIFIER_LENGTH))); ++ dma_addr_identifier = msm_virt_to_dma(chip, onfi_identifier_buf); ++ ++ wait_event(chip->wait_queue, (onfi_param_info_buf = ++ msm_nand_get_dma_buffer(chip, ONFI_PARAM_INFO_LENGTH))); ++ dma_addr_param_info = msm_virt_to_dma(chip, onfi_param_info_buf); ++ ++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer ++ (chip, sizeof(*dma_buffer)))); ++ ++ dma_buffer->data.sflash_bcfg_orig = flash_rd_reg ++ (chip, MSM_NAND_SFLASHC_BURST_CFG); ++ dma_buffer->data.devcmd1_orig = flash_rd_reg(chip, MSM_NAND_DEV_CMD1); ++ dma_buffer->data.devcmdvld_orig = flash_rd_reg(chip, ++ MSM_NAND_DEV_CMD_VLD); ++ dma_buffer->data.chip_select = 4; ++ ++ while (cmd_set_count-- > 0) { ++ cmd = dma_buffer->cmd; ++ ++ dma_buffer->data.devcmd1_mod = (dma_buffer->data.devcmd1_orig & ++ 0xFFFFFF00) | (cmd_set_count ++ ? FLASH_READ_ONFI_IDENTIFIER_COMMAND ++ : FLASH_READ_ONFI_PARAMETERS_COMMAND); ++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; ++ dma_buffer->data.addr0 = (page_address << 16) | (cmd_set_count ++ ? FLASH_READ_ONFI_IDENTIFIER_ADDRESS ++ : FLASH_READ_ONFI_PARAMETERS_ADDRESS); ++ dma_buffer->data.addr1 = (page_address >> 16) & 0xFF; ++ dma_buffer->data.cfg0 = (cmd_set_count ++ ? MSM_NAND_CFG0_RAW_ONFI_IDENTIFIER ++ : MSM_NAND_CFG0_RAW_ONFI_PARAM_INFO); ++ dma_buffer->data.cfg1 = (cmd_set_count ++ ? MSM_NAND_CFG1_RAW_ONFI_IDENTIFIER ++ : MSM_NAND_CFG1_RAW_ONFI_PARAM_INFO); ++ dma_buffer->data.sflash_bcfg_mod = 0x00000000; ++ dma_buffer->data.devcmdvld_mod = (dma_buffer-> ++ data.devcmdvld_orig & 0xFFFFFFFE); ++ dma_buffer->data.exec = 1; ++ dma_buffer->data.flash_status = 0xeeeeeeee; ++ ++ /* Put the Nand ctlr in Async mode and disable SFlash ctlr */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.sflash_bcfg_mod); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.chip_select); ++ cmd->dst = MSM_NAND_FLASH_CHIP_SELECT; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on cmd ready, & write CMD,ADDR0,ADDR1,CHIPSEL regs */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = MSM_NAND_FLASH_CMD; ++ cmd->len = 12; ++ cmd++; ++ ++ /* Configure the CFG0 and CFG1 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0); ++ cmd->dst = MSM_NAND_DEV0_CFG0; ++ cmd->len = 8; ++ cmd++; ++ ++ /* Configure the DEV_CMD_VLD register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.devcmdvld_mod); ++ cmd->dst = MSM_NAND_DEV_CMD_VLD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Configure the DEV_CMD1 register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.devcmd1_mod); ++ cmd->dst = MSM_NAND_DEV_CMD1; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.exec); ++ cmd->dst = MSM_NAND_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the two status registers */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_FLASH_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.flash_status); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read data block - valid only if status says success */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_FLASH_BUFFER; ++ cmd->dst = (cmd_set_count ? dma_addr_identifier : ++ dma_addr_param_info); ++ cmd->len = (cmd_set_count ? ONFI_IDENTIFIER_LENGTH : ++ ONFI_PARAM_INFO_LENGTH); ++ cmd++; ++ ++ /* Restore the DEV_CMD1 register */ ++ cmd->cmd = 0 ; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.devcmd1_orig); ++ cmd->dst = MSM_NAND_DEV_CMD1; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Restore the DEV_CMD_VLD register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.devcmdvld_orig); ++ cmd->dst = MSM_NAND_DEV_CMD_VLD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Restore the SFLASH_BURST_CONFIG register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.sflash_bcfg_orig); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ BUILD_BUG_ON(12 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) ++ >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ ++ /* Check for errors, protection violations etc */ ++ if (dma_buffer->data.flash_status & 0x110) { ++ pr_info("MPU/OP error (0x%x) during " ++ "ONFI probe\n", ++ dma_buffer->data.flash_status); ++ err = -EIO; ++ break; ++ } ++ ++ if (cmd_set_count) { ++ onfi_param_page_ptr = (struct onfi_param_page *) ++ (&(onfi_identifier_buf[0])); ++ if (onfi_param_page_ptr->parameter_page_signature != ++ ONFI_PARAMETER_PAGE_SIGNATURE) { ++ pr_info("ONFI probe : Found a non" ++ "ONFI Compliant device \n"); ++ err = -EIO; ++ break; ++ } ++ } else { ++ for (crc_chk_count = 0; crc_chk_count < ++ ONFI_PARAM_INFO_LENGTH ++ / ONFI_PARAM_PAGE_LENGTH; ++ crc_chk_count++) { ++ onfi_param_page_ptr = ++ (struct onfi_param_page *) ++ (&(onfi_param_info_buf ++ [ONFI_PARAM_PAGE_LENGTH * ++ crc_chk_count])); ++ if (flash_onfi_crc_check( ++ (uint8_t *)onfi_param_page_ptr, ++ ONFI_PARAM_PAGE_LENGTH - 2) == ++ onfi_param_page_ptr->integrity_crc) { ++ break; ++ } ++ } ++ if (crc_chk_count >= ONFI_PARAM_INFO_LENGTH ++ / ONFI_PARAM_PAGE_LENGTH) { ++ pr_info("ONFI probe : CRC Check " ++ "failed on ONFI Parameter " ++ "data \n"); ++ err = -EIO; ++ break; ++ } else { ++ supported_flash.flash_id = ++ flash_read_id(chip); ++ supported_flash.widebus = ++ onfi_param_page_ptr-> ++ features_supported & 0x01; ++ supported_flash.pagesize = ++ onfi_param_page_ptr-> ++ number_of_data_bytes_per_page; ++ supported_flash.blksize = ++ onfi_param_page_ptr-> ++ number_of_pages_per_block * ++ supported_flash.pagesize; ++ supported_flash.oobsize = ++ onfi_param_page_ptr-> ++ number_of_spare_bytes_per_page; ++ supported_flash.density = ++ onfi_param_page_ptr-> ++ number_of_blocks_per_logical_unit ++ * supported_flash.blksize; ++ supported_flash.ecc_correctability = ++ onfi_param_page_ptr-> ++ number_of_bits_ecc_correctability; ++ ++ pr_info("ONFI probe : Found an ONFI " ++ "compliant device %s\n", ++ onfi_param_page_ptr->device_model); ++ ++ /* Temporary hack for MT29F4G08ABC device. ++ * Since the device is not properly adhering ++ * to ONFi specification it is reporting ++ * as 16 bit device though it is 8 bit device!!! ++ */ ++ if (!strncmp(onfi_param_page_ptr->device_model, ++ "MT29F4G08ABC", 12)) ++ supported_flash.widebus = 0; ++ } ++ } ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ msm_nand_release_dma_buffer(chip, onfi_param_info_buf, ++ ONFI_PARAM_INFO_LENGTH); ++ msm_nand_release_dma_buffer(chip, onfi_identifier_buf, ++ ONFI_IDENTIFIER_LENGTH); ++ ++ return err; ++} ++ ++static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, ++ struct mtd_oob_ops *ops) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ ++ struct { ++ dmov_s cmd[8 * 5 + 2]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t chipsel; ++ uint32_t cfg0; ++ uint32_t cfg1; ++ uint32_t eccbchcfg; ++ uint32_t exec; ++ uint32_t ecccfg; ++ struct { ++ uint32_t flash_status; ++ uint32_t buffer_status; ++ } result[8]; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ unsigned n; ++ unsigned page = 0; ++ uint32_t oob_len; ++ uint32_t sectordatasize; ++ uint32_t sectoroobsize; ++ int err, pageerr, rawerr; ++ dma_addr_t data_dma_addr = 0; ++ dma_addr_t oob_dma_addr = 0; ++ dma_addr_t data_dma_addr_curr = 0; ++ dma_addr_t oob_dma_addr_curr = 0; ++ uint8_t *dat_bounce_buf = NULL; ++ uint8_t *oob_bounce_buf = NULL; ++ uint32_t oob_col = 0; ++ unsigned page_count; ++ unsigned pages_read = 0; ++ unsigned start_sector = 0; ++ uint32_t ecc_errors; ++ uint32_t total_ecc_errors = 0; ++ unsigned cwperpage; ++#if VERBOSE ++ pr_info("=================================================" ++ "================\n"); ++ pr_info("%s:\nfrom 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x" ++ "\noobbuf 0x%p ooblen 0x%x\n", ++ __func__, from, ops->mode, ops->datbuf, ops->len, ++ ops->oobbuf, ops->ooblen); ++#endif ++ ++ if (mtd->writesize == 2048) ++ page = from >> 11; ++ ++ if (mtd->writesize == 4096) ++ page = from >> 12; ++ ++ oob_len = ops->ooblen; ++ cwperpage = (mtd->writesize >> 9); ++ ++ if (from & (mtd->writesize - 1)) { ++ pr_err("%s: unsupported from, 0x%llx\n", ++ __func__, from); ++ return -EINVAL; ++ } ++ if (ops->mode != MTD_OPS_RAW) { ++ if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { ++ /* when ops->datbuf is NULL, ops->len can be ooblen */ ++ pr_err("%s: unsupported ops->len, %d\n", ++ __func__, ops->len); ++ return -EINVAL; ++ } ++ } else { ++ if (ops->datbuf != NULL && ++ (ops->len % (mtd->writesize + mtd->oobsize)) != 0) { ++ pr_err("%s: unsupported ops->len," ++ " %d for MTD_OPS_RAW\n", __func__, ops->len); ++ return -EINVAL; ++ } ++ } ++ ++ if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) { ++ pr_err("%s: unsupported ops->ooboffs, %d\n", ++ __func__, ops->ooboffs); ++ return -EINVAL; ++ } ++ ++ if (ops->oobbuf && !ops->datbuf && ops->mode == MTD_OPS_AUTO_OOB) ++ start_sector = cwperpage - 1; ++ ++ if (ops->oobbuf && !ops->datbuf) { ++ page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ? ++ mtd->oobavail : mtd->oobsize); ++ if ((page_count == 0) && (ops->ooblen)) ++ page_count = 1; ++ } else if (ops->mode != MTD_OPS_RAW) ++ page_count = ops->len / mtd->writesize; ++ else ++ page_count = ops->len / (mtd->writesize + mtd->oobsize); ++ ++ if (ops->datbuf) { ++ data_dma_addr_curr = data_dma_addr = ++ msm_nand_dma_map(chip->dev, ops->datbuf, ops->len, ++ DMA_FROM_DEVICE, &dat_bounce_buf); ++ if (dma_mapping_error(chip->dev, data_dma_addr)) { ++ pr_err("msm_nand_read_oob: failed to get dma addr " ++ "for %p\n", ops->datbuf); ++ return -EIO; ++ } ++ } ++ if (ops->oobbuf) { ++ memset(ops->oobbuf, 0xff, ops->ooblen); ++ oob_dma_addr_curr = oob_dma_addr = ++ msm_nand_dma_map(chip->dev, ops->oobbuf, ++ ops->ooblen, DMA_BIDIRECTIONAL, ++ &oob_bounce_buf); ++ if (dma_mapping_error(chip->dev, oob_dma_addr)) { ++ pr_err("msm_nand_read_oob: failed to get dma addr " ++ "for %p\n", ops->oobbuf); ++ err = -EIO; ++ goto err_dma_map_oobbuf_failed; ++ } ++ } ++ ++ wait_event(chip->wait_queue, ++ (dma_buffer = msm_nand_get_dma_buffer( ++ chip, sizeof(*dma_buffer)))); ++ ++ oob_col = start_sector * chip->cw_size; ++ if (chip->CFG1 & CFG1_WIDE_FLASH) ++ oob_col >>= 1; ++ ++ err = 0; ++ while (page_count-- > 0) { ++ cmd = dma_buffer->cmd; ++ ++ /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */ ++ if (ops->mode != MTD_OPS_RAW) { ++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ_ECC; ++ dma_buffer->data.cfg0 = ++ (chip->CFG0 & ~(7U << 6)) ++ | (((cwperpage-1) - start_sector) << 6); ++ dma_buffer->data.cfg1 = chip->CFG1; ++ if (enable_bch_ecc) ++ dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg; ++ } else { ++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; ++ dma_buffer->data.cfg0 = (chip->CFG0_RAW ++ & ~(7U << 6)) | ((cwperpage-1) << 6); ++ dma_buffer->data.cfg1 = chip->CFG1_RAW | ++ (chip->CFG1 & CFG1_WIDE_FLASH); ++ } ++ ++ dma_buffer->data.addr0 = (page << 16) | oob_col; ++ dma_buffer->data.addr1 = (page >> 16) & 0xff; ++ /* chipsel_0 + enable DM interface */ ++ dma_buffer->data.chipsel = 0 | 4; ++ ++ ++ /* GO bit for the EXEC register */ ++ dma_buffer->data.exec = 1; ++ ++ ++ BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data.result)); ++ ++ for (n = start_sector; n < cwperpage; n++) { ++ /* flash + buffer status return words */ ++ dma_buffer->data.result[n].flash_status = 0xeeeeeeee; ++ dma_buffer->data.result[n].buffer_status = 0xeeeeeeee; ++ ++ /* block on cmd ready, then ++ * write CMD / ADDR0 / ADDR1 / CHIPSEL ++ * regs in a burst ++ */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = MSM_NAND_FLASH_CMD; ++ if (n == start_sector) ++ cmd->len = 16; ++ else ++ cmd->len = 4; ++ cmd++; ++ ++ if (n == start_sector) { ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0); ++ cmd->dst = MSM_NAND_DEV0_CFG0; ++ if (enable_bch_ecc) ++ cmd->len = 12; ++ else ++ cmd->len = 8; ++ cmd++; ++ ++ dma_buffer->data.ecccfg = chip->ecc_buf_cfg; ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.ecccfg); ++ cmd->dst = MSM_NAND_EBI2_ECC_BUF_CFG; ++ cmd->len = 4; ++ cmd++; ++ } ++ ++ /* kick the execute register */ ++ cmd->cmd = 0; ++ cmd->src = ++ msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = MSM_NAND_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* block on data ready, then ++ * read the status register ++ */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_FLASH_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.result[n]); ++ /* MSM_NAND_FLASH_STATUS + MSM_NAND_BUFFER_STATUS */ ++ cmd->len = 8; ++ cmd++; ++ ++ /* read data block ++ * (only valid if status says success) ++ */ ++ if (ops->datbuf) { ++ if (ops->mode != MTD_OPS_RAW) { ++ if (!boot_layout) ++ sectordatasize = (n < (cwperpage - 1)) ++ ? 516 : (512 - ((cwperpage - 1) << 2)); ++ else ++ sectordatasize = 512; ++ } else { ++ sectordatasize = chip->cw_size; ++ } ++ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_FLASH_BUFFER; ++ cmd->dst = data_dma_addr_curr; ++ data_dma_addr_curr += sectordatasize; ++ cmd->len = sectordatasize; ++ cmd++; ++ } ++ ++ if (ops->oobbuf && (n == (cwperpage - 1) ++ || ops->mode != MTD_OPS_AUTO_OOB)) { ++ cmd->cmd = 0; ++ if (n == (cwperpage - 1)) { ++ cmd->src = MSM_NAND_FLASH_BUFFER + ++ (512 - ((cwperpage - 1) << 2)); ++ sectoroobsize = (cwperpage << 2); ++ if (ops->mode != MTD_OPS_AUTO_OOB) ++ sectoroobsize += ++ chip->ecc_parity_bytes; ++ } else { ++ cmd->src = MSM_NAND_FLASH_BUFFER + 516; ++ sectoroobsize = chip->ecc_parity_bytes; ++ } ++ ++ cmd->dst = oob_dma_addr_curr; ++ if (sectoroobsize < oob_len) ++ cmd->len = sectoroobsize; ++ else ++ cmd->len = oob_len; ++ oob_dma_addr_curr += cmd->len; ++ oob_len -= cmd->len; ++ if (cmd->len > 0) ++ cmd++; ++ } ++ } ++ ++ BUILD_BUG_ON(8 * 5 + 2 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = ++ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) ++ | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ ++ /* if any of the writes failed (0x10), or there ++ * was a protection violation (0x100), we lose ++ */ ++ pageerr = rawerr = 0; ++ for (n = start_sector; n < cwperpage; n++) { ++ if (dma_buffer->data.result[n].flash_status & 0x110) { ++ rawerr = -EIO; ++ break; ++ } ++ } ++ if (rawerr) { ++ if (ops->datbuf && ops->mode != MTD_OPS_RAW) { ++ uint8_t *datbuf = ops->datbuf + ++ pages_read * mtd->writesize; ++ ++ dma_sync_single_for_cpu(chip->dev, ++ data_dma_addr_curr-mtd->writesize, ++ mtd->writesize, DMA_BIDIRECTIONAL); ++ ++ for (n = 0; n < mtd->writesize; n++) { ++ /* empty blocks read 0x54 at ++ * these offsets ++ */ ++ if ((n % 516 == 3 || n % 516 == 175) ++ && datbuf[n] == 0x54) ++ datbuf[n] = 0xff; ++ if (datbuf[n] != 0xff) { ++ pageerr = rawerr; ++ break; ++ } ++ } ++ ++ dma_sync_single_for_device(chip->dev, ++ data_dma_addr_curr-mtd->writesize, ++ mtd->writesize, DMA_BIDIRECTIONAL); ++ ++ } ++ if (ops->oobbuf) { ++ dma_sync_single_for_cpu(chip->dev, ++ oob_dma_addr_curr - (ops->ooblen - oob_len), ++ ops->ooblen - oob_len, DMA_BIDIRECTIONAL); ++ ++ for (n = 0; n < ops->ooblen; n++) { ++ if (ops->oobbuf[n] != 0xff) { ++ pageerr = rawerr; ++ break; ++ } ++ } ++ ++ dma_sync_single_for_device(chip->dev, ++ oob_dma_addr_curr - (ops->ooblen - oob_len), ++ ops->ooblen - oob_len, DMA_BIDIRECTIONAL); ++ } ++ } ++ if (pageerr) { ++ for (n = start_sector; n < cwperpage; n++) { ++ if (dma_buffer->data.result[n].buffer_status & ++ chip->uncorrectable_bit_mask) { ++ /* not thread safe */ ++ mtd->ecc_stats.failed++; ++ pageerr = -EBADMSG; ++ break; ++ } ++ } ++ } ++ if (!rawerr) { /* check for corretable errors */ ++ for (n = start_sector; n < cwperpage; n++) { ++ ecc_errors = ++ (dma_buffer->data.result[n].buffer_status ++ & chip->num_err_mask); ++ if (ecc_errors) { ++ total_ecc_errors += ecc_errors; ++ /* not thread safe */ ++ mtd->ecc_stats.corrected += ecc_errors; ++ if (ecc_errors > 1) ++ pageerr = -EUCLEAN; ++ } ++ } ++ } ++ if (pageerr && (pageerr != -EUCLEAN || err == 0)) ++ err = pageerr; ++ ++#if VERBOSE ++ if (rawerr && !pageerr) { ++ pr_err("msm_nand_read_oob %llx %x %x empty page\n", ++ (loff_t)page * mtd->writesize, ops->len, ++ ops->ooblen); ++ } else { ++ for (n = start_sector; n < cwperpage; n++) ++ pr_info("flash_status[%d] = %x,\ ++ buffr_status[%d] = %x\n", ++ n, dma_buffer->data.result[n].flash_status, ++ n, dma_buffer->data.result[n].buffer_status); ++ } ++#endif ++ if (err && err != -EUCLEAN && err != -EBADMSG) ++ break; ++ pages_read++; ++ page++; ++ } ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++ if (ops->oobbuf) { ++ msm_nand_dma_unmap(chip->dev, oob_dma_addr, ++ ops->ooblen, DMA_FROM_DEVICE, ++ ops->oobbuf, oob_bounce_buf); ++ } ++err_dma_map_oobbuf_failed: ++ if (ops->datbuf) { ++ msm_nand_dma_unmap(chip->dev, data_dma_addr, ++ ops->len, DMA_BIDIRECTIONAL, ++ ops->datbuf, dat_bounce_buf); ++ } ++ ++ if (ops->mode != MTD_OPS_RAW) ++ ops->retlen = mtd->writesize * pages_read; ++ else ++ ops->retlen = (mtd->writesize + mtd->oobsize) * ++ pages_read; ++ ops->oobretlen = ops->ooblen - oob_len; ++ if (err) ++ pr_err("msm_nand_read_oob %llx %x %x failed %d, corrected %d\n", ++ from, ops->datbuf ? ops->len : 0, ops->ooblen, err, ++ total_ecc_errors); ++#if VERBOSE ++ pr_info("\n%s: ret %d, retlen %d oobretlen %d\n", ++ __func__, err, ops->retlen, ops->oobretlen); ++ ++ pr_info("===================================================" ++ "==============\n"); ++#endif ++ return err; ++} ++ ++static int msm_nand_read_oob_dualnandc(struct mtd_info *mtd, loff_t from, ++ struct mtd_oob_ops *ops) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ ++ struct { ++ dmov_s cmd[16 * 6 + 20]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t nandc01_addr0; ++ uint32_t nandc10_addr0; ++ uint32_t nandc11_addr1; ++ uint32_t chipsel_cs0; ++ uint32_t chipsel_cs1; ++ uint32_t cfg0; ++ uint32_t cfg1; ++ uint32_t eccbchcfg; ++ uint32_t exec; ++ uint32_t ecccfg; ++ uint32_t ebi2_chip_select_cfg0; ++ uint32_t adm_mux_data_ack_req_nc01; ++ uint32_t adm_mux_cmd_ack_req_nc01; ++ uint32_t adm_mux_data_ack_req_nc10; ++ uint32_t adm_mux_cmd_ack_req_nc10; ++ uint32_t adm_default_mux; ++ uint32_t default_ebi2_chip_select_cfg0; ++ uint32_t nc10_flash_dev_cmd_vld; ++ uint32_t nc10_flash_dev_cmd1; ++ uint32_t nc10_flash_dev_cmd_vld_default; ++ uint32_t nc10_flash_dev_cmd1_default; ++ struct { ++ uint32_t flash_status; ++ uint32_t buffer_status; ++ } result[16]; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ unsigned n; ++ unsigned page = 0; ++ uint32_t oob_len; ++ uint32_t sectordatasize; ++ uint32_t sectoroobsize; ++ int err, pageerr, rawerr; ++ dma_addr_t data_dma_addr = 0; ++ dma_addr_t oob_dma_addr = 0; ++ dma_addr_t data_dma_addr_curr = 0; ++ dma_addr_t oob_dma_addr_curr = 0; ++ uint32_t oob_col = 0; ++ unsigned page_count; ++ unsigned pages_read = 0; ++ unsigned start_sector = 0; ++ uint32_t ecc_errors; ++ uint32_t total_ecc_errors = 0; ++ unsigned cwperpage; ++ unsigned cw_offset = chip->cw_size; ++#if VERBOSE ++ pr_info("=================================================" ++ "============\n"); ++ pr_info("%s:\nfrom 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x" ++ "\noobbuf 0x%p ooblen 0x%x\n\n", ++ __func__, from, ops->mode, ops->datbuf, ++ ops->len, ops->oobbuf, ops->ooblen); ++#endif ++ ++ if (mtd->writesize == 2048) ++ page = from >> 11; ++ ++ if (mtd->writesize == 4096) ++ page = from >> 12; ++ ++ if (interleave_enable) ++ page = (from >> 1) >> 12; ++ ++ oob_len = ops->ooblen; ++ cwperpage = (mtd->writesize >> 9); ++ ++ if (from & (mtd->writesize - 1)) { ++ pr_err("%s: unsupported from, 0x%llx\n", ++ __func__, from); ++ return -EINVAL; ++ } ++ if (ops->mode != MTD_OPS_RAW) { ++ if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { ++ pr_err("%s: unsupported ops->len, %d\n", ++ __func__, ops->len); ++ return -EINVAL; ++ } ++ } else { ++ if (ops->datbuf != NULL && ++ (ops->len % (mtd->writesize + mtd->oobsize)) != 0) { ++ pr_err("%s: unsupported ops->len," ++ " %d for MTD_OPS_RAW\n", __func__, ops->len); ++ return -EINVAL; ++ } ++ } ++ ++ if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) { ++ pr_err("%s: unsupported ops->ooboffs, %d\n", ++ __func__, ops->ooboffs); ++ return -EINVAL; ++ } ++ ++ if (ops->oobbuf && !ops->datbuf && ops->mode == MTD_OPS_AUTO_OOB) ++ start_sector = cwperpage - 1; ++ ++ if (ops->oobbuf && !ops->datbuf) { ++ page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ? ++ mtd->oobavail : mtd->oobsize); ++ if ((page_count == 0) && (ops->ooblen)) ++ page_count = 1; ++ } else if (ops->mode != MTD_OPS_RAW) ++ page_count = ops->len / mtd->writesize; ++ else ++ page_count = ops->len / (mtd->writesize + mtd->oobsize); ++ ++ if (ops->datbuf) { ++ data_dma_addr_curr = data_dma_addr = ++ msm_nand_dma_map(chip->dev, ops->datbuf, ops->len, ++ DMA_FROM_DEVICE, NULL); ++ if (dma_mapping_error(chip->dev, data_dma_addr)) { ++ pr_err("msm_nand_read_oob_dualnandc: " ++ "failed to get dma addr for %p\n", ++ ops->datbuf); ++ return -EIO; ++ } ++ } ++ if (ops->oobbuf) { ++ memset(ops->oobbuf, 0xff, ops->ooblen); ++ oob_dma_addr_curr = oob_dma_addr = ++ msm_nand_dma_map(chip->dev, ops->oobbuf, ++ ops->ooblen, DMA_BIDIRECTIONAL, NULL); ++ if (dma_mapping_error(chip->dev, oob_dma_addr)) { ++ pr_err("msm_nand_read_oob_dualnandc: " ++ "failed to get dma addr for %p\n", ++ ops->oobbuf); ++ err = -EIO; ++ goto err_dma_map_oobbuf_failed; ++ } ++ } ++ ++ wait_event(chip->wait_queue, ++ (dma_buffer = msm_nand_get_dma_buffer( ++ chip, sizeof(*dma_buffer)))); ++ ++ oob_col = start_sector * chip->cw_size; ++ if (chip->CFG1 & CFG1_WIDE_FLASH) { ++ oob_col >>= 1; ++ cw_offset >>= 1; ++ } ++ ++ err = 0; ++ while (page_count-- > 0) { ++ cmd = dma_buffer->cmd; ++ ++ if (ops->mode != MTD_OPS_RAW) { ++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ_ECC; ++ if (start_sector == (cwperpage - 1)) { ++ dma_buffer->data.cfg0 = (chip->CFG0 & ++ ~(7U << 6)); ++ } else { ++ dma_buffer->data.cfg0 = (chip->CFG0 & ++ ~(7U << 6)) ++ | (((cwperpage >> 1)-1) << 6); ++ } ++ dma_buffer->data.cfg1 = chip->CFG1; ++ if (enable_bch_ecc) ++ dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg; ++ } else { ++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; ++ dma_buffer->data.cfg0 = ((chip->CFG0_RAW & ++ ~(7U << 6)) | ((((cwperpage >> 1)-1) << 6))); ++ dma_buffer->data.cfg1 = chip->CFG1_RAW | ++ (chip->CFG1 & CFG1_WIDE_FLASH); ++ } ++ ++ if (!interleave_enable) { ++ if (start_sector == (cwperpage - 1)) { ++ dma_buffer->data.nandc10_addr0 = ++ (page << 16) | oob_col; ++ dma_buffer->data.nc10_flash_dev_cmd_vld = 0xD; ++ dma_buffer->data.nc10_flash_dev_cmd1 = ++ 0xF00F3000; ++ } else { ++ dma_buffer->data.nandc01_addr0 = page << 16; ++ /* NC10 ADDR0 points to the next code word */ ++ dma_buffer->data.nandc10_addr0 = (page << 16) | ++ cw_offset; ++ dma_buffer->data.nc10_flash_dev_cmd_vld = 0x1D; ++ dma_buffer->data.nc10_flash_dev_cmd1 = ++ 0xF00FE005; ++ } ++ } else { ++ dma_buffer->data.nandc01_addr0 = ++ dma_buffer->data.nandc10_addr0 = ++ (page << 16) | oob_col; ++ } ++ /* ADDR1 */ ++ dma_buffer->data.nandc11_addr1 = (page >> 16) & 0xff; ++ ++ dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C; ++ dma_buffer->data.adm_mux_cmd_ack_req_nc01 = 0x0000053C; ++ dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28; ++ dma_buffer->data.adm_mux_cmd_ack_req_nc10 = 0x00000F14; ++ dma_buffer->data.adm_default_mux = 0x00000FC0; ++ dma_buffer->data.nc10_flash_dev_cmd_vld_default = 0x1D; ++ dma_buffer->data.nc10_flash_dev_cmd1_default = 0xF00F3000; ++ ++ dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805; ++ dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801; ++ ++ /* chipsel_0 + enable DM interface */ ++ dma_buffer->data.chipsel_cs0 = (1<<4) | 4; ++ /* chipsel_1 + enable DM interface */ ++ dma_buffer->data.chipsel_cs1 = (1<<4) | 5; ++ ++ /* GO bit for the EXEC register */ ++ dma_buffer->data.exec = 1; ++ ++ BUILD_BUG_ON(16 != ARRAY_SIZE(dma_buffer->data.result)); ++ ++ for (n = start_sector; n < cwperpage; n++) { ++ /* flash + buffer status return words */ ++ dma_buffer->data.result[n].flash_status = 0xeeeeeeee; ++ dma_buffer->data.result[n].buffer_status = 0xeeeeeeee; ++ ++ if (n == start_sector) { ++ if (!interleave_enable) { ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.nc10_flash_dev_cmd_vld); ++ cmd->dst = NC10(MSM_NAND_DEV_CMD_VLD); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nc10_flash_dev_cmd1); ++ cmd->dst = NC10(MSM_NAND_DEV_CMD1); ++ cmd->len = 4; ++ cmd++; ++ ++ /* NC01, NC10 --> ADDR1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nandc11_addr1); ++ cmd->dst = NC11(MSM_NAND_ADDR1); ++ cmd->len = 8; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0); ++ cmd->dst = NC11(MSM_NAND_DEV0_CFG0); ++ if (enable_bch_ecc) ++ cmd->len = 12; ++ else ++ cmd->len = 8; ++ cmd++; ++ } else { ++ /* enable CS0 & CS1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.ebi2_chip_select_cfg0); ++ cmd->dst = EBI2_CHIP_SELECT_CFG0; ++ cmd->len = 4; ++ cmd++; ++ ++ /* NC01, NC10 --> ADDR1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nandc11_addr1); ++ cmd->dst = NC11(MSM_NAND_ADDR1); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Enable CS0 for NC01 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.chipsel_cs0); ++ cmd->dst = ++ NC01(MSM_NAND_FLASH_CHIP_SELECT); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Enable CS1 for NC10 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.chipsel_cs1); ++ cmd->dst = ++ NC10(MSM_NAND_FLASH_CHIP_SELECT); ++ cmd->len = 4; ++ cmd++; ++ ++ /* config DEV0_CFG0 & CFG1 for CS0 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0); ++ cmd->dst = NC01(MSM_NAND_DEV0_CFG0); ++ cmd->len = 8; ++ cmd++; ++ ++ /* config DEV1_CFG0 & CFG1 for CS1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0); ++ cmd->dst = NC10(MSM_NAND_DEV1_CFG0); ++ cmd->len = 8; ++ cmd++; ++ } ++ ++ dma_buffer->data.ecccfg = chip->ecc_buf_cfg; ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.ecccfg); ++ cmd->dst = NC11(MSM_NAND_EBI2_ECC_BUF_CFG); ++ cmd->len = 4; ++ cmd++; ++ ++ /* if 'only' the last code word */ ++ if (n == cwperpage - 1) { ++ /* MASK CMD ACK/REQ --> NC01 (0x53C)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.adm_mux_cmd_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* CMD */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cmd); ++ cmd->dst = NC10(MSM_NAND_FLASH_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* NC10 --> ADDR0 ( 0x0 ) */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nandc10_addr0); ++ cmd->dst = NC10(MSM_NAND_ADDR0); ++ cmd->len = 4; ++ cmd++; ++ ++ /* kick the execute reg for NC10 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.exec); ++ cmd->dst = NC10(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* MASK DATA ACK/REQ --> NC01 (0xA3C)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.adm_mux_data_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* block on data ready from NC10, then ++ * read the status register ++ */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = NC10(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.result[n]); ++ /* MSM_NAND_FLASH_STATUS + ++ * MSM_NAND_BUFFER_STATUS ++ */ ++ cmd->len = 8; ++ cmd++; ++ } else { ++ /* NC01 --> ADDR0 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nandc01_addr0); ++ cmd->dst = NC01(MSM_NAND_ADDR0); ++ cmd->len = 4; ++ cmd++; ++ ++ /* NC10 --> ADDR1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nandc10_addr0); ++ cmd->dst = NC10(MSM_NAND_ADDR0); ++ cmd->len = 4; ++ cmd++; ++ ++ /* MASK CMD ACK/REQ --> NC10 (0xF14)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.adm_mux_cmd_ack_req_nc10); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* CMD */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cmd); ++ cmd->dst = NC01(MSM_NAND_FLASH_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* kick the execute register for NC01*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.exec); ++ cmd->dst = NC01(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ } ++ } ++ ++ /* read data block ++ * (only valid if status says success) ++ */ ++ if (ops->datbuf || (ops->oobbuf && ++ ops->mode != MTD_OPS_AUTO_OOB)) { ++ if (ops->mode != MTD_OPS_RAW) ++ sectordatasize = (n < (cwperpage - 1)) ++ ? 516 : (512 - ((cwperpage - 1) << 2)); ++ else ++ sectordatasize = chip->cw_size; ++ ++ if (n % 2 == 0) { ++ /* MASK DATA ACK/REQ --> NC10 (0xF28)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.adm_mux_data_ack_req_nc10); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* block on data ready from NC01, then ++ * read the status register ++ */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = NC01(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.result[n]); ++ /* MSM_NAND_FLASH_STATUS + ++ * MSM_NAND_BUFFER_STATUS ++ */ ++ cmd->len = 8; ++ cmd++; ++ ++ /* MASK CMD ACK/REQ --> NC01 (0x53C)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.adm_mux_cmd_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* CMD */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cmd); ++ cmd->dst = NC10(MSM_NAND_FLASH_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* kick the execute register for NC10 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.exec); ++ cmd->dst = NC10(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read only when there is data ++ * buffer ++ */ ++ if (ops->datbuf) { ++ cmd->cmd = 0; ++ cmd->src = ++ NC01(MSM_NAND_FLASH_BUFFER); ++ cmd->dst = data_dma_addr_curr; ++ data_dma_addr_curr += ++ sectordatasize; ++ cmd->len = sectordatasize; ++ cmd++; ++ } ++ } else { ++ /* MASK DATA ACK/REQ --> ++ * NC01 (0xA3C) ++ */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.adm_mux_data_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* block on data ready from NC10 ++ * then read the status register ++ */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = ++ NC10(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.result[n]); ++ /* MSM_NAND_FLASH_STATUS + ++ * MSM_NAND_BUFFER_STATUS ++ */ ++ cmd->len = 8; ++ cmd++; ++ if (n != cwperpage - 1) { ++ /* MASK CMD ACK/REQ --> ++ * NC10 (0xF14) ++ */ ++ cmd->cmd = 0; ++ cmd->src = ++ msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.adm_mux_cmd_ack_req_nc10); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* CMD */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cmd); ++ cmd->dst = ++ NC01(MSM_NAND_FLASH_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* EXEC */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.exec); ++ cmd->dst = ++ NC01(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ } ++ ++ /* Read only when there is data ++ * buffer ++ */ ++ if (ops->datbuf) { ++ cmd->cmd = 0; ++ cmd->src = ++ NC10(MSM_NAND_FLASH_BUFFER); ++ cmd->dst = data_dma_addr_curr; ++ data_dma_addr_curr += ++ sectordatasize; ++ cmd->len = sectordatasize; ++ cmd++; ++ } ++ } ++ } ++ ++ if (ops->oobbuf && (n == (cwperpage - 1) ++ || ops->mode != MTD_OPS_AUTO_OOB)) { ++ cmd->cmd = 0; ++ if (n == (cwperpage - 1)) { ++ /* Use NC10 for reading the ++ * last codeword!!! ++ */ ++ cmd->src = NC10(MSM_NAND_FLASH_BUFFER) + ++ (512 - ((cwperpage - 1) << 2)); ++ sectoroobsize = (cwperpage << 2); ++ if (ops->mode != MTD_OPS_AUTO_OOB) ++ sectoroobsize += ++ chip->ecc_parity_bytes; ++ } else { ++ if (n % 2 == 0) ++ cmd->src = ++ NC01(MSM_NAND_FLASH_BUFFER) ++ + 516; ++ else ++ cmd->src = ++ NC10(MSM_NAND_FLASH_BUFFER) ++ + 516; ++ sectoroobsize = chip->ecc_parity_bytes; ++ } ++ cmd->dst = oob_dma_addr_curr; ++ if (sectoroobsize < oob_len) ++ cmd->len = sectoroobsize; ++ else ++ cmd->len = oob_len; ++ oob_dma_addr_curr += cmd->len; ++ oob_len -= cmd->len; ++ if (cmd->len > 0) ++ cmd++; ++ } ++ } ++ /* ADM --> Default mux state (0xFC0) */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_default_mux); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ if (!interleave_enable) { ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nc10_flash_dev_cmd_vld_default); ++ cmd->dst = NC10(MSM_NAND_DEV_CMD_VLD); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nc10_flash_dev_cmd1_default); ++ cmd->dst = NC10(MSM_NAND_DEV_CMD1); ++ cmd->len = 4; ++ cmd++; ++ } else { ++ /* disable CS1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.default_ebi2_chip_select_cfg0); ++ cmd->dst = EBI2_CHIP_SELECT_CFG0; ++ cmd->len = 4; ++ cmd++; ++ } ++ ++ BUILD_BUG_ON(16 * 6 + 20 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = ++ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) ++ | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ ++ /* if any of the writes failed (0x10), or there ++ * was a protection violation (0x100), we lose ++ */ ++ pageerr = rawerr = 0; ++ for (n = start_sector; n < cwperpage; n++) { ++ if (dma_buffer->data.result[n].flash_status & 0x110) { ++ rawerr = -EIO; ++ break; ++ } ++ } ++ if (rawerr) { ++ if (ops->datbuf && ops->mode != MTD_OPS_RAW) { ++ uint8_t *datbuf = ops->datbuf + ++ pages_read * mtd->writesize; ++ ++ dma_sync_single_for_cpu(chip->dev, ++ data_dma_addr_curr-mtd->writesize, ++ mtd->writesize, DMA_BIDIRECTIONAL); ++ ++ for (n = 0; n < mtd->writesize; n++) { ++ /* empty blocks read 0x54 at ++ * these offsets ++ */ ++ if ((n % 516 == 3 || n % 516 == 175) ++ && datbuf[n] == 0x54) ++ datbuf[n] = 0xff; ++ if (datbuf[n] != 0xff) { ++ pageerr = rawerr; ++ break; ++ } ++ } ++ ++ dma_sync_single_for_device(chip->dev, ++ data_dma_addr_curr-mtd->writesize, ++ mtd->writesize, DMA_BIDIRECTIONAL); ++ ++ } ++ if (ops->oobbuf) { ++ dma_sync_single_for_cpu(chip->dev, ++ oob_dma_addr_curr - (ops->ooblen - oob_len), ++ ops->ooblen - oob_len, DMA_BIDIRECTIONAL); ++ ++ for (n = 0; n < ops->ooblen; n++) { ++ if (ops->oobbuf[n] != 0xff) { ++ pageerr = rawerr; ++ break; ++ } ++ } ++ ++ dma_sync_single_for_device(chip->dev, ++ oob_dma_addr_curr - (ops->ooblen - oob_len), ++ ops->ooblen - oob_len, DMA_BIDIRECTIONAL); ++ } ++ } ++ if (pageerr) { ++ for (n = start_sector; n < cwperpage; n++) { ++ if (dma_buffer->data.result[n].buffer_status ++ & chip->uncorrectable_bit_mask) { ++ /* not thread safe */ ++ mtd->ecc_stats.failed++; ++ pageerr = -EBADMSG; ++ break; ++ } ++ } ++ } ++ if (!rawerr) { /* check for corretable errors */ ++ for (n = start_sector; n < cwperpage; n++) { ++ ecc_errors = dma_buffer->data. ++ result[n].buffer_status ++ & chip->num_err_mask; ++ if (ecc_errors) { ++ total_ecc_errors += ecc_errors; ++ /* not thread safe */ ++ mtd->ecc_stats.corrected += ecc_errors; ++ if (ecc_errors > 1) ++ pageerr = -EUCLEAN; ++ } ++ } ++ } ++ if (pageerr && (pageerr != -EUCLEAN || err == 0)) ++ err = pageerr; ++ ++#if VERBOSE ++ if (rawerr && !pageerr) { ++ pr_err("msm_nand_read_oob_dualnandc " ++ "%llx %x %x empty page\n", ++ (loff_t)page * mtd->writesize, ops->len, ++ ops->ooblen); ++ } else { ++ for (n = start_sector; n < cwperpage; n++) { ++ if (n%2) { ++ pr_info("NC10: flash_status[%d] = %x, " ++ "buffr_status[%d] = %x\n", ++ n, dma_buffer-> ++ data.result[n].flash_status, ++ n, dma_buffer-> ++ data.result[n].buffer_status); ++ } else { ++ pr_info("NC01: flash_status[%d] = %x, " ++ "buffr_status[%d] = %x\n", ++ n, dma_buffer-> ++ data.result[n].flash_status, ++ n, dma_buffer-> ++ data.result[n].buffer_status); ++ } ++ } ++ } ++#endif ++ if (err && err != -EUCLEAN && err != -EBADMSG) ++ break; ++ pages_read++; ++ page++; ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++ if (ops->oobbuf) { ++ dma_unmap_page(chip->dev, oob_dma_addr, ++ ops->ooblen, DMA_FROM_DEVICE); ++ } ++err_dma_map_oobbuf_failed: ++ if (ops->datbuf) { ++ dma_unmap_page(chip->dev, data_dma_addr, ++ ops->len, DMA_BIDIRECTIONAL); ++ } ++ ++ if (ops->mode != MTD_OPS_RAW) ++ ops->retlen = mtd->writesize * pages_read; ++ else ++ ops->retlen = (mtd->writesize + mtd->oobsize) * ++ pages_read; ++ ops->oobretlen = ops->ooblen - oob_len; ++ if (err) ++ pr_err("msm_nand_read_oob_dualnandc " ++ "%llx %x %x failed %d, corrected %d\n", ++ from, ops->datbuf ? ops->len : 0, ops->ooblen, err, ++ total_ecc_errors); ++#if VERBOSE ++ pr_info("\n%s: ret %d, retlen %d oobretlen %d\n", ++ __func__, err, ops->retlen, ops->oobretlen); ++ ++ pr_info("===================================================" ++ "==========\n"); ++#endif ++ return err; ++} ++ ++static int ++msm_nand_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ int ret; ++ struct mtd_ecc_stats stats; ++ struct mtd_oob_ops ops; ++ int (*read_oob)(struct mtd_info *, loff_t, struct mtd_oob_ops *); ++ ++ if (!dual_nand_ctlr_present) ++ read_oob = msm_nand_read_oob; ++ else ++ read_oob = msm_nand_read_oob_dualnandc; ++ ++ ops.mode = MTD_OPS_PLACE_OOB; ++ ops.retlen = 0; ++ ops.ooblen = 0; ++ ops.oobbuf = NULL; ++ ret = 0; ++ *retlen = 0; ++ stats = mtd->ecc_stats; ++ ++ if ((from & (mtd->writesize - 1)) == 0 && len == mtd->writesize) { ++ /* reading a page on page boundary */ ++ ops.len = len; ++ ops.datbuf = buf; ++ ret = read_oob(mtd, from, &ops); ++ *retlen = ops.retlen; ++ } else if (len > 0) { ++ /* reading any size on any offset. partial page is supported */ ++ u8 *bounce_buf; ++ loff_t aligned_from; ++ loff_t offset; ++ size_t actual_len; ++ ++ bounce_buf = kmalloc(mtd->writesize, GFP_KERNEL); ++ if (!bounce_buf) { ++ pr_err("%s: could not allocate memory\n", __func__); ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ops.len = mtd->writesize; ++ offset = from & (mtd->writesize - 1); ++ aligned_from = from - offset; ++ ++ for (;;) { ++ int no_copy; ++ ++ actual_len = mtd->writesize - offset; ++ if (actual_len > len) ++ actual_len = len; ++ ++ no_copy = (offset == 0 && actual_len == mtd->writesize); ++ ops.datbuf = (no_copy) ? buf : bounce_buf; ++ ++ /* ++ * MTD API requires that all the pages are to ++ * be read even if uncorrectable or ++ * correctable ECC errors occur. ++ */ ++ ret = read_oob(mtd, aligned_from, &ops); ++ if (ret == -EBADMSG || ret == -EUCLEAN) ++ ret = 0; ++ ++ if (ret < 0) ++ break; ++ ++ if (!no_copy) ++ memcpy(buf, bounce_buf + offset, actual_len); ++ ++ len -= actual_len; ++ *retlen += actual_len; ++ if (len == 0) ++ break; ++ ++ buf += actual_len; ++ offset = 0; ++ aligned_from += mtd->writesize; ++ } ++ ++ kfree(bounce_buf); ++ } ++ ++out: ++ if (ret) ++ return ret; ++ ++ if (mtd->ecc_stats.failed - stats.failed) ++ return -EBADMSG; ++ ++ if (mtd->ecc_stats.corrected - stats.corrected) ++ return -EUCLEAN; ++ ++ return 0; ++} ++ ++static int ++msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ struct { ++ dmov_s cmd[8 * 7 + 2]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t chipsel; ++ uint32_t cfg0; ++ uint32_t cfg1; ++ uint32_t eccbchcfg; ++ uint32_t exec; ++ uint32_t ecccfg; ++ uint32_t clrfstatus; ++ uint32_t clrrstatus; ++ uint32_t flash_status[8]; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ unsigned n; ++ unsigned page = 0; ++ uint32_t oob_len; ++ uint32_t sectordatawritesize; ++ int err = 0; ++ dma_addr_t data_dma_addr = 0; ++ dma_addr_t oob_dma_addr = 0; ++ dma_addr_t data_dma_addr_curr = 0; ++ dma_addr_t oob_dma_addr_curr = 0; ++ uint8_t *dat_bounce_buf = NULL; ++ uint8_t *oob_bounce_buf = NULL; ++ unsigned page_count; ++ unsigned pages_written = 0; ++ unsigned cwperpage; ++#if VERBOSE ++ pr_info("=================================================" ++ "================\n"); ++ pr_info("%s:\nto 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x" ++ "\noobbuf 0x%p ooblen 0x%x\n", ++ __func__, to, ops->mode, ops->datbuf, ops->len, ++ ops->oobbuf, ops->ooblen); ++#endif ++ ++ if (mtd->writesize == 2048) ++ page = to >> 11; ++ ++ if (mtd->writesize == 4096) ++ page = to >> 12; ++ ++ oob_len = ops->ooblen; ++ cwperpage = (mtd->writesize >> 9); ++ ++ if (to & (mtd->writesize - 1)) { ++ pr_err("%s: unsupported to, 0x%llx\n", __func__, to); ++ return -EINVAL; ++ } ++ ++ if (ops->mode != MTD_OPS_RAW) { ++ if (ops->ooblen != 0 && ops->mode != MTD_OPS_AUTO_OOB) { ++ pr_err("%s: unsupported ops->mode,%d\n", ++ __func__, ops->mode); ++ return -EINVAL; ++ } ++ if ((ops->len % mtd->writesize) != 0) { ++ pr_err("%s: unsupported ops->len, %d\n", ++ __func__, ops->len); ++ return -EINVAL; ++ } ++ } else { ++ if ((ops->len % (mtd->writesize + mtd->oobsize)) != 0) { ++ pr_err("%s: unsupported ops->len, " ++ "%d for MTD_OPS_RAW mode\n", ++ __func__, ops->len); ++ return -EINVAL; ++ } ++ } ++ ++ if (ops->datbuf == NULL) { ++ pr_err("%s: unsupported ops->datbuf == NULL\n", __func__); ++ return -EINVAL; ++ } ++ if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) { ++ pr_err("%s: unsupported ops->ooboffs, %d\n", ++ __func__, ops->ooboffs); ++ return -EINVAL; ++ } ++ ++ if (ops->datbuf) { ++ data_dma_addr_curr = data_dma_addr = ++ msm_nand_dma_map(chip->dev, ops->datbuf, ++ ops->len, DMA_TO_DEVICE, ++ &dat_bounce_buf); ++ if (dma_mapping_error(chip->dev, data_dma_addr)) { ++ pr_err("msm_nand_write_oob: failed to get dma addr " ++ "for %p\n", ops->datbuf); ++ return -EIO; ++ } ++ } ++ if (ops->oobbuf) { ++ oob_dma_addr_curr = oob_dma_addr = ++ msm_nand_dma_map(chip->dev, ops->oobbuf, ++ ops->ooblen, DMA_TO_DEVICE, ++ &oob_bounce_buf); ++ if (dma_mapping_error(chip->dev, oob_dma_addr)) { ++ pr_err("msm_nand_write_oob: failed to get dma addr " ++ "for %p\n", ops->oobbuf); ++ err = -EIO; ++ goto err_dma_map_oobbuf_failed; ++ } ++ } ++ if (ops->mode != MTD_OPS_RAW) ++ page_count = ops->len / mtd->writesize; ++ else ++ page_count = ops->len / (mtd->writesize + mtd->oobsize); ++ ++ wait_event(chip->wait_queue, (dma_buffer = ++ msm_nand_get_dma_buffer(chip, sizeof(*dma_buffer)))); ++ ++ while (page_count-- > 0) { ++ cmd = dma_buffer->cmd; ++ ++ if (ops->mode != MTD_OPS_RAW) { ++ dma_buffer->data.cfg0 = chip->CFG0; ++ dma_buffer->data.cfg1 = chip->CFG1; ++ if (enable_bch_ecc) ++ dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg; ++ } else { ++ dma_buffer->data.cfg0 = (chip->CFG0_RAW & ++ ~(7U << 6)) | ((cwperpage-1) << 6); ++ dma_buffer->data.cfg1 = chip->CFG1_RAW | ++ (chip->CFG1 & CFG1_WIDE_FLASH); ++ } ++ ++ /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */ ++ dma_buffer->data.cmd = MSM_NAND_CMD_PRG_PAGE; ++ dma_buffer->data.addr0 = page << 16; ++ dma_buffer->data.addr1 = (page >> 16) & 0xff; ++ /* chipsel_0 + enable DM interface */ ++ dma_buffer->data.chipsel = 0 | 4; ++ ++ ++ /* GO bit for the EXEC register */ ++ dma_buffer->data.exec = 1; ++ dma_buffer->data.clrfstatus = 0x00000020; ++ dma_buffer->data.clrrstatus = 0x000000C0; ++ ++ BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data.flash_status)); ++ ++ for (n = 0; n < cwperpage ; n++) { ++ /* status return words */ ++ dma_buffer->data.flash_status[n] = 0xeeeeeeee; ++ /* block on cmd ready, then ++ * write CMD / ADDR0 / ADDR1 / CHIPSEL regs in a burst ++ */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = ++ msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = MSM_NAND_FLASH_CMD; ++ if (n == 0) ++ cmd->len = 16; ++ else ++ cmd->len = 4; ++ cmd++; ++ ++ if (n == 0) { ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0); ++ cmd->dst = MSM_NAND_DEV0_CFG0; ++ if (enable_bch_ecc) ++ cmd->len = 12; ++ else ++ cmd->len = 8; ++ cmd++; ++ ++ dma_buffer->data.ecccfg = chip->ecc_buf_cfg; ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.ecccfg); ++ cmd->dst = MSM_NAND_EBI2_ECC_BUF_CFG; ++ cmd->len = 4; ++ cmd++; ++ } ++ ++ /* write data block */ ++ if (ops->mode != MTD_OPS_RAW) { ++ if (!boot_layout) ++ sectordatawritesize = (n < (cwperpage - 1)) ? ++ 516 : (512 - ((cwperpage - 1) << 2)); ++ else ++ sectordatawritesize = 512; ++ } else { ++ sectordatawritesize = chip->cw_size; ++ } ++ ++ cmd->cmd = 0; ++ cmd->src = data_dma_addr_curr; ++ data_dma_addr_curr += sectordatawritesize; ++ cmd->dst = MSM_NAND_FLASH_BUFFER; ++ cmd->len = sectordatawritesize; ++ cmd++; ++ ++ if (ops->oobbuf) { ++ if (n == (cwperpage - 1)) { ++ cmd->cmd = 0; ++ cmd->src = oob_dma_addr_curr; ++ cmd->dst = MSM_NAND_FLASH_BUFFER + ++ (512 - ((cwperpage - 1) << 2)); ++ if ((cwperpage << 2) < oob_len) ++ cmd->len = (cwperpage << 2); ++ else ++ cmd->len = oob_len; ++ oob_dma_addr_curr += cmd->len; ++ oob_len -= cmd->len; ++ if (cmd->len > 0) ++ cmd++; ++ } ++ if (ops->mode != MTD_OPS_AUTO_OOB) { ++ /* skip ecc bytes in oobbuf */ ++ if (oob_len < chip->ecc_parity_bytes) { ++ oob_dma_addr_curr += ++ chip->ecc_parity_bytes; ++ oob_len -= ++ chip->ecc_parity_bytes; ++ } else { ++ oob_dma_addr_curr += oob_len; ++ oob_len = 0; ++ } ++ } ++ } ++ ++ /* kick the execute register */ ++ cmd->cmd = 0; ++ cmd->src = ++ msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = MSM_NAND_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* block on data ready, then ++ * read the status register ++ */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_FLASH_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.flash_status[n]); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.clrfstatus); ++ cmd->dst = MSM_NAND_FLASH_STATUS; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.clrrstatus); ++ cmd->dst = MSM_NAND_READ_STATUS; ++ cmd->len = 4; ++ cmd++; ++ ++ } ++ ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ BUILD_BUG_ON(8 * 7 + 2 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmdptr = ++ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | ++ CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR( ++ msm_virt_to_dma(chip, &dma_buffer->cmdptr))); ++ mb(); ++ ++ /* if any of the writes failed (0x10), or there was a ++ * protection violation (0x100), or the program success ++ * bit (0x80) is unset, we lose ++ */ ++ err = 0; ++ for (n = 0; n < cwperpage; n++) { ++ if (dma_buffer->data.flash_status[n] & 0x110) { ++ err = -EIO; ++ break; ++ } ++ if (!(dma_buffer->data.flash_status[n] & 0x80)) { ++ err = -EIO; ++ break; ++ } ++ } ++ ++#if VERBOSE ++ for (n = 0; n < cwperpage; n++) ++ pr_info("write pg %d: flash_status[%d] = %x\n", page, ++ n, dma_buffer->data.flash_status[n]); ++ ++#endif ++ if (err) ++ break; ++ pages_written++; ++ page++; ++ } ++ if (ops->mode != MTD_OPS_RAW) ++ ops->retlen = mtd->writesize * pages_written; ++ else ++ ops->retlen = (mtd->writesize + mtd->oobsize) * pages_written; ++ ++ ops->oobretlen = ops->ooblen - oob_len; ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++ if (ops->oobbuf) { ++ msm_nand_dma_unmap(chip->dev, oob_dma_addr, ++ ops->ooblen, DMA_TO_DEVICE, ++ ops->oobbuf, oob_bounce_buf); ++ } ++err_dma_map_oobbuf_failed: ++ if (ops->datbuf) { ++ msm_nand_dma_unmap(chip->dev, data_dma_addr, ops->len, ++ DMA_TO_DEVICE, ops->datbuf, ++ dat_bounce_buf); ++ } ++ if (err) ++ pr_err("msm_nand_write_oob %llx %x %x failed %d\n", ++ to, ops->len, ops->ooblen, err); ++ ++#if VERBOSE ++ pr_info("\n%s: ret %d, retlen %d oobretlen %d\n", ++ __func__, err, ops->retlen, ops->oobretlen); ++ ++ pr_info("===================================================" ++ "==============\n"); ++#endif ++ return err; ++} ++ ++static int ++msm_nand_write_oob_dualnandc(struct mtd_info *mtd, loff_t to, ++ struct mtd_oob_ops *ops) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ struct { ++ dmov_s cmd[16 * 6 + 18]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t nandc01_addr0; ++ uint32_t nandc10_addr0; ++ uint32_t nandc11_addr1; ++ uint32_t chipsel_cs0; ++ uint32_t chipsel_cs1; ++ uint32_t cfg0; ++ uint32_t cfg1; ++ uint32_t eccbchcfg; ++ uint32_t exec; ++ uint32_t ecccfg; ++ uint32_t cfg0_nc01; ++ uint32_t ebi2_chip_select_cfg0; ++ uint32_t adm_mux_data_ack_req_nc01; ++ uint32_t adm_mux_cmd_ack_req_nc01; ++ uint32_t adm_mux_data_ack_req_nc10; ++ uint32_t adm_mux_cmd_ack_req_nc10; ++ uint32_t adm_default_mux; ++ uint32_t default_ebi2_chip_select_cfg0; ++ uint32_t nc01_flash_dev_cmd_vld; ++ uint32_t nc10_flash_dev_cmd0; ++ uint32_t nc01_flash_dev_cmd_vld_default; ++ uint32_t nc10_flash_dev_cmd0_default; ++ uint32_t flash_status[16]; ++ uint32_t clrfstatus; ++ uint32_t clrrstatus; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ unsigned n; ++ unsigned page = 0; ++ uint32_t oob_len; ++ uint32_t sectordatawritesize; ++ int err = 0; ++ dma_addr_t data_dma_addr = 0; ++ dma_addr_t oob_dma_addr = 0; ++ dma_addr_t data_dma_addr_curr = 0; ++ dma_addr_t oob_dma_addr_curr = 0; ++ unsigned page_count; ++ unsigned pages_written = 0; ++ unsigned cwperpage; ++ unsigned cw_offset = chip->cw_size; ++#if VERBOSE ++ pr_info("=================================================" ++ "============\n"); ++ pr_info("%s:\nto 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x" ++ "\noobbuf 0x%p ooblen 0x%x\n\n", ++ __func__, to, ops->mode, ops->datbuf, ops->len, ++ ops->oobbuf, ops->ooblen); ++#endif ++ ++ if (mtd->writesize == 2048) ++ page = to >> 11; ++ ++ if (mtd->writesize == 4096) ++ page = to >> 12; ++ ++ if (interleave_enable) ++ page = (to >> 1) >> 12; ++ ++ oob_len = ops->ooblen; ++ cwperpage = (mtd->writesize >> 9); ++ ++ if (to & (mtd->writesize - 1)) { ++ pr_err("%s: unsupported to, 0x%llx\n", __func__, to); ++ return -EINVAL; ++ } ++ ++ if (ops->mode != MTD_OPS_RAW) { ++ if (ops->ooblen != 0 && ops->mode != MTD_OPS_AUTO_OOB) { ++ pr_err("%s: unsupported ops->mode,%d\n", ++ __func__, ops->mode); ++ return -EINVAL; ++ } ++ if ((ops->len % mtd->writesize) != 0) { ++ pr_err("%s: unsupported ops->len, %d\n", ++ __func__, ops->len); ++ return -EINVAL; ++ } ++ } else { ++ if ((ops->len % (mtd->writesize + mtd->oobsize)) != 0) { ++ pr_err("%s: unsupported ops->len, " ++ "%d for MTD_OPS_RAW mode\n", ++ __func__, ops->len); ++ return -EINVAL; ++ } ++ } ++ ++ if (ops->datbuf == NULL) { ++ pr_err("%s: unsupported ops->datbuf == NULL\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) { ++ pr_err("%s: unsupported ops->ooboffs, %d\n", ++ __func__, ops->ooboffs); ++ return -EINVAL; ++ } ++ ++ if (ops->datbuf) { ++ data_dma_addr_curr = data_dma_addr = ++ msm_nand_dma_map(chip->dev, ops->datbuf, ++ ops->len, DMA_TO_DEVICE, NULL); ++ if (dma_mapping_error(chip->dev, data_dma_addr)) { ++ pr_err("msm_nand_write_oob_dualnandc:" ++ "failed to get dma addr " ++ "for %p\n", ops->datbuf); ++ return -EIO; ++ } ++ } ++ if (ops->oobbuf) { ++ oob_dma_addr_curr = oob_dma_addr = ++ msm_nand_dma_map(chip->dev, ops->oobbuf, ++ ops->ooblen, DMA_TO_DEVICE, NULL); ++ if (dma_mapping_error(chip->dev, oob_dma_addr)) { ++ pr_err("msm_nand_write_oob_dualnandc:" ++ "failed to get dma addr " ++ "for %p\n", ops->oobbuf); ++ err = -EIO; ++ goto err_dma_map_oobbuf_failed; ++ } ++ } ++ if (ops->mode != MTD_OPS_RAW) ++ page_count = ops->len / mtd->writesize; ++ else ++ page_count = ops->len / (mtd->writesize + mtd->oobsize); ++ ++ wait_event(chip->wait_queue, (dma_buffer = ++ msm_nand_get_dma_buffer(chip, sizeof(*dma_buffer)))); ++ ++ if (chip->CFG1 & CFG1_WIDE_FLASH) ++ cw_offset >>= 1; ++ ++ dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805; ++ dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C; ++ dma_buffer->data.adm_mux_cmd_ack_req_nc01 = 0x0000053C; ++ dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28; ++ dma_buffer->data.adm_mux_cmd_ack_req_nc10 = 0x00000F14; ++ dma_buffer->data.adm_default_mux = 0x00000FC0; ++ dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801; ++ dma_buffer->data.nc01_flash_dev_cmd_vld = 0x9; ++ dma_buffer->data.nc10_flash_dev_cmd0 = 0x1085D060; ++ dma_buffer->data.nc01_flash_dev_cmd_vld_default = 0x1D; ++ dma_buffer->data.nc10_flash_dev_cmd0_default = 0x1080D060; ++ dma_buffer->data.clrfstatus = 0x00000020; ++ dma_buffer->data.clrrstatus = 0x000000C0; ++ ++ while (page_count-- > 0) { ++ cmd = dma_buffer->cmd; ++ ++ if (ops->mode != MTD_OPS_RAW) { ++ dma_buffer->data.cfg0 = ((chip->CFG0 & ~(7U << 6)) ++ & ~(1 << 4)) | ((((cwperpage >> 1)-1)) << 6); ++ dma_buffer->data.cfg1 = chip->CFG1; ++ if (enable_bch_ecc) ++ dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg; ++ } else { ++ dma_buffer->data.cfg0 = ((chip->CFG0_RAW & ++ ~(7U << 6)) & ~(1 << 4)) | (((cwperpage >> 1)-1) << 6); ++ dma_buffer->data.cfg1 = chip->CFG1_RAW | ++ (chip->CFG1 & CFG1_WIDE_FLASH); ++ } ++ ++ /* Disables the automatic issuing of the read ++ * status command for first NAND controller. ++ */ ++ if (!interleave_enable) ++ dma_buffer->data.cfg0_nc01 = dma_buffer->data.cfg0 ++ | (1 << 4); ++ else ++ dma_buffer->data.cfg0 |= (1 << 4); ++ ++ dma_buffer->data.cmd = MSM_NAND_CMD_PRG_PAGE; ++ dma_buffer->data.chipsel_cs0 = (1<<4) | 4; ++ dma_buffer->data.chipsel_cs1 = (1<<4) | 5; ++ ++ /* GO bit for the EXEC register */ ++ dma_buffer->data.exec = 1; ++ ++ if (!interleave_enable) { ++ dma_buffer->data.nandc01_addr0 = (page << 16) | 0x0; ++ /* NC10 ADDR0 points to the next code word */ ++ dma_buffer->data.nandc10_addr0 = ++ (page << 16) | cw_offset; ++ } else { ++ dma_buffer->data.nandc01_addr0 = ++ dma_buffer->data.nandc10_addr0 = (page << 16) | 0x0; ++ } ++ /* ADDR1 */ ++ dma_buffer->data.nandc11_addr1 = (page >> 16) & 0xff; ++ ++ BUILD_BUG_ON(16 != ARRAY_SIZE(dma_buffer->data.flash_status)); ++ ++ for (n = 0; n < cwperpage; n++) { ++ /* status return words */ ++ dma_buffer->data.flash_status[n] = 0xeeeeeeee; ++ ++ if (n == 0) { ++ if (!interleave_enable) { ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.nc01_flash_dev_cmd_vld); ++ cmd->dst = NC01(MSM_NAND_DEV_CMD_VLD); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nc10_flash_dev_cmd0); ++ cmd->dst = NC10(MSM_NAND_DEV_CMD0); ++ cmd->len = 4; ++ cmd++; ++ ++ /* common settings for both NC01 & NC10 ++ * NC01, NC10 --> ADDR1 / CHIPSEL ++ */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nandc11_addr1); ++ cmd->dst = NC11(MSM_NAND_ADDR1); ++ cmd->len = 8; ++ cmd++; ++ ++ /* Disables the automatic issue of the ++ * read status command after the write ++ * operation. ++ */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0_nc01); ++ cmd->dst = NC01(MSM_NAND_DEV0_CFG0); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0); ++ cmd->dst = NC10(MSM_NAND_DEV0_CFG0); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg1); ++ cmd->dst = NC11(MSM_NAND_DEV0_CFG1); ++ if (enable_bch_ecc) ++ cmd->len = 8; ++ else ++ cmd->len = 4; ++ cmd++; ++ } else { ++ /* enable CS1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.ebi2_chip_select_cfg0); ++ cmd->dst = EBI2_CHIP_SELECT_CFG0; ++ cmd->len = 4; ++ cmd++; ++ ++ /* NC11 --> ADDR1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nandc11_addr1); ++ cmd->dst = NC11(MSM_NAND_ADDR1); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Enable CS0 for NC01 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.chipsel_cs0); ++ cmd->dst = ++ NC01(MSM_NAND_FLASH_CHIP_SELECT); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Enable CS1 for NC10 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.chipsel_cs1); ++ cmd->dst = ++ NC10(MSM_NAND_FLASH_CHIP_SELECT); ++ cmd->len = 4; ++ cmd++; ++ ++ /* config DEV0_CFG0 & CFG1 for CS0 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0); ++ cmd->dst = NC01(MSM_NAND_DEV0_CFG0); ++ cmd->len = 8; ++ cmd++; ++ ++ /* config DEV1_CFG0 & CFG1 for CS1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0); ++ cmd->dst = NC10(MSM_NAND_DEV1_CFG0); ++ cmd->len = 8; ++ cmd++; ++ } ++ ++ dma_buffer->data.ecccfg = chip->ecc_buf_cfg; ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.ecccfg); ++ cmd->dst = NC11(MSM_NAND_EBI2_ECC_BUF_CFG); ++ cmd->len = 4; ++ cmd++; ++ ++ /* NC01 --> ADDR0 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nandc01_addr0); ++ cmd->dst = NC01(MSM_NAND_ADDR0); ++ cmd->len = 4; ++ cmd++; ++ ++ /* NC10 --> ADDR0 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nandc10_addr0); ++ cmd->dst = NC10(MSM_NAND_ADDR0); ++ cmd->len = 4; ++ cmd++; ++ } ++ ++ if (n % 2 == 0) { ++ /* MASK CMD ACK/REQ --> NC10 (0xF14)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_cmd_ack_req_nc10); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* CMD */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cmd); ++ cmd->dst = NC01(MSM_NAND_FLASH_CMD); ++ cmd->len = 4; ++ cmd++; ++ } else { ++ /* MASK CMD ACK/REQ --> NC01 (0x53C)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_cmd_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* CMD */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cmd); ++ cmd->dst = NC10(MSM_NAND_FLASH_CMD); ++ cmd->len = 4; ++ cmd++; ++ } ++ ++ if (ops->mode != MTD_OPS_RAW) ++ sectordatawritesize = (n < (cwperpage - 1)) ? ++ 516 : (512 - ((cwperpage - 1) << 2)); ++ else ++ sectordatawritesize = chip->cw_size; ++ ++ cmd->cmd = 0; ++ cmd->src = data_dma_addr_curr; ++ data_dma_addr_curr += sectordatawritesize; ++ ++ if (n % 2 == 0) ++ cmd->dst = NC01(MSM_NAND_FLASH_BUFFER); ++ else ++ cmd->dst = NC10(MSM_NAND_FLASH_BUFFER); ++ cmd->len = sectordatawritesize; ++ cmd++; ++ ++ if (ops->oobbuf) { ++ if (n == (cwperpage - 1)) { ++ cmd->cmd = 0; ++ cmd->src = oob_dma_addr_curr; ++ cmd->dst = NC10(MSM_NAND_FLASH_BUFFER) + ++ (512 - ((cwperpage - 1) << 2)); ++ if ((cwperpage << 2) < oob_len) ++ cmd->len = (cwperpage << 2); ++ else ++ cmd->len = oob_len; ++ oob_dma_addr_curr += cmd->len; ++ oob_len -= cmd->len; ++ if (cmd->len > 0) ++ cmd++; ++ } ++ if (ops->mode != MTD_OPS_AUTO_OOB) { ++ /* skip ecc bytes in oobbuf */ ++ if (oob_len < chip->ecc_parity_bytes) { ++ oob_dma_addr_curr += ++ chip->ecc_parity_bytes; ++ oob_len -= ++ chip->ecc_parity_bytes; ++ } else { ++ oob_dma_addr_curr += oob_len; ++ oob_len = 0; ++ } ++ } ++ } ++ ++ if (n % 2 == 0) { ++ if (n != 0) { ++ /* MASK DATA ACK/REQ --> NC01 (0xA3C)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.adm_mux_data_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* block on data ready from NC10, then ++ * read the status register ++ */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = NC10(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.flash_status[n-1]); ++ cmd->len = 4; ++ cmd++; ++ } ++ /* kick the NC01 execute register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.exec); ++ cmd->dst = NC01(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ } else { ++ /* MASK DATA ACK/REQ --> NC10 (0xF28)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_data_ack_req_nc10); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* block on data ready from NC01, then ++ * read the status register ++ */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = NC01(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.flash_status[n-1]); ++ cmd->len = 4; ++ cmd++; ++ ++ /* kick the execute register */ ++ cmd->cmd = 0; ++ cmd->src = ++ msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = NC10(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ } ++ } ++ ++ /* MASK DATA ACK/REQ --> NC01 (0xA3C)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_data_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* we should process outstanding request */ ++ /* block on data ready, then ++ * read the status register ++ */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = NC10(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.flash_status[n-1]); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrfstatus); ++ cmd->dst = NC11(MSM_NAND_FLASH_STATUS); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrrstatus); ++ cmd->dst = NC11(MSM_NAND_READ_STATUS); ++ cmd->len = 4; ++ cmd++; ++ ++ /* MASK DATA ACK/REQ --> NC01 (0xFC0)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_default_mux); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ if (!interleave_enable) { ++ /* setting to defalut values back */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nc01_flash_dev_cmd_vld_default); ++ cmd->dst = NC01(MSM_NAND_DEV_CMD_VLD); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nc10_flash_dev_cmd0_default); ++ cmd->dst = NC10(MSM_NAND_DEV_CMD0); ++ cmd->len = 4; ++ cmd++; ++ } else { ++ /* disable CS1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.default_ebi2_chip_select_cfg0); ++ cmd->dst = EBI2_CHIP_SELECT_CFG0; ++ cmd->len = 4; ++ cmd++; ++ } ++ ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ BUILD_BUG_ON(16 * 6 + 18 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmdptr = ++ ((msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP); ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR( ++ msm_virt_to_dma(chip, &dma_buffer->cmdptr))); ++ mb(); ++ ++ /* if any of the writes failed (0x10), or there was a ++ * protection violation (0x100), or the program success ++ * bit (0x80) is unset, we lose ++ */ ++ err = 0; ++ for (n = 0; n < cwperpage; n++) { ++ if (dma_buffer->data.flash_status[n] & 0x110) { ++ err = -EIO; ++ break; ++ } ++ if (!(dma_buffer->data.flash_status[n] & 0x80)) { ++ err = -EIO; ++ break; ++ } ++ } ++ /* check for flash status busy for the last codeword */ ++ if (!interleave_enable) ++ if (!(dma_buffer->data.flash_status[cwperpage - 1] ++ & 0x20)) { ++ err = -EIO; ++ break; ++ } ++#if VERBOSE ++ for (n = 0; n < cwperpage; n++) { ++ if (n%2) { ++ pr_info("NC10: write pg %d: flash_status[%d] = %x\n", ++ page, n, dma_buffer->data.flash_status[n]); ++ } else { ++ pr_info("NC01: write pg %d: flash_status[%d] = %x\n", ++ page, n, dma_buffer->data.flash_status[n]); ++ } ++ } ++#endif ++ if (err) ++ break; ++ pages_written++; ++ page++; ++ } ++ if (ops->mode != MTD_OPS_RAW) ++ ops->retlen = mtd->writesize * pages_written; ++ else ++ ops->retlen = (mtd->writesize + mtd->oobsize) * pages_written; ++ ++ ops->oobretlen = ops->ooblen - oob_len; ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++ if (ops->oobbuf) ++ dma_unmap_page(chip->dev, oob_dma_addr, ++ ops->ooblen, DMA_TO_DEVICE); ++err_dma_map_oobbuf_failed: ++ if (ops->datbuf) ++ dma_unmap_page(chip->dev, data_dma_addr, ops->len, ++ DMA_TO_DEVICE); ++ if (err) ++ pr_err("msm_nand_write_oob_dualnandc %llx %x %x failed %d\n", ++ to, ops->len, ops->ooblen, err); ++ ++#if VERBOSE ++ pr_info("\n%s: ret %d, retlen %d oobretlen %d\n", ++ __func__, err, ops->retlen, ops->oobretlen); ++ ++ pr_info("===================================================" ++ "==========\n"); ++#endif ++ return err; ++} ++ ++static int msm_nand_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ int ret; ++ struct mtd_oob_ops ops; ++ int (*write_oob)(struct mtd_info *, loff_t, struct mtd_oob_ops *); ++ ++ if (!dual_nand_ctlr_present) ++ write_oob = msm_nand_write_oob; ++ else ++ write_oob = msm_nand_write_oob_dualnandc; ++ ++ ops.mode = MTD_OPS_PLACE_OOB; ++ ops.retlen = 0; ++ ops.ooblen = 0; ++ ops.oobbuf = NULL; ++ ret = 0; ++ *retlen = 0; ++ ++ if (!virt_addr_valid(buf) && ++ ((to | len) & (mtd->writesize - 1)) == 0 && ++ ((unsigned long) buf & ~PAGE_MASK) + len > PAGE_SIZE) { ++ /* ++ * Handle writing of large size write buffer in vmalloc ++ * address space that does not fit in an MMU page. ++ * The destination address must be on page boundary, ++ * and the size must be multiple of NAND page size. ++ * Writing partial page is not supported. ++ */ ++ ops.len = mtd->writesize; ++ ++ for (;;) { ++ ops.datbuf = (uint8_t *) buf; ++ ++ ret = write_oob(mtd, to, &ops); ++ if (ret < 0) ++ break; ++ ++ len -= mtd->writesize; ++ *retlen += mtd->writesize; ++ if (len == 0) ++ break; ++ ++ buf += mtd->writesize; ++ to += mtd->writesize; ++ } ++ } else { ++ ops.len = len; ++ ops.datbuf = (uint8_t *) buf; ++ ret = write_oob(mtd, to, &ops); ++ *retlen = ops.retlen; ++ } ++ ++ return ret; ++} ++ ++static int ++msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ int err; ++ struct msm_nand_chip *chip = mtd->priv; ++ struct { ++ dmov_s cmd[6]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t chipsel; ++ uint32_t cfg0; ++ uint32_t cfg1; ++ uint32_t exec; ++ uint32_t flash_status; ++ uint32_t clrfstatus; ++ uint32_t clrrstatus; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ unsigned page = 0; ++ ++ if (mtd->writesize == 2048) ++ page = instr->addr >> 11; ++ ++ if (mtd->writesize == 4096) ++ page = instr->addr >> 12; ++ ++ if (instr->addr & (mtd->erasesize - 1)) { ++ pr_err("%s: unsupported erase address, 0x%llx\n", ++ __func__, instr->addr); ++ return -EINVAL; ++ } ++ if (instr->len != mtd->erasesize) { ++ pr_err("%s: unsupported erase len, %lld\n", ++ __func__, instr->len); ++ return -EINVAL; ++ } ++ ++ wait_event(chip->wait_queue, ++ (dma_buffer = msm_nand_get_dma_buffer( ++ chip, sizeof(*dma_buffer)))); ++ ++ cmd = dma_buffer->cmd; ++ ++ dma_buffer->data.cmd = MSM_NAND_CMD_BLOCK_ERASE; ++ dma_buffer->data.addr0 = page; ++ dma_buffer->data.addr1 = 0; ++ dma_buffer->data.chipsel = 0 | 4; ++ dma_buffer->data.exec = 1; ++ dma_buffer->data.flash_status = 0xeeeeeeee; ++ dma_buffer->data.cfg0 = chip->CFG0 & (~(7 << 6)); /* CW_PER_PAGE = 0 */ ++ dma_buffer->data.cfg1 = chip->CFG1; ++ dma_buffer->data.clrfstatus = 0x00000020; ++ dma_buffer->data.clrrstatus = 0x000000C0; ++ ++ cmd->cmd = DST_CRCI_NAND_CMD | CMD_OCB; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = MSM_NAND_FLASH_CMD; ++ cmd->len = 16; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); ++ cmd->dst = MSM_NAND_DEV0_CFG0; ++ cmd->len = 8; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = MSM_NAND_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_FLASH_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrfstatus); ++ cmd->dst = MSM_NAND_FLASH_STATUS; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = CMD_OCU | CMD_LC; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrrstatus); ++ cmd->dst = MSM_NAND_READ_STATUS; ++ cmd->len = 4; ++ cmd++; ++ ++ BUILD_BUG_ON(5 != ARRAY_SIZE(dma_buffer->cmd) - 1); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmdptr = ++ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd( ++ chip->dma_channel, DMOV_CMD_PTR_LIST | ++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); ++ mb(); ++ ++ /* we fail if there was an operation error, a mpu error, or the ++ * erase success bit was not set. ++ */ ++ ++ if (dma_buffer->data.flash_status & 0x110 || ++ !(dma_buffer->data.flash_status & 0x80)) ++ err = -EIO; ++ else ++ err = 0; ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ if (err) { ++ pr_err("%s: erase failed, 0x%llx\n", __func__, instr->addr); ++ instr->fail_addr = instr->addr; ++ instr->state = MTD_ERASE_FAILED; ++ } else { ++ instr->state = MTD_ERASE_DONE; ++ instr->fail_addr = 0xffffffff; ++ mtd_erase_callback(instr); ++ } ++ return err; ++} ++ ++static int ++msm_nand_erase_dualnandc(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ int err; ++ struct msm_nand_chip *chip = mtd->priv; ++ struct { ++ dmov_s cmd[18]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t chipsel_cs0; ++ uint32_t chipsel_cs1; ++ uint32_t cfg0; ++ uint32_t cfg1; ++ uint32_t exec; ++ uint32_t ecccfg; ++ uint32_t ebi2_chip_select_cfg0; ++ uint32_t adm_mux_data_ack_req_nc01; ++ uint32_t adm_mux_cmd_ack_req_nc01; ++ uint32_t adm_mux_data_ack_req_nc10; ++ uint32_t adm_mux_cmd_ack_req_nc10; ++ uint32_t adm_default_mux; ++ uint32_t default_ebi2_chip_select_cfg0; ++ uint32_t nc01_flash_dev_cmd0; ++ uint32_t nc01_flash_dev_cmd0_default; ++ uint32_t flash_status[2]; ++ uint32_t clrfstatus; ++ uint32_t clrrstatus; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ unsigned page = 0; ++ ++ if (mtd->writesize == 2048) ++ page = instr->addr >> 11; ++ ++ if (mtd->writesize == 4096) ++ page = instr->addr >> 12; ++ ++ if (mtd->writesize == 8192) ++ page = (instr->addr >> 1) >> 12; ++ ++ if (instr->addr & (mtd->erasesize - 1)) { ++ pr_err("%s: unsupported erase address, 0x%llx\n", ++ __func__, instr->addr); ++ return -EINVAL; ++ } ++ if (instr->len != mtd->erasesize) { ++ pr_err("%s: unsupported erase len, %lld\n", ++ __func__, instr->len); ++ return -EINVAL; ++ } ++ ++ wait_event(chip->wait_queue, ++ (dma_buffer = msm_nand_get_dma_buffer( ++ chip, sizeof(*dma_buffer)))); ++ ++ cmd = dma_buffer->cmd; ++ ++ dma_buffer->data.cmd = MSM_NAND_CMD_BLOCK_ERASE; ++ dma_buffer->data.addr0 = page; ++ dma_buffer->data.addr1 = 0; ++ dma_buffer->data.chipsel_cs0 = (1<<4) | 4; ++ dma_buffer->data.chipsel_cs1 = (1<<4) | 5; ++ dma_buffer->data.exec = 1; ++ dma_buffer->data.flash_status[0] = 0xeeeeeeee; ++ dma_buffer->data.flash_status[1] = 0xeeeeeeee; ++ dma_buffer->data.cfg0 = chip->CFG0 & (~(7 << 6)); /* CW_PER_PAGE = 0 */ ++ dma_buffer->data.cfg1 = chip->CFG1; ++ dma_buffer->data.clrfstatus = 0x00000020; ++ dma_buffer->data.clrrstatus = 0x000000C0; ++ ++ dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805; ++ dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C; ++ dma_buffer->data.adm_mux_cmd_ack_req_nc01 = 0x0000053C; ++ dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28; ++ dma_buffer->data.adm_mux_cmd_ack_req_nc10 = 0x00000F14; ++ dma_buffer->data.adm_default_mux = 0x00000FC0; ++ dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801; ++ ++ /* enable CS1 */ ++ cmd->cmd = 0 | CMD_OCB; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.ebi2_chip_select_cfg0); ++ cmd->dst = EBI2_CHIP_SELECT_CFG0; ++ cmd->len = 4; ++ cmd++; ++ ++ /* erase CS0 block now !!! */ ++ /* 0xF14 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_cmd_ack_req_nc10); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = NC01(MSM_NAND_FLASH_CMD); ++ cmd->len = 16; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); ++ cmd->dst = NC01(MSM_NAND_DEV0_CFG0); ++ cmd->len = 8; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = NC01(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* 0xF28 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_data_ack_req_nc10); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = NC01(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status[0]); ++ cmd->len = 4; ++ cmd++; ++ ++ /* erase CS1 block now !!! */ ++ /* 0x53C */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_cmd_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = NC10(MSM_NAND_FLASH_CMD); ++ cmd->len = 12; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.chipsel_cs1); ++ cmd->dst = NC10(MSM_NAND_FLASH_CHIP_SELECT); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); ++ cmd->dst = NC10(MSM_NAND_DEV1_CFG0); ++ cmd->len = 8; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = NC10(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* 0xA3C */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_data_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = NC10(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status[1]); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrfstatus); ++ cmd->dst = NC11(MSM_NAND_FLASH_STATUS); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrrstatus); ++ cmd->dst = NC11(MSM_NAND_READ_STATUS); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_default_mux); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* disable CS1 */ ++ cmd->cmd = CMD_OCU | CMD_LC; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.default_ebi2_chip_select_cfg0); ++ cmd->dst = EBI2_CHIP_SELECT_CFG0; ++ cmd->len = 4; ++ cmd++; ++ ++ BUILD_BUG_ON(17 != ARRAY_SIZE(dma_buffer->cmd) - 1); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ ++ dma_buffer->cmdptr = ++ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd( ++ chip->dma_channel, DMOV_CMD_PTR_LIST | ++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); ++ mb(); ++ ++ /* we fail if there was an operation error, a mpu error, or the ++ * erase success bit was not set. ++ */ ++ ++ if (dma_buffer->data.flash_status[0] & 0x110 || ++ !(dma_buffer->data.flash_status[0] & 0x80) || ++ dma_buffer->data.flash_status[1] & 0x110 || ++ !(dma_buffer->data.flash_status[1] & 0x80)) ++ err = -EIO; ++ else ++ err = 0; ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ if (err) { ++ pr_err("%s: erase failed, 0x%llx\n", __func__, instr->addr); ++ instr->fail_addr = instr->addr; ++ instr->state = MTD_ERASE_FAILED; ++ } else { ++ instr->state = MTD_ERASE_DONE; ++ instr->fail_addr = 0xffffffff; ++ mtd_erase_callback(instr); ++ } ++ return err; ++} ++ ++static int ++msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ int ret; ++ struct { ++ dmov_s cmd[5]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t chipsel; ++ uint32_t cfg0; ++ uint32_t cfg1; ++ uint32_t eccbchcfg; ++ uint32_t exec; ++ uint32_t ecccfg; ++ struct { ++ uint32_t flash_status; ++ uint32_t buffer_status; ++ } result; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ uint8_t *buf; ++ unsigned page = 0; ++ unsigned cwperpage; ++ ++ if (mtd->writesize == 2048) ++ page = ofs >> 11; ++ ++ if (mtd->writesize == 4096) ++ page = ofs >> 12; ++ ++ cwperpage = (mtd->writesize >> 9); ++ ++ /* Check for invalid offset */ ++ if (ofs > mtd->size) ++ return -EINVAL; ++ if (ofs & (mtd->erasesize - 1)) { ++ pr_err("%s: unsupported block address, 0x%x\n", ++ __func__, (uint32_t)ofs); ++ return -EINVAL; ++ } ++ ++ wait_event(chip->wait_queue, ++ (dma_buffer = msm_nand_get_dma_buffer(chip , ++ sizeof(*dma_buffer) + 4))); ++ buf = (uint8_t *)dma_buffer + sizeof(*dma_buffer); ++ ++ /* Read 4 bytes starting from the bad block marker location ++ * in the last code word of the page ++ */ ++ ++ cmd = dma_buffer->cmd; ++ ++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; ++ dma_buffer->data.cfg0 = chip->CFG0_RAW & ~(7U << 6); ++ dma_buffer->data.cfg1 = chip->CFG1_RAW | ++ (chip->CFG1 & CFG1_WIDE_FLASH); ++ if (enable_bch_ecc) ++ dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg; ++ ++ if (chip->CFG1 & CFG1_WIDE_FLASH) ++ dma_buffer->data.addr0 = (page << 16) | ++ ((chip->cw_size * (cwperpage-1)) >> 1); ++ else ++ dma_buffer->data.addr0 = (page << 16) | ++ (chip->cw_size * (cwperpage-1)); ++ ++ dma_buffer->data.addr1 = (page >> 16) & 0xff; ++ dma_buffer->data.chipsel = 0 | 4; ++ ++ dma_buffer->data.exec = 1; ++ ++ dma_buffer->data.result.flash_status = 0xeeeeeeee; ++ dma_buffer->data.result.buffer_status = 0xeeeeeeee; ++ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = MSM_NAND_FLASH_CMD; ++ cmd->len = 16; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); ++ cmd->dst = MSM_NAND_DEV0_CFG0; ++ if (enable_bch_ecc) ++ cmd->len = 12; ++ else ++ cmd->len = 8; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = MSM_NAND_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_FLASH_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result); ++ cmd->len = 8; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_FLASH_BUFFER + ++ (mtd->writesize - (chip->cw_size * (cwperpage-1))); ++ cmd->dst = msm_virt_to_dma(chip, buf); ++ cmd->len = 4; ++ cmd++; ++ ++ BUILD_BUG_ON(5 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, ++ dma_buffer->cmd) >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST | ++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); ++ mb(); ++ ++ ret = 0; ++ if (dma_buffer->data.result.flash_status & 0x110) ++ ret = -EIO; ++ ++ if (!ret) { ++ /* Check for bad block marker byte */ ++ if (chip->CFG1 & CFG1_WIDE_FLASH) { ++ if (buf[0] != 0xFF || buf[1] != 0xFF) ++ ret = 1; ++ } else { ++ if (buf[0] != 0xFF) ++ ret = 1; ++ } ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer) + 4); ++ return ret; ++} ++ ++static int ++msm_nand_block_isbad_dualnandc(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ int ret; ++ struct { ++ dmov_s cmd[18]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t chipsel_cs0; ++ uint32_t chipsel_cs1; ++ uint32_t cfg0; ++ uint32_t cfg1; ++ uint32_t exec; ++ uint32_t ecccfg; ++ uint32_t ebi2_chip_select_cfg0; ++ uint32_t adm_mux_data_ack_req_nc01; ++ uint32_t adm_mux_cmd_ack_req_nc01; ++ uint32_t adm_mux_data_ack_req_nc10; ++ uint32_t adm_mux_cmd_ack_req_nc10; ++ uint32_t adm_default_mux; ++ uint32_t default_ebi2_chip_select_cfg0; ++ struct { ++ uint32_t flash_status; ++ uint32_t buffer_status; ++ } result[2]; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ uint8_t *buf01; ++ uint8_t *buf10; ++ unsigned page = 0; ++ unsigned cwperpage; ++ ++ if (mtd->writesize == 2048) ++ page = ofs >> 11; ++ ++ if (mtd->writesize == 4096) ++ page = ofs >> 12; ++ ++ if (mtd->writesize == 8192) ++ page = (ofs >> 1) >> 12; ++ ++ cwperpage = ((mtd->writesize >> 1) >> 9); ++ ++ /* Check for invalid offset */ ++ if (ofs > mtd->size) ++ return -EINVAL; ++ if (ofs & (mtd->erasesize - 1)) { ++ pr_err("%s: unsupported block address, 0x%x\n", ++ __func__, (uint32_t)ofs); ++ return -EINVAL; ++ } ++ ++ wait_event(chip->wait_queue, ++ (dma_buffer = msm_nand_get_dma_buffer(chip , ++ sizeof(*dma_buffer) + 8))); ++ buf01 = (uint8_t *)dma_buffer + sizeof(*dma_buffer); ++ buf10 = buf01 + 4; ++ ++ /* Read 4 bytes starting from the bad block marker location ++ * in the last code word of the page ++ */ ++ cmd = dma_buffer->cmd; ++ ++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; ++ dma_buffer->data.cfg0 = chip->CFG0_RAW & ~(7U << 6); ++ dma_buffer->data.cfg1 = chip->CFG1_RAW | ++ (chip->CFG1 & CFG1_WIDE_FLASH); ++ ++ if (chip->CFG1 & CFG1_WIDE_FLASH) ++ dma_buffer->data.addr0 = (page << 16) | ++ ((528*(cwperpage-1)) >> 1); ++ else ++ dma_buffer->data.addr0 = (page << 16) | ++ (528*(cwperpage-1)); ++ ++ dma_buffer->data.addr1 = (page >> 16) & 0xff; ++ dma_buffer->data.chipsel_cs0 = (1<<4) | 4; ++ dma_buffer->data.chipsel_cs1 = (1<<4) | 5; ++ ++ dma_buffer->data.exec = 1; ++ ++ dma_buffer->data.result[0].flash_status = 0xeeeeeeee; ++ dma_buffer->data.result[0].buffer_status = 0xeeeeeeee; ++ dma_buffer->data.result[1].flash_status = 0xeeeeeeee; ++ dma_buffer->data.result[1].buffer_status = 0xeeeeeeee; ++ ++ dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805; ++ dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C; ++ dma_buffer->data.adm_mux_cmd_ack_req_nc01 = 0x0000053C; ++ dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28; ++ dma_buffer->data.adm_mux_cmd_ack_req_nc10 = 0x00000F14; ++ dma_buffer->data.adm_default_mux = 0x00000FC0; ++ dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801; ++ ++ /* Reading last code word from NC01 */ ++ /* enable CS1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.ebi2_chip_select_cfg0); ++ cmd->dst = EBI2_CHIP_SELECT_CFG0; ++ cmd->len = 4; ++ cmd++; ++ ++ /* 0xF14 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_cmd_ack_req_nc10); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = NC01(MSM_NAND_FLASH_CMD); ++ cmd->len = 16; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); ++ cmd->dst = NC01(MSM_NAND_DEV0_CFG0); ++ cmd->len = 8; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = NC01(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* 0xF28 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_data_ack_req_nc10); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = NC01(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result[0]); ++ cmd->len = 8; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = NC01(MSM_NAND_FLASH_BUFFER) + ((mtd->writesize >> 1) - ++ (528*(cwperpage-1))); ++ cmd->dst = msm_virt_to_dma(chip, buf01); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Reading last code word from NC10 */ ++ /* 0x53C */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_cmd_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = NC10(MSM_NAND_FLASH_CMD); ++ cmd->len = 12; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.chipsel_cs1); ++ cmd->dst = NC10(MSM_NAND_FLASH_CHIP_SELECT); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); ++ cmd->dst = NC10(MSM_NAND_DEV1_CFG0); ++ cmd->len = 8; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = NC10(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* A3C */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_data_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = NC10(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result[1]); ++ cmd->len = 8; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = NC10(MSM_NAND_FLASH_BUFFER) + ((mtd->writesize >> 1) - ++ (528*(cwperpage-1))); ++ cmd->dst = msm_virt_to_dma(chip, buf10); ++ cmd->len = 4; ++ cmd++; ++ ++ /* FC0 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_default_mux); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* disble CS1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.ebi2_chip_select_cfg0); ++ cmd->dst = EBI2_CHIP_SELECT_CFG0; ++ cmd->len = 4; ++ cmd++; ++ ++ BUILD_BUG_ON(18 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, ++ dma_buffer->cmd) >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST | ++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); ++ mb(); ++ ++ ret = 0; ++ if ((dma_buffer->data.result[0].flash_status & 0x110) || ++ (dma_buffer->data.result[1].flash_status & 0x110)) ++ ret = -EIO; ++ ++ if (!ret) { ++ /* Check for bad block marker byte for NC01 & NC10 */ ++ if (chip->CFG1 & CFG1_WIDE_FLASH) { ++ if ((buf01[0] != 0xFF || buf01[1] != 0xFF) || ++ (buf10[0] != 0xFF || buf10[1] != 0xFF)) ++ ret = 1; ++ } else { ++ if (buf01[0] != 0xFF || buf10[0] != 0xFF) ++ ret = 1; ++ } ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer) + 8); ++ return ret; ++} ++ ++static int ++msm_nand_block_markbad(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct mtd_oob_ops ops; ++ int ret; ++ uint8_t *buf; ++ ++ /* Check for invalid offset */ ++ if (ofs > mtd->size) ++ return -EINVAL; ++ if (ofs & (mtd->erasesize - 1)) { ++ pr_err("%s: unsupported block address, 0x%x\n", ++ __func__, (uint32_t)ofs); ++ return -EINVAL; ++ } ++ ++ /* ++ Write all 0s to the first page ++ This will set the BB marker to 0 ++ */ ++ buf = page_address(ZERO_PAGE()); ++ ++ ops.mode = MTD_OPS_RAW; ++ ops.len = mtd->writesize + mtd->oobsize; ++ ops.retlen = 0; ++ ops.ooblen = 0; ++ ops.datbuf = buf; ++ ops.oobbuf = NULL; ++ if (!interleave_enable) ++ ret = msm_nand_write_oob(mtd, ofs, &ops); ++ else ++ ret = msm_nand_write_oob_dualnandc(mtd, ofs, &ops); ++ ++ return ret; ++} ++ ++/** ++ * msm_nand_suspend - [MTD Interface] Suspend the msm_nand flash ++ * @param mtd MTD device structure ++ */ ++static int msm_nand_suspend(struct mtd_info *mtd) ++{ ++ return 0; ++} ++ ++/** ++ * msm_nand_resume - [MTD Interface] Resume the msm_nand flash ++ * @param mtd MTD device structure ++ */ ++static void msm_nand_resume(struct mtd_info *mtd) ++{ ++} ++ ++struct onenand_information { ++ uint16_t manufacturer_id; ++ uint16_t device_id; ++ uint16_t version_id; ++ uint16_t data_buf_size; ++ uint16_t boot_buf_size; ++ uint16_t num_of_buffers; ++ uint16_t technology; ++}; ++ ++static struct onenand_information onenand_info; ++static uint32_t nand_sfcmd_mode; ++ ++uint32_t flash_onenand_probe(struct msm_nand_chip *chip) ++{ ++ struct { ++ dmov_s cmd[7]; ++ unsigned cmdptr; ++ struct { ++ uint32_t bcfg; ++ uint32_t cmd; ++ uint32_t exec; ++ uint32_t status; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t addr2; ++ uint32_t addr3; ++ uint32_t addr4; ++ uint32_t addr5; ++ uint32_t addr6; ++ uint32_t data0; ++ uint32_t data1; ++ uint32_t data2; ++ uint32_t data3; ++ uint32_t data4; ++ uint32_t data5; ++ uint32_t data6; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ ++ int err = 0; ++ uint32_t initialsflashcmd = 0; ++ ++ initialsflashcmd = flash_rd_reg(chip, MSM_NAND_SFLASHC_CMD); ++ ++ if ((initialsflashcmd & 0x10) == 0x10) ++ nand_sfcmd_mode = MSM_NAND_SFCMD_ASYNC; ++ else ++ nand_sfcmd_mode = MSM_NAND_SFCMD_BURST; ++ ++ printk(KERN_INFO "SFLASHC Async Mode bit: %x \n", nand_sfcmd_mode); ++ ++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer ++ (chip, sizeof(*dma_buffer)))); ++ ++ cmd = dma_buffer->cmd; ++ ++ dma_buffer->data.bcfg = SFLASH_BCFG | ++ (nand_sfcmd_mode ? 0 : (1 << 24)); ++ dma_buffer->data.cmd = SFLASH_PREPCMD(7, 0, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGRD); ++ dma_buffer->data.exec = 1; ++ dma_buffer->data.status = CLEAN_DATA_32; ++ dma_buffer->data.addr0 = (ONENAND_DEVICE_ID << 16) | ++ (ONENAND_MANUFACTURER_ID); ++ dma_buffer->data.addr1 = (ONENAND_DATA_BUFFER_SIZE << 16) | ++ (ONENAND_VERSION_ID); ++ dma_buffer->data.addr2 = (ONENAND_AMOUNT_OF_BUFFERS << 16) | ++ (ONENAND_BOOT_BUFFER_SIZE); ++ dma_buffer->data.addr3 = (CLEAN_DATA_16 << 16) | ++ (ONENAND_TECHNOLOGY << 0); ++ dma_buffer->data.data0 = CLEAN_DATA_32; ++ dma_buffer->data.data1 = CLEAN_DATA_32; ++ dma_buffer->data.data2 = CLEAN_DATA_32; ++ dma_buffer->data.data3 = CLEAN_DATA_32; ++ ++ /* Enable and configure the SFlash controller */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.bcfg); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Configure the ADDR0 and ADDR1 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0); ++ cmd->dst = MSM_NAND_ADDR0; ++ cmd->len = 8; ++ cmd++; ++ ++ /* Configure the ADDR2 and ADDR3 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2); ++ cmd->dst = MSM_NAND_ADDR2; ++ cmd->len = 8; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the two status registers */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.status); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read data registers - valid only if status says success */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_GENP_REG0; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data0); ++ cmd->len = 16; ++ cmd++; ++ ++ BUILD_BUG_ON(7 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) ++ >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST ++ | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ ++ /* Check for errors, protection violations etc */ ++ if (dma_buffer->data.status & 0x110) { ++ pr_info("%s: MPU/OP error" ++ "(0x%x) during Onenand probe\n", ++ __func__, dma_buffer->data.status); ++ err = -EIO; ++ } else { ++ ++ onenand_info.manufacturer_id = ++ (dma_buffer->data.data0 >> 0) & 0x0000FFFF; ++ onenand_info.device_id = ++ (dma_buffer->data.data0 >> 16) & 0x0000FFFF; ++ onenand_info.version_id = ++ (dma_buffer->data.data1 >> 0) & 0x0000FFFF; ++ onenand_info.data_buf_size = ++ (dma_buffer->data.data1 >> 16) & 0x0000FFFF; ++ onenand_info.boot_buf_size = ++ (dma_buffer->data.data2 >> 0) & 0x0000FFFF; ++ onenand_info.num_of_buffers = ++ (dma_buffer->data.data2 >> 16) & 0x0000FFFF; ++ onenand_info.technology = ++ (dma_buffer->data.data3 >> 0) & 0x0000FFFF; ++ ++ ++ pr_info("=======================================" ++ "==========================\n"); ++ ++ pr_info("%s: manufacturer_id = 0x%x\n" ++ , __func__, onenand_info.manufacturer_id); ++ pr_info("%s: device_id = 0x%x\n" ++ , __func__, onenand_info.device_id); ++ pr_info("%s: version_id = 0x%x\n" ++ , __func__, onenand_info.version_id); ++ pr_info("%s: data_buf_size = 0x%x\n" ++ , __func__, onenand_info.data_buf_size); ++ pr_info("%s: boot_buf_size = 0x%x\n" ++ , __func__, onenand_info.boot_buf_size); ++ pr_info("%s: num_of_buffers = 0x%x\n" ++ , __func__, onenand_info.num_of_buffers); ++ pr_info("%s: technology = 0x%x\n" ++ , __func__, onenand_info.technology); ++ ++ pr_info("=======================================" ++ "==========================\n"); ++ ++ if ((onenand_info.manufacturer_id != 0x00EC) ++ || ((onenand_info.device_id & 0x0040) != 0x0040) ++ || (onenand_info.data_buf_size != 0x0800) ++ || (onenand_info.boot_buf_size != 0x0200) ++ || (onenand_info.num_of_buffers != 0x0201) ++ || (onenand_info.technology != 0)) { ++ ++ pr_info("%s: Detected an unsupported device\n" ++ , __func__); ++ err = -EIO; ++ } ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++ return err; ++} ++ ++int msm_onenand_read_oob(struct mtd_info *mtd, ++ loff_t from, struct mtd_oob_ops *ops) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ ++ struct { ++ dmov_s cmd[53]; ++ unsigned cmdptr; ++ struct { ++ uint32_t sfbcfg; ++ uint32_t sfcmd[9]; ++ uint32_t sfexec; ++ uint32_t sfstat[9]; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t addr2; ++ uint32_t addr3; ++ uint32_t addr4; ++ uint32_t addr5; ++ uint32_t addr6; ++ uint32_t data0; ++ uint32_t data1; ++ uint32_t data2; ++ uint32_t data3; ++ uint32_t data4; ++ uint32_t data5; ++ uint32_t data6; ++ uint32_t macro[5]; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ ++ int err = 0; ++ int i; ++ dma_addr_t data_dma_addr = 0; ++ dma_addr_t oob_dma_addr = 0; ++ dma_addr_t data_dma_addr_curr = 0; ++ dma_addr_t oob_dma_addr_curr = 0; ++ ++ loff_t from_curr = 0; ++ unsigned page_count; ++ unsigned pages_read = 0; ++ ++ uint16_t onenand_startaddr1; ++ uint16_t onenand_startaddr8; ++ uint16_t onenand_startaddr2; ++ uint16_t onenand_startbuffer; ++ uint16_t onenand_sysconfig1; ++ uint16_t controller_status; ++ uint16_t interrupt_status; ++ uint16_t ecc_status; ++#if VERBOSE ++ pr_info("=================================================" ++ "================\n"); ++ pr_info("%s: from 0x%llx mode %d \ndatbuf 0x%p datlen 0x%x" ++ "\noobbuf 0x%p ooblen 0x%x\n", ++ __func__, from, ops->mode, ops->datbuf, ops->len, ++ ops->oobbuf, ops->ooblen); ++#endif ++ if (!mtd) { ++ pr_err("%s: invalid mtd pointer, 0x%x\n", __func__, ++ (uint32_t)mtd); ++ return -EINVAL; ++ } ++ if (from & (mtd->writesize - 1)) { ++ pr_err("%s: unsupported from, 0x%llx\n", __func__, ++ from); ++ return -EINVAL; ++ } ++ ++ if ((ops->mode != MTD_OPS_PLACE_OOB) && (ops->mode != MTD_OPS_AUTO_OOB) && ++ (ops->mode != MTD_OPS_RAW)) { ++ pr_err("%s: unsupported ops->mode, %d\n", __func__, ++ ops->mode); ++ return -EINVAL; ++ } ++ ++ if (((ops->datbuf == NULL) || (ops->len == 0)) && ++ ((ops->oobbuf == NULL) || (ops->ooblen == 0))) { ++ pr_err("%s: incorrect ops fields - nothing to do\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if ((ops->datbuf != NULL) && (ops->len == 0)) { ++ pr_err("%s: data buffer passed but length 0\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if ((ops->oobbuf != NULL) && (ops->ooblen == 0)) { ++ pr_err("%s: oob buffer passed but length 0\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (ops->mode != MTD_OPS_RAW) { ++ if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { ++ /* when ops->datbuf is NULL, ops->len can be ooblen */ ++ pr_err("%s: unsupported ops->len, %d\n", __func__, ++ ops->len); ++ return -EINVAL; ++ } ++ } else { ++ if (ops->datbuf != NULL && ++ (ops->len % (mtd->writesize + mtd->oobsize)) != 0) { ++ pr_err("%s: unsupported ops->len," ++ " %d for MTD_OPS_RAW\n", __func__, ops->len); ++ return -EINVAL; ++ } ++ } ++ ++ if ((ops->mode == MTD_OPS_RAW) && (ops->oobbuf)) { ++ pr_err("%s: unsupported operation, oobbuf pointer " ++ "passed in for RAW mode, %x\n", __func__, ++ (uint32_t)ops->oobbuf); ++ return -EINVAL; ++ } ++ ++ if (ops->oobbuf && !ops->datbuf) { ++ page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ? ++ mtd->oobavail : mtd->oobsize); ++ if ((page_count == 0) && (ops->ooblen)) ++ page_count = 1; ++ } else if (ops->mode != MTD_OPS_RAW) ++ page_count = ops->len / mtd->writesize; ++ else ++ page_count = ops->len / (mtd->writesize + mtd->oobsize); ++ ++ if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->oobbuf != NULL)) { ++ if (page_count * mtd->oobsize > ops->ooblen) { ++ pr_err("%s: unsupported ops->ooblen for " ++ "PLACE, %d\n", __func__, ops->ooblen); ++ return -EINVAL; ++ } ++ } ++ ++ if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->ooblen != 0) && ++ (ops->ooboffs != 0)) { ++ pr_err("%s: unsupported ops->ooboffs, %d\n", __func__, ++ ops->ooboffs); ++ return -EINVAL; ++ } ++ ++ if (ops->datbuf) { ++ memset(ops->datbuf, 0x55, ops->len); ++ data_dma_addr_curr = data_dma_addr = msm_nand_dma_map(chip->dev, ++ ops->datbuf, ops->len, DMA_FROM_DEVICE, NULL); ++ if (dma_mapping_error(chip->dev, data_dma_addr)) { ++ pr_err("%s: failed to get dma addr for %p\n", ++ __func__, ops->datbuf); ++ return -EIO; ++ } ++ } ++ if (ops->oobbuf) { ++ memset(ops->oobbuf, 0x55, ops->ooblen); ++ oob_dma_addr_curr = oob_dma_addr = msm_nand_dma_map(chip->dev, ++ ops->oobbuf, ops->ooblen, DMA_FROM_DEVICE, NULL); ++ if (dma_mapping_error(chip->dev, oob_dma_addr)) { ++ pr_err("%s: failed to get dma addr for %p\n", ++ __func__, ops->oobbuf); ++ err = -EIO; ++ goto err_dma_map_oobbuf_failed; ++ } ++ } ++ ++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer ++ (chip, sizeof(*dma_buffer)))); ++ ++ from_curr = from; ++ ++ while (page_count-- > 0) { ++ ++ cmd = dma_buffer->cmd; ++ ++ if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP) ++ && (from_curr >= (mtd->size>>1))) { /* DDP Device */ ++ onenand_startaddr1 = DEVICE_FLASHCORE_1 | ++ (((uint32_t)(from_curr-(mtd->size>>1)) ++ / mtd->erasesize)); ++ onenand_startaddr2 = DEVICE_BUFFERRAM_1; ++ } else { ++ onenand_startaddr1 = DEVICE_FLASHCORE_0 | ++ ((uint32_t)from_curr / mtd->erasesize) ; ++ onenand_startaddr2 = DEVICE_BUFFERRAM_0; ++ } ++ ++ onenand_startaddr8 = (((uint32_t)from_curr & ++ (mtd->erasesize - 1)) / mtd->writesize) << 2; ++ onenand_startbuffer = DATARAM0_0 << 8; ++ onenand_sysconfig1 = (ops->mode == MTD_OPS_RAW) ? ++ ONENAND_SYSCFG1_ECCDIS(nand_sfcmd_mode) : ++ ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode); ++ ++ dma_buffer->data.sfbcfg = SFLASH_BCFG | ++ (nand_sfcmd_mode ? 0 : (1 << 24)); ++ dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_INTHI); ++ dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGRD); ++ dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(256, 0, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATRD); ++ dma_buffer->data.sfcmd[4] = SFLASH_PREPCMD(256, 0, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATRD); ++ dma_buffer->data.sfcmd[5] = SFLASH_PREPCMD(256, 0, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATRD); ++ dma_buffer->data.sfcmd[6] = SFLASH_PREPCMD(256, 0, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATRD); ++ dma_buffer->data.sfcmd[7] = SFLASH_PREPCMD(32, 0, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATRD); ++ dma_buffer->data.sfcmd[8] = SFLASH_PREPCMD(4, 10, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfexec = 1; ++ dma_buffer->data.sfstat[0] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[1] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[2] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[3] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[4] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[5] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[6] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[7] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[8] = CLEAN_DATA_32; ++ dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.addr2 = (ONENAND_START_BUFFER << 16) | ++ (ONENAND_START_ADDRESS_2); ++ dma_buffer->data.addr3 = (ONENAND_ECC_STATUS << 16) | ++ (ONENAND_COMMAND); ++ dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) | ++ (ONENAND_INTERRUPT_STATUS); ++ dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) | ++ (onenand_sysconfig1); ++ dma_buffer->data.data1 = (onenand_startaddr8 << 16) | ++ (onenand_startaddr1); ++ dma_buffer->data.data2 = (onenand_startbuffer << 16) | ++ (onenand_startaddr2); ++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | ++ (ONENAND_CMDLOADSPARE); ++ dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) | ++ (CLEAN_DATA_16); ++ dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) | ++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); ++ dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) | ++ (ONENAND_STARTADDR1_RES); ++ dma_buffer->data.macro[0] = 0x0200; ++ dma_buffer->data.macro[1] = 0x0300; ++ dma_buffer->data.macro[2] = 0x0400; ++ dma_buffer->data.macro[3] = 0x0500; ++ dma_buffer->data.macro[4] = 0x8010; ++ ++ /*************************************************************/ ++ /* Write necessary address registers in the onenand device */ ++ /*************************************************************/ ++ ++ /* Enable and configure the SFlash controller */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the ADDR0 and ADDR1 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0); ++ cmd->dst = MSM_NAND_ADDR0; ++ cmd->len = 8; ++ cmd++; ++ ++ /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2); ++ cmd->dst = MSM_NAND_ADDR2; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the ADDR6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6); ++ cmd->dst = MSM_NAND_ADDR6; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the GENP0, GENP1, GENP2, GENP3 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0); ++ cmd->dst = MSM_NAND_GENP_REG0; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the FLASH_DEV_CMD4,5,6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->dst = MSM_NAND_DEV_CMD4; ++ cmd->len = 12; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*************************************************************/ ++ /* Wait for the interrupt from the Onenand device controller */ ++ /*************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*************************************************************/ ++ /* Read necessary status registers from the onenand device */ ++ /*************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the GENP3 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_GENP_REG3; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the DEVCMD4 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_DEV_CMD4; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->len = 4; ++ cmd++; ++ ++ /*************************************************************/ ++ /* Read the data ram area from the onenand buffer ram */ ++ /*************************************************************/ ++ ++ if (ops->datbuf) { ++ ++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | ++ (ONENAND_CMDLOAD); ++ ++ for (i = 0; i < 4; i++) { ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.sfcmd[3+i]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the MACRO1 register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.macro[i]); ++ cmd->dst = MSM_NAND_MACRO1_REG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data rdy, & read status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.sfstat[3+i]); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Transfer nand ctlr buf contents to usr buf */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_FLASH_BUFFER; ++ cmd->dst = data_dma_addr_curr; ++ cmd->len = 512; ++ data_dma_addr_curr += 512; ++ cmd++; ++ } ++ } ++ ++ if ((ops->oobbuf) || (ops->mode == MTD_OPS_RAW)) { ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.sfcmd[7]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the MACRO1 register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.macro[4]); ++ cmd->dst = MSM_NAND_MACRO1_REG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.sfstat[7]); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Transfer nand ctlr buffer contents into usr buf */ ++ if (ops->mode == MTD_OPS_AUTO_OOB) { ++ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) { ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_FLASH_BUFFER + ++ mtd->ecclayout->oobfree[i].offset; ++ cmd->dst = oob_dma_addr_curr; ++ cmd->len = ++ mtd->ecclayout->oobfree[i].length; ++ oob_dma_addr_curr += ++ mtd->ecclayout->oobfree[i].length; ++ cmd++; ++ } ++ } ++ if (ops->mode == MTD_OPS_PLACE_OOB) { ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_FLASH_BUFFER; ++ cmd->dst = oob_dma_addr_curr; ++ cmd->len = mtd->oobsize; ++ oob_dma_addr_curr += mtd->oobsize; ++ cmd++; ++ } ++ if (ops->mode == MTD_OPS_RAW) { ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_FLASH_BUFFER; ++ cmd->dst = data_dma_addr_curr; ++ cmd->len = mtd->oobsize; ++ data_dma_addr_curr += mtd->oobsize; ++ cmd++; ++ } ++ } ++ ++ /*************************************************************/ ++ /* Restore the necessary registers to proper values */ ++ /*************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[8]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[8]); ++ cmd->len = 4; ++ cmd++; ++ ++ ++ BUILD_BUG_ON(53 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) ++ >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ ++ ecc_status = (dma_buffer->data.data3 >> 16) & ++ 0x0000FFFF; ++ interrupt_status = (dma_buffer->data.data4 >> 0) & ++ 0x0000FFFF; ++ controller_status = (dma_buffer->data.data4 >> 16) & ++ 0x0000FFFF; ++ ++#if VERBOSE ++ pr_info("\n%s: sflash status %x %x %x %x %x %x %x" ++ "%x %x\n", __func__, ++ dma_buffer->data.sfstat[0], ++ dma_buffer->data.sfstat[1], ++ dma_buffer->data.sfstat[2], ++ dma_buffer->data.sfstat[3], ++ dma_buffer->data.sfstat[4], ++ dma_buffer->data.sfstat[5], ++ dma_buffer->data.sfstat[6], ++ dma_buffer->data.sfstat[7], ++ dma_buffer->data.sfstat[8]); ++ ++ pr_info("%s: controller_status = %x\n", __func__, ++ controller_status); ++ pr_info("%s: interrupt_status = %x\n", __func__, ++ interrupt_status); ++ pr_info("%s: ecc_status = %x\n", __func__, ++ ecc_status); ++#endif ++ /* Check for errors, protection violations etc */ ++ if ((controller_status != 0) ++ || (dma_buffer->data.sfstat[0] & 0x110) ++ || (dma_buffer->data.sfstat[1] & 0x110) ++ || (dma_buffer->data.sfstat[2] & 0x110) ++ || (dma_buffer->data.sfstat[8] & 0x110) ++ || ((dma_buffer->data.sfstat[3] & 0x110) && ++ (ops->datbuf)) ++ || ((dma_buffer->data.sfstat[4] & 0x110) && ++ (ops->datbuf)) ++ || ((dma_buffer->data.sfstat[5] & 0x110) && ++ (ops->datbuf)) ++ || ((dma_buffer->data.sfstat[6] & 0x110) && ++ (ops->datbuf)) ++ || ((dma_buffer->data.sfstat[7] & 0x110) && ++ ((ops->oobbuf) ++ || (ops->mode == MTD_OPS_RAW)))) { ++ pr_info("%s: ECC/MPU/OP error\n", __func__); ++ err = -EIO; ++ } ++ ++ if (err) ++ break; ++ pages_read++; ++ from_curr += mtd->writesize; ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++ if (ops->oobbuf) { ++ dma_unmap_page(chip->dev, oob_dma_addr, ops->ooblen, ++ DMA_FROM_DEVICE); ++ } ++err_dma_map_oobbuf_failed: ++ if (ops->datbuf) { ++ dma_unmap_page(chip->dev, data_dma_addr, ops->len, ++ DMA_FROM_DEVICE); ++ } ++ ++ if (err) { ++ pr_err("%s: %llx %x %x failed\n", __func__, from_curr, ++ ops->datbuf ? ops->len : 0, ops->ooblen); ++ } else { ++ ops->retlen = ops->oobretlen = 0; ++ if (ops->datbuf != NULL) { ++ if (ops->mode != MTD_OPS_RAW) ++ ops->retlen = mtd->writesize * pages_read; ++ else ++ ops->retlen = (mtd->writesize + mtd->oobsize) ++ * pages_read; ++ } ++ if (ops->oobbuf != NULL) { ++ if (ops->mode == MTD_OPS_AUTO_OOB) ++ ops->oobretlen = mtd->oobavail * pages_read; ++ else ++ ops->oobretlen = mtd->oobsize * pages_read; ++ } ++ } ++ ++#if VERBOSE ++ pr_info("\n%s: ret %d, retlen %d oobretlen %d\n", ++ __func__, err, ops->retlen, ops->oobretlen); ++ ++ pr_info("===================================================" ++ "==============\n"); ++#endif ++ return err; ++} ++ ++int msm_onenand_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ int ret; ++ struct mtd_oob_ops ops; ++ ++ ops.mode = MTD_OPS_PLACE_OOB; ++ ops.datbuf = buf; ++ ops.len = len; ++ ops.retlen = 0; ++ ops.oobbuf = NULL; ++ ops.ooblen = 0; ++ ops.oobretlen = 0; ++ ret = msm_onenand_read_oob(mtd, from, &ops); ++ *retlen = ops.retlen; ++ ++ return ret; ++} ++ ++static int msm_onenand_write_oob(struct mtd_info *mtd, loff_t to, ++ struct mtd_oob_ops *ops) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ ++ struct { ++ dmov_s cmd[53]; ++ unsigned cmdptr; ++ struct { ++ uint32_t sfbcfg; ++ uint32_t sfcmd[10]; ++ uint32_t sfexec; ++ uint32_t sfstat[10]; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t addr2; ++ uint32_t addr3; ++ uint32_t addr4; ++ uint32_t addr5; ++ uint32_t addr6; ++ uint32_t data0; ++ uint32_t data1; ++ uint32_t data2; ++ uint32_t data3; ++ uint32_t data4; ++ uint32_t data5; ++ uint32_t data6; ++ uint32_t macro[5]; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ ++ int err = 0; ++ int i, j, k; ++ dma_addr_t data_dma_addr = 0; ++ dma_addr_t oob_dma_addr = 0; ++ dma_addr_t init_dma_addr = 0; ++ dma_addr_t data_dma_addr_curr = 0; ++ dma_addr_t oob_dma_addr_curr = 0; ++ uint8_t *init_spare_bytes; ++ ++ loff_t to_curr = 0; ++ unsigned page_count; ++ unsigned pages_written = 0; ++ ++ uint16_t onenand_startaddr1; ++ uint16_t onenand_startaddr8; ++ uint16_t onenand_startaddr2; ++ uint16_t onenand_startbuffer; ++ uint16_t onenand_sysconfig1; ++ ++ uint16_t controller_status; ++ uint16_t interrupt_status; ++ uint16_t ecc_status; ++ ++#if VERBOSE ++ pr_info("=================================================" ++ "================\n"); ++ pr_info("%s: to 0x%llx mode %d \ndatbuf 0x%p datlen 0x%x" ++ "\noobbuf 0x%p ooblen 0x%x\n", ++ __func__, to, ops->mode, ops->datbuf, ops->len, ++ ops->oobbuf, ops->ooblen); ++#endif ++ if (!mtd) { ++ pr_err("%s: invalid mtd pointer, 0x%x\n", __func__, ++ (uint32_t)mtd); ++ return -EINVAL; ++ } ++ if (to & (mtd->writesize - 1)) { ++ pr_err("%s: unsupported to, 0x%llx\n", __func__, to); ++ return -EINVAL; ++ } ++ ++ if ((ops->mode != MTD_OPS_PLACE_OOB) && (ops->mode != MTD_OPS_AUTO_OOB) && ++ (ops->mode != MTD_OPS_RAW)) { ++ pr_err("%s: unsupported ops->mode, %d\n", __func__, ++ ops->mode); ++ return -EINVAL; ++ } ++ ++ if (((ops->datbuf == NULL) || (ops->len == 0)) && ++ ((ops->oobbuf == NULL) || (ops->ooblen == 0))) { ++ pr_err("%s: incorrect ops fields - nothing to do\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if ((ops->datbuf != NULL) && (ops->len == 0)) { ++ pr_err("%s: data buffer passed but length 0\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if ((ops->oobbuf != NULL) && (ops->ooblen == 0)) { ++ pr_err("%s: oob buffer passed but length 0\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (ops->mode != MTD_OPS_RAW) { ++ if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { ++ /* when ops->datbuf is NULL, ops->len can be ooblen */ ++ pr_err("%s: unsupported ops->len, %d\n", __func__, ++ ops->len); ++ return -EINVAL; ++ } ++ } else { ++ if (ops->datbuf != NULL && ++ (ops->len % (mtd->writesize + mtd->oobsize)) != 0) { ++ pr_err("%s: unsupported ops->len," ++ " %d for MTD_OPS_RAW\n", __func__, ops->len); ++ return -EINVAL; ++ } ++ } ++ ++ if ((ops->mode == MTD_OPS_RAW) && (ops->oobbuf)) { ++ pr_err("%s: unsupported operation, oobbuf pointer " ++ "passed in for RAW mode, %x\n", __func__, ++ (uint32_t)ops->oobbuf); ++ return -EINVAL; ++ } ++ ++ if (ops->oobbuf && !ops->datbuf) { ++ page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ? ++ mtd->oobavail : mtd->oobsize); ++ if ((page_count == 0) && (ops->ooblen)) ++ page_count = 1; ++ } else if (ops->mode != MTD_OPS_RAW) ++ page_count = ops->len / mtd->writesize; ++ else ++ page_count = ops->len / (mtd->writesize + mtd->oobsize); ++ ++ if ((ops->mode == MTD_OPS_AUTO_OOB) && (ops->oobbuf != NULL)) { ++ if (page_count > 1) { ++ pr_err("%s: unsupported ops->ooblen for" ++ "AUTO, %d\n", __func__, ops->ooblen); ++ return -EINVAL; ++ } ++ } ++ ++ if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->oobbuf != NULL)) { ++ if (page_count * mtd->oobsize > ops->ooblen) { ++ pr_err("%s: unsupported ops->ooblen for" ++ "PLACE, %d\n", __func__, ops->ooblen); ++ return -EINVAL; ++ } ++ } ++ ++ if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->ooblen != 0) && ++ (ops->ooboffs != 0)) { ++ pr_err("%s: unsupported ops->ooboffs, %d\n", ++ __func__, ops->ooboffs); ++ return -EINVAL; ++ } ++ ++ init_spare_bytes = kmalloc(64, GFP_KERNEL); ++ if (!init_spare_bytes) { ++ pr_err("%s: failed to alloc init_spare_bytes buffer\n", ++ __func__); ++ return -ENOMEM; ++ } ++ for (i = 0; i < 64; i++) ++ init_spare_bytes[i] = 0xFF; ++ ++ if ((ops->oobbuf) && (ops->mode == MTD_OPS_AUTO_OOB)) { ++ for (i = 0, k = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) ++ for (j = 0; j < mtd->ecclayout->oobfree[i].length; ++ j++) { ++ init_spare_bytes[j + ++ mtd->ecclayout->oobfree[i].offset] ++ = (ops->oobbuf)[k]; ++ k++; ++ } ++ } ++ ++ if (ops->datbuf) { ++ data_dma_addr_curr = data_dma_addr = msm_nand_dma_map(chip->dev, ++ ops->datbuf, ops->len, DMA_TO_DEVICE, NULL); ++ if (dma_mapping_error(chip->dev, data_dma_addr)) { ++ pr_err("%s: failed to get dma addr for %p\n", ++ __func__, ops->datbuf); ++ return -EIO; ++ } ++ } ++ if (ops->oobbuf) { ++ oob_dma_addr_curr = oob_dma_addr = msm_nand_dma_map(chip->dev, ++ ops->oobbuf, ops->ooblen, DMA_TO_DEVICE, NULL); ++ if (dma_mapping_error(chip->dev, oob_dma_addr)) { ++ pr_err("%s: failed to get dma addr for %p\n", ++ __func__, ops->oobbuf); ++ err = -EIO; ++ goto err_dma_map_oobbuf_failed; ++ } ++ } ++ ++ init_dma_addr = msm_nand_dma_map(chip->dev, init_spare_bytes, 64, ++ DMA_TO_DEVICE, NULL); ++ if (dma_mapping_error(chip->dev, init_dma_addr)) { ++ pr_err("%s: failed to get dma addr for %p\n", ++ __func__, init_spare_bytes); ++ err = -EIO; ++ goto err_dma_map_initbuf_failed; ++ } ++ ++ ++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer ++ (chip, sizeof(*dma_buffer)))); ++ ++ to_curr = to; ++ ++ while (page_count-- > 0) { ++ cmd = dma_buffer->cmd; ++ ++ if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP) ++ && (to_curr >= (mtd->size>>1))) { /* DDP Device */ ++ onenand_startaddr1 = DEVICE_FLASHCORE_1 | ++ (((uint32_t)(to_curr-(mtd->size>>1)) ++ / mtd->erasesize)); ++ onenand_startaddr2 = DEVICE_BUFFERRAM_1; ++ } else { ++ onenand_startaddr1 = DEVICE_FLASHCORE_0 | ++ ((uint32_t)to_curr / mtd->erasesize) ; ++ onenand_startaddr2 = DEVICE_BUFFERRAM_0; ++ } ++ ++ onenand_startaddr8 = (((uint32_t)to_curr & ++ (mtd->erasesize - 1)) / mtd->writesize) << 2; ++ onenand_startbuffer = DATARAM0_0 << 8; ++ onenand_sysconfig1 = (ops->mode == MTD_OPS_RAW) ? ++ ONENAND_SYSCFG1_ECCDIS(nand_sfcmd_mode) : ++ ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode); ++ ++ dma_buffer->data.sfbcfg = SFLASH_BCFG | ++ (nand_sfcmd_mode ? 0 : (1 << 24)); ++ dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(6, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(256, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATWR); ++ dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(256, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATWR); ++ dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(256, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATWR); ++ dma_buffer->data.sfcmd[4] = SFLASH_PREPCMD(256, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATWR); ++ dma_buffer->data.sfcmd[5] = SFLASH_PREPCMD(32, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATWR); ++ dma_buffer->data.sfcmd[6] = SFLASH_PREPCMD(1, 6, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfcmd[7] = SFLASH_PREPCMD(0, 0, 32, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_INTHI); ++ dma_buffer->data.sfcmd[8] = SFLASH_PREPCMD(3, 7, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGRD); ++ dma_buffer->data.sfcmd[9] = SFLASH_PREPCMD(4, 10, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfexec = 1; ++ dma_buffer->data.sfstat[0] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[1] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[2] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[3] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[4] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[5] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[6] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[7] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[8] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[9] = CLEAN_DATA_32; ++ dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.addr2 = (ONENAND_START_BUFFER << 16) | ++ (ONENAND_START_ADDRESS_2); ++ dma_buffer->data.addr3 = (ONENAND_ECC_STATUS << 16) | ++ (ONENAND_COMMAND); ++ dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) | ++ (ONENAND_INTERRUPT_STATUS); ++ dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) | ++ (onenand_sysconfig1); ++ dma_buffer->data.data1 = (onenand_startaddr8 << 16) | ++ (onenand_startaddr1); ++ dma_buffer->data.data2 = (onenand_startbuffer << 16) | ++ (onenand_startaddr2); ++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | ++ (ONENAND_CMDPROGSPARE); ++ dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) | ++ (CLEAN_DATA_16); ++ dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) | ++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); ++ dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) | ++ (ONENAND_STARTADDR1_RES); ++ dma_buffer->data.macro[0] = 0x0200; ++ dma_buffer->data.macro[1] = 0x0300; ++ dma_buffer->data.macro[2] = 0x0400; ++ dma_buffer->data.macro[3] = 0x0500; ++ dma_buffer->data.macro[4] = 0x8010; ++ ++ ++ /*************************************************************/ ++ /* Write necessary address registers in the onenand device */ ++ /*************************************************************/ ++ ++ /* Enable and configure the SFlash controller */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the ADDR0 and ADDR1 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0); ++ cmd->dst = MSM_NAND_ADDR0; ++ cmd->len = 8; ++ cmd++; ++ ++ /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2); ++ cmd->dst = MSM_NAND_ADDR2; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the ADDR6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6); ++ cmd->dst = MSM_NAND_ADDR6; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the GENP0, GENP1, GENP2, GENP3 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0); ++ cmd->dst = MSM_NAND_GENP_REG0; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the FLASH_DEV_CMD4,5,6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->dst = MSM_NAND_DEV_CMD4; ++ cmd->len = 12; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*************************************************************/ ++ /* Write the data ram area in the onenand buffer ram */ ++ /*************************************************************/ ++ ++ if (ops->datbuf) { ++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | ++ (ONENAND_CMDPROG); ++ ++ for (i = 0; i < 4; i++) { ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.sfcmd[1+i]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Trnsfr usr buf contents to nand ctlr buf */ ++ cmd->cmd = 0; ++ cmd->src = data_dma_addr_curr; ++ cmd->dst = MSM_NAND_FLASH_BUFFER; ++ cmd->len = 512; ++ data_dma_addr_curr += 512; ++ cmd++; ++ ++ /* Write the MACRO1 register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.macro[i]); ++ cmd->dst = MSM_NAND_MACRO1_REG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data rdy, & read status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.sfstat[1+i]); ++ cmd->len = 4; ++ cmd++; ++ ++ } ++ } ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[5]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ if ((ops->oobbuf) || (ops->mode == MTD_OPS_RAW)) { ++ ++ /* Transfer user buf contents into nand ctlr buffer */ ++ if (ops->mode == MTD_OPS_AUTO_OOB) { ++ cmd->cmd = 0; ++ cmd->src = init_dma_addr; ++ cmd->dst = MSM_NAND_FLASH_BUFFER; ++ cmd->len = mtd->oobsize; ++ cmd++; ++ } ++ if (ops->mode == MTD_OPS_PLACE_OOB) { ++ cmd->cmd = 0; ++ cmd->src = oob_dma_addr_curr; ++ cmd->dst = MSM_NAND_FLASH_BUFFER; ++ cmd->len = mtd->oobsize; ++ oob_dma_addr_curr += mtd->oobsize; ++ cmd++; ++ } ++ if (ops->mode == MTD_OPS_RAW) { ++ cmd->cmd = 0; ++ cmd->src = data_dma_addr_curr; ++ cmd->dst = MSM_NAND_FLASH_BUFFER; ++ cmd->len = mtd->oobsize; ++ data_dma_addr_curr += mtd->oobsize; ++ cmd++; ++ } ++ } else { ++ cmd->cmd = 0; ++ cmd->src = init_dma_addr; ++ cmd->dst = MSM_NAND_FLASH_BUFFER; ++ cmd->len = mtd->oobsize; ++ cmd++; ++ } ++ ++ /* Write the MACRO1 register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.macro[4]); ++ cmd->dst = MSM_NAND_MACRO1_REG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[5]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*********************************************************/ ++ /* Issuing write command */ ++ /*********************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[6]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[6]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*************************************************************/ ++ /* Wait for the interrupt from the Onenand device controller */ ++ /*************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[7]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[7]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*************************************************************/ ++ /* Read necessary status registers from the onenand device */ ++ /*************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[8]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[8]); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the GENP3 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_GENP_REG3; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the DEVCMD4 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_DEV_CMD4; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->len = 4; ++ cmd++; ++ ++ /*************************************************************/ ++ /* Restore the necessary registers to proper values */ ++ /*************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[9]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[9]); ++ cmd->len = 4; ++ cmd++; ++ ++ ++ BUILD_BUG_ON(53 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) ++ >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ ++ ecc_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF; ++ interrupt_status = (dma_buffer->data.data4 >> 0)&0x0000FFFF; ++ controller_status = (dma_buffer->data.data4 >> 16)&0x0000FFFF; ++ ++#if VERBOSE ++ pr_info("\n%s: sflash status %x %x %x %x %x %x %x" ++ " %x %x %x\n", __func__, ++ dma_buffer->data.sfstat[0], ++ dma_buffer->data.sfstat[1], ++ dma_buffer->data.sfstat[2], ++ dma_buffer->data.sfstat[3], ++ dma_buffer->data.sfstat[4], ++ dma_buffer->data.sfstat[5], ++ dma_buffer->data.sfstat[6], ++ dma_buffer->data.sfstat[7], ++ dma_buffer->data.sfstat[8], ++ dma_buffer->data.sfstat[9]); ++ ++ pr_info("%s: controller_status = %x\n", __func__, ++ controller_status); ++ pr_info("%s: interrupt_status = %x\n", __func__, ++ interrupt_status); ++ pr_info("%s: ecc_status = %x\n", __func__, ++ ecc_status); ++#endif ++ /* Check for errors, protection violations etc */ ++ if ((controller_status != 0) ++ || (dma_buffer->data.sfstat[0] & 0x110) ++ || (dma_buffer->data.sfstat[6] & 0x110) ++ || (dma_buffer->data.sfstat[7] & 0x110) ++ || (dma_buffer->data.sfstat[8] & 0x110) ++ || (dma_buffer->data.sfstat[9] & 0x110) ++ || ((dma_buffer->data.sfstat[1] & 0x110) && ++ (ops->datbuf)) ++ || ((dma_buffer->data.sfstat[2] & 0x110) && ++ (ops->datbuf)) ++ || ((dma_buffer->data.sfstat[3] & 0x110) && ++ (ops->datbuf)) ++ || ((dma_buffer->data.sfstat[4] & 0x110) && ++ (ops->datbuf)) ++ || ((dma_buffer->data.sfstat[5] & 0x110) && ++ ((ops->oobbuf) ++ || (ops->mode == MTD_OPS_RAW)))) { ++ pr_info("%s: ECC/MPU/OP error\n", __func__); ++ err = -EIO; ++ } ++ ++ if (err) ++ break; ++ pages_written++; ++ to_curr += mtd->writesize; ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++ dma_unmap_page(chip->dev, init_dma_addr, 64, DMA_TO_DEVICE); ++ ++err_dma_map_initbuf_failed: ++ if (ops->oobbuf) { ++ dma_unmap_page(chip->dev, oob_dma_addr, ops->ooblen, ++ DMA_TO_DEVICE); ++ } ++err_dma_map_oobbuf_failed: ++ if (ops->datbuf) { ++ dma_unmap_page(chip->dev, data_dma_addr, ops->len, ++ DMA_TO_DEVICE); ++ } ++ ++ if (err) { ++ pr_err("%s: %llx %x %x failed\n", __func__, to_curr, ++ ops->datbuf ? ops->len : 0, ops->ooblen); ++ } else { ++ ops->retlen = ops->oobretlen = 0; ++ if (ops->datbuf != NULL) { ++ if (ops->mode != MTD_OPS_RAW) ++ ops->retlen = mtd->writesize * pages_written; ++ else ++ ops->retlen = (mtd->writesize + mtd->oobsize) ++ * pages_written; ++ } ++ if (ops->oobbuf != NULL) { ++ if (ops->mode == MTD_OPS_AUTO_OOB) ++ ops->oobretlen = mtd->oobavail * pages_written; ++ else ++ ops->oobretlen = mtd->oobsize * pages_written; ++ } ++ } ++ ++#if VERBOSE ++ pr_info("\n%s: ret %d, retlen %d oobretlen %d\n", ++ __func__, err, ops->retlen, ops->oobretlen); ++ ++ pr_info("=================================================" ++ "================\n"); ++#endif ++ kfree(init_spare_bytes); ++ return err; ++} ++ ++static int msm_onenand_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ int ret; ++ struct mtd_oob_ops ops; ++ ++ ops.mode = MTD_OPS_PLACE_OOB; ++ ops.datbuf = (uint8_t *)buf; ++ ops.len = len; ++ ops.retlen = 0; ++ ops.oobbuf = NULL; ++ ops.ooblen = 0; ++ ops.oobretlen = 0; ++ ret = msm_onenand_write_oob(mtd, to, &ops); ++ *retlen = ops.retlen; ++ ++ return ret; ++} ++ ++static int msm_onenand_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ ++ struct { ++ dmov_s cmd[20]; ++ unsigned cmdptr; ++ struct { ++ uint32_t sfbcfg; ++ uint32_t sfcmd[4]; ++ uint32_t sfexec; ++ uint32_t sfstat[4]; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t addr2; ++ uint32_t addr3; ++ uint32_t addr4; ++ uint32_t addr5; ++ uint32_t addr6; ++ uint32_t data0; ++ uint32_t data1; ++ uint32_t data2; ++ uint32_t data3; ++ uint32_t data4; ++ uint32_t data5; ++ uint32_t data6; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ ++ int err = 0; ++ ++ uint16_t onenand_startaddr1; ++ uint16_t onenand_startaddr8; ++ uint16_t onenand_startaddr2; ++ uint16_t onenand_startbuffer; ++ ++ uint16_t controller_status; ++ uint16_t interrupt_status; ++ uint16_t ecc_status; ++ ++ uint64_t temp; ++ ++#if VERBOSE ++ pr_info("=================================================" ++ "================\n"); ++ pr_info("%s: addr 0x%llx len 0x%llx\n", ++ __func__, instr->addr, instr->len); ++#endif ++ if (instr->addr & (mtd->erasesize - 1)) { ++ pr_err("%s: Unsupported erase address, 0x%llx\n", ++ __func__, instr->addr); ++ return -EINVAL; ++ } ++ if (instr->len != mtd->erasesize) { ++ pr_err("%s: Unsupported erase len, %lld\n", ++ __func__, instr->len); ++ return -EINVAL; ++ } ++ ++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer ++ (chip, sizeof(*dma_buffer)))); ++ ++ cmd = dma_buffer->cmd; ++ ++ temp = instr->addr; ++ ++ if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP) ++ && (temp >= (mtd->size>>1))) { /* DDP Device */ ++ onenand_startaddr1 = DEVICE_FLASHCORE_1 | ++ (((uint32_t)(temp-(mtd->size>>1)) ++ / mtd->erasesize)); ++ onenand_startaddr2 = DEVICE_BUFFERRAM_1; ++ } else { ++ onenand_startaddr1 = DEVICE_FLASHCORE_0 | ++ ((uint32_t)temp / mtd->erasesize) ; ++ onenand_startaddr2 = DEVICE_BUFFERRAM_0; ++ } ++ ++ onenand_startaddr8 = 0x0000; ++ onenand_startbuffer = DATARAM0_0 << 8; ++ ++ dma_buffer->data.sfbcfg = SFLASH_BCFG | ++ (nand_sfcmd_mode ? 0 : (1 << 24)); ++ dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_INTHI); ++ dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGRD); ++ dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(4, 10, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfexec = 1; ++ dma_buffer->data.sfstat[0] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[1] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[2] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[3] = CLEAN_DATA_32; ++ dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.addr2 = (ONENAND_START_BUFFER << 16) | ++ (ONENAND_START_ADDRESS_2); ++ dma_buffer->data.addr3 = (ONENAND_ECC_STATUS << 16) | ++ (ONENAND_COMMAND); ++ dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) | ++ (ONENAND_INTERRUPT_STATUS); ++ dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) | ++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); ++ dma_buffer->data.data1 = (onenand_startaddr8 << 16) | ++ (onenand_startaddr1); ++ dma_buffer->data.data2 = (onenand_startbuffer << 16) | ++ (onenand_startaddr2); ++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | ++ (ONENAND_CMDERAS); ++ dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) | ++ (CLEAN_DATA_16); ++ dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) | ++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); ++ dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) | ++ (ONENAND_STARTADDR1_RES); ++ ++ /***************************************************************/ ++ /* Write the necessary address registers in the onenand device */ ++ /***************************************************************/ ++ ++ /* Enable and configure the SFlash controller */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the ADDR0 and ADDR1 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0); ++ cmd->dst = MSM_NAND_ADDR0; ++ cmd->len = 8; ++ cmd++; ++ ++ /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2); ++ cmd->dst = MSM_NAND_ADDR2; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the ADDR6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6); ++ cmd->dst = MSM_NAND_ADDR6; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the GENP0, GENP1, GENP2, GENP3, GENP4 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0); ++ cmd->dst = MSM_NAND_GENP_REG0; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the FLASH_DEV_CMD4,5,6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->dst = MSM_NAND_DEV_CMD4; ++ cmd->len = 12; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]); ++ cmd->len = 4; ++ cmd++; ++ ++ /***************************************************************/ ++ /* Wait for the interrupt from the Onenand device controller */ ++ /***************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]); ++ cmd->len = 4; ++ cmd++; ++ ++ /***************************************************************/ ++ /* Read the necessary status registers from the onenand device */ ++ /***************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the GENP3 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_GENP_REG3; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the DEVCMD4 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_DEV_CMD4; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->len = 4; ++ cmd++; ++ ++ /***************************************************************/ ++ /* Restore the necessary registers to proper values */ ++ /***************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[3]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[3]); ++ cmd->len = 4; ++ cmd++; ++ ++ ++ BUILD_BUG_ON(20 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) ++ >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST ++ | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ ++ ecc_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF; ++ interrupt_status = (dma_buffer->data.data4 >> 0) & 0x0000FFFF; ++ controller_status = (dma_buffer->data.data4 >> 16) & 0x0000FFFF; ++ ++#if VERBOSE ++ pr_info("\n%s: sflash status %x %x %x %x\n", __func__, ++ dma_buffer->data.sfstat[0], ++ dma_buffer->data.sfstat[1], ++ dma_buffer->data.sfstat[2], ++ dma_buffer->data.sfstat[3]); ++ ++ pr_info("%s: controller_status = %x\n", __func__, ++ controller_status); ++ pr_info("%s: interrupt_status = %x\n", __func__, ++ interrupt_status); ++ pr_info("%s: ecc_status = %x\n", __func__, ++ ecc_status); ++#endif ++ /* Check for errors, protection violations etc */ ++ if ((controller_status != 0) ++ || (dma_buffer->data.sfstat[0] & 0x110) ++ || (dma_buffer->data.sfstat[1] & 0x110) ++ || (dma_buffer->data.sfstat[2] & 0x110) ++ || (dma_buffer->data.sfstat[3] & 0x110)) { ++ pr_err("%s: ECC/MPU/OP error\n", __func__); ++ err = -EIO; ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++ if (err) { ++ pr_err("%s: Erase failed, 0x%llx\n", __func__, ++ instr->addr); ++ instr->fail_addr = instr->addr; ++ instr->state = MTD_ERASE_FAILED; ++ } else { ++ instr->state = MTD_ERASE_DONE; ++ instr->fail_addr = 0xffffffff; ++ mtd_erase_callback(instr); ++ } ++ ++#if VERBOSE ++ pr_info("\n%s: ret %d\n", __func__, err); ++ pr_info("====================================================" ++ "=============\n"); ++#endif ++ return err; ++} ++ ++static int msm_onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct mtd_oob_ops ops; ++ int rval, i; ++ int ret = 0; ++ uint8_t *buffer; ++ uint8_t *oobptr; ++ ++ if ((ofs > mtd->size) || (ofs & (mtd->erasesize - 1))) { ++ pr_err("%s: unsupported block address, 0x%x\n", ++ __func__, (uint32_t)ofs); ++ return -EINVAL; ++ } ++ ++ buffer = kmalloc(2112, GFP_KERNEL|GFP_DMA); ++ if (buffer == 0) { ++ pr_err("%s: Could not kmalloc for buffer\n", ++ __func__); ++ return -ENOMEM; ++ } ++ ++ memset(buffer, 0x00, 2112); ++ oobptr = &(buffer[2048]); ++ ++ ops.mode = MTD_OPS_RAW; ++ ops.len = 2112; ++ ops.retlen = 0; ++ ops.ooblen = 0; ++ ops.oobretlen = 0; ++ ops.ooboffs = 0; ++ ops.datbuf = buffer; ++ ops.oobbuf = NULL; ++ ++ for (i = 0; i < 2; i++) { ++ ofs = ofs + i*mtd->writesize; ++ rval = msm_onenand_read_oob(mtd, ofs, &ops); ++ if (rval) { ++ pr_err("%s: Error in reading bad blk info\n", ++ __func__); ++ ret = rval; ++ break; ++ } ++ if ((oobptr[0] != 0xFF) || (oobptr[1] != 0xFF) || ++ (oobptr[16] != 0xFF) || (oobptr[17] != 0xFF) || ++ (oobptr[32] != 0xFF) || (oobptr[33] != 0xFF) || ++ (oobptr[48] != 0xFF) || (oobptr[49] != 0xFF) ++ ) { ++ ret = 1; ++ break; ++ } ++ } ++ ++ kfree(buffer); ++ ++#if VERBOSE ++ if (ret == 1) ++ pr_info("%s : Block containing 0x%x is bad\n", ++ __func__, (unsigned int)ofs); ++#endif ++ return ret; ++} ++ ++static int msm_onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct mtd_oob_ops ops; ++ int rval, i; ++ int ret = 0; ++ uint8_t *buffer; ++ ++ if ((ofs > mtd->size) || (ofs & (mtd->erasesize - 1))) { ++ pr_err("%s: unsupported block address, 0x%x\n", ++ __func__, (uint32_t)ofs); ++ return -EINVAL; ++ } ++ ++ buffer = page_address(ZERO_PAGE()); ++ ++ ops.mode = MTD_OPS_RAW; ++ ops.len = 2112; ++ ops.retlen = 0; ++ ops.ooblen = 0; ++ ops.oobretlen = 0; ++ ops.ooboffs = 0; ++ ops.datbuf = buffer; ++ ops.oobbuf = NULL; ++ ++ for (i = 0; i < 2; i++) { ++ ofs = ofs + i*mtd->writesize; ++ rval = msm_onenand_write_oob(mtd, ofs, &ops); ++ if (rval) { ++ pr_err("%s: Error in writing bad blk info\n", ++ __func__); ++ ret = rval; ++ break; ++ } ++ } ++ ++ return ret; ++} ++ ++static int msm_onenand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ ++ struct { ++ dmov_s cmd[20]; ++ unsigned cmdptr; ++ struct { ++ uint32_t sfbcfg; ++ uint32_t sfcmd[4]; ++ uint32_t sfexec; ++ uint32_t sfstat[4]; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t addr2; ++ uint32_t addr3; ++ uint32_t addr4; ++ uint32_t addr5; ++ uint32_t addr6; ++ uint32_t data0; ++ uint32_t data1; ++ uint32_t data2; ++ uint32_t data3; ++ uint32_t data4; ++ uint32_t data5; ++ uint32_t data6; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ ++ int err = 0; ++ ++ uint16_t onenand_startaddr1; ++ uint16_t onenand_startaddr8; ++ uint16_t onenand_startaddr2; ++ uint16_t onenand_startblock; ++ ++ uint16_t controller_status; ++ uint16_t interrupt_status; ++ uint16_t write_prot_status; ++ ++ uint64_t start_ofs; ++ ++#if VERBOSE ++ pr_info("====================================================" ++ "=============\n"); ++ pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len); ++#endif ++ /* 'ofs' & 'len' should align to block size */ ++ if (ofs&(mtd->erasesize - 1)) { ++ pr_err("%s: Unsupported ofs address, 0x%llx\n", ++ __func__, ofs); ++ return -EINVAL; ++ } ++ ++ if (len&(mtd->erasesize - 1)) { ++ pr_err("%s: Unsupported len, %lld\n", ++ __func__, len); ++ return -EINVAL; ++ } ++ ++ if (ofs+len > mtd->size) { ++ pr_err("%s: Maximum chip size exceeded\n", __func__); ++ return -EINVAL; ++ } ++ ++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer ++ (chip, sizeof(*dma_buffer)))); ++ ++ for (start_ofs = ofs; ofs < start_ofs+len; ofs = ofs+mtd->erasesize) { ++#if VERBOSE ++ pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len); ++#endif ++ ++ cmd = dma_buffer->cmd; ++ if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP) ++ && (ofs >= (mtd->size>>1))) { /* DDP Device */ ++ onenand_startaddr1 = DEVICE_FLASHCORE_1 | ++ (((uint32_t)(ofs - (mtd->size>>1)) ++ / mtd->erasesize)); ++ onenand_startaddr2 = DEVICE_BUFFERRAM_1; ++ onenand_startblock = ((uint32_t)(ofs - (mtd->size>>1)) ++ / mtd->erasesize); ++ } else { ++ onenand_startaddr1 = DEVICE_FLASHCORE_0 | ++ ((uint32_t)ofs / mtd->erasesize) ; ++ onenand_startaddr2 = DEVICE_BUFFERRAM_0; ++ onenand_startblock = ((uint32_t)ofs ++ / mtd->erasesize); ++ } ++ ++ onenand_startaddr8 = 0x0000; ++ dma_buffer->data.sfbcfg = SFLASH_BCFG | ++ (nand_sfcmd_mode ? 0 : (1 << 24)); ++ dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_INTHI); ++ dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGRD); ++ dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(4, 10, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfexec = 1; ++ dma_buffer->data.sfstat[0] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[1] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[2] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[3] = CLEAN_DATA_32; ++ dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.addr2 = (ONENAND_START_BLOCK_ADDRESS << 16) | ++ (ONENAND_START_ADDRESS_2); ++ dma_buffer->data.addr3 = (ONENAND_WRITE_PROT_STATUS << 16) | ++ (ONENAND_COMMAND); ++ dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) | ++ (ONENAND_INTERRUPT_STATUS); ++ dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) | ++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); ++ dma_buffer->data.data1 = (onenand_startaddr8 << 16) | ++ (onenand_startaddr1); ++ dma_buffer->data.data2 = (onenand_startblock << 16) | ++ (onenand_startaddr2); ++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | ++ (ONENAND_CMD_UNLOCK); ++ dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) | ++ (CLEAN_DATA_16); ++ dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) | ++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); ++ dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) | ++ (ONENAND_STARTADDR1_RES); ++ ++ /*************************************************************/ ++ /* Write the necessary address reg in the onenand device */ ++ /*************************************************************/ ++ ++ /* Enable and configure the SFlash controller */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the ADDR0 and ADDR1 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0); ++ cmd->dst = MSM_NAND_ADDR0; ++ cmd->len = 8; ++ cmd++; ++ ++ /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2); ++ cmd->dst = MSM_NAND_ADDR2; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the ADDR6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6); ++ cmd->dst = MSM_NAND_ADDR6; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the GENP0, GENP1, GENP2, GENP3, GENP4 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0); ++ cmd->dst = MSM_NAND_GENP_REG0; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the FLASH_DEV_CMD4,5,6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->dst = MSM_NAND_DEV_CMD4; ++ cmd->len = 12; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*************************************************************/ ++ /* Wait for the interrupt from the Onenand device controller */ ++ /*************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*********************************************************/ ++ /* Read the necessary status reg from the onenand device */ ++ /*********************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the GENP3 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_GENP_REG3; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the DEVCMD4 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_DEV_CMD4; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->len = 4; ++ cmd++; ++ ++ /************************************************************/ ++ /* Restore the necessary registers to proper values */ ++ /************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[3]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[3]); ++ cmd->len = 4; ++ cmd++; ++ ++ ++ BUILD_BUG_ON(20 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) ++ >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ ++ write_prot_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF; ++ interrupt_status = (dma_buffer->data.data4 >> 0) & 0x0000FFFF; ++ controller_status = (dma_buffer->data.data4 >> 16) & 0x0000FFFF; ++ ++#if VERBOSE ++ pr_info("\n%s: sflash status %x %x %x %x\n", __func__, ++ dma_buffer->data.sfstat[0], ++ dma_buffer->data.sfstat[1], ++ dma_buffer->data.sfstat[2], ++ dma_buffer->data.sfstat[3]); ++ ++ pr_info("%s: controller_status = %x\n", __func__, ++ controller_status); ++ pr_info("%s: interrupt_status = %x\n", __func__, ++ interrupt_status); ++ pr_info("%s: write_prot_status = %x\n", __func__, ++ write_prot_status); ++#endif ++ /* Check for errors, protection violations etc */ ++ if ((controller_status != 0) ++ || (dma_buffer->data.sfstat[0] & 0x110) ++ || (dma_buffer->data.sfstat[1] & 0x110) ++ || (dma_buffer->data.sfstat[2] & 0x110) ++ || (dma_buffer->data.sfstat[3] & 0x110)) { ++ pr_err("%s: ECC/MPU/OP error\n", __func__); ++ err = -EIO; ++ } ++ ++ if (!(write_prot_status & ONENAND_WP_US)) { ++ pr_err("%s: Unexpected status ofs = 0x%llx," ++ "wp_status = %x\n", ++ __func__, ofs, write_prot_status); ++ err = -EIO; ++ } ++ ++ if (err) ++ break; ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++#if VERBOSE ++ pr_info("\n%s: ret %d\n", __func__, err); ++ pr_info("====================================================" ++ "=============\n"); ++#endif ++ return err; ++} ++ ++static int msm_onenand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ ++ struct { ++ dmov_s cmd[20]; ++ unsigned cmdptr; ++ struct { ++ uint32_t sfbcfg; ++ uint32_t sfcmd[4]; ++ uint32_t sfexec; ++ uint32_t sfstat[4]; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t addr2; ++ uint32_t addr3; ++ uint32_t addr4; ++ uint32_t addr5; ++ uint32_t addr6; ++ uint32_t data0; ++ uint32_t data1; ++ uint32_t data2; ++ uint32_t data3; ++ uint32_t data4; ++ uint32_t data5; ++ uint32_t data6; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ ++ int err = 0; ++ ++ uint16_t onenand_startaddr1; ++ uint16_t onenand_startaddr8; ++ uint16_t onenand_startaddr2; ++ uint16_t onenand_startblock; ++ ++ uint16_t controller_status; ++ uint16_t interrupt_status; ++ uint16_t write_prot_status; ++ ++ uint64_t start_ofs; ++ ++#if VERBOSE ++ pr_info("====================================================" ++ "=============\n"); ++ pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len); ++#endif ++ /* 'ofs' & 'len' should align to block size */ ++ if (ofs&(mtd->erasesize - 1)) { ++ pr_err("%s: Unsupported ofs address, 0x%llx\n", ++ __func__, ofs); ++ return -EINVAL; ++ } ++ ++ if (len&(mtd->erasesize - 1)) { ++ pr_err("%s: Unsupported len, %lld\n", ++ __func__, len); ++ return -EINVAL; ++ } ++ ++ if (ofs+len > mtd->size) { ++ pr_err("%s: Maximum chip size exceeded\n", __func__); ++ return -EINVAL; ++ } ++ ++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer ++ (chip, sizeof(*dma_buffer)))); ++ ++ for (start_ofs = ofs; ofs < start_ofs+len; ofs = ofs+mtd->erasesize) { ++#if VERBOSE ++ pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len); ++#endif ++ ++ cmd = dma_buffer->cmd; ++ if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP) ++ && (ofs >= (mtd->size>>1))) { /* DDP Device */ ++ onenand_startaddr1 = DEVICE_FLASHCORE_1 | ++ (((uint32_t)(ofs - (mtd->size>>1)) ++ / mtd->erasesize)); ++ onenand_startaddr2 = DEVICE_BUFFERRAM_1; ++ onenand_startblock = ((uint32_t)(ofs - (mtd->size>>1)) ++ / mtd->erasesize); ++ } else { ++ onenand_startaddr1 = DEVICE_FLASHCORE_0 | ++ ((uint32_t)ofs / mtd->erasesize) ; ++ onenand_startaddr2 = DEVICE_BUFFERRAM_0; ++ onenand_startblock = ((uint32_t)ofs ++ / mtd->erasesize); ++ } ++ ++ onenand_startaddr8 = 0x0000; ++ dma_buffer->data.sfbcfg = SFLASH_BCFG | ++ (nand_sfcmd_mode ? 0 : (1 << 24)); ++ dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_INTHI); ++ dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGRD); ++ dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(4, 10, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfexec = 1; ++ dma_buffer->data.sfstat[0] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[1] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[2] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[3] = CLEAN_DATA_32; ++ dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.addr2 = (ONENAND_START_BLOCK_ADDRESS << 16) | ++ (ONENAND_START_ADDRESS_2); ++ dma_buffer->data.addr3 = (ONENAND_WRITE_PROT_STATUS << 16) | ++ (ONENAND_COMMAND); ++ dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) | ++ (ONENAND_INTERRUPT_STATUS); ++ dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) | ++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); ++ dma_buffer->data.data1 = (onenand_startaddr8 << 16) | ++ (onenand_startaddr1); ++ dma_buffer->data.data2 = (onenand_startblock << 16) | ++ (onenand_startaddr2); ++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | ++ (ONENAND_CMD_LOCK); ++ dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) | ++ (CLEAN_DATA_16); ++ dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) | ++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); ++ dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) | ++ (ONENAND_STARTADDR1_RES); ++ ++ /*************************************************************/ ++ /* Write the necessary address reg in the onenand device */ ++ /*************************************************************/ ++ ++ /* Enable and configure the SFlash controller */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the ADDR0 and ADDR1 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0); ++ cmd->dst = MSM_NAND_ADDR0; ++ cmd->len = 8; ++ cmd++; ++ ++ /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2); ++ cmd->dst = MSM_NAND_ADDR2; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the ADDR6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6); ++ cmd->dst = MSM_NAND_ADDR6; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the GENP0, GENP1, GENP2, GENP3, GENP4 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0); ++ cmd->dst = MSM_NAND_GENP_REG0; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the FLASH_DEV_CMD4,5,6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->dst = MSM_NAND_DEV_CMD4; ++ cmd->len = 12; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*************************************************************/ ++ /* Wait for the interrupt from the Onenand device controller */ ++ /*************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*********************************************************/ ++ /* Read the necessary status reg from the onenand device */ ++ /*********************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the GENP3 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_GENP_REG3; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the DEVCMD4 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_DEV_CMD4; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->len = 4; ++ cmd++; ++ ++ /************************************************************/ ++ /* Restore the necessary registers to proper values */ ++ /************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[3]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[3]); ++ cmd->len = 4; ++ cmd++; ++ ++ ++ BUILD_BUG_ON(20 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) ++ >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ ++ write_prot_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF; ++ interrupt_status = (dma_buffer->data.data4 >> 0) & 0x0000FFFF; ++ controller_status = (dma_buffer->data.data4 >> 16) & 0x0000FFFF; ++ ++#if VERBOSE ++ pr_info("\n%s: sflash status %x %x %x %x\n", __func__, ++ dma_buffer->data.sfstat[0], ++ dma_buffer->data.sfstat[1], ++ dma_buffer->data.sfstat[2], ++ dma_buffer->data.sfstat[3]); ++ ++ pr_info("%s: controller_status = %x\n", __func__, ++ controller_status); ++ pr_info("%s: interrupt_status = %x\n", __func__, ++ interrupt_status); ++ pr_info("%s: write_prot_status = %x\n", __func__, ++ write_prot_status); ++#endif ++ /* Check for errors, protection violations etc */ ++ if ((controller_status != 0) ++ || (dma_buffer->data.sfstat[0] & 0x110) ++ || (dma_buffer->data.sfstat[1] & 0x110) ++ || (dma_buffer->data.sfstat[2] & 0x110) ++ || (dma_buffer->data.sfstat[3] & 0x110)) { ++ pr_err("%s: ECC/MPU/OP error\n", __func__); ++ err = -EIO; ++ } ++ ++ if (!(write_prot_status & ONENAND_WP_LS)) { ++ pr_err("%s: Unexpected status ofs = 0x%llx," ++ "wp_status = %x\n", ++ __func__, ofs, write_prot_status); ++ err = -EIO; ++ } ++ ++ if (err) ++ break; ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++#if VERBOSE ++ pr_info("\n%s: ret %d\n", __func__, err); ++ pr_info("====================================================" ++ "=============\n"); ++#endif ++ return err; ++} ++ ++static int msm_onenand_suspend(struct mtd_info *mtd) ++{ ++ return 0; ++} ++ ++static void msm_onenand_resume(struct mtd_info *mtd) ++{ ++} ++ ++int msm_onenand_scan(struct mtd_info *mtd, int maxchips) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ ++ /* Probe and check whether onenand device is present */ ++ if (flash_onenand_probe(chip)) ++ return -ENODEV; ++ ++ mtd->size = 0x1000000 << ((onenand_info.device_id & 0xF0) >> 4); ++ mtd->writesize = onenand_info.data_buf_size; ++ mtd->oobsize = mtd->writesize >> 5; ++ mtd->erasesize = mtd->writesize << 6; ++ mtd->oobavail = msm_onenand_oob_64.oobavail; ++ mtd->ecclayout = &msm_onenand_oob_64; ++ ++ mtd->type = MTD_NANDFLASH; ++ mtd->flags = MTD_CAP_NANDFLASH; ++ mtd->_erase = msm_onenand_erase; ++ mtd->_point = NULL; ++ mtd->_unpoint = NULL; ++ mtd->_read = msm_onenand_read; ++ mtd->_write = msm_onenand_write; ++ mtd->_read_oob = msm_onenand_read_oob; ++ mtd->_write_oob = msm_onenand_write_oob; ++ mtd->_lock = msm_onenand_lock; ++ mtd->_unlock = msm_onenand_unlock; ++ mtd->_suspend = msm_onenand_suspend; ++ mtd->_resume = msm_onenand_resume; ++ mtd->_block_isbad = msm_onenand_block_isbad; ++ mtd->_block_markbad = msm_onenand_block_markbad; ++ mtd->owner = THIS_MODULE; ++ ++ pr_info("Found a supported onenand device\n"); ++ ++ return 0; ++} ++ ++static const unsigned int bch_sup_cntrl[] = { ++ 0x307, /* MSM7x2xA */ ++ 0x4030, /* MDM 9x15 */ ++}; ++ ++static inline bool msm_nand_has_bch_ecc_engine(unsigned int hw_id) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(bch_sup_cntrl); i++) { ++ if (hw_id == bch_sup_cntrl[i]) ++ return true; ++ } ++ ++ return false; ++} ++ ++/** ++ * msm_nand_scan - [msm_nand Interface] Scan for the msm_nand device ++ * @param mtd MTD device structure ++ * @param maxchips Number of chips to scan for ++ * ++ * This fills out all the not initialized function pointers ++ * with the defaults. ++ * The flash ID is read and the mtd/chip structures are ++ * filled with the appropriate values. ++ */ ++int msm_nand_scan(struct mtd_info *mtd, int maxchips) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ uint32_t flash_id = 0, i, mtd_writesize; ++ uint8_t dev_found = 0; ++ uint8_t wide_bus; ++ uint32_t manid; ++ uint32_t devid; ++ uint32_t devcfg; ++ struct nand_flash_dev *flashdev = NULL; ++ struct nand_manufacturers *flashman = NULL; ++ unsigned int hw_id; ++ ++ /* ++ * Some Spansion parts, like the S34MS04G2, requires that the ++ * NAND Flash be reset before issuing an ONFI probe. ++ */ ++ flash_reset(chip); ++ ++ /* Probe the Flash device for ONFI compliance */ ++ if (!flash_onfi_probe(chip)) { ++ dev_found = 1; ++ } else { ++ /* Read the Flash ID from the Nand Flash Device */ ++ flash_id = flash_read_id(chip); ++ manid = flash_id & 0xFF; ++ devid = (flash_id >> 8) & 0xFF; ++ devcfg = (flash_id >> 24) & 0xFF; ++ ++ for (i = 0; !flashman && nand_manuf_ids[i].id; ++i) ++ if (nand_manuf_ids[i].id == manid) ++ flashman = &nand_manuf_ids[i]; ++ for (i = 0; !flashdev && nand_flash_ids[i].id; ++i) ++ if (nand_flash_ids[i].id == devid) ++ flashdev = &nand_flash_ids[i]; ++ if (!flashdev || !flashman) { ++ pr_err("ERROR: unknown nand device manuf=%x devid=%x\n", ++ manid, devid); ++ return -ENOENT; ++ } else ++ dev_found = 1; ++ ++ if (!flashdev->pagesize) { ++ supported_flash.flash_id = flash_id; ++ supported_flash.density = flashdev->chipsize << 20; ++ supported_flash.widebus = devcfg & (1 << 6) ? 1 : 0; ++ supported_flash.pagesize = 1024 << (devcfg & 0x3); ++ supported_flash.blksize = (64 * 1024) << ++ ((devcfg >> 4) & 0x3); ++ supported_flash.oobsize = (8 << ((devcfg >> 2) & 0x3)) * ++ (supported_flash.pagesize >> 9); ++ ++ if ((supported_flash.oobsize > 64) && ++ (supported_flash.pagesize == 2048)) { ++ pr_info("msm_nand: Found a 2K page device with" ++ " %d oobsize - changing oobsize to 64 " ++ "bytes.\n", supported_flash.oobsize); ++ supported_flash.oobsize = 64; ++ } ++ } else { ++ supported_flash.flash_id = flash_id; ++ supported_flash.density = flashdev->chipsize << 20; ++ supported_flash.widebus = flashdev->options & ++ NAND_BUSWIDTH_16 ? 1 : 0; ++ supported_flash.pagesize = flashdev->pagesize; ++ supported_flash.blksize = flashdev->erasesize; ++ supported_flash.oobsize = flashdev->pagesize >> 5; ++ } ++ } ++ ++ if (dev_found) { ++ (!interleave_enable) ? (i = 1) : (i = 2); ++ wide_bus = supported_flash.widebus; ++ mtd->size = supported_flash.density * i; ++ mtd->writesize = supported_flash.pagesize * i; ++ mtd->oobsize = supported_flash.oobsize * i; ++ mtd->erasesize = supported_flash.blksize * i; ++ mtd->writebufsize = mtd->writesize; ++ ++ if (!interleave_enable) ++ mtd_writesize = mtd->writesize; ++ else ++ mtd_writesize = mtd->writesize >> 1; ++ ++ /* Check whether controller and NAND device support 8bit ECC*/ ++ hw_id = flash_rd_reg(chip, MSM_NAND_HW_INFO); ++ if (msm_nand_has_bch_ecc_engine(hw_id) ++ && (supported_flash.ecc_correctability >= 8)) { ++ pr_info("Found supported NAND device for %dbit ECC\n", ++ supported_flash.ecc_correctability); ++ enable_bch_ecc = 1; ++ } else { ++ pr_info("Found a supported NAND device\n"); ++ } ++ pr_info("NAND Controller ID : 0x%x\n", hw_id); ++ pr_info("NAND Device ID : 0x%x\n", supported_flash.flash_id); ++ pr_info("Buswidth : %d Bits\n", (wide_bus) ? 16 : 8); ++ pr_info("Density : %lld MByte\n", (mtd->size>>20)); ++ pr_info("Pagesize : %d Bytes\n", mtd->writesize); ++ pr_info("Erasesize: %d Bytes\n", mtd->erasesize); ++ pr_info("Oobsize : %d Bytes\n", mtd->oobsize); ++ } else { ++ pr_err("Unsupported Nand,Id: 0x%x \n", flash_id); ++ return -ENODEV; ++ } ++ ++ /* Size of each codeword is 532Bytes incase of 8bit BCH ECC*/ ++ chip->cw_size = enable_bch_ecc ? 532 : 528; ++ chip->CFG0 = (((mtd_writesize >> 9)-1) << 6) /* 4/8 cw/pg for 2/4k */ ++ | (516 << 9) /* 516 user data bytes */ ++ | (10 << 19) /* 10 parity bytes */ ++ | (5 << 27) /* 5 address cycles */ ++ | (0 << 30) /* Do not read status before data */ ++ | (1 << 31) /* Send read cmd */ ++ /* 0 spare bytes for 16 bit nand or 1/2 spare bytes for 8 bit */ ++ | (wide_bus ? 0 << 23 : (enable_bch_ecc ? 2 << 23 : 1 << 23)); ++ ++ chip->CFG1 = (0 << 0) /* Enable ecc */ ++ | (7 << 2) /* 8 recovery cycles */ ++ | (0 << 5) /* Allow CS deassertion */ ++ /* Bad block marker location */ ++ | ((mtd_writesize - (chip->cw_size * ( ++ (mtd_writesize >> 9) - 1)) + 1) << 6) ++ | (0 << 16) /* Bad block in user data area */ ++ | (2 << 17) /* 6 cycle tWB/tRB */ ++ | ((wide_bus) ? CFG1_WIDE_FLASH : 0); /* Wide flash bit */ ++ ++ chip->ecc_buf_cfg = 0x203; ++ chip->CFG0_RAW = 0xA80420C0; ++ chip->CFG1_RAW = 0x5045D; ++ ++ if (enable_bch_ecc) { ++ chip->CFG1 |= (1 << 27); /* Enable BCH engine */ ++ chip->ecc_bch_cfg = (0 << 0) /* Enable ECC*/ ++ | (0 << 1) /* Enable/Disable SW reset of ECC engine */ ++ | (1 << 4) /* 8bit ecc*/ ++ | ((wide_bus) ? (14 << 8) : (13 << 8))/*parity bytes*/ ++ | (516 << 16) /* 516 user data bytes */ ++ | (1 << 30); /* Turn on ECC engine clocks always */ ++ chip->CFG0_RAW = 0xA80428C0; /* CW size is increased to 532B */ ++ } ++ ++ /* ++ * For 4bit RS ECC (default ECC), parity bytes = 10 (for x8 and x16 I/O) ++ * For 8bit BCH ECC, parity bytes = 13 (x8) or 14 (x16 I/O). ++ */ ++ chip->ecc_parity_bytes = enable_bch_ecc ? (wide_bus ? 14 : 13) : 10; ++ ++ pr_info("CFG0 Init : 0x%08x\n", chip->CFG0); ++ pr_info("CFG1 Init : 0x%08x\n", chip->CFG1); ++ pr_info("ECCBUFCFG : 0x%08x\n", chip->ecc_buf_cfg); ++ ++ if (mtd->oobsize == 64) { ++ mtd->oobavail = msm_nand_oob_64.oobavail; ++ mtd->ecclayout = &msm_nand_oob_64; ++ } else if (mtd->oobsize == 128) { ++ mtd->oobavail = msm_nand_oob_128.oobavail; ++ mtd->ecclayout = &msm_nand_oob_128; ++ } else if (mtd->oobsize == 224) { ++ mtd->oobavail = wide_bus ? msm_nand_oob_224_x16.oobavail : ++ msm_nand_oob_224_x8.oobavail; ++ mtd->ecclayout = wide_bus ? &msm_nand_oob_224_x16 : ++ &msm_nand_oob_224_x8; ++ } else if (mtd->oobsize == 256) { ++ mtd->oobavail = msm_nand_oob_256.oobavail; ++ mtd->ecclayout = &msm_nand_oob_256; ++ } else { ++ pr_err("Unsupported Nand, oobsize: 0x%x \n", ++ mtd->oobsize); ++ return -ENODEV; ++ } ++ ++ /* Fill in remaining MTD driver data */ ++ mtd->type = MTD_NANDFLASH; ++ mtd->flags = MTD_CAP_NANDFLASH; ++ /* mtd->ecctype = MTD_ECC_SW; */ ++ mtd->_erase = msm_nand_erase; ++ mtd->_block_isbad = msm_nand_block_isbad; ++ mtd->_block_markbad = msm_nand_block_markbad; ++ mtd->_point = NULL; ++ mtd->_unpoint = NULL; ++ mtd->_read = msm_nand_read; ++ mtd->_write = msm_nand_write; ++ mtd->_read_oob = msm_nand_read_oob; ++ mtd->_write_oob = msm_nand_write_oob; ++ if (dual_nand_ctlr_present) { ++ mtd->_read_oob = msm_nand_read_oob_dualnandc; ++ mtd->_write_oob = msm_nand_write_oob_dualnandc; ++ if (interleave_enable) { ++ mtd->_erase = msm_nand_erase_dualnandc; ++ mtd->_block_isbad = msm_nand_block_isbad_dualnandc; ++ } ++ } ++ ++ /* mtd->sync = msm_nand_sync; */ ++ mtd->_lock = NULL; ++ /* mtd->_unlock = msm_nand_unlock; */ ++ mtd->_suspend = msm_nand_suspend; ++ mtd->_resume = msm_nand_resume; ++ mtd->owner = THIS_MODULE; ++ ++ /* Unlock whole block */ ++ /* msm_nand_unlock_all(mtd); */ ++ ++ /* return this->scan_bbt(mtd); */ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(msm_nand_scan); ++ ++/** ++ * msm_nand_release - [msm_nand Interface] Free resources held by the msm_nand device ++ * @param mtd MTD device structure ++ */ ++void msm_nand_release(struct mtd_info *mtd) ++{ ++ /* struct msm_nand_chip *this = mtd->priv; */ ++ ++ /* Deregister the device */ ++ mtd_device_unregister(mtd); ++} ++EXPORT_SYMBOL_GPL(msm_nand_release); ++ ++struct msm_nand_info { ++ struct mtd_info mtd; ++ struct mtd_partition *parts; ++ struct msm_nand_chip msm_nand; ++}; ++ ++/* duplicating the NC01 XFR contents to NC10 */ ++static int msm_nand_nc10_xfr_settings(struct mtd_info *mtd) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ ++ struct { ++ dmov_s cmd[2]; ++ unsigned cmdptr; ++ } *dma_buffer; ++ dmov_s *cmd; ++ ++ wait_event(chip->wait_queue, ++ (dma_buffer = msm_nand_get_dma_buffer( ++ chip, sizeof(*dma_buffer)))); ++ ++ cmd = dma_buffer->cmd; ++ ++ /* Copying XFR register contents from NC01 --> NC10 */ ++ cmd->cmd = 0; ++ cmd->src = NC01(MSM_NAND_XFR_STEP1); ++ cmd->dst = NC10(MSM_NAND_XFR_STEP1); ++ cmd->len = 28; ++ cmd++; ++ ++ BUILD_BUG_ON(2 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) ++ | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST ++ | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ return 0; ++} ++ ++static ssize_t boot_layout_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ return sprintf(buf, "%d\n", boot_layout); ++} ++ ++static ssize_t boot_layout_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t n) ++{ ++ struct msm_nand_info *info = dev_get_drvdata(dev); ++ struct msm_nand_chip *chip = info->mtd.priv; ++ unsigned int ud_size; ++ unsigned int spare_size; ++ unsigned int ecc_num_data_bytes; ++ ++ sscanf(buf, "%d", &boot_layout); ++ ++ ud_size = boot_layout? 512: 516; ++ spare_size = boot_layout? (chip->cw_size - ++ (chip->ecc_parity_bytes+ 1+ ud_size)): ++ (enable_bch_ecc ? 2 : 1); ++ ecc_num_data_bytes = boot_layout? 512: 516; ++ ++ chip->CFG0 = (chip->CFG0 & ~SPARE_SIZE_BYTES_MASK); ++ chip->CFG0 |= (spare_size << 23); ++ ++ chip->CFG0 = (chip->CFG0 & ~UD_SIZE_BYTES_MASK); ++ chip->CFG0 |= (ud_size << 9); ++ ++ chip->ecc_buf_cfg = (chip->ecc_buf_cfg & ~ECC_NUM_DATA_BYTES_MASK) ++ | (ecc_num_data_bytes << 16); ++ ++ return n; ++} ++ ++static const DEVICE_ATTR(boot_layout, 0644, boot_layout_show, boot_layout_store); ++ ++static int msm_nand_probe(struct platform_device *pdev) ++ ++{ ++ struct msm_nand_info *info; ++ struct resource *res; ++ int err; ++ struct mtd_part_parser_data ppdata = {}; ++ ++ ++ res = platform_get_resource(pdev, ++ IORESOURCE_MEM, 0); ++ if (!res || !res->start) { ++ pr_err("%s: msm_nand_phys resource invalid/absent\n", ++ __func__); ++ return -ENODEV; ++ } ++ msm_nand_phys = res->start; ++ ++ info = devm_kzalloc(&pdev->dev, sizeof(struct msm_nand_info), GFP_KERNEL); ++ if (!info) { ++ pr_err("%s: No memory for msm_nand_info\n", __func__); ++ return -ENOMEM; ++ } ++ ++ info->msm_nand.dev = &pdev->dev; ++ ++ init_waitqueue_head(&info->msm_nand.wait_queue); ++ ++ info->msm_nand.dma_channel = 3; ++ pr_info("%s: dmac 0x%x\n", __func__, info->msm_nand.dma_channel); ++ ++ /* this currently fails if dev is passed in */ ++ info->msm_nand.dma_buffer = ++ dma_alloc_coherent(/*dev*/ NULL, MSM_NAND_DMA_BUFFER_SIZE, ++ &info->msm_nand.dma_addr, GFP_KERNEL); ++ if (info->msm_nand.dma_buffer == NULL) { ++ pr_err("%s: No memory for msm_nand.dma_buffer\n", __func__); ++ err = -ENOMEM; ++ goto out_free_info; ++ } ++ ++ pr_info("%s: allocated dma buffer at %p, dma_addr %x\n", ++ __func__, info->msm_nand.dma_buffer, info->msm_nand.dma_addr); ++ ++ /* Let default be VERSION_1 for backward compatibility */ ++ info->msm_nand.uncorrectable_bit_mask = BIT(8); ++ info->msm_nand.num_err_mask = 0x1F; ++ ++ info->mtd.name = dev_name(&pdev->dev); ++ info->mtd.priv = &info->msm_nand; ++ info->mtd.owner = THIS_MODULE; ++ ++ /* config ebi2_cfg register only for ping pong mode!!! */ ++ if (!interleave_enable && dual_nand_ctlr_present) ++ flash_wr_reg(&info->msm_nand, EBI2_CFG_REG, 0x4010080); ++ ++ if (dual_nand_ctlr_present) ++ msm_nand_nc10_xfr_settings(&info->mtd); ++ ++ if (msm_nand_scan(&info->mtd, 1)) ++ if (msm_onenand_scan(&info->mtd, 1)) { ++ pr_err("%s: No nand device found\n", __func__); ++ err = -ENXIO; ++ goto out_free_dma_buffer; ++ } ++ ++ flash_wr_reg(&info->msm_nand, MSM_NAND_DEV_CMD_VLD, ++ DEV_CMD_VLD_SEQ_READ_START_VLD | ++ DEV_CMD_VLD_ERASE_START_VLD | ++ DEV_CMD_VLD_WRITE_START_VLD | ++ DEV_CMD_VLD_READ_START_VLD); ++ ++ ppdata.of_node = pdev->dev.of_node; ++ err = mtd_device_parse_register(&info->mtd, NULL, &ppdata, NULL, 0); ++ ++ if (err < 0) { ++ pr_err("%s: mtd_device_parse_register failed with err=%d\n", ++ __func__, err); ++ goto out_free_dma_buffer; ++ } ++ ++ err = sysfs_create_file(&pdev->dev.kobj, &dev_attr_boot_layout.attr); ++ if (err) ++ goto out_free_dma_buffer; ++ ++ dev_set_drvdata(&pdev->dev, info); ++ ++ return 0; ++ ++out_free_dma_buffer: ++ dma_free_coherent(NULL, MSM_NAND_DMA_BUFFER_SIZE, ++ info->msm_nand.dma_buffer, ++ info->msm_nand.dma_addr); ++out_free_info: ++ return err; ++} ++ ++static int msm_nand_remove(struct platform_device *pdev) ++{ ++ struct msm_nand_info *info = dev_get_drvdata(&pdev->dev); ++ ++ dev_set_drvdata(&pdev->dev, NULL); ++ ++ if (info) { ++ msm_nand_release(&info->mtd); ++ dma_free_coherent(NULL, MSM_NAND_DMA_BUFFER_SIZE, ++ info->msm_nand.dma_buffer, ++ info->msm_nand.dma_addr); ++ } ++ ++ sysfs_remove_file(&pdev->dev.kobj, &dev_attr_boot_layout.attr); ++ ++ return 0; ++} ++ ++ ++#ifdef CONFIG_OF ++static const struct of_device_id msm_nand_of_match[] = { ++ { .compatible = "qcom,qcom_nand", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, msm_nand_of_match); ++#endif ++ ++ ++static struct platform_driver msm_nand_driver = { ++ .probe = msm_nand_probe, ++ .remove = msm_nand_remove, ++ .driver = { ++ .name = "qcom_nand", ++ .owner = THIS_MODULE, ++ .of_match_table = msm_nand_of_match, ++ } ++}; ++ ++ ++module_platform_driver(msm_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("msm_nand flash driver code"); +diff --git a/drivers/mtd/nand/qcom_nand.h b/drivers/mtd/nand/qcom_nand.h +new file mode 100644 +index 0000000..468186c +--- /dev/null ++++ b/drivers/mtd/nand/qcom_nand.h +@@ -0,0 +1,196 @@ ++/* drivers/mtd/devices/msm_nand.h ++ * ++ * Copyright (c) 2008-2011, The Linux Foundation. All rights reserved. ++ * Copyright (C) 2007 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef __DRIVERS_MTD_DEVICES_MSM_NAND_H ++#define __DRIVERS_MTD_DEVICES_MSM_NAND_H ++ ++extern unsigned long msm_nand_phys; ++extern unsigned long msm_nandc01_phys; ++extern unsigned long msm_nandc10_phys; ++extern unsigned long msm_nandc11_phys; ++extern unsigned long ebi2_register_base; ++ ++#define NC01(X) ((X) + msm_nandc01_phys - msm_nand_phys) ++#define NC10(X) ((X) + msm_nandc10_phys - msm_nand_phys) ++#define NC11(X) ((X) + msm_nandc11_phys - msm_nand_phys) ++ ++#define MSM_NAND_REG(off) (msm_nand_phys + (off)) ++ ++#define MSM_NAND_FLASH_CMD MSM_NAND_REG(0x0000) ++#define MSM_NAND_ADDR0 MSM_NAND_REG(0x0004) ++#define MSM_NAND_ADDR1 MSM_NAND_REG(0x0008) ++#define MSM_NAND_FLASH_CHIP_SELECT MSM_NAND_REG(0x000C) ++#define MSM_NAND_EXEC_CMD MSM_NAND_REG(0x0010) ++#define MSM_NAND_FLASH_STATUS MSM_NAND_REG(0x0014) ++#define MSM_NAND_BUFFER_STATUS MSM_NAND_REG(0x0018) ++#define MSM_NAND_SFLASHC_STATUS MSM_NAND_REG(0x001C) ++#define MSM_NAND_DEV0_CFG0 MSM_NAND_REG(0x0020) ++#define MSM_NAND_DEV0_CFG1 MSM_NAND_REG(0x0024) ++#define MSM_NAND_DEV0_ECC_CFG MSM_NAND_REG(0x0028) ++#define MSM_NAND_DEV1_ECC_CFG MSM_NAND_REG(0x002C) ++#define MSM_NAND_DEV1_CFG0 MSM_NAND_REG(0x0030) ++#define MSM_NAND_DEV1_CFG1 MSM_NAND_REG(0x0034) ++#define MSM_NAND_SFLASHC_CMD MSM_NAND_REG(0x0038) ++#define MSM_NAND_SFLASHC_EXEC_CMD MSM_NAND_REG(0x003C) ++#define MSM_NAND_READ_ID MSM_NAND_REG(0x0040) ++#define MSM_NAND_READ_STATUS MSM_NAND_REG(0x0044) ++#define MSM_NAND_CONFIG_DATA MSM_NAND_REG(0x0050) ++#define MSM_NAND_CONFIG MSM_NAND_REG(0x0054) ++#define MSM_NAND_CONFIG_MODE MSM_NAND_REG(0x0058) ++#define MSM_NAND_CONFIG_STATUS MSM_NAND_REG(0x0060) ++#define MSM_NAND_MACRO1_REG MSM_NAND_REG(0x0064) ++#define MSM_NAND_XFR_STEP1 MSM_NAND_REG(0x0070) ++#define MSM_NAND_XFR_STEP2 MSM_NAND_REG(0x0074) ++#define MSM_NAND_XFR_STEP3 MSM_NAND_REG(0x0078) ++#define MSM_NAND_XFR_STEP4 MSM_NAND_REG(0x007C) ++#define MSM_NAND_XFR_STEP5 MSM_NAND_REG(0x0080) ++#define MSM_NAND_XFR_STEP6 MSM_NAND_REG(0x0084) ++#define MSM_NAND_XFR_STEP7 MSM_NAND_REG(0x0088) ++#define MSM_NAND_GENP_REG0 MSM_NAND_REG(0x0090) ++#define MSM_NAND_GENP_REG1 MSM_NAND_REG(0x0094) ++#define MSM_NAND_GENP_REG2 MSM_NAND_REG(0x0098) ++#define MSM_NAND_GENP_REG3 MSM_NAND_REG(0x009C) ++#define MSM_NAND_DEV_CMD0 MSM_NAND_REG(0x00A0) ++#define MSM_NAND_DEV_CMD1 MSM_NAND_REG(0x00A4) ++#define MSM_NAND_DEV_CMD2 MSM_NAND_REG(0x00A8) ++#define MSM_NAND_DEV_CMD_VLD MSM_NAND_REG(0x00AC) ++#define DEV_CMD_VLD_SEQ_READ_START_VLD 0x10 ++#define DEV_CMD_VLD_ERASE_START_VLD 0x8 ++#define DEV_CMD_VLD_WRITE_START_VLD 0x4 ++#define DEV_CMD_VLD_READ_STOP_VLD 0x2 ++#define DEV_CMD_VLD_READ_START_VLD 0x1 ++ ++#define MSM_NAND_EBI2_MISR_SIG_REG MSM_NAND_REG(0x00B0) ++#define MSM_NAND_ADDR2 MSM_NAND_REG(0x00C0) ++#define MSM_NAND_ADDR3 MSM_NAND_REG(0x00C4) ++#define MSM_NAND_ADDR4 MSM_NAND_REG(0x00C8) ++#define MSM_NAND_ADDR5 MSM_NAND_REG(0x00CC) ++#define MSM_NAND_DEV_CMD3 MSM_NAND_REG(0x00D0) ++#define MSM_NAND_DEV_CMD4 MSM_NAND_REG(0x00D4) ++#define MSM_NAND_DEV_CMD5 MSM_NAND_REG(0x00D8) ++#define MSM_NAND_DEV_CMD6 MSM_NAND_REG(0x00DC) ++#define MSM_NAND_SFLASHC_BURST_CFG MSM_NAND_REG(0x00E0) ++#define MSM_NAND_ADDR6 MSM_NAND_REG(0x00E4) ++#define MSM_NAND_EBI2_ECC_BUF_CFG MSM_NAND_REG(0x00F0) ++#define MSM_NAND_HW_INFO MSM_NAND_REG(0x00FC) ++#define MSM_NAND_FLASH_BUFFER MSM_NAND_REG(0x0100) ++ ++/* device commands */ ++ ++#define MSM_NAND_CMD_SOFT_RESET 0x01 ++#define MSM_NAND_CMD_PAGE_READ 0x32 ++#define MSM_NAND_CMD_PAGE_READ_ECC 0x33 ++#define MSM_NAND_CMD_PAGE_READ_ALL 0x34 ++#define MSM_NAND_CMD_SEQ_PAGE_READ 0x15 ++#define MSM_NAND_CMD_PRG_PAGE 0x36 ++#define MSM_NAND_CMD_PRG_PAGE_ECC 0x37 ++#define MSM_NAND_CMD_PRG_PAGE_ALL 0x39 ++#define MSM_NAND_CMD_BLOCK_ERASE 0x3A ++#define MSM_NAND_CMD_FETCH_ID 0x0B ++#define MSM_NAND_CMD_STATUS 0x0C ++#define MSM_NAND_CMD_RESET 0x0D ++ ++/* Sflash Commands */ ++ ++#define MSM_NAND_SFCMD_DATXS 0x0 ++#define MSM_NAND_SFCMD_CMDXS 0x1 ++#define MSM_NAND_SFCMD_BURST 0x0 ++#define MSM_NAND_SFCMD_ASYNC 0x1 ++#define MSM_NAND_SFCMD_ABORT 0x1 ++#define MSM_NAND_SFCMD_REGRD 0x2 ++#define MSM_NAND_SFCMD_REGWR 0x3 ++#define MSM_NAND_SFCMD_INTLO 0x4 ++#define MSM_NAND_SFCMD_INTHI 0x5 ++#define MSM_NAND_SFCMD_DATRD 0x6 ++#define MSM_NAND_SFCMD_DATWR 0x7 ++ ++#define SFLASH_PREPCMD(numxfr, offval, delval, trnstp, mode, opcode) \ ++ ((numxfr<<20)|(offval<<12)|(delval<<6)|(trnstp<<5)|(mode<<4)|opcode) ++ ++#define SFLASH_BCFG 0x20100327 ++ ++/* Onenand addresses */ ++ ++#define ONENAND_MANUFACTURER_ID 0xF000 ++#define ONENAND_DEVICE_ID 0xF001 ++#define ONENAND_VERSION_ID 0xF002 ++#define ONENAND_DATA_BUFFER_SIZE 0xF003 ++#define ONENAND_BOOT_BUFFER_SIZE 0xF004 ++#define ONENAND_AMOUNT_OF_BUFFERS 0xF005 ++#define ONENAND_TECHNOLOGY 0xF006 ++#define ONENAND_START_ADDRESS_1 0xF100 ++#define ONENAND_START_ADDRESS_2 0xF101 ++#define ONENAND_START_ADDRESS_3 0xF102 ++#define ONENAND_START_ADDRESS_4 0xF103 ++#define ONENAND_START_ADDRESS_5 0xF104 ++#define ONENAND_START_ADDRESS_6 0xF105 ++#define ONENAND_START_ADDRESS_7 0xF106 ++#define ONENAND_START_ADDRESS_8 0xF107 ++#define ONENAND_START_BUFFER 0xF200 ++#define ONENAND_COMMAND 0xF220 ++#define ONENAND_SYSTEM_CONFIG_1 0xF221 ++#define ONENAND_SYSTEM_CONFIG_2 0xF222 ++#define ONENAND_CONTROLLER_STATUS 0xF240 ++#define ONENAND_INTERRUPT_STATUS 0xF241 ++#define ONENAND_START_BLOCK_ADDRESS 0xF24C ++#define ONENAND_WRITE_PROT_STATUS 0xF24E ++#define ONENAND_ECC_STATUS 0xFF00 ++#define ONENAND_ECC_ERRPOS_MAIN0 0xFF01 ++#define ONENAND_ECC_ERRPOS_SPARE0 0xFF02 ++#define ONENAND_ECC_ERRPOS_MAIN1 0xFF03 ++#define ONENAND_ECC_ERRPOS_SPARE1 0xFF04 ++#define ONENAND_ECC_ERRPOS_MAIN2 0xFF05 ++#define ONENAND_ECC_ERRPOS_SPARE2 0xFF06 ++#define ONENAND_ECC_ERRPOS_MAIN3 0xFF07 ++#define ONENAND_ECC_ERRPOS_SPARE3 0xFF08 ++ ++/* Onenand commands */ ++#define ONENAND_WP_US (1 << 2) ++#define ONENAND_WP_LS (1 << 1) ++ ++#define ONENAND_CMDLOAD 0x0000 ++#define ONENAND_CMDLOADSPARE 0x0013 ++#define ONENAND_CMDPROG 0x0080 ++#define ONENAND_CMDPROGSPARE 0x001A ++#define ONENAND_CMDERAS 0x0094 ++#define ONENAND_CMD_UNLOCK 0x0023 ++#define ONENAND_CMD_LOCK 0x002A ++ ++#define ONENAND_SYSCFG1_ECCENA(mode) (0x40E0 | (mode ? 0 : 0x8002)) ++#define ONENAND_SYSCFG1_ECCDIS(mode) (0x41E0 | (mode ? 0 : 0x8002)) ++ ++#define ONENAND_CLRINTR 0x0000 ++#define ONENAND_STARTADDR1_RES 0x07FF ++#define ONENAND_STARTADDR3_RES 0x07FF ++ ++#define DATARAM0_0 0x8 ++#define DEVICE_FLASHCORE_0 (0 << 15) ++#define DEVICE_FLASHCORE_1 (1 << 15) ++#define DEVICE_BUFFERRAM_0 (0 << 15) ++#define DEVICE_BUFFERRAM_1 (1 << 15) ++#define ONENAND_DEVICE_IS_DDP (1 << 3) ++ ++#define CLEAN_DATA_16 0xFFFF ++#define CLEAN_DATA_32 0xFFFFFFFF ++ ++#define EBI2_REG(off) (ebi2_register_base + (off)) ++#define EBI2_CHIP_SELECT_CFG0 EBI2_REG(0x0000) ++#define EBI2_CFG_REG EBI2_REG(0x0004) ++#define EBI2_NAND_ADM_MUX EBI2_REG(0x005C) ++ ++extern struct flash_platform_data msm_nand_data; ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0151-ARM-ipq8064-Add-nand-device-info.patch b/target/linux/ipq806x/patches/0151-ARM-ipq8064-Add-nand-device-info.patch new file mode 100644 index 0000000000..b79c16f34e --- /dev/null +++ b/target/linux/ipq806x/patches/0151-ARM-ipq8064-Add-nand-device-info.patch @@ -0,0 +1,115 @@ +From 4490cfa66379909cdddc3518c8e75b7cd26d8f69 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 16 Jun 2014 16:53:49 -0500 +Subject: [PATCH 151/182] ARM: ipq8064: Add nand device info + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 34 ++++++++++++++++++++++++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 33 +++++++++++++++++++++++++++++ + 2 files changed, 67 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +index c752889..4062eb6 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -45,6 +45,29 @@ + bias-none; + }; + }; ++ nand_pins: nand_pins { ++ mux { ++ pins = "gpio34", "gpio35", "gpio36", ++ "gpio37", "gpio38", "gpio39", ++ "gpio40", "gpio41", "gpio42", ++ "gpio43", "gpio44", "gpio45", ++ "gpio46", "gpio47"; ++ function = "nand"; ++ drive-strength = <10>; ++ bias-disable; ++ }; ++ pullups { ++ pins = "gpio39"; ++ bias-pull-up; ++ }; ++ hold { ++ pins = "gpio40", "gpio41", "gpio42", ++ "gpio43", "gpio44", "gpio45", ++ "gpio46", "gpio47"; ++ bias-bus-hold; ++ }; ++ }; ++ + }; + + gsbi@16300000 { +@@ -126,5 +149,16 @@ + sata@29000000 { + status = "ok"; + }; ++ ++ dma@18300000 { ++ status = "ok"; ++ }; ++ ++ nand@0x1ac00000 { ++ status = "ok"; ++ ++ pinctrl-0 = <&nand_pins>; ++ pinctrl-names = "default"; ++ }; + }; + }; +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +index 93c0315..d9fce15 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -76,6 +76,7 @@ + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <0 32 0x4>; ++ + }; + + intc: interrupt-controller@2000000 { +@@ -369,5 +370,37 @@ + phy-names = "sata-phy"; + status = "disabled"; + }; ++ ++ adm_dma: dma@18300000 { ++ compatible = "qcom,adm"; ++ reg = <0x18300000 0x100000>; ++ interrupts = <0 170 0>; ++ ++ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; ++ clock-names = "core_clk", "iface_clk"; ++ ++ resets = <&gcc ADM0_RESET>, ++ <&gcc ADM0_PBUS_RESET>, ++ <&gcc ADM0_C0_RESET>, ++ <&gcc ADM0_C1_RESET>, ++ <&gcc ADM0_C2_RESET>; ++ ++ reset-names = "adm", "pbus", "c0", "c1", "c2"; ++ ++ status = "disabled"; ++ }; ++ ++ nand@0x1ac00000 { ++ compatible = "qcom,qcom_nand"; ++ reg = <0x1ac00000 0x800>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ clocks = <&gcc EBI2_CLK>; ++ clock-names = "core_clk"; ++ ++ ++ status = "disabled"; ++ }; + }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0152-ARM-qcom-config-Add-NAND-config-options.patch b/target/linux/ipq806x/patches/0152-ARM-qcom-config-Add-NAND-config-options.patch new file mode 100644 index 0000000000..97f012dfb6 --- /dev/null +++ b/target/linux/ipq806x/patches/0152-ARM-qcom-config-Add-NAND-config-options.patch @@ -0,0 +1,27 @@ +From d04f697bf7cfe756de6187be1d0e5669d4d5bca0 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 16 Jun 2014 17:30:04 -0500 +Subject: [PATCH 152/182] ARM: qcom: config: Add NAND config options + +Add NAND config options to enable QCOM NAND controller + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + arch/arm/configs/qcom_defconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig +index 85a35af..e3f9013 100644 +--- a/arch/arm/configs/qcom_defconfig ++++ b/arch/arm/configs/qcom_defconfig +@@ -56,6 +56,7 @@ CONFIG_DEVTMPFS_MOUNT=y + CONFIG_MTD=y + CONFIG_MTD_BLOCK=y + CONFIG_MTD_M25P80=y ++CONFIG_MTD_NAND=y + CONFIG_BLK_DEV_LOOP=y + CONFIG_BLK_DEV_RAM=y + CONFIG_SCSI_TGT=y +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0153-soc-qcom-tcsr-Add-TCSR-driver.patch b/target/linux/ipq806x/patches/0153-soc-qcom-tcsr-Add-TCSR-driver.patch new file mode 100644 index 0000000000..03b65e6872 --- /dev/null +++ b/target/linux/ipq806x/patches/0153-soc-qcom-tcsr-Add-TCSR-driver.patch @@ -0,0 +1,169 @@ +From 0b469f3f4c0e6a0e0b1b60a059600e325a03b6f5 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 12 Jun 2014 00:52:06 -0500 +Subject: [PATCH 153/182] soc: qcom: tcsr: Add TCSR driver + +This patch adds support for the TCSR IP block present in Qualcomm processors. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + .../devicetree/bindings/soc/qcom/qcom,tcsr.txt | 25 ++++++++ + drivers/soc/qcom/Kconfig | 6 ++ + drivers/soc/qcom/Makefile | 1 + + drivers/soc/qcom/qcom_tcsr.c | 64 ++++++++++++++++++++ + include/dt-bindings/soc/qcom,tcsr.h | 19 ++++++ + 5 files changed, 115 insertions(+) + create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,tcsr.txt + create mode 100644 drivers/soc/qcom/qcom_tcsr.c + create mode 100644 include/dt-bindings/soc/qcom,tcsr.h + +diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,tcsr.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,tcsr.txt +new file mode 100644 +index 0000000..6ea74c1 +--- /dev/null ++++ b/Documentation/devicetree/bindings/soc/qcom/qcom,tcsr.txt +@@ -0,0 +1,25 @@ ++QCOM TCSR (Top Control and Status Register) Driver ++ ++The TCSR provides miscellaneous control functions and status registers for ++Qualcomm processors. ++ ++Required properties: ++- compatible: must contain "qcom,tcsr" for IPQ806x ++- reg: Address range for TCSR registers ++ ++Optional properties: ++- qcom,usb-ctrl-select : indicates USB port type selection. Please reference ++ dt-bindings/soc/qcom,tcsr.h for valid USB port selection values. ++ ++Example for IPQ8064: ++ ++#include <dt-bindings/soc/qcom,tcsr.h> ++ ++ tcsr: tcsr@1a400000 { ++ compatible = "qcom,tcsr"; ++ reg = <0x1a400000 0x100>; ++ ++ qcom,usb-ctrl-select = <TCSR_USB_SELECT_USB3_DUAL>; ++ }; ++ ++ +diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig +index 7bd2c94..3e4486a 100644 +--- a/drivers/soc/qcom/Kconfig ++++ b/drivers/soc/qcom/Kconfig +@@ -9,3 +9,9 @@ config QCOM_GSBI + functions for connecting the underlying serial UART, SPI, and I2C + devices to the output pins. + ++config QCOM_TCSR ++ tristate "QCOM Top Control and Status Registers" ++ depends on ARCH_QCOM ++ help ++ Say y here to enable TCSR support. The TCSR provides control ++ functions for various peripherals. +diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile +index 4389012..d299492 100644 +--- a/drivers/soc/qcom/Makefile ++++ b/drivers/soc/qcom/Makefile +@@ -1 +1,2 @@ + obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o ++obj-$(CONFIG_QCOM_TCSR) += qcom_tcsr.o +diff --git a/drivers/soc/qcom/qcom_tcsr.c b/drivers/soc/qcom/qcom_tcsr.c +new file mode 100644 +index 0000000..dd33153 +--- /dev/null ++++ b/drivers/soc/qcom/qcom_tcsr.c +@@ -0,0 +1,64 @@ ++/* ++ * Copyright (c) 2014, The Linux foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License rev 2 and ++ * only rev 2 as published by the free Software foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or fITNESS fOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/err.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_platform.h> ++#include <linux/platform_device.h> ++ ++#define TCSR_USB_PORT_SEL 0xb0 ++ ++static int tcsr_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ const struct device_node *node = pdev->dev.of_node; ++ void __iomem *base; ++ u32 val; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ if (!of_property_read_u32(node, "qcom,usb-ctrl-select", &val)) { ++ dev_err(&pdev->dev, "setting usb port select = %d\n", val); ++ writel(val, base + TCSR_USB_PORT_SEL); ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id tcsr_dt_match[] = { ++ { .compatible = "qcom,tcsr", }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(of, tcsr_dt_match); ++ ++static struct platform_driver tcsr_driver = { ++ .driver = { ++ .name = "tcsr", ++ .owner = THIS_MODULE, ++ .of_match_table = tcsr_dt_match, ++ }, ++ .probe = tcsr_probe, ++}; ++ ++module_platform_driver(tcsr_driver); ++ ++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); ++MODULE_DESCRIPTION("QCOM TCSR driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/dt-bindings/soc/qcom,tcsr.h b/include/dt-bindings/soc/qcom,tcsr.h +new file mode 100644 +index 0000000..c9d497a +--- /dev/null ++++ b/include/dt-bindings/soc/qcom,tcsr.h +@@ -0,0 +1,19 @@ ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#ifndef __DT_BINDINGS_QCOM_TCSR_H ++#define __DT_BINDINGS_QCOM_TCSR_H ++ ++#define TCSR_USB_SELECT_USB3_P0 0x1 ++#define TCSR_USB_SELECT_USB3_P1 0x2 ++#define TCSR_USB_SELECT_USB3_DUAL 0x3 ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0154-clk-qcom-Correct-UTMI-clock-frequency-table.patch b/target/linux/ipq806x/patches/0154-clk-qcom-Correct-UTMI-clock-frequency-table.patch new file mode 100644 index 0000000000..28951ab18b --- /dev/null +++ b/target/linux/ipq806x/patches/0154-clk-qcom-Correct-UTMI-clock-frequency-table.patch @@ -0,0 +1,29 @@ +From 2561c49dcf5cb35ad72df34bf225a51fe2fa87e5 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 12 Jun 2014 00:53:19 -0500 +Subject: [PATCH 154/182] clk: qcom: Correct UTMI clock frequency table + +This patch changes the UTMI clock frequency from 20MHz to the correct value of +60MHz. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + drivers/clk/qcom/gcc-ipq806x.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c +index f7916be..d80dc69 100644 +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -1992,7 +1992,7 @@ static struct clk_branch usb30_1_branch_clk = { + }; + + static const struct freq_tbl clk_tbl_usb30_utmi[] = { +- { 60000000, P_PLL0, 1, 1, 40 }, ++ { 60000000, P_PLL8, 1, 5, 32 }, + { } + }; + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0155-clk-qcom-Fix-incorrect-UTMI-DT-include-values.patch b/target/linux/ipq806x/patches/0155-clk-qcom-Fix-incorrect-UTMI-DT-include-values.patch new file mode 100644 index 0000000000..7bfbbcb8f1 --- /dev/null +++ b/target/linux/ipq806x/patches/0155-clk-qcom-Fix-incorrect-UTMI-DT-include-values.patch @@ -0,0 +1,63 @@ +From 9ab5cb48696dca02bf43170b50d1034a96fb9e85 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Sun, 15 Jun 2014 00:39:57 -0500 +Subject: [PATCH 155/182] clk: qcom: Fix incorrect UTMI DT include values + +Corrected values for UTMI clock definitions. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + include/dt-bindings/clock/qcom,gcc-ipq806x.h | 38 +++++++++++++------------- + 1 file changed, 19 insertions(+), 19 deletions(-) + +diff --git a/include/dt-bindings/clock/qcom,gcc-ipq806x.h b/include/dt-bindings/clock/qcom,gcc-ipq806x.h +index 0fd3e8a..163ba85 100644 +--- a/include/dt-bindings/clock/qcom,gcc-ipq806x.h ++++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h +@@ -273,24 +273,24 @@ + #define USB30_SLEEP_CLK 262 + #define USB30_UTMI_SRC 263 + #define USB30_0_UTMI_CLK 264 +-#define USB30_1_UTMI_CLK 264 +-#define USB30_MASTER_SRC 265 +-#define USB30_0_MASTER_CLK 266 +-#define USB30_1_MASTER_CLK 267 +-#define GMAC_CORE1_CLK_SRC 268 +-#define GMAC_CORE2_CLK_SRC 269 +-#define GMAC_CORE3_CLK_SRC 270 +-#define GMAC_CORE4_CLK_SRC 271 +-#define GMAC_CORE1_CLK 272 +-#define GMAC_CORE2_CLK 273 +-#define GMAC_CORE3_CLK 274 +-#define GMAC_CORE4_CLK 275 +-#define UBI32_CORE1_CLK_SRC 276 +-#define UBI32_CORE2_CLK_SRC 277 +-#define UBI32_CORE1_CLK 278 +-#define UBI32_CORE2_CLK 279 +-#define NSSTCM_CLK_SRC 280 +-#define NSSTCM_CLK 281 +-#define NSS_CORE_CLK 282 /* Virtual */ ++#define USB30_1_UTMI_CLK 265 ++#define USB30_MASTER_SRC 266 ++#define USB30_0_MASTER_CLK 267 ++#define USB30_1_MASTER_CLK 268 ++#define GMAC_CORE1_CLK_SRC 269 ++#define GMAC_CORE2_CLK_SRC 270 ++#define GMAC_CORE3_CLK_SRC 271 ++#define GMAC_CORE4_CLK_SRC 272 ++#define GMAC_CORE1_CLK 273 ++#define GMAC_CORE2_CLK 274 ++#define GMAC_CORE3_CLK 275 ++#define GMAC_CORE4_CLK 276 ++#define UBI32_CORE1_CLK_SRC 277 ++#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 ++#define NSS_CORE_CLK 283 /* Virtual */ + + #endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0156-usb-dwc3-Add-Qualcomm-DWC3-glue-layer-driver.patch b/target/linux/ipq806x/patches/0156-usb-dwc3-Add-Qualcomm-DWC3-glue-layer-driver.patch new file mode 100644 index 0000000000..f8ad3cdbcb --- /dev/null +++ b/target/linux/ipq806x/patches/0156-usb-dwc3-Add-Qualcomm-DWC3-glue-layer-driver.patch @@ -0,0 +1,212 @@ +From 364532aeb9024d0ff7b88121f9a953f559b1c136 Mon Sep 17 00:00:00 2001 +From: "Ivan T. Ivanov" <iivanov@mm-sol.com> +Date: Mon, 7 Oct 2013 10:44:57 +0300 +Subject: [PATCH 156/182] usb: dwc3: Add Qualcomm DWC3 glue layer driver + +DWC3 glue layer is hardware layer around Synopsys DesignWare +USB3 core. Its purpose is to supply Synopsys IP with required +clocks, voltages and interface it with the rest of the SoC. + +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +--- + drivers/usb/dwc3/Kconfig | 8 +++ + drivers/usb/dwc3/Makefile | 1 + + drivers/usb/dwc3/dwc3-qcom.c | 156 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 165 insertions(+) + create mode 100644 drivers/usb/dwc3/dwc3-qcom.c + +diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig +index e2c730f..2d01983 100644 +--- a/drivers/usb/dwc3/Kconfig ++++ b/drivers/usb/dwc3/Kconfig +@@ -59,6 +59,14 @@ config USB_DWC3_EXYNOS + Recent Exynos5 SoCs ship with one DesignWare Core USB3 IP inside, + say 'Y' or 'M' if you have one such device. + ++config USB_DWC3_QCOM ++ tristate "Qualcomm Platforms" ++ default USB_DWC3 ++ select USB_QCOM_DWC3_PHYS ++ help ++ Recent Qualcomm SoCs ship with one DesignWare Core USB3 IP inside, ++ say 'Y' or 'M' if you have one such device. ++ + config USB_DWC3_PCI + tristate "PCIe-based Platforms" + depends on PCI +diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile +index 10ac3e7..4066e4e 100644 +--- a/drivers/usb/dwc3/Makefile ++++ b/drivers/usb/dwc3/Makefile +@@ -31,5 +31,6 @@ endif + + obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o + obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o ++obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o + obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o + obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o +diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c +new file mode 100644 +index 0000000..8d17360 +--- /dev/null ++++ b/drivers/usb/dwc3/dwc3-qcom.c +@@ -0,0 +1,156 @@ ++/* 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/clk.h> ++#include <linux/err.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_platform.h> ++#include <linux/platform_device.h> ++#include <linux/regulator/consumer.h> ++#include <linux/usb/phy.h> ++ ++#include "core.h" ++ ++ ++struct dwc3_qcom { ++ struct device *dev; ++ ++ struct clk *core_clk; ++ struct clk *iface_clk; ++ struct clk *sleep_clk; ++ ++ struct regulator *gdsc; ++}; ++ ++static int dwc3_qcom_probe(struct platform_device *pdev) ++{ ++ struct device_node *node = pdev->dev.of_node; ++ struct dwc3_qcom *mdwc; ++ int ret = 0; ++ ++ mdwc = devm_kzalloc(&pdev->dev, sizeof(*mdwc), GFP_KERNEL); ++ if (!mdwc) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, mdwc); ++ ++ mdwc->dev = &pdev->dev; ++ ++ mdwc->gdsc = devm_regulator_get(mdwc->dev, "gdsc"); ++ ++ mdwc->core_clk = devm_clk_get(mdwc->dev, "core"); ++ if (IS_ERR(mdwc->core_clk)) { ++ dev_dbg(mdwc->dev, "failed to get core clock\n"); ++ return PTR_ERR(mdwc->core_clk); ++ } ++ ++ mdwc->iface_clk = devm_clk_get(mdwc->dev, "iface"); ++ if (IS_ERR(mdwc->iface_clk)) { ++ dev_dbg(mdwc->dev, "failed to get iface clock, skipping\n"); ++ mdwc->iface_clk = NULL; ++ } ++ ++ mdwc->sleep_clk = devm_clk_get(mdwc->dev, "sleep"); ++ if (IS_ERR(mdwc->sleep_clk)) { ++ dev_dbg(mdwc->dev, "failed to get sleep clock, skipping\n"); ++ mdwc->sleep_clk = NULL; ++ } ++ ++ if (!IS_ERR(mdwc->gdsc)) { ++ ret = regulator_enable(mdwc->gdsc); ++ if (ret) ++ dev_err(mdwc->dev, "cannot enable gdsc\n"); ++ } ++ ++ clk_prepare_enable(mdwc->core_clk); ++ ++ if (mdwc->iface_clk) ++ clk_prepare_enable(mdwc->iface_clk); ++ ++ if (mdwc->sleep_clk) ++ clk_prepare_enable(mdwc->sleep_clk); ++ ++ ret = of_platform_populate(node, NULL, NULL, mdwc->dev); ++ if (ret) { ++ dev_err(mdwc->dev, "failed to register core - %d\n", ret); ++ dev_dbg(mdwc->dev, "failed to add create dwc3 core\n"); ++ goto dis_clks; ++ } ++ ++ return 0; ++ ++dis_clks: ++ ++ dev_err(mdwc->dev, "disabling clocks\n"); ++ ++ if (mdwc->sleep_clk) ++ clk_disable_unprepare(mdwc->sleep_clk); ++ ++ if (mdwc->iface_clk) ++ clk_disable_unprepare(mdwc->iface_clk); ++ ++ clk_disable_unprepare(mdwc->core_clk); ++ ++ if (!IS_ERR(mdwc->gdsc)) { ++ ret = regulator_disable(mdwc->gdsc); ++ if (ret) ++ dev_dbg(mdwc->dev, "cannot disable gdsc\n"); ++ } ++ ++ return ret; ++} ++ ++static int dwc3_qcom_remove(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ struct dwc3_qcom *mdwc = platform_get_drvdata(pdev); ++ ++ if (mdwc->sleep_clk) ++ clk_disable_unprepare(mdwc->sleep_clk); ++ ++ if (mdwc->iface_clk) ++ clk_disable_unprepare(mdwc->iface_clk); ++ ++ clk_disable_unprepare(mdwc->core_clk); ++ ++ if (!IS_ERR(mdwc->gdsc)) { ++ ret = regulator_disable(mdwc->gdsc); ++ if (ret) ++ dev_dbg(mdwc->dev, "cannot disable gdsc\n"); ++ } ++ return ret; ++} ++ ++static const struct of_device_id of_dwc3_match[] = { ++ { .compatible = "qcom,dwc3" }, ++ { /* Sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, of_dwc3_match); ++ ++static struct platform_driver dwc3_qcom_driver = { ++ .probe = dwc3_qcom_probe, ++ .remove = dwc3_qcom_remove, ++ .driver = { ++ .name = "qcom-dwc3", ++ .owner = THIS_MODULE, ++ .of_match_table = of_dwc3_match, ++ }, ++}; ++ ++module_platform_driver(dwc3_qcom_driver); ++ ++MODULE_ALIAS("platform:qcom-dwc3"); ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("DesignWare USB3 QCOM Glue Layer"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0157-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches/0157-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch new file mode 100644 index 0000000000..332a32c910 --- /dev/null +++ b/target/linux/ipq806x/patches/0157-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch @@ -0,0 +1,874 @@ +From d968f8c82db73141c6bc145148642391cb698442 Mon Sep 17 00:00:00 2001 +From: "Ivan T. Ivanov" <iivanov@mm-sol.com> +Date: Mon, 7 Oct 2013 10:44:56 +0300 +Subject: [PATCH 157/182] usb: phy: Add Qualcomm DWC3 HS/SS PHY drivers + +These drivers handles control and configuration of the HS +and SS USB PHY transceivers. They are part of the driver +which manage Synopsys DesignWare USB3 controller stack +inside Qualcomm SoC's. + +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +--- + drivers/usb/phy/Kconfig | 13 +- + drivers/usb/phy/Makefile | 2 + + drivers/usb/phy/phy-qcom-hsusb.c | 340 ++++++++++++++++++++++++++++ + drivers/usb/phy/phy-qcom-ssusb.c | 455 ++++++++++++++++++++++++++++++++++++++ + 4 files changed, 809 insertions(+), 1 deletion(-) + create mode 100644 drivers/usb/phy/phy-qcom-hsusb.c + create mode 100644 drivers/usb/phy/phy-qcom-ssusb.c + +diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig +index 7d1451d..ddb65be 100644 +--- a/drivers/usb/phy/Kconfig ++++ b/drivers/usb/phy/Kconfig +@@ -193,7 +193,7 @@ config USB_ISP1301 + + config USB_MSM_OTG + tristate "OTG support for Qualcomm on-chip USB controller" +- depends on (USB || USB_GADGET) && ARCH_MSM ++ depends on (USB || USB_GADGET) && ARCH_QCOM + select USB_PHY + help + Enable this to support the USB OTG transceiver on MSM chips. It +@@ -251,6 +251,17 @@ config USB_RCAR_GEN2_PHY + To compile this driver as a module, choose M here: the + module will be called phy-rcar-gen2-usb. + ++config USB_QCOM_DWC3_PHY ++ tristate "Qualcomm USB controller DWC3 PHY wrappers support" ++ depends on (USB || USB_GADGET) && ARCH_QCOM ++ select USB_PHY ++ help ++ Enable this to support the DWC3 USB PHY transceivers on QCOM chips ++ with DWC3 USB core. It handles PHY initialization, clock ++ management required after resetting the hardware and power ++ management. This driver is required even for peripheral only or ++ host only mode configurations. ++ + config USB_ULPI + bool "Generic ULPI Transceiver Driver" + depends on ARM +diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile +index be58ada..857f04e 100644 +--- a/drivers/usb/phy/Makefile ++++ b/drivers/usb/phy/Makefile +@@ -26,6 +26,8 @@ obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o + obj-$(CONFIG_USB_GPIO_VBUS) += phy-gpio-vbus-usb.o + obj-$(CONFIG_USB_ISP1301) += phy-isp1301.o + obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o ++obj-$(CONFIG_USB_QCOM_DWC3_PHY) += phy-qcom-hsusb.o ++obj-$(CONFIG_USB_QCOM_DWC3_PHY) += phy-qcom-ssusb.o + obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o + obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o + obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o +diff --git a/drivers/usb/phy/phy-qcom-hsusb.c b/drivers/usb/phy/phy-qcom-hsusb.c +new file mode 100644 +index 0000000..f96b2b9 +--- /dev/null ++++ b/drivers/usb/phy/phy-qcom-hsusb.c +@@ -0,0 +1,340 @@ ++/* 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/platform_device.h> ++#include <linux/regulator/consumer.h> ++#include <linux/usb/phy.h> ++ ++/** ++ * USB QSCRATCH Hardware registers ++ */ ++#define QSCRATCH_CTRL_REG (0x04) ++#define QSCRATCH_GENERAL_CFG (0x08) ++#define PHY_CTRL_REG (0x10) ++#define PARAMETER_OVERRIDE_X_REG (0x14) ++#define CHARGING_DET_CTRL_REG (0x18) ++#define CHARGING_DET_OUTPUT_REG (0x1c) ++#define ALT_INTERRUPT_EN_REG (0x20) ++#define PHY_IRQ_STAT_REG (0x24) ++#define CGCTL_REG (0x28) ++ ++#define PHY_3P3_VOL_MIN 3050000 /* uV */ ++#define PHY_3P3_VOL_MAX 3300000 /* uV */ ++#define PHY_3P3_HPM_LOAD 16000 /* uA */ ++ ++#define PHY_1P8_VOL_MIN 1800000 /* uV */ ++#define PHY_1P8_VOL_MAX 1800000 /* uV */ ++#define PHY_1P8_HPM_LOAD 19000 /* uA */ ++ ++/* TODO: these are suspicious */ ++#define USB_VDDCX_NO 1 /* index */ ++#define USB_VDDCX_MIN 5 /* index */ ++#define USB_VDDCX_MAX 7 /* index */ ++ ++struct qcom_dwc3_hs_phy { ++ struct usb_phy phy; ++ void __iomem *base; ++ struct device *dev; ++ ++ struct clk *xo_clk; ++ struct clk *utmi_clk; ++ ++ struct regulator *v3p3; ++ struct regulator *v1p8; ++ struct regulator *vddcx; ++ struct regulator *vbus; ++}; ++ ++#define phy_to_dw_phy(x) container_of((x), struct qcom_dwc3_hs_phy, phy) ++ ++ ++/** ++ * Write register. ++ * ++ * @base - QCOM DWC3 PHY base virtual address. ++ * @offset - register offset. ++ * @val - value to write. ++ */ ++static inline void qcom_dwc3_hs_write(void __iomem *base, u32 offset, u32 val) ++{ ++ writel(val, base + offset); ++} ++ ++/** ++ * 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_hs_write_readback(void __iomem *base, u32 offset, ++ const u32 mask, u32 val) ++{ ++ u32 write_val, tmp = readl(base + offset); ++ ++ tmp &= ~mask; /* retain other bits */ ++ write_val = tmp | val; ++ ++ writel(write_val, base + offset); ++ ++ /* Read back to see if val was written */ ++ tmp = readl(base + offset); ++ tmp &= mask; /* clear other bits */ ++ ++ if (tmp != val) ++ pr_err("write: %x to QSCRATCH: %x FAILED\n", val, offset); ++} ++ ++static int qcom_dwc3_hs_notify_connect(struct usb_phy *x, ++ enum usb_device_speed speed) ++{ ++ struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x); ++ ++ dev_err(phy->dev, "notify connect\n"); ++ return 0; ++} ++ ++static int qcom_dwc3_hs_notify_disconnect(struct usb_phy *x, ++ enum usb_device_speed speed) ++{ ++ struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x); ++ ++ dev_err(phy->dev, "notify disconnect\n"); ++ return 0; ++} ++ ++ ++static void qcom_dwc3_hs_phy_shutdown(struct usb_phy *x) ++{ ++ struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x); ++ int ret; ++ ++ ret = regulator_set_voltage(phy->v3p3, 0, PHY_3P3_VOL_MAX); ++ if (ret) ++ dev_err(phy->dev, "cannot set voltage for v3p3\n"); ++ ++ ret = regulator_set_voltage(phy->v1p8, 0, PHY_1P8_VOL_MAX); ++ if (ret) ++ dev_err(phy->dev, "cannot set voltage for v1p8\n"); ++ ++ ret = regulator_disable(phy->v1p8); ++ if (ret) ++ dev_err(phy->dev, "cannot disable v1p8\n"); ++ ++ ret = regulator_disable(phy->v3p3); ++ if (ret) ++ dev_err(phy->dev, "cannot disable v3p3\n"); ++ ++ ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_NO, USB_VDDCX_MAX); ++ if (ret) ++ dev_err(phy->dev, "cannot set voltage for vddcx\n"); ++ ++ ret = regulator_disable(phy->vddcx); ++ if (ret) ++ dev_err(phy->dev, "cannot enable vddcx\n"); ++ ++ clk_disable_unprepare(phy->utmi_clk); ++} ++ ++static int qcom_dwc3_hs_phy_init(struct usb_phy *x) ++{ ++ struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x); ++ int ret; ++ u32 val; ++ ++ clk_prepare_enable(phy->utmi_clk); ++ ++ ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_MIN, USB_VDDCX_MAX); ++ if (ret) ++ dev_err(phy->dev, "cannot set voltage for vddcx\n"); ++ ++ ret = regulator_enable(phy->vddcx); ++ if (ret) ++ dev_err(phy->dev, "cannot enable vddcx\n"); ++ ++ ret = regulator_set_voltage(phy->v3p3, PHY_3P3_VOL_MIN, ++ PHY_3P3_VOL_MAX); ++ if (ret) ++ dev_err(phy->dev, "cannot set voltage for v3p3\n"); ++ ++ ret = regulator_set_voltage(phy->v1p8, PHY_1P8_VOL_MIN, ++ PHY_1P8_VOL_MAX); ++ if (ret) ++ dev_err(phy->dev, "cannot set voltage for v1p8\n"); ++ ++ ret = regulator_set_optimum_mode(phy->v1p8, PHY_1P8_HPM_LOAD); ++ if (ret < 0) ++ dev_err(phy->dev, "cannot set HPM of regulator v1p8\n"); ++ ++ ret = regulator_enable(phy->v1p8); ++ if (ret) ++ dev_err(phy->dev, "cannot enable v1p8\n"); ++ ++ ret = regulator_set_optimum_mode(phy->v3p3, PHY_3P3_HPM_LOAD); ++ if (ret < 0) ++ dev_err(phy->dev, "cannot set HPM of regulator v3p3\n"); ++ ++ ret = regulator_enable(phy->v3p3); ++ if (ret) ++ dev_err(phy->dev, "cannot enable v3p3\n"); ++ ++ /* ++ * HSPHY Initialization: Enable UTMI clock and clamp enable HVINTs, ++ * and disable RETENTION (power-on default is ENABLED) ++ */ ++ val = readl(phy->base + PHY_CTRL_REG); ++ val |= BIT(18) | BIT(20) | BIT(11) | 0x70; ++ qcom_dwc3_hs_write(phy->base, PHY_CTRL_REG, val); ++ usleep_range(2000, 2200); ++ ++ /* ++ * write HSPHY init value to QSCRATCH reg to set HSPHY parameters like ++ * VBUS valid threshold, disconnect valid threshold, DC voltage level, ++ * preempasis and rise/fall time. ++ */ ++ qcom_dwc3_hs_write_readback(phy->base, PARAMETER_OVERRIDE_X_REG, ++ 0x03ffffff, 0x00d191a4); ++ ++ /* Disable (bypass) VBUS and ID filters */ ++ qcom_dwc3_hs_write(phy->base, QSCRATCH_GENERAL_CFG, BIT(2)); ++ ++ return 0; ++} ++ ++static int qcom_dwc3_hs_phy_set_vbus(struct usb_phy *x, int on) ++{ ++ struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x); ++ int ret = 0; ++ ++ if (IS_ERR(phy->vbus)) ++ return on ? PTR_ERR(phy->vbus) : 0; ++ ++ if (on) ++ ret = regulator_enable(phy->vbus); ++ else ++ ret = regulator_disable(phy->vbus); ++ ++ if (ret) ++ dev_err(x->dev, "Cannot %s Vbus\n", on ? "set" : "off"); ++ return ret; ++} ++ ++static int qcom_dwc3_hs_probe(struct platform_device *pdev) ++{ ++ struct qcom_dwc3_hs_phy *phy; ++ struct resource *res; ++ void __iomem *base; ++ ++ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, phy); ++ ++ phy->dev = &pdev->dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(phy->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ phy->vddcx = devm_regulator_get(phy->dev, "vddcx"); ++ if (IS_ERR(phy->vddcx)) { ++ dev_dbg(phy->dev, "cannot get vddcx\n"); ++ return PTR_ERR(phy->vddcx); ++ } ++ ++ phy->v3p3 = devm_regulator_get(phy->dev, "v3p3"); ++ if (IS_ERR(phy->v3p3)) { ++ dev_dbg(phy->dev, "cannot get v3p3\n"); ++ return PTR_ERR(phy->v3p3); ++ } ++ ++ phy->v1p8 = devm_regulator_get(phy->dev, "v1p8"); ++ if (IS_ERR(phy->v1p8)) { ++ dev_dbg(phy->dev, "cannot get v1p8\n"); ++ return PTR_ERR(phy->v1p8); ++ } ++ ++ phy->vbus = devm_regulator_get(phy->dev, "vbus"); ++ if (IS_ERR(phy->vbus)) ++ dev_dbg(phy->dev, "Failed to get vbus\n"); ++ ++ phy->utmi_clk = devm_clk_get(phy->dev, "utmi"); ++ if (IS_ERR(phy->utmi_clk)) { ++ dev_dbg(phy->dev, "cannot get UTMI handle\n"); ++ return PTR_ERR(phy->utmi_clk); ++ } ++ ++ phy->xo_clk = devm_clk_get(phy->dev, "xo"); ++ if (IS_ERR(phy->xo_clk)) { ++ dev_dbg(phy->dev, "cannot get TCXO buffer handle\n"); ++ phy->xo_clk = NULL; ++ } ++ ++ clk_set_rate(phy->utmi_clk, 60000000); ++ ++ if (phy->xo_clk) ++ clk_prepare_enable(phy->xo_clk); ++ ++ phy->base = base; ++ phy->phy.dev = phy->dev; ++ phy->phy.label = "qcom-dwc3-hsphy"; ++ phy->phy.init = qcom_dwc3_hs_phy_init; ++ phy->phy.notify_connect = qcom_dwc3_hs_notify_connect; ++ phy->phy.notify_disconnect = qcom_dwc3_hs_notify_disconnect; ++ phy->phy.shutdown = qcom_dwc3_hs_phy_shutdown; ++ phy->phy.set_vbus = qcom_dwc3_hs_phy_set_vbus; ++ phy->phy.type = USB_PHY_TYPE_USB2; ++ phy->phy.state = OTG_STATE_UNDEFINED; ++ ++ usb_add_phy_dev(&phy->phy); ++ return 0; ++} ++ ++static int qcom_dwc3_hs_remove(struct platform_device *pdev) ++{ ++ struct qcom_dwc3_hs_phy *phy = platform_get_drvdata(pdev); ++ ++ if (phy->xo_clk) ++ clk_disable_unprepare(phy->xo_clk); ++ usb_remove_phy(&phy->phy); ++ return 0; ++} ++ ++static const struct of_device_id qcom_dwc3_hs_id_table[] = { ++ { .compatible = "qcom,dwc3-hsphy" }, ++ { /* Sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, qcom_dwc3_hs_id_table); ++ ++static struct platform_driver qcom_dwc3_hs_driver = { ++ .probe = qcom_dwc3_hs_probe, ++ .remove = qcom_dwc3_hs_remove, ++ .driver = { ++ .name = "qcom-dwc3-hsphy", ++ .owner = THIS_MODULE, ++ .of_match_table = qcom_dwc3_hs_id_table, ++ }, ++}; ++ ++module_platform_driver(qcom_dwc3_hs_driver); ++ ++MODULE_ALIAS("platform:qcom-dwc3-hsphy"); ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("DesignWare USB3 QCOM HSPHY driver"); +diff --git a/drivers/usb/phy/phy-qcom-ssusb.c b/drivers/usb/phy/phy-qcom-ssusb.c +new file mode 100644 +index 0000000..3da778f +--- /dev/null ++++ b/drivers/usb/phy/phy-qcom-ssusb.c +@@ -0,0 +1,455 @@ ++/* 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/platform_device.h> ++#include <linux/regulator/consumer.h> ++#include <linux/usb/phy.h> ++ ++/** ++ * USB QSCRATCH Hardware registers ++ */ ++#define PHY_CTRL_REG (0x00) ++#define PHY_PARAM_CTRL_1 (0x04) ++#define 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) ++ ++#define PHY_1P8_VOL_MIN 1800000 /* uV */ ++#define PHY_1P8_VOL_MAX 1800000 /* uV */ ++#define PHY_1P8_HPM_LOAD 23000 /* uA */ ++ ++/* TODO: these are suspicious */ ++#define USB_VDDCX_NO 1 /* index */ ++#define USB_VDDCX_MIN 5 /* index */ ++#define USB_VDDCX_MAX 7 /* index */ ++ ++struct qcom_dwc3_ss_phy { ++ struct usb_phy phy; ++ void __iomem *base; ++ struct device *dev; ++ ++ struct regulator *v1p8; ++ struct regulator *vddcx; ++ ++ struct clk *xo_clk; ++ struct clk *ref_clk; ++}; ++ ++#define phy_to_dw_phy(x) container_of((x), struct qcom_dwc3_ss_phy, phy) ++ ++ ++/** ++ * Write register ++ * ++ * @base - QCOM DWC3 PHY base virtual address. ++ * @offset - register offset. ++ * @val - value to write. ++ */ ++static inline void qcom_dwc3_ss_write(void __iomem *base, u32 offset, u32 val) ++{ ++ writel(val, base + offset); ++} ++ ++/** ++ * 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_ss_write_readback(void __iomem *base, u32 offset, ++ const u32 mask, u32 val) ++{ ++ u32 write_val, tmp = readl(base + offset); ++ ++ tmp &= ~mask; /* retain other bits */ ++ write_val = tmp | val; ++ ++ writel(write_val, base + offset); ++ ++ /* Read back to see if val was written */ ++ tmp = readl(base + offset); ++ tmp &= mask; /* clear other bits */ ++ ++ if (tmp != val) ++ pr_err("write: %x to QSCRATCH: %x FAILED\n", val, offset); ++} ++ ++/** ++ * Write SSPHY register ++ * ++ * @base - QCOM DWC3 PHY base virtual address. ++ * @addr - SSPHY address to write. ++ * @val - value to write. ++ */ ++static void qcom_dwc3_ss_write_phycreg(void __iomem *base, u32 addr, u32 val) ++{ ++ writel(addr, base + CR_PROTOCOL_DATA_IN_REG); ++ writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG); ++ while (readl(base + CR_PROTOCOL_CAP_ADDR_REG)) ++ cpu_relax(); ++ ++ writel(val, base + CR_PROTOCOL_DATA_IN_REG); ++ writel(0x1, base + CR_PROTOCOL_CAP_DATA_REG); ++ while (readl(base + CR_PROTOCOL_CAP_DATA_REG)) ++ cpu_relax(); ++ ++ writel(0x1, base + CR_PROTOCOL_WRITE_REG); ++ while (readl(base + CR_PROTOCOL_WRITE_REG)) ++ cpu_relax(); ++} ++ ++/** ++ * Read SSPHY register. ++ * ++ * @base - QCOM DWC3 PHY base virtual address. ++ * @addr - SSPHY address to read. ++ */ ++static u32 qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr) ++{ ++ bool first_read = true; ++ ++ writel(addr, base + CR_PROTOCOL_DATA_IN_REG); ++ writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG); ++ while (readl(base + CR_PROTOCOL_CAP_ADDR_REG)) ++ cpu_relax(); ++ ++ /* ++ * 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); ++ while (readl(base + CR_PROTOCOL_READ_REG)) ++ cpu_relax(); ++ ++ if (first_read) { ++ readl(base + CR_PROTOCOL_DATA_OUT_REG); ++ first_read = false; ++ goto retry; ++ } ++ ++ return readl(base + CR_PROTOCOL_DATA_OUT_REG); ++} ++ ++static void qcom_dwc3_ss_phy_shutdown(struct usb_phy *x) ++{ ++ struct qcom_dwc3_ss_phy *phy = phy_to_dw_phy(x); ++ int ret; ++ ++ /* 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_ss_write_readback(phy->base, PHY_CTRL_REG, (1 << 8), 0x0); ++ qcom_dwc3_ss_write_readback(phy->base, PHY_CTRL_REG, (1 << 28), 0x0); ++ qcom_dwc3_ss_write_readback(phy->base, PHY_CTRL_REG, ++ (1 << 26), (1 << 26)); ++ ++ usleep_range(1000, 1200); ++ clk_disable_unprepare(phy->ref_clk); ++ ++ ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_NO, USB_VDDCX_MAX); ++ if (ret) ++ dev_err(phy->dev, "cannot set voltage for vddcx\n"); ++ ++ regulator_disable(phy->vddcx); ++ ++ ret = regulator_set_voltage(phy->v1p8, 0, PHY_1P8_VOL_MAX); ++ if (ret) ++ dev_err(phy->dev, "cannot set v1p8\n"); ++ ++ regulator_disable(phy->v1p8); ++} ++ ++static int qcom_dwc3_ss_phy_init(struct usb_phy *x) ++{ ++ struct qcom_dwc3_ss_phy *phy = phy_to_dw_phy(x); ++ u32 data = 0; ++ int ret; ++ ++ ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_MIN, USB_VDDCX_MAX); ++ if (ret) { ++ dev_err(phy->dev, "cannot set voltage for vddcx\n"); ++ return ret; ++ } ++ ++ ret = regulator_enable(phy->vddcx); ++ if (ret) { ++ dev_err(phy->dev, "cannot enable vddcx\n"); ++ return ret; ++ } ++ ++ ret = regulator_set_voltage(phy->v1p8, PHY_1P8_VOL_MIN, ++ PHY_1P8_VOL_MAX); ++ if (ret) { ++ regulator_disable(phy->vddcx); ++ dev_err(phy->dev, "cannot set v1p8\n"); ++ return ret; ++ } ++ ++ ret = regulator_enable(phy->v1p8); ++ if (ret) { ++ regulator_disable(phy->vddcx); ++ dev_err(phy->dev, "cannot enable v1p8\n"); ++ return ret; ++ } ++ ++ clk_prepare_enable(phy->ref_clk); ++ usleep_range(1000, 1200); ++ ++ /* reset phy */ ++ data = readl_relaxed(phy->base + PHY_CTRL_REG); ++ writel_relaxed(data | BIT(7), phy->base + PHY_CTRL_REG); ++ usleep_range(2000, 2200); ++ writel_relaxed(data, phy->base + PHY_CTRL_REG); ++ ++ /* clear REF_PAD, we don't have XO clk */ ++ data &= ~BIT(28); ++ writel_relaxed(data, phy->base + PHY_CTRL_REG); ++ msleep(30); ++ ++ data |= BIT(8) | BIT(24); ++ writel_relaxed(data, phy->base + PHY_CTRL_REG); ++ ++ /* ++ * WORKAROUND: There is SSPHY suspend bug due to which USB enumerates ++ * in HS mode instead of SS mode. Workaround it by asserting ++ * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus mode ++ */ ++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x102d); ++ data |= (1 << 7); ++ qcom_dwc3_ss_write_phycreg(phy->base, 0x102D, data); ++ ++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1010); ++ data &= ~0xff0; ++ data |= 0x20; ++ qcom_dwc3_ss_write_phycreg(phy->base, 0x1010, data); ++ ++ /* ++ * 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 ++ */ ++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1006); ++ data &= ~(1 << 6); ++ data |= (1 << 7); ++ data &= ~(0x7 << 8); ++ data |= (0x3 << 8); ++ data |= (0x1 << 11); ++ qcom_dwc3_ss_write_phycreg(phy->base, 0x1006, data); ++ ++ /* ++ * 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. ++ */ ++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1002); ++ data &= ~0x3f80; ++ data |= (0x16 << 7); ++ data &= ~0x7f; ++ data |= (0x7f | (1 << 14)); ++ qcom_dwc3_ss_write_phycreg(phy->base, 0x1002, data); ++ ++ /* ++ * 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_ss_write_readback(phy->base, PHY_PARAM_CTRL_1, ++ 0x07f03f07, 0x07f01605); ++ return 0; ++} ++ ++static int qcom_dwc3_ss_set_suspend(struct usb_phy *x, int suspend) ++{ ++ struct qcom_dwc3_ss_phy *phy = phy_to_dw_phy(x); ++ u32 data; ++ ++ if (!suspend) { ++ /* reset phy */ ++ data = readl_relaxed(phy->base + PHY_CTRL_REG); ++ writel_relaxed(data | BIT(7), phy->base + PHY_CTRL_REG); ++ usleep_range(2000, 2200); ++ writel_relaxed(data, phy->base + PHY_CTRL_REG); ++ ++ /* clear REF_PAD, we don't have XO clk */ ++ data &= ~BIT(28); ++ writel_relaxed(data, phy->base + PHY_CTRL_REG); ++ msleep(30); ++ ++ data |= BIT(8) | BIT(24); ++ writel_relaxed(data, phy->base + PHY_CTRL_REG); ++ ++ /* ++ * WORKAROUND: There is SSPHY suspend bug due to which USB ++ * enumerates in HS mode instead of SS mode. Workaround it by ++ * asserting LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use ++ * alt bus mode ++ */ ++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x102d); ++ data |= (1 << 7); ++ qcom_dwc3_ss_write_phycreg(phy->base, 0x102D, data); ++ ++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1010); ++ data &= ~0xff0; ++ data |= 0x20; ++ qcom_dwc3_ss_write_phycreg(phy->base, 0x1010, data); ++ ++ /* ++ * 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 ++ */ ++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1006); ++ data &= ~(1 << 6); ++ data |= (1 << 7); ++ data &= ~(0x7 << 8); ++ data |= (0x3 << 8); ++ data |= (0x1 << 11); ++ qcom_dwc3_ss_write_phycreg(phy->base, 0x1006, data); ++ ++ /* ++ * 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. ++ */ ++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1002); ++ data &= ~0x3f80; ++ data |= (0x16 << 7); ++ data &= ~0x7f; ++ data |= (0x7f | (1 << 14)); ++ qcom_dwc3_ss_write_phycreg(phy->base, 0x1002, data); ++ ++ /* ++ * 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_ss_write_readback(phy->base, PHY_PARAM_CTRL_1, ++ 0x07f03f07, 0x07f01605); ++ } ++ return 0; ++} ++ ++static int qcom_dwc3_ss_probe(struct platform_device *pdev) ++{ ++ struct qcom_dwc3_ss_phy *phy; ++ struct resource *res; ++ void __iomem *base; ++ int ret; ++ ++ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, phy); ++ ++ phy->dev = &pdev->dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(phy->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ phy->vddcx = devm_regulator_get(phy->dev, "vddcx"); ++ if (IS_ERR(phy->vddcx)) { ++ dev_dbg(phy->dev, "cannot get vddcx\n"); ++ return PTR_ERR(phy->vddcx); ++ } ++ ++ phy->v1p8 = devm_regulator_get(phy->dev, "v1p8"); ++ if (IS_ERR(phy->v1p8)) { ++ dev_dbg(phy->dev, "cannot get v1p8\n"); ++ return PTR_ERR(phy->v1p8); ++ } ++ ++ phy->xo_clk = devm_clk_get(phy->dev, "xo"); ++ if (IS_ERR(phy->xo_clk)) { ++ dev_dbg(phy->dev, "cannot get XO clk, assuming not present\n"); ++ phy->xo_clk = NULL; ++ } ++ ++ phy->ref_clk = devm_clk_get(phy->dev, "ref"); ++ if (IS_ERR(phy->ref_clk)) { ++ dev_dbg(phy->dev, "cannot get ref clock handle\n"); ++ return PTR_ERR(phy->ref_clk); ++ } ++ ++ clk_set_rate(phy->ref_clk, 125000000); ++ if (phy->xo_clk) ++ clk_prepare_enable(phy->xo_clk); ++ ++ phy->base = base; ++ phy->phy.dev = phy->dev; ++ phy->phy.label = "qcom-dwc3-ssphy"; ++ phy->phy.init = qcom_dwc3_ss_phy_init; ++ phy->phy.shutdown = qcom_dwc3_ss_phy_shutdown; ++ phy->phy.set_suspend = qcom_dwc3_ss_set_suspend; ++ phy->phy.type = USB_PHY_TYPE_USB3; ++ ++ ret = usb_add_phy_dev(&phy->phy); ++ return ret; ++} ++ ++static int qcom_dwc3_ss_remove(struct platform_device *pdev) ++{ ++ struct qcom_dwc3_ss_phy *phy = platform_get_drvdata(pdev); ++ ++ if (phy->xo_clk) ++ clk_disable_unprepare(phy->xo_clk); ++ usb_remove_phy(&phy->phy); ++ return 0; ++} ++ ++static const struct of_device_id qcom_dwc3_ss_id_table[] = { ++ { .compatible = "qcom,dwc3-ssphy" }, ++ { /* Sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, qcom_dwc3_ss_id_table); ++ ++static struct platform_driver qcom_dwc3_ss_driver = { ++ .probe = qcom_dwc3_ss_probe, ++ .remove = qcom_dwc3_ss_remove, ++ .driver = { ++ .name = "qcom-dwc3-ssphy", ++ .owner = THIS_MODULE, ++ .of_match_table = qcom_dwc3_ss_id_table, ++ }, ++}; ++ ++module_platform_driver(qcom_dwc3_ss_driver); ++ ++MODULE_ALIAS("platform:qcom-dwc3-ssphy"); ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("DesignWare USB3 QCOM SSPHY driver"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0158-usb-dwc3-qcom-Add-device-tree-binding.patch b/target/linux/ipq806x/patches/0158-usb-dwc3-qcom-Add-device-tree-binding.patch new file mode 100644 index 0000000000..cf1b300b50 --- /dev/null +++ b/target/linux/ipq806x/patches/0158-usb-dwc3-qcom-Add-device-tree-binding.patch @@ -0,0 +1,131 @@ +From c7045330c5976eb31bd79bc57c5db684588d595e Mon Sep 17 00:00:00 2001 +From: "Ivan T. Ivanov" <iivanov@mm-sol.com> +Date: Mon, 7 Oct 2013 10:44:55 +0300 +Subject: [PATCH 158/182] usb: dwc3: qcom: Add device tree binding + +QCOM USB3.0 core wrapper consist of USB3.0 IP from Synopsys +(SNPS) and HS, SS PHY's control and configuration registers. + +It could operate in device mode (SS, HS, FS) and host +mode (SS, HS, FS, LS). + +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Acked-by: Stephen Warren <swarren@nvidia.com> +--- + .../devicetree/bindings/usb/qcom,dwc3.txt | 104 ++++++++++++++++++++ + 1 file changed, 104 insertions(+) + create mode 100644 Documentation/devicetree/bindings/usb/qcom,dwc3.txt + +diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.txt b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt +new file mode 100644 +index 0000000..105b6b7 +--- /dev/null ++++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt +@@ -0,0 +1,104 @@ ++Qualcomm SuperSpeed DWC3 USB SoC controller ++ ++ ++QCOM DWC3 Highspeed USB PHY ++======================== ++Required properities: ++- compatible: should contain "qcom,dwc3-hsphy"; ++- reg: offset and length of the register set in the memory map ++- clocks: A list of phandle + clock-specifier pairs for the ++ clocks listed in clock-names ++- clock-names: Should contain the following: ++ "utmi" UTMI clock ++- v1p8-supply: phandle to the regulator for the 1.8v supply to HSPHY. ++- v3p3-supply: phandle to the regulator for the 3.3v supply to HSPHY. ++- vbus-supply: phandle to the regulator for the vbus supply for host ++ mode. ++- vddcx-supply: phandle to the regulator for the vdd supply for HSPHY ++ digital circuit operation. ++ ++Optional clocks: ++ "xo" External reference clock ++ ++ ++QCOM DWC3 Superspeed USB PHY ++========================= ++Required properities: ++- compatible: should contain "qcom,dwc3-ssphy"; ++- reg: offset and length of the register set in the memory map ++- clocks: A list of phandle + clock-specifier pairs for the ++ clocks listed in clock-names ++- clock-names: Should contain the following: ++ "ref" Reference clock used in host mode. ++- v1p8-supply: phandle to the regulator for the 1.8v supply to HSPHY. ++- vddcx-supply: phandle to the regulator for the vdd supply for HSPHY ++ digital circuit operation. ++ ++Optional clocks: ++ "xo" External reference clock ++ ++QCOM DWC3 controller wrapper ++=========================== ++Required properties: ++- compatible: should contain "qcom,dwc3" ++- clocks: A list of phandle + clock-specifier pairs for the ++ clocks listed in clock-names ++- clock-names: Should contain the following: ++ "core" Master/Core clock, have to be >= 125 MHz for SS ++ operation and >= 60MHz for HS operation ++ ++Optional clocks: ++ "iface" System bus AXI clock. Not present on all platforms ++ "sleep" Sleep clock, used when USB3 core goes into low ++ power mode (U3). ++ ++Optional regulator: ++- gdsc-supply: phandle to the regulator from globally distributed ++ switch controller ++ ++Required child node: ++A child node must exist to represent the core DWC3 IP block. The name of ++the node is not important. The content of the node is defined in dwc3.txt. ++ ++Example device nodes: ++ ++ hs_phy_0: phy@110f8800 { ++ compatible = "qcom,dwc3-hsphy"; ++ reg = <0x110f8800 0x30>; ++ clocks = <&gcc USB30_0_UTMI_CLK>; ++ clock-names = "utmi"; ++ ++ status = "disabled"; ++ }; ++ ++ ss_phy_0: phy@110f8830 { ++ compatible = "qcom,dwc3-ssphy"; ++ reg = <0x110f8830 0x30>; ++ ++ clocks = <&gcc USB30_0_MASTER_CLK>; ++ clock-names = "ref"; ++ ++ 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>; ++ usb-phy = <&hs_phy_0>, <&ss_phy_0>; ++ phy-names = "usb2-phy", "usb3-phy"; ++ tx-fifo-resize; ++ dr_mode = "host"; ++ }; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0159-arm-ipq8064-Add-USB3-DT-information.patch b/target/linux/ipq806x/patches/0159-arm-ipq8064-Add-USB3-DT-information.patch new file mode 100644 index 0000000000..5a0d099af8 --- /dev/null +++ b/target/linux/ipq806x/patches/0159-arm-ipq8064-Add-USB3-DT-information.patch @@ -0,0 +1,162 @@ +From 269a71c81438604d27f01ec703daa7f5e3f39e8b Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Sun, 15 Jun 2014 00:48:18 -0500 +Subject: [PATCH 159/182] arm: ipq8064: Add USB3 DT information + +This patch fleshes out the USB3 specific information for the IPQ8064 platform. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 29 ++++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 90 ++++++++++++++++++++++++++++++ + 2 files changed, 119 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +index 4062eb6..2b2d63c 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -160,5 +160,34 @@ + pinctrl-0 = <&nand_pins>; + pinctrl-names = "default"; + }; ++ ++ tcsr@1a400000 { ++ status = "ok"; ++ qcom,usb-ctrl-select = <TCSR_USB_SELECT_USB3_DUAL>; ++ }; ++ ++ phy@100f8800 { /* USB3 port 1 HS phy */ ++ status = "ok"; ++ }; ++ ++ phy@100f8830 { /* USB3 port 1 SS phy */ ++ status = "ok"; ++ }; ++ ++ phy@110f8800 { /* USB3 port 0 HS phy */ ++ status = "ok"; ++ }; ++ ++ phy@110f8830 { /* USB3 port 0 SS phy */ ++ status = "ok"; ++ }; ++ ++ usb30@0 { ++ status = "ok"; ++ }; ++ ++ usb30@1 { ++ status = "ok"; ++ }; + }; + }; +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +index d9fce15..6be6ac9 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -4,6 +4,7 @@ + #include <dt-bindings/clock/qcom,gcc-ipq806x.h> + #include <dt-bindings/reset/qcom,gcc-ipq806x.h> + #include <dt-bindings/soc/qcom,gsbi.h> ++#include <dt-bindings/soc/qcom,tcsr.h> + + / { + model = "Qualcomm IPQ8064"; +@@ -402,5 +403,94 @@ + + status = "disabled"; + }; ++ ++ tcsr: tcsr@1a400000 { ++ compatible = "qcom,tcsr"; ++ reg = <0x1a400000 0x100>; ++ ++ status = "disabled"; ++ }; ++ ++ hs_phy_1: phy@100f8800 { ++ compatible = "qcom,dwc3-hsphy"; ++ reg = <0x100f8800 0x30>; ++ clocks = <&gcc USB30_1_UTMI_CLK>; ++ clock-names = "utmi"; ++ ++ status = "disabled"; ++ }; ++ ++ ss_phy_1: phy@100f8830 { ++ compatible = "qcom,dwc3-ssphy"; ++ reg = <0x100f8830 0x30>; ++ ++ clocks = <&gcc USB30_1_MASTER_CLK>; ++ clock-names = "ref"; ++ ++ status = "disabled"; ++ }; ++ ++ hs_phy_0: phy@110f8800 { ++ compatible = "qcom,dwc3-hsphy"; ++ reg = <0x110f8800 0x30>; ++ clocks = <&gcc USB30_0_UTMI_CLK>; ++ clock-names = "utmi"; ++ ++ status = "disabled"; ++ }; ++ ++ ss_phy_0: phy@110f8830 { ++ compatible = "qcom,dwc3-ssphy"; ++ reg = <0x110f8830 0x30>; ++ ++ clocks = <&gcc USB30_0_MASTER_CLK>; ++ clock-names = "ref"; ++ ++ 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>; ++ usb-phy = <&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>; ++ usb-phy = <&hs_phy_1>, <&ss_phy_1>; ++ phy-names = "usb2-phy", "usb3-phy"; ++ tx-fifo-resize; ++ dr_mode = "host"; ++ }; ++ }; + }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0160-ARM-qcom-config-Add-TCSR-and-USB3-options.patch b/target/linux/ipq806x/patches/0160-ARM-qcom-config-Add-TCSR-and-USB3-options.patch new file mode 100644 index 0000000000..b74db3edbc --- /dev/null +++ b/target/linux/ipq806x/patches/0160-ARM-qcom-config-Add-TCSR-and-USB3-options.patch @@ -0,0 +1,42 @@ +From 8e69ee9a592f363476f18e91f57d871662e9a393 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 16 Jun 2014 16:37:22 -0500 +Subject: [PATCH 160/182] ARM: qcom: config: Add TCSR and USB3 options + +Enable TCSR and USB3 options. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + arch/arm/configs/qcom_defconfig | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig +index e3f9013..3d55d79 100644 +--- a/arch/arm/configs/qcom_defconfig ++++ b/arch/arm/configs/qcom_defconfig +@@ -122,9 +122,14 @@ CONFIG_HID_BATTERY_STRENGTH=y + CONFIG_USB=y + CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + CONFIG_USB_MON=y ++CONFIG_USB_XHCI_HCD=y + CONFIG_USB_EHCI_HCD=y + CONFIG_USB_ACM=y ++CONFIG_USB_STORAGE=y ++CONFIG_USB_DWC3=y ++CONFIG_USB_DWC3_HOST=y + CONFIG_USB_SERIAL=y ++CONFIG_USB_QCOM_DWC3_PHY=y + CONFIG_USB_GADGET=y + CONFIG_USB_GADGET_DEBUG_FILES=y + CONFIG_USB_GADGET_VBUS_DRAW=500 +@@ -138,6 +143,7 @@ CONFIG_DMADEVICES=y + CONFIG_QCOM_BAM_DMA=y + CONFIG_STAGING=y + CONFIG_QCOM_GSBI=y ++CONFIG_QCOM_TCSR=y + CONFIG_COMMON_CLK_QCOM=y + CONFIG_IPQ_GCC_806X=y + CONFIG_MSM_GCC_8660=y +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0161-ARM-Remove-ARCH_HAS_CPUFREQ-config-option.patch b/target/linux/ipq806x/patches/0161-ARM-Remove-ARCH_HAS_CPUFREQ-config-option.patch new file mode 100644 index 0000000000..41dae6e81b --- /dev/null +++ b/target/linux/ipq806x/patches/0161-ARM-Remove-ARCH_HAS_CPUFREQ-config-option.patch @@ -0,0 +1,259 @@ +From 5d102a45ee224fa32c775deb75bf7eb9d2ee2cf0 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Tue, 3 Jun 2014 11:24:23 -0700 +Subject: [PATCH 161/182] ARM: Remove ARCH_HAS_CPUFREQ config option + +This config exists entirely to hide the cpufreq menu from the +kernel configuration unless a platform has selected it. Nothing +is actually built if this config is 'Y' and it just leads to more +patches that add a select under a platform Kconfig so that some +other CPUfreq option can be chosen. Let's remove the option so +that we can always enable CPUfreq drivers on ARM platforms. + +Acked-by: Viresh Kumar <viresh.kumar@linaro.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + arch/arm/Kconfig | 17 ----------------- + arch/arm/mach-davinci/Kconfig | 1 - + arch/arm/mach-highbank/Kconfig | 1 - + arch/arm/mach-imx/Kconfig | 2 -- + arch/arm/mach-omap2/Kconfig | 1 - + arch/arm/mach-shmobile/Kconfig | 2 -- + arch/arm/mach-spear/Kconfig | 1 - + arch/arm/mach-tegra/Kconfig | 1 - + arch/arm/mach-ux500/Kconfig | 1 - + arch/arm/mach-vexpress/Kconfig | 1 - + arch/arm/mach-vt8500/Kconfig | 1 - + 11 files changed, 29 deletions(-) + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 4332e8d..1a61d4a 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -180,13 +180,6 @@ config ARCH_HAS_ILOG2_U32 + config ARCH_HAS_ILOG2_U64 + bool + +-config ARCH_HAS_CPUFREQ +- bool +- help +- Internal node to signify that the ARCH has CPUFREQ support +- and that the relevant menu configurations are displayed for +- it. +- + config ARCH_HAS_BANDGAP + bool + +@@ -315,7 +308,6 @@ config ARCH_MULTIPLATFORM + + config ARCH_INTEGRATOR + bool "ARM Ltd. Integrator family" +- select ARCH_HAS_CPUFREQ + select ARM_AMBA + select ARM_PATCH_PHYS_VIRT + select AUTO_ZRELADDR +@@ -540,7 +532,6 @@ config ARCH_DOVE + + config ARCH_KIRKWOOD + bool "Marvell Kirkwood" +- select ARCH_HAS_CPUFREQ + select ARCH_REQUIRE_GPIOLIB + select CPU_FEROCEON + select GENERIC_CLOCKEVENTS +@@ -641,7 +632,6 @@ config ARCH_LPC32XX + config ARCH_PXA + bool "PXA2xx/PXA3xx-based" + depends on MMU +- select ARCH_HAS_CPUFREQ + select ARCH_MTD_XIP + select ARCH_REQUIRE_GPIOLIB + select ARM_CPU_SUSPEND if PM +@@ -710,7 +700,6 @@ config ARCH_RPC + + config ARCH_SA1100 + bool "SA1100-based" +- select ARCH_HAS_CPUFREQ + select ARCH_MTD_XIP + select ARCH_REQUIRE_GPIOLIB + select ARCH_SPARSEMEM_ENABLE +@@ -728,7 +717,6 @@ config ARCH_SA1100 + + config ARCH_S3C24XX + bool "Samsung S3C24XX SoCs" +- select ARCH_HAS_CPUFREQ + select ARCH_REQUIRE_GPIOLIB + select CLKDEV_LOOKUP + select CLKSRC_SAMSUNG_PWM +@@ -748,7 +736,6 @@ config ARCH_S3C24XX + + config ARCH_S3C64XX + bool "Samsung S3C64XX" +- select ARCH_HAS_CPUFREQ + select ARCH_REQUIRE_GPIOLIB + select ARM_AMBA + select ARM_VIC +@@ -809,7 +796,6 @@ config ARCH_S5PC100 + + config ARCH_S5PV210 + bool "Samsung S5PV210/S5PC110" +- select ARCH_HAS_CPUFREQ + select ARCH_HAS_HOLES_MEMORYMODEL + select ARCH_SPARSEMEM_ENABLE + select CLKDEV_LOOKUP +@@ -863,7 +849,6 @@ config ARCH_DAVINCI + config ARCH_OMAP1 + bool "TI OMAP1" + depends on MMU +- select ARCH_HAS_CPUFREQ + select ARCH_HAS_HOLES_MEMORYMODEL + select ARCH_OMAP + select ARCH_REQUIRE_GPIOLIB +@@ -2170,9 +2155,7 @@ endmenu + + menu "CPU Power Management" + +-if ARCH_HAS_CPUFREQ + source "drivers/cpufreq/Kconfig" +-endif + + source "drivers/cpuidle/Kconfig" + +diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig +index a075b3e..34cfecc 100644 +--- a/arch/arm/mach-davinci/Kconfig ++++ b/arch/arm/mach-davinci/Kconfig +@@ -39,7 +39,6 @@ config ARCH_DAVINCI_DA830 + config ARCH_DAVINCI_DA850 + bool "DA850/OMAP-L138/AM18x based system" + select ARCH_DAVINCI_DA8XX +- select ARCH_HAS_CPUFREQ + select CP_INTC + + config ARCH_DAVINCI_DA8XX +diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig +index 0aded64..9f74755 100644 +--- a/arch/arm/mach-highbank/Kconfig ++++ b/arch/arm/mach-highbank/Kconfig +@@ -1,7 +1,6 @@ + config ARCH_HIGHBANK + bool "Calxeda ECX-1000/2000 (Highbank/Midway)" if ARCH_MULTI_V7 + select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE +- select ARCH_HAS_CPUFREQ + select ARCH_HAS_HOLES_MEMORYMODEL + select ARCH_HAS_OPP + select ARCH_SUPPORTS_BIG_ENDIAN +diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig +index 33567aa..e7ae0ee 100644 +--- a/arch/arm/mach-imx/Kconfig ++++ b/arch/arm/mach-imx/Kconfig +@@ -103,7 +103,6 @@ config SOC_IMX25 + + config SOC_IMX27 + bool +- select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP + select CPU_ARM926T + select IMX_HAVE_IOMUX_V1 +@@ -129,7 +128,6 @@ config SOC_IMX35 + + config SOC_IMX5 + bool +- select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP + select ARCH_MXC_IOMUX_V3 + select CPU_V7 +diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig +index 0af7ca0..0674cb7 100644 +--- a/arch/arm/mach-omap2/Kconfig ++++ b/arch/arm/mach-omap2/Kconfig +@@ -93,7 +93,6 @@ config SOC_DRA7XX + config ARCH_OMAP2PLUS + bool + select ARCH_HAS_BANDGAP +- select ARCH_HAS_CPUFREQ + select ARCH_HAS_HOLES_MEMORYMODEL + select ARCH_OMAP + select ARCH_REQUIRE_GPIOLIB +diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig +index 05fa505..61d4d31 100644 +--- a/arch/arm/mach-shmobile/Kconfig ++++ b/arch/arm/mach-shmobile/Kconfig +@@ -85,7 +85,6 @@ config ARCH_R8A73A4 + select CPU_V7 + select SH_CLK_CPG + select RENESAS_IRQC +- select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP + + config ARCH_R8A7740 +@@ -271,7 +270,6 @@ config MACH_KOELSCH + config MACH_KZM9G + bool "KZM-A9-GT board" + depends on ARCH_SH73A0 +- select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP + select ARCH_REQUIRE_GPIOLIB + select REGULATOR_FIXED_VOLTAGE if REGULATOR +diff --git a/arch/arm/mach-spear/Kconfig b/arch/arm/mach-spear/Kconfig +index ac1710e6..811ba13 100644 +--- a/arch/arm/mach-spear/Kconfig ++++ b/arch/arm/mach-spear/Kconfig +@@ -16,7 +16,6 @@ if PLAT_SPEAR + config ARCH_SPEAR13XX + bool "ST SPEAr13xx" + depends on ARCH_MULTI_V7 || PLAT_SPEAR_SINGLE +- select ARCH_HAS_CPUFREQ + select ARM_GIC + select CPU_V7 + select GPIO_SPEAR_SPICS +diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig +index b1232d8..52bfc9e 100644 +--- a/arch/arm/mach-tegra/Kconfig ++++ b/arch/arm/mach-tegra/Kconfig +@@ -1,6 +1,5 @@ + config ARCH_TEGRA + bool "NVIDIA Tegra" if ARCH_MULTI_V7 +- select ARCH_HAS_CPUFREQ + select ARCH_REQUIRE_GPIOLIB + select ARCH_SUPPORTS_TRUSTED_FOUNDATIONS + select ARM_GIC +diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig +index 0034d2c..cb1176e 100644 +--- a/arch/arm/mach-ux500/Kconfig ++++ b/arch/arm/mach-ux500/Kconfig +@@ -3,7 +3,6 @@ config ARCH_U8500 + depends on MMU + select AB8500_CORE + select ABX500_CORE +- select ARCH_HAS_CPUFREQ + select ARCH_REQUIRE_GPIOLIB + select ARM_AMBA + select ARM_ERRATA_754322 +diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig +index 4a70be4..ca5b7e5 100644 +--- a/arch/arm/mach-vexpress/Kconfig ++++ b/arch/arm/mach-vexpress/Kconfig +@@ -67,7 +67,6 @@ config ARCH_VEXPRESS_DCSCB + + config ARCH_VEXPRESS_SPC + bool "Versatile Express Serial Power Controller (SPC)" +- select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP + select PM_OPP + help +diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig +index 927be93..788d0b4 100644 +--- a/arch/arm/mach-vt8500/Kconfig ++++ b/arch/arm/mach-vt8500/Kconfig +@@ -1,6 +1,5 @@ + config ARCH_VT8500 + bool +- select ARCH_HAS_CPUFREQ + select ARCH_REQUIRE_GPIOLIB + select CLKDEV_LOOKUP + select CLKSRC_OF +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0162-PM-OPP-Remove-ARCH_HAS_OPP.patch b/target/linux/ipq806x/patches/0162-PM-OPP-Remove-ARCH_HAS_OPP.patch new file mode 100644 index 0000000000..979212fe8f --- /dev/null +++ b/target/linux/ipq806x/patches/0162-PM-OPP-Remove-ARCH_HAS_OPP.patch @@ -0,0 +1,134 @@ +From 18e4542f7b02f586d46594977818cd6b24d9cbcb Mon Sep 17 00:00:00 2001 +From: Mark Brown <broonie@linaro.org> +Date: Fri, 6 Jun 2014 11:36:56 +0100 +Subject: [PATCH 162/182] PM / OPP: Remove ARCH_HAS_OPP + +Since the OPP layer is a kernel library which has been converted to be +directly selectable by its callers rather than user selectable and +requiring architectures to enable it explicitly the ARCH_HAS_OPP symbol +has become redundant and can be removed. Do so. + +Signed-off-by: Mark Brown <broonie@linaro.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + Documentation/power/opp.txt | 3 --- + arch/arm/mach-exynos/Kconfig | 1 - + arch/arm/mach-highbank/Kconfig | 1 - + arch/arm/mach-omap2/Kconfig | 1 - + arch/arm/mach-shmobile/Kconfig | 2 -- + arch/arm/mach-vexpress/Kconfig | 1 - + drivers/devfreq/Kconfig | 1 - + kernel/power/Kconfig | 3 --- + 8 files changed, 13 deletions(-) + +diff --git a/Documentation/power/opp.txt b/Documentation/power/opp.txt +index b8a907d..7b6a021 100644 +--- a/Documentation/power/opp.txt ++++ b/Documentation/power/opp.txt +@@ -52,9 +52,6 @@ Typical usage of the OPP library is as follows: + SoC framework -> modifies on required cases certain OPPs -> OPP layer + -> queries to search/retrieve information -> + +-Architectures that provide a SoC framework for OPP should select ARCH_HAS_OPP +-to make the OPP layer available. +- + OPP layer expects each domain to be represented by a unique device pointer. SoC + framework registers a set of initial OPPs per device with the OPP layer. This + list is expected to be an optimally small number typically around 5 per device. +diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig +index 4c414af..67e69a8 100644 +--- a/arch/arm/mach-exynos/Kconfig ++++ b/arch/arm/mach-exynos/Kconfig +@@ -107,7 +107,6 @@ config SOC_EXYNOS5440 + depends on ARCH_EXYNOS5 + select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE + select ARCH_HAS_BANDGAP +- select ARCH_HAS_OPP + select HAVE_ARM_ARCH_TIMER + select AUTO_ZRELADDR + select MIGHT_HAVE_PCI +diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig +index 9f74755..43bd782 100644 +--- a/arch/arm/mach-highbank/Kconfig ++++ b/arch/arm/mach-highbank/Kconfig +@@ -2,7 +2,6 @@ config ARCH_HIGHBANK + bool "Calxeda ECX-1000/2000 (Highbank/Midway)" if ARCH_MULTI_V7 + select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE + select ARCH_HAS_HOLES_MEMORYMODEL +- select ARCH_HAS_OPP + select ARCH_SUPPORTS_BIG_ENDIAN + select ARCH_WANT_OPTIONAL_GPIOLIB + select ARM_AMBA +diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig +index 0674cb7..3e6596c 100644 +--- a/arch/arm/mach-omap2/Kconfig ++++ b/arch/arm/mach-omap2/Kconfig +@@ -13,7 +13,6 @@ config ARCH_OMAP3 + bool "TI OMAP3" + depends on ARCH_MULTI_V7 + select ARCH_OMAP2PLUS +- select ARCH_HAS_OPP + select ARM_CPU_SUSPEND if PM + select CPU_V7 + select MULTI_IRQ_HANDLER +diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig +index 61d4d31..9fc2dd2 100644 +--- a/arch/arm/mach-shmobile/Kconfig ++++ b/arch/arm/mach-shmobile/Kconfig +@@ -85,7 +85,6 @@ config ARCH_R8A73A4 + select CPU_V7 + select SH_CLK_CPG + select RENESAS_IRQC +- select ARCH_HAS_OPP + + config ARCH_R8A7740 + bool "R-Mobile A1 (R8A77400)" +@@ -270,7 +269,6 @@ config MACH_KOELSCH + config MACH_KZM9G + bool "KZM-A9-GT board" + depends on ARCH_SH73A0 +- select ARCH_HAS_OPP + select ARCH_REQUIRE_GPIOLIB + select REGULATOR_FIXED_VOLTAGE if REGULATOR + select SND_SOC_AK4642 if SND_SIMPLE_CARD +diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig +index ca5b7e5..4ed6e97 100644 +--- a/arch/arm/mach-vexpress/Kconfig ++++ b/arch/arm/mach-vexpress/Kconfig +@@ -67,7 +67,6 @@ config ARCH_VEXPRESS_DCSCB + + config ARCH_VEXPRESS_SPC + bool "Versatile Express Serial Power Controller (SPC)" +- select ARCH_HAS_OPP + select PM_OPP + help + The TC2 (A15x2 A7x3) versatile express core tile integrates a logic +diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig +index 7d2f435..d416754 100644 +--- a/drivers/devfreq/Kconfig ++++ b/drivers/devfreq/Kconfig +@@ -68,7 +68,6 @@ comment "DEVFREQ Drivers" + config ARM_EXYNOS4_BUS_DEVFREQ + bool "ARM Exynos4210/4212/4412 Memory Bus DEVFREQ Driver" + depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM +- select ARCH_HAS_OPP + select DEVFREQ_GOV_SIMPLE_ONDEMAND + help + This adds the DEVFREQ driver for Exynos4210 memory bus (vdd_int) +diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig +index 2fac9cc..caa040c 100644 +--- a/kernel/power/Kconfig ++++ b/kernel/power/Kconfig +@@ -253,9 +253,6 @@ config APM_EMULATION + anything, try disabling/enabling this option (or disabling/enabling + APM in your BIOS). + +-config ARCH_HAS_OPP +- bool +- + config PM_OPP + bool "Operating Performance Point (OPP) Layer library" + depends on ARCH_HAS_OPP +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0163-clk-return-probe-defer-when-DT-clock-not-yet-ready.patch b/target/linux/ipq806x/patches/0163-clk-return-probe-defer-when-DT-clock-not-yet-ready.patch new file mode 100644 index 0000000000..7ea69f331b --- /dev/null +++ b/target/linux/ipq806x/patches/0163-clk-return-probe-defer-when-DT-clock-not-yet-ready.patch @@ -0,0 +1,49 @@ +From 1ce8667062bc7b8813adf6103ad2374d9dd52fb0 Mon Sep 17 00:00:00 2001 +From: Jean-Francois Moine <moinejf@free.fr> +Date: Mon, 25 Nov 2013 19:47:04 +0100 +Subject: [PATCH 163/182] clk: return probe defer when DT clock not yet ready + +At probe time, a clock device may not be ready when some other device +wants to use it. + +This patch lets the functions clk_get/devm_clk_get return a probe defer +when the clock is defined in the DT but not yet available. + +Signed-off-by: Jean-Francois Moine <moinejf@free.fr> +Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com> +Tested-by: Sylwester Nawrocki <s.nawrocki@samsung.com> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +--- + drivers/clk/clk.c | 2 +- + drivers/clk/clkdev.c | 2 ++ + 2 files changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c +index 0582068..a3facad 100644 +--- a/drivers/clk/clk.c ++++ b/drivers/clk/clk.c +@@ -2502,7 +2502,7 @@ EXPORT_SYMBOL_GPL(of_clk_del_provider); + struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec) + { + struct of_clk_provider *provider; +- struct clk *clk = ERR_PTR(-ENOENT); ++ struct clk *clk = ERR_PTR(-EPROBE_DEFER); + + /* Check if we have such a provider in our array */ + list_for_each_entry(provider, &of_clk_providers, link) { +diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c +index 48f6721..a360b2e 100644 +--- a/drivers/clk/clkdev.c ++++ b/drivers/clk/clkdev.c +@@ -167,6 +167,8 @@ struct clk *clk_get(struct device *dev, const char *con_id) + clk = of_clk_get_by_name(dev->of_node, con_id); + if (!IS_ERR(clk)) + return clk; ++ if (PTR_ERR(clk) == -EPROBE_DEFER) ++ return clk; + } + + return clk_get_sys(dev_id, con_id); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0164-ARM-Add-Krait-L2-register-accessor-functions.patch b/target/linux/ipq806x/patches/0164-ARM-Add-Krait-L2-register-accessor-functions.patch new file mode 100644 index 0000000000..652590e984 --- /dev/null +++ b/target/linux/ipq806x/patches/0164-ARM-Add-Krait-L2-register-accessor-functions.patch @@ -0,0 +1,146 @@ +From d8bf5e13683e027c52476b89b874d50e5281c130 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 18 Oct 2013 16:45:05 -0700 +Subject: [PATCH 164/182] ARM: Add Krait L2 register accessor functions + +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 + +diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig +index c3a4e9c..9da52dc 100644 +--- 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 + +diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile +index 4bdc4162..2836f99 100644 +--- 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 +diff --git a/arch/arm/common/krait-l2-accessors.c b/arch/arm/common/krait-l2-accessors.c +new file mode 100644 +index 0000000..5d514bb +--- /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); +diff --git a/arch/arm/include/asm/krait-l2-accessors.h b/arch/arm/include/asm/krait-l2-accessors.h +new file mode 100644 +index 0000000..48fe552 +--- /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.7.10.4 + diff --git a/target/linux/ipq806x/patches/0165-clk-qcom-Add-support-for-muxes-dividers-and-mux-divi.patch b/target/linux/ipq806x/patches/0165-clk-qcom-Add-support-for-muxes-dividers-and-mux-divi.patch new file mode 100644 index 0000000000..0964106770 --- /dev/null +++ b/target/linux/ipq806x/patches/0165-clk-qcom-Add-support-for-muxes-dividers-and-mux-divi.patch @@ -0,0 +1,663 @@ +From 151d7e91baaa4016ba687b80e8f7ccead62d6c72 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Tue, 25 Mar 2014 13:37:55 -0700 +Subject: [PATCH 165/182] clk: qcom: Add support for muxes, dividers, and mux + dividers + +The Krait CPU clocks are made up of muxes and dividers with a +handful of sources. Add a set of clk_ops that allow us to +configure these clocks so we can support CPU frequency scaling on +Krait CPUs. + +Based on code originally written by Saravana Kannan. + +Cc: Saravana Kannan <skannan@codeaurora.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-generic.c | 405 +++++++++++++++++++++++++++++++++++ + include/linux/clk/msm-clk-generic.h | 208 ++++++++++++++++++ + 3 files changed, 614 insertions(+) + create mode 100644 drivers/clk/qcom/clk-generic.c + create mode 100644 include/linux/clk/msm-clk-generic.h + +diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile +index df2a1b3..2cc6039 100644 +--- 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-generic.o + clk-qcom-y += reset.o + + obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o +diff --git a/drivers/clk/qcom/clk-generic.c b/drivers/clk/qcom/clk-generic.c +new file mode 100644 +index 0000000..a0d778b +--- /dev/null ++++ b/drivers/clk/qcom/clk-generic.c +@@ -0,0 +1,405 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/export.h> ++#include <linux/bug.h> ++#include <linux/err.h> ++#include <linux/clk-provider.h> ++#include <linux/clk/msm-clk-generic.h> ++ ++ ++/* ==================== Mux clock ==================== */ ++ ++static int mux_set_parent(struct clk_hw *hw, u8 sel) ++{ ++ struct mux_clk *mux = to_mux_clk(hw); ++ ++ if (mux->parent_map) ++ sel = mux->parent_map[sel]; ++ ++ return mux->ops->set_mux_sel(mux, sel); ++} ++ ++static u8 mux_get_parent(struct clk_hw *hw) ++{ ++ struct mux_clk *mux = to_mux_clk(hw); ++ int num_parents = __clk_get_num_parents(hw->clk); ++ int i; ++ u8 sel; ++ ++ sel = mux->ops->get_mux_sel(mux); ++ if (mux->parent_map) { ++ for (i = 0; i < num_parents; i++) ++ if (sel == mux->parent_map[i]) ++ return i; ++ WARN(1, "Can't find parent\n"); ++ return -EINVAL; ++ } ++ ++ return sel; ++} ++ ++static int mux_enable(struct clk_hw *hw) ++{ ++ struct mux_clk *mux = to_mux_clk(hw); ++ if (mux->ops->enable) ++ return mux->ops->enable(mux); ++ return 0; ++} ++ ++static void mux_disable(struct clk_hw *hw) ++{ ++ struct mux_clk *mux = to_mux_clk(hw); ++ if (mux->ops->disable) ++ return mux->ops->disable(mux); ++} ++ ++static struct clk *mux_get_safe_parent(struct clk_hw *hw) ++{ ++ int i; ++ struct mux_clk *mux = to_mux_clk(hw); ++ int num_parents = __clk_get_num_parents(hw->clk); ++ ++ if (!mux->has_safe_parent) ++ return NULL; ++ ++ i = mux->safe_sel; ++ if (mux->parent_map) ++ for (i = 0; i < num_parents; i++) ++ if (mux->safe_sel == mux->parent_map[i]) ++ break; ++ ++ return clk_get_parent_by_index(hw->clk, i); ++} ++ ++const struct clk_ops clk_ops_gen_mux = { ++ .enable = mux_enable, ++ .disable = mux_disable, ++ .set_parent = mux_set_parent, ++ .get_parent = mux_get_parent, ++ .determine_rate = __clk_mux_determine_rate, ++ .get_safe_parent = mux_get_safe_parent, ++}; ++EXPORT_SYMBOL_GPL(clk_ops_gen_mux); ++ ++/* ==================== Divider clock ==================== */ ++ ++static long __div_round_rate(struct div_data *data, unsigned long rate, ++ struct clk *parent, unsigned int *best_div, unsigned long *best_prate, ++ bool set_parent) ++{ ++ unsigned int div, min_div, max_div, _best_div = 1; ++ unsigned long prate, _best_prate = 0, rrate = 0, req_prate, actual_rate; ++ unsigned int numer; ++ ++ rate = max(rate, 1UL); ++ ++ min_div = max(data->min_div, 1U); ++ max_div = min(data->max_div, (unsigned int) (ULONG_MAX / rate)); ++ ++ /* ++ * div values are doubled for half dividers. ++ * Adjust for that by picking a numer of 2. ++ */ ++ numer = data->is_half_divider ? 2 : 1; ++ ++ if (!set_parent) { ++ prate = *best_prate * numer; ++ div = DIV_ROUND_UP(prate, rate); ++ div = clamp(1U, div, max_div); ++ if (best_div) ++ *best_div = div; ++ return mult_frac(*best_prate, numer, div); ++ } ++ ++ for (div = min_div; div <= max_div; div++) { ++ req_prate = mult_frac(rate, div, numer); ++ prate = __clk_round_rate(parent, req_prate); ++ if (IS_ERR_VALUE(prate)) ++ break; ++ ++ actual_rate = mult_frac(prate, numer, div); ++ if (is_better_rate(rate, rrate, actual_rate)) { ++ rrate = actual_rate; ++ _best_div = div; ++ _best_prate = prate; ++ } ++ ++ /* ++ * Trying higher dividers is only going to ask the parent for ++ * a higher rate. If it can't even output a rate higher than ++ * the one we request for this divider, the parent is not ++ * going to be able to output an even higher rate required ++ * for a higher divider. So, stop trying higher dividers. ++ */ ++ if (actual_rate < rate) ++ break; ++ ++ if (rrate <= rate) ++ break; ++ } ++ ++ if (!rrate) ++ return -EINVAL; ++ if (best_div) ++ *best_div = _best_div; ++ if (best_prate) ++ *best_prate = _best_prate; ++ ++ return rrate; ++} ++ ++static long div_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ struct div_clk *d = to_div_clk(hw); ++ bool set_parent = __clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT; ++ ++ return __div_round_rate(&d->data, rate, __clk_get_parent(hw->clk), ++ NULL, parent_rate, set_parent); ++} ++ ++static int div_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long ++ parent_rate) ++{ ++ struct div_clk *d = to_div_clk(hw); ++ int div, rc = 0; ++ struct div_data *data = &d->data; ++ ++ div = parent_rate / rate; ++ if (div != data->div) ++ rc = d->ops->set_div(d, div); ++ data->div = div; ++ ++ return rc; ++} ++ ++static int div_enable(struct clk_hw *hw) ++{ ++ struct div_clk *d = to_div_clk(hw); ++ if (d->ops && d->ops->enable) ++ return d->ops->enable(d); ++ return 0; ++} ++ ++static void div_disable(struct clk_hw *hw) ++{ ++ struct div_clk *d = to_div_clk(hw); ++ if (d->ops && d->ops->disable) ++ return d->ops->disable(d); ++} ++ ++static unsigned long div_recalc_rate(struct clk_hw *hw, unsigned long prate) ++{ ++ struct div_clk *d = to_div_clk(hw); ++ unsigned int div = d->data.div; ++ ++ if (d->ops && d->ops->get_div) ++ div = max(d->ops->get_div(d), 1); ++ div = max(div, 1U); ++ ++ if (!d->ops || !d->ops->set_div) ++ d->data.min_div = d->data.max_div = div; ++ d->data.div = div; ++ ++ return prate / div; ++} ++ ++const struct clk_ops clk_ops_div = { ++ .enable = div_enable, ++ .disable = div_disable, ++ .round_rate = div_round_rate, ++ .set_rate = div_set_rate, ++ .recalc_rate = div_recalc_rate, ++}; ++EXPORT_SYMBOL_GPL(clk_ops_div); ++ ++/* ==================== Mux_div clock ==================== */ ++ ++static int mux_div_clk_enable(struct clk_hw *hw) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ ++ if (md->ops->enable) ++ return md->ops->enable(md); ++ return 0; ++} ++ ++static void mux_div_clk_disable(struct clk_hw *hw) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ ++ if (md->ops->disable) ++ return md->ops->disable(md); ++} ++ ++static long __mux_div_round_rate(struct clk_hw *hw, unsigned long rate, ++ struct clk **best_parent, int *best_div, unsigned long *best_prate) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ unsigned int i; ++ unsigned long rrate, best = 0, _best_div = 0, _best_prate = 0; ++ struct clk *_best_parent = 0; ++ int num_parents = __clk_get_num_parents(hw->clk); ++ bool set_parent = __clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT; ++ ++ for (i = 0; i < num_parents; i++) { ++ int div; ++ unsigned long prate; ++ struct clk *p = clk_get_parent_by_index(hw->clk, i); ++ ++ rrate = __div_round_rate(&md->data, rate, p, &div, &prate, ++ set_parent); ++ ++ if (is_better_rate(rate, best, rrate)) { ++ best = rrate; ++ _best_div = div; ++ _best_prate = prate; ++ _best_parent = p; ++ } ++ ++ if (rate <= rrate) ++ break; ++ } ++ ++ if (best_div) ++ *best_div = _best_div; ++ if (best_prate) ++ *best_prate = _best_prate; ++ if (best_parent) ++ *best_parent = _best_parent; ++ ++ if (best) ++ return best; ++ return -EINVAL; ++} ++ ++static long mux_div_clk_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ return __mux_div_round_rate(hw, rate, NULL, NULL, parent_rate); ++} ++ ++/* requires enable lock to be held */ ++static int __set_src_div(struct mux_div_clk *md, u8 src_sel, u32 div) ++{ ++ int rc; ++ ++ rc = md->ops->set_src_div(md, src_sel, div); ++ if (!rc) { ++ md->data.div = div; ++ md->src_sel = src_sel; ++ } ++ ++ return rc; ++} ++ ++/* Must be called after handoff to ensure parent clock rates are initialized */ ++static int safe_parent_init_once(struct clk_hw *hw) ++{ ++ unsigned long rrate; ++ u32 best_div; ++ struct clk *best_parent; ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ ++ if (IS_ERR(md->safe_parent)) ++ return -EINVAL; ++ if (!md->safe_freq || md->safe_parent) ++ return 0; ++ ++ rrate = __mux_div_round_rate(hw, md->safe_freq, &best_parent, ++ &best_div, NULL); ++ ++ if (rrate == md->safe_freq) { ++ md->safe_div = best_div; ++ md->safe_parent = best_parent; ++ } else { ++ md->safe_parent = ERR_PTR(-EINVAL); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int ++__mux_div_clk_set_rate_and_parent(struct clk_hw *hw, u8 index, u32 div) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ int rc; ++ ++ rc = safe_parent_init_once(hw); ++ if (rc) ++ return rc; ++ ++ return __set_src_div(md, index, div); ++} ++ ++static int mux_div_clk_set_rate_and_parent(struct clk_hw *hw, ++ unsigned long rate, unsigned long parent_rate, u8 index) ++{ ++ return __mux_div_clk_set_rate_and_parent(hw, index, parent_rate / rate); ++} ++ ++static int mux_div_clk_set_rate(struct clk_hw *hw, ++ unsigned long rate, unsigned long parent_rate) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ return __mux_div_clk_set_rate_and_parent(hw, md->src_sel, ++ parent_rate / rate); ++} ++ ++static int mux_div_clk_set_parent(struct clk_hw *hw, u8 index) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ return __mux_div_clk_set_rate_and_parent(hw, md->parent_map[index], ++ md->data.div); ++} ++ ++static u8 mux_div_clk_get_parent(struct clk_hw *hw) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ int num_parents = __clk_get_num_parents(hw->clk); ++ u32 i, div, sel; ++ ++ md->ops->get_src_div(md, &sel, &div); ++ md->src_sel = sel; ++ ++ for (i = 0; i < num_parents; i++) ++ if (sel == md->parent_map[i]) ++ return i; ++ WARN(1, "Can't find parent\n"); ++ return -EINVAL; ++} ++ ++static unsigned long ++mux_div_clk_recalc_rate(struct clk_hw *hw, unsigned long prate) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ u32 div, sel; ++ ++ md->ops->get_src_div(md, &sel, &div); ++ ++ return prate / div; ++} ++ ++const struct clk_ops clk_ops_mux_div_clk = { ++ .enable = mux_div_clk_enable, ++ .disable = mux_div_clk_disable, ++ .set_rate_and_parent = mux_div_clk_set_rate_and_parent, ++ .set_rate = mux_div_clk_set_rate, ++ .set_parent = mux_div_clk_set_parent, ++ .round_rate = mux_div_clk_round_rate, ++ .get_parent = mux_div_clk_get_parent, ++ .recalc_rate = mux_div_clk_recalc_rate, ++}; ++EXPORT_SYMBOL_GPL(clk_ops_mux_div_clk); +diff --git a/include/linux/clk/msm-clk-generic.h b/include/linux/clk/msm-clk-generic.h +new file mode 100644 +index 0000000..cee3863 +--- /dev/null ++++ b/include/linux/clk/msm-clk-generic.h +@@ -0,0 +1,208 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __QCOM_CLK_GENERIC_H__ ++#define __QCOM_CLK_GENERIC_H__ ++ ++#include <linux/err.h> ++#include <linux/clk-provider.h> ++ ++static inline bool is_better_rate(unsigned long req, unsigned long best, ++ unsigned long new) ++{ ++ if (IS_ERR_VALUE(new)) ++ return false; ++ ++ return (req <= new && new < best) || (best < req && best < new); ++} ++ ++/* ==================== Mux clock ==================== */ ++ ++struct mux_clk; ++ ++struct clk_mux_ops { ++ int (*set_mux_sel)(struct mux_clk *clk, int sel); ++ int (*get_mux_sel)(struct mux_clk *clk); ++ ++ /* Optional */ ++ bool (*is_enabled)(struct mux_clk *clk); ++ int (*enable)(struct mux_clk *clk); ++ void (*disable)(struct mux_clk *clk); ++}; ++ ++struct mux_clk { ++ /* Parents in decreasing order of preference for obtaining rates. */ ++ u8 *parent_map; ++ bool has_safe_parent; ++ u8 safe_sel; ++ const struct clk_mux_ops *ops; ++ ++ /* Fields not used by helper function. */ ++ void __iomem *base; ++ u32 offset; ++ u32 en_offset; ++ int en_reg; ++ u32 mask; ++ u32 shift; ++ u32 en_mask; ++ void *priv; ++ ++ struct clk_hw hw; ++}; ++ ++static inline struct mux_clk *to_mux_clk(struct clk_hw *hw) ++{ ++ return container_of(hw, struct mux_clk, hw); ++} ++ ++extern const struct clk_ops clk_ops_gen_mux; ++ ++/* ==================== Divider clock ==================== */ ++ ++struct div_clk; ++ ++struct clk_div_ops { ++ int (*set_div)(struct div_clk *clk, int div); ++ int (*get_div)(struct div_clk *clk); ++ bool (*is_enabled)(struct div_clk *clk); ++ int (*enable)(struct div_clk *clk); ++ void (*disable)(struct div_clk *clk); ++}; ++ ++struct div_data { ++ unsigned int div; ++ unsigned int min_div; ++ unsigned int max_div; ++ /* ++ * Indicate whether this divider clock supports half-interger divider. ++ * If it is, all the min_div and max_div have been doubled. It means ++ * they are 2*N. ++ */ ++ bool is_half_divider; ++}; ++ ++struct div_clk { ++ struct div_data data; ++ ++ /* Optional */ ++ const struct clk_div_ops *ops; ++ ++ /* Fields not used by helper function. */ ++ void __iomem *base; ++ u32 offset; ++ u32 mask; ++ u32 shift; ++ u32 en_mask; ++ void *priv; ++ struct clk_hw hw; ++}; ++ ++static inline struct div_clk *to_div_clk(struct clk_hw *hw) ++{ ++ return container_of(hw, struct div_clk, hw); ++} ++ ++extern const struct clk_ops clk_ops_div; ++ ++#define DEFINE_FIXED_DIV_CLK(clk_name, _div, _parent) \ ++static struct div_clk clk_name = { \ ++ .data = { \ ++ .max_div = _div, \ ++ .min_div = _div, \ ++ .div = _div, \ ++ }, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .parent_names = (const char *[]){ _parent }, \ ++ .num_parents = 1, \ ++ .name = #clk_name, \ ++ .ops = &clk_ops_div, \ ++ .flags = CLK_SET_RATE_PARENT, \ ++ } \ ++} ++ ++/* ==================== Mux Div clock ==================== */ ++ ++struct mux_div_clk; ++ ++/* ++ * struct mux_div_ops ++ * the enable and disable ops are optional. ++ */ ++ ++struct mux_div_ops { ++ int (*set_src_div)(struct mux_div_clk *, u32 src_sel, u32 div); ++ void (*get_src_div)(struct mux_div_clk *, u32 *src_sel, u32 *div); ++ int (*enable)(struct mux_div_clk *); ++ void (*disable)(struct mux_div_clk *); ++ bool (*is_enabled)(struct mux_div_clk *); ++}; ++ ++/* ++ * struct mux_div_clk - combined mux/divider clock ++ * @priv ++ parameters needed by ops ++ * @safe_freq ++ when switching rates from A to B, the mux div clock will ++ instead switch from A -> safe_freq -> B. This allows the ++ mux_div clock to change rates while enabled, even if this ++ behavior is not supported by the parent clocks. ++ ++ If changing the rate of parent A also causes the rate of ++ parent B to change, then safe_freq must be defined. ++ ++ safe_freq is expected to have a source clock which is always ++ on and runs at only one rate. ++ * @parents ++ list of parents and mux indicies ++ * @ops ++ function pointers for hw specific operations ++ * @src_sel ++ the mux index which will be used if the clock is enabled. ++ */ ++ ++struct mux_div_clk { ++ /* Required parameters */ ++ const struct mux_div_ops *ops; ++ struct div_data data; ++ u8 *parent_map; ++ ++ struct clk_hw hw; ++ ++ /* Internal */ ++ u32 src_sel; ++ ++ /* Optional parameters */ ++ void *priv; ++ void __iomem *base; ++ u32 div_mask; ++ u32 div_offset; ++ u32 div_shift; ++ u32 src_mask; ++ u32 src_offset; ++ u32 src_shift; ++ u32 en_mask; ++ u32 en_offset; ++ ++ u32 safe_div; ++ struct clk *safe_parent; ++ unsigned long safe_freq; ++}; ++ ++static inline struct mux_div_clk *to_mux_div_clk(struct clk_hw *hw) ++{ ++ return container_of(hw, struct mux_div_clk, hw); ++} ++ ++extern const struct clk_ops clk_ops_mux_div_clk; ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0166-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch b/target/linux/ipq806x/patches/0166-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch new file mode 100644 index 0000000000..3ff1a95def --- /dev/null +++ b/target/linux/ipq806x/patches/0166-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch @@ -0,0 +1,359 @@ +From 8a51ac2a4e36505e1ff82b0e49fd8b2edd0b7695 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Wed, 18 Jun 2014 14:15:58 -0700 +Subject: [PATCH 166/182] clk: qcom: Add support for High-Frequency PLLs + (HFPLLs) + +HFPLLs are the main frequency source for Krait CPU clocks. Add +support for changing the rate of these PLLs. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-hfpll.c | 260 ++++++++++++++++++++++++++++++++++++++++++ + drivers/clk/qcom/clk-hfpll.h | 54 +++++++++ + 3 files changed, 315 insertions(+) + create mode 100644 drivers/clk/qcom/clk-hfpll.c + create mode 100644 drivers/clk/qcom/clk-hfpll.h + +diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile +index 2cc6039..93fd03f 100644 +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -7,6 +7,7 @@ clk-qcom-y += clk-rcg.o + clk-qcom-y += clk-rcg2.o + clk-qcom-y += clk-branch.o + clk-qcom-y += clk-generic.o ++clk-qcom-y += clk-hfpll.o + clk-qcom-y += reset.o + + obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o +diff --git a/drivers/clk/qcom/clk-hfpll.c b/drivers/clk/qcom/clk-hfpll.c +new file mode 100644 +index 0000000..f8a40a7 +--- /dev/null ++++ b/drivers/clk/qcom/clk-hfpll.c +@@ -0,0 +1,260 @@ ++/* ++ * 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/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. ++ */ ++ mb(); ++ 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 { ++ mb(); ++ udelay(60); ++ } ++ ++ /* Enable PLL output. */ ++ regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL); ++ ++ /* Make sure the enable is done before returning. */ ++ mb(); ++} ++ ++/* 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_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ 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(hw); ++ 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(hw); ++ ++ /* 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); +diff --git a/drivers/clk/qcom/clk-hfpll.h b/drivers/clk/qcom/clk-hfpll.h +new file mode 100644 +index 0000000..48c18d6 +--- /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.7.10.4 + diff --git a/target/linux/ipq806x/patches/0167-clk-qcom-Add-HFPLL-driver.patch b/target/linux/ipq806x/patches/0167-clk-qcom-Add-HFPLL-driver.patch new file mode 100644 index 0000000000..c2c1680ea2 --- /dev/null +++ b/target/linux/ipq806x/patches/0167-clk-qcom-Add-HFPLL-driver.patch @@ -0,0 +1,162 @@ +From 49134da893bc11e833e3d87139c57e3b84e65219 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 19 Jun 2014 18:46:31 -0700 +Subject: [PATCH 167/182] clk: qcom: Add HFPLL driver + +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. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/Kconfig | 8 ++++ + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/hfpll.c | 110 +++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 119 insertions(+) + create mode 100644 drivers/clk/qcom/hfpll.c + +diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig +index cfaa54c..de8ba31 100644 +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -53,3 +53,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. +diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile +index 93fd03f..d0d8e3d 100644 +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -16,3 +16,4 @@ obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o + 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 +diff --git a/drivers/clk/qcom/hfpll.c b/drivers/clk/qcom/hfpll.c +new file mode 100644 +index 0000000..701a377 +--- /dev/null ++++ b/drivers/clk/qcom/hfpll.c +@@ -0,0 +1,110 @@ ++/* ++ * 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 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", ++ .owner = THIS_MODULE, ++ .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.7.10.4 + diff --git a/target/linux/ipq806x/patches/0168-clk-qcom-Add-MSM8960-s-HFPLLs.patch b/target/linux/ipq806x/patches/0168-clk-qcom-Add-MSM8960-s-HFPLLs.patch new file mode 100644 index 0000000000..68fda612eb --- /dev/null +++ b/target/linux/ipq806x/patches/0168-clk-qcom-Add-MSM8960-s-HFPLLs.patch @@ -0,0 +1,122 @@ +From 0a38d7a21ef0e851d025e4e16f096d5579226299 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Mon, 16 Jun 2014 17:44:08 -0700 +Subject: [PATCH 168/182] clk: qcom: Add MSM8960's HFPLLs + +Describe the HFPLLs present on MSM8960 devices. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/gcc-msm8960.c | 82 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 82 insertions(+) + +diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c +index f4ffd91..d04fc99 100644 +--- a/drivers/clk/qcom/gcc-msm8960.c ++++ b/drivers/clk/qcom/gcc-msm8960.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 pll3 = { +@@ -75,6 +76,84 @@ static struct clk_regmap pll8_vote = { + }, + }; + ++static struct hfpll_data hfpll0_data = { ++ .mode_reg = 0x3200, ++ .l_reg = 0x3208, ++ .m_reg = 0x320c, ++ .n_reg = 0x3210, ++ .config_reg = 0x3204, ++ .status_reg = 0x321c, ++ .config_val = 0x7845c665, ++ .droop_reg = 0x3214, ++ .droop_val = 0x0108c000, ++ .min_rate = 600000000UL, ++ .max_rate = 1800000000UL, ++}; ++ ++static struct clk_hfpll hfpll0 = { ++ .d = &hfpll0_data, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .name = "hfpll0", ++ .ops = &clk_ops_hfpll, ++ .flags = CLK_IGNORE_UNUSED, ++ }, ++ .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock), ++}; ++ ++static struct hfpll_data hfpll1_data = { ++ .mode_reg = 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 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 = 0x3400, ++ .l_reg = 0x3408, ++ .m_reg = 0x340c, ++ .n_reg = 0x3410, ++ .config_reg = 0x3404, ++ .status_reg = 0x341c, ++ .config_val = 0x7845c665, ++ .droop_reg = 0x3414, ++ .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, +@@ -2763,6 +2842,9 @@ static struct clk_regmap *gcc_msm8960_clks[] = { + [PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr, + [PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr, + [RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr, ++ [PLL9] = &hfpll0.clkr, ++ [PLL10] = &hfpll1.clkr, ++ [PLL12] = &hfpll_l2.clkr, + }; + + static const struct qcom_reset_map gcc_msm8960_resets[] = { +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0169-clk-qcom-Add-support-for-Krait-clocks.patch b/target/linux/ipq806x/patches/0169-clk-qcom-Add-support-for-Krait-clocks.patch new file mode 100644 index 0000000000..9b76f59508 --- /dev/null +++ b/target/linux/ipq806x/patches/0169-clk-qcom-Add-support-for-Krait-clocks.patch @@ -0,0 +1,203 @@ +From 63ecfef8560631a15ee13129b2778cd4dffbcfe2 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Wed, 18 Jun 2014 14:18:31 -0700 +Subject: [PATCH 169/182] clk: qcom: Add support for Krait clocks + +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 | 121 ++++++++++++++++++++++++++++++++++++++++++ + drivers/clk/qcom/clk-krait.h | 22 ++++++++ + 4 files changed, 148 insertions(+) + create mode 100644 drivers/clk/qcom/clk-krait.c + create mode 100644 drivers/clk/qcom/clk-krait.h + +diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig +index de8ba31..70b6a7c 100644 +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -61,3 +61,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 +diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile +index d0d8e3d..6482165 100644 +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -7,6 +7,7 @@ clk-qcom-y += clk-rcg.o + clk-qcom-y += clk-rcg2.o + clk-qcom-y += clk-branch.o + clk-qcom-y += clk-generic.o ++clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o + clk-qcom-y += clk-hfpll.o + clk-qcom-y += reset.o + +diff --git a/drivers/clk/qcom/clk-krait.c b/drivers/clk/qcom/clk-krait.c +new file mode 100644 +index 0000000..4283426 +--- /dev/null ++++ b/drivers/clk/qcom/clk-krait.c +@@ -0,0 +1,121 @@ ++/* ++ * 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(kpss_clock_reg_lock); ++ ++#define LPL_SHIFT 8 ++static void __kpss_mux_set_sel(struct mux_clk *mux, int sel) ++{ ++ unsigned long flags; ++ u32 regval; ++ ++ spin_lock_irqsave(&kpss_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->priv) { ++ 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(&kpss_clock_reg_lock, flags); ++ ++ /* Wait for switch to complete. */ ++ mb(); ++ udelay(1); ++} ++ ++static int kpss_mux_set_sel(struct mux_clk *mux, int sel) ++{ ++ mux->en_mask = sel; ++ /* Don't touch mux if CPU is off as it won't work */ ++ if (__clk_is_enabled(mux->hw.clk)) ++ __kpss_mux_set_sel(mux, sel); ++ return 0; ++} ++ ++static int kpss_mux_get_sel(struct mux_clk *mux) ++{ ++ u32 sel; ++ ++ sel = krait_get_l2_indirect_reg(mux->offset); ++ sel >>= mux->shift; ++ sel &= mux->mask; ++ mux->en_mask = sel; ++ ++ return sel; ++} ++ ++static int kpss_mux_enable(struct mux_clk *mux) ++{ ++ __kpss_mux_set_sel(mux, mux->en_mask); ++ return 0; ++} ++ ++static void kpss_mux_disable(struct mux_clk *mux) ++{ ++ __kpss_mux_set_sel(mux, mux->safe_sel); ++} ++ ++const struct clk_mux_ops clk_mux_ops_kpss = { ++ .enable = kpss_mux_enable, ++ .disable = kpss_mux_disable, ++ .set_mux_sel = kpss_mux_set_sel, ++ .get_mux_sel = kpss_mux_get_sel, ++}; ++EXPORT_SYMBOL_GPL(clk_mux_ops_kpss); ++ ++/* ++ * The divider can divide by 2, 4, 6 and 8. But we only really need div-2. So ++ * force it to div-2 during handoff and treat it like a fixed div-2 clock. ++ */ ++static int kpss_div2_get_div(struct div_clk *div) ++{ ++ unsigned long flags; ++ u32 regval; ++ int val; ++ ++ spin_lock_irqsave(&kpss_clock_reg_lock, flags); ++ regval = krait_get_l2_indirect_reg(div->offset); ++ val = (regval >> div->shift) & div->mask; ++ regval &= ~(div->mask << div->shift); ++ if (div->priv) ++ regval &= ~(div->mask << (div->shift + LPL_SHIFT)); ++ krait_set_l2_indirect_reg(div->offset, regval); ++ spin_unlock_irqrestore(&kpss_clock_reg_lock, flags); ++ ++ val = (val + 1) * 2; ++ WARN(val != 2, "Divider %s was configured to div-%d instead of 2!\n", ++ __clk_get_name(div->hw.clk), val); ++ ++ return 2; ++} ++ ++const struct clk_div_ops clk_div_ops_kpss_div2 = { ++ .get_div = kpss_div2_get_div, ++}; ++EXPORT_SYMBOL_GPL(clk_div_ops_kpss_div2); +diff --git a/drivers/clk/qcom/clk-krait.h b/drivers/clk/qcom/clk-krait.h +new file mode 100644 +index 0000000..9c3eb38 +--- /dev/null ++++ b/drivers/clk/qcom/clk-krait.h +@@ -0,0 +1,22 @@ ++/* ++ * 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 __SOC_QCOM_CLOCK_KRAIT_H ++#define __SOC_QCOM_CLOCK_KRAIT_H ++ ++#include <linux/clk/msm-clk-generic.h> ++ ++extern const struct clk_mux_ops clk_mux_ops_kpss; ++extern const struct clk_div_ops clk_div_ops_kpss_div2; ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0170-clk-qcom-Add-KPSS-ACC-GCC-driver.patch b/target/linux/ipq806x/patches/0170-clk-qcom-Add-KPSS-ACC-GCC-driver.patch new file mode 100644 index 0000000000..8817bbbf49 --- /dev/null +++ b/target/linux/ipq806x/patches/0170-clk-qcom-Add-KPSS-ACC-GCC-driver.patch @@ -0,0 +1,171 @@ +From 67a9d5a02b178644da624ef9c32b4e6abb2c4f6e Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Wed, 18 Jun 2014 14:25:41 -0700 +Subject: [PATCH 170/182] clk: qcom: Add KPSS ACC/GCC driver + +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. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/Kconfig | 8 +++ + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/kpss-xcc.c | 115 +++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 124 insertions(+) + create mode 100644 drivers/clk/qcom/kpss-xcc.c + +diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig +index 70b6a7c..e9e5360 100644 +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -62,6 +62,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 +diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile +index 6482165..29b2a45 100644 +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -17,4 +17,5 @@ obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o + 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 +diff --git a/drivers/clk/qcom/kpss-xcc.c b/drivers/clk/qcom/kpss-xcc.c +new file mode 100644 +index 0000000..1061668 +--- /dev/null ++++ b/drivers/clk/qcom/kpss-xcc.c +@@ -0,0 +1,115 @@ ++/* 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/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/clk/msm-clk-generic.h> ++ ++static int kpss_xcc_set_mux_sel(struct mux_clk *clk, int sel) ++{ ++ writel_relaxed(sel, clk->base + clk->offset); ++ return 0; ++} ++ ++static int kpss_xcc_get_mux_sel(struct mux_clk *clk) ++{ ++ return readl_relaxed(clk->base + clk->offset); ++} ++ ++static const struct clk_mux_ops kpss_xcc_ops = { ++ .set_mux_sel = kpss_xcc_set_mux_sel, ++ .get_mux_sel = kpss_xcc_get_mux_sel, ++}; ++ ++static const char *aux_parents[] = { ++ "pll8_vote", ++ "pxo", ++}; ++ ++static u8 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; ++ struct mux_clk *mux_clk; ++ struct clk_init_data init = { ++ .parent_names = aux_parents, ++ .num_parents = 2, ++ .ops = &clk_ops_gen_mux, ++ }; ++ ++ 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); ++ ++ mux_clk = devm_kzalloc(&pdev->dev, sizeof(*mux_clk), GFP_KERNEL); ++ if (!mux_clk) ++ return -ENOMEM; ++ ++ mux_clk->mask = 0x3; ++ mux_clk->parent_map = aux_parent_map; ++ mux_clk->ops = &kpss_xcc_ops; ++ mux_clk->base = base; ++ mux_clk->hw.init = &init; ++ ++ if (id->data) { ++ if (of_property_read_string_index(pdev->dev.of_node, ++ "clock-output-names", 0, &init.name)) ++ return -ENODEV; ++ mux_clk->offset = 0x14; ++ } else { ++ init.name = "acpu_l2_aux"; ++ mux_clk->offset = 0x28; ++ } ++ ++ clk = devm_clk_register(&pdev->dev, &mux_clk->hw); ++ ++ return PTR_ERR_OR_ZERO(clk); ++} ++ ++static struct platform_driver kpss_xcc_driver = { ++ .probe = kpss_xcc_driver_probe, ++ .driver = { ++ .name = "kpss-xcc", ++ .of_match_table = kpss_xcc_match_table, ++ .owner = THIS_MODULE, ++ }, ++}; ++module_platform_driver(kpss_xcc_driver); ++ ++MODULE_LICENSE("GPL v2"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0171-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches/0171-clk-qcom-Add-Krait-clock-controller-driver.patch new file mode 100644 index 0000000000..8fa83dfdc2 --- /dev/null +++ b/target/linux/ipq806x/patches/0171-clk-qcom-Add-Krait-clock-controller-driver.patch @@ -0,0 +1,420 @@ +From 6912e27d97ba5671e8c2434bed0ebd23fde5e13d Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Wed, 18 Jun 2014 14:29:29 -0700 +Subject: [PATCH 171/182] clk: qcom: Add Krait clock controller driver + +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. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/Kconfig | 8 + + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/krait-cc.c | 364 +++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 373 insertions(+) + create mode 100644 drivers/clk/qcom/krait-cc.c + +diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig +index e9e5360..7418108 100644 +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -70,6 +70,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 +diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile +index 29b2a45..1b88abe 100644 +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -19,3 +19,4 @@ 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 ++obj-$(CONFIG_KRAITCC) += krait-cc.o +diff --git a/drivers/clk/qcom/krait-cc.c b/drivers/clk/qcom/krait-cc.c +new file mode 100644 +index 0000000..90985ea +--- /dev/null ++++ b/drivers/clk/qcom/krait-cc.c +@@ -0,0 +1,364 @@ ++/* 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/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 <asm/smp_plat.h> ++ ++#include "clk-krait.h" ++ ++DEFINE_FIXED_DIV_CLK(acpu_aux, 2, "gpll0_vote"); ++ ++static u8 sec_mux_map[] = { ++ 2, ++ 0, ++}; ++ ++static u8 pri_mux_map[] = { ++ 1, ++ 2, ++ 0, ++}; ++ ++static int ++krait_add_div(struct device *dev, int id, const char *s, unsigned offset) ++{ ++ struct div_clk *div; ++ struct clk_init_data init = { ++ .num_parents = 1, ++ .ops = &clk_ops_div, ++ .flags = CLK_SET_RATE_PARENT, ++ }; ++ const char *p_names[1]; ++ struct clk *clk; ++ ++ div = devm_kzalloc(dev, sizeof(*dev), GFP_KERNEL); ++ if (!div) ++ return -ENOMEM; ++ ++ div->data.div = 2; ++ div->data.min_div = 2; ++ div->data.max_div = 2; ++ div->ops = &clk_div_ops_kpss_div2; ++ div->mask = 0x3; ++ div->shift = 6; ++ div->priv = (void *)(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 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 = &clk_ops_gen_mux, ++ .flags = CLK_SET_RATE_PARENT, ++ }; ++ struct clk *clk; ++ ++ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); ++ if (!mux) ++ return -ENOMEM; ++ ++ mux->offset = offset; ++ mux->priv = (void *)(id >= 0); ++ mux->has_safe_parent = true; ++ mux->safe_sel = 2; ++ mux->ops = &clk_mux_ops_kpss; ++ 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 mux_clk *mux; ++ const char *p_names[3]; ++ struct clk_init_data init = { ++ .parent_names = p_names, ++ .num_parents = ARRAY_SIZE(p_names), ++ .ops = &clk_ops_gen_mux, ++ .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->ops = &clk_mux_ops_kpss; ++ mux->mask = 0x3; ++ mux->shift = 0; ++ mux->offset = offset; ++ mux->priv = (void *)(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 i, cpu; ++ struct clk *clk; ++ struct clk **clks; ++ struct clk *l2_pri_mux_clk; ++ ++ id = of_match_device(krait_cc_match_table, &pdev->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 = devm_clk_register(dev, &acpu_aux.hw); ++ 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(i) { ++ cpu = cpu_logical_map(i); ++ 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(i) { ++ cpu = cpu_logical_map(i); ++ 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(i) { ++ cpu = cpu_logical_map(i); ++ clk = clks[cpu]; ++ cur_rate = clk_get_rate(clk); ++ if (cur_rate == 1) { ++ pr_info("CPU%d @ QSB rate. Forcing new rate.\n", i); ++ 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", i, 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 = "clock-krait", ++ .of_match_table = krait_cc_match_table, ++ .owner = THIS_MODULE, ++ }, ++}; ++module_platform_driver(krait_cc_driver); ++ ++MODULE_DESCRIPTION("Krait CPU Clock Driver"); ++MODULE_LICENSE("GPL v2"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0172-cpufreq-Add-a-cpufreq-krait-based-on-cpufreq-cpu0.patch b/target/linux/ipq806x/patches/0172-cpufreq-Add-a-cpufreq-krait-based-on-cpufreq-cpu0.patch new file mode 100644 index 0000000000..3150a4dfb8 --- /dev/null +++ b/target/linux/ipq806x/patches/0172-cpufreq-Add-a-cpufreq-krait-based-on-cpufreq-cpu0.patch @@ -0,0 +1,255 @@ +From 5cf343c60d7557aefb468603065cac5062f11b8d Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 30 May 2014 16:36:11 -0700 +Subject: [PATCH 172/182] 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 + +diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig +index 4b029c0..4051528 100644 +--- a/drivers/cpufreq/Kconfig ++++ b/drivers/cpufreq/Kconfig +@@ -194,6 +194,19 @@ config GENERIC_CPUFREQ_CPU0 + + 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" +diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile +index 7494565..f6f4485 100644 +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -12,6 +12,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o + obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o + + obj-$(CONFIG_GENERIC_CPUFREQ_CPU0) += cpufreq-cpu0.o ++obj-$(CONFIG_GENERIC_CPUFREQ_KRAIT) += cpufreq-krait.o + + ################################################################################## + # x86 drivers. +diff --git a/drivers/cpufreq/cpufreq-krait.c b/drivers/cpufreq/cpufreq-krait.c +new file mode 100644 +index 0000000..7b38b9c +--- /dev/null ++++ b/drivers/cpufreq/cpufreq-krait.c +@@ -0,0 +1,190 @@ ++/* ++ * 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/slab.h> ++#include <linux/thermal.h> ++ ++static unsigned int transition_latency; ++ ++static struct device *cpu_dev; ++static DEFINE_PER_CPU(struct clk *, krait_cpu_clks); ++static struct cpufreq_frequency_table *freq_table; ++static struct thermal_cooling_device *cdev; ++ ++static int krait_set_target(struct cpufreq_policy *policy, unsigned int index) ++{ ++ unsigned long volt = 0, volt_old = 0; ++ unsigned int old_freq, new_freq; ++ long freq_Hz, freq_exact; ++ int ret; ++ struct clk *cpu_clk; ++ ++ 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; ++ ++ 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); ++ ++ ret = clk_set_rate(cpu_clk, freq_exact); ++ if (ret) ++ pr_err("failed to set clock rate: %d\n", ret); ++ ++ 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; ++ int ret; ++ unsigned int cpu; ++ struct device *dev; ++ struct clk *clk; ++ ++ 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; ++ } ++ ++ for_each_possible_cpu(cpu) { ++ dev = get_cpu_device(cpu); ++ if (!dev) { ++ pr_err("failed to get krait device\n"); ++ ret = -ENOENT; ++ goto out_put_node; ++ } ++ per_cpu(krait_cpu_clks, cpu) = clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(clk)) { ++ ret = PTR_ERR(clk); ++ goto out_put_node; ++ } ++ } ++ ++ ret = of_init_opp_table(cpu_dev); ++ if (ret) { ++ pr_err("failed to init OPP table: %d\n", ret); ++ goto out_put_node; ++ } ++ ++ 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; ++ } ++ ++ if (of_property_read_u32(np, "clock-latency", &transition_latency)) ++ transition_latency = CPUFREQ_ETERNAL; ++ ++ 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: ++ 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); ++ ++ 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"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0173-cpufreq-Add-module-to-register-cpufreq-krait-device.patch b/target/linux/ipq806x/patches/0173-cpufreq-Add-module-to-register-cpufreq-krait-device.patch new file mode 100644 index 0000000000..c43221070c --- /dev/null +++ b/target/linux/ipq806x/patches/0173-cpufreq-Add-module-to-register-cpufreq-krait-device.patch @@ -0,0 +1,104 @@ +From f1db56284b01b1212a211023dcaa7846fd07d0ec Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 30 May 2014 17:16:53 -0700 +Subject: [PATCH 173/182] cpufreq: Add module to register cpufreq-krait device + +Register a cpufreq-krait device whenever we detect that a +"qcom,krait" compatible CPU is present in DT. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/cpufreq/Kconfig.arm | 8 +++++++ + drivers/cpufreq/Makefile | 1 + + drivers/cpufreq/qcom-cpufreq.c | 48 ++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 57 insertions(+) + create mode 100644 drivers/cpufreq/qcom-cpufreq.c + +diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm +index 3129749..6ae884d 100644 +--- a/drivers/cpufreq/Kconfig.arm ++++ b/drivers/cpufreq/Kconfig.arm +@@ -123,6 +123,14 @@ config ARM_OMAP2PLUS_CPUFREQ + depends on ARCH_OMAP2PLUS + default ARCH_OMAP2PLUS + ++config ARM_QCOM_CPUFREQ ++ tristate "Qualcomm based" ++ depends on ARCH_QCOM ++ help ++ This adds the CPUFreq driver for Qualcomm SoC based boards. ++ ++ If in doubt, say N. ++ + config ARM_S3C_CPUFREQ + bool + help +diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile +index f6f4485..f5d18a3 100644 +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -60,6 +60,7 @@ obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o + obj-$(CONFIG_ARM_INTEGRATOR) += integrator-cpufreq.o + obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o + obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o ++obj-$(CONFIG_ARM_QCOM_CPUFREQ) += qcom-cpufreq.o + obj-$(CONFIG_PXA25x) += pxa2xx-cpufreq.o + obj-$(CONFIG_PXA27x) += pxa2xx-cpufreq.o + obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o +diff --git a/drivers/cpufreq/qcom-cpufreq.c b/drivers/cpufreq/qcom-cpufreq.c +new file mode 100644 +index 0000000..71f4387 +--- /dev/null ++++ b/drivers/cpufreq/qcom-cpufreq.c +@@ -0,0 +1,48 @@ ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/cpu.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/err.h> ++ ++static int qcom_cpufreq_driver_init(void) ++{ ++ struct platform_device_info devinfo = { .name = "cpufreq-krait", }; ++ struct device *cpu_dev; ++ struct device_node *np; ++ struct platform_device *pdev; ++ ++ 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); ++ ++ pdev = platform_device_register_full(&devinfo); ++ ++ return PTR_ERR_OR_ZERO(pdev); ++} ++module_init(qcom_cpufreq_driver_init); ++ ++MODULE_DESCRIPTION("Qualcomm CPUfreq driver"); ++MODULE_LICENSE("GPL v2"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0174-clk-qcom-Add-HFPLLs-to-IPQ806X-driver.patch b/target/linux/ipq806x/patches/0174-clk-qcom-Add-HFPLLs-to-IPQ806X-driver.patch new file mode 100644 index 0000000000..38dbd6526e --- /dev/null +++ b/target/linux/ipq806x/patches/0174-clk-qcom-Add-HFPLLs-to-IPQ806X-driver.patch @@ -0,0 +1,121 @@ +From b9b3815f2a71af88ca68d3524ee4d9b6b4739257 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Wed, 18 Jun 2014 15:57:06 -0700 +Subject: [PATCH 174/182] clk: qcom: Add HFPLLs to IPQ806X driver + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/gcc-ipq806x.c | 83 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 83 insertions(+) + +diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c +index d80dc69..83a73cb 100644 +--- 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, +@@ -2878,6 +2958,9 @@ static struct clk_regmap *gcc_ipq806x_clks[] = { + [NSSTCM_CLK_SRC] = &nss_tcm_src.clkr, + [NSSTCM_CLK] = &nss_tcm_clk.clkr, + [NSS_CORE_CLK] = &nss_core_clk, ++ [PLL9] = &hfpll0.clkr, ++ [PLL10] = &hfpll1.clkr, ++ [PLL12] = &hfpll_l2.clkr, + }; + + static const struct qcom_reset_map gcc_ipq806x_resets[] = { +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0175-ARM-dts-ipq8064-Add-necessary-DT-data-for-Krait-cpuf.patch b/target/linux/ipq806x/patches/0175-ARM-dts-ipq8064-Add-necessary-DT-data-for-Krait-cpuf.patch new file mode 100644 index 0000000000..6fea560286 --- /dev/null +++ b/target/linux/ipq806x/patches/0175-ARM-dts-ipq8064-Add-necessary-DT-data-for-Krait-cpuf.patch @@ -0,0 +1,96 @@ +From 258a3ea23ec048603debe1621c681b0cc733f236 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Wed, 18 Jun 2014 16:04:37 -0700 +Subject: [PATCH 175/182] ARM: dts: ipq8064: Add necessary DT data for Krait + cpufreq + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + arch/arm/boot/dts/qcom-ipq8064.dtsi | 45 +++++++++++++++++++++++++++++++++++ + 1 file changed, 45 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +index 6be6ac9..97e4c3d 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -23,6 +23,22 @@ + next-level-cache = <&L2>; + qcom,acc = <&acc0>; + qcom,saw = <&saw0>; ++ clocks = <&kraitcc 0>; ++ clock-names = "cpu"; ++ operating-points = < ++ /* kHz ignored */ ++ 1400000 1000000 ++ 1200000 1000000 ++ 1000000 1000000 ++ 800000 1000000 ++ 600000 1000000 ++ 384000 1000000 ++ >; ++ clock-latency = <100000>; ++ ++ cooling-min-state = <0>; ++ cooling-max-state = <10>; ++ #cooling-cells = <2>; + }; + + cpu@1 { +@@ -33,6 +49,22 @@ + next-level-cache = <&L2>; + qcom,acc = <&acc1>; + qcom,saw = <&saw1>; ++ clocks = <&kraitcc 1>; ++ clock-names = "cpu"; ++ operating-points = < ++ /* kHz ignored */ ++ 1400000 1000000 ++ 1200000 1000000 ++ 1000000 1000000 ++ 800000 1000000 ++ 600000 1000000 ++ 384000 1000000 ++ >; ++ clock-latency = <100000>; ++ ++ cooling-min-state = <0>; ++ cooling-max-state = <10>; ++ #cooling-cells = <2>; + }; + + L2: l2-cache { +@@ -46,6 +78,11 @@ + interrupts = <1 10 0x304>; + }; + ++ kraitcc: clock-controller { ++ compatible = "qcom,krait-cc-v1"; ++ #clock-cells = <1>; ++ }; ++ + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; +@@ -102,11 +139,19 @@ + 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 { ++ compatible = "qcom,kpss-gcc"; ++ reg = <0x2011000 0x1000>; ++ clock-output-names = "acpu_l2_aux"; + }; + + saw0: regulator@2089000 { +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0176-ARM-qcom_defconfig-Enable-CPUfreq-options.patch b/target/linux/ipq806x/patches/0176-ARM-qcom_defconfig-Enable-CPUfreq-options.patch new file mode 100644 index 0000000000..e5656020b7 --- /dev/null +++ b/target/linux/ipq806x/patches/0176-ARM-qcom_defconfig-Enable-CPUfreq-options.patch @@ -0,0 +1,44 @@ +From e8f4d4bf1e4974bf8d17c6702fa4c410637216f3 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Mon, 30 Jun 2014 10:50:14 -0700 +Subject: [PATCH 176/182] ARM: qcom_defconfig: Enable CPUfreq options + +Enable required options to allow the CPUfreq driver to probe on +Krait based platforms. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + arch/arm/configs/qcom_defconfig | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig +index 3d55d79..3bc72eb 100644 +--- a/arch/arm/configs/qcom_defconfig ++++ b/arch/arm/configs/qcom_defconfig +@@ -31,6 +31,13 @@ CONFIG_HIGHPTE=y + CONFIG_CLEANCACHE=y + CONFIG_ARM_APPENDED_DTB=y + CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_GENERIC_CPUFREQ_KRAIT=y ++CONFIG_ARM_QCOM_CPUFREQ=y + CONFIG_CPU_IDLE=y + CONFIG_VFP=y + CONFIG_NEON=y +@@ -149,6 +156,9 @@ CONFIG_IPQ_GCC_806X=y + CONFIG_MSM_GCC_8660=y + CONFIG_MSM_MMCC_8960=y + CONFIG_MSM_MMCC_8974=y ++CONFIG_QCOM_HFPLL=y ++CONFIG_KPSS_XCC=y ++CONFIG_KRAITCC=y + CONFIG_MSM_IOMMU=y + CONFIG_PHY_QCOM_IPQ806X_SATA=y + CONFIG_EXT2_FS=y +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0177-dmaengine-Add-QCOM-ADM-DMA-driver.patch b/target/linux/ipq806x/patches/0177-dmaengine-Add-QCOM-ADM-DMA-driver.patch new file mode 100644 index 0000000000..d7d301dede --- /dev/null +++ b/target/linux/ipq806x/patches/0177-dmaengine-Add-QCOM-ADM-DMA-driver.patch @@ -0,0 +1,931 @@ +From 8984e3fc6db029479d6aa78882b39235379aebff Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Wed, 14 May 2014 13:45:07 -0500 +Subject: [PATCH 177/182] dmaengine: Add QCOM ADM DMA driver + +Add the DMA engine driver for the QCOM Application Data Mover (ADM) DMA +controller found in the MSM8960 and IPQ/APQ8064 platforms. + +The ADM supports both memory to memory transactions and memory +to/from peripheral device transactions. The controller also provides flow +control capabilities for transactions to/from peripheral devices. + +The initial release of this driver supports slave transfers to/from peripherals +and also incorporates CRCI (client rate control interface) flow control. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + drivers/dma/Kconfig | 10 + + drivers/dma/Makefile | 1 + + drivers/dma/qcom_adm.c | 871 ++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 882 insertions(+) + create mode 100644 drivers/dma/qcom_adm.c + +diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig +index f87cef9..79155fa 100644 +--- a/drivers/dma/Kconfig ++++ b/drivers/dma/Kconfig +@@ -410,4 +410,14 @@ config QCOM_BAM_DMA + Enable support for the QCOM BAM DMA controller. This controller + provides DMA capabilities for a variety of on-chip devices. + ++config QCOM_ADM ++ tristate "Qualcomm ADM support" ++ depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM) ++ select DMA_ENGINE ++ select DMA_VIRTUAL_CHANNELS ++ ---help--- ++ Enable support for the Qualcomm ADM DMA controller. This controller ++ provides DMA capabilities for both general purpose and on-chip ++ peripheral devices. ++ + endif +diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile +index 5150c82..4a4f521 100644 +--- a/drivers/dma/Makefile ++++ b/drivers/dma/Makefile +@@ -46,3 +46,4 @@ obj-$(CONFIG_K3_DMA) += k3dma.o + obj-$(CONFIG_MOXART_DMA) += moxart-dma.o + obj-$(CONFIG_FSL_EDMA) += fsl-edma.o + obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o ++obj-$(CONFIG_QCOM_ADM) += qcom_adm.o +diff --git a/drivers/dma/qcom_adm.c b/drivers/dma/qcom_adm.c +new file mode 100644 +index 0000000..035f606 +--- /dev/null ++++ b/drivers/dma/qcom_adm.c +@@ -0,0 +1,871 @@ ++/* ++ * 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/io.h> ++#include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/interrupt.h> ++#include <linux/dma-mapping.h> ++#include <linux/scatterlist.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/of_irq.h> ++#include <linux/of_dma.h> ++#include <linux/reset.h> ++#include <linux/clk.h> ++#include <linux/dmaengine.h> ++ ++#include "dmaengine.h" ++#include "virt-dma.h" ++ ++/* ADM registers - calculated from channel number and security domain */ ++#define HI_CH_CMD_PTR(chan, ee) (4*chan + 0x20800*ee) ++#define HI_CH_RSLT(chan, ee) (0x40 + 4*chan + 0x20800*ee) ++#define HI_CH_FLUSH_STATE0(chan, ee) (0x80 + 4*chan + 0x20800*ee) ++#define HI_CH_FLUSH_STATE1(chan, ee) (0xc0 + 4*chan + 0x20800*ee) ++#define HI_CH_FLUSH_STATE2(chan, ee) (0x100 + 4*chan + 0x20800*ee) ++#define HI_CH_FLUSH_STATE3(chan, ee) (0x140 + 4*chan + 0x20800*ee) ++#define HI_CH_FLUSH_STATE4(chan, ee) (0x180 + 4*chan + 0x20800*ee) ++#define HI_CH_FLUSH_STATE5(chan, ee) (0x1c0 + 4*chan + 0x20800*ee) ++#define HI_CH_STATUS_SD(chan, ee) (0x200 + 4*chan + 0x20800*ee) ++#define HI_CH_CONF(chan) (0x240 + 4*chan) ++#define HI_CH_RSLT_CONF(chan, ee) (0x300 + 4*chan + 0x20800*ee) ++#define HI_SEC_DOMAIN_IRQ_STATUS(ee) (0x380 + 0x20800*ee) ++#define HI_CI_CONF(ci) (0x390 + 4*ci) ++#define HI_CRCI_CONF0 0x3d0 ++#define HI_CRCI_CONF1 0x3d4 ++#define HI_GP_CTL 0x3d8 ++#define HI_CRCI_CTL(chan, ee) (0x400 + 0x4*chan + 0x20800*ee) ++ ++/* channel status */ ++#define CH_STATUS_VALID BIT(1) ++ ++/* channel result */ ++#define CH_RSLT_VALID BIT(31) ++#define CH_RSLT_ERR BIT(3) ++#define CH_RSLT_FLUSH BIT(2) ++#define CH_RSLT_TPD BIT(1) ++ ++/* channel conf */ ++#define CH_CONF_MPU_DISABLE BIT(11) ++#define CH_CONF_PERM_MPU_CONF BIT(9) ++#define CH_CONF_FLUSH_RSLT_EN BIT(8) ++#define CH_CONF_FORCE_RSLT_EN BIT(7) ++#define CH_CONF_IRQ_EN BIT(6) ++ ++/* channel result conf */ ++#define CH_RSLT_CONF_FLUSH_EN BIT(1) ++#define CH_RSLT_CONF_IRQ_EN BIT(0) ++ ++/* CRCI CTL */ ++#define CRCI_CTL_RST BIT(17) ++ ++/* CI configuration */ ++#define CI_RANGE_END(x) (x << 24) ++#define CI_RANGE_START(x) (x << 16) ++#define CI_BURST_4_WORDS 0x4 ++#define CI_BURST_8_WORDS 0x8 ++ ++/* GP CTL */ ++#define GP_CTL_LP_EN BIT(12) ++#define GP_CTL_LP_CNT(x) (x << 8) ++ ++/* Command pointer list entry */ ++#define CPLE_LP BIT(31) ++ ++/* Command list entry */ ++#define CMD_LC BIT(31) ++#define CMD_DST_CRCI(n) (((n) & 0xf) << 7) ++#define CMD_SRC_CRCI(n) (((n) & 0xf) << 3) ++ ++#define CMD_TYPE_SINGLE 0x0 ++#define CMD_TYPE_BOX 0x3 ++ ++#define ADM_DESC_ALIGN 8 ++#define ADM_MAX_XFER (SZ_64K-1) ++#define ADM_MAX_ROWS (SZ_64K-1) ++ ++/* Command Pointer List Entry */ ++#define CMD_LP BIT(31) ++#define CMD_PT_MASK (0x3 << 29) ++#define CMD_ADDR_MASK 0x3fffffff ++ ++struct adm_desc_hw { ++ u32 cmd; ++ u32 src_addr; ++ u32 dst_addr; ++ u32 row_len; ++ u32 num_rows; ++ u32 row_offset; ++}; ++ ++struct adm_cmd_ptr_list { ++ u32 cple; /* command ptr list entry */ ++ struct adm_desc_hw desc[0]; ++}; ++ ++struct adm_async_desc { ++ struct virt_dma_desc vd; ++ struct adm_device *adev; ++ ++ size_t length; ++ enum dma_transfer_direction dir; ++ dma_addr_t dma_addr; ++ size_t dma_len; ++ ++ struct adm_cmd_ptr_list *cpl; ++ u32 num_desc; ++}; ++ ++struct adm_chan { ++ struct virt_dma_chan vc; ++ struct adm_device *adev; ++ ++ /* parsed from DT */ ++ u32 id; /* channel id */ ++ u32 crci; /* CRCI to be used for transfers */ ++ u32 blk_size; /* block size for CRCI, default 16 byte */ ++ ++ struct adm_async_desc *curr_txd; ++ struct dma_slave_config slave; ++ struct list_head node; ++ ++ int error; ++ int initialized; ++}; ++ ++static inline struct adm_chan *to_adm_chan(struct dma_chan *common) ++{ ++ return container_of(common, struct adm_chan, vc.chan); ++} ++ ++struct adm_device { ++ void __iomem *regs; ++ struct device *dev; ++ struct dma_device common; ++ struct device_dma_parameters dma_parms; ++ struct adm_chan *channels; ++ u32 num_channels; ++ ++ u32 ee; ++ ++ struct clk *core_clk; ++ struct clk *iface_clk; ++ ++ struct reset_control *clk_reset; ++ struct reset_control *c0_reset; ++ struct reset_control *c1_reset; ++ struct reset_control *c2_reset; ++ int irq; ++}; ++ ++/** ++ * adm_alloc_chan - Allocates channel resources for DMA channel ++ * ++ * This function is effectively a stub, as we don't need to setup any resources ++ */ ++static int adm_alloc_chan(struct dma_chan *chan) ++{ ++ return 0; ++} ++ ++/** ++ * adm_free_chan - Frees dma resources associated with the specific channel ++ * ++ * Free all allocated descriptors associated with this channel ++ * ++ */ ++static void adm_free_chan(struct dma_chan *chan) ++{ ++ /* free all queued descriptors */ ++ vchan_free_chan_resources(to_virt_chan(chan)); ++} ++ ++/** ++ * adm_prep_slave_sg - Prep slave sg transaction ++ * ++ * @chan: dma channel ++ * @sgl: scatter gather list ++ * @sg_len: length of sg ++ * @direction: DMA transfer direction ++ * @flags: DMA flags ++ * @context: transfer context (unused) ++ */ ++static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan, ++ struct scatterlist *sgl, unsigned int sg_len, ++ enum dma_transfer_direction direction, unsigned long flags, ++ void *context) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ struct adm_device *adev = achan->adev; ++ struct adm_async_desc *async_desc; ++ struct scatterlist *sg; ++ u32 i, rows, num_desc = 0, idx = 0, desc_offset; ++ struct adm_desc_hw *desc; ++ struct adm_cmd_ptr_list *cpl; ++ u32 burst = ADM_MAX_XFER; ++ ++ ++ if (!is_slave_direction(direction)) { ++ dev_err(adev->dev, "invalid dma direction\n"); ++ return NULL; ++ } ++ ++ /* if using CRCI flow control, validate burst settings */ ++ if (achan->slave.device_fc) { ++ burst = (direction == DMA_MEM_TO_DEV) ? ++ achan->slave.dst_maxburst : ++ achan->slave.src_maxburst; ++ ++ if (!burst) { ++ dev_err(adev->dev, "invalid burst value w/ crci: %d\n", ++ burst); ++ return ERR_PTR(-EINVAL); ++ } ++ } ++ ++ /* iterate through sgs and compute allocation size of structures */ ++ for_each_sg(sgl, sg, sg_len, i) { ++ ++ /* calculate boxes using burst */ ++ rows = DIV_ROUND_UP(sg_dma_len(sg), burst); ++ num_desc += DIV_ROUND_UP(rows, ADM_MAX_ROWS); ++ ++ /* flow control requires length as a multiple of burst */ ++ if (achan->slave.device_fc && (sg_dma_len(sg) % burst)) { ++ dev_err(adev->dev, "length is not multiple of burst\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ } ++ ++ async_desc = kzalloc(sizeof(*async_desc), GFP_NOWAIT); ++ if (!async_desc) ++ return ERR_PTR(-ENOMEM); ++ ++ async_desc->dma_len = num_desc * sizeof(*desc) + sizeof(*cpl) + ++ ADM_DESC_ALIGN; ++ async_desc->cpl = dma_alloc_writecombine(adev->dev, async_desc->dma_len, ++ &async_desc->dma_addr, GFP_NOWAIT); ++ ++ if (!async_desc->cpl) { ++ kfree(async_desc); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ async_desc->num_desc = num_desc; ++ async_desc->adev = adev; ++ cpl = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN); ++ desc = PTR_ALIGN(&cpl->desc[0], ADM_DESC_ALIGN); ++ desc_offset = (u32)desc - (u32)async_desc->cpl; ++ ++ /* init cmd list */ ++ cpl->cple |= CPLE_LP; ++ cpl->cple |= (async_desc->dma_addr + desc_offset) >> 3; ++ ++ for_each_sg(sgl, sg, sg_len, i) { ++ unsigned int remainder = sg_dma_len(sg); ++ unsigned int curr_offset = 0; ++ unsigned int row_len; ++ ++ do { ++ desc[idx].cmd = CMD_TYPE_BOX; ++ desc[idx].row_offset = 0; ++ ++ if (direction == DMA_DEV_TO_MEM) { ++ desc[idx].dst_addr = sg_dma_address(sg) + ++ curr_offset; ++ desc[idx].src_addr = achan->slave.src_addr; ++ desc[idx].cmd |= CMD_SRC_CRCI(achan->crci); ++ desc[idx].row_offset = burst; ++ } else { ++ desc[idx].src_addr = sg_dma_address(sg) + ++ curr_offset; ++ desc[idx].dst_addr = achan->slave.dst_addr; ++ desc[idx].cmd |= CMD_DST_CRCI(achan->crci); ++ desc[idx].row_offset = burst << 16; ++ } ++ ++ if (remainder < burst) { ++ rows = 1; ++ row_len = remainder; ++ } else { ++ rows = remainder / burst; ++ rows = min_t(u32, rows, ADM_MAX_ROWS); ++ row_len = burst; ++ } ++ ++ desc[idx].num_rows = rows << 16 | rows; ++ desc[idx].row_len = row_len << 16 | row_len; ++ ++ remainder -= row_len * rows; ++ async_desc->length += row_len * rows; ++ curr_offset += row_len * rows; ++ ++ idx++; ++ } while (remainder > 0); ++ } ++ ++ /* set last command flag */ ++ desc[idx - 1].cmd |= CMD_LC; ++ ++ /* reset channel error */ ++ achan->error = 0; ++ ++ return vchan_tx_prep(&achan->vc, &async_desc->vd, flags); ++} ++ ++/** ++ * adm_slave_config - set slave configuration for channel ++ * @chan: dma channel ++ * @cfg: slave configuration ++ * ++ * Sets slave configuration for channel ++ * ++ */ ++static int adm_slave_config(struct adm_chan *achan, ++ struct dma_slave_config *cfg) ++{ ++ int ret = 0; ++ u32 burst; ++ struct adm_device *adev = achan->adev; ++ ++ memcpy(&achan->slave, cfg, sizeof(*cfg)); ++ ++ /* set channel CRCI burst, if applicable */ ++ if (achan->crci) { ++ burst = max_t(u32, cfg->src_maxburst, cfg->dst_maxburst); ++ ++ switch (burst) { ++ case 16: ++ achan->blk_size = 0; ++ break; ++ case 32: ++ achan->blk_size = 1; ++ break; ++ case 64: ++ achan->blk_size = 2; ++ break; ++ case 128: ++ achan->blk_size = 3; ++ break; ++ case 192: ++ achan->blk_size = 4; ++ break; ++ case 256: ++ achan->blk_size = 5; ++ break; ++ default: ++ achan->slave.src_maxburst = 0; ++ achan->slave.dst_maxburst = 0; ++ ret = -EINVAL; ++ break; ++ } ++ ++ if (!ret) ++ writel(achan->blk_size, ++ adev->regs + HI_CRCI_CTL(achan->id, adev->ee)); ++ } ++ ++ return ret; ++} ++ ++/** ++ * adm_terminate_all - terminate all transactions on a channel ++ * @achan: adm dma channel ++ * ++ * Dequeues and frees all transactions, aborts current transaction ++ * No callbacks are done ++ * ++ */ ++static void adm_terminate_all(struct adm_chan *achan) ++{ ++ struct adm_device *adev = achan->adev; ++ unsigned long flags; ++ LIST_HEAD(head); ++ ++ /* send flush command to terminate current transaction */ ++ writel_relaxed(0x0, ++ adev->regs + HI_CH_FLUSH_STATE0(achan->id, adev->ee)); ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ vchan_get_all_descriptors(&achan->vc, &head); ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++ ++ vchan_dma_desc_free_list(&achan->vc, &head); ++} ++ ++/** ++ * adm_control - DMA device control ++ * @chan: dma channel ++ * @cmd: control cmd ++ * @arg: cmd argument ++ * ++ * Perform DMA control command ++ * ++ */ ++static int adm_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, ++ unsigned long arg) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ unsigned long flag; ++ int ret = 0; ++ ++ switch (cmd) { ++ case DMA_SLAVE_CONFIG: ++ spin_lock_irqsave(&achan->vc.lock, flag); ++ ret = adm_slave_config(achan, (struct dma_slave_config *)arg); ++ spin_unlock_irqrestore(&achan->vc.lock, flag); ++ break; ++ ++ case DMA_TERMINATE_ALL: ++ adm_terminate_all(achan); ++ break; ++ ++ default: ++ ret = -ENXIO; ++ break; ++ }; ++ ++ return ret; ++} ++ ++/** ++ * adm_start_dma - start next transaction ++ * @achan - ADM dma channel ++ */ ++static void adm_start_dma(struct adm_chan *achan) ++{ ++ struct virt_dma_desc *vd = vchan_next_desc(&achan->vc); ++ struct adm_device *adev = achan->adev; ++ struct adm_async_desc *async_desc; ++ struct adm_desc_hw *desc; ++ struct adm_cmd_ptr_list *cpl; ++ ++ lockdep_assert_held(&achan->vc.lock); ++ ++ if (!vd) ++ return; ++ ++ list_del(&vd->node); ++ ++ /* write next command list out to the CMD FIFO */ ++ async_desc = container_of(vd, struct adm_async_desc, vd); ++ achan->curr_txd = async_desc; ++ ++ cpl = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN); ++ desc = PTR_ALIGN(&cpl->desc[0], ADM_DESC_ALIGN); ++ ++ if (!achan->initialized) { ++ /* enable interrupts */ ++ writel(CH_CONF_IRQ_EN | CH_CONF_FLUSH_RSLT_EN | ++ CH_CONF_FORCE_RSLT_EN | CH_CONF_PERM_MPU_CONF | ++ CH_CONF_MPU_DISABLE, ++ adev->regs + HI_CH_CONF(achan->id)); ++ ++ writel(CH_RSLT_CONF_IRQ_EN | CH_RSLT_CONF_FLUSH_EN, ++ adev->regs + HI_CH_RSLT_CONF(achan->id, adev->ee)); ++ ++ if (achan->crci) ++ writel(achan->blk_size, adev->regs + ++ HI_CRCI_CTL(achan->crci, adev->ee)); ++ ++ achan->initialized = 1; ++ } ++ ++ /* make sure IRQ enable doesn't get reordered */ ++ wmb(); ++ ++ /* write next command list out to the CMD FIFO */ ++ writel(round_up(async_desc->dma_addr, ADM_DESC_ALIGN) >> 3, ++ adev->regs + HI_CH_CMD_PTR(achan->id, adev->ee)); ++} ++ ++/** ++ * adm_dma_irq - irq handler for ADM controller ++ * @irq: IRQ of interrupt ++ * @data: callback data ++ * ++ * IRQ handler for the bam controller ++ */ ++static irqreturn_t adm_dma_irq(int irq, void *data) ++{ ++ struct adm_device *adev = data; ++ u32 srcs, i; ++ struct adm_async_desc *async_desc; ++ unsigned long flags; ++ ++ srcs = readl_relaxed(adev->regs + ++ HI_SEC_DOMAIN_IRQ_STATUS(adev->ee)); ++ ++ for (i = 0; i < 16; i++) { ++ struct adm_chan *achan = &adev->channels[i]; ++ u32 status, result; ++ if (srcs & BIT(i)) { ++ status = readl_relaxed(adev->regs + ++ HI_CH_STATUS_SD(i, adev->ee)); ++ ++ /* if no result present, skip */ ++ if (!(status & CH_STATUS_VALID)) ++ continue; ++ ++ result = readl_relaxed(adev->regs + ++ HI_CH_RSLT(i, adev->ee)); ++ ++ /* no valid results, skip */ ++ if (!(result & CH_RSLT_VALID)) ++ continue; ++ ++ /* flag error if transaction was flushed or failed */ ++ if (result & (CH_RSLT_ERR | CH_RSLT_FLUSH)) ++ achan->error = 1; ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ async_desc = achan->curr_txd; ++ ++ achan->curr_txd = NULL; ++ ++ if (async_desc) { ++ vchan_cookie_complete(&async_desc->vd); ++ ++ /* kick off next DMA */ ++ adm_start_dma(achan); ++ } ++ ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/** ++ * adm_tx_status - returns status of transaction ++ * @chan: dma channel ++ * @cookie: transaction cookie ++ * @txstate: DMA transaction state ++ * ++ * Return status of dma transaction ++ */ ++static enum dma_status adm_tx_status(struct dma_chan *chan, dma_cookie_t cookie, ++ struct dma_tx_state *txstate) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ struct virt_dma_desc *vd; ++ enum dma_status ret; ++ unsigned long flags; ++ size_t residue = 0; ++ ++ ret = dma_cookie_status(chan, cookie, txstate); ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ ++ vd = vchan_find_desc(&achan->vc, cookie); ++ if (vd) ++ residue = container_of(vd, struct adm_async_desc, vd)->length; ++ else if (achan->curr_txd && achan->curr_txd->vd.tx.cookie == cookie) ++ residue = achan->curr_txd->length; ++ ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++ ++ dma_set_residue(txstate, residue); ++ ++ if (achan->error) ++ return DMA_ERROR; ++ ++ return ret; ++} ++ ++static struct dma_chan *adm_dma_xlate(struct of_phandle_args *dma_spec, ++ struct of_dma *of) ++{ ++ struct adm_device *adev = container_of(of->of_dma_data, ++ struct adm_device, common); ++ struct adm_chan *achan; ++ struct dma_chan *chan; ++ unsigned int request; ++ unsigned int crci; ++ ++ if (dma_spec->args_count != 2) { ++ dev_err(adev->dev, "incorrect number of dma arguments\n"); ++ return NULL; ++ } ++ ++ request = dma_spec->args[0]; ++ if (request >= adev->num_channels) ++ return NULL; ++ ++ crci = dma_spec->args[1]; ++ ++ chan = dma_get_slave_channel(&(adev->channels[request].vc.chan)); ++ ++ if (!chan) ++ return NULL; ++ ++ achan = to_adm_chan(chan); ++ achan->crci = crci; ++ ++ return chan; ++} ++ ++/** ++ * adm_issue_pending - starts pending transactions ++ * @chan: dma channel ++ * ++ * Issues all pending transactions and starts DMA ++ */ ++static void adm_issue_pending(struct dma_chan *chan) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ ++ if (vchan_issue_pending(&achan->vc) && !achan->curr_txd) ++ adm_start_dma(achan); ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++} ++ ++/** ++ * adm_dma_free_desc - free descriptor memory ++ * @vd: virtual descriptor ++ * ++ */ ++static void adm_dma_free_desc(struct virt_dma_desc *vd) ++{ ++ struct adm_async_desc *async_desc = container_of(vd, ++ struct adm_async_desc, vd); ++ ++ dma_free_writecombine(async_desc->adev->dev, async_desc->dma_len, ++ async_desc->cpl, async_desc->dma_addr); ++ kfree(async_desc); ++} ++ ++static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan, ++ u32 index) ++{ ++ achan->id = index; ++ achan->adev = adev; ++ ++ vchan_init(&achan->vc, &adev->common); ++ achan->vc.desc_free = adm_dma_free_desc; ++} ++ ++static int adm_dma_probe(struct platform_device *pdev) ++{ ++ struct adm_device *adev; ++ struct resource *iores; ++ int ret; ++ u32 i; ++ ++ adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL); ++ if (!adev) ++ return -ENOMEM; ++ ++ adev->dev = &pdev->dev; ++ ++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ adev->regs = devm_ioremap_resource(&pdev->dev, iores); ++ if (IS_ERR(adev->regs)) ++ return PTR_ERR(adev->regs); ++ ++ adev->irq = platform_get_irq(pdev, 0); ++ if (adev->irq < 0) ++ return adev->irq; ++ ++ ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &adev->ee); ++ if (ret) { ++ dev_err(adev->dev, "Execution environment unspecified\n"); ++ return ret; ++ } ++ ++ adev->core_clk = devm_clk_get(adev->dev, "core"); ++ if (IS_ERR(adev->core_clk)) ++ return PTR_ERR(adev->core_clk); ++ ++ ret = clk_prepare_enable(adev->core_clk); ++ if (ret) { ++ dev_err(adev->dev, "failed to prepare/enable core clock\n"); ++ return ret; ++ } ++ ++ adev->iface_clk = devm_clk_get(adev->dev, "iface"); ++ if (IS_ERR(adev->iface_clk)) ++ return PTR_ERR(adev->iface_clk); ++ ++ ret = clk_prepare_enable(adev->iface_clk); ++ if (ret) { ++ dev_err(adev->dev, "failed to prepare/enable iface clock\n"); ++ return ret; ++ } ++ ++ adev->clk_reset = devm_reset_control_get(&pdev->dev, "clk"); ++ if (IS_ERR(adev->clk_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 reset\n"); ++ return PTR_ERR(adev->clk_reset); ++ } ++ ++ adev->c0_reset = devm_reset_control_get(&pdev->dev, "c0"); ++ if (IS_ERR(adev->c0_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 C0 reset\n"); ++ return PTR_ERR(adev->c0_reset); ++ } ++ ++ adev->c1_reset = devm_reset_control_get(&pdev->dev, "c1"); ++ if (IS_ERR(adev->c1_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 C1 reset\n"); ++ return PTR_ERR(adev->c1_reset); ++ } ++ ++ adev->c2_reset = devm_reset_control_get(&pdev->dev, "c2"); ++ if (IS_ERR(adev->c2_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 C2 reset\n"); ++ return PTR_ERR(adev->c2_reset); ++ } ++ ++ reset_control_assert(adev->clk_reset); ++ reset_control_assert(adev->c0_reset); ++ reset_control_assert(adev->c1_reset); ++ reset_control_assert(adev->c2_reset); ++ ++ reset_control_deassert(adev->clk_reset); ++ reset_control_deassert(adev->c0_reset); ++ reset_control_deassert(adev->c1_reset); ++ reset_control_deassert(adev->c2_reset); ++ ++ adev->num_channels = 16; ++ ++ adev->channels = devm_kcalloc(adev->dev, adev->num_channels, ++ sizeof(*adev->channels), GFP_KERNEL); ++ ++ if (!adev->channels) { ++ ret = -ENOMEM; ++ goto err_disable_clk; ++ } ++ ++ /* allocate and initialize channels */ ++ INIT_LIST_HEAD(&adev->common.channels); ++ ++ for (i = 0; i < adev->num_channels; i++) ++ adm_channel_init(adev, &adev->channels[i], i); ++ ++ /* reset CRCIs */ ++ for (i = 0; i < 16; i++) ++ writel(CRCI_CTL_RST, adev->regs + HI_CRCI_CTL(i, adev->ee)); ++ ++ /* configure client interfaces */ ++ writel(CI_RANGE_START(0x40) | CI_RANGE_END(0xb0) | CI_BURST_8_WORDS, ++ adev->regs + HI_CI_CONF(0)); ++ writel(CI_RANGE_START(0x2a) | CI_RANGE_END(0x2c) | CI_BURST_8_WORDS, ++ adev->regs + HI_CI_CONF(1)); ++ writel(CI_RANGE_START(0x12) | CI_RANGE_END(0x28) | CI_BURST_8_WORDS, ++ adev->regs + HI_CI_CONF(2)); ++ writel(GP_CTL_LP_EN | GP_CTL_LP_CNT(0xf), adev->regs + HI_GP_CTL); ++ ++ ret = devm_request_irq(adev->dev, adev->irq, adm_dma_irq, ++ 0, "adm_dma", adev); ++ if (ret) ++ goto err_disable_clk; ++ ++ platform_set_drvdata(pdev, adev); ++ ++ adev->common.dev = adev->dev; ++ adev->common.dev->dma_parms = &adev->dma_parms; ++ ++ /* set capabilities */ ++ dma_cap_zero(adev->common.cap_mask); ++ dma_cap_set(DMA_SLAVE, adev->common.cap_mask); ++ dma_cap_set(DMA_PRIVATE, adev->common.cap_mask); ++ ++ /* initialize dmaengine apis */ ++ adev->common.device_alloc_chan_resources = adm_alloc_chan; ++ adev->common.device_free_chan_resources = adm_free_chan; ++ adev->common.device_prep_slave_sg = adm_prep_slave_sg; ++ adev->common.device_control = adm_control; ++ adev->common.device_issue_pending = adm_issue_pending; ++ adev->common.device_tx_status = adm_tx_status; ++ ++ ret = dma_async_device_register(&adev->common); ++ if (ret) { ++ dev_err(adev->dev, "failed to register dma async device\n"); ++ goto err_disable_clk; ++ } ++ ++ ret = of_dma_controller_register(pdev->dev.of_node, adm_dma_xlate, ++ &adev->common); ++ if (ret) ++ goto err_unregister_dma; ++ ++ return 0; ++ ++err_unregister_dma: ++ dma_async_device_unregister(&adev->common); ++err_disable_clk: ++ clk_disable_unprepare(adev->core_clk); ++ clk_disable_unprepare(adev->iface_clk); ++ ++ return ret; ++} ++ ++static int adm_dma_remove(struct platform_device *pdev) ++{ ++ struct adm_device *adev = platform_get_drvdata(pdev); ++ struct adm_chan *achan; ++ u32 i; ++ ++ of_dma_controller_free(pdev->dev.of_node); ++ dma_async_device_unregister(&adev->common); ++ ++ devm_free_irq(adev->dev, adev->irq, adev); ++ ++ for (i = 0; i < adev->num_channels; i++) { ++ achan = &adev->channels[i]; ++ writel(CH_CONF_FLUSH_RSLT_EN, ++ adev->regs + HI_CH_CONF(achan->id)); ++ writel(CH_RSLT_CONF_FLUSH_EN, ++ adev->regs + HI_CH_RSLT_CONF(achan->id, adev->ee)); ++ ++ adm_terminate_all(&adev->channels[i]); ++ } ++ ++ clk_disable_unprepare(adev->core_clk); ++ clk_disable_unprepare(adev->iface_clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id adm_of_match[] = { ++ { .compatible = "qcom,adm", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, adm_of_match); ++ ++static struct platform_driver adm_dma_driver = { ++ .probe = adm_dma_probe, ++ .remove = adm_dma_remove, ++ .driver = { ++ .name = "adm-dma-engine", ++ .owner = THIS_MODULE, ++ .of_match_table = adm_of_match, ++ }, ++}; ++ ++module_platform_driver(adm_dma_driver); ++ ++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); ++MODULE_DESCRIPTION("QCOM ADM DMA engine driver"); ++MODULE_LICENSE("GPL v2"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0178-dmaengine-qcom_adm-Add-device-tree-binding.patch b/target/linux/ipq806x/patches/0178-dmaengine-qcom_adm-Add-device-tree-binding.patch new file mode 100644 index 0000000000..a80fcfa7f5 --- /dev/null +++ b/target/linux/ipq806x/patches/0178-dmaengine-qcom_adm-Add-device-tree-binding.patch @@ -0,0 +1,82 @@ +From 331294fa5c703536e27b79e9c112d162393f725a Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 26 Jun 2014 13:55:10 -0500 +Subject: [PATCH 178/182] dmaengine: qcom_adm: Add device tree binding + +Add device tree binding support for the QCOM ADM DMA driver. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + Documentation/devicetree/bindings/dma/qcom_adm.txt | 60 ++++++++++++++++++++ + 1 file changed, 60 insertions(+) + create mode 100644 Documentation/devicetree/bindings/dma/qcom_adm.txt + +diff --git a/Documentation/devicetree/bindings/dma/qcom_adm.txt b/Documentation/devicetree/bindings/dma/qcom_adm.txt +new file mode 100644 +index 0000000..7f05cb5 +--- /dev/null ++++ b/Documentation/devicetree/bindings/dma/qcom_adm.txt +@@ -0,0 +1,60 @@ ++QCOM ADM DMA Controller ++ ++Required properties: ++- compatible: must contain "qcom,adm" for IPQ/APQ8064 and MSM8960 ++- reg: Address range for DMA registers ++- interrupts: Should contain one interrupt shared by all channels ++- #dma-cells: must be <2>. First cell denotes the channel number. Second cell ++ denotes CRCI (client rate control interface) flow control assignment. ++- clocks: Should contain the core clock and interface clock. ++- clock-names: Must contain "core" for the core clock and "iface" for the ++ interface clock. ++- resets: Must contain an entry for each entry in reset names. ++- reset-names: Must include the following entries: ++ - clk ++ - c0 ++ - c1 ++ - c2 ++- qcom,ee: indicates the security domain identifier used in the secure world. ++ ++Example: ++ adm_dma: dma@18300000 { ++ compatible = "qcom,adm"; ++ reg = <0x18300000 0x100000>; ++ interrupts = <0 170 0>; ++ #dma-cells = <2>; ++ ++ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; ++ clock-names = "core", "iface"; ++ ++ resets = <&gcc ADM0_RESET>, ++ <&gcc ADM0_C0_RESET>, ++ <&gcc ADM0_C1_RESET>, ++ <&gcc ADM0_C2_RESET>; ++ reset-names = "clk", "c0", "c1", "c2"; ++ qcom,ee = <0>; ++ }; ++ ++DMA clients must use the format descripted in the dma.txt file, using a three ++cell specifier for each channel. ++ ++Each dmas request consists of 3 cells: ++ 1. phandle pointing to the DMA controller ++ 2. channel number ++ 3. CRCI assignment, if applicable. If no CRCI flow control is required, use 0. ++ ++Example: ++ ++ spi4: spi@1a280000 { ++ status = "ok"; ++ spi-max-frequency = <50000000>; ++ ++ pinctrl-0 = <&spi_pins>; ++ pinctrl-names = "default"; ++ ++ cs-gpios = <&qcom_pinmux 20 0>; ++ ++ dmas = <&adm_dma 6 9>, ++ <&adm_dma 5 10>; ++ dma-names = "rx", "tx"; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0179-spi-qup-Add-DMA-capabilities.patch b/target/linux/ipq806x/patches/0179-spi-qup-Add-DMA-capabilities.patch new file mode 100644 index 0000000000..c35aeb17b3 --- /dev/null +++ b/target/linux/ipq806x/patches/0179-spi-qup-Add-DMA-capabilities.patch @@ -0,0 +1,495 @@ +From 8322bafdcee1d7eaf15540ff013415bff1eacb28 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 26 Jun 2014 10:50:24 -0500 +Subject: [PATCH 179/182] spi: qup: Add DMA capabilities + +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 greater than the length of a block. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + drivers/spi/spi-qup.c | 361 ++++++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 340 insertions(+), 21 deletions(-) + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index c137226..28754ae 100644 +--- 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,14 @@ 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; ++ void *dummy; ++ atomic_t dma_outstanding; + }; + + +@@ -266,6 +278,221 @@ static void spi_qup_fifo_write(struct spi_qup *controller, + } + } + ++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 +542,13 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) + 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 +568,8 @@ 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; + 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 +598,13 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + 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) && ++ !is_vmalloc_addr(xfer->tx_buf) && ++ !is_vmalloc_addr(xfer->rx_buf)) ++ 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 +612,30 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + /* must be zero for FIFO */ + writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); + writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); +- } 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,11 +668,20 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + 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 */ + if (!controller->qup_v1) + writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK); ++ + return 0; + } + +@@ -452,26 +710,32 @@ static int spi_qup_transfer_one(struct spi_master *master, + 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 (!ret && !wait_for_completion_timeout(&controller->done, ++ timeout)) ++ ret = -ETIMEDOUT; ++ } + exit: ++ + spi_qup_set_state(controller, QUP_STATE_RESET); + spin_lock_irqsave(&controller->lock, flags); + controller->xfer = NULL; +@@ -553,6 +817,7 @@ static int spi_qup_probe(struct platform_device *pdev) + 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); + +@@ -612,6 +877,55 @@ static int spi_qup_probe(struct platform_device *pdev) + writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN, + base + SPI_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; ++ } ++ } ++ + /* if earlier version of the QUP, disable INPUT_OVERRUN */ + if (controller->qup_v1) + writel_relaxed(QUP_ERROR_OUTPUT_OVER_RUN | +@@ -730,6 +1044,11 @@ static int spi_qup_remove(struct platform_device *pdev) + 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.7.10.4 + diff --git a/target/linux/ipq806x/patches/0180-ARM-dts-Add-ADM-DMA-nodes-and-SPI-linkage.patch b/target/linux/ipq806x/patches/0180-ARM-dts-Add-ADM-DMA-nodes-and-SPI-linkage.patch new file mode 100644 index 0000000000..5fed04123e --- /dev/null +++ b/target/linux/ipq806x/patches/0180-ARM-dts-Add-ADM-DMA-nodes-and-SPI-linkage.patch @@ -0,0 +1,90 @@ +From c78ae23b6c174c9f2e0973a247942b6b4adb7e82 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 26 Jun 2014 13:02:59 -0500 +Subject: [PATCH 180/182] ARM: dts: Add ADM DMA nodes and SPI linkage + +This patch adds the ADM DMA controller DT nodes and also enables the use of dma +in SPI. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 11 +++++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 8 +++++--- + 2 files changed, 16 insertions(+), 3 deletions(-) + +diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +index 2b2d63c..c54a3ee 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -44,6 +44,10 @@ + drive-strength = <10>; + bias-none; + }; ++ cs { ++ pins = "gpio20"; ++ drive-strength = <12>; ++ }; + }; + nand_pins: nand_pins { + mux { +@@ -100,12 +104,17 @@ + + cs-gpios = <&qcom_pinmux 20 0>; + ++ dmas = <&adm_dma 6 9>, ++ <&adm_dma 5 10>; ++ dma-names = "rx", "tx"; ++ + flash: m25p80@0 { + compatible = "s25fl256s1"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <50000000>; + reg = <0>; ++ m25p,fast-read; + + partition@0 { + label = "rootfs"; +@@ -140,8 +149,10 @@ + ranges = <0x00000000 0 0x00000000 0x31f00000 0 0x00100000 /* configuration space */ + 0x81000000 0 0 0x31e00000 0 0x00100000 /* downstream I/O */ + 0x82000000 0 0x00000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */ ++ + }; + ++ + sata-phy@1b400000 { + status = "ok"; + }; +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +index 97e4c3d..f74e923 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -421,19 +421,21 @@ + compatible = "qcom,adm"; + reg = <0x18300000 0x100000>; + interrupts = <0 170 0>; ++ #dma-cells = <2>; + + clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; +- clock-names = "core_clk", "iface_clk"; ++ clock-names = "core", "iface"; + + resets = <&gcc ADM0_RESET>, + <&gcc ADM0_PBUS_RESET>, + <&gcc ADM0_C0_RESET>, + <&gcc ADM0_C1_RESET>, + <&gcc ADM0_C2_RESET>; +- +- reset-names = "adm", "pbus", "c0", "c1", "c2"; ++ reset-names = "clk", "pbus", "c0", "c1", "c2"; ++ qcom,ee = <0>; + + status = "disabled"; ++ + }; + + nand@0x1ac00000 { +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0181-mtd-nand-qcom-Align-clk-and-reset-names.patch b/target/linux/ipq806x/patches/0181-mtd-nand-qcom-Align-clk-and-reset-names.patch new file mode 100644 index 0000000000..763a2455c5 --- /dev/null +++ b/target/linux/ipq806x/patches/0181-mtd-nand-qcom-Align-clk-and-reset-names.patch @@ -0,0 +1,46 @@ +From ae0e9ee3bcfafc5bd5932b544e0a5c107948fc97 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 30 Jun 2014 21:17:26 -0500 +Subject: [PATCH 181/182] mtd: nand: qcom: Align clk and reset names + +This patch aligns the clk and reset names to the same ones that the dmaengine +driver uses. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + drivers/mtd/nand/qcom_adm_dma.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/mtd/nand/qcom_adm_dma.c b/drivers/mtd/nand/qcom_adm_dma.c +index 46d8473..542f901 100644 +--- a/drivers/mtd/nand/qcom_adm_dma.c ++++ b/drivers/mtd/nand/qcom_adm_dma.c +@@ -568,14 +568,14 @@ static int msm_dmov_init_clocks(struct platform_device *pdev) + int adm = (pdev->id >= 0) ? pdev->id : 0; + int ret; + +- dmov_conf[adm].clk = devm_clk_get(&pdev->dev, "core_clk"); ++ dmov_conf[adm].clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(dmov_conf[adm].clk)) { + printk(KERN_ERR "%s: Error getting adm_clk\n", __func__); + dmov_conf[adm].clk = NULL; + return -ENOENT; + } + +- dmov_conf[adm].pclk = devm_clk_get(&pdev->dev, "iface_clk"); ++ dmov_conf[adm].pclk = devm_clk_get(&pdev->dev, "iface"); + if (IS_ERR(dmov_conf[adm].pclk)) { + dmov_conf[adm].pclk = NULL; + /* pclk not present on all SoCs, don't bail on failure */ +@@ -690,7 +690,7 @@ static int msm_dmov_probe(struct platform_device *pdev) + } + + /* get resets */ +- dmov_conf[adm].adm_reset = devm_reset_control_get(&pdev->dev, "adm"); ++ dmov_conf[adm].adm_reset = devm_reset_control_get(&pdev->dev, "clk"); + if (IS_ERR(dmov_conf[adm].adm_reset)) { + dev_err(&pdev->dev, "failed to get adm reset\n"); + ret = PTR_ERR(dmov_conf[adm].adm_reset); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0182-qcom-Kconfig-Make-drivers-mutually-exclusive.patch b/target/linux/ipq806x/patches/0182-qcom-Kconfig-Make-drivers-mutually-exclusive.patch new file mode 100644 index 0000000000..87e2fad612 --- /dev/null +++ b/target/linux/ipq806x/patches/0182-qcom-Kconfig-Make-drivers-mutually-exclusive.patch @@ -0,0 +1,54 @@ +From 0771849495b4128cac2faf7d49c85c729fc48b20 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 30 Jun 2014 21:18:39 -0500 +Subject: [PATCH 182/182] qcom: Kconfig: Make drivers mutually exclusive + +This patch makes sure QCOM ADM dmaengine and QCOM Nand cannot be enabled at the +same time. This is an issue because the dma drivers will conflict with one +another. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + drivers/dma/Kconfig | 2 +- + drivers/mtd/nand/Kconfig | 6 +++--- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig +index 79155fa..ed7a5f6 100644 +--- a/drivers/dma/Kconfig ++++ b/drivers/dma/Kconfig +@@ -412,7 +412,7 @@ config QCOM_BAM_DMA + + config QCOM_ADM + tristate "Qualcomm ADM support" +- depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM) ++ depends on !MTD_QCOM_ADM && (ARCH_QCOM || (COMPILE_TEST && OF && ARM)) + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + ---help--- +diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig +index 6e3842f..4a84264 100644 +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -511,15 +511,15 @@ config MTD_NAND_XWAY + to the External Bus Unit (EBU). + + config MTD_QCOM_DMA +- tristate "QCMO NAND DMA Support" +- depends on ARCH_QCOM && MTD_QCOM_NAND ++ tristate "QCOM NAND DMA Support" ++ depends on !QCOM_ADM && ARCH_QCOM && MTD_QCOM_NAND + default n + help + DMA support for QCOM NAND + + config MTD_QCOM_NAND + tristate "QCOM NAND Device Support" +- depends on MTD && ARCH_QCOM ++ depends on MTD && ARCH_QCOM && !QCOM_ADM + select CRC16 + select BITREVERSE + select MTD_NAND_IDS +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/profiles/default.mk b/target/linux/ipq806x/profiles/default.mk new file mode 100644 index 0000000000..ded0164df4 --- /dev/null +++ b/target/linux/ipq806x/profiles/default.mk @@ -0,0 +1,18 @@ +# +# 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 +endef + +define Profile/Default/Description + Default package set compatible with most boards. +endef +$(eval $(call Profile,Default)) |