From: John Crispin Date: Wed, 22 Mar 2017 08:43:01 +0000 (+0100) Subject: ipq806x: remove v4.4 support X-Git-Url: http://git.cdn.openwrt.org/?a=commitdiff_plain;h=3a3564ead5e4cf2f6ff73302c1e680b5575079ec;p=openwrt%2Fstaging%2Fdangole.git ipq806x: remove v4.4 support Signed-off-by: John Crispin --- diff --git a/target/linux/ipq806x/config-4.4 b/target/linux/ipq806x/config-4.4 deleted file mode 100644 index 898e32ff03..0000000000 --- a/target/linux/ipq806x/config-4.4 +++ /dev/null @@ -1,426 +0,0 @@ -CONFIG_ALIGNMENT_TRAP=y -# CONFIG_AMBA_PL08X is not set -CONFIG_APQ_GCC_8084=y -CONFIG_APQ_MMCC_8084=y -CONFIG_AR8216_PHY=y -CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y -CONFIG_ARCH_HAS_ELF_RANDOMIZE=y -CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y -CONFIG_ARCH_HAS_SG_CHAIN=y -CONFIG_ARCH_HAS_TICK_BROADCAST=y -CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y -CONFIG_ARCH_HIBERNATION_POSSIBLE=y -CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y -CONFIG_ARCH_MSM8960=y -CONFIG_ARCH_MSM8974=y -CONFIG_ARCH_MSM8X60=y -CONFIG_ARCH_MULTIPLATFORM=y -# CONFIG_ARCH_MULTI_CPU_AUTO is not set -CONFIG_ARCH_MULTI_V6_V7=y -CONFIG_ARCH_MULTI_V7=y -CONFIG_ARCH_NR_GPIO=0 -CONFIG_ARCH_QCOM=y -# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set -# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set -CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y -CONFIG_ARCH_SUPPORTS_BIG_ENDIAN=y -CONFIG_ARCH_SUPPORTS_UPROBES=y -CONFIG_ARCH_SUSPEND_POSSIBLE=y -CONFIG_ARCH_USE_BUILTIN_BSWAP=y -CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y -CONFIG_ARCH_WANT_GENERAL_HUGETLB=y -CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y -CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y -CONFIG_ARM=y -CONFIG_ARM_AMBA=y -CONFIG_ARM_APPENDED_DTB=y -CONFIG_ARM_ARCH_TIMER=y -CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y -CONFIG_ARM_ATAG_DTB_COMPAT=y -# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set -# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER is not set -CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE=y -CONFIG_ARM_CPU_SUSPEND=y -# CONFIG_ARM_CPU_TOPOLOGY is not set -CONFIG_ARM_GIC=y -CONFIG_ARM_HAS_SG_CHAIN=y -CONFIG_ARM_L1_CACHE_SHIFT=6 -CONFIG_ARM_L1_CACHE_SHIFT_6=y -# CONFIG_ARM_LPAE is not set -CONFIG_ARM_PATCH_PHYS_VIRT=y -CONFIG_ARM_QCOM_CPUFREQ=y -CONFIG_ARM_QCOM_CPUIDLE=y -# CONFIG_ARM_SMMU is not set -# CONFIG_ARM_SP805_WATCHDOG is not set -CONFIG_ARM_THUMB=y -# CONFIG_ARM_THUMBEE is not set -CONFIG_ARM_UNWIND=y -CONFIG_ARM_VIRT_EXT=y -CONFIG_AT803X_PHY=y -CONFIG_BLK_DEV_LOOP=y -# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set -CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 -CONFIG_BOUNCE=y -# CONFIG_CACHE_L2X0 is not set -CONFIG_CLKDEV_LOOKUP=y -CONFIG_CLKSRC_OF=y -CONFIG_CLKSRC_PROBE=y -CONFIG_CLKSRC_QCOM=y -CONFIG_CLONE_BACKWARDS=y -CONFIG_COMMON_CLK=y -CONFIG_COMMON_CLK_QCOM=y -CONFIG_CPUFREQ_DT=y -CONFIG_CPU_32v6K=y -CONFIG_CPU_32v7=y -CONFIG_CPU_ABRT_EV7=y -# CONFIG_CPU_BIG_ENDIAN is not set -# CONFIG_CPU_BPREDICT_DISABLE is not set -CONFIG_CPU_CACHE_V7=y -CONFIG_CPU_CACHE_VIPT=y -CONFIG_CPU_COPY_V6=y -CONFIG_CPU_CP15=y -CONFIG_CPU_CP15_MMU=y -CONFIG_CPU_FREQ=y -CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y -# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set -CONFIG_CPU_FREQ_GOV_COMMON=y -# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set -CONFIG_CPU_FREQ_GOV_ONDEMAND=y -CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set -# CONFIG_CPU_FREQ_GOV_USERSPACE is not set -CONFIG_CPU_FREQ_STAT=y -CONFIG_CPU_HAS_ASID=y -# CONFIG_CPU_ICACHE_DISABLE is not set -CONFIG_CPU_IDLE=y -CONFIG_CPU_IDLE_GOV_LADDER=y -CONFIG_CPU_IDLE_GOV_MENU=y -CONFIG_CPU_PABRT_V7=y -CONFIG_CPU_PM=y -CONFIG_CPU_RMAP=y -CONFIG_CPU_THERMAL=y -CONFIG_CPU_TLB_V7=y -CONFIG_CPU_V7=y -CONFIG_CRC16=y -# CONFIG_CRC32_SARWATE is not set -CONFIG_CRC32_SLICEBY8=y -CONFIG_CRYPTO_DEFLATE=y -CONFIG_CRYPTO_LZO=y -CONFIG_CRYPTO_RNG2=y -CONFIG_CRYPTO_WORKQUEUE=y -CONFIG_DCACHE_WORD_ACCESS=y -CONFIG_DEBUG_GPIO=y -CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" -# CONFIG_DEBUG_UART_8250 is not set -# CONFIG_DEBUG_USER is not set -CONFIG_DMADEVICES=y -CONFIG_DMA_ENGINE=y -CONFIG_DMA_OF=y -CONFIG_DMA_VIRTUAL_CHANNELS=y -CONFIG_DTC=y -# CONFIG_DWMAC_GENERIC is not set -CONFIG_DWMAC_IPQ806X=y -# CONFIG_DWMAC_SUNXI is not set -# CONFIG_DW_DMAC_PCI is not set -CONFIG_DYNAMIC_DEBUG=y -CONFIG_EDAC_ATOMIC_SCRUB=y -CONFIG_EDAC_SUPPORT=y -CONFIG_ETHERNET_PACKET_MANGLE=y -CONFIG_FIXED_PHY=y -CONFIG_FIX_EARLYCON_MEM=y -CONFIG_GENERIC_ALLOCATOR=y -CONFIG_GENERIC_BUG=y -CONFIG_GENERIC_CLOCKEVENTS=y -CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y -CONFIG_GENERIC_IDLE_POLL_SETUP=y -CONFIG_GENERIC_IO=y -CONFIG_GENERIC_IRQ_SHOW=y -CONFIG_GENERIC_IRQ_SHOW_LEVEL=y -CONFIG_GENERIC_MSI_IRQ=y -CONFIG_GENERIC_PCI_IOMAP=y -CONFIG_GENERIC_PHY=y -CONFIG_GENERIC_PINCONF=y -CONFIG_GENERIC_SCHED_CLOCK=y -CONFIG_GENERIC_SMP_IDLE_THREAD=y -CONFIG_GENERIC_STRNCPY_FROM_USER=y -CONFIG_GENERIC_STRNLEN_USER=y -CONFIG_GENERIC_TIME_VSYSCALL=y -CONFIG_GPIOLIB=y -CONFIG_GPIOLIB_IRQCHIP=y -CONFIG_GPIO_DEVRES=y -CONFIG_GPIO_SYSFS=y -CONFIG_HANDLE_DOMAIN_IRQ=y -CONFIG_HARDIRQS_SW_RESEND=y -CONFIG_HAS_DMA=y -CONFIG_HAS_IOMEM=y -CONFIG_HAS_IOPORT_MAP=y -# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set -CONFIG_HAVE_ARCH_AUDITSYSCALL=y -CONFIG_HAVE_ARCH_BITREVERSE=y -CONFIG_HAVE_ARCH_JUMP_LABEL=y -CONFIG_HAVE_ARCH_KGDB=y -CONFIG_HAVE_ARCH_PFN_VALID=y -CONFIG_HAVE_ARCH_SECCOMP_FILTER=y -CONFIG_HAVE_ARCH_TRACEHOOK=y -CONFIG_HAVE_ARM_ARCH_TIMER=y -# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set -CONFIG_HAVE_BPF_JIT=y -CONFIG_HAVE_CC_STACKPROTECTOR=y -CONFIG_HAVE_CLK=y -CONFIG_HAVE_CLK_PREPARE=y -CONFIG_HAVE_CONTEXT_TRACKING=y -CONFIG_HAVE_C_RECORDMCOUNT=y -CONFIG_HAVE_DEBUG_KMEMLEAK=y -CONFIG_HAVE_DMA_API_DEBUG=y -CONFIG_HAVE_DMA_ATTRS=y -CONFIG_HAVE_DMA_CONTIGUOUS=y -CONFIG_HAVE_DYNAMIC_FTRACE=y -CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y -CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y -CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y -CONFIG_HAVE_FUNCTION_TRACER=y -CONFIG_HAVE_GENERIC_DMA_COHERENT=y -CONFIG_HAVE_IDE=y -CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y -CONFIG_HAVE_MEMBLOCK=y -CONFIG_HAVE_MOD_ARCH_SPECIFIC=y -CONFIG_HAVE_NET_DSA=y -CONFIG_HAVE_OPROFILE=y -CONFIG_HAVE_OPTPROBES=y -CONFIG_HAVE_PERF_EVENTS=y -CONFIG_HAVE_PERF_REGS=y -CONFIG_HAVE_PERF_USER_STACK_DUMP=y -CONFIG_HAVE_PROC_CPU=y -CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y -CONFIG_HAVE_SMP=y -CONFIG_HAVE_SYSCALL_TRACEPOINTS=y -CONFIG_HAVE_UID16=y -CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y -CONFIG_HIGHMEM=y -# CONFIG_HIGHPTE is not set -CONFIG_HWMON=y -CONFIG_HWSPINLOCK=y -CONFIG_HWSPINLOCK_QCOM=y -CONFIG_HW_RANDOM=y -CONFIG_HW_RANDOM_MSM=y -CONFIG_HZ_FIXED=0 -CONFIG_I2C=y -CONFIG_I2C_BOARDINFO=y -CONFIG_I2C_CHARDEV=y -CONFIG_I2C_HELPER_AUTO=y -CONFIG_I2C_QUP=y -CONFIG_INITRAMFS_SOURCE="" -CONFIG_IOMMU_HELPER=y -# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set -CONFIG_IOMMU_SUPPORT=y -CONFIG_IPQ_GCC_806X=y -# CONFIG_IPQ_LCC_806X is not set -CONFIG_IRQCHIP=y -CONFIG_IRQ_DOMAIN=y -CONFIG_IRQ_DOMAIN_HIERARCHY=y -CONFIG_IRQ_FORCED_THREADING=y -CONFIG_IRQ_WORK=y -CONFIG_KPSS_XCC=y -CONFIG_KRAITCC=y -CONFIG_KRAIT_CLOCKS=y -CONFIG_KRAIT_L2_ACCESSORS=y -CONFIG_LIBFDT=y -CONFIG_LOCKUP_DETECTOR=y -CONFIG_LOCK_SPIN_ON_OWNER=y -CONFIG_LZO_COMPRESS=y -CONFIG_LZO_DECOMPRESS=y -CONFIG_MDIO_BITBANG=y -CONFIG_MDIO_BOARDINFO=y -CONFIG_MDIO_GPIO=y -CONFIG_MFD_QCOM_RPM=y -# CONFIG_MFD_SPMI_PMIC is not set -CONFIG_MFD_SYSCON=y -CONFIG_MIGHT_HAVE_CACHE_L2X0=y -CONFIG_MIGHT_HAVE_PCI=y -CONFIG_MMC=y -CONFIG_MMC_ARMMMCI=y -CONFIG_MMC_BLOCK=y -CONFIG_MMC_BLOCK_MINORS=16 -CONFIG_MMC_QCOM_DML=y -CONFIG_MMC_SDHCI=y -CONFIG_MMC_SDHCI_MSM=y -# CONFIG_MMC_SDHCI_PCI is not set -CONFIG_MMC_SDHCI_PLTFM=y -# CONFIG_MMC_TIFM_SD is not set -CONFIG_MODULES_USE_ELF_REL=y -CONFIG_MSM_GCC_8660=y -# CONFIG_MSM_GCC_8916 is not set -CONFIG_MSM_GCC_8960=y -CONFIG_MSM_GCC_8974=y -# CONFIG_MSM_LCC_8960 is not set -CONFIG_MSM_MMCC_8960=y -CONFIG_MSM_MMCC_8974=y -CONFIG_MTD_CMDLINE_PARTS=y -CONFIG_MTD_M25P80=y -CONFIG_MTD_NAND=y -CONFIG_MTD_NAND_ECC=y -CONFIG_MTD_NAND_QCOM=y -CONFIG_MTD_QCOM_SMEM_PARTS=y -CONFIG_MTD_SPI_NOR=y -CONFIG_MTD_SPLIT_FIRMWARE=y -CONFIG_MTD_SPLIT_FIT_FW=y -CONFIG_MTD_UBI=y -CONFIG_MTD_UBI_BEB_LIMIT=20 -CONFIG_MTD_UBI_BLOCK=y -# CONFIG_MTD_UBI_FASTMAP is not set -# CONFIG_MTD_UBI_GLUEBI is not set -CONFIG_MTD_UBI_WL_THRESHOLD=4096 -CONFIG_MULTI_IRQ_HANDLER=y -CONFIG_MUTEX_SPIN_ON_OWNER=y -CONFIG_NEED_DMA_MAP_STATE=y -CONFIG_NEON=y -CONFIG_NET_DSA=y -# CONFIG_NET_DSA_AR8XXX is not set -CONFIG_NET_DSA_HWMON=y -CONFIG_NET_FLOW_LIMIT=y -CONFIG_NET_PTP_CLASSIFY=y -CONFIG_NET_SWITCHDEV=y -CONFIG_NO_BOOTMEM=y -CONFIG_NO_HZ=y -CONFIG_NO_HZ_COMMON=y -CONFIG_NO_HZ_IDLE=y -CONFIG_NR_CPUS=4 -CONFIG_NVMEM=y -CONFIG_OF=y -CONFIG_OF_ADDRESS=y -CONFIG_OF_ADDRESS_PCI=y -CONFIG_OF_EARLY_FLATTREE=y -CONFIG_OF_FLATTREE=y -CONFIG_OF_GPIO=y -CONFIG_OF_IRQ=y -CONFIG_OF_MDIO=y -CONFIG_OF_MTD=y -CONFIG_OF_NET=y -CONFIG_OF_PCI=y -CONFIG_OF_PCI_IRQ=y -CONFIG_OF_RESERVED_MEM=y -CONFIG_OLD_SIGACTION=y -CONFIG_OLD_SIGSUSPEND3=y -CONFIG_PAGE_OFFSET=0xC0000000 -CONFIG_PCI=y -CONFIG_PCIEAER=y -CONFIG_PCIEPORTBUS=y -CONFIG_PCIE_DW=y -CONFIG_PCIE_QCOM=y -CONFIG_PCI_DEBUG=y -CONFIG_PCI_DISABLE_COMMON_QUIRKS=y -CONFIG_PCI_DOMAINS=y -CONFIG_PCI_DOMAINS_GENERIC=y -CONFIG_PCI_MSI=y -CONFIG_PERF_USE_VMALLOC=y -CONFIG_PGTABLE_LEVELS=2 -CONFIG_PHYLIB=y -# CONFIG_PHY_QCOM_APQ8064_SATA is not set -CONFIG_PHY_QCOM_IPQ806X_SATA=y -# CONFIG_PHY_QCOM_UFS is not set -CONFIG_PINCTRL=y -CONFIG_PINCTRL_APQ8064=y -# CONFIG_PINCTRL_APQ8084 is not set -CONFIG_PINCTRL_IPQ8064=y -CONFIG_PINCTRL_MSM=y -# CONFIG_PINCTRL_MSM8660 is not set -# CONFIG_PINCTRL_MSM8916 is not set -# CONFIG_PINCTRL_MSM8960 is not set -CONFIG_PINCTRL_MSM8X74=y -# CONFIG_PINCTRL_QCOM_SPMI_PMIC is not set -# CONFIG_PINCTRL_QCOM_SSBI_PMIC is not set -# CONFIG_PL330_DMA is not set -CONFIG_PM_OPP=y -CONFIG_POWER_RESET=y -CONFIG_POWER_RESET_MSM=y -CONFIG_POWER_SUPPLY=y -CONFIG_PPS=y -CONFIG_PRINTK_TIME=y -CONFIG_PTP_1588_CLOCK=y -CONFIG_QCOM_ADM=y -CONFIG_QCOM_BAM_DMA=y -CONFIG_QCOM_CLK_RPM=y -CONFIG_QCOM_GDSC=y -CONFIG_QCOM_GSBI=y -CONFIG_QCOM_HFPLL=y -CONFIG_QCOM_PM=y -CONFIG_QCOM_QFPROM=y -CONFIG_QCOM_RPMCC=y -CONFIG_QCOM_SCM=y -CONFIG_QCOM_SCM_32=y -# CONFIG_QCOM_SMD is not set -CONFIG_QCOM_SMEM=y -CONFIG_QCOM_TSENS=y -CONFIG_QCOM_WDT=y -CONFIG_RAS=y -CONFIG_RATIONAL=y -CONFIG_RCU_CPU_STALL_TIMEOUT=21 -CONFIG_RCU_STALL_COMMON=y -CONFIG_REGMAP=y -CONFIG_REGMAP_MMIO=y -CONFIG_REGULATOR=y -CONFIG_REGULATOR_FIXED_VOLTAGE=y -CONFIG_REGULATOR_QCOM_RPM=y -# CONFIG_REGULATOR_QCOM_SPMI is not set -CONFIG_RESET_CONTROLLER=y -CONFIG_RFS_ACCEL=y -CONFIG_RPS=y -CONFIG_RTC_CLASS=y -# CONFIG_RTC_DRV_CMOS is not set -CONFIG_RWSEM_SPIN_ON_OWNER=y -CONFIG_RWSEM_XCHGADD_ALGORITHM=y -CONFIG_SCHED_HRTICK=y -# CONFIG_SCHED_INFO is not set -# CONFIG_SCSI_DMA is not set -CONFIG_SERIAL_8250_FSL=y -# CONFIG_SERIAL_AMBA_PL010 is not set -# CONFIG_SERIAL_AMBA_PL011 is not set -CONFIG_SERIAL_MSM=y -CONFIG_SERIAL_MSM_CONSOLE=y -CONFIG_SMP=y -CONFIG_SMP_ON_UP=y -CONFIG_SPARSE_IRQ=y -CONFIG_SPI=y -CONFIG_SPI_MASTER=y -CONFIG_SPI_QUP=y -CONFIG_SPMI=y -CONFIG_SPMI_MSM_PMIC_ARB=y -CONFIG_SRCU=y -CONFIG_STMMAC_ETH=y -CONFIG_STMMAC_PLATFORM=y -CONFIG_SWCONFIG=y -CONFIG_SWCONFIG_LEDS=y -CONFIG_SWIOTLB=y -CONFIG_SWP_EMULATE=y -CONFIG_SYS_SUPPORTS_APM_EMULATION=y -CONFIG_THERMAL=y -CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y -CONFIG_THERMAL_GOV_STEP_WISE=y -CONFIG_THERMAL_HWMON=y -CONFIG_THERMAL_OF=y -# CONFIG_THUMB2_KERNEL is not set -CONFIG_TICK_CPU_ACCOUNTING=y -CONFIG_TREE_RCU=y -CONFIG_UBIFS_FS=y -CONFIG_UBIFS_FS_ADVANCED_COMPR=y -CONFIG_UBIFS_FS_LZO=y -CONFIG_UBIFS_FS_ZLIB=y -CONFIG_UEVENT_HELPER_PATH="" -CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" -CONFIG_USB_SUPPORT=y -CONFIG_USE_OF=y -CONFIG_VDSO=y -CONFIG_VECTORS_BASE=0xffff0000 -CONFIG_VFP=y -CONFIG_VFPv3=y -CONFIG_WATCHDOG_CORE=y -# CONFIG_WATCHDOG_SYSFS is not set -# CONFIG_WL_TI is not set -CONFIG_XPS=y -CONFIG_XZ_DEC_ARM=y -CONFIG_XZ_DEC_BCJ=y -CONFIG_ZBOOT_ROM_BSS=0 -CONFIG_ZBOOT_ROM_TEXT=0 -CONFIG_ZLIB_DEFLATE=y -CONFIG_ZLIB_INFLATE=y -CONFIG_ZONE_DMA_FLAG=0 diff --git a/target/linux/ipq806x/patches-4.4/001-add-API-register-brd-clks-bckwrds-cmptblt.patch b/target/linux/ipq806x/patches-4.4/001-add-API-register-brd-clks-bckwrds-cmptblt.patch deleted file mode 100644 index b9056e9618..0000000000 --- a/target/linux/ipq806x/patches-4.4/001-add-API-register-brd-clks-bckwrds-cmptblt.patch +++ /dev/null @@ -1,135 +0,0 @@ -From ee15faffef11309219aa87a24efc86f6dd13f7cb Mon Sep 17 00:00:00 2001 -From: Stephen Boyd -Date: Mon, 26 Oct 2015 17:11:32 -0700 -Subject: clk: qcom: common: Add API to register board clocks backwards - compatibly - -We want to put the XO board clocks into the dt files, but we also -need to be backwards compatible with an older dtb. Add an API to -the common code to do this. This also makes a place for us to -handle the case when the RPM clock driver is enabled and we don't -want to register the fixed factor clock. - -Cc: Georgi Djakov -Signed-off-by: Stephen Boyd ---- - drivers/clk/qcom/common.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++ - drivers/clk/qcom/common.h | 4 +++ - 2 files changed, 91 insertions(+) - ---- a/drivers/clk/qcom/common.c -+++ b/drivers/clk/qcom/common.c -@@ -17,6 +17,7 @@ - #include - #include - #include -+#include - - #include "common.h" - #include "clk-rcg.h" -@@ -88,6 +89,92 @@ static void qcom_cc_gdsc_unregister(void - gdsc_unregister(data); - } - -+/* -+ * Backwards compatibility with old DTs. Register a pass-through factor 1/1 -+ * clock to translate 'path' clk into 'name' clk and regsiter the 'path' -+ * clk as a fixed rate clock if it isn't present. -+ */ -+static int _qcom_cc_register_board_clk(struct device *dev, const char *path, -+ const char *name, unsigned long rate, -+ bool add_factor) -+{ -+ struct device_node *node = NULL; -+ struct device_node *clocks_node; -+ struct clk_fixed_factor *factor; -+ struct clk_fixed_rate *fixed; -+ struct clk *clk; -+ struct clk_init_data init_data = { }; -+ -+ clocks_node = of_find_node_by_path("/clocks"); -+ if (clocks_node) -+ node = of_find_node_by_name(clocks_node, path); -+ of_node_put(clocks_node); -+ -+ if (!node) { -+ fixed = devm_kzalloc(dev, sizeof(*fixed), GFP_KERNEL); -+ if (!fixed) -+ return -EINVAL; -+ -+ fixed->fixed_rate = rate; -+ fixed->hw.init = &init_data; -+ -+ init_data.name = path; -+ init_data.flags = CLK_IS_ROOT; -+ init_data.ops = &clk_fixed_rate_ops; -+ -+ clk = devm_clk_register(dev, &fixed->hw); -+ if (IS_ERR(clk)) -+ return PTR_ERR(clk); -+ } -+ of_node_put(node); -+ -+ if (add_factor) { -+ factor = devm_kzalloc(dev, sizeof(*factor), GFP_KERNEL); -+ if (!factor) -+ return -EINVAL; -+ -+ factor->mult = factor->div = 1; -+ factor->hw.init = &init_data; -+ -+ init_data.name = name; -+ init_data.parent_names = &path; -+ init_data.num_parents = 1; -+ init_data.flags = 0; -+ init_data.ops = &clk_fixed_factor_ops; -+ -+ clk = devm_clk_register(dev, &factor->hw); -+ if (IS_ERR(clk)) -+ return PTR_ERR(clk); -+ } -+ -+ return 0; -+} -+ -+int qcom_cc_register_board_clk(struct device *dev, const char *path, -+ const char *name, unsigned long rate) -+{ -+ bool add_factor = true; -+ struct device_node *node; -+ -+ /* The RPM clock driver will add the factor clock if present */ -+ if (IS_ENABLED(CONFIG_QCOM_RPMCC)) { -+ node = of_find_compatible_node(NULL, NULL, "qcom,rpmcc"); -+ if (of_device_is_available(node)) -+ add_factor = false; -+ of_node_put(node); -+ } -+ -+ return _qcom_cc_register_board_clk(dev, path, name, rate, add_factor); -+} -+EXPORT_SYMBOL_GPL(qcom_cc_register_board_clk); -+ -+int qcom_cc_register_sleep_clk(struct device *dev) -+{ -+ return _qcom_cc_register_board_clk(dev, "sleep_clk", "sleep_clk_src", -+ 32768, true); -+} -+EXPORT_SYMBOL_GPL(qcom_cc_register_sleep_clk); -+ - int qcom_cc_really_probe(struct platform_device *pdev, - const struct qcom_cc_desc *desc, struct regmap *regmap) - { ---- a/drivers/clk/qcom/common.h -+++ b/drivers/clk/qcom/common.h -@@ -37,6 +37,10 @@ extern const struct freq_tbl *qcom_find_ - extern int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, - u8 src); - -+extern int qcom_cc_register_board_clk(struct device *dev, const char *path, -+ const char *name, unsigned long rate); -+extern int qcom_cc_register_sleep_clk(struct device *dev); -+ - extern struct regmap *qcom_cc_map(struct platform_device *pdev, - const struct qcom_cc_desc *desc); - extern int qcom_cc_really_probe(struct platform_device *pdev, diff --git a/target/linux/ipq806x/patches-4.4/002-make-reset-control-ops-const.patch b/target/linux/ipq806x/patches-4.4/002-make-reset-control-ops-const.patch deleted file mode 100644 index 6261125473..0000000000 --- a/target/linux/ipq806x/patches-4.4/002-make-reset-control-ops-const.patch +++ /dev/null @@ -1,35 +0,0 @@ -From add479eeb1a208a31ab913ae7c97506a81383079 Mon Sep 17 00:00:00 2001 -From: Philipp Zabel -Date: Thu, 25 Feb 2016 10:45:12 +0100 -Subject: clk: qcom: Make reset_control_ops const - -The qcom_reset_ops structure is never modified. Make it const. - -Signed-off-by: Philipp Zabel -Signed-off-by: Stephen Boyd ---- - drivers/clk/qcom/reset.c | 2 +- - drivers/clk/qcom/reset.h | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - ---- a/drivers/clk/qcom/reset.c -+++ b/drivers/clk/qcom/reset.c -@@ -55,7 +55,7 @@ qcom_reset_deassert(struct reset_control - return regmap_update_bits(rst->regmap, map->reg, mask, 0); - } - --struct reset_control_ops qcom_reset_ops = { -+const struct reset_control_ops qcom_reset_ops = { - .reset = qcom_reset, - .assert = qcom_reset_assert, - .deassert = qcom_reset_deassert, ---- a/drivers/clk/qcom/reset.h -+++ b/drivers/clk/qcom/reset.h -@@ -32,6 +32,6 @@ struct qcom_reset_controller { - #define to_qcom_reset_controller(r) \ - container_of(r, struct qcom_reset_controller, rcdev); - --extern struct reset_control_ops qcom_reset_ops; -+extern const struct reset_control_ops qcom_reset_ops; - - #endif diff --git a/target/linux/ipq806x/patches-4.4/003-mv-cxo-pxo-xo-into-DT.patch b/target/linux/ipq806x/patches-4.4/003-mv-cxo-pxo-xo-into-DT.patch deleted file mode 100644 index 4932e702a2..0000000000 --- a/target/linux/ipq806x/patches-4.4/003-mv-cxo-pxo-xo-into-DT.patch +++ /dev/null @@ -1,172 +0,0 @@ -From a085f877a882b465fce74188c9d8efd12bd5acd4 Mon Sep 17 00:00:00 2001 -From: Stephen Boyd -Date: Mon, 26 Oct 2015 18:10:09 -0700 -Subject: clk: qcom: Move cxo/pxo/xo into dt files - -Put these clocks into the dt files instead of registering them -from C code. This provides a few benefits. It allows us to -specify the frequency of these clocks at the board level instead -of hard-coding them in the driver. It allows us to insert an RPM -clock in between the consumers of the crystals and the actual -clock. And finally, it helps us transition the GCC driver to use -RPM clocks when that configuration is enabled. - -Cc: Georgi Djakov -Signed-off-by: Stephen Boyd ---- - drivers/clk/qcom/gcc-apq8084.c | 16 +++++++--------- - drivers/clk/qcom/gcc-ipq806x.c | 14 ++++++-------- - drivers/clk/qcom/gcc-msm8660.c | 15 +++++++-------- - drivers/clk/qcom/gcc-msm8960.c | 14 ++++++-------- - drivers/clk/qcom/gcc-msm8974.c | 17 +++++++---------- - 5 files changed, 33 insertions(+), 43 deletions(-) - ---- a/drivers/clk/qcom/gcc-apq8084.c -+++ b/drivers/clk/qcom/gcc-apq8084.c -@@ -3607,18 +3607,16 @@ MODULE_DEVICE_TABLE(of, gcc_apq8084_matc - - static int gcc_apq8084_probe(struct platform_device *pdev) - { -- struct clk *clk; -+ int ret; - struct device *dev = &pdev->dev; - -- /* Temporary until RPM clocks supported */ -- clk = clk_register_fixed_rate(dev, "xo", NULL, CLK_IS_ROOT, 19200000); -- if (IS_ERR(clk)) -- return PTR_ERR(clk); -+ ret = qcom_cc_register_board_clk(dev, "xo_board", "xo", 19200000); -+ if (ret) -+ return ret; - -- clk = clk_register_fixed_rate(dev, "sleep_clk_src", NULL, -- CLK_IS_ROOT, 32768); -- if (IS_ERR(clk)) -- return PTR_ERR(clk); -+ ret = qcom_cc_register_sleep_clk(dev); -+ if (ret) -+ return ret; - - return qcom_cc_probe(pdev, &gcc_apq8084_desc); - } ---- a/drivers/clk/qcom/gcc-ipq806x.c -+++ b/drivers/clk/qcom/gcc-ipq806x.c -@@ -3023,19 +3023,17 @@ MODULE_DEVICE_TABLE(of, gcc_ipq806x_matc - - 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); -- if (IS_ERR(clk)) -- return PTR_ERR(clk); -+ ret = qcom_cc_register_board_clk(dev, "cxo_board", "cxo", 25000000); -+ if (ret) -+ return ret; - -- clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 25000000); -- if (IS_ERR(clk)) -- return PTR_ERR(clk); -+ ret = qcom_cc_register_board_clk(dev, "pxo_board", "pxo", 25000000); -+ if (ret) -+ return ret; - - ret = qcom_cc_probe(pdev, &gcc_ipq806x_desc); - if (ret) ---- a/drivers/clk/qcom/gcc-msm8660.c -+++ b/drivers/clk/qcom/gcc-msm8660.c -@@ -2720,17 +2720,16 @@ MODULE_DEVICE_TABLE(of, gcc_msm8660_matc - - static int gcc_msm8660_probe(struct platform_device *pdev) - { -- struct clk *clk; -+ int ret; - 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); -+ ret = qcom_cc_register_board_clk(dev, "cxo_board", "cxo", 19200000); -+ if (ret) -+ return ret; - -- clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 27000000); -- if (IS_ERR(clk)) -- return PTR_ERR(clk); -+ ret = qcom_cc_register_board_clk(dev, "pxo_board", "pxo", 27000000); -+ if (ret) -+ return ret; - - return qcom_cc_probe(pdev, &gcc_msm8660_desc); - } ---- a/drivers/clk/qcom/gcc-msm8960.c -+++ b/drivers/clk/qcom/gcc-msm8960.c -@@ -3503,7 +3503,6 @@ MODULE_DEVICE_TABLE(of, gcc_msm8960_matc - - static int gcc_msm8960_probe(struct platform_device *pdev) - { -- struct clk *clk; - struct device *dev = &pdev->dev; - const struct of_device_id *match; - struct platform_device *tsens; -@@ -3513,14 +3512,13 @@ static int gcc_msm8960_probe(struct plat - if (!match) - return -EINVAL; - -- /* 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); -+ ret = qcom_cc_register_board_clk(dev, "cxo_board", "cxo", 19200000); -+ if (ret) -+ return ret; - -- clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 27000000); -- if (IS_ERR(clk)) -- return PTR_ERR(clk); -+ ret = qcom_cc_register_board_clk(dev, "pxo_board", "pxo", 27000000); -+ if (ret) -+ return ret; - - ret = qcom_cc_probe(pdev, match->data); - if (ret) ---- a/drivers/clk/qcom/gcc-msm8974.c -+++ b/drivers/clk/qcom/gcc-msm8974.c -@@ -2717,7 +2717,7 @@ static void msm8974_pro_clock_override(v - - static int gcc_msm8974_probe(struct platform_device *pdev) - { -- struct clk *clk; -+ int ret; - struct device *dev = &pdev->dev; - bool pro; - const struct of_device_id *id; -@@ -2730,16 +2730,13 @@ static int gcc_msm8974_probe(struct plat - if (pro) - msm8974_pro_clock_override(); - -- /* Temporary until RPM clocks supported */ -- clk = clk_register_fixed_rate(dev, "xo", NULL, CLK_IS_ROOT, 19200000); -- if (IS_ERR(clk)) -- return PTR_ERR(clk); -- -- /* Should move to DT node? */ -- clk = clk_register_fixed_rate(dev, "sleep_clk_src", NULL, -- CLK_IS_ROOT, 32768); -- if (IS_ERR(clk)) -- return PTR_ERR(clk); -+ ret = qcom_cc_register_board_clk(dev, "xo_board", "xo", 19200000); -+ if (ret) -+ return ret; -+ -+ ret = qcom_cc_register_sleep_clk(dev); -+ if (ret) -+ return ret; - - return qcom_cc_probe(pdev, &gcc_msm8974_desc); - } diff --git a/target/linux/ipq806x/patches-4.4/005-mfd-qcom_rpm-Add-missing-of_node_put-after-calling-of_parse_phandle.patch b/target/linux/ipq806x/patches-4.4/005-mfd-qcom_rpm-Add-missing-of_node_put-after-calling-of_parse_phandle.patch deleted file mode 100644 index b3cef7b3fd..0000000000 --- a/target/linux/ipq806x/patches-4.4/005-mfd-qcom_rpm-Add-missing-of_node_put-after-calling-of_parse_phandle.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 349290fc9e761aaef6d6882721189f668ec5ff49 Mon Sep 17 00:00:00 2001 -From: Peter Chen -Date: Fri, 15 Jul 2016 17:38:46 +0800 -Subject: mfd: qcom_rpm: Add missing of_node_put after calling of_parse_phandle - -of_node_put needs to be called when the device node which is got -from of_parse_phandle has finished using. - -Signed-off-by: Peter Chen -Reviewed-by: Bjorn Andersson -Signed-off-by: Lee Jones ---- - drivers/mfd/qcom_rpm.c | 1 + - 1 file changed, 1 insertion(+) - ---- a/drivers/mfd/qcom_rpm.c -+++ b/drivers/mfd/qcom_rpm.c -@@ -538,6 +538,7 @@ static int qcom_rpm_probe(struct platfor - } - - rpm->ipc_regmap = syscon_node_to_regmap(syscon_np); -+ of_node_put(syscon_np); - if (IS_ERR(rpm->ipc_regmap)) - return PTR_ERR(rpm->ipc_regmap); - diff --git a/target/linux/ipq806x/patches-4.4/006-mfd-qcom_rpm-Handle-message-RAM-clock.patch b/target/linux/ipq806x/patches-4.4/006-mfd-qcom_rpm-Handle-message-RAM-clock.patch deleted file mode 100644 index 96b768d97c..0000000000 --- a/target/linux/ipq806x/patches-4.4/006-mfd-qcom_rpm-Handle-message-RAM-clock.patch +++ /dev/null @@ -1,90 +0,0 @@ -From 3526403353c2a1b94c3181f900582626d23c339b Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Thu, 18 Aug 2016 20:40:45 +0200 -Subject: mfd: qcom_rpm: Handle message RAM clock -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The MSM8660, APQ8060, IPQ806x and MSM8960 have a GCC clock -to the message RAM used by the RPM. This needs to be enabled -for messages to pass through. This is a crude solution that -simply prepare/enable at probe() and disable/unprepare -at remove(). More elaborate PM is probably possible to -add later. - -The construction uses IS_ERR() to gracefully handle the -platforms that do not provide a message RAM clock. It will -bail out of probe only if the clock is hitting a probe -deferral situation. - -Of course this requires the proper device tree set-up: - -rpm: rpm@104000 { - compatible = "qcom,rpm-msm8660"; - clocks = <&gcc RPM_MSG_RAM_H_CLK>; - clock-names = "ram"; - ... -}; - -I have provided this in the MSM8660 device tree, and will -provide patches for the other targets. - -Cc: Björn Andersson -Signed-off-by: Linus Walleij -Signed-off-by: Lee Jones ---- - drivers/mfd/qcom_rpm.c | 20 ++++++++++++++++++++ - 1 file changed, 20 insertions(+) - ---- a/drivers/mfd/qcom_rpm.c -+++ b/drivers/mfd/qcom_rpm.c -@@ -21,6 +21,7 @@ - #include - #include - #include -+#include - - #include - -@@ -48,6 +49,7 @@ struct qcom_rpm { - struct regmap *ipc_regmap; - unsigned ipc_offset; - unsigned ipc_bit; -+ struct clk *ramclk; - - struct completion ack; - struct mutex lock; -@@ -503,6 +505,20 @@ static int qcom_rpm_probe(struct platfor - mutex_init(&rpm->lock); - init_completion(&rpm->ack); - -+ /* Enable message RAM clock */ -+ rpm->ramclk = devm_clk_get(&pdev->dev, "ram"); -+ if (IS_ERR(rpm->ramclk)) { -+ ret = PTR_ERR(rpm->ramclk); -+ if (ret == -EPROBE_DEFER) -+ return ret; -+ /* -+ * Fall through in all other cases, as the clock is -+ * optional. (Does not exist on all platforms.) -+ */ -+ rpm->ramclk = NULL; -+ } -+ clk_prepare_enable(rpm->ramclk); /* Accepts NULL */ -+ - irq_ack = platform_get_irq_byname(pdev, "ack"); - if (irq_ack < 0) { - dev_err(&pdev->dev, "required ack interrupt missing\n"); -@@ -621,7 +637,11 @@ static int qcom_rpm_probe(struct platfor - - static int qcom_rpm_remove(struct platform_device *pdev) - { -+ struct qcom_rpm *rpm = dev_get_drvdata(&pdev->dev); -+ - of_platform_depopulate(&pdev->dev); -+ clk_disable_unprepare(rpm->ramclk); -+ - return 0; - } - diff --git a/target/linux/ipq806x/patches-4.4/009-1-watchdog-core-add-restart-handler-support.patch b/target/linux/ipq806x/patches-4.4/009-1-watchdog-core-add-restart-handler-support.patch deleted file mode 100644 index 0534e78a27..0000000000 --- a/target/linux/ipq806x/patches-4.4/009-1-watchdog-core-add-restart-handler-support.patch +++ /dev/null @@ -1,205 +0,0 @@ -From 2165bf524da5f5e496d1cdb8c5afae1345ecce1e Mon Sep 17 00:00:00 2001 -From: Damien Riegel -Date: Mon, 16 Nov 2015 12:27:59 -0500 -Subject: watchdog: core: add restart handler support - -Many watchdog drivers implement the same code to register a restart -handler. This patch provides a generic way to set such a function. - -The patch adds a new restart watchdog operation. If a restart priority -greater than 0 is needed, the driver can call -watchdog_set_restart_priority to set it. - -Suggested-by: Vivien Didelot -Signed-off-by: Damien Riegel -Reviewed-by: Guenter Roeck -Reviewed-by: Vivien Didelot -Signed-off-by: Guenter Roeck -Signed-off-by: Wim Van Sebroeck ---- - Documentation/watchdog/watchdog-kernel-api.txt | 19 ++++++++++ - drivers/watchdog/watchdog_core.c | 48 ++++++++++++++++++++++++++ - include/linux/watchdog.h | 6 ++++ - 3 files changed, 73 insertions(+) - ---- a/Documentation/watchdog/watchdog-kernel-api.txt -+++ b/Documentation/watchdog/watchdog-kernel-api.txt -@@ -53,6 +53,7 @@ struct watchdog_device { - unsigned int timeout; - unsigned int min_timeout; - unsigned int max_timeout; -+ struct notifier_block restart_nb; - void *driver_data; - struct mutex lock; - unsigned long status; -@@ -75,6 +76,10 @@ It contains following fields: - * timeout: the watchdog timer's timeout value (in seconds). - * min_timeout: the watchdog timer's minimum timeout value (in seconds). - * max_timeout: the watchdog timer's maximum timeout value (in seconds). -+* restart_nb: notifier block that is registered for machine restart, for -+ internal use only. If a watchdog is capable of restarting the machine, it -+ should define ops->restart. Priority can be changed through -+ watchdog_set_restart_priority. - * bootstatus: status of the device after booting (reported with watchdog - WDIOF_* status bits). - * driver_data: a pointer to the drivers private data of a watchdog device. -@@ -100,6 +105,7 @@ struct watchdog_ops { - unsigned int (*status)(struct watchdog_device *); - int (*set_timeout)(struct watchdog_device *, unsigned int); - unsigned int (*get_timeleft)(struct watchdog_device *); -+ int (*restart)(struct watchdog_device *); - void (*ref)(struct watchdog_device *); - void (*unref)(struct watchdog_device *); - long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); -@@ -164,6 +170,8 @@ they are supported. These optional routi - (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the - watchdog's info structure). - * get_timeleft: this routines returns the time that's left before a reset. -+* restart: this routine restarts the machine. It returns 0 on success or a -+ negative errno code for failure. - * ref: the operation that calls kref_get on the kref of a dynamically - allocated watchdog_device struct. - * unref: the operation that calls kref_put on the kref of a dynamically -@@ -231,3 +239,14 @@ the device tree (if the module timeout p - to set the default timeout value as timeout value in the watchdog_device and - then use this function to set the user "preferred" timeout value. - This routine returns zero on success and a negative errno code for failure. -+ -+To change the priority of the restart handler the following helper should be -+used: -+ -+void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority); -+ -+User should follow the following guidelines for setting the priority: -+* 0: should be called in last resort, has limited restart capabilities -+* 128: default restart handler, use if no other handler is expected to be -+ available, and/or if restart is sufficient to restart the entire system -+* 255: highest priority, will preempt all other restart handlers ---- a/drivers/watchdog/watchdog_core.c -+++ b/drivers/watchdog/watchdog_core.c -@@ -32,6 +32,7 @@ - #include /* For standard types */ - #include /* For the -ENODEV/... values */ - #include /* For printk/panic/... */ -+#include /* For restart handler */ - #include /* For watchdog specific items */ - #include /* For __init/__exit/... */ - #include /* For ida_* macros */ -@@ -137,6 +138,41 @@ int watchdog_init_timeout(struct watchdo - } - EXPORT_SYMBOL_GPL(watchdog_init_timeout); - -+static int watchdog_restart_notifier(struct notifier_block *nb, -+ unsigned long action, void *data) -+{ -+ struct watchdog_device *wdd = container_of(nb, struct watchdog_device, -+ restart_nb); -+ -+ int ret; -+ -+ ret = wdd->ops->restart(wdd); -+ if (ret) -+ return NOTIFY_BAD; -+ -+ return NOTIFY_DONE; -+} -+ -+/** -+ * watchdog_set_restart_priority - Change priority of restart handler -+ * @wdd: watchdog device -+ * @priority: priority of the restart handler, should follow these guidelines: -+ * 0: use watchdog's restart function as last resort, has limited restart -+ * capabilies -+ * 128: default restart handler, use if no other handler is expected to be -+ * available and/or if restart is sufficient to restart the entire system -+ * 255: preempt all other handlers -+ * -+ * If a wdd->ops->restart function is provided when watchdog_register_device is -+ * called, it will be registered as a restart handler with the priority given -+ * here. -+ */ -+void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority) -+{ -+ wdd->restart_nb.priority = priority; -+} -+EXPORT_SYMBOL_GPL(watchdog_set_restart_priority); -+ - static int __watchdog_register_device(struct watchdog_device *wdd) - { - int ret, id = -1, devno; -@@ -202,6 +238,15 @@ static int __watchdog_register_device(st - return ret; - } - -+ if (wdd->ops->restart) { -+ wdd->restart_nb.notifier_call = watchdog_restart_notifier; -+ -+ ret = register_restart_handler(&wdd->restart_nb); -+ if (ret) -+ dev_warn(wdd->dev, "Cannot register restart handler (%d)\n", -+ ret); -+ } -+ - return 0; - } - -@@ -238,6 +283,9 @@ static void __watchdog_unregister_device - if (wdd == NULL) - return; - -+ if (wdd->ops->restart) -+ unregister_restart_handler(&wdd->restart_nb); -+ - devno = wdd->cdev.dev; - ret = watchdog_dev_unregister(wdd); - if (ret) ---- a/include/linux/watchdog.h -+++ b/include/linux/watchdog.h -@@ -12,6 +12,7 @@ - #include - #include - #include -+#include - #include - - struct watchdog_ops; -@@ -26,6 +27,7 @@ struct watchdog_device; - * @status: The routine that shows the status of the watchdog device. - * @set_timeout:The routine for setting the watchdog devices timeout value (in seconds). - * @get_timeleft:The routine that gets the time left before a reset (in seconds). -+ * @restart: The routine for restarting the machine. - * @ref: The ref operation for dyn. allocated watchdog_device structs - * @unref: The unref operation for dyn. allocated watchdog_device structs - * @ioctl: The routines that handles extra ioctl calls. -@@ -45,6 +47,7 @@ struct watchdog_ops { - unsigned int (*status)(struct watchdog_device *); - int (*set_timeout)(struct watchdog_device *, unsigned int); - unsigned int (*get_timeleft)(struct watchdog_device *); -+ int (*restart)(struct watchdog_device *); - void (*ref)(struct watchdog_device *); - void (*unref)(struct watchdog_device *); - long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); -@@ -62,6 +65,7 @@ struct watchdog_ops { - * @timeout: The watchdog devices timeout value (in seconds). - * @min_timeout:The watchdog devices minimum timeout value (in seconds). - * @max_timeout:The watchdog devices maximum timeout value (in seconds). -+ * @restart_nb: The notifier block to register a restart function. - * @driver-data:Pointer to the drivers private data. - * @lock: Lock for watchdog core internal use only. - * @status: Field that contains the devices internal status bits. -@@ -88,6 +92,7 @@ struct watchdog_device { - unsigned int timeout; - unsigned int min_timeout; - unsigned int max_timeout; -+ struct notifier_block restart_nb; - void *driver_data; - struct mutex lock; - unsigned long status; -@@ -142,6 +147,7 @@ static inline void *watchdog_get_drvdata - } - - /* drivers/watchdog/watchdog_core.c */ -+void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority); - extern int watchdog_init_timeout(struct watchdog_device *wdd, - unsigned int timeout_parm, struct device *dev); - extern int watchdog_register_device(struct watchdog_device *); diff --git a/target/linux/ipq806x/patches-4.4/009-2-watchdog-core-add-reboot-notifier-support.patch b/target/linux/ipq806x/patches-4.4/009-2-watchdog-core-add-reboot-notifier-support.patch deleted file mode 100644 index 2afa4e566b..0000000000 --- a/target/linux/ipq806x/patches-4.4/009-2-watchdog-core-add-reboot-notifier-support.patch +++ /dev/null @@ -1,160 +0,0 @@ -From e131319669e0ef5e6fcd75174daeffa40492135c Mon Sep 17 00:00:00 2001 -From: Damien Riegel -Date: Fri, 20 Nov 2015 16:54:51 -0500 -Subject: watchdog: core: add reboot notifier support - -Many watchdog drivers register a reboot notifier in order to stop the -watchdog on system reboot. Thus we can factorize this code in the -watchdog core. - -For that purpose, a new notifier block is added in watchdog_device for -internal use only, as well as a new watchdog_stop_on_reboot helper -function. - -If this helper is called, watchdog core registers the related notifier -block and will stop the watchdog when SYS_HALT or SYS_DOWN is received. - -Since this operation can be critical on some platforms, abort the device -registration if the reboot notifier registration fails. - -Suggested-by: Vivien Didelot -Signed-off-by: Damien Riegel -Reviewed-by: Vivien Didelot -Signed-off-by: Guenter Roeck -Signed-off-by: Wim Van Sebroeck ---- - Documentation/watchdog/watchdog-kernel-api.txt | 8 ++++++ - drivers/watchdog/watchdog_core.c | 37 ++++++++++++++++++++++++++ - include/linux/watchdog.h | 9 +++++++ - 3 files changed, 54 insertions(+) - ---- a/Documentation/watchdog/watchdog-kernel-api.txt -+++ b/Documentation/watchdog/watchdog-kernel-api.txt -@@ -53,6 +53,7 @@ struct watchdog_device { - unsigned int timeout; - unsigned int min_timeout; - unsigned int max_timeout; -+ struct notifier_block reboot_nb; - struct notifier_block restart_nb; - void *driver_data; - struct mutex lock; -@@ -76,6 +77,9 @@ It contains following fields: - * timeout: the watchdog timer's timeout value (in seconds). - * min_timeout: the watchdog timer's minimum timeout value (in seconds). - * max_timeout: the watchdog timer's maximum timeout value (in seconds). -+* reboot_nb: notifier block that is registered for reboot notifications, for -+ internal use only. If the driver calls watchdog_stop_on_reboot, watchdog core -+ will stop the watchdog on such notifications. - * restart_nb: notifier block that is registered for machine restart, for - internal use only. If a watchdog is capable of restarting the machine, it - should define ops->restart. Priority can be changed through -@@ -240,6 +244,10 @@ to set the default timeout value as time - then use this function to set the user "preferred" timeout value. - This routine returns zero on success and a negative errno code for failure. - -+To disable the watchdog on reboot, the user must call the following helper: -+ -+static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd); -+ - To change the priority of the restart handler the following helper should be - used: - ---- a/drivers/watchdog/watchdog_core.c -+++ b/drivers/watchdog/watchdog_core.c -@@ -138,6 +138,25 @@ int watchdog_init_timeout(struct watchdo - } - EXPORT_SYMBOL_GPL(watchdog_init_timeout); - -+static int watchdog_reboot_notifier(struct notifier_block *nb, -+ unsigned long code, void *data) -+{ -+ struct watchdog_device *wdd = container_of(nb, struct watchdog_device, -+ reboot_nb); -+ -+ if (code == SYS_DOWN || code == SYS_HALT) { -+ if (watchdog_active(wdd)) { -+ int ret; -+ -+ ret = wdd->ops->stop(wdd); -+ if (ret) -+ return NOTIFY_BAD; -+ } -+ } -+ -+ return NOTIFY_DONE; -+} -+ - static int watchdog_restart_notifier(struct notifier_block *nb, - unsigned long action, void *data) - { -@@ -238,6 +257,21 @@ static int __watchdog_register_device(st - return ret; - } - -+ if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) { -+ wdd->reboot_nb.notifier_call = watchdog_reboot_notifier; -+ -+ ret = register_reboot_notifier(&wdd->reboot_nb); -+ if (ret) { -+ dev_err(wdd->dev, "Cannot register reboot notifier (%d)\n", -+ ret); -+ watchdog_dev_unregister(wdd); -+ device_destroy(watchdog_class, devno); -+ ida_simple_remove(&watchdog_ida, wdd->id); -+ wdd->dev = NULL; -+ return ret; -+ } -+ } -+ - if (wdd->ops->restart) { - wdd->restart_nb.notifier_call = watchdog_restart_notifier; - -@@ -286,6 +320,9 @@ static void __watchdog_unregister_device - if (wdd->ops->restart) - unregister_restart_handler(&wdd->restart_nb); - -+ if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) -+ unregister_reboot_notifier(&wdd->reboot_nb); -+ - devno = wdd->cdev.dev; - ret = watchdog_dev_unregister(wdd); - if (ret) ---- a/include/linux/watchdog.h -+++ b/include/linux/watchdog.h -@@ -65,6 +65,7 @@ struct watchdog_ops { - * @timeout: The watchdog devices timeout value (in seconds). - * @min_timeout:The watchdog devices minimum timeout value (in seconds). - * @max_timeout:The watchdog devices maximum timeout value (in seconds). -+ * @reboot_nb: The notifier block to stop watchdog on reboot. - * @restart_nb: The notifier block to register a restart function. - * @driver-data:Pointer to the drivers private data. - * @lock: Lock for watchdog core internal use only. -@@ -92,6 +93,7 @@ struct watchdog_device { - unsigned int timeout; - unsigned int min_timeout; - unsigned int max_timeout; -+ struct notifier_block reboot_nb; - struct notifier_block restart_nb; - void *driver_data; - struct mutex lock; -@@ -102,6 +104,7 @@ struct watchdog_device { - #define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ - #define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */ - #define WDOG_UNREGISTERED 4 /* Has the device been unregistered */ -+#define WDOG_STOP_ON_REBOOT 5 /* Should be stopped on reboot */ - struct list_head deferred; - }; - -@@ -121,6 +124,12 @@ static inline void watchdog_set_nowayout - set_bit(WDOG_NO_WAY_OUT, &wdd->status); - } - -+/* Use the following function to stop the watchdog on reboot */ -+static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd) -+{ -+ set_bit(WDOG_STOP_ON_REBOOT, &wdd->status); -+} -+ - /* Use the following function to check if a timeout value is invalid */ - static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigned int t) - { diff --git a/target/linux/ipq806x/patches-4.4/009-3-watchdog-Use-static-struct-class-watchdog_class-instead-of-pointer.patch b/target/linux/ipq806x/patches-4.4/009-3-watchdog-Use-static-struct-class-watchdog_class-instead-of-pointer.patch deleted file mode 100644 index 1906a1f4e5..0000000000 --- a/target/linux/ipq806x/patches-4.4/009-3-watchdog-Use-static-struct-class-watchdog_class-instead-of-pointer.patch +++ /dev/null @@ -1,111 +0,0 @@ -From 906d7a5cfeda508e7361f021605579a00cd82815 Mon Sep 17 00:00:00 2001 -From: Pratyush Anand -Date: Thu, 17 Dec 2015 17:53:58 +0530 -Subject: watchdog: Use static struct class watchdog_class in stead of pointer - -We need few sysfs attributes to know different status of a watchdog device. -To do that, we need to associate .dev_groups with watchdog_class. So -convert it from pointer to static. -Putting this static struct in watchdog_dev.c, so that static device -attributes defined in that file can be attached to it. - -Signed-off-by: Pratyush Anand -Suggested-by: Guenter Roeck -Reviewed-by: Guenter Roeck -Signed-off-by: Guenter Roeck -Signed-off-by: Wim Van Sebroeck ---- - drivers/watchdog/watchdog_core.c | 15 ++------------- - drivers/watchdog/watchdog_core.h | 2 +- - drivers/watchdog/watchdog_dev.c | 26 ++++++++++++++++++++++---- - 3 files changed, 25 insertions(+), 18 deletions(-) - ---- a/drivers/watchdog/watchdog_core.c -+++ b/drivers/watchdog/watchdog_core.c -@@ -370,19 +370,9 @@ static int __init watchdog_deferred_regi - - static int __init watchdog_init(void) - { -- int err; -- -- watchdog_class = class_create(THIS_MODULE, "watchdog"); -- if (IS_ERR(watchdog_class)) { -- pr_err("couldn't create class\n"); -+ watchdog_class = watchdog_dev_init(); -+ if (IS_ERR(watchdog_class)) - return PTR_ERR(watchdog_class); -- } -- -- err = watchdog_dev_init(); -- if (err < 0) { -- class_destroy(watchdog_class); -- return err; -- } - - watchdog_deferred_registration(); - return 0; -@@ -391,7 +381,6 @@ static int __init watchdog_init(void) - static void __exit watchdog_exit(void) - { - watchdog_dev_exit(); -- class_destroy(watchdog_class); - ida_destroy(&watchdog_ida); - } - ---- a/drivers/watchdog/watchdog_core.h -+++ b/drivers/watchdog/watchdog_core.h -@@ -33,5 +33,5 @@ - */ - extern int watchdog_dev_register(struct watchdog_device *); - extern int watchdog_dev_unregister(struct watchdog_device *); --extern int __init watchdog_dev_init(void); -+extern struct class * __init watchdog_dev_init(void); - extern void __exit watchdog_dev_exit(void); ---- a/drivers/watchdog/watchdog_dev.c -+++ b/drivers/watchdog/watchdog_dev.c -@@ -581,18 +581,35 @@ int watchdog_dev_unregister(struct watch - return 0; - } - -+static struct class watchdog_class = { -+ .name = "watchdog", -+ .owner = THIS_MODULE, -+}; -+ - /* - * watchdog_dev_init: init dev part of watchdog core - * - * Allocate a range of chardev nodes to use for watchdog devices - */ - --int __init watchdog_dev_init(void) -+struct class * __init watchdog_dev_init(void) - { -- int err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog"); -- if (err < 0) -+ int err; -+ -+ err = class_register(&watchdog_class); -+ if (err < 0) { -+ pr_err("couldn't register class\n"); -+ return ERR_PTR(err); -+ } -+ -+ err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog"); -+ if (err < 0) { - pr_err("watchdog: unable to allocate char dev region\n"); -- return err; -+ class_unregister(&watchdog_class); -+ return ERR_PTR(err); -+ } -+ -+ return &watchdog_class; - } - - /* -@@ -604,4 +621,5 @@ int __init watchdog_dev_init(void) - void __exit watchdog_dev_exit(void) - { - unregister_chrdev_region(watchdog_devt, MAX_DOGS); -+ class_unregister(&watchdog_class); - } diff --git a/target/linux/ipq806x/patches-4.4/009-4-watchdog-Read-device-status-through-sysfs-attributes.patch b/target/linux/ipq806x/patches-4.4/009-4-watchdog-Read-device-status-through-sysfs-attributes.patch deleted file mode 100644 index fd4b38a9fb..0000000000 --- a/target/linux/ipq806x/patches-4.4/009-4-watchdog-Read-device-status-through-sysfs-attributes.patch +++ /dev/null @@ -1,260 +0,0 @@ -From 33b711269ade3f6bc9d9d15e4343e6fa922d999b Mon Sep 17 00:00:00 2001 -From: Pratyush Anand -Date: Thu, 17 Dec 2015 17:53:59 +0530 -Subject: watchdog: Read device status through sysfs attributes - -This patch adds following attributes to watchdog device's sysfs interface -to read its different status. - -* state - reads whether device is active or not -* identity - reads Watchdog device's identity string. -* timeout - reads current timeout. -* timeleft - reads timeleft before watchdog generates a reset -* bootstatus - reads status of the watchdog device at boot -* status - reads watchdog device's internal status bits -* nowayout - reads whether nowayout feature was set or not - -Testing with iTCO_wdt: - # cd /sys/class/watchdog/watchdog1/ - # ls -bootstatus dev device identity nowayout power state -subsystem timeleft timeout uevent - # cat identity -iTCO_wdt - # cat timeout -30 - # cat state -inactive - # echo > /dev/watchdog1 - # cat timeleft -26 - # cat state -active - # cat bootstatus -0 - # cat nowayout -0 - -Signed-off-by: Pratyush Anand -Reviewed-by: Guenter Roeck -Signed-off-by: Guenter Roeck -Signed-off-by: Wim Van Sebroeck ---- - Documentation/ABI/testing/sysfs-class-watchdog | 51 +++++++++++ - drivers/watchdog/Kconfig | 7 ++ - drivers/watchdog/watchdog_core.c | 2 +- - drivers/watchdog/watchdog_dev.c | 114 +++++++++++++++++++++++++ - 4 files changed, 173 insertions(+), 1 deletion(-) - create mode 100644 Documentation/ABI/testing/sysfs-class-watchdog - ---- /dev/null -+++ b/Documentation/ABI/testing/sysfs-class-watchdog -@@ -0,0 +1,51 @@ -+What: /sys/class/watchdog/watchdogn/bootstatus -+Date: August 2015 -+Contact: Wim Van Sebroeck -+Description: -+ It is a read only file. It contains status of the watchdog -+ device at boot. It is equivalent to WDIOC_GETBOOTSTATUS of -+ ioctl interface. -+ -+What: /sys/class/watchdog/watchdogn/identity -+Date: August 2015 -+Contact: Wim Van Sebroeck -+Description: -+ It is a read only file. It contains identity string of -+ watchdog device. -+ -+What: /sys/class/watchdog/watchdogn/nowayout -+Date: August 2015 -+Contact: Wim Van Sebroeck -+Description: -+ It is a read only file. While reading, it gives '1' if that -+ device supports nowayout feature else, it gives '0'. -+ -+What: /sys/class/watchdog/watchdogn/state -+Date: August 2015 -+Contact: Wim Van Sebroeck -+Description: -+ It is a read only file. It gives active/inactive status of -+ watchdog device. -+ -+What: /sys/class/watchdog/watchdogn/status -+Date: August 2015 -+Contact: Wim Van Sebroeck -+Description: -+ It is a read only file. It contains watchdog device's -+ internal status bits. It is equivalent to WDIOC_GETSTATUS -+ of ioctl interface. -+ -+What: /sys/class/watchdog/watchdogn/timeleft -+Date: August 2015 -+Contact: Wim Van Sebroeck -+Description: -+ It is a read only file. It contains value of time left for -+ reset generation. It is equivalent to WDIOC_GETTIMELEFT of -+ ioctl interface. -+ -+What: /sys/class/watchdog/watchdogn/timeout -+Date: August 2015 -+Contact: Wim Van Sebroeck -+Description: -+ It is a read only file. It is read to know about current -+ value of timeout programmed. ---- a/drivers/watchdog/Kconfig -+++ b/drivers/watchdog/Kconfig -@@ -46,6 +46,13 @@ config WATCHDOG_NOWAYOUT - get killed. If you say Y here, the watchdog cannot be stopped once - it has been started. - -+config WATCHDOG_SYSFS -+ bool "Read different watchdog information through sysfs" -+ default n -+ help -+ Say Y here if you want to enable watchdog device status read through -+ sysfs attributes. -+ - # - # General Watchdog drivers - # ---- a/drivers/watchdog/watchdog_core.c -+++ b/drivers/watchdog/watchdog_core.c -@@ -249,7 +249,7 @@ static int __watchdog_register_device(st - - devno = wdd->cdev.dev; - wdd->dev = device_create(watchdog_class, wdd->parent, devno, -- NULL, "watchdog%d", wdd->id); -+ wdd, "watchdog%d", wdd->id); - if (IS_ERR(wdd->dev)) { - watchdog_dev_unregister(wdd); - ida_simple_remove(&watchdog_ida, id); ---- a/drivers/watchdog/watchdog_dev.c -+++ b/drivers/watchdog/watchdog_dev.c -@@ -247,6 +247,119 @@ out_timeleft: - return err; - } - -+#ifdef CONFIG_WATCHDOG_SYSFS -+static ssize_t nowayout_show(struct device *dev, struct device_attribute *attr, -+ char *buf) -+{ -+ struct watchdog_device *wdd = dev_get_drvdata(dev); -+ -+ return sprintf(buf, "%d\n", !!test_bit(WDOG_NO_WAY_OUT, &wdd->status)); -+} -+static DEVICE_ATTR_RO(nowayout); -+ -+static ssize_t status_show(struct device *dev, struct device_attribute *attr, -+ char *buf) -+{ -+ struct watchdog_device *wdd = dev_get_drvdata(dev); -+ ssize_t status; -+ unsigned int val; -+ -+ status = watchdog_get_status(wdd, &val); -+ if (!status) -+ status = sprintf(buf, "%u\n", val); -+ -+ return status; -+} -+static DEVICE_ATTR_RO(status); -+ -+static ssize_t bootstatus_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct watchdog_device *wdd = dev_get_drvdata(dev); -+ -+ return sprintf(buf, "%u\n", wdd->bootstatus); -+} -+static DEVICE_ATTR_RO(bootstatus); -+ -+static ssize_t timeleft_show(struct device *dev, struct device_attribute *attr, -+ char *buf) -+{ -+ struct watchdog_device *wdd = dev_get_drvdata(dev); -+ ssize_t status; -+ unsigned int val; -+ -+ status = watchdog_get_timeleft(wdd, &val); -+ if (!status) -+ status = sprintf(buf, "%u\n", val); -+ -+ return status; -+} -+static DEVICE_ATTR_RO(timeleft); -+ -+static ssize_t timeout_show(struct device *dev, struct device_attribute *attr, -+ char *buf) -+{ -+ struct watchdog_device *wdd = dev_get_drvdata(dev); -+ -+ return sprintf(buf, "%u\n", wdd->timeout); -+} -+static DEVICE_ATTR_RO(timeout); -+ -+static ssize_t identity_show(struct device *dev, struct device_attribute *attr, -+ char *buf) -+{ -+ struct watchdog_device *wdd = dev_get_drvdata(dev); -+ -+ return sprintf(buf, "%s\n", wdd->info->identity); -+} -+static DEVICE_ATTR_RO(identity); -+ -+static ssize_t state_show(struct device *dev, struct device_attribute *attr, -+ char *buf) -+{ -+ struct watchdog_device *wdd = dev_get_drvdata(dev); -+ -+ if (watchdog_active(wdd)) -+ return sprintf(buf, "active\n"); -+ -+ return sprintf(buf, "inactive\n"); -+} -+static DEVICE_ATTR_RO(state); -+ -+static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, -+ int n) -+{ -+ struct device *dev = container_of(kobj, struct device, kobj); -+ struct watchdog_device *wdd = dev_get_drvdata(dev); -+ umode_t mode = attr->mode; -+ -+ if (attr == &dev_attr_status.attr && !wdd->ops->status) -+ mode = 0; -+ else if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft) -+ mode = 0; -+ -+ return mode; -+} -+static struct attribute *wdt_attrs[] = { -+ &dev_attr_state.attr, -+ &dev_attr_identity.attr, -+ &dev_attr_timeout.attr, -+ &dev_attr_timeleft.attr, -+ &dev_attr_bootstatus.attr, -+ &dev_attr_status.attr, -+ &dev_attr_nowayout.attr, -+ NULL, -+}; -+ -+static const struct attribute_group wdt_group = { -+ .attrs = wdt_attrs, -+ .is_visible = wdt_is_visible, -+}; -+__ATTRIBUTE_GROUPS(wdt); -+#else -+#define wdt_groups NULL -+#endif -+ - /* - * watchdog_ioctl_op: call the watchdog drivers ioctl op if defined - * @wdd: the watchdog device to do the ioctl on -@@ -584,6 +697,7 @@ int watchdog_dev_unregister(struct watch - static struct class watchdog_class = { - .name = "watchdog", - .owner = THIS_MODULE, -+ .dev_groups = wdt_groups, - }; - - /* diff --git a/target/linux/ipq806x/patches-4.4/009-5-watchdog-Create-watchdog-device-in-watchdog_dev-c.patch b/target/linux/ipq806x/patches-4.4/009-5-watchdog-Create-watchdog-device-in-watchdog_dev-c.patch deleted file mode 100644 index 059ebdd823..0000000000 --- a/target/linux/ipq806x/patches-4.4/009-5-watchdog-Create-watchdog-device-in-watchdog_dev-c.patch +++ /dev/null @@ -1,261 +0,0 @@ -From 32ecc6392654a0db34b310e8924b5b2c3b8bf503 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Fri, 25 Dec 2015 16:01:40 -0800 -Subject: watchdog: Create watchdog device in watchdog_dev.c - -The watchdog character device is currently created in watchdog_dev.c, -and the watchdog device in watchdog_core.c. This results in -cross-dependencies, since device creation needs to know the watchdog -character device number as well as the watchdog class, both of which -reside in watchdog_dev.c. - -Create the watchdog device in watchdog_dev.c to simplify the code. - -Inspired by earlier patch set from Damien Riegel. - -Cc: Damien Riegel -Signed-off-by: Guenter Roeck -Signed-off-by: Wim Van Sebroeck ---- - drivers/watchdog/watchdog_core.c | 33 ++++-------------- - drivers/watchdog/watchdog_core.h | 4 +-- - drivers/watchdog/watchdog_dev.c | 73 +++++++++++++++++++++++++++++++++------- - 3 files changed, 69 insertions(+), 41 deletions(-) - ---- a/drivers/watchdog/watchdog_core.c -+++ b/drivers/watchdog/watchdog_core.c -@@ -42,7 +42,6 @@ - #include "watchdog_core.h" /* For watchdog_dev_register/... */ - - static DEFINE_IDA(watchdog_ida); --static struct class *watchdog_class; - - /* - * Deferred Registration infrastructure. -@@ -194,7 +193,7 @@ EXPORT_SYMBOL_GPL(watchdog_set_restart_p - - static int __watchdog_register_device(struct watchdog_device *wdd) - { -- int ret, id = -1, devno; -+ int ret, id = -1; - - if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL) - return -EINVAL; -@@ -247,16 +246,6 @@ static int __watchdog_register_device(st - } - } - -- devno = wdd->cdev.dev; -- wdd->dev = device_create(watchdog_class, wdd->parent, devno, -- wdd, "watchdog%d", wdd->id); -- if (IS_ERR(wdd->dev)) { -- watchdog_dev_unregister(wdd); -- ida_simple_remove(&watchdog_ida, id); -- ret = PTR_ERR(wdd->dev); -- return ret; -- } -- - if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) { - wdd->reboot_nb.notifier_call = watchdog_reboot_notifier; - -@@ -265,9 +254,7 @@ static int __watchdog_register_device(st - dev_err(wdd->dev, "Cannot register reboot notifier (%d)\n", - ret); - watchdog_dev_unregister(wdd); -- device_destroy(watchdog_class, devno); - ida_simple_remove(&watchdog_ida, wdd->id); -- wdd->dev = NULL; - return ret; - } - } -@@ -311,9 +298,6 @@ EXPORT_SYMBOL_GPL(watchdog_register_devi - - static void __watchdog_unregister_device(struct watchdog_device *wdd) - { -- int ret; -- int devno; -- - if (wdd == NULL) - return; - -@@ -323,13 +307,8 @@ static void __watchdog_unregister_device - if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) - unregister_reboot_notifier(&wdd->reboot_nb); - -- devno = wdd->cdev.dev; -- ret = watchdog_dev_unregister(wdd); -- if (ret) -- pr_err("error unregistering /dev/watchdog (err=%d)\n", ret); -- device_destroy(watchdog_class, devno); -+ watchdog_dev_unregister(wdd); - ida_simple_remove(&watchdog_ida, wdd->id); -- wdd->dev = NULL; - } - - /** -@@ -370,9 +349,11 @@ static int __init watchdog_deferred_regi - - static int __init watchdog_init(void) - { -- watchdog_class = watchdog_dev_init(); -- if (IS_ERR(watchdog_class)) -- return PTR_ERR(watchdog_class); -+ int err; -+ -+ err = watchdog_dev_init(); -+ if (err < 0) -+ return err; - - watchdog_deferred_registration(); - return 0; ---- a/drivers/watchdog/watchdog_core.h -+++ b/drivers/watchdog/watchdog_core.h -@@ -32,6 +32,6 @@ - * Functions/procedures to be called by the core - */ - extern int watchdog_dev_register(struct watchdog_device *); --extern int watchdog_dev_unregister(struct watchdog_device *); --extern struct class * __init watchdog_dev_init(void); -+extern void watchdog_dev_unregister(struct watchdog_device *); -+extern int __init watchdog_dev_init(void); - extern void __exit watchdog_dev_exit(void); ---- a/drivers/watchdog/watchdog_dev.c -+++ b/drivers/watchdog/watchdog_dev.c -@@ -628,17 +628,18 @@ static struct miscdevice watchdog_miscde - }; - - /* -- * watchdog_dev_register: register a watchdog device -+ * watchdog_cdev_register: register watchdog character device - * @wdd: watchdog device -+ * @devno: character device number - * -- * Register a watchdog device including handling the legacy -+ * Register a watchdog character device including handling the legacy - * /dev/watchdog node. /dev/watchdog is actually a miscdevice and - * thus we set it up like that. - */ - --int watchdog_dev_register(struct watchdog_device *wdd) -+static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) - { -- int err, devno; -+ int err; - - if (wdd->id == 0) { - old_wdd = wdd; -@@ -656,7 +657,6 @@ int watchdog_dev_register(struct watchdo - } - - /* Fill in the data structures */ -- devno = MKDEV(MAJOR(watchdog_devt), wdd->id); - cdev_init(&wdd->cdev, &watchdog_fops); - wdd->cdev.owner = wdd->ops->owner; - -@@ -674,13 +674,14 @@ int watchdog_dev_register(struct watchdo - } - - /* -- * watchdog_dev_unregister: unregister a watchdog device -+ * watchdog_cdev_unregister: unregister watchdog character device - * @watchdog: watchdog device - * -- * Unregister the watchdog and if needed the legacy /dev/watchdog device. -+ * Unregister watchdog character device and if needed the legacy -+ * /dev/watchdog device. - */ - --int watchdog_dev_unregister(struct watchdog_device *wdd) -+static void watchdog_cdev_unregister(struct watchdog_device *wdd) - { - mutex_lock(&wdd->lock); - set_bit(WDOG_UNREGISTERED, &wdd->status); -@@ -691,7 +692,6 @@ int watchdog_dev_unregister(struct watch - misc_deregister(&watchdog_miscdev); - old_wdd = NULL; - } -- return 0; - } - - static struct class watchdog_class = { -@@ -701,29 +701,76 @@ static struct class watchdog_class = { - }; - - /* -+ * watchdog_dev_register: register a watchdog device -+ * @wdd: watchdog device -+ * -+ * Register a watchdog device including handling the legacy -+ * /dev/watchdog node. /dev/watchdog is actually a miscdevice and -+ * thus we set it up like that. -+ */ -+ -+int watchdog_dev_register(struct watchdog_device *wdd) -+{ -+ struct device *dev; -+ dev_t devno; -+ int ret; -+ -+ devno = MKDEV(MAJOR(watchdog_devt), wdd->id); -+ -+ ret = watchdog_cdev_register(wdd, devno); -+ if (ret) -+ return ret; -+ -+ dev = device_create(&watchdog_class, wdd->parent, devno, wdd, -+ "watchdog%d", wdd->id); -+ if (IS_ERR(dev)) { -+ watchdog_cdev_unregister(wdd); -+ return PTR_ERR(dev); -+ } -+ wdd->dev = dev; -+ -+ return ret; -+} -+ -+/* -+ * watchdog_dev_unregister: unregister a watchdog device -+ * @watchdog: watchdog device -+ * -+ * Unregister watchdog device and if needed the legacy -+ * /dev/watchdog device. -+ */ -+ -+void watchdog_dev_unregister(struct watchdog_device *wdd) -+{ -+ watchdog_cdev_unregister(wdd); -+ device_destroy(&watchdog_class, wdd->dev->devt); -+ wdd->dev = NULL; -+} -+ -+/* - * watchdog_dev_init: init dev part of watchdog core - * - * Allocate a range of chardev nodes to use for watchdog devices - */ - --struct class * __init watchdog_dev_init(void) -+int __init watchdog_dev_init(void) - { - int err; - - err = class_register(&watchdog_class); - if (err < 0) { - pr_err("couldn't register class\n"); -- return ERR_PTR(err); -+ return err; - } - - err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog"); - if (err < 0) { - pr_err("watchdog: unable to allocate char dev region\n"); - class_unregister(&watchdog_class); -- return ERR_PTR(err); -+ return err; - } - -- return &watchdog_class; -+ return 0; - } - - /* diff --git a/target/linux/ipq806x/patches-4.4/009-6-watchdog-Separate-and-maintain-variables-based-on-variable-lifetime.patch b/target/linux/ipq806x/patches-4.4/009-6-watchdog-Separate-and-maintain-variables-based-on-variable-lifetime.patch deleted file mode 100644 index 214dabfb9a..0000000000 --- a/target/linux/ipq806x/patches-4.4/009-6-watchdog-Separate-and-maintain-variables-based-on-variable-lifetime.patch +++ /dev/null @@ -1,969 +0,0 @@ -From b4ffb1909843b28f3b1b60197d517b123b7a9b66 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Fri, 25 Dec 2015 16:01:42 -0800 -Subject: watchdog: Separate and maintain variables based on variable lifetime - -All variables required by the watchdog core to manage a watchdog are -currently stored in struct watchdog_device. The lifetime of those -variables is determined by the watchdog driver. However, the lifetime -of variables used by the watchdog core differs from the lifetime of -struct watchdog_device. To remedy this situation, watchdog drivers -can implement ref and unref callbacks, to be used by the watchdog -core to lock struct watchdog_device in memory. - -While this solves the immediate problem, it depends on watchdog drivers -to actually implement the ref/unref callbacks. This is error prone, -often not implemented in the first place, or not implemented correctly. - -To solve the problem without requiring driver support, split the variables -in struct watchdog_device into two data structures - one for variables -associated with the watchdog driver, one for variables associated with -the watchdog core. With this approach, the watchdog core can keep track -of its variable lifetime and no longer depends on ref/unref callbacks -in the driver. As a side effect, some of the variables originally in -struct watchdog_driver are now private to the watchdog core and no longer -visible in watchdog drivers. - -As a side effect of the changes made, an ioctl will now always fail -with -ENODEV after a watchdog device was unregistered with the character -device still open. Previously, it would only fail with -ENODEV in some -situations. Also, ioctl operations are now atomic from driver perspective. -With this change, it is now guaranteed that the driver will not unregister -a watchdog between a timeout change and the subsequent ping. - -The 'ref' and 'unref' callbacks in struct watchdog_driver are no longer -used and marked as deprecated. - -Signed-off-by: Guenter Roeck -Signed-off-by: Wim Van Sebroeck ---- - Documentation/watchdog/watchdog-kernel-api.txt | 45 +-- - drivers/watchdog/watchdog_core.c | 2 - - drivers/watchdog/watchdog_dev.c | 383 +++++++++++++------------ - include/linux/watchdog.h | 22 +- - 4 files changed, 218 insertions(+), 234 deletions(-) - ---- a/Documentation/watchdog/watchdog-kernel-api.txt -+++ b/Documentation/watchdog/watchdog-kernel-api.txt -@@ -44,7 +44,6 @@ The watchdog device structure looks like - - struct watchdog_device { - int id; -- struct cdev cdev; - struct device *dev; - struct device *parent; - const struct watchdog_info *info; -@@ -56,7 +55,7 @@ struct watchdog_device { - struct notifier_block reboot_nb; - struct notifier_block restart_nb; - void *driver_data; -- struct mutex lock; -+ struct watchdog_core_data *wd_data; - unsigned long status; - struct list_head deferred; - }; -@@ -66,8 +65,6 @@ It contains following fields: - /dev/watchdog0 cdev (dynamic major, minor 0) as well as the old - /dev/watchdog miscdev. The id is set automatically when calling - watchdog_register_device. --* cdev: cdev for the dynamic /dev/watchdog device nodes. This -- field is also populated by watchdog_register_device. - * dev: device under the watchdog class (created by watchdog_register_device). - * parent: set this to the parent device (or NULL) before calling - watchdog_register_device. -@@ -89,11 +86,10 @@ It contains following fields: - * driver_data: a pointer to the drivers private data of a watchdog device. - This data should only be accessed via the watchdog_set_drvdata and - watchdog_get_drvdata routines. --* lock: Mutex for WatchDog Timer Driver Core internal use only. -+* wd_data: a pointer to watchdog core internal data. - * status: this field contains a number of status bits that give extra - information about the status of the device (Like: is the watchdog timer -- running/active, is the nowayout bit set, is the device opened via -- the /dev/watchdog interface or not, ...). -+ running/active, or is the nowayout bit set). - * deferred: entry in wtd_deferred_reg_list which is used to - register early initialized watchdogs. - -@@ -110,8 +106,8 @@ struct watchdog_ops { - int (*set_timeout)(struct watchdog_device *, unsigned int); - unsigned int (*get_timeleft)(struct watchdog_device *); - int (*restart)(struct watchdog_device *); -- void (*ref)(struct watchdog_device *); -- void (*unref)(struct watchdog_device *); -+ void (*ref)(struct watchdog_device *) __deprecated; -+ void (*unref)(struct watchdog_device *) __deprecated; - long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); - }; - -@@ -120,20 +116,6 @@ driver's operations. This module owner w - the watchdog is active. (This to avoid a system crash when you unload the - module and /dev/watchdog is still open). - --If the watchdog_device struct is dynamically allocated, just locking the module --is not enough and a driver also needs to define the ref and unref operations to --ensure the structure holding the watchdog_device does not go away. -- --The simplest (and usually sufficient) implementation of this is to: --1) Add a kref struct to the same structure which is holding the watchdog_device --2) Define a release callback for the kref which frees the struct holding both --3) Call kref_init on this kref *before* calling watchdog_register_device() --4) Define a ref operation calling kref_get on this kref --5) Define a unref operation calling kref_put on this kref --6) When it is time to cleanup: -- * Do not kfree() the struct holding both, the last kref_put will do this! -- * *After* calling watchdog_unregister_device() call kref_put on the kref -- - Some operations are mandatory and some are optional. The mandatory operations - are: - * start: this is a pointer to the routine that starts the watchdog timer -@@ -176,34 +158,21 @@ they are supported. These optional routi - * get_timeleft: this routines returns the time that's left before a reset. - * restart: this routine restarts the machine. It returns 0 on success or a - negative errno code for failure. --* ref: the operation that calls kref_get on the kref of a dynamically -- allocated watchdog_device struct. --* unref: the operation that calls kref_put on the kref of a dynamically -- allocated watchdog_device struct. - * ioctl: if this routine is present then it will be called first before we do - our own internal ioctl call handling. This routine should return -ENOIOCTLCMD - if a command is not supported. The parameters that are passed to the ioctl - call are: watchdog_device, cmd and arg. - -+The 'ref' and 'unref' operations are no longer used and deprecated. -+ - The status bits should (preferably) be set with the set_bit and clear_bit alike - bit-operations. The status bits that are defined are: - * WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device - is active or not. When the watchdog is active after booting, then you should - set this status bit (Note: when you register the watchdog timer device with - this bit set, then opening /dev/watchdog will skip the start operation) --* WDOG_DEV_OPEN: this status bit shows whether or not the watchdog device -- was opened via /dev/watchdog. -- (This bit should only be used by the WatchDog Timer Driver Core). --* WDOG_ALLOW_RELEASE: this bit stores whether or not the magic close character -- has been sent (so that we can support the magic close feature). -- (This bit should only be used by the WatchDog Timer Driver Core). - * WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. - If this bit is set then the watchdog timer will not be able to stop. --* WDOG_UNREGISTERED: this bit gets set by the WatchDog Timer Driver Core -- after calling watchdog_unregister_device, and then checked before calling -- any watchdog_ops, so that you can be sure that no operations (other then -- unref) will get called after unregister, even if userspace still holds a -- reference to /dev/watchdog - - To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog - timer device) you can either: ---- a/drivers/watchdog/watchdog_core.c -+++ b/drivers/watchdog/watchdog_core.c -@@ -210,8 +210,6 @@ static int __watchdog_register_device(st - * corrupted in a later stage then we expect a kernel panic! - */ - -- mutex_init(&wdd->lock); -- - /* Use alias for watchdog id if possible */ - if (wdd->parent) { - ret = of_alias_get_id(wdd->parent->of_node, "watchdog"); ---- a/drivers/watchdog/watchdog_dev.c -+++ b/drivers/watchdog/watchdog_dev.c -@@ -32,27 +32,51 @@ - - #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - --#include /* For module stuff/... */ --#include /* For standard types (like size_t) */ -+#include /* For character device */ - #include /* For the -ENODEV/... values */ --#include /* For printk/panic/... */ - #include /* For file operations */ --#include /* For watchdog specific items */ --#include /* For handling misc devices */ - #include /* For __init/__exit/... */ -+#include /* For printk/panic/... */ -+#include /* For data references */ -+#include /* For handling misc devices */ -+#include /* For module stuff/... */ -+#include /* For mutexes */ -+#include /* For memory functions */ -+#include /* For standard types (like size_t) */ -+#include /* For watchdog specific items */ - #include /* For copy_to_user/put_user/... */ - - #include "watchdog_core.h" - -+/* -+ * struct watchdog_core_data - watchdog core internal data -+ * @kref: Reference count. -+ * @cdev: The watchdog's Character device. -+ * @wdd: Pointer to watchdog device. -+ * @lock: Lock for watchdog core. -+ * @status: Watchdog core internal status bits. -+ */ -+struct watchdog_core_data { -+ struct kref kref; -+ struct cdev cdev; -+ struct watchdog_device *wdd; -+ struct mutex lock; -+ unsigned long status; /* Internal status bits */ -+#define _WDOG_DEV_OPEN 0 /* Opened ? */ -+#define _WDOG_ALLOW_RELEASE 1 /* Did we receive the magic char ? */ -+}; -+ - /* the dev_t structure to store the dynamically allocated watchdog devices */ - static dev_t watchdog_devt; --/* the watchdog device behind /dev/watchdog */ --static struct watchdog_device *old_wdd; -+/* Reference to watchdog device behind /dev/watchdog */ -+static struct watchdog_core_data *old_wd_data; - - /* - * watchdog_ping: ping the watchdog. - * @wdd: the watchdog device to ping - * -+ * The caller must hold wd_data->lock. -+ * - * If the watchdog has no own ping operation then it needs to be - * restarted via the start operation. This wrapper function does - * exactly that. -@@ -61,25 +85,16 @@ static struct watchdog_device *old_wdd; - - static int watchdog_ping(struct watchdog_device *wdd) - { -- int err = 0; -- -- mutex_lock(&wdd->lock); -- -- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { -- err = -ENODEV; -- goto out_ping; -- } -+ int err; - - if (!watchdog_active(wdd)) -- goto out_ping; -+ return 0; - - if (wdd->ops->ping) - err = wdd->ops->ping(wdd); /* ping the watchdog */ - else - err = wdd->ops->start(wdd); /* restart watchdog */ - --out_ping: -- mutex_unlock(&wdd->lock); - return err; - } - -@@ -87,6 +102,8 @@ out_ping: - * watchdog_start: wrapper to start the watchdog. - * @wdd: the watchdog device to start - * -+ * The caller must hold wd_data->lock. -+ * - * Start the watchdog if it is not active and mark it active. - * This function returns zero on success or a negative errno code for - * failure. -@@ -94,24 +111,15 @@ out_ping: - - static int watchdog_start(struct watchdog_device *wdd) - { -- int err = 0; -- -- mutex_lock(&wdd->lock); -- -- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { -- err = -ENODEV; -- goto out_start; -- } -+ int err; - - if (watchdog_active(wdd)) -- goto out_start; -+ return 0; - - err = wdd->ops->start(wdd); - if (err == 0) - set_bit(WDOG_ACTIVE, &wdd->status); - --out_start: -- mutex_unlock(&wdd->lock); - return err; - } - -@@ -119,6 +127,8 @@ out_start: - * watchdog_stop: wrapper to stop the watchdog. - * @wdd: the watchdog device to stop - * -+ * The caller must hold wd_data->lock. -+ * - * Stop the watchdog if it is still active and unmark it active. - * This function returns zero on success or a negative errno code for - * failure. -@@ -127,93 +137,58 @@ out_start: - - static int watchdog_stop(struct watchdog_device *wdd) - { -- int err = 0; -- -- mutex_lock(&wdd->lock); -- -- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { -- err = -ENODEV; -- goto out_stop; -- } -+ int err; - - if (!watchdog_active(wdd)) -- goto out_stop; -+ return 0; - - if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) { - dev_info(wdd->dev, "nowayout prevents watchdog being stopped!\n"); -- err = -EBUSY; -- goto out_stop; -+ return -EBUSY; - } - - err = wdd->ops->stop(wdd); - if (err == 0) - clear_bit(WDOG_ACTIVE, &wdd->status); - --out_stop: -- mutex_unlock(&wdd->lock); - return err; - } - - /* - * watchdog_get_status: wrapper to get the watchdog status - * @wdd: the watchdog device to get the status from -- * @status: the status of the watchdog device -+ * -+ * The caller must hold wd_data->lock. - * - * Get the watchdog's status flags. - */ - --static int watchdog_get_status(struct watchdog_device *wdd, -- unsigned int *status) -+static unsigned int watchdog_get_status(struct watchdog_device *wdd) - { -- int err = 0; -- -- *status = 0; - if (!wdd->ops->status) -- return -EOPNOTSUPP; -- -- mutex_lock(&wdd->lock); -- -- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { -- err = -ENODEV; -- goto out_status; -- } -- -- *status = wdd->ops->status(wdd); -+ return 0; - --out_status: -- mutex_unlock(&wdd->lock); -- return err; -+ return wdd->ops->status(wdd); - } - - /* - * watchdog_set_timeout: set the watchdog timer timeout - * @wdd: the watchdog device to set the timeout for - * @timeout: timeout to set in seconds -+ * -+ * The caller must hold wd_data->lock. - */ - - static int watchdog_set_timeout(struct watchdog_device *wdd, - unsigned int timeout) - { -- int err; -- - if (!wdd->ops->set_timeout || !(wdd->info->options & WDIOF_SETTIMEOUT)) - return -EOPNOTSUPP; - - if (watchdog_timeout_invalid(wdd, timeout)) - return -EINVAL; - -- mutex_lock(&wdd->lock); -- -- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { -- err = -ENODEV; -- goto out_timeout; -- } -- -- err = wdd->ops->set_timeout(wdd, timeout); -- --out_timeout: -- mutex_unlock(&wdd->lock); -- return err; -+ return wdd->ops->set_timeout(wdd, timeout); - } - - /* -@@ -221,30 +196,22 @@ out_timeout: - * @wdd: the watchdog device to get the remaining time from - * @timeleft: the time that's left - * -+ * The caller must hold wd_data->lock. -+ * - * Get the time before a watchdog will reboot (if not pinged). - */ - - static int watchdog_get_timeleft(struct watchdog_device *wdd, - unsigned int *timeleft) - { -- int err = 0; -- - *timeleft = 0; -+ - if (!wdd->ops->get_timeleft) - return -EOPNOTSUPP; - -- mutex_lock(&wdd->lock); -- -- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { -- err = -ENODEV; -- goto out_timeleft; -- } -- - *timeleft = wdd->ops->get_timeleft(wdd); - --out_timeleft: -- mutex_unlock(&wdd->lock); -- return err; -+ return 0; - } - - #ifdef CONFIG_WATCHDOG_SYSFS -@@ -261,14 +228,14 @@ static ssize_t status_show(struct device - char *buf) - { - struct watchdog_device *wdd = dev_get_drvdata(dev); -- ssize_t status; -- unsigned int val; -+ struct watchdog_core_data *wd_data = wdd->wd_data; -+ unsigned int status; - -- status = watchdog_get_status(wdd, &val); -- if (!status) -- status = sprintf(buf, "%u\n", val); -+ mutex_lock(&wd_data->lock); -+ status = watchdog_get_status(wdd); -+ mutex_unlock(&wd_data->lock); - -- return status; -+ return sprintf(buf, "%u\n", status); - } - static DEVICE_ATTR_RO(status); - -@@ -285,10 +252,13 @@ static ssize_t timeleft_show(struct devi - char *buf) - { - struct watchdog_device *wdd = dev_get_drvdata(dev); -+ struct watchdog_core_data *wd_data = wdd->wd_data; - ssize_t status; - unsigned int val; - -+ mutex_lock(&wd_data->lock); - status = watchdog_get_timeleft(wdd, &val); -+ mutex_unlock(&wd_data->lock); - if (!status) - status = sprintf(buf, "%u\n", val); - -@@ -365,28 +335,17 @@ __ATTRIBUTE_GROUPS(wdt); - * @wdd: the watchdog device to do the ioctl on - * @cmd: watchdog command - * @arg: argument pointer -+ * -+ * The caller must hold wd_data->lock. - */ - - static int watchdog_ioctl_op(struct watchdog_device *wdd, unsigned int cmd, - unsigned long arg) - { -- int err; -- - if (!wdd->ops->ioctl) - return -ENOIOCTLCMD; - -- mutex_lock(&wdd->lock); -- -- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { -- err = -ENODEV; -- goto out_ioctl; -- } -- -- err = wdd->ops->ioctl(wdd, cmd, arg); -- --out_ioctl: -- mutex_unlock(&wdd->lock); -- return err; -+ return wdd->ops->ioctl(wdd, cmd, arg); - } - - /* -@@ -404,10 +363,11 @@ out_ioctl: - static ssize_t watchdog_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) - { -- struct watchdog_device *wdd = file->private_data; -+ struct watchdog_core_data *wd_data = file->private_data; -+ struct watchdog_device *wdd; -+ int err; - size_t i; - char c; -- int err; - - if (len == 0) - return 0; -@@ -416,18 +376,25 @@ static ssize_t watchdog_write(struct fil - * Note: just in case someone wrote the magic character - * five months ago... - */ -- clear_bit(WDOG_ALLOW_RELEASE, &wdd->status); -+ clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status); - - /* scan to see whether or not we got the magic character */ - for (i = 0; i != len; i++) { - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') -- set_bit(WDOG_ALLOW_RELEASE, &wdd->status); -+ set_bit(_WDOG_ALLOW_RELEASE, &wd_data->status); - } - - /* someone wrote to us, so we send the watchdog a keepalive ping */ -- err = watchdog_ping(wdd); -+ -+ err = -ENODEV; -+ mutex_lock(&wd_data->lock); -+ wdd = wd_data->wdd; -+ if (wdd) -+ err = watchdog_ping(wdd); -+ mutex_unlock(&wd_data->lock); -+ - if (err < 0) - return err; - -@@ -447,71 +414,94 @@ static ssize_t watchdog_write(struct fil - static long watchdog_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) - { -- struct watchdog_device *wdd = file->private_data; -+ struct watchdog_core_data *wd_data = file->private_data; - void __user *argp = (void __user *)arg; -+ struct watchdog_device *wdd; - int __user *p = argp; - unsigned int val; - int err; - -+ mutex_lock(&wd_data->lock); -+ -+ wdd = wd_data->wdd; -+ if (!wdd) { -+ err = -ENODEV; -+ goto out_ioctl; -+ } -+ - err = watchdog_ioctl_op(wdd, cmd, arg); - if (err != -ENOIOCTLCMD) -- return err; -+ goto out_ioctl; - - switch (cmd) { - case WDIOC_GETSUPPORT: -- return copy_to_user(argp, wdd->info, -+ err = copy_to_user(argp, wdd->info, - sizeof(struct watchdog_info)) ? -EFAULT : 0; -+ break; - case WDIOC_GETSTATUS: -- err = watchdog_get_status(wdd, &val); -- if (err == -ENODEV) -- return err; -- return put_user(val, p); -+ val = watchdog_get_status(wdd); -+ err = put_user(val, p); -+ break; - case WDIOC_GETBOOTSTATUS: -- return put_user(wdd->bootstatus, p); -+ err = put_user(wdd->bootstatus, p); -+ break; - case WDIOC_SETOPTIONS: -- if (get_user(val, p)) -- return -EFAULT; -+ if (get_user(val, p)) { -+ err = -EFAULT; -+ break; -+ } - if (val & WDIOS_DISABLECARD) { - err = watchdog_stop(wdd); - if (err < 0) -- return err; -+ break; - } -- if (val & WDIOS_ENABLECARD) { -+ if (val & WDIOS_ENABLECARD) - err = watchdog_start(wdd); -- if (err < 0) -- return err; -- } -- return 0; -+ break; - case WDIOC_KEEPALIVE: -- if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) -- return -EOPNOTSUPP; -- return watchdog_ping(wdd); -+ if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) { -+ err = -EOPNOTSUPP; -+ break; -+ } -+ err = watchdog_ping(wdd); -+ break; - case WDIOC_SETTIMEOUT: -- if (get_user(val, p)) -- return -EFAULT; -+ if (get_user(val, p)) { -+ err = -EFAULT; -+ break; -+ } - err = watchdog_set_timeout(wdd, val); - if (err < 0) -- return err; -+ break; - /* If the watchdog is active then we send a keepalive ping - * to make sure that the watchdog keep's running (and if - * possible that it takes the new timeout) */ - err = watchdog_ping(wdd); - if (err < 0) -- return err; -+ break; - /* Fall */ - case WDIOC_GETTIMEOUT: - /* timeout == 0 means that we don't know the timeout */ -- if (wdd->timeout == 0) -- return -EOPNOTSUPP; -- return put_user(wdd->timeout, p); -+ if (wdd->timeout == 0) { -+ err = -EOPNOTSUPP; -+ break; -+ } -+ err = put_user(wdd->timeout, p); -+ break; - case WDIOC_GETTIMELEFT: - err = watchdog_get_timeleft(wdd, &val); -- if (err) -- return err; -- return put_user(val, p); -+ if (err < 0) -+ break; -+ err = put_user(val, p); -+ break; - default: -- return -ENOTTY; -+ err = -ENOTTY; -+ break; - } -+ -+out_ioctl: -+ mutex_unlock(&wd_data->lock); -+ return err; - } - - /* -@@ -526,45 +516,59 @@ static long watchdog_ioctl(struct file * - - static int watchdog_open(struct inode *inode, struct file *file) - { -- int err = -EBUSY; -+ struct watchdog_core_data *wd_data; - struct watchdog_device *wdd; -+ int err; - - /* Get the corresponding watchdog device */ - if (imajor(inode) == MISC_MAJOR) -- wdd = old_wdd; -+ wd_data = old_wd_data; - else -- wdd = container_of(inode->i_cdev, struct watchdog_device, cdev); -+ wd_data = container_of(inode->i_cdev, struct watchdog_core_data, -+ cdev); - - /* the watchdog is single open! */ -- if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status)) -+ if (test_and_set_bit(_WDOG_DEV_OPEN, &wd_data->status)) - return -EBUSY; - -+ wdd = wd_data->wdd; -+ - /* - * If the /dev/watchdog device is open, we don't want the module - * to be unloaded. - */ -- if (!try_module_get(wdd->ops->owner)) -- goto out; -+ if (!try_module_get(wdd->ops->owner)) { -+ err = -EBUSY; -+ goto out_clear; -+ } - - err = watchdog_start(wdd); - if (err < 0) - goto out_mod; - -- file->private_data = wdd; -+ file->private_data = wd_data; - -- if (wdd->ops->ref) -- wdd->ops->ref(wdd); -+ kref_get(&wd_data->kref); - - /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ - return nonseekable_open(inode, file); - - out_mod: -- module_put(wdd->ops->owner); --out: -- clear_bit(WDOG_DEV_OPEN, &wdd->status); -+ module_put(wd_data->wdd->ops->owner); -+out_clear: -+ clear_bit(_WDOG_DEV_OPEN, &wd_data->status); - return err; - } - -+static void watchdog_core_data_release(struct kref *kref) -+{ -+ struct watchdog_core_data *wd_data; -+ -+ wd_data = container_of(kref, struct watchdog_core_data, kref); -+ -+ kfree(wd_data); -+} -+ - /* - * watchdog_release: release the watchdog device. - * @inode: inode of device -@@ -577,9 +581,16 @@ out: - - static int watchdog_release(struct inode *inode, struct file *file) - { -- struct watchdog_device *wdd = file->private_data; -+ struct watchdog_core_data *wd_data = file->private_data; -+ struct watchdog_device *wdd; - int err = -EBUSY; - -+ mutex_lock(&wd_data->lock); -+ -+ wdd = wd_data->wdd; -+ if (!wdd) -+ goto done; -+ - /* - * We only stop the watchdog if we received the magic character - * or if WDIOF_MAGICCLOSE is not set. If nowayout was set then -@@ -587,29 +598,24 @@ static int watchdog_release(struct inode - */ - if (!test_bit(WDOG_ACTIVE, &wdd->status)) - err = 0; -- else if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) || -+ else if (test_and_clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status) || - !(wdd->info->options & WDIOF_MAGICCLOSE)) - err = watchdog_stop(wdd); - - /* If the watchdog was not stopped, send a keepalive ping */ - if (err < 0) { -- mutex_lock(&wdd->lock); -- if (!test_bit(WDOG_UNREGISTERED, &wdd->status)) -- dev_crit(wdd->dev, "watchdog did not stop!\n"); -- mutex_unlock(&wdd->lock); -+ dev_crit(wdd->dev, "watchdog did not stop!\n"); - watchdog_ping(wdd); - } - -- /* Allow the owner module to be unloaded again */ -- module_put(wdd->ops->owner); -- - /* make sure that /dev/watchdog can be re-opened */ -- clear_bit(WDOG_DEV_OPEN, &wdd->status); -- -- /* Note wdd may be gone after this, do not use after this! */ -- if (wdd->ops->unref) -- wdd->ops->unref(wdd); -+ clear_bit(_WDOG_DEV_OPEN, &wd_data->status); - -+done: -+ mutex_unlock(&wd_data->lock); -+ /* Allow the owner module to be unloaded again */ -+ module_put(wd_data->cdev.owner); -+ kref_put(&wd_data->kref, watchdog_core_data_release); - return 0; - } - -@@ -639,10 +645,20 @@ static struct miscdevice watchdog_miscde - - static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) - { -+ struct watchdog_core_data *wd_data; - int err; - -+ wd_data = kzalloc(sizeof(struct watchdog_core_data), GFP_KERNEL); -+ if (!wd_data) -+ return -ENOMEM; -+ kref_init(&wd_data->kref); -+ mutex_init(&wd_data->lock); -+ -+ wd_data->wdd = wdd; -+ wdd->wd_data = wd_data; -+ - if (wdd->id == 0) { -- old_wdd = wdd; -+ old_wd_data = wd_data; - watchdog_miscdev.parent = wdd->parent; - err = misc_register(&watchdog_miscdev); - if (err != 0) { -@@ -651,23 +667,25 @@ static int watchdog_cdev_register(struct - if (err == -EBUSY) - pr_err("%s: a legacy watchdog module is probably present.\n", - wdd->info->identity); -- old_wdd = NULL; -+ old_wd_data = NULL; -+ kfree(wd_data); - return err; - } - } - - /* Fill in the data structures */ -- cdev_init(&wdd->cdev, &watchdog_fops); -- wdd->cdev.owner = wdd->ops->owner; -+ cdev_init(&wd_data->cdev, &watchdog_fops); -+ wd_data->cdev.owner = wdd->ops->owner; - - /* Add the device */ -- err = cdev_add(&wdd->cdev, devno, 1); -+ err = cdev_add(&wd_data->cdev, devno, 1); - if (err) { - pr_err("watchdog%d unable to add device %d:%d\n", - wdd->id, MAJOR(watchdog_devt), wdd->id); - if (wdd->id == 0) { - misc_deregister(&watchdog_miscdev); -- old_wdd = NULL; -+ old_wd_data = NULL; -+ kref_put(&wd_data->kref, watchdog_core_data_release); - } - } - return err; -@@ -683,15 +701,20 @@ static int watchdog_cdev_register(struct - - static void watchdog_cdev_unregister(struct watchdog_device *wdd) - { -- mutex_lock(&wdd->lock); -- set_bit(WDOG_UNREGISTERED, &wdd->status); -- mutex_unlock(&wdd->lock); -+ struct watchdog_core_data *wd_data = wdd->wd_data; - -- cdev_del(&wdd->cdev); -+ cdev_del(&wd_data->cdev); - if (wdd->id == 0) { - misc_deregister(&watchdog_miscdev); -- old_wdd = NULL; -+ old_wd_data = NULL; - } -+ -+ mutex_lock(&wd_data->lock); -+ wd_data->wdd = NULL; -+ wdd->wd_data = NULL; -+ mutex_unlock(&wd_data->lock); -+ -+ kref_put(&wd_data->kref, watchdog_core_data_release); - } - - static struct class watchdog_class = { -@@ -742,9 +765,9 @@ int watchdog_dev_register(struct watchdo - - void watchdog_dev_unregister(struct watchdog_device *wdd) - { -- watchdog_cdev_unregister(wdd); - device_destroy(&watchdog_class, wdd->dev->devt); - wdd->dev = NULL; -+ watchdog_cdev_unregister(wdd); - } - - /* ---- a/include/linux/watchdog.h -+++ b/include/linux/watchdog.h -@@ -17,6 +17,7 @@ - - struct watchdog_ops; - struct watchdog_device; -+struct watchdog_core_data; - - /** struct watchdog_ops - The watchdog-devices operations - * -@@ -28,8 +29,6 @@ struct watchdog_device; - * @set_timeout:The routine for setting the watchdog devices timeout value (in seconds). - * @get_timeleft:The routine that gets the time left before a reset (in seconds). - * @restart: The routine for restarting the machine. -- * @ref: The ref operation for dyn. allocated watchdog_device structs -- * @unref: The unref operation for dyn. allocated watchdog_device structs - * @ioctl: The routines that handles extra ioctl calls. - * - * The watchdog_ops structure contains a list of low-level operations -@@ -48,15 +47,14 @@ struct watchdog_ops { - int (*set_timeout)(struct watchdog_device *, unsigned int); - unsigned int (*get_timeleft)(struct watchdog_device *); - int (*restart)(struct watchdog_device *); -- void (*ref)(struct watchdog_device *); -- void (*unref)(struct watchdog_device *); -+ void (*ref)(struct watchdog_device *) __deprecated; -+ void (*unref)(struct watchdog_device *) __deprecated; - long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); - }; - - /** struct watchdog_device - The structure that defines a watchdog device - * - * @id: The watchdog's ID. (Allocated by watchdog_register_device) -- * @cdev: The watchdog's Character device. - * @dev: The device for our watchdog - * @parent: The parent bus device - * @info: Pointer to a watchdog_info structure. -@@ -67,8 +65,8 @@ struct watchdog_ops { - * @max_timeout:The watchdog devices maximum timeout value (in seconds). - * @reboot_nb: The notifier block to stop watchdog on reboot. - * @restart_nb: The notifier block to register a restart function. -- * @driver-data:Pointer to the drivers private data. -- * @lock: Lock for watchdog core internal use only. -+ * @driver_data:Pointer to the drivers private data. -+ * @wd_data: Pointer to watchdog core internal data. - * @status: Field that contains the devices internal status bits. - * @deferred: entry in wtd_deferred_reg_list which is used to - * register early initialized watchdogs. -@@ -84,7 +82,6 @@ struct watchdog_ops { - */ - struct watchdog_device { - int id; -- struct cdev cdev; - struct device *dev; - struct device *parent; - const struct watchdog_info *info; -@@ -96,15 +93,12 @@ struct watchdog_device { - struct notifier_block reboot_nb; - struct notifier_block restart_nb; - void *driver_data; -- struct mutex lock; -+ struct watchdog_core_data *wd_data; - unsigned long status; - /* Bit numbers for status flags */ - #define WDOG_ACTIVE 0 /* Is the watchdog running/active */ --#define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */ --#define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ --#define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */ --#define WDOG_UNREGISTERED 4 /* Has the device been unregistered */ --#define WDOG_STOP_ON_REBOOT 5 /* Should be stopped on reboot */ -+#define WDOG_NO_WAY_OUT 1 /* Is 'nowayout' feature set ? */ -+#define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */ - struct list_head deferred; - }; - diff --git a/target/linux/ipq806x/patches-4.4/009-7-watchdog-Drop-pointer-to-watchdog-device-from-struct-watchdog_device.patch b/target/linux/ipq806x/patches-4.4/009-7-watchdog-Drop-pointer-to-watchdog-device-from-struct-watchdog_device.patch deleted file mode 100644 index 9640cd4757..0000000000 --- a/target/linux/ipq806x/patches-4.4/009-7-watchdog-Drop-pointer-to-watchdog-device-from-struct-watchdog_device.patch +++ /dev/null @@ -1,117 +0,0 @@ -From 0254e953537c92df3e7d0176f401a211e944fd61 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Sun, 3 Jan 2016 15:11:58 -0800 -Subject: watchdog: Drop pointer to watchdog device from struct watchdog_device - -The lifetime of the watchdog device pointer is different from the lifetime -of its character device. Remove it entirely to avoid race conditions. - -Signed-off-by: Guenter Roeck -Signed-off-by: Guenter Roeck -Signed-off-by: Wim Van Sebroeck ---- - Documentation/watchdog/watchdog-kernel-api.txt | 2 -- - drivers/watchdog/watchdog_core.c | 8 ++++---- - drivers/watchdog/watchdog_dev.c | 9 ++++----- - include/linux/watchdog.h | 2 -- - 4 files changed, 8 insertions(+), 13 deletions(-) - ---- a/Documentation/watchdog/watchdog-kernel-api.txt -+++ b/Documentation/watchdog/watchdog-kernel-api.txt -@@ -44,7 +44,6 @@ The watchdog device structure looks like - - struct watchdog_device { - int id; -- struct device *dev; - struct device *parent; - const struct watchdog_info *info; - const struct watchdog_ops *ops; -@@ -65,7 +64,6 @@ It contains following fields: - /dev/watchdog0 cdev (dynamic major, minor 0) as well as the old - /dev/watchdog miscdev. The id is set automatically when calling - watchdog_register_device. --* dev: device under the watchdog class (created by watchdog_register_device). - * parent: set this to the parent device (or NULL) before calling - watchdog_register_device. - * info: a pointer to a watchdog_info structure. This structure gives some ---- a/drivers/watchdog/watchdog_core.c -+++ b/drivers/watchdog/watchdog_core.c -@@ -249,8 +249,8 @@ static int __watchdog_register_device(st - - ret = register_reboot_notifier(&wdd->reboot_nb); - if (ret) { -- dev_err(wdd->dev, "Cannot register reboot notifier (%d)\n", -- ret); -+ pr_err("watchdog%d: Cannot register reboot notifier (%d)\n", -+ wdd->id, ret); - watchdog_dev_unregister(wdd); - ida_simple_remove(&watchdog_ida, wdd->id); - return ret; -@@ -262,8 +262,8 @@ static int __watchdog_register_device(st - - ret = register_restart_handler(&wdd->restart_nb); - if (ret) -- dev_warn(wdd->dev, "Cannot register restart handler (%d)\n", -- ret); -+ pr_warn("watchog%d: Cannot register restart handler (%d)\n", -+ wdd->id, ret); - } - - return 0; ---- a/drivers/watchdog/watchdog_dev.c -+++ b/drivers/watchdog/watchdog_dev.c -@@ -143,7 +143,8 @@ static int watchdog_stop(struct watchdog - return 0; - - if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) { -- dev_info(wdd->dev, "nowayout prevents watchdog being stopped!\n"); -+ pr_info("watchdog%d: nowayout prevents watchdog being stopped!\n", -+ wdd->id); - return -EBUSY; - } - -@@ -604,7 +605,7 @@ static int watchdog_release(struct inode - - /* If the watchdog was not stopped, send a keepalive ping */ - if (err < 0) { -- dev_crit(wdd->dev, "watchdog did not stop!\n"); -+ pr_crit("watchdog%d: watchdog did not stop!\n", wdd->id); - watchdog_ping(wdd); - } - -@@ -750,7 +751,6 @@ int watchdog_dev_register(struct watchdo - watchdog_cdev_unregister(wdd); - return PTR_ERR(dev); - } -- wdd->dev = dev; - - return ret; - } -@@ -765,8 +765,7 @@ int watchdog_dev_register(struct watchdo - - void watchdog_dev_unregister(struct watchdog_device *wdd) - { -- device_destroy(&watchdog_class, wdd->dev->devt); -- wdd->dev = NULL; -+ device_destroy(&watchdog_class, wdd->wd_data->cdev.dev); - watchdog_cdev_unregister(wdd); - } - ---- a/include/linux/watchdog.h -+++ b/include/linux/watchdog.h -@@ -55,7 +55,6 @@ struct watchdog_ops { - /** struct watchdog_device - The structure that defines a watchdog device - * - * @id: The watchdog's ID. (Allocated by watchdog_register_device) -- * @dev: The device for our watchdog - * @parent: The parent bus device - * @info: Pointer to a watchdog_info structure. - * @ops: Pointer to the list of watchdog operations. -@@ -82,7 +81,6 @@ struct watchdog_ops { - */ - struct watchdog_device { - int id; -- struct device *dev; - struct device *parent; - const struct watchdog_info *info; - const struct watchdog_ops *ops; diff --git a/target/linux/ipq806x/patches-4.4/009-8-watchdog-kill-unref-ref-ops.patch b/target/linux/ipq806x/patches-4.4/009-8-watchdog-kill-unref-ref-ops.patch deleted file mode 100644 index ba56a86538..0000000000 --- a/target/linux/ipq806x/patches-4.4/009-8-watchdog-kill-unref-ref-ops.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 62cd1c40ce1c7c16835b599751c7a002eb5bbdf5 Mon Sep 17 00:00:00 2001 -From: Tomas Winkler -Date: Sun, 3 Jan 2016 13:32:37 +0200 -Subject: watchdog: kill unref/ref ops - -ref/unref ops are not called at all so even marked them as deprecated -is misleading, we need to just drop the API. - -Signed-off-by: Tomas Winkler -Signed-off-by: Guenter Roeck -Signed-off-by: Wim Van Sebroeck ---- - include/linux/watchdog.h | 2 -- - 1 file changed, 2 deletions(-) - ---- a/include/linux/watchdog.h -+++ b/include/linux/watchdog.h -@@ -47,8 +47,6 @@ struct watchdog_ops { - int (*set_timeout)(struct watchdog_device *, unsigned int); - unsigned int (*get_timeleft)(struct watchdog_device *); - int (*restart)(struct watchdog_device *); -- void (*ref)(struct watchdog_device *) __deprecated; -- void (*unref)(struct watchdog_device *) __deprecated; - long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); - }; - diff --git a/target/linux/ipq806x/patches-4.4/010-1-qcom-wdt-use-core-restart-handler.patch b/target/linux/ipq806x/patches-4.4/010-1-qcom-wdt-use-core-restart-handler.patch deleted file mode 100644 index d36dedcd1c..0000000000 --- a/target/linux/ipq806x/patches-4.4/010-1-qcom-wdt-use-core-restart-handler.patch +++ /dev/null @@ -1,113 +0,0 @@ -From 80969a68ffed12f82e2a29908306ff43a6861a61 Mon Sep 17 00:00:00 2001 -From: Damien Riegel -Date: Mon, 16 Nov 2015 12:28:09 -0500 -Subject: watchdog: qcom-wdt: use core restart handler - -Get rid of the custom restart handler by using the one provided by the -watchdog core. - -Signed-off-by: Damien Riegel -Reviewed-by: Guenter Roeck -Reviewed-by: Vivien Didelot -Signed-off-by: Guenter Roeck -Signed-off-by: Wim Van Sebroeck ---- - drivers/watchdog/qcom-wdt.c | 49 ++++++++++++++++++--------------------------- - 1 file changed, 19 insertions(+), 30 deletions(-) - ---- a/drivers/watchdog/qcom-wdt.c -+++ b/drivers/watchdog/qcom-wdt.c -@@ -17,7 +17,6 @@ - #include - #include - #include --#include - #include - - #define WDT_RST 0x38 -@@ -28,7 +27,6 @@ struct qcom_wdt { - struct watchdog_device wdd; - struct clk *clk; - unsigned long rate; -- struct notifier_block restart_nb; - void __iomem *base; - }; - -@@ -72,25 +70,9 @@ static int qcom_wdt_set_timeout(struct w - return qcom_wdt_start(wdd); - } - --static const struct watchdog_ops qcom_wdt_ops = { -- .start = qcom_wdt_start, -- .stop = qcom_wdt_stop, -- .ping = qcom_wdt_ping, -- .set_timeout = qcom_wdt_set_timeout, -- .owner = THIS_MODULE, --}; -- --static const struct watchdog_info qcom_wdt_info = { -- .options = WDIOF_KEEPALIVEPING -- | WDIOF_MAGICCLOSE -- | WDIOF_SETTIMEOUT, -- .identity = KBUILD_MODNAME, --}; -- --static int qcom_wdt_restart(struct notifier_block *nb, unsigned long action, -- void *data) -+static int qcom_wdt_restart(struct watchdog_device *wdd) - { -- struct qcom_wdt *wdt = container_of(nb, struct qcom_wdt, restart_nb); -+ struct qcom_wdt *wdt = to_qcom_wdt(wdd); - u32 timeout; - - /* -@@ -110,9 +92,25 @@ static int qcom_wdt_restart(struct notif - wmb(); - - msleep(150); -- return NOTIFY_DONE; -+ return 0; - } - -+static const struct watchdog_ops qcom_wdt_ops = { -+ .start = qcom_wdt_start, -+ .stop = qcom_wdt_stop, -+ .ping = qcom_wdt_ping, -+ .set_timeout = qcom_wdt_set_timeout, -+ .restart = qcom_wdt_restart, -+ .owner = THIS_MODULE, -+}; -+ -+static const struct watchdog_info qcom_wdt_info = { -+ .options = WDIOF_KEEPALIVEPING -+ | WDIOF_MAGICCLOSE -+ | WDIOF_SETTIMEOUT, -+ .identity = KBUILD_MODNAME, -+}; -+ - static int qcom_wdt_probe(struct platform_device *pdev) - { - struct qcom_wdt *wdt; -@@ -187,14 +185,6 @@ static int qcom_wdt_probe(struct platfor - goto err_clk_unprepare; - } - -- /* -- * WDT restart notifier has priority 0 (use as a last resort) -- */ -- wdt->restart_nb.notifier_call = qcom_wdt_restart; -- ret = register_restart_handler(&wdt->restart_nb); -- if (ret) -- dev_err(&pdev->dev, "failed to setup restart handler\n"); -- - platform_set_drvdata(pdev, wdt); - return 0; - -@@ -207,7 +197,6 @@ static int qcom_wdt_remove(struct platfo - { - struct qcom_wdt *wdt = platform_get_drvdata(pdev); - -- unregister_restart_handler(&wdt->restart_nb); - watchdog_unregister_device(&wdt->wdd); - clk_disable_unprepare(wdt->clk); - return 0; diff --git a/target/linux/ipq806x/patches-4.4/010-2-qcom-wdt-Do-not-set-dev-in-struct-watchdog_device.patch b/target/linux/ipq806x/patches-4.4/010-2-qcom-wdt-Do-not-set-dev-in-struct-watchdog_device.patch deleted file mode 100644 index 31371c2c81..0000000000 --- a/target/linux/ipq806x/patches-4.4/010-2-qcom-wdt-Do-not-set-dev-in-struct-watchdog_device.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 0933b453f1c7104d873aacf8524f8ac380a7ed08 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Thu, 24 Dec 2015 14:22:04 -0800 -Subject: watchdog: qcom-wdt: Do not set 'dev' in struct watchdog_device - -The 'dev' pointer in struct watchdog_device is set by the watchdog core -when registering the watchdog device and not by the driver. It points to -the watchdog device, not its parent. - -Signed-off-by: Guenter Roeck -Signed-off-by: Wim Van Sebroeck ---- - drivers/watchdog/qcom-wdt.c | 1 - - 1 file changed, 1 deletion(-) - ---- a/drivers/watchdog/qcom-wdt.c -+++ b/drivers/watchdog/qcom-wdt.c -@@ -164,7 +164,6 @@ static int qcom_wdt_probe(struct platfor - goto err_clk_unprepare; - } - -- wdt->wdd.dev = &pdev->dev; - wdt->wdd.info = &qcom_wdt_info; - wdt->wdd.ops = &qcom_wdt_ops; - wdt->wdd.min_timeout = 1; diff --git a/target/linux/ipq806x/patches-4.4/010-3-watchdog-Add-action-and-data-parameters-to-restart-handler-callback.patch b/target/linux/ipq806x/patches-4.4/010-3-watchdog-Add-action-and-data-parameters-to-restart-handler-callback.patch deleted file mode 100644 index 7ceff32caa..0000000000 --- a/target/linux/ipq806x/patches-4.4/010-3-watchdog-Add-action-and-data-parameters-to-restart-handler-callback.patch +++ /dev/null @@ -1,51 +0,0 @@ -rom 4d8b229d5ea610affe672e919021e9d02cd877da Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Fri, 26 Feb 2016 17:32:49 -0800 -Subject: watchdog: Add 'action' and 'data' parameters to restart handler - callback - -The 'action' (or restart mode) and data parameters may be used by restart -handlers, so they should be passed to the restart callback functions. - -Cc: Sylvain Lemieux -Signed-off-by: Guenter Roeck -Signed-off-by: Wim Van Sebroeck ---- - drivers/watchdog/qcom-wdt.c | 3 ++- - drivers/watchdog/watchdog_core.c | 2 +- - include/linux/watchdog.h | 2 +- - ---- a/drivers/watchdog/qcom-wdt.c -+++ b/drivers/watchdog/qcom-wdt.c -@@ -70,7 +70,8 @@ static int qcom_wdt_set_timeout(struct w - return qcom_wdt_start(wdd); - } - --static int qcom_wdt_restart(struct watchdog_device *wdd) -+static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action, -+ void *data) - { - struct qcom_wdt *wdt = to_qcom_wdt(wdd); - u32 timeout; ---- a/drivers/watchdog/watchdog_core.c -+++ b/drivers/watchdog/watchdog_core.c -@@ -164,7 +164,7 @@ static int watchdog_restart_notifier(str - - int ret; - -- ret = wdd->ops->restart(wdd); -+ ret = wdd->ops->restart(wdd, action, data); - if (ret) - return NOTIFY_BAD; - ---- a/include/linux/watchdog.h -+++ b/include/linux/watchdog.h -@@ -46,7 +46,7 @@ struct watchdog_ops { - unsigned int (*status)(struct watchdog_device *); - int (*set_timeout)(struct watchdog_device *, unsigned int); - unsigned int (*get_timeleft)(struct watchdog_device *); -- int (*restart)(struct watchdog_device *); -+ int (*restart)(struct watchdog_device *, unsigned long, void *); - long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); - }; - diff --git a/target/linux/ipq806x/patches-4.4/010-4-watchdog-qcom-Report-reboot-reason.patch b/target/linux/ipq806x/patches-4.4/010-4-watchdog-qcom-Report-reboot-reason.patch deleted file mode 100644 index f7fcaeeda2..0000000000 --- a/target/linux/ipq806x/patches-4.4/010-4-watchdog-qcom-Report-reboot-reason.patch +++ /dev/null @@ -1,46 +0,0 @@ -From b6ef36d2c1e391adc1fe1b2dd2a0f887a9f3052b Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Mon, 4 Apr 2016 17:37:46 -0700 -Subject: watchdog: qcom: Report reboot reason - -The Qualcom watchdog timer block reports if the system was reset by the -watchdog. Pass the information to user space. - -Reviewed-by: Grant Grundler -Tested-by: Grant Grundler -Signed-off-by: Guenter Roeck -Signed-off-by: Wim Van Sebroeck ---- - drivers/watchdog/qcom-wdt.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - ---- a/drivers/watchdog/qcom-wdt.c -+++ b/drivers/watchdog/qcom-wdt.c -@@ -21,6 +21,7 @@ - - #define WDT_RST 0x38 - #define WDT_EN 0x40 -+#define WDT_STS 0x44 - #define WDT_BITE_TIME 0x5C - - struct qcom_wdt { -@@ -108,7 +109,8 @@ static const struct watchdog_ops qcom_wd - static const struct watchdog_info qcom_wdt_info = { - .options = WDIOF_KEEPALIVEPING - | WDIOF_MAGICCLOSE -- | WDIOF_SETTIMEOUT, -+ | WDIOF_SETTIMEOUT -+ | WDIOF_CARDRESET, - .identity = KBUILD_MODNAME, - }; - -@@ -171,6 +173,9 @@ static int qcom_wdt_probe(struct platfor - wdt->wdd.max_timeout = 0x10000000U / wdt->rate; - wdt->wdd.parent = &pdev->dev; - -+ if (readl(wdt->base + WDT_STS) & 1) -+ wdt->wdd.bootstatus = WDIOF_CARDRESET; -+ - /* - * If 'timeout-sec' unspecified in devicetree, assume a 30 second - * default, unless the max timeout is less than 30 seconds, then use diff --git a/target/linux/ipq806x/patches-4.4/010-5-watchdog-qcom-add-option-for-standalone-watchdog-not-in-timer-block.patch b/target/linux/ipq806x/patches-4.4/010-5-watchdog-qcom-add-option-for-standalone-watchdog-not-in-timer-block.patch deleted file mode 100644 index 82dd875434..0000000000 --- a/target/linux/ipq806x/patches-4.4/010-5-watchdog-qcom-add-option-for-standalone-watchdog-not-in-timer-block.patch +++ /dev/null @@ -1,162 +0,0 @@ -From f0d9d0f4b44ae5503ea368e7f066b20f12ca1d37 Mon Sep 17 00:00:00 2001 -From: Matthew McClintock -Date: Wed, 29 Jun 2016 10:50:01 -0700 -Subject: watchdog: qcom: add option for standalone watchdog not in timer block - -Commit 0dfd582e026a ("watchdog: qcom: use timer devicetree -binding") moved to use the watchdog as a subset timer -register block. Some devices have the watchdog completely -standalone with slightly different register offsets as -well so let's account for the differences here. - -The existing "kpss-standalone" compatible string doesn't -make it entirely clear exactly what the device is so -rename to "kpss-wdt" to reflect watchdog timer -functionality. Also update ipq4019 DTS with an SoC -specific compatible. - -Signed-off-by: Matthew McClintock -Signed-off-by: Thomas Pedersen -Reviewed-by: Guenter Roeck -Signed-off-by: Guenter Roeck -Signed-off-by: Wim Van Sebroeck ---- - .../devicetree/bindings/watchdog/qcom-wdt.txt | 2 + - arch/arm/boot/dts/qcom-ipq4019.dtsi | 2 +- - drivers/watchdog/qcom-wdt.c | 64 ++++++++++++++++------ - 3 files changed, 51 insertions(+), 17 deletions(-) - ---- a/drivers/watchdog/qcom-wdt.c -+++ b/drivers/watchdog/qcom-wdt.c -@@ -18,19 +18,42 @@ - #include - #include - #include -+#include - --#define WDT_RST 0x38 --#define WDT_EN 0x40 --#define WDT_STS 0x44 --#define WDT_BITE_TIME 0x5C -+enum wdt_reg { -+ WDT_RST, -+ WDT_EN, -+ WDT_STS, -+ WDT_BITE_TIME, -+}; -+ -+static const u32 reg_offset_data_apcs_tmr[] = { -+ [WDT_RST] = 0x38, -+ [WDT_EN] = 0x40, -+ [WDT_STS] = 0x44, -+ [WDT_BITE_TIME] = 0x5C, -+}; -+ -+static const u32 reg_offset_data_kpss[] = { -+ [WDT_RST] = 0x4, -+ [WDT_EN] = 0x8, -+ [WDT_STS] = 0xC, -+ [WDT_BITE_TIME] = 0x14, -+}; - - struct qcom_wdt { - struct watchdog_device wdd; - struct clk *clk; - unsigned long rate; - void __iomem *base; -+ const u32 *layout; - }; - -+static void __iomem *wdt_addr(struct qcom_wdt *wdt, enum wdt_reg reg) -+{ -+ return wdt->base + wdt->layout[reg]; -+} -+ - static inline - struct qcom_wdt *to_qcom_wdt(struct watchdog_device *wdd) - { -@@ -41,10 +64,10 @@ static int qcom_wdt_start(struct watchdo - { - struct qcom_wdt *wdt = to_qcom_wdt(wdd); - -- writel(0, wdt->base + WDT_EN); -- writel(1, wdt->base + WDT_RST); -- writel(wdd->timeout * wdt->rate, wdt->base + WDT_BITE_TIME); -- writel(1, wdt->base + WDT_EN); -+ writel(0, wdt_addr(wdt, WDT_EN)); -+ writel(1, wdt_addr(wdt, WDT_RST)); -+ writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BITE_TIME)); -+ writel(1, wdt_addr(wdt, WDT_EN)); - return 0; - } - -@@ -52,7 +75,7 @@ static int qcom_wdt_stop(struct watchdog - { - struct qcom_wdt *wdt = to_qcom_wdt(wdd); - -- writel(0, wdt->base + WDT_EN); -+ writel(0, wdt_addr(wdt, WDT_EN)); - return 0; - } - -@@ -60,7 +83,7 @@ static int qcom_wdt_ping(struct watchdog - { - struct qcom_wdt *wdt = to_qcom_wdt(wdd); - -- writel(1, wdt->base + WDT_RST); -+ writel(1, wdt_addr(wdt, WDT_RST)); - return 0; - } - -@@ -83,10 +106,10 @@ static int qcom_wdt_restart(struct watch - */ - timeout = 128 * wdt->rate / 1000; - -- writel(0, wdt->base + WDT_EN); -- writel(1, wdt->base + WDT_RST); -- writel(timeout, wdt->base + WDT_BITE_TIME); -- writel(1, wdt->base + WDT_EN); -+ writel(0, wdt_addr(wdt, WDT_EN)); -+ writel(1, wdt_addr(wdt, WDT_RST)); -+ writel(timeout, wdt_addr(wdt, WDT_BITE_TIME)); -+ writel(1, wdt_addr(wdt, WDT_EN)); - - /* - * Actually make sure the above sequence hits hardware before sleeping. -@@ -119,9 +142,16 @@ static int qcom_wdt_probe(struct platfor - struct qcom_wdt *wdt; - struct resource *res; - struct device_node *np = pdev->dev.of_node; -+ const u32 *regs; - u32 percpu_offset; - int ret; - -+ regs = of_device_get_match_data(&pdev->dev); -+ if (!regs) { -+ dev_err(&pdev->dev, "Unsupported QCOM WDT module\n"); -+ return -ENODEV; -+ } -+ - wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); - if (!wdt) - return -ENOMEM; -@@ -172,6 +202,7 @@ static int qcom_wdt_probe(struct platfor - wdt->wdd.min_timeout = 1; - wdt->wdd.max_timeout = 0x10000000U / wdt->rate; - wdt->wdd.parent = &pdev->dev; -+ wdt->layout = regs; - - if (readl(wdt->base + WDT_STS) & 1) - wdt->wdd.bootstatus = WDIOF_CARDRESET; -@@ -208,8 +239,9 @@ static int qcom_wdt_remove(struct platfo - } - - static const struct of_device_id qcom_wdt_of_table[] = { -- { .compatible = "qcom,kpss-timer" }, -- { .compatible = "qcom,scss-timer" }, -+ { .compatible = "qcom,kpss-timer", .data = reg_offset_data_apcs_tmr }, -+ { .compatible = "qcom,scss-timer", .data = reg_offset_data_apcs_tmr }, -+ { .compatible = "qcom,kpss-wdt", .data = reg_offset_data_kpss }, - { }, - }; - MODULE_DEVICE_TABLE(of, qcom_wdt_of_table); diff --git a/target/linux/ipq806x/patches-4.4/010-6-watchdog-qcom-configure-BARK-time-in-addition-to-BITE-time.patch b/target/linux/ipq806x/patches-4.4/010-6-watchdog-qcom-configure-BARK-time-in-addition-to-BITE-time.patch deleted file mode 100644 index bdc3f99897..0000000000 --- a/target/linux/ipq806x/patches-4.4/010-6-watchdog-qcom-configure-BARK-time-in-addition-to-BITE-time.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 10073a205df269abcbd9c3fbc690a813827107ef Mon Sep 17 00:00:00 2001 -From: Matthew McClintock -Date: Tue, 28 Jun 2016 11:35:21 -0700 -Subject: watchdog: qcom: configure BARK time in addition to BITE time - -For certain parts and some versions of TZ, TZ will reset the chip -when a BARK is triggered even though it was not configured here. So -by default let's configure this BARK time as well. - -Signed-off-by: Matthew McClintock -Reviewed-by: Guenter Roeck -Signed-off-by: Thomas Pedersen -Signed-off-by: Guenter Roeck -Signed-off-by: Wim Van Sebroeck ---- - drivers/watchdog/qcom-wdt.c | 5 +++++ - 1 file changed, 5 insertions(+) - ---- a/drivers/watchdog/qcom-wdt.c -+++ b/drivers/watchdog/qcom-wdt.c -@@ -24,6 +24,7 @@ enum wdt_reg { - WDT_RST, - WDT_EN, - WDT_STS, -+ WDT_BARK_TIME, - WDT_BITE_TIME, - }; - -@@ -31,6 +32,7 @@ static const u32 reg_offset_data_apcs_tm - [WDT_RST] = 0x38, - [WDT_EN] = 0x40, - [WDT_STS] = 0x44, -+ [WDT_BARK_TIME] = 0x4C, - [WDT_BITE_TIME] = 0x5C, - }; - -@@ -38,6 +40,7 @@ static const u32 reg_offset_data_kpss[] - [WDT_RST] = 0x4, - [WDT_EN] = 0x8, - [WDT_STS] = 0xC, -+ [WDT_BARK_TIME] = 0x10, - [WDT_BITE_TIME] = 0x14, - }; - -@@ -66,6 +69,7 @@ static int qcom_wdt_start(struct watchdo - - writel(0, wdt_addr(wdt, WDT_EN)); - writel(1, wdt_addr(wdt, WDT_RST)); -+ writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BARK_TIME)); - writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BITE_TIME)); - writel(1, wdt_addr(wdt, WDT_EN)); - return 0; -@@ -108,6 +112,7 @@ static int qcom_wdt_restart(struct watch - - writel(0, wdt_addr(wdt, WDT_EN)); - writel(1, wdt_addr(wdt, WDT_RST)); -+ writel(timeout, wdt_addr(wdt, WDT_BARK_TIME)); - writel(timeout, wdt_addr(wdt, WDT_BITE_TIME)); - writel(1, wdt_addr(wdt, WDT_EN)); - diff --git a/target/linux/ipq806x/patches-4.4/011-1-fix-bld-errs-hwmon-sch56xx-Drop-watchdog-driver-data-reference-count-callbacks.patch b/target/linux/ipq806x/patches-4.4/011-1-fix-bld-errs-hwmon-sch56xx-Drop-watchdog-driver-data-reference-count-callbacks.patch deleted file mode 100644 index c78202b325..0000000000 --- a/target/linux/ipq806x/patches-4.4/011-1-fix-bld-errs-hwmon-sch56xx-Drop-watchdog-driver-data-reference-count-callbacks.patch +++ /dev/null @@ -1,95 +0,0 @@ -From 3b8d058cfe6a3b14abee324f4c4b33e64bf61aeb Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Fri, 25 Dec 2015 16:01:45 -0800 -Subject: hwmon: (sch56xx) Drop watchdog driver data reference count callbacks - -Reference counting is now implemented in the watchdog core and no longer -required in watchdog drivers. - -Signed-off-by: Guenter Roeck -Signed-off-by: Wim Van Sebroeck ---- - drivers/hwmon/sch56xx-common.c | 31 +------------------------------ - 1 file changed, 1 insertion(+), 30 deletions(-) - ---- a/drivers/hwmon/sch56xx-common.c -+++ b/drivers/hwmon/sch56xx-common.c -@@ -30,7 +30,6 @@ - #include - #include - #include --#include - #include - #include "sch56xx-common.h" - -@@ -67,7 +66,6 @@ MODULE_PARM_DESC(nowayout, "Watchdog can - struct sch56xx_watchdog_data { - u16 addr; - struct mutex *io_lock; -- struct kref kref; - struct watchdog_info wdinfo; - struct watchdog_device wddev; - u8 watchdog_preset; -@@ -258,15 +256,6 @@ EXPORT_SYMBOL(sch56xx_read_virtual_reg12 - * Watchdog routines - */ - --/* Release our data struct when we're unregistered *and* -- all references to our watchdog device are released */ --static void watchdog_release_resources(struct kref *r) --{ -- struct sch56xx_watchdog_data *data = -- container_of(r, struct sch56xx_watchdog_data, kref); -- kfree(data); --} -- - static int watchdog_set_timeout(struct watchdog_device *wddev, - unsigned int timeout) - { -@@ -395,28 +384,12 @@ static int watchdog_stop(struct watchdog - return 0; - } - --static void watchdog_ref(struct watchdog_device *wddev) --{ -- struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); -- -- kref_get(&data->kref); --} -- --static void watchdog_unref(struct watchdog_device *wddev) --{ -- struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); -- -- kref_put(&data->kref, watchdog_release_resources); --} -- - static const struct watchdog_ops watchdog_ops = { - .owner = THIS_MODULE, - .start = watchdog_start, - .stop = watchdog_stop, - .ping = watchdog_trigger, - .set_timeout = watchdog_set_timeout, -- .ref = watchdog_ref, -- .unref = watchdog_unref, - }; - - struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, -@@ -448,7 +421,6 @@ struct sch56xx_watchdog_data *sch56xx_wa - - data->addr = addr; - data->io_lock = io_lock; -- kref_init(&data->kref); - - strlcpy(data->wdinfo.identity, "sch56xx watchdog", - sizeof(data->wdinfo.identity)); -@@ -494,8 +466,7 @@ EXPORT_SYMBOL(sch56xx_watchdog_register) - void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data) - { - watchdog_unregister_device(&data->wddev); -- kref_put(&data->kref, watchdog_release_resources); -- /* Don't touch data after this it may have been free-ed! */ -+ kfree(data); - } - EXPORT_SYMBOL(sch56xx_watchdog_unregister); - diff --git a/target/linux/ipq806x/patches-4.4/011-2-fix-bld-errs-watchdog-da9052_wdt-Drop-reference-counting.patch b/target/linux/ipq806x/patches-4.4/011-2-fix-bld-errs-watchdog-da9052_wdt-Drop-reference-counting.patch deleted file mode 100644 index 501bd13636..0000000000 --- a/target/linux/ipq806x/patches-4.4/011-2-fix-bld-errs-watchdog-da9052_wdt-Drop-reference-counting.patch +++ /dev/null @@ -1,87 +0,0 @@ -From 756d1e9247dff6d416b0c9e073247f5e808bb5fa Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Fri, 25 Dec 2015 16:01:43 -0800 -Subject: watchdog: da9052_wdt: Drop reference counting - -Reference counting is now implemented in the watchdog core and no longer -required in watchdog drivers. - -Since it was implememented a no-op, and since the local memory is allocated -with devm_kzalloc(), the reference counting code in the driver really did -not really work anyway, and this patch effectively fixes a bug which could -cause a crash on unloading if the watchdog device was still open. - -Signed-off-by: Guenter Roeck -Signed-off-by: Wim Van Sebroeck ---- - drivers/watchdog/da9052_wdt.c | 24 ------------------------ - 1 file changed, 24 deletions(-) - ---- a/drivers/watchdog/da9052_wdt.c -+++ b/drivers/watchdog/da9052_wdt.c -@@ -31,7 +31,6 @@ - struct da9052_wdt_data { - struct watchdog_device wdt; - struct da9052 *da9052; -- struct kref kref; - unsigned long jpast; - }; - -@@ -51,10 +50,6 @@ static const struct { - }; - - --static void da9052_wdt_release_resources(struct kref *r) --{ --} -- - static int da9052_wdt_set_timeout(struct watchdog_device *wdt_dev, - unsigned int timeout) - { -@@ -104,20 +99,6 @@ static int da9052_wdt_set_timeout(struct - return 0; - } - --static void da9052_wdt_ref(struct watchdog_device *wdt_dev) --{ -- struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); -- -- kref_get(&driver_data->kref); --} -- --static void da9052_wdt_unref(struct watchdog_device *wdt_dev) --{ -- struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); -- -- kref_put(&driver_data->kref, da9052_wdt_release_resources); --} -- - static int da9052_wdt_start(struct watchdog_device *wdt_dev) - { - return da9052_wdt_set_timeout(wdt_dev, wdt_dev->timeout); -@@ -170,8 +151,6 @@ static const struct watchdog_ops da9052_ - .stop = da9052_wdt_stop, - .ping = da9052_wdt_ping, - .set_timeout = da9052_wdt_set_timeout, -- .ref = da9052_wdt_ref, -- .unref = da9052_wdt_unref, - }; - - -@@ -198,8 +177,6 @@ static int da9052_wdt_probe(struct platf - da9052_wdt->parent = &pdev->dev; - watchdog_set_drvdata(da9052_wdt, driver_data); - -- kref_init(&driver_data->kref); -- - ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, - DA9052_CONTROLD_TWDSCALE, 0); - if (ret < 0) { -@@ -225,7 +202,6 @@ static int da9052_wdt_remove(struct plat - struct da9052_wdt_data *driver_data = platform_get_drvdata(pdev); - - watchdog_unregister_device(&driver_data->wdt); -- kref_put(&driver_data->kref, da9052_wdt_release_resources); - - return 0; - } diff --git a/target/linux/ipq806x/patches-4.4/011-3-fix-bld-errs-watchdog-da9055_wdt-Drop-reference-counting.patch b/target/linux/ipq806x/patches-4.4/011-3-fix-bld-errs-watchdog-da9055_wdt-Drop-reference-counting.patch deleted file mode 100644 index 3cf723a9bc..0000000000 --- a/target/linux/ipq806x/patches-4.4/011-3-fix-bld-errs-watchdog-da9055_wdt-Drop-reference-counting.patch +++ /dev/null @@ -1,80 +0,0 @@ -From 43f676ace2e0591718ff493d290bc49b35ec2ffc Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Fri, 25 Dec 2015 16:01:44 -0800 -Subject: watchdog: da9055_wdt: Drop reference counting - -Reference counting is now implemented in the watchdog core and no longer -required in watchdog drivers. - -Since it was implememented a no-op, and since the local memory is allocated -with devm_kzalloc(), the reference counting code in the driver really did -not really work anyway, and this patch effectively fixes a bug which could -cause a crash on unloading if the watchdog device was still open. - -Signed-off-by: Guenter Roeck -Signed-off-by: Wim Van Sebroeck ---- - drivers/watchdog/da9055_wdt.c | 24 ------------------------ - 1 file changed, 24 deletions(-) - ---- a/drivers/watchdog/da9055_wdt.c -+++ b/drivers/watchdog/da9055_wdt.c -@@ -35,7 +35,6 @@ MODULE_PARM_DESC(nowayout, - struct da9055_wdt_data { - struct watchdog_device wdt; - struct da9055 *da9055; -- struct kref kref; - }; - - static const struct { -@@ -99,24 +98,6 @@ static int da9055_wdt_ping(struct watchd - DA9055_WATCHDOG_MASK, 1); - } - --static void da9055_wdt_release_resources(struct kref *r) --{ --} -- --static void da9055_wdt_ref(struct watchdog_device *wdt_dev) --{ -- struct da9055_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); -- -- kref_get(&driver_data->kref); --} -- --static void da9055_wdt_unref(struct watchdog_device *wdt_dev) --{ -- struct da9055_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); -- -- kref_put(&driver_data->kref, da9055_wdt_release_resources); --} -- - static int da9055_wdt_start(struct watchdog_device *wdt_dev) - { - return da9055_wdt_set_timeout(wdt_dev, wdt_dev->timeout); -@@ -138,8 +119,6 @@ static const struct watchdog_ops da9055_ - .stop = da9055_wdt_stop, - .ping = da9055_wdt_ping, - .set_timeout = da9055_wdt_set_timeout, -- .ref = da9055_wdt_ref, -- .unref = da9055_wdt_unref, - }; - - static int da9055_wdt_probe(struct platform_device *pdev) -@@ -165,8 +144,6 @@ static int da9055_wdt_probe(struct platf - watchdog_set_nowayout(da9055_wdt, nowayout); - watchdog_set_drvdata(da9055_wdt, driver_data); - -- kref_init(&driver_data->kref); -- - ret = da9055_wdt_stop(da9055_wdt); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to stop watchdog, %d\n", ret); -@@ -189,7 +166,6 @@ static int da9055_wdt_remove(struct plat - struct da9055_wdt_data *driver_data = platform_get_drvdata(pdev); - - watchdog_unregister_device(&driver_data->wdt); -- kref_put(&driver_data->kref, da9055_wdt_release_resources); - - return 0; - } diff --git a/target/linux/ipq806x/patches-4.4/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch b/target/linux/ipq806x/patches-4.4/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch deleted file mode 100644 index 83b97bf5cf..0000000000 --- a/target/linux/ipq806x/patches-4.4/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch +++ /dev/null @@ -1,746 +0,0 @@ -From patchwork Wed Nov 2 15:56:56 2016 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v9,1/3] clk: qcom: Add support for SMD-RPM Clocks -From: Georgi Djakov -X-Patchwork-Id: 9409419 -Message-Id: <20161102155658.32203-2-georgi.djakov@linaro.org> -To: sboyd@codeaurora.org, mturquette@baylibre.com -Cc: linux-clk@vger.kernel.org, devicetree@vger.kernel.org, - robh+dt@kernel.org, mark.rutland@arm.com, - linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - georgi.djakov@linaro.org -Date: Wed, 2 Nov 2016 17:56:56 +0200 - -This adds initial support for clocks controlled by the Resource -Power Manager (RPM) processor on some Qualcomm SoCs, which use -the qcom_smd_rpm driver to communicate with RPM. -Such platforms are msm8916, apq8084 and msm8974. - -The RPM is a dedicated hardware engine for managing the shared -SoC resources in order to keep the lowest power profile. It -communicates with other hardware subsystems via shared memory -and accepts clock requests, aggregates the requests and turns -the clocks on/off or scales them on demand. - -This driver is based on the codeaurora.org driver: -https://www.codeaurora.org/cgit/quic/la/kernel/msm-3.10/tree/drivers/clk/qcom/clock-rpm.c - -Signed-off-by: Georgi Djakov ---- - .../devicetree/bindings/clock/qcom,rpmcc.txt | 36 ++ - drivers/clk/qcom/Kconfig | 16 + - drivers/clk/qcom/Makefile | 1 + - drivers/clk/qcom/clk-smd-rpm.c | 571 +++++++++++++++++++++ - include/dt-bindings/clock/qcom,rpmcc.h | 45 ++ - 5 files changed, 669 insertions(+) - create mode 100644 Documentation/devicetree/bindings/clock/qcom,rpmcc.txt - create mode 100644 drivers/clk/qcom/clk-smd-rpm.c - create mode 100644 include/dt-bindings/clock/qcom,rpmcc.h - --- -To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in -the body of a message to majordomo@vger.kernel.org -More majordomo info at http://vger.kernel.org/majordomo-info.html - ---- /dev/null -+++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt -@@ -0,0 +1,36 @@ -+Qualcomm RPM Clock Controller Binding -+------------------------------------------------ -+The RPM is a dedicated hardware engine for managing the shared -+SoC resources in order to keep the lowest power profile. It -+communicates with other hardware subsystems via shared memory -+and accepts clock requests, aggregates the requests and turns -+the clocks on/off or scales them on demand. -+ -+Required properties : -+- compatible : shall contain only one of the following. The generic -+ compatible "qcom,rpmcc" should be also included. -+ -+ "qcom,rpmcc-msm8916", "qcom,rpmcc" -+ -+- #clock-cells : shall contain 1 -+ -+Example: -+ smd { -+ compatible = "qcom,smd"; -+ -+ rpm { -+ interrupts = <0 168 1>; -+ qcom,ipc = <&apcs 8 0>; -+ qcom,smd-edge = <15>; -+ -+ rpm_requests { -+ compatible = "qcom,rpm-msm8916"; -+ qcom,smd-channels = "rpm_requests"; -+ -+ rpmcc: clock-controller { -+ compatible = "qcom,rpmcc-msm8916", "qcom,rpmcc"; -+ #clock-cells = <1>; -+ }; -+ }; -+ }; -+ }; ---- a/drivers/clk/qcom/Kconfig -+++ b/drivers/clk/qcom/Kconfig -@@ -2,6 +2,9 @@ config QCOM_GDSC - bool - select PM_GENERIC_DOMAINS if PM - -+config QCOM_RPMCC -+ bool -+ - config COMMON_CLK_QCOM - tristate "Support for Qualcomm's clock controllers" - depends on OF -@@ -9,6 +12,19 @@ config COMMON_CLK_QCOM - select REGMAP_MMIO - select RESET_CONTROLLER - -+config QCOM_CLK_SMD_RPM -+ tristate "RPM over SMD based Clock Controller" -+ depends on COMMON_CLK_QCOM && QCOM_SMD_RPM -+ select QCOM_RPMCC -+ help -+ The RPM (Resource Power Manager) is a dedicated hardware engine for -+ managing the shared SoC resources in order to keep the lowest power -+ profile. It communicates with other hardware subsystems via shared -+ memory and accepts clock requests, aggregates the requests and turns -+ the clocks on/off or scales them on demand. -+ Say Y if you want to support the clocks exposed by the RPM on -+ platforms such as apq8016, apq8084, msm8974 etc. -+ - config APQ_GCC_8084 - tristate "APQ8084 Global Clock Controller" - select QCOM_GDSC ---- a/drivers/clk/qcom/Makefile -+++ b/drivers/clk/qcom/Makefile -@@ -22,3 +22,4 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm896 - obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o - obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o - obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o -+obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o ---- /dev/null -+++ b/drivers/clk/qcom/clk-smd-rpm.c -@@ -0,0 +1,571 @@ -+/* -+ * Copyright (c) 2016, Linaro Limited -+ * Copyright (c) 2014, The Linux Foundation. All rights reserved. -+ * -+ * This software is licensed under the terms of the GNU General Public -+ * License version 2, as published by the Free Software Foundation, and -+ * may be copied, distributed, and modified under those terms. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773 -+#define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370 -+#define QCOM_RPM_SMD_KEY_RATE 0x007a484b -+#define QCOM_RPM_SMD_KEY_ENABLE 0x62616e45 -+#define QCOM_RPM_SMD_KEY_STATE 0x54415453 -+#define QCOM_RPM_SCALING_ENABLE_ID 0x2 -+ -+#define __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, stat_id, \ -+ key) \ -+ static struct clk_smd_rpm _platform##_##_active; \ -+ static struct clk_smd_rpm _platform##_##_name = { \ -+ .rpm_res_type = (type), \ -+ .rpm_clk_id = (r_id), \ -+ .rpm_status_id = (stat_id), \ -+ .rpm_key = (key), \ -+ .peer = &_platform##_##_active, \ -+ .rate = INT_MAX, \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_smd_rpm_ops, \ -+ .name = #_name, \ -+ .parent_names = (const char *[]){ "xo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ }; \ -+ static struct clk_smd_rpm _platform##_##_active = { \ -+ .rpm_res_type = (type), \ -+ .rpm_clk_id = (r_id), \ -+ .rpm_status_id = (stat_id), \ -+ .active_only = true, \ -+ .rpm_key = (key), \ -+ .peer = &_platform##_##_name, \ -+ .rate = INT_MAX, \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_smd_rpm_ops, \ -+ .name = #_active, \ -+ .parent_names = (const char *[]){ "xo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ } -+ -+#define __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, \ -+ stat_id, r, key) \ -+ static struct clk_smd_rpm _platform##_##_active; \ -+ static struct clk_smd_rpm _platform##_##_name = { \ -+ .rpm_res_type = (type), \ -+ .rpm_clk_id = (r_id), \ -+ .rpm_status_id = (stat_id), \ -+ .rpm_key = (key), \ -+ .branch = true, \ -+ .peer = &_platform##_##_active, \ -+ .rate = (r), \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_smd_rpm_branch_ops, \ -+ .name = #_name, \ -+ .parent_names = (const char *[]){ "xo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ }; \ -+ static struct clk_smd_rpm _platform##_##_active = { \ -+ .rpm_res_type = (type), \ -+ .rpm_clk_id = (r_id), \ -+ .rpm_status_id = (stat_id), \ -+ .active_only = true, \ -+ .rpm_key = (key), \ -+ .branch = true, \ -+ .peer = &_platform##_##_name, \ -+ .rate = (r), \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_smd_rpm_branch_ops, \ -+ .name = #_active, \ -+ .parent_names = (const char *[]){ "xo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ } -+ -+#define DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id) \ -+ __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \ -+ 0, QCOM_RPM_SMD_KEY_RATE) -+ -+#define DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, r) \ -+ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, \ -+ r_id, 0, r, QCOM_RPM_SMD_KEY_ENABLE) -+ -+#define DEFINE_CLK_SMD_RPM_QDSS(_platform, _name, _active, type, r_id) \ -+ __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \ -+ 0, QCOM_RPM_SMD_KEY_STATE) -+ -+#define DEFINE_CLK_SMD_RPM_XO_BUFFER(_platform, _name, _active, r_id) \ -+ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \ -+ QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \ -+ QCOM_RPM_KEY_SOFTWARE_ENABLE) -+ -+#define DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(_platform, _name, _active, r_id) \ -+ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \ -+ QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \ -+ QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY) -+ -+#define to_clk_smd_rpm(_hw) container_of(_hw, struct clk_smd_rpm, hw) -+ -+struct clk_smd_rpm { -+ const int rpm_res_type; -+ const int rpm_key; -+ const int rpm_clk_id; -+ const int rpm_status_id; -+ const bool active_only; -+ bool enabled; -+ bool branch; -+ struct clk_smd_rpm *peer; -+ struct clk_hw hw; -+ unsigned long rate; -+ struct qcom_smd_rpm *rpm; -+}; -+ -+struct clk_smd_rpm_req { -+ __le32 key; -+ __le32 nbytes; -+ __le32 value; -+}; -+ -+struct rpm_cc { -+ struct qcom_rpm *rpm; -+ struct clk_hw_onecell_data data; -+ struct clk_hw *hws[]; -+}; -+ -+struct rpm_smd_clk_desc { -+ struct clk_smd_rpm **clks; -+ size_t num_clks; -+}; -+ -+static DEFINE_MUTEX(rpm_smd_clk_lock); -+ -+static int clk_smd_rpm_handoff(struct clk_smd_rpm *r) -+{ -+ int ret; -+ struct clk_smd_rpm_req req = { -+ .key = cpu_to_le32(r->rpm_key), -+ .nbytes = cpu_to_le32(sizeof(u32)), -+ .value = cpu_to_le32(INT_MAX), -+ }; -+ -+ ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE, -+ r->rpm_res_type, r->rpm_clk_id, &req, -+ sizeof(req)); -+ if (ret) -+ return ret; -+ ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE, -+ r->rpm_res_type, r->rpm_clk_id, &req, -+ sizeof(req)); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int clk_smd_rpm_set_rate_active(struct clk_smd_rpm *r, -+ unsigned long rate) -+{ -+ struct clk_smd_rpm_req req = { -+ .key = cpu_to_le32(r->rpm_key), -+ .nbytes = cpu_to_le32(sizeof(u32)), -+ .value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */ -+ }; -+ -+ return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE, -+ r->rpm_res_type, r->rpm_clk_id, &req, -+ sizeof(req)); -+} -+ -+static int clk_smd_rpm_set_rate_sleep(struct clk_smd_rpm *r, -+ unsigned long rate) -+{ -+ struct clk_smd_rpm_req req = { -+ .key = cpu_to_le32(r->rpm_key), -+ .nbytes = cpu_to_le32(sizeof(u32)), -+ .value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */ -+ }; -+ -+ return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE, -+ r->rpm_res_type, r->rpm_clk_id, &req, -+ sizeof(req)); -+} -+ -+static void to_active_sleep(struct clk_smd_rpm *r, unsigned long rate, -+ unsigned long *active, unsigned long *sleep) -+{ -+ *active = rate; -+ -+ /* -+ * Active-only clocks don't care what the rate is during sleep. So, -+ * they vote for zero. -+ */ -+ if (r->active_only) -+ *sleep = 0; -+ else -+ *sleep = *active; -+} -+ -+static int clk_smd_rpm_prepare(struct clk_hw *hw) -+{ -+ struct clk_smd_rpm *r = to_clk_smd_rpm(hw); -+ struct clk_smd_rpm *peer = r->peer; -+ unsigned long this_rate = 0, this_sleep_rate = 0; -+ unsigned long peer_rate = 0, peer_sleep_rate = 0; -+ unsigned long active_rate, sleep_rate; -+ int ret = 0; -+ -+ mutex_lock(&rpm_smd_clk_lock); -+ -+ /* Don't send requests to the RPM if the rate has not been set. */ -+ if (!r->rate) -+ goto out; -+ -+ to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate); -+ -+ /* Take peer clock's rate into account only if it's enabled. */ -+ if (peer->enabled) -+ to_active_sleep(peer, peer->rate, -+ &peer_rate, &peer_sleep_rate); -+ -+ active_rate = max(this_rate, peer_rate); -+ -+ if (r->branch) -+ active_rate = !!active_rate; -+ -+ ret = clk_smd_rpm_set_rate_active(r, active_rate); -+ if (ret) -+ goto out; -+ -+ sleep_rate = max(this_sleep_rate, peer_sleep_rate); -+ if (r->branch) -+ sleep_rate = !!sleep_rate; -+ -+ ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate); -+ if (ret) -+ /* Undo the active set vote and restore it */ -+ ret = clk_smd_rpm_set_rate_active(r, peer_rate); -+ -+out: -+ if (!ret) -+ r->enabled = true; -+ -+ mutex_unlock(&rpm_smd_clk_lock); -+ -+ return ret; -+} -+ -+static void clk_smd_rpm_unprepare(struct clk_hw *hw) -+{ -+ struct clk_smd_rpm *r = to_clk_smd_rpm(hw); -+ struct clk_smd_rpm *peer = r->peer; -+ unsigned long peer_rate = 0, peer_sleep_rate = 0; -+ unsigned long active_rate, sleep_rate; -+ int ret; -+ -+ mutex_lock(&rpm_smd_clk_lock); -+ -+ if (!r->rate) -+ goto out; -+ -+ /* Take peer clock's rate into account only if it's enabled. */ -+ if (peer->enabled) -+ to_active_sleep(peer, peer->rate, &peer_rate, -+ &peer_sleep_rate); -+ -+ active_rate = r->branch ? !!peer_rate : peer_rate; -+ ret = clk_smd_rpm_set_rate_active(r, active_rate); -+ if (ret) -+ goto out; -+ -+ sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate; -+ ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate); -+ if (ret) -+ goto out; -+ -+ r->enabled = false; -+ -+out: -+ mutex_unlock(&rpm_smd_clk_lock); -+} -+ -+static int clk_smd_rpm_set_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long parent_rate) -+{ -+ struct clk_smd_rpm *r = to_clk_smd_rpm(hw); -+ struct clk_smd_rpm *peer = r->peer; -+ unsigned long active_rate, sleep_rate; -+ unsigned long this_rate = 0, this_sleep_rate = 0; -+ unsigned long peer_rate = 0, peer_sleep_rate = 0; -+ int ret = 0; -+ -+ mutex_lock(&rpm_smd_clk_lock); -+ -+ if (!r->enabled) -+ goto out; -+ -+ to_active_sleep(r, rate, &this_rate, &this_sleep_rate); -+ -+ /* Take peer clock's rate into account only if it's enabled. */ -+ if (peer->enabled) -+ to_active_sleep(peer, peer->rate, -+ &peer_rate, &peer_sleep_rate); -+ -+ active_rate = max(this_rate, peer_rate); -+ ret = clk_smd_rpm_set_rate_active(r, active_rate); -+ if (ret) -+ goto out; -+ -+ sleep_rate = max(this_sleep_rate, peer_sleep_rate); -+ ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate); -+ if (ret) -+ goto out; -+ -+ r->rate = rate; -+ -+out: -+ mutex_unlock(&rpm_smd_clk_lock); -+ -+ return ret; -+} -+ -+static long clk_smd_rpm_round_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long *parent_rate) -+{ -+ /* -+ * RPM handles rate rounding and we don't have a way to -+ * know what the rate will be, so just return whatever -+ * rate is requested. -+ */ -+ return rate; -+} -+ -+static unsigned long clk_smd_rpm_recalc_rate(struct clk_hw *hw, -+ unsigned long parent_rate) -+{ -+ struct clk_smd_rpm *r = to_clk_smd_rpm(hw); -+ -+ /* -+ * RPM handles rate rounding and we don't have a way to -+ * know what the rate will be, so just return whatever -+ * rate was set. -+ */ -+ return r->rate; -+} -+ -+static int clk_smd_rpm_enable_scaling(struct qcom_smd_rpm *rpm) -+{ -+ int ret; -+ struct clk_smd_rpm_req req = { -+ .key = cpu_to_le32(QCOM_RPM_SMD_KEY_ENABLE), -+ .nbytes = cpu_to_le32(sizeof(u32)), -+ .value = cpu_to_le32(1), -+ }; -+ -+ ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_SLEEP_STATE, -+ QCOM_SMD_RPM_MISC_CLK, -+ QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req)); -+ if (ret) { -+ pr_err("RPM clock scaling (sleep set) not enabled!\n"); -+ return ret; -+ } -+ -+ ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_ACTIVE_STATE, -+ QCOM_SMD_RPM_MISC_CLK, -+ QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req)); -+ if (ret) { -+ pr_err("RPM clock scaling (active set) not enabled!\n"); -+ return ret; -+ } -+ -+ pr_debug("%s: RPM clock scaling is enabled\n", __func__); -+ return 0; -+} -+ -+static const struct clk_ops clk_smd_rpm_ops = { -+ .prepare = clk_smd_rpm_prepare, -+ .unprepare = clk_smd_rpm_unprepare, -+ .set_rate = clk_smd_rpm_set_rate, -+ .round_rate = clk_smd_rpm_round_rate, -+ .recalc_rate = clk_smd_rpm_recalc_rate, -+}; -+ -+static const struct clk_ops clk_smd_rpm_branch_ops = { -+ .prepare = clk_smd_rpm_prepare, -+ .unprepare = clk_smd_rpm_unprepare, -+ .round_rate = clk_smd_rpm_round_rate, -+ .recalc_rate = clk_smd_rpm_recalc_rate, -+}; -+ -+/* msm8916 */ -+DEFINE_CLK_SMD_RPM(msm8916, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); -+DEFINE_CLK_SMD_RPM(msm8916, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); -+DEFINE_CLK_SMD_RPM(msm8916, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); -+DEFINE_CLK_SMD_RPM_QDSS(msm8916, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1); -+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk1, bb_clk1_a, 1); -+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk2, bb_clk2_a, 2); -+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk1, rf_clk1_a, 4); -+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk2, rf_clk2_a, 5); -+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk1_pin, bb_clk1_a_pin, 1); -+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk2_pin, bb_clk2_a_pin, 2); -+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk1_pin, rf_clk1_a_pin, 4); -+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk2_pin, rf_clk2_a_pin, 5); -+ -+static struct clk_smd_rpm *msm8916_clks[] = { -+ [RPM_SMD_PCNOC_CLK] = &msm8916_pcnoc_clk, -+ [RPM_SMD_PCNOC_A_CLK] = &msm8916_pcnoc_a_clk, -+ [RPM_SMD_SNOC_CLK] = &msm8916_snoc_clk, -+ [RPM_SMD_SNOC_A_CLK] = &msm8916_snoc_a_clk, -+ [RPM_SMD_BIMC_CLK] = &msm8916_bimc_clk, -+ [RPM_SMD_BIMC_A_CLK] = &msm8916_bimc_a_clk, -+ [RPM_SMD_QDSS_CLK] = &msm8916_qdss_clk, -+ [RPM_SMD_QDSS_A_CLK] = &msm8916_qdss_a_clk, -+ [RPM_SMD_BB_CLK1] = &msm8916_bb_clk1, -+ [RPM_SMD_BB_CLK1_A] = &msm8916_bb_clk1_a, -+ [RPM_SMD_BB_CLK2] = &msm8916_bb_clk2, -+ [RPM_SMD_BB_CLK2_A] = &msm8916_bb_clk2_a, -+ [RPM_SMD_RF_CLK1] = &msm8916_rf_clk1, -+ [RPM_SMD_RF_CLK1_A] = &msm8916_rf_clk1_a, -+ [RPM_SMD_RF_CLK2] = &msm8916_rf_clk2, -+ [RPM_SMD_RF_CLK2_A] = &msm8916_rf_clk2_a, -+ [RPM_SMD_BB_CLK1_PIN] = &msm8916_bb_clk1_pin, -+ [RPM_SMD_BB_CLK1_A_PIN] = &msm8916_bb_clk1_a_pin, -+ [RPM_SMD_BB_CLK2_PIN] = &msm8916_bb_clk2_pin, -+ [RPM_SMD_BB_CLK2_A_PIN] = &msm8916_bb_clk2_a_pin, -+ [RPM_SMD_RF_CLK1_PIN] = &msm8916_rf_clk1_pin, -+ [RPM_SMD_RF_CLK1_A_PIN] = &msm8916_rf_clk1_a_pin, -+ [RPM_SMD_RF_CLK2_PIN] = &msm8916_rf_clk2_pin, -+ [RPM_SMD_RF_CLK2_A_PIN] = &msm8916_rf_clk2_a_pin, -+}; -+ -+static const struct rpm_smd_clk_desc rpm_clk_msm8916 = { -+ .clks = msm8916_clks, -+ .num_clks = ARRAY_SIZE(msm8916_clks), -+}; -+ -+static const struct of_device_id rpm_smd_clk_match_table[] = { -+ { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table); -+ -+static int rpm_smd_clk_probe(struct platform_device *pdev) -+{ -+ struct clk_hw **hws; -+ struct rpm_cc *rcc; -+ struct clk_hw_onecell_data *data; -+ int ret; -+ size_t num_clks, i; -+ struct qcom_smd_rpm *rpm; -+ struct clk_smd_rpm **rpm_smd_clks; -+ const struct rpm_smd_clk_desc *desc; -+ -+ rpm = dev_get_drvdata(pdev->dev.parent); -+ if (!rpm) { -+ dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); -+ return -ENODEV; -+ } -+ -+ desc = of_device_get_match_data(&pdev->dev); -+ if (!desc) -+ return -EINVAL; -+ -+ rpm_smd_clks = desc->clks; -+ num_clks = desc->num_clks; -+ -+ rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks, -+ GFP_KERNEL); -+ if (!rcc) -+ return -ENOMEM; -+ -+ hws = rcc->hws; -+ data = &rcc->data; -+ data->num = num_clks; -+ -+ for (i = 0; i < num_clks; i++) { -+ if (!rpm_smd_clks[i]) { -+ continue; -+ } -+ -+ rpm_smd_clks[i]->rpm = rpm; -+ -+ ret = clk_smd_rpm_handoff(rpm_smd_clks[i]); -+ if (ret) -+ goto err; -+ } -+ -+ ret = clk_smd_rpm_enable_scaling(rpm); -+ if (ret) -+ goto err; -+ -+ for (i = 0; i < num_clks; i++) { -+ if (!rpm_smd_clks[i]) { -+ data->hws[i] = ERR_PTR(-ENOENT); -+ continue; -+ } -+ -+ ret = devm_clk_hw_register(&pdev->dev, &rpm_smd_clks[i]->hw); -+ if (ret) -+ goto err; -+ } -+ -+ ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, -+ data); -+ if (ret) -+ goto err; -+ -+ return 0; -+err: -+ dev_err(&pdev->dev, "Error registering SMD clock driver (%d)\n", ret); -+ return ret; -+} -+ -+static int rpm_smd_clk_remove(struct platform_device *pdev) -+{ -+ of_clk_del_provider(pdev->dev.of_node); -+ return 0; -+} -+ -+static struct platform_driver rpm_smd_clk_driver = { -+ .driver = { -+ .name = "qcom-clk-smd-rpm", -+ .of_match_table = rpm_smd_clk_match_table, -+ }, -+ .probe = rpm_smd_clk_probe, -+ .remove = rpm_smd_clk_remove, -+}; -+ -+static int __init rpm_smd_clk_init(void) -+{ -+ return platform_driver_register(&rpm_smd_clk_driver); -+} -+core_initcall(rpm_smd_clk_init); -+ -+static void __exit rpm_smd_clk_exit(void) -+{ -+ platform_driver_unregister(&rpm_smd_clk_driver); -+} -+module_exit(rpm_smd_clk_exit); -+ -+MODULE_DESCRIPTION("Qualcomm RPM over SMD Clock Controller Driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:qcom-clk-smd-rpm"); ---- /dev/null -+++ b/include/dt-bindings/clock/qcom,rpmcc.h -@@ -0,0 +1,45 @@ -+/* -+ * Copyright 2015 Linaro Limited -+ * -+ * This software is licensed under the terms of the GNU General Public -+ * License version 2, as published by the Free Software Foundation, and -+ * may be copied, distributed, and modified under those terms. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#ifndef _DT_BINDINGS_CLK_MSM_RPMCC_H -+#define _DT_BINDINGS_CLK_MSM_RPMCC_H -+ -+/* msm8916 */ -+#define RPM_SMD_XO_CLK_SRC 0 -+#define RPM_SMD_XO_A_CLK_SRC 1 -+#define RPM_SMD_PCNOC_CLK 2 -+#define RPM_SMD_PCNOC_A_CLK 3 -+#define RPM_SMD_SNOC_CLK 4 -+#define RPM_SMD_SNOC_A_CLK 5 -+#define RPM_SMD_BIMC_CLK 6 -+#define RPM_SMD_BIMC_A_CLK 7 -+#define RPM_SMD_QDSS_CLK 8 -+#define RPM_SMD_QDSS_A_CLK 9 -+#define RPM_SMD_BB_CLK1 10 -+#define RPM_SMD_BB_CLK1_A 11 -+#define RPM_SMD_BB_CLK2 12 -+#define RPM_SMD_BB_CLK2_A 13 -+#define RPM_SMD_RF_CLK1 14 -+#define RPM_SMD_RF_CLK1_A 15 -+#define RPM_SMD_RF_CLK2 16 -+#define RPM_SMD_RF_CLK2_A 17 -+#define RPM_SMD_BB_CLK1_PIN 18 -+#define RPM_SMD_BB_CLK1_A_PIN 19 -+#define RPM_SMD_BB_CLK2_PIN 20 -+#define RPM_SMD_BB_CLK2_A_PIN 21 -+#define RPM_SMD_RF_CLK1_PIN 22 -+#define RPM_SMD_RF_CLK1_A_PIN 23 -+#define RPM_SMD_RF_CLK2_PIN 24 -+#define RPM_SMD_RF_CLK2_A_PIN 25 -+ -+#endif diff --git a/target/linux/ipq806x/patches-4.4/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch b/target/linux/ipq806x/patches-4.4/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch deleted file mode 100644 index 8a38ead567..0000000000 --- a/target/linux/ipq806x/patches-4.4/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch +++ /dev/null @@ -1,586 +0,0 @@ -From 872f91b5ea720c72f81fb46d353c43ecb3263ffa Mon Sep 17 00:00:00 2001 -From: Georgi Djakov -Date: Wed, 2 Nov 2016 17:56:57 +0200 -Subject: clk: qcom: Add support for RPM Clocks - -This adds initial support for clocks controlled by the Resource -Power Manager (RPM) processor on some Qualcomm SoCs, which use -the qcom_rpm driver to communicate with RPM. -Such platforms are apq8064 and msm8960. - -Signed-off-by: Georgi Djakov -Acked-by: Rob Herring -Signed-off-by: Stephen Boyd ---- - .../devicetree/bindings/clock/qcom,rpmcc.txt | 1 + - drivers/clk/qcom/Kconfig | 13 + - drivers/clk/qcom/Makefile | 1 + - drivers/clk/qcom/clk-rpm.c | 489 +++++++++++++++++++++ - include/dt-bindings/clock/qcom,rpmcc.h | 24 + - 5 files changed, 528 insertions(+) - create mode 100644 drivers/clk/qcom/clk-rpm.c - ---- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt -+++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt -@@ -11,6 +11,7 @@ Required properties : - compatible "qcom,rpmcc" should be also included. - - "qcom,rpmcc-msm8916", "qcom,rpmcc" -+ "qcom,rpmcc-apq8064", "qcom,rpmcc" - - - #clock-cells : shall contain 1 - ---- a/drivers/clk/qcom/Kconfig -+++ b/drivers/clk/qcom/Kconfig -@@ -12,6 +12,19 @@ config COMMON_CLK_QCOM - select REGMAP_MMIO - select RESET_CONTROLLER - -+config QCOM_CLK_RPM -+ tristate "RPM based Clock Controller" -+ depends on COMMON_CLK_QCOM && MFD_QCOM_RPM -+ select QCOM_RPMCC -+ help -+ The RPM (Resource Power Manager) is a dedicated hardware engine for -+ managing the shared SoC resources in order to keep the lowest power -+ profile. It communicates with other hardware subsystems via shared -+ memory and accepts clock requests, aggregates the requests and turns -+ the clocks on/off or scales them on demand. -+ Say Y if you want to support the clocks exposed by the RPM on -+ platforms such as ipq806x, msm8660, msm8960 etc. -+ - config QCOM_CLK_SMD_RPM - tristate "RPM over SMD based Clock Controller" - depends on COMMON_CLK_QCOM && QCOM_SMD_RPM ---- a/drivers/clk/qcom/Makefile -+++ b/drivers/clk/qcom/Makefile -@@ -23,3 +23,4 @@ obj-$(CONFIG_MSM_GCC_8974) += gcc-msm897 - obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o - obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o - obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o -+obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o ---- /dev/null -+++ b/drivers/clk/qcom/clk-rpm.c -@@ -0,0 +1,489 @@ -+/* -+ * Copyright (c) 2016, Linaro Limited -+ * Copyright (c) 2014, The Linux Foundation. All rights reserved. -+ * -+ * This software is licensed under the terms of the GNU General Public -+ * License version 2, as published by the Free Software Foundation, and -+ * may be copied, distributed, and modified under those terms. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#define QCOM_RPM_MISC_CLK_TYPE 0x306b6c63 -+#define QCOM_RPM_SCALING_ENABLE_ID 0x2 -+ -+#define DEFINE_CLK_RPM(_platform, _name, _active, r_id) \ -+ static struct clk_rpm _platform##_##_active; \ -+ static struct clk_rpm _platform##_##_name = { \ -+ .rpm_clk_id = (r_id), \ -+ .peer = &_platform##_##_active, \ -+ .rate = INT_MAX, \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_rpm_ops, \ -+ .name = #_name, \ -+ .parent_names = (const char *[]){ "pxo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ }; \ -+ static struct clk_rpm _platform##_##_active = { \ -+ .rpm_clk_id = (r_id), \ -+ .peer = &_platform##_##_name, \ -+ .active_only = true, \ -+ .rate = INT_MAX, \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_rpm_ops, \ -+ .name = #_active, \ -+ .parent_names = (const char *[]){ "pxo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ } -+ -+#define DEFINE_CLK_RPM_PXO_BRANCH(_platform, _name, _active, r_id, r) \ -+ static struct clk_rpm _platform##_##_active; \ -+ static struct clk_rpm _platform##_##_name = { \ -+ .rpm_clk_id = (r_id), \ -+ .active_only = true, \ -+ .peer = &_platform##_##_active, \ -+ .rate = (r), \ -+ .branch = true, \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_rpm_branch_ops, \ -+ .name = #_name, \ -+ .parent_names = (const char *[]){ "pxo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ }; \ -+ static struct clk_rpm _platform##_##_active = { \ -+ .rpm_clk_id = (r_id), \ -+ .peer = &_platform##_##_name, \ -+ .rate = (r), \ -+ .branch = true, \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_rpm_branch_ops, \ -+ .name = #_active, \ -+ .parent_names = (const char *[]){ "pxo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ } -+ -+#define DEFINE_CLK_RPM_CXO_BRANCH(_platform, _name, _active, r_id, r) \ -+ static struct clk_rpm _platform##_##_active; \ -+ static struct clk_rpm _platform##_##_name = { \ -+ .rpm_clk_id = (r_id), \ -+ .peer = &_platform##_##_active, \ -+ .rate = (r), \ -+ .branch = true, \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_rpm_branch_ops, \ -+ .name = #_name, \ -+ .parent_names = (const char *[]){ "cxo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ }; \ -+ static struct clk_rpm _platform##_##_active = { \ -+ .rpm_clk_id = (r_id), \ -+ .active_only = true, \ -+ .peer = &_platform##_##_name, \ -+ .rate = (r), \ -+ .branch = true, \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_rpm_branch_ops, \ -+ .name = #_active, \ -+ .parent_names = (const char *[]){ "cxo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ } -+ -+#define to_clk_rpm(_hw) container_of(_hw, struct clk_rpm, hw) -+ -+struct clk_rpm { -+ const int rpm_clk_id; -+ const bool active_only; -+ unsigned long rate; -+ bool enabled; -+ bool branch; -+ struct clk_rpm *peer; -+ struct clk_hw hw; -+ struct qcom_rpm *rpm; -+}; -+ -+struct rpm_cc { -+ struct qcom_rpm *rpm; -+ struct clk_hw_onecell_data data; -+ struct clk_hw *hws[]; -+}; -+ -+struct rpm_clk_desc { -+ struct clk_rpm **clks; -+ size_t num_clks; -+}; -+ -+static DEFINE_MUTEX(rpm_clk_lock); -+ -+static int clk_rpm_handoff(struct clk_rpm *r) -+{ -+ int ret; -+ u32 value = INT_MAX; -+ -+ ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, -+ r->rpm_clk_id, &value, 1); -+ if (ret) -+ return ret; -+ ret = qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, -+ r->rpm_clk_id, &value, 1); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int clk_rpm_set_rate_active(struct clk_rpm *r, unsigned long rate) -+{ -+ u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ -+ -+ return qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, -+ r->rpm_clk_id, &value, 1); -+} -+ -+static int clk_rpm_set_rate_sleep(struct clk_rpm *r, unsigned long rate) -+{ -+ u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ -+ -+ return qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, -+ r->rpm_clk_id, &value, 1); -+} -+ -+static void to_active_sleep(struct clk_rpm *r, unsigned long rate, -+ unsigned long *active, unsigned long *sleep) -+{ -+ *active = rate; -+ -+ /* -+ * Active-only clocks don't care what the rate is during sleep. So, -+ * they vote for zero. -+ */ -+ if (r->active_only) -+ *sleep = 0; -+ else -+ *sleep = *active; -+} -+ -+static int clk_rpm_prepare(struct clk_hw *hw) -+{ -+ struct clk_rpm *r = to_clk_rpm(hw); -+ struct clk_rpm *peer = r->peer; -+ unsigned long this_rate = 0, this_sleep_rate = 0; -+ unsigned long peer_rate = 0, peer_sleep_rate = 0; -+ unsigned long active_rate, sleep_rate; -+ int ret = 0; -+ -+ mutex_lock(&rpm_clk_lock); -+ -+ /* Don't send requests to the RPM if the rate has not been set. */ -+ if (!r->rate) -+ goto out; -+ -+ to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate); -+ -+ /* Take peer clock's rate into account only if it's enabled. */ -+ if (peer->enabled) -+ to_active_sleep(peer, peer->rate, -+ &peer_rate, &peer_sleep_rate); -+ -+ active_rate = max(this_rate, peer_rate); -+ -+ if (r->branch) -+ active_rate = !!active_rate; -+ -+ ret = clk_rpm_set_rate_active(r, active_rate); -+ if (ret) -+ goto out; -+ -+ sleep_rate = max(this_sleep_rate, peer_sleep_rate); -+ if (r->branch) -+ sleep_rate = !!sleep_rate; -+ -+ ret = clk_rpm_set_rate_sleep(r, sleep_rate); -+ if (ret) -+ /* Undo the active set vote and restore it */ -+ ret = clk_rpm_set_rate_active(r, peer_rate); -+ -+out: -+ if (!ret) -+ r->enabled = true; -+ -+ mutex_unlock(&rpm_clk_lock); -+ -+ return ret; -+} -+ -+static void clk_rpm_unprepare(struct clk_hw *hw) -+{ -+ struct clk_rpm *r = to_clk_rpm(hw); -+ struct clk_rpm *peer = r->peer; -+ unsigned long peer_rate = 0, peer_sleep_rate = 0; -+ unsigned long active_rate, sleep_rate; -+ int ret; -+ -+ mutex_lock(&rpm_clk_lock); -+ -+ if (!r->rate) -+ goto out; -+ -+ /* Take peer clock's rate into account only if it's enabled. */ -+ if (peer->enabled) -+ to_active_sleep(peer, peer->rate, &peer_rate, -+ &peer_sleep_rate); -+ -+ active_rate = r->branch ? !!peer_rate : peer_rate; -+ ret = clk_rpm_set_rate_active(r, active_rate); -+ if (ret) -+ goto out; -+ -+ sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate; -+ ret = clk_rpm_set_rate_sleep(r, sleep_rate); -+ if (ret) -+ goto out; -+ -+ r->enabled = false; -+ -+out: -+ mutex_unlock(&rpm_clk_lock); -+} -+ -+static int clk_rpm_set_rate(struct clk_hw *hw, -+ unsigned long rate, unsigned long parent_rate) -+{ -+ struct clk_rpm *r = to_clk_rpm(hw); -+ struct clk_rpm *peer = r->peer; -+ unsigned long active_rate, sleep_rate; -+ unsigned long this_rate = 0, this_sleep_rate = 0; -+ unsigned long peer_rate = 0, peer_sleep_rate = 0; -+ int ret = 0; -+ -+ mutex_lock(&rpm_clk_lock); -+ -+ if (!r->enabled) -+ goto out; -+ -+ to_active_sleep(r, rate, &this_rate, &this_sleep_rate); -+ -+ /* Take peer clock's rate into account only if it's enabled. */ -+ if (peer->enabled) -+ to_active_sleep(peer, peer->rate, -+ &peer_rate, &peer_sleep_rate); -+ -+ active_rate = max(this_rate, peer_rate); -+ ret = clk_rpm_set_rate_active(r, active_rate); -+ if (ret) -+ goto out; -+ -+ sleep_rate = max(this_sleep_rate, peer_sleep_rate); -+ ret = clk_rpm_set_rate_sleep(r, sleep_rate); -+ if (ret) -+ goto out; -+ -+ r->rate = rate; -+ -+out: -+ mutex_unlock(&rpm_clk_lock); -+ -+ return ret; -+} -+ -+static long clk_rpm_round_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long *parent_rate) -+{ -+ /* -+ * RPM handles rate rounding and we don't have a way to -+ * know what the rate will be, so just return whatever -+ * rate is requested. -+ */ -+ return rate; -+} -+ -+static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw, -+ unsigned long parent_rate) -+{ -+ struct clk_rpm *r = to_clk_rpm(hw); -+ -+ /* -+ * RPM handles rate rounding and we don't have a way to -+ * know what the rate will be, so just return whatever -+ * rate was set. -+ */ -+ return r->rate; -+} -+ -+static const struct clk_ops clk_rpm_ops = { -+ .prepare = clk_rpm_prepare, -+ .unprepare = clk_rpm_unprepare, -+ .set_rate = clk_rpm_set_rate, -+ .round_rate = clk_rpm_round_rate, -+ .recalc_rate = clk_rpm_recalc_rate, -+}; -+ -+static const struct clk_ops clk_rpm_branch_ops = { -+ .prepare = clk_rpm_prepare, -+ .unprepare = clk_rpm_unprepare, -+ .round_rate = clk_rpm_round_rate, -+ .recalc_rate = clk_rpm_recalc_rate, -+}; -+ -+/* apq8064 */ -+DEFINE_CLK_RPM(apq8064, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); -+DEFINE_CLK_RPM(apq8064, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); -+DEFINE_CLK_RPM(apq8064, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); -+DEFINE_CLK_RPM(apq8064, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); -+DEFINE_CLK_RPM(apq8064, mmfab_clk, mmfab_a_clk, QCOM_RPM_MM_FABRIC_CLK); -+DEFINE_CLK_RPM(apq8064, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK); -+DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); -+DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); -+DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK); -+ -+static struct clk_rpm *apq8064_clks[] = { -+ [RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk, -+ [RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk, -+ [RPM_CFPB_CLK] = &apq8064_cfpb_clk, -+ [RPM_CFPB_A_CLK] = &apq8064_cfpb_a_clk, -+ [RPM_DAYTONA_FABRIC_CLK] = &apq8064_daytona_clk, -+ [RPM_DAYTONA_FABRIC_A_CLK] = &apq8064_daytona_a_clk, -+ [RPM_EBI1_CLK] = &apq8064_ebi1_clk, -+ [RPM_EBI1_A_CLK] = &apq8064_ebi1_a_clk, -+ [RPM_MM_FABRIC_CLK] = &apq8064_mmfab_clk, -+ [RPM_MM_FABRIC_A_CLK] = &apq8064_mmfab_a_clk, -+ [RPM_MMFPB_CLK] = &apq8064_mmfpb_clk, -+ [RPM_MMFPB_A_CLK] = &apq8064_mmfpb_a_clk, -+ [RPM_SYS_FABRIC_CLK] = &apq8064_sfab_clk, -+ [RPM_SYS_FABRIC_A_CLK] = &apq8064_sfab_a_clk, -+ [RPM_SFPB_CLK] = &apq8064_sfpb_clk, -+ [RPM_SFPB_A_CLK] = &apq8064_sfpb_a_clk, -+ [RPM_QDSS_CLK] = &apq8064_qdss_clk, -+ [RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk, -+}; -+ -+static const struct rpm_clk_desc rpm_clk_apq8064 = { -+ .clks = apq8064_clks, -+ .num_clks = ARRAY_SIZE(apq8064_clks), -+}; -+ -+static const struct of_device_id rpm_clk_match_table[] = { -+ { .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, rpm_clk_match_table); -+ -+static int rpm_clk_probe(struct platform_device *pdev) -+{ -+ struct clk_hw **hws; -+ struct rpm_cc *rcc; -+ struct clk_hw_onecell_data *data; -+ int ret; -+ size_t num_clks, i; -+ struct qcom_rpm *rpm; -+ struct clk_rpm **rpm_clks; -+ const struct rpm_clk_desc *desc; -+ -+ rpm = dev_get_drvdata(pdev->dev.parent); -+ if (!rpm) { -+ dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); -+ return -ENODEV; -+ } -+ -+ desc = of_device_get_match_data(&pdev->dev); -+ if (!desc) -+ return -EINVAL; -+ -+ rpm_clks = desc->clks; -+ num_clks = desc->num_clks; -+ -+ rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks, -+ GFP_KERNEL); -+ if (!rcc) -+ return -ENOMEM; -+ -+ hws = rcc->hws; -+ data = &rcc->data; -+ data->num = num_clks; -+ -+ for (i = 0; i < num_clks; i++) { -+ if (!rpm_clks[i]) -+ continue; -+ -+ rpm_clks[i]->rpm = rpm; -+ -+ ret = clk_rpm_handoff(rpm_clks[i]); -+ if (ret) -+ goto err; -+ } -+ -+ for (i = 0; i < num_clks; i++) { -+ if (!rpm_clks[i]) { -+ data->hws[i] = ERR_PTR(-ENOENT); -+ continue; -+ } -+ -+ ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw); -+ if (ret) -+ goto err; -+ } -+ -+ ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, -+ data); -+ if (ret) -+ goto err; -+ -+ return 0; -+err: -+ dev_err(&pdev->dev, "Error registering RPM Clock driver (%d)\n", ret); -+ return ret; -+} -+ -+static int rpm_clk_remove(struct platform_device *pdev) -+{ -+ of_clk_del_provider(pdev->dev.of_node); -+ return 0; -+} -+ -+static struct platform_driver rpm_clk_driver = { -+ .driver = { -+ .name = "qcom-clk-rpm", -+ .of_match_table = rpm_clk_match_table, -+ }, -+ .probe = rpm_clk_probe, -+ .remove = rpm_clk_remove, -+}; -+ -+static int __init rpm_clk_init(void) -+{ -+ return platform_driver_register(&rpm_clk_driver); -+} -+core_initcall(rpm_clk_init); -+ -+static void __exit rpm_clk_exit(void) -+{ -+ platform_driver_unregister(&rpm_clk_driver); -+} -+module_exit(rpm_clk_exit); -+ -+MODULE_DESCRIPTION("Qualcomm RPM Clock Controller Driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:qcom-clk-rpm"); ---- a/include/dt-bindings/clock/qcom,rpmcc.h -+++ b/include/dt-bindings/clock/qcom,rpmcc.h -@@ -14,6 +14,30 @@ - #ifndef _DT_BINDINGS_CLK_MSM_RPMCC_H - #define _DT_BINDINGS_CLK_MSM_RPMCC_H - -+/* apq8064 */ -+#define RPM_PXO_CLK 0 -+#define RPM_PXO_A_CLK 1 -+#define RPM_CXO_CLK 2 -+#define RPM_CXO_A_CLK 3 -+#define RPM_APPS_FABRIC_CLK 4 -+#define RPM_APPS_FABRIC_A_CLK 5 -+#define RPM_CFPB_CLK 6 -+#define RPM_CFPB_A_CLK 7 -+#define RPM_QDSS_CLK 8 -+#define RPM_QDSS_A_CLK 9 -+#define RPM_DAYTONA_FABRIC_CLK 10 -+#define RPM_DAYTONA_FABRIC_A_CLK 11 -+#define RPM_EBI1_CLK 12 -+#define RPM_EBI1_A_CLK 13 -+#define RPM_MM_FABRIC_CLK 14 -+#define RPM_MM_FABRIC_A_CLK 15 -+#define RPM_MMFPB_CLK 16 -+#define RPM_MMFPB_A_CLK 17 -+#define RPM_SYS_FABRIC_CLK 18 -+#define RPM_SYS_FABRIC_A_CLK 19 -+#define RPM_SFPB_CLK 20 -+#define RPM_SFPB_A_CLK 21 -+ - /* msm8916 */ - #define RPM_SMD_XO_CLK_SRC 0 - #define RPM_SMD_XO_A_CLK_SRC 1 diff --git a/target/linux/ipq806x/patches-4.4/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch b/target/linux/ipq806x/patches-4.4/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch deleted file mode 100644 index e5c1870eab..0000000000 --- a/target/linux/ipq806x/patches-4.4/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch +++ /dev/null @@ -1,94 +0,0 @@ -From c260524aba53b57f72b5446ed553080df30b4d09 Mon Sep 17 00:00:00 2001 -From: Georgi Djakov -Date: Wed, 23 Nov 2016 16:52:49 +0200 -Subject: clk: qcom: clk-rpm: Fix clk_hw references - -Fix the clk_hw references to the actual clocks and add a xlate function -to return the hw pointers from the already existing static array. - -Reported-by: Michael Scott -Signed-off-by: Georgi Djakov -Signed-off-by: Stephen Boyd ---- - drivers/clk/qcom/clk-rpm.c | 36 ++++++++++++++++++++++-------------- - 1 file changed, 22 insertions(+), 14 deletions(-) - ---- a/drivers/clk/qcom/clk-rpm.c -+++ b/drivers/clk/qcom/clk-rpm.c -@@ -127,8 +127,8 @@ struct clk_rpm { - - struct rpm_cc { - struct qcom_rpm *rpm; -- struct clk_hw_onecell_data data; -- struct clk_hw *hws[]; -+ struct clk_rpm **clks; -+ size_t num_clks; - }; - - struct rpm_clk_desc { -@@ -391,11 +391,23 @@ static const struct of_device_id rpm_clk - }; - MODULE_DEVICE_TABLE(of, rpm_clk_match_table); - -+static struct clk_hw *qcom_rpm_clk_hw_get(struct of_phandle_args *clkspec, -+ void *data) -+{ -+ struct rpm_cc *rcc = data; -+ unsigned int idx = clkspec->args[0]; -+ -+ if (idx >= rcc->num_clks) { -+ pr_err("%s: invalid index %u\n", __func__, idx); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ return rcc->clks[idx] ? &rcc->clks[idx]->hw : ERR_PTR(-ENOENT); -+} -+ - static int rpm_clk_probe(struct platform_device *pdev) - { -- struct clk_hw **hws; - struct rpm_cc *rcc; -- struct clk_hw_onecell_data *data; - int ret; - size_t num_clks, i; - struct qcom_rpm *rpm; -@@ -415,14 +427,12 @@ static int rpm_clk_probe(struct platform - rpm_clks = desc->clks; - num_clks = desc->num_clks; - -- rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks, -- GFP_KERNEL); -+ rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc), GFP_KERNEL); - if (!rcc) - return -ENOMEM; - -- hws = rcc->hws; -- data = &rcc->data; -- data->num = num_clks; -+ rcc->clks = rpm_clks; -+ rcc->num_clks = num_clks; - - for (i = 0; i < num_clks; i++) { - if (!rpm_clks[i]) -@@ -436,18 +446,16 @@ static int rpm_clk_probe(struct platform - } - - for (i = 0; i < num_clks; i++) { -- if (!rpm_clks[i]) { -- data->hws[i] = ERR_PTR(-ENOENT); -+ if (!rpm_clks[i]) - continue; -- } - - ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw); - if (ret) - goto err; - } - -- ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, -- data); -+ ret = of_clk_add_hw_provider(pdev->dev.of_node, qcom_rpm_clk_hw_get, -+ rcc); - if (ret) - goto err; - diff --git a/target/linux/ipq806x/patches-4.4/015-1-thermal-qcom-tsens-Add-a-skeletal-TSENS-drivers.patch b/target/linux/ipq806x/patches-4.4/015-1-thermal-qcom-tsens-Add-a-skeletal-TSENS-drivers.patch deleted file mode 100644 index ef7e2f4152..0000000000 --- a/target/linux/ipq806x/patches-4.4/015-1-thermal-qcom-tsens-Add-a-skeletal-TSENS-drivers.patch +++ /dev/null @@ -1,536 +0,0 @@ -From 9066073c6c27994a30187abf3b674770b4088348 Mon Sep 17 00:00:00 2001 -From: Rajendra Nayak -Date: Thu, 5 May 2016 14:21:39 +0530 -Subject: thermal: qcom: tsens: Add a skeletal TSENS drivers - -TSENS is Qualcomms' thermal temperature sensor device. It -supports reading temperatures from multiple thermal sensors -present on various QCOM SoCs. -Calibration data is generally read from a non-volatile memory -(eeprom) device. - -Add a skeleton driver with all the necessary abstractions so -a variety of qcom device families which support TSENS can -add driver extensions. - -Also add the required device tree bindings which can be used -to describe the TSENS device in DT. - -Signed-off-by: Rajendra Nayak -Reviewed-by: Lina Iyer -Signed-off-by: Eduardo Valentin -Signed-off-by: Zhang Rui ---- - .../devicetree/bindings/thermal/qcom-tsens.txt | 21 +++ - drivers/thermal/Kconfig | 5 + - drivers/thermal/Makefile | 1 + - drivers/thermal/qcom/Kconfig | 11 ++ - drivers/thermal/qcom/Makefile | 2 + - drivers/thermal/qcom/tsens-common.c | 141 +++++++++++++++ - drivers/thermal/qcom/tsens.c | 195 +++++++++++++++++++++ - drivers/thermal/qcom/tsens.h | 90 ++++++++++ - 8 files changed, 466 insertions(+) - create mode 100644 Documentation/devicetree/bindings/thermal/qcom-tsens.txt - create mode 100644 drivers/thermal/qcom/Kconfig - create mode 100644 drivers/thermal/qcom/Makefile - create mode 100644 drivers/thermal/qcom/tsens-common.c - create mode 100644 drivers/thermal/qcom/tsens.c - create mode 100644 drivers/thermal/qcom/tsens.h - ---- /dev/null -+++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt -@@ -0,0 +1,21 @@ -+* QCOM SoC Temperature Sensor (TSENS) -+ -+Required properties: -+- compatible : -+ - "qcom,msm8916-tsens" : For 8916 Family of SoCs -+ - "qcom,msm8974-tsens" : For 8974 Family of SoCs -+ - "qcom,msm8996-tsens" : For 8996 Family of SoCs -+ -+- reg: Address range of the thermal registers -+- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description. -+- Refer to Documentation/devicetree/bindings/nvmem/nvmem.txt to know how to specify -+nvmem cells -+ -+Example: -+tsens: thermal-sensor@900000 { -+ compatible = "qcom,msm8916-tsens"; -+ reg = <0x4a8000 0x2000>; -+ nvmem-cells = <&tsens_caldata>, <&tsens_calsel>; -+ nvmem-cell-names = "caldata", "calsel"; -+ #thermal-sensor-cells = <1>; -+ }; ---- a/drivers/thermal/Kconfig -+++ b/drivers/thermal/Kconfig -@@ -391,4 +391,9 @@ config QCOM_SPMI_TEMP_ALARM - real time die temperature if an ADC is present or an estimate of the - temperature based upon the over temperature stage value. - -+menu "Qualcomm thermal drivers" -+depends on (ARCH_QCOM && OF) || COMPILE_TEST -+source "drivers/thermal/qcom/Kconfig" -+endmenu -+ - endif ---- a/drivers/thermal/Makefile -+++ b/drivers/thermal/Makefile -@@ -48,3 +48,4 @@ obj-$(CONFIG_INTEL_PCH_THERMAL) += intel - obj-$(CONFIG_ST_THERMAL) += st/ - obj-$(CONFIG_TEGRA_SOCTHERM) += tegra_soctherm.o - obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o -+obj-$(CONFIG_QCOM_TSENS) += qcom/ ---- /dev/null -+++ b/drivers/thermal/qcom/Kconfig -@@ -0,0 +1,11 @@ -+config QCOM_TSENS -+ tristate "Qualcomm TSENS Temperature Alarm" -+ depends on THERMAL -+ depends on QCOM_QFPROM -+ depends on ARCH_QCOM || COMPILE_TEST -+ help -+ This enables the thermal sysfs driver for the TSENS device. It shows -+ up in Sysfs as a thermal zone with multiple trip points. Disabling the -+ thermal zone device via the mode file results in disabling the sensor. -+ Also able to set threshold temperature for both hot and cold and update -+ when a threshold is reached. ---- /dev/null -+++ b/drivers/thermal/qcom/Makefile -@@ -0,0 +1,2 @@ -+obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o -+qcom_tsens-y += tsens.o tsens-common.o ---- /dev/null -+++ b/drivers/thermal/qcom/tsens-common.c -@@ -0,0 +1,141 @@ -+/* -+ * Copyright (c) 2015, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include "tsens.h" -+ -+#define S0_ST_ADDR 0x1030 -+#define SN_ADDR_OFFSET 0x4 -+#define SN_ST_TEMP_MASK 0x3ff -+#define CAL_DEGC_PT1 30 -+#define CAL_DEGC_PT2 120 -+#define SLOPE_FACTOR 1000 -+#define SLOPE_DEFAULT 3200 -+ -+char *qfprom_read(struct device *dev, const char *cname) -+{ -+ struct nvmem_cell *cell; -+ ssize_t data; -+ char *ret; -+ -+ cell = nvmem_cell_get(dev, cname); -+ if (IS_ERR(cell)) -+ return ERR_CAST(cell); -+ -+ ret = nvmem_cell_read(cell, &data); -+ nvmem_cell_put(cell); -+ -+ return ret; -+} -+ -+/* -+ * Use this function on devices where slope and offset calculations -+ * depend on calibration data read from qfprom. On others the slope -+ * and offset values are derived from tz->tzp->slope and tz->tzp->offset -+ * resp. -+ */ -+void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1, -+ u32 *p2, u32 mode) -+{ -+ int i; -+ int num, den; -+ -+ for (i = 0; i < tmdev->num_sensors; i++) { -+ dev_dbg(tmdev->dev, -+ "sensor%d - data_point1:%#x data_point2:%#x\n", -+ i, p1[i], p2[i]); -+ -+ tmdev->sensor[i].slope = SLOPE_DEFAULT; -+ if (mode == TWO_PT_CALIB) { -+ /* -+ * slope (m) = adc_code2 - adc_code1 (y2 - y1)/ -+ * temp_120_degc - temp_30_degc (x2 - x1) -+ */ -+ num = p2[i] - p1[i]; -+ num *= SLOPE_FACTOR; -+ den = CAL_DEGC_PT2 - CAL_DEGC_PT1; -+ tmdev->sensor[i].slope = num / den; -+ } -+ -+ tmdev->sensor[i].offset = (p1[i] * SLOPE_FACTOR) - -+ (CAL_DEGC_PT1 * -+ tmdev->sensor[i].slope); -+ dev_dbg(tmdev->dev, "offset:%d\n", tmdev->sensor[i].offset); -+ } -+} -+ -+static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s) -+{ -+ int degc, num, den; -+ -+ num = (adc_code * SLOPE_FACTOR) - s->offset; -+ den = s->slope; -+ -+ if (num > 0) -+ degc = num + (den / 2); -+ else if (num < 0) -+ degc = num - (den / 2); -+ else -+ degc = num; -+ -+ degc /= den; -+ -+ return degc; -+} -+ -+int get_temp_common(struct tsens_device *tmdev, int id, int *temp) -+{ -+ struct tsens_sensor *s = &tmdev->sensor[id]; -+ u32 code; -+ unsigned int sensor_addr; -+ int last_temp = 0, ret; -+ -+ sensor_addr = S0_ST_ADDR + s->hw_id * SN_ADDR_OFFSET; -+ ret = regmap_read(tmdev->map, sensor_addr, &code); -+ if (ret) -+ return ret; -+ last_temp = code & SN_ST_TEMP_MASK; -+ -+ *temp = code_to_degc(last_temp, s) * 1000; -+ -+ return 0; -+} -+ -+static const struct regmap_config tsens_config = { -+ .reg_bits = 32, -+ .val_bits = 32, -+ .reg_stride = 4, -+}; -+ -+int __init init_common(struct tsens_device *tmdev) -+{ -+ void __iomem *base; -+ -+ base = of_iomap(tmdev->dev->of_node, 0); -+ if (IS_ERR(base)) -+ return -EINVAL; -+ -+ tmdev->map = devm_regmap_init_mmio(tmdev->dev, base, &tsens_config); -+ if (!tmdev->map) { -+ iounmap(base); -+ return -ENODEV; -+ } -+ -+ return 0; -+} ---- /dev/null -+++ b/drivers/thermal/qcom/tsens.c -@@ -0,0 +1,195 @@ -+/* -+ * Copyright (c) 2015, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "tsens.h" -+ -+static int tsens_get_temp(void *data, int *temp) -+{ -+ const struct tsens_sensor *s = data; -+ struct tsens_device *tmdev = s->tmdev; -+ -+ return tmdev->ops->get_temp(tmdev, s->id, temp); -+} -+ -+static int tsens_get_trend(void *data, long *temp) -+{ -+ const struct tsens_sensor *s = data; -+ struct tsens_device *tmdev = s->tmdev; -+ -+ if (tmdev->ops->get_trend) -+ return tmdev->ops->get_trend(tmdev, s->id, temp); -+ -+ return -ENOTSUPP; -+} -+ -+static int tsens_suspend(struct device *dev) -+{ -+ struct tsens_device *tmdev = dev_get_drvdata(dev); -+ -+ if (tmdev->ops && tmdev->ops->suspend) -+ return tmdev->ops->suspend(tmdev); -+ -+ return 0; -+} -+ -+static int tsens_resume(struct device *dev) -+{ -+ struct tsens_device *tmdev = dev_get_drvdata(dev); -+ -+ if (tmdev->ops && tmdev->ops->resume) -+ return tmdev->ops->resume(tmdev); -+ -+ return 0; -+} -+ -+static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume); -+ -+static const struct of_device_id tsens_table[] = { -+ { -+ .compatible = "qcom,msm8916-tsens", -+ }, { -+ .compatible = "qcom,msm8974-tsens", -+ }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, tsens_table); -+ -+static const struct thermal_zone_of_device_ops tsens_of_ops = { -+ .get_temp = tsens_get_temp, -+ .get_trend = tsens_get_trend, -+}; -+ -+static int tsens_register(struct tsens_device *tmdev) -+{ -+ int i; -+ struct thermal_zone_device *tzd; -+ u32 *hw_id, n = tmdev->num_sensors; -+ -+ hw_id = devm_kcalloc(tmdev->dev, n, sizeof(u32), GFP_KERNEL); -+ if (!hw_id) -+ return -ENOMEM; -+ -+ for (i = 0; i < tmdev->num_sensors; i++) { -+ tmdev->sensor[i].tmdev = tmdev; -+ tmdev->sensor[i].id = i; -+ tzd = devm_thermal_zone_of_sensor_register(tmdev->dev, i, -+ &tmdev->sensor[i], -+ &tsens_of_ops); -+ if (IS_ERR(tzd)) -+ continue; -+ tmdev->sensor[i].tzd = tzd; -+ if (tmdev->ops->enable) -+ tmdev->ops->enable(tmdev, i); -+ } -+ return 0; -+} -+ -+static int tsens_probe(struct platform_device *pdev) -+{ -+ int ret, i; -+ struct device *dev; -+ struct device_node *np; -+ struct tsens_sensor *s; -+ struct tsens_device *tmdev; -+ const struct tsens_data *data; -+ const struct of_device_id *id; -+ -+ if (pdev->dev.of_node) -+ dev = &pdev->dev; -+ else -+ dev = pdev->dev.parent; -+ -+ np = dev->of_node; -+ -+ id = of_match_node(tsens_table, np); -+ if (!id) -+ return -EINVAL; -+ -+ data = id->data; -+ -+ if (data->num_sensors <= 0) { -+ dev_err(dev, "invalid number of sensors\n"); -+ return -EINVAL; -+ } -+ -+ tmdev = devm_kzalloc(dev, sizeof(*tmdev) + -+ data->num_sensors * sizeof(*s), GFP_KERNEL); -+ if (!tmdev) -+ return -ENOMEM; -+ -+ tmdev->dev = dev; -+ tmdev->num_sensors = data->num_sensors; -+ tmdev->ops = data->ops; -+ for (i = 0; i < tmdev->num_sensors; i++) { -+ if (data->hw_ids) -+ tmdev->sensor[i].hw_id = data->hw_ids[i]; -+ else -+ tmdev->sensor[i].hw_id = i; -+ } -+ -+ if (!tmdev->ops || !tmdev->ops->init || !tmdev->ops->get_temp) -+ return -EINVAL; -+ -+ ret = tmdev->ops->init(tmdev); -+ if (ret < 0) { -+ dev_err(dev, "tsens init failed\n"); -+ return ret; -+ } -+ -+ if (tmdev->ops->calibrate) { -+ ret = tmdev->ops->calibrate(tmdev); -+ if (ret < 0) { -+ dev_err(dev, "tsens calibration failed\n"); -+ return ret; -+ } -+ } -+ -+ ret = tsens_register(tmdev); -+ -+ platform_set_drvdata(pdev, tmdev); -+ -+ return ret; -+} -+ -+static int tsens_remove(struct platform_device *pdev) -+{ -+ struct tsens_device *tmdev = platform_get_drvdata(pdev); -+ -+ if (tmdev->ops->disable) -+ tmdev->ops->disable(tmdev); -+ -+ return 0; -+} -+ -+static struct platform_driver tsens_driver = { -+ .probe = tsens_probe, -+ .remove = tsens_remove, -+ .driver = { -+ .name = "qcom-tsens", -+ .pm = &tsens_pm_ops, -+ .of_match_table = tsens_table, -+ }, -+}; -+module_platform_driver(tsens_driver); -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_DESCRIPTION("QCOM Temperature Sensor driver"); -+MODULE_ALIAS("platform:qcom-tsens"); ---- /dev/null -+++ b/drivers/thermal/qcom/tsens.h -@@ -0,0 +1,90 @@ -+/* -+ * Copyright (c) 2015, The Linux Foundation. All rights reserved. -+ * -+ * This software is licensed under the terms of the GNU General Public -+ * License version 2, as published by the Free Software Foundation, and -+ * may be copied, distributed, and modified under those terms. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+#ifndef __QCOM_TSENS_H__ -+#define __QCOM_TSENS_H__ -+ -+#define ONE_PT_CALIB 0x1 -+#define ONE_PT_CALIB2 0x2 -+#define TWO_PT_CALIB 0x3 -+ -+struct tsens_device; -+ -+struct tsens_sensor { -+ struct tsens_device *tmdev; -+ struct thermal_zone_device *tzd; -+ int offset; -+ int id; -+ int hw_id; -+ int slope; -+ u32 status; -+}; -+ -+/** -+ * struct tsens_ops - operations as supported by the tsens device -+ * @init: Function to initialize the tsens device -+ * @calibrate: Function to calibrate the tsens device -+ * @get_temp: Function which returns the temp in millidegC -+ * @enable: Function to enable (clocks/power) tsens device -+ * @disable: Function to disable the tsens device -+ * @suspend: Function to suspend the tsens device -+ * @resume: Function to resume the tsens device -+ * @get_trend: Function to get the thermal/temp trend -+ */ -+struct tsens_ops { -+ /* mandatory callbacks */ -+ int (*init)(struct tsens_device *); -+ int (*calibrate)(struct tsens_device *); -+ int (*get_temp)(struct tsens_device *, int, int *); -+ /* optional callbacks */ -+ int (*enable)(struct tsens_device *, int); -+ void (*disable)(struct tsens_device *); -+ int (*suspend)(struct tsens_device *); -+ int (*resume)(struct tsens_device *); -+ int (*get_trend)(struct tsens_device *, int, long *); -+}; -+ -+/** -+ * struct tsens_data - tsens instance specific data -+ * @num_sensors: Max number of sensors supported by platform -+ * @ops: operations the tsens instance supports -+ * @hw_ids: Subset of sensors ids supported by platform, if not the first n -+ */ -+struct tsens_data { -+ const u32 num_sensors; -+ const struct tsens_ops *ops; -+ unsigned int *hw_ids; -+}; -+ -+/* Registers to be saved/restored across a context loss */ -+struct tsens_context { -+ int threshold; -+ int control; -+}; -+ -+struct tsens_device { -+ struct device *dev; -+ u32 num_sensors; -+ struct regmap *map; -+ struct regmap_field *status_field; -+ struct tsens_context ctx; -+ bool trdy; -+ const struct tsens_ops *ops; -+ struct tsens_sensor sensor[0]; -+}; -+ -+char *qfprom_read(struct device *, const char *); -+void compute_intercept_slope(struct tsens_device *, u32 *, u32 *, u32); -+int init_common(struct tsens_device *); -+int get_temp_common(struct tsens_device *, int, int *); -+ -+#endif /* __QCOM_TSENS_H__ */ diff --git a/target/linux/ipq806x/patches-4.4/015-2-thermal-qcom-tsens-8916-Add-support-for-8916-family-of-SoCs.patch b/target/linux/ipq806x/patches-4.4/015-2-thermal-qcom-tsens-8916-Add-support-for-8916-family-of-SoCs.patch deleted file mode 100644 index d07619645c..0000000000 --- a/target/linux/ipq806x/patches-4.4/015-2-thermal-qcom-tsens-8916-Add-support-for-8916-family-of-SoCs.patch +++ /dev/null @@ -1,165 +0,0 @@ -From 840a5bd3ed3fdd62456d4d26c3128ec10496555b Mon Sep 17 00:00:00 2001 -From: Rajendra Nayak -Date: Thu, 5 May 2016 14:21:40 +0530 -Subject: thermal: qcom: tsens-8916: Add support for 8916 family of SoCs - -Add support to calibrate sensors on 8916 family and also add common -functions to read temperature from sensors (This can be reused on -other SoCs having similar TSENS device) -The calibration data is read from eeprom using the generic nvmem -framework apis. - -Based on the original code by Siddartha Mohanadoss and Stephen Boyd. - -Signed-off-by: Rajendra Nayak -Signed-off-by: Eduardo Valentin -Signed-off-by: Zhang Rui ---- - drivers/thermal/qcom/Makefile | 2 +- - drivers/thermal/qcom/tsens-8916.c | 113 ++++++++++++++++++++++++++++++++++++++ - drivers/thermal/qcom/tsens.c | 1 + - drivers/thermal/qcom/tsens.h | 2 + - 4 files changed, 117 insertions(+), 1 deletion(-) - create mode 100644 drivers/thermal/qcom/tsens-8916.c - ---- a/drivers/thermal/qcom/Makefile -+++ b/drivers/thermal/qcom/Makefile -@@ -1,2 +1,2 @@ - obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o --qcom_tsens-y += tsens.o tsens-common.o -+qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o ---- /dev/null -+++ b/drivers/thermal/qcom/tsens-8916.c -@@ -0,0 +1,113 @@ -+/* -+ * Copyright (c) 2015, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+ -+#include -+#include "tsens.h" -+ -+/* eeprom layout data for 8916 */ -+#define BASE0_MASK 0x0000007f -+#define BASE1_MASK 0xfe000000 -+#define BASE0_SHIFT 0 -+#define BASE1_SHIFT 25 -+ -+#define S0_P1_MASK 0x00000f80 -+#define S1_P1_MASK 0x003e0000 -+#define S2_P1_MASK 0xf8000000 -+#define S3_P1_MASK 0x000003e0 -+#define S4_P1_MASK 0x000f8000 -+ -+#define S0_P2_MASK 0x0001f000 -+#define S1_P2_MASK 0x07c00000 -+#define S2_P2_MASK 0x0000001f -+#define S3_P2_MASK 0x00007c00 -+#define S4_P2_MASK 0x01f00000 -+ -+#define S0_P1_SHIFT 7 -+#define S1_P1_SHIFT 17 -+#define S2_P1_SHIFT 27 -+#define S3_P1_SHIFT 5 -+#define S4_P1_SHIFT 15 -+ -+#define S0_P2_SHIFT 12 -+#define S1_P2_SHIFT 22 -+#define S2_P2_SHIFT 0 -+#define S3_P2_SHIFT 10 -+#define S4_P2_SHIFT 20 -+ -+#define CAL_SEL_MASK 0xe0000000 -+#define CAL_SEL_SHIFT 29 -+ -+static int calibrate_8916(struct tsens_device *tmdev) -+{ -+ int base0 = 0, base1 = 0, i; -+ u32 p1[5], p2[5]; -+ int mode = 0; -+ u32 *qfprom_cdata, *qfprom_csel; -+ -+ qfprom_cdata = (u32 *)qfprom_read(tmdev->dev, "calib"); -+ if (IS_ERR(qfprom_cdata)) -+ return PTR_ERR(qfprom_cdata); -+ -+ qfprom_csel = (u32 *)qfprom_read(tmdev->dev, "calib_sel"); -+ if (IS_ERR(qfprom_csel)) -+ return PTR_ERR(qfprom_csel); -+ -+ mode = (qfprom_csel[0] & CAL_SEL_MASK) >> CAL_SEL_SHIFT; -+ dev_dbg(tmdev->dev, "calibration mode is %d\n", mode); -+ -+ switch (mode) { -+ case TWO_PT_CALIB: -+ base1 = (qfprom_cdata[1] & BASE1_MASK) >> BASE1_SHIFT; -+ p2[0] = (qfprom_cdata[0] & S0_P2_MASK) >> S0_P2_SHIFT; -+ p2[1] = (qfprom_cdata[0] & S1_P2_MASK) >> S1_P2_SHIFT; -+ p2[2] = (qfprom_cdata[1] & S2_P2_MASK) >> S2_P2_SHIFT; -+ p2[3] = (qfprom_cdata[1] & S3_P2_MASK) >> S3_P2_SHIFT; -+ p2[4] = (qfprom_cdata[1] & S4_P2_MASK) >> S4_P2_SHIFT; -+ for (i = 0; i < tmdev->num_sensors; i++) -+ p2[i] = ((base1 + p2[i]) << 3); -+ /* Fall through */ -+ case ONE_PT_CALIB2: -+ base0 = (qfprom_cdata[0] & BASE0_MASK); -+ p1[0] = (qfprom_cdata[0] & S0_P1_MASK) >> S0_P1_SHIFT; -+ p1[1] = (qfprom_cdata[0] & S1_P1_MASK) >> S1_P1_SHIFT; -+ p1[2] = (qfprom_cdata[0] & S2_P1_MASK) >> S2_P1_SHIFT; -+ p1[3] = (qfprom_cdata[1] & S3_P1_MASK) >> S3_P1_SHIFT; -+ p1[4] = (qfprom_cdata[1] & S4_P1_MASK) >> S4_P1_SHIFT; -+ for (i = 0; i < tmdev->num_sensors; i++) -+ p1[i] = (((base0) + p1[i]) << 3); -+ break; -+ default: -+ for (i = 0; i < tmdev->num_sensors; i++) { -+ p1[i] = 500; -+ p2[i] = 780; -+ } -+ break; -+ } -+ -+ compute_intercept_slope(tmdev, p1, p2, mode); -+ -+ return 0; -+} -+ -+const struct tsens_ops ops_8916 = { -+ .init = init_common, -+ .calibrate = calibrate_8916, -+ .get_temp = get_temp_common, -+}; -+ -+const struct tsens_data data_8916 = { -+ .num_sensors = 5, -+ .ops = &ops_8916, -+ .hw_ids = (unsigned int []){0, 1, 2, 4, 5 }, -+}; ---- a/drivers/thermal/qcom/tsens.c -+++ b/drivers/thermal/qcom/tsens.c -@@ -65,6 +65,7 @@ static SIMPLE_DEV_PM_OPS(tsens_pm_ops, t - static const struct of_device_id tsens_table[] = { - { - .compatible = "qcom,msm8916-tsens", -+ .data = &data_8916, - }, { - .compatible = "qcom,msm8974-tsens", - }, ---- a/drivers/thermal/qcom/tsens.h -+++ b/drivers/thermal/qcom/tsens.h -@@ -87,4 +87,6 @@ void compute_intercept_slope(struct tsen - int init_common(struct tsens_device *); - int get_temp_common(struct tsens_device *, int, int *); - -+extern const struct tsens_data data_8916; -+ - #endif /* __QCOM_TSENS_H__ */ diff --git a/target/linux/ipq806x/patches-4.4/015-3-thermal-qcom-tsens-8974-Add-support-for-8974-family-of-SoCs.patch b/target/linux/ipq806x/patches-4.4/015-3-thermal-qcom-tsens-8974-Add-support-for-8974-family-of-SoCs.patch deleted file mode 100644 index 671f461057..0000000000 --- a/target/linux/ipq806x/patches-4.4/015-3-thermal-qcom-tsens-8974-Add-support-for-8974-family-of-SoCs.patch +++ /dev/null @@ -1,293 +0,0 @@ -From 5e6703bd2d83548998848865cb9a9a795f31a311 Mon Sep 17 00:00:00 2001 -From: Rajendra Nayak -Date: Thu, 5 May 2016 14:21:41 +0530 -Subject: thermal: qcom: tsens-8974: Add support for 8974 family of SoCs - -Add .calibrate support for 8974 family as part of tsens_ops. - -Based on the original code by Siddartha Mohanadoss and Stephen Boyd. - -Signed-off-by: Rajendra Nayak -Signed-off-by: Eduardo Valentin -Signed-off-by: Zhang Rui ---- - drivers/thermal/qcom/Makefile | 2 +- - drivers/thermal/qcom/tsens-8974.c | 244 ++++++++++++++++++++++++++++++++++++++ - drivers/thermal/qcom/tsens.c | 1 + - drivers/thermal/qcom/tsens.h | 2 +- - 4 files changed, 247 insertions(+), 2 deletions(-) - create mode 100644 drivers/thermal/qcom/tsens-8974.c - ---- a/drivers/thermal/qcom/Makefile -+++ b/drivers/thermal/qcom/Makefile -@@ -1,2 +1,2 @@ - obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o --qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o -+qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o ---- /dev/null -+++ b/drivers/thermal/qcom/tsens-8974.c -@@ -0,0 +1,244 @@ -+/* -+ * Copyright (c) 2015, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+ -+#include -+#include "tsens.h" -+ -+/* eeprom layout data for 8974 */ -+#define BASE1_MASK 0xff -+#define S0_P1_MASK 0x3f00 -+#define S1_P1_MASK 0xfc000 -+#define S2_P1_MASK 0x3f00000 -+#define S3_P1_MASK 0xfc000000 -+#define S4_P1_MASK 0x3f -+#define S5_P1_MASK 0xfc0 -+#define S6_P1_MASK 0x3f000 -+#define S7_P1_MASK 0xfc0000 -+#define S8_P1_MASK 0x3f000000 -+#define S8_P1_MASK_BKP 0x3f -+#define S9_P1_MASK 0x3f -+#define S9_P1_MASK_BKP 0xfc0 -+#define S10_P1_MASK 0xfc0 -+#define S10_P1_MASK_BKP 0x3f000 -+#define CAL_SEL_0_1 0xc0000000 -+#define CAL_SEL_2 0x40000000 -+#define CAL_SEL_SHIFT 30 -+#define CAL_SEL_SHIFT_2 28 -+ -+#define S0_P1_SHIFT 8 -+#define S1_P1_SHIFT 14 -+#define S2_P1_SHIFT 20 -+#define S3_P1_SHIFT 26 -+#define S5_P1_SHIFT 6 -+#define S6_P1_SHIFT 12 -+#define S7_P1_SHIFT 18 -+#define S8_P1_SHIFT 24 -+#define S9_P1_BKP_SHIFT 6 -+#define S10_P1_SHIFT 6 -+#define S10_P1_BKP_SHIFT 12 -+ -+#define BASE2_SHIFT 12 -+#define BASE2_BKP_SHIFT 18 -+#define S0_P2_SHIFT 20 -+#define S0_P2_BKP_SHIFT 26 -+#define S1_P2_SHIFT 26 -+#define S2_P2_BKP_SHIFT 6 -+#define S3_P2_SHIFT 6 -+#define S3_P2_BKP_SHIFT 12 -+#define S4_P2_SHIFT 12 -+#define S4_P2_BKP_SHIFT 18 -+#define S5_P2_SHIFT 18 -+#define S5_P2_BKP_SHIFT 24 -+#define S6_P2_SHIFT 24 -+#define S7_P2_BKP_SHIFT 6 -+#define S8_P2_SHIFT 6 -+#define S8_P2_BKP_SHIFT 12 -+#define S9_P2_SHIFT 12 -+#define S9_P2_BKP_SHIFT 18 -+#define S10_P2_SHIFT 18 -+#define S10_P2_BKP_SHIFT 24 -+ -+#define BASE2_MASK 0xff000 -+#define BASE2_BKP_MASK 0xfc0000 -+#define S0_P2_MASK 0x3f00000 -+#define S0_P2_BKP_MASK 0xfc000000 -+#define S1_P2_MASK 0xfc000000 -+#define S1_P2_BKP_MASK 0x3f -+#define S2_P2_MASK 0x3f -+#define S2_P2_BKP_MASK 0xfc0 -+#define S3_P2_MASK 0xfc0 -+#define S3_P2_BKP_MASK 0x3f000 -+#define S4_P2_MASK 0x3f000 -+#define S4_P2_BKP_MASK 0xfc0000 -+#define S5_P2_MASK 0xfc0000 -+#define S5_P2_BKP_MASK 0x3f000000 -+#define S6_P2_MASK 0x3f000000 -+#define S6_P2_BKP_MASK 0x3f -+#define S7_P2_MASK 0x3f -+#define S7_P2_BKP_MASK 0xfc0 -+#define S8_P2_MASK 0xfc0 -+#define S8_P2_BKP_MASK 0x3f000 -+#define S9_P2_MASK 0x3f000 -+#define S9_P2_BKP_MASK 0xfc0000 -+#define S10_P2_MASK 0xfc0000 -+#define S10_P2_BKP_MASK 0x3f000000 -+ -+#define BKP_SEL 0x3 -+#define BKP_REDUN_SEL 0xe0000000 -+#define BKP_REDUN_SHIFT 29 -+ -+#define BIT_APPEND 0x3 -+ -+static int calibrate_8974(struct tsens_device *tmdev) -+{ -+ int base1 = 0, base2 = 0, i; -+ u32 p1[11], p2[11]; -+ int mode = 0; -+ u32 *calib, *bkp; -+ u32 calib_redun_sel; -+ -+ calib = (u32 *)qfprom_read(tmdev->dev, "calib"); -+ if (IS_ERR(calib)) -+ return PTR_ERR(calib); -+ -+ bkp = (u32 *)qfprom_read(tmdev->dev, "calib_backup"); -+ if (IS_ERR(bkp)) -+ return PTR_ERR(bkp); -+ -+ calib_redun_sel = bkp[1] & BKP_REDUN_SEL; -+ calib_redun_sel >>= BKP_REDUN_SHIFT; -+ -+ if (calib_redun_sel == BKP_SEL) { -+ mode = (calib[4] & CAL_SEL_0_1) >> CAL_SEL_SHIFT; -+ mode |= (calib[5] & CAL_SEL_2) >> CAL_SEL_SHIFT_2; -+ -+ switch (mode) { -+ case TWO_PT_CALIB: -+ base2 = (bkp[2] & BASE2_BKP_MASK) >> BASE2_BKP_SHIFT; -+ p2[0] = (bkp[2] & S0_P2_BKP_MASK) >> S0_P2_BKP_SHIFT; -+ p2[1] = (bkp[3] & S1_P2_BKP_MASK); -+ p2[2] = (bkp[3] & S2_P2_BKP_MASK) >> S2_P2_BKP_SHIFT; -+ p2[3] = (bkp[3] & S3_P2_BKP_MASK) >> S3_P2_BKP_SHIFT; -+ p2[4] = (bkp[3] & S4_P2_BKP_MASK) >> S4_P2_BKP_SHIFT; -+ p2[5] = (calib[4] & S5_P2_BKP_MASK) >> S5_P2_BKP_SHIFT; -+ p2[6] = (calib[5] & S6_P2_BKP_MASK); -+ p2[7] = (calib[5] & S7_P2_BKP_MASK) >> S7_P2_BKP_SHIFT; -+ p2[8] = (calib[5] & S8_P2_BKP_MASK) >> S8_P2_BKP_SHIFT; -+ p2[9] = (calib[5] & S9_P2_BKP_MASK) >> S9_P2_BKP_SHIFT; -+ p2[10] = (calib[5] & S10_P2_BKP_MASK) >> S10_P2_BKP_SHIFT; -+ /* Fall through */ -+ case ONE_PT_CALIB: -+ case ONE_PT_CALIB2: -+ base1 = bkp[0] & BASE1_MASK; -+ p1[0] = (bkp[0] & S0_P1_MASK) >> S0_P1_SHIFT; -+ p1[1] = (bkp[0] & S1_P1_MASK) >> S1_P1_SHIFT; -+ p1[2] = (bkp[0] & S2_P1_MASK) >> S2_P1_SHIFT; -+ p1[3] = (bkp[0] & S3_P1_MASK) >> S3_P1_SHIFT; -+ p1[4] = (bkp[1] & S4_P1_MASK); -+ p1[5] = (bkp[1] & S5_P1_MASK) >> S5_P1_SHIFT; -+ p1[6] = (bkp[1] & S6_P1_MASK) >> S6_P1_SHIFT; -+ p1[7] = (bkp[1] & S7_P1_MASK) >> S7_P1_SHIFT; -+ p1[8] = (bkp[2] & S8_P1_MASK_BKP) >> S8_P1_SHIFT; -+ p1[9] = (bkp[2] & S9_P1_MASK_BKP) >> S9_P1_BKP_SHIFT; -+ p1[10] = (bkp[2] & S10_P1_MASK_BKP) >> S10_P1_BKP_SHIFT; -+ break; -+ } -+ } else { -+ mode = (calib[1] & CAL_SEL_0_1) >> CAL_SEL_SHIFT; -+ mode |= (calib[3] & CAL_SEL_2) >> CAL_SEL_SHIFT_2; -+ -+ switch (mode) { -+ case TWO_PT_CALIB: -+ base2 = (calib[2] & BASE2_MASK) >> BASE2_SHIFT; -+ p2[0] = (calib[2] & S0_P2_MASK) >> S0_P2_SHIFT; -+ p2[1] = (calib[2] & S1_P2_MASK) >> S1_P2_SHIFT; -+ p2[2] = (calib[3] & S2_P2_MASK); -+ p2[3] = (calib[3] & S3_P2_MASK) >> S3_P2_SHIFT; -+ p2[4] = (calib[3] & S4_P2_MASK) >> S4_P2_SHIFT; -+ p2[5] = (calib[3] & S5_P2_MASK) >> S5_P2_SHIFT; -+ p2[6] = (calib[3] & S6_P2_MASK) >> S6_P2_SHIFT; -+ p2[7] = (calib[4] & S7_P2_MASK); -+ p2[8] = (calib[4] & S8_P2_MASK) >> S8_P2_SHIFT; -+ p2[9] = (calib[4] & S9_P2_MASK) >> S9_P2_SHIFT; -+ p2[10] = (calib[4] & S10_P2_MASK) >> S10_P2_SHIFT; -+ /* Fall through */ -+ case ONE_PT_CALIB: -+ case ONE_PT_CALIB2: -+ base1 = calib[0] & BASE1_MASK; -+ p1[0] = (calib[0] & S0_P1_MASK) >> S0_P1_SHIFT; -+ p1[1] = (calib[0] & S1_P1_MASK) >> S1_P1_SHIFT; -+ p1[2] = (calib[0] & S2_P1_MASK) >> S2_P1_SHIFT; -+ p1[3] = (calib[0] & S3_P1_MASK) >> S3_P1_SHIFT; -+ p1[4] = (calib[1] & S4_P1_MASK); -+ p1[5] = (calib[1] & S5_P1_MASK) >> S5_P1_SHIFT; -+ p1[6] = (calib[1] & S6_P1_MASK) >> S6_P1_SHIFT; -+ p1[7] = (calib[1] & S7_P1_MASK) >> S7_P1_SHIFT; -+ p1[8] = (calib[1] & S8_P1_MASK) >> S8_P1_SHIFT; -+ p1[9] = (calib[2] & S9_P1_MASK); -+ p1[10] = (calib[2] & S10_P1_MASK) >> S10_P1_SHIFT; -+ break; -+ } -+ } -+ -+ switch (mode) { -+ case ONE_PT_CALIB: -+ for (i = 0; i < tmdev->num_sensors; i++) -+ p1[i] += (base1 << 2) | BIT_APPEND; -+ break; -+ case TWO_PT_CALIB: -+ for (i = 0; i < tmdev->num_sensors; i++) { -+ p2[i] += base2; -+ p2[i] <<= 2; -+ p2[i] |= BIT_APPEND; -+ } -+ /* Fall through */ -+ case ONE_PT_CALIB2: -+ for (i = 0; i < tmdev->num_sensors; i++) { -+ p1[i] += base1; -+ p1[i] <<= 2; -+ p1[i] |= BIT_APPEND; -+ } -+ break; -+ default: -+ for (i = 0; i < tmdev->num_sensors; i++) -+ p2[i] = 780; -+ p1[0] = 502; -+ p1[1] = 509; -+ p1[2] = 503; -+ p1[3] = 509; -+ p1[4] = 505; -+ p1[5] = 509; -+ p1[6] = 507; -+ p1[7] = 510; -+ p1[8] = 508; -+ p1[9] = 509; -+ p1[10] = 508; -+ break; -+ } -+ -+ compute_intercept_slope(tmdev, p1, p2, mode); -+ -+ return 0; -+} -+ -+const struct tsens_ops ops_8974 = { -+ .init = init_common, -+ .calibrate = calibrate_8974, -+ .get_temp = get_temp_common, -+}; -+ -+const struct tsens_data data_8974 = { -+ .num_sensors = 11, -+ .ops = &ops_8974, -+}; ---- a/drivers/thermal/qcom/tsens.c -+++ b/drivers/thermal/qcom/tsens.c -@@ -68,6 +68,7 @@ static const struct of_device_id tsens_t - .data = &data_8916, - }, { - .compatible = "qcom,msm8974-tsens", -+ .data = &data_8974, - }, - {} - }; ---- a/drivers/thermal/qcom/tsens.h -+++ b/drivers/thermal/qcom/tsens.h -@@ -87,6 +87,6 @@ void compute_intercept_slope(struct tsen - int init_common(struct tsens_device *); - int get_temp_common(struct tsens_device *, int, int *); - --extern const struct tsens_data data_8916; -+extern const struct tsens_data data_8916, data_8974; - - #endif /* __QCOM_TSENS_H__ */ diff --git a/target/linux/ipq806x/patches-4.4/015-4-thermal-qcom-tsens-8960-Add-support-for-8960-family-of-SoCs.patch b/target/linux/ipq806x/patches-4.4/015-4-thermal-qcom-tsens-8960-Add-support-for-8960-family-of-SoCs.patch deleted file mode 100644 index 05490cd97d..0000000000 --- a/target/linux/ipq806x/patches-4.4/015-4-thermal-qcom-tsens-8960-Add-support-for-8960-family-of-SoCs.patch +++ /dev/null @@ -1,364 +0,0 @@ -From 20d4fd84bf524ad91e2cc3e4ab4020c27cfc0081 Mon Sep 17 00:00:00 2001 -From: Rajendra Nayak -Date: Thu, 5 May 2016 14:21:43 +0530 -Subject: thermal: qcom: tsens-8960: Add support for 8960 family of SoCs - -8960 family of SoCs have the TSENS device as part of GCC, hence -the driver probes the virtual child device created by GCC and -uses the parent to extract all DT properties and reuses the GCC -regmap. - -Also GCC/TSENS are part of a domain thats not always ON. -Hence add .suspend and .resume hooks to save and restore some of -the inited register context. - -Also 8960 family have some of the TSENS init sequence thats -required to be done by the HLOS driver (some later versions of TSENS -do not export these registers to non-secure world, and hence need -these initializations to be done by secure bootloaders) - -8660 from the same family has just one sensor and hence some register -offset/layout differences which need special handling in the driver. - -Based on the original code from Siddartha Mohanadoss, Stephen Boyd and -Narendran Rajan. - -Signed-off-by: Rajendra Nayak -Signed-off-by: Eduardo Valentin -Signed-off-by: Zhang Rui ---- - drivers/thermal/qcom/Makefile | 2 +- - drivers/thermal/qcom/tsens-8960.c | 292 ++++++++++++++++++++++++++++++++++++++ - drivers/thermal/qcom/tsens.c | 8 +- - drivers/thermal/qcom/tsens.h | 2 +- - 4 files changed, 298 insertions(+), 6 deletions(-) - create mode 100644 drivers/thermal/qcom/tsens-8960.c - ---- a/drivers/thermal/qcom/Makefile -+++ b/drivers/thermal/qcom/Makefile -@@ -1,2 +1,2 @@ - obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o --qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o -+qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o ---- /dev/null -+++ b/drivers/thermal/qcom/tsens-8960.c -@@ -0,0 +1,292 @@ -+/* -+ * Copyright (c) 2015, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include "tsens.h" -+ -+#define CAL_MDEGC 30000 -+ -+#define CONFIG_ADDR 0x3640 -+#define CONFIG_ADDR_8660 0x3620 -+/* CONFIG_ADDR bitmasks */ -+#define CONFIG 0x9b -+#define CONFIG_MASK 0xf -+#define CONFIG_8660 1 -+#define CONFIG_SHIFT_8660 28 -+#define CONFIG_MASK_8660 (3 << CONFIG_SHIFT_8660) -+ -+#define STATUS_CNTL_ADDR_8064 0x3660 -+#define CNTL_ADDR 0x3620 -+/* CNTL_ADDR bitmasks */ -+#define EN BIT(0) -+#define SW_RST BIT(1) -+#define SENSOR0_EN BIT(3) -+#define SLP_CLK_ENA BIT(26) -+#define SLP_CLK_ENA_8660 BIT(24) -+#define MEASURE_PERIOD 1 -+#define SENSOR0_SHIFT 3 -+ -+/* INT_STATUS_ADDR bitmasks */ -+#define MIN_STATUS_MASK BIT(0) -+#define LOWER_STATUS_CLR BIT(1) -+#define UPPER_STATUS_CLR BIT(2) -+#define MAX_STATUS_MASK BIT(3) -+ -+#define THRESHOLD_ADDR 0x3624 -+/* THRESHOLD_ADDR bitmasks */ -+#define THRESHOLD_MAX_LIMIT_SHIFT 24 -+#define THRESHOLD_MIN_LIMIT_SHIFT 16 -+#define THRESHOLD_UPPER_LIMIT_SHIFT 8 -+#define THRESHOLD_LOWER_LIMIT_SHIFT 0 -+ -+/* Initial temperature threshold values */ -+#define LOWER_LIMIT_TH 0x50 -+#define UPPER_LIMIT_TH 0xdf -+#define MIN_LIMIT_TH 0x0 -+#define MAX_LIMIT_TH 0xff -+ -+#define S0_STATUS_ADDR 0x3628 -+#define INT_STATUS_ADDR 0x363c -+#define TRDY_MASK BIT(7) -+#define TIMEOUT_US 100 -+ -+static int suspend_8960(struct tsens_device *tmdev) -+{ -+ int ret; -+ unsigned int mask; -+ struct regmap *map = tmdev->map; -+ -+ ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold); -+ if (ret) -+ return ret; -+ -+ ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control); -+ if (ret) -+ return ret; -+ -+ if (tmdev->num_sensors > 1) -+ mask = SLP_CLK_ENA | EN; -+ else -+ mask = SLP_CLK_ENA_8660 | EN; -+ -+ ret = regmap_update_bits(map, CNTL_ADDR, mask, 0); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int resume_8960(struct tsens_device *tmdev) -+{ -+ int ret; -+ struct regmap *map = tmdev->map; -+ -+ ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST); -+ if (ret) -+ return ret; -+ -+ /* -+ * Separate CONFIG restore is not needed only for 8660 as -+ * config is part of CTRL Addr and its restored as such -+ */ -+ if (tmdev->num_sensors > 1) { -+ ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG); -+ if (ret) -+ return ret; -+ } -+ -+ ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold); -+ if (ret) -+ return ret; -+ -+ ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int enable_8960(struct tsens_device *tmdev, int id) -+{ -+ int ret; -+ u32 reg, mask; -+ -+ ret = regmap_read(tmdev->map, CNTL_ADDR, ®); -+ if (ret) -+ return ret; -+ -+ mask = BIT(id + SENSOR0_SHIFT); -+ ret = regmap_write(tmdev->map, CNTL_ADDR, reg | SW_RST); -+ if (ret) -+ return ret; -+ -+ if (tmdev->num_sensors > 1) -+ reg |= mask | SLP_CLK_ENA | EN; -+ else -+ reg |= mask | SLP_CLK_ENA_8660 | EN; -+ -+ ret = regmap_write(tmdev->map, CNTL_ADDR, reg); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static void disable_8960(struct tsens_device *tmdev) -+{ -+ int ret; -+ u32 reg_cntl; -+ u32 mask; -+ -+ mask = GENMASK(tmdev->num_sensors - 1, 0); -+ mask <<= SENSOR0_SHIFT; -+ mask |= EN; -+ -+ ret = regmap_read(tmdev->map, CNTL_ADDR, ®_cntl); -+ if (ret) -+ return; -+ -+ reg_cntl &= ~mask; -+ -+ if (tmdev->num_sensors > 1) -+ reg_cntl &= ~SLP_CLK_ENA; -+ else -+ reg_cntl &= ~SLP_CLK_ENA_8660; -+ -+ regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); -+} -+ -+static int init_8960(struct tsens_device *tmdev) -+{ -+ int ret, i; -+ u32 reg_cntl; -+ -+ tmdev->map = dev_get_regmap(tmdev->dev, NULL); -+ if (!tmdev->map) -+ return -ENODEV; -+ -+ /* -+ * The status registers for each sensor are discontiguous -+ * because some SoCs have 5 sensors while others have more -+ * but the control registers stay in the same place, i.e -+ * directly after the first 5 status registers. -+ */ -+ for (i = 0; i < tmdev->num_sensors; i++) { -+ if (i >= 5) -+ tmdev->sensor[i].status = S0_STATUS_ADDR + 40; -+ tmdev->sensor[i].status += i * 4; -+ } -+ -+ reg_cntl = SW_RST; -+ ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl); -+ if (ret) -+ return ret; -+ -+ if (tmdev->num_sensors > 1) { -+ reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18); -+ reg_cntl &= ~SW_RST; -+ ret = regmap_update_bits(tmdev->map, CONFIG_ADDR, -+ CONFIG_MASK, CONFIG); -+ } else { -+ reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16); -+ reg_cntl &= ~CONFIG_MASK_8660; -+ reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660; -+ } -+ -+ reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT; -+ ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); -+ if (ret) -+ return ret; -+ -+ reg_cntl |= EN; -+ ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int calibrate_8960(struct tsens_device *tmdev) -+{ -+ int i; -+ char *data; -+ -+ ssize_t num_read = tmdev->num_sensors; -+ struct tsens_sensor *s = tmdev->sensor; -+ -+ data = qfprom_read(tmdev->dev, "calib"); -+ if (IS_ERR(data)) -+ data = qfprom_read(tmdev->dev, "calib_backup"); -+ if (IS_ERR(data)) -+ return PTR_ERR(data); -+ -+ for (i = 0; i < num_read; i++, s++) -+ s->offset = data[i]; -+ -+ return 0; -+} -+ -+/* Temperature on y axis and ADC-code on x-axis */ -+static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s) -+{ -+ int slope, offset; -+ -+ slope = thermal_zone_get_slope(s->tzd); -+ offset = CAL_MDEGC - slope * s->offset; -+ -+ return adc_code * slope + offset; -+} -+ -+static int get_temp_8960(struct tsens_device *tmdev, int id, int *temp) -+{ -+ int ret; -+ u32 code, trdy; -+ const struct tsens_sensor *s = &tmdev->sensor[id]; -+ unsigned long timeout; -+ -+ timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); -+ do { -+ ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy); -+ if (ret) -+ return ret; -+ if (!(trdy & TRDY_MASK)) -+ continue; -+ ret = regmap_read(tmdev->map, s->status, &code); -+ if (ret) -+ return ret; -+ *temp = code_to_mdegC(code, s); -+ return 0; -+ } while (time_before(jiffies, timeout)); -+ -+ return -ETIMEDOUT; -+} -+ -+const struct tsens_ops ops_8960 = { -+ .init = init_8960, -+ .calibrate = calibrate_8960, -+ .get_temp = get_temp_8960, -+ .enable = enable_8960, -+ .disable = disable_8960, -+ .suspend = suspend_8960, -+ .resume = resume_8960, -+}; -+ -+const struct tsens_data data_8960 = { -+ .num_sensors = 11, -+ .ops = &ops_8960, -+}; ---- a/drivers/thermal/qcom/tsens.c -+++ b/drivers/thermal/qcom/tsens.c -@@ -122,10 +122,10 @@ static int tsens_probe(struct platform_d - np = dev->of_node; - - id = of_match_node(tsens_table, np); -- if (!id) -- return -EINVAL; -- -- data = id->data; -+ if (id) -+ data = id->data; -+ else -+ data = &data_8960; - - if (data->num_sensors <= 0) { - dev_err(dev, "invalid number of sensors\n"); ---- a/drivers/thermal/qcom/tsens.h -+++ b/drivers/thermal/qcom/tsens.h -@@ -87,6 +87,6 @@ void compute_intercept_slope(struct tsen - int init_common(struct tsens_device *); - int get_temp_common(struct tsens_device *, int, int *); - --extern const struct tsens_data data_8916, data_8974; -+extern const struct tsens_data data_8916, data_8974, data_8960; - - #endif /* __QCOM_TSENS_H__ */ diff --git a/target/linux/ipq806x/patches-4.4/015-8-qcom-tsens-8916-mark-PM-functions-__maybe_unused.patch b/target/linux/ipq806x/patches-4.4/015-8-qcom-tsens-8916-mark-PM-functions-__maybe_unused.patch deleted file mode 100644 index 39d2173fe9..0000000000 --- a/target/linux/ipq806x/patches-4.4/015-8-qcom-tsens-8916-mark-PM-functions-__maybe_unused.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 5b97469a55872a30a0d53a1279a8ae8b1c68b52c Mon Sep 17 00:00:00 2001 -From: Arnd Bergmann -Date: Mon, 4 Jul 2016 15:12:28 +0200 -Subject: thermal: qcom: tsens-8916: mark PM functions __maybe_unused - -The newly added tsens-8916 driver produces warnings when CONFIG_PM -is disabled: - -drivers/thermal/qcom/tsens.c:53:12: error: 'tsens_resume' defined but not used [-Werror=unused-function] - static int tsens_resume(struct device *dev) - ^~~~~~~~~~~~ -drivers/thermal/qcom/tsens.c:43:12: error: 'tsens_suspend' defined but not used [-Werror=unused-function] - static int tsens_suspend(struct device *dev) - ^~~~~~~~~~~~~ - -This marks both functions __maybe_unused to let the compiler -know that they might be used in other configurations, without -adding ugly #ifdef logic. - -Signed-off-by: Arnd Bergmann -Reviewed-by: Rajendra Nayak -Signed-off-by: Zhang Rui ---- - drivers/thermal/qcom/tsens.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - ---- a/drivers/thermal/qcom/tsens.c -+++ b/drivers/thermal/qcom/tsens.c -@@ -40,7 +40,7 @@ static int tsens_get_trend(void *data, l - return -ENOTSUPP; - } - --static int tsens_suspend(struct device *dev) -+static int __maybe_unused tsens_suspend(struct device *dev) - { - struct tsens_device *tmdev = dev_get_drvdata(dev); - -@@ -50,7 +50,7 @@ static int tsens_suspend(struct device * - return 0; - } - --static int tsens_resume(struct device *dev) -+static int __maybe_unused tsens_resume(struct device *dev) - { - struct tsens_device *tmdev = dev_get_drvdata(dev); - diff --git a/target/linux/ipq806x/patches-4.4/016-2-thermal-of-thermal-Add-devm-version-of.patch b/target/linux/ipq806x/patches-4.4/016-2-thermal-of-thermal-Add-devm-version-of.patch deleted file mode 100644 index 1ca7326832..0000000000 --- a/target/linux/ipq806x/patches-4.4/016-2-thermal-of-thermal-Add-devm-version-of.patch +++ /dev/null @@ -1,143 +0,0 @@ -From e498b4984db82b4ba3ceea7dba813222a31e9c2e Mon Sep 17 00:00:00 2001 -From: Laxman Dewangan -Date: Wed, 9 Mar 2016 18:40:06 +0530 -Subject: thermal: of-thermal: Add devm version of - thermal_zone_of_sensor_register - -Add resource managed version of thermal_zone_of_sensor_register() and -thermal_zone_of_sensor_unregister(). - -This helps in reducing the code size in error path, remove of -driver remove callbacks and making proper sequence for deallocations. - -Signed-off-by: Laxman Dewangan -Signed-off-by: Eduardo Valentin ---- - drivers/thermal/of-thermal.c | 81 ++++++++++++++++++++++++++++++++++++++++++++ - include/linux/thermal.h | 18 ++++++++++ - 2 files changed, 99 insertions(+) - ---- a/drivers/thermal/of-thermal.c -+++ b/drivers/thermal/of-thermal.c -@@ -559,6 +559,87 @@ void thermal_zone_of_sensor_unregister(s - } - EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister); - -+static void devm_thermal_zone_of_sensor_release(struct device *dev, void *res) -+{ -+ thermal_zone_of_sensor_unregister(dev, -+ *(struct thermal_zone_device **)res); -+} -+ -+static int devm_thermal_zone_of_sensor_match(struct device *dev, void *res, -+ void *data) -+{ -+ struct thermal_zone_device **r = res; -+ -+ if (WARN_ON(!r || !*r)) -+ return 0; -+ -+ return *r == data; -+} -+ -+/** -+ * devm_thermal_zone_of_sensor_register - Resource managed version of -+ * thermal_zone_of_sensor_register() -+ * @dev: a valid struct device pointer of a sensor device. Must contain -+ * a valid .of_node, for the sensor node. -+ * @sensor_id: a sensor identifier, in case the sensor IP has more -+ * than one sensors -+ * @data: a private pointer (owned by the caller) that will be passed -+ * back, when a temperature reading is needed. -+ * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp. -+ * -+ * Refer thermal_zone_of_sensor_register() for more details. -+ * -+ * Return: On success returns a valid struct thermal_zone_device, -+ * otherwise, it returns a corresponding ERR_PTR(). Caller must -+ * check the return value with help of IS_ERR() helper. -+ * Registered hermal_zone_device device will automatically be -+ * released when device is unbounded. -+ */ -+struct thermal_zone_device *devm_thermal_zone_of_sensor_register( -+ struct device *dev, int sensor_id, -+ void *data, const struct thermal_zone_of_device_ops *ops) -+{ -+ struct thermal_zone_device **ptr, *tzd; -+ -+ ptr = devres_alloc(devm_thermal_zone_of_sensor_release, sizeof(*ptr), -+ GFP_KERNEL); -+ if (!ptr) -+ return ERR_PTR(-ENOMEM); -+ -+ tzd = thermal_zone_of_sensor_register(dev, sensor_id, data, ops); -+ if (IS_ERR(tzd)) { -+ devres_free(ptr); -+ return tzd; -+ } -+ -+ *ptr = tzd; -+ devres_add(dev, ptr); -+ -+ return tzd; -+} -+EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_register); -+ -+/** -+ * devm_thermal_zone_of_sensor_unregister - Resource managed version of -+ * thermal_zone_of_sensor_unregister(). -+ * @dev: Device for which which resource was allocated. -+ * @tzd: a pointer to struct thermal_zone_device where the sensor is registered. -+ * -+ * This function removes the sensor callbacks and private data from the -+ * thermal zone device registered with devm_thermal_zone_of_sensor_register() -+ * API. It will also silent the zone by remove the .get_temp() and .get_trend() -+ * thermal zone device callbacks. -+ * Normally this function will not need to be called and the resource -+ * management code will ensure that the resource is freed. -+ */ -+void devm_thermal_zone_of_sensor_unregister(struct device *dev, -+ struct thermal_zone_device *tzd) -+{ -+ WARN_ON(devres_release(dev, devm_thermal_zone_of_sensor_release, -+ devm_thermal_zone_of_sensor_match, tzd)); -+} -+EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_unregister); -+ - /*** functions parsing device tree nodes ***/ - - /** ---- a/include/linux/thermal.h -+++ b/include/linux/thermal.h -@@ -364,6 +364,11 @@ thermal_zone_of_sensor_register(struct d - const struct thermal_zone_of_device_ops *ops); - void thermal_zone_of_sensor_unregister(struct device *dev, - struct thermal_zone_device *tz); -+struct thermal_zone_device *devm_thermal_zone_of_sensor_register( -+ struct device *dev, int id, void *data, -+ const struct thermal_zone_of_device_ops *ops); -+void devm_thermal_zone_of_sensor_unregister(struct device *dev, -+ struct thermal_zone_device *tz); - #else - static inline struct thermal_zone_device * - thermal_zone_of_sensor_register(struct device *dev, int id, void *data, -@@ -378,6 +383,19 @@ void thermal_zone_of_sensor_unregister(s - { - } - -+static inline struct thermal_zone_device *devm_thermal_zone_of_sensor_register( -+ struct device *dev, int id, void *data, -+ const struct thermal_zone_of_device_ops *ops) -+{ -+ return ERR_PTR(-ENODEV); -+} -+ -+static inline -+void devm_thermal_zone_of_sensor_unregister(struct device *dev, -+ struct thermal_zone_device *tz) -+{ -+} -+ - #endif - - #if IS_ENABLED(CONFIG_THERMAL) diff --git a/target/linux/ipq806x/patches-4.4/017-09-thermal-core-export-apis-to-get-slope-and-offset.patch b/target/linux/ipq806x/patches-4.4/017-09-thermal-core-export-apis-to-get-slope-and-offset.patch deleted file mode 100644 index 3fbe5f521a..0000000000 --- a/target/linux/ipq806x/patches-4.4/017-09-thermal-core-export-apis-to-get-slope-and-offset.patch +++ /dev/null @@ -1,101 +0,0 @@ -From 4a7069a32c99a81950de035535b0a064dcceaeba Mon Sep 17 00:00:00 2001 -From: Rajendra Nayak -Date: Thu, 5 May 2016 14:21:42 +0530 -Subject: [PATCH] thermal: core: export apis to get slope and offset - -Add apis for platform thermal drivers to query for slope and offset -attributes, which might be needed for temperature calculations. - -Signed-off-by: Rajendra Nayak -Signed-off-by: Eduardo Valentin -Signed-off-by: Zhang Rui ---- - Documentation/thermal/sysfs-api.txt | 12 ++++++++++++ - drivers/thermal/thermal_core.c | 30 ++++++++++++++++++++++++++++++ - include/linux/thermal.h | 8 ++++++++ - 3 files changed, 50 insertions(+) - ---- a/Documentation/thermal/sysfs-api.txt -+++ b/Documentation/thermal/sysfs-api.txt -@@ -72,6 +72,18 @@ temperature) and throttle appropriate de - It deletes the corresponding entry form /sys/class/thermal folder and - unbind all the thermal cooling devices it uses. - -+1.1.7 int thermal_zone_get_slope(struct thermal_zone_device *tz) -+ -+ This interface is used to read the slope attribute value -+ for the thermal zone device, which might be useful for platform -+ drivers for temperature calculations. -+ -+1.1.8 int thermal_zone_get_offset(struct thermal_zone_device *tz) -+ -+ This interface is used to read the offset attribute value -+ for the thermal zone device, which might be useful for platform -+ drivers for temperature calculations. -+ - 1.2 thermal cooling device interface - 1.2.1 struct thermal_cooling_device *thermal_cooling_device_register(char *name, - void *devdata, struct thermal_cooling_device_ops *) ---- a/drivers/thermal/thermal_core.c -+++ b/drivers/thermal/thermal_core.c -@@ -2061,6 +2061,36 @@ exit: - } - EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name); - -+/** -+ * thermal_zone_get_slope - return the slope attribute of the thermal zone -+ * @tz: thermal zone device with the slope attribute -+ * -+ * Return: If the thermal zone device has a slope attribute, return it, else -+ * return 1. -+ */ -+int thermal_zone_get_slope(struct thermal_zone_device *tz) -+{ -+ if (tz && tz->tzp) -+ return tz->tzp->slope; -+ return 1; -+} -+EXPORT_SYMBOL_GPL(thermal_zone_get_slope); -+ -+/** -+ * thermal_zone_get_offset - return the offset attribute of the thermal zone -+ * @tz: thermal zone device with the offset attribute -+ * -+ * Return: If the thermal zone device has a offset attribute, return it, else -+ * return 0. -+ */ -+int thermal_zone_get_offset(struct thermal_zone_device *tz) -+{ -+ if (tz && tz->tzp) -+ return tz->tzp->offset; -+ return 0; -+} -+EXPORT_SYMBOL_GPL(thermal_zone_get_offset); -+ - #ifdef CONFIG_NET - static const struct genl_multicast_group thermal_event_mcgrps[] = { - { .name = THERMAL_GENL_MCAST_GROUP_NAME, }, ---- a/include/linux/thermal.h -+++ b/include/linux/thermal.h -@@ -432,6 +432,8 @@ thermal_of_cooling_device_register(struc - void thermal_cooling_device_unregister(struct thermal_cooling_device *); - struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name); - int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp); -+int thermal_zone_get_slope(struct thermal_zone_device *tz); -+int thermal_zone_get_offset(struct thermal_zone_device *tz); - - int get_tz_trend(struct thermal_zone_device *, int); - struct thermal_instance *get_thermal_instance(struct thermal_zone_device *, -@@ -489,6 +491,12 @@ static inline struct thermal_zone_device - static inline int thermal_zone_get_temp( - struct thermal_zone_device *tz, int *temp) - { return -ENODEV; } -+static inline int thermal_zone_get_slope( -+ struct thermal_zone_device *tz) -+{ return -ENODEV; } -+static inline int thermal_zone_get_offset( -+ struct thermal_zone_device *tz) -+{ return -ENODEV; } - static inline int get_tz_trend(struct thermal_zone_device *tz, int trip) - { return -ENODEV; } - static inline struct thermal_instance * diff --git a/target/linux/ipq806x/patches-4.4/019-1-nvmem-core-return-error-for-non-word-aligned-access.patch b/target/linux/ipq806x/patches-4.4/019-1-nvmem-core-return-error-for-non-word-aligned-access.patch deleted file mode 100644 index 13415f51d0..0000000000 --- a/target/linux/ipq806x/patches-4.4/019-1-nvmem-core-return-error-for-non-word-aligned-access.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 313a72ff983cc2e00ac4dcb791d40ebf2f9d5718 Mon Sep 17 00:00:00 2001 -From: Srinivas Kandagatla -Date: Tue, 17 Nov 2015 09:12:41 +0000 -Subject: nvmem: core: return error for non word aligned access - -nvmem providers have restrictions on register strides, so return error -when users attempt to read/write buffers with sizes which are less -than word size. - -Without this patch the userspace would continue to try as it does not -get any error from the nvmem core, resulting in a hang or endless loop -in userspace. - -Reported-by: Ariel D'Alessandro -Signed-off-by: Srinivas Kandagatla -Signed-off-by: Greg Kroah-Hartman ---- - drivers/nvmem/core.c | 6 ++++++ - 1 file changed, 6 insertions(+) - ---- a/drivers/nvmem/core.c -+++ b/drivers/nvmem/core.c -@@ -70,6 +70,9 @@ static ssize_t bin_attr_nvmem_read(struc - if (pos >= nvmem->size) - return 0; - -+ if (count < nvmem->word_size) -+ return -EINVAL; -+ - if (pos + count > nvmem->size) - count = nvmem->size - pos; - -@@ -95,6 +98,9 @@ static ssize_t bin_attr_nvmem_write(stru - if (pos >= nvmem->size) - return 0; - -+ if (count < nvmem->word_size) -+ return -EINVAL; -+ - if (pos + count > nvmem->size) - count = nvmem->size - pos; - diff --git a/target/linux/ipq806x/patches-4.4/019-2-nvmem-core-fix-error-path-in-nvmem_add_cells.patch b/target/linux/ipq806x/patches-4.4/019-2-nvmem-core-fix-error-path-in-nvmem_add_cells.patch deleted file mode 100644 index 1f9473bafd..0000000000 --- a/target/linux/ipq806x/patches-4.4/019-2-nvmem-core-fix-error-path-in-nvmem_add_cells.patch +++ /dev/null @@ -1,34 +0,0 @@ -From dfdf141429f0895b63c882facc42c86f225033cb Mon Sep 17 00:00:00 2001 -From: Rasmus Villemoes -Date: Mon, 8 Feb 2016 22:04:29 +0100 -Subject: nvmem: core: fix error path in nvmem_add_cells() - -The current code fails to nvmem_cell_drop(cells[0]) - even worse, if -the loop above fails already at i==0, we'll enter an essentially -infinite loop doing nvmem_cell_drop on cells[-1], cells[-2], ... which -is unlikely to end well. - -Also, we're not freeing the temporary backing array cells on the error -path. - -Signed-off-by: Rasmus Villemoes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/nvmem/core.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - ---- a/drivers/nvmem/core.c -+++ b/drivers/nvmem/core.c -@@ -294,9 +294,11 @@ static int nvmem_add_cells(struct nvmem_ - - return 0; - err: -- while (--i) -+ while (i--) - nvmem_cell_drop(cells[i]); - -+ kfree(cells); -+ - return rval; - } - diff --git a/target/linux/ipq806x/patches-4.4/019-3-nvmem-Add-flag-to-export-NVMEM-to-root-only.patch b/target/linux/ipq806x/patches-4.4/019-3-nvmem-Add-flag-to-export-NVMEM-to-root-only.patch deleted file mode 100644 index 77136eab72..0000000000 --- a/target/linux/ipq806x/patches-4.4/019-3-nvmem-Add-flag-to-export-NVMEM-to-root-only.patch +++ /dev/null @@ -1,101 +0,0 @@ -From 811b0d6538b9f26f3eb0f90fe4e6118f2480ec6f Mon Sep 17 00:00:00 2001 -From: Andrew Lunn -Date: Fri, 26 Feb 2016 20:59:18 +0100 -Subject: nvmem: Add flag to export NVMEM to root only - -Legacy AT24, AT25 EEPROMs are exported in sys so that only root can -read the contents. The EEPROMs may contain sensitive information. Add -a flag so the provide can indicate that NVMEM should also restrict -access to root only. - -Signed-off-by: Andrew Lunn -Acked-by: Srinivas Kandagatla -Signed-off-by: Greg Kroah-Hartman ---- - drivers/nvmem/core.c | 57 ++++++++++++++++++++++++++++++++++++++++-- - include/linux/nvmem-provider.h | 1 + - 2 files changed, 56 insertions(+), 2 deletions(-) - ---- a/drivers/nvmem/core.c -+++ b/drivers/nvmem/core.c -@@ -161,6 +161,53 @@ static const struct attribute_group *nvm - NULL, - }; - -+/* default read/write permissions, root only */ -+static struct bin_attribute bin_attr_rw_root_nvmem = { -+ .attr = { -+ .name = "nvmem", -+ .mode = S_IWUSR | S_IRUSR, -+ }, -+ .read = bin_attr_nvmem_read, -+ .write = bin_attr_nvmem_write, -+}; -+ -+static struct bin_attribute *nvmem_bin_rw_root_attributes[] = { -+ &bin_attr_rw_root_nvmem, -+ NULL, -+}; -+ -+static const struct attribute_group nvmem_bin_rw_root_group = { -+ .bin_attrs = nvmem_bin_rw_root_attributes, -+}; -+ -+static const struct attribute_group *nvmem_rw_root_dev_groups[] = { -+ &nvmem_bin_rw_root_group, -+ NULL, -+}; -+ -+/* read only permission, root only */ -+static struct bin_attribute bin_attr_ro_root_nvmem = { -+ .attr = { -+ .name = "nvmem", -+ .mode = S_IRUSR, -+ }, -+ .read = bin_attr_nvmem_read, -+}; -+ -+static struct bin_attribute *nvmem_bin_ro_root_attributes[] = { -+ &bin_attr_ro_root_nvmem, -+ NULL, -+}; -+ -+static const struct attribute_group nvmem_bin_ro_root_group = { -+ .bin_attrs = nvmem_bin_ro_root_attributes, -+}; -+ -+static const struct attribute_group *nvmem_ro_root_dev_groups[] = { -+ &nvmem_bin_ro_root_group, -+ NULL, -+}; -+ - static void nvmem_release(struct device *dev) - { - struct nvmem_device *nvmem = to_nvmem_device(dev); -@@ -355,8 +402,14 @@ struct nvmem_device *nvmem_register(cons - nvmem->read_only = of_property_read_bool(np, "read-only") | - config->read_only; - -- nvmem->dev.groups = nvmem->read_only ? nvmem_ro_dev_groups : -- nvmem_rw_dev_groups; -+ if (config->root_only) -+ nvmem->dev.groups = nvmem->read_only ? -+ nvmem_ro_root_dev_groups : -+ nvmem_rw_root_dev_groups; -+ else -+ nvmem->dev.groups = nvmem->read_only ? -+ nvmem_ro_dev_groups : -+ nvmem_rw_dev_groups; - - device_initialize(&nvmem->dev); - ---- a/include/linux/nvmem-provider.h -+++ b/include/linux/nvmem-provider.h -@@ -23,6 +23,7 @@ struct nvmem_config { - const struct nvmem_cell_info *cells; - int ncells; - bool read_only; -+ bool root_only; - }; - - #if IS_ENABLED(CONFIG_NVMEM) diff --git a/target/linux/ipq806x/patches-4.4/019-4-nvmem-Add-backwards-compatibility-support-for-older-EEPROM-drivers.patch b/target/linux/ipq806x/patches-4.4/019-4-nvmem-Add-backwards-compatibility-support-for-older-EEPROM-drivers.patch deleted file mode 100644 index 6344d0ed03..0000000000 --- a/target/linux/ipq806x/patches-4.4/019-4-nvmem-Add-backwards-compatibility-support-for-older-EEPROM-drivers.patch +++ /dev/null @@ -1,181 +0,0 @@ -From b6c217ab9be6895384cf0b284ace84ad79e5c53b Mon Sep 17 00:00:00 2001 -From: Andrew Lunn -Date: Fri, 26 Feb 2016 20:59:19 +0100 -Subject: nvmem: Add backwards compatibility support for older EEPROM drivers. - -Older drivers made an 'eeprom' file available in the /sys device -directory. Have the NVMEM core provide this to retain backwards -compatibility. - -Signed-off-by: Andrew Lunn -Acked-by: Srinivas Kandagatla -Signed-off-by: Greg Kroah-Hartman ---- - drivers/nvmem/core.c | 84 ++++++++++++++++++++++++++++++++++++++---- - include/linux/nvmem-provider.h | 4 +- - 2 files changed, 79 insertions(+), 9 deletions(-) - ---- a/drivers/nvmem/core.c -+++ b/drivers/nvmem/core.c -@@ -38,8 +38,13 @@ struct nvmem_device { - int users; - size_t size; - bool read_only; -+ int flags; -+ struct bin_attribute eeprom; -+ struct device *base_dev; - }; - -+#define FLAG_COMPAT BIT(0) -+ - struct nvmem_cell { - const char *name; - int offset; -@@ -56,16 +61,26 @@ static DEFINE_IDA(nvmem_ida); - static LIST_HEAD(nvmem_cells); - static DEFINE_MUTEX(nvmem_cells_mutex); - -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+static struct lock_class_key eeprom_lock_key; -+#endif -+ - #define to_nvmem_device(d) container_of(d, struct nvmem_device, dev) - - static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t pos, size_t count) - { -- struct device *dev = container_of(kobj, struct device, kobj); -- struct nvmem_device *nvmem = to_nvmem_device(dev); -+ struct device *dev; -+ struct nvmem_device *nvmem; - int rc; - -+ if (attr->private) -+ dev = attr->private; -+ else -+ dev = container_of(kobj, struct device, kobj); -+ nvmem = to_nvmem_device(dev); -+ - /* Stop the user from reading */ - if (pos >= nvmem->size) - return 0; -@@ -90,10 +105,16 @@ static ssize_t bin_attr_nvmem_write(stru - struct bin_attribute *attr, - char *buf, loff_t pos, size_t count) - { -- struct device *dev = container_of(kobj, struct device, kobj); -- struct nvmem_device *nvmem = to_nvmem_device(dev); -+ struct device *dev; -+ struct nvmem_device *nvmem; - int rc; - -+ if (attr->private) -+ dev = attr->private; -+ else -+ dev = container_of(kobj, struct device, kobj); -+ nvmem = to_nvmem_device(dev); -+ - /* Stop the user from writing */ - if (pos >= nvmem->size) - return 0; -@@ -349,6 +370,43 @@ err: - return rval; - } - -+/* -+ * nvmem_setup_compat() - Create an additional binary entry in -+ * drivers sys directory, to be backwards compatible with the older -+ * drivers/misc/eeprom drivers. -+ */ -+static int nvmem_setup_compat(struct nvmem_device *nvmem, -+ const struct nvmem_config *config) -+{ -+ int rval; -+ -+ if (!config->base_dev) -+ return -EINVAL; -+ -+ if (nvmem->read_only) -+ nvmem->eeprom = bin_attr_ro_root_nvmem; -+ else -+ nvmem->eeprom = bin_attr_rw_root_nvmem; -+ nvmem->eeprom.attr.name = "eeprom"; -+ nvmem->eeprom.size = nvmem->size; -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+ nvmem->eeprom.attr.key = &eeprom_lock_key; -+#endif -+ nvmem->eeprom.private = &nvmem->dev; -+ nvmem->base_dev = config->base_dev; -+ -+ rval = device_create_bin_file(nvmem->base_dev, &nvmem->eeprom); -+ if (rval) { -+ dev_err(&nvmem->dev, -+ "Failed to create eeprom binary file %d\n", rval); -+ return rval; -+ } -+ -+ nvmem->flags |= FLAG_COMPAT; -+ -+ return 0; -+} -+ - /** - * nvmem_register() - Register a nvmem device for given nvmem_config. - * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem -@@ -416,16 +474,23 @@ struct nvmem_device *nvmem_register(cons - dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name); - - rval = device_add(&nvmem->dev); -- if (rval) { -- ida_simple_remove(&nvmem_ida, nvmem->id); -- kfree(nvmem); -- return ERR_PTR(rval); -+ if (rval) -+ goto out; -+ -+ if (config->compat) { -+ rval = nvmem_setup_compat(nvmem, config); -+ if (rval) -+ goto out; - } - - if (config->cells) - nvmem_add_cells(nvmem, config); - - return nvmem; -+out: -+ ida_simple_remove(&nvmem_ida, nvmem->id); -+ kfree(nvmem); -+ return ERR_PTR(rval); - } - EXPORT_SYMBOL_GPL(nvmem_register); - -@@ -445,6 +510,9 @@ int nvmem_unregister(struct nvmem_device - } - mutex_unlock(&nvmem_mutex); - -+ if (nvmem->flags & FLAG_COMPAT) -+ device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); -+ - nvmem_device_remove_all_cells(nvmem); - device_del(&nvmem->dev); - ---- a/include/linux/nvmem-provider.h -+++ b/include/linux/nvmem-provider.h -@@ -24,6 +24,9 @@ struct nvmem_config { - int ncells; - bool read_only; - bool root_only; -+ /* To be only used by old driver/misc/eeprom drivers */ -+ bool compat; -+ struct device *base_dev; - }; - - #if IS_ENABLED(CONFIG_NVMEM) -@@ -44,5 +47,4 @@ static inline int nvmem_unregister(struc - } - - #endif /* CONFIG_NVMEM */ -- - #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */ diff --git a/target/linux/ipq806x/patches-4.4/020-add-ap148-bootargs.patch b/target/linux/ipq806x/patches-4.4/020-add-ap148-bootargs.patch deleted file mode 100644 index 9c33bad5c1..0000000000 --- a/target/linux/ipq806x/patches-4.4/020-add-ap148-bootargs.patch +++ /dev/null @@ -1,61 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -4,14 +4,6 @@ - model = "Qualcomm IPQ8064/AP148"; - compatible = "qcom,ipq8064-ap148", "qcom,ipq8064"; - -- aliases { -- serial0 = &gsbi4_serial; -- }; -- -- chosen { -- stdout-path = "serial0:115200n8"; -- }; -- - reserved-memory { - #address-cells = <1>; - #size-cells = <1>; -@@ -22,6 +14,14 @@ - }; - }; - -+ alias { -+ serial0 = &uart4; -+ }; -+ -+ chosen { -+ linux,stdout-path = "serial0:115200n8"; -+ }; -+ - soc { - pinmux@800000 { - i2c4_pins: i2c4_pinmux { ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -159,7 +159,7 @@ - - syscon-tcsr = <&tcsr>; - -- serial@12490000 { -+ uart2: serial@12490000 { - compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; - reg = <0x12490000 0x1000>, - <0x12480000 0x1000>; -@@ -197,7 +197,7 @@ - - syscon-tcsr = <&tcsr>; - -- gsbi4_serial: serial@16340000 { -+ uart4: serial@16340000 { - compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; - reg = <0x16340000 0x1000>, - <0x16300000 0x1000>; -@@ -234,7 +234,7 @@ - - syscon-tcsr = <&tcsr>; - -- serial@1a240000 { -+ uart5: serial@1a240000 { - compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; - reg = <0x1a240000 0x1000>, - <0x1a200000 0x1000>; diff --git a/target/linux/ipq806x/patches-4.4/021-add-ap148-partitions.patch b/target/linux/ipq806x/patches-4.4/021-add-ap148-partitions.patch deleted file mode 100644 index bfdb30fe14..0000000000 --- a/target/linux/ipq806x/patches-4.4/021-add-ap148-partitions.patch +++ /dev/null @@ -1,19 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -77,15 +77,7 @@ - spi-max-frequency = <50000000>; - reg = <0>; - -- partition@0 { -- label = "rootfs"; -- reg = <0x0 0x1000000>; -- }; -- -- partition@1 { -- label = "scratch"; -- reg = <0x1000000 0x1000000>; -- }; -+ linux,part-probe = "qcom-smem"; - }; - }; - }; diff --git a/target/linux/ipq806x/patches-4.4/022-add-db149-dts.patch b/target/linux/ipq806x/patches-4.4/022-add-db149-dts.patch deleted file mode 100644 index 0845d3f213..0000000000 --- a/target/linux/ipq806x/patches-4.4/022-add-db149-dts.patch +++ /dev/null @@ -1,160 +0,0 @@ -From f26cc3733bdd697bd81ae505fc133fa7c9b6ea19 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Tue, 7 Apr 2015 19:58:58 -0700 -Subject: [PATCH] ARM: dts: qcom: add initial DB149 device-tree - -Add basic DB149 (IPQ806x based platform) device-tree. It supports UART, -SATA, USB2, USB3 and NOR flash. - -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/Makefile | 1 + - arch/arm/boot/dts/qcom-ipq8064-db149.dts | 132 +++++++++++++++++++++++++++++++ - 2 files changed, 133 insertions(+) - create mode 100644 arch/arm/boot/dts/qcom-ipq8064-db149.dts - ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -506,6 +506,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \ - qcom-apq8084-ifc6540.dtb \ - qcom-apq8084-mtp.dtb \ - qcom-ipq8064-ap148.dtb \ -+ qcom-ipq8064-db149.dtb \ - qcom-msm8660-surf.dtb \ - qcom-msm8960-cdp.dtb \ - qcom-msm8974-sony-xperia-honami.dtb ---- /dev/null -+++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts -@@ -0,0 +1,132 @@ -+#include "qcom-ipq8064-v1.0.dtsi" -+ -+/ { -+ model = "Qualcomm IPQ8064/DB149"; -+ compatible = "qcom,ipq8064-db149", "qcom,ipq8064"; -+ -+ reserved-memory { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ ranges; -+ rsvd@41200000 { -+ reg = <0x41200000 0x300000>; -+ no-map; -+ }; -+ }; -+ -+ alias { -+ serial0 = &uart2; -+ }; -+ -+ chosen { -+ linux,stdout-path = "serial0:115200n8"; -+ }; -+ -+ soc { -+ pinmux@800000 { -+ i2c4_pins: i2c4_pinmux { -+ pins = "gpio12", "gpio13"; -+ function = "gsbi4"; -+ bias-disable; -+ }; -+ -+ spi_pins: spi_pins { -+ mux { -+ pins = "gpio18", "gpio19", "gpio21"; -+ function = "gsbi5"; -+ drive-strength = <10>; -+ bias-none; -+ }; -+ }; -+ }; -+ -+ gsbi2: gsbi@12480000 { -+ qcom,mode = ; -+ status = "ok"; -+ uart2: serial@12490000 { -+ status = "ok"; -+ }; -+ }; -+ -+ gsbi5: gsbi@1a200000 { -+ qcom,mode = ; -+ status = "ok"; -+ -+ spi4: spi@1a280000 { -+ status = "ok"; -+ spi-max-frequency = <50000000>; -+ -+ pinctrl-0 = <&spi_pins>; -+ pinctrl-names = "default"; -+ -+ cs-gpios = <&qcom_pinmux 20 0>; -+ -+ flash: m25p80@0 { -+ compatible = "s25fl256s1"; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ spi-max-frequency = <50000000>; -+ reg = <0>; -+ m25p,fast-read; -+ -+ partition@0 { -+ label = "lowlevel_init"; -+ reg = <0x0 0x1b0000>; -+ }; -+ -+ partition@1 { -+ label = "u-boot"; -+ reg = <0x1b0000 0x80000>; -+ }; -+ -+ partition@2 { -+ label = "u-boot-env"; -+ reg = <0x230000 0x40000>; -+ }; -+ -+ partition@3 { -+ label = "caldata"; -+ reg = <0x270000 0x40000>; -+ }; -+ -+ partition@4 { -+ label = "firmware"; -+ reg = <0x2b0000 0x1d50000>; -+ }; -+ }; -+ }; -+ }; -+ -+ sata-phy@1b400000 { -+ status = "ok"; -+ }; -+ -+ sata@29000000 { -+ status = "ok"; -+ }; -+ -+ phy@100f8800 { /* USB3 port 1 HS phy */ -+ status = "ok"; -+ }; -+ -+ phy@100f8830 { /* USB3 port 1 SS phy */ -+ status = "ok"; -+ }; -+ -+ phy@110f8800 { /* USB3 port 0 HS phy */ -+ status = "ok"; -+ }; -+ -+ phy@110f8830 { /* USB3 port 0 SS phy */ -+ status = "ok"; -+ }; -+ -+ usb30@0 { -+ status = "ok"; -+ }; -+ -+ usb30@1 { -+ status = "ok"; -+ }; -+ }; -+}; diff --git a/target/linux/ipq806x/patches-4.4/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch b/target/linux/ipq806x/patches-4.4/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch deleted file mode 100644 index 705306d9f0..0000000000 --- a/target/linux/ipq806x/patches-4.4/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch +++ /dev/null @@ -1,52 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -47,14 +47,12 @@ - status = "ok"; - }; - -- i2c4: i2c@16380000 { -- status = "ok"; -- -- clock-frequency = <200000>; -- -- pinctrl-0 = <&i2c4_pins>; -- pinctrl-names = "default"; -- }; -+ /* -+ * The i2c device on gsbi4 should not be enabled. -+ * On ipq806x designs gsbi4 i2c is meant for exclusive -+ * RPM usage. Turning this on in kernel manifests as -+ * i2c failure for the RPM. -+ */ - }; - - gsbi5: gsbi@1a200000 { ---- a/drivers/clk/qcom/gcc-ipq806x.c -+++ b/drivers/clk/qcom/gcc-ipq806x.c -@@ -294,7 +294,7 @@ static struct clk_rcg gsbi1_uart_src = { - .parent_names = gcc_pxo_pll8, - .num_parents = 2, - .ops = &clk_rcg_ops, -- .flags = CLK_SET_PARENT_GATE, -+ .flags = CLK_SET_PARENT_GATE | CLK_IGNORE_UNUSED, - }, - }, - }; -@@ -312,7 +312,7 @@ static struct clk_branch gsbi1_uart_clk - }, - .num_parents = 1, - .ops = &clk_branch_ops, -- .flags = CLK_SET_RATE_PARENT, -+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, - }, - }, - }; -@@ -890,7 +890,7 @@ static struct clk_branch gsbi1_h_clk = { - .hw.init = &(struct clk_init_data){ - .name = "gsbi1_h_clk", - .ops = &clk_branch_ops, -- .flags = CLK_IS_ROOT, -+ .flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED, - }, - }, - }; diff --git a/target/linux/ipq806x/patches-4.4/024-ap148-add-memory-node.patch b/target/linux/ipq806x/patches-4.4/024-ap148-add-memory-node.patch deleted file mode 100644 index f026ed9394..0000000000 --- a/target/linux/ipq806x/patches-4.4/024-ap148-add-memory-node.patch +++ /dev/null @@ -1,14 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -4,6 +4,11 @@ - model = "Qualcomm IPQ8064/AP148"; - compatible = "qcom,ipq8064-ap148", "qcom,ipq8064"; - -+ memory@0 { -+ reg = <0x42000000 0x1e000000>; -+ device_type = "memory"; -+ }; -+ - reserved-memory { - #address-cells = <1>; - #size-cells = <1>; diff --git a/target/linux/ipq806x/patches-4.4/030-ARM-qcom-select-ARM_CPU_SUSPEND-for-power-management.patch b/target/linux/ipq806x/patches-4.4/030-ARM-qcom-select-ARM_CPU_SUSPEND-for-power-management.patch deleted file mode 100644 index cb2d303973..0000000000 --- a/target/linux/ipq806x/patches-4.4/030-ARM-qcom-select-ARM_CPU_SUSPEND-for-power-management.patch +++ /dev/null @@ -1,30 +0,0 @@ -From: Arnd Bergmann -Date: Tue, 24 Nov 2015 23:13:09 +0100 -Subject: [PATCH] ARM: qcom: select ARM_CPU_SUSPEND for power management - -The qcom spm driver uses cpu_resume_arm(), which is not included -in the kernel in all configurations: - -drivers/built-in.o: In function `qcom_cpu_spc': -:(.text+0xbc022): undefined reference to `cpu_suspend' -drivers/built-in.o: In function `qcom_cpuidle_init': -:(.init.text+0x610c): undefined reference to `cpu_resume_arm' - -This adds a 'select' Kconfig statement to ensure it's always -enabled. - -Signed-off-by: Arnd Bergmann -Reviewed-by: Stephen Boyd -Signed-off-by: Andy Gross ---- - ---- a/drivers/soc/qcom/Kconfig -+++ b/drivers/soc/qcom/Kconfig -@@ -13,6 +13,7 @@ config QCOM_GSBI - config QCOM_PM - bool "Qualcomm Power Management" - depends on ARCH_QCOM && !ARM64 -+ select ARM_CPU_SUSPEND - select QCOM_SCM - help - QCOM Platform specific power driver to manage cores and L2 low power diff --git a/target/linux/ipq806x/patches-4.4/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-4.4/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch deleted file mode 100644 index e1d317de47..0000000000 --- a/target/linux/ipq806x/patches-4.4/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch +++ /dev/null @@ -1,33 +0,0 @@ -From c7c482da19a5e4ba7101198c21c2434056b0b2da Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Thu, 13 Aug 2015 09:45:26 -0700 -Subject: [PATCH 1/3] ARM: qcom: add SFPB nodes to IPQ806x dts - -Add one new node to the ipq806x.dtsi file to declare & register the -hardware spinlock devices. This mechanism is required to be used by -other drivers such as SMEM. - -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/qcom-ipq8064.dtsi | 11 +++++++++++ - 1 file changed, 11 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -329,5 +329,16 @@ - #reset-cells = <1>; - }; - -+ sfpb_mutex_block: syscon@1200600 { -+ compatible = "syscon"; -+ reg = <0x01200600 0x100>; -+ }; -+ }; -+ -+ sfpb_mutex: sfpb-mutex { -+ compatible = "qcom,sfpb-mutex"; -+ syscon = <&sfpb_mutex_block 4 4>; -+ -+ #hwlock-cells = <1>; - }; - }; diff --git a/target/linux/ipq806x/patches-4.4/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-4.4/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch deleted file mode 100644 index 06b14052ef..0000000000 --- a/target/linux/ipq806x/patches-4.4/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch +++ /dev/null @@ -1,36 +0,0 @@ -From f212be3a6134db8dd7c5f6f0987536a669401fae Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Fri, 14 Aug 2015 11:17:20 -0700 -Subject: [PATCH 2/3] ARM: qcom: add SMEM device node to IPQ806x dts - -SMEM is used on IPQ806x to store various board related information such -as boot device and flash partition layout. We'll declare it as a device -so we can make use of it thanks to the new SMEM soc driver. - -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/qcom-ipq8064.dtsi | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -55,7 +55,7 @@ - no-map; - }; - -- smem@41000000 { -+ smem: smem@41000000 { - reg = <0x41000000 0x200000>; - no-map; - }; -@@ -341,4 +341,10 @@ - - #hwlock-cells = <1>; - }; -+ -+ smem { -+ compatible = "qcom,smem"; -+ memory-region = <&smem>; -+ hwlocks = <&sfpb_mutex 3>; -+ }; - }; diff --git a/target/linux/ipq806x/patches-4.4/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch b/target/linux/ipq806x/patches-4.4/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch deleted file mode 100644 index 7c0c803565..0000000000 --- a/target/linux/ipq806x/patches-4.4/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch +++ /dev/null @@ -1,275 +0,0 @@ -From 61e8e1b1af77f24339da3f0822a76fa65ed635c6 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Thu, 13 Aug 2015 09:53:14 -0700 -Subject: [PATCH] mtd: add SMEM parser for QCOM platforms - -On QCOM platforms using MTD devices storage (such as IPQ806x), SMEM is -used to store partition layout. This new parser can now be used to read -SMEM and use it to register an MTD layout according to its content. - -Signed-off-by: Mathieu Olivari -Signed-off-by: Ram Chandra Jangir ---- - drivers/mtd/Kconfig | 7 ++ - drivers/mtd/Makefile | 1 + - drivers/mtd/qcom_smem_part.c | 228 +++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 236 insertions(+) - create mode 100644 drivers/mtd/qcom_smem_part.c - ---- a/drivers/mtd/Kconfig -+++ b/drivers/mtd/Kconfig -@@ -190,6 +190,13 @@ config MTD_MYLOADER_PARTS - You will still need the parsing functions to be called by the driver - for your particular device. It won't happen automatically. - -+config MTD_QCOM_SMEM_PARTS -+ tristate "QCOM SMEM partitioning support" -+ depends on QCOM_SMEM -+ help -+ This provides partitions parser for QCOM devices using SMEM -+ such as IPQ806x. -+ - comment "User Modules And Translation Layers" - - # ---- a/drivers/mtd/Makefile -+++ b/drivers/mtd/Makefile -@@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o - obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o - obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o - obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o -+obj-$(CONFIG_MTD_QCOM_SMEM_PARTS) += qcom_smem_part.o - - # 'Users' - code which presents functionality to userspace. - obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o ---- /dev/null -+++ b/drivers/mtd/qcom_smem_part.c -@@ -0,0 +1,228 @@ -+/* -+ * Copyright (c) 2015, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include -+ -+/* Processor/host identifier for the application processor */ -+#define SMEM_HOST_APPS 0 -+ -+/* SMEM items index */ -+#define SMEM_AARM_PARTITION_TABLE 9 -+#define SMEM_BOOT_FLASH_TYPE 421 -+#define SMEM_BOOT_FLASH_BLOCK_SIZE 424 -+ -+/* SMEM Flash types */ -+#define SMEM_FLASH_NAND 2 -+#define SMEM_FLASH_SPI 6 -+ -+#define SMEM_PART_NAME_SZ 16 -+#define SMEM_PARTS_MAX 32 -+ -+struct smem_partition { -+ char name[SMEM_PART_NAME_SZ]; -+ __le32 start; -+ __le32 size; -+ __le32 attr; -+}; -+ -+struct smem_partition_table { -+ u8 magic[8]; -+ __le32 version; -+ __le32 len; -+ struct smem_partition parts[SMEM_PARTS_MAX]; -+}; -+ -+/* SMEM Magic values in partition table */ -+static const u8 SMEM_PTABLE_MAGIC[] = { -+ 0xaa, 0x73, 0xee, 0x55, -+ 0xdb, 0xbd, 0x5e, 0xe3, -+}; -+ -+static int qcom_smem_get_flash_blksz(u64 **smem_blksz) -+{ -+ size_t size; -+ -+ *smem_blksz = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_BLOCK_SIZE, -+ &size); -+ -+ if (IS_ERR(*smem_blksz)) { -+ pr_err("Unable to read flash blksz from SMEM\n"); -+ return -ENOENT; -+ } -+ -+ if (size != sizeof(**smem_blksz)) { -+ pr_err("Invalid flash blksz size in SMEM\n"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int qcom_smem_get_flash_type(u64 **smem_flash_type) -+{ -+ size_t size; -+ -+ *smem_flash_type = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_TYPE, -+ &size); -+ -+ if (IS_ERR(*smem_flash_type)) { -+ pr_err("Unable to read flash type from SMEM\n"); -+ return -ENOENT; -+ } -+ -+ if (size != sizeof(**smem_flash_type)) { -+ pr_err("Invalid flash type size in SMEM\n"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int qcom_smem_get_flash_partitions(struct smem_partition_table **pparts) -+{ -+ size_t size; -+ -+ *pparts = qcom_smem_get(SMEM_HOST_APPS, SMEM_AARM_PARTITION_TABLE, -+ &size); -+ -+ if (IS_ERR(*pparts)) { -+ pr_err("Unable to read partition table from SMEM\n"); -+ return -ENOENT; -+ } -+ -+ return 0; -+} -+ -+static int of_dev_node_match(struct device *dev, void *data) -+{ -+ return dev->of_node == data; -+} -+ -+static bool is_spi_device(struct device_node *np) -+{ -+ struct device *dev; -+ -+ dev = bus_find_device(&spi_bus_type, NULL, np, of_dev_node_match); -+ if (!dev) -+ return false; -+ -+ put_device(dev); -+ return true; -+} -+ -+static int parse_qcom_smem_partitions(struct mtd_info *master, -+ struct mtd_partition **pparts, -+ struct mtd_part_parser_data *data) -+{ -+ struct smem_partition_table *smem_parts; -+ u64 *smem_flash_type, *smem_blksz; -+ struct mtd_partition *mtd_parts; -+ struct device_node *of_node = data->of_node; -+ int i, ret; -+ -+ /* -+ * SMEM will only store the partition table of the boot device. -+ * If this is not the boot device, do not return any partition. -+ */ -+ ret = qcom_smem_get_flash_type(&smem_flash_type); -+ if (ret < 0) -+ return ret; -+ -+ if ((*smem_flash_type == SMEM_FLASH_NAND && !mtd_type_is_nand(master)) -+ || (*smem_flash_type == SMEM_FLASH_SPI && !is_spi_device(of_node))) -+ return 0; -+ -+ /* -+ * Just for sanity purpose, make sure the block size in SMEM matches the -+ * block size of the MTD device -+ */ -+ ret = qcom_smem_get_flash_blksz(&smem_blksz); -+ if (ret < 0) -+ return ret; -+ -+ if (*smem_blksz != master->erasesize) { -+ pr_err("SMEM block size differs from MTD block size\n"); -+ return -EINVAL; -+ } -+ -+ /* Get partition pointer from SMEM */ -+ ret = qcom_smem_get_flash_partitions(&smem_parts); -+ if (ret < 0) -+ return ret; -+ -+ if (memcmp(SMEM_PTABLE_MAGIC, smem_parts->magic, -+ sizeof(SMEM_PTABLE_MAGIC))) { -+ pr_err("SMEM partition magic invalid\n"); -+ return -EINVAL; -+ } -+ -+ /* Allocate and populate the mtd structures */ -+ mtd_parts = kcalloc(le32_to_cpu(smem_parts->len), sizeof(*mtd_parts), -+ GFP_KERNEL); -+ if (!mtd_parts) -+ return -ENOMEM; -+ -+ for (i = 0; i < smem_parts->len; i++) { -+ struct smem_partition *s_part = &smem_parts->parts[i]; -+ struct mtd_partition *m_part = &mtd_parts[i]; -+ -+ m_part->name = s_part->name; -+ m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz); -+ m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz); -+ -+ /* -+ * The last SMEM partition may have its size marked as -+ * something like 0xffffffff, which means "until the end of the -+ * flash device". In this case, truncate it. -+ */ -+ if (m_part->offset + m_part->size > master->size) -+ m_part->size = master->size - m_part->offset; -+ } -+ -+ *pparts = mtd_parts; -+ -+ return smem_parts->len; -+} -+ -+static struct mtd_part_parser qcom_smem_parser = { -+ .owner = THIS_MODULE, -+ .parse_fn = parse_qcom_smem_partitions, -+ .name = "qcom-smem", -+}; -+ -+static int __init qcom_smem_parser_init(void) -+{ -+ register_mtd_parser(&qcom_smem_parser); -+ return 0; -+} -+ -+static void __exit qcom_smem_parser_exit(void) -+{ -+ deregister_mtd_parser(&qcom_smem_parser); -+} -+ -+module_init(qcom_smem_parser_init); -+module_exit(qcom_smem_parser_exit); -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Mathieu Olivari "); -+MODULE_DESCRIPTION("Parsing code for SMEM based partition tables"); diff --git a/target/linux/ipq806x/patches-4.4/096-01-usb-dwc3-core-purge-dev_dbg-calls.patch b/target/linux/ipq806x/patches-4.4/096-01-usb-dwc3-core-purge-dev_dbg-calls.patch deleted file mode 100644 index 6ace50cbc0..0000000000 --- a/target/linux/ipq806x/patches-4.4/096-01-usb-dwc3-core-purge-dev_dbg-calls.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 1407bf13e3bf5f1168484c3e68b6ef9d8cf2bc72 Mon Sep 17 00:00:00 2001 -From: Felipe Balbi -Date: Mon, 16 Nov 2015 16:06:37 -0600 -Subject: usb: dwc3: core: purge dev_dbg() calls - -The last few dev_dbg() messages are converted to -tracepoints and we can finally ignore dev_dbg() -messages during debug sessions. - -Signed-off-by: Felipe Balbi ---- - drivers/usb/dwc3/core.c | 8 +++++--- - 1 file changed, 5 insertions(+), 3 deletions(-) - ---- a/drivers/usb/dwc3/core.c -+++ b/drivers/usb/dwc3/core.c -@@ -272,7 +272,8 @@ static int dwc3_event_buffers_setup(stru - - for (n = 0; n < dwc->num_event_buffers; n++) { - evt = dwc->ev_buffs[n]; -- dev_dbg(dwc->dev, "Event buf %p dma %08llx length %d\n", -+ dwc3_trace(trace_dwc3_core, -+ "Event buf %p dma %08llx length %d\n", - evt->buf, (unsigned long long) evt->dma, - evt->length); - -@@ -608,12 +609,13 @@ static int dwc3_core_init(struct dwc3 *d - reg |= DWC3_GCTL_GBLHIBERNATIONEN; - break; - default: -- dev_dbg(dwc->dev, "No power optimization available\n"); -+ dwc3_trace(trace_dwc3_core, "No power optimization available\n"); - } - - /* check if current dwc3 is on simulation board */ - if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) { -- dev_dbg(dwc->dev, "it is on FPGA board\n"); -+ dwc3_trace(trace_dwc3_core, -+ "running on FPGA platform\n"); - dwc->is_fpga = true; - } - diff --git a/target/linux/ipq806x/patches-4.4/096-02-usb-dwc3-Update-maximum_speed-for-SuperSpeedPlus.patch b/target/linux/ipq806x/patches-4.4/096-02-usb-dwc3-Update-maximum_speed-for-SuperSpeedPlus.patch deleted file mode 100644 index de0e47fc14..0000000000 --- a/target/linux/ipq806x/patches-4.4/096-02-usb-dwc3-Update-maximum_speed-for-SuperSpeedPlus.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 2c7f1bd9127a1a49ee25d9c2b2ce17b11c7fb05f Mon Sep 17 00:00:00 2001 -From: John Youn -Date: Fri, 5 Feb 2016 17:08:59 -0800 -Subject: usb: dwc3: Update maximum_speed for SuperSpeedPlus - -If the maximum_speed is not set, set it to a known value, either -SuperSpeed or SuperSpeedPlus based on the type of controller we are -using. If we are on DWC_usb31 controller, check the PHY interface to see -if it is capable of SuperSpeedPlus. - -Also this check is moved after dwc3_core_init() so that we can check -dwc->revision. - -Signed-off-by: John Youn -Signed-off-by: Felipe Balbi ---- - drivers/usb/dwc3/core.c | 17 +++++++++++++---- - 1 file changed, 13 insertions(+), 4 deletions(-) - ---- a/drivers/usb/dwc3/core.c -+++ b/drivers/usb/dwc3/core.c -@@ -962,10 +962,6 @@ static int dwc3_probe(struct platform_de - fladj = pdata->fladj_value; - } - -- /* default to superspeed if no maximum_speed passed */ -- if (dwc->maximum_speed == USB_SPEED_UNKNOWN) -- dwc->maximum_speed = USB_SPEED_SUPER; -- - dwc->lpm_nyet_threshold = lpm_nyet_threshold; - dwc->tx_de_emphasis = tx_de_emphasis; - -@@ -1016,6 +1012,19 @@ static int dwc3_probe(struct platform_de - goto err1; - } - -+ /* default to superspeed if no maximum_speed passed */ -+ if (dwc->maximum_speed == USB_SPEED_UNKNOWN) { -+ dwc->maximum_speed = USB_SPEED_SUPER; -+ -+ /* -+ * default to superspeed plus if we are capable. -+ */ -+ if (dwc3_is_usb31(dwc) && -+ (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == -+ DWC3_GHWPARAMS3_SSPHY_IFC_GEN2)) -+ dwc->maximum_speed = USB_SPEED_SUPER_PLUS; -+ } -+ - /* Adjust Frame Length */ - dwc3_frame_length_adjustment(dwc, fladj); - diff --git a/target/linux/ipq806x/patches-4.4/096-03-usb-dwc3-Validate-the-maximum_speed-parameter.patch b/target/linux/ipq806x/patches-4.4/096-03-usb-dwc3-Validate-the-maximum_speed-parameter.patch deleted file mode 100644 index 0f17918d36..0000000000 --- a/target/linux/ipq806x/patches-4.4/096-03-usb-dwc3-Validate-the-maximum_speed-parameter.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 77966eb85e6d988a6daaf8ac14ac33026ceb3ab7 Mon Sep 17 00:00:00 2001 -From: John Youn -Date: Fri, 19 Feb 2016 17:31:01 -0800 -Subject: usb: dwc3: Validate the maximum_speed parameter - -Check that dwc->maximum_speed is set to a valid value. Also add an error -when we use it later if we encounter an invalid value. - -Signed-off-by: John Youn -Signed-off-by: Felipe Balbi ---- - drivers/usb/dwc3/core.c | 18 ++++++++++++++++-- - drivers/usb/dwc3/gadget.c | 9 ++++++--- - 2 files changed, 22 insertions(+), 5 deletions(-) - ---- a/drivers/usb/dwc3/core.c -+++ b/drivers/usb/dwc3/core.c -@@ -1012,8 +1012,20 @@ static int dwc3_probe(struct platform_de - goto err1; - } - -- /* default to superspeed if no maximum_speed passed */ -- if (dwc->maximum_speed == USB_SPEED_UNKNOWN) { -+ /* Check the maximum_speed parameter */ -+ switch (dwc->maximum_speed) { -+ case USB_SPEED_LOW: -+ case USB_SPEED_FULL: -+ case USB_SPEED_HIGH: -+ case USB_SPEED_SUPER: -+ case USB_SPEED_SUPER_PLUS: -+ break; -+ default: -+ dev_err(dev, "invalid maximum_speed parameter %d\n", -+ dwc->maximum_speed); -+ /* fall through */ -+ case USB_SPEED_UNKNOWN: -+ /* default to superspeed */ - dwc->maximum_speed = USB_SPEED_SUPER; - - /* -@@ -1023,6 +1035,8 @@ static int dwc3_probe(struct platform_de - (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == - DWC3_GHWPARAMS3_SSPHY_IFC_GEN2)) - dwc->maximum_speed = USB_SPEED_SUPER_PLUS; -+ -+ break; - } - - /* Adjust Frame Length */ ---- a/drivers/usb/dwc3/gadget.c -+++ b/drivers/usb/dwc3/gadget.c -@@ -1634,10 +1634,13 @@ static int dwc3_gadget_start(struct usb_ - case USB_SPEED_HIGH: - reg |= DWC3_DSTS_HIGHSPEED; - break; -- case USB_SPEED_SUPER: /* FALLTHROUGH */ -- case USB_SPEED_UNKNOWN: /* FALTHROUGH */ - default: -- reg |= DWC3_DSTS_SUPERSPEED; -+ dev_err(dwc->dev, "invalid dwc->maximum_speed (%d)\n", -+ dwc->maximum_speed); -+ /* fall through */ -+ case USB_SPEED_SUPER: -+ reg |= DWC3_DCFG_SUPERSPEED; -+ break; - } - } - dwc3_writel(dwc->regs, DWC3_DCFG, reg); diff --git a/target/linux/ipq806x/patches-4.4/096-04-usb-dwc3-DWC_usb31-controller-check.patch b/target/linux/ipq806x/patches-4.4/096-04-usb-dwc3-DWC_usb31-controller-check.patch deleted file mode 100644 index b6f583053f..0000000000 --- a/target/linux/ipq806x/patches-4.4/096-04-usb-dwc3-DWC_usb31-controller-check.patch +++ /dev/null @@ -1,28 +0,0 @@ -From c4137a9c841ec7fb300782d211f2d6907f4d6e20 Mon Sep 17 00:00:00 2001 -From: John Youn -Date: Fri, 5 Feb 2016 17:08:18 -0800 -Subject: usb: dwc3: DWC_usb31 controller check - -Add a convenience function to check if the controller is DWC_usb31. - -Signed-off-by: John Youn -Signed-off-by: Felipe Balbi ---- - drivers/usb/dwc3/core.h | 6 ++++++ - 1 file changed, 6 insertions(+) - ---- a/drivers/usb/dwc3/core.h -+++ b/drivers/usb/dwc3/core.h -@@ -1019,6 +1019,12 @@ struct dwc3_gadget_ep_cmd_params { - void dwc3_set_mode(struct dwc3 *dwc, u32 mode); - int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc); - -+/* check whether we are on the DWC_usb31 core */ -+static inline bool dwc3_is_usb31(struct dwc3 *dwc) -+{ -+ return !!(dwc->revision & DWC3_REVISION_IS_DWC31); -+} -+ - #if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) - int dwc3_host_init(struct dwc3 *dwc); - void dwc3_host_exit(struct dwc3 *dwc); diff --git a/target/linux/ipq806x/patches-4.4/096-05-usb-dwc3-Update-register-fields-for-SuperSpeedPlus.patch b/target/linux/ipq806x/patches-4.4/096-05-usb-dwc3-Update-register-fields-for-SuperSpeedPlus.patch deleted file mode 100644 index 264893d53d..0000000000 --- a/target/linux/ipq806x/patches-4.4/096-05-usb-dwc3-Update-register-fields-for-SuperSpeedPlus.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 1f38f88a24c86d46cf47782ffabd5457f231f8ca Mon Sep 17 00:00:00 2001 -From: John Youn -Date: Fri, 5 Feb 2016 17:08:31 -0800 -Subject: usb: dwc3: Update register fields for SuperSpeedPlus - -Update various registers fields definitions for the DWC_usb31 controller -for SuperSpeedPlus support. - -Signed-off-by: John Youn -Signed-off-by: Felipe Balbi ---- - drivers/usb/dwc3/core.h | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - ---- a/drivers/usb/dwc3/core.h -+++ b/drivers/usb/dwc3/core.h -@@ -220,7 +220,8 @@ - /* Global HWPARAMS3 Register */ - #define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3) - #define DWC3_GHWPARAMS3_SSPHY_IFC_DIS 0 --#define DWC3_GHWPARAMS3_SSPHY_IFC_ENA 1 -+#define DWC3_GHWPARAMS3_SSPHY_IFC_GEN1 1 -+#define DWC3_GHWPARAMS3_SSPHY_IFC_GEN2 2 /* DWC_usb31 only */ - #define DWC3_GHWPARAMS3_HSPHY_IFC(n) (((n) & (3 << 2)) >> 2) - #define DWC3_GHWPARAMS3_HSPHY_IFC_DIS 0 - #define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI 1 -@@ -246,6 +247,7 @@ - #define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) - - #define DWC3_DCFG_SPEED_MASK (7 << 0) -+#define DWC3_DCFG_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */ - #define DWC3_DCFG_SUPERSPEED (4 << 0) - #define DWC3_DCFG_HIGHSPEED (0 << 0) - #define DWC3_DCFG_FULLSPEED2 (1 << 0) -@@ -336,6 +338,7 @@ - - #define DWC3_DSTS_CONNECTSPD (7 << 0) - -+#define DWC3_DSTS_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */ - #define DWC3_DSTS_SUPERSPEED (4 << 0) - #define DWC3_DSTS_HIGHSPEED (0 << 0) - #define DWC3_DSTS_FULLSPEED2 (1 << 0) diff --git a/target/linux/ipq806x/patches-4.4/096-06-usb-dwc3-core-improve-reset-sequence.patch b/target/linux/ipq806x/patches-4.4/096-06-usb-dwc3-core-improve-reset-sequence.patch deleted file mode 100644 index 428a796e07..0000000000 --- a/target/linux/ipq806x/patches-4.4/096-06-usb-dwc3-core-improve-reset-sequence.patch +++ /dev/null @@ -1,100 +0,0 @@ -From f59dcab176293b646e1358144c93c58c3cda2813 Mon Sep 17 00:00:00 2001 -From: Felipe Balbi -Date: Fri, 11 Mar 2016 10:51:52 +0200 -Subject: usb: dwc3: core: improve reset sequence - -According to Synopsys Databook, we shouldn't be -relying on GCTL.CORESOFTRESET bit as that's only for -debugging purposes. Instead, let's use DCTL.CSFTRST -if we're OTG or PERIPHERAL mode. - -Host side block will be reset by XHCI driver if -necessary. Note that this reduces amount of time -spent on dwc3_probe() by a long margin. - -We're still gonna wait for reset to finish for a -long time (default to 1ms max), but tests show that -the reset polling loop executed at most 19 times -(modprobe dwc3 && modprobe -r dwc3 executed 1000 -times in a row). - -Suggested-by: Mian Yousaf Kaukab -Signed-off-by: Felipe Balbi ---- - drivers/usb/dwc3/core.c | 48 ++++++++++++++++++------------------------------ - 1 file changed, 18 insertions(+), 30 deletions(-) - ---- a/drivers/usb/dwc3/core.c -+++ b/drivers/usb/dwc3/core.c -@@ -67,23 +67,9 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 - static int dwc3_core_soft_reset(struct dwc3 *dwc) - { - u32 reg; -+ int retries = 1000; - int ret; - -- /* Before Resetting PHY, put Core in Reset */ -- reg = dwc3_readl(dwc->regs, DWC3_GCTL); -- reg |= DWC3_GCTL_CORESOFTRESET; -- dwc3_writel(dwc->regs, DWC3_GCTL, reg); -- -- /* Assert USB3 PHY reset */ -- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); -- reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST; -- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); -- -- /* Assert USB2 PHY reset */ -- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); -- reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST; -- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); -- - usb_phy_init(dwc->usb2_phy); - usb_phy_init(dwc->usb3_phy); - ret = phy_init(dwc->usb2_generic_phy); -@@ -95,26 +81,28 @@ static int dwc3_core_soft_reset(struct d - phy_exit(dwc->usb2_generic_phy); - return ret; - } -- mdelay(100); - -- /* Clear USB3 PHY reset */ -- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); -- reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST; -- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); -- -- /* Clear USB2 PHY reset */ -- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); -- reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST; -- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); -- -- mdelay(100); -- -- /* After PHYs are stable we can take Core out of reset state */ -- reg = dwc3_readl(dwc->regs, DWC3_GCTL); -- reg &= ~DWC3_GCTL_CORESOFTRESET; -- dwc3_writel(dwc->regs, DWC3_GCTL, reg); -+ /* -+ * We're resetting only the device side because, if we're in host mode, -+ * XHCI driver will reset the host block. If dwc3 was configured for -+ * host-only mode, then we can return early. -+ */ -+ if (dwc->dr_mode == USB_DR_MODE_HOST) -+ return 0; -+ -+ reg = dwc3_readl(dwc->regs, DWC3_DCTL); -+ reg |= DWC3_DCTL_CSFTRST; -+ dwc3_writel(dwc->regs, DWC3_DCTL, reg); -+ -+ do { -+ reg = dwc3_readl(dwc->regs, DWC3_DCTL); -+ if (!(reg & DWC3_DCTL_CSFTRST)) -+ return 0; -+ -+ udelay(1); -+ } while (--retries); - -- return 0; -+ return -ETIMEDOUT; - } - - /** diff --git a/target/linux/ipq806x/patches-4.4/096-07-usb-dwc3-drop-FIFO-resizing-logic.patch b/target/linux/ipq806x/patches-4.4/096-07-usb-dwc3-drop-FIFO-resizing-logic.patch deleted file mode 100644 index 132d131dfb..0000000000 --- a/target/linux/ipq806x/patches-4.4/096-07-usb-dwc3-drop-FIFO-resizing-logic.patch +++ /dev/null @@ -1,242 +0,0 @@ -From bc5081617faeb3b2f0c126dc37264b87af7da47f Mon Sep 17 00:00:00 2001 -From: Felipe Balbi -Date: Thu, 4 Feb 2016 14:18:01 +0200 -Subject: usb: dwc3: drop FIFO resizing logic - -That FIFO resizing logic was added to support OMAP5 -ES1.0 which had a bogus default FIFO size. I can't -remember the exact size of default FIFO, but it was -less than one bulk superspeed packet (<1024) which -would prevent USB3 from ever working on OMAP5 ES1.0. - -However, OMAP5 ES1.0 support has been dropped by -commit aa2f4b16f830 ("ARM: OMAP5: id: Remove ES1.0 -support") which renders FIFO resizing unnecessary. - -Tested-by: Kishon Vijay Abraham I -Signed-off-by: Felipe Balbi ---- - Documentation/devicetree/bindings/usb/dwc3.txt | 4 +- - .../devicetree/bindings/usb/qcom,dwc3.txt | 1 - - drivers/usb/dwc3/core.c | 4 - - drivers/usb/dwc3/core.h | 5 -- - drivers/usb/dwc3/ep0.c | 9 --- - drivers/usb/dwc3/gadget.c | 86 ---------------------- - drivers/usb/dwc3/platform_data.h | 1 - - 7 files changed, 2 insertions(+), 108 deletions(-) - ---- a/Documentation/devicetree/bindings/usb/dwc3.txt -+++ b/Documentation/devicetree/bindings/usb/dwc3.txt -@@ -14,7 +14,6 @@ Optional properties: - the second element is expected to be a handle to the USB3/SS PHY - - phys: from the *Generic PHY* bindings - - phy-names: from the *Generic PHY* bindings -- - tx-fifo-resize: determines if the FIFO *has* to be reallocated. - - snps,usb3_lpm_capable: determines if platform is USB3 LPM capable - - snps,disable_scramble_quirk: true when SW should disable data scrambling. - Only really useful for FPGA builds. -@@ -47,6 +46,8 @@ Optional properties: - register for post-silicon frame length adjustment when the - fladj_30mhz_sdbnd signal is invalid or incorrect. - -+ - tx-fifo-resize: determines if the FIFO *has* to be reallocated. -+ - This is usually a subnode to DWC3 glue to which it is connected. - - dwc3@4a030000 { -@@ -54,5 +55,4 @@ dwc3@4a030000 { - reg = <0x4a030000 0xcfff>; - interrupts = <0 92 4> - usb-phy = <&usb2_phy>, <&usb3,phy>; -- tx-fifo-resize; - }; ---- a/Documentation/devicetree/bindings/usb/qcom,dwc3.txt -+++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt -@@ -59,7 +59,6 @@ Example device nodes: - interrupts = <0 205 0x4>; - phys = <&hs_phy>, <&ss_phy>; - phy-names = "usb2-phy", "usb3-phy"; -- tx-fifo-resize; - dr_mode = "host"; - }; - }; ---- a/drivers/usb/dwc3/core.c -+++ b/drivers/usb/dwc3/core.c -@@ -882,9 +882,6 @@ static int dwc3_probe(struct platform_de - dwc->usb3_lpm_capable = device_property_read_bool(dev, - "snps,usb3_lpm_capable"); - -- dwc->needs_fifo_resize = device_property_read_bool(dev, -- "tx-fifo-resize"); -- - dwc->disable_scramble_quirk = device_property_read_bool(dev, - "snps,disable_scramble_quirk"); - dwc->u2exit_lfps_quirk = device_property_read_bool(dev, -@@ -926,7 +923,6 @@ static int dwc3_probe(struct platform_de - if (pdata->hird_threshold) - hird_threshold = pdata->hird_threshold; - -- dwc->needs_fifo_resize = pdata->tx_fifo_resize; - dwc->usb3_lpm_capable = pdata->usb3_lpm_capable; - dwc->dr_mode = pdata->dr_mode; - ---- a/drivers/usb/dwc3/core.h -+++ b/drivers/usb/dwc3/core.h -@@ -705,9 +705,7 @@ struct dwc3_scratchpad_array { - * 0 - utmi_sleep_n - * 1 - utmi_l1_suspend_n - * @is_fpga: true when we are using the FPGA board -- * @needs_fifo_resize: not all users might want fifo resizing, flag it - * @pullups_connected: true when Run/Stop bit is set -- * @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes. - * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround - * @start_config_issued: true when StartConfig command has been issued - * @three_stage_setup: set if we perform a three phase setup -@@ -850,9 +848,7 @@ struct dwc3 { - unsigned has_lpm_erratum:1; - unsigned is_utmi_l1_suspend:1; - unsigned is_fpga:1; -- unsigned needs_fifo_resize:1; - unsigned pullups_connected:1; -- unsigned resize_fifos:1; - unsigned setup_packet_pending:1; - unsigned three_stage_setup:1; - unsigned usb3_lpm_capable:1; -@@ -1020,7 +1016,6 @@ struct dwc3_gadget_ep_cmd_params { - - /* prototypes */ - void dwc3_set_mode(struct dwc3 *dwc, u32 mode); --int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc); - - /* check whether we are on the DWC_usb31 core */ - static inline bool dwc3_is_usb31(struct dwc3 *dwc) ---- a/drivers/usb/dwc3/ep0.c -+++ b/drivers/usb/dwc3/ep0.c -@@ -587,9 +587,6 @@ static int dwc3_ep0_set_config(struct dw - reg = dwc3_readl(dwc->regs, DWC3_DCTL); - reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA); - dwc3_writel(dwc->regs, DWC3_DCTL, reg); -- -- dwc->resize_fifos = true; -- dwc3_trace(trace_dwc3_ep0, "resize FIFOs flag SET"); - } - break; - -@@ -1028,12 +1025,6 @@ static int dwc3_ep0_start_control_status - - static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep) - { -- if (dwc->resize_fifos) { -- dwc3_trace(trace_dwc3_ep0, "Resizing FIFOs"); -- dwc3_gadget_resize_tx_fifos(dwc); -- dwc->resize_fifos = 0; -- } -- - WARN_ON(dwc3_ep0_start_control_status(dep)); - } - ---- a/drivers/usb/dwc3/gadget.c -+++ b/drivers/usb/dwc3/gadget.c -@@ -145,92 +145,6 @@ int dwc3_gadget_set_link_state(struct dw - return -ETIMEDOUT; - } - --/** -- * dwc3_gadget_resize_tx_fifos - reallocate fifo spaces for current use-case -- * @dwc: pointer to our context structure -- * -- * This function will a best effort FIFO allocation in order -- * to improve FIFO usage and throughput, while still allowing -- * us to enable as many endpoints as possible. -- * -- * Keep in mind that this operation will be highly dependent -- * on the configured size for RAM1 - which contains TxFifo -, -- * the amount of endpoints enabled on coreConsultant tool, and -- * the width of the Master Bus. -- * -- * In the ideal world, we would always be able to satisfy the -- * following equation: -- * -- * ((512 + 2 * MDWIDTH-Bytes) + (Number of IN Endpoints - 1) * \ -- * (3 * (1024 + MDWIDTH-Bytes) + MDWIDTH-Bytes)) / MDWIDTH-Bytes -- * -- * Unfortunately, due to many variables that's not always the case. -- */ --int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc) --{ -- int last_fifo_depth = 0; -- int ram1_depth; -- int fifo_size; -- int mdwidth; -- int num; -- -- if (!dwc->needs_fifo_resize) -- return 0; -- -- ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7); -- mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0); -- -- /* MDWIDTH is represented in bits, we need it in bytes */ -- mdwidth >>= 3; -- -- /* -- * FIXME For now we will only allocate 1 wMaxPacketSize space -- * for each enabled endpoint, later patches will come to -- * improve this algorithm so that we better use the internal -- * FIFO space -- */ -- for (num = 0; num < dwc->num_in_eps; num++) { -- /* bit0 indicates direction; 1 means IN ep */ -- struct dwc3_ep *dep = dwc->eps[(num << 1) | 1]; -- int mult = 1; -- int tmp; -- -- if (!(dep->flags & DWC3_EP_ENABLED)) -- continue; -- -- if (usb_endpoint_xfer_bulk(dep->endpoint.desc) -- || usb_endpoint_xfer_isoc(dep->endpoint.desc)) -- mult = 3; -- -- /* -- * REVISIT: the following assumes we will always have enough -- * space available on the FIFO RAM for all possible use cases. -- * Make sure that's true somehow and change FIFO allocation -- * accordingly. -- * -- * If we have Bulk or Isochronous endpoints, we want -- * them to be able to be very, very fast. So we're giving -- * those endpoints a fifo_size which is enough for 3 full -- * packets -- */ -- tmp = mult * (dep->endpoint.maxpacket + mdwidth); -- tmp += mdwidth; -- -- fifo_size = DIV_ROUND_UP(tmp, mdwidth); -- -- fifo_size |= (last_fifo_depth << 16); -- -- dwc3_trace(trace_dwc3_gadget, "%s: Fifo Addr %04x Size %d", -- dep->name, last_fifo_depth, fifo_size & 0xffff); -- -- dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), fifo_size); -- -- last_fifo_depth += (fifo_size & 0xffff); -- } -- -- return 0; --} -- - void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, - int status) - { ---- a/drivers/usb/dwc3/platform_data.h -+++ b/drivers/usb/dwc3/platform_data.h -@@ -23,7 +23,6 @@ - struct dwc3_platform_data { - enum usb_device_speed maximum_speed; - enum usb_dr_mode dr_mode; -- bool tx_fifo_resize; - bool usb3_lpm_capable; - - unsigned is_utmi_l1_suspend:1; diff --git a/target/linux/ipq806x/patches-4.4/096-08-usb-dwc3-remove-num_event_buffers.patch b/target/linux/ipq806x/patches-4.4/096-08-usb-dwc3-remove-num_event_buffers.patch deleted file mode 100644 index 5067548019..0000000000 --- a/target/linux/ipq806x/patches-4.4/096-08-usb-dwc3-remove-num_event_buffers.patch +++ /dev/null @@ -1,265 +0,0 @@ -From 660e9bde74d6915227d7ee3485b11e5f52637b26 Mon Sep 17 00:00:00 2001 -From: Felipe Balbi -Date: Wed, 30 Mar 2016 09:26:24 +0300 -Subject: usb: dwc3: remove num_event_buffers - -We never, ever route any of the other event buffers -so we might as well drop support for them. - -Until someone has a real, proper benefit for -multiple event buffers, we will rely on a single -one. This also helps reduce memory footprint of -dwc3.ko which won't allocate memory for the extra -event buffers. - -Signed-off-by: Felipe Balbi ---- - drivers/usb/dwc3/core.c | 81 +++++++++++++++++++---------------------------- - drivers/usb/dwc3/core.h | 2 -- - drivers/usb/dwc3/gadget.c | 38 +++++++--------------- - 3 files changed, 44 insertions(+), 77 deletions(-) - ---- a/drivers/usb/dwc3/core.c -+++ b/drivers/usb/dwc3/core.c -@@ -203,13 +203,10 @@ static struct dwc3_event_buffer *dwc3_al - static void dwc3_free_event_buffers(struct dwc3 *dwc) - { - struct dwc3_event_buffer *evt; -- int i; - -- for (i = 0; i < dwc->num_event_buffers; i++) { -- evt = dwc->ev_buffs[i]; -- if (evt) -- dwc3_free_one_event_buffer(dwc, evt); -- } -+ evt = dwc->ev_buffs[0]; -+ if (evt) -+ dwc3_free_one_event_buffer(dwc, evt); - } - - /** -@@ -222,27 +219,19 @@ static void dwc3_free_event_buffers(stru - */ - static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) - { -- int num; -- int i; -- -- num = DWC3_NUM_INT(dwc->hwparams.hwparams1); -- dwc->num_event_buffers = num; -+ struct dwc3_event_buffer *evt; - -- dwc->ev_buffs = devm_kzalloc(dwc->dev, sizeof(*dwc->ev_buffs) * num, -+ dwc->ev_buffs = devm_kzalloc(dwc->dev, sizeof(*dwc->ev_buffs), - GFP_KERNEL); - if (!dwc->ev_buffs) - return -ENOMEM; - -- for (i = 0; i < num; i++) { -- struct dwc3_event_buffer *evt; -- -- evt = dwc3_alloc_one_event_buffer(dwc, length); -- if (IS_ERR(evt)) { -- dev_err(dwc->dev, "can't allocate event buffer\n"); -- return PTR_ERR(evt); -- } -- dwc->ev_buffs[i] = evt; -+ evt = dwc3_alloc_one_event_buffer(dwc, length); -+ if (IS_ERR(evt)) { -+ dev_err(dwc->dev, "can't allocate event buffer\n"); -+ return PTR_ERR(evt); - } -+ dwc->ev_buffs[0] = evt; - - return 0; - } -@@ -256,25 +245,22 @@ static int dwc3_alloc_event_buffers(stru - static int dwc3_event_buffers_setup(struct dwc3 *dwc) - { - struct dwc3_event_buffer *evt; -- int n; - -- for (n = 0; n < dwc->num_event_buffers; n++) { -- evt = dwc->ev_buffs[n]; -- dwc3_trace(trace_dwc3_core, -- "Event buf %p dma %08llx length %d\n", -- evt->buf, (unsigned long long) evt->dma, -- evt->length); -- -- evt->lpos = 0; -- -- dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), -- lower_32_bits(evt->dma)); -- dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), -- upper_32_bits(evt->dma)); -- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), -- DWC3_GEVNTSIZ_SIZE(evt->length)); -- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0); -- } -+ evt = dwc->ev_buffs[0]; -+ dwc3_trace(trace_dwc3_core, -+ "Event buf %p dma %08llx length %d\n", -+ evt->buf, (unsigned long long) evt->dma, -+ evt->length); -+ -+ evt->lpos = 0; -+ -+ dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), -+ lower_32_bits(evt->dma)); -+ dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), -+ upper_32_bits(evt->dma)); -+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), -+ DWC3_GEVNTSIZ_SIZE(evt->length)); -+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0); - - return 0; - } -@@ -282,19 +268,16 @@ static int dwc3_event_buffers_setup(stru - static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) - { - struct dwc3_event_buffer *evt; -- int n; - -- for (n = 0; n < dwc->num_event_buffers; n++) { -- evt = dwc->ev_buffs[n]; -+ evt = dwc->ev_buffs[0]; - -- evt->lpos = 0; -+ evt->lpos = 0; - -- dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0); -- dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0); -- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), DWC3_GEVNTSIZ_INTMASK -- | DWC3_GEVNTSIZ_SIZE(0)); -- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0); -- } -+ dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), 0); -+ dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), 0); -+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK -+ | DWC3_GEVNTSIZ_SIZE(0)); -+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0); - } - - static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc) ---- a/drivers/usb/dwc3/core.h -+++ b/drivers/usb/dwc3/core.h -@@ -663,7 +663,6 @@ struct dwc3_scratchpad_array { - * @regs: base address for our registers - * @regs_size: address space size - * @nr_scratch: number of scratch buffers -- * @num_event_buffers: calculated number of event buffers - * @u1u2: only used on revisions <1.83a for workaround - * @maximum_speed: maximum speed requested (mainly for testing purposes) - * @revision: revision register contents -@@ -773,7 +772,6 @@ struct dwc3 { - u32 gctl; - - u32 nr_scratch; -- u32 num_event_buffers; - u32 u1u2; - u32 maximum_speed; - ---- a/drivers/usb/dwc3/gadget.c -+++ b/drivers/usb/dwc3/gadget.c -@@ -2556,14 +2556,14 @@ static void dwc3_process_event_entry(str - } - } - --static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf) -+static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc) - { - struct dwc3_event_buffer *evt; - irqreturn_t ret = IRQ_NONE; - int left; - u32 reg; - -- evt = dwc->ev_buffs[buf]; -+ evt = dwc->ev_buffs[0]; - left = evt->count; - - if (!(evt->flags & DWC3_EVENT_PENDING)) -@@ -2588,7 +2588,7 @@ static irqreturn_t dwc3_process_event_bu - evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE; - left -= 4; - -- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4); -+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 4); - } - - evt->count = 0; -@@ -2596,9 +2596,9 @@ static irqreturn_t dwc3_process_event_bu - ret = IRQ_HANDLED; - - /* Unmask interrupt */ -- reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(buf)); -+ reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(0)); - reg &= ~DWC3_GEVNTSIZ_INTMASK; -- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg); -+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg); - - return ret; - } -@@ -2608,27 +2608,23 @@ static irqreturn_t dwc3_thread_interrupt - struct dwc3 *dwc = _dwc; - unsigned long flags; - irqreturn_t ret = IRQ_NONE; -- int i; - - spin_lock_irqsave(&dwc->lock, flags); -- -- for (i = 0; i < dwc->num_event_buffers; i++) -- ret |= dwc3_process_event_buf(dwc, i); -- -+ ret = dwc3_process_event_buf(dwc); - spin_unlock_irqrestore(&dwc->lock, flags); - - return ret; - } - --static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc, u32 buf) -+static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc) - { - struct dwc3_event_buffer *evt; - u32 count; - u32 reg; - -- evt = dwc->ev_buffs[buf]; -+ evt = dwc->ev_buffs[0]; - -- count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(buf)); -+ count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); - count &= DWC3_GEVNTCOUNT_MASK; - if (!count) - return IRQ_NONE; -@@ -2637,9 +2633,9 @@ static irqreturn_t dwc3_check_event_buf( - evt->flags |= DWC3_EVENT_PENDING; - - /* Mask interrupt */ -- reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(buf)); -+ reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(0)); - reg |= DWC3_GEVNTSIZ_INTMASK; -- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg); -+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg); - - return IRQ_WAKE_THREAD; - } -@@ -2647,18 +2643,8 @@ static irqreturn_t dwc3_check_event_buf( - static irqreturn_t dwc3_interrupt(int irq, void *_dwc) - { - struct dwc3 *dwc = _dwc; -- int i; -- irqreturn_t ret = IRQ_NONE; -- -- for (i = 0; i < dwc->num_event_buffers; i++) { -- irqreturn_t status; - -- status = dwc3_check_event_buf(dwc, i); -- if (status == IRQ_WAKE_THREAD) -- ret = status; -- } -- -- return ret; -+ return dwc3_check_event_buf(dwc); - } - - /** diff --git a/target/linux/ipq806x/patches-4.4/096-09-usb-dwc3-drop-ev_buffs-array.patch b/target/linux/ipq806x/patches-4.4/096-09-usb-dwc3-drop-ev_buffs-array.patch deleted file mode 100644 index 827b621c98..0000000000 --- a/target/linux/ipq806x/patches-4.4/096-09-usb-dwc3-drop-ev_buffs-array.patch +++ /dev/null @@ -1,96 +0,0 @@ -From 696c8b1282205caa5206264449f80ef756f14ef7 Mon Sep 17 00:00:00 2001 -From: Felipe Balbi -Date: Wed, 30 Mar 2016 09:37:03 +0300 -Subject: usb: dwc3: drop ev_buffs array - -we will be using a single event buffer and that -renders ev_buffs array unnecessary. Let's remove it -in favor of a single pointer to a single event -buffer. - -Signed-off-by: Felipe Balbi ---- - drivers/usb/dwc3/core.c | 13 ++++--------- - drivers/usb/dwc3/core.h | 2 +- - drivers/usb/dwc3/gadget.c | 4 ++-- - 3 files changed, 7 insertions(+), 12 deletions(-) - ---- a/drivers/usb/dwc3/core.c -+++ b/drivers/usb/dwc3/core.c -@@ -204,7 +204,7 @@ static void dwc3_free_event_buffers(stru - { - struct dwc3_event_buffer *evt; - -- evt = dwc->ev_buffs[0]; -+ evt = dwc->ev_buf; - if (evt) - dwc3_free_one_event_buffer(dwc, evt); - } -@@ -221,17 +221,12 @@ static int dwc3_alloc_event_buffers(stru - { - struct dwc3_event_buffer *evt; - -- dwc->ev_buffs = devm_kzalloc(dwc->dev, sizeof(*dwc->ev_buffs), -- GFP_KERNEL); -- if (!dwc->ev_buffs) -- return -ENOMEM; -- - evt = dwc3_alloc_one_event_buffer(dwc, length); - if (IS_ERR(evt)) { - dev_err(dwc->dev, "can't allocate event buffer\n"); - return PTR_ERR(evt); - } -- dwc->ev_buffs[0] = evt; -+ dwc->ev_buf = evt; - - return 0; - } -@@ -246,7 +241,7 @@ static int dwc3_event_buffers_setup(stru - { - struct dwc3_event_buffer *evt; - -- evt = dwc->ev_buffs[0]; -+ evt = dwc->ev_buf; - dwc3_trace(trace_dwc3_core, - "Event buf %p dma %08llx length %d\n", - evt->buf, (unsigned long long) evt->dma, -@@ -269,7 +264,7 @@ static void dwc3_event_buffers_cleanup(s - { - struct dwc3_event_buffer *evt; - -- evt = dwc->ev_buffs[0]; -+ evt = dwc->ev_buf; - - evt->lpos = 0; - ---- a/drivers/usb/dwc3/core.h -+++ b/drivers/usb/dwc3/core.h -@@ -748,7 +748,7 @@ struct dwc3 { - struct platform_device *xhci; - struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM]; - -- struct dwc3_event_buffer **ev_buffs; -+ struct dwc3_event_buffer *ev_buf; - struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM]; - - struct usb_gadget gadget; ---- a/drivers/usb/dwc3/gadget.c -+++ b/drivers/usb/dwc3/gadget.c -@@ -2563,7 +2563,7 @@ static irqreturn_t dwc3_process_event_bu - int left; - u32 reg; - -- evt = dwc->ev_buffs[0]; -+ evt = dwc->ev_buf; - left = evt->count; - - if (!(evt->flags & DWC3_EVENT_PENDING)) -@@ -2622,7 +2622,7 @@ static irqreturn_t dwc3_check_event_buf( - u32 count; - u32 reg; - -- evt = dwc->ev_buffs[0]; -+ evt = dwc->ev_buf; - - count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); - count &= DWC3_GEVNTCOUNT_MASK; diff --git a/target/linux/ipq806x/patches-4.4/096-10-usb-dwc3-core-fix-PHY-handling-during-suspend.patch b/target/linux/ipq806x/patches-4.4/096-10-usb-dwc3-core-fix-PHY-handling-during-suspend.patch deleted file mode 100644 index 8bf09edfbd..0000000000 --- a/target/linux/ipq806x/patches-4.4/096-10-usb-dwc3-core-fix-PHY-handling-during-suspend.patch +++ /dev/null @@ -1,67 +0,0 @@ -From 5c4ad318de3b8e8680d654c82a254c4b65243739 Mon Sep 17 00:00:00 2001 -From: Felipe Balbi -Date: Mon, 11 Apr 2016 17:12:34 +0300 -Subject: usb: dwc3: core: fix PHY handling during suspend - -we need to power off the PHY during suspend and -power it back on during resume. - -Signed-off-by: Felipe Balbi -[nsekhar@ti.com: fix call to usb_phy_set_suspend() in dwc3_suspend()] -Signed-off-by: Sekhar Nori -Signed-off-by: Roger Quadros -Signed-off-by: Felipe Balbi ---- - drivers/usb/dwc3/core.c | 23 ++++++++++++++++++++++- - 1 file changed, 22 insertions(+), 1 deletion(-) - ---- a/drivers/usb/dwc3/core.c -+++ b/drivers/usb/dwc3/core.c -@@ -1124,6 +1124,11 @@ static int dwc3_suspend(struct device *d - phy_exit(dwc->usb2_generic_phy); - phy_exit(dwc->usb3_generic_phy); - -+ usb_phy_set_suspend(dwc->usb2_phy, 1); -+ usb_phy_set_suspend(dwc->usb3_phy, 1); -+ WARN_ON(phy_power_off(dwc->usb2_generic_phy) < 0); -+ WARN_ON(phy_power_off(dwc->usb3_generic_phy) < 0); -+ - pinctrl_pm_select_sleep_state(dev); - - return 0; -@@ -1137,11 +1142,21 @@ static int dwc3_resume(struct device *de - - pinctrl_pm_select_default_state(dev); - -+ usb_phy_set_suspend(dwc->usb2_phy, 0); -+ usb_phy_set_suspend(dwc->usb3_phy, 0); -+ ret = phy_power_on(dwc->usb2_generic_phy); -+ if (ret < 0) -+ return ret; -+ -+ ret = phy_power_on(dwc->usb3_generic_phy); -+ if (ret < 0) -+ goto err_usb2phy_power; -+ - usb_phy_init(dwc->usb3_phy); - usb_phy_init(dwc->usb2_phy); - ret = phy_init(dwc->usb2_generic_phy); - if (ret < 0) -- return ret; -+ goto err_usb3phy_power; - - ret = phy_init(dwc->usb3_generic_phy); - if (ret < 0) -@@ -1174,6 +1189,12 @@ static int dwc3_resume(struct device *de - err_usb2phy_init: - phy_exit(dwc->usb2_generic_phy); - -+err_usb3phy_power: -+ phy_power_off(dwc->usb3_generic_phy); -+ -+err_usb2phy_power: -+ phy_power_off(dwc->usb2_generic_phy); -+ - return ret; - } - diff --git a/target/linux/ipq806x/patches-4.4/097-1-usb-dwc3-add-generic-OF-glue-layer.patch b/target/linux/ipq806x/patches-4.4/097-1-usb-dwc3-add-generic-OF-glue-layer.patch deleted file mode 100644 index 214bedc95a..0000000000 --- a/target/linux/ipq806x/patches-4.4/097-1-usb-dwc3-add-generic-OF-glue-layer.patch +++ /dev/null @@ -1,234 +0,0 @@ -From 41c2b5280cd2fa3e198c422cdf223ba6e48f857a Mon Sep 17 00:00:00 2001 -From: Felipe Balbi -Date: Wed, 18 Nov 2015 13:15:20 -0600 -Subject: [PATCH] usb: dwc3: add generic OF glue layer - -For simple platforms which merely enable some clocks -and populate its children, we can use this generic -glue layer to avoid boilerplate code duplication. - -For now this supports Qcom and Xilinx, but if we -find a way to add generic handling of regulators and -optional PHYs, we can absorb exynos as well. - -Tested-by: Subbaraya Sundeep Bhatta -Signed-off-by: Felipe Balbi -(cherry picked from commit 16adc674d0d68a50dfc725574738d7ae11cf5d7e) - -Change-Id: I6fd260442997b198dc12ca726814b7a9518e6353 -Signed-off-by: Nitheesh Sekar ---- - drivers/usb/dwc3/Kconfig | 9 ++ - drivers/usb/dwc3/Makefile | 1 + - drivers/usb/dwc3/dwc3-of-simple.c | 178 ++++++++++++++++++++++++++++++++++++++ - 3 files changed, 188 insertions(+) - create mode 100644 drivers/usb/dwc3/dwc3-of-simple.c - ---- a/drivers/usb/dwc3/Kconfig -+++ b/drivers/usb/dwc3/Kconfig -@@ -87,6 +87,15 @@ config USB_DWC3_KEYSTONE - Support of USB2/3 functionality in TI Keystone2 platforms. - Say 'Y' or 'M' here if you have one such device - -+config USB_DWC3_OF_SIMPLE -+ tristate "Generic OF Simple Glue Layer" -+ depends on OF && COMMON_CLK -+ default USB_DWC3 -+ help -+ Support USB2/3 functionality in simple SoC integrations. -+ Currently supports Xilinx and Qualcomm DWC USB3 IP. -+ Say 'Y' or 'M' if you have one such device. -+ - config USB_DWC3_ST - tristate "STMicroelectronics Platforms" - depends on ARCH_STI && OF ---- a/drivers/usb/dwc3/Makefile -+++ b/drivers/usb/dwc3/Makefile -@@ -37,5 +37,6 @@ obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-oma - obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o - obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o - obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o -+obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o - obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o - obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o ---- /dev/null -+++ b/drivers/usb/dwc3/dwc3-of-simple.c -@@ -0,0 +1,178 @@ -+/** -+ * dwc3-of-simple.c - OF glue layer for simple integrations -+ * -+ * Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com -+ * -+ * Author: Felipe Balbi -+ * -+ * This program is free software: you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 of -+ * the License 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. -+ * -+ * This is a combination of the old dwc3-qcom.c by Ivan T. Ivanov -+ * and the original patch adding support for Xilinx' SoC -+ * by Subbaraya Sundeep Bhatta -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+struct dwc3_of_simple { -+ struct device *dev; -+ struct clk **clks; -+ int num_clocks; -+}; -+ -+static int dwc3_of_simple_probe(struct platform_device *pdev) -+{ -+ struct dwc3_of_simple *simple; -+ struct device *dev = &pdev->dev; -+ struct device_node *np = dev->of_node; -+ -+ int ret; -+ int i; -+ -+ simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL); -+ if (!simple) -+ return -ENOMEM; -+ -+ ret = of_clk_get_parent_count(np); -+ if (ret < 0) -+ return ret; -+ -+ simple->num_clocks = ret; -+ -+ simple->clks = devm_kcalloc(dev, simple->num_clocks, -+ sizeof(struct clk *), GFP_KERNEL); -+ if (!simple->clks) -+ return -ENOMEM; -+ -+ simple->dev = dev; -+ -+ for (i = 0; i < simple->num_clocks; i++) { -+ struct clk *clk; -+ -+ clk = of_clk_get(np, i); -+ if (IS_ERR(clk)) { -+ while (--i >= 0) -+ clk_put(simple->clks[i]); -+ return PTR_ERR(clk); -+ } -+ -+ ret = clk_prepare_enable(clk); -+ if (ret < 0) { -+ while (--i >= 0) { -+ clk_disable_unprepare(simple->clks[i]); -+ clk_put(simple->clks[i]); -+ } -+ clk_put(clk); -+ -+ return ret; -+ } -+ -+ simple->clks[i] = clk; -+ } -+ -+ ret = of_platform_populate(np, NULL, NULL, dev); -+ if (ret) { -+ for (i = 0; i < simple->num_clocks; i++) { -+ clk_disable_unprepare(simple->clks[i]); -+ clk_put(simple->clks[i]); -+ } -+ -+ return ret; -+ } -+ -+ pm_runtime_set_active(dev); -+ pm_runtime_enable(dev); -+ pm_runtime_get_sync(dev); -+ -+ return 0; -+} -+ -+static int dwc3_of_simple_remove(struct platform_device *pdev) -+{ -+ struct dwc3_of_simple *simple = platform_get_drvdata(pdev); -+ struct device *dev = &pdev->dev; -+ int i; -+ -+ for (i = 0; i < simple->num_clocks; i++) { -+ clk_unprepare(simple->clks[i]); -+ clk_put(simple->clks[i]); -+ } -+ -+ of_platform_depopulate(dev); -+ -+ pm_runtime_put_sync(dev); -+ pm_runtime_disable(dev); -+ -+ return 0; -+} -+ -+static int dwc3_of_simple_runtime_suspend(struct device *dev) -+{ -+ struct dwc3_of_simple *simple = dev_get_drvdata(dev); -+ int i; -+ -+ for (i = 0; i < simple->num_clocks; i++) -+ clk_disable(simple->clks[i]); -+ -+ return 0; -+} -+ -+static int dwc3_of_simple_runtime_resume(struct device *dev) -+{ -+ struct dwc3_of_simple *simple = dev_get_drvdata(dev); -+ int ret; -+ int i; -+ -+ for (i = 0; i < simple->num_clocks; i++) { -+ ret = clk_enable(simple->clks[i]); -+ if (ret < 0) { -+ while (--i >= 0) -+ clk_disable(simple->clks[i]); -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = { -+ SET_RUNTIME_PM_OPS(dwc3_of_simple_runtime_suspend, -+ dwc3_of_simple_runtime_resume, NULL) -+}; -+ -+static const struct of_device_id of_dwc3_simple_match[] = { -+ { .compatible = "qcom,dwc3" }, -+ { .compatible = "xlnx,zynqmp-dwc3" }, -+ { /* Sentinel */ } -+}; -+MODULE_DEVICE_TABLE(of, of_dwc3_simple_match); -+ -+static struct platform_driver dwc3_of_simple_driver = { -+ .probe = dwc3_of_simple_probe, -+ .remove = dwc3_of_simple_remove, -+ .driver = { -+ .name = "dwc3-of-simple", -+ .of_match_table = of_dwc3_simple_match, -+ }, -+}; -+ -+module_platform_driver(dwc3_of_simple_driver); -+MODULE_LICENSE("GPL v2"); -+MODULE_DESCRIPTION("DesignWare USB3 OF Simple Glue Layer"); -+MODULE_AUTHOR("Felipe Balbi "); diff --git a/target/linux/ipq806x/patches-4.4/097-2-usb-dwc3-of-simple-fix-build-warning-on-PM.patch b/target/linux/ipq806x/patches-4.4/097-2-usb-dwc3-of-simple-fix-build-warning-on-PM.patch deleted file mode 100644 index b982c82367..0000000000 --- a/target/linux/ipq806x/patches-4.4/097-2-usb-dwc3-of-simple-fix-build-warning-on-PM.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 131386d63ca3177d471aa93808c69b85fdac520d Mon Sep 17 00:00:00 2001 -From: Felipe Balbi -Date: Tue, 22 Dec 2015 21:56:10 -0600 -Subject: [PATCH] usb: dwc3: of-simple: fix build warning on !PM - -if we have a !PM kernel build, our runtime -suspend/resume callbacks will be left defined but -unused. Add a ifdef CONFIG_PM guard. - -Signed-off-by: Felipe Balbi -(cherry picked from commit 5072cfc40a80cea3749fd3413b3896630d8c787e) - -Change-Id: I088186c33aa917ec8da2985372ceefc289b24242 -Signed-off-by: Nitheesh Sekar ---- - drivers/usb/dwc3/dwc3-of-simple.c | 2 ++ - 1 file changed, 2 insertions(+) - ---- a/drivers/usb/dwc3/dwc3-of-simple.c -+++ b/drivers/usb/dwc3/dwc3-of-simple.c -@@ -122,6 +122,7 @@ static int dwc3_of_simple_remove(struct - return 0; - } - -+#ifdef CONFIG_PM - static int dwc3_of_simple_runtime_suspend(struct device *dev) - { - struct dwc3_of_simple *simple = dev_get_drvdata(dev); -@@ -150,6 +151,7 @@ static int dwc3_of_simple_runtime_resume - - return 0; - } -+#endif - - static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = { - SET_RUNTIME_PM_OPS(dwc3_of_simple_runtime_suspend, diff --git a/target/linux/ipq806x/patches-4.4/097-3-usb-dwc3-Remove-impossible-check-for-of_clk_get_pare.patch b/target/linux/ipq806x/patches-4.4/097-3-usb-dwc3-Remove-impossible-check-for-of_clk_get_pare.patch deleted file mode 100644 index 32f9e34177..0000000000 --- a/target/linux/ipq806x/patches-4.4/097-3-usb-dwc3-Remove-impossible-check-for-of_clk_get_pare.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 07c8b15688055d81ac8e1c8c964b9e4c302287f1 Mon Sep 17 00:00:00 2001 -From: Stephen Boyd -Date: Mon, 22 Feb 2016 11:12:47 -0800 -Subject: [PATCH] usb: dwc3: Remove impossible check for - of_clk_get_parent_count() < 0 - -The check for < 0 is impossible now that -of_clk_get_parent_count() returns an unsigned int. Simplify the -code and update the types. - -Acked-by: Felipe Balbi -Cc: -Signed-off-by: Stephen Boyd -(cherry picked from commit 3d755dcc20dd452b52532eca17da40ebbd12aee9) - -Change-Id: Iaa38e064d801fb36c855fea51c0443840368e0d3 -Signed-off-by: Nitheesh Sekar ---- - drivers/usb/dwc3/dwc3-of-simple.c | 9 +++++---- - 1 file changed, 5 insertions(+), 4 deletions(-) - ---- a/drivers/usb/dwc3/dwc3-of-simple.c -+++ b/drivers/usb/dwc3/dwc3-of-simple.c -@@ -42,6 +42,7 @@ static int dwc3_of_simple_probe(struct p - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - -+ unsigned int count; - int ret; - int i; - -@@ -49,11 +50,11 @@ static int dwc3_of_simple_probe(struct p - if (!simple) - return -ENOMEM; - -- ret = of_clk_get_parent_count(np); -- if (ret < 0) -- return ret; -+ count = of_clk_get_parent_count(np); -+ if (!count) -+ return -ENOENT; - -- simple->num_clocks = ret; -+ simple->num_clocks = count; - - simple->clks = devm_kcalloc(dev, simple->num_clocks, - sizeof(struct clk *), GFP_KERNEL); diff --git a/target/linux/ipq806x/patches-4.4/097-4-usb-dwc3-fix-missing-platform_set_drvdata-in-dwc3_of_simple_probe.patch b/target/linux/ipq806x/patches-4.4/097-4-usb-dwc3-fix-missing-platform_set_drvdata-in-dwc3_of_simple_probe.patch deleted file mode 100644 index 2d18e41514..0000000000 --- a/target/linux/ipq806x/patches-4.4/097-4-usb-dwc3-fix-missing-platform_set_drvdata-in-dwc3_of_simple_probe.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 4c4f106c032ff32b89c98a4d819e68e6e643c14e Mon Sep 17 00:00:00 2001 -From: Wei Yongjun -Date: Tue, 26 Jul 2016 14:47:00 +0000 -Subject: usb: dwc3: fix missing platform_set_drvdata() in - dwc3_of_simple_probe() - -Add missing platform_set_drvdata() in dwc3_of_simple_probe(), otherwise -calling platform_get_drvdata() in remove returns NULL. - -This is detected by Coccinelle semantic patch. - -Signed-off-by: Wei Yongjun -Signed-off-by: Felipe Balbi ---- - drivers/usb/dwc3/dwc3-of-simple.c | 1 + - 1 file changed, 1 insertion(+) - ---- a/drivers/usb/dwc3/dwc3-of-simple.c -+++ b/drivers/usb/dwc3/dwc3-of-simple.c -@@ -61,6 +61,7 @@ static int dwc3_of_simple_probe(struct p - if (!simple->clks) - return -ENOMEM; - -+ platform_set_drvdata(pdev, simple); - simple->dev = dev; - - for (i = 0; i < simple->num_clocks; i++) { diff --git a/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch deleted file mode 100644 index 0da1927b44..0000000000 --- a/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch +++ /dev/null @@ -1,512 +0,0 @@ ---- a/drivers/phy/Kconfig -+++ b/drivers/phy/Kconfig -@@ -390,4 +390,15 @@ config PHY_CYGNUS_PCIE - Enable this to support the Broadcom Cygnus PCIe PHY. - If unsure, say N. - -+config PHY_QCOM_DWC3 -+ tristate "QCOM DWC3 USB PHY support" -+ depends on ARCH_QCOM -+ depends on HAS_IOMEM -+ depends on OF -+ select GENERIC_PHY -+ help -+ This option enables support for the Synopsis PHYs present inside the -+ Qualcomm USB3.0 DWC3 controller. This driver supports both HS and SS -+ PHY controllers. -+ - endmenu ---- a/drivers/phy/Makefile -+++ b/drivers/phy/Makefile -@@ -48,3 +48,4 @@ obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1 - obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o - obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o - obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o -+obj-$(CONFIG_PHY_QCOM_DWC3) += phy-qcom-dwc3.o ---- /dev/null -+++ b/drivers/phy/phy-qcom-dwc3.c -@@ -0,0 +1,484 @@ -+/* Copyright (c) 2014-2015, Code Aurora Forum. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+* This program is distributed in the hope that it will be useful, -+* but WITHOUT ANY WARRANTY; without even the implied warranty of -+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+* GNU General Public License for more details. -+*/ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/** -+ * USB QSCRATCH Hardware registers -+ */ -+#define QSCRATCH_GENERAL_CFG (0x08) -+#define HSUSB_PHY_CTRL_REG (0x10) -+ -+/* PHY_CTRL_REG */ -+#define HSUSB_CTRL_DMSEHV_CLAMP BIT(24) -+#define HSUSB_CTRL_USB2_SUSPEND BIT(23) -+#define HSUSB_CTRL_UTMI_CLK_EN BIT(21) -+#define HSUSB_CTRL_UTMI_OTG_VBUS_VALID BIT(20) -+#define HSUSB_CTRL_USE_CLKCORE BIT(18) -+#define HSUSB_CTRL_DPSEHV_CLAMP BIT(17) -+#define HSUSB_CTRL_COMMONONN BIT(11) -+#define HSUSB_CTRL_ID_HV_CLAMP BIT(9) -+#define HSUSB_CTRL_OTGSESSVLD_CLAMP BIT(8) -+#define HSUSB_CTRL_CLAMP_EN BIT(7) -+#define HSUSB_CTRL_RETENABLEN BIT(1) -+#define HSUSB_CTRL_POR BIT(0) -+ -+/* QSCRATCH_GENERAL_CFG */ -+#define HSUSB_GCFG_XHCI_REV BIT(2) -+ -+/** -+ * USB QSCRATCH Hardware registers -+ */ -+#define SSUSB_PHY_CTRL_REG (0x00) -+#define SSUSB_PHY_PARAM_CTRL_1 (0x04) -+#define SSUSB_PHY_PARAM_CTRL_2 (0x08) -+#define CR_PROTOCOL_DATA_IN_REG (0x0c) -+#define CR_PROTOCOL_DATA_OUT_REG (0x10) -+#define CR_PROTOCOL_CAP_ADDR_REG (0x14) -+#define CR_PROTOCOL_CAP_DATA_REG (0x18) -+#define CR_PROTOCOL_READ_REG (0x1c) -+#define CR_PROTOCOL_WRITE_REG (0x20) -+ -+/* PHY_CTRL_REG */ -+#define SSUSB_CTRL_REF_USE_PAD BIT(28) -+#define SSUSB_CTRL_TEST_POWERDOWN BIT(27) -+#define SSUSB_CTRL_LANE0_PWR_PRESENT BIT(24) -+#define SSUSB_CTRL_SS_PHY_EN BIT(8) -+#define SSUSB_CTRL_SS_PHY_RESET BIT(7) -+ -+/* SSPHY control registers */ -+#define SSPHY_CTRL_RX_OVRD_IN_HI(lane) (0x1006 + 0x100 * lane) -+#define SSPHY_CTRL_TX_OVRD_DRV_LO(lane) (0x1002 + 0x100 * lane) -+ -+/* RX OVRD IN HI bits */ -+#define RX_OVRD_IN_HI_RX_RESET_OVRD BIT(13) -+#define RX_OVRD_IN_HI_RX_RX_RESET BIT(12) -+#define RX_OVRD_IN_HI_RX_EQ_OVRD BIT(11) -+#define RX_OVRD_IN_HI_RX_EQ_MASK 0x0700 -+#define RX_OVRD_IN_HI_RX_EQ_SHIFT 8 -+#define RX_OVRD_IN_HI_RX_EQ_EN_OVRD BIT(7) -+#define RX_OVRD_IN_HI_RX_EQ_EN BIT(6) -+#define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD BIT(5) -+#define RX_OVRD_IN_HI_RX_LOS_FILTER_MASK 0x0018 -+#define RX_OVRD_IN_HI_RX_RATE_OVRD BIT(2) -+#define RX_OVRD_IN_HI_RX_RATE_MASK 0x0003 -+ -+/* TX OVRD DRV LO register bits */ -+#define TX_OVRD_DRV_LO_AMPLITUDE_MASK 0x007F -+#define TX_OVRD_DRV_LO_PREEMPH_MASK 0x3F80 -+#define TX_OVRD_DRV_LO_PREEMPH_SHIFT 7 -+#define TX_OVRD_DRV_LO_EN BIT(14) -+ -+/* SS CAP register bits */ -+#define SS_CR_CAP_ADDR_REG BIT(0) -+#define SS_CR_CAP_DATA_REG BIT(0) -+#define SS_CR_READ_REG BIT(0) -+#define SS_CR_WRITE_REG BIT(0) -+ -+struct qcom_dwc3_usb_phy { -+ void __iomem *base; -+ struct device *dev; -+ struct clk *xo_clk; -+ struct clk *ref_clk; -+}; -+ -+struct qcom_dwc3_phy_drvdata { -+ struct phy_ops ops; -+ u32 clk_rate; -+}; -+ -+/** -+ * Write register and read back masked value to confirm it is written -+ * -+ * @base - QCOM DWC3 PHY base virtual address. -+ * @offset - register offset. -+ * @mask - register bitmask specifying what should be updated -+ * @val - value to write. -+ */ -+static inline void qcom_dwc3_phy_write_readback( -+ struct qcom_dwc3_usb_phy *phy_dwc3, u32 offset, -+ const u32 mask, u32 val) -+{ -+ u32 write_val, tmp = readl(phy_dwc3->base + offset); -+ -+ tmp &= ~mask; /* retain other bits */ -+ write_val = tmp | val; -+ -+ writel(write_val, phy_dwc3->base + offset); -+ -+ /* Read back to see if val was written */ -+ tmp = readl(phy_dwc3->base + offset); -+ tmp &= mask; /* clear other bits */ -+ -+ if (tmp != val) -+ dev_err(phy_dwc3->dev, "write: %x to QSCRATCH: %x FAILED\n", -+ val, offset); -+} -+ -+static int wait_for_latch(void __iomem *addr) -+{ -+ u32 retry = 10; -+ -+ while (true) { -+ if (!readl(addr)) -+ break; -+ -+ if (--retry == 0) -+ return -ETIMEDOUT; -+ -+ usleep_range(10, 20); -+ } -+ -+ return 0; -+} -+ -+/** -+ * Write SSPHY register -+ * -+ * @base - QCOM DWC3 PHY base virtual address. -+ * @addr - SSPHY address to write. -+ * @val - value to write. -+ */ -+static int qcom_dwc3_ss_write_phycreg(struct qcom_dwc3_usb_phy *phy_dwc3, -+ u32 addr, u32 val) -+{ -+ int ret; -+ -+ writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); -+ writel(SS_CR_CAP_ADDR_REG, phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); -+ -+ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); -+ if (ret) -+ goto err_wait; -+ -+ writel(val, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); -+ writel(SS_CR_CAP_DATA_REG, phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG); -+ -+ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG); -+ if (ret) -+ goto err_wait; -+ -+ writel(SS_CR_WRITE_REG, phy_dwc3->base + CR_PROTOCOL_WRITE_REG); -+ -+ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_WRITE_REG); -+ -+err_wait: -+ if (ret) -+ dev_err(phy_dwc3->dev, "timeout waiting for latch\n"); -+ return ret; -+} -+ -+/** -+ * Read SSPHY register. -+ * -+ * @base - QCOM DWC3 PHY base virtual address. -+ * @addr - SSPHY address to read. -+ */ -+static int qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr, u32 *val) -+{ -+ int ret; -+ -+ writel(addr, base + CR_PROTOCOL_DATA_IN_REG); -+ writel(SS_CR_CAP_ADDR_REG, base + CR_PROTOCOL_CAP_ADDR_REG); -+ -+ ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG); -+ if (ret) -+ goto err_wait; -+ -+ /* -+ * Due to hardware bug, first read of SSPHY register might be -+ * incorrect. Hence as workaround, SW should perform SSPHY register -+ * read twice, but use only second read and ignore first read. -+ */ -+ writel(SS_CR_READ_REG, base + CR_PROTOCOL_READ_REG); -+ -+ ret = wait_for_latch(base + CR_PROTOCOL_READ_REG); -+ if (ret) -+ goto err_wait; -+ -+ /* throwaway read */ -+ readl(base + CR_PROTOCOL_DATA_OUT_REG); -+ -+ writel(SS_CR_READ_REG, base + CR_PROTOCOL_READ_REG); -+ -+ ret = wait_for_latch(base + CR_PROTOCOL_READ_REG); -+ if (ret) -+ goto err_wait; -+ -+ *val = readl(base + CR_PROTOCOL_DATA_OUT_REG); -+ -+err_wait: -+ return ret; -+} -+ -+static int qcom_dwc3_phy_power_on(struct phy *phy) -+{ -+ int ret; -+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); -+ -+ ret = clk_prepare_enable(phy_dwc3->xo_clk); -+ if (ret) -+ return ret; -+ -+ ret = clk_prepare_enable(phy_dwc3->ref_clk); -+ if (ret) -+ clk_disable_unprepare(phy_dwc3->xo_clk); -+ -+ return ret; -+} -+ -+static int qcom_dwc3_phy_power_off(struct phy *phy) -+{ -+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); -+ -+ clk_disable_unprepare(phy_dwc3->ref_clk); -+ clk_disable_unprepare(phy_dwc3->xo_clk); -+ -+ return 0; -+} -+ -+static int qcom_dwc3_hs_phy_init(struct phy *phy) -+{ -+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); -+ u32 val; -+ -+ /* -+ * HSPHY Initialization: Enable UTMI clock, select 19.2MHz fsel -+ * enable clamping, and disable RETENTION (power-on default is ENABLED) -+ */ -+ val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP | -+ HSUSB_CTRL_RETENABLEN | HSUSB_CTRL_COMMONONN | -+ HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP | -+ HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_UTMI_OTG_VBUS_VALID | -+ HSUSB_CTRL_UTMI_CLK_EN | HSUSB_CTRL_CLAMP_EN | 0x70; -+ -+ /* use core clock if external reference is not present */ -+ if (!phy_dwc3->xo_clk) -+ val |= HSUSB_CTRL_USE_CLKCORE; -+ -+ writel(val, phy_dwc3->base + HSUSB_PHY_CTRL_REG); -+ usleep_range(2000, 2200); -+ -+ /* Disable (bypass) VBUS and ID filters */ -+ writel(HSUSB_GCFG_XHCI_REV, phy_dwc3->base + QSCRATCH_GENERAL_CFG); -+ -+ return 0; -+} -+ -+static int qcom_dwc3_ss_phy_init(struct phy *phy) -+{ -+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); -+ int ret; -+ u32 data = 0; -+ -+ /* reset phy */ -+ data = readl(phy_dwc3->base + SSUSB_PHY_CTRL_REG); -+ writel(data | SSUSB_CTRL_SS_PHY_RESET, -+ phy_dwc3->base + SSUSB_PHY_CTRL_REG); -+ usleep_range(2000, 2200); -+ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); -+ -+ /* clear REF_PAD if we don't have XO clk */ -+ if (!phy_dwc3->xo_clk) -+ data &= ~SSUSB_CTRL_REF_USE_PAD; -+ else -+ data |= SSUSB_CTRL_REF_USE_PAD; -+ -+ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); -+ -+ /* wait for ref clk to become stable, this can take up to 30ms */ -+ msleep(30); -+ -+ data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT; -+ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); -+ -+ /* -+ * Fix RX Equalization setting as follows -+ * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0 -+ * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1 -+ * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3 -+ * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1 -+ */ -+ ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base, -+ SSPHY_CTRL_RX_OVRD_IN_HI(0), &data); -+ if (ret) -+ goto err_phy_trans; -+ -+ data &= ~RX_OVRD_IN_HI_RX_EQ_EN; -+ data |= RX_OVRD_IN_HI_RX_EQ_EN_OVRD; -+ data &= ~RX_OVRD_IN_HI_RX_EQ_MASK; -+ data |= 0x3 << RX_OVRD_IN_HI_RX_EQ_SHIFT; -+ data |= RX_OVRD_IN_HI_RX_EQ_OVRD; -+ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3, -+ SSPHY_CTRL_RX_OVRD_IN_HI(0), data); -+ if (ret) -+ goto err_phy_trans; -+ -+ /* -+ * Set EQ and TX launch amplitudes as follows -+ * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22 -+ * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127 -+ * LANE0.TX_OVRD_DRV_LO.EN set to 1. -+ */ -+ ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base, -+ SSPHY_CTRL_TX_OVRD_DRV_LO(0), &data); -+ if (ret) -+ goto err_phy_trans; -+ -+ data &= ~TX_OVRD_DRV_LO_PREEMPH_MASK; -+ data |= 0x16 << TX_OVRD_DRV_LO_PREEMPH_SHIFT; -+ data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK; -+ data |= 0x7f; -+ data |= TX_OVRD_DRV_LO_EN; -+ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3, -+ SSPHY_CTRL_TX_OVRD_DRV_LO(0), data); -+ if (ret) -+ goto err_phy_trans; -+ -+ /* -+ * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows -+ * TX_FULL_SWING [26:20] amplitude to 127 -+ * TX_DEEMPH_3_5DB [13:8] to 22 -+ * LOS_BIAS [2:0] to 0x5 -+ */ -+ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1, -+ 0x07f03f07, 0x07f01605); -+ -+err_phy_trans: -+ return ret; -+} -+ -+static int qcom_dwc3_ss_phy_exit(struct phy *phy) -+{ -+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); -+ -+ /* Sequence to put SSPHY in low power state: -+ * 1. Clear REF_PHY_EN in PHY_CTRL_REG -+ * 2. Clear REF_USE_PAD in PHY_CTRL_REG -+ * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention -+ */ -+ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, -+ SSUSB_CTRL_SS_PHY_EN, 0x0); -+ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, -+ SSUSB_CTRL_REF_USE_PAD, 0x0); -+ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, -+ 0x0, SSUSB_CTRL_TEST_POWERDOWN); -+ -+ return 0; -+} -+ -+static const struct qcom_dwc3_phy_drvdata qcom_dwc3_hs_drvdata = { -+ .ops = { -+ .init = qcom_dwc3_hs_phy_init, -+ .power_on = qcom_dwc3_phy_power_on, -+ .power_off = qcom_dwc3_phy_power_off, -+ .owner = THIS_MODULE, -+ }, -+ .clk_rate = 60000000, -+}; -+ -+static const struct qcom_dwc3_phy_drvdata qcom_dwc3_ss_drvdata = { -+ .ops = { -+ .init = qcom_dwc3_ss_phy_init, -+ .exit = qcom_dwc3_ss_phy_exit, -+ .power_on = qcom_dwc3_phy_power_on, -+ .power_off = qcom_dwc3_phy_power_off, -+ .owner = THIS_MODULE, -+ }, -+ .clk_rate = 125000000, -+}; -+ -+static const struct of_device_id qcom_dwc3_phy_table[] = { -+ { .compatible = "qcom,dwc3-hs-usb-phy", .data = &qcom_dwc3_hs_drvdata }, -+ { .compatible = "qcom,dwc3-ss-usb-phy", .data = &qcom_dwc3_ss_drvdata }, -+ { /* Sentinel */ } -+}; -+MODULE_DEVICE_TABLE(of, qcom_dwc3_phy_table); -+ -+static int qcom_dwc3_phy_probe(struct platform_device *pdev) -+{ -+ struct qcom_dwc3_usb_phy *phy_dwc3; -+ struct phy_provider *phy_provider; -+ struct phy *generic_phy; -+ struct resource *res; -+ const struct of_device_id *match; -+ const struct qcom_dwc3_phy_drvdata *data; -+ -+ phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL); -+ if (!phy_dwc3) -+ return -ENOMEM; -+ -+ match = of_match_node(qcom_dwc3_phy_table, pdev->dev.of_node); -+ data = match->data; -+ -+ phy_dwc3->dev = &pdev->dev; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ phy_dwc3->base = devm_ioremap_resource(phy_dwc3->dev, res); -+ if (IS_ERR(phy_dwc3->base)) -+ return PTR_ERR(phy_dwc3->base); -+ -+ phy_dwc3->ref_clk = devm_clk_get(phy_dwc3->dev, "ref"); -+ if (IS_ERR(phy_dwc3->ref_clk)) { -+ dev_dbg(phy_dwc3->dev, "cannot get reference clock\n"); -+ return PTR_ERR(phy_dwc3->ref_clk); -+ } -+ -+ clk_set_rate(phy_dwc3->ref_clk, data->clk_rate); -+ -+ phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo"); -+ if (IS_ERR(phy_dwc3->xo_clk)) { -+ dev_dbg(phy_dwc3->dev, "cannot get TCXO clock\n"); -+ phy_dwc3->xo_clk = NULL; -+ } -+ -+ generic_phy = devm_phy_create(phy_dwc3->dev, pdev->dev.of_node, -+ &data->ops); -+ -+ if (IS_ERR(generic_phy)) -+ return PTR_ERR(generic_phy); -+ -+ phy_set_drvdata(generic_phy, phy_dwc3); -+ platform_set_drvdata(pdev, phy_dwc3); -+ -+ phy_provider = devm_of_phy_provider_register(phy_dwc3->dev, -+ of_phy_simple_xlate); -+ -+ if (IS_ERR(phy_provider)) -+ return PTR_ERR(phy_provider); -+ -+ return 0; -+} -+ -+static struct platform_driver qcom_dwc3_phy_driver = { -+ .probe = qcom_dwc3_phy_probe, -+ .driver = { -+ .name = "qcom-dwc3-usb-phy", -+ .owner = THIS_MODULE, -+ .of_match_table = qcom_dwc3_phy_table, -+ }, -+}; -+ -+module_platform_driver(qcom_dwc3_phy_driver); -+ -+MODULE_ALIAS("platform:phy-qcom-dwc3"); -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Andy Gross "); -+MODULE_AUTHOR("Ivan T. Ivanov "); -+MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver"); diff --git a/target/linux/ipq806x/patches-4.4/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch b/target/linux/ipq806x/patches-4.4/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch deleted file mode 100644 index d1f19c367e..0000000000 --- a/target/linux/ipq806x/patches-4.4/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch +++ /dev/null @@ -1,123 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -92,5 +92,29 @@ - sata@29000000 { - status = "ok"; - }; -+ -+ phy@100f8800 { /* USB3 port 1 HS phy */ -+ status = "ok"; -+ }; -+ -+ phy@100f8830 { /* USB3 port 1 SS phy */ -+ status = "ok"; -+ }; -+ -+ phy@110f8800 { /* USB3 port 0 HS phy */ -+ status = "ok"; -+ }; -+ -+ phy@110f8830 { /* USB3 port 0 SS phy */ -+ status = "ok"; -+ }; -+ -+ usb30@0 { -+ status = "ok"; -+ }; -+ -+ usb30@1 { -+ status = "ok"; -+ }; - }; - }; ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -333,6 +333,88 @@ - compatible = "syscon"; - reg = <0x01200600 0x100>; - }; -+ -+ hs_phy_1: phy@100f8800 { -+ compatible = "qcom,dwc3-hs-usb-phy"; -+ reg = <0x100f8800 0x30>; -+ clocks = <&gcc USB30_1_UTMI_CLK>; -+ clock-names = "ref"; -+ #phy-cells = <0>; -+ -+ status = "disabled"; -+ }; -+ -+ ss_phy_1: phy@100f8830 { -+ compatible = "qcom,dwc3-ss-usb-phy"; -+ reg = <0x100f8830 0x30>; -+ clocks = <&gcc USB30_1_MASTER_CLK>; -+ clock-names = "ref"; -+ #phy-cells = <0>; -+ -+ status = "disabled"; -+ }; -+ -+ hs_phy_0: phy@110f8800 { -+ compatible = "qcom,dwc3-hs-usb-phy"; -+ reg = <0x110f8800 0x30>; -+ clocks = <&gcc USB30_0_UTMI_CLK>; -+ clock-names = "ref"; -+ #phy-cells = <0>; -+ -+ status = "disabled"; -+ }; -+ -+ ss_phy_0: phy@110f8830 { -+ compatible = "qcom,dwc3-ss-usb-phy"; -+ reg = <0x110f8830 0x30>; -+ clocks = <&gcc USB30_0_MASTER_CLK>; -+ clock-names = "ref"; -+ #phy-cells = <0>; -+ -+ status = "disabled"; -+ }; -+ -+ usb3_0: usb30@0 { -+ compatible = "qcom,dwc3"; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ clocks = <&gcc USB30_0_MASTER_CLK>; -+ clock-names = "core"; -+ -+ ranges; -+ -+ status = "disabled"; -+ -+ dwc3@11000000 { -+ compatible = "snps,dwc3"; -+ reg = <0x11000000 0xcd00>; -+ interrupts = <0 110 0x4>; -+ phys = <&hs_phy_0>, <&ss_phy_0>; -+ phy-names = "usb2-phy", "usb3-phy"; -+ dr_mode = "host"; -+ }; -+ }; -+ -+ usb3_1: usb30@1 { -+ compatible = "qcom,dwc3"; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ clocks = <&gcc USB30_1_MASTER_CLK>; -+ clock-names = "core"; -+ -+ ranges; -+ -+ status = "disabled"; -+ -+ dwc3@10000000 { -+ compatible = "snps,dwc3"; -+ reg = <0x10000000 0xcd00>; -+ interrupts = <0 205 0x4>; -+ phys = <&hs_phy_1>, <&ss_phy_1>; -+ phy-names = "usb2-phy", "usb3-phy"; -+ dr_mode = "host"; -+ }; -+ }; - }; - - sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.4/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch b/target/linux/ipq806x/patches-4.4/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch deleted file mode 100644 index 41f91fae7c..0000000000 --- a/target/linux/ipq806x/patches-4.4/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch +++ /dev/null @@ -1,263 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v2,3/5] DT: PCI: qcom: Document PCIe devicetree bindings -From: Stanimir Varbanov -X-Patchwork-Id: 6326181 -Message-Id: <1430743338-10441-4-git-send-email-svarbanov@mm-sol.com> -To: Rob Herring , Kumar Gala , - Mark Rutland , - Grant Likely , - Bjorn Helgaas , - Kishon Vijay Abraham I , - Russell King , Arnd Bergmann -Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, - linux-pci@vger.kernel.org, Mathieu Olivari , - Srinivas Kandagatla , - Stanimir Varbanov -Date: Mon, 4 May 2015 15:42:16 +0300 - -Document Qualcomm PCIe driver devicetree bindings. - -Signed-off-by: Stanimir Varbanov - ---- -.../devicetree/bindings/pci/qcom,pcie.txt | 231 ++++++++++++++++++++ - 1 files changed, 231 insertions(+), 0 deletions(-) - create mode 100644 Documentation/devicetree/bindings/pci/qcom,pcie.txt - ---- /dev/null -+++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt -@@ -0,0 +1,231 @@ -+* Qualcomm PCI express root complex -+ -+- compatible: -+ Usage: required -+ Value type: -+ Definition: Value shall include -+ - "qcom,pcie-v0" for apq/ipq8064 -+ - "qcom,pcie-v1" for apq8084 -+ -+- reg: -+ Usage: required -+ Value type: -+ Definition: Register ranges as listed in the reg-names property -+ -+- reg-names: -+ Usage: required -+ Value type: -+ Definition: Must include the following entries -+ - "parf" Qualcomm specific registers -+ - "dbi" Designware PCIe registers -+ - "elbi" External local bus interface registers -+ - "config" PCIe configuration space -+ -+- device_type: -+ Usage: required -+ Value type: -+ Definition: Should be "pci". As specified in designware-pcie.txt -+ -+- #address-cells: -+ Usage: required -+ Value type: -+ Definition: Should be set to 3. As specified in designware-pcie.txt -+ -+- #size-cells: -+ Usage: required -+ Value type: -+ Definition: Should be set 2. As specified in designware-pcie.txt -+ -+- ranges: -+ Usage: required -+ Value type: -+ Definition: As specified in designware-pcie.txt -+ -+- interrupts: -+ Usage: required -+ Value type: -+ Definition: MSI interrupt -+ -+- interrupt-names: -+ Usage: required -+ Value type: -+ Definition: Should contain "msi" -+ -+- #interrupt-cells: -+ Usage: required -+ Value type: -+ Definition: Should be 1. As specified in designware-pcie.txt -+ -+- interrupt-map-mask: -+ Usage: required -+ Value type: -+ Definition: As specified in designware-pcie.txt -+ -+- interrupt-map: -+ Usage: required -+ Value type: -+ Definition: As specified in designware-pcie.txt -+ -+- clocks: -+ Usage: required -+ Value type: -+ Definition: List of phandle and clock specifier pairs as listed -+ in clock-names property -+ -+- clock-names: -+ Usage: required -+ Value type: -+ Definition: Should contain the following entries -+ * should be populated for v0 and v1 -+ - "iface" Configuration AHB clock -+ -+ * should be populated for v0 -+ - "core" Clocks the pcie hw block -+ - "phy" Clocks the pcie PHY block -+ -+ * should be populated for v1 -+ - "aux" Auxiliary (AUX) clock -+ - "bus_master" Master AXI clock -+ - "bus_slave" Slave AXI clock -+ -+- resets: -+ Usage: required -+ Value type: -+ Definition: List of phandle and reset specifier pairs as listed -+ in reset-names property -+ -+- reset-names: -+ Usage: required -+ Value type: -+ Definition: Should contain the following entries -+ * should be populated for v0 -+ - "axi" AXI reset -+ - "ahb" AHB reset -+ - "por" POR reset -+ - "pci" PCI reset -+ - "phy" PHY reset -+ -+ * should be populated for v1 -+ - "core" Core reset -+ -+- power-domains: -+ Usage: required (for v1 only) -+ Value type: -+ Definition: A phandle and power domain specifier pair to the -+ power domain which is responsible for collapsing -+ and restoring power to the peripheral -+ -+- -supply: -+ Usage: required -+ Value type: -+ Definition: List of phandles to the power supply regulator(s) -+ * should be populated for v0 and v1 -+ - "vdda" core analog power supply -+ -+ * should be populated for v0 -+ - "vdda_phy" analog power supply for PHY -+ - "vdda_refclk" analog power supply for IC which generate -+ reference clock -+ -+- phys: -+ Usage: required (for v1 only) -+ Value type: -+ Definition: List of phandle(s) as listed in phy-names property -+ -+- phy-names: -+ Usage: required (for v1 only) -+ Value type: -+ Definition: Should contain "pciephy" -+ -+- -gpio: -+ Usage: optional -+ Value type: -+ Definition: List of phandle and gpio specifier pairs. Should contain -+ - "perst" PCIe endpoint reset signal line -+ - "pewake" PCIe endpoint wake signal line -+ -+- pinctrl-0: -+ Usage: required -+ Value type: -+ Definition: List of phandles pointing at a pin(s) configuration -+ -+- pinctrl-names -+ Usage: required -+ Value type: -+ Definition: List of names of pinctrl-0 state -+ -+* Example for v0 -+ pcie0: pci@1b500000 { -+ compatible = "qcom,pcie-v0"; -+ reg = <0x1b500000 0x1000 -+ 0x1b502000 0x80 -+ 0x1b600000 0x100 -+ 0x0ff00000 0x100000>; -+ reg-names = "dbi", "elbi", "parf", "config"; -+ device_type = "pci"; -+ linux,pci-domain = <0>; -+ bus-range = <0x00 0xff>; -+ num-lanes = <1>; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ ranges = <0x81000000 0 0 0x0fe00000 0 0x00100000 /* I/O */ -+ 0x82000000 0 0x00000000 0x08000000 0 0x07e00000>; /* memory */ -+ interrupts = ; -+ interrupt-names = "msi"; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 0x7>; -+ interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ -+ <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ -+ <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ -+ <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ -+ clocks = <&gcc PCIE_A_CLK>, -+ <&gcc PCIE_H_CLK>, -+ <&gcc PCIE_PHY_CLK>; -+ clock-names = "core", "iface", "phy"; -+ resets = <&gcc PCIE_ACLK_RESET>, -+ <&gcc PCIE_HCLK_RESET>, -+ <&gcc PCIE_POR_RESET>, -+ <&gcc PCIE_PCI_RESET>, -+ <&gcc PCIE_PHY_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy"; -+ }; -+ -+* Example for v1 -+ pcie0@fc520000 { -+ compatible = "qcom,pcie-v1"; -+ reg = <0xfc520000 0x2000>, -+ <0xff000000 0x1000>, -+ <0xff001000 0x1000>, -+ <0xff002000 0x2000>; -+ reg-names = "parf", "dbi", "elbi", "config"; -+ device_type = "pci"; -+ linux,pci-domain = <0>; -+ bus-range = <0x00 0xff>; -+ num-lanes = <1>; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ ranges = <0x81000000 0 0 0xff200000 0 0x00100000 /* I/O */ -+ 0x82000000 0 0x00300000 0xff300000 0 0x00d00000>; /* memory */ -+ interrupts = ; -+ interrupt-names = "msi"; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 0x7>; -+ interrupt-map = <0 0 0 1 &intc 0 244 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ -+ <0 0 0 2 &intc 0 245 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ -+ <0 0 0 3 &intc 0 247 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ -+ <0 0 0 4 &intc 0 248 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ -+ clocks = <&gcc GCC_PCIE_0_CFG_AHB_CLK>, -+ <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, -+ <&gcc GCC_PCIE_0_SLV_AXI_CLK>, -+ <&gcc GCC_PCIE_0_AUX_CLK>; -+ clock-names = "iface", "master_bus", "slave_bus", "aux"; -+ resets = <&gcc GCC_PCIE_0_BCR>; -+ reset-names = "core"; -+ power-domains = <&gcc PCIE0_GDSC>; -+ vdda-supply = <&pma8084_l3>; -+ phys = <&pciephy0>; -+ phy-names = "pciephy"; -+ perst-gpio = <&tlmm 70 GPIO_ACTIVE_LOW>; -+ pinctrl-0 = <&pcie0_pins_default>; -+ pinctrl-names = "default"; -+ }; diff --git a/target/linux/ipq806x/patches-4.4/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch b/target/linux/ipq806x/patches-4.4/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch deleted file mode 100644 index b140787ac7..0000000000 --- a/target/linux/ipq806x/patches-4.4/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch +++ /dev/null @@ -1,752 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v2,4/5] PCI: qcom: Add Qualcomm PCIe controller driver -From: Stanimir Varbanov -X-Patchwork-Id: 6326161 -Message-Id: <1430743338-10441-5-git-send-email-svarbanov@mm-sol.com> -To: Rob Herring , Kumar Gala , - Mark Rutland , - Grant Likely , - Bjorn Helgaas , - Kishon Vijay Abraham I , - Russell King , Arnd Bergmann -Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, - linux-pci@vger.kernel.org, Mathieu Olivari , - Srinivas Kandagatla , - Stanimir Varbanov -Date: Mon, 4 May 2015 15:42:17 +0300 - -The PCIe driver reuse the Designware common code for host -and MSI initialization, and also program the Qualcomm -application specific registers. - -Signed-off-by: Stanimir Varbanov - ---- -MAINTAINERS | 7 + - drivers/pci/host/Kconfig | 9 + - drivers/pci/host/Makefile | 1 + - drivers/pci/host/pcie-qcom.c | 677 ++++++++++++++++++++++++++++++++++++++++++ - 4 files changed, 694 insertions(+), 0 deletions(-) - create mode 100644 drivers/pci/host/pcie-qcom.c - ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -8248,6 +8248,13 @@ S: Maintained - F: Documentation/devicetree/bindings/pci/hisilicon-pcie.txt - F: drivers/pci/host/pcie-hisi.c - -+PCIE DRIVER FOR QUALCOMM MSM -+M: Stanimir Varbanov -+L: linux-pci@vger.kernel.org -+L: linux-arm-msm@vger.kernel.org -+S: Maintained -+F: drivers/pci/host/*qcom* -+ - PCMCIA SUBSYSTEM - P: Linux PCMCIA Team - L: linux-pcmcia@lists.infradead.org ---- a/drivers/pci/host/Kconfig -+++ b/drivers/pci/host/Kconfig -@@ -173,4 +173,13 @@ config PCI_HISI - help - Say Y here if you want PCIe controller support on HiSilicon HIP05 SoC - -+config PCIE_QCOM -+ bool "Qualcomm PCIe controller" -+ depends on ARCH_QCOM && OF || (ARM && COMPILE_TEST) -+ select PCIE_DW -+ select PCIEPORTBUS -+ help -+ Say Y here to enable PCIe controller support on Qualcomm SoCs. The -+ PCIe controller use Designware core plus Qualcomm specific hardware -+ wrappers. - endmenu ---- /dev/null -+++ b/drivers/pci/host/pcie-qcom.c -@@ -0,0 +1,676 @@ -+/* -+ * Copyright (c) 2014, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "pcie-designware.h" -+ -+#define PCIE20_PARF_PHY_CTRL 0x40 -+#define PCIE20_PARF_PHY_REFCLK 0x4C -+#define PCIE20_PARF_DBI_BASE_ADDR 0x168 -+#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c -+#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 -+ -+#define PCIE20_ELBI_SYS_CTRL 0x04 -+#define PCIE20_ELBI_SYS_STTS 0x08 -+#define XMLH_LINK_UP BIT(10) -+ -+#define PCIE20_CAP 0x70 -+#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) -+ -+#define PERST_DELAY_MIN_US 1000 -+#define PERST_DELAY_MAX_US 1005 -+ -+#define LINKUP_DELAY_MIN_US 5000 -+#define LINKUP_DELAY_MAX_US 5100 -+#define LINKUP_RETRIES_COUNT 20 -+ -+#define PCIE_V0 0 /* apq8064 */ -+#define PCIE_V1 1 /* apq8084 */ -+ -+struct qcom_pcie_resources_v0 { -+ struct clk *iface_clk; -+ struct clk *core_clk; -+ struct clk *phy_clk; -+ struct reset_control *pci_reset; -+ struct reset_control *axi_reset; -+ struct reset_control *ahb_reset; -+ struct reset_control *por_reset; -+ struct reset_control *phy_reset; -+ struct regulator *vdda; -+ struct regulator *vdda_phy; -+ struct regulator *vdda_refclk; -+}; -+ -+struct qcom_pcie_resources_v1 { -+ struct clk *iface; -+ struct clk *aux; -+ struct clk *master_bus; -+ struct clk *slave_bus; -+ struct reset_control *core; -+ struct regulator *vdda; -+}; -+ -+union pcie_resources { -+ struct qcom_pcie_resources_v0 v0; -+ struct qcom_pcie_resources_v1 v1; -+}; -+ -+struct qcom_pcie { -+ struct pcie_port pp; -+ struct device *dev; -+ union pcie_resources res; -+ void __iomem *parf; -+ void __iomem *dbi; -+ void __iomem *elbi; -+ struct phy *phy; -+ struct gpio_desc *reset; -+ unsigned int version; -+}; -+ -+#define to_qcom_pcie(x) container_of(x, struct qcom_pcie, pp) -+ -+static inline void -+writel_masked(void __iomem *addr, u32 clear_mask, u32 set_mask) -+{ -+ u32 val = readl(addr); -+ -+ val &= ~clear_mask; -+ val |= set_mask; -+ writel(val, addr); -+} -+ -+static void qcom_ep_reset_assert_deassert(struct qcom_pcie *pcie, int assert) -+{ -+ int val, active_low; -+ -+ if (IS_ERR_OR_NULL(pcie->reset)) -+ return; -+ -+ active_low = gpiod_is_active_low(pcie->reset); -+ -+ if (assert) -+ val = !!active_low; -+ else -+ val = !active_low; -+ -+ gpiod_set_value(pcie->reset, val); -+ -+ usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US); -+} -+ -+static void qcom_ep_reset_assert(struct qcom_pcie *pcie) -+{ -+ qcom_ep_reset_assert_deassert(pcie, 1); -+} -+ -+static void qcom_ep_reset_deassert(struct qcom_pcie *pcie) -+{ -+ qcom_ep_reset_assert_deassert(pcie, 0); -+} -+ -+static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg) -+{ -+ struct pcie_port *pp = arg; -+ -+ return dw_handle_msi_irq(pp); -+} -+ -+static int qcom_pcie_link_up(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ u32 val = readl(pcie->dbi + PCIE20_CAP_LINKCTRLSTATUS); -+ -+ return val & BIT(29) ? 1 : 0; -+} -+ -+static void qcom_pcie_disable_resources_v0(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; -+ -+ reset_control_assert(res->pci_reset); -+ reset_control_assert(res->axi_reset); -+ reset_control_assert(res->ahb_reset); -+ reset_control_assert(res->por_reset); -+ reset_control_assert(res->pci_reset); -+ clk_disable_unprepare(res->iface_clk); -+ clk_disable_unprepare(res->core_clk); -+ clk_disable_unprepare(res->phy_clk); -+ regulator_disable(res->vdda); -+ regulator_disable(res->vdda_phy); -+ regulator_disable(res->vdda_refclk); -+} -+ -+static void qcom_pcie_disable_resources_v1(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; -+ -+ reset_control_assert(res->core); -+ clk_disable_unprepare(res->slave_bus); -+ clk_disable_unprepare(res->master_bus); -+ clk_disable_unprepare(res->iface); -+ clk_disable_unprepare(res->aux); -+ regulator_disable(res->vdda); -+} -+ -+static int qcom_pcie_enable_resources_v0(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; -+ struct device *dev = pcie->dev; -+ int ret; -+ -+ ret = regulator_enable(res->vdda); -+ if (ret) { -+ dev_err(dev, "cannot enable vdda regulator\n"); -+ return ret; -+ } -+ -+ ret = regulator_enable(res->vdda_refclk); -+ if (ret) { -+ dev_err(dev, "cannot enable vdda_refclk regulator\n"); -+ goto err_refclk; -+ } -+ -+ ret = regulator_enable(res->vdda_phy); -+ if (ret) { -+ dev_err(dev, "cannot enable vdda_phy regulator\n"); -+ goto err_vdda_phy; -+ } -+ -+ ret = clk_prepare_enable(res->iface_clk); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable iface clock\n"); -+ goto err_iface; -+ } -+ -+ ret = clk_prepare_enable(res->core_clk); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable core clock\n"); -+ goto err_clk_core; -+ } -+ -+ ret = clk_prepare_enable(res->phy_clk); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable phy clock\n"); -+ goto err_clk_phy; -+ } -+ -+ ret = reset_control_deassert(res->ahb_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert ahb reset\n"); -+ goto err_reset_ahb; -+ } -+ -+ return 0; -+ -+err_reset_ahb: -+ clk_disable_unprepare(res->phy_clk); -+err_clk_phy: -+ clk_disable_unprepare(res->core_clk); -+err_clk_core: -+ clk_disable_unprepare(res->iface_clk); -+err_iface: -+ regulator_disable(res->vdda_phy); -+err_vdda_phy: -+ regulator_disable(res->vdda_refclk); -+err_refclk: -+ regulator_disable(res->vdda); -+ return ret; -+} -+ -+static int qcom_pcie_enable_resources_v1(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; -+ struct device *dev = pcie->dev; -+ int ret; -+ -+ ret = reset_control_deassert(res->core); -+ if (ret) { -+ dev_err(dev, "cannot deassert core reset\n"); -+ return ret; -+ } -+ -+ ret = clk_prepare_enable(res->aux); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable aux clock\n"); -+ goto err_res; -+ } -+ -+ ret = clk_prepare_enable(res->iface); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable iface clock\n"); -+ goto err_aux; -+ } -+ -+ ret = clk_prepare_enable(res->master_bus); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable master_bus clock\n"); -+ goto err_iface; -+ } -+ -+ ret = clk_prepare_enable(res->slave_bus); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable slave_bus clock\n"); -+ goto err_master; -+ } -+ -+ ret = regulator_enable(res->vdda); -+ if (ret) { -+ dev_err(dev, "cannot enable vdda regulator\n"); -+ goto err_slave; -+ } -+ -+ return 0; -+ -+err_slave: -+ clk_disable_unprepare(res->slave_bus); -+err_master: -+ clk_disable_unprepare(res->master_bus); -+err_iface: -+ clk_disable_unprepare(res->iface); -+err_aux: -+ clk_disable_unprepare(res->aux); -+err_res: -+ reset_control_assert(res->core); -+ -+ return ret; -+} -+ -+static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; -+ struct device *dev = pcie->dev; -+ -+ res->vdda = devm_regulator_get(dev, "vdda"); -+ if (IS_ERR(res->vdda)) -+ return PTR_ERR(res->vdda); -+ -+ res->vdda_phy = devm_regulator_get(dev, "vdda_phy"); -+ if (IS_ERR(res->vdda_phy)) -+ return PTR_ERR(res->vdda_phy); -+ -+ res->vdda_refclk = devm_regulator_get(dev, "vdda_refclk"); -+ if (IS_ERR(res->vdda_refclk)) -+ return PTR_ERR(res->vdda_refclk); -+ -+ res->iface_clk = devm_clk_get(dev, "iface"); -+ if (IS_ERR(res->iface_clk)) -+ return PTR_ERR(res->iface_clk); -+ -+ res->core_clk = devm_clk_get(dev, "core"); -+ if (IS_ERR(res->core_clk)) -+ return PTR_ERR(res->core_clk); -+ -+ res->phy_clk = devm_clk_get(dev, "phy"); -+ if (IS_ERR(res->phy_clk)) -+ return PTR_ERR(res->phy_clk); -+ -+ res->pci_reset = devm_reset_control_get(dev, "pci"); -+ if (IS_ERR(res->pci_reset)) -+ return PTR_ERR(res->pci_reset); -+ -+ res->axi_reset = devm_reset_control_get(dev, "axi"); -+ if (IS_ERR(res->axi_reset)) -+ return PTR_ERR(res->axi_reset); -+ -+ res->ahb_reset = devm_reset_control_get(dev, "ahb"); -+ if (IS_ERR(res->ahb_reset)) -+ return PTR_ERR(res->ahb_reset); -+ -+ res->por_reset = devm_reset_control_get(dev, "por"); -+ if (IS_ERR(res->por_reset)) -+ return PTR_ERR(res->por_reset); -+ -+ res->phy_reset = devm_reset_control_get(dev, "phy"); -+ if (IS_ERR(res->phy_reset)) -+ return PTR_ERR(res->phy_reset); -+ -+ return 0; -+} -+ -+static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; -+ struct device *dev = pcie->dev; -+ -+ res->vdda = devm_regulator_get(dev, "vdda"); -+ if (IS_ERR(res->vdda)) -+ return PTR_ERR(res->vdda); -+ -+ res->iface = devm_clk_get(dev, "iface"); -+ if (IS_ERR(res->iface)) -+ return PTR_ERR(res->iface); -+ -+ res->aux = devm_clk_get(dev, "aux"); -+ if (IS_ERR(res->aux) && PTR_ERR(res->aux) == -EPROBE_DEFER) -+ return -EPROBE_DEFER; -+ else if (IS_ERR(res->aux)) -+ res->aux = NULL; -+ -+ res->master_bus = devm_clk_get(dev, "master_bus"); -+ if (IS_ERR(res->master_bus)) -+ return PTR_ERR(res->master_bus); -+ -+ res->slave_bus = devm_clk_get(dev, "slave_bus"); -+ if (IS_ERR(res->slave_bus)) -+ return PTR_ERR(res->slave_bus); -+ -+ res->core = devm_reset_control_get(dev, "core"); -+ if (IS_ERR(res->core)) -+ return PTR_ERR(res->core); -+ -+ return 0; -+} -+ -+static int qcom_pcie_enable_link_training(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ struct device *dev = pp->dev; -+ int retries; -+ u32 val; -+ -+ /* enable link training */ -+ writel_masked(pcie->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0)); -+ -+ /* wait for up to 100ms for the link to come up */ -+ retries = LINKUP_RETRIES_COUNT; -+ do { -+ val = readl(pcie->elbi + PCIE20_ELBI_SYS_STTS); -+ if (val & XMLH_LINK_UP) -+ break; -+ usleep_range(LINKUP_DELAY_MIN_US, LINKUP_DELAY_MAX_US); -+ } while (retries--); -+ -+ if (retries < 0 || !dw_pcie_link_up(pp)) { -+ dev_err(dev, "link initialization failed\n"); -+ return -ENXIO; -+ } -+ -+ return 0; -+} -+ -+static void qcom_pcie_host_init_v1(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ int ret; -+ -+ qcom_ep_reset_assert(pcie); -+ -+ ret = qcom_pcie_enable_resources_v1(pcie); -+ if (ret) -+ return; -+ -+ /* change DBI base address */ -+ writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); -+ -+ if (IS_ENABLED(CONFIG_PCI_MSI)) -+ writel_masked(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT, -+ 0, BIT(31)); -+ -+ ret = phy_init(pcie->phy); -+ if (ret) -+ goto err_res; -+ -+ ret = phy_power_on(pcie->phy); -+ if (ret) -+ goto err_phy; -+ -+ dw_pcie_setup_rc(pp); -+ -+ if (IS_ENABLED(CONFIG_PCI_MSI)) -+ dw_pcie_msi_init(pp); -+ -+ qcom_ep_reset_deassert(pcie); -+ -+ ret = qcom_pcie_enable_link_training(pp); -+ if (ret) -+ goto err; -+ -+ return; -+ -+err: -+ qcom_ep_reset_assert(pcie); -+ phy_power_off(pcie->phy); -+err_phy: -+ phy_exit(pcie->phy); -+err_res: -+ qcom_pcie_disable_resources_v1(pcie); -+} -+ -+static void qcom_pcie_host_init_v0(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; -+ struct device *dev = pcie->dev; -+ int ret; -+ -+ qcom_ep_reset_assert(pcie); -+ -+ ret = qcom_pcie_enable_resources_v0(pcie); -+ if (ret) -+ return; -+ -+ writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0); -+ -+ /* enable external reference clock */ -+ writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16)); -+ -+ ret = reset_control_deassert(res->phy_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert phy reset\n"); -+ return; -+ } -+ -+ ret = reset_control_deassert(res->pci_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert pci reset\n"); -+ return; -+ } -+ -+ ret = reset_control_deassert(res->por_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert por reset\n"); -+ return; -+ } -+ -+ ret = reset_control_deassert(res->axi_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert axi reset\n"); -+ return; -+ } -+ -+ /* wait 150ms for clock acquisition */ -+ usleep_range(10000, 15000); -+ -+ dw_pcie_setup_rc(pp); -+ -+ if (IS_ENABLED(CONFIG_PCI_MSI)) -+ dw_pcie_msi_init(pp); -+ -+ qcom_ep_reset_deassert(pcie); -+ -+ ret = qcom_pcie_enable_link_training(pp); -+ if (ret) -+ goto err; -+ -+ return; -+err: -+ qcom_ep_reset_assert(pcie); -+ qcom_pcie_disable_resources_v0(pcie); -+} -+ -+static void qcom_pcie_host_init(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ -+ if (pcie->version == PCIE_V0) -+ return qcom_pcie_host_init_v0(pp); -+ else -+ return qcom_pcie_host_init_v1(pp); -+} -+ -+static int -+qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val) -+{ -+ /* the device class is not reported correctly from the register */ -+ if (where == PCI_CLASS_REVISION && size == 4) { -+ *val = readl(pp->dbi_base + PCI_CLASS_REVISION); -+ *val &= ~(0xffff << 16); -+ *val |= PCI_CLASS_BRIDGE_PCI << 16; -+ return PCIBIOS_SUCCESSFUL; -+ } -+ -+ return dw_pcie_cfg_read(pp->dbi_base + where, size, val); -+} -+ -+static struct pcie_host_ops qcom_pcie_ops = { -+ .link_up = qcom_pcie_link_up, -+ .host_init = qcom_pcie_host_init, -+ .rd_own_conf = qcom_pcie_rd_own_conf, -+}; -+ -+static const struct of_device_id qcom_pcie_match[] = { -+ { .compatible = "qcom,pcie-v0", .data = (void *)PCIE_V0 }, -+ { .compatible = "qcom,pcie-v1", .data = (void *)PCIE_V1 }, -+ { } -+}; -+ -+static int qcom_pcie_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ const struct of_device_id *match; -+ struct resource *res; -+ struct qcom_pcie *pcie; -+ struct pcie_port *pp; -+ int ret; -+ -+ match = of_match_node(qcom_pcie_match, dev->of_node); -+ if (!match) -+ return -ENXIO; -+ -+ pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); -+ if (!pcie) -+ return -ENOMEM; -+ -+ pcie->version = (unsigned int)match->data; -+ -+ pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_LOW); -+ if (IS_ERR(pcie->reset) && PTR_ERR(pcie->reset) == -EPROBE_DEFER) -+ return PTR_ERR(pcie->reset); -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf"); -+ pcie->parf = devm_ioremap_resource(dev, res); -+ if (IS_ERR(pcie->parf)) -+ return PTR_ERR(pcie->parf); -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); -+ pcie->dbi = devm_ioremap_resource(dev, res); -+ if (IS_ERR(pcie->dbi)) -+ return PTR_ERR(pcie->dbi); -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); -+ pcie->elbi = devm_ioremap_resource(dev, res); -+ if (IS_ERR(pcie->elbi)) -+ return PTR_ERR(pcie->elbi); -+ -+ pcie->phy = devm_phy_optional_get(dev, "pciephy"); -+ if (IS_ERR(pcie->phy)) -+ return PTR_ERR(pcie->phy); -+ -+ pcie->dev = dev; -+ -+ if (pcie->version == PCIE_V0) -+ ret = qcom_pcie_get_resources_v0(pcie); -+ else -+ ret = qcom_pcie_get_resources_v1(pcie); -+ -+ if (ret) -+ return ret; -+ -+ pp = &pcie->pp; -+ pp->dev = dev; -+ pp->dbi_base = pcie->dbi; -+ pp->root_bus_nr = -1; -+ pp->ops = &qcom_pcie_ops; -+ -+ if (IS_ENABLED(CONFIG_PCI_MSI)) { -+ pp->msi_irq = platform_get_irq_byname(pdev, "msi"); -+ if (pp->msi_irq < 0) { -+ dev_err(dev, "cannot get msi irq\n"); -+ return pp->msi_irq; -+ } -+ -+ ret = devm_request_irq(dev, pp->msi_irq, -+ qcom_pcie_msi_irq_handler, -+ IRQF_SHARED, "qcom-pcie-msi", pp); -+ if (ret) { -+ dev_err(dev, "cannot request msi irq\n"); -+ return ret; -+ } -+ } -+ -+ ret = dw_pcie_host_init(pp); -+ if (ret) { -+ dev_err(dev, "cannot initialize host\n"); -+ return ret; -+ } -+ -+ platform_set_drvdata(pdev, pcie); -+ -+ return 0; -+} -+ -+static int qcom_pcie_remove(struct platform_device *pdev) -+{ -+ struct qcom_pcie *pcie = platform_get_drvdata(pdev); -+ -+ qcom_ep_reset_assert(pcie); -+ phy_power_off(pcie->phy); -+ phy_exit(pcie->phy); -+ if (pcie->version == PCIE_V0) -+ qcom_pcie_disable_resources_v0(pcie); -+ else -+ qcom_pcie_disable_resources_v1(pcie); -+ -+ return 0; -+} -+ -+static struct platform_driver qcom_pcie_driver = { -+ .probe = qcom_pcie_probe, -+ .remove = qcom_pcie_remove, -+ .driver = { -+ .name = "qcom-pcie", -+ .of_match_table = qcom_pcie_match, -+ }, -+}; -+ -+module_platform_driver(qcom_pcie_driver); -+ -+MODULE_AUTHOR("Stanimir Varbanov "); -+MODULE_DESCRIPTION("Qualcomm PCIe root complex driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:qcom-pcie"); ---- a/drivers/pci/host/Makefile -+++ b/drivers/pci/host/Makefile -@@ -20,3 +20,4 @@ obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-ip - obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o - obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o - obj-$(CONFIG_PCI_HISI) += pcie-hisi.o -+obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o diff --git a/target/linux/ipq806x/patches-4.4/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.4/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch deleted file mode 100644 index 90631c6694..0000000000 --- a/target/linux/ipq806x/patches-4.4/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch +++ /dev/null @@ -1,245 +0,0 @@ -From 5b40516b2f5fb9b2a7d6d3e2e924f12ec9d183a8 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Tue, 21 Apr 2015 19:01:42 -0700 -Subject: [PATCH 8/9] ARM: dts: qcom: add pcie nodes to ipq806x platforms - -qcom-pcie driver now supports version 0 of the controller. This change -adds the corresponding entries to the IPQ806x dtsi file and -corresponding platform (AP148). - -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 30 ++++++++ - arch/arm/boot/dts/qcom-ipq8064.dtsi | 124 +++++++++++++++++++++++++++++++ - 2 files changed, 154 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -116,5 +116,15 @@ - usb30@1 { - status = "ok"; - }; -+ -+ pcie0: pci@1b500000 { -+ status = "ok"; -+ phy-tx0-term-offset = <7>; -+ }; -+ -+ pcie1: pci@1b700000 { -+ status = "ok"; -+ phy-tx0-term-offset = <7>; -+ }; - }; - }; ---- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts -@@ -128,5 +128,17 @@ - usb30@1 { - status = "ok"; - }; -+ -+ pcie0: pci@1b500000 { -+ status = "ok"; -+ }; -+ -+ pcie1: pci@1b700000 { -+ status = "ok"; -+ }; -+ -+ pcie2: pci@1b900000 { -+ status = "ok"; -+ }; - }; - }; ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -4,6 +4,9 @@ - #include - #include - #include -+#include -+#include -+#include - - / { - model = "Qualcomm IPQ8064"; -@@ -99,6 +102,34 @@ - interrupt-controller; - #interrupt-cells = <2>; - interrupts = <0 16 0x4>; -+ -+ pcie0_pins: pcie0_pinmux { -+ mux { -+ pins = "gpio3"; -+ function = "pcie1_rst"; -+ drive-strength = <2>; -+ bias-disable; -+ }; -+ }; -+ -+ pcie1_pins: pcie1_pinmux { -+ mux { -+ pins = "gpio48"; -+ function = "pcie2_rst"; -+ drive-strength = <2>; -+ bias-disable; -+ }; -+ }; -+ -+ pcie2_pins: pcie2_pinmux { -+ mux { -+ pins = "gpio63"; -+ function = "pcie3_rst"; -+ drive-strength = <2>; -+ bias-disable; -+ output-low; -+ }; -+ }; - }; - - intc: interrupt-controller@2000000 { -@@ -415,6 +446,144 @@ - dr_mode = "host"; - }; - }; -+ -+ pcie0: pci@1b500000 { -+ compatible = "qcom,pcie-v0"; -+ reg = <0x1b500000 0x1000 -+ 0x1b502000 0x80 -+ 0x1b600000 0x100 -+ 0x0ff00000 0x100000>; -+ reg-names = "dbi", "elbi", "parf", "config"; -+ device_type = "pci"; -+ linux,pci-domain = <0>; -+ bus-range = <0x00 0xff>; -+ num-lanes = <1>; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ -+ ranges = <0x81000000 0 0x0fe00000 0x0fe00000 0 0x00100000 /* downstream I/O */ -+ 0x82000000 0 0x08000000 0x08000000 0 0x07e00000>; /* non-prefetchable memory */ -+ -+ interrupts = ; -+ interrupt-names = "msi"; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 0x7>; -+ interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ -+ <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ -+ <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ -+ <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ -+ -+ clocks = <&gcc PCIE_A_CLK>, -+ <&gcc PCIE_H_CLK>, -+ <&gcc PCIE_PHY_CLK>; -+ clock-names = "core", "iface", "phy"; -+ -+ resets = <&gcc PCIE_ACLK_RESET>, -+ <&gcc PCIE_HCLK_RESET>, -+ <&gcc PCIE_POR_RESET>, -+ <&gcc PCIE_PCI_RESET>, -+ <&gcc PCIE_PHY_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy"; -+ -+ pinctrl-0 = <&pcie0_pins>; -+ pinctrl-names = "default"; -+ -+ perst-gpios = <&qcom_pinmux 3 GPIO_ACTIVE_LOW>; -+ -+ status = "disabled"; -+ }; -+ -+ pcie1: pci@1b700000 { -+ compatible = "qcom,pcie-v0"; -+ reg = <0x1b700000 0x1000 -+ 0x1b702000 0x80 -+ 0x1b800000 0x100 -+ 0x31f00000 0x100000>; -+ reg-names = "dbi", "elbi", "parf", "config"; -+ device_type = "pci"; -+ linux,pci-domain = <1>; -+ bus-range = <0x00 0xff>; -+ num-lanes = <1>; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ -+ ranges = <0x81000000 0 0x31e00000 0x31e00000 0 0x00100000 /* downstream I/O */ -+ 0x82000000 0 0x2e000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */ -+ -+ interrupts = ; -+ interrupt-names = "msi"; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 0x7>; -+ interrupt-map = <0 0 0 1 &intc 0 58 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ -+ <0 0 0 2 &intc 0 59 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ -+ <0 0 0 3 &intc 0 60 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ -+ <0 0 0 4 &intc 0 61 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ -+ -+ clocks = <&gcc PCIE_1_A_CLK>, -+ <&gcc PCIE_1_H_CLK>, -+ <&gcc PCIE_1_PHY_CLK>; -+ clock-names = "core", "iface", "phy"; -+ -+ resets = <&gcc PCIE_1_ACLK_RESET>, -+ <&gcc PCIE_1_HCLK_RESET>, -+ <&gcc PCIE_1_POR_RESET>, -+ <&gcc PCIE_1_PCI_RESET>, -+ <&gcc PCIE_1_PHY_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy"; -+ -+ pinctrl-0 = <&pcie1_pins>; -+ pinctrl-names = "default"; -+ -+ perst-gpios = <&qcom_pinmux 48 GPIO_ACTIVE_LOW>; -+ -+ status = "disabled"; -+ }; -+ -+ pcie2: pci@1b900000 { -+ compatible = "qcom,pcie-v0"; -+ reg = <0x1b900000 0x1000 -+ 0x1b902000 0x80 -+ 0x1ba00000 0x100 -+ 0x35f00000 0x100000>; -+ reg-names = "dbi", "elbi", "parf", "config"; -+ device_type = "pci"; -+ linux,pci-domain = <2>; -+ bus-range = <0x00 0xff>; -+ num-lanes = <1>; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ -+ ranges = <0x81000000 0 0x35e00000 0x35e00000 0 0x00100000 /* downstream I/O */ -+ 0x82000000 0 0x32000000 0x32000000 0 0x03e00000>; /* non-prefetchable memory */ -+ -+ interrupts = ; -+ interrupt-names = "msi"; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 0x7>; -+ interrupt-map = <0 0 0 1 &intc 0 72 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ -+ <0 0 0 2 &intc 0 73 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ -+ <0 0 0 3 &intc 0 74 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ -+ <0 0 0 4 &intc 0 75 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ -+ -+ clocks = <&gcc PCIE_2_A_CLK>, -+ <&gcc PCIE_2_H_CLK>, -+ <&gcc PCIE_2_PHY_CLK>; -+ clock-names = "core", "iface", "phy"; -+ -+ resets = <&gcc PCIE_2_ACLK_RESET>, -+ <&gcc PCIE_2_HCLK_RESET>, -+ <&gcc PCIE_2_POR_RESET>, -+ <&gcc PCIE_2_PCI_RESET>, -+ <&gcc PCIE_2_PHY_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy"; -+ -+ pinctrl-0 = <&pcie2_pins>; -+ pinctrl-names = "default"; -+ -+ perst-gpios = <&qcom_pinmux 63 GPIO_ACTIVE_LOW>; -+ -+ status = "disabled"; -+ }; - }; - - sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.4/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch b/target/linux/ipq806x/patches-4.4/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch deleted file mode 100644 index e2d31354ed..0000000000 --- a/target/linux/ipq806x/patches-4.4/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch +++ /dev/null @@ -1,29 +0,0 @@ -From f004aa1dec6e2e206be025de15b115d60f2b21e3 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Tue, 21 Apr 2015 19:09:07 -0700 -Subject: [PATCH 9/9] ARM: qcom: automatically select PCI_DOMAINS if PCI is - enabled - -If multiple PCIe devices are present in the system, the kernel will -panic at boot time when trying to scan the PCI buses. This happens on -IPQ806x based platforms, which has 3 PCIe ports. - -Enabling this option allows the kernel to assign the pci-domains -according to the device-tree content. This allows multiple PCIe -controllers to coexist in the system. - -Signed-off-by: Mathieu Olivari ---- - arch/arm/mach-qcom/Kconfig | 1 + - 1 file changed, 1 insertion(+) - ---- a/arch/arm/mach-qcom/Kconfig -+++ b/arch/arm/mach-qcom/Kconfig -@@ -5,6 +5,7 @@ menuconfig ARCH_QCOM - select ARM_AMBA - select PINCTRL - select QCOM_SCM if SMP -+ select PCI_DOMAINS if PCI - help - Support for Qualcomm's devicetree based systems. - diff --git a/target/linux/ipq806x/patches-4.4/114-pcie-add-ctlr-init.patch b/target/linux/ipq806x/patches-4.4/114-pcie-add-ctlr-init.patch deleted file mode 100644 index 6328113c06..0000000000 --- a/target/linux/ipq806x/patches-4.4/114-pcie-add-ctlr-init.patch +++ /dev/null @@ -1,311 +0,0 @@ ---- a/drivers/pci/host/pcie-qcom.c -+++ b/drivers/pci/host/pcie-qcom.c -@@ -29,8 +29,53 @@ - - #include "pcie-designware.h" - -+/* DBI registers */ -+#define PCIE20_CAP 0x70 -+#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) -+ -+#define PCIE20_AXI_MSTR_RESP_COMP_CTRL0 0x818 -+#define PCIE20_AXI_MSTR_RESP_COMP_CTRL1 0x81c -+ -+#define PCIE20_PLR_IATU_VIEWPORT 0x900 -+#define PCIE20_PLR_IATU_REGION_OUTBOUND (0x0 << 31) -+#define PCIE20_PLR_IATU_REGION_INDEX(x) (x << 0) -+ -+#define PCIE20_PLR_IATU_CTRL1 0x904 -+#define PCIE20_PLR_IATU_TYPE_CFG0 (0x4 << 0) -+#define PCIE20_PLR_IATU_TYPE_MEM (0x0 << 0) -+ -+#define PCIE20_PLR_IATU_CTRL2 0x908 -+#define PCIE20_PLR_IATU_ENABLE BIT(31) -+ -+#define PCIE20_PLR_IATU_LBAR 0x90C -+#define PCIE20_PLR_IATU_UBAR 0x910 -+#define PCIE20_PLR_IATU_LAR 0x914 -+#define PCIE20_PLR_IATU_LTAR 0x918 -+#define PCIE20_PLR_IATU_UTAR 0x91c -+ -+#define MSM_PCIE_DEV_CFG_ADDR 0x01000000 -+ -+/* PARF registers */ -+#define PCIE20_PARF_PCS_DEEMPH 0x34 -+#define PCS_DEEMPH_TX_DEEMPH_GEN1(x) (x << 16) -+#define PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x) (x << 8) -+#define PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x) (x << 0) -+ -+#define PCIE20_PARF_PCS_SWING 0x38 -+#define PCS_SWING_TX_SWING_FULL(x) (x << 8) -+#define PCS_SWING_TX_SWING_LOW(x) (x << 0) -+ - #define PCIE20_PARF_PHY_CTRL 0x40 -+#define PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK (0x1f << 16) -+#define PHY_CTRL_PHY_TX0_TERM_OFFSET(x) (x << 16) -+ - #define PCIE20_PARF_PHY_REFCLK 0x4C -+#define REF_SSP_EN BIT(16) -+#define REF_USE_PAD BIT(12) -+ -+#define PCIE20_PARF_CONFIG_BITS 0x50 -+#define PHY_RX0_EQ(x) (x << 24) -+ - #define PCIE20_PARF_DBI_BASE_ADDR 0x168 - #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c - #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 -@@ -39,9 +84,6 @@ - #define PCIE20_ELBI_SYS_STTS 0x08 - #define XMLH_LINK_UP BIT(10) - --#define PCIE20_CAP 0x70 --#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) -- - #define PERST_DELAY_MIN_US 1000 - #define PERST_DELAY_MAX_US 1005 - -@@ -56,14 +98,18 @@ struct qcom_pcie_resources_v0 { - struct clk *iface_clk; - struct clk *core_clk; - struct clk *phy_clk; -+ struct clk *aux_clk; -+ struct clk *ref_clk; - struct reset_control *pci_reset; - struct reset_control *axi_reset; - struct reset_control *ahb_reset; - struct reset_control *por_reset; - struct reset_control *phy_reset; -+ struct reset_control *ext_reset; - struct regulator *vdda; - struct regulator *vdda_phy; - struct regulator *vdda_refclk; -+ uint8_t phy_tx0_term_offset; - }; - - struct qcom_pcie_resources_v1 { -@@ -106,20 +152,10 @@ writel_masked(void __iomem *addr, u32 cl - - static void qcom_ep_reset_assert_deassert(struct qcom_pcie *pcie, int assert) - { -- int val, active_low; -- - if (IS_ERR_OR_NULL(pcie->reset)) - return; - -- active_low = gpiod_is_active_low(pcie->reset); -- -- if (assert) -- val = !!active_low; -- else -- val = !active_low; -- -- gpiod_set_value(pcie->reset, val); -- -+ gpiod_set_value(pcie->reset, assert); - usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US); - } - -@@ -156,10 +192,13 @@ static void qcom_pcie_disable_resources_ - reset_control_assert(res->axi_reset); - reset_control_assert(res->ahb_reset); - reset_control_assert(res->por_reset); -- reset_control_assert(res->pci_reset); -+ reset_control_assert(res->phy_reset); -+ reset_control_assert(res->ext_reset); - clk_disable_unprepare(res->iface_clk); - clk_disable_unprepare(res->core_clk); - clk_disable_unprepare(res->phy_clk); -+ clk_disable_unprepare(res->aux_clk); -+ clk_disable_unprepare(res->ref_clk); - regulator_disable(res->vdda); - regulator_disable(res->vdda_phy); - regulator_disable(res->vdda_refclk); -@@ -201,6 +240,12 @@ static int qcom_pcie_enable_resources_v0 - goto err_vdda_phy; - } - -+ ret = reset_control_deassert(res->ext_reset); -+ if (ret) { -+ dev_err(dev, "cannot assert ext reset\n"); -+ goto err_reset_ext; -+ } -+ - ret = clk_prepare_enable(res->iface_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable iface clock\n"); -@@ -219,21 +264,40 @@ static int qcom_pcie_enable_resources_v0 - goto err_clk_phy; - } - -+ ret = clk_prepare_enable(res->aux_clk); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable aux clock\n"); -+ goto err_clk_aux; -+ } -+ -+ ret = clk_prepare_enable(res->ref_clk); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable ref clock\n"); -+ goto err_clk_ref; -+ } -+ - ret = reset_control_deassert(res->ahb_reset); - if (ret) { - dev_err(dev, "cannot deassert ahb reset\n"); - goto err_reset_ahb; - } -+ udelay(1); - - return 0; - - err_reset_ahb: -+ clk_disable_unprepare(res->ref_clk); -+err_clk_ref: -+ clk_disable_unprepare(res->aux_clk); -+err_clk_aux: - clk_disable_unprepare(res->phy_clk); - err_clk_phy: - clk_disable_unprepare(res->core_clk); - err_clk_core: - clk_disable_unprepare(res->iface_clk); - err_iface: -+ reset_control_assert(res->ext_reset); -+err_reset_ext: - regulator_disable(res->vdda_phy); - err_vdda_phy: - regulator_disable(res->vdda_refclk); -@@ -329,6 +393,14 @@ static int qcom_pcie_get_resources_v0(st - if (IS_ERR(res->phy_clk)) - return PTR_ERR(res->phy_clk); - -+ res->aux_clk = devm_clk_get(dev, "aux"); -+ if (IS_ERR(res->aux_clk)) -+ return PTR_ERR(res->aux_clk); -+ -+ res->ref_clk = devm_clk_get(dev, "ref"); -+ if (IS_ERR(res->ref_clk)) -+ return PTR_ERR(res->ref_clk); -+ - res->pci_reset = devm_reset_control_get(dev, "pci"); - if (IS_ERR(res->pci_reset)) - return PTR_ERR(res->pci_reset); -@@ -349,6 +421,14 @@ static int qcom_pcie_get_resources_v0(st - if (IS_ERR(res->phy_reset)) - return PTR_ERR(res->phy_reset); - -+ res->ext_reset = devm_reset_control_get(dev, "ext"); -+ if (IS_ERR(res->ext_reset)) -+ return PTR_ERR(res->ext_reset); -+ -+ if (of_property_read_u8(dev->of_node, "phy-tx0-term-offset", -+ &res->phy_tx0_term_offset)) -+ res->phy_tx0_term_offset = 0; -+ - return 0; - } - -@@ -461,6 +541,57 @@ err_res: - qcom_pcie_disable_resources_v1(pcie); - } - -+static void qcom_pcie_prog_viewport_cfg0(struct qcom_pcie *pcie, u32 busdev) -+{ -+ struct pcie_port *pp = &pcie->pp; -+ -+ /* -+ * program and enable address translation region 0 (device config -+ * address space); region type config; -+ * axi config address range to device config address range -+ */ -+ writel(PCIE20_PLR_IATU_REGION_OUTBOUND | -+ PCIE20_PLR_IATU_REGION_INDEX(0), -+ pcie->dbi + PCIE20_PLR_IATU_VIEWPORT); -+ -+ writel(PCIE20_PLR_IATU_TYPE_CFG0, pcie->dbi + PCIE20_PLR_IATU_CTRL1); -+ writel(PCIE20_PLR_IATU_ENABLE, pcie->dbi + PCIE20_PLR_IATU_CTRL2); -+ writel(pp->cfg0_base, pcie->dbi + PCIE20_PLR_IATU_LBAR); -+ writel((pp->cfg0_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR); -+ writel((pp->cfg0_base + pp->cfg0_size - 1), -+ pcie->dbi + PCIE20_PLR_IATU_LAR); -+ writel(busdev, pcie->dbi + PCIE20_PLR_IATU_LTAR); -+ writel(0, pcie->dbi + PCIE20_PLR_IATU_UTAR); -+} -+ -+static void qcom_pcie_prog_viewport_mem2_outbound(struct qcom_pcie *pcie) -+{ -+ struct pcie_port *pp = &pcie->pp; -+ -+ /* -+ * program and enable address translation region 2 (device resource -+ * address space); region type memory; -+ * axi device bar address range to device bar address range -+ */ -+ writel(PCIE20_PLR_IATU_REGION_OUTBOUND | -+ PCIE20_PLR_IATU_REGION_INDEX(2), -+ pcie->dbi + PCIE20_PLR_IATU_VIEWPORT); -+ -+ writel(PCIE20_PLR_IATU_TYPE_MEM, pcie->dbi + PCIE20_PLR_IATU_CTRL1); -+ writel(PCIE20_PLR_IATU_ENABLE, pcie->dbi + PCIE20_PLR_IATU_CTRL2); -+ writel(pp->mem_base, pcie->dbi + PCIE20_PLR_IATU_LBAR); -+ writel((pp->mem_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR); -+ writel(pp->mem_base + pp->mem_size - 1, -+ pcie->dbi + PCIE20_PLR_IATU_LAR); -+ writel(pp->mem_bus_addr, pcie->dbi + PCIE20_PLR_IATU_LTAR); -+ writel(upper_32_bits(pp->mem_bus_addr), -+ pcie->dbi + PCIE20_PLR_IATU_UTAR); -+ -+ /* 256B PCIE buffer setting */ -+ writel(0x1, pcie->dbi + PCIE20_AXI_MSTR_RESP_COMP_CTRL0); -+ writel(0x1, pcie->dbi + PCIE20_AXI_MSTR_RESP_COMP_CTRL1); -+} -+ - static void qcom_pcie_host_init_v0(struct pcie_port *pp) - { - struct qcom_pcie *pcie = to_qcom_pcie(pp); -@@ -470,15 +601,34 @@ static void qcom_pcie_host_init_v0(struc - - qcom_ep_reset_assert(pcie); - -+ reset_control_assert(res->ahb_reset); -+ - ret = qcom_pcie_enable_resources_v0(pcie); - if (ret) - return; - - writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0); - -- /* enable external reference clock */ -- writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16)); -+ /* Set Tx termination offset */ -+ writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, -+ PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK, -+ PHY_CTRL_PHY_TX0_TERM_OFFSET(res->phy_tx0_term_offset)); -+ -+ /* PARF programming */ -+ writel(PCS_DEEMPH_TX_DEEMPH_GEN1(0x18) | -+ PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(0x18) | -+ PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(0x22), -+ pcie->parf + PCIE20_PARF_PCS_DEEMPH); -+ writel(PCS_SWING_TX_SWING_FULL(0x78) | -+ PCS_SWING_TX_SWING_LOW(0x78), -+ pcie->parf + PCIE20_PARF_PCS_SWING); -+ writel(PHY_RX0_EQ(0x4), pcie->parf + PCIE20_PARF_CONFIG_BITS); -+ -+ /* Enable reference clock */ -+ writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, -+ REF_USE_PAD, REF_SSP_EN); - -+ /* De-assert PHY, PCIe, POR and AXI resets */ - ret = reset_control_deassert(res->phy_reset); - if (ret) { - dev_err(dev, "cannot deassert phy reset\n"); -@@ -517,6 +667,9 @@ static void qcom_pcie_host_init_v0(struc - if (ret) - goto err; - -+ qcom_pcie_prog_viewport_cfg0(pcie, MSM_PCIE_DEV_CFG_ADDR); -+ qcom_pcie_prog_viewport_mem2_outbound(pcie); -+ - return; - err: - qcom_ep_reset_assert(pcie); diff --git a/target/linux/ipq806x/patches-4.4/115-add-pcie-aux-clk-dts.patch b/target/linux/ipq806x/patches-4.4/115-add-pcie-aux-clk-dts.patch deleted file mode 100644 index 636a6c76a9..0000000000 --- a/target/linux/ipq806x/patches-4.4/115-add-pcie-aux-clk-dts.patch +++ /dev/null @@ -1,80 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -475,15 +475,21 @@ - - clocks = <&gcc PCIE_A_CLK>, - <&gcc PCIE_H_CLK>, -- <&gcc PCIE_PHY_CLK>; -- clock-names = "core", "iface", "phy"; -+ <&gcc PCIE_PHY_CLK>, -+ <&gcc PCIE_AUX_CLK>, -+ <&gcc PCIE_ALT_REF_CLK>; -+ clock-names = "core", "iface", "phy", "aux", "ref"; -+ -+ assigned-clocks = <&gcc PCIE_ALT_REF_CLK>; -+ assigned-clock-rates = <100000000>; - - resets = <&gcc PCIE_ACLK_RESET>, - <&gcc PCIE_HCLK_RESET>, - <&gcc PCIE_POR_RESET>, - <&gcc PCIE_PCI_RESET>, -- <&gcc PCIE_PHY_RESET>; -- reset-names = "axi", "ahb", "por", "pci", "phy"; -+ <&gcc PCIE_PHY_RESET>, -+ <&gcc PCIE_EXT_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; - - pinctrl-0 = <&pcie0_pins>; - pinctrl-names = "default"; -@@ -521,15 +527,21 @@ - - clocks = <&gcc PCIE_1_A_CLK>, - <&gcc PCIE_1_H_CLK>, -- <&gcc PCIE_1_PHY_CLK>; -- clock-names = "core", "iface", "phy"; -+ <&gcc PCIE_1_PHY_CLK>, -+ <&gcc PCIE_1_AUX_CLK>, -+ <&gcc PCIE_1_ALT_REF_CLK>; -+ clock-names = "core", "iface", "phy", "aux", "ref"; -+ -+ assigned-clocks = <&gcc PCIE_1_ALT_REF_CLK>; -+ assigned-clock-rates = <100000000>; - - resets = <&gcc PCIE_1_ACLK_RESET>, - <&gcc PCIE_1_HCLK_RESET>, - <&gcc PCIE_1_POR_RESET>, - <&gcc PCIE_1_PCI_RESET>, -- <&gcc PCIE_1_PHY_RESET>; -- reset-names = "axi", "ahb", "por", "pci", "phy"; -+ <&gcc PCIE_1_PHY_RESET>, -+ <&gcc PCIE_1_EXT_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; - - pinctrl-0 = <&pcie1_pins>; - pinctrl-names = "default"; -@@ -567,15 +579,21 @@ - - clocks = <&gcc PCIE_2_A_CLK>, - <&gcc PCIE_2_H_CLK>, -- <&gcc PCIE_2_PHY_CLK>; -- clock-names = "core", "iface", "phy"; -+ <&gcc PCIE_2_PHY_CLK>, -+ <&gcc PCIE_2_AUX_CLK>, -+ <&gcc PCIE_2_ALT_REF_CLK>; -+ clock-names = "core", "iface", "phy", "aux", "ref"; -+ -+ assigned-clocks = <&gcc PCIE_2_ALT_REF_CLK>; -+ assigned-clock-rates = <100000000>; - - resets = <&gcc PCIE_2_ACLK_RESET>, - <&gcc PCIE_2_HCLK_RESET>, - <&gcc PCIE_2_POR_RESET>, - <&gcc PCIE_2_PCI_RESET>, -- <&gcc PCIE_2_PHY_RESET>; -- reset-names = "axi", "ahb", "por", "pci", "phy"; -+ <&gcc PCIE_2_PHY_RESET>, -+ <&gcc PCIE_2_EXT_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; - - pinctrl-0 = <&pcie2_pins>; - pinctrl-names = "default"; diff --git a/target/linux/ipq806x/patches-4.4/126-add-rpm-to-ipq8064-dts.patch b/target/linux/ipq806x/patches-4.4/126-add-rpm-to-ipq8064-dts.patch deleted file mode 100644 index 6e589d26ae..0000000000 --- a/target/linux/ipq806x/patches-4.4/126-add-rpm-to-ipq8064-dts.patch +++ /dev/null @@ -1,87 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -2,6 +2,7 @@ - - #include "skeleton.dtsi" - #include -+#include - #include - #include - #include -@@ -93,6 +94,63 @@ - reg-names = "lpass-lpaif"; - }; - -+ rpm@108000 { -+ compatible = "qcom,rpm-ipq8064"; -+ reg = <0x108000 0x1000>; -+ qcom,ipc = <&l2cc 0x8 2>; -+ -+ interrupts = <0 19 0>, -+ <0 21 0>, -+ <0 22 0>; -+ interrupt-names = "ack", -+ "err", -+ "wakeup"; -+ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ smb208_s1a: smb208-s1a { -+ compatible = "qcom,rpm-smb208"; -+ reg = ; -+ -+ regulator-min-microvolt = <1050000>; -+ regulator-max-microvolt = <1150000>; -+ -+ qcom,switch-mode-frequency = <1200000>; -+ -+ }; -+ -+ smb208_s1b: smb208-s1b { -+ compatible = "qcom,rpm-smb208"; -+ reg = ; -+ -+ regulator-min-microvolt = <1050000>; -+ regulator-max-microvolt = <1150000>; -+ -+ qcom,switch-mode-frequency = <1200000>; -+ }; -+ -+ smb208_s2a: smb208-s2a { -+ compatible = "qcom,rpm-smb208"; -+ reg = ; -+ -+ regulator-min-microvolt = < 800000>; -+ regulator-max-microvolt = <1250000>; -+ -+ qcom,switch-mode-frequency = <1200000>; -+ }; -+ -+ smb208_s2b: smb208-s2b { -+ compatible = "qcom,rpm-smb208"; -+ reg = ; -+ -+ regulator-min-microvolt = < 800000>; -+ regulator-max-microvolt = <1250000>; -+ -+ qcom,switch-mode-frequency = <1200000>; -+ }; -+ }; -+ - qcom_pinmux: pinmux@800000 { - compatible = "qcom,ipq8064-pinctrl"; - reg = <0x800000 0x4000>; -@@ -165,6 +223,12 @@ - reg = <0x02098000 0x1000>, <0x02008000 0x1000>; - }; - -+ l2cc: clock-controller@2011000 { -+ compatible = "qcom,kpss-gcc", "syscon"; -+ reg = <0x2011000 0x1000>; -+ clock-output-names = "acpu_l2_aux"; -+ }; -+ - saw0: regulator@2089000 { - compatible = "qcom,saw2"; - reg = <0x02089000 0x1000>, <0x02009000 0x1000>; diff --git a/target/linux/ipq806x/patches-4.4/133-ARM-Add-Krait-L2-register-accessor-functions.patch b/target/linux/ipq806x/patches-4.4/133-ARM-Add-Krait-L2-register-accessor-functions.patch deleted file mode 100644 index 36a92c858a..0000000000 --- a/target/linux/ipq806x/patches-4.4/133-ARM-Add-Krait-L2-register-accessor-functions.patch +++ /dev/null @@ -1,144 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,01/13] ARM: Add Krait L2 register accessor functions -From: Stephen Boyd -X-Patchwork-Id: 6063051 -Message-Id: <1426920332-9340-2-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar , - Mark Rutland , Russell King , - Courtney Cavin -Date: Fri, 20 Mar 2015 23:45:20 -0700 - -Krait CPUs have a handful of L2 cache controller registers that -live behind a cp15 based indirection register. First you program -the indirection register (l2cpselr) to point the L2 'window' -register (l2cpdr) at what you want to read/write. Then you -read/write the 'window' register to do what you want. The -l2cpselr register is not banked per-cpu so we must lock around -accesses to it to prevent other CPUs from re-pointing l2cpdr -underneath us. - -Cc: Mark Rutland -Cc: Russell King -Cc: Courtney Cavin -Signed-off-by: Stephen Boyd - ---- -arch/arm/common/Kconfig | 3 ++ - arch/arm/common/Makefile | 1 + - arch/arm/common/krait-l2-accessors.c | 58 +++++++++++++++++++++++++++++++ - arch/arm/include/asm/krait-l2-accessors.h | 20 +++++++++++ - 4 files changed, 82 insertions(+) - create mode 100644 arch/arm/common/krait-l2-accessors.c - create mode 100644 arch/arm/include/asm/krait-l2-accessors.h - ---- a/arch/arm/common/Kconfig -+++ b/arch/arm/common/Kconfig -@@ -9,6 +9,9 @@ config DMABOUNCE - bool - select ZONE_DMA - -+config KRAIT_L2_ACCESSORS -+ bool -+ - config SHARP_LOCOMO - bool - ---- a/arch/arm/common/Makefile -+++ b/arch/arm/common/Makefile -@@ -7,6 +7,7 @@ obj-y += firmware.o - obj-$(CONFIG_ICST) += icst.o - obj-$(CONFIG_SA1111) += sa1111.o - obj-$(CONFIG_DMABOUNCE) += dmabounce.o -+obj-$(CONFIG_KRAIT_L2_ACCESSORS) += krait-l2-accessors.o - obj-$(CONFIG_SHARP_LOCOMO) += locomo.o - obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o - obj-$(CONFIG_SHARP_SCOOP) += scoop.o ---- /dev/null -+++ b/arch/arm/common/krait-l2-accessors.c -@@ -0,0 +1,58 @@ -+/* -+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include -+#include -+ -+#include -+#include -+ -+static DEFINE_RAW_SPINLOCK(krait_l2_lock); -+ -+void krait_set_l2_indirect_reg(u32 addr, u32 val) -+{ -+ unsigned long flags; -+ -+ raw_spin_lock_irqsave(&krait_l2_lock, flags); -+ /* -+ * Select the L2 window by poking l2cpselr, then write to the window -+ * via l2cpdr. -+ */ -+ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr)); -+ isb(); -+ asm volatile ("mcr p15, 3, %0, c15, c0, 7 @ l2cpdr" : : "r" (val)); -+ isb(); -+ -+ raw_spin_unlock_irqrestore(&krait_l2_lock, flags); -+} -+EXPORT_SYMBOL(krait_set_l2_indirect_reg); -+ -+u32 krait_get_l2_indirect_reg(u32 addr) -+{ -+ u32 val; -+ unsigned long flags; -+ -+ raw_spin_lock_irqsave(&krait_l2_lock, flags); -+ /* -+ * Select the L2 window by poking l2cpselr, then read from the window -+ * via l2cpdr. -+ */ -+ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr)); -+ isb(); -+ asm volatile ("mrc p15, 3, %0, c15, c0, 7 @ l2cpdr" : "=r" (val)); -+ -+ raw_spin_unlock_irqrestore(&krait_l2_lock, flags); -+ -+ return val; -+} -+EXPORT_SYMBOL(krait_get_l2_indirect_reg); ---- /dev/null -+++ b/arch/arm/include/asm/krait-l2-accessors.h -@@ -0,0 +1,20 @@ -+/* -+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#ifndef __ASMARM_KRAIT_L2_ACCESSORS_H -+#define __ASMARM_KRAIT_L2_ACCESSORS_H -+ -+extern void krait_set_l2_indirect_reg(u32 addr, u32 val); -+extern u32 krait_get_l2_indirect_reg(u32 addr); -+ -+#endif diff --git a/target/linux/ipq806x/patches-4.4/134-clk-mux-Split-out-register-accessors-for-reuse.patch b/target/linux/ipq806x/patches-4.4/134-clk-mux-Split-out-register-accessors-for-reuse.patch deleted file mode 100644 index 6efa5d8045..0000000000 --- a/target/linux/ipq806x/patches-4.4/134-clk-mux-Split-out-register-accessors-for-reuse.patch +++ /dev/null @@ -1,177 +0,0 @@ -From 4c28a15ea536281c8d619e5c6716ade914c79a6e Mon Sep 17 00:00:00 2001 -From: Stephen Boyd -Date: Fri, 20 Mar 2015 23:45:21 -0700 -Subject: [PATCH 1/2] clk: mux: Split out register accessors for reuse - -We want to reuse the logic in clk-mux.c for other clock drivers -that don't use readl as register accessors. Fortunately, there -really isn't much to the mux code besides the table indirection -and quirk flags if you assume any bit shifting and masking has -been done already. Pull that logic out into reusable functions -that operate on an optional table and some flags so that other -drivers can use the same logic. - -Signed-off-by: Stephen Boyd -Signed-off-by: Ram Chandra Jangir ---- - drivers/clk/clk-mux.c | 74 +++++++++++++++++++++++++++----------------- - include/linux/clk-provider.h | 9 ++++-- - 2 files changed, 53 insertions(+), 30 deletions(-) - ---- a/drivers/clk/clk-mux.c -+++ b/drivers/clk/clk-mux.c -@@ -28,35 +28,24 @@ - - #define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw) - --static u8 clk_mux_get_parent(struct clk_hw *hw) -+unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val, -+ unsigned int *table, unsigned long flags) - { -- struct clk_mux *mux = to_clk_mux(hw); - int num_parents = clk_hw_get_num_parents(hw); -- u32 val; - -- /* -- * FIXME need a mux-specific flag to determine if val is bitwise or numeric -- * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1 -- * to 0x7 (index starts at one) -- * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so -- * val = 0x4 really means "bit 2, index starts at bit 0" -- */ -- val = clk_readl(mux->reg) >> mux->shift; -- val &= mux->mask; -- -- if (mux->table) { -+ if (table) { - int i; - - for (i = 0; i < num_parents; i++) -- if (mux->table[i] == val) -+ if (table[i] == val) - return i; - return -EINVAL; - } - -- if (val && (mux->flags & CLK_MUX_INDEX_BIT)) -+ if (val && (flags & CLK_MUX_INDEX_BIT)) - val = ffs(val) - 1; - -- if (val && (mux->flags & CLK_MUX_INDEX_ONE)) -+ if (val && (flags & CLK_MUX_INDEX_ONE)) - val--; - - if (val >= num_parents) -@@ -64,24 +53,53 @@ static u8 clk_mux_get_parent(struct clk_ - - return val; - } -+EXPORT_SYMBOL_GPL(clk_mux_get_parent); - --static int clk_mux_set_parent(struct clk_hw *hw, u8 index) -+static u8 _clk_mux_get_parent(struct clk_hw *hw) - { - struct clk_mux *mux = to_clk_mux(hw); - u32 val; -- unsigned long flags = 0; - -- if (mux->table) -- index = mux->table[index]; -+ /* -+ * FIXME need a mux-specific flag to determine if val is bitwise or numeric -+ * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1 -+ * to 0x7 (index starts at one) -+ * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so -+ * val = 0x4 really means "bit 2, index starts at bit 0" -+ */ -+ val = clk_readl(mux->reg) >> mux->shift; -+ val &= mux->mask; - -- else { -- if (mux->flags & CLK_MUX_INDEX_BIT) -- index = 1 << index; -+ return clk_mux_get_parent(hw, val, mux->table, mux->flags); -+} -+ -+unsigned int clk_mux_reindex(u8 index, unsigned int *table, -+ unsigned long flags) -+{ -+ unsigned int val = index; - -- if (mux->flags & CLK_MUX_INDEX_ONE) -- index++; -+ if (table) { -+ val = table[val]; -+ } else { -+ if (flags & CLK_MUX_INDEX_BIT) -+ val = 1 << index; -+ -+ if (flags & CLK_MUX_INDEX_ONE) -+ val++; - } - -+ return val; -+} -+EXPORT_SYMBOL_GPL(clk_mux_reindex); -+ -+static int clk_mux_set_parent(struct clk_hw *hw, u8 index) -+{ -+ struct clk_mux *mux = to_clk_mux(hw); -+ u32 val; -+ unsigned long flags = 0; -+ -+ index = clk_mux_reindex(index, mux->table, mux->flags); -+ - if (mux->lock) - spin_lock_irqsave(mux->lock, flags); - else -@@ -105,7 +123,7 @@ static int clk_mux_set_parent(struct clk - } - - const struct clk_ops clk_mux_ops = { -- .get_parent = clk_mux_get_parent, -+ .get_parent = _clk_mux_get_parent, - .set_parent = clk_mux_set_parent, - .determine_rate = __clk_mux_determine_rate, - }; -@@ -120,7 +138,7 @@ struct clk *clk_register_mux_table(struc - const char * const *parent_names, u8 num_parents, - unsigned long flags, - void __iomem *reg, u8 shift, u32 mask, -- u8 clk_mux_flags, u32 *table, spinlock_t *lock) -+ u8 clk_mux_flags, unsigned int *table, spinlock_t *lock) - { - struct clk_mux *mux; - struct clk *clk; ---- a/include/linux/clk-provider.h -+++ b/include/linux/clk-provider.h -@@ -433,7 +433,7 @@ void clk_unregister_divider(struct clk * - struct clk_mux { - struct clk_hw hw; - void __iomem *reg; -- u32 *table; -+ unsigned int *table; - u32 mask; - u8 shift; - u8 flags; -@@ -449,6 +449,11 @@ struct clk_mux { - extern const struct clk_ops clk_mux_ops; - extern const struct clk_ops clk_mux_ro_ops; - -+unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val, -+ unsigned int *table, unsigned long flags); -+unsigned int clk_mux_reindex(u8 index, unsigned int *table, -+ unsigned long flags); -+ - struct clk *clk_register_mux(struct device *dev, const char *name, - const char * const *parent_names, u8 num_parents, - unsigned long flags, -@@ -459,7 +464,7 @@ struct clk *clk_register_mux_table(struc - const char * const *parent_names, u8 num_parents, - unsigned long flags, - void __iomem *reg, u8 shift, u32 mask, -- u8 clk_mux_flags, u32 *table, spinlock_t *lock); -+ u8 clk_mux_flags, unsigned int *table, spinlock_t *lock); - - void clk_unregister_mux(struct clk *clk); - diff --git a/target/linux/ipq806x/patches-4.4/135-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch b/target/linux/ipq806x/patches-4.4/135-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch deleted file mode 100644 index 5015d32d86..0000000000 --- a/target/linux/ipq806x/patches-4.4/135-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch +++ /dev/null @@ -1,122 +0,0 @@ -From 39d42ce5031d2a4f92fa203b87acfbab340b15a2 Mon Sep 17 00:00:00 2001 -From: Stephen Boyd -Date: Fri, 20 Mar 2015 23:45:22 -0700 -Subject: [PATCH 2/2] clk: Avoid sending high rates to downstream clocks during - set_rate - -If a clock is on and we call clk_set_rate() on it we may get into -a situation where the clock temporarily increases in rate -dramatically while we walk the tree and call .set_rate() ops. For -example, consider a case where a PLL feeds into a divider. -Initially the divider is set to divide by 1 and the PLL is -running fairly slow (100MHz). The downstream consumer of the -divider output can only handle rates =< 400 MHz, but the divider -can only choose between divisors of 1 and 4. - - +-----+ +----------------+ - | PLL |-->| div 1 or div 4 |---> consumer device - +-----+ +----------------+ - -To achieve a rate of 400MHz on the output of the divider, we -would have to set the rate of the PLL to 1.6 GHz and then divide -it by 4. The current code would set the PLL to 1.6GHz first while -the divider is still set to 1, thus causing the downstream -consumer of the clock to receive a few clock cycles of 1.6GHz -clock (far beyond it's maximum acceptable rate). We should be -changing the divider first before increasing the PLL rate to -avoid this problem. - -Therefore, set the rate of any child clocks that are increasing -in rate from their current rate so that they can increase their -dividers if necessary. We assume that there isn't such a thing as -minimum rate requirements. - -Signed-off-by: Stephen Boyd -Signed-off-by: Ram Chandra Jangir ---- - drivers/clk/clk.c | 34 ++++++++++++++++++++++------------ - 1 file changed, 22 insertions(+), 12 deletions(-) - ---- a/drivers/clk/clk.c -+++ b/drivers/clk/clk.c -@@ -1427,21 +1427,24 @@ static struct clk_core *clk_propagate_ra - * walk down a subtree and set the new rates notifying the rate - * change on the way - */ --static void clk_change_rate(struct clk_core *core) -+static void -+clk_change_rate(struct clk_core *core, unsigned long best_parent_rate) - { - struct clk_core *child; - struct hlist_node *tmp; - unsigned long old_rate; -- unsigned long best_parent_rate = 0; - bool skip_set_rate = false; - struct clk_core *old_parent; - -- old_rate = core->rate; -+ hlist_for_each_entry(child, &core->children, child_node) { -+ /* Skip children who will be reparented to another clock */ -+ if (child->new_parent && child->new_parent != core) -+ continue; -+ if (child->new_rate > child->rate) -+ clk_change_rate(child, core->new_rate); -+ } - -- if (core->new_parent) -- best_parent_rate = core->new_parent->rate; -- else if (core->parent) -- best_parent_rate = core->parent->rate; -+ old_rate = core->rate; - - if (core->new_parent && core->new_parent != core->parent) { - old_parent = __clk_set_parent_before(core, core->new_parent); -@@ -1467,7 +1470,7 @@ static void clk_change_rate(struct clk_c - - trace_clk_set_rate_complete(core, core->new_rate); - -- core->rate = clk_recalc(core, best_parent_rate); -+ core->rate = core->new_rate; - - if (core->notifier_count && old_rate != core->rate) - __clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate); -@@ -1483,12 +1486,13 @@ static void clk_change_rate(struct clk_c - /* Skip children who will be reparented to another clock */ - if (child->new_parent && child->new_parent != core) - continue; -- clk_change_rate(child); -+ if (child->new_rate != child->rate) -+ clk_change_rate(child, core->new_rate); - } - - /* handle the new child who might not be in core->children yet */ -- if (core->new_child) -- clk_change_rate(core->new_child); -+ if (core->new_child && core->new_child->new_rate != core->new_child->rate) -+ clk_change_rate(core->new_child, core->new_rate); - } - - static int clk_core_set_rate_nolock(struct clk_core *core, -@@ -1497,6 +1501,7 @@ static int clk_core_set_rate_nolock(stru - struct clk_core *top, *fail_clk; - unsigned long rate = req_rate; - int ret = 0; -+ unsigned long parent_rate; - - if (!core) - return 0; -@@ -1522,8 +1527,13 @@ static int clk_core_set_rate_nolock(stru - return -EBUSY; - } - -+ if (top->parent) -+ parent_rate = top->parent->rate; -+ else -+ parent_rate = 0; -+ - /* change the rates */ -- clk_change_rate(top); -+ clk_change_rate(top, parent_rate); - - core->req_rate = req_rate; - diff --git a/target/linux/ipq806x/patches-4.4/136-clk-Add-safe-switch-hook.patch b/target/linux/ipq806x/patches-4.4/136-clk-Add-safe-switch-hook.patch deleted file mode 100644 index de3ac7d14c..0000000000 --- a/target/linux/ipq806x/patches-4.4/136-clk-Add-safe-switch-hook.patch +++ /dev/null @@ -1,151 +0,0 @@ -From f7a00ea959be31f9b742042294a359d508edce94 Mon Sep 17 00:00:00 2001 -From: Stephen Boyd -Date: Fri, 20 Mar 2015 23:45:23 -0700 -Subject: [PATCH] clk: Add safe switch hook - -Sometimes clocks can't accept their parent source turning off -while the source is reprogrammed to a different rate. Most -notably CPU clocks require a way to switch away from the current -PLL they're running on, reprogram that PLL to a new rate, and -then switch back to the PLL with the new rate once they're done. -Add a hook that drivers can implement allowing them to return a -'safe parent' that they can switch their parent to while the -upstream source is reprogrammed to support this. - -Signed-off-by: Stephen Boyd -Signed-off-by: Ram Chandra Jangir ---- - drivers/clk/clk.c | 61 ++++++++++++++++++++++++++++++++++++++------ - include/linux/clk-provider.h | 1 + - 2 files changed, 54 insertions(+), 8 deletions(-) - ---- a/drivers/clk/clk.c -+++ b/drivers/clk/clk.c -@@ -51,9 +51,12 @@ struct clk_core { - struct clk_core **parents; - u8 num_parents; - u8 new_parent_index; -+ u8 safe_parent_index; - unsigned long rate; - unsigned long req_rate; -+ unsigned long old_rate; - unsigned long new_rate; -+ struct clk_core *safe_parent; - struct clk_core *new_parent; - struct clk_core *new_child; - unsigned long flags; -@@ -1271,7 +1274,8 @@ out: - static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate, - struct clk_core *new_parent, u8 p_index) - { -- struct clk_core *child; -+ struct clk_core *child, *parent; -+ struct clk_hw *parent_hw; - - core->new_rate = new_rate; - core->new_parent = new_parent; -@@ -1281,6 +1285,18 @@ static void clk_calc_subtree(struct clk_ - if (new_parent && new_parent != core->parent) - new_parent->new_child = core; - -+ if (core->ops->get_safe_parent) { -+ parent_hw = core->ops->get_safe_parent(core->hw); -+ if (parent_hw) { -+ parent = parent_hw->core; -+ p_index = clk_fetch_parent_index(core, parent); -+ core->safe_parent_index = p_index; -+ core->safe_parent = parent; -+ } -+ } else { -+ core->safe_parent = NULL; -+ } -+ - hlist_for_each_entry(child, &core->children, child_node) { - child->new_rate = clk_recalc(child, new_rate); - clk_calc_subtree(child, child->new_rate, NULL, 0); -@@ -1393,14 +1409,43 @@ static struct clk_core *clk_propagate_ra - unsigned long event) - { - struct clk_core *child, *tmp_clk, *fail_clk = NULL; -+ struct clk_core *old_parent; - int ret = NOTIFY_DONE; - -- if (core->rate == core->new_rate) -+ if (core->rate == core->new_rate && event != POST_RATE_CHANGE) - return NULL; - -+ switch (event) { -+ case PRE_RATE_CHANGE: -+ if (core->safe_parent) -+ core->ops->set_parent(core->hw, core->safe_parent_index); -+ core->old_rate = core->rate; -+ break; -+ case POST_RATE_CHANGE: -+ if (core->safe_parent) { -+ old_parent = __clk_set_parent_before(core, -+ core->new_parent); -+ if (core->ops->set_rate_and_parent) { -+ core->ops->set_rate_and_parent(core->hw, -+ core->new_rate, -+ core->new_parent ? -+ core->new_parent->rate : 0, -+ core->new_parent_index); -+ } else if (core->ops->set_parent) { -+ core->ops->set_parent(core->hw, -+ core->new_parent_index); -+ } -+ __clk_set_parent_after(core, core->new_parent, -+ old_parent); -+ } -+ break; -+ } -+ - if (core->notifier_count) { -- ret = __clk_notify(core, event, core->rate, core->new_rate); -- if (ret & NOTIFY_STOP_MASK) -+ if (event != POST_RATE_CHANGE || core->old_rate != core->rate) -+ ret = __clk_notify(core, event, core->old_rate, -+ core->new_rate); -+ if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE) - fail_clk = core; - } - -@@ -1446,7 +1491,8 @@ clk_change_rate(struct clk_core *core, u - - old_rate = core->rate; - -- if (core->new_parent && core->new_parent != core->parent) { -+ if (core->new_parent && core->new_parent != core->parent && -+ !core->safe_parent) { - old_parent = __clk_set_parent_before(core, core->new_parent); - trace_clk_set_parent(core, core->new_parent); - -@@ -1472,9 +1518,6 @@ clk_change_rate(struct clk_core *core, u - - core->rate = core->new_rate; - -- if (core->notifier_count && old_rate != core->rate) -- __clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate); -- - if (core->flags & CLK_RECALC_NEW_RATES) - (void)clk_calc_new_rates(core, core->new_rate); - -@@ -1537,6 +1580,8 @@ static int clk_core_set_rate_nolock(stru - - core->req_rate = req_rate; - -+ clk_propagate_rate_change(top, POST_RATE_CHANGE); -+ - return ret; - } - ---- a/include/linux/clk-provider.h -+++ b/include/linux/clk-provider.h -@@ -202,6 +202,7 @@ struct clk_ops { - struct clk_rate_request *req); - int (*set_parent)(struct clk_hw *hw, u8 index); - u8 (*get_parent)(struct clk_hw *hw); -+ struct clk_hw *(*get_safe_parent)(struct clk_hw *hw); - int (*set_rate)(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate); - int (*set_rate_and_parent)(struct clk_hw *hw, diff --git a/target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch b/target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch deleted file mode 100644 index 7e964490e0..0000000000 --- a/target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch +++ /dev/null @@ -1,351 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,05/13] clk: qcom: Add support for High-Frequency PLLs (HFPLLs) -From: Stephen Boyd -X-Patchwork-Id: 6063261 -Message-Id: <1426920332-9340-6-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar -Date: Fri, 20 Mar 2015 23:45:24 -0700 - -HFPLLs are the main frequency source for Krait CPU clocks. Add -support for changing the rate of these PLLs. - -Signed-off-by: Stephen Boyd - ---- -I'd really like to get rid of __clk_hfpll_init_once() if possible... - - drivers/clk/qcom/Makefile | 1 + - drivers/clk/qcom/clk-hfpll.c | 253 +++++++++++++++++++++++++++++++++++++++++++ - drivers/clk/qcom/clk-hfpll.h | 54 +++++++++ - 3 files changed, 308 insertions(+) - create mode 100644 drivers/clk/qcom/clk-hfpll.c - create mode 100644 drivers/clk/qcom/clk-hfpll.h - ---- a/drivers/clk/qcom/Makefile -+++ b/drivers/clk/qcom/Makefile -@@ -8,6 +8,7 @@ clk-qcom-y += clk-rcg2.o - clk-qcom-y += clk-branch.o - clk-qcom-y += clk-regmap-divider.o - clk-qcom-y += clk-regmap-mux.o -+clk-qcom-y += clk-hfpll.o - clk-qcom-y += reset.o - clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o - ---- /dev/null -+++ b/drivers/clk/qcom/clk-hfpll.c -@@ -0,0 +1,253 @@ -+/* -+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "clk-regmap.h" -+#include "clk-hfpll.h" -+ -+#define PLL_OUTCTRL BIT(0) -+#define PLL_BYPASSNL BIT(1) -+#define PLL_RESET_N BIT(2) -+ -+/* Initialize a HFPLL at a given rate and enable it. */ -+static void __clk_hfpll_init_once(struct clk_hw *hw) -+{ -+ struct clk_hfpll *h = to_clk_hfpll(hw); -+ struct hfpll_data const *hd = h->d; -+ struct regmap *regmap = h->clkr.regmap; -+ -+ if (likely(h->init_done)) -+ return; -+ -+ /* Configure PLL parameters for integer mode. */ -+ if (hd->config_val) -+ regmap_write(regmap, hd->config_reg, hd->config_val); -+ regmap_write(regmap, hd->m_reg, 0); -+ regmap_write(regmap, hd->n_reg, 1); -+ -+ if (hd->user_reg) { -+ u32 regval = hd->user_val; -+ unsigned long rate; -+ -+ rate = clk_hw_get_rate(hw); -+ -+ /* Pick the right VCO. */ -+ if (hd->user_vco_mask && rate > hd->low_vco_max_rate) -+ regval |= hd->user_vco_mask; -+ regmap_write(regmap, hd->user_reg, regval); -+ } -+ -+ if (hd->droop_reg) -+ regmap_write(regmap, hd->droop_reg, hd->droop_val); -+ -+ h->init_done = true; -+} -+ -+static void __clk_hfpll_enable(struct clk_hw *hw) -+{ -+ struct clk_hfpll *h = to_clk_hfpll(hw); -+ struct hfpll_data const *hd = h->d; -+ struct regmap *regmap = h->clkr.regmap; -+ u32 val; -+ -+ __clk_hfpll_init_once(hw); -+ -+ /* Disable PLL bypass mode. */ -+ regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL); -+ -+ /* -+ * H/W requires a 5us delay between disabling the bypass and -+ * de-asserting the reset. Delay 10us just to be safe. -+ */ -+ udelay(10); -+ -+ /* De-assert active-low PLL reset. */ -+ regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N); -+ -+ /* Wait for PLL to lock. */ -+ if (hd->status_reg) { -+ do { -+ regmap_read(regmap, hd->status_reg, &val); -+ } while (!(val & BIT(hd->lock_bit))); -+ } else { -+ udelay(60); -+ } -+ -+ /* Enable PLL output. */ -+ regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL); -+} -+ -+/* Enable an already-configured HFPLL. */ -+static int clk_hfpll_enable(struct clk_hw *hw) -+{ -+ unsigned long flags; -+ struct clk_hfpll *h = to_clk_hfpll(hw); -+ struct hfpll_data const *hd = h->d; -+ struct regmap *regmap = h->clkr.regmap; -+ u32 mode; -+ -+ spin_lock_irqsave(&h->lock, flags); -+ regmap_read(regmap, hd->mode_reg, &mode); -+ if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL))) -+ __clk_hfpll_enable(hw); -+ spin_unlock_irqrestore(&h->lock, flags); -+ -+ return 0; -+} -+ -+static void __clk_hfpll_disable(struct clk_hfpll *h) -+{ -+ struct hfpll_data const *hd = h->d; -+ struct regmap *regmap = h->clkr.regmap; -+ -+ /* -+ * Disable the PLL output, disable test mode, enable the bypass mode, -+ * and assert the reset. -+ */ -+ regmap_update_bits(regmap, hd->mode_reg, -+ PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0); -+} -+ -+static void clk_hfpll_disable(struct clk_hw *hw) -+{ -+ struct clk_hfpll *h = to_clk_hfpll(hw); -+ unsigned long flags; -+ -+ spin_lock_irqsave(&h->lock, flags); -+ __clk_hfpll_disable(h); -+ spin_unlock_irqrestore(&h->lock, flags); -+} -+ -+static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long *parent_rate) -+{ -+ struct clk_hfpll *h = to_clk_hfpll(hw); -+ struct hfpll_data const *hd = h->d; -+ unsigned long rrate; -+ -+ rate = clamp(rate, hd->min_rate, hd->max_rate); -+ -+ rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate; -+ if (rrate > hd->max_rate) -+ rrate -= *parent_rate; -+ -+ return rrate; -+} -+ -+/* -+ * For optimization reasons, assumes no downstream clocks are actively using -+ * it. -+ */ -+static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long parent_rate) -+{ -+ struct clk_hfpll *h = to_clk_hfpll(hw); -+ struct hfpll_data const *hd = h->d; -+ struct regmap *regmap = h->clkr.regmap; -+ unsigned long flags; -+ u32 l_val, val; -+ bool enabled; -+ -+ l_val = rate / parent_rate; -+ -+ spin_lock_irqsave(&h->lock, flags); -+ -+ enabled = __clk_is_enabled(hw->clk); -+ if (enabled) -+ __clk_hfpll_disable(h); -+ -+ /* Pick the right VCO. */ -+ if (hd->user_reg && hd->user_vco_mask) { -+ regmap_read(regmap, hd->user_reg, &val); -+ if (rate <= hd->low_vco_max_rate) -+ val &= ~hd->user_vco_mask; -+ else -+ val |= hd->user_vco_mask; -+ regmap_write(regmap, hd->user_reg, val); -+ } -+ -+ regmap_write(regmap, hd->l_reg, l_val); -+ -+ if (enabled) -+ __clk_hfpll_enable(hw); -+ -+ spin_unlock_irqrestore(&h->lock, flags); -+ -+ return 0; -+} -+ -+static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw, -+ unsigned long parent_rate) -+{ -+ struct clk_hfpll *h = to_clk_hfpll(hw); -+ struct hfpll_data const *hd = h->d; -+ struct regmap *regmap = h->clkr.regmap; -+ u32 l_val; -+ -+ regmap_read(regmap, hd->l_reg, &l_val); -+ -+ return l_val * parent_rate; -+} -+ -+static void clk_hfpll_init(struct clk_hw *hw) -+{ -+ struct clk_hfpll *h = to_clk_hfpll(hw); -+ struct hfpll_data const *hd = h->d; -+ struct regmap *regmap = h->clkr.regmap; -+ u32 mode, status; -+ -+ regmap_read(regmap, hd->mode_reg, &mode); -+ if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) { -+ __clk_hfpll_init_once(hw); -+ return; -+ } -+ -+ if (hd->status_reg) { -+ regmap_read(regmap, hd->status_reg, &status); -+ if (!(status & BIT(hd->lock_bit))) { -+ WARN(1, "HFPLL %s is ON, but not locked!\n", -+ __clk_get_name(hw->clk)); -+ clk_hfpll_disable(hw); -+ __clk_hfpll_init_once(hw); -+ } -+ } -+} -+ -+static int hfpll_is_enabled(struct clk_hw *hw) -+{ -+ struct clk_hfpll *h = to_clk_hfpll(hw); -+ struct hfpll_data const *hd = h->d; -+ struct regmap *regmap = h->clkr.regmap; -+ u32 mode; -+ -+ regmap_read(regmap, hd->mode_reg, &mode); -+ mode &= 0x7; -+ return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL); -+} -+ -+const struct clk_ops clk_ops_hfpll = { -+ .enable = clk_hfpll_enable, -+ .disable = clk_hfpll_disable, -+ .is_enabled = hfpll_is_enabled, -+ .round_rate = clk_hfpll_round_rate, -+ .set_rate = clk_hfpll_set_rate, -+ .recalc_rate = clk_hfpll_recalc_rate, -+ .init = clk_hfpll_init, -+}; -+EXPORT_SYMBOL_GPL(clk_ops_hfpll); ---- /dev/null -+++ b/drivers/clk/qcom/clk-hfpll.h -@@ -0,0 +1,54 @@ -+/* -+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+#ifndef __QCOM_CLK_HFPLL_H__ -+#define __QCOM_CLK_HFPLL_H__ -+ -+#include -+#include -+#include "clk-regmap.h" -+ -+struct hfpll_data { -+ u32 mode_reg; -+ u32 l_reg; -+ u32 m_reg; -+ u32 n_reg; -+ u32 user_reg; -+ u32 droop_reg; -+ u32 config_reg; -+ u32 status_reg; -+ u8 lock_bit; -+ -+ u32 droop_val; -+ u32 config_val; -+ u32 user_val; -+ u32 user_vco_mask; -+ unsigned long low_vco_max_rate; -+ -+ unsigned long min_rate; -+ unsigned long max_rate; -+}; -+ -+struct clk_hfpll { -+ struct hfpll_data const *d; -+ int init_done; -+ -+ struct clk_regmap clkr; -+ spinlock_t lock; -+}; -+ -+#define to_clk_hfpll(_hw) \ -+ container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr) -+ -+extern const struct clk_ops clk_ops_hfpll; -+ -+#endif diff --git a/target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch b/target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch deleted file mode 100644 index 83623206ce..0000000000 --- a/target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch +++ /dev/null @@ -1,206 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,06/13] clk: qcom: Add HFPLL driver -From: Stephen Boyd -X-Patchwork-Id: 6063231 -Message-Id: <1426920332-9340-7-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar , -Date: Fri, 20 Mar 2015 23:45:25 -0700 - -On some devices (MSM8974 for example), the HFPLLs are -instantiated within the Krait processor subsystem as separate -register regions. Add a driver for these PLLs so that we can -provide HFPLL clocks for use by the system. - -Cc: -Signed-off-by: Stephen Boyd - ---- -.../devicetree/bindings/clock/qcom,hfpll.txt | 40 ++++++++ - drivers/clk/qcom/Kconfig | 8 ++ - drivers/clk/qcom/Makefile | 1 + - drivers/clk/qcom/hfpll.c | 109 +++++++++++++++++++++ - 4 files changed, 158 insertions(+) - create mode 100644 Documentation/devicetree/bindings/clock/qcom,hfpll.txt - create mode 100644 drivers/clk/qcom/hfpll.c - ---- /dev/null -+++ b/Documentation/devicetree/bindings/clock/qcom,hfpll.txt -@@ -0,0 +1,40 @@ -+High-Frequency PLL (HFPLL) -+ -+PROPERTIES -+ -+- compatible: -+ Usage: required -+ Value type: -+ Definition: must be "qcom,hfpll" -+ -+- reg: -+ Usage: required -+ Value type: -+ Definition: address and size of HPLL registers. An optional second -+ element specifies the address and size of the alias -+ register region. -+ -+- clock-output-names: -+ Usage: required -+ Value type: -+ Definition: Name of the PLL. Typically hfpllX where X is a CPU number -+ starting at 0. Otherwise hfpll_Y where Y is more specific -+ such as "l2". -+ -+Example: -+ -+1) An HFPLL for the L2 cache. -+ -+ clock-controller@f9016000 { -+ compatible = "qcom,hfpll"; -+ reg = <0xf9016000 0x30>; -+ clock-output-names = "hfpll_l2"; -+ }; -+ -+2) An HFPLL for CPU0. This HFPLL has the alias register region. -+ -+ clock-controller@f908a000 { -+ compatible = "qcom,hfpll"; -+ reg = <0xf908a000 0x30>, <0xf900a000 0x30>; -+ clock-output-names = "hfpll0"; -+ }; ---- a/drivers/clk/qcom/Kconfig -+++ b/drivers/clk/qcom/Kconfig -@@ -135,3 +135,11 @@ config MSM_MMCC_8974 - Support for the multimedia clock controller on msm8974 devices. - Say Y if you want to support multimedia devices such as display, - graphics, video encode/decode, camera, etc. -+ -+config QCOM_HFPLL -+ tristate "High-Frequency PLL (HFPLL) Clock Controller" -+ depends on COMMON_CLK_QCOM -+ help -+ Support for the high-frequency PLLs present on Qualcomm devices. -+ Say Y if you want to support CPU frequency scaling on devices -+ such as MSM8974, APQ8084, etc. ---- a/drivers/clk/qcom/Makefile -+++ b/drivers/clk/qcom/Makefile -@@ -25,3 +25,4 @@ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8 - obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o - obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o - obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o -+obj-$(CONFIG_QCOM_HFPLL) += hfpll.o ---- /dev/null -+++ b/drivers/clk/qcom/hfpll.c -@@ -0,0 +1,109 @@ -+/* -+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "clk-regmap.h" -+#include "clk-hfpll.h" -+ -+static const struct hfpll_data hdata = { -+ .mode_reg = 0x00, -+ .l_reg = 0x04, -+ .m_reg = 0x08, -+ .n_reg = 0x0c, -+ .user_reg = 0x10, -+ .config_reg = 0x14, -+ .config_val = 0x430405d, -+ .status_reg = 0x1c, -+ .lock_bit = 16, -+ -+ .user_val = 0x8, -+ .user_vco_mask = 0x100000, -+ .low_vco_max_rate = 1248000000, -+ .min_rate = 537600000UL, -+ .max_rate = 2900000000UL, -+}; -+ -+static const struct of_device_id qcom_hfpll_match_table[] = { -+ { .compatible = "qcom,hfpll" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, qcom_hfpll_match_table); -+ -+static const struct regmap_config hfpll_regmap_config = { -+ .reg_bits = 32, -+ .reg_stride = 4, -+ .val_bits = 32, -+ .max_register = 0x30, -+ .fast_io = true, -+}; -+ -+static int qcom_hfpll_probe(struct platform_device *pdev) -+{ -+ struct clk *clk; -+ struct resource *res; -+ struct device *dev = &pdev->dev; -+ void __iomem *base; -+ struct regmap *regmap; -+ struct clk_hfpll *h; -+ struct clk_init_data init = { -+ .parent_names = (const char *[]){ "xo" }, -+ .num_parents = 1, -+ .ops = &clk_ops_hfpll, -+ }; -+ -+ h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL); -+ if (!h) -+ return -ENOMEM; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ base = devm_ioremap_resource(dev, res); -+ if (IS_ERR(base)) -+ return PTR_ERR(base); -+ -+ regmap = devm_regmap_init_mmio(&pdev->dev, base, &hfpll_regmap_config); -+ if (IS_ERR(regmap)) -+ return PTR_ERR(regmap); -+ -+ if (of_property_read_string_index(dev->of_node, "clock-output-names", -+ 0, &init.name)) -+ return -ENODEV; -+ -+ h->d = &hdata; -+ h->clkr.hw.init = &init; -+ spin_lock_init(&h->lock); -+ -+ clk = devm_clk_register_regmap(&pdev->dev, &h->clkr); -+ -+ return PTR_ERR_OR_ZERO(clk); -+} -+ -+static struct platform_driver qcom_hfpll_driver = { -+ .probe = qcom_hfpll_probe, -+ .driver = { -+ .name = "qcom-hfpll", -+ .of_match_table = qcom_hfpll_match_table, -+ }, -+}; -+module_platform_driver(qcom_hfpll_driver); -+ -+MODULE_DESCRIPTION("QCOM HFPLL Clock Driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:qcom-hfpll"); diff --git a/target/linux/ipq806x/patches-4.4/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch b/target/linux/ipq806x/patches-4.4/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch deleted file mode 100644 index a01decfd8b..0000000000 --- a/target/linux/ipq806x/patches-4.4/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch +++ /dev/null @@ -1,127 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,08/13] clk: qcom: Add IPQ806X's HFPLLs -From: Stephen Boyd -X-Patchwork-Id: 6063241 -Message-Id: <1426920332-9340-9-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar -Date: Fri, 20 Mar 2015 23:45:27 -0700 - -Describe the HFPLLs present on IPQ806X devices. - -Signed-off-by: Stephen Boyd - ---- -drivers/clk/qcom/gcc-ipq806x.c | 83 ++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 83 insertions(+) - ---- a/drivers/clk/qcom/gcc-ipq806x.c -+++ b/drivers/clk/qcom/gcc-ipq806x.c -@@ -30,6 +30,7 @@ - #include "clk-pll.h" - #include "clk-rcg.h" - #include "clk-branch.h" -+#include "clk-hfpll.h" - #include "reset.h" - - static struct clk_pll pll0 = { -@@ -113,6 +114,85 @@ static struct clk_regmap pll8_vote = { - }, - }; - -+static struct hfpll_data hfpll0_data = { -+ .mode_reg = 0x3200, -+ .l_reg = 0x3208, -+ .m_reg = 0x320c, -+ .n_reg = 0x3210, -+ .config_reg = 0x3204, -+ .status_reg = 0x321c, -+ .config_val = 0x7845c665, -+ .droop_reg = 0x3214, -+ .droop_val = 0x0108c000, -+ .min_rate = 600000000UL, -+ .max_rate = 1800000000UL, -+}; -+ -+static struct clk_hfpll hfpll0 = { -+ .d = &hfpll0_data, -+ .clkr.hw.init = &(struct clk_init_data){ -+ .parent_names = (const char *[]){ "pxo" }, -+ .num_parents = 1, -+ .name = "hfpll0", -+ .ops = &clk_ops_hfpll, -+ .flags = CLK_IGNORE_UNUSED, -+ }, -+ .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock), -+}; -+ -+static struct hfpll_data hfpll1_data = { -+ .mode_reg = 0x3240, -+ .l_reg = 0x3248, -+ .m_reg = 0x324c, -+ .n_reg = 0x3250, -+ .config_reg = 0x3244, -+ .status_reg = 0x325c, -+ .config_val = 0x7845c665, -+ .droop_reg = 0x3314, -+ .droop_val = 0x0108c000, -+ .min_rate = 600000000UL, -+ .max_rate = 1800000000UL, -+}; -+ -+static struct clk_hfpll hfpll1 = { -+ .d = &hfpll1_data, -+ .clkr.hw.init = &(struct clk_init_data){ -+ .parent_names = (const char *[]){ "pxo" }, -+ .num_parents = 1, -+ .name = "hfpll1", -+ .ops = &clk_ops_hfpll, -+ .flags = CLK_IGNORE_UNUSED, -+ }, -+ .lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock), -+}; -+ -+static struct hfpll_data hfpll_l2_data = { -+ .mode_reg = 0x3300, -+ .l_reg = 0x3308, -+ .m_reg = 0x330c, -+ .n_reg = 0x3310, -+ .config_reg = 0x3304, -+ .status_reg = 0x331c, -+ .config_val = 0x7845c665, -+ .droop_reg = 0x3314, -+ .droop_val = 0x0108c000, -+ .min_rate = 600000000UL, -+ .max_rate = 1800000000UL, -+}; -+ -+static struct clk_hfpll hfpll_l2 = { -+ .d = &hfpll_l2_data, -+ .clkr.hw.init = &(struct clk_init_data){ -+ .parent_names = (const char *[]){ "pxo" }, -+ .num_parents = 1, -+ .name = "hfpll_l2", -+ .ops = &clk_ops_hfpll, -+ .flags = CLK_IGNORE_UNUSED, -+ }, -+ .lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock), -+}; -+ -+ - static struct clk_pll pll14 = { - .l_reg = 0x31c4, - .m_reg = 0x31c8, -@@ -2837,6 +2917,9 @@ static struct clk_regmap *gcc_ipq806x_cl - [UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr, - [NSSTCM_CLK_SRC] = &nss_tcm_src.clkr, - [NSSTCM_CLK] = &nss_tcm_clk.clkr, -+ [PLL9] = &hfpll0.clkr, -+ [PLL10] = &hfpll1.clkr, -+ [PLL12] = &hfpll_l2.clkr, - }; - - static const struct qcom_reset_map gcc_ipq806x_resets[] = { diff --git a/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch b/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch deleted file mode 100644 index bbfcab9cc0..0000000000 --- a/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch +++ /dev/null @@ -1,272 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,09/13] clk: qcom: Add support for Krait clocks -From: Stephen Boyd -X-Patchwork-Id: 6063251 -Message-Id: <1426920332-9340-10-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar -Date: Fri, 20 Mar 2015 23:45:28 -0700 - -The Krait clocks are made up of a series of muxes and a divider -that choose between a fixed rate clock and dedicated HFPLLs for -each CPU. Instead of using mmio accesses to remux parents, the -Krait implementation exposes the remux control via cp15 -registers. Support these clocks. - -Signed-off-by: Stephen Boyd - ---- -drivers/clk/qcom/Kconfig | 4 ++ - drivers/clk/qcom/Makefile | 1 + - drivers/clk/qcom/clk-krait.c | 166 +++++++++++++++++++++++++++++++++++++++++++ - drivers/clk/qcom/clk-krait.h | 49 +++++++++++++ - 4 files changed, 220 insertions(+) - create mode 100644 drivers/clk/qcom/clk-krait.c - create mode 100644 drivers/clk/qcom/clk-krait.h - ---- a/drivers/clk/qcom/Kconfig -+++ b/drivers/clk/qcom/Kconfig -@@ -143,3 +143,7 @@ config QCOM_HFPLL - Support for the high-frequency PLLs present on Qualcomm devices. - Say Y if you want to support CPU frequency scaling on devices - such as MSM8974, APQ8084, etc. -+ -+config KRAIT_CLOCKS -+ bool -+ select KRAIT_L2_ACCESSORS ---- a/drivers/clk/qcom/Makefile -+++ b/drivers/clk/qcom/Makefile -@@ -8,6 +8,7 @@ clk-qcom-y += clk-rcg2.o - clk-qcom-y += clk-branch.o - clk-qcom-y += clk-regmap-divider.o - clk-qcom-y += clk-regmap-mux.o -+clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o - clk-qcom-y += clk-hfpll.o - clk-qcom-y += reset.o - clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o ---- /dev/null -+++ b/drivers/clk/qcom/clk-krait.c -@@ -0,0 +1,167 @@ -+/* -+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include "clk-krait.h" -+ -+/* Secondary and primary muxes share the same cp15 register */ -+static DEFINE_SPINLOCK(krait_clock_reg_lock); -+ -+#define LPL_SHIFT 8 -+static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel) -+{ -+ unsigned long flags; -+ u32 regval; -+ -+ spin_lock_irqsave(&krait_clock_reg_lock, flags); -+ regval = krait_get_l2_indirect_reg(mux->offset); -+ regval &= ~(mux->mask << mux->shift); -+ regval |= (sel & mux->mask) << mux->shift; -+ if (mux->lpl) { -+ regval &= ~(mux->mask << (mux->shift + LPL_SHIFT)); -+ regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT); -+ } -+ krait_set_l2_indirect_reg(mux->offset, regval); -+ spin_unlock_irqrestore(&krait_clock_reg_lock, flags); -+ -+ /* Wait for switch to complete. */ -+ mb(); -+ udelay(1); -+} -+ -+static int krait_mux_set_parent(struct clk_hw *hw, u8 index) -+{ -+ struct krait_mux_clk *mux = to_krait_mux_clk(hw); -+ u32 sel; -+ -+ sel = clk_mux_reindex(index, mux->parent_map, 0); -+ mux->en_mask = sel; -+ /* Don't touch mux if CPU is off as it won't work */ -+ if (__clk_is_enabled(hw->clk)) -+ __krait_mux_set_sel(mux, sel); -+ return 0; -+} -+ -+static u8 krait_mux_get_parent(struct clk_hw *hw) -+{ -+ struct krait_mux_clk *mux = to_krait_mux_clk(hw); -+ u32 sel; -+ -+ sel = krait_get_l2_indirect_reg(mux->offset); -+ sel >>= mux->shift; -+ sel &= mux->mask; -+ mux->en_mask = sel; -+ -+ return clk_mux_get_parent(hw, sel, mux->parent_map, 0); -+} -+ -+static struct clk_hw *krait_mux_get_safe_parent(struct clk_hw *hw, -+ unsigned long *safe_freq) -+{ -+ int i; -+ struct krait_mux_clk *mux = to_krait_mux_clk(hw); -+ int num_parents = clk_hw_get_num_parents(hw); -+ -+ i = mux->safe_sel; -+ for (i = 0; i < num_parents; i++) -+ if (mux->safe_sel == mux->parent_map[i]) -+ break; -+ -+ return clk_hw_get_parent_by_index(hw, i); -+} -+ -+static int krait_mux_enable(struct clk_hw *hw) -+{ -+ struct krait_mux_clk *mux = to_krait_mux_clk(hw); -+ -+ __krait_mux_set_sel(mux, mux->en_mask); -+ -+ return 0; -+} -+ -+static void krait_mux_disable(struct clk_hw *hw) -+{ -+ struct krait_mux_clk *mux = to_krait_mux_clk(hw); -+ -+ __krait_mux_set_sel(mux, mux->safe_sel); -+} -+ -+const struct clk_ops krait_mux_clk_ops = { -+ .enable = krait_mux_enable, -+ .disable = krait_mux_disable, -+ .set_parent = krait_mux_set_parent, -+ .get_parent = krait_mux_get_parent, -+ .determine_rate = __clk_mux_determine_rate_closest, -+ .get_safe_parent = krait_mux_get_safe_parent, -+}; -+EXPORT_SYMBOL_GPL(krait_mux_clk_ops); -+ -+/* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */ -+static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long *parent_rate) -+{ -+ *parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), rate * 2); -+ return DIV_ROUND_UP(*parent_rate, 2); -+} -+ -+static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long parent_rate) -+{ -+ struct krait_div2_clk *d = to_krait_div2_clk(hw); -+ unsigned long flags; -+ u32 val; -+ u32 mask = BIT(d->width) - 1; -+ -+ if (d->lpl) -+ mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift; -+ -+ spin_lock_irqsave(&krait_clock_reg_lock, flags); -+ val = krait_get_l2_indirect_reg(d->offset); -+ val &= ~mask; -+ krait_set_l2_indirect_reg(d->offset, val); -+ spin_unlock_irqrestore(&krait_clock_reg_lock, flags); -+ -+ return 0; -+} -+ -+static unsigned long -+krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) -+{ -+ struct krait_div2_clk *d = to_krait_div2_clk(hw); -+ u32 mask = BIT(d->width) - 1; -+ u32 div; -+ -+ div = krait_get_l2_indirect_reg(d->offset); -+ div >>= d->shift; -+ div &= mask; -+ div = (div + 1) * 2; -+ -+ return DIV_ROUND_UP(parent_rate, div); -+} -+ -+const struct clk_ops krait_div2_clk_ops = { -+ .round_rate = krait_div2_round_rate, -+ .set_rate = krait_div2_set_rate, -+ .recalc_rate = krait_div2_recalc_rate, -+}; -+EXPORT_SYMBOL_GPL(krait_div2_clk_ops); ---- /dev/null -+++ b/drivers/clk/qcom/clk-krait.h -@@ -0,0 +1,49 @@ -+/* -+ * Copyright (c) 2013, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#ifndef __QCOM_CLK_KRAIT_H -+#define __QCOM_CLK_KRAIT_H -+ -+#include -+ -+struct krait_mux_clk { -+ unsigned int *parent_map; -+ bool has_safe_parent; -+ u8 safe_sel; -+ u32 offset; -+ u32 mask; -+ u32 shift; -+ u32 en_mask; -+ bool lpl; -+ -+ struct clk_hw hw; -+}; -+ -+#define to_krait_mux_clk(_hw) container_of(_hw, struct krait_mux_clk, hw) -+ -+extern const struct clk_ops krait_mux_clk_ops; -+ -+struct krait_div2_clk { -+ u32 offset; -+ u8 width; -+ u32 shift; -+ bool lpl; -+ -+ struct clk_hw hw; -+}; -+ -+#define to_krait_div2_clk(_hw) container_of(_hw, struct krait_div2_clk, hw) -+ -+extern const struct clk_ops krait_div2_clk_ops; -+ -+#endif diff --git a/target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch b/target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch deleted file mode 100644 index 56374c5d0c..0000000000 --- a/target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch +++ /dev/null @@ -1,207 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,10/13] clk: qcom: Add KPSS ACC/GCC driver -From: Stephen Boyd -X-Patchwork-Id: 6063201 -Message-Id: <1426920332-9340-11-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar , -Date: Fri, 20 Mar 2015 23:45:29 -0700 - -The ACC and GCC regions present in KPSSv1 contain registers to -control clocks and power to each Krait CPU and L2. For CPUfreq -purposes probe these devices and expose a mux clock that chooses -between PXO and PLL8. - -Cc: -Signed-off-by: Stephen Boyd - ---- -.../devicetree/bindings/arm/msm/qcom,kpss-acc.txt | 7 ++ - .../devicetree/bindings/arm/msm/qcom,kpss-gcc.txt | 28 +++++++ - drivers/clk/qcom/Kconfig | 8 ++ - drivers/clk/qcom/Makefile | 1 + - drivers/clk/qcom/kpss-xcc.c | 95 ++++++++++++++++++++++ - 5 files changed, 139 insertions(+) - create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt - create mode 100644 drivers/clk/qcom/kpss-xcc.c - ---- a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt -+++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt -@@ -21,10 +21,17 @@ PROPERTIES - the register region. An optional second element specifies - the base address and size of the alias register region. - -+- clock-output-names: -+ Usage: optional -+ Value type: -+ Definition: Name of the output clock. Typically acpuX_aux where X is a -+ CPU number starting at 0. -+ - Example: - - clock-controller@2088000 { - compatible = "qcom,kpss-acc-v2"; - reg = <0x02088000 0x1000>, - <0x02008000 0x1000>; -+ clock-output-names = "acpu0_aux"; - }; ---- /dev/null -+++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt -@@ -0,0 +1,28 @@ -+Krait Processor Sub-system (KPSS) Global Clock Controller (GCC) -+ -+PROPERTIES -+ -+- compatible: -+ Usage: required -+ Value type: -+ Definition: should be one of: -+ "qcom,kpss-gcc" -+ -+- reg: -+ Usage: required -+ Value type: -+ Definition: base address and size of the register region -+ -+- clock-output-names: -+ Usage: required -+ Value type: -+ Definition: Name of the output clock. Typically acpu_l2_aux indicating -+ an L2 cache auxiliary clock. -+ -+Example: -+ -+ l2cc: clock-controller@2011000 { -+ compatible = "qcom,kpss-gcc"; -+ reg = <0x2011000 0x1000>; -+ clock-output-names = "acpu_l2_aux"; -+ }; ---- a/drivers/clk/qcom/Kconfig -+++ b/drivers/clk/qcom/Kconfig -@@ -144,6 +144,14 @@ config QCOM_HFPLL - Say Y if you want to support CPU frequency scaling on devices - such as MSM8974, APQ8084, etc. - -+config KPSS_XCC -+ tristate "KPSS Clock Controller" -+ depends on COMMON_CLK_QCOM -+ help -+ Support for the Krait ACC and GCC clock controllers. Say Y -+ if you want to support CPU frequency scaling on devices such -+ as MSM8960, APQ8064, etc. -+ - config KRAIT_CLOCKS - bool - select KRAIT_L2_ACCESSORS ---- a/drivers/clk/qcom/Makefile -+++ b/drivers/clk/qcom/Makefile -@@ -9,6 +9,7 @@ clk-qcom-y += clk-branch.o - clk-qcom-y += clk-regmap-divider.o - clk-qcom-y += clk-regmap-mux.o - clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o -+obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o - clk-qcom-y += clk-hfpll.o - clk-qcom-y += reset.o - clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o ---- /dev/null -+++ b/drivers/clk/qcom/kpss-xcc.c -@@ -0,0 +1,95 @@ -+/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+static const char *aux_parents[] = { -+ "pll8_vote", -+ "pxo", -+}; -+ -+static unsigned int aux_parent_map[] = { -+ 3, -+ 0, -+}; -+ -+static const struct of_device_id kpss_xcc_match_table[] = { -+ { .compatible = "qcom,kpss-acc-v1", .data = (void *)1UL }, -+ { .compatible = "qcom,kpss-gcc" }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, kpss_xcc_match_table); -+ -+static int kpss_xcc_driver_probe(struct platform_device *pdev) -+{ -+ const struct of_device_id *id; -+ struct clk *clk; -+ struct resource *res; -+ void __iomem *base; -+ const char *name; -+ -+ id = of_match_device(kpss_xcc_match_table, &pdev->dev); -+ if (!id) -+ return -ENODEV; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ base = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(base)) -+ return PTR_ERR(base); -+ -+ if (id->data) { -+ if (of_property_read_string_index(pdev->dev.of_node, -+ "clock-output-names", 0, &name)) -+ return -ENODEV; -+ base += 0x14; -+ } else { -+ name = "acpu_l2_aux"; -+ base += 0x28; -+ } -+ -+ clk = clk_register_mux_table(&pdev->dev, name, aux_parents, -+ ARRAY_SIZE(aux_parents), 0, base, 0, 0x3, -+ 0, aux_parent_map, NULL); -+ -+ platform_set_drvdata(pdev, clk); -+ -+ return PTR_ERR_OR_ZERO(clk); -+} -+ -+static int kpss_xcc_driver_remove(struct platform_device *pdev) -+{ -+ clk_unregister_mux(platform_get_drvdata(pdev)); -+ return 0; -+} -+ -+static struct platform_driver kpss_xcc_driver = { -+ .probe = kpss_xcc_driver_probe, -+ .remove = kpss_xcc_driver_remove, -+ .driver = { -+ .name = "kpss-xcc", -+ .of_match_table = kpss_xcc_match_table, -+ }, -+}; -+module_platform_driver(kpss_xcc_driver); -+ -+MODULE_DESCRIPTION("Krait Processor Sub System (KPSS) Clock Driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:kpss-xcc"); diff --git a/target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch deleted file mode 100644 index bd4e7bca3d..0000000000 --- a/target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch +++ /dev/null @@ -1,435 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,11/13] clk: qcom: Add Krait clock controller driver -From: Stephen Boyd -X-Patchwork-Id: 6063121 -Message-Id: <1426920332-9340-12-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar , -Date: Fri, 20 Mar 2015 23:45:30 -0700 - -The Krait CPU clocks are made up of a primary mux and secondary -mux for each CPU and the L2, controlled via cp15 accessors. For -Kraits within KPSSv1 each secondary mux accepts a different aux -source, but on KPSSv2 each secondary mux accepts the same aux -source. - -Cc: -Signed-off-by: Stephen Boyd - ---- -.../devicetree/bindings/clock/qcom,krait-cc.txt | 22 ++ - drivers/clk/qcom/Kconfig | 8 + - drivers/clk/qcom/Makefile | 1 + - drivers/clk/qcom/krait-cc.c | 352 +++++++++++++++++++++ - 4 files changed, 383 insertions(+) - create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt - create mode 100644 drivers/clk/qcom/krait-cc.c - ---- /dev/null -+++ b/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt -@@ -0,0 +1,22 @@ -+Krait Clock Controller -+ -+PROPERTIES -+ -+- compatible: -+ Usage: required -+ Value type: -+ Definition: must be one of: -+ "qcom,krait-cc-v1" -+ "qcom,krait-cc-v2" -+ -+- #clock-cells: -+ Usage: required -+ Value type: -+ Definition: must be 1 -+ -+Example: -+ -+ kraitcc: clock-controller { -+ compatible = "qcom,krait-cc-v1"; -+ #clock-cells = <1>; -+ }; ---- a/drivers/clk/qcom/Kconfig -+++ b/drivers/clk/qcom/Kconfig -@@ -152,6 +152,14 @@ config KPSS_XCC - if you want to support CPU frequency scaling on devices such - as MSM8960, APQ8064, etc. - -+config KRAITCC -+ tristate "Krait Clock Controller" -+ depends on COMMON_CLK_QCOM && ARM -+ select KRAIT_CLOCKS -+ help -+ Support for the Krait CPU clocks on Qualcomm devices. -+ Say Y if you want to support CPU frequency scaling. -+ - config KRAIT_CLOCKS - bool - select KRAIT_L2_ACCESSORS ---- a/drivers/clk/qcom/Makefile -+++ b/drivers/clk/qcom/Makefile -@@ -28,3 +28,4 @@ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8 - obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o - obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o - obj-$(CONFIG_QCOM_HFPLL) += hfpll.o -+obj-$(CONFIG_KRAITCC) += krait-cc.o ---- /dev/null -+++ b/drivers/clk/qcom/krait-cc.c -@@ -0,0 +1,352 @@ -+/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "clk-krait.h" -+ -+static unsigned int sec_mux_map[] = { -+ 2, -+ 0, -+}; -+ -+static unsigned int pri_mux_map[] = { -+ 1, -+ 2, -+ 0, -+}; -+ -+static int -+krait_add_div(struct device *dev, int id, const char *s, unsigned offset) -+{ -+ struct krait_div2_clk *div; -+ struct clk_init_data init = { -+ .num_parents = 1, -+ .ops = &krait_div2_clk_ops, -+ .flags = CLK_SET_RATE_PARENT, -+ }; -+ const char *p_names[1]; -+ struct clk *clk; -+ -+ div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL); -+ if (!div) -+ return -ENOMEM; -+ -+ div->width = 2; -+ div->shift = 6; -+ div->lpl = id >= 0; -+ div->offset = offset; -+ div->hw.init = &init; -+ -+ init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s); -+ if (!init.name) -+ return -ENOMEM; -+ -+ init.parent_names = p_names; -+ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s); -+ if (!p_names[0]) { -+ kfree(init.name); -+ return -ENOMEM; -+ } -+ -+ clk = devm_clk_register(dev, &div->hw); -+ kfree(p_names[0]); -+ kfree(init.name); -+ -+ return PTR_ERR_OR_ZERO(clk); -+} -+ -+static int -+krait_add_sec_mux(struct device *dev, int id, const char *s, unsigned offset, -+ bool unique_aux) -+{ -+ struct krait_mux_clk *mux; -+ static const char *sec_mux_list[] = { -+ "acpu_aux", -+ "qsb", -+ }; -+ struct clk_init_data init = { -+ .parent_names = sec_mux_list, -+ .num_parents = ARRAY_SIZE(sec_mux_list), -+ .ops = &krait_mux_clk_ops, -+ .flags = CLK_SET_RATE_PARENT, -+ }; -+ struct clk *clk; -+ -+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); -+ if (!mux) -+ return -ENOMEM; -+ -+ mux->offset = offset; -+ mux->lpl = id >= 0; -+ mux->has_safe_parent = true; -+ mux->safe_sel = 2; -+ mux->mask = 0x3; -+ mux->shift = 2; -+ mux->parent_map = sec_mux_map; -+ mux->hw.init = &init; -+ -+ init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); -+ if (!init.name) -+ return -ENOMEM; -+ -+ if (unique_aux) { -+ sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s); -+ if (!sec_mux_list[0]) { -+ clk = ERR_PTR(-ENOMEM); -+ goto err_aux; -+ } -+ } -+ -+ clk = devm_clk_register(dev, &mux->hw); -+ -+ if (unique_aux) -+ kfree(sec_mux_list[0]); -+err_aux: -+ kfree(init.name); -+ return PTR_ERR_OR_ZERO(clk); -+} -+ -+static struct clk * -+krait_add_pri_mux(struct device *dev, int id, const char *s, unsigned offset) -+{ -+ struct krait_mux_clk *mux; -+ const char *p_names[3]; -+ struct clk_init_data init = { -+ .parent_names = p_names, -+ .num_parents = ARRAY_SIZE(p_names), -+ .ops = &krait_mux_clk_ops, -+ .flags = CLK_SET_RATE_PARENT, -+ }; -+ struct clk *clk; -+ -+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); -+ if (!mux) -+ return ERR_PTR(-ENOMEM); -+ -+ mux->has_safe_parent = true; -+ mux->safe_sel = 0; -+ mux->mask = 0x3; -+ mux->shift = 0; -+ mux->offset = offset; -+ mux->lpl = id >= 0; -+ mux->parent_map = pri_mux_map; -+ mux->hw.init = &init; -+ -+ init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s); -+ if (!init.name) -+ return ERR_PTR(-ENOMEM); -+ -+ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s); -+ if (!p_names[0]) { -+ clk = ERR_PTR(-ENOMEM); -+ goto err_p0; -+ } -+ -+ p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s); -+ if (!p_names[1]) { -+ clk = ERR_PTR(-ENOMEM); -+ goto err_p1; -+ } -+ -+ p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); -+ if (!p_names[2]) { -+ clk = ERR_PTR(-ENOMEM); -+ goto err_p2; -+ } -+ -+ clk = devm_clk_register(dev, &mux->hw); -+ -+ kfree(p_names[2]); -+err_p2: -+ kfree(p_names[1]); -+err_p1: -+ kfree(p_names[0]); -+err_p0: -+ kfree(init.name); -+ return clk; -+} -+ -+/* id < 0 for L2, otherwise id == physical CPU number */ -+static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux) -+{ -+ int ret; -+ unsigned offset; -+ void *p = NULL; -+ const char *s; -+ struct clk *clk; -+ -+ if (id >= 0) { -+ offset = 0x4501 + (0x1000 * id); -+ s = p = kasprintf(GFP_KERNEL, "%d", id); -+ if (!s) -+ return ERR_PTR(-ENOMEM); -+ } else { -+ offset = 0x500; -+ s = "_l2"; -+ } -+ -+ ret = krait_add_div(dev, id, s, offset); -+ if (ret) { -+ clk = ERR_PTR(ret); -+ goto err; -+ } -+ -+ ret = krait_add_sec_mux(dev, id, s, offset, unique_aux); -+ if (ret) { -+ clk = ERR_PTR(ret); -+ goto err; -+ } -+ -+ clk = krait_add_pri_mux(dev, id, s, offset); -+err: -+ kfree(p); -+ return clk; -+} -+ -+static struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data) -+{ -+ unsigned int idx = clkspec->args[0]; -+ struct clk **clks = data; -+ -+ if (idx >= 5) { -+ pr_err("%s: invalid clock index %d\n", __func__, idx); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ return clks[idx] ? : ERR_PTR(-ENODEV); -+} -+ -+static const struct of_device_id krait_cc_match_table[] = { -+ { .compatible = "qcom,krait-cc-v1", (void *)1UL }, -+ { .compatible = "qcom,krait-cc-v2" }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, krait_cc_match_table); -+ -+static int krait_cc_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ const struct of_device_id *id; -+ unsigned long cur_rate, aux_rate; -+ int cpu; -+ struct clk *clk; -+ struct clk **clks; -+ struct clk *l2_pri_mux_clk; -+ -+ id = of_match_device(krait_cc_match_table, dev); -+ if (!id) -+ return -ENODEV; -+ -+ /* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */ -+ clk = clk_register_fixed_rate(dev, "qsb", NULL, CLK_IS_ROOT, 1); -+ if (IS_ERR(clk)) -+ return PTR_ERR(clk); -+ -+ if (!id->data) { -+ clk = clk_register_fixed_factor(dev, "acpu_aux", -+ "gpll0_vote", 0, 1, 2); -+ if (IS_ERR(clk)) -+ return PTR_ERR(clk); -+ } -+ -+ /* Krait configurations have at most 4 CPUs and one L2 */ -+ clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL); -+ if (!clks) -+ return -ENOMEM; -+ -+ for_each_possible_cpu(cpu) { -+ clk = krait_add_clks(dev, cpu, id->data); -+ if (IS_ERR(clk)) -+ return PTR_ERR(clk); -+ clks[cpu] = clk; -+ } -+ -+ l2_pri_mux_clk = krait_add_clks(dev, -1, id->data); -+ if (IS_ERR(l2_pri_mux_clk)) -+ return PTR_ERR(l2_pri_mux_clk); -+ clks[4] = l2_pri_mux_clk; -+ -+ /* -+ * We don't want the CPU or L2 clocks to be turned off at late init -+ * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the -+ * refcount of these clocks. Any cpufreq/hotplug manager can assume -+ * that the clocks have already been prepared and enabled by the time -+ * they take over. -+ */ -+ for_each_online_cpu(cpu) { -+ clk_prepare_enable(l2_pri_mux_clk); -+ WARN(clk_prepare_enable(clks[cpu]), -+ "Unable to turn on CPU%d clock", cpu); -+ } -+ -+ /* -+ * Force reinit of HFPLLs and muxes to overwrite any potential -+ * incorrect configuration of HFPLLs and muxes by the bootloader. -+ * While at it, also make sure the cores are running at known rates -+ * and print the current rate. -+ * -+ * The clocks are set to aux clock rate first to make sure the -+ * secondary mux is not sourcing off of QSB. The rate is then set to -+ * two different rates to force a HFPLL reinit under all -+ * circumstances. -+ */ -+ cur_rate = clk_get_rate(l2_pri_mux_clk); -+ aux_rate = 384000000; -+ if (cur_rate == 1) { -+ pr_info("L2 @ QSB rate. Forcing new rate.\n"); -+ cur_rate = aux_rate; -+ } -+ clk_set_rate(l2_pri_mux_clk, aux_rate); -+ clk_set_rate(l2_pri_mux_clk, 2); -+ clk_set_rate(l2_pri_mux_clk, cur_rate); -+ pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000); -+ for_each_possible_cpu(cpu) { -+ clk = clks[cpu]; -+ cur_rate = clk_get_rate(clk); -+ if (cur_rate == 1) { -+ pr_info("CPU%d @ QSB rate. Forcing new rate.\n", cpu); -+ cur_rate = aux_rate; -+ } -+ clk_set_rate(clk, aux_rate); -+ clk_set_rate(clk, 2); -+ clk_set_rate(clk, cur_rate); -+ pr_info("CPU%d @ %lu KHz\n", cpu, clk_get_rate(clk) / 1000); -+ } -+ -+ of_clk_add_provider(dev->of_node, krait_of_get, clks); -+ -+ return 0; -+} -+ -+static struct platform_driver krait_cc_driver = { -+ .probe = krait_cc_probe, -+ .driver = { -+ .name = "krait-cc", -+ .of_match_table = krait_cc_match_table, -+ }, -+}; -+module_platform_driver(krait_cc_driver); -+ -+MODULE_DESCRIPTION("Krait CPU Clock Driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:krait-cc"); diff --git a/target/linux/ipq806x/patches-4.4/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch b/target/linux/ipq806x/patches-4.4/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch deleted file mode 100644 index e8d8ddf674..0000000000 --- a/target/linux/ipq806x/patches-4.4/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch +++ /dev/null @@ -1,304 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,12/13] cpufreq: Add module to register cpufreq on Krait CPUs -From: Stephen Boyd -X-Patchwork-Id: 6063191 -Message-Id: <1426920332-9340-13-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar , -Date: Fri, 20 Mar 2015 23:45:31 -0700 - -Register a cpufreq-generic device whenever we detect that a -"qcom,krait" compatible CPU is present in DT. - -Cc: -Signed-off-by: Stephen Boyd - ---- -.../devicetree/bindings/arm/msm/qcom,pvs.txt | 38 ++++ - drivers/cpufreq/Kconfig.arm | 9 + - drivers/cpufreq/Makefile | 1 + - drivers/cpufreq/qcom-cpufreq.c | 204 +++++++++++++++++++++ - 4 files changed, 252 insertions(+) - create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt - create mode 100644 drivers/cpufreq/qcom-cpufreq.c - ---- /dev/null -+++ b/Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt -@@ -0,0 +1,38 @@ -+Qualcomm Process Voltage Scaling Tables -+ -+The node name is required to be "qcom,pvs". There shall only be one -+such node present in the root of the tree. -+ -+PROPERTIES -+ -+- qcom,pvs-format-a or qcom,pvs-format-b: -+ Usage: required -+ Value type: -+ Definition: Indicates the format of qcom,speedX-pvsY-bin-vZ properties. -+ If qcom,pvs-format-a is used the table is two columns -+ (frequency and voltage in that order). If qcom,pvs-format-b is used the table is three columns (frequency, voltage, -+ and current in that order). -+ -+- qcom,speedX-pvsY-bin-vZ: -+ Usage: required -+ Value type: -+ Definition: The PVS table corresponding to the speed bin X, pvs bin Y, -+ and version Z. -+Example: -+ -+ qcom,pvs { -+ qcom,pvs-format-a; -+ qcom,speed0-pvs0-bin-v0 = -+ < 384000000 950000 >, -+ < 486000000 975000 >, -+ < 594000000 1000000 >, -+ < 702000000 1025000 >, -+ < 810000000 1075000 >, -+ < 918000000 1100000 >, -+ < 1026000000 1125000 >, -+ < 1134000000 1175000 >, -+ < 1242000000 1200000 >, -+ < 1350000000 1225000 >, -+ < 1458000000 1237500 >, -+ < 1512000000 1250000 >; -+ }; ---- a/drivers/cpufreq/Kconfig.arm -+++ b/drivers/cpufreq/Kconfig.arm -@@ -95,6 +95,15 @@ config ARM_OMAP2PLUS_CPUFREQ - depends on ARCH_OMAP2PLUS - default ARCH_OMAP2PLUS - -+config ARM_QCOM_CPUFREQ -+ tristate "Qualcomm based" -+ depends on ARCH_QCOM -+ select PM_OPP -+ help -+ This adds the CPUFreq driver for Qualcomm SoC based boards. -+ -+ If in doubt, say N. -+ - config ARM_S3C_CPUFREQ - bool - help ---- a/drivers/cpufreq/Makefile -+++ b/drivers/cpufreq/Makefile -@@ -61,6 +61,7 @@ obj-$(CONFIG_ARM_MT8173_CPUFREQ) += mt81 - obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o - obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o - obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o -+obj-$(CONFIG_ARM_QCOM_CPUFREQ) += qcom-cpufreq.o - obj-$(CONFIG_ARM_S3C24XX_CPUFREQ) += s3c24xx-cpufreq.o - obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o - obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o ---- /dev/null -+++ b/drivers/cpufreq/qcom-cpufreq.c -@@ -0,0 +1,204 @@ -+/* Copyright (c) 2014, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+static void __init get_krait_bin_format_a(int *speed, int *pvs, int *pvs_ver) -+{ -+ void __iomem *base; -+ u32 pte_efuse; -+ -+ *speed = *pvs = *pvs_ver = 0; -+ -+ base = ioremap(0x007000c0, 4); -+ if (!base) { -+ pr_warn("Unable to read efuse data. Defaulting to 0!\n"); -+ return; -+ } -+ -+ pte_efuse = readl_relaxed(base); -+ iounmap(base); -+ -+ *speed = pte_efuse & 0xf; -+ if (*speed == 0xf) -+ *speed = (pte_efuse >> 4) & 0xf; -+ -+ if (*speed == 0xf) { -+ *speed = 0; -+ pr_warn("Speed bin: Defaulting to %d\n", *speed); -+ } else { -+ pr_info("Speed bin: %d\n", *speed); -+ } -+ -+ *pvs = (pte_efuse >> 10) & 0x7; -+ if (*pvs == 0x7) -+ *pvs = (pte_efuse >> 13) & 0x7; -+ -+ if (*pvs == 0x7) { -+ *pvs = 0; -+ pr_warn("PVS bin: Defaulting to %d\n", *pvs); -+ } else { -+ pr_info("PVS bin: %d\n", *pvs); -+ } -+} -+ -+static void __init get_krait_bin_format_b(int *speed, int *pvs, int *pvs_ver) -+{ -+ u32 pte_efuse, redundant_sel; -+ void __iomem *base; -+ -+ *speed = 0; -+ *pvs = 0; -+ *pvs_ver = 0; -+ -+ base = ioremap(0xfc4b80b0, 8); -+ if (!base) { -+ pr_warn("Unable to read efuse data. Defaulting to 0!\n"); -+ return; -+ } -+ -+ pte_efuse = readl_relaxed(base); -+ redundant_sel = (pte_efuse >> 24) & 0x7; -+ *speed = pte_efuse & 0x7; -+ /* 4 bits of PVS are in efuse register bits 31, 8-6. */ -+ *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7); -+ *pvs_ver = (pte_efuse >> 4) & 0x3; -+ -+ switch (redundant_sel) { -+ case 1: -+ *speed = (pte_efuse >> 27) & 0xf; -+ break; -+ case 2: -+ *pvs = (pte_efuse >> 27) & 0xf; -+ break; -+ } -+ -+ /* Check SPEED_BIN_BLOW_STATUS */ -+ if (pte_efuse & BIT(3)) { -+ pr_info("Speed bin: %d\n", *speed); -+ } else { -+ pr_warn("Speed bin not set. Defaulting to 0!\n"); -+ *speed = 0; -+ } -+ -+ /* Check PVS_BLOW_STATUS */ -+ pte_efuse = readl_relaxed(base + 0x4) & BIT(21); -+ if (pte_efuse) { -+ pr_info("PVS bin: %d\n", *pvs); -+ } else { -+ pr_warn("PVS bin not set. Defaulting to 0!\n"); -+ *pvs = 0; -+ } -+ -+ pr_info("PVS version: %d\n", *pvs_ver); -+ iounmap(base); -+} -+ -+static int __init qcom_cpufreq_populate_opps(void) -+{ -+ int len, rows, cols, i, k, speed, pvs, pvs_ver; -+ char table_name[] = "qcom,speedXX-pvsXX-bin-vXX"; -+ struct device_node *np; -+ struct device *dev; -+ int cpu = 0; -+ -+ np = of_find_node_by_name(NULL, "qcom,pvs"); -+ if (!np) -+ return -ENODEV; -+ -+ if (of_property_read_bool(np, "qcom,pvs-format-a")) { -+ get_krait_bin_format_a(&speed, &pvs, &pvs_ver); -+ cols = 2; -+ } else if (of_property_read_bool(np, "qcom,pvs-format-b")) { -+ get_krait_bin_format_b(&speed, &pvs, &pvs_ver); -+ cols = 3; -+ } else { -+ return -ENODEV; -+ } -+ -+ snprintf(table_name, sizeof(table_name), -+ "qcom,speed%d-pvs%d-bin-v%d", speed, pvs, pvs_ver); -+ -+ if (!of_find_property(np, table_name, &len)) -+ return -EINVAL; -+ -+ len /= sizeof(u32); -+ if (len % cols || len == 0) -+ return -EINVAL; -+ -+ rows = len / cols; -+ -+ for (i = 0, k = 0; i < rows; i++) { -+ u32 freq, volt; -+ -+ of_property_read_u32_index(np, table_name, k++, &freq); -+ of_property_read_u32_index(np, table_name, k++, &volt); -+ while (k % cols) -+ k++; /* Skip uA entries if present */ -+ for (cpu = 0; cpu < num_possible_cpus(); cpu++) { -+ dev = get_cpu_device(cpu); -+ if (!dev) -+ return -ENODEV; -+ if (dev_pm_opp_add(dev, freq, volt)) -+ pr_warn("failed to add OPP %u\n", freq); -+ } -+ } -+ -+ return 0; -+} -+ -+static int __init qcom_cpufreq_driver_init(void) -+{ -+ struct cpufreq_dt_platform_data pdata = { .independent_clocks = true }; -+ struct platform_device_info devinfo = { -+ .name = "cpufreq-dt", -+ .data = &pdata, -+ .size_data = sizeof(pdata), -+ }; -+ struct device *cpu_dev; -+ struct device_node *np; -+ int ret; -+ -+ cpu_dev = get_cpu_device(0); -+ if (!cpu_dev) -+ return -ENODEV; -+ -+ np = of_node_get(cpu_dev->of_node); -+ if (!np) -+ return -ENOENT; -+ -+ if (!of_device_is_compatible(np, "qcom,krait")) { -+ of_node_put(np); -+ return -ENODEV; -+ } -+ of_node_put(np); -+ -+ ret = qcom_cpufreq_populate_opps(); -+ if (ret) -+ return ret; -+ -+ return PTR_ERR_OR_ZERO(platform_device_register_full(&devinfo)); -+} -+module_init(qcom_cpufreq_driver_init); -+ -+MODULE_DESCRIPTION("Qualcomm CPUfreq driver"); -+MODULE_LICENSE("GPL v2"); diff --git a/target/linux/ipq806x/patches-4.4/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch b/target/linux/ipq806x/patches-4.4/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch deleted file mode 100644 index 45870c436a..0000000000 --- a/target/linux/ipq806x/patches-4.4/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch +++ /dev/null @@ -1,96 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -26,6 +26,11 @@ - next-level-cache = <&L2>; - qcom,acc = <&acc0>; - qcom,saw = <&saw0>; -+ clocks = <&kraitcc 0>, <&kraitcc 4>; -+ clock-names = "cpu", "l2"; -+ clock-latency = <100000>; -+ cpu-supply = <&smb208_s2a>; -+ voltage-tolerance = <5>; - }; - - cpu@1 { -@@ -36,12 +41,20 @@ - next-level-cache = <&L2>; - qcom,acc = <&acc1>; - qcom,saw = <&saw1>; -+ clocks = <&kraitcc 1>, <&kraitcc 4>; -+ clock-names = "cpu", "l2"; -+ clock-latency = <100000>; -+ cpu-supply = <&smb208_s2b>; - }; - - L2: l2-cache { - compatible = "cache"; - cache-level = <2>; - }; -+ -+ qcom,l2 { -+ qcom,l2-rates = <384000000 1000000000 1200000000>; -+ }; - }; - - cpu-pmu { -@@ -73,6 +86,46 @@ - }; - }; - -+ kraitcc: clock-controller { -+ compatible = "qcom,krait-cc-v1"; -+ #clock-cells = <1>; -+ }; -+ -+ qcom,pvs { -+ qcom,pvs-format-a; -+ qcom,speed0-pvs0-bin-v0 = -+ < 1400000000 1250000 >, -+ < 1200000000 1200000 >, -+ < 1000000000 1150000 >, -+ < 800000000 1100000 >, -+ < 600000000 1050000 >, -+ < 384000000 1000000 >; -+ -+ qcom,speed0-pvs1-bin-v0 = -+ < 1400000000 1175000 >, -+ < 1200000000 1125000 >, -+ < 1000000000 1075000 >, -+ < 800000000 1025000 >, -+ < 600000000 975000 >, -+ < 384000000 925000 >; -+ -+ qcom,speed0-pvs2-bin-v0 = -+ < 1400000000 1125000 >, -+ < 1200000000 1075000 >, -+ < 1000000000 1025000 >, -+ < 800000000 995000 >, -+ < 600000000 925000 >, -+ < 384000000 875000 >; -+ -+ qcom,speed0-pvs3-bin-v0 = -+ < 1400000000 1050000 >, -+ < 1200000000 1000000 >, -+ < 1000000000 950000 >, -+ < 800000000 900000 >, -+ < 600000000 850000 >, -+ < 384000000 800000 >; -+ }; -+ - soc: soc { - #address-cells = <1>; - #size-cells = <1>; -@@ -216,11 +269,13 @@ - acc0: clock-controller@2088000 { - compatible = "qcom,kpss-acc-v1"; - reg = <0x02088000 0x1000>, <0x02008000 0x1000>; -+ clock-output-names = "acpu0_aux"; - }; - - acc1: clock-controller@2098000 { - compatible = "qcom,kpss-acc-v1"; - reg = <0x02098000 0x1000>, <0x02008000 0x1000>; -+ clock-output-names = "acpu1_aux"; - }; - - l2cc: clock-controller@2011000 { diff --git a/target/linux/ipq806x/patches-4.4/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch b/target/linux/ipq806x/patches-4.4/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch deleted file mode 100644 index 4f5c0efb7b..0000000000 --- a/target/linux/ipq806x/patches-4.4/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch +++ /dev/null @@ -1,76 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v6,1/2] dt/bindings: qcom_adm: Fix channel specifiers -From: Andy Gross -X-Patchwork-Id: 6027361 -Message-Id: <1426571172-9711-2-git-send-email-agross@codeaurora.org> -To: Vinod Koul -Cc: devicetree@vger.kernel.org, dmaengine@vger.kernel.org, - linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, Kumar Gala , - Bjorn Andersson , - Andy Gross -Date: Tue, 17 Mar 2015 00:46:11 -0500 - -This patch removes the crci information from the dma channel property. At least -one client device requires using more than one CRCI value for a channel. This -does not match the current binding and the crci information needs to be removed. - -Instead, the client device will provide this information via other means. - -Signed-off-by: Andy Gross - ---- -Documentation/devicetree/bindings/dma/qcom_adm.txt | 16 ++++++---------- - 1 file changed, 6 insertions(+), 10 deletions(-) - ---- a/Documentation/devicetree/bindings/dma/qcom_adm.txt -+++ b/Documentation/devicetree/bindings/dma/qcom_adm.txt -@@ -4,8 +4,7 @@ Required properties: - - compatible: must contain "qcom,adm" for IPQ/APQ8064 and MSM8960 - - reg: Address range for DMA registers - - interrupts: Should contain one interrupt shared by all channels --- #dma-cells: must be <2>. First cell denotes the channel number. Second cell -- denotes CRCI (client rate control interface) flow control assignment. -+- #dma-cells: must be <1>. First cell denotes the channel number. - - clocks: Should contain the core clock and interface clock. - - clock-names: Must contain "core" for the core clock and "iface" for the - interface clock. -@@ -22,7 +21,7 @@ Example: - compatible = "qcom,adm"; - reg = <0x18300000 0x100000>; - interrupts = <0 170 0>; -- #dma-cells = <2>; -+ #dma-cells = <1>; - - clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; - clock-names = "core", "iface"; -@@ -35,15 +34,12 @@ Example: - qcom,ee = <0>; - }; - --DMA clients must use the format descripted in the dma.txt file, using a three -+DMA clients must use the format descripted in the dma.txt file, using a two - cell specifier for each channel. - --Each dmas request consists of 3 cells: -+Each dmas request consists of two cells: - 1. phandle pointing to the DMA controller - 2. channel number -- 3. CRCI assignment, if applicable. If no CRCI flow control is required, use 0. -- The CRCI is used for flow control. It identifies the peripheral device that -- is the source/destination for the transferred data. - - Example: - -@@ -56,7 +52,7 @@ Example: - - cs-gpios = <&qcom_pinmux 20 0>; - -- dmas = <&adm_dma 6 9>, -- <&adm_dma 5 10>; -+ dmas = <&adm_dma 6>, -+ <&adm_dma 5>; - dma-names = "rx", "tx"; - }; diff --git a/target/linux/ipq806x/patches-4.4/156-dmaengine-Add-ADM-driver.patch b/target/linux/ipq806x/patches-4.4/156-dmaengine-Add-ADM-driver.patch deleted file mode 100644 index 02038aa942..0000000000 --- a/target/linux/ipq806x/patches-4.4/156-dmaengine-Add-ADM-driver.patch +++ /dev/null @@ -1,964 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v6,2/2] dmaengine: Add ADM driver -From: Andy Gross -X-Patchwork-Id: 6027351 -Message-Id: <1426571172-9711-3-git-send-email-agross@codeaurora.org> -To: Vinod Koul -Cc: devicetree@vger.kernel.org, dmaengine@vger.kernel.org, - linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, Kumar Gala , - Bjorn Andersson , - Andy Gross -Date: Tue, 17 Mar 2015 00:46:12 -0500 - -Add the DMA engine driver for the QCOM Application Data Mover (ADM) DMA -controller found in the MSM8x60 and IPQ/APQ8064 platforms. - -The ADM supports both memory to memory transactions and memory -to/from peripheral device transactions. The controller also provides flow -control capabilities for transactions to/from peripheral devices. - -The initial release of this driver supports slave transfers to/from peripherals -and also incorporates CRCI (client rate control interface) flow control. - -Signed-off-by: Andy Gross -Reviewed-by: sricharan - ---- -drivers/dma/Kconfig | 10 + - drivers/dma/Makefile | 1 + - drivers/dma/qcom_adm.c | 900 ++++++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 911 insertions(+) - create mode 100644 drivers/dma/qcom_adm.c - ---- a/drivers/dma/Kconfig -+++ b/drivers/dma/Kconfig -@@ -558,4 +558,14 @@ config DMATEST - config DMA_ENGINE_RAID - bool - -+config QCOM_ADM -+ tristate "Qualcomm ADM support" -+ depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM) -+ select DMA_ENGINE -+ select DMA_VIRTUAL_CHANNELS -+ ---help--- -+ Enable support for the Qualcomm ADM DMA controller. This controller -+ provides DMA capabilities for both general purpose and on-chip -+ peripheral devices. -+ - endif ---- /dev/null -+++ b/drivers/dma/qcom_adm.c -@@ -0,0 +1,900 @@ -+/* -+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "dmaengine.h" -+#include "virt-dma.h" -+ -+/* ADM registers - calculated from channel number and security domain */ -+#define ADM_CHAN_MULTI 0x4 -+#define ADM_CI_MULTI 0x4 -+#define ADM_CRCI_MULTI 0x4 -+#define ADM_EE_MULTI 0x800 -+#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * chan) -+#define ADM_EE_OFFS(ee) (ADM_EE_MULTI * ee) -+#define ADM_CHAN_EE_OFFS(chan, ee) (ADM_CHAN_OFFS(chan) + ADM_EE_OFFS(ee)) -+#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * chan) -+#define ADM_CI_OFFS(ci) (ADM_CHAN_OFF(ci)) -+#define ADM_CH_CMD_PTR(chan, ee) (ADM_CHAN_EE_OFFS(chan, ee)) -+#define ADM_CH_RSLT(chan, ee) (0x40 + ADM_CHAN_EE_OFFS(chan, ee)) -+#define ADM_CH_FLUSH_STATE0(chan, ee) (0x80 + ADM_CHAN_EE_OFFS(chan, ee)) -+#define ADM_CH_STATUS_SD(chan, ee) (0x200 + ADM_CHAN_EE_OFFS(chan, ee)) -+#define ADM_CH_CONF(chan) (0x240 + ADM_CHAN_OFFS(chan)) -+#define ADM_CH_RSLT_CONF(chan, ee) (0x300 + ADM_CHAN_EE_OFFS(chan, ee)) -+#define ADM_SEC_DOMAIN_IRQ_STATUS(ee) (0x380 + ADM_EE_OFFS(ee)) -+#define ADM_CI_CONF(ci) (0x390 + ci * ADM_CI_MULTI) -+#define ADM_GP_CTL 0x3d8 -+#define ADM_CRCI_CTL(crci, ee) (0x400 + crci * ADM_CRCI_MULTI + \ -+ ADM_EE_OFFS(ee)) -+ -+/* channel status */ -+#define ADM_CH_STATUS_VALID BIT(1) -+ -+/* channel result */ -+#define ADM_CH_RSLT_VALID BIT(31) -+#define ADM_CH_RSLT_ERR BIT(3) -+#define ADM_CH_RSLT_FLUSH BIT(2) -+#define ADM_CH_RSLT_TPD BIT(1) -+ -+/* channel conf */ -+#define ADM_CH_CONF_SHADOW_EN BIT(12) -+#define ADM_CH_CONF_MPU_DISABLE BIT(11) -+#define ADM_CH_CONF_PERM_MPU_CONF BIT(9) -+#define ADM_CH_CONF_FORCE_RSLT_EN BIT(7) -+#define ADM_CH_CONF_SEC_DOMAIN(ee) (((ee & 0x3) << 4) | ((ee & 0x4) << 11)) -+ -+/* channel result conf */ -+#define ADM_CH_RSLT_CONF_FLUSH_EN BIT(1) -+#define ADM_CH_RSLT_CONF_IRQ_EN BIT(0) -+ -+/* CRCI CTL */ -+#define ADM_CRCI_CTL_MUX_SEL BIT(18) -+#define ADM_CRCI_CTL_RST BIT(17) -+ -+/* CI configuration */ -+#define ADM_CI_RANGE_END(x) (x << 24) -+#define ADM_CI_RANGE_START(x) (x << 16) -+#define ADM_CI_BURST_4_WORDS BIT(2) -+#define ADM_CI_BURST_8_WORDS BIT(3) -+ -+/* GP CTL */ -+#define ADM_GP_CTL_LP_EN BIT(12) -+#define ADM_GP_CTL_LP_CNT(x) (x << 8) -+ -+/* Command pointer list entry */ -+#define ADM_CPLE_LP BIT(31) -+#define ADM_CPLE_CMD_PTR_LIST BIT(29) -+ -+/* Command list entry */ -+#define ADM_CMD_LC BIT(31) -+#define ADM_CMD_DST_CRCI(n) (((n) & 0xf) << 7) -+#define ADM_CMD_SRC_CRCI(n) (((n) & 0xf) << 3) -+ -+#define ADM_CMD_TYPE_SINGLE 0x0 -+#define ADM_CMD_TYPE_BOX 0x3 -+ -+#define ADM_CRCI_MUX_SEL BIT(4) -+#define ADM_DESC_ALIGN 8 -+#define ADM_MAX_XFER (SZ_64K-1) -+#define ADM_MAX_ROWS (SZ_64K-1) -+#define ADM_MAX_CHANNELS 16 -+ -+struct adm_desc_hw_box { -+ u32 cmd; -+ u32 src_addr; -+ u32 dst_addr; -+ u32 row_len; -+ u32 num_rows; -+ u32 row_offset; -+}; -+ -+struct adm_desc_hw_single { -+ u32 cmd; -+ u32 src_addr; -+ u32 dst_addr; -+ u32 len; -+}; -+ -+struct adm_async_desc { -+ struct virt_dma_desc vd; -+ struct adm_device *adev; -+ -+ size_t length; -+ enum dma_transfer_direction dir; -+ dma_addr_t dma_addr; -+ size_t dma_len; -+ -+ void *cpl; -+ dma_addr_t cp_addr; -+ u32 crci; -+ u32 mux; -+ u32 blk_size; -+}; -+ -+struct adm_chan { -+ struct virt_dma_chan vc; -+ struct adm_device *adev; -+ -+ /* parsed from DT */ -+ u32 id; /* channel id */ -+ -+ struct adm_async_desc *curr_txd; -+ struct dma_slave_config slave; -+ struct list_head node; -+ -+ int error; -+ int initialized; -+}; -+ -+static inline struct adm_chan *to_adm_chan(struct dma_chan *common) -+{ -+ return container_of(common, struct adm_chan, vc.chan); -+} -+ -+struct adm_device { -+ void __iomem *regs; -+ struct device *dev; -+ struct dma_device common; -+ struct device_dma_parameters dma_parms; -+ struct adm_chan *channels; -+ -+ u32 ee; -+ -+ struct clk *core_clk; -+ struct clk *iface_clk; -+ -+ struct reset_control *clk_reset; -+ struct reset_control *c0_reset; -+ struct reset_control *c1_reset; -+ struct reset_control *c2_reset; -+ int irq; -+}; -+ -+/** -+ * adm_free_chan - Frees dma resources associated with the specific channel -+ * -+ * Free all allocated descriptors associated with this channel -+ * -+ */ -+static void adm_free_chan(struct dma_chan *chan) -+{ -+ /* free all queued descriptors */ -+ vchan_free_chan_resources(to_virt_chan(chan)); -+} -+ -+/** -+ * adm_get_blksize - Get block size from burst value -+ * -+ */ -+static int adm_get_blksize(unsigned int burst) -+{ -+ int ret; -+ -+ switch (burst) { -+ case 16: -+ case 32: -+ case 64: -+ case 128: -+ ret = ffs(burst>>4) - 1; -+ break; -+ case 192: -+ ret = 4; -+ break; -+ case 256: -+ ret = 5; -+ break; -+ default: -+ ret = -EINVAL; -+ break; -+ } -+ -+ return ret; -+} -+ -+/** -+ * adm_process_fc_descriptors - Process descriptors for flow controlled xfers -+ * -+ * @achan: ADM channel -+ * @desc: Descriptor memory pointer -+ * @sg: Scatterlist entry -+ * @crci: CRCI value -+ * @burst: Burst size of transaction -+ * @direction: DMA transfer direction -+ */ -+static void *adm_process_fc_descriptors(struct adm_chan *achan, -+ void *desc, struct scatterlist *sg, u32 crci, u32 burst, -+ enum dma_transfer_direction direction) -+{ -+ struct adm_desc_hw_box *box_desc = NULL; -+ struct adm_desc_hw_single *single_desc; -+ u32 remainder = sg_dma_len(sg); -+ u32 rows, row_offset, crci_cmd; -+ u32 mem_addr = sg_dma_address(sg); -+ u32 *incr_addr = &mem_addr; -+ u32 *src, *dst; -+ -+ if (direction == DMA_DEV_TO_MEM) { -+ crci_cmd = ADM_CMD_SRC_CRCI(crci); -+ row_offset = burst; -+ src = &achan->slave.src_addr; -+ dst = &mem_addr; -+ } else { -+ crci_cmd = ADM_CMD_DST_CRCI(crci); -+ row_offset = burst << 16; -+ src = &mem_addr; -+ dst = &achan->slave.dst_addr; -+ } -+ -+ while (remainder >= burst) { -+ box_desc = desc; -+ box_desc->cmd = ADM_CMD_TYPE_BOX | crci_cmd; -+ box_desc->row_offset = row_offset; -+ box_desc->src_addr = *src; -+ box_desc->dst_addr = *dst; -+ -+ rows = remainder / burst; -+ rows = min_t(u32, rows, ADM_MAX_ROWS); -+ box_desc->num_rows = rows << 16 | rows; -+ box_desc->row_len = burst << 16 | burst; -+ -+ *incr_addr += burst * rows; -+ remainder -= burst * rows; -+ desc += sizeof(*box_desc); -+ } -+ -+ /* if leftover bytes, do one single descriptor */ -+ if (remainder) { -+ single_desc = desc; -+ single_desc->cmd = ADM_CMD_TYPE_SINGLE | crci_cmd; -+ single_desc->len = remainder; -+ single_desc->src_addr = *src; -+ single_desc->dst_addr = *dst; -+ desc += sizeof(*single_desc); -+ -+ if (sg_is_last(sg)) -+ single_desc->cmd |= ADM_CMD_LC; -+ } else { -+ if (box_desc && sg_is_last(sg)) -+ box_desc->cmd |= ADM_CMD_LC; -+ } -+ -+ return desc; -+} -+ -+/** -+ * adm_process_non_fc_descriptors - Process descriptors for non-fc xfers -+ * -+ * @achan: ADM channel -+ * @desc: Descriptor memory pointer -+ * @sg: Scatterlist entry -+ * @direction: DMA transfer direction -+ */ -+static void *adm_process_non_fc_descriptors(struct adm_chan *achan, -+ void *desc, struct scatterlist *sg, -+ enum dma_transfer_direction direction) -+{ -+ struct adm_desc_hw_single *single_desc; -+ u32 remainder = sg_dma_len(sg); -+ u32 mem_addr = sg_dma_address(sg); -+ u32 *incr_addr = &mem_addr; -+ u32 *src, *dst; -+ -+ if (direction == DMA_DEV_TO_MEM) { -+ src = &achan->slave.src_addr; -+ dst = &mem_addr; -+ } else { -+ src = &mem_addr; -+ dst = &achan->slave.dst_addr; -+ } -+ -+ do { -+ single_desc = desc; -+ single_desc->cmd = ADM_CMD_TYPE_SINGLE; -+ single_desc->src_addr = *src; -+ single_desc->dst_addr = *dst; -+ single_desc->len = (remainder > ADM_MAX_XFER) ? -+ ADM_MAX_XFER : remainder; -+ -+ remainder -= single_desc->len; -+ *incr_addr += single_desc->len; -+ desc += sizeof(*single_desc); -+ } while (remainder); -+ -+ /* set last command if this is the end of the whole transaction */ -+ if (sg_is_last(sg)) -+ single_desc->cmd |= ADM_CMD_LC; -+ -+ return desc; -+} -+ -+/** -+ * adm_prep_slave_sg - Prep slave sg transaction -+ * -+ * @chan: dma channel -+ * @sgl: scatter gather list -+ * @sg_len: length of sg -+ * @direction: DMA transfer direction -+ * @flags: DMA flags -+ * @context: transfer context (unused) -+ */ -+static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan, -+ struct scatterlist *sgl, unsigned int sg_len, -+ enum dma_transfer_direction direction, unsigned long flags, -+ void *context) -+{ -+ struct adm_chan *achan = to_adm_chan(chan); -+ struct adm_device *adev = achan->adev; -+ struct adm_async_desc *async_desc; -+ struct scatterlist *sg; -+ u32 i, burst; -+ u32 single_count = 0, box_count = 0, crci = 0; -+ void *desc; -+ u32 *cple; -+ int blk_size = 0; -+ -+ if (!is_slave_direction(direction)) { -+ dev_err(adev->dev, "invalid dma direction\n"); -+ return NULL; -+ } -+ -+ /* -+ * get burst value from slave configuration -+ */ -+ burst = (direction == DMA_MEM_TO_DEV) ? -+ achan->slave.dst_maxburst : -+ achan->slave.src_maxburst; -+ -+ /* if using flow control, validate burst and crci values */ -+ if (achan->slave.device_fc) { -+ -+ blk_size = adm_get_blksize(burst); -+ if (blk_size < 0) { -+ dev_err(adev->dev, "invalid burst value: %d\n", -+ burst); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ crci = achan->slave.slave_id & 0xf; -+ if (!crci || achan->slave.slave_id > 0x1f) { -+ dev_err(adev->dev, "invalid crci value\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ } -+ -+ /* iterate through sgs and compute allocation size of structures */ -+ for_each_sg(sgl, sg, sg_len, i) { -+ if (achan->slave.device_fc) { -+ box_count += DIV_ROUND_UP(sg_dma_len(sg) / burst, -+ ADM_MAX_ROWS); -+ if (sg_dma_len(sg) % burst) -+ single_count++; -+ } else { -+ single_count += DIV_ROUND_UP(sg_dma_len(sg), -+ ADM_MAX_XFER); -+ } -+ } -+ -+ async_desc = kzalloc(sizeof(*async_desc), GFP_NOWAIT); -+ if (!async_desc) -+ return ERR_PTR(-ENOMEM); -+ -+ if (crci) -+ async_desc->mux = achan->slave.slave_id & ADM_CRCI_MUX_SEL ? -+ ADM_CRCI_CTL_MUX_SEL : 0; -+ async_desc->crci = crci; -+ async_desc->blk_size = blk_size; -+ async_desc->dma_len = single_count * sizeof(struct adm_desc_hw_single) + -+ box_count * sizeof(struct adm_desc_hw_box) + -+ sizeof(*cple) + 2 * ADM_DESC_ALIGN; -+ -+ async_desc->cpl = dma_alloc_writecombine(adev->dev, async_desc->dma_len, -+ &async_desc->dma_addr, GFP_NOWAIT); -+ -+ if (!async_desc->cpl) { -+ kfree(async_desc); -+ return ERR_PTR(-ENOMEM); -+ } -+ -+ async_desc->adev = adev; -+ -+ /* both command list entry and descriptors must be 8 byte aligned */ -+ cple = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN); -+ desc = PTR_ALIGN(cple + 1, ADM_DESC_ALIGN); -+ -+ /* init cmd list */ -+ *cple = ADM_CPLE_LP; -+ *cple |= (desc - async_desc->cpl + async_desc->dma_addr) >> 3; -+ -+ for_each_sg(sgl, sg, sg_len, i) { -+ async_desc->length += sg_dma_len(sg); -+ -+ if (achan->slave.device_fc) -+ desc = adm_process_fc_descriptors(achan, desc, sg, crci, -+ burst, direction); -+ else -+ desc = adm_process_non_fc_descriptors(achan, desc, sg, -+ direction); -+ } -+ -+ return vchan_tx_prep(&achan->vc, &async_desc->vd, flags); -+} -+ -+/** -+ * adm_terminate_all - terminate all transactions on a channel -+ * @achan: adm dma channel -+ * -+ * Dequeues and frees all transactions, aborts current transaction -+ * No callbacks are done -+ * -+ */ -+static int adm_terminate_all(struct dma_chan *chan) -+{ -+ struct adm_chan *achan = to_adm_chan(chan); -+ struct adm_device *adev = achan->adev; -+ unsigned long flags; -+ LIST_HEAD(head); -+ -+ spin_lock_irqsave(&achan->vc.lock, flags); -+ vchan_get_all_descriptors(&achan->vc, &head); -+ -+ /* send flush command to terminate current transaction */ -+ writel_relaxed(0x0, -+ adev->regs + ADM_CH_FLUSH_STATE0(achan->id, adev->ee)); -+ -+ spin_unlock_irqrestore(&achan->vc.lock, flags); -+ -+ vchan_dma_desc_free_list(&achan->vc, &head); -+ -+ return 0; -+} -+ -+static int adm_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg) -+{ -+ struct adm_chan *achan = to_adm_chan(chan); -+ unsigned long flag; -+ -+ spin_lock_irqsave(&achan->vc.lock, flag); -+ memcpy(&achan->slave, cfg, sizeof(struct dma_slave_config)); -+ spin_unlock_irqrestore(&achan->vc.lock, flag); -+ -+ return 0; -+} -+ -+/** -+ * adm_start_dma - start next transaction -+ * @achan - ADM dma channel -+ */ -+static void adm_start_dma(struct adm_chan *achan) -+{ -+ struct virt_dma_desc *vd = vchan_next_desc(&achan->vc); -+ struct adm_device *adev = achan->adev; -+ struct adm_async_desc *async_desc; -+ -+ lockdep_assert_held(&achan->vc.lock); -+ -+ if (!vd) -+ return; -+ -+ list_del(&vd->node); -+ -+ /* write next command list out to the CMD FIFO */ -+ async_desc = container_of(vd, struct adm_async_desc, vd); -+ achan->curr_txd = async_desc; -+ -+ /* reset channel error */ -+ achan->error = 0; -+ -+ if (!achan->initialized) { -+ /* enable interrupts */ -+ writel(ADM_CH_CONF_SHADOW_EN | -+ ADM_CH_CONF_PERM_MPU_CONF | -+ ADM_CH_CONF_MPU_DISABLE | -+ ADM_CH_CONF_SEC_DOMAIN(adev->ee), -+ adev->regs + ADM_CH_CONF(achan->id)); -+ -+ writel(ADM_CH_RSLT_CONF_IRQ_EN | ADM_CH_RSLT_CONF_FLUSH_EN, -+ adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee)); -+ -+ achan->initialized = 1; -+ } -+ -+ /* set the crci block size if this transaction requires CRCI */ -+ if (async_desc->crci) { -+ writel(async_desc->mux | async_desc->blk_size, -+ adev->regs + ADM_CRCI_CTL(async_desc->crci, adev->ee)); -+ } -+ -+ /* make sure IRQ enable doesn't get reordered */ -+ wmb(); -+ -+ /* write next command list out to the CMD FIFO */ -+ writel(ALIGN(async_desc->dma_addr, ADM_DESC_ALIGN) >> 3, -+ adev->regs + ADM_CH_CMD_PTR(achan->id, adev->ee)); -+} -+ -+/** -+ * adm_dma_irq - irq handler for ADM controller -+ * @irq: IRQ of interrupt -+ * @data: callback data -+ * -+ * IRQ handler for the bam controller -+ */ -+static irqreturn_t adm_dma_irq(int irq, void *data) -+{ -+ struct adm_device *adev = data; -+ u32 srcs, i; -+ struct adm_async_desc *async_desc; -+ unsigned long flags; -+ -+ srcs = readl_relaxed(adev->regs + -+ ADM_SEC_DOMAIN_IRQ_STATUS(adev->ee)); -+ -+ for (i = 0; i < ADM_MAX_CHANNELS; i++) { -+ struct adm_chan *achan = &adev->channels[i]; -+ u32 status, result; -+ -+ if (srcs & BIT(i)) { -+ status = readl_relaxed(adev->regs + -+ ADM_CH_STATUS_SD(i, adev->ee)); -+ -+ /* if no result present, skip */ -+ if (!(status & ADM_CH_STATUS_VALID)) -+ continue; -+ -+ result = readl_relaxed(adev->regs + -+ ADM_CH_RSLT(i, adev->ee)); -+ -+ /* no valid results, skip */ -+ if (!(result & ADM_CH_RSLT_VALID)) -+ continue; -+ -+ /* flag error if transaction was flushed or failed */ -+ if (result & (ADM_CH_RSLT_ERR | ADM_CH_RSLT_FLUSH)) -+ achan->error = 1; -+ -+ spin_lock_irqsave(&achan->vc.lock, flags); -+ async_desc = achan->curr_txd; -+ -+ achan->curr_txd = NULL; -+ -+ if (async_desc) { -+ vchan_cookie_complete(&async_desc->vd); -+ -+ /* kick off next DMA */ -+ adm_start_dma(achan); -+ } -+ -+ spin_unlock_irqrestore(&achan->vc.lock, flags); -+ } -+ } -+ -+ return IRQ_HANDLED; -+} -+ -+/** -+ * adm_tx_status - returns status of transaction -+ * @chan: dma channel -+ * @cookie: transaction cookie -+ * @txstate: DMA transaction state -+ * -+ * Return status of dma transaction -+ */ -+static enum dma_status adm_tx_status(struct dma_chan *chan, dma_cookie_t cookie, -+ struct dma_tx_state *txstate) -+{ -+ struct adm_chan *achan = to_adm_chan(chan); -+ struct virt_dma_desc *vd; -+ enum dma_status ret; -+ unsigned long flags; -+ size_t residue = 0; -+ -+ ret = dma_cookie_status(chan, cookie, txstate); -+ if (ret == DMA_COMPLETE || !txstate) -+ return ret; -+ -+ spin_lock_irqsave(&achan->vc.lock, flags); -+ -+ vd = vchan_find_desc(&achan->vc, cookie); -+ if (vd) -+ residue = container_of(vd, struct adm_async_desc, vd)->length; -+ -+ spin_unlock_irqrestore(&achan->vc.lock, flags); -+ -+ /* -+ * residue is either the full length if it is in the issued list, or 0 -+ * if it is in progress. We have no reliable way of determining -+ * anything inbetween -+ */ -+ dma_set_residue(txstate, residue); -+ -+ if (achan->error) -+ return DMA_ERROR; -+ -+ return ret; -+} -+ -+/** -+ * adm_issue_pending - starts pending transactions -+ * @chan: dma channel -+ * -+ * Issues all pending transactions and starts DMA -+ */ -+static void adm_issue_pending(struct dma_chan *chan) -+{ -+ struct adm_chan *achan = to_adm_chan(chan); -+ unsigned long flags; -+ -+ spin_lock_irqsave(&achan->vc.lock, flags); -+ -+ if (vchan_issue_pending(&achan->vc) && !achan->curr_txd) -+ adm_start_dma(achan); -+ spin_unlock_irqrestore(&achan->vc.lock, flags); -+} -+ -+/** -+ * adm_dma_free_desc - free descriptor memory -+ * @vd: virtual descriptor -+ * -+ */ -+static void adm_dma_free_desc(struct virt_dma_desc *vd) -+{ -+ struct adm_async_desc *async_desc = container_of(vd, -+ struct adm_async_desc, vd); -+ -+ dma_free_writecombine(async_desc->adev->dev, async_desc->dma_len, -+ async_desc->cpl, async_desc->dma_addr); -+ kfree(async_desc); -+} -+ -+static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan, -+ u32 index) -+{ -+ achan->id = index; -+ achan->adev = adev; -+ -+ vchan_init(&achan->vc, &adev->common); -+ achan->vc.desc_free = adm_dma_free_desc; -+} -+ -+static int adm_dma_probe(struct platform_device *pdev) -+{ -+ struct adm_device *adev; -+ struct resource *iores; -+ int ret; -+ u32 i; -+ -+ adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL); -+ if (!adev) -+ return -ENOMEM; -+ -+ adev->dev = &pdev->dev; -+ -+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ adev->regs = devm_ioremap_resource(&pdev->dev, iores); -+ if (IS_ERR(adev->regs)) -+ return PTR_ERR(adev->regs); -+ -+ adev->irq = platform_get_irq(pdev, 0); -+ if (adev->irq < 0) -+ return adev->irq; -+ -+ ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &adev->ee); -+ if (ret) { -+ dev_err(adev->dev, "Execution environment unspecified\n"); -+ return ret; -+ } -+ -+ adev->core_clk = devm_clk_get(adev->dev, "core"); -+ if (IS_ERR(adev->core_clk)) -+ return PTR_ERR(adev->core_clk); -+ -+ ret = clk_prepare_enable(adev->core_clk); -+ if (ret) { -+ dev_err(adev->dev, "failed to prepare/enable core clock\n"); -+ return ret; -+ } -+ -+ adev->iface_clk = devm_clk_get(adev->dev, "iface"); -+ if (IS_ERR(adev->iface_clk)) { -+ ret = PTR_ERR(adev->iface_clk); -+ goto err_disable_core_clk; -+ } -+ -+ ret = clk_prepare_enable(adev->iface_clk); -+ if (ret) { -+ dev_err(adev->dev, "failed to prepare/enable iface clock\n"); -+ goto err_disable_core_clk; -+ } -+ -+ adev->clk_reset = devm_reset_control_get(&pdev->dev, "clk"); -+ if (IS_ERR(adev->clk_reset)) { -+ dev_err(adev->dev, "failed to get ADM0 reset\n"); -+ ret = PTR_ERR(adev->clk_reset); -+ goto err_disable_clks; -+ } -+ -+ adev->c0_reset = devm_reset_control_get(&pdev->dev, "c0"); -+ if (IS_ERR(adev->c0_reset)) { -+ dev_err(adev->dev, "failed to get ADM0 C0 reset\n"); -+ ret = PTR_ERR(adev->c0_reset); -+ goto err_disable_clks; -+ } -+ -+ adev->c1_reset = devm_reset_control_get(&pdev->dev, "c1"); -+ if (IS_ERR(adev->c1_reset)) { -+ dev_err(adev->dev, "failed to get ADM0 C1 reset\n"); -+ ret = PTR_ERR(adev->c1_reset); -+ goto err_disable_clks; -+ } -+ -+ adev->c2_reset = devm_reset_control_get(&pdev->dev, "c2"); -+ if (IS_ERR(adev->c2_reset)) { -+ dev_err(adev->dev, "failed to get ADM0 C2 reset\n"); -+ ret = PTR_ERR(adev->c2_reset); -+ goto err_disable_clks; -+ } -+ -+ reset_control_assert(adev->clk_reset); -+ reset_control_assert(adev->c0_reset); -+ reset_control_assert(adev->c1_reset); -+ reset_control_assert(adev->c2_reset); -+ -+ reset_control_deassert(adev->clk_reset); -+ reset_control_deassert(adev->c0_reset); -+ reset_control_deassert(adev->c1_reset); -+ reset_control_deassert(adev->c2_reset); -+ -+ adev->channels = devm_kcalloc(adev->dev, ADM_MAX_CHANNELS, -+ sizeof(*adev->channels), GFP_KERNEL); -+ -+ if (!adev->channels) { -+ ret = -ENOMEM; -+ goto err_disable_clks; -+ } -+ -+ /* allocate and initialize channels */ -+ INIT_LIST_HEAD(&adev->common.channels); -+ -+ for (i = 0; i < ADM_MAX_CHANNELS; i++) -+ adm_channel_init(adev, &adev->channels[i], i); -+ -+ /* reset CRCIs */ -+ for (i = 0; i < 16; i++) -+ writel(ADM_CRCI_CTL_RST, adev->regs + -+ ADM_CRCI_CTL(i, adev->ee)); -+ -+ /* configure client interfaces */ -+ writel(ADM_CI_RANGE_START(0x40) | ADM_CI_RANGE_END(0xb0) | -+ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(0)); -+ writel(ADM_CI_RANGE_START(0x2a) | ADM_CI_RANGE_END(0x2c) | -+ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(1)); -+ writel(ADM_CI_RANGE_START(0x12) | ADM_CI_RANGE_END(0x28) | -+ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(2)); -+ writel(ADM_GP_CTL_LP_EN | ADM_GP_CTL_LP_CNT(0xf), -+ adev->regs + ADM_GP_CTL); -+ -+ ret = devm_request_irq(adev->dev, adev->irq, adm_dma_irq, -+ 0, "adm_dma", adev); -+ if (ret) -+ goto err_disable_clks; -+ -+ platform_set_drvdata(pdev, adev); -+ -+ adev->common.dev = adev->dev; -+ adev->common.dev->dma_parms = &adev->dma_parms; -+ -+ /* set capabilities */ -+ dma_cap_zero(adev->common.cap_mask); -+ dma_cap_set(DMA_SLAVE, adev->common.cap_mask); -+ dma_cap_set(DMA_PRIVATE, adev->common.cap_mask); -+ -+ /* initialize dmaengine apis */ -+ adev->common.directions = BIT(DMA_DEV_TO_MEM | DMA_MEM_TO_DEV); -+ adev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; -+ adev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES; -+ adev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES; -+ adev->common.device_free_chan_resources = adm_free_chan; -+ adev->common.device_prep_slave_sg = adm_prep_slave_sg; -+ adev->common.device_issue_pending = adm_issue_pending; -+ adev->common.device_tx_status = adm_tx_status; -+ adev->common.device_terminate_all = adm_terminate_all; -+ adev->common.device_config = adm_slave_config; -+ -+ ret = dma_async_device_register(&adev->common); -+ if (ret) { -+ dev_err(adev->dev, "failed to register dma async device\n"); -+ goto err_disable_clks; -+ } -+ -+ ret = of_dma_controller_register(pdev->dev.of_node, -+ of_dma_xlate_by_chan_id, -+ &adev->common); -+ if (ret) -+ goto err_unregister_dma; -+ -+ return 0; -+ -+err_unregister_dma: -+ dma_async_device_unregister(&adev->common); -+err_disable_clks: -+ clk_disable_unprepare(adev->iface_clk); -+err_disable_core_clk: -+ clk_disable_unprepare(adev->core_clk); -+ -+ return ret; -+} -+ -+static int adm_dma_remove(struct platform_device *pdev) -+{ -+ struct adm_device *adev = platform_get_drvdata(pdev); -+ struct adm_chan *achan; -+ u32 i; -+ -+ of_dma_controller_free(pdev->dev.of_node); -+ dma_async_device_unregister(&adev->common); -+ -+ for (i = 0; i < ADM_MAX_CHANNELS; i++) { -+ achan = &adev->channels[i]; -+ -+ /* mask IRQs for this channel/EE pair */ -+ writel(0, adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee)); -+ -+ adm_terminate_all(&adev->channels[i].vc.chan); -+ } -+ -+ devm_free_irq(adev->dev, adev->irq, adev); -+ -+ clk_disable_unprepare(adev->core_clk); -+ clk_disable_unprepare(adev->iface_clk); -+ -+ return 0; -+} -+ -+static const struct of_device_id adm_of_match[] = { -+ { .compatible = "qcom,adm", }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, adm_of_match); -+ -+static struct platform_driver adm_dma_driver = { -+ .probe = adm_dma_probe, -+ .remove = adm_dma_remove, -+ .driver = { -+ .name = "adm-dma-engine", -+ .of_match_table = adm_of_match, -+ }, -+}; -+ -+module_platform_driver(adm_dma_driver); -+ -+MODULE_AUTHOR("Andy Gross "); -+MODULE_DESCRIPTION("QCOM ADM DMA engine driver"); -+MODULE_LICENSE("GPL v2"); ---- a/drivers/dma/Makefile -+++ b/drivers/dma/Makefile -@@ -65,5 +65,6 @@ obj-$(CONFIG_TI_DMA_CROSSBAR) += ti-dma- - obj-$(CONFIG_TI_EDMA) += edma.o - obj-$(CONFIG_XGENE_DMA) += xgene-dma.o - obj-$(CONFIG_ZX_DMA) += zx296702_dma.o -+obj-$(CONFIG_QCOM_ADM) += qcom_adm.o - - obj-y += xilinx/ diff --git a/target/linux/ipq806x/patches-4.4/157-ARM-DT-ipq8064-Add-ADM-device-node.patch b/target/linux/ipq806x/patches-4.4/157-ARM-DT-ipq8064-Add-ADM-device-node.patch deleted file mode 100644 index 5383041ffc..0000000000 --- a/target/linux/ipq806x/patches-4.4/157-ARM-DT-ipq8064-Add-ADM-device-node.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 1fb18acab2d71e7e4efd9c10492edb1baf84dcc0 Mon Sep 17 00:00:00 2001 -From: Andy Gross -Date: Wed, 20 May 2015 15:41:07 +0530 -Subject: [PATCH] ARM: DT: ipq8064: Add ADM device node - -This patch adds support for the ADM DMA on the IPQ8064 SOC - -Signed-off-by: Andy Gross ---- - arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 4 ++++ - arch/arm/boot/dts/qcom-ipq8064.dtsi | 21 +++++++++++++++++++++ - 2 files changed, 25 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -721,6 +721,26 @@ - - status = "disabled"; - }; -+ -+ adm_dma: dma@18300000 { -+ compatible = "qcom,adm"; -+ reg = <0x18300000 0x100000>; -+ interrupts = <0 170 0>; -+ #dma-cells = <1>; -+ -+ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; -+ clock-names = "core", "iface"; -+ -+ resets = <&gcc ADM0_RESET>, -+ <&gcc ADM0_PBUS_RESET>, -+ <&gcc ADM0_C0_RESET>, -+ <&gcc ADM0_C1_RESET>, -+ <&gcc ADM0_C2_RESET>; -+ reset-names = "clk", "pbus", "c0", "c1", "c2"; -+ qcom,ee = <0>; -+ -+ status = "disabled"; -+ }; - }; - - sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.4/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch b/target/linux/ipq806x/patches-4.4/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch deleted file mode 100644 index 154649dca6..0000000000 --- a/target/linux/ipq806x/patches-4.4/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch +++ /dev/null @@ -1,83 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3, - 1/5] mtd: nand: Create a BBT flag to access bad block markers in raw - mode -From: Archit Taneja -X-Patchwork-Id: 6927081 -Message-Id: <1438578498-32254-2-git-send-email-architt@codeaurora.org> -To: linux-mtd@lists.infradead.org, dehrenberg@google.com, - cernekee@gmail.com, computersforpeace@gmail.com -Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, - sboyd@codeaurora.org, linux-kernel@vger.kernel.org, - Archit Taneja -Date: Mon, 3 Aug 2015 10:38:14 +0530 - -Some controllers can access the factory bad block marker from OOB only -when they read it in raw mode. When ECC is enabled, these controllers -discard reading/writing bad block markers, preventing access to them -altogether. - -The bbt driver assumes MTD_OPS_PLACE_OOB when scanning for bad blocks. -This results in the nand driver's ecc->read_oob() op to be called, which -works with ECC enabled. - -Create a new BBT option flag that tells nand_bbt to force the mode to -MTD_OPS_RAW. This would result in the correct op being called for the -underlying nand controller driver. - -Reviewed-by: Andy Gross -Signed-off-by: Archit Taneja - ---- -drivers/mtd/nand/nand_base.c | 6 +++++- - drivers/mtd/nand/nand_bbt.c | 6 +++++- - include/linux/mtd/bbm.h | 7 +++++++ - 3 files changed, 17 insertions(+), 2 deletions(-) - ---- a/drivers/mtd/nand/nand_base.c -+++ b/drivers/mtd/nand/nand_base.c -@@ -394,7 +394,11 @@ static int nand_default_block_markbad(st - } else { - ops.len = ops.ooblen = 1; - } -- ops.mode = MTD_OPS_PLACE_OOB; -+ -+ if (unlikely(chip->bbt_options & NAND_BBT_ACCESS_BBM_RAW)) -+ ops.mode = MTD_OPS_RAW; -+ else -+ ops.mode = MTD_OPS_PLACE_OOB; - - /* Write to first/last page(s) if necessary */ - if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) ---- a/drivers/mtd/nand/nand_bbt.c -+++ b/drivers/mtd/nand/nand_bbt.c -@@ -420,7 +420,11 @@ static int scan_block_fast(struct mtd_in - ops.oobbuf = buf; - ops.ooboffs = 0; - ops.datbuf = NULL; -- ops.mode = MTD_OPS_PLACE_OOB; -+ -+ if (unlikely(bd->options & NAND_BBT_ACCESS_BBM_RAW)) -+ ops.mode = MTD_OPS_RAW; -+ else -+ ops.mode = MTD_OPS_PLACE_OOB; - - for (j = 0; j < numpages; j++) { - /* ---- a/include/linux/mtd/bbm.h -+++ b/include/linux/mtd/bbm.h -@@ -116,6 +116,12 @@ struct nand_bbt_descr { - #define NAND_BBT_NO_OOB_BBM 0x00080000 - - /* -+ * Force MTD_OPS_RAW mode when trying to access bad block markes from OOB. To -+ * be used by controllers which can access BBM only when ECC is disabled, i.e, -+ * when in RAW access mode -+ */ -+#define NAND_BBT_ACCESS_BBM_RAW 0x00100000 -+/* - * Flag set by nand_create_default_bbt_descr(), marking that the nand_bbt_descr - * was allocated dynamicaly and must be freed in nand_release(). Has no meaning - * in nand_chip.bbt_options. diff --git a/target/linux/ipq806x/patches-4.4/162-mtd-nand-Qualcomm-NAND-controller-driver.patch b/target/linux/ipq806x/patches-4.4/162-mtd-nand-Qualcomm-NAND-controller-driver.patch deleted file mode 100644 index 84ff6e74e0..0000000000 --- a/target/linux/ipq806x/patches-4.4/162-mtd-nand-Qualcomm-NAND-controller-driver.patch +++ /dev/null @@ -1,2024 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,2/5] mtd: nand: Qualcomm NAND controller driver -From: Archit Taneja -X-Patchwork-Id: 6927101 -Message-Id: <1438578498-32254-3-git-send-email-architt@codeaurora.org> -To: linux-mtd@lists.infradead.org, dehrenberg@google.com, - cernekee@gmail.com, computersforpeace@gmail.com -Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, - sboyd@codeaurora.org, linux-kernel@vger.kernel.org, - Archit Taneja -Date: Mon, 3 Aug 2015 10:38:15 +0530 - -The Qualcomm NAND controller is found in SoCs like IPQ806x, MSM7xx, -MDM9x15 series. - -It exists as a sub block inside the IPs EBI2 (External Bus Interface 2) -and QPIC (Qualcomm Parallel Interface Controller). These IPs provide a -broader interface for external slow peripheral devices such as LCD and -NAND/NOR flash memory or SRAM like interfaces. - -We add support for the NAND controller found within EBI2. For the SoCs -of our interest, we only use the NAND controller within EBI2. Therefore, -it's safe for us to assume that the NAND controller is a standalone block -within the SoC. - -The controller supports 512B, 2kB, 4kB and 8kB page 8-bit and 16-bit NAND -flash devices. It contains a HW ECC block that supports BCH ECC (4, 8 and -16 bit correction/step) and RS ECC(4 bit correction/step) that covers main -and spare data. The controller contains an internal 512 byte page buffer -to which we read/write via DMA. The EBI2 type NAND controller uses ADM DMA -for register read/write and data transfers. The controller performs page -reads and writes at a codeword/step level of 512 bytes. It can support up -to 2 external chips of different configurations. - -The driver prepares register read and write configuration descriptors for -each codeword, followed by data descriptors to read or write data from the -controller's internal buffer. It uses a single ADM DMA channel that we get -via dmaengine API. The controller requires 2 ADM CRCIs for command and -data flow control. These are passed via DT. - -The ecc layout used by the controller is syndrome like, but we can't use -the standard syndrome ecc ops because of several reasons. First, the amount -of data bytes covered by ecc isn't same in each step. Second, writing to -free oob space requires us writing to the entire step in which the oob -lies. This forces us to create our own ecc ops. - -One more difference is how the controller accesses the bad block marker. -The controller ignores reading the marker when ECC is enabled. ECC needs -to be explicity disabled to read or write to the bad block marker. For -this reason, we use the newly created flag NAND_BBT_ACCESS_BBM_RAW to -read the factory provided bad block markers. - -v3: -- Refactor dma functions for maximum reuse -- Use dma_slave_confing on stack -- optimize and clean upempty_page_fixup using memchr_inv -- ensure portability with dma register reads using le32_* funcs -- use NAND_USE_BOUNCE_BUFFER instead of doing it ourselves -- fix handling of return values of dmaengine funcs -- constify wherever possible -- Remove dependency on ADM DMA in Kconfig -- Misc fixes and clean ups - -v2: -- Use new BBT flag that allows us to read BBM in raw mode -- reduce memcpy-s in the driver -- some refactor and clean ups because of above changes - -Reviewed-by: Andy Gross -Signed-off-by: Archit Taneja - ---- -drivers/mtd/nand/Kconfig | 7 + - drivers/mtd/nand/Makefile | 1 + - drivers/mtd/nand/qcom_nandc.c | 1913 +++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 1921 insertions(+) - create mode 100644 drivers/mtd/nand/qcom_nandc.c - ---- a/drivers/mtd/nand/Kconfig -+++ b/drivers/mtd/nand/Kconfig -@@ -546,4 +546,11 @@ config MTD_NAND_HISI504 - help - Enables support for NAND controller on Hisilicon SoC Hip04. - -+config MTD_NAND_QCOM -+ tristate "Support for NAND on QCOM SoCs" -+ depends on ARCH_QCOM -+ help -+ Enables support for NAND flash chips on SoCs containing the EBI2 NAND -+ controller. This controller is found on IPQ806x SoC. -+ - endif # MTD_NAND ---- /dev/null -+++ b/drivers/mtd/nand/qcom_nandc.c -@@ -0,0 +1,1918 @@ -+/* -+ * Copyright (c) 2015, The Linux Foundation. All rights reserved. -+ * -+ * This software is licensed under the terms of the GNU General Public -+ * License version 2, as published by the Free Software Foundation, and -+ * may be copied, distributed, and modified under those terms. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* NANDc reg offsets */ -+#define NAND_FLASH_CMD 0x00 -+#define NAND_ADDR0 0x04 -+#define NAND_ADDR1 0x08 -+#define NAND_FLASH_CHIP_SELECT 0x0c -+#define NAND_EXEC_CMD 0x10 -+#define NAND_FLASH_STATUS 0x14 -+#define NAND_BUFFER_STATUS 0x18 -+#define NAND_DEV0_CFG0 0x20 -+#define NAND_DEV0_CFG1 0x24 -+#define NAND_DEV0_ECC_CFG 0x28 -+#define NAND_DEV1_ECC_CFG 0x2c -+#define NAND_DEV1_CFG0 0x30 -+#define NAND_DEV1_CFG1 0x34 -+#define NAND_READ_ID 0x40 -+#define NAND_READ_STATUS 0x44 -+#define NAND_DEV_CMD0 0xa0 -+#define NAND_DEV_CMD1 0xa4 -+#define NAND_DEV_CMD2 0xa8 -+#define NAND_DEV_CMD_VLD 0xac -+#define SFLASHC_BURST_CFG 0xe0 -+#define NAND_ERASED_CW_DETECT_CFG 0xe8 -+#define NAND_ERASED_CW_DETECT_STATUS 0xec -+#define NAND_EBI2_ECC_BUF_CFG 0xf0 -+#define FLASH_BUF_ACC 0x100 -+ -+#define NAND_CTRL 0xf00 -+#define NAND_VERSION 0xf08 -+#define NAND_READ_LOCATION_0 0xf20 -+#define NAND_READ_LOCATION_1 0xf24 -+ -+/* dummy register offsets, used by write_reg_dma */ -+#define NAND_DEV_CMD1_RESTORE 0xdead -+#define NAND_DEV_CMD_VLD_RESTORE 0xbeef -+ -+/* NAND_FLASH_CMD bits */ -+#define PAGE_ACC BIT(4) -+#define LAST_PAGE BIT(5) -+ -+/* NAND_FLASH_CHIP_SELECT bits */ -+#define NAND_DEV_SEL 0 -+#define DM_EN BIT(2) -+ -+/* NAND_FLASH_STATUS bits */ -+#define FS_OP_ERR BIT(4) -+#define FS_READY_BSY_N BIT(5) -+#define FS_MPU_ERR BIT(8) -+#define FS_DEVICE_STS_ERR BIT(16) -+#define FS_DEVICE_WP BIT(23) -+ -+/* NAND_BUFFER_STATUS bits */ -+#define BS_UNCORRECTABLE_BIT BIT(8) -+#define BS_CORRECTABLE_ERR_MSK 0x1f -+ -+/* NAND_DEVn_CFG0 bits */ -+#define DISABLE_STATUS_AFTER_WRITE 4 -+#define CW_PER_PAGE 6 -+#define UD_SIZE_BYTES 9 -+#define ECC_PARITY_SIZE_BYTES_RS 19 -+#define SPARE_SIZE_BYTES 23 -+#define NUM_ADDR_CYCLES 27 -+#define STATUS_BFR_READ 30 -+#define SET_RD_MODE_AFTER_STATUS 31 -+ -+/* NAND_DEVn_CFG0 bits */ -+#define DEV0_CFG1_ECC_DISABLE 0 -+#define WIDE_FLASH 1 -+#define NAND_RECOVERY_CYCLES 2 -+#define CS_ACTIVE_BSY 5 -+#define BAD_BLOCK_BYTE_NUM 6 -+#define BAD_BLOCK_IN_SPARE_AREA 16 -+#define WR_RD_BSY_GAP 17 -+#define ENABLE_BCH_ECC 27 -+ -+/* NAND_DEV0_ECC_CFG bits */ -+#define ECC_CFG_ECC_DISABLE 0 -+#define ECC_SW_RESET 1 -+#define ECC_MODE 4 -+#define ECC_PARITY_SIZE_BYTES_BCH 8 -+#define ECC_NUM_DATA_BYTES 16 -+#define ECC_FORCE_CLK_OPEN 30 -+ -+/* NAND_DEV_CMD1 bits */ -+#define READ_ADDR 0 -+ -+/* NAND_DEV_CMD_VLD bits */ -+#define READ_START_VLD 0 -+ -+/* NAND_EBI2_ECC_BUF_CFG bits */ -+#define NUM_STEPS 0 -+ -+/* NAND_ERASED_CW_DETECT_CFG bits */ -+#define ERASED_CW_ECC_MASK 1 -+#define AUTO_DETECT_RES 0 -+#define MASK_ECC (1 << ERASED_CW_ECC_MASK) -+#define RESET_ERASED_DET (1 << AUTO_DETECT_RES) -+#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES) -+#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC) -+#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC) -+ -+/* NAND_ERASED_CW_DETECT_STATUS bits */ -+#define PAGE_ALL_ERASED BIT(7) -+#define CODEWORD_ALL_ERASED BIT(6) -+#define PAGE_ERASED BIT(5) -+#define CODEWORD_ERASED BIT(4) -+#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED) -+#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED) -+ -+/* Version Mask */ -+#define NAND_VERSION_MAJOR_MASK 0xf0000000 -+#define NAND_VERSION_MAJOR_SHIFT 28 -+#define NAND_VERSION_MINOR_MASK 0x0fff0000 -+#define NAND_VERSION_MINOR_SHIFT 16 -+ -+/* NAND OP_CMDs */ -+#define PAGE_READ 0x2 -+#define PAGE_READ_WITH_ECC 0x3 -+#define PAGE_READ_WITH_ECC_SPARE 0x4 -+#define PROGRAM_PAGE 0x6 -+#define PAGE_PROGRAM_WITH_ECC 0x7 -+#define PROGRAM_PAGE_SPARE 0x9 -+#define BLOCK_ERASE 0xa -+#define FETCH_ID 0xb -+#define RESET_DEVICE 0xd -+ -+/* -+ * the NAND controller performs reads/writes with ECC in 516 byte chunks. -+ * the driver calls the chunks 'step' or 'codeword' interchangeably -+ */ -+#define NANDC_STEP_SIZE 512 -+ -+/* -+ * the largest page size we support is 8K, this will have 16 steps/codewords -+ * of 512 bytes each -+ */ -+#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE) -+ -+/* we read at most 3 registers per codeword scan */ -+#define MAX_REG_RD (3 * MAX_NUM_STEPS) -+ -+/* ECC modes */ -+#define ECC_NONE BIT(0) -+#define ECC_RS_4BIT BIT(1) -+#define ECC_BCH_4BIT BIT(2) -+#define ECC_BCH_8BIT BIT(3) -+ -+struct desc_info { -+ struct list_head list; -+ -+ enum dma_transfer_direction dir; -+ struct scatterlist sgl; -+ struct dma_async_tx_descriptor *dma_desc; -+}; -+ -+/* -+ * holds the current register values that we want to write. acts as a contiguous -+ * chunk of memory which we use to write the controller registers through DMA. -+ */ -+struct nandc_regs { -+ u32 cmd; -+ u32 addr0; -+ u32 addr1; -+ u32 chip_sel; -+ u32 exec; -+ -+ u32 cfg0; -+ u32 cfg1; -+ u32 ecc_bch_cfg; -+ -+ u32 clrflashstatus; -+ u32 clrreadstatus; -+ -+ u32 cmd1; -+ u32 vld; -+ -+ u32 orig_cmd1; -+ u32 orig_vld; -+ -+ u32 ecc_buf_cfg; -+}; -+ -+/* -+ * @cmd_crci: ADM DMA CRCI for command flow control -+ * @data_crci: ADM DMA CRCI for data flow control -+ * @list: DMA descriptor list (list of desc_infos) -+ * @dma_done: completion param to denote end of last -+ * descriptor in the list -+ * @data_buffer: our local DMA buffer for page read/writes, -+ * used when we can't use the buffer provided -+ * by upper layers directly -+ * @buf_size/count/start: markers for chip->read_buf/write_buf functions -+ * @reg_read_buf: buffer for reading register data via DMA -+ * @reg_read_pos: marker for data read in reg_read_buf -+ * @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for -+ * ecc/non-ecc mode for the current nand flash -+ * device -+ * @regs: a contiguous chunk of memory for DMA register -+ * writes -+ * @ecc_strength: 4 bit or 8 bit ecc, received via DT -+ * @bus_width: 8 bit or 16 bit NAND bus width, received via DT -+ * @ecc_modes: supported ECC modes by the current controller, -+ * initialized via DT match data -+ * @cw_size: the number of bytes in a single step/codeword -+ * of a page, consisting of all data, ecc, spare -+ * and reserved bytes -+ * @cw_data: the number of bytes within a codeword protected -+ * by ECC -+ * @bch_enabled: flag to tell whether BCH or RS ECC mode is used -+ * @status: value to be returned if NAND_CMD_STATUS command -+ * is executed -+ */ -+struct qcom_nandc_data { -+ struct platform_device *pdev; -+ struct device *dev; -+ -+ void __iomem *base; -+ struct resource *res; -+ -+ struct clk *core_clk; -+ struct clk *aon_clk; -+ -+ /* DMA stuff */ -+ struct dma_chan *chan; -+ struct dma_slave_config slave_conf; -+ unsigned int cmd_crci; -+ unsigned int data_crci; -+ struct list_head list; -+ struct completion dma_done; -+ -+ /* MTD stuff */ -+ struct nand_chip chip; -+ struct mtd_info mtd; -+ -+ /* local data buffer and markers */ -+ u8 *data_buffer; -+ int buf_size; -+ int buf_count; -+ int buf_start; -+ -+ /* local buffer to read back registers */ -+ u32 *reg_read_buf; -+ int reg_read_pos; -+ -+ /* required configs */ -+ u32 cfg0, cfg1; -+ u32 cfg0_raw, cfg1_raw; -+ u32 ecc_buf_cfg; -+ u32 ecc_bch_cfg; -+ u32 clrflashstatus; -+ u32 clrreadstatus; -+ u32 sflashc_burst_cfg; -+ u32 cmd1, vld; -+ -+ /* register state */ -+ struct nandc_regs *regs; -+ -+ /* things we get from DT */ -+ int ecc_strength; -+ int bus_width; -+ -+ u32 ecc_modes; -+ -+ /* misc params */ -+ int cw_size; -+ int cw_data; -+ bool use_ecc; -+ bool bch_enabled; -+ u8 status; -+ int last_command; -+}; -+ -+static inline u32 nandc_read(struct qcom_nandc_data *this, int offset) -+{ -+ return ioread32(this->base + offset); -+} -+ -+static inline void nandc_write(struct qcom_nandc_data *this, int offset, -+ u32 val) -+{ -+ iowrite32(val, this->base + offset); -+} -+ -+/* helper to configure address register values */ -+static void set_address(struct qcom_nandc_data *this, u16 column, int page) -+{ -+ struct nand_chip *chip = &this->chip; -+ struct nandc_regs *regs = this->regs; -+ -+ if (chip->options & NAND_BUSWIDTH_16) -+ column >>= 1; -+ -+ regs->addr0 = page << 16 | column; -+ regs->addr1 = page >> 16 & 0xff; -+} -+ -+/* -+ * update_rw_regs: set up read/write register values, these will be -+ * written to the NAND controller registers via DMA -+ * -+ * @num_cw: number of steps for the read/write operation -+ * @read: read or write operation -+ */ -+static void update_rw_regs(struct qcom_nandc_data *this, int num_cw, bool read) -+{ -+ struct nandc_regs *regs = this->regs; -+ -+ if (read) { -+ if (this->use_ecc) -+ regs->cmd = PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE; -+ else -+ regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE; -+ } else { -+ regs->cmd = PROGRAM_PAGE | PAGE_ACC | LAST_PAGE; -+ } -+ -+ if (this->use_ecc) { -+ regs->cfg0 = (this->cfg0 & ~(7U << CW_PER_PAGE)) | -+ (num_cw - 1) << CW_PER_PAGE; -+ -+ regs->cfg1 = this->cfg1; -+ regs->ecc_bch_cfg = this->ecc_bch_cfg; -+ } else { -+ regs->cfg0 = (this->cfg0_raw & ~(7U << CW_PER_PAGE)) | -+ (num_cw - 1) << CW_PER_PAGE; -+ -+ regs->cfg1 = this->cfg1_raw; -+ regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; -+ } -+ -+ regs->ecc_buf_cfg = this->ecc_buf_cfg; -+ regs->clrflashstatus = this->clrflashstatus; -+ regs->clrreadstatus = this->clrreadstatus; -+ regs->exec = 1; -+} -+ -+static int prep_dma_desc(struct qcom_nandc_data *this, bool read, int reg_off, -+ const void *vaddr, int size, bool flow_control) -+{ -+ struct desc_info *desc; -+ struct dma_async_tx_descriptor *dma_desc; -+ struct scatterlist *sgl; -+ struct dma_slave_config slave_conf; -+ int r; -+ -+ desc = kzalloc(sizeof(*desc), GFP_KERNEL); -+ if (!desc) -+ return -ENOMEM; -+ -+ list_add_tail(&desc->list, &this->list); -+ -+ sgl = &desc->sgl; -+ -+ sg_init_one(sgl, vaddr, size); -+ -+ desc->dir = read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; -+ -+ r = dma_map_sg(this->dev, sgl, 1, desc->dir); -+ if (r == 0) { -+ r = -ENOMEM; -+ goto err; -+ } -+ -+ memset(&slave_conf, 0x00, sizeof(slave_conf)); -+ -+ slave_conf.device_fc = flow_control; -+ if (read) { -+ slave_conf.src_maxburst = 16; -+ slave_conf.src_addr = this->res->start + reg_off; -+ slave_conf.slave_id = this->data_crci; -+ } else { -+ slave_conf.dst_maxburst = 16; -+ slave_conf.dst_addr = this->res->start + reg_off; -+ slave_conf.slave_id = this->cmd_crci; -+ } -+ -+ r = dmaengine_slave_config(this->chan, &slave_conf); -+ if (r) { -+ dev_err(this->dev, "failed to configure dma channel\n"); -+ goto err; -+ } -+ -+ dma_desc = dmaengine_prep_slave_sg(this->chan, sgl, 1, desc->dir, 0); -+ if (!dma_desc) { -+ dev_err(this->dev, "failed to prepare desc\n"); -+ r = -EINVAL; -+ goto err; -+ } -+ -+ desc->dma_desc = dma_desc; -+ -+ return 0; -+err: -+ kfree(desc); -+ -+ return r; -+} -+ -+/* -+ * read_reg_dma: prepares a descriptor to read a given number of -+ * contiguous registers to the reg_read_buf pointer -+ * -+ * @first: offset of the first register in the contiguous block -+ * @num_regs: number of registers to read -+ */ -+static int read_reg_dma(struct qcom_nandc_data *this, int first, int num_regs) -+{ -+ bool flow_control = false; -+ void *vaddr; -+ int size; -+ -+ if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) -+ flow_control = true; -+ -+ size = num_regs * sizeof(u32); -+ vaddr = this->reg_read_buf + this->reg_read_pos; -+ this->reg_read_pos += num_regs; -+ -+ return prep_dma_desc(this, true, first, vaddr, size, flow_control); -+} -+ -+/* -+ * write_reg_dma: prepares a descriptor to write a given number of -+ * contiguous registers -+ * -+ * @first: offset of the first register in the contiguous block -+ * @num_regs: number of registers to write -+ */ -+static int write_reg_dma(struct qcom_nandc_data *this, int first, int num_regs) -+{ -+ bool flow_control = false; -+ struct nandc_regs *regs = this->regs; -+ void *vaddr; -+ int size; -+ -+ switch (first) { -+ case NAND_FLASH_CMD: -+ vaddr = ®s->cmd; -+ flow_control = true; -+ break; -+ case NAND_EXEC_CMD: -+ vaddr = ®s->exec; -+ break; -+ case NAND_FLASH_STATUS: -+ vaddr = ®s->clrflashstatus; -+ break; -+ case NAND_DEV0_CFG0: -+ vaddr = ®s->cfg0; -+ break; -+ case NAND_READ_STATUS: -+ vaddr = ®s->clrreadstatus; -+ break; -+ case NAND_DEV_CMD1: -+ vaddr = ®s->cmd1; -+ break; -+ case NAND_DEV_CMD1_RESTORE: -+ first = NAND_DEV_CMD1; -+ vaddr = ®s->orig_cmd1; -+ break; -+ case NAND_DEV_CMD_VLD: -+ vaddr = ®s->vld; -+ break; -+ case NAND_DEV_CMD_VLD_RESTORE: -+ first = NAND_DEV_CMD_VLD; -+ vaddr = ®s->orig_vld; -+ break; -+ case NAND_EBI2_ECC_BUF_CFG: -+ vaddr = ®s->ecc_buf_cfg; -+ break; -+ default: -+ dev_err(this->dev, "invalid starting register\n"); -+ return -EINVAL; -+ } -+ -+ size = num_regs * sizeof(u32); -+ -+ return prep_dma_desc(this, false, first, vaddr, size, flow_control); -+} -+ -+/* -+ * read_data_dma: prepares a DMA descriptor to transfer data from the -+ * controller's internal buffer to the buffer 'vaddr' -+ * -+ * @reg_off: offset within the controller's data buffer -+ * @vaddr: virtual address of the buffer we want to write to -+ * @size: DMA transaction size in bytes -+ */ -+static int read_data_dma(struct qcom_nandc_data *this, int reg_off, -+ const u8 *vaddr, int size) -+{ -+ return prep_dma_desc(this, true, reg_off, vaddr, size, false); -+} -+ -+/* -+ * write_data_dma: prepares a DMA descriptor to transfer data from -+ * 'vaddr' to the controller's internal buffer -+ * -+ * @reg_off: offset within the controller's data buffer -+ * @vaddr: virtual address of the buffer we want to read from -+ * @size: DMA transaction size in bytes -+ */ -+static int write_data_dma(struct qcom_nandc_data *this, int reg_off, -+ const u8 *vaddr, int size) -+{ -+ return prep_dma_desc(this, false, reg_off, vaddr, size, false); -+} -+ -+/* -+ * helper to prepare dma descriptors to configure registers needed for reading a -+ * codeword/step in a page -+ */ -+static void config_cw_read(struct qcom_nandc_data *this) -+{ -+ write_reg_dma(this, NAND_FLASH_CMD, 3); -+ write_reg_dma(this, NAND_DEV0_CFG0, 3); -+ write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1); -+ -+ write_reg_dma(this, NAND_EXEC_CMD, 1); -+ -+ read_reg_dma(this, NAND_FLASH_STATUS, 2); -+ read_reg_dma(this, NAND_ERASED_CW_DETECT_STATUS, 1); -+} -+ -+/* -+ * helpers to prepare dma descriptors used to configure registers needed for -+ * writing a codeword/step in a page -+ */ -+static void config_cw_write_pre(struct qcom_nandc_data *this) -+{ -+ write_reg_dma(this, NAND_FLASH_CMD, 3); -+ write_reg_dma(this, NAND_DEV0_CFG0, 3); -+ write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1); -+} -+ -+static void config_cw_write_post(struct qcom_nandc_data *this) -+{ -+ write_reg_dma(this, NAND_EXEC_CMD, 1); -+ -+ read_reg_dma(this, NAND_FLASH_STATUS, 1); -+ -+ write_reg_dma(this, NAND_FLASH_STATUS, 1); -+ write_reg_dma(this, NAND_READ_STATUS, 1); -+} -+ -+/* -+ * the following functions are used within chip->cmdfunc() to perform different -+ * NAND_CMD_* commands -+ */ -+ -+/* sets up descriptors for NAND_CMD_PARAM */ -+static int nandc_param(struct qcom_nandc_data *this) -+{ -+ struct nandc_regs *regs = this->regs; -+ -+ /* -+ * NAND_CMD_PARAM is called before we know much about the FLASH chip -+ * in use. we configure the controller to perform a raw read of 512 -+ * bytes to read onfi params -+ */ -+ regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE; -+ regs->addr0 = 0; -+ regs->addr1 = 0; -+ regs->cfg0 = 0 << CW_PER_PAGE -+ | 512 << UD_SIZE_BYTES -+ | 5 << NUM_ADDR_CYCLES -+ | 0 << SPARE_SIZE_BYTES; -+ -+ regs->cfg1 = 7 << NAND_RECOVERY_CYCLES -+ | 0 << CS_ACTIVE_BSY -+ | 17 << BAD_BLOCK_BYTE_NUM -+ | 1 << BAD_BLOCK_IN_SPARE_AREA -+ | 2 << WR_RD_BSY_GAP -+ | 0 << WIDE_FLASH -+ | 1 << DEV0_CFG1_ECC_DISABLE; -+ -+ regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; -+ -+ /* configure CMD1 and VLD for ONFI param probing */ -+ regs->vld = (this->vld & ~(1 << READ_START_VLD)) -+ | 0 << READ_START_VLD; -+ -+ regs->cmd1 = (this->cmd1 & ~(0xFF << READ_ADDR)) -+ | NAND_CMD_PARAM << READ_ADDR; -+ -+ regs->exec = 1; -+ -+ regs->orig_cmd1 = this->cmd1; -+ regs->orig_vld = this->vld; -+ -+ write_reg_dma(this, NAND_DEV_CMD_VLD, 1); -+ write_reg_dma(this, NAND_DEV_CMD1, 1); -+ -+ this->buf_count = 512; -+ memset(this->data_buffer, 0xff, this->buf_count); -+ -+ config_cw_read(this); -+ -+ read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->buf_count); -+ -+ /* restore CMD1 and VLD regs */ -+ write_reg_dma(this, NAND_DEV_CMD1_RESTORE, 1); -+ write_reg_dma(this, NAND_DEV_CMD_VLD_RESTORE, 1); -+ -+ return 0; -+} -+ -+/* sets up descriptors for NAND_CMD_ERASE1 */ -+static int erase_block(struct qcom_nandc_data *this, int page_addr) -+{ -+ struct nandc_regs *regs = this->regs; -+ -+ regs->cmd = BLOCK_ERASE | PAGE_ACC | LAST_PAGE; -+ regs->addr0 = page_addr; -+ regs->addr1 = 0; -+ regs->cfg0 = this->cfg0_raw & ~(7 << CW_PER_PAGE); -+ regs->cfg1 = this->cfg1_raw; -+ regs->exec = 1; -+ regs->clrflashstatus = this->clrflashstatus; -+ regs->clrreadstatus = this->clrreadstatus; -+ -+ write_reg_dma(this, NAND_FLASH_CMD, 3); -+ write_reg_dma(this, NAND_DEV0_CFG0, 2); -+ write_reg_dma(this, NAND_EXEC_CMD, 1); -+ -+ read_reg_dma(this, NAND_FLASH_STATUS, 1); -+ -+ write_reg_dma(this, NAND_FLASH_STATUS, 1); -+ write_reg_dma(this, NAND_READ_STATUS, 1); -+ -+ return 0; -+} -+ -+/* sets up descriptors for NAND_CMD_READID */ -+static int read_id(struct qcom_nandc_data *this, int column) -+{ -+ struct nandc_regs *regs = this->regs; -+ -+ if (column == -1) -+ return 0; -+ -+ regs->cmd = FETCH_ID; -+ regs->addr0 = column; -+ regs->addr1 = 0; -+ regs->chip_sel = DM_EN; -+ regs->exec = 1; -+ -+ write_reg_dma(this, NAND_FLASH_CMD, 4); -+ write_reg_dma(this, NAND_EXEC_CMD, 1); -+ -+ read_reg_dma(this, NAND_READ_ID, 1); -+ -+ return 0; -+} -+ -+/* sets up descriptors for NAND_CMD_RESET */ -+static int reset(struct qcom_nandc_data *this) -+{ -+ struct nandc_regs *regs = this->regs; -+ -+ regs->cmd = RESET_DEVICE; -+ regs->exec = 1; -+ -+ write_reg_dma(this, NAND_FLASH_CMD, 1); -+ write_reg_dma(this, NAND_EXEC_CMD, 1); -+ -+ read_reg_dma(this, NAND_FLASH_STATUS, 1); -+ -+ return 0; -+} -+ -+/* helpers to submit/free our list of dma descriptors */ -+static void dma_callback(void *param) -+{ -+ struct qcom_nandc_data *this = param; -+ struct completion *c = &this->dma_done; -+ -+ complete(c); -+} -+ -+static int submit_descs(struct qcom_nandc_data *this) -+{ -+ struct completion *c = &this->dma_done; -+ struct desc_info *desc; -+ int r; -+ -+ init_completion(c); -+ -+ list_for_each_entry(desc, &this->list, list) { -+ /* -+ * we add a callback to the last descriptor in our list to -+ * notify completion of command -+ */ -+ if (list_is_last(&desc->list, &this->list)) { -+ desc->dma_desc->callback = dma_callback; -+ desc->dma_desc->callback_param = this; -+ } -+ -+ dmaengine_submit(desc->dma_desc); -+ } -+ -+ dma_async_issue_pending(this->chan); -+ -+ r = wait_for_completion_timeout(c, msecs_to_jiffies(500)); -+ if (!r) -+ return -ETIMEDOUT; -+ -+ return 0; -+} -+ -+static void free_descs(struct qcom_nandc_data *this) -+{ -+ struct desc_info *desc, *n; -+ -+ list_for_each_entry_safe(desc, n, &this->list, list) { -+ list_del(&desc->list); -+ dma_unmap_sg(this->dev, &desc->sgl, 1, desc->dir); -+ kfree(desc); -+ } -+} -+ -+/* reset the register read buffer for next NAND operation */ -+static void clear_read_regs(struct qcom_nandc_data *this) -+{ -+ this->reg_read_pos = 0; -+ memset(this->reg_read_buf, 0, MAX_REG_RD * sizeof(*this->reg_read_buf)); -+} -+ -+static void pre_command(struct qcom_nandc_data *this, int command) -+{ -+ this->buf_count = 0; -+ this->buf_start = 0; -+ this->use_ecc = false; -+ this->last_command = command; -+ -+ clear_read_regs(this); -+} -+ -+/* -+ * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our -+ * privately maintained status byte, this status byte can be read after -+ * NAND_CMD_STATUS is called -+ */ -+static void parse_erase_write_errors(struct qcom_nandc_data *this, int command) -+{ -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int num_cw; -+ int i; -+ -+ num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1; -+ -+ for (i = 0; i < num_cw; i++) { -+ __le32 flash_status = le32_to_cpu(this->reg_read_buf[i]); -+ -+ if (flash_status & FS_MPU_ERR) -+ this->status &= ~NAND_STATUS_WP; -+ -+ if (flash_status & FS_OP_ERR || (i == (num_cw - 1) && -+ (flash_status & FS_DEVICE_STS_ERR))) -+ this->status |= NAND_STATUS_FAIL; -+ } -+} -+ -+static void post_command(struct qcom_nandc_data *this, int command) -+{ -+ switch (command) { -+ case NAND_CMD_READID: -+ memcpy(this->data_buffer, this->reg_read_buf, this->buf_count); -+ break; -+ case NAND_CMD_PAGEPROG: -+ case NAND_CMD_ERASE1: -+ parse_erase_write_errors(this, command); -+ break; -+ default: -+ break; -+ } -+} -+ -+/* -+ * Implements chip->cmdfunc. It's only used for a limited set of commands. -+ * The rest of the commands wouldn't be called by upper layers. For example, -+ * NAND_CMD_READOOB would never be called because we have our own versions -+ * of read_oob ops for nand_ecc_ctrl. -+ */ -+static void qcom_nandc_command(struct mtd_info *mtd, unsigned int command, -+ int column, int page_addr) -+{ -+ struct nand_chip *chip = mtd->priv; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ struct qcom_nandc_data *this = chip->priv; -+ bool wait = false; -+ int r = 0; -+ -+ pre_command(this, command); -+ -+ switch (command) { -+ case NAND_CMD_RESET: -+ r = reset(this); -+ wait = true; -+ break; -+ -+ case NAND_CMD_READID: -+ this->buf_count = 4; -+ r = read_id(this, column); -+ wait = true; -+ break; -+ -+ case NAND_CMD_PARAM: -+ r = nandc_param(this); -+ wait = true; -+ break; -+ -+ case NAND_CMD_ERASE1: -+ r = erase_block(this, page_addr); -+ wait = true; -+ break; -+ -+ case NAND_CMD_READ0: -+ /* we read the entire page for now */ -+ WARN_ON(column != 0); -+ -+ this->use_ecc = true; -+ set_address(this, 0, page_addr); -+ update_rw_regs(this, ecc->steps, true); -+ break; -+ -+ case NAND_CMD_SEQIN: -+ WARN_ON(column != 0); -+ set_address(this, 0, page_addr); -+ break; -+ -+ case NAND_CMD_PAGEPROG: -+ case NAND_CMD_STATUS: -+ case NAND_CMD_NONE: -+ default: -+ break; -+ } -+ -+ if (r) { -+ dev_err(this->dev, "failure executing command %d\n", -+ command); -+ free_descs(this); -+ return; -+ } -+ -+ if (wait) { -+ r = submit_descs(this); -+ if (r) -+ dev_err(this->dev, -+ "failure submitting descs for command %d\n", -+ command); -+ } -+ -+ free_descs(this); -+ -+ post_command(this, command); -+} -+ -+/* -+ * when using RS ECC, the NAND controller flags an error when reading an -+ * erased page. however, there are special characters at certain offsets when -+ * we read the erased page. we check here if the page is really empty. if so, -+ * we replace the magic characters with 0xffs -+ */ -+static bool empty_page_fixup(struct qcom_nandc_data *this, u8 *data_buf) -+{ -+ struct mtd_info *mtd = &this->mtd; -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int cwperpage = ecc->steps; -+ u8 orig1[MAX_NUM_STEPS], orig2[MAX_NUM_STEPS]; -+ int i, j; -+ -+ /* if BCH is enabled, HW will take care of detecting erased pages */ -+ if (this->bch_enabled || !this->use_ecc) -+ return false; -+ -+ for (i = 0; i < cwperpage; i++) { -+ u8 *empty1, *empty2; -+ __le32 flash_status = le32_to_cpu(this->reg_read_buf[3 * i]); -+ -+ /* -+ * an erased page flags an error in NAND_FLASH_STATUS, check if -+ * the page is erased by looking for 0x54s at offsets 3 and 175 -+ * from the beginning of each codeword -+ */ -+ if (!(flash_status & FS_OP_ERR)) -+ break; -+ -+ empty1 = &data_buf[3 + i * this->cw_data]; -+ empty2 = &data_buf[175 + i * this->cw_data]; -+ -+ /* -+ * if the error wasn't because of an erased page, bail out and -+ * and let someone else do the error checking -+ */ -+ if ((*empty1 == 0x54 && *empty2 == 0xff) || -+ (*empty1 == 0xff && *empty2 == 0x54)) { -+ orig1[i] = *empty1; -+ orig2[i] = *empty2; -+ -+ *empty1 = 0xff; -+ *empty2 = 0xff; -+ } else { -+ break; -+ } -+ } -+ -+ if (i < cwperpage || memchr_inv(data_buf, 0xff, mtd->writesize)) -+ goto not_empty; -+ -+ /* -+ * tell the caller that the page was empty and is fixed up, so that -+ * parse_read_errors() doesn't think it's an error -+ */ -+ return true; -+ -+not_empty: -+ /* restore original values if not empty*/ -+ for (j = 0; j < i; j++) { -+ data_buf[3 + j * this->cw_data] = orig1[j]; -+ data_buf[175 + j * this->cw_data] = orig2[j]; -+ } -+ -+ return false; -+} -+ -+struct read_stats { -+ __le32 flash; -+ __le32 buffer; -+ __le32 erased_cw; -+}; -+ -+/* -+ * reads back status registers set by the controller to notify page read -+ * errors. this is equivalent to what 'ecc->correct()' would do. -+ */ -+static int parse_read_errors(struct qcom_nandc_data *this, bool erased_page) -+{ -+ struct mtd_info *mtd = &this->mtd; -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int cwperpage = ecc->steps; -+ unsigned int max_bitflips = 0; -+ int i; -+ -+ for (i = 0; i < cwperpage; i++) { -+ int stat; -+ struct read_stats *buf; -+ -+ buf = (struct read_stats *) (this->reg_read_buf + 3 * i); -+ -+ buf->flash = le32_to_cpu(buf->flash); -+ buf->buffer = le32_to_cpu(buf->buffer); -+ buf->erased_cw = le32_to_cpu(buf->erased_cw); -+ -+ if (buf->flash & (FS_OP_ERR | FS_MPU_ERR)) { -+ -+ /* ignore erased codeword errors */ -+ if (this->bch_enabled) { -+ if ((buf->erased_cw & ERASED_CW) == ERASED_CW) -+ continue; -+ } else if (erased_page) { -+ continue; -+ } -+ -+ if (buf->buffer & BS_UNCORRECTABLE_BIT) { -+ mtd->ecc_stats.failed++; -+ continue; -+ } -+ } -+ -+ stat = buf->buffer & BS_CORRECTABLE_ERR_MSK; -+ mtd->ecc_stats.corrected += stat; -+ -+ max_bitflips = max_t(unsigned int, max_bitflips, stat); -+ } -+ -+ return max_bitflips; -+} -+ -+/* -+ * helper to perform the actual page read operation, used by ecc->read_page() -+ * and ecc->read_oob() -+ */ -+static int read_page_low(struct qcom_nandc_data *this, u8 *data_buf, -+ u8 *oob_buf) -+{ -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int i, r; -+ -+ /* queue cmd descs for each codeword */ -+ for (i = 0; i < ecc->steps; i++) { -+ int data_size, oob_size; -+ -+ if (i == (ecc->steps - 1)) { -+ data_size = ecc->size - ((ecc->steps - 1) << 2); -+ oob_size = (ecc->steps << 2) + ecc->bytes; -+ } else { -+ data_size = this->cw_data; -+ oob_size = ecc->bytes; -+ } -+ -+ config_cw_read(this); -+ -+ if (data_buf) -+ read_data_dma(this, FLASH_BUF_ACC, data_buf, data_size); -+ -+ if (oob_buf) -+ read_data_dma(this, FLASH_BUF_ACC + data_size, oob_buf, -+ oob_size); -+ -+ if (data_buf) -+ data_buf += data_size; -+ if (oob_buf) -+ oob_buf += oob_size; -+ } -+ -+ r = submit_descs(this); -+ if (r) -+ dev_err(this->dev, "failure to read page/oob\n"); -+ -+ free_descs(this); -+ -+ return r; -+} -+ -+/* -+ * a helper that copies the last step/codeword of a page (containing free oob) -+ * into our local buffer -+ */ -+static int copy_last_cw(struct qcom_nandc_data *this, bool use_ecc, int page) -+{ -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int size; -+ int r; -+ -+ clear_read_regs(this); -+ -+ size = use_ecc ? this->cw_data : this->cw_size; -+ -+ /* prepare a clean read buffer */ -+ memset(this->data_buffer, 0xff, size); -+ -+ this->use_ecc = use_ecc; -+ set_address(this, this->cw_size * (ecc->steps - 1), page); -+ update_rw_regs(this, 1, true); -+ -+ config_cw_read(this); -+ -+ read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, size); -+ -+ r = submit_descs(this); -+ if (r) -+ dev_err(this->dev, "failed to copy last codeword\n"); -+ -+ free_descs(this); -+ -+ return r; -+} -+ -+/* implements ecc->read_page() */ -+static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, -+ uint8_t *buf, int oob_required, int page) -+{ -+ struct qcom_nandc_data *this = chip->priv; -+ u8 *data_buf, *oob_buf = NULL; -+ bool erased_page; -+ int r; -+ -+ data_buf = buf; -+ oob_buf = oob_required ? chip->oob_poi : NULL; -+ -+ r = read_page_low(this, data_buf, oob_buf); -+ if (r) { -+ dev_err(this->dev, "failure to read page\n"); -+ return r; -+ } -+ -+ erased_page = empty_page_fixup(this, data_buf); -+ -+ return parse_read_errors(this, erased_page); -+} -+ -+/* implements ecc->read_oob() */ -+static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, -+ int page) -+{ -+ struct qcom_nandc_data *this = chip->priv; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int r; -+ -+ clear_read_regs(this); -+ -+ this->use_ecc = true; -+ set_address(this, 0, page); -+ update_rw_regs(this, ecc->steps, true); -+ -+ r = read_page_low(this, NULL, chip->oob_poi); -+ if (r) -+ dev_err(this->dev, "failure to read oob\n"); -+ -+ return r; -+} -+ -+/* implements ecc->read_oob_raw(), used to read the bad block marker flag */ -+static int qcom_nandc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, -+ int page) -+{ -+ struct qcom_nandc_data *this = chip->priv; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ uint8_t *oob = chip->oob_poi; -+ int start, length; -+ int r; -+ -+ /* -+ * configure registers for a raw page read, the address is set to the -+ * beginning of the last codeword, we don't care about reading ecc -+ * portion of oob, just the free stuff -+ */ -+ r = copy_last_cw(this, false, page); -+ if (r) -+ return r; -+ -+ /* -+ * reading raw oob has 2 parts, first the bad block byte, then the -+ * actual free oob region. perform a memcpy in two steps -+ */ -+ start = mtd->writesize - (this->cw_size * (ecc->steps - 1)); -+ length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1; -+ -+ memcpy(oob, this->data_buffer + start, length); -+ -+ oob += length; -+ -+ start = this->cw_data - (ecc->steps << 2) + 1; -+ length = ecc->steps << 2; -+ -+ memcpy(oob, this->data_buffer + start, length); -+ -+ return 0; -+} -+ -+/* implements ecc->write_page() */ -+static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip, -+ const uint8_t *buf, int oob_required) -+{ -+ struct qcom_nandc_data *this = chip->priv; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ u8 *data_buf, *oob_buf; -+ int i, r = 0; -+ -+ clear_read_regs(this); -+ -+ data_buf = (u8 *) buf; -+ oob_buf = chip->oob_poi; -+ -+ this->use_ecc = true; -+ update_rw_regs(this, ecc->steps, false); -+ -+ for (i = 0; i < ecc->steps; i++) { -+ int data_size, oob_size; -+ -+ if (i == (ecc->steps - 1)) { -+ data_size = ecc->size - ((ecc->steps - 1) << 2); -+ oob_size = (ecc->steps << 2) + ecc->bytes; -+ } else { -+ data_size = this->cw_data; -+ oob_size = ecc->bytes; -+ } -+ -+ config_cw_write_pre(this); -+ write_data_dma(this, FLASH_BUF_ACC, data_buf, data_size); -+ -+ /* -+ * we don't really need to write anything to oob for the -+ * first n - 1 codewords since these oob regions just -+ * contain ecc that's written by the controller itself -+ */ -+ if (i == (ecc->steps - 1)) -+ write_data_dma(this, FLASH_BUF_ACC + data_size, -+ oob_buf, oob_size); -+ config_cw_write_post(this); -+ -+ data_buf += data_size; -+ oob_buf += oob_size; -+ } -+ -+ r = submit_descs(this); -+ if (r) -+ dev_err(this->dev, "failure to write page\n"); -+ -+ free_descs(this); -+ -+ return r; -+} -+ -+/* -+ * implements ecc->write_oob() -+ * -+ * the NAND controller cannot write only data or only oob within a codeword, -+ * since ecc is calculated for the combined codeword. we first copy the -+ * entire contents for the last codeword(data + oob), replace the old oob -+ * with the new one in chip->oob_poi, and then write the entire codeword. -+ * this read-copy-write operation results in a slight perormance loss. -+ */ -+static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, -+ int page) -+{ -+ struct qcom_nandc_data *this = chip->priv; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ uint8_t *oob = chip->oob_poi; -+ int free_boff; -+ int data_size, oob_size; -+ int r, status = 0; -+ -+ r = copy_last_cw(this, true, page); -+ if (r) -+ return r; -+ -+ clear_read_regs(this); -+ -+ /* calculate the data and oob size for the last codeword/step */ -+ data_size = ecc->size - ((ecc->steps - 1) << 2); -+ oob_size = (ecc->steps << 2) + ecc->bytes; -+ -+ /* -+ * the location of spare data in the oob buffer, we could also use -+ * ecc->layout.oobfree here -+ */ -+ free_boff = ecc->bytes * (ecc->steps - 1); -+ -+ /* override new oob content to last codeword */ -+ memcpy(this->data_buffer + data_size, oob + free_boff, oob_size); -+ -+ this->use_ecc = true; -+ set_address(this, this->cw_size * (ecc->steps - 1), page); -+ update_rw_regs(this, 1, false); -+ -+ config_cw_write_pre(this); -+ write_data_dma(this, FLASH_BUF_ACC, this->data_buffer, -+ data_size + oob_size); -+ config_cw_write_post(this); -+ -+ r = submit_descs(this); -+ -+ free_descs(this); -+ -+ if (r) { -+ dev_err(this->dev, "failure to write oob\n"); -+ return -EIO; -+ } -+ -+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); -+ -+ status = chip->waitfunc(mtd, chip); -+ -+ return status & NAND_STATUS_FAIL ? -EIO : 0; -+} -+ -+/* implements ecc->write_oob_raw(), used to write bad block marker flag */ -+static int qcom_nandc_write_oob_raw(struct mtd_info *mtd, -+ struct nand_chip *chip, int page) -+{ -+ struct qcom_nandc_data *this = chip->priv; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ uint8_t *oob = chip->oob_poi; -+ int start, length; -+ int r, status = 0; -+ -+ r = copy_last_cw(this, false, page); -+ if (r) -+ return r; -+ -+ clear_read_regs(this); -+ -+ /* -+ * writing raw oob has 2 parts, first the bad block region, then the -+ * actual free region -+ */ -+ start = mtd->writesize - (this->cw_size * (ecc->steps - 1)); -+ length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1; -+ -+ memcpy(this->data_buffer + start, oob, length); -+ -+ oob += length; -+ -+ start = this->cw_data - (ecc->steps << 2) + 1; -+ length = ecc->steps << 2; -+ -+ memcpy(this->data_buffer + start, oob, length); -+ -+ /* prepare write */ -+ this->use_ecc = false; -+ set_address(this, this->cw_size * (ecc->steps - 1), page); -+ update_rw_regs(this, 1, false); -+ -+ config_cw_write_pre(this); -+ write_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->cw_size); -+ config_cw_write_post(this); -+ -+ r = submit_descs(this); -+ -+ free_descs(this); -+ -+ if (r) { -+ dev_err(this->dev, "failure to write updated oob\n"); -+ return -EIO; -+ } -+ -+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); -+ -+ status = chip->waitfunc(mtd, chip); -+ -+ return status & NAND_STATUS_FAIL ? -EIO : 0; -+} -+ -+/* -+ * the three functions below implement chip->read_byte(), chip->read_buf() -+ * and chip->write_buf() respectively. these aren't used for -+ * reading/writing page data, they are used for smaller data like reading -+ * id, status etc -+ */ -+static uint8_t qcom_nandc_read_byte(struct mtd_info *mtd) -+{ -+ struct nand_chip *chip = mtd->priv; -+ struct qcom_nandc_data *this = chip->priv; -+ uint8_t *buf = this->data_buffer; -+ uint8_t ret = 0x0; -+ -+ if (this->last_command == NAND_CMD_STATUS) { -+ ret = this->status; -+ -+ this->status = NAND_STATUS_READY | NAND_STATUS_WP; -+ -+ return ret; -+ } -+ -+ if (this->buf_start < this->buf_count) -+ ret = buf[this->buf_start++]; -+ -+ return ret; -+} -+ -+static void qcom_nandc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -+{ -+ struct nand_chip *chip = mtd->priv; -+ struct qcom_nandc_data *this = chip->priv; -+ int real_len = min_t(size_t, len, this->buf_count - this->buf_start); -+ -+ memcpy(buf, this->data_buffer + this->buf_start, real_len); -+ this->buf_start += real_len; -+} -+ -+static void qcom_nandc_write_buf(struct mtd_info *mtd, const uint8_t *buf, -+ int len) -+{ -+ struct nand_chip *chip = mtd->priv; -+ struct qcom_nandc_data *this = chip->priv; -+ int real_len = min_t(size_t, len, this->buf_count - this->buf_start); -+ -+ memcpy(this->data_buffer + this->buf_start, buf, real_len); -+ -+ this->buf_start += real_len; -+} -+ -+/* we support only one external chip for now */ -+static void qcom_nandc_select_chip(struct mtd_info *mtd, int chipnr) -+{ -+ struct nand_chip *chip = mtd->priv; -+ struct qcom_nandc_data *this = chip->priv; -+ -+ if (chipnr <= 0) -+ return; -+ -+ dev_warn(this->dev, "invalid chip select\n"); -+} -+ -+/* -+ * NAND controller page layout info -+ * -+ * |-----------------------| |---------------------------------| -+ * | xx.......xx| | *********xx.......xx| -+ * | DATA xx..ECC..xx| | DATA **SPARE**xx..ECC..xx| -+ * | (516) xx.......xx| | (516-n*4) **(n*4)**xx.......xx| -+ * | xx.......xx| | *********xx.......xx| -+ * |-----------------------| |---------------------------------| -+ * codeword 1,2..n-1 codeword n -+ * <---(528/532 Bytes)----> <-------(528/532 Bytes)----------> -+ * -+ * n = number of codewords in the page -+ * . = ECC bytes -+ * * = spare bytes -+ * x = unused/reserved bytes -+ * -+ * 2K page: n = 4, spare = 16 bytes -+ * 4K page: n = 8, spare = 32 bytes -+ * 8K page: n = 16, spare = 64 bytes -+ * -+ * the qcom nand controller operates at a sub page/codeword level. each -+ * codeword is 528 and 532 bytes for 4 bit and 8 bit ECC modes respectively. -+ * the number of ECC bytes vary based on the ECC strength and the bus width. -+ * -+ * the first n - 1 codewords contains 516 bytes of user data, the remaining -+ * 12/16 bytes consist of ECC and reserved data. The nth codeword contains -+ * both user data and spare(oobavail) bytes that sum up to 516 bytes. -+ * -+ * the layout described above is used by the controller when the ECC block is -+ * enabled. When we read a page with ECC enabled, the unused/reserved bytes are -+ * skipped and not copied to our internal buffer. therefore, the nand_ecclayout -+ * layouts defined below doesn't consider the positions occupied by the reserved -+ * bytes -+ * -+ * when the ECC block is disabled, one unused byte (or two for 16 bit bus width) -+ * in the last codeword is the position of bad block marker. the bad block -+ * marker cannot be accessed when ECC is enabled. -+ * -+ */ -+ -+/* -+ * Layouts for different page sizes and ecc modes. We skip the eccpos field -+ * since it isn't needed for this driver -+ */ -+ -+/* 2K page, 4 bit ECC */ -+static struct nand_ecclayout layout_oob_64 = { -+ .eccbytes = 40, -+ .oobfree = { -+ { 30, 16 }, -+ }, -+}; -+ -+/* 4K page, 4 bit ECC, 8/16 bit bus width */ -+static struct nand_ecclayout layout_oob_128 = { -+ .eccbytes = 80, -+ .oobfree = { -+ { 70, 32 }, -+ }, -+}; -+ -+/* 4K page, 8 bit ECC, 8 bit bus width */ -+static struct nand_ecclayout layout_oob_224_x8 = { -+ .eccbytes = 104, -+ .oobfree = { -+ { 91, 32 }, -+ }, -+}; -+ -+/* 4K page, 8 bit ECC, 16 bit bus width */ -+static struct nand_ecclayout layout_oob_224_x16 = { -+ .eccbytes = 112, -+ .oobfree = { -+ { 98, 32 }, -+ }, -+}; -+ -+/* 8K page, 4 bit ECC, 8/16 bit bus width */ -+static struct nand_ecclayout layout_oob_256 = { -+ .eccbytes = 160, -+ .oobfree = { -+ { 151, 64 }, -+ }, -+}; -+ -+/* -+ * this is called before scan_ident, we do some minimal configurations so -+ * that reading ID and ONFI params work -+ */ -+static void qcom_nandc_pre_init(struct qcom_nandc_data *this) -+{ -+ /* kill onenand */ -+ nandc_write(this, SFLASHC_BURST_CFG, 0); -+ -+ /* enable ADM DMA */ -+ nandc_write(this, NAND_FLASH_CHIP_SELECT, DM_EN); -+ -+ /* save the original values of these registers */ -+ this->cmd1 = nandc_read(this, NAND_DEV_CMD1); -+ this->vld = nandc_read(this, NAND_DEV_CMD_VLD); -+ -+ /* initial status value */ -+ this->status = NAND_STATUS_READY | NAND_STATUS_WP; -+} -+ -+static int qcom_nandc_ecc_init(struct qcom_nandc_data *this) -+{ -+ struct mtd_info *mtd = &this->mtd; -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int cwperpage; -+ bool wide_bus; -+ -+ /* the nand controller fetches codewords/chunks of 512 bytes */ -+ cwperpage = mtd->writesize >> 9; -+ -+ ecc->strength = this->ecc_strength; -+ -+ wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; -+ -+ if (ecc->strength >= 8) { -+ /* 8 bit ECC defaults to BCH ECC on all platforms */ -+ ecc->bytes = wide_bus ? 14 : 13; -+ } else { -+ /* -+ * if the controller supports BCH for 4 bit ECC, the controller -+ * uses lesser bytes for ECC. If RS is used, the ECC bytes is -+ * always 10 bytes -+ */ -+ if (this->ecc_modes & ECC_BCH_4BIT) -+ ecc->bytes = wide_bus ? 8 : 7; -+ else -+ ecc->bytes = 10; -+ } -+ -+ /* each step consists of 512 bytes of data */ -+ ecc->size = NANDC_STEP_SIZE; -+ -+ ecc->read_page = qcom_nandc_read_page; -+ ecc->read_oob = qcom_nandc_read_oob; -+ ecc->write_page = qcom_nandc_write_page; -+ ecc->write_oob = qcom_nandc_write_oob; -+ -+ /* -+ * the bad block marker is readable only when we read the page with ECC -+ * disabled. all the ops above run with ECC enabled. We need raw read -+ * and write function for oob in order to access bad block marker. -+ */ -+ ecc->read_oob_raw = qcom_nandc_read_oob_raw; -+ ecc->write_oob_raw = qcom_nandc_write_oob_raw; -+ -+ switch (mtd->oobsize) { -+ case 64: -+ ecc->layout = &layout_oob_64; -+ break; -+ case 128: -+ ecc->layout = &layout_oob_128; -+ break; -+ case 224: -+ if (wide_bus) -+ ecc->layout = &layout_oob_224_x16; -+ else -+ ecc->layout = &layout_oob_224_x8; -+ break; -+ case 256: -+ ecc->layout = &layout_oob_256; -+ break; -+ default: -+ dev_err(this->dev, "unsupported NAND device, oobsize %d\n", -+ mtd->oobsize); -+ return -ENODEV; -+ } -+ -+ ecc->mode = NAND_ECC_HW; -+ -+ /* enable ecc by default */ -+ this->use_ecc = true; -+ -+ return 0; -+} -+ -+static void qcom_nandc_hw_post_init(struct qcom_nandc_data *this) -+{ -+ struct mtd_info *mtd = &this->mtd; -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int cwperpage = mtd->writesize / ecc->size; -+ int spare_bytes, bad_block_byte; -+ bool wide_bus; -+ int ecc_mode = 0; -+ -+ wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; -+ -+ if (ecc->strength >= 8) { -+ this->cw_size = 532; -+ -+ spare_bytes = wide_bus ? 0 : 2; -+ -+ this->bch_enabled = true; -+ ecc_mode = 1; -+ } else { -+ this->cw_size = 528; -+ -+ if (this->ecc_modes & ECC_BCH_4BIT) { -+ spare_bytes = wide_bus ? 2 : 4; -+ -+ this->bch_enabled = true; -+ ecc_mode = 0; -+ } else { -+ spare_bytes = wide_bus ? 0 : 1; -+ } -+ } -+ -+ /* -+ * DATA_UD_BYTES varies based on whether the read/write command protects -+ * spare data with ECC too. We protect spare data by default, so we set -+ * it to main + spare data, which are 512 and 4 bytes respectively. -+ */ -+ this->cw_data = 516; -+ -+ bad_block_byte = mtd->writesize - this->cw_size * (cwperpage - 1) + 1; -+ -+ this->cfg0 = (cwperpage - 1) << CW_PER_PAGE -+ | this->cw_data << UD_SIZE_BYTES -+ | 0 << DISABLE_STATUS_AFTER_WRITE -+ | 5 << NUM_ADDR_CYCLES -+ | ecc->bytes << ECC_PARITY_SIZE_BYTES_RS -+ | 0 << STATUS_BFR_READ -+ | 1 << SET_RD_MODE_AFTER_STATUS -+ | spare_bytes << SPARE_SIZE_BYTES; -+ -+ this->cfg1 = 7 << NAND_RECOVERY_CYCLES -+ | 0 << CS_ACTIVE_BSY -+ | bad_block_byte << BAD_BLOCK_BYTE_NUM -+ | 0 << BAD_BLOCK_IN_SPARE_AREA -+ | 2 << WR_RD_BSY_GAP -+ | wide_bus << WIDE_FLASH -+ | this->bch_enabled << ENABLE_BCH_ECC; -+ -+ this->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE -+ | this->cw_size << UD_SIZE_BYTES -+ | 5 << NUM_ADDR_CYCLES -+ | 0 << SPARE_SIZE_BYTES; -+ -+ this->cfg1_raw = 7 << NAND_RECOVERY_CYCLES -+ | 0 << CS_ACTIVE_BSY -+ | 17 << BAD_BLOCK_BYTE_NUM -+ | 1 << BAD_BLOCK_IN_SPARE_AREA -+ | 2 << WR_RD_BSY_GAP -+ | wide_bus << WIDE_FLASH -+ | 1 << DEV0_CFG1_ECC_DISABLE; -+ -+ this->ecc_bch_cfg = this->bch_enabled << ECC_CFG_ECC_DISABLE -+ | 0 << ECC_SW_RESET -+ | this->cw_data << ECC_NUM_DATA_BYTES -+ | 1 << ECC_FORCE_CLK_OPEN -+ | ecc_mode << ECC_MODE -+ | ecc->bytes << ECC_PARITY_SIZE_BYTES_BCH; -+ -+ this->ecc_buf_cfg = 0x203 << NUM_STEPS; -+ -+ this->clrflashstatus = FS_READY_BSY_N; -+ this->clrreadstatus = 0xc0; -+ -+ dev_dbg(this->dev, -+ "cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n", -+ this->cfg0, this->cfg1, this->ecc_buf_cfg, -+ this->ecc_bch_cfg, this->cw_size, this->cw_data, -+ ecc->strength, ecc->bytes, cwperpage); -+} -+ -+static int qcom_nandc_alloc(struct qcom_nandc_data *this) -+{ -+ int r; -+ -+ r = dma_set_coherent_mask(this->dev, DMA_BIT_MASK(32)); -+ if (r) { -+ dev_err(this->dev, "failed to set DMA mask\n"); -+ return r; -+ } -+ -+ /* -+ * we use the internal buffer for reading ONFI params, reading small -+ * data like ID and status, and preforming read-copy-write operations -+ * when writing to a codeword partially. 532 is the maximum possible -+ * size of a codeword for our nand controller -+ */ -+ this->buf_size = 532; -+ -+ this->data_buffer = devm_kzalloc(this->dev, this->buf_size, GFP_KERNEL); -+ if (!this->data_buffer) -+ return -ENOMEM; -+ -+ this->regs = devm_kzalloc(this->dev, sizeof(*this->regs), GFP_KERNEL); -+ if (!this->regs) -+ return -ENOMEM; -+ -+ this->reg_read_buf = devm_kzalloc(this->dev, -+ MAX_REG_RD * sizeof(*this->reg_read_buf), -+ GFP_KERNEL); -+ if (!this->reg_read_buf) -+ return -ENOMEM; -+ -+ INIT_LIST_HEAD(&this->list); -+ -+ this->chan = dma_request_slave_channel(this->dev, "rxtx"); -+ if (!this->chan) { -+ dev_err(this->dev, "failed to request slave channel\n"); -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+static void qcom_nandc_unalloc(struct qcom_nandc_data *this) -+{ -+ dma_release_channel(this->chan); -+} -+ -+static int qcom_nandc_init(struct qcom_nandc_data *this) -+{ -+ struct mtd_info *mtd = &this->mtd; -+ struct nand_chip *chip = &this->chip; -+ struct device_node *np = this->dev->of_node; -+ struct mtd_part_parser_data ppdata = { .of_node = np }; -+ int r; -+ -+ mtd->priv = chip; -+ mtd->name = "qcom-nandc"; -+ mtd->owner = THIS_MODULE; -+ -+ chip->priv = this; -+ -+ chip->cmdfunc = qcom_nandc_command; -+ chip->select_chip = qcom_nandc_select_chip; -+ chip->read_byte = qcom_nandc_read_byte; -+ chip->read_buf = qcom_nandc_read_buf; -+ chip->write_buf = qcom_nandc_write_buf; -+ -+ chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER; -+ if (this->bus_width == 16) -+ chip->options |= NAND_BUSWIDTH_16; -+ -+ chip->bbt_options = NAND_BBT_ACCESS_BBM_RAW; -+ if (of_get_nand_on_flash_bbt(np)) -+ chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; -+ -+ qcom_nandc_pre_init(this); -+ -+ r = nand_scan_ident(mtd, 1, NULL); -+ if (r) -+ return r; -+ -+ r = qcom_nandc_ecc_init(this); -+ if (r) -+ return r; -+ -+ qcom_nandc_hw_post_init(this); -+ -+ r = nand_scan_tail(mtd); -+ if (r) -+ return r; -+ -+ return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); -+} -+ -+static int qcom_nandc_parse_dt(struct platform_device *pdev) -+{ -+ struct qcom_nandc_data *this = platform_get_drvdata(pdev); -+ struct device_node *np = this->dev->of_node; -+ int r; -+ -+ this->ecc_strength = of_get_nand_ecc_strength(np); -+ if (this->ecc_strength < 0) { -+ dev_warn(this->dev, -+ "incorrect ecc strength, setting to 4 bits/step\n"); -+ this->ecc_strength = 4; -+ } -+ -+ this->bus_width = of_get_nand_bus_width(np); -+ if (this->bus_width < 0) { -+ dev_warn(this->dev, "incorrect bus width, setting to 8\n"); -+ this->bus_width = 8; -+ } -+ -+ r = of_property_read_u32(np, "qcom,cmd-crci", &this->cmd_crci); -+ if (r) { -+ dev_err(this->dev, "command CRCI unspecified\n"); -+ return r; -+ } -+ -+ r = of_property_read_u32(np, "qcom,data-crci", &this->data_crci); -+ if (r) { -+ dev_err(this->dev, "data CRCI unspecified\n"); -+ return r; -+ } -+ -+ return 0; -+} -+ -+static int qcom_nandc_probe(struct platform_device *pdev) -+{ -+ struct qcom_nandc_data *this; -+ const struct of_device_id *match; -+ int r; -+ -+ this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL); -+ if (!this) -+ return -ENOMEM; -+ -+ platform_set_drvdata(pdev, this); -+ -+ this->pdev = pdev; -+ this->dev = &pdev->dev; -+ -+ match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); -+ if (!match) { -+ dev_err(&pdev->dev, "failed to match device\n"); -+ return -ENODEV; -+ } -+ -+ if (!match->data) { -+ dev_err(&pdev->dev, "failed to get device data\n"); -+ return -ENODEV; -+ } -+ -+ this->ecc_modes = (u32) match->data; -+ -+ this->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ this->base = devm_ioremap_resource(&pdev->dev, this->res); -+ if (IS_ERR(this->base)) -+ return PTR_ERR(this->base); -+ -+ this->core_clk = devm_clk_get(&pdev->dev, "core"); -+ if (IS_ERR(this->core_clk)) -+ return PTR_ERR(this->core_clk); -+ -+ this->aon_clk = devm_clk_get(&pdev->dev, "aon"); -+ if (IS_ERR(this->aon_clk)) -+ return PTR_ERR(this->aon_clk); -+ -+ r = qcom_nandc_parse_dt(pdev); -+ if (r) -+ return r; -+ -+ r = qcom_nandc_alloc(this); -+ if (r) -+ return r; -+ -+ r = clk_prepare_enable(this->core_clk); -+ if (r) -+ goto err_core_clk; -+ -+ r = clk_prepare_enable(this->aon_clk); -+ if (r) -+ goto err_aon_clk; -+ -+ r = qcom_nandc_init(this); -+ if (r) -+ goto err_init; -+ -+ return 0; -+ -+err_init: -+ clk_disable_unprepare(this->aon_clk); -+err_aon_clk: -+ clk_disable_unprepare(this->core_clk); -+err_core_clk: -+ qcom_nandc_unalloc(this); -+ -+ return r; -+} -+ -+static int qcom_nandc_remove(struct platform_device *pdev) -+{ -+ struct qcom_nandc_data *this = platform_get_drvdata(pdev); -+ -+ qcom_nandc_unalloc(this); -+ -+ clk_disable_unprepare(this->aon_clk); -+ clk_disable_unprepare(this->core_clk); -+ -+ return 0; -+} -+ -+#define EBI2_NANDC_ECC_MODES (ECC_RS_4BIT | ECC_BCH_8BIT) -+ -+/* -+ * data will hold a struct pointer containing more differences once we support -+ * more IPs -+ */ -+static const struct of_device_id qcom_nandc_of_match[] = { -+ { .compatible = "qcom,ebi2-nandc", -+ .data = (void *) EBI2_NANDC_ECC_MODES, -+ }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, qcom_nandc_of_match); -+ -+static struct platform_driver qcom_nandc_driver = { -+ .driver = { -+ .name = "qcom-nandc", -+ .of_match_table = qcom_nandc_of_match, -+ }, -+ .probe = qcom_nandc_probe, -+ .remove = qcom_nandc_remove, -+}; -+module_platform_driver(qcom_nandc_driver); -+ -+MODULE_AUTHOR("Archit Taneja "); -+MODULE_DESCRIPTION("Qualcomm NAND Controller driver"); -+MODULE_LICENSE("GPL v2"); ---- a/drivers/mtd/nand/Makefile -+++ b/drivers/mtd/nand/Makefile -@@ -55,5 +55,6 @@ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += - obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o - obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o - obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ -+obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o - - nand-objs := nand_base.o nand_bbt.o nand_timings.o diff --git a/target/linux/ipq806x/patches-4.4/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch b/target/linux/ipq806x/patches-4.4/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch deleted file mode 100644 index 6530eb1c86..0000000000 --- a/target/linux/ipq806x/patches-4.4/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch +++ /dev/null @@ -1,82 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,3/5] dt/bindings: qcom_nandc: Add DT bindings -From: Archit Taneja -X-Patchwork-Id: 6927141 -Message-Id: <1438578498-32254-4-git-send-email-architt@codeaurora.org> -To: linux-mtd@lists.infradead.org, dehrenberg@google.com, - cernekee@gmail.com, computersforpeace@gmail.com -Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, - sboyd@codeaurora.org, linux-kernel@vger.kernel.org, - Archit Taneja , devicetree@vger.kernel.org -Date: Mon, 3 Aug 2015 10:38:16 +0530 - -Add DT bindings document for the Qualcomm NAND controller driver. - -Cc: devicetree@vger.kernel.org - -v3: -- Don't use '0x' when specifying nand controller address space -- Add optional property for on-flash bbt usage - -Acked-by: Andy Gross -Signed-off-by: Archit Taneja - ---- -.../devicetree/bindings/mtd/qcom_nandc.txt | 49 ++++++++++++++++++++++ - 1 file changed, 49 insertions(+) - create mode 100644 Documentation/devicetree/bindings/mtd/qcom_nandc.txt - ---- /dev/null -+++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt -@@ -0,0 +1,49 @@ -+* Qualcomm NAND controller -+ -+Required properties: -+- compatible: should be "qcom,ebi2-nand" for IPQ806x -+- reg: MMIO address range -+- clocks: must contain core clock and always on clock -+- clock-names: must contain "core" for the core clock and "aon" for the -+ always on clock -+- dmas: DMA specifier, consisting of a phandle to the ADM DMA -+ controller node and the channel number to be used for -+ NAND. Refer to dma.txt and qcom_adm.txt for more details -+- dma-names: must be "rxtx" -+- qcom,cmd-crci: must contain the ADM command type CRCI block instance -+ number specified for the NAND controller on the given -+ platform -+- qcom,data-crci: must contain the ADM data type CRCI block instance -+ number specified for the NAND controller on the given -+ platform -+ -+Optional properties: -+- nand-bus-width: bus width. Must be 8 or 16. If not present, 8 is chosen -+ as default -+ -+- nand-ecc-strength: number of bits to correct per ECC step. Must be 4 or 8 -+ bits. If not present, 4 is chosen as default -+- nand-on-flash-bbt: Create/use on-flash bad block table -+ -+The device tree may optionally contain sub-nodes describing partitions of the -+address space. See partition.txt for more detail. -+ -+Example: -+ -+nand@1ac00000 { -+ compatible = "qcom,ebi2-nandc"; -+ reg = <0x1ac00000 0x800>; -+ -+ clocks = <&gcc EBI2_CLK>, -+ <&gcc EBI2_AON_CLK>; -+ clock-names = "core", "aon"; -+ -+ dmas = <&adm_dma 3>; -+ dma-names = "rxtx"; -+ qcom,cmd-crci = <15>; -+ qcom,data-crci = <3>; -+ -+ partition@0 { -+ ... -+ }; -+}; diff --git a/target/linux/ipq806x/patches-4.4/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch b/target/linux/ipq806x/patches-4.4/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch deleted file mode 100644 index 664e7b5b53..0000000000 --- a/target/linux/ipq806x/patches-4.4/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch +++ /dev/null @@ -1,51 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,4/5] arm: qcom: dts: Add NAND controller node for ipq806x -From: Archit Taneja -X-Patchwork-Id: 6927121 -Message-Id: <1438578498-32254-5-git-send-email-architt@codeaurora.org> -To: linux-mtd@lists.infradead.org, dehrenberg@google.com, - cernekee@gmail.com, computersforpeace@gmail.com -Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, - sboyd@codeaurora.org, linux-kernel@vger.kernel.org, - Archit Taneja , devicetree@vger.kernel.org -Date: Mon, 3 Aug 2015 10:38:17 +0530 - -The nand controller in IPQ806x is of the 'EBI2 type'. Use the corresponding -compatible string. - -Cc: devicetree@vger.kernel.org - -Reviewed-by: Andy Gross -Signed-off-by: Archit Taneja - ---- -arch/arm/boot/dts/qcom-ipq8064.dtsi | 15 +++++++++++++++ - 1 file changed, 15 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -741,6 +741,22 @@ - - status = "disabled"; - }; -+ -+ nand@1ac00000 { -+ compatible = "qcom,ebi2-nandc"; -+ reg = <0x1ac00000 0x800>; -+ -+ clocks = <&gcc EBI2_CLK>, -+ <&gcc EBI2_AON_CLK>; -+ clock-names = "core", "aon"; -+ -+ dmas = <&adm_dma 3>; -+ dma-names = "rxtx"; -+ qcom,cmd-crci = <15>; -+ qcom,data-crci = <3>; -+ -+ status = "disabled"; -+ }; - }; - - sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.4/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch b/target/linux/ipq806x/patches-4.4/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch deleted file mode 100644 index 9de3d8fe81..0000000000 --- a/target/linux/ipq806x/patches-4.4/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch +++ /dev/null @@ -1,76 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,5/5] arm: qcom: dts: Enable NAND node on IPQ8064 AP148 platform -From: Archit Taneja -X-Patchwork-Id: 6927091 -Message-Id: <1438578498-32254-6-git-send-email-architt@codeaurora.org> -To: linux-mtd@lists.infradead.org, dehrenberg@google.com, - cernekee@gmail.com, computersforpeace@gmail.com -Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, - sboyd@codeaurora.org, linux-kernel@vger.kernel.org, - Archit Taneja , devicetree@vger.kernel.org -Date: Mon, 3 Aug 2015 10:38:18 +0530 - -Enable the NAND controller node on the AP148 platform. Provide pinmux -information. - -Cc: devicetree@vger.kernel.org - -Signed-off-by: Archit Taneja - ---- -arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 36 ++++++++++++++++++++++++++++++++ - 1 file changed, 36 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -43,6 +43,28 @@ - bias-none; - }; - }; -+ nand_pins: nand_pins { -+ mux { -+ pins = "gpio34", "gpio35", "gpio36", -+ "gpio37", "gpio38", "gpio39", -+ "gpio40", "gpio41", "gpio42", -+ "gpio43", "gpio44", "gpio45", -+ "gpio46", "gpio47"; -+ function = "nand"; -+ drive-strength = <10>; -+ bias-disable; -+ }; -+ pullups { -+ pins = "gpio39"; -+ bias-pull-up; -+ }; -+ hold { -+ pins = "gpio40", "gpio41", "gpio42", -+ "gpio43", "gpio44", "gpio45", -+ "gpio46", "gpio47"; -+ bias-bus-hold; -+ }; -+ }; - }; - - gsbi@16300000 { -@@ -126,5 +148,19 @@ - status = "ok"; - phy-tx0-term-offset = <7>; - }; -+ -+ nand@1ac00000 { -+ status = "ok"; -+ -+ pinctrl-0 = <&nand_pins>; -+ pinctrl-names = "default"; -+ -+ nand-ecc-strength = <4>; -+ nand-bus-width = <8>; -+ }; - }; - }; -+ -+&adm_dma { -+ status = "ok"; -+}; diff --git a/target/linux/ipq806x/patches-4.4/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch b/target/linux/ipq806x/patches-4.4/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch deleted file mode 100644 index 706028259d..0000000000 --- a/target/linux/ipq806x/patches-4.4/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -157,6 +157,8 @@ - - nand-ecc-strength = <4>; - nand-bus-width = <8>; -+ -+ linux,part-probe = "qcom-smem"; - }; - }; - }; diff --git a/target/linux/ipq806x/patches-4.4/167-ARM-qcom_rpm_fix_support_for_smb208.patch b/target/linux/ipq806x/patches-4.4/167-ARM-qcom_rpm_fix_support_for_smb208.patch deleted file mode 100644 index 8da70340de..0000000000 --- a/target/linux/ipq806x/patches-4.4/167-ARM-qcom_rpm_fix_support_for_smb208.patch +++ /dev/null @@ -1,50 +0,0 @@ - -In commit "regulator: qcom: Rework to single platform device" the smb208 regulator -used in IPQ8064 was left out. - -Add it to that new framework and update Docs accordingly. - -Signed-off-by: Adrian Panella - ---- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt -+++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt -@@ -59,6 +59,7 @@ Regulator nodes are identified by their - "qcom,rpm-pm8058-regulators" - "qcom,rpm-pm8901-regulators" - "qcom,rpm-pm8921-regulators" -+ "qcom,rpm-smb208-regulators" - - - vdd_l0_l1_lvs-supply: - - vdd_l2_l11_l12-supply: -@@ -156,6 +157,9 @@ pm8921: - l29, lvs1, lvs2, lvs3, lvs4, lvs5, lvs6, lvs7, usb-switch, hdmi-switch, - ncp - -+smb208: -+ s1a, s1b, s2a, s2b -+ - The content of each sub-node is defined by the standard binding for regulators - - see regulator.txt - with additional custom properties described below: - ---- a/drivers/regulator/qcom_rpm-regulator.c -+++ b/drivers/regulator/qcom_rpm-regulator.c -@@ -869,10 +869,19 @@ static const struct rpm_regulator_data r - { } - }; - -+static const struct rpm_regulator_data rpm_smb208_regulators[] = { -+ { "s1a", QCOM_RPM_SMB208_S1a, &smb208_smps, "vin_s1a" }, -+ { "s1b", QCOM_RPM_SMB208_S1b, &smb208_smps, "vin_s1b" }, -+ { "s2a", QCOM_RPM_SMB208_S2a, &smb208_smps, "vin_s2a" }, -+ { "s2b", QCOM_RPM_SMB208_S2b, &smb208_smps, "vin_s2b" }, -+ { } -+}; -+ - static const struct of_device_id rpm_of_match[] = { - { .compatible = "qcom,rpm-pm8058-regulators", .data = &rpm_pm8058_regulators }, - { .compatible = "qcom,rpm-pm8901-regulators", .data = &rpm_pm8901_regulators }, - { .compatible = "qcom,rpm-pm8921-regulators", .data = &rpm_pm8921_regulators }, -+ { .compatible = "qcom,rpm-smb208-regulators", .data = &rpm_smb208_regulators }, - { } - }; - MODULE_DEVICE_TABLE(of, rpm_of_match); diff --git a/target/linux/ipq806x/patches-4.4/168-ARM-qcom-add-smb208-DT.patch b/target/linux/ipq806x/patches-4.4/168-ARM-qcom-add-smb208-DT.patch deleted file mode 100644 index 1a15a8c645..0000000000 --- a/target/linux/ipq806x/patches-4.4/168-ARM-qcom-add-smb208-DT.patch +++ /dev/null @@ -1,74 +0,0 @@ -Change DT to use new smb208 regulator driver. - -Signed-off-by: Adrian Panella - ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -162,45 +162,37 @@ - #address-cells = <1>; - #size-cells = <0>; - -- smb208_s1a: smb208-s1a { -- compatible = "qcom,rpm-smb208"; -- reg = ; -+ regulators { -+ compatible = "qcom,rpm-smb208-regulators"; - -- regulator-min-microvolt = <1050000>; -- regulator-max-microvolt = <1150000>; -+ smb208_s1a: s1a { -+ regulator-min-microvolt = <1050000>; -+ regulator-max-microvolt = <1150000>; - -- qcom,switch-mode-frequency = <1200000>; -+ qcom,switch-mode-frequency = <1200000>; - -- }; -- -- smb208_s1b: smb208-s1b { -- compatible = "qcom,rpm-smb208"; -- reg = ; -- -- regulator-min-microvolt = <1050000>; -- regulator-max-microvolt = <1150000>; -+ }; - -- qcom,switch-mode-frequency = <1200000>; -- }; -- -- smb208_s2a: smb208-s2a { -- compatible = "qcom,rpm-smb208"; -- reg = ; -+ smb208_s1b: s1b { -+ regulator-min-microvolt = <1050000>; -+ regulator-max-microvolt = <1150000>; - -- regulator-min-microvolt = < 800000>; -- regulator-max-microvolt = <1250000>; -+ qcom,switch-mode-frequency = <1200000>; -+ }; - -- qcom,switch-mode-frequency = <1200000>; -- }; -+ smb208_s2a: s2a { -+ regulator-min-microvolt = < 800000>; -+ regulator-max-microvolt = <1250000>; - -- smb208_s2b: smb208-s2b { -- compatible = "qcom,rpm-smb208"; -- reg = ; -+ qcom,switch-mode-frequency = <1200000>; -+ }; - -- regulator-min-microvolt = < 800000>; -- regulator-max-microvolt = <1250000>; -+ smb208_s2b: s2b { -+ regulator-min-microvolt = < 800000>; -+ regulator-max-microvolt = <1250000>; - -- qcom,switch-mode-frequency = <1200000>; -+ qcom,switch-mode-frequency = <1200000>; -+ }; - }; - }; - diff --git a/target/linux/ipq806x/patches-4.4/169-OPP-Support-adjusting-OPP-voltages-at-runtime.patch b/target/linux/ipq806x/patches-4.4/169-OPP-Support-adjusting-OPP-voltages-at-runtime.patch deleted file mode 100644 index 8f0afb504c..0000000000 --- a/target/linux/ipq806x/patches-4.4/169-OPP-Support-adjusting-OPP-voltages-at-runtime.patch +++ /dev/null @@ -1,145 +0,0 @@ -From 111139c943a082364fbbcd9e0cc94cd442481340 Mon Sep 17 00:00:00 2001 -From: Stephen Boyd -Date: Mon, 1 Jun 2015 18:47:56 -0700 -Subject: PM / OPP: Support adjusting OPP voltages at runtime - -On some SoCs the Adaptive Voltage Scaling (AVS) technique is -employed to optimize the operating voltage of a device. At a -given frequency, the hardware monitors dynamic factors and either -makes a suggestion for how much to adjust a voltage for the -current frequency, or it automatically adjusts the voltage -without software intervention. Add an API to the OPP library for -the former case, so that AVS type devices can update the voltages -for an OPP when the hardware determines the voltage should -change. The assumption is that drivers like CPUfreq or devfreq -will register for the OPP notifiers and adjust the voltage -according to suggestions that AVS makes. - -Cc: Nishanth Menon -Cc: Viresh Kumar -Signed-off-by: Stephen Boyd -Acked-by: Viresh Kumar ---- - drivers/base/power/opp/core.c | 77 +++++++++++++++++++++++++++++++++++++++++++ - include/linux/pm_opp.h | 10 ++++++ - 2 files changed, 87 insertions(+) - ---- a/drivers/base/power/opp/core.c -+++ b/drivers/base/power/opp/core.c -@@ -1039,6 +1039,83 @@ unlock: - } - - /** -+ * dev_pm_opp_adjust_voltage() - helper to change the voltage of an opp -+ * @dev: device for which we do this operation -+ * @freq: OPP frequency to adjust voltage of -+ * @u_volt: new OPP voltage -+ * -+ * Change the voltage of an OPP with an RCU operation. -+ * -+ * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the -+ * copy operation, returns 0 if no modifcation was done OR modification was -+ * successful. -+ * -+ * Locking: The internal device_opp and opp structures are RCU protected. -+ * Hence this function internally uses RCU updater strategy with mutex locks to -+ * keep the integrity of the internal data structures. Callers should ensure -+ * that this function is *NOT* called under RCU protection or in contexts where -+ * mutex locking or synchronize_rcu() blocking calls cannot be used. -+ */ -+int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq, -+ unsigned long u_volt) -+{ -+ struct device_opp *dev_opp; -+ struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV); -+ int r = 0; -+ -+ /* keep the node allocated */ -+ new_opp = kmalloc(sizeof(*new_opp), GFP_KERNEL); -+ if (!new_opp) -+ return -ENOMEM; -+ -+ mutex_lock(&dev_opp_list_lock); -+ -+ /* Find the device_opp */ -+ dev_opp = _find_device_opp(dev); -+ if (IS_ERR(dev_opp)) { -+ r = PTR_ERR(dev_opp); -+ dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r); -+ goto unlock; -+ } -+ -+ /* Do we have the frequency? */ -+ list_for_each_entry(tmp_opp, &dev_opp->opp_list, node) { -+ if (tmp_opp->rate == freq) { -+ opp = tmp_opp; -+ break; -+ } -+ } -+ if (IS_ERR(opp)) { -+ r = PTR_ERR(opp); -+ goto unlock; -+ } -+ -+ /* Is update really needed? */ -+ if (opp->u_volt == u_volt) -+ goto unlock; -+ /* copy the old data over */ -+ *new_opp = *opp; -+ -+ /* plug in new node */ -+ new_opp->u_volt = u_volt; -+ -+ list_replace_rcu(&opp->node, &new_opp->node); -+ mutex_unlock(&dev_opp_list_lock); -+ call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); -+ -+ /* Notify the change of the OPP */ -+ srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ADJUST_VOLTAGE, -+ new_opp); -+ -+ return 0; -+ -+unlock: -+ mutex_unlock(&dev_opp_list_lock); -+ kfree(new_opp); -+ return r; -+} -+ -+/** - * dev_pm_opp_enable() - Enable a specific OPP - * @dev: device for which we do this operation - * @freq: OPP frequency to enable ---- a/include/linux/pm_opp.h -+++ b/include/linux/pm_opp.h -@@ -22,6 +22,7 @@ struct device; - - enum dev_pm_opp_event { - OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE, -+ OPP_EVENT_ADJUST_VOLTAGE, - }; - - #if defined(CONFIG_PM_OPP) -@@ -50,6 +51,9 @@ int dev_pm_opp_add(struct device *dev, u - unsigned long u_volt); - void dev_pm_opp_remove(struct device *dev, unsigned long freq); - -+int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq, -+ unsigned long u_volt); -+ - int dev_pm_opp_enable(struct device *dev, unsigned long freq); - - int dev_pm_opp_disable(struct device *dev, unsigned long freq); -@@ -114,6 +118,12 @@ static inline void dev_pm_opp_remove(str - { - } - -+static inline int dev_pm_opp_adjust_voltage(struct device *dev, -+ unsigned long freq, unsigned long u_volt) -+{ -+ return 0; -+} -+ - static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq) - { - return 0; diff --git a/target/linux/ipq806x/patches-4.4/170-OPP-Allow-notifiers-to-call-dev_pm_opp_get_voltage-freq-RCU-free.patch b/target/linux/ipq806x/patches-4.4/170-OPP-Allow-notifiers-to-call-dev_pm_opp_get_voltage-freq-RCU-free.patch deleted file mode 100644 index 7be47a3509..0000000000 --- a/target/linux/ipq806x/patches-4.4/170-OPP-Allow-notifiers-to-call-dev_pm_opp_get_voltage-freq-RCU-free.patch +++ /dev/null @@ -1,114 +0,0 @@ -From d330eae026b4a73e77ca0422f5cae5207d80f738 Mon Sep 17 00:00:00 2001 -From: Stephen Boyd -Date: Mon, 1 Jun 2015 18:47:57 -0700 -Subject: OPP: Allow notifiers to call dev_pm_opp_get_{voltage, freq} RCU-free - -We pass the dev_pm_opp structure to OPP notifiers but the users -of the notifier need to surround calls to dev_pm_opp_get_*() with -RCU read locks to avoid lockdep warnings. The notifier is already -called with the dev_opp's srcu lock held, so it should be safe to -assume the devm_pm_opp structure is already protected inside the -notifier. Update the lockdep check for this. - -Cc: Krzysztof Kozlowski -Signed-off-by: Stephen Boyd ---- - drivers/base/power/opp/core.c | 27 +++++++++++++++------------ - 1 file changed, 15 insertions(+), 12 deletions(-) - ---- a/drivers/base/power/opp/core.c -+++ b/drivers/base/power/opp/core.c -@@ -31,9 +31,10 @@ static LIST_HEAD(dev_opp_list); - /* Lock to allow exclusive modification to the device and opp lists */ - DEFINE_MUTEX(dev_opp_list_lock); - --#define opp_rcu_lockdep_assert() \ -+#define opp_rcu_lockdep_assert(s) \ - do { \ - RCU_LOCKDEP_WARN(!rcu_read_lock_held() && \ -+ !(s && srcu_read_lock_held(s)) && \ - !lockdep_is_held(&dev_opp_list_lock), \ - "Missing rcu_read_lock() or " \ - "dev_opp_list_lock protection"); \ -@@ -91,7 +92,7 @@ struct device_opp *_find_device_opp(stru - { - struct device_opp *dev_opp; - -- opp_rcu_lockdep_assert(); -+ opp_rcu_lockdep_assert(NULL); - - if (IS_ERR_OR_NULL(dev)) { - pr_err("%s: Invalid parameters\n", __func__); -@@ -125,10 +126,11 @@ unsigned long dev_pm_opp_get_voltage(str - struct dev_pm_opp *tmp_opp; - unsigned long v = 0; - -- opp_rcu_lockdep_assert(); -+ opp_rcu_lockdep_assert(&opp->dev_opp->srcu_head.srcu); - -- tmp_opp = rcu_dereference(opp); -- if (IS_ERR_OR_NULL(tmp_opp)) -+ tmp_opp = srcu_dereference_check(opp, &opp->dev_opp->srcu_head.srcu, -+ rcu_read_lock_held()); -+ if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) - pr_err("%s: Invalid parameters\n", __func__); - else - v = tmp_opp->u_volt; -@@ -157,9 +159,10 @@ unsigned long dev_pm_opp_get_freq(struct - struct dev_pm_opp *tmp_opp; - unsigned long f = 0; - -- opp_rcu_lockdep_assert(); -+ opp_rcu_lockdep_assert(&opp->dev_opp->srcu_head.srcu); - -- tmp_opp = rcu_dereference(opp); -+ tmp_opp = srcu_dereference_check(opp, &opp->dev_opp->srcu_head.srcu, -+ rcu_read_lock_held()); - if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) - pr_err("%s: Invalid parameters\n", __func__); - else -@@ -191,7 +194,7 @@ bool dev_pm_opp_is_turbo(struct dev_pm_o - { - struct dev_pm_opp *tmp_opp; - -- opp_rcu_lockdep_assert(); -+ opp_rcu_lockdep_assert(&opp->dev_opp->srcu_head.srcu); - - tmp_opp = rcu_dereference(opp); - if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) { -@@ -246,7 +249,7 @@ struct dev_pm_opp *dev_pm_opp_get_suspen - { - struct device_opp *dev_opp; - -- opp_rcu_lockdep_assert(); -+ opp_rcu_lockdep_assert(NULL); - - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp) || !dev_opp->suspend_opp || -@@ -326,7 +329,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ - struct device_opp *dev_opp; - struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - -- opp_rcu_lockdep_assert(); -+ opp_rcu_lockdep_assert(NULL); - - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) { -@@ -374,7 +377,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ - struct device_opp *dev_opp; - struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - -- opp_rcu_lockdep_assert(); -+ opp_rcu_lockdep_assert(NULL); - - if (!dev || !freq) { - dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); -@@ -424,7 +427,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ - struct device_opp *dev_opp; - struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - -- opp_rcu_lockdep_assert(); -+ opp_rcu_lockdep_assert(NULL); - - if (!dev || !freq) { - dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); diff --git a/target/linux/ipq806x/patches-4.4/172-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch b/target/linux/ipq806x/patches-4.4/172-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch deleted file mode 100644 index 60ebe13b1b..0000000000 --- a/target/linux/ipq806x/patches-4.4/172-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch +++ /dev/null @@ -1,184 +0,0 @@ -From 175329015c8a0b480240da222822d2f8316f074d Mon Sep 17 00:00:00 2001 -From: Stephen Boyd -Date: Mon, 1 Jun 2015 18:47:58 -0700 -Subject: cpufreq-dt: Handle OPP voltage adjust events - -On some SoCs the Adaptive Voltage Scaling (AVS) technique is -employed to optimize the operating voltage of a device. At a -given frequency, the hardware monitors dynamic factors and either -makes a suggestion for how much to adjust a voltage for the -current frequency, or it automatically adjusts the voltage -without software intervention. - -In the former case, an AVS driver will call -dev_pm_opp_modify_voltage() and update the voltage for the -particular OPP the CPUs are using. Add an OPP notifier to -cpufreq-dt so that we can adjust the voltage of the CPU when AVS -updates the OPP. - -Signed-off-by: Stephen Boyd ---- - drivers/cpufreq/cpufreq-dt.c | 72 ++++++++++++++++++++++++++++++++++++++++---- - 1 file changed, 66 insertions(+), 6 deletions(-) - ---- a/drivers/cpufreq/cpufreq-dt.c -+++ b/drivers/cpufreq/cpufreq-dt.c -@@ -34,6 +34,9 @@ struct private_data { - struct regulator *cpu_reg; - struct thermal_cooling_device *cdev; - unsigned int voltage_tolerance; /* in percentage */ -+ struct notifier_block opp_nb; -+ struct mutex lock; -+ unsigned long opp_freq; - }; - - static struct freq_attr *cpufreq_dt_attr[] = { -@@ -42,6 +45,42 @@ static struct freq_attr *cpufreq_dt_attr - NULL, - }; - -+static int opp_notifier(struct notifier_block *nb, unsigned long event, -+ void *data) -+{ -+ struct dev_pm_opp *opp = data; -+ struct private_data *priv = container_of(nb, struct private_data, -+ opp_nb); -+ struct device *cpu_dev = priv->cpu_dev; -+ struct regulator *cpu_reg = priv->cpu_reg; -+ unsigned long volt, tol, freq; -+ int ret = 0; -+ -+ switch (event) { -+ case OPP_EVENT_ADJUST_VOLTAGE: -+ volt = dev_pm_opp_get_voltage(opp); -+ freq = dev_pm_opp_get_freq(opp); -+ tol = volt * priv->voltage_tolerance / 100; -+ -+ mutex_lock(&priv->lock); -+ if (freq == priv->opp_freq) -+ ret = regulator_set_voltage_tol(cpu_reg, volt, -+ tol); -+ mutex_unlock(&priv->lock); -+ if (ret) { -+ dev_err(cpu_dev, -+ "failed to scale voltage up: %d\n", -+ ret); -+ return ret; -+ } -+ break; -+ default: -+ break; -+ } -+ -+ return 0; -+} -+ - static int set_target(struct cpufreq_policy *policy, unsigned int index) - { - struct dev_pm_opp *opp; -@@ -53,6 +92,7 @@ static int set_target(struct cpufreq_pol - unsigned long volt = 0, volt_old = 0, tol = 0; - unsigned int old_freq, new_freq; - long freq_Hz, freq_exact; -+ unsigned long opp_freq = 0; - int ret; - - freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); -@@ -63,8 +103,8 @@ static int set_target(struct cpufreq_pol - new_freq = freq_Hz / 1000; - old_freq = clk_get_rate(cpu_clk) / 1000; - -+ mutex_lock(&priv->lock); - if (!IS_ERR(cpu_reg)) { -- unsigned long opp_freq; - - rcu_read_lock(); - opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); -@@ -72,7 +112,8 @@ static int set_target(struct cpufreq_pol - rcu_read_unlock(); - dev_err(cpu_dev, "failed to find OPP for %ld\n", - freq_Hz); -- return PTR_ERR(opp); -+ ret = PTR_ERR(opp); -+ goto out; - } - volt = dev_pm_opp_get_voltage(opp); - opp_freq = dev_pm_opp_get_freq(opp); -@@ -93,7 +134,7 @@ static int set_target(struct cpufreq_pol - if (ret) { - dev_err(cpu_dev, "failed to scale voltage up: %d\n", - ret); -- return ret; -+ goto out; - } - } - -@@ -102,7 +143,7 @@ static int set_target(struct cpufreq_pol - dev_err(cpu_dev, "failed to set clock rate: %d\n", ret); - if (!IS_ERR(cpu_reg) && volt_old > 0) - regulator_set_voltage_tol(cpu_reg, volt_old, tol); -- return ret; -+ goto out; - } - - /* scaling down? scale voltage after frequency */ -@@ -112,9 +153,12 @@ static int set_target(struct cpufreq_pol - dev_err(cpu_dev, "failed to scale voltage down: %d\n", - ret); - clk_set_rate(cpu_clk, old_freq * 1000); -+ goto out; - } - } -- -+ priv->opp_freq = opp_freq; -+out: -+ mutex_unlock(&priv->lock); - return ret; - } - -@@ -201,6 +245,7 @@ static int cpufreq_init(struct cpufreq_p - unsigned int transition_latency; - bool need_update = false; - int ret; -+ struct srcu_notifier_head *opp_srcu_head; - - ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk); - if (ret) { -@@ -277,6 +322,19 @@ static int cpufreq_init(struct cpufreq_p - goto out_free_opp; - } - -+ mutex_init(&priv->lock); -+ -+ opp_srcu_head = dev_pm_opp_get_notifier(cpu_dev); -+ if (IS_ERR(opp_srcu_head)) { -+ ret = PTR_ERR(opp_srcu_head); -+ goto out_free_priv; -+ } -+ -+ priv->opp_nb.notifier_call = opp_notifier; -+ ret = srcu_notifier_chain_register(opp_srcu_head, &priv->opp_nb); -+ if (ret) -+ goto out_free_priv; -+ - of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance); - - if (!transition_latency) -@@ -326,7 +384,7 @@ static int cpufreq_init(struct cpufreq_p - 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_free_priv; -+ goto out_unregister_nb; - } - - priv->cpu_dev = cpu_dev; -@@ -365,6 +423,8 @@ static int cpufreq_init(struct cpufreq_p - - out_free_cpufreq_table: - dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); -+out_unregister_nb: -+ srcu_notifier_chain_unregister(opp_srcu_head, &priv->opp_nb); - out_free_priv: - kfree(priv); - out_free_opp: diff --git a/target/linux/ipq806x/patches-4.4/173-cpufreq-dt-Add-L2-frequency-scaling-support.patch b/target/linux/ipq806x/patches-4.4/173-cpufreq-dt-Add-L2-frequency-scaling-support.patch deleted file mode 100644 index 56930520c9..0000000000 --- a/target/linux/ipq806x/patches-4.4/173-cpufreq-dt-Add-L2-frequency-scaling-support.patch +++ /dev/null @@ -1,161 +0,0 @@ -From b4629f9e30e865402c643de6d4668be790fc0539 Mon Sep 17 00:00:00 2001 -From: Georgi Djakov -Date: Tue, 8 Sep 2015 11:24:41 +0300 -Subject: cpufreq-dt: Add L2 frequency scaling support - -Signed-off-by: Georgi Djakov - -Conflicts: - drivers/cpufreq/cpufreq-dt.c ---- - drivers/cpufreq/cpufreq-dt.c | 54 ++++++++++++++++++++++++++++++++++++++------ - include/linux/cpufreq.h | 2 ++ - 2 files changed, 49 insertions(+), 7 deletions(-) - ---- a/drivers/cpufreq/cpufreq-dt.c -+++ b/drivers/cpufreq/cpufreq-dt.c -@@ -86,11 +86,13 @@ static int set_target(struct cpufreq_pol - struct dev_pm_opp *opp; - struct cpufreq_frequency_table *freq_table = policy->freq_table; - struct clk *cpu_clk = policy->clk; -+ struct clk *l2_clk = policy->l2_clk; - struct private_data *priv = policy->driver_data; - struct device *cpu_dev = priv->cpu_dev; - struct regulator *cpu_reg = priv->cpu_reg; - unsigned long volt = 0, volt_old = 0, tol = 0; -- unsigned int old_freq, new_freq; -+ unsigned int old_freq, new_freq, l2_freq; -+ unsigned long new_l2_freq = 0; - long freq_Hz, freq_exact; - unsigned long opp_freq = 0; - int ret; -@@ -146,6 +148,30 @@ static int set_target(struct cpufreq_pol - goto out; - } - -+ if (!IS_ERR(l2_clk) && policy->l2_rate[0] && policy->l2_rate[1] && -+ policy->l2_rate[2]) { -+ static unsigned long krait_l2[CONFIG_NR_CPUS] = { }; -+ int cpu, ret = 0; -+ -+ if (freq_exact >= policy->l2_rate[2]) -+ new_l2_freq = policy->l2_rate[2]; -+ else if (freq_exact >= policy->l2_rate[1]) -+ new_l2_freq = policy->l2_rate[1]; -+ else -+ new_l2_freq = policy->l2_rate[0]; -+ -+ krait_l2[policy->cpu] = new_l2_freq; -+ for_each_present_cpu(cpu) -+ new_l2_freq = max(new_l2_freq, krait_l2[cpu]); -+ -+ l2_freq = clk_get_rate(l2_clk); -+ -+ if (l2_freq != new_l2_freq) { -+ /* scale l2 with the core */ -+ ret = clk_set_rate(l2_clk, new_l2_freq); -+ } -+ } -+ - /* scaling down? scale voltage after frequency */ - if (!IS_ERR(cpu_reg) && new_freq < old_freq) { - ret = regulator_set_voltage_tol(cpu_reg, volt, tol); -@@ -156,18 +182,21 @@ static int set_target(struct cpufreq_pol - goto out; - } - } -+ - priv->opp_freq = opp_freq; -+ - out: - mutex_unlock(&priv->lock); - return ret; - } - - static int allocate_resources(int cpu, struct device **cdev, -- struct regulator **creg, struct clk **cclk) -+ struct regulator **creg, struct clk **cclk, -+ struct clk **l2) - { - struct device *cpu_dev; - struct regulator *cpu_reg; -- struct clk *cpu_clk; -+ struct clk *cpu_clk, *l2_clk = NULL; - int ret = 0; - char *reg_cpu0 = "cpu0", *reg_cpu = "cpu", *reg; - -@@ -227,6 +256,10 @@ try_again: - *cdev = cpu_dev; - *creg = cpu_reg; - *cclk = cpu_clk; -+ -+ l2_clk = clk_get(cpu_dev, "l2"); -+ if (!IS_ERR(l2_clk)) -+ *l2 = l2_clk; - } - - return ret; -@@ -236,18 +269,20 @@ static int cpufreq_init(struct cpufreq_p - { - struct cpufreq_frequency_table *freq_table; - struct device_node *np; -+ struct device_node *l2_np; - struct private_data *priv; - struct device *cpu_dev; - struct regulator *cpu_reg; -- struct clk *cpu_clk; - struct dev_pm_opp *suspend_opp; -+ struct clk *cpu_clk, *l2_clk; - unsigned long min_uV = ~0, max_uV = 0; - unsigned int transition_latency; - bool need_update = false; - int ret; - struct srcu_notifier_head *opp_srcu_head; - -- ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk); -+ ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk, -+ &l2_clk); - if (ret) { - pr_err("%s: Failed to allocate resources: %d\n", __func__, ret); - return ret; -@@ -398,6 +433,11 @@ static int cpufreq_init(struct cpufreq_p - if (suspend_opp) - policy->suspend_freq = dev_pm_opp_get_freq(suspend_opp) / 1000; - rcu_read_unlock(); -+ policy->l2_clk = l2_clk; -+ -+ l2_np = of_find_node_by_name(NULL, "qcom,l2"); -+ if (l2_np) -+ of_property_read_u32_array(l2_np, "qcom,l2-rates", policy->l2_rate, 3); - - ret = cpufreq_table_validate_and_show(policy, freq_table); - if (ret) { -@@ -498,7 +538,7 @@ static int dt_cpufreq_probe(struct platf - { - struct device *cpu_dev; - struct regulator *cpu_reg; -- struct clk *cpu_clk; -+ struct clk *cpu_clk, *l2_clk; - int ret; - - /* -@@ -508,7 +548,7 @@ static int dt_cpufreq_probe(struct platf - * - * FIXME: Is checking this only for CPU0 sufficient ? - */ -- ret = allocate_resources(0, &cpu_dev, &cpu_reg, &cpu_clk); -+ ret = allocate_resources(0, &cpu_dev, &cpu_reg, &cpu_clk, &l2_clk); - if (ret) - return ret; - ---- a/include/linux/cpufreq.h -+++ b/include/linux/cpufreq.h -@@ -67,6 +67,8 @@ struct cpufreq_policy { - unsigned int cpu; /* cpu managing this policy, must be online */ - - struct clk *clk; -+ struct clk *l2_clk; /* L2 clock */ -+ unsigned int l2_rate[3]; /* L2 bus clock rate thresholds */ - struct cpufreq_cpuinfo cpuinfo;/* see above */ - - unsigned int min; /* in kHz */ diff --git a/target/linux/ipq806x/patches-4.4/174-cpufreq-dt-Add-missing-rcu_read_lock-for-find_device_opp.patch b/target/linux/ipq806x/patches-4.4/174-cpufreq-dt-Add-missing-rcu_read_lock-for-find_device_opp.patch deleted file mode 100644 index 124272154f..0000000000 --- a/target/linux/ipq806x/patches-4.4/174-cpufreq-dt-Add-missing-rcu_read_lock-for-find_device_opp.patch +++ /dev/null @@ -1,33 +0,0 @@ -From dafae9c5b39e2871bfd8db0b4bad6e850e42ef49 Mon Sep 17 00:00:00 2001 -From: Georgi Djakov -Date: Wed, 13 Jan 2016 15:10:25 +0200 -Subject: cpufreq-dt: Add missing rcu_read_lock() for find_device_opp() - -The function dev_pm_opp_get_notifier() must be called with held -rcu_read_lock. In order to keep the pointer valid, add rcu_read_lock(). - -Signed-off-by: Georgi Djakov ---- - drivers/cpufreq/cpufreq-dt.c | 3 +++ - 1 file changed, 3 insertions(+) - ---- a/drivers/cpufreq/cpufreq-dt.c -+++ b/drivers/cpufreq/cpufreq-dt.c -@@ -359,14 +359,17 @@ static int cpufreq_init(struct cpufreq_p - - mutex_init(&priv->lock); - -+ rcu_read_lock(); - opp_srcu_head = dev_pm_opp_get_notifier(cpu_dev); - if (IS_ERR(opp_srcu_head)) { - ret = PTR_ERR(opp_srcu_head); -+ rcu_read_unlock(); - goto out_free_priv; - } - - priv->opp_nb.notifier_call = opp_notifier; - ret = srcu_notifier_chain_register(opp_srcu_head, &priv->opp_nb); -+ rcu_read_unlock(); - if (ret) - goto out_free_priv; - diff --git a/target/linux/ipq806x/patches-4.4/175-add-regmap-mux-div.patch b/target/linux/ipq806x/patches-4.4/175-add-regmap-mux-div.patch deleted file mode 100644 index b5c23b5165..0000000000 --- a/target/linux/ipq806x/patches-4.4/175-add-regmap-mux-div.patch +++ /dev/null @@ -1,386 +0,0 @@ -From 7727b1cae43e9abac746ef016c3dbf50ee81a6d6 Mon Sep 17 00:00:00 2001 -From: Georgi Djakov -Date: Wed, 18 Mar 2015 17:23:29 +0200 -Subject: clk: qcom: Add support for regmap mux-div clocks - -Add support for hardware that support switching both parent clocks and the -divider at the same time. This avoids generating intermediate frequencies -from either the old parent clock and new divider or new parent clock and -old divider combinations. - -Signed-off-by: Georgi Djakov ---- - drivers/clk/qcom/Makefile | 1 + - drivers/clk/qcom/clk-regmap-mux-div.c | 288 ++++++++++++++++++++++++++++++++++ - drivers/clk/qcom/clk-regmap-mux-div.h | 63 ++++++++ - 3 files changed, 352 insertions(+) - create mode 100644 drivers/clk/qcom/clk-regmap-mux-div.c - create mode 100644 drivers/clk/qcom/clk-regmap-mux-div.h - ---- a/drivers/clk/qcom/Makefile -+++ b/drivers/clk/qcom/Makefile -@@ -8,6 +8,7 @@ clk-qcom-y += clk-rcg2.o - clk-qcom-y += clk-branch.o - clk-qcom-y += clk-regmap-divider.o - clk-qcom-y += clk-regmap-mux.o -+clk-qcom-y += clk-regmap-mux-div.o - clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o - obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o - clk-qcom-y += clk-hfpll.o ---- /dev/null -+++ b/drivers/clk/qcom/clk-regmap-mux-div.c -@@ -0,0 +1,288 @@ -+/* -+ * Copyright (c) 2015, Linaro Limited -+ * Copyright (c) 2014, The Linux Foundation. All rights reserved. -+ * -+ * This software is licensed under the terms of the GNU General Public -+ * License version 2, as published by the Free Software Foundation, and -+ * may be copied, distributed, and modified under those terms. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "clk-regmap-mux-div.h" -+ -+#define CMD_RCGR 0x0 -+#define CMD_RCGR_UPDATE BIT(0) -+#define CMD_RCGR_DIRTY_CFG BIT(4) -+#define CMD_RCGR_ROOT_OFF BIT(31) -+#define CFG_RCGR 0x4 -+ -+static int __mux_div_update_config(struct clk_regmap_mux_div *md) -+{ -+ int ret; -+ u32 val, count; -+ const char *name = clk_hw_get_name(&md->clkr.hw); -+ -+ ret = regmap_update_bits(md->clkr.regmap, CMD_RCGR + md->reg_offset, -+ CMD_RCGR_UPDATE, CMD_RCGR_UPDATE); -+ if (ret) -+ return ret; -+ -+ /* Wait for update to take effect */ -+ for (count = 500; count > 0; count--) { -+ ret = regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, -+ &val); -+ if (ret) -+ return ret; -+ if (!(val & CMD_RCGR_UPDATE)) -+ return 0; -+ udelay(1); -+ } -+ -+ pr_err("%s: RCG did not update its configuration", name); -+ return -EBUSY; -+} -+ -+static int __mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src_sel, -+ u32 src_div) -+{ -+ int ret; -+ u32 val, mask; -+ -+ val = (src_div << md->hid_shift) | (src_sel << md->src_shift); -+ mask = ((BIT(md->hid_width) - 1) << md->hid_shift) | -+ ((BIT(md->src_width) - 1) << md->src_shift); -+ -+ ret = regmap_update_bits(md->clkr.regmap, CFG_RCGR + md->reg_offset, -+ mask, val); -+ if (ret) -+ return ret; -+ -+ return __mux_div_update_config(md); -+} -+ -+static void __mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src_sel, -+ u32 *src_div) -+{ -+ u32 val, div, src; -+ const char *name = clk_hw_get_name(&md->clkr.hw); -+ -+ regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, &val); -+ -+ if (val & CMD_RCGR_DIRTY_CFG) { -+ pr_err("%s: RCG configuration is pending\n", name); -+ return; -+ } -+ -+ regmap_read(md->clkr.regmap, CFG_RCGR + md->reg_offset, &val); -+ src = (val >> md->src_shift); -+ src &= BIT(md->src_width) - 1; -+ *src_sel = src; -+ -+ div = (val >> md->hid_shift); -+ div &= BIT(md->hid_width) - 1; -+ *src_div = div; -+} -+ -+static int mux_div_enable(struct clk_hw *hw) -+{ -+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); -+ -+ return __mux_div_set_src_div(md, md->src_sel, md->div); -+} -+ -+static inline bool is_better_rate(unsigned long req, unsigned long best, -+ unsigned long new) -+{ -+ return (req <= new && new < best) || (best < req && best < new); -+} -+ -+static int mux_div_determine_rate(struct clk_hw *hw, -+ struct clk_rate_request *req) -+{ -+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); -+ unsigned int i, div, max_div; -+ unsigned long actual_rate, best_rate = 0; -+ unsigned long req_rate = req->rate; -+ -+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) { -+ struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i); -+ unsigned long parent_rate = clk_hw_get_rate(parent); -+ -+ max_div = BIT(md->hid_width) - 1; -+ for (div = 1; div < max_div; div++) { -+ parent_rate = mult_frac(req_rate, div, 2); -+ parent_rate = clk_hw_round_rate(parent, parent_rate); -+ actual_rate = mult_frac(parent_rate, 2, div); -+ -+ if (is_better_rate(req_rate, best_rate, actual_rate)) { -+ best_rate = actual_rate; -+ req->rate = best_rate; -+ req->best_parent_rate = parent_rate; -+ req->best_parent_hw = parent; -+ } -+ -+ if (actual_rate < req_rate || best_rate <= req_rate) -+ break; -+ } -+ } -+ -+ if (!best_rate) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+static int __mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, -+ unsigned long prate, u32 src_sel) -+{ -+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); -+ int ret, i; -+ u32 div, max_div, best_src = 0, best_div = 0; -+ unsigned long actual_rate, best_rate = 0; -+ -+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) { -+ struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i); -+ unsigned long parent_rate = clk_hw_get_rate(parent); -+ -+ max_div = BIT(md->hid_width) - 1; -+ for (div = 1; div < max_div; div++) { -+ parent_rate = mult_frac(rate, div, 2); -+ parent_rate = clk_hw_round_rate(parent, parent_rate); -+ actual_rate = mult_frac(parent_rate, 2, div); -+ -+ if (is_better_rate(rate, best_rate, actual_rate)) { -+ best_rate = actual_rate; -+ best_src = md->parent_map[i].cfg; -+ best_div = div - 1; -+ } -+ -+ if (actual_rate < rate || best_rate <= rate) -+ break; -+ } -+ } -+ -+ ret = __mux_div_set_src_div(md, best_src, best_div); -+ if (!ret) { -+ md->div = best_div; -+ md->src_sel = best_src; -+ } -+ -+ return ret; -+} -+ -+static u8 mux_div_get_parent(struct clk_hw *hw) -+{ -+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); -+ const char *name = clk_hw_get_name(hw); -+ u32 i, div, src; -+ -+ __mux_div_get_src_div(md, &src, &div); -+ -+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) -+ if (src == md->parent_map[i].cfg) -+ return i; -+ -+ pr_err("%s: Can't find parent %d\n", name, src); -+ return 0; -+} -+ -+static int mux_div_set_parent(struct clk_hw *hw, u8 index) -+{ -+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); -+ -+ return __mux_div_set_src_div(md, md->parent_map[index].cfg, md->div); -+} -+ -+static int mux_div_set_rate(struct clk_hw *hw, -+ unsigned long rate, unsigned long prate) -+{ -+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); -+ -+ return __mux_div_set_rate_and_parent(hw, rate, prate, md->src_sel); -+} -+ -+static int mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, -+ unsigned long prate, u8 index) -+{ -+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); -+ -+ return __mux_div_set_rate_and_parent(hw, rate, prate, -+ md->parent_map[index].cfg); -+} -+ -+static unsigned long mux_div_recalc_rate(struct clk_hw *hw, unsigned long prate) -+{ -+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); -+ u32 div, src; -+ int i, num_parents = clk_hw_get_num_parents(hw); -+ const char *name = clk_hw_get_name(hw); -+ -+ __mux_div_get_src_div(md, &src, &div); -+ for (i = 0; i < num_parents; i++) -+ if (src == md->parent_map[i].cfg) { -+ struct clk_hw *p = clk_hw_get_parent_by_index(hw, i); -+ unsigned long parent_rate = clk_hw_get_rate(p); -+ -+ return mult_frac(parent_rate, 2, div + 1); -+ } -+ -+ pr_err("%s: Can't find parent %d\n", name, src); -+ return 0; -+} -+ -+static struct clk_hw *mux_div_get_safe_parent(struct clk_hw *hw, -+ unsigned long *safe_freq) -+{ -+ int i; -+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); -+ -+ if (md->safe_freq) -+ *safe_freq = md->safe_freq; -+ -+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) -+ if (md->safe_src == md->parent_map[i].cfg) -+ break; -+ -+ return clk_hw_get_parent_by_index(hw, i); -+} -+ -+static void mux_div_disable(struct clk_hw *hw) -+{ -+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); -+ struct clk_hw *parent; -+ u32 div; -+ -+ if (!md->safe_freq || !md->safe_src) -+ return; -+ -+ parent = mux_div_get_safe_parent(hw, &md->safe_freq); -+ div = divider_get_val(md->safe_freq, clk_get_rate(parent->clk), NULL, -+ md->hid_width, CLK_DIVIDER_ROUND_CLOSEST); -+ div = 2 * div + 1; -+ -+ __mux_div_set_src_div(md, md->safe_src, div); -+} -+ -+const struct clk_ops clk_regmap_mux_div_ops = { -+ .enable = mux_div_enable, -+ .disable = mux_div_disable, -+ .get_parent = mux_div_get_parent, -+ .set_parent = mux_div_set_parent, -+ .set_rate = mux_div_set_rate, -+ .set_rate_and_parent = mux_div_set_rate_and_parent, -+ .determine_rate = mux_div_determine_rate, -+ .recalc_rate = mux_div_recalc_rate, -+ .get_safe_parent = mux_div_get_safe_parent, -+}; -+EXPORT_SYMBOL_GPL(clk_regmap_mux_div_ops); ---- /dev/null -+++ b/drivers/clk/qcom/clk-regmap-mux-div.h -@@ -0,0 +1,63 @@ -+/* -+ * Copyright (c) 2015, Linaro Limited -+ * Copyright (c) 2014, The Linux Foundation. All rights reserved. -+ * -+ * This software is licensed under the terms of the GNU General Public -+ * License version 2, as published by the Free Software Foundation, and -+ * may be copied, distributed, and modified under those terms. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#ifndef __QCOM_CLK_REGMAP_MUX_DIV_H__ -+#define __QCOM_CLK_REGMAP_MUX_DIV_H__ -+ -+#include -+#include "clk-regmap.h" -+#include "clk-rcg.h" -+ -+/** -+ * struct mux_div_clk - combined mux/divider clock -+ * @reg_offset: offset of the mux/divider register -+ * @hid_width: number of bits in half integer divider -+ * @hid_shift: lowest bit of hid value field -+ * @src_width: number of bits in source select -+ * @src_shift: lowest bit of source select field -+ * @div: the divider raw configuration value -+ * @src_sel: the mux index which will be used if the clock is enabled -+ * @safe_src: the safe source mux index for this clock -+ * @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. -+ * @parent_map: pointer to parent_map struct -+ * @clkr: handle between common and hardware-specific interfaces -+ */ -+ -+struct clk_regmap_mux_div { -+ u32 reg_offset; -+ u32 hid_width; -+ u32 hid_shift; -+ u32 src_width; -+ u32 src_shift; -+ u32 div; -+ u32 src_sel; -+ u32 safe_src; -+ unsigned long safe_freq; -+ const struct parent_map *parent_map; -+ struct clk_regmap clkr; -+}; -+ -+#define to_clk_regmap_mux_div(_hw) \ -+ container_of(to_clk_regmap(_hw), struct clk_regmap_mux_div, clkr) -+ -+extern const struct clk_ops clk_regmap_mux_div_ops; -+ -+#endif diff --git a/target/linux/ipq806x/patches-4.4/180-clk-qcom-Always-add-factor-clock-for-xo-clocks.patch b/target/linux/ipq806x/patches-4.4/180-clk-qcom-Always-add-factor-clock-for-xo-clocks.patch deleted file mode 100644 index c8d83e9e57..0000000000 --- a/target/linux/ipq806x/patches-4.4/180-clk-qcom-Always-add-factor-clock-for-xo-clocks.patch +++ /dev/null @@ -1,53 +0,0 @@ -From patchwork Wed Nov 2 15:56:58 2016 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v9,3/3] clk: qcom: Always add factor clock for xo clocks -From: Georgi Djakov -X-Patchwork-Id: 9409421 -Message-Id: <20161102155658.32203-4-georgi.djakov@linaro.org> -To: sboyd@codeaurora.org, mturquette@baylibre.com -Cc: linux-clk@vger.kernel.org, devicetree@vger.kernel.org, - robh+dt@kernel.org, mark.rutland@arm.com, - linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - georgi.djakov@linaro.org -Date: Wed, 2 Nov 2016 17:56:58 +0200 - -Currently the RPM/RPM-SMD clock drivers do not register the xo clocks, -so we should always add factor clock. When we later add xo clocks support -into the drivers, we should update this function to skip registration. -By doing so we avoid any DT dependencies. - -Signed-off-by: Georgi Djakov ---- - drivers/clk/qcom/common.c | 15 ++++++--------- - 1 file changed, 6 insertions(+), 9 deletions(-) - --- -To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in -the body of a message to majordomo@vger.kernel.org -More majordomo info at http://vger.kernel.org/majordomo-info.html - ---- a/drivers/clk/qcom/common.c -+++ b/drivers/clk/qcom/common.c -@@ -154,15 +154,12 @@ int qcom_cc_register_board_clk(struct de - const char *name, unsigned long rate) - { - bool add_factor = true; -- struct device_node *node; - -- /* The RPM clock driver will add the factor clock if present */ -- if (IS_ENABLED(CONFIG_QCOM_RPMCC)) { -- node = of_find_compatible_node(NULL, NULL, "qcom,rpmcc"); -- if (of_device_is_available(node)) -- add_factor = false; -- of_node_put(node); -- } -+ /* -+ * TODO: The RPM clock driver currently does not support the xo clock. -+ * When xo is added to the RPM clock driver, we should change this -+ * function to skip registration of xo factor clocks. -+ */ - - return _qcom_cc_register_board_clk(dev, path, name, rate, add_factor); - } diff --git a/target/linux/ipq806x/patches-4.4/181-cpuidle-Add-cpuidle-support-for-QCOM-cpus.patch b/target/linux/ipq806x/patches-4.4/181-cpuidle-Add-cpuidle-support-for-QCOM-cpus.patch deleted file mode 100644 index b766ec07ec..0000000000 --- a/target/linux/ipq806x/patches-4.4/181-cpuidle-Add-cpuidle-support-for-QCOM-cpus.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 252a4e72dfd7add3c37604449fd20db29d84c6f8 Mon Sep 17 00:00:00 2001 -From: Lina Iyer -Date: Wed, 25 Mar 2015 14:25:29 -0600 -Subject: ARM: cpuidle: Add cpuidle support for QCOM cpus - -Define ARM_QCOM_CPUIDLE config item to enable cpuidle support. - -Cc: Stephen Boyd -Cc: Arnd Bergmann -Cc: Kevin Hilman -Cc: Daniel Lezcano -Signed-off-by: Lina Iyer ---- - drivers/cpuidle/Kconfig.arm | 7 +++++++ - 1 file changed, 7 insertions(+) - ---- a/drivers/cpuidle/Kconfig.arm -+++ b/drivers/cpuidle/Kconfig.arm -@@ -74,3 +74,10 @@ config ARM_MVEBU_V7_CPUIDLE - depends on ARCH_MVEBU && !ARM64 - help - Select this to enable cpuidle on Armada 370, 38x and XP processors. -+ -+config ARM_QCOM_CPUIDLE -+ bool "CPU Idle Driver for QCOM processors" -+ depends on ARCH_QCOM -+ select ARM_CPUIDLE -+ help -+ Select this to enable cpuidle on QCOM processors. diff --git a/target/linux/ipq806x/patches-4.4/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch b/target/linux/ipq806x/patches-4.4/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch deleted file mode 100644 index 35ba6fbb22..0000000000 --- a/target/linux/ipq806x/patches-4.4/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch +++ /dev/null @@ -1,62 +0,0 @@ -From b12e230f09d4481424e6a5d7e2ae566b6954e83f Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Wed, 29 Apr 2015 15:21:46 -0700 -Subject: [PATCH] HACK: arch: arm: force ZRELADDR on arch-qcom - -ARCH_QCOM is using the ARCH_MULTIPLATFORM option, as now recommended -on most ARM architectures. This automatically calculate ZRELADDR by -masking PHYS_OFFSET with 0xf8000000. - -However, on IPQ806x, the first ~20MB of RAM is reserved for the hardware -network accelerators, and the bootloader removes this section from the -layout passed from the ATAGS (when used). - -For newer bootloader, when DT is used, this is not a problem, we just -reserve this memory in the device tree. But if the bootloader doesn't -have DT support, then ATAGS have to be used. In this case, the ARM -decompressor will position the kernel in this low mem, which will not be -in the RAM section mapped by the bootloader, which means the kernel will -freeze in the middle of the boot process trying to map the memory. - -As a work around, this patch allows disabling AUTO_ZRELADDR when -ARCH_QCOM is selected. It makes the zImage usage possible on bootloaders -which don't support device-tree, which is the case on certain early -IPQ806x based designs. - -Signed-off-by: Mathieu Olivari ---- - arch/arm/Kconfig | 2 +- - arch/arm/Makefile | 2 ++ - arch/arm/mach-qcom/Makefile.boot | 1 + - 3 files changed, 4 insertions(+), 1 deletion(-) - create mode 100644 arch/arm/mach-qcom/Makefile.boot - ---- a/arch/arm/Kconfig -+++ b/arch/arm/Kconfig -@@ -324,7 +324,7 @@ config ARCH_MULTIPLATFORM - select ARCH_WANT_OPTIONAL_GPIOLIB - select ARM_HAS_SG_CHAIN - select ARM_PATCH_PHYS_VIRT -- select AUTO_ZRELADDR -+ select AUTO_ZRELADDR if !ARCH_QCOM - select CLKSRC_OF - select COMMON_CLK - select GENERIC_CLOCKEVENTS ---- a/arch/arm/Makefile -+++ b/arch/arm/Makefile -@@ -256,9 +256,11 @@ MACHINE := arch/arm/mach-$(word 1,$(mac - else - MACHINE := - endif -+ifeq ($(CONFIG_ARCH_QCOM),) - ifeq ($(CONFIG_ARCH_MULTIPLATFORM),y) - MACHINE := - endif -+endif - - machdirs := $(patsubst %,arch/arm/mach-%/,$(machine-y)) - platdirs := $(patsubst %,arch/arm/plat-%/,$(sort $(plat-y))) ---- /dev/null -+++ b/arch/arm/mach-qcom/Makefile.boot -@@ -0,0 +1 @@ -+zreladdr-y+= 0x42208000 diff --git a/target/linux/ipq806x/patches-4.4/302-mtd-qcom-smem-rename-rootfs-ubi.patch b/target/linux/ipq806x/patches-4.4/302-mtd-qcom-smem-rename-rootfs-ubi.patch deleted file mode 100644 index 8890a91731..0000000000 --- a/target/linux/ipq806x/patches-4.4/302-mtd-qcom-smem-rename-rootfs-ubi.patch +++ /dev/null @@ -1,13 +0,0 @@ ---- a/drivers/mtd/qcom_smem_part.c -+++ b/drivers/mtd/qcom_smem_part.c -@@ -189,6 +189,10 @@ static int parse_qcom_smem_partitions(st - m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz); - m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz); - -+ /* "rootfs" conflicts with OpenWrt auto mounting */ -+ if (mtd_type_is_nand(master) && !strcmp(m_part->name, "rootfs")) -+ m_part->name = "ubi"; -+ - /* - * The last SMEM partition may have its size marked as - * something like 0xffffffff, which means "until the end of the diff --git a/target/linux/ipq806x/patches-4.4/303-add-saw_l2-and-sns-into-ipq8064-DT.patch b/target/linux/ipq806x/patches-4.4/303-add-saw_l2-and-sns-into-ipq8064-DT.patch deleted file mode 100644 index f7ea6fd252..0000000000 --- a/target/linux/ipq806x/patches-4.4/303-add-saw_l2-and-sns-into-ipq8064-DT.patch +++ /dev/null @@ -1,41 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -50,6 +50,7 @@ - L2: l2-cache { - compatible = "cache"; - cache-level = <2>; -+ qcom,saw = <&saw_l2>; - }; - - qcom,l2 { -@@ -277,17 +278,28 @@ - }; - - saw0: regulator@2089000 { -- compatible = "qcom,saw2"; -+ compatible = "qcom,saw2", "syscon"; - reg = <0x02089000 0x1000>, <0x02009000 0x1000>; - regulator; - }; - - saw1: regulator@2099000 { -- compatible = "qcom,saw2"; -+ compatible = "qcom,saw2", "syscon"; - reg = <0x02099000 0x1000>, <0x02009000 0x1000>; - regulator; - }; - -+ saw_l2: regulator@02012000 { -+ compatible = "qcom,saw2", "syscon"; -+ reg = <0x02012000 0x1000>; -+ regulator; -+ }; -+ -+ sic_non_secure: sic-non-secure@12100000 { -+ compatible = "syscon"; -+ reg = <0x12100000 0x10000>; -+ }; -+ - gsbi2: gsbi@12480000 { - compatible = "qcom,gsbi-v1.0.0"; - cell-index = <2>; diff --git a/target/linux/ipq806x/patches-4.4/304-add-cpu-idle-state-into-ipq8064-DT.patch b/target/linux/ipq806x/patches-4.4/304-add-cpu-idle-state-into-ipq8064-DT.patch deleted file mode 100644 index 42b37ca2c2..0000000000 --- a/target/linux/ipq806x/patches-4.4/304-add-cpu-idle-state-into-ipq8064-DT.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 3985d2c24d96f72211488fc6ebb53b032e4d0a05 Mon Sep 17 00:00:00 2001 -From: Pavel Kubelun -Date: Sun, 6 Nov 2016 19:02:34 +0300 -Subject: [PATCH] ipq806x: add cpu idle state into ipq8064 DT - -Signed-off-by: Pavel Kubelun ---- - arch/arm/boot/dts/qcom-ipq8064.dtsi | 16 ++++++++++++++-- - 1 file changed, 14 insertions(+), 2 deletions(-) - ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -18,7 +18,7 @@ - #address-cells = <1>; - #size-cells = <0>; - -- cpu@0 { -+ cpu0: cpu@0 { - compatible = "qcom,krait"; - enable-method = "qcom,kpss-acc-v1"; - device_type = "cpu"; -@@ -31,9 +31,10 @@ - clock-latency = <100000>; - cpu-supply = <&smb208_s2a>; - voltage-tolerance = <5>; -+ cpu-idle-states = <&CPU_SPC>; - }; - -- cpu@1 { -+ cpu1: cpu@1 { - compatible = "qcom,krait"; - enable-method = "qcom,kpss-acc-v1"; - device_type = "cpu"; -@@ -45,6 +46,7 @@ - clock-names = "cpu", "l2"; - clock-latency = <100000>; - cpu-supply = <&smb208_s2b>; -+ cpu-idle-states = <&CPU_SPC>; - }; - - L2: l2-cache { -@@ -56,6 +58,16 @@ - qcom,l2 { - qcom,l2-rates = <384000000 1000000000 1200000000>; - }; -+ -+ idle-states { -+ CPU_SPC: spc { -+ compatible = "qcom,idle-state-spc", -+ "arm,idle-state"; -+ entry-latency-us = <400>; -+ exit-latency-us = <900>; -+ min-residency-us = <3000>; -+ }; -+ }; - }; - - cpu-pmu { diff --git a/target/linux/ipq806x/patches-4.4/305-add-board-clocks-and-rpmcc-into-DT.patch b/target/linux/ipq806x/patches-4.4/305-add-board-clocks-and-rpmcc-into-DT.patch deleted file mode 100644 index be45895824..0000000000 --- a/target/linux/ipq806x/patches-4.4/305-add-board-clocks-and-rpmcc-into-DT.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 6da1b6260843da455cad8180c18d020679fd4a46 Mon Sep 17 00:00:00 2001 -From: Pavel Kubelun -Date: Sun, 6 Nov 2016 19:07:24 +0300 -Subject: [PATCH] ipq806x: add board clocks and rpmcc into DT - -Signed-off-by: Pavel Kubelun ---- - arch/arm/boot/dts/qcom-ipq8064.dtsi | 17 +++++++++++++++++ - 1 file changed, 17 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -92,6 +92,18 @@ - }; - - clocks { -+ cxo_board { -+ compatible = "fixed-clock"; -+ #clock-cells = <0>; -+ clock-frequency = <25000000>; -+ }; -+ -+ pxo_board { -+ compatible = "fixed-clock"; -+ #clock-cells = <0>; -+ clock-frequency = <25000000>; -+ }; -+ - sleep_clk: sleep_clk { - compatible = "fixed-clock"; - clock-frequency = <32768>; -@@ -175,6 +187,11 @@ - #address-cells = <1>; - #size-cells = <0>; - -+ rpmcc: clock-controller { -+ compatible = "qcom,rpmcc-ipq806x", "qcom,rpmcc"; -+ #clock-cells = <1>; -+ }; -+ - regulators { - compatible = "qcom,rpm-smb208-regulators"; - diff --git a/target/linux/ipq806x/patches-4.4/306-add-RPM-msg-RAM-into-DT.patch b/target/linux/ipq806x/patches-4.4/306-add-RPM-msg-RAM-into-DT.patch deleted file mode 100644 index 005f980eba..0000000000 --- a/target/linux/ipq806x/patches-4.4/306-add-RPM-msg-RAM-into-DT.patch +++ /dev/null @@ -1,22 +0,0 @@ -From 9bf6b333d6a4d3a3ef0d3fc7ca0becd008c81820 Mon Sep 17 00:00:00 2001 -From: Pavel Kubelun -Date: Sun, 6 Nov 2016 19:12:46 +0300 -Subject: [PATCH] ipq806x: add RPM msg RAM into DT - -Signed-off-by: Pavel Kubelun ---- - arch/arm/boot/dts/qcom-ipq8064.dtsi | 3 +++ - 1 file changed, 3 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -184,6 +184,9 @@ - "err", - "wakeup"; - -+ clocks = <&gcc RPM_MSG_RAM_H_CLK>; -+ clock-names = "ram"; -+ - #address-cells = <1>; - #size-cells = <0>; - diff --git a/target/linux/ipq806x/patches-4.4/307-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch b/target/linux/ipq806x/patches-4.4/307-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch deleted file mode 100644 index e90f4a6faa..0000000000 --- a/target/linux/ipq806x/patches-4.4/307-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch +++ /dev/null @@ -1,25 +0,0 @@ -From dd43e356db678a564ad2cc111962d72ba3162a38 Mon Sep 17 00:00:00 2001 -From: Abhishek Sahu -Date: Wed, 18 Nov 2015 12:38:56 +0530 -Subject: ipq806x: gcc: Added the enable regs and mask for PRNG - -kernel got hanged while reading from /dev/hwrng at the -time of PRNG clock enable - -Change-Id: I89856c7e19e6639508e6a2774304583a3ec91172 -Signed-off-by: Abhishek Sahu ---- - drivers/clk/qcom/gcc-ipq806x.c | 2 ++ - 1 file changed, 2 insertions(+) - ---- a/drivers/clk/qcom/gcc-ipq806x.c -+++ b/drivers/clk/qcom/gcc-ipq806x.c -@@ -1240,6 +1240,8 @@ static struct clk_rcg prng_src = { - .parent_map = gcc_pxo_pll8_map, - }, - .clkr = { -+ .enable_reg = 0x2e80, -+ .enable_mask = BIT(11), - .hw.init = &(struct clk_init_data){ - .name = "prng_src", - .parent_names = gcc_pxo_pll8, diff --git a/target/linux/ipq806x/patches-4.4/308-soc-qcom-ipq806x-Increase-Atomic-Coherent-Pool-size.patch b/target/linux/ipq806x/patches-4.4/308-soc-qcom-ipq806x-Increase-Atomic-Coherent-Pool-size.patch deleted file mode 100644 index 1c997ab256..0000000000 --- a/target/linux/ipq806x/patches-4.4/308-soc-qcom-ipq806x-Increase-Atomic-Coherent-Pool-size.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 689a8f1ec58a88152669d21572a1539ad34024cb Mon Sep 17 00:00:00 2001 -From: Varadarajan Narayanan -Date: Mon, 30 Mar 2015 13:25:21 +0530 -Subject: soc: qcom: ipq806x: Increase Atomic Coherent Pool size - -Linux 3.14, by default allocates a 256K Atomic Coherent Pool. -However, Linux 3.4 seems to have allocated ~1.8M for the same. -256K doesn't seem to be enough for the WiFi driver. Hence, -setting the size to be similar to 3.4. - -CRs-Fixed: 810357 - -Change-Id: I5b98a8531dcb33aff451a943f8b83402f9d13fa7 -Signed-off-by: Varadarajan Narayanan ---- - arch/arm/mach-qcom/board.c | 20 ++++++++++++++++++++ - 1 file changed, 20 insertions(+) - ---- a/arch/arm/mach-qcom/board.c -+++ b/arch/arm/mach-qcom/board.c -@@ -12,6 +12,11 @@ - - #include - -+#include -+#include -+#include -+#include -+ - #include - - static const char * const qcom_dt_match[] __initconst = { -@@ -28,3 +33,19 @@ static const char * const qcom_dt_match[ - DT_MACHINE_START(QCOM_DT, "Qualcomm (Flattened Device Tree)") - .dt_compat = qcom_dt_match, - MACHINE_END -+ -+ -+static int __init qcom_atomic_pool_size_set(void) -+{ -+#define ATOMIC_DMA_COHERENT_POOL_SIZE SZ_2M -+ -+ init_dma_coherent_pool_size(ATOMIC_DMA_COHERENT_POOL_SIZE); -+ -+ return 0; -+} -+ -+/* -+ * This should happen before atomic_pool_init(), which is a -+ * postcore_initcall. -+ */ -+core_initcall(qcom_atomic_pool_size_set); diff --git a/target/linux/ipq806x/patches-4.4/309-clk-gcc-add-tsens-child-node.patch b/target/linux/ipq806x/patches-4.4/309-clk-gcc-add-tsens-child-node.patch deleted file mode 100644 index 0eae3e7bfd..0000000000 --- a/target/linux/ipq806x/patches-4.4/309-clk-gcc-add-tsens-child-node.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 856371ca1561ca9b3280cc323ff296c7c5e1fa93 Mon Sep 17 00:00:00 2001 -From: Pavel Kubelun -Date: Tue, 22 Nov 2016 17:37:56 +0300 -Subject: [PATCH] ipq806x: clk: gcc: add tsens child node - -Thermal sensors in ipq806x are inside a Global clock controller. -Add a child node into it to be used by the TSENS driver. - -Signed-off-by: Pavel Kubelun - ---- - drivers/clk/qcom/gcc-ipq806x.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - ---- a/drivers/clk/qcom/gcc-ipq806x.c -+++ b/drivers/clk/qcom/gcc-ipq806x.c -@@ -3109,6 +3109,7 @@ MODULE_DEVICE_TABLE(of, gcc_ipq806x_matc - static int gcc_ipq806x_probe(struct platform_device *pdev) - { - struct device *dev = &pdev->dev; -+ struct platform_device *tsens; - struct regmap *regmap; - int ret; - -@@ -3138,6 +3139,13 @@ static int gcc_ipq806x_probe(struct plat - regmap_write(regmap, 0x3cf8, 8); - regmap_write(regmap, 0x3d18, 8); - -+ tsens = platform_device_register_data(&pdev->dev, "qcom-tsens", -1, -+ NULL, 0); -+ if (IS_ERR(tsens)) -+ return PTR_ERR(tsens); -+ -+ platform_set_drvdata(pdev, tsens); -+ - return 0; - } - diff --git a/target/linux/ipq806x/patches-4.4/310-add-necessary-thermal-data.patch b/target/linux/ipq806x/patches-4.4/310-add-necessary-thermal-data.patch deleted file mode 100644 index b2564a5407..0000000000 --- a/target/linux/ipq806x/patches-4.4/310-add-necessary-thermal-data.patch +++ /dev/null @@ -1,150 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -31,6 +31,9 @@ - clock-latency = <100000>; - cpu-supply = <&smb208_s2a>; - voltage-tolerance = <5>; -+ cooling-min-state = <0>; -+ cooling-max-state = <10>; -+ #cooling-cells = <2>; - cpu-idle-states = <&CPU_SPC>; - }; - -@@ -46,6 +49,9 @@ - clock-names = "cpu", "l2"; - clock-latency = <100000>; - cpu-supply = <&smb208_s2b>; -+ cooling-min-state = <0>; -+ cooling-max-state = <10>; -+ #cooling-cells = <2>; - cpu-idle-states = <&CPU_SPC>; - }; - -@@ -70,6 +76,92 @@ - }; - }; - -+ thermal-zones { -+ cpu-thermal0 { -+ polling-delay-passive = <250>; -+ polling-delay = <1000>; -+ -+ thermal-sensors = <&gcc 5>; -+ coefficients = <1132 0>; -+ -+ trips { -+ cpu_alert0: trip0 { -+ temperature = <75000>; -+ hysteresis = <2000>; -+ type = "passive"; -+ }; -+ cpu_crit0: trip1 { -+ temperature = <110000>; -+ hysteresis = <2000>; -+ type = "critical"; -+ }; -+ }; -+ }; -+ -+ cpu-thermal1 { -+ polling-delay-passive = <250>; -+ polling-delay = <1000>; -+ -+ thermal-sensors = <&gcc 6>; -+ coefficients = <1132 0>; -+ -+ trips { -+ cpu_alert1: trip0 { -+ temperature = <75000>; -+ hysteresis = <2000>; -+ type = "passive"; -+ }; -+ cpu_crit1: trip1 { -+ temperature = <110000>; -+ hysteresis = <2000>; -+ type = "critical"; -+ }; -+ }; -+ }; -+ -+ cpu-thermal2 { -+ polling-delay-passive = <250>; -+ polling-delay = <1000>; -+ -+ thermal-sensors = <&gcc 7>; -+ coefficients = <1199 0>; -+ -+ trips { -+ cpu_alert2: trip0 { -+ temperature = <75000>; -+ hysteresis = <2000>; -+ type = "passive"; -+ }; -+ cpu_crit2: trip1 { -+ temperature = <110000>; -+ hysteresis = <2000>; -+ type = "critical"; -+ }; -+ }; -+ }; -+ -+ cpu-thermal3 { -+ polling-delay-passive = <250>; -+ polling-delay = <1000>; -+ -+ thermal-sensors = <&gcc 8>; -+ coefficients = <1132 0>; -+ -+ trips { -+ cpu_alert3: trip0 { -+ temperature = <75000>; -+ hysteresis = <2000>; -+ type = "passive"; -+ }; -+ cpu_crit3: trip1 { -+ temperature = <110000>; -+ hysteresis = <2000>; -+ type = "critical"; -+ }; -+ }; -+ }; -+ }; -+ - cpu-pmu { - compatible = "qcom,krait-pmu"; - interrupts = <1 10 0x304>; -@@ -172,6 +264,21 @@ - reg-names = "lpass-lpaif"; - }; - -+ qfprom: qfprom@700000 { -+ compatible = "qcom,qfprom", "syscon"; -+ reg = <0x00700000 0x1000>; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ ranges; -+ -+ tsens_calib: calib { -+ reg = <0x400 0x10>; -+ }; -+ tsens_backup: backup_calib { -+ reg = <0x410 0x10>; -+ }; -+ }; -+ - rpm@108000 { - compatible = "qcom,rpm-ipq8064"; - reg = <0x108000 0x1000>; -@@ -499,8 +606,12 @@ - gcc: clock-controller@900000 { - compatible = "qcom,gcc-ipq8064"; - reg = <0x00900000 0x4000>; -+ nvmem-cells = <&tsens_calib>, <&tsens_backup>; -+ nvmem-cell-names = "calib", "calib_backup"; - #clock-cells = <1>; - #reset-cells = <1>; -+ #power-domain-cells = <1>; -+ #thermal-sensor-cells = <1>; - }; - - tcsr: syscon@1a400000 { diff --git a/target/linux/ipq806x/patches-4.4/311-add-rpmcc-for-ipq806x.patch b/target/linux/ipq806x/patches-4.4/311-add-rpmcc-for-ipq806x.patch deleted file mode 100644 index bb9dd87dd6..0000000000 --- a/target/linux/ipq806x/patches-4.4/311-add-rpmcc-for-ipq806x.patch +++ /dev/null @@ -1,81 +0,0 @@ ---- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt -+++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt -@@ -12,6 +12,7 @@ Required properties : - - "qcom,rpmcc-msm8916", "qcom,rpmcc" - "qcom,rpmcc-apq8064", "qcom,rpmcc" -+ "qcom,rpmcc-ipq806x", "qcom,rpmcc" - - - #clock-cells : shall contain 1 - ---- a/drivers/clk/qcom/clk-rpm.c -+++ b/drivers/clk/qcom/clk-rpm.c -@@ -359,6 +359,16 @@ DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a - DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); - DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK); - -+/* ipq806x */ -+DEFINE_CLK_RPM(ipq806x, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); -+DEFINE_CLK_RPM(ipq806x, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); -+DEFINE_CLK_RPM(ipq806x, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); -+DEFINE_CLK_RPM(ipq806x, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); -+DEFINE_CLK_RPM(ipq806x, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); -+DEFINE_CLK_RPM(ipq806x, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); -+DEFINE_CLK_RPM(ipq806x, nss_fabric_0_clk, nss_fabric_0_a_clk, QCOM_RPM_NSS_FABRIC_0_CLK); -+DEFINE_CLK_RPM(ipq806x, nss_fabric_1_clk, nss_fabric_1_a_clk, QCOM_RPM_NSS_FABRIC_1_CLK); -+ - static struct clk_rpm *apq8064_clks[] = { - [RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk, - [RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk, -@@ -380,13 +390,38 @@ static struct clk_rpm *apq8064_clks[] = - [RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk, - }; - -+static struct clk_rpm *ipq806x_clks[] = { -+ [RPM_APPS_FABRIC_CLK] = &ipq806x_afab_clk, -+ [RPM_APPS_FABRIC_A_CLK] = &ipq806x_afab_a_clk, -+ [RPM_CFPB_CLK] = &ipq806x_cfpb_clk, -+ [RPM_CFPB_A_CLK] = &ipq806x_cfpb_a_clk, -+ [RPM_DAYTONA_FABRIC_CLK] = &ipq806x_daytona_clk, -+ [RPM_DAYTONA_FABRIC_A_CLK] = &ipq806x_daytona_a_clk, -+ [RPM_EBI1_CLK] = &ipq806x_ebi1_clk, -+ [RPM_EBI1_A_CLK] = &ipq806x_ebi1_a_clk, -+ [RPM_SYS_FABRIC_CLK] = &ipq806x_sfab_clk, -+ [RPM_SYS_FABRIC_A_CLK] = &ipq806x_sfab_a_clk, -+ [RPM_SFPB_CLK] = &ipq806x_sfpb_clk, -+ [RPM_SFPB_A_CLK] = &ipq806x_sfpb_a_clk, -+ [RPM_NSS_FABRIC_0_CLK] = &ipq806x_nss_fabric_0_clk, -+ [RPM_NSS_FABRIC_0_A_CLK] = &ipq806x_nss_fabric_0_a_clk, -+ [RPM_NSS_FABRIC_1_CLK] = &ipq806x_nss_fabric_1_clk, -+ [RPM_NSS_FABRIC_1_A_CLK] = &ipq806x_nss_fabric_1_a_clk, -+}; -+ - static const struct rpm_clk_desc rpm_clk_apq8064 = { - .clks = apq8064_clks, - .num_clks = ARRAY_SIZE(apq8064_clks), - }; - -+static const struct rpm_clk_desc rpm_clk_ipq806x = { -+ .clks = ipq806x_clks, -+ .num_clks = ARRAY_SIZE(ipq806x_clks), -+}; -+ - static const struct of_device_id rpm_clk_match_table[] = { - { .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 }, -+ { .compatible = "qcom,rpmcc-ipq806x", .data = &rpm_clk_ipq806x }, - { } - }; - MODULE_DEVICE_TABLE(of, rpm_clk_match_table); ---- a/include/dt-bindings/clock/qcom,rpmcc.h -+++ b/include/dt-bindings/clock/qcom,rpmcc.h -@@ -37,6 +37,10 @@ - #define RPM_SYS_FABRIC_A_CLK 19 - #define RPM_SFPB_CLK 20 - #define RPM_SFPB_A_CLK 21 -+#define RPM_NSS_FABRIC_0_CLK 22 -+#define RPM_NSS_FABRIC_0_A_CLK 23 -+#define RPM_NSS_FABRIC_1_CLK 24 -+#define RPM_NSS_FABRIC_1_A_CLK 25 - - /* msm8916 */ - #define RPM_SMD_XO_CLK_SRC 0 diff --git a/target/linux/ipq806x/patches-4.4/315-disable-usb3-phy-suspend.patch b/target/linux/ipq806x/patches-4.4/315-disable-usb3-phy-suspend.patch deleted file mode 100644 index 4b5d480d33..0000000000 --- a/target/linux/ipq806x/patches-4.4/315-disable-usb3-phy-suspend.patch +++ /dev/null @@ -1,36 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -678,6 +678,8 @@ - clocks = <&gcc USB30_0_MASTER_CLK>; - clock-names = "core"; - -+ syscon-tcsr = <&tcsr 0xb0 1>; -+ - ranges; - - status = "disabled"; -@@ -689,6 +691,7 @@ - phys = <&hs_phy_0>, <&ss_phy_0>; - phy-names = "usb2-phy", "usb3-phy"; - dr_mode = "host"; -+ snps,dis_u3_susphy_quirk; - }; - }; - -@@ -699,6 +702,8 @@ - clocks = <&gcc USB30_1_MASTER_CLK>; - clock-names = "core"; - -+ syscon-tcsr = <&tcsr 0xb0 0>; -+ - ranges; - - status = "disabled"; -@@ -710,6 +715,7 @@ - phys = <&hs_phy_1>, <&ss_phy_1>; - phy-names = "usb2-phy", "usb3-phy"; - dr_mode = "host"; -+ snps,dis_u3_susphy_quirk; - }; - }; - diff --git a/target/linux/ipq806x/patches-4.4/316-dt-add-prng-support.patch b/target/linux/ipq806x/patches-4.4/316-dt-add-prng-support.patch deleted file mode 100644 index f2faf64f00..0000000000 --- a/target/linux/ipq806x/patches-4.4/316-dt-add-prng-support.patch +++ /dev/null @@ -1,16 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -336,6 +336,13 @@ - }; - }; - -+ rng@1a500000 { -+ compatible = "qcom,prng"; -+ reg = <0x1a500000 0x200>; -+ clocks = <&gcc PRNG_CLK>; -+ clock-names = "core"; -+ }; -+ - qcom_pinmux: pinmux@800000 { - compatible = "qcom,ipq8064-pinctrl"; - reg = <0x800000 0x4000>; diff --git a/target/linux/ipq806x/patches-4.4/400-dsa-add-qca.patch b/target/linux/ipq806x/patches-4.4/400-dsa-add-qca.patch deleted file mode 100644 index 1cadba2009..0000000000 --- a/target/linux/ipq806x/patches-4.4/400-dsa-add-qca.patch +++ /dev/null @@ -1,1068 +0,0 @@ -From patchwork Fri May 29 01:42:16 2015 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [1/7] net: dsa: add new driver for ar8xxx family -From: Mathieu Olivari -X-Patchwork-Id: 477523 -X-Patchwork-Delegate: davem@davemloft.net -Message-Id: <1432863742-18427-2-git-send-email-mathieu@codeaurora.org> -To: robh+dt@kernel.org, pawel.moll@arm.com, mark.rutland@arm.com, - ijc+devicetree@hellion.org.uk, galak@codeaurora.org, - davem@davemloft.net, mathieu@codeaurora.org, andrew@lunn.ch, - f.fainelli@gmail.com, linux@roeck-us.net, gang.chen.5i5j@gmail.com, - jiri@resnulli.us, leitec@staticky.com, fabf@skynet.be, - alexander.h.duyck@intel.com, pavel.nakonechny@skitlab.ru, - joe@perches.com, sfeldma@gmail.com, nbd@nbd.name, juhosg@openwrt.org -Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, - netdev@vger.kernel.org -Date: Thu, 28 May 2015 18:42:16 -0700 - -This patch contains initial init & registration code for QCA8337. It -will detect a QCA8337 switch, if present and declared in DT/platform. - -Each port will be represented through a standalone net_device interface, -as for other DSA switches. CPU can communicate with any of the ports by -setting an IP@ on ethN interface. Ports cannot communicate with each -other just yet. - -Link status will be reported through polling, and we don't use any -encapsulation. - -Signed-off-by: Mathieu Olivari ---- - drivers/net/dsa/Kconfig | 7 ++ - drivers/net/dsa/Makefile | 1 + - drivers/net/dsa/ar8xxx.c | 303 +++++++++++++++++++++++++++++++++++++++++++++++ - drivers/net/dsa/ar8xxx.h | 82 +++++++++++++ - net/dsa/dsa.c | 1 + - 5 files changed, 394 insertions(+) - create mode 100644 drivers/net/dsa/ar8xxx.c - create mode 100644 drivers/net/dsa/ar8xxx.h - ---- a/drivers/net/dsa/Kconfig -+++ b/drivers/net/dsa/Kconfig -@@ -65,4 +65,13 @@ config NET_DSA_BCM_SF2 - This enables support for the Broadcom Starfighter 2 Ethernet - switch chips. - -+config NET_DSA_AR8XXX -+ tristate "Qualcomm Atheros AR8XXX Ethernet switch family support" -+ depends on NET_DSA -+ select NET_DSA_TAG_QCA -+ select REGMAP -+ ---help--- -+ This enables support for the Qualcomm Atheros AR8XXX Ethernet -+ switch chips. -+ - endmenu ---- a/drivers/net/dsa/Makefile -+++ b/drivers/net/dsa/Makefile -@@ -14,3 +14,4 @@ ifdef CONFIG_NET_DSA_MV88E6171 - mv88e6xxx_drv-y += mv88e6171.o - endif - obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm_sf2.o -+obj-$(CONFIG_NET_DSA_AR8XXX) += ar8xxx.o ---- /dev/null -+++ b/drivers/net/dsa/ar8xxx.c -@@ -0,0 +1,529 @@ -+/* -+ * Copyright (C) 2009 Felix Fietkau -+ * Copyright (C) 2011-2012 Gabor Juhos -+ * Copyright (c) 2015, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ar8xxx.h" -+ -+#define MIB_DESC(_s, _o, _n) \ -+ { \ -+ .size = (_s), \ -+ .offset = (_o), \ -+ .name = (_n), \ -+ } -+ -+static const struct ar8xxx_mib_desc ar8327_mib[] = { -+ MIB_DESC(1, 0x00, "RxBroad"), -+ MIB_DESC(1, 0x04, "RxPause"), -+ MIB_DESC(1, 0x08, "RxMulti"), -+ MIB_DESC(1, 0x0c, "RxFcsErr"), -+ MIB_DESC(1, 0x10, "RxAlignErr"), -+ MIB_DESC(1, 0x14, "RxRunt"), -+ MIB_DESC(1, 0x18, "RxFragment"), -+ MIB_DESC(1, 0x1c, "Rx64Byte"), -+ MIB_DESC(1, 0x20, "Rx128Byte"), -+ MIB_DESC(1, 0x24, "Rx256Byte"), -+ MIB_DESC(1, 0x28, "Rx512Byte"), -+ MIB_DESC(1, 0x2c, "Rx1024Byte"), -+ MIB_DESC(1, 0x30, "Rx1518Byte"), -+ MIB_DESC(1, 0x34, "RxMaxByte"), -+ MIB_DESC(1, 0x38, "RxTooLong"), -+ MIB_DESC(2, 0x3c, "RxGoodByte"), -+ MIB_DESC(2, 0x44, "RxBadByte"), -+ MIB_DESC(1, 0x4c, "RxOverFlow"), -+ MIB_DESC(1, 0x50, "Filtered"), -+ MIB_DESC(1, 0x54, "TxBroad"), -+ MIB_DESC(1, 0x58, "TxPause"), -+ MIB_DESC(1, 0x5c, "TxMulti"), -+ MIB_DESC(1, 0x60, "TxUnderRun"), -+ MIB_DESC(1, 0x64, "Tx64Byte"), -+ MIB_DESC(1, 0x68, "Tx128Byte"), -+ MIB_DESC(1, 0x6c, "Tx256Byte"), -+ MIB_DESC(1, 0x70, "Tx512Byte"), -+ MIB_DESC(1, 0x74, "Tx1024Byte"), -+ MIB_DESC(1, 0x78, "Tx1518Byte"), -+ MIB_DESC(1, 0x7c, "TxMaxByte"), -+ MIB_DESC(1, 0x80, "TxOverSize"), -+ MIB_DESC(2, 0x84, "TxByte"), -+ MIB_DESC(1, 0x8c, "TxCollision"), -+ MIB_DESC(1, 0x90, "TxAbortCol"), -+ MIB_DESC(1, 0x94, "TxMultiCol"), -+ MIB_DESC(1, 0x98, "TxSingleCol"), -+ MIB_DESC(1, 0x9c, "TxExcDefer"), -+ MIB_DESC(1, 0xa0, "TxDefer"), -+ MIB_DESC(1, 0xa4, "TxLateCol"), -+}; -+ -+u32 -+ar8xxx_mii_read32(struct mii_bus *bus, int phy_id, int regnum) -+{ -+ u16 lo, hi; -+ -+ lo = bus->read(bus, phy_id, regnum); -+ hi = bus->read(bus, phy_id, regnum + 1); -+ -+ return (hi << 16) | lo; -+} -+ -+void -+ar8xxx_mii_write32(struct mii_bus *bus, int phy_id, int regnum, u32 val) -+{ -+ u16 lo, hi; -+ -+ lo = val & 0xffff; -+ hi = (u16)(val >> 16); -+ -+ bus->write(bus, phy_id, regnum, lo); -+ bus->write(bus, phy_id, regnum + 1, hi); -+} -+ -+u32 ar8xxx_read(struct dsa_switch *ds, int reg) -+{ -+ struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); -+ u16 r1, r2, page; -+ u32 val; -+ -+ split_addr((u32)reg, &r1, &r2, &page); -+ -+ mutex_lock(&bus->mdio_lock); -+ -+ bus->write(bus, 0x18, 0, page); -+ wait_for_page_switch(); -+ val = ar8xxx_mii_read32(bus, 0x10 | r2, r1); -+ -+ mutex_unlock(&bus->mdio_lock); -+ -+ return val; -+} -+ -+void ar8xxx_write(struct dsa_switch *ds, int reg, u32 val) -+{ -+ struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); -+ u16 r1, r2, page; -+ -+ split_addr((u32)reg, &r1, &r2, &page); -+ -+ mutex_lock(&bus->mdio_lock); -+ -+ bus->write(bus, 0x18, 0, page); -+ wait_for_page_switch(); -+ ar8xxx_mii_write32(bus, 0x10 | r2, r1, val); -+ -+ mutex_unlock(&bus->mdio_lock); -+} -+ -+u32 -+ar8xxx_rmw(struct dsa_switch *ds, int reg, u32 mask, u32 val) -+{ -+ struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); -+ u16 r1, r2, page; -+ u32 ret; -+ -+ split_addr((u32)reg, &r1, &r2, &page); -+ -+ mutex_lock(&bus->mdio_lock); -+ -+ bus->write(bus, 0x18, 0, page); -+ wait_for_page_switch(); -+ -+ ret = ar8xxx_mii_read32(bus, 0x10 | r2, r1); -+ ret &= ~mask; -+ ret |= val; -+ ar8xxx_mii_write32(bus, 0x10 | r2, r1, ret); -+ -+ mutex_unlock(&bus->mdio_lock); -+ -+ return ret; -+} -+ -+static char *ar8xxx_probe(struct device *host_dev, int sw_addr) -+{ -+ struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev); -+ u32 phy_id; -+ -+ if (!bus) -+ return NULL; -+ -+ /* sw_addr is irrelevant as the switch occupies the MDIO bus from -+ * addresses 0 to 4 (PHYs) and 16-23 (for MDIO 32bits protocol). So -+ * we'll probe address 0 to see if we see the right switch family. -+ */ -+ phy_id = mdiobus_read(bus, 0, MII_PHYSID1) << 16; -+ phy_id |= mdiobus_read(bus, 0, MII_PHYSID2); -+ -+ switch (phy_id) { -+ case PHY_ID_QCA8337: -+ return "QCA8337"; -+ default: -+ return NULL; -+ } -+} -+ -+static int ar8xxx_regmap_read(void *ctx, uint32_t reg, uint32_t *val) -+{ -+ struct dsa_switch *ds = (struct dsa_switch *)ctx; -+ -+ *val = ar8xxx_read(ds, reg); -+ -+ return 0; -+} -+ -+static int ar8xxx_regmap_write(void *ctx, uint32_t reg, uint32_t val) -+{ -+ struct dsa_switch *ds = (struct dsa_switch *)ctx; -+ -+ ar8xxx_write(ds, reg, val); -+ -+ return 0; -+} -+ -+static const struct regmap_range ar8xxx_readable_ranges[] = { -+ regmap_reg_range(0x0000, 0x00e4), /* Global control */ -+ regmap_reg_range(0x0100, 0x0168), /* EEE control */ -+ regmap_reg_range(0x0200, 0x0270), /* Parser control */ -+ regmap_reg_range(0x0400, 0x0454), /* ACL */ -+ regmap_reg_range(0x0600, 0x0718), /* Lookup */ -+ regmap_reg_range(0x0800, 0x0b70), /* QM */ -+ regmap_reg_range(0x0C00, 0x0c80), /* PKT */ -+ regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ -+ regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ -+ regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ -+ regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ -+ regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ -+ regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ -+ regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ -+ -+}; -+ -+static struct regmap_access_table ar8xxx_readable_table = { -+ .yes_ranges = ar8xxx_readable_ranges, -+ .n_yes_ranges = ARRAY_SIZE(ar8xxx_readable_ranges), -+}; -+ -+struct regmap_config ar8xxx_regmap_config = { -+ .reg_bits = 16, -+ .val_bits = 32, -+ .reg_stride = 4, -+ .max_register = 0x16ac, /* end MIB - Port6 range */ -+ .reg_read = ar8xxx_regmap_read, -+ .reg_write = ar8xxx_regmap_write, -+ .rd_table = &ar8xxx_readable_table, -+}; -+ -+static int ar8xxx_set_pad_ctrl(struct dsa_switch *ds, int port, int mode) -+{ -+ int reg; -+ -+ switch (port) { -+ case 0: -+ reg = AR8327_REG_PORT0_PAD_CTRL; -+ break; -+ case 6: -+ reg = AR8327_REG_PORT6_PAD_CTRL; -+ break; -+ default: -+ pr_err("Can't set PAD_CTRL on port %d\n", port); -+ return -EINVAL; -+ } -+ -+ /* DSA only supports 1 CPU port for now, so we'll take the assumption -+ * that P0 is connected to the CPU master_dev. -+ */ -+ switch (mode) { -+ case PHY_INTERFACE_MODE_RGMII: -+ ar8xxx_write(ds, reg, -+ AR8327_PORT_PAD_RGMII_EN | -+ AR8327_PORT_PAD_RGMII_TX_DELAY(3) | -+ AR8327_PORT_PAD_RGMII_RX_DELAY(3)); -+ -+ /* According to the datasheet, RGMII delay is enabled through -+ * PORT5_PAD_CTRL for all ports, rather than individual port -+ * registers -+ */ -+ ar8xxx_write(ds, AR8327_REG_PORT5_PAD_CTRL, -+ AR8327_PORT_PAD_RGMII_RX_DELAY_EN); -+ break; -+ case PHY_INTERFACE_MODE_SGMII: -+ ar8xxx_write(ds, reg, AR8327_PORT_PAD_SGMII_EN); -+ break; -+ default: -+ pr_err("xMII mode %d not supported\n", mode); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int ar8xxx_of_setup(struct dsa_switch *ds) -+{ -+ struct device_node *dn = ds->pd->of_node; -+ const char *s_phymode; -+ int ret, mode; -+ u32 phy_id, ctrl; -+ -+ /* If port6-phy-mode property exists, configure it accordingly */ -+ if (!of_property_read_string(dn, "qca,port6-phy-mode", &s_phymode)) { -+ for (mode = 0; mode < PHY_INTERFACE_MODE_MAX; mode++) -+ if (!strcasecmp(s_phymode, phy_modes(mode))) -+ break; -+ -+ if (mode == PHY_INTERFACE_MODE_MAX) -+ pr_err("Unknown phy-mode: \"%s\"\n", s_phymode); -+ -+ ret = ar8xxx_set_pad_ctrl(ds, 6, mode); -+ if (ret < 0) -+ return ret; -+ } -+ -+ /* If a phy ID is specified for PORT6 mac, connect them together */ -+ if (!of_property_read_u32(dn, "qca,port6-phy-id", &phy_id)) { -+ ar8xxx_rmw(ds, AR8327_PORT_LOOKUP_CTRL(6), -+ AR8327_PORT_LOOKUP_MEMBER, BIT(phy_to_port(phy_id))); -+ ar8xxx_rmw(ds, AR8327_PORT_LOOKUP_CTRL(phy_to_port(phy_id)), -+ AR8327_PORT_LOOKUP_MEMBER, BIT(6)); -+ -+ /* We want the switch to be pass-through and act like a PHY on -+ * these ports. So BC/MC/UC & IGMP frames need to be accepted -+ */ -+ ctrl = BIT(phy_to_port(phy_id)) | BIT(6); -+ ar8xxx_reg_set(ds, AR8327_REG_GLOBAL_FW_CTRL1, -+ ctrl << AR8327_GLOBAL_FW_CTRL1_IGMP_DP_S | -+ ctrl << AR8327_GLOBAL_FW_CTRL1_BC_DP_S | -+ ctrl << AR8327_GLOBAL_FW_CTRL1_MC_DP_S | -+ ctrl << AR8327_GLOBAL_FW_CTRL1_UC_DP_S); -+ } -+ -+ return 0; -+} -+ -+static int ar8xxx_setup(struct dsa_switch *ds) -+{ -+ struct ar8xxx_priv *priv = ds_to_priv(ds); -+ struct net_device *netdev = ds->dst->pd->of_netdev; -+ int ret, i, phy_mode; -+ -+ /* Start by setting up the register mapping */ -+ priv->regmap = devm_regmap_init(ds->master_dev, NULL, ds, -+ &ar8xxx_regmap_config); -+ -+ if (IS_ERR(priv->regmap)) -+ pr_warn("regmap initialization failed"); -+ -+ /* Initialize CPU port pad mode (xMII type, delays...) */ -+ phy_mode = of_get_phy_mode(netdev->dev.parent->of_node); -+ if (phy_mode < 0) { -+ pr_err("Can't find phy-mode for master device\n"); -+ return phy_mode; -+ } -+ -+ ret = ar8xxx_set_pad_ctrl(ds, 0, phy_mode); -+ if (ret < 0) -+ return ret; -+ -+ /* Enable CPU Port */ -+ ar8xxx_reg_set(ds, AR8327_REG_GLOBAL_FW_CTRL0, -+ AR8327_GLOBAL_FW_CTRL0_CPU_PORT_EN); -+ -+ /* Enable MIB counters */ -+ ar8xxx_reg_set(ds, AR8327_REG_MIB, AR8327_MIB_CPU_KEEP); -+ ar8xxx_write(ds, AR8327_REG_MODULE_EN, AR8327_MODULE_EN_MIB); -+ -+ /* Enable QCA header mode on Port 0 */ -+ ar8xxx_write(ds, AR8327_REG_PORT_HDR_CTRL(0), -+ AR8327_PORT_HDR_CTRL_ALL << AR8327_PORT_HDR_CTRL_TX_S | -+ AR8327_PORT_HDR_CTRL_ALL << AR8327_PORT_HDR_CTRL_RX_S); -+ -+ /* Disable forwarding by default on all ports */ -+ for (i = 0; i < AR8327_NUM_PORTS; i++) -+ ar8xxx_rmw(ds, AR8327_PORT_LOOKUP_CTRL(i), -+ AR8327_PORT_LOOKUP_MEMBER, 0); -+ -+ /* Forward all unknown frames to CPU port for Linux processing */ -+ ar8xxx_write(ds, AR8327_REG_GLOBAL_FW_CTRL1, -+ BIT(0) << AR8327_GLOBAL_FW_CTRL1_IGMP_DP_S | -+ BIT(0) << AR8327_GLOBAL_FW_CTRL1_BC_DP_S | -+ BIT(0) << AR8327_GLOBAL_FW_CTRL1_MC_DP_S | -+ BIT(0) << AR8327_GLOBAL_FW_CTRL1_UC_DP_S); -+ -+ /* Setup connection between CPU ports & PHYs */ -+ for (i = 0; i < DSA_MAX_PORTS; i++) { -+ /* CPU port gets connected to all PHYs in the switch */ -+ if (dsa_is_cpu_port(ds, i)) { -+ ar8xxx_rmw(ds, AR8327_PORT_LOOKUP_CTRL(0), -+ AR8327_PORT_LOOKUP_MEMBER, -+ ds->phys_port_mask << 1); -+ } -+ -+ /* Invividual PHYs gets connected to CPU port only */ -+ if (ds->phys_port_mask & BIT(i)) { -+ int phy = phy_to_port(i); -+ -+ ar8xxx_rmw(ds, AR8327_PORT_LOOKUP_CTRL(phy), -+ AR8327_PORT_LOOKUP_MEMBER, BIT(0)); -+ -+ /* Disable Auto-learning by default so the switch -+ * doesn't try to forward the frame to another port -+ */ -+ ar8xxx_reg_clear(ds, AR8327_PORT_LOOKUP_CTRL(phy), -+ AR8327_PORT_LOOKUP_LEARN); -+ } -+ } -+ -+ ret = ar8xxx_of_setup(ds); -+ if (ret < 0) -+ return ret; -+ -+ return 0; -+} -+ -+static int ar8xxx_set_addr(struct dsa_switch *ds, u8 *addr) -+{ -+ return 0; -+} -+ -+static int ar8xxx_phy_read(struct dsa_switch *ds, int phy, int regnum) -+{ -+ struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); -+ -+ return mdiobus_read(bus, phy, regnum); -+} -+ -+static int -+ar8xxx_phy_write(struct dsa_switch *ds, int phy, int regnum, u16 val) -+{ -+ struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); -+ -+ return mdiobus_write(bus, phy, regnum, val); -+} -+ -+static void ar8xxx_get_strings(struct dsa_switch *ds, int phy, uint8_t *data) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) { -+ strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, -+ ETH_GSTRING_LEN); -+ } -+} -+ -+static void ar8xxx_get_ethtool_stats(struct dsa_switch *ds, int phy, -+ uint64_t *data) -+{ -+ const struct ar8xxx_mib_desc *mib; -+ uint32_t reg, i, port; -+ u64 hi; -+ -+ port = phy_to_port(phy); -+ -+ for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) { -+ mib = &ar8327_mib[i]; -+ reg = AR8327_PORT_MIB_COUNTER(port) + mib->offset; -+ -+ data[i] = ar8xxx_read(ds, reg); -+ if (mib->size == 2) { -+ hi = ar8xxx_read(ds, reg + 4); -+ data[i] |= hi << 32; -+ } -+ } -+} -+ -+static int ar8xxx_get_sset_count(struct dsa_switch *ds) -+{ -+ return ARRAY_SIZE(ar8327_mib); -+} -+ -+static void ar8xxx_poll_link(struct dsa_switch *ds) -+{ -+ int i = 0; -+ struct net_device *dev; -+ -+ while ((dev = ds->ports[i++]) != NULL) { -+ u32 status; -+ int link; -+ int speed; -+ int duplex; -+ -+ status = ar8xxx_read(ds, AR8327_REG_PORT_STATUS(i)); -+ link = !!(status & AR8XXX_PORT_STATUS_LINK_UP); -+ duplex = !!(status & AR8XXX_PORT_STATUS_DUPLEX); -+ -+ switch (status & AR8XXX_PORT_STATUS_SPEED) { -+ case AR8XXX_PORT_SPEED_10M: -+ speed = 10; -+ break; -+ case AR8XXX_PORT_SPEED_100M: -+ speed = 100; -+ break; -+ case AR8XXX_PORT_SPEED_1000M: -+ speed = 1000; -+ break; -+ default: -+ speed = 0; -+ } -+ -+ if (!link) { -+ /* This poll happens every ~1s, so we don't want to -+ * print the status every time. Only when the device -+ * transitions from Link UP to Link DOWN -+ */ -+ if (netif_carrier_ok(dev)) -+ netif_carrier_off(dev); -+ continue; -+ } else { -+ /* Same thing here. But we detect a Link UP event */ -+ if (!netif_carrier_ok(dev)) -+ netif_carrier_on(dev); -+ continue; -+ } -+ } -+} -+ -+static struct dsa_switch_driver ar8xxx_switch_driver = { -+ .tag_protocol = DSA_TAG_PROTO_QCA, -+ .priv_size = sizeof(struct ar8xxx_priv), -+ .probe = ar8xxx_probe, -+ .setup = ar8xxx_setup, -+ .set_addr = ar8xxx_set_addr, -+ .poll_link = ar8xxx_poll_link, -+ .phy_read = ar8xxx_phy_read, -+ .phy_write = ar8xxx_phy_write, -+ .get_strings = ar8xxx_get_strings, -+ .get_ethtool_stats = ar8xxx_get_ethtool_stats, -+ .get_sset_count = ar8xxx_get_sset_count, -+}; -+ -+static int __init ar8xxx_init(void) -+{ -+ register_switch_driver(&ar8xxx_switch_driver); -+ return 0; -+} -+module_init(ar8xxx_init); -+ -+static void __exit ar8xxx_cleanup(void) -+{ -+ unregister_switch_driver(&ar8xxx_switch_driver); -+} -+module_exit(ar8xxx_cleanup); -+ -+MODULE_AUTHOR("Mathieu Olivari "); -+MODULE_DESCRIPTION("Driver for AR8XXX ethernet switch family"); -+MODULE_LICENSE("GPL"); -+MODULE_ALIAS("platform:ar8xxx"); ---- /dev/null -+++ b/drivers/net/dsa/ar8xxx.h -@@ -0,0 +1,156 @@ -+/* -+ * Copyright (C) 2009 Felix Fietkau -+ * Copyright (C) 2011-2012 Gabor Juhos -+ * Copyright (c) 2015, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#ifndef __AR8XXX_H -+#define __AR8XXX_H -+ -+#include -+#include -+ -+struct ar8xxx_priv { -+ struct regmap *regmap; -+}; -+ -+struct ar8xxx_mib_desc { -+ unsigned int size; -+ unsigned int offset; -+ const char *name; -+}; -+ -+#define AR8327_NUM_PORTS 7 -+ -+#define PHY_ID_QCA8337 0x004dd036 -+ -+#define AR8327_REG_PORT0_PAD_CTRL 0x004 -+#define AR8327_REG_PORT5_PAD_CTRL 0x008 -+#define AR8327_REG_PORT6_PAD_CTRL 0x00c -+#define AR8327_PORT_PAD_RGMII_EN BIT(26) -+#define AR8327_PORT_PAD_RGMII_TX_DELAY(x) ((0x8 + (x & 0x3)) << 22) -+#define AR8327_PORT_PAD_RGMII_RX_DELAY(x) ((0x10 + (x & 0x3)) << 20) -+#define AR8327_PORT_PAD_RGMII_RX_DELAY_EN BIT(24) -+#define AR8327_PORT_PAD_SGMII_EN BIT(7) -+ -+#define AR8327_REG_MODULE_EN 0x030 -+#define AR8327_MODULE_EN_MIB BIT(0) -+#define AR8327_MODULE_EN_ACL BIT(1) -+#define AR8327_MODULE_EN_L3 BIT(2) -+ -+#define AR8327_REG_MIB 0x034 -+#define AR8327_MIB_CPU_KEEP BIT(20) -+ -+#define AR8327_REG_PORT_STATUS(_i) (0x07c + (_i) * 4) -+#define AR8XXX_PORT_STATUS_SPEED GENMASK(2, 0) -+#define AR8XXX_PORT_STATUS_SPEED_S 0 -+#define AR8XXX_PORT_STATUS_TXMAC BIT(2) -+#define AR8XXX_PORT_STATUS_RXMAC BIT(3) -+#define AR8XXX_PORT_STATUS_TXFLOW BIT(4) -+#define AR8XXX_PORT_STATUS_RXFLOW BIT(5) -+#define AR8XXX_PORT_STATUS_DUPLEX BIT(6) -+#define AR8XXX_PORT_STATUS_LINK_UP BIT(8) -+#define AR8XXX_PORT_STATUS_LINK_AUTO BIT(9) -+#define AR8XXX_PORT_STATUS_LINK_PAUSE BIT(10) -+ -+#define AR8327_REG_PORT_HDR_CTRL(_i) (0x9c + (_i * 4)) -+#define AR8327_PORT_HDR_CTRL_RX_MASK GENMASK(3, 2) -+#define AR8327_PORT_HDR_CTRL_RX_S 2 -+#define AR8327_PORT_HDR_CTRL_TX_MASK GENMASK(1, 0) -+#define AR8327_PORT_HDR_CTRL_TX_S 0 -+#define AR8327_PORT_HDR_CTRL_ALL 2 -+#define AR8327_PORT_HDR_CTRL_MGMT 1 -+#define AR8327_PORT_HDR_CTRL_NONE 0 -+ -+#define AR8327_REG_GLOBAL_FW_CTRL0 0x620 -+#define AR8327_GLOBAL_FW_CTRL0_CPU_PORT_EN BIT(10) -+ -+#define AR8327_REG_GLOBAL_FW_CTRL1 0x624 -+#define AR8327_GLOBAL_FW_CTRL1_IGMP_DP_MASK GENMASK(30, 24) -+#define AR8327_GLOBAL_FW_CTRL1_IGMP_DP_S 24 -+#define AR8327_GLOBAL_FW_CTRL1_BC_DP_MASK GENMASK(22, 16) -+#define AR8327_GLOBAL_FW_CTRL1_BC_DP_S 16 -+#define AR8327_GLOBAL_FW_CTRL1_MC_DP_MASK GENMASK(14, 8) -+#define AR8327_GLOBAL_FW_CTRL1_MC_DP_S 8 -+#define AR8327_GLOBAL_FW_CTRL1_UC_DP_MASK GENMASK(6, 0) -+#define AR8327_GLOBAL_FW_CTRL1_UC_DP_S 0 -+ -+#define AR8327_PORT_LOOKUP_CTRL(_i) (0x660 + (_i) * 0xc) -+#define AR8327_PORT_LOOKUP_MEMBER GENMASK(6, 0) -+#define AR8327_PORT_LOOKUP_IN_MODE GENMASK(9, 8) -+#define AR8327_PORT_LOOKUP_IN_MODE_S 8 -+#define AR8327_PORT_LOOKUP_STATE GENMASK(18, 16) -+#define AR8327_PORT_LOOKUP_STATE_S 16 -+#define AR8327_PORT_LOOKUP_LEARN BIT(20) -+#define AR8327_PORT_LOOKUP_ING_MIRROR_EN BIT(25) -+ -+#define AR8327_PORT_MIB_COUNTER(_i) (0x1000 + (_i) * 0x100) -+ -+/* port speed */ -+enum { -+ AR8XXX_PORT_SPEED_10M = 0, -+ AR8XXX_PORT_SPEED_100M = 1, -+ AR8XXX_PORT_SPEED_1000M = 2, -+ AR8XXX_PORT_SPEED_ERR = 3, -+}; -+ -+static inline int port_to_phy(int port) -+{ -+ if (port >= 1 && port <= 6) -+ return port - 1; -+ -+ return -1; -+} -+ -+static inline int phy_to_port(int phy) -+{ -+ if (phy < 5) -+ return phy + 1; -+ -+ return -1; -+} -+ -+u32 -+ar8xxx_rmw(struct dsa_switch *ds, int reg, u32 mask, u32 val); -+ -+static inline void -+split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) -+{ -+ regaddr >>= 1; -+ *r1 = regaddr & 0x1e; -+ -+ regaddr >>= 5; -+ *r2 = regaddr & 0x7; -+ -+ regaddr >>= 3; -+ *page = regaddr & 0x1ff; -+} -+ -+static inline void -+wait_for_page_switch(void) -+{ -+ udelay(5); -+} -+ -+static inline void -+ar8xxx_reg_set(struct dsa_switch *ds, int reg, u32 val) -+{ -+ ar8xxx_rmw(ds, reg, 0, val); -+} -+ -+static inline void -+ar8xxx_reg_clear(struct dsa_switch *ds, int reg, u32 val) -+{ -+ ar8xxx_rmw(ds, reg, val, 0); -+} -+ -+#endif /* __AR8XXX_H */ ---- a/net/dsa/dsa.c -+++ b/net/dsa/dsa.c -@@ -285,6 +285,11 @@ static int dsa_switch_setup_one(struct d - dst->rcv = brcm_netdev_ops.rcv; - break; - #endif -+#ifdef CONFIG_NET_DSA_TAG_QCA -+ case DSA_TAG_PROTO_QCA: -+ dst->rcv = qca_netdev_ops.rcv; -+ break; -+#endif - case DSA_TAG_PROTO_NONE: - break; - default: -@@ -1041,6 +1046,7 @@ static SIMPLE_DEV_PM_OPS(dsa_pm_ops, dsa - - static const struct of_device_id dsa_of_match_table[] = { - { .compatible = "brcm,bcm7445-switch-v4.0" }, -+ { .compatible = "qca,ar8xxx", }, - { .compatible = "marvell,dsa", }, - {} - }; ---- a/include/net/dsa.h -+++ b/include/net/dsa.h -@@ -26,6 +26,7 @@ enum dsa_tag_protocol { - DSA_TAG_PROTO_TRAILER, - DSA_TAG_PROTO_EDSA, - DSA_TAG_PROTO_BRCM, -+ DSA_TAG_PROTO_QCA, - }; - - #define DSA_MAX_SWITCHES 4 ---- a/net/dsa/Kconfig -+++ b/net/dsa/Kconfig -@@ -26,6 +26,9 @@ config NET_DSA_HWMON - via the hwmon sysfs interface and exposes the onboard sensors. - - # tagging formats -+config NET_DSA_TAG_QCA -+ bool -+ - config NET_DSA_TAG_BRCM - bool - ---- a/net/dsa/Makefile -+++ b/net/dsa/Makefile -@@ -3,6 +3,7 @@ obj-$(CONFIG_NET_DSA) += dsa_core.o - dsa_core-y += dsa.o slave.o - - # tagging formats -+dsa_core-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o - dsa_core-$(CONFIG_NET_DSA_TAG_BRCM) += tag_brcm.o - dsa_core-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o - dsa_core-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o ---- a/net/dsa/dsa_priv.h -+++ b/net/dsa/dsa_priv.h -@@ -78,5 +78,7 @@ extern const struct dsa_device_ops trail - /* tag_brcm.c */ - extern const struct dsa_device_ops brcm_netdev_ops; - -+/* tag_qca.c */ -+extern const struct dsa_device_ops qca_netdev_ops; - - #endif ---- a/net/dsa/slave.c -+++ b/net/dsa/slave.c -@@ -1182,6 +1182,11 @@ int dsa_slave_create(struct dsa_switch * - p->xmit = brcm_netdev_ops.xmit; - break; - #endif -+#ifdef CONFIG_NET_DSA_TAG_QCA -+ case DSA_TAG_PROTO_QCA: -+ p->xmit = qca_netdev_ops.xmit; -+ break; -+#endif - default: - p->xmit = dsa_slave_notag_xmit; - break; ---- /dev/null -+++ b/net/dsa/tag_qca.c -@@ -0,0 +1,158 @@ -+/* -+ * Copyright (c) 2015, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include -+#include "dsa_priv.h" -+ -+#define QCA_HDR_LEN 2 -+#define QCA_HDR_VERSION 0x2 -+ -+#define QCA_HDR_RECV_VERSION_MASK GENMASK(15, 14) -+#define QCA_HDR_RECV_VERSION_S 14 -+#define QCA_HDR_RECV_PRIORITY_MASK GENMASK(13, 11) -+#define QCA_HDR_RECV_PRIORITY_S 11 -+#define QCA_HDR_RECV_TYPE_MASK GENMASK(10, 6) -+#define QCA_HDR_RECV_TYPE_S 6 -+#define QCA_HDR_RECV_FRAME_IS_TAGGED BIT(3) -+#define QCA_HDR_RECV_SOURCE_PORT_MASK GENMASK(2, 0) -+ -+#define QCA_HDR_XMIT_VERSION_MASK GENMASK(15, 14) -+#define QCA_HDR_XMIT_VERSION_S 14 -+#define QCA_HDR_XMIT_PRIORITY_MASK GENMASK(13, 11) -+#define QCA_HDR_XMIT_PRIORITY_S 11 -+#define QCA_HDR_XMIT_CONTROL_MASK GENMASK(10, 8) -+#define QCA_HDR_XMIT_CONTROL_S 8 -+#define QCA_HDR_XMIT_FROM_CPU BIT(7) -+#define QCA_HDR_XMIT_DP_BIT_MASK GENMASK(6, 0) -+ -+static inline int reg_to_port(int reg) -+{ -+ if (reg < 5) -+ return reg + 1; -+ -+ return -1; -+} -+ -+static inline int port_to_reg(int port) -+{ -+ if (port >= 1 && port <= 6) -+ return port - 1; -+ -+ return -1; -+} -+ -+static netdev_tx_t qca_tag_xmit(struct sk_buff *skb, struct net_device *dev) -+{ -+ struct dsa_slave_priv *p = netdev_priv(dev); -+ u16 *phdr, hdr; -+ -+ dev->stats.tx_packets++; -+ dev->stats.tx_bytes += skb->len; -+ -+ if (skb_cow_head(skb, 0) < 0) -+ goto out_free; -+ -+ skb_push(skb, QCA_HDR_LEN); -+ -+ memmove(skb->data, skb->data + QCA_HDR_LEN, 2 * ETH_ALEN); -+ phdr = (u16 *)(skb->data + 2 * ETH_ALEN); -+ -+ /* Set the version field, and set destination port information */ -+ hdr = QCA_HDR_VERSION << QCA_HDR_XMIT_VERSION_S | -+ QCA_HDR_XMIT_FROM_CPU | -+ 1 << reg_to_port(p->port); -+ -+ *phdr = htons(hdr); -+ -+ skb->dev = p->parent->dst->master_netdev; -+ dev_queue_xmit(skb); -+ -+ return NETDEV_TX_OK; -+ -+out_free: -+ kfree_skb(skb); -+ return NETDEV_TX_OK; -+} -+ -+static int qca_tag_rcv(struct sk_buff *skb, struct net_device *dev, -+ struct packet_type *pt, struct net_device *orig_dev) -+{ -+ struct dsa_switch_tree *dst = dev->dsa_ptr; -+ struct dsa_switch *ds; -+ u8 ver; -+ int port, phy; -+ __be16 *phdr, hdr; -+ -+ if (unlikely(!dst)) -+ goto out_drop; -+ -+ skb = skb_unshare(skb, GFP_ATOMIC); -+ if (!skb) -+ goto out; -+ -+ if (unlikely(!pskb_may_pull(skb, QCA_HDR_LEN))) -+ goto out_drop; -+ -+ /* Ethernet is added by the switch between src addr and Ethertype -+ * At this point, skb->data points to ethertype so header should be -+ * right before -+ */ -+ phdr = (__be16 *)(skb->data - 2); -+ hdr = ntohs(*phdr); -+ -+ /* Make sure the version is correct */ -+ ver = (hdr & QCA_HDR_RECV_VERSION_MASK) >> QCA_HDR_RECV_VERSION_S; -+ if (unlikely(ver != QCA_HDR_VERSION)) -+ goto out_drop; -+ -+ /* Remove QCA tag and recalculate checksum */ -+ skb_pull_rcsum(skb, QCA_HDR_LEN); -+ memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - QCA_HDR_LEN, -+ ETH_HLEN - QCA_HDR_LEN); -+ -+ /* This protocol doesn't support cascading multiple switches so it's -+ * safe to assume the switch is first in the tree -+ */ -+ ds = dst->ds[0]; -+ if (!ds) -+ goto out_drop; -+ -+ /* Get source port information */ -+ port = (hdr & QCA_HDR_RECV_SOURCE_PORT_MASK); -+ phy = port_to_reg(port); -+ if (unlikely(phy < 0) || !ds->ports[phy]) -+ goto out_drop; -+ -+ /* Update skb & forward the frame accordingly */ -+ skb_push(skb, ETH_HLEN); -+ skb->pkt_type = PACKET_HOST; -+ skb->dev = ds->ports[phy]; -+ skb->protocol = eth_type_trans(skb, skb->dev); -+ -+ skb->dev->stats.rx_packets++; -+ skb->dev->stats.rx_bytes += skb->len; -+ -+ netif_receive_skb(skb); -+ -+ return 0; -+ -+out_drop: -+ kfree_skb(skb); -+out: -+ return 0; -+} -+ -+const struct dsa_device_ops qca_netdev_ops = { -+ .xmit = qca_tag_xmit, -+ .rcv = qca_tag_rcv, -+}; ---- /dev/null -+++ b/Documentation/devicetree/bindings/net/dsa/qca-ar8xxx.txt -@@ -0,0 +1,70 @@ -+* Qualcomm Atheros AR8xxx switch family -+ -+Required properties: -+ -+- compatible: should be "qca,ar8xxx" -+- dsa,mii-bus: phandle to the MDIO bus controller, see dsa/dsa.txt -+- dsa,ethernet: phandle to the CPU network interface controller, see dsa/dsa.txt -+- #size-cells: must be 0 -+- #address-cells: must be 2, see dsa/dsa.txt -+ -+Subnodes: -+ -+The integrated switch subnode should be specified according to the binding -+described in dsa/dsa.txt. -+ -+Optional properties: -+ -+- qca,port6-phy-mode: if specified, the driver will configure Port 6 in the -+ given phy-mode. See Documentation/devicetree/bindings/net/ethernet.txt for -+ the list of valid phy-mode. -+ -+- qca,port6-phy-id: if specified, the driver will connect Port 6 to the PHY -+ given as a parameter. In this case, Port6 and the corresponding PHY will be -+ isolated from the rest of the switch. From a system perspective, they will -+ act as a regular PHY. -+ -+Example: -+ -+ dsa@0 { -+ compatible = "qca,ar8xxx"; -+ #address-cells = <2>; -+ #size-cells = <0>; -+ -+ dsa,ethernet = <ðernet0>; -+ dsa,mii-bus = <&mii_bus0>; -+ -+ switch@0 { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0 0>; /* MDIO address 0, switch 0 in tree */ -+ -+ qca,port6-phy-mode = "sgmii"; -+ qca,port6-phy-id = <4>; -+ -+ port@0 { -+ reg = <11>; -+ label = "cpu"; -+ }; -+ -+ port@1 { -+ reg = <0>; -+ label = "lan1"; -+ }; -+ -+ port@2 { -+ reg = <1>; -+ label = "lan2"; -+ }; -+ -+ port@3 { -+ reg = <2>; -+ label = "lan3"; -+ }; -+ -+ port@4 { -+ reg = <3>; -+ label = "lan4"; -+ }; -+ }; -+ }; diff --git a/target/linux/ipq806x/patches-4.4/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch b/target/linux/ipq806x/patches-4.4/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch deleted file mode 100644 index 102cd76b49..0000000000 --- a/target/linux/ipq806x/patches-4.4/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch +++ /dev/null @@ -1,146 +0,0 @@ -From e81de9d28bd0421c236df322872e64edf4ee1852 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Mon, 11 May 2015 16:32:09 -0700 -Subject: [PATCH 7/8] ARM: dts: qcom: add mdio nodes to ap148 & db149 - -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 40 ++++++++++++++++++++++++++- - arch/arm/boot/dts/qcom-ipq8064-db149.dts | 46 ++++++++++++++++++++++++++++++++ - 2 files changed, 85 insertions(+), 1 deletion(-) - ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -19,8 +19,9 @@ - }; - }; - -- alias { -+ aliases { - serial0 = &uart4; -+ mdio-gpio0 = &mdio0; - }; - - chosen { -@@ -65,6 +66,15 @@ - bias-bus-hold; - }; - }; -+ -+ mdio0_pins: mdio0_pins { -+ mux { -+ pins = "gpio0", "gpio1"; -+ function = "gpio"; -+ drive-strength = <8>; -+ bias-disable; -+ }; -+ }; - }; - - gsbi@16300000 { -@@ -160,6 +170,34 @@ - - linux,part-probe = "qcom-smem"; - }; -+ -+ mdio0: mdio { -+ compatible = "virtual,mdio-gpio"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>; -+ pinctrl-0 = <&mdio0_pins>; -+ pinctrl-names = "default"; -+ -+ phy0: ethernet-phy@0 { -+ device_type = "ethernet-phy"; -+ reg = <0>; -+ qca,ar8327-initvals = < -+ 0x00004 0x7600000 /* PAD0_MODE */ -+ 0x00008 0x1000000 /* PAD5_MODE */ -+ 0x0000c 0x80 /* PAD6_MODE */ -+ 0x000e4 0x6a545 /* MAC_POWER_SEL */ -+ 0x000e0 0xc74164de /* SGMII_CTRL */ -+ 0x0007c 0x4e /* PORT0_STATUS */ -+ 0x00094 0x4e /* PORT6_STATUS */ -+ >; -+ }; -+ -+ phy4: ethernet-phy@4 { -+ device_type = "ethernet-phy"; -+ reg = <4>; -+ }; -+ }; - }; - }; - ---- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts -@@ -16,6 +16,7 @@ - - alias { - serial0 = &uart2; -+ mdio-gpio0 = &mdio0; - }; - - chosen { -@@ -38,6 +39,15 @@ - bias-none; - }; - }; -+ -+ mdio0_pins: mdio0_pins { -+ mux { -+ pins = "gpio0", "gpio1"; -+ function = "gpio"; -+ drive-strength = <8>; -+ bias-disable; -+ }; -+ }; - }; - - gsbi2: gsbi@12480000 { -@@ -140,5 +150,44 @@ - pcie2: pci@1b900000 { - status = "ok"; - }; -+ -+ mdio0: mdio { -+ compatible = "virtual,mdio-gpio"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>; -+ -+ pinctrl-0 = <&mdio0_pins>; -+ pinctrl-names = "default"; -+ -+ phy0: ethernet-phy@0 { -+ device_type = "ethernet-phy"; -+ reg = <0>; -+ qca,ar8327-initvals = < -+ 0x00004 0x7600000 /* PAD0_MODE */ -+ 0x00008 0x1000000 /* PAD5_MODE */ -+ 0x0000c 0x80 /* PAD6_MODE */ -+ 0x000e4 0x6a545 /* MAC_POWER_SEL */ -+ 0x000e0 0xc74164de /* SGMII_CTRL */ -+ 0x0007c 0x4e /* PORT0_STATUS */ -+ 0x00094 0x4e /* PORT6_STATUS */ -+ >; -+ }; -+ -+ phy4: ethernet-phy@4 { -+ device_type = "ethernet-phy"; -+ reg = <4>; -+ }; -+ -+ phy6: ethernet-phy@6 { -+ device_type = "ethernet-phy"; -+ reg = <6>; -+ }; -+ -+ phy7: ethernet-phy@7 { -+ device_type = "ethernet-phy"; -+ reg = <7>; -+ }; -+ }; - }; - }; diff --git a/target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch deleted file mode 100644 index b722dfa25f..0000000000 --- a/target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch +++ /dev/null @@ -1,216 +0,0 @@ -From cab1f4720e82f2e17eaeed9a9ad9e4f07c742977 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Mon, 11 May 2015 12:29:18 -0700 -Subject: [PATCH 8/8] ARM: dts: qcom: add gmac nodes to ipq806x platforms - -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 31 ++++++++++++ - arch/arm/boot/dts/qcom-ipq8064-db149.dts | 43 ++++++++++++++++ - arch/arm/boot/dts/qcom-ipq8064.dtsi | 86 ++++++++++++++++++++++++++++++++ - 3 files changed, 160 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -75,6 +75,16 @@ - bias-disable; - }; - }; -+ -+ rgmii2_pins: rgmii2_pins { -+ mux { -+ pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", -+ "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; -+ function = "rgmii2"; -+ drive-strength = <8>; -+ bias-disable; -+ }; -+ }; - }; - - gsbi@16300000 { -@@ -198,6 +208,31 @@ - reg = <4>; - }; - }; -+ -+ gmac1: ethernet@37200000 { -+ status = "ok"; -+ phy-mode = "rgmii"; -+ qcom,id = <1>; -+ -+ pinctrl-0 = <&rgmii2_pins>; -+ pinctrl-names = "default"; -+ -+ fixed-link { -+ speed = <1000>; -+ full-duplex; -+ }; -+ }; -+ -+ gmac2: ethernet@37400000 { -+ status = "ok"; -+ phy-mode = "sgmii"; -+ qcom,id = <2>; -+ -+ fixed-link { -+ speed = <1000>; -+ full-duplex; -+ }; -+ }; - }; - }; - ---- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts -@@ -48,6 +48,14 @@ - bias-disable; - }; - }; -+ -+ rgmii0_pins: rgmii0_pins { -+ mux { -+ pins = "gpio2", "gpio66"; -+ drive-strength = <8>; -+ bias-disable; -+ }; -+ }; - }; - - gsbi2: gsbi@12480000 { -@@ -189,5 +197,40 @@ - reg = <7>; - }; - }; -+ -+ gmac0: ethernet@37000000 { -+ status = "ok"; -+ phy-mode = "rgmii"; -+ qcom,id = <0>; -+ phy-handle = <&phy4>; -+ -+ pinctrl-0 = <&rgmii0_pins>; -+ pinctrl-names = "default"; -+ }; -+ -+ gmac1: ethernet@37200000 { -+ status = "ok"; -+ phy-mode = "sgmii"; -+ qcom,id = <1>; -+ -+ fixed-link { -+ speed = <1000>; -+ full-duplex; -+ }; -+ }; -+ -+ gmac2: ethernet@37400000 { -+ status = "ok"; -+ phy-mode = "sgmii"; -+ qcom,id = <2>; -+ phy-handle = <&phy6>; -+ }; -+ -+ gmac3: ethernet@37600000 { -+ status = "ok"; -+ phy-mode = "sgmii"; -+ qcom,id = <3>; -+ phy-handle = <&phy7>; -+ }; - }; - }; ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -917,6 +917,92 @@ - - status = "disabled"; - }; -+ -+ nss_common: syscon@03000000 { -+ compatible = "syscon"; -+ reg = <0x03000000 0x0000FFFF>; -+ }; -+ -+ qsgmii_csr: syscon@1bb00000 { -+ compatible = "syscon"; -+ reg = <0x1bb00000 0x000001FF>; -+ }; -+ -+ gmac0: ethernet@37000000 { -+ device_type = "network"; -+ compatible = "qcom,ipq806x-gmac"; -+ reg = <0x37000000 0x200000>; -+ interrupts = ; -+ interrupt-names = "macirq"; -+ -+ qcom,nss-common = <&nss_common>; -+ qcom,qsgmii-csr = <&qsgmii_csr>; -+ -+ clocks = <&gcc GMAC_CORE1_CLK>; -+ clock-names = "stmmaceth"; -+ -+ resets = <&gcc GMAC_CORE1_RESET>; -+ reset-names = "stmmaceth"; -+ -+ status = "disabled"; -+ }; -+ -+ gmac1: ethernet@37200000 { -+ device_type = "network"; -+ compatible = "qcom,ipq806x-gmac"; -+ reg = <0x37200000 0x200000>; -+ interrupts = ; -+ interrupt-names = "macirq"; -+ -+ qcom,nss-common = <&nss_common>; -+ qcom,qsgmii-csr = <&qsgmii_csr>; -+ -+ clocks = <&gcc GMAC_CORE2_CLK>; -+ clock-names = "stmmaceth"; -+ -+ resets = <&gcc GMAC_CORE2_RESET>; -+ reset-names = "stmmaceth"; -+ -+ status = "disabled"; -+ }; -+ -+ gmac2: ethernet@37400000 { -+ device_type = "network"; -+ compatible = "qcom,ipq806x-gmac"; -+ reg = <0x37400000 0x200000>; -+ interrupts = ; -+ interrupt-names = "macirq"; -+ -+ qcom,nss-common = <&nss_common>; -+ qcom,qsgmii-csr = <&qsgmii_csr>; -+ -+ clocks = <&gcc GMAC_CORE3_CLK>; -+ clock-names = "stmmaceth"; -+ -+ resets = <&gcc GMAC_CORE3_RESET>; -+ reset-names = "stmmaceth"; -+ -+ status = "disabled"; -+ }; -+ -+ gmac3: ethernet@37600000 { -+ device_type = "network"; -+ compatible = "qcom,ipq806x-gmac"; -+ reg = <0x37600000 0x200000>; -+ interrupts = ; -+ interrupt-names = "macirq"; -+ -+ qcom,nss-common = <&nss_common>; -+ qcom,qsgmii-csr = <&qsgmii_csr>; -+ -+ clocks = <&gcc GMAC_CORE4_CLK>; -+ clock-names = "stmmaceth"; -+ -+ resets = <&gcc GMAC_CORE4_RESET>; -+ reset-names = "stmmaceth"; -+ -+ status = "disabled"; -+ }; - }; - - sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.4/709-spi-qup-Fix-fifo-and-dma-support-for-IPQ806x.patch b/target/linux/ipq806x/patches-4.4/709-spi-qup-Fix-fifo-and-dma-support-for-IPQ806x.patch deleted file mode 100644 index fa78a0aeda..0000000000 --- a/target/linux/ipq806x/patches-4.4/709-spi-qup-Fix-fifo-and-dma-support-for-IPQ806x.patch +++ /dev/null @@ -1,129 +0,0 @@ -From 16d2871830ff3fe12a6bff582549a9264adff278 Mon Sep 17 00:00:00 2001 -From: Ram Chandra Jangir -Date: Tue, 10 May 2016 20:19:31 +0530 -Subject: [PATCH] spi: qup: Fix fifo and dma support for IPQ806x - -Signed-off-by: Ram Chandra Jangir ---- - drivers/spi/spi-qup.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++-- - 1 file changed, 52 insertions(+), 2 deletions(-) - ---- a/drivers/spi/spi-qup.c -+++ b/drivers/spi/spi-qup.c -@@ -24,6 +24,7 @@ - #include - #include - #include -+#include - - #define QUP_CONFIG 0x0000 - #define QUP_STATE 0x0004 -@@ -152,6 +153,7 @@ struct spi_qup { - int use_dma; - struct dma_slave_config rx_conf; - struct dma_slave_config tx_conf; -+ int mode; - }; - - -@@ -370,7 +372,8 @@ static int spi_qup_do_pio(struct spi_mas - return ret; - } - -- spi_qup_fifo_write(qup, xfer); -+ if (qup->mode == QUP_IO_M_MODE_FIFO) -+ spi_qup_fifo_write(qup, xfer); - - return 0; - } -@@ -448,6 +451,7 @@ spi_qup_get_mode(struct spi_master *mast - { - struct spi_qup *qup = spi_master_get_devdata(master); - u32 mode; -+ size_t dma_align = dma_get_cache_alignment(); - - qup->w_size = 4; - -@@ -458,6 +462,14 @@ spi_qup_get_mode(struct spi_master *mast - - qup->n_words = xfer->len / qup->w_size; - -+ if (!IS_ERR_OR_NULL(master->dma_rx) && -+ IS_ALIGNED((size_t)xfer->tx_buf, dma_align) && -+ IS_ALIGNED((size_t)xfer->rx_buf, dma_align) && -+ !is_vmalloc_addr(xfer->tx_buf) && -+ !is_vmalloc_addr(xfer->rx_buf) && -+ (xfer->len > 3*qup->in_blk_sz)) -+ qup->use_dma = 1; -+ - if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32))) - mode = QUP_IO_M_MODE_FIFO; - else -@@ -491,7 +503,7 @@ static int spi_qup_io_config(struct spi_ - return -EIO; - } - -- mode = spi_qup_get_mode(spi->master, xfer); -+ controller->mode = mode = spi_qup_get_mode(spi->master, xfer); - n_words = controller->n_words; - - if (mode == QUP_IO_M_MODE_FIFO) { -@@ -500,6 +512,7 @@ static int spi_qup_io_config(struct spi_ - /* must be zero for FIFO */ - writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); - writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); -+ controller->use_dma = 0; - } else if (!controller->use_dma) { - writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); - writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); -@@ -750,6 +763,38 @@ err_tx: - return ret; - } - -+static void spi_qup_set_cs(struct spi_device *spi, bool val) -+{ -+ struct spi_qup *controller; -+ u32 spi_ioc; -+ u32 spi_ioc_orig; -+ -+ controller = spi_master_get_devdata(spi->master); -+ spi_ioc = readl_relaxed(controller->base + SPI_IO_CONTROL); -+ spi_ioc_orig = spi_ioc; -+ if (!val) -+ spi_ioc |= SPI_IO_C_FORCE_CS; -+ else -+ spi_ioc &= ~SPI_IO_C_FORCE_CS; -+ -+ if (spi_ioc != spi_ioc_orig) -+ writel_relaxed(spi_ioc, controller->base + SPI_IO_CONTROL); -+} -+ -+static int spi_qup_setup(struct spi_device *spi) -+{ -+ if (spi->cs_gpio >= 0) { -+ if (spi->mode & SPI_CS_HIGH) -+ gpio_set_value(spi->cs_gpio, 0); -+ else -+ gpio_set_value(spi->cs_gpio, 1); -+ -+ udelay(10); -+ } -+ -+ return 0; -+} -+ - static int spi_qup_probe(struct platform_device *pdev) - { - struct spi_master *master; -@@ -846,6 +891,11 @@ static int spi_qup_probe(struct platform - if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1")) - controller->qup_v1 = 1; - -+ if (!controller->qup_v1) -+ master->set_cs = spi_qup_set_cs; -+ else -+ master->setup = spi_qup_setup; -+ - spin_lock_init(&controller->lock); - init_completion(&controller->done); - diff --git a/target/linux/ipq806x/patches-4.4/710-spi-qup-Make-sure-mode-is-only-determined-once.patch b/target/linux/ipq806x/patches-4.4/710-spi-qup-Make-sure-mode-is-only-determined-once.patch deleted file mode 100644 index 338c3a0c2b..0000000000 --- a/target/linux/ipq806x/patches-4.4/710-spi-qup-Make-sure-mode-is-only-determined-once.patch +++ /dev/null @@ -1,220 +0,0 @@ -From 93f99afbc534e00d72d58336061823055ee820f1 Mon Sep 17 00:00:00 2001 -From: Andy Gross -Date: Tue, 12 Apr 2016 09:11:47 -0500 -Subject: [PATCH] spi: qup: Make sure mode is only determined once - -This patch calculates the mode once. All decisions on the current -transaction -is made using the mode instead of use_dma - -Signed-off-by: Andy Gross - -Change-Id: If3cdd924355e037d77dc8201a72895fac0461aa5 ---- - drivers/spi/spi-qup.c | 96 +++++++++++++++++++-------------------------------- - 1 file changed, 36 insertions(+), 60 deletions(-) - ---- a/drivers/spi/spi-qup.c -+++ b/drivers/spi/spi-qup.c -@@ -150,13 +150,20 @@ struct spi_qup { - int rx_bytes; - int qup_v1; - -- int use_dma; -+ int mode; - struct dma_slave_config rx_conf; - struct dma_slave_config tx_conf; -- int mode; - }; - - -+static inline bool spi_qup_is_dma_xfer(int mode) -+{ -+ if (mode == QUP_IO_M_MODE_DMOV || mode == QUP_IO_M_MODE_BAM) -+ return true; -+ -+ return false; -+} -+ - static inline bool spi_qup_is_valid_state(struct spi_qup *controller) - { - u32 opstate = readl_relaxed(controller->base + QUP_STATE); -@@ -427,7 +434,7 @@ static irqreturn_t spi_qup_qup_irq(int i - error = -EIO; - } - -- if (!controller->use_dma) { -+ if (!spi_qup_is_dma_xfer(controller->mode)) { - if (opflags & QUP_OP_IN_SERVICE_FLAG) - spi_qup_fifo_read(controller, xfer); - -@@ -446,43 +453,11 @@ static irqreturn_t spi_qup_qup_irq(int i - return IRQ_HANDLED; - } - --static u32 --spi_qup_get_mode(struct spi_master *master, struct spi_transfer *xfer) --{ -- struct spi_qup *qup = spi_master_get_devdata(master); -- u32 mode; -- size_t dma_align = dma_get_cache_alignment(); -- -- qup->w_size = 4; -- -- if (xfer->bits_per_word <= 8) -- qup->w_size = 1; -- else if (xfer->bits_per_word <= 16) -- qup->w_size = 2; -- -- qup->n_words = xfer->len / qup->w_size; -- -- if (!IS_ERR_OR_NULL(master->dma_rx) && -- IS_ALIGNED((size_t)xfer->tx_buf, dma_align) && -- IS_ALIGNED((size_t)xfer->rx_buf, dma_align) && -- !is_vmalloc_addr(xfer->tx_buf) && -- !is_vmalloc_addr(xfer->rx_buf) && -- (xfer->len > 3*qup->in_blk_sz)) -- qup->use_dma = 1; -- -- if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32))) -- mode = QUP_IO_M_MODE_FIFO; -- else -- mode = QUP_IO_M_MODE_BLOCK; -- -- return mode; --} -- - /* set clock freq ... bits per word */ - static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) - { - struct spi_qup *controller = spi_master_get_devdata(spi->master); -- u32 config, iomode, mode, control; -+ u32 config, iomode, control; - int ret, n_words; - - if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { -@@ -503,24 +478,22 @@ static int spi_qup_io_config(struct spi_ - return -EIO; - } - -- controller->mode = mode = spi_qup_get_mode(spi->master, xfer); -+ controller->w_size = DIV_ROUND_UP(xfer->bits_per_word, 8); -+ controller->n_words = xfer->len / controller->w_size; - n_words = controller->n_words; - -- if (mode == QUP_IO_M_MODE_FIFO) { -+ if (n_words <= (controller->in_fifo_sz / sizeof(u32))) { -+ controller->mode = QUP_IO_M_MODE_FIFO; - writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT); - writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT); - /* must be zero for FIFO */ - writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); - writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); - controller->use_dma = 0; -- } else if (!controller->use_dma) { -- writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); -- writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); -- /* must be zero for BLOCK and BAM */ -- writel_relaxed(0, controller->base + QUP_MX_READ_CNT); -- writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); -- } else { -- mode = QUP_IO_M_MODE_BAM; -+ } else if (spi->master->can_dma && -+ spi->master->can_dma(spi->master, spi, xfer) && -+ spi->master->cur_msg_mapped) { -+ controller->mode = QUP_IO_M_MODE_BAM; - writel_relaxed(0, controller->base + QUP_MX_READ_CNT); - writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); - -@@ -541,19 +514,26 @@ static int spi_qup_io_config(struct spi_ - - writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); - } -+ } else { -+ controller->mode = QUP_IO_M_MODE_BLOCK; -+ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); -+ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); -+ /* must be zero for BLOCK and BAM */ -+ writel_relaxed(0, controller->base + QUP_MX_READ_CNT); -+ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); - } - - iomode = readl_relaxed(controller->base + QUP_IO_M_MODES); - /* Set input and output transfer mode */ - iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK); - -- if (!controller->use_dma) -+ if (!spi_qup_is_dma_xfer(controller->mode)) - iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN); - else - iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN; - -- iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); -- iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); -+ iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); -+ iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); - - writel_relaxed(iomode, controller->base + QUP_IO_M_MODES); - -@@ -594,7 +574,7 @@ static int spi_qup_io_config(struct spi_ - config |= xfer->bits_per_word - 1; - config |= QUP_CONFIG_SPI_MODE; - -- if (controller->use_dma) { -+ if (spi_qup_is_dma_xfer(controller->mode)) { - if (!xfer->tx_buf) - config |= QUP_CONFIG_NO_OUTPUT; - if (!xfer->rx_buf) -@@ -612,7 +592,7 @@ static int spi_qup_io_config(struct spi_ - * status change in BAM mode - */ - -- if (mode == QUP_IO_M_MODE_BAM) -+ if (spi_qup_is_dma_xfer(controller->mode)) - mask = QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG; - - writel_relaxed(mask, controller->base + QUP_OPERATIONAL_MASK); -@@ -646,7 +626,7 @@ static int spi_qup_transfer_one(struct s - controller->tx_bytes = 0; - spin_unlock_irqrestore(&controller->lock, flags); - -- if (controller->use_dma) -+ if (spi_qup_is_dma_xfer(controller->mode)) - ret = spi_qup_do_dma(master, xfer); - else - ret = spi_qup_do_pio(master, xfer); -@@ -670,7 +650,7 @@ exit: - ret = controller->error; - spin_unlock_irqrestore(&controller->lock, flags); - -- if (ret && controller->use_dma) -+ if (ret && spi_qup_is_dma_xfer(controller->mode)) - spi_qup_dma_terminate(master, xfer); - - return ret; -@@ -681,9 +661,7 @@ static bool spi_qup_can_dma(struct spi_m - { - struct spi_qup *qup = spi_master_get_devdata(master); - size_t dma_align = dma_get_cache_alignment(); -- u32 mode; -- -- qup->use_dma = 0; -+ int n_words; - - if (xfer->rx_buf && (xfer->len % qup->in_blk_sz || - IS_ERR_OR_NULL(master->dma_rx) || -@@ -695,12 +673,10 @@ static bool spi_qup_can_dma(struct spi_m - !IS_ALIGNED((size_t)xfer->tx_buf, dma_align))) - return false; - -- mode = spi_qup_get_mode(master, xfer); -- if (mode == QUP_IO_M_MODE_FIFO) -+ n_words = xfer->len / DIV_ROUND_UP(xfer->bits_per_word, 8); -+ if (n_words <= (qup->in_fifo_sz / sizeof(u32))) - return false; - -- qup->use_dma = 1; -- - return true; - } - diff --git a/target/linux/ipq806x/patches-4.4/711-spi-qup-Fix-transaction-done-signaling.patch b/target/linux/ipq806x/patches-4.4/711-spi-qup-Fix-transaction-done-signaling.patch deleted file mode 100644 index 99f455ef3c..0000000000 --- a/target/linux/ipq806x/patches-4.4/711-spi-qup-Fix-transaction-done-signaling.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 8e830bd17e945e74964a5b61353d74e34c0791cd Mon Sep 17 00:00:00 2001 -From: Andy Gross -Date: Fri, 29 Jan 2016 22:06:50 -0600 -Subject: [PATCH] spi: qup: Fix transaction done signaling - -Wait to signal done until we get all of the interrupts we are expecting -to get for a transaction. If we don't wait for the input done flag, we -can be inbetween transactions when the done flag comes in and this can -mess up the next transaction. - -Change-Id: I08d78376e71590663158d6434a3fb7c0623264c9 -CC: Grant Grundler -CC: Sarthak Kukreti -Signed-off-by: Andy Gross ---- - drivers/spi/spi-qup.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - ---- a/drivers/spi/spi-qup.c -+++ b/drivers/spi/spi-qup.c -@@ -447,7 +447,8 @@ static irqreturn_t spi_qup_qup_irq(int i - controller->xfer = xfer; - spin_unlock_irqrestore(&controller->lock, flags); - -- if (controller->rx_bytes == xfer->len || error) -+ if ((controller->rx_bytes == xfer->len && -+ (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error) - complete(&controller->done); - - return IRQ_HANDLED; diff --git a/target/linux/ipq806x/patches-4.4/711-stmmac-fix-ipq806x-DMA-configuration.patch b/target/linux/ipq806x/patches-4.4/711-stmmac-fix-ipq806x-DMA-configuration.patch deleted file mode 100644 index 58400314c7..0000000000 --- a/target/linux/ipq806x/patches-4.4/711-stmmac-fix-ipq806x-DMA-configuration.patch +++ /dev/null @@ -1,117 +0,0 @@ ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c -@@ -259,6 +259,7 @@ static int ipq806x_gmac_probe(struct pla - { - struct plat_stmmacenet_data *plat_dat; - struct stmmac_resources stmmac_res; -+ struct stmmac_dma_cfg *dma_cfg; - struct device *dev = &pdev->dev; - struct ipq806x_gmac *gmac; - int val; -@@ -348,6 +349,17 @@ static int ipq806x_gmac_probe(struct pla - plat_dat->bsp_priv = gmac; - plat_dat->fix_mac_speed = ipq806x_gmac_fix_mac_speed; - -+ dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), -+ GFP_KERNEL); -+ -+ dma_cfg->pbl = 32; -+ dma_cfg->aal = 1; -+ dma_cfg->burst_len = DMA_AXI_BLEN_16 | -+ (7 << DMA_AXI_RD_OSR_LMT_SHIFT) | -+ (7 << DMA_AXI_WR_OSR_LMT_SHIFT); -+ -+ plat_dat->dma_cfg = dma_cfg; -+ - return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); - } - ---- a/include/linux/stmmac.h -+++ b/include/linux/stmmac.h -@@ -73,6 +73,9 @@ - | DMA_AXI_BLEN_32 | DMA_AXI_BLEN_64 \ - | DMA_AXI_BLEN_128 | DMA_AXI_BLEN_256) - -+#define DMA_AXI_RD_OSR_LMT_SHIFT 16 -+#define DMA_AXI_WR_OSR_LMT_SHIFT 20 -+ - /* Platfrom data for platform device structure's platform_data field */ - - struct stmmac_mdio_bus_data { -@@ -88,6 +91,7 @@ struct stmmac_mdio_bus_data { - - struct stmmac_dma_cfg { - int pbl; -+ int aal; - int fixed_burst; - int mixed_burst; - int burst_len; ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c -@@ -31,7 +31,8 @@ - #include "dwmac_dma.h" - - static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb, -- int burst_len, u32 dma_tx, u32 dma_rx, int atds) -+ int burst_len, u32 dma_tx, u32 dma_rx, int atds, -+ int aal) - { - u32 value = readl(ioaddr + DMA_BUS_MODE); - int limit; -@@ -62,6 +63,10 @@ static int dwmac1000_dma_init(void __iom - value = DMA_BUS_MODE_PBL | ((pbl << DMA_BUS_MODE_PBL_SHIFT) | - (pbl << DMA_BUS_MODE_RPBL_SHIFT)); - -+ /* Address Aligned Beats */ -+ if (aal) -+ value |= DMA_BUS_MODE_AAL; -+ - /* Set the Fixed burst mode */ - if (fb) - value |= DMA_BUS_MODE_FB; ---- a/drivers/net/ethernet/stmicro/stmmac/common.h -+++ b/drivers/net/ethernet/stmicro/stmmac/common.h -@@ -352,7 +352,7 @@ extern const struct stmmac_desc_ops ndes - struct stmmac_dma_ops { - /* DMA core initialization */ - int (*init) (void __iomem *ioaddr, int pbl, int fb, int mb, -- int burst_len, u32 dma_tx, u32 dma_rx, int atds); -+ int burst_len, u32 dma_tx, u32 dma_rx, int atds, int aal); - /* Dump DMA registers */ - void (*dump_regs) (void __iomem *ioaddr); - /* Set tx/rx threshold in the csr6 register ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c -@@ -33,7 +33,8 @@ - #include "dwmac_dma.h" - - static int dwmac100_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb, -- int burst_len, u32 dma_tx, u32 dma_rx, int atds) -+ int burst_len, u32 dma_tx, u32 dma_rx, int atds, -+ int aal) - { - u32 value = readl(ioaddr + DMA_BUS_MODE); - int limit; ---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c -@@ -1641,9 +1641,11 @@ static int stmmac_init_dma_engine(struct - int pbl = DEFAULT_DMA_PBL, fixed_burst = 0, burst_len = 0; - int mixed_burst = 0; - int atds = 0; -+ int aal = 0; - - if (priv->plat->dma_cfg) { - pbl = priv->plat->dma_cfg->pbl; -+ aal = priv->plat->dma_cfg->aal; - fixed_burst = priv->plat->dma_cfg->fixed_burst; - mixed_burst = priv->plat->dma_cfg->mixed_burst; - burst_len = priv->plat->dma_cfg->burst_len; -@@ -1654,7 +1656,7 @@ static int stmmac_init_dma_engine(struct - - return priv->hw->dma->init(priv->ioaddr, pbl, fixed_burst, mixed_burst, - burst_len, priv->dma_tx_phy, -- priv->dma_rx_phy, atds); -+ priv->dma_rx_phy, atds, aal); - } - - /** diff --git a/target/linux/ipq806x/patches-4.4/712-spi-qup-Fix-DMA-mode-to-work-correctly.patch b/target/linux/ipq806x/patches-4.4/712-spi-qup-Fix-DMA-mode-to-work-correctly.patch deleted file mode 100644 index 9c7bbeabda..0000000000 --- a/target/linux/ipq806x/patches-4.4/712-spi-qup-Fix-DMA-mode-to-work-correctly.patch +++ /dev/null @@ -1,221 +0,0 @@ -From ed56e6322b067a898a25bda1774eb1180a832246 Mon Sep 17 00:00:00 2001 -From: Andy Gross -Date: Tue, 2 Feb 2016 17:00:53 -0600 -Subject: [PATCH] spi: qup: Fix DMA mode to work correctly - -This patch fixes a few issues with the DMA mode. The QUP needs to be -placed in the run mode before the DMA transactions are executed. The -conditions for being able to DMA vary between revisions of the QUP. -This is due to v1.1.1 using ADM DMA and later revisions using BAM. - -Change-Id: Ib1f876eaa05d079e0bca4358d2b25b2940986089 -Signed-off-by: Andy Gross ---- - drivers/spi/spi-qup.c | 95 ++++++++++++++++++++++++++++++++++----------------- - 1 file changed, 63 insertions(+), 32 deletions(-) - ---- a/drivers/spi/spi-qup.c -+++ b/drivers/spi/spi-qup.c -@@ -143,6 +143,7 @@ struct spi_qup { - - struct spi_transfer *xfer; - struct completion done; -+ struct completion dma_tx_done; - int error; - int w_size; /* bytes per SPI word */ - int n_words; -@@ -285,16 +286,16 @@ static void spi_qup_fifo_write(struct sp - - static void spi_qup_dma_done(void *data) - { -- struct spi_qup *qup = data; -+ struct completion *done = data; - -- complete(&qup->done); -+ complete(done); - } - - static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer, - enum dma_transfer_direction dir, -- dma_async_tx_callback callback) -+ dma_async_tx_callback callback, -+ void *data) - { -- struct spi_qup *qup = spi_master_get_devdata(master); - unsigned long flags = DMA_PREP_INTERRUPT | DMA_PREP_FENCE; - struct dma_async_tx_descriptor *desc; - struct scatterlist *sgl; -@@ -313,11 +314,11 @@ static int spi_qup_prep_sg(struct spi_ma - } - - desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags); -- if (!desc) -- return -EINVAL; -+ if (IS_ERR_OR_NULL(desc)) -+ return desc ? PTR_ERR(desc) : -EINVAL; - - desc->callback = callback; -- desc->callback_param = qup; -+ desc->callback_param = data; - - cookie = dmaengine_submit(desc); - -@@ -333,18 +334,29 @@ static void spi_qup_dma_terminate(struct - dmaengine_terminate_all(master->dma_rx); - } - --static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer) -+static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer, -+unsigned long timeout) - { -+ struct spi_qup *qup = spi_master_get_devdata(master); - dma_async_tx_callback rx_done = NULL, tx_done = NULL; - int ret; - -+ /* before issuing the descriptors, set the QUP to run */ -+ ret = spi_qup_set_state(qup, QUP_STATE_RUN); -+ if (ret) { -+ dev_warn(qup->dev, "cannot set RUN state\n"); -+ return ret; -+ } -+ - if (xfer->rx_buf) - rx_done = spi_qup_dma_done; -- else if (xfer->tx_buf) -+ -+ if (xfer->tx_buf) - tx_done = spi_qup_dma_done; - - if (xfer->rx_buf) { -- ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done); -+ ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done, -+ &qup->done); - if (ret) - return ret; - -@@ -352,17 +364,26 @@ static int spi_qup_do_dma(struct spi_mas - } - - if (xfer->tx_buf) { -- ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done); -+ ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done, -+ &qup->dma_tx_done); - if (ret) - return ret; - - dma_async_issue_pending(master->dma_tx); - } - -- return 0; -+ if (xfer->rx_buf && !wait_for_completion_timeout(&qup->done, timeout)) -+ return -ETIMEDOUT; -+ -+ if (xfer->tx_buf && -+ !wait_for_completion_timeout(&qup->dma_tx_done, timeout)) -+ ret = -ETIMEDOUT; -+ -+ return ret; - } - --static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer) -+static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer, -+ unsigned long timeout) - { - struct spi_qup *qup = spi_master_get_devdata(master); - int ret; -@@ -382,6 +403,15 @@ static int spi_qup_do_pio(struct spi_mas - if (qup->mode == QUP_IO_M_MODE_FIFO) - spi_qup_fifo_write(qup, xfer); - -+ ret = spi_qup_set_state(qup, QUP_STATE_RUN); -+ if (ret) { -+ dev_warn(qup->dev, "cannot set RUN state\n"); -+ return ret; -+ } -+ -+ if (!wait_for_completion_timeout(&qup->done, timeout)) -+ return -ETIMEDOUT; -+ - return 0; - } - -@@ -430,7 +460,6 @@ static irqreturn_t spi_qup_qup_irq(int i - dev_warn(controller->dev, "CLK_OVER_RUN\n"); - if (spi_err & SPI_ERROR_CLK_UNDER_RUN) - dev_warn(controller->dev, "CLK_UNDER_RUN\n"); -- - error = -EIO; - } - -@@ -619,6 +648,7 @@ static int spi_qup_transfer_one(struct s - timeout = 100 * msecs_to_jiffies(timeout); - - reinit_completion(&controller->done); -+ reinit_completion(&controller->dma_tx_done); - - spin_lock_irqsave(&controller->lock, flags); - controller->xfer = xfer; -@@ -628,21 +658,13 @@ static int spi_qup_transfer_one(struct s - spin_unlock_irqrestore(&controller->lock, flags); - - if (spi_qup_is_dma_xfer(controller->mode)) -- ret = spi_qup_do_dma(master, xfer); -+ ret = spi_qup_do_dma(master, xfer, timeout); - else -- ret = spi_qup_do_pio(master, xfer); -+ ret = spi_qup_do_pio(master, xfer, timeout); - - if (ret) - goto exit; - -- if (spi_qup_set_state(controller, QUP_STATE_RUN)) { -- dev_warn(controller->dev, "cannot set EXECUTE state\n"); -- goto exit; -- } -- -- if (!wait_for_completion_timeout(&controller->done, timeout)) -- ret = -ETIMEDOUT; -- - exit: - spi_qup_set_state(controller, QUP_STATE_RESET); - spin_lock_irqsave(&controller->lock, flags); -@@ -664,15 +686,23 @@ static bool spi_qup_can_dma(struct spi_m - size_t dma_align = dma_get_cache_alignment(); - int n_words; - -- if (xfer->rx_buf && (xfer->len % qup->in_blk_sz || -- IS_ERR_OR_NULL(master->dma_rx) || -- !IS_ALIGNED((size_t)xfer->rx_buf, dma_align))) -- return false; -+ if (xfer->rx_buf) { -+ if (!IS_ALIGNED((size_t)xfer->rx_buf, dma_align) || -+ IS_ERR_OR_NULL(master->dma_rx)) -+ return false; - -- if (xfer->tx_buf && (xfer->len % qup->out_blk_sz || -- IS_ERR_OR_NULL(master->dma_tx) || -- !IS_ALIGNED((size_t)xfer->tx_buf, dma_align))) -- return false; -+ if (qup->qup_v1 && (xfer->len % qup->in_blk_sz)) -+ return false; -+ } -+ -+ if (xfer->tx_buf) { -+ if (!IS_ALIGNED((size_t)xfer->tx_buf, dma_align) || -+ IS_ERR_OR_NULL(master->dma_tx)) -+ return false; -+ -+ if (qup->qup_v1 && (xfer->len % qup->out_blk_sz)) -+ return false; -+ } - - n_words = xfer->len / DIV_ROUND_UP(xfer->bits_per_word, 8); - if (n_words <= (qup->in_fifo_sz / sizeof(u32))) -@@ -875,6 +905,7 @@ static int spi_qup_probe(struct platform - - spin_lock_init(&controller->lock); - init_completion(&controller->done); -+ init_completion(&controller->dma_tx_done); - - iomode = readl_relaxed(base + QUP_IO_M_MODES); - diff --git a/target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch b/target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch deleted file mode 100644 index eb0b45cd1c..0000000000 --- a/target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch +++ /dev/null @@ -1,312 +0,0 @@ -From 148f77310a9ddf4db5036066458d7aed92cea9ae Mon Sep 17 00:00:00 2001 -From: Andy Gross -Date: Sun, 31 Jan 2016 21:28:13 -0600 -Subject: [PATCH] spi: qup: Fix block mode to work correctly - -This patch corrects the behavior of the BLOCK -transactions. During block transactions, the controller -must be read/written to in block size transactions. - -Signed-off-by: Andy Gross - -Change-Id: I4b4f4d25be57e6e8148f6f0d24bed376eb287ecf ---- - drivers/spi/spi-qup.c | 181 +++++++++++++++++++++++++++++++++++++++----------- - 1 file changed, 141 insertions(+), 40 deletions(-) - ---- a/drivers/spi/spi-qup.c -+++ b/drivers/spi/spi-qup.c -@@ -83,6 +83,8 @@ - #define QUP_IO_M_MODE_BAM 3 - - /* QUP_OPERATIONAL fields */ -+#define QUP_OP_IN_BLOCK_READ_REQ BIT(13) -+#define QUP_OP_OUT_BLOCK_WRITE_REQ BIT(12) - #define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11) - #define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10) - #define QUP_OP_IN_SERVICE_FLAG BIT(9) -@@ -156,6 +158,12 @@ struct spi_qup { - struct dma_slave_config tx_conf; - }; - -+static inline bool spi_qup_is_flag_set(struct spi_qup *controller, u32 flag) -+{ -+ u32 opflag = readl_relaxed(controller->base + QUP_OPERATIONAL); -+ -+ return opflag & flag; -+} - - static inline bool spi_qup_is_dma_xfer(int mode) - { -@@ -217,29 +225,26 @@ static int spi_qup_set_state(struct spi_ - return 0; - } - --static void spi_qup_fifo_read(struct spi_qup *controller, -- struct spi_transfer *xfer) -+static void spi_qup_read_from_fifo(struct spi_qup *controller, -+ struct spi_transfer *xfer, u32 num_words) - { - u8 *rx_buf = xfer->rx_buf; -- u32 word, state; -- int idx, shift, w_size; -- -- w_size = controller->w_size; -+ int i, shift, num_bytes; -+ u32 word; - -- while (controller->rx_bytes < xfer->len) { -- -- state = readl_relaxed(controller->base + QUP_OPERATIONAL); -- if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY)) -- break; -+ for (; num_words; num_words--) { - - word = readl_relaxed(controller->base + QUP_INPUT_FIFO); - -+ num_bytes = min_t(int, xfer->len - controller->rx_bytes, -+ controller->w_size); -+ - if (!rx_buf) { -- controller->rx_bytes += w_size; -+ controller->rx_bytes += num_bytes; - continue; - } - -- for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) { -+ for (i = 0; i < num_bytes; i++, controller->rx_bytes++) { - /* - * The data format depends on bytes per SPI word: - * 4 bytes: 0x12345678 -@@ -247,38 +252,80 @@ static void spi_qup_fifo_read(struct spi - * 1 byte : 0x00000012 - */ - shift = BITS_PER_BYTE; -- shift *= (w_size - idx - 1); -+ shift *= (controller->w_size - i - 1); - rx_buf[controller->rx_bytes] = word >> shift; - } - } - } - --static void spi_qup_fifo_write(struct spi_qup *controller, -+static void spi_qup_read(struct spi_qup *controller, - struct spi_transfer *xfer) - { -- const u8 *tx_buf = xfer->tx_buf; -- u32 word, state, data; -- int idx, w_size; -+ u32 remainder, words_per_block, num_words; -+ bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK; -+ -+ remainder = DIV_ROUND_UP(xfer->len - controller->rx_bytes, -+ controller->w_size); -+ words_per_block = controller->in_blk_sz >> 2; -+ -+ do { -+ /* ACK by clearing service flag */ -+ writel_relaxed(QUP_OP_IN_SERVICE_FLAG, -+ controller->base + QUP_OPERATIONAL); -+ -+ if (is_block_mode) { -+ num_words = (remainder > words_per_block) ? -+ words_per_block : remainder; -+ } else { -+ if (!spi_qup_is_flag_set(controller, -+ QUP_OP_IN_FIFO_NOT_EMPTY)) -+ break; - -- w_size = controller->w_size; -+ num_words = 1; -+ } -+ -+ /* read up to the maximum transfer size available */ -+ spi_qup_read_from_fifo(controller, xfer, num_words); - -- while (controller->tx_bytes < xfer->len) { -+ remainder -= num_words; - -- state = readl_relaxed(controller->base + QUP_OPERATIONAL); -- if (state & QUP_OP_OUT_FIFO_FULL) -+ /* if block mode, check to see if next block is available */ -+ if (is_block_mode && !spi_qup_is_flag_set(controller, -+ QUP_OP_IN_BLOCK_READ_REQ)) - break; - -+ } while (remainder); -+ -+ /* -+ * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block -+ * mode reads, it has to be cleared again at the very end -+ */ -+ if (is_block_mode && spi_qup_is_flag_set(controller, -+ QUP_OP_MAX_INPUT_DONE_FLAG)) -+ writel_relaxed(QUP_OP_IN_SERVICE_FLAG, -+ controller->base + QUP_OPERATIONAL); -+ -+} -+ -+static void spi_qup_write_to_fifo(struct spi_qup *controller, -+ struct spi_transfer *xfer, u32 num_words) -+{ -+ const u8 *tx_buf = xfer->tx_buf; -+ int i, num_bytes; -+ u32 word, data; -+ -+ for (; num_words; num_words--) { - word = 0; -- for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) { - -- if (!tx_buf) { -- controller->tx_bytes += w_size; -- break; -+ num_bytes = min_t(int, xfer->len - controller->tx_bytes, -+ controller->w_size); -+ if (tx_buf) -+ for (i = 0; i < num_bytes; i++) { -+ data = tx_buf[controller->tx_bytes + i]; -+ word |= data << (BITS_PER_BYTE * (3 - i)); - } - -- data = tx_buf[controller->tx_bytes]; -- word |= data << (BITS_PER_BYTE * (3 - idx)); -- } -+ controller->tx_bytes += num_bytes; - - writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO); - } -@@ -291,6 +338,44 @@ static void spi_qup_dma_done(void *data) - complete(done); - } - -+static void spi_qup_write(struct spi_qup *controller, -+ struct spi_transfer *xfer) -+{ -+ bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK; -+ u32 remainder, words_per_block, num_words; -+ -+ remainder = DIV_ROUND_UP(xfer->len - controller->tx_bytes, -+ controller->w_size); -+ words_per_block = controller->out_blk_sz >> 2; -+ -+ do { -+ /* ACK by clearing service flag */ -+ writel_relaxed(QUP_OP_OUT_SERVICE_FLAG, -+ controller->base + QUP_OPERATIONAL); -+ -+ if (is_block_mode) { -+ num_words = (remainder > words_per_block) ? -+ words_per_block : remainder; -+ } else { -+ if (spi_qup_is_flag_set(controller, -+ QUP_OP_OUT_FIFO_FULL)) -+ break; -+ -+ num_words = 1; -+ } -+ -+ spi_qup_write_to_fifo(controller, xfer, num_words); -+ -+ remainder -= num_words; -+ -+ /* if block mode, check to see if next block is available */ -+ if (is_block_mode && !spi_qup_is_flag_set(controller, -+ QUP_OP_OUT_BLOCK_WRITE_REQ)) -+ break; -+ -+ } while (remainder); -+} -+ - static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer, - enum dma_transfer_direction dir, - dma_async_tx_callback callback, -@@ -348,11 +433,13 @@ unsigned long timeout) - return ret; - } - -- if (xfer->rx_buf) -- rx_done = spi_qup_dma_done; -+ if (!qup->qup_v1) { -+ if (xfer->rx_buf) -+ rx_done = spi_qup_dma_done; - -- if (xfer->tx_buf) -- tx_done = spi_qup_dma_done; -+ if (xfer->tx_buf) -+ tx_done = spi_qup_dma_done; -+ } - - if (xfer->rx_buf) { - ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done, -@@ -401,7 +488,7 @@ static int spi_qup_do_pio(struct spi_mas - } - - if (qup->mode == QUP_IO_M_MODE_FIFO) -- spi_qup_fifo_write(qup, xfer); -+ spi_qup_write(qup, xfer); - - ret = spi_qup_set_state(qup, QUP_STATE_RUN); - if (ret) { -@@ -434,10 +521,11 @@ static irqreturn_t spi_qup_qup_irq(int i - - writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS); - writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS); -- writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); - - if (!xfer) { -- dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n", -+ writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); -+ dev_err_ratelimited(controller->dev, -+ "unexpected irq %08x %08x %08x\n", - qup_err, spi_err, opflags); - return IRQ_HANDLED; - } -@@ -463,12 +551,20 @@ static irqreturn_t spi_qup_qup_irq(int i - error = -EIO; - } - -- if (!spi_qup_is_dma_xfer(controller->mode)) { -+ if (spi_qup_is_dma_xfer(controller->mode)) { -+ writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); -+ if (opflags & QUP_OP_IN_SERVICE_FLAG && -+ opflags & QUP_OP_MAX_INPUT_DONE_FLAG) -+ complete(&controller->done); -+ if (opflags & QUP_OP_OUT_SERVICE_FLAG && -+ opflags & QUP_OP_MAX_OUTPUT_DONE_FLAG) -+ complete(&controller->dma_tx_done); -+ } else { - if (opflags & QUP_OP_IN_SERVICE_FLAG) -- spi_qup_fifo_read(controller, xfer); -+ spi_qup_read(controller, xfer); - - if (opflags & QUP_OP_OUT_SERVICE_FLAG) -- spi_qup_fifo_write(controller, xfer); -+ spi_qup_write(controller, xfer); - } - - spin_lock_irqsave(&controller->lock, flags); -@@ -476,6 +572,9 @@ static irqreturn_t spi_qup_qup_irq(int i - controller->xfer = xfer; - spin_unlock_irqrestore(&controller->lock, flags); - -+ /* re-read opflags as flags may have changed due to actions above */ -+ opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); -+ - if ((controller->rx_bytes == xfer->len && - (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error) - complete(&controller->done); -@@ -519,11 +618,13 @@ static int spi_qup_io_config(struct spi_ - /* must be zero for FIFO */ - writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); - writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); -- controller->use_dma = 0; - } else if (spi->master->can_dma && - spi->master->can_dma(spi->master, spi, xfer) && - spi->master->cur_msg_mapped) { - controller->mode = QUP_IO_M_MODE_BAM; -+ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); -+ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); -+ /* must be zero for BLOCK and BAM */ - writel_relaxed(0, controller->base + QUP_MX_READ_CNT); - writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); - diff --git a/target/linux/ipq806x/patches-4.4/714-spi-qup-properly-detect-extra-interrupts.patch b/target/linux/ipq806x/patches-4.4/714-spi-qup-properly-detect-extra-interrupts.patch deleted file mode 100644 index 0039962481..0000000000 --- a/target/linux/ipq806x/patches-4.4/714-spi-qup-properly-detect-extra-interrupts.patch +++ /dev/null @@ -1,62 +0,0 @@ -From b69e5e855aaae2dd9f7fc6f4a40af8e6e0cf98ee Mon Sep 17 00:00:00 2001 -From: Matthew McClintock -Date: Thu, 10 Mar 2016 16:44:55 -0600 -Subject: [PATCH] spi: qup: properly detect extra interrupts - -It's possible for a SPI transaction to complete and get another -interrupt and have it processed on the same spi_transfer before the -transfer_one can set it to NULL. - -This masks unexpected interrupts, so let's set the spi_transfer to -NULL in the interrupt once the transaction is done. So we can -properly detect these bad interrupts and print warning messages. - -Change-Id: I0e70ed59fb50e5c48a72a38f74bd178b17c9f24d -Signed-off-by: Matthew McClintock ---- - drivers/spi/spi-qup.c | 15 +++++++++------ - 1 file changed, 9 insertions(+), 6 deletions(-) - ---- a/drivers/spi/spi-qup.c -+++ b/drivers/spi/spi-qup.c -@@ -509,6 +509,7 @@ static irqreturn_t spi_qup_qup_irq(int i - u32 opflags, qup_err, spi_err; - unsigned long flags; - int error = 0; -+ bool done = 0; - - spin_lock_irqsave(&controller->lock, flags); - xfer = controller->xfer; -@@ -567,16 +568,19 @@ static irqreturn_t spi_qup_qup_irq(int i - spi_qup_write(controller, xfer); - } - -- spin_lock_irqsave(&controller->lock, flags); -- controller->error = error; -- controller->xfer = xfer; -- spin_unlock_irqrestore(&controller->lock, flags); -- - /* re-read opflags as flags may have changed due to actions above */ - opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); - - if ((controller->rx_bytes == xfer->len && - (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error) -+ done = true; -+ -+ spin_lock_irqsave(&controller->lock, flags); -+ controller->error = error; -+ controller->xfer = done ? NULL : xfer; -+ spin_unlock_irqrestore(&controller->lock, flags); -+ -+ if (done) - complete(&controller->done); - - return IRQ_HANDLED; -@@ -769,7 +773,6 @@ static int spi_qup_transfer_one(struct s - exit: - spi_qup_set_state(controller, QUP_STATE_RESET); - spin_lock_irqsave(&controller->lock, flags); -- controller->xfer = NULL; - if (!ret) - ret = controller->error; - spin_unlock_irqrestore(&controller->lock, flags); diff --git a/target/linux/ipq806x/patches-4.4/715-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch b/target/linux/ipq806x/patches-4.4/715-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch deleted file mode 100644 index a08b442d50..0000000000 --- a/target/linux/ipq806x/patches-4.4/715-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch +++ /dev/null @@ -1,27 +0,0 @@ -From f57ff801665b868d8607c9e872466b54982564bc Mon Sep 17 00:00:00 2001 -From: Matthew McClintock -Date: Thu, 10 Mar 2016 16:48:27 -0600 -Subject: [PATCH] spi: qup: don't re-read opflags to see if transaction is done - for reads - -For reads, we will get another interrupt so we need to handle things -then. For writes, we can finish up now. - -Change-Id: I4fa95ae7bb9d78f8ba54c613b981b37d4ea81d7e -Signed-off-by: Matthew McClintock ---- - drivers/spi/spi-qup.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - ---- a/drivers/spi/spi-qup.c -+++ b/drivers/spi/spi-qup.c -@@ -569,7 +569,8 @@ static irqreturn_t spi_qup_qup_irq(int i - } - - /* re-read opflags as flags may have changed due to actions above */ -- opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); -+ if (opflags & QUP_OP_OUT_SERVICE_FLAG) -+ opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); - - if ((controller->rx_bytes == xfer->len && - (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error) diff --git a/target/linux/ipq806x/patches-4.4/800-devicetree.patch b/target/linux/ipq806x/patches-4.4/800-devicetree.patch deleted file mode 100644 index 916e2efd72..0000000000 --- a/target/linux/ipq806x/patches-4.4/800-devicetree.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 7e77aa188a7a7c4391856a9e5ef5ef58f769e679 Mon Sep 17 00:00:00 2001 -From: Jonas Gorski -Date: Sun, 9 Aug 2015 13:02:38 +0200 -Subject: [PATCH] ARM: qcom: add Netgear Nighthawk X4 R7500 device tree - -Signed-off-by: Jonas Gorski ---- - arch/arm/boot/dts/Makefile | 1 + - arch/arm/boot/dts/qcom-ipq8064-r7500.dts | 370 +++++++++++++++++++++++++++++++ - 2 files changed, 371 insertions(+) - create mode 100644 arch/arm/boot/dts/qcom-ipq8064-r7500.dts - ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -506,7 +506,14 @@ dtb-$(CONFIG_ARCH_QCOM) += \ - qcom-apq8084-ifc6540.dtb \ - qcom-apq8084-mtp.dtb \ - qcom-ipq8064-ap148.dtb \ -+ qcom-ipq8064-c2600.dtb \ -+ qcom-ipq8064-d7800.dtb \ - qcom-ipq8064-db149.dtb \ -+ qcom-ipq8064-ea8500.dtb \ -+ qcom-ipq8064-r7500.dtb \ -+ qcom-ipq8064-r7500v2.dtb \ -+ qcom-ipq8065-nbg6817.dtb \ -+ qcom-ipq8065-r7800.dtb \ - qcom-msm8660-surf.dtb \ - qcom-msm8960-cdp.dtb \ - qcom-msm8974-sony-xperia-honami.dtb diff --git a/target/linux/ipq806x/patches-4.4/801-override-compiler-flags.patch b/target/linux/ipq806x/patches-4.4/801-override-compiler-flags.patch deleted file mode 100644 index 5b419a75d8..0000000000 --- a/target/linux/ipq806x/patches-4.4/801-override-compiler-flags.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/arch/arm/Makefile -+++ b/arch/arm/Makefile -@@ -72,7 +72,7 @@ KBUILD_CFLAGS += $(call cc-option,-fno-i - # macro, but instead defines a whole series of macros which makes - # testing for a specific architecture or later rather impossible. - arch-$(CONFIG_CPU_32v7M) =-D__LINUX_ARM_ARCH__=7 -march=armv7-m -Wa,-march=armv7-m --arch-$(CONFIG_CPU_32v7) =-D__LINUX_ARM_ARCH__=7 $(call cc-option,-march=armv7-a,-march=armv5t -Wa$(comma)-march=armv7-a) -+arch-$(CONFIG_CPU_32v7) =-D__LINUX_ARM_ARCH__=7 -mcpu=cortex-a15 - arch-$(CONFIG_CPU_32v6) =-D__LINUX_ARM_ARCH__=6 $(call cc-option,-march=armv6,-march=armv5t -Wa$(comma)-march=armv6) - # Only override the compiler option if ARMv6. The ARMv6K extensions are - # always available in ARMv7 diff --git a/target/linux/ipq806x/patches-4.4/802-GPIO-add-named-gpio-exports.patch b/target/linux/ipq806x/patches-4.4/802-GPIO-add-named-gpio-exports.patch deleted file mode 100644 index cfdab760a5..0000000000 --- a/target/linux/ipq806x/patches-4.4/802-GPIO-add-named-gpio-exports.patch +++ /dev/null @@ -1,166 +0,0 @@ -From 4267880319bc1a2270d352e0ded6d6386242a7ef Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Tue, 12 Aug 2014 20:49:27 +0200 -Subject: [PATCH 24/53] GPIO: add named gpio exports - -Signed-off-by: John Crispin ---- - drivers/gpio/gpiolib-of.c | 68 +++++++++++++++++++++++++++++++++++++++++ - drivers/gpio/gpiolib-sysfs.c | 10 +++++- - include/asm-generic/gpio.h | 6 ++++ - include/linux/gpio/consumer.h | 8 +++++ - 4 files changed, 91 insertions(+), 1 deletion(-) - ---- a/drivers/gpio/gpiolib-of.c -+++ b/drivers/gpio/gpiolib-of.c -@@ -23,6 +23,8 @@ - #include - #include - #include -+#include -+#include - - #include "gpiolib.h" - -@@ -450,3 +452,69 @@ void of_gpiochip_remove(struct gpio_chip - gpiochip_remove_pin_ranges(chip); - of_node_put(chip->of_node); - } -+ -+static struct of_device_id gpio_export_ids[] = { -+ { .compatible = "gpio-export" }, -+ { /* sentinel */ } -+}; -+ -+static int __init of_gpio_export_probe(struct platform_device *pdev) -+{ -+ struct device_node *np = pdev->dev.of_node; -+ struct device_node *cnp; -+ u32 val; -+ int nb = 0; -+ -+ for_each_child_of_node(np, cnp) { -+ const char *name = NULL; -+ int gpio; -+ bool dmc; -+ int max_gpio = 1; -+ int i; -+ -+ of_property_read_string(cnp, "gpio-export,name", &name); -+ -+ if (!name) -+ max_gpio = of_gpio_count(cnp); -+ -+ for (i = 0; i < max_gpio; i++) { -+ unsigned flags = 0; -+ enum of_gpio_flags of_flags; -+ -+ gpio = of_get_gpio_flags(cnp, i, &of_flags); -+ -+ if (of_flags == OF_GPIO_ACTIVE_LOW) -+ flags |= GPIOF_ACTIVE_LOW; -+ -+ if (!of_property_read_u32(cnp, "gpio-export,output", &val)) -+ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; -+ else -+ flags |= GPIOF_IN; -+ -+ if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np))) -+ continue; -+ -+ dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change"); -+ gpio_export_with_name(gpio, dmc, name); -+ nb++; -+ } -+ } -+ -+ dev_info(&pdev->dev, "%d gpio(s) exported\n", nb); -+ -+ return 0; -+} -+ -+static struct platform_driver gpio_export_driver = { -+ .driver = { -+ .name = "gpio-export", -+ .owner = THIS_MODULE, -+ .of_match_table = of_match_ptr(gpio_export_ids), -+ }, -+}; -+ -+static int __init of_gpio_export_init(void) -+{ -+ return platform_driver_probe(&gpio_export_driver, of_gpio_export_probe); -+} -+device_initcall(of_gpio_export_init); ---- a/drivers/gpio/gpiolib-sysfs.c -+++ b/drivers/gpio/gpiolib-sysfs.c -@@ -544,7 +544,7 @@ static struct class gpio_class = { - * - * Returns zero on success, else an error. - */ --int gpiod_export(struct gpio_desc *desc, bool direction_may_change) -+int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name) - { - struct gpio_chip *chip; - struct gpiod_data *data; -@@ -604,6 +604,8 @@ int gpiod_export(struct gpio_desc *desc, - offset = gpio_chip_hwgpio(desc); - if (chip->names && chip->names[offset]) - ioname = chip->names[offset]; -+ if (name) -+ ioname = name; - - dev = device_create_with_groups(&gpio_class, chip->dev, - MKDEV(0, 0), data, gpio_groups, -@@ -625,6 +627,12 @@ err_unlock: - gpiod_dbg(desc, "%s: status %d\n", __func__, status); - return status; - } -+EXPORT_SYMBOL_GPL(__gpiod_export); -+ -+int gpiod_export(struct gpio_desc *desc, bool direction_may_change) -+{ -+ return __gpiod_export(desc, direction_may_change, NULL); -+} - EXPORT_SYMBOL_GPL(gpiod_export); - - static int match_export(struct device *dev, const void *desc) ---- a/include/asm-generic/gpio.h -+++ b/include/asm-generic/gpio.h -@@ -122,6 +122,12 @@ static inline int gpio_export(unsigned g - return gpiod_export(gpio_to_desc(gpio), direction_may_change); - } - -+int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); -+static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name) -+{ -+ return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name); -+} -+ - static inline int gpio_export_link(struct device *dev, const char *name, - unsigned gpio) - { ---- a/include/linux/gpio/consumer.h -+++ b/include/linux/gpio/consumer.h -@@ -427,6 +427,7 @@ static inline struct gpio_desc *devm_get - - #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS) - -+int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); - int gpiod_export(struct gpio_desc *desc, bool direction_may_change); - int gpiod_export_link(struct device *dev, const char *name, - struct gpio_desc *desc); -@@ -434,6 +435,13 @@ void gpiod_unexport(struct gpio_desc *de - - #else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */ - -+static inline int _gpiod_export(struct gpio_desc *desc, -+ bool direction_may_change, -+ const char *name) -+{ -+ return -ENOSYS; -+} -+ - static inline int gpiod_export(struct gpio_desc *desc, - bool direction_may_change) - { diff --git a/target/linux/ipq806x/patches-4.4/996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch b/target/linux/ipq806x/patches-4.4/996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch deleted file mode 100644 index be15ec4772..0000000000 --- a/target/linux/ipq806x/patches-4.4/996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch +++ /dev/null @@ -1,185 +0,0 @@ -Author: Adrian Panella -Date: Fri Jun 10 19:10:15 2016 -0500 - -generic: Mangle bootloader's kernel arguments - -The command-line arguments provided by the boot loader will be -appended to a new device tree property: bootloader-args. -If there is a property "append-rootblock" in DT under /chosen -and a root= option in bootloaders command line it will be parsed -and added to DT bootargs with the form: XX. -Only command line ATAG will be processed, the rest of the ATAGs -sent by bootloader will be ignored. -This is usefull in dual boot systems, to get the current root partition -without afecting the rest of the system. - - -Signed-off-by: Adrian Panella - ---- a/arch/arm/Kconfig -+++ b/arch/arm/Kconfig -@@ -1928,6 +1928,17 @@ config ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEN - The command-line arguments provided by the boot loader will be - appended to the the device tree bootargs property. - -+config ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE -+ bool "Append rootblock parsing bootloader's kernel arguments" -+ help -+ The command-line arguments provided by the boot loader will be -+ appended to a new device tree property: bootloader-args. -+ If there is a property "append-rootblock" in DT under /chosen -+ and a root= option in bootloaders command line it will be parsed -+ and added to DT bootargs with the form: XX. -+ Only command line ATAG will be processed, the rest of the ATAGs -+ sent by bootloader will be ignored. -+ - endchoice - - config CMDLINE ---- a/arch/arm/boot/compressed/atags_to_fdt.c -+++ b/arch/arm/boot/compressed/atags_to_fdt.c -@@ -3,6 +3,8 @@ - - #if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND) - #define do_extend_cmdline 1 -+#elif defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) -+#define do_extend_cmdline 1 - #else - #define do_extend_cmdline 0 - #endif -@@ -66,6 +68,59 @@ static uint32_t get_cell_size(const void - return cell_size; - } - -+#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) -+ -+static char *append_rootblock(char *dest, const char *str, int len, void *fdt) -+{ -+ char *ptr, *end; -+ char *root="root="; -+ int i, l; -+ const char *rootblock; -+ -+ //ARM doesn't have __HAVE_ARCH_STRSTR, so search manually -+ ptr = str - 1; -+ -+ do { -+ //first find an 'r' at the begining or after a space -+ do { -+ ptr++; -+ ptr = strchr(ptr, 'r'); -+ if(!ptr) return dest; -+ -+ } while (ptr != str && *(ptr-1) != ' '); -+ -+ //then check for the rest -+ for(i = 1; i <= 4; i++) -+ if(*(ptr+i) != *(root+i)) break; -+ -+ } while (i != 5); -+ -+ end = strchr(ptr, ' '); -+ end = end ? (end - 1) : (strchr(ptr, 0) - 1); -+ -+ //find partition number (assumes format root=/dev/mtdXX | /dev/mtdblockXX | yy:XX ) -+ for( i = 0; end >= ptr && *end >= '0' && *end <= '9'; end--, i++); -+ ptr = end + 1; -+ -+ /* if append-rootblock property is set use it to append to command line */ -+ rootblock = getprop(fdt, "/chosen", "append-rootblock", &l); -+ if(rootblock != NULL) { -+ if(*dest != ' ') { -+ *dest = ' '; -+ dest++; -+ len++; -+ } -+ if (len + l + i <= COMMAND_LINE_SIZE) { -+ memcpy(dest, rootblock, l); -+ dest += l - 1; -+ memcpy(dest, ptr, i); -+ dest += i; -+ } -+ } -+ return dest; -+} -+#endif -+ - static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline) - { - char cmdline[COMMAND_LINE_SIZE]; -@@ -85,12 +140,21 @@ static void merge_fdt_bootargs(void *fdt - - /* and append the ATAG_CMDLINE */ - if (fdt_cmdline) { -+ -+#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) -+ //save original bootloader args -+ //and append ubi.mtd with root partition number to current cmdline -+ setprop_string(fdt, "/chosen", "bootloader-args", fdt_cmdline); -+ ptr = append_rootblock(ptr, fdt_cmdline, len, fdt); -+ -+#else - len = strlen(fdt_cmdline); - if (ptr - cmdline + len + 2 < COMMAND_LINE_SIZE) { - *ptr++ = ' '; - memcpy(ptr, fdt_cmdline, len); - ptr += len; - } -+#endif - } - *ptr = '\0'; - -@@ -147,7 +211,9 @@ int atags_to_fdt(void *atag_list, void * - else - setprop_string(fdt, "/chosen", "bootargs", - atag->u.cmdline.cmdline); -- } else if (atag->hdr.tag == ATAG_MEM) { -+ } -+#ifndef CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE -+ else if (atag->hdr.tag == ATAG_MEM) { - if (memcount >= sizeof(mem_reg_property)/4) - continue; - if (!atag->u.mem.size) -@@ -186,6 +252,10 @@ int atags_to_fdt(void *atag_list, void * - setprop(fdt, "/memory", "reg", mem_reg_property, - 4 * memcount * memsize); - } -+#else -+ -+ } -+#endif - - return fdt_pack(fdt); - } ---- a/init/main.c -+++ b/init/main.c -@@ -88,6 +88,10 @@ - #include - #include - -+#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) -+#include -+#endif -+ - static int kernel_init(void *); - - extern void init_IRQ(void); -@@ -560,6 +564,18 @@ asmlinkage __visible void __init start_k - page_alloc_init(); - - pr_notice("Kernel command line: %s\n", boot_command_line); -+ -+#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) -+ //Show bootloader's original command line for reference -+ if(of_chosen) { -+ const char *prop = of_get_property(of_chosen, "bootloader-args", NULL); -+ if(prop) -+ pr_notice("Bootloader command line (ignored): %s\n", prop); -+ else -+ pr_notice("Bootloader command line not present\n"); -+ } -+#endif -+ - parse_early_param(); - after_dashes = parse_args("Booting kernel", - static_command_line, __start___param,