aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/ipq806x/patches-4.9
diff options
context:
space:
mode:
authorJohn Crispin <john@phrozen.org>2017-02-16 12:25:25 +0100
committerJohn Crispin <john@phrozen.org>2017-02-16 20:25:32 +0100
commitbb255f74290d889b65a563bac7a4be0427fdbec8 (patch)
tree94eba9e702b4ed6203ee4aecb1bcd5778dde01b1 /target/linux/ipq806x/patches-4.9
parent04c4b6f7fd1c48c09333ba0051ad24a6905491a0 (diff)
downloadupstream-bb255f74290d889b65a563bac7a4be0427fdbec8.tar.gz
upstream-bb255f74290d889b65a563bac7a4be0427fdbec8.tar.bz2
upstream-bb255f74290d889b65a563bac7a4be0427fdbec8.zip
ipq806x: add v4.9 support
Signed-off-by: John Crispin <john@phrozen.org>
Diffstat (limited to 'target/linux/ipq806x/patches-4.9')
-rw-r--r--target/linux/ipq806x/patches-4.9/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch71
-rw-r--r--target/linux/ipq806x/patches-4.9/0002-dmaengine-Add-ADM-driver.patch952
-rw-r--r--target/linux/ipq806x/patches-4.9/0003-arm-qcom-dts-ipq8064-Add-ADM-device-node.patch52
-rw-r--r--target/linux/ipq806x/patches-4.9/0004-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch38
-rw-r--r--target/linux/ipq806x/patches-4.9/0005-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platf.patch74
-rw-r--r--target/linux/ipq806x/patches-4.9/0006-spi-qup-Make-sure-mode-is-only-determined-once.patch208
-rw-r--r--target/linux/ipq806x/patches-4.9/0007-spi-qup-Fix-transaction-done-signaling.patch29
-rw-r--r--target/linux/ipq806x/patches-4.9/0008-spi-qup-Fix-DMA-mode-to-work-correctly.patch219
-rw-r--r--target/linux/ipq806x/patches-4.9/0009-spi-qup-Fix-block-mode-to-work-correctly.patch310
-rw-r--r--target/linux/ipq806x/patches-4.9/0010-spi-qup-properly-detect-extra-interrupts.patch61
-rw-r--r--target/linux/ipq806x/patches-4.9/0011-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch26
-rw-r--r--target/linux/ipq806x/patches-4.9/0012-spi-qup-refactor-spi_qup_io_config-in-two-functions.patch202
-rw-r--r--target/linux/ipq806x/patches-4.9/0013-spi-qup-call-io_config-in-mode-specific-function.patch391
-rw-r--r--target/linux/ipq806x/patches-4.9/0014-spi-qup-allow-block-mode-to-generate-multiple-transa.patch268
-rw-r--r--target/linux/ipq806x/patches-4.9/0015-spi-qup-refactor-spi_qup_prep_sg-to-be-more-take-spe.patch73
-rw-r--r--target/linux/ipq806x/patches-4.9/0016-spi-qup-allow-mulitple-DMA-transactions-per-spi-xfer.patch166
-rw-r--r--target/linux/ipq806x/patches-4.9/0017-spi-qup-Fix-sg-nents-calculation.patch86
-rw-r--r--target/linux/ipq806x/patches-4.9/0026-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch28
-rw-r--r--target/linux/ipq806x/patches-4.9/0027-clk-ipq4019-report-accurate-fixed-clock-rates.patch33
-rw-r--r--target/linux/ipq806x/patches-4.9/0028-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch77
-rw-r--r--target/linux/ipq806x/patches-4.9/0029-qcom-ipq4019-turn-on-DMA-for-i2c.patch23
-rw-r--r--target/linux/ipq806x/patches-4.9/0030-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch28
-rw-r--r--target/linux/ipq806x/patches-4.9/0031-qcom-ipq4019-enable-DMA-for-spi.patch23
-rw-r--r--target/linux/ipq806x/patches-4.9/0032-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch108
-rw-r--r--target/linux/ipq806x/patches-4.9/0033-dts-ipq4019-support-ARMv7-PMU.patch28
-rw-r--r--target/linux/ipq806x/patches-4.9/0034-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch488
-rw-r--r--target/linux/ipq806x/patches-4.9/0035-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch88
-rw-r--r--target/linux/ipq806x/patches-4.9/0036-qcom-ipq4019-enable-USB-bus-for-DK01.1-board.patch45
-rw-r--r--target/linux/ipq806x/patches-4.9/0037-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch258
-rw-r--r--target/linux/ipq806x/patches-4.9/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch746
-rw-r--r--target/linux/ipq806x/patches-4.9/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch586
-rw-r--r--target/linux/ipq806x/patches-4.9/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch94
-rw-r--r--target/linux/ipq806x/patches-4.9/167-ARM-qcom_rpm_fix_support_for_smb208.patch44
-rw-r--r--target/linux/ipq806x/patches-4.9/307-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch25
-rw-r--r--target/linux/ipq806x/patches-4.9/311-add-rpmcc-for-ipq806x.patch81
-rw-r--r--target/linux/ipq806x/patches-4.9/801-override-compiler-flags.patch11
-rw-r--r--target/linux/ipq806x/patches-4.9/802-GPIO-add-named-gpio-exports.patch166
-rw-r--r--target/linux/ipq806x/patches-4.9/996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch185
-rw-r--r--target/linux/ipq806x/patches-4.9/999-dts.patch5046
39 files changed, 11437 insertions, 0 deletions
diff --git a/target/linux/ipq806x/patches-4.9/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch b/target/linux/ipq806x/patches-4.9/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch
new file mode 100644
index 0000000000..fb629aea55
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch
@@ -0,0 +1,71 @@
+From f275ae09b82aa423d2ea5a2be3ea315c8fcf6143 Mon Sep 17 00:00:00 2001
+From: Thomas Pedersen <twp@codeaurora.org>
+Date: Mon, 16 May 2016 17:58:50 -0700
+Subject: [PATCH 01/37] dtbindings: qcom_adm: Fix channel specifiers
+
+Original patch from Andy Gross.
+
+This patch removes the crci information from the dma
+channel property. At least one client device requires
+using more than one CRCI value for a channel. This does
+not match the current binding and the crci information
+needs to be removed.
+
+Instead, the client device will provide this information
+via other means.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
+---
+ Documentation/devicetree/bindings/dma/qcom_adm.txt | 16 ++++++----------
+ 1 file changed, 6 insertions(+), 10 deletions(-)
+
+--- a/Documentation/devicetree/bindings/dma/qcom_adm.txt
++++ b/Documentation/devicetree/bindings/dma/qcom_adm.txt
+@@ -4,8 +4,7 @@ Required properties:
+ - compatible: must contain "qcom,adm" for IPQ/APQ8064 and MSM8960
+ - reg: Address range for DMA registers
+ - interrupts: Should contain one interrupt shared by all channels
+-- #dma-cells: must be <2>. First cell denotes the channel number. Second cell
+- denotes CRCI (client rate control interface) flow control assignment.
++- #dma-cells: must be <1>. First cell denotes the channel number.
+ - clocks: Should contain the core clock and interface clock.
+ - clock-names: Must contain "core" for the core clock and "iface" for the
+ interface clock.
+@@ -22,7 +21,7 @@ Example:
+ compatible = "qcom,adm";
+ reg = <0x18300000 0x100000>;
+ interrupts = <0 170 0>;
+- #dma-cells = <2>;
++ #dma-cells = <1>;
+
+ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>;
+ clock-names = "core", "iface";
+@@ -35,15 +34,12 @@ Example:
+ qcom,ee = <0>;
+ };
+
+-DMA clients must use the format descripted in the dma.txt file, using a three
++DMA clients must use the format descripted in the dma.txt file, using a two
+ cell specifier for each channel.
+
+-Each dmas request consists of 3 cells:
++Each dmas request consists of two cells:
+ 1. phandle pointing to the DMA controller
+ 2. channel number
+- 3. CRCI assignment, if applicable. If no CRCI flow control is required, use 0.
+- The CRCI is used for flow control. It identifies the peripheral device that
+- is the source/destination for the transferred data.
+
+ Example:
+
+@@ -56,7 +52,7 @@ Example:
+
+ cs-gpios = <&qcom_pinmux 20 0>;
+
+- dmas = <&adm_dma 6 9>,
+- <&adm_dma 5 10>;
++ dmas = <&adm_dma 6>,
++ <&adm_dma 5>;
+ dma-names = "rx", "tx";
+ };
diff --git a/target/linux/ipq806x/patches-4.9/0002-dmaengine-Add-ADM-driver.patch b/target/linux/ipq806x/patches-4.9/0002-dmaengine-Add-ADM-driver.patch
new file mode 100644
index 0000000000..4b56fed0bb
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0002-dmaengine-Add-ADM-driver.patch
@@ -0,0 +1,952 @@
+From 1d32bf93c8e83db0aca04d2961badef7e86d663b Mon Sep 17 00:00:00 2001
+From: Thomas Pedersen <twp@codeaurora.org>
+Date: Mon, 16 May 2016 17:58:51 -0700
+Subject: [PATCH 02/37] dmaengine: Add ADM driver
+
+Original patch by Andy Gross.
+
+Add the DMA engine driver for the QCOM Application Data Mover (ADM) DMA
+controller found in the MSM8x60 and IPQ/APQ8064 platforms.
+
+The ADM supports both memory to memory transactions and memory
+to/from peripheral device transactions. The controller also provides flow
+control capabilities for transactions to/from peripheral devices.
+
+The initial release of this driver supports slave transfers to/from peripherals
+and also incorporates CRCI (client rate control interface) flow control.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
+---
+ drivers/dma/qcom/Kconfig | 10 +
+ drivers/dma/qcom/Makefile | 1 +
+ drivers/dma/qcom/qcom_adm.c | 900 +++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 911 insertions(+)
+ create mode 100644 drivers/dma/qcom/qcom_adm.c
+
+--- a/drivers/dma/qcom/Kconfig
++++ b/drivers/dma/qcom/Kconfig
+@@ -27,3 +27,13 @@ config QCOM_HIDMA
+ (user to kernel, kernel to kernel, etc.). It only supports
+ memcpy interface. The core is not intended for general
+ purpose slave DMA.
++
++config QCOM_ADM
++ tristate "Qualcomm ADM support"
++ depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM)
++ select DMA_ENGINE
++ select DMA_VIRTUAL_CHANNELS
++ ---help---
++ Enable support for the Qualcomm ADM DMA controller. This controller
++ provides DMA capabilities for both general purpose and on-chip
++ peripheral devices.
+--- a/drivers/dma/qcom/Makefile
++++ b/drivers/dma/qcom/Makefile
+@@ -3,3 +3,4 @@ obj-$(CONFIG_QCOM_HIDMA_MGMT) += hdma_mg
+ hdma_mgmt-objs := hidma_mgmt.o hidma_mgmt_sys.o
+ obj-$(CONFIG_QCOM_HIDMA) += hdma.o
+ hdma-objs := hidma_ll.o hidma.o hidma_dbg.o
++obj-$(CONFIG_QCOM_ADM) += qcom_adm.o
+--- /dev/null
++++ b/drivers/dma/qcom/qcom_adm.c
+@@ -0,0 +1,900 @@
++/*
++ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/io.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/interrupt.h>
++#include <linux/dma-mapping.h>
++#include <linux/scatterlist.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++#include <linux/of_dma.h>
++#include <linux/reset.h>
++#include <linux/clk.h>
++#include <linux/dmaengine.h>
++
++#include "../dmaengine.h"
++#include "../virt-dma.h"
++
++/* ADM registers - calculated from channel number and security domain */
++#define ADM_CHAN_MULTI 0x4
++#define ADM_CI_MULTI 0x4
++#define ADM_CRCI_MULTI 0x4
++#define ADM_EE_MULTI 0x800
++#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * chan)
++#define ADM_EE_OFFS(ee) (ADM_EE_MULTI * ee)
++#define ADM_CHAN_EE_OFFS(chan, ee) (ADM_CHAN_OFFS(chan) + ADM_EE_OFFS(ee))
++#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * chan)
++#define ADM_CI_OFFS(ci) (ADM_CHAN_OFF(ci))
++#define ADM_CH_CMD_PTR(chan, ee) (ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_RSLT(chan, ee) (0x40 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_FLUSH_STATE0(chan, ee) (0x80 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_STATUS_SD(chan, ee) (0x200 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_CONF(chan) (0x240 + ADM_CHAN_OFFS(chan))
++#define ADM_CH_RSLT_CONF(chan, ee) (0x300 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_SEC_DOMAIN_IRQ_STATUS(ee) (0x380 + ADM_EE_OFFS(ee))
++#define ADM_CI_CONF(ci) (0x390 + ci * ADM_CI_MULTI)
++#define ADM_GP_CTL 0x3d8
++#define ADM_CRCI_CTL(crci, ee) (0x400 + crci * ADM_CRCI_MULTI + \
++ ADM_EE_OFFS(ee))
++
++/* channel status */
++#define ADM_CH_STATUS_VALID BIT(1)
++
++/* channel result */
++#define ADM_CH_RSLT_VALID BIT(31)
++#define ADM_CH_RSLT_ERR BIT(3)
++#define ADM_CH_RSLT_FLUSH BIT(2)
++#define ADM_CH_RSLT_TPD BIT(1)
++
++/* channel conf */
++#define ADM_CH_CONF_SHADOW_EN BIT(12)
++#define ADM_CH_CONF_MPU_DISABLE BIT(11)
++#define ADM_CH_CONF_PERM_MPU_CONF BIT(9)
++#define ADM_CH_CONF_FORCE_RSLT_EN BIT(7)
++#define ADM_CH_CONF_SEC_DOMAIN(ee) (((ee & 0x3) << 4) | ((ee & 0x4) << 11))
++
++/* channel result conf */
++#define ADM_CH_RSLT_CONF_FLUSH_EN BIT(1)
++#define ADM_CH_RSLT_CONF_IRQ_EN BIT(0)
++
++/* CRCI CTL */
++#define ADM_CRCI_CTL_MUX_SEL BIT(18)
++#define ADM_CRCI_CTL_RST BIT(17)
++
++/* CI configuration */
++#define ADM_CI_RANGE_END(x) (x << 24)
++#define ADM_CI_RANGE_START(x) (x << 16)
++#define ADM_CI_BURST_4_WORDS BIT(2)
++#define ADM_CI_BURST_8_WORDS BIT(3)
++
++/* GP CTL */
++#define ADM_GP_CTL_LP_EN BIT(12)
++#define ADM_GP_CTL_LP_CNT(x) (x << 8)
++
++/* Command pointer list entry */
++#define ADM_CPLE_LP BIT(31)
++#define ADM_CPLE_CMD_PTR_LIST BIT(29)
++
++/* Command list entry */
++#define ADM_CMD_LC BIT(31)
++#define ADM_CMD_DST_CRCI(n) (((n) & 0xf) << 7)
++#define ADM_CMD_SRC_CRCI(n) (((n) & 0xf) << 3)
++
++#define ADM_CMD_TYPE_SINGLE 0x0
++#define ADM_CMD_TYPE_BOX 0x3
++
++#define ADM_CRCI_MUX_SEL BIT(4)
++#define ADM_DESC_ALIGN 8
++#define ADM_MAX_XFER (SZ_64K-1)
++#define ADM_MAX_ROWS (SZ_64K-1)
++#define ADM_MAX_CHANNELS 16
++
++struct adm_desc_hw_box {
++ u32 cmd;
++ u32 src_addr;
++ u32 dst_addr;
++ u32 row_len;
++ u32 num_rows;
++ u32 row_offset;
++};
++
++struct adm_desc_hw_single {
++ u32 cmd;
++ u32 src_addr;
++ u32 dst_addr;
++ u32 len;
++};
++
++struct adm_async_desc {
++ struct virt_dma_desc vd;
++ struct adm_device *adev;
++
++ size_t length;
++ enum dma_transfer_direction dir;
++ dma_addr_t dma_addr;
++ size_t dma_len;
++
++ void *cpl;
++ dma_addr_t cp_addr;
++ u32 crci;
++ u32 mux;
++ u32 blk_size;
++};
++
++struct adm_chan {
++ struct virt_dma_chan vc;
++ struct adm_device *adev;
++
++ /* parsed from DT */
++ u32 id; /* channel id */
++
++ struct adm_async_desc *curr_txd;
++ struct dma_slave_config slave;
++ struct list_head node;
++
++ int error;
++ int initialized;
++};
++
++static inline struct adm_chan *to_adm_chan(struct dma_chan *common)
++{
++ return container_of(common, struct adm_chan, vc.chan);
++}
++
++struct adm_device {
++ void __iomem *regs;
++ struct device *dev;
++ struct dma_device common;
++ struct device_dma_parameters dma_parms;
++ struct adm_chan *channels;
++
++ u32 ee;
++
++ struct clk *core_clk;
++ struct clk *iface_clk;
++
++ struct reset_control *clk_reset;
++ struct reset_control *c0_reset;
++ struct reset_control *c1_reset;
++ struct reset_control *c2_reset;
++ int irq;
++};
++
++/**
++ * adm_free_chan - Frees dma resources associated with the specific channel
++ *
++ * Free all allocated descriptors associated with this channel
++ *
++ */
++static void adm_free_chan(struct dma_chan *chan)
++{
++ /* free all queued descriptors */
++ vchan_free_chan_resources(to_virt_chan(chan));
++}
++
++/**
++ * adm_get_blksize - Get block size from burst value
++ *
++ */
++static int adm_get_blksize(unsigned int burst)
++{
++ int ret;
++
++ switch (burst) {
++ case 16:
++ case 32:
++ case 64:
++ case 128:
++ ret = ffs(burst>>4) - 1;
++ break;
++ case 192:
++ ret = 4;
++ break;
++ case 256:
++ ret = 5;
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++
++ return ret;
++}
++
++/**
++ * adm_process_fc_descriptors - Process descriptors for flow controlled xfers
++ *
++ * @achan: ADM channel
++ * @desc: Descriptor memory pointer
++ * @sg: Scatterlist entry
++ * @crci: CRCI value
++ * @burst: Burst size of transaction
++ * @direction: DMA transfer direction
++ */
++static void *adm_process_fc_descriptors(struct adm_chan *achan,
++ void *desc, struct scatterlist *sg, u32 crci, u32 burst,
++ enum dma_transfer_direction direction)
++{
++ struct adm_desc_hw_box *box_desc = NULL;
++ struct adm_desc_hw_single *single_desc;
++ u32 remainder = sg_dma_len(sg);
++ u32 rows, row_offset, crci_cmd;
++ u32 mem_addr = sg_dma_address(sg);
++ u32 *incr_addr = &mem_addr;
++ u32 *src, *dst;
++
++ if (direction == DMA_DEV_TO_MEM) {
++ crci_cmd = ADM_CMD_SRC_CRCI(crci);
++ row_offset = burst;
++ src = &achan->slave.src_addr;
++ dst = &mem_addr;
++ } else {
++ crci_cmd = ADM_CMD_DST_CRCI(crci);
++ row_offset = burst << 16;
++ src = &mem_addr;
++ dst = &achan->slave.dst_addr;
++ }
++
++ while (remainder >= burst) {
++ box_desc = desc;
++ box_desc->cmd = ADM_CMD_TYPE_BOX | crci_cmd;
++ box_desc->row_offset = row_offset;
++ box_desc->src_addr = *src;
++ box_desc->dst_addr = *dst;
++
++ rows = remainder / burst;
++ rows = min_t(u32, rows, ADM_MAX_ROWS);
++ box_desc->num_rows = rows << 16 | rows;
++ box_desc->row_len = burst << 16 | burst;
++
++ *incr_addr += burst * rows;
++ remainder -= burst * rows;
++ desc += sizeof(*box_desc);
++ }
++
++ /* if leftover bytes, do one single descriptor */
++ if (remainder) {
++ single_desc = desc;
++ single_desc->cmd = ADM_CMD_TYPE_SINGLE | crci_cmd;
++ single_desc->len = remainder;
++ single_desc->src_addr = *src;
++ single_desc->dst_addr = *dst;
++ desc += sizeof(*single_desc);
++
++ if (sg_is_last(sg))
++ single_desc->cmd |= ADM_CMD_LC;
++ } else {
++ if (box_desc && sg_is_last(sg))
++ box_desc->cmd |= ADM_CMD_LC;
++ }
++
++ return desc;
++}
++
++/**
++ * adm_process_non_fc_descriptors - Process descriptors for non-fc xfers
++ *
++ * @achan: ADM channel
++ * @desc: Descriptor memory pointer
++ * @sg: Scatterlist entry
++ * @direction: DMA transfer direction
++ */
++static void *adm_process_non_fc_descriptors(struct adm_chan *achan,
++ void *desc, struct scatterlist *sg,
++ enum dma_transfer_direction direction)
++{
++ struct adm_desc_hw_single *single_desc;
++ u32 remainder = sg_dma_len(sg);
++ u32 mem_addr = sg_dma_address(sg);
++ u32 *incr_addr = &mem_addr;
++ u32 *src, *dst;
++
++ if (direction == DMA_DEV_TO_MEM) {
++ src = &achan->slave.src_addr;
++ dst = &mem_addr;
++ } else {
++ src = &mem_addr;
++ dst = &achan->slave.dst_addr;
++ }
++
++ do {
++ single_desc = desc;
++ single_desc->cmd = ADM_CMD_TYPE_SINGLE;
++ single_desc->src_addr = *src;
++ single_desc->dst_addr = *dst;
++ single_desc->len = (remainder > ADM_MAX_XFER) ?
++ ADM_MAX_XFER : remainder;
++
++ remainder -= single_desc->len;
++ *incr_addr += single_desc->len;
++ desc += sizeof(*single_desc);
++ } while (remainder);
++
++ /* set last command if this is the end of the whole transaction */
++ if (sg_is_last(sg))
++ single_desc->cmd |= ADM_CMD_LC;
++
++ return desc;
++}
++
++/**
++ * adm_prep_slave_sg - Prep slave sg transaction
++ *
++ * @chan: dma channel
++ * @sgl: scatter gather list
++ * @sg_len: length of sg
++ * @direction: DMA transfer direction
++ * @flags: DMA flags
++ * @context: transfer context (unused)
++ */
++static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan,
++ struct scatterlist *sgl, unsigned int sg_len,
++ enum dma_transfer_direction direction, unsigned long flags,
++ void *context)
++{
++ struct adm_chan *achan = to_adm_chan(chan);
++ struct adm_device *adev = achan->adev;
++ struct adm_async_desc *async_desc;
++ struct scatterlist *sg;
++ u32 i, burst;
++ u32 single_count = 0, box_count = 0, crci = 0;
++ void *desc;
++ u32 *cple;
++ int blk_size = 0;
++
++ if (!is_slave_direction(direction)) {
++ dev_err(adev->dev, "invalid dma direction\n");
++ return NULL;
++ }
++
++ /*
++ * get burst value from slave configuration
++ */
++ burst = (direction == DMA_MEM_TO_DEV) ?
++ achan->slave.dst_maxburst :
++ achan->slave.src_maxburst;
++
++ /* if using flow control, validate burst and crci values */
++ if (achan->slave.device_fc) {
++
++ blk_size = adm_get_blksize(burst);
++ if (blk_size < 0) {
++ dev_err(adev->dev, "invalid burst value: %d\n",
++ burst);
++ return ERR_PTR(-EINVAL);
++ }
++
++ crci = achan->slave.slave_id & 0xf;
++ if (!crci || achan->slave.slave_id > 0x1f) {
++ dev_err(adev->dev, "invalid crci value\n");
++ return ERR_PTR(-EINVAL);
++ }
++ }
++
++ /* iterate through sgs and compute allocation size of structures */
++ for_each_sg(sgl, sg, sg_len, i) {
++ if (achan->slave.device_fc) {
++ box_count += DIV_ROUND_UP(sg_dma_len(sg) / burst,
++ ADM_MAX_ROWS);
++ if (sg_dma_len(sg) % burst)
++ single_count++;
++ } else {
++ single_count += DIV_ROUND_UP(sg_dma_len(sg),
++ ADM_MAX_XFER);
++ }
++ }
++
++ async_desc = kzalloc(sizeof(*async_desc), GFP_NOWAIT);
++ if (!async_desc)
++ return ERR_PTR(-ENOMEM);
++
++ if (crci)
++ async_desc->mux = achan->slave.slave_id & ADM_CRCI_MUX_SEL ?
++ ADM_CRCI_CTL_MUX_SEL : 0;
++ async_desc->crci = crci;
++ async_desc->blk_size = blk_size;
++ async_desc->dma_len = single_count * sizeof(struct adm_desc_hw_single) +
++ box_count * sizeof(struct adm_desc_hw_box) +
++ sizeof(*cple) + 2 * ADM_DESC_ALIGN;
++
++ async_desc->cpl = dma_alloc_writecombine(adev->dev, async_desc->dma_len,
++ &async_desc->dma_addr, GFP_NOWAIT);
++
++ if (!async_desc->cpl) {
++ kfree(async_desc);
++ return ERR_PTR(-ENOMEM);
++ }
++
++ async_desc->adev = adev;
++
++ /* both command list entry and descriptors must be 8 byte aligned */
++ cple = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN);
++ desc = PTR_ALIGN(cple + 1, ADM_DESC_ALIGN);
++
++ /* init cmd list */
++ *cple = ADM_CPLE_LP;
++ *cple |= (desc - async_desc->cpl + async_desc->dma_addr) >> 3;
++
++ for_each_sg(sgl, sg, sg_len, i) {
++ async_desc->length += sg_dma_len(sg);
++
++ if (achan->slave.device_fc)
++ desc = adm_process_fc_descriptors(achan, desc, sg, crci,
++ burst, direction);
++ else
++ desc = adm_process_non_fc_descriptors(achan, desc, sg,
++ direction);
++ }
++
++ return vchan_tx_prep(&achan->vc, &async_desc->vd, flags);
++}
++
++/**
++ * adm_terminate_all - terminate all transactions on a channel
++ * @achan: adm dma channel
++ *
++ * Dequeues and frees all transactions, aborts current transaction
++ * No callbacks are done
++ *
++ */
++static int adm_terminate_all(struct dma_chan *chan)
++{
++ struct adm_chan *achan = to_adm_chan(chan);
++ struct adm_device *adev = achan->adev;
++ unsigned long flags;
++ LIST_HEAD(head);
++
++ spin_lock_irqsave(&achan->vc.lock, flags);
++ vchan_get_all_descriptors(&achan->vc, &head);
++
++ /* send flush command to terminate current transaction */
++ writel_relaxed(0x0,
++ adev->regs + ADM_CH_FLUSH_STATE0(achan->id, adev->ee));
++
++ spin_unlock_irqrestore(&achan->vc.lock, flags);
++
++ vchan_dma_desc_free_list(&achan->vc, &head);
++
++ return 0;
++}
++
++static int adm_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg)
++{
++ struct adm_chan *achan = to_adm_chan(chan);
++ unsigned long flag;
++
++ spin_lock_irqsave(&achan->vc.lock, flag);
++ memcpy(&achan->slave, cfg, sizeof(struct dma_slave_config));
++ spin_unlock_irqrestore(&achan->vc.lock, flag);
++
++ return 0;
++}
++
++/**
++ * adm_start_dma - start next transaction
++ * @achan - ADM dma channel
++ */
++static void adm_start_dma(struct adm_chan *achan)
++{
++ struct virt_dma_desc *vd = vchan_next_desc(&achan->vc);
++ struct adm_device *adev = achan->adev;
++ struct adm_async_desc *async_desc;
++
++ lockdep_assert_held(&achan->vc.lock);
++
++ if (!vd)
++ return;
++
++ list_del(&vd->node);
++
++ /* write next command list out to the CMD FIFO */
++ async_desc = container_of(vd, struct adm_async_desc, vd);
++ achan->curr_txd = async_desc;
++
++ /* reset channel error */
++ achan->error = 0;
++
++ if (!achan->initialized) {
++ /* enable interrupts */
++ writel(ADM_CH_CONF_SHADOW_EN |
++ ADM_CH_CONF_PERM_MPU_CONF |
++ ADM_CH_CONF_MPU_DISABLE |
++ ADM_CH_CONF_SEC_DOMAIN(adev->ee),
++ adev->regs + ADM_CH_CONF(achan->id));
++
++ writel(ADM_CH_RSLT_CONF_IRQ_EN | ADM_CH_RSLT_CONF_FLUSH_EN,
++ adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee));
++
++ achan->initialized = 1;
++ }
++
++ /* set the crci block size if this transaction requires CRCI */
++ if (async_desc->crci) {
++ writel(async_desc->mux | async_desc->blk_size,
++ adev->regs + ADM_CRCI_CTL(async_desc->crci, adev->ee));
++ }
++
++ /* make sure IRQ enable doesn't get reordered */
++ wmb();
++
++ /* write next command list out to the CMD FIFO */
++ writel(ALIGN(async_desc->dma_addr, ADM_DESC_ALIGN) >> 3,
++ adev->regs + ADM_CH_CMD_PTR(achan->id, adev->ee));
++}
++
++/**
++ * adm_dma_irq - irq handler for ADM controller
++ * @irq: IRQ of interrupt
++ * @data: callback data
++ *
++ * IRQ handler for the bam controller
++ */
++static irqreturn_t adm_dma_irq(int irq, void *data)
++{
++ struct adm_device *adev = data;
++ u32 srcs, i;
++ struct adm_async_desc *async_desc;
++ unsigned long flags;
++
++ srcs = readl_relaxed(adev->regs +
++ ADM_SEC_DOMAIN_IRQ_STATUS(adev->ee));
++
++ for (i = 0; i < ADM_MAX_CHANNELS; i++) {
++ struct adm_chan *achan = &adev->channels[i];
++ u32 status, result;
++
++ if (srcs & BIT(i)) {
++ status = readl_relaxed(adev->regs +
++ ADM_CH_STATUS_SD(i, adev->ee));
++
++ /* if no result present, skip */
++ if (!(status & ADM_CH_STATUS_VALID))
++ continue;
++
++ result = readl_relaxed(adev->regs +
++ ADM_CH_RSLT(i, adev->ee));
++
++ /* no valid results, skip */
++ if (!(result & ADM_CH_RSLT_VALID))
++ continue;
++
++ /* flag error if transaction was flushed or failed */
++ if (result & (ADM_CH_RSLT_ERR | ADM_CH_RSLT_FLUSH))
++ achan->error = 1;
++
++ spin_lock_irqsave(&achan->vc.lock, flags);
++ async_desc = achan->curr_txd;
++
++ achan->curr_txd = NULL;
++
++ if (async_desc) {
++ vchan_cookie_complete(&async_desc->vd);
++
++ /* kick off next DMA */
++ adm_start_dma(achan);
++ }
++
++ spin_unlock_irqrestore(&achan->vc.lock, flags);
++ }
++ }
++
++ return IRQ_HANDLED;
++}
++
++/**
++ * adm_tx_status - returns status of transaction
++ * @chan: dma channel
++ * @cookie: transaction cookie
++ * @txstate: DMA transaction state
++ *
++ * Return status of dma transaction
++ */
++static enum dma_status adm_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
++ struct dma_tx_state *txstate)
++{
++ struct adm_chan *achan = to_adm_chan(chan);
++ struct virt_dma_desc *vd;
++ enum dma_status ret;
++ unsigned long flags;
++ size_t residue = 0;
++
++ ret = dma_cookie_status(chan, cookie, txstate);
++ if (ret == DMA_COMPLETE || !txstate)
++ return ret;
++
++ spin_lock_irqsave(&achan->vc.lock, flags);
++
++ vd = vchan_find_desc(&achan->vc, cookie);
++ if (vd)
++ residue = container_of(vd, struct adm_async_desc, vd)->length;
++
++ spin_unlock_irqrestore(&achan->vc.lock, flags);
++
++ /*
++ * residue is either the full length if it is in the issued list, or 0
++ * if it is in progress. We have no reliable way of determining
++ * anything inbetween
++ */
++ dma_set_residue(txstate, residue);
++
++ if (achan->error)
++ return DMA_ERROR;
++
++ return ret;
++}
++
++/**
++ * adm_issue_pending - starts pending transactions
++ * @chan: dma channel
++ *
++ * Issues all pending transactions and starts DMA
++ */
++static void adm_issue_pending(struct dma_chan *chan)
++{
++ struct adm_chan *achan = to_adm_chan(chan);
++ unsigned long flags;
++
++ spin_lock_irqsave(&achan->vc.lock, flags);
++
++ if (vchan_issue_pending(&achan->vc) && !achan->curr_txd)
++ adm_start_dma(achan);
++ spin_unlock_irqrestore(&achan->vc.lock, flags);
++}
++
++/**
++ * adm_dma_free_desc - free descriptor memory
++ * @vd: virtual descriptor
++ *
++ */
++static void adm_dma_free_desc(struct virt_dma_desc *vd)
++{
++ struct adm_async_desc *async_desc = container_of(vd,
++ struct adm_async_desc, vd);
++
++ dma_free_writecombine(async_desc->adev->dev, async_desc->dma_len,
++ async_desc->cpl, async_desc->dma_addr);
++ kfree(async_desc);
++}
++
++static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan,
++ u32 index)
++{
++ achan->id = index;
++ achan->adev = adev;
++
++ vchan_init(&achan->vc, &adev->common);
++ achan->vc.desc_free = adm_dma_free_desc;
++}
++
++static int adm_dma_probe(struct platform_device *pdev)
++{
++ struct adm_device *adev;
++ struct resource *iores;
++ int ret;
++ u32 i;
++
++ adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
++ if (!adev)
++ return -ENOMEM;
++
++ adev->dev = &pdev->dev;
++
++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ adev->regs = devm_ioremap_resource(&pdev->dev, iores);
++ if (IS_ERR(adev->regs))
++ return PTR_ERR(adev->regs);
++
++ adev->irq = platform_get_irq(pdev, 0);
++ if (adev->irq < 0)
++ return adev->irq;
++
++ ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &adev->ee);
++ if (ret) {
++ dev_err(adev->dev, "Execution environment unspecified\n");
++ return ret;
++ }
++
++ adev->core_clk = devm_clk_get(adev->dev, "core");
++ if (IS_ERR(adev->core_clk))
++ return PTR_ERR(adev->core_clk);
++
++ ret = clk_prepare_enable(adev->core_clk);
++ if (ret) {
++ dev_err(adev->dev, "failed to prepare/enable core clock\n");
++ return ret;
++ }
++
++ adev->iface_clk = devm_clk_get(adev->dev, "iface");
++ if (IS_ERR(adev->iface_clk)) {
++ ret = PTR_ERR(adev->iface_clk);
++ goto err_disable_core_clk;
++ }
++
++ ret = clk_prepare_enable(adev->iface_clk);
++ if (ret) {
++ dev_err(adev->dev, "failed to prepare/enable iface clock\n");
++ goto err_disable_core_clk;
++ }
++
++ adev->clk_reset = devm_reset_control_get(&pdev->dev, "clk");
++ if (IS_ERR(adev->clk_reset)) {
++ dev_err(adev->dev, "failed to get ADM0 reset\n");
++ ret = PTR_ERR(adev->clk_reset);
++ goto err_disable_clks;
++ }
++
++ adev->c0_reset = devm_reset_control_get(&pdev->dev, "c0");
++ if (IS_ERR(adev->c0_reset)) {
++ dev_err(adev->dev, "failed to get ADM0 C0 reset\n");
++ ret = PTR_ERR(adev->c0_reset);
++ goto err_disable_clks;
++ }
++
++ adev->c1_reset = devm_reset_control_get(&pdev->dev, "c1");
++ if (IS_ERR(adev->c1_reset)) {
++ dev_err(adev->dev, "failed to get ADM0 C1 reset\n");
++ ret = PTR_ERR(adev->c1_reset);
++ goto err_disable_clks;
++ }
++
++ adev->c2_reset = devm_reset_control_get(&pdev->dev, "c2");
++ if (IS_ERR(adev->c2_reset)) {
++ dev_err(adev->dev, "failed to get ADM0 C2 reset\n");
++ ret = PTR_ERR(adev->c2_reset);
++ goto err_disable_clks;
++ }
++
++ reset_control_assert(adev->clk_reset);
++ reset_control_assert(adev->c0_reset);
++ reset_control_assert(adev->c1_reset);
++ reset_control_assert(adev->c2_reset);
++
++ reset_control_deassert(adev->clk_reset);
++ reset_control_deassert(adev->c0_reset);
++ reset_control_deassert(adev->c1_reset);
++ reset_control_deassert(adev->c2_reset);
++
++ adev->channels = devm_kcalloc(adev->dev, ADM_MAX_CHANNELS,
++ sizeof(*adev->channels), GFP_KERNEL);
++
++ if (!adev->channels) {
++ ret = -ENOMEM;
++ goto err_disable_clks;
++ }
++
++ /* allocate and initialize channels */
++ INIT_LIST_HEAD(&adev->common.channels);
++
++ for (i = 0; i < ADM_MAX_CHANNELS; i++)
++ adm_channel_init(adev, &adev->channels[i], i);
++
++ /* reset CRCIs */
++ for (i = 0; i < 16; i++)
++ writel(ADM_CRCI_CTL_RST, adev->regs +
++ ADM_CRCI_CTL(i, adev->ee));
++
++ /* configure client interfaces */
++ writel(ADM_CI_RANGE_START(0x40) | ADM_CI_RANGE_END(0xb0) |
++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(0));
++ writel(ADM_CI_RANGE_START(0x2a) | ADM_CI_RANGE_END(0x2c) |
++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(1));
++ writel(ADM_CI_RANGE_START(0x12) | ADM_CI_RANGE_END(0x28) |
++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(2));
++ writel(ADM_GP_CTL_LP_EN | ADM_GP_CTL_LP_CNT(0xf),
++ adev->regs + ADM_GP_CTL);
++
++ ret = devm_request_irq(adev->dev, adev->irq, adm_dma_irq,
++ 0, "adm_dma", adev);
++ if (ret)
++ goto err_disable_clks;
++
++ platform_set_drvdata(pdev, adev);
++
++ adev->common.dev = adev->dev;
++ adev->common.dev->dma_parms = &adev->dma_parms;
++
++ /* set capabilities */
++ dma_cap_zero(adev->common.cap_mask);
++ dma_cap_set(DMA_SLAVE, adev->common.cap_mask);
++ dma_cap_set(DMA_PRIVATE, adev->common.cap_mask);
++
++ /* initialize dmaengine apis */
++ adev->common.directions = BIT(DMA_DEV_TO_MEM | DMA_MEM_TO_DEV);
++ adev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
++ adev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
++ adev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
++ adev->common.device_free_chan_resources = adm_free_chan;
++ adev->common.device_prep_slave_sg = adm_prep_slave_sg;
++ adev->common.device_issue_pending = adm_issue_pending;
++ adev->common.device_tx_status = adm_tx_status;
++ adev->common.device_terminate_all = adm_terminate_all;
++ adev->common.device_config = adm_slave_config;
++
++ ret = dma_async_device_register(&adev->common);
++ if (ret) {
++ dev_err(adev->dev, "failed to register dma async device\n");
++ goto err_disable_clks;
++ }
++
++ ret = of_dma_controller_register(pdev->dev.of_node,
++ of_dma_xlate_by_chan_id,
++ &adev->common);
++ if (ret)
++ goto err_unregister_dma;
++
++ return 0;
++
++err_unregister_dma:
++ dma_async_device_unregister(&adev->common);
++err_disable_clks:
++ clk_disable_unprepare(adev->iface_clk);
++err_disable_core_clk:
++ clk_disable_unprepare(adev->core_clk);
++
++ return ret;
++}
++
++static int adm_dma_remove(struct platform_device *pdev)
++{
++ struct adm_device *adev = platform_get_drvdata(pdev);
++ struct adm_chan *achan;
++ u32 i;
++
++ of_dma_controller_free(pdev->dev.of_node);
++ dma_async_device_unregister(&adev->common);
++
++ for (i = 0; i < ADM_MAX_CHANNELS; i++) {
++ achan = &adev->channels[i];
++
++ /* mask IRQs for this channel/EE pair */
++ writel(0, adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee));
++
++ adm_terminate_all(&adev->channels[i].vc.chan);
++ }
++
++ devm_free_irq(adev->dev, adev->irq, adev);
++
++ clk_disable_unprepare(adev->core_clk);
++ clk_disable_unprepare(adev->iface_clk);
++
++ return 0;
++}
++
++static const struct of_device_id adm_of_match[] = {
++ { .compatible = "qcom,adm", },
++ {}
++};
++MODULE_DEVICE_TABLE(of, adm_of_match);
++
++static struct platform_driver adm_dma_driver = {
++ .probe = adm_dma_probe,
++ .remove = adm_dma_remove,
++ .driver = {
++ .name = "adm-dma-engine",
++ .of_match_table = adm_of_match,
++ },
++};
++
++module_platform_driver(adm_dma_driver);
++
++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
++MODULE_DESCRIPTION("QCOM ADM DMA engine driver");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/ipq806x/patches-4.9/0003-arm-qcom-dts-ipq8064-Add-ADM-device-node.patch b/target/linux/ipq806x/patches-4.9/0003-arm-qcom-dts-ipq8064-Add-ADM-device-node.patch
new file mode 100644
index 0000000000..7bd00975f0
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0003-arm-qcom-dts-ipq8064-Add-ADM-device-node.patch
@@ -0,0 +1,52 @@
+From eb694e964310ba402d6ff99f08e0ef78345e7397 Mon Sep 17 00:00:00 2001
+From: Thomas Pedersen <twp@codeaurora.org>
+Date: Mon, 16 May 2016 17:58:52 -0700
+Subject: [PATCH 03/37] arm: qcom: dts: ipq8064: Add ADM device node
+
+Original patch by Andy Gross.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 21 +++++++++++++++++++++
+ 1 file changed, 21 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -1,9 +1,11 @@
+ /dts-v1/;
+
+ #include "skeleton.dtsi"
++#include <dt-bindings/reset/qcom,gcc-ipq806x.h>
+ #include <dt-bindings/clock/qcom,gcc-ipq806x.h>
+ #include <dt-bindings/clock/qcom,lcc-ipq806x.h>
+ #include <dt-bindings/soc/qcom,gsbi.h>
++#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ / {
+ model = "Qualcomm IPQ8064";
+@@ -342,5 +344,24 @@
+ #reset-cells = <1>;
+ };
+
++ adm_dma: dma@18300000 {
++ compatible = "qcom,adm";
++ reg = <0x18300000 0x100000>;
++ interrupts = <GIC_SPI 170 IRQ_TYPE_NONE>;
++ #dma-cells = <1>;
++
++ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>;
++ clock-names = "core", "iface";
++
++ resets = <&gcc ADM0_RESET>,
++ <&gcc ADM0_PBUS_RESET>,
++ <&gcc ADM0_C0_RESET>,
++ <&gcc ADM0_C1_RESET>,
++ <&gcc ADM0_C2_RESET>;
++ reset-names = "clk", "pbus", "c0", "c1", "c2";
++ qcom,ee = <0>;
++
++ status = "disabled";
++ };
+ };
+ };
diff --git a/target/linux/ipq806x/patches-4.9/0004-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch b/target/linux/ipq806x/patches-4.9/0004-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch
new file mode 100644
index 0000000000..4fd75c9345
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0004-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch
@@ -0,0 +1,38 @@
+From c6d45af259eb4fb2a598c0396c6dd580b1658558 Mon Sep 17 00:00:00 2001
+From: Thomas Pedersen <twp@codeaurora.org>
+Date: Mon, 16 May 2016 17:58:53 -0700
+Subject: [PATCH 04/37] arm: qcom: dts: Add NAND controller node for ipq806x
+
+Original patch by Archit Taneja.
+
+Signed-off-by: Archit Taneja <architt@codeaurora.org>
+Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -363,5 +363,22 @@
+
+ status = "disabled";
+ };
++
++ nand@1ac00000 {
++ compatible = "qcom,ipq806x-nand";
++ reg = <0x1ac00000 0x800>;
++
++ clocks = <&gcc EBI2_CLK>,
++ <&gcc EBI2_AON_CLK>;
++ clock-names = "core", "aon";
++
++ dmas = <&adm_dma 3>;
++ dma-names = "rxtx";
++ qcom,cmd-crci = <15>;
++ qcom,data-crci = <3>;
++
++ status = "disabled";
++ };
++
+ };
+ };
diff --git a/target/linux/ipq806x/patches-4.9/0005-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platf.patch b/target/linux/ipq806x/patches-4.9/0005-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platf.patch
new file mode 100644
index 0000000000..2bfbf08d52
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0005-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platf.patch
@@ -0,0 +1,74 @@
+From f8d0939eca47f56449ec583810a6ff41a1caaa91 Mon Sep 17 00:00:00 2001
+From: Thomas Pedersen <twp@codeaurora.org>
+Date: Mon, 16 May 2016 17:58:54 -0700
+Subject: [PATCH 05/37] arm: qcom: dts: Enable NAND node on IPQ8064 AP148
+ platform
+
+Original patch by Archit Taneja.
+
+Enable the NAND controller node on the AP148 platform. Provide pinmux
+information.
+
+Signed-off-by: Archit Taneja <architt@codeaurora.org>
+Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 42 ++++++++++++++++++++++++++++++
+ 1 file changed, 42 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -38,6 +38,28 @@
+ bias-none;
+ };
+ };
++ nand_pins: nand_pins {
++ mux {
++ pins = "gpio34", "gpio35", "gpio36",
++ "gpio37", "gpio38", "gpio39",
++ "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ function = "nand";
++ drive-strength = <10>;
++ bias-disable;
++ };
++ pullups {
++ pins = "gpio39";
++ bias-pull-up;
++ };
++ hold {
++ pins = "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ bias-bus-hold;
++ };
++ };
+ };
+
+ gsbi@16300000 {
+@@ -98,5 +120,25 @@
+ ports-implemented = <0x1>;
+ status = "ok";
+ };
++
++ nand@1ac00000 {
++ status = "ok";
++
++ pinctrl-0 = <&nand_pins>;
++ pinctrl-names = "default";
++
++ nandcs@0 {
++ compatible = "qcom,nandcs";
++ reg = <0>;
++
++ nand-ecc-strength = <4>;
++ nand-ecc-step-size = <512>;
++ nand-bus-width = <8>;
++ };
++ };
+ };
+ };
++
++&adm_dma {
++ status = "ok";
++};
diff --git a/target/linux/ipq806x/patches-4.9/0006-spi-qup-Make-sure-mode-is-only-determined-once.patch b/target/linux/ipq806x/patches-4.9/0006-spi-qup-Make-sure-mode-is-only-determined-once.patch
new file mode 100644
index 0000000000..9d99699721
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0006-spi-qup-Make-sure-mode-is-only-determined-once.patch
@@ -0,0 +1,208 @@
+From 2852d6df4b60987f9248c3d36d5fe2462e6556b9 Mon Sep 17 00:00:00 2001
+From: Andy Gross <andy.gross@linaro.org>
+Date: Tue, 12 Apr 2016 09:11:47 -0500
+Subject: [PATCH 06/37] spi: qup: Make sure mode is only determined once
+
+This patch calculates the mode once. All decisions on the current
+transaction
+is made using the mode instead of use_dma
+
+Signed-off-by: Andy Gross <andy.gross@linaro.org>
+---
+ drivers/spi/spi-qup.c | 87 +++++++++++++++++++++----------------------------
+ 1 file changed, 37 insertions(+), 50 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -149,12 +149,20 @@ struct spi_qup {
+ int rx_bytes;
+ int qup_v1;
+
+- int use_dma;
++ int mode;
+ struct dma_slave_config rx_conf;
+ struct dma_slave_config tx_conf;
+ };
+
+
++static inline bool spi_qup_is_dma_xfer(int mode)
++{
++ if (mode == QUP_IO_M_MODE_DMOV || mode == QUP_IO_M_MODE_BAM)
++ return true;
++
++ return false;
++}
++
+ static inline bool spi_qup_is_valid_state(struct spi_qup *controller)
+ {
+ u32 opstate = readl_relaxed(controller->base + QUP_STATE);
+@@ -424,7 +432,7 @@ static irqreturn_t spi_qup_qup_irq(int i
+ error = -EIO;
+ }
+
+- if (!controller->use_dma) {
++ if (!spi_qup_is_dma_xfer(controller->mode)) {
+ if (opflags & QUP_OP_IN_SERVICE_FLAG)
+ spi_qup_fifo_read(controller, xfer);
+
+@@ -443,34 +451,11 @@ static irqreturn_t spi_qup_qup_irq(int i
+ return IRQ_HANDLED;
+ }
+
+-static u32
+-spi_qup_get_mode(struct spi_master *master, struct spi_transfer *xfer)
+-{
+- struct spi_qup *qup = spi_master_get_devdata(master);
+- u32 mode;
+-
+- qup->w_size = 4;
+-
+- if (xfer->bits_per_word <= 8)
+- qup->w_size = 1;
+- else if (xfer->bits_per_word <= 16)
+- qup->w_size = 2;
+-
+- qup->n_words = xfer->len / qup->w_size;
+-
+- if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32)))
+- mode = QUP_IO_M_MODE_FIFO;
+- else
+- mode = QUP_IO_M_MODE_BLOCK;
+-
+- return mode;
+-}
+-
+ /* set clock freq ... bits per word */
+ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+ {
+ struct spi_qup *controller = spi_master_get_devdata(spi->master);
+- u32 config, iomode, mode, control;
++ u32 config, iomode, control;
+ int ret, n_words;
+
+ if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
+@@ -491,23 +476,22 @@ static int spi_qup_io_config(struct spi_
+ return -EIO;
+ }
+
+- mode = spi_qup_get_mode(spi->master, xfer);
++ controller->w_size = DIV_ROUND_UP(xfer->bits_per_word, 8);
++ controller->n_words = xfer->len / controller->w_size;
+ n_words = controller->n_words;
+
+- if (mode == QUP_IO_M_MODE_FIFO) {
++ if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
++ controller->mode = QUP_IO_M_MODE_FIFO;
+ writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
+ writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT);
+ /* must be zero for FIFO */
+ writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
+ writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+- } else if (!controller->use_dma) {
+- writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
+- writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
+- /* must be zero for BLOCK and BAM */
+- writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+- writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+- } else {
+- mode = QUP_IO_M_MODE_BAM;
++
++ } else if (spi->master->can_dma &&
++ spi->master->can_dma(spi->master, spi, xfer) &&
++ spi->master->cur_msg_mapped) {
++ controller->mode = QUP_IO_M_MODE_BAM;
+ writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+
+@@ -528,19 +512,26 @@ static int spi_qup_io_config(struct spi_
+
+ writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+ }
++ } else {
++ controller->mode = QUP_IO_M_MODE_BLOCK;
++ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
++ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
++ /* must be zero for BLOCK and BAM */
++ writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
++ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+ }
+
+ iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
+ /* Set input and output transfer mode */
+ iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
+
+- if (!controller->use_dma)
++ if (!spi_qup_is_dma_xfer(controller->mode))
+ iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
+ else
+ iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
+
+- iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
+- iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
++ iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
++ iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
+
+ writel_relaxed(iomode, controller->base + QUP_IO_M_MODES);
+
+@@ -581,7 +572,7 @@ static int spi_qup_io_config(struct spi_
+ config |= xfer->bits_per_word - 1;
+ config |= QUP_CONFIG_SPI_MODE;
+
+- if (controller->use_dma) {
++ if (spi_qup_is_dma_xfer(controller->mode)) {
+ if (!xfer->tx_buf)
+ config |= QUP_CONFIG_NO_OUTPUT;
+ if (!xfer->rx_buf)
+@@ -599,7 +590,7 @@ static int spi_qup_io_config(struct spi_
+ * status change in BAM mode
+ */
+
+- if (mode == QUP_IO_M_MODE_BAM)
++ if (spi_qup_is_dma_xfer(controller->mode))
+ mask = QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG;
+
+ writel_relaxed(mask, controller->base + QUP_OPERATIONAL_MASK);
+@@ -633,7 +624,7 @@ static int spi_qup_transfer_one(struct s
+ controller->tx_bytes = 0;
+ spin_unlock_irqrestore(&controller->lock, flags);
+
+- if (controller->use_dma)
++ if (spi_qup_is_dma_xfer(controller->mode))
+ ret = spi_qup_do_dma(master, xfer);
+ else
+ ret = spi_qup_do_pio(master, xfer);
+@@ -657,7 +648,7 @@ exit:
+ ret = controller->error;
+ spin_unlock_irqrestore(&controller->lock, flags);
+
+- if (ret && controller->use_dma)
++ if (ret && spi_qup_is_dma_xfer(controller->mode))
+ spi_qup_dma_terminate(master, xfer);
+
+ return ret;
+@@ -668,9 +659,7 @@ static bool spi_qup_can_dma(struct spi_m
+ {
+ struct spi_qup *qup = spi_master_get_devdata(master);
+ size_t dma_align = dma_get_cache_alignment();
+- u32 mode;
+-
+- qup->use_dma = 0;
++ int n_words;
+
+ if (xfer->rx_buf && (xfer->len % qup->in_blk_sz ||
+ IS_ERR_OR_NULL(master->dma_rx) ||
+@@ -682,12 +671,10 @@ static bool spi_qup_can_dma(struct spi_m
+ !IS_ALIGNED((size_t)xfer->tx_buf, dma_align)))
+ return false;
+
+- mode = spi_qup_get_mode(master, xfer);
+- if (mode == QUP_IO_M_MODE_FIFO)
++ n_words = xfer->len / DIV_ROUND_UP(xfer->bits_per_word, 8);
++ if (n_words <= (qup->in_fifo_sz / sizeof(u32)))
+ return false;
+
+- qup->use_dma = 1;
+-
+ return true;
+ }
+
diff --git a/target/linux/ipq806x/patches-4.9/0007-spi-qup-Fix-transaction-done-signaling.patch b/target/linux/ipq806x/patches-4.9/0007-spi-qup-Fix-transaction-done-signaling.patch
new file mode 100644
index 0000000000..b42f8cdc1a
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0007-spi-qup-Fix-transaction-done-signaling.patch
@@ -0,0 +1,29 @@
+From 826290ee1fd1bcd26b6771f94f6680a4ff8951c4 Mon Sep 17 00:00:00 2001
+From: Andy Gross <andy.gross@linaro.org>
+Date: Fri, 29 Jan 2016 22:06:50 -0600
+Subject: [PATCH 07/37] spi: qup: Fix transaction done signaling
+
+Wait to signal done until we get all of the interrupts we are expecting
+to get for a transaction. If we don't wait for the input done flag, we
+can be inbetween transactions when the done flag comes in and this can
+mess up the next transaction.
+
+CC: Grant Grundler <grundler@chromium.org>
+CC: Sarthak Kukreti <skukreti@codeaurora.org>
+Signed-off-by: Andy Gross <andy.gross@linaro.org>
+---
+ drivers/spi/spi-qup.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -445,7 +445,8 @@ static irqreturn_t spi_qup_qup_irq(int i
+ controller->xfer = xfer;
+ spin_unlock_irqrestore(&controller->lock, flags);
+
+- if (controller->rx_bytes == xfer->len || error)
++ if ((controller->rx_bytes == xfer->len &&
++ (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error)
+ complete(&controller->done);
+
+ return IRQ_HANDLED;
diff --git a/target/linux/ipq806x/patches-4.9/0008-spi-qup-Fix-DMA-mode-to-work-correctly.patch b/target/linux/ipq806x/patches-4.9/0008-spi-qup-Fix-DMA-mode-to-work-correctly.patch
new file mode 100644
index 0000000000..d950446837
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0008-spi-qup-Fix-DMA-mode-to-work-correctly.patch
@@ -0,0 +1,219 @@
+From 715d008b67b21fb8bfefaeeefa5b8ddf23777872 Mon Sep 17 00:00:00 2001
+From: Andy Gross <andy.gross@linaro.org>
+Date: Tue, 2 Feb 2016 17:00:53 -0600
+Subject: [PATCH 08/37] spi: qup: Fix DMA mode to work correctly
+
+This patch fixes a few issues with the DMA mode. The QUP needs to be
+placed in the run mode before the DMA transactions are executed. The
+conditions for being able to DMA vary between revisions of the QUP.
+This is due to v1.1.1 using ADM DMA and later revisions using BAM.
+
+Signed-off-by: Andy Gross <andy.gross@linaro.org>
+---
+ drivers/spi/spi-qup.c | 94 ++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 62 insertions(+), 32 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -142,6 +142,7 @@ struct spi_qup {
+
+ struct spi_transfer *xfer;
+ struct completion done;
++ struct completion dma_tx_done;
+ int error;
+ int w_size; /* bytes per SPI word */
+ int n_words;
+@@ -284,16 +285,16 @@ static void spi_qup_fifo_write(struct sp
+
+ static void spi_qup_dma_done(void *data)
+ {
+- struct spi_qup *qup = data;
++ struct completion *done = data;
+
+- complete(&qup->done);
++ complete(done);
+ }
+
+ static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer,
+ enum dma_transfer_direction dir,
+- dma_async_tx_callback callback)
++ dma_async_tx_callback callback,
++ void *data)
+ {
+- struct spi_qup *qup = spi_master_get_devdata(master);
+ unsigned long flags = DMA_PREP_INTERRUPT | DMA_PREP_FENCE;
+ struct dma_async_tx_descriptor *desc;
+ struct scatterlist *sgl;
+@@ -312,11 +313,11 @@ static int spi_qup_prep_sg(struct spi_ma
+ }
+
+ desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags);
+- if (!desc)
+- return -EINVAL;
++ if (IS_ERR_OR_NULL(desc))
++ return desc ? PTR_ERR(desc) : -EINVAL;
+
+ desc->callback = callback;
+- desc->callback_param = qup;
++ desc->callback_param = data;
+
+ cookie = dmaengine_submit(desc);
+
+@@ -332,18 +333,29 @@ static void spi_qup_dma_terminate(struct
+ dmaengine_terminate_all(master->dma_rx);
+ }
+
+-static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer)
++static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer,
++unsigned long timeout)
+ {
++ struct spi_qup *qup = spi_master_get_devdata(master);
+ dma_async_tx_callback rx_done = NULL, tx_done = NULL;
+ int ret;
+
++ /* before issuing the descriptors, set the QUP to run */
++ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
++ if (ret) {
++ dev_warn(qup->dev, "cannot set RUN state\n");
++ return ret;
++ }
++
+ if (xfer->rx_buf)
+ rx_done = spi_qup_dma_done;
+- else if (xfer->tx_buf)
++
++ if (xfer->tx_buf)
+ tx_done = spi_qup_dma_done;
+
+ if (xfer->rx_buf) {
+- ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done);
++ ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done,
++ &qup->done);
+ if (ret)
+ return ret;
+
+@@ -351,17 +363,25 @@ static int spi_qup_do_dma(struct spi_mas
+ }
+
+ if (xfer->tx_buf) {
+- ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done);
++ ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done,
++ &qup->dma_tx_done);
+ if (ret)
+ return ret;
+
+ dma_async_issue_pending(master->dma_tx);
+ }
+
+- return 0;
++ if (xfer->rx_buf && !wait_for_completion_timeout(&qup->done, timeout))
++ return -ETIMEDOUT;
++
++ if (xfer->tx_buf && !wait_for_completion_timeout(&qup->dma_tx_done, timeout))
++ ret = -ETIMEDOUT;
++
++ return ret;
+ }
+
+-static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer)
++static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer,
++ unsigned long timeout)
+ {
+ struct spi_qup *qup = spi_master_get_devdata(master);
+ int ret;
+@@ -380,6 +400,15 @@ static int spi_qup_do_pio(struct spi_mas
+
+ spi_qup_fifo_write(qup, xfer);
+
++ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
++ if (ret) {
++ dev_warn(qup->dev, "cannot set RUN state\n");
++ return ret;
++ }
++
++ if (!wait_for_completion_timeout(&qup->done, timeout))
++ return -ETIMEDOUT;
++
+ return 0;
+ }
+
+@@ -428,7 +457,6 @@ static irqreturn_t spi_qup_qup_irq(int i
+ dev_warn(controller->dev, "CLK_OVER_RUN\n");
+ if (spi_err & SPI_ERROR_CLK_UNDER_RUN)
+ dev_warn(controller->dev, "CLK_UNDER_RUN\n");
+-
+ error = -EIO;
+ }
+
+@@ -617,6 +645,7 @@ static int spi_qup_transfer_one(struct s
+ timeout = 100 * msecs_to_jiffies(timeout);
+
+ reinit_completion(&controller->done);
++ reinit_completion(&controller->dma_tx_done);
+
+ spin_lock_irqsave(&controller->lock, flags);
+ controller->xfer = xfer;
+@@ -626,21 +655,13 @@ static int spi_qup_transfer_one(struct s
+ spin_unlock_irqrestore(&controller->lock, flags);
+
+ if (spi_qup_is_dma_xfer(controller->mode))
+- ret = spi_qup_do_dma(master, xfer);
++ ret = spi_qup_do_dma(master, xfer, timeout);
+ else
+- ret = spi_qup_do_pio(master, xfer);
++ ret = spi_qup_do_pio(master, xfer, timeout);
+
+ if (ret)
+ goto exit;
+
+- if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
+- dev_warn(controller->dev, "cannot set EXECUTE state\n");
+- goto exit;
+- }
+-
+- if (!wait_for_completion_timeout(&controller->done, timeout))
+- ret = -ETIMEDOUT;
+-
+ exit:
+ spi_qup_set_state(controller, QUP_STATE_RESET);
+ spin_lock_irqsave(&controller->lock, flags);
+@@ -662,15 +683,23 @@ static bool spi_qup_can_dma(struct spi_m
+ size_t dma_align = dma_get_cache_alignment();
+ int n_words;
+
+- if (xfer->rx_buf && (xfer->len % qup->in_blk_sz ||
+- IS_ERR_OR_NULL(master->dma_rx) ||
+- !IS_ALIGNED((size_t)xfer->rx_buf, dma_align)))
+- return false;
++ if (xfer->rx_buf) {
++ if (!IS_ALIGNED((size_t)xfer->rx_buf, dma_align) ||
++ IS_ERR_OR_NULL(master->dma_rx))
++ return false;
+
+- if (xfer->tx_buf && (xfer->len % qup->out_blk_sz ||
+- IS_ERR_OR_NULL(master->dma_tx) ||
+- !IS_ALIGNED((size_t)xfer->tx_buf, dma_align)))
+- return false;
++ if (qup->qup_v1 && (xfer->len % qup->in_blk_sz))
++ return false;
++ }
++
++ if (xfer->tx_buf) {
++ if (!IS_ALIGNED((size_t)xfer->tx_buf, dma_align) ||
++ IS_ERR_OR_NULL(master->dma_tx))
++ return false;
++
++ if (qup->qup_v1 && (xfer->len % qup->out_blk_sz))
++ return false;
++ }
+
+ n_words = xfer->len / DIV_ROUND_UP(xfer->bits_per_word, 8);
+ if (n_words <= (qup->in_fifo_sz / sizeof(u32)))
+@@ -836,6 +865,7 @@ static int spi_qup_probe(struct platform
+
+ spin_lock_init(&controller->lock);
+ init_completion(&controller->done);
++ init_completion(&controller->dma_tx_done);
+
+ iomode = readl_relaxed(base + QUP_IO_M_MODES);
+
diff --git a/target/linux/ipq806x/patches-4.9/0009-spi-qup-Fix-block-mode-to-work-correctly.patch b/target/linux/ipq806x/patches-4.9/0009-spi-qup-Fix-block-mode-to-work-correctly.patch
new file mode 100644
index 0000000000..2f316b1a6d
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0009-spi-qup-Fix-block-mode-to-work-correctly.patch
@@ -0,0 +1,310 @@
+From 4dc7631bbf7c7ac7548026ce45d889235e4f5892 Mon Sep 17 00:00:00 2001
+From: Andy Gross <andy.gross@linaro.org>
+Date: Sun, 31 Jan 2016 21:28:13 -0600
+Subject: [PATCH 09/37] spi: qup: Fix block mode to work correctly
+
+This patch corrects the behavior of the BLOCK transactions. During block
+transactions, the controller must be read/written to in block size transactions.
+
+Signed-off-by: Andy Gross <andy.gross@linaro.org>
+---
+ drivers/spi/spi-qup.c | 182 ++++++++++++++++++++++++++++++++++++++-----------
+ 1 file changed, 142 insertions(+), 40 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -82,6 +82,8 @@
+ #define QUP_IO_M_MODE_BAM 3
+
+ /* QUP_OPERATIONAL fields */
++#define QUP_OP_IN_BLOCK_READ_REQ BIT(13)
++#define QUP_OP_OUT_BLOCK_WRITE_REQ BIT(12)
+ #define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11)
+ #define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10)
+ #define QUP_OP_IN_SERVICE_FLAG BIT(9)
+@@ -155,6 +157,12 @@ struct spi_qup {
+ struct dma_slave_config tx_conf;
+ };
+
++static inline bool spi_qup_is_flag_set(struct spi_qup *controller, u32 flag)
++{
++ u32 opflag = readl_relaxed(controller->base + QUP_OPERATIONAL);
++
++ return opflag & flag;
++}
+
+ static inline bool spi_qup_is_dma_xfer(int mode)
+ {
+@@ -216,29 +224,26 @@ static int spi_qup_set_state(struct spi_
+ return 0;
+ }
+
+-static void spi_qup_fifo_read(struct spi_qup *controller,
+- struct spi_transfer *xfer)
++static void spi_qup_read_from_fifo(struct spi_qup *controller,
++ struct spi_transfer *xfer, u32 num_words)
+ {
+ u8 *rx_buf = xfer->rx_buf;
+- u32 word, state;
+- int idx, shift, w_size;
+-
+- w_size = controller->w_size;
++ int i, shift, num_bytes;
++ u32 word;
+
+- while (controller->rx_bytes < xfer->len) {
+-
+- state = readl_relaxed(controller->base + QUP_OPERATIONAL);
+- if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY))
+- break;
++ for (; num_words; num_words--) {
+
+ word = readl_relaxed(controller->base + QUP_INPUT_FIFO);
+
++ num_bytes = min_t(int, xfer->len - controller->rx_bytes,
++ controller->w_size);
++
+ if (!rx_buf) {
+- controller->rx_bytes += w_size;
++ controller->rx_bytes += num_bytes;
+ continue;
+ }
+
+- for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) {
++ for (i = 0; i < num_bytes; i++, controller->rx_bytes++) {
+ /*
+ * The data format depends on bytes per SPI word:
+ * 4 bytes: 0x12345678
+@@ -246,38 +251,80 @@ static void spi_qup_fifo_read(struct spi
+ * 1 byte : 0x00000012
+ */
+ shift = BITS_PER_BYTE;
+- shift *= (w_size - idx - 1);
++ shift *= (controller->w_size - i - 1);
+ rx_buf[controller->rx_bytes] = word >> shift;
+ }
+ }
+ }
+
+-static void spi_qup_fifo_write(struct spi_qup *controller,
++static void spi_qup_read(struct spi_qup *controller,
+ struct spi_transfer *xfer)
+ {
+- const u8 *tx_buf = xfer->tx_buf;
+- u32 word, state, data;
+- int idx, w_size;
++ u32 remainder, words_per_block, num_words;
++ bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
++
++ remainder = DIV_ROUND_UP(xfer->len - controller->rx_bytes,
++ controller->w_size);
++ words_per_block = controller->in_blk_sz >> 2;
++
++ do {
++ /* ACK by clearing service flag */
++ writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
++ controller->base + QUP_OPERATIONAL);
++
++ if (is_block_mode) {
++ num_words = (remainder > words_per_block) ?
++ words_per_block : remainder;
++ } else {
++ if (!spi_qup_is_flag_set(controller,
++ QUP_OP_IN_FIFO_NOT_EMPTY))
++ break;
+
+- w_size = controller->w_size;
++ num_words = 1;
++ }
++
++ /* read up to the maximum transfer size available */
++ spi_qup_read_from_fifo(controller, xfer, num_words);
+
+- while (controller->tx_bytes < xfer->len) {
++ remainder -= num_words;
+
+- state = readl_relaxed(controller->base + QUP_OPERATIONAL);
+- if (state & QUP_OP_OUT_FIFO_FULL)
++ /* if block mode, check to see if next block is available */
++ if (is_block_mode && !spi_qup_is_flag_set(controller,
++ QUP_OP_IN_BLOCK_READ_REQ))
+ break;
+
++ } while (remainder);
++
++ /*
++ * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block
++ * mode reads, it has to be cleared again at the very end
++ */
++ if (is_block_mode && spi_qup_is_flag_set(controller,
++ QUP_OP_MAX_INPUT_DONE_FLAG))
++ writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
++ controller->base + QUP_OPERATIONAL);
++
++}
++
++static void spi_qup_write_to_fifo(struct spi_qup *controller,
++ struct spi_transfer *xfer, u32 num_words)
++{
++ const u8 *tx_buf = xfer->tx_buf;
++ int i, num_bytes;
++ u32 word, data;
++
++ for (; num_words; num_words--) {
+ word = 0;
+- for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) {
+
+- if (!tx_buf) {
+- controller->tx_bytes += w_size;
+- break;
++ num_bytes = min_t(int, xfer->len - controller->tx_bytes,
++ controller->w_size);
++ if (tx_buf)
++ for (i = 0; i < num_bytes; i++) {
++ data = tx_buf[controller->tx_bytes + i];
++ word |= data << (BITS_PER_BYTE * (3 - i));
+ }
+
+- data = tx_buf[controller->tx_bytes];
+- word |= data << (BITS_PER_BYTE * (3 - idx));
+- }
++ controller->tx_bytes += num_bytes;
+
+ writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO);
+ }
+@@ -290,6 +337,44 @@ static void spi_qup_dma_done(void *data)
+ complete(done);
+ }
+
++static void spi_qup_write(struct spi_qup *controller,
++ struct spi_transfer *xfer)
++{
++ bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
++ u32 remainder, words_per_block, num_words;
++
++ remainder = DIV_ROUND_UP(xfer->len - controller->tx_bytes,
++ controller->w_size);
++ words_per_block = controller->out_blk_sz >> 2;
++
++ do {
++ /* ACK by clearing service flag */
++ writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
++ controller->base + QUP_OPERATIONAL);
++
++ if (is_block_mode) {
++ num_words = (remainder > words_per_block) ?
++ words_per_block : remainder;
++ } else {
++ if (spi_qup_is_flag_set(controller,
++ QUP_OP_OUT_FIFO_FULL))
++ break;
++
++ num_words = 1;
++ }
++
++ spi_qup_write_to_fifo(controller, xfer, num_words);
++
++ remainder -= num_words;
++
++ /* if block mode, check to see if next block is available */
++ if (is_block_mode && !spi_qup_is_flag_set(controller,
++ QUP_OP_OUT_BLOCK_WRITE_REQ))
++ break;
++
++ } while (remainder);
++}
++
+ static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer,
+ enum dma_transfer_direction dir,
+ dma_async_tx_callback callback,
+@@ -347,11 +432,13 @@ unsigned long timeout)
+ return ret;
+ }
+
+- if (xfer->rx_buf)
+- rx_done = spi_qup_dma_done;
++ if (!qup->qup_v1) {
++ if (xfer->rx_buf)
++ rx_done = spi_qup_dma_done;
+
+- if (xfer->tx_buf)
+- tx_done = spi_qup_dma_done;
++ if (xfer->tx_buf)
++ tx_done = spi_qup_dma_done;
++ }
+
+ if (xfer->rx_buf) {
+ ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done,
+@@ -398,7 +485,8 @@ static int spi_qup_do_pio(struct spi_mas
+ return ret;
+ }
+
+- spi_qup_fifo_write(qup, xfer);
++ if (qup->mode == QUP_IO_M_MODE_FIFO)
++ spi_qup_write(qup, xfer);
+
+ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+ if (ret) {
+@@ -431,10 +519,11 @@ static irqreturn_t spi_qup_qup_irq(int i
+
+ writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS);
+ writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS);
+- writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
+
+ if (!xfer) {
+- dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n",
++ writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
++ dev_err_ratelimited(controller->dev,
++ "unexpected irq %08x %08x %08x\n",
+ qup_err, spi_err, opflags);
+ return IRQ_HANDLED;
+ }
+@@ -460,12 +549,20 @@ static irqreturn_t spi_qup_qup_irq(int i
+ error = -EIO;
+ }
+
+- if (!spi_qup_is_dma_xfer(controller->mode)) {
++ if (spi_qup_is_dma_xfer(controller->mode)) {
++ writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
++ if (opflags & QUP_OP_IN_SERVICE_FLAG &&
++ opflags & QUP_OP_MAX_INPUT_DONE_FLAG)
++ complete(&controller->done);
++ if (opflags & QUP_OP_OUT_SERVICE_FLAG &&
++ opflags & QUP_OP_MAX_OUTPUT_DONE_FLAG)
++ complete(&controller->dma_tx_done);
++ } else {
+ if (opflags & QUP_OP_IN_SERVICE_FLAG)
+- spi_qup_fifo_read(controller, xfer);
++ spi_qup_read(controller, xfer);
+
+ if (opflags & QUP_OP_OUT_SERVICE_FLAG)
+- spi_qup_fifo_write(controller, xfer);
++ spi_qup_write(controller, xfer);
+ }
+
+ spin_lock_irqsave(&controller->lock, flags);
+@@ -473,6 +570,9 @@ static irqreturn_t spi_qup_qup_irq(int i
+ controller->xfer = xfer;
+ spin_unlock_irqrestore(&controller->lock, flags);
+
++ /* re-read opflags as flags may have changed due to actions above */
++ opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
++
+ if ((controller->rx_bytes == xfer->len &&
+ (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error)
+ complete(&controller->done);
+@@ -516,11 +616,13 @@ static int spi_qup_io_config(struct spi_
+ /* must be zero for FIFO */
+ writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
+ writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+-
+ } else if (spi->master->can_dma &&
+ spi->master->can_dma(spi->master, spi, xfer) &&
+ spi->master->cur_msg_mapped) {
+ controller->mode = QUP_IO_M_MODE_BAM;
++ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
++ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
++ /* must be zero for BLOCK and BAM */
+ writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+
diff --git a/target/linux/ipq806x/patches-4.9/0010-spi-qup-properly-detect-extra-interrupts.patch b/target/linux/ipq806x/patches-4.9/0010-spi-qup-properly-detect-extra-interrupts.patch
new file mode 100644
index 0000000000..74ef25ac16
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0010-spi-qup-properly-detect-extra-interrupts.patch
@@ -0,0 +1,61 @@
+From 543618f5388d487ba88e3d5304c161fc3ccf61d1 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Thu, 10 Mar 2016 16:44:55 -0600
+Subject: [PATCH 10/37] spi: qup: properly detect extra interrupts
+
+It's possible for a SPI transaction to complete and get another
+interrupt and have it processed on the same spi_transfer before the
+transfer_one can set it to NULL.
+
+This masks unexpected interrupts, so let's set the spi_transfer to
+NULL in the interrupt once the transaction is done. So we can
+properly detect these bad interrupts and print warning messages.
+
+Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
+---
+ drivers/spi/spi-qup.c | 15 +++++++++------
+ 1 file changed, 9 insertions(+), 6 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -507,6 +507,7 @@ static irqreturn_t spi_qup_qup_irq(int i
+ u32 opflags, qup_err, spi_err;
+ unsigned long flags;
+ int error = 0;
++ bool done = 0;
+
+ spin_lock_irqsave(&controller->lock, flags);
+ xfer = controller->xfer;
+@@ -565,16 +566,19 @@ static irqreturn_t spi_qup_qup_irq(int i
+ spi_qup_write(controller, xfer);
+ }
+
+- spin_lock_irqsave(&controller->lock, flags);
+- controller->error = error;
+- controller->xfer = xfer;
+- spin_unlock_irqrestore(&controller->lock, flags);
+-
+ /* re-read opflags as flags may have changed due to actions above */
+ opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
+
+ if ((controller->rx_bytes == xfer->len &&
+ (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error)
++ done = true;
++
++ spin_lock_irqsave(&controller->lock, flags);
++ controller->error = error;
++ controller->xfer = done ? NULL : xfer;
++ spin_unlock_irqrestore(&controller->lock, flags);
++
++ if (done)
+ complete(&controller->done);
+
+ return IRQ_HANDLED;
+@@ -767,7 +771,6 @@ static int spi_qup_transfer_one(struct s
+ exit:
+ spi_qup_set_state(controller, QUP_STATE_RESET);
+ spin_lock_irqsave(&controller->lock, flags);
+- controller->xfer = NULL;
+ if (!ret)
+ ret = controller->error;
+ spin_unlock_irqrestore(&controller->lock, flags);
diff --git a/target/linux/ipq806x/patches-4.9/0011-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch b/target/linux/ipq806x/patches-4.9/0011-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch
new file mode 100644
index 0000000000..da1569fd12
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0011-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch
@@ -0,0 +1,26 @@
+From ba96e9449a63acd658d8ad0a5b3755b559410999 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Thu, 10 Mar 2016 16:48:27 -0600
+Subject: [PATCH 11/37] spi: qup: don't re-read opflags to see if transaction
+ is done for reads
+
+For reads, we will get another interrupt so we need to handle things
+then. For writes, we can finish up now.
+
+Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
+---
+ drivers/spi/spi-qup.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -567,7 +567,8 @@ static irqreturn_t spi_qup_qup_irq(int i
+ }
+
+ /* re-read opflags as flags may have changed due to actions above */
+- opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
++ if (opflags & QUP_OP_OUT_SERVICE_FLAG)
++ opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
+
+ if ((controller->rx_bytes == xfer->len &&
+ (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error)
diff --git a/target/linux/ipq806x/patches-4.9/0012-spi-qup-refactor-spi_qup_io_config-in-two-functions.patch b/target/linux/ipq806x/patches-4.9/0012-spi-qup-refactor-spi_qup_io_config-in-two-functions.patch
new file mode 100644
index 0000000000..7beb5d9701
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0012-spi-qup-refactor-spi_qup_io_config-in-two-functions.patch
@@ -0,0 +1,202 @@
+From ef00ad56d728618203358d9eba7ca8e7eb48e701 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Tue, 26 Apr 2016 12:57:46 -0500
+Subject: [PATCH 12/37] spi: qup: refactor spi_qup_io_config in two functions
+
+This is preparation for handling transactions larger than 64K-1 bytes in
+block mode which is currently unsupported quietly fails.
+
+We need to break these into two functions 1) prep is called once per
+spi_message and 2) io_config is calle once per spi-qup bus transaction
+
+This is just refactoring, there should be no functional change
+
+Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
+---
+ drivers/spi/spi-qup.c | 141 ++++++++++++++++++++++++++++++-------------------
+ 1 file changed, 86 insertions(+), 55 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -585,12 +585,11 @@ static irqreturn_t spi_qup_qup_irq(int i
+ return IRQ_HANDLED;
+ }
+
+-/* set clock freq ... bits per word */
+-static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
++/* set clock freq ... bits per word, determine mode */
++static int spi_qup_io_prep(struct spi_device *spi, struct spi_transfer *xfer)
+ {
+ struct spi_qup *controller = spi_master_get_devdata(spi->master);
+- u32 config, iomode, control;
+- int ret, n_words;
++ int ret;
+
+ if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
+ dev_err(controller->dev, "too big size for loopback %d > %d\n",
+@@ -605,56 +604,94 @@ static int spi_qup_io_config(struct spi_
+ return -EIO;
+ }
+
+- if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
+- dev_err(controller->dev, "cannot set RESET state\n");
+- return -EIO;
+- }
+-
+ controller->w_size = DIV_ROUND_UP(xfer->bits_per_word, 8);
+ controller->n_words = xfer->len / controller->w_size;
+- n_words = controller->n_words;
+
+- if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
++ if (controller->n_words <= (controller->in_fifo_sz / sizeof(u32)))
+ controller->mode = QUP_IO_M_MODE_FIFO;
+- writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
+- writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT);
+- /* must be zero for FIFO */
+- writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
+- writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+- } else if (spi->master->can_dma &&
+- spi->master->can_dma(spi->master, spi, xfer) &&
+- spi->master->cur_msg_mapped) {
++ else if (spi->master->can_dma &&
++ spi->master->can_dma(spi->master, spi, xfer) &&
++ spi->master->cur_msg_mapped)
+ controller->mode = QUP_IO_M_MODE_BAM;
+- writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
+- writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
+- /* must be zero for BLOCK and BAM */
+- writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+- writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+-
+- if (!controller->qup_v1) {
+- void __iomem *input_cnt;
+-
+- input_cnt = controller->base + QUP_MX_INPUT_CNT;
+- /*
+- * for DMA transfers, both QUP_MX_INPUT_CNT and
+- * QUP_MX_OUTPUT_CNT must be zero to all cases but one.
+- * That case is a non-balanced transfer when there is
+- * only a rx_buf.
+- */
+- if (xfer->tx_buf)
+- writel_relaxed(0, input_cnt);
+- else
+- writel_relaxed(n_words, input_cnt);
++ else
++ controller->mode = QUP_IO_M_MODE_BLOCK;
++
++ return 0;
++}
+
++/* prep qup for another spi transaction of specific type */
++static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
++{
++ struct spi_qup *controller = spi_master_get_devdata(spi->master);
++ u32 config, iomode, control;
++ unsigned long flags;
++
++ reinit_completion(&controller->done);
++ reinit_completion(&controller->dma_tx_done);
++
++ spin_lock_irqsave(&controller->lock, flags);
++ controller->xfer = xfer;
++ controller->error = 0;
++ controller->rx_bytes = 0;
++ controller->tx_bytes = 0;
++ spin_unlock_irqrestore(&controller->lock, flags);
++
++
++ if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
++ dev_err(controller->dev, "cannot set RESET state\n");
++ return -EIO;
++ }
++
++ switch (controller->mode) {
++ case QUP_IO_M_MODE_FIFO:
++ writel_relaxed(controller->n_words,
++ controller->base + QUP_MX_READ_CNT);
++ writel_relaxed(controller->n_words,
++ controller->base + QUP_MX_WRITE_CNT);
++ /* must be zero for FIFO */
++ writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
+ writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+- }
+- } else {
+- controller->mode = QUP_IO_M_MODE_BLOCK;
+- writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
+- writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
+- /* must be zero for BLOCK and BAM */
+- writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+- writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
++ break;
++ case QUP_IO_M_MODE_BAM:
++ writel_relaxed(controller->n_words,
++ controller->base + QUP_MX_INPUT_CNT);
++ writel_relaxed(controller->n_words,
++ controller->base + QUP_MX_OUTPUT_CNT);
++ /* must be zero for BLOCK and BAM */
++ writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
++ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
++ if (!controller->qup_v1) {
++ void __iomem *input_cnt;
++
++ input_cnt = controller->base + QUP_MX_INPUT_CNT;
++ /*
++ * for DMA transfers, both QUP_MX_INPUT_CNT and
++ * QUP_MX_OUTPUT_CNT must be zero to all cases
++ * but one. That case is a non-balanced
++ * transfer when there is only a rx_buf.
++ */
++ if (xfer->tx_buf)
++ writel_relaxed(0, input_cnt);
++ else
++ writel_relaxed(controller->n_words,
++ input_cnt);
++
++ writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
++ }
++ break;
++ case QUP_IO_M_MODE_BLOCK:
++ writel_relaxed(controller->n_words,
++ controller->base + QUP_MX_INPUT_CNT);
++ writel_relaxed(controller->n_words,
++ controller->base + QUP_MX_OUTPUT_CNT);
++ /* must be zero for BLOCK and BAM */
++ writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
++ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
++ break;
++ default:
++ dev_err(controller->dev, "unknown mode = %d\n",
++ controller->mode);
++ return -EIO;
+ }
+
+ iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
+@@ -743,6 +780,10 @@ static int spi_qup_transfer_one(struct s
+ unsigned long timeout, flags;
+ int ret = -EIO;
+
++ ret = spi_qup_io_prep(spi, xfer);
++ if (ret)
++ return ret;
++
+ ret = spi_qup_io_config(spi, xfer);
+ if (ret)
+ return ret;
+@@ -751,16 +792,6 @@ static int spi_qup_transfer_one(struct s
+ timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
+ timeout = 100 * msecs_to_jiffies(timeout);
+
+- reinit_completion(&controller->done);
+- reinit_completion(&controller->dma_tx_done);
+-
+- spin_lock_irqsave(&controller->lock, flags);
+- controller->xfer = xfer;
+- controller->error = 0;
+- controller->rx_bytes = 0;
+- controller->tx_bytes = 0;
+- spin_unlock_irqrestore(&controller->lock, flags);
+-
+ if (spi_qup_is_dma_xfer(controller->mode))
+ ret = spi_qup_do_dma(master, xfer, timeout);
+ else
diff --git a/target/linux/ipq806x/patches-4.9/0013-spi-qup-call-io_config-in-mode-specific-function.patch b/target/linux/ipq806x/patches-4.9/0013-spi-qup-call-io_config-in-mode-specific-function.patch
new file mode 100644
index 0000000000..3aebaaca17
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0013-spi-qup-call-io_config-in-mode-specific-function.patch
@@ -0,0 +1,391 @@
+From 9263d98e255e1d51b41c752d53e39877728a9419 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Tue, 26 Apr 2016 13:14:45 -0500
+Subject: [PATCH 13/37] spi: qup: call io_config in mode specific function
+
+DMA transactions should only only need to call io_config only once, but
+block mode might call it several times to setup several transactions so
+it can handle reads/writes larger than the max size per transaction, so
+we move the call to the do_ functions.
+
+This is just refactoring, there should be no functional change
+
+Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
+---
+ drivers/spi/spi-qup.c | 327 +++++++++++++++++++++++++------------------------
+ 1 file changed, 166 insertions(+), 161 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -418,13 +418,170 @@ static void spi_qup_dma_terminate(struct
+ dmaengine_terminate_all(master->dma_rx);
+ }
+
+-static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer,
++/* prep qup for another spi transaction of specific type */
++static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
++{
++ struct spi_qup *controller = spi_master_get_devdata(spi->master);
++ u32 config, iomode, control;
++ unsigned long flags;
++
++ reinit_completion(&controller->done);
++ reinit_completion(&controller->dma_tx_done);
++
++ spin_lock_irqsave(&controller->lock, flags);
++ controller->xfer = xfer;
++ controller->error = 0;
++ controller->rx_bytes = 0;
++ controller->tx_bytes = 0;
++ spin_unlock_irqrestore(&controller->lock, flags);
++
++ if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
++ dev_err(controller->dev, "cannot set RESET state\n");
++ return -EIO;
++ }
++
++ switch (controller->mode) {
++ case QUP_IO_M_MODE_FIFO:
++ writel_relaxed(controller->n_words,
++ controller->base + QUP_MX_READ_CNT);
++ writel_relaxed(controller->n_words,
++ controller->base + QUP_MX_WRITE_CNT);
++ /* must be zero for FIFO */
++ writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
++ writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
++ break;
++ case QUP_IO_M_MODE_BAM:
++ writel_relaxed(controller->n_words,
++ controller->base + QUP_MX_INPUT_CNT);
++ writel_relaxed(controller->n_words,
++ controller->base + QUP_MX_OUTPUT_CNT);
++ /* must be zero for BLOCK and BAM */
++ writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
++ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
++ if (!controller->qup_v1) {
++ void __iomem *input_cnt;
++
++ input_cnt = controller->base + QUP_MX_INPUT_CNT;
++ /*
++ * for DMA transfers, both QUP_MX_INPUT_CNT and
++ * QUP_MX_OUTPUT_CNT must be zero to all cases
++ * but one. That case is a non-balanced
++ * transfer when there is only a rx_buf.
++ */
++ if (xfer->tx_buf)
++ writel_relaxed(0, input_cnt);
++ else
++ writel_relaxed(controller->n_words,
++ input_cnt);
++
++ writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
++ }
++ break;
++ case QUP_IO_M_MODE_BLOCK:
++ writel_relaxed(controller->n_words,
++ controller->base + QUP_MX_INPUT_CNT);
++ writel_relaxed(controller->n_words,
++ controller->base + QUP_MX_OUTPUT_CNT);
++ /* must be zero for BLOCK and BAM */
++ writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
++ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
++ break;
++ default:
++ dev_err(controller->dev, "unknown mode = %d\n",
++ controller->mode);
++ return -EIO;
++ }
++
++ iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
++ /* Set input and output transfer mode */
++ iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
++
++ if (!spi_qup_is_dma_xfer(controller->mode))
++ iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
++ else
++ iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
++
++ iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
++ iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
++
++ writel_relaxed(iomode, controller->base + QUP_IO_M_MODES);
++
++ control = readl_relaxed(controller->base + SPI_IO_CONTROL);
++
++ if (spi->mode & SPI_CPOL)
++ control |= SPI_IO_C_CLK_IDLE_HIGH;
++ else
++ control &= ~SPI_IO_C_CLK_IDLE_HIGH;
++
++ writel_relaxed(control, controller->base + SPI_IO_CONTROL);
++
++ config = readl_relaxed(controller->base + SPI_CONFIG);
++
++ if (spi->mode & SPI_LOOP)
++ config |= SPI_CONFIG_LOOPBACK;
++ else
++ config &= ~SPI_CONFIG_LOOPBACK;
++
++ if (spi->mode & SPI_CPHA)
++ config &= ~SPI_CONFIG_INPUT_FIRST;
++ else
++ config |= SPI_CONFIG_INPUT_FIRST;
++
++ /*
++ * HS_MODE improves signal stability for spi-clk high rates,
++ * but is invalid in loop back mode.
++ */
++ if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(spi->mode & SPI_LOOP))
++ config |= SPI_CONFIG_HS_MODE;
++ else
++ config &= ~SPI_CONFIG_HS_MODE;
++
++ writel_relaxed(config, controller->base + SPI_CONFIG);
++
++ config = readl_relaxed(controller->base + QUP_CONFIG);
++ config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N);
++ config |= xfer->bits_per_word - 1;
++ config |= QUP_CONFIG_SPI_MODE;
++
++ if (spi_qup_is_dma_xfer(controller->mode)) {
++ if (!xfer->tx_buf)
++ config |= QUP_CONFIG_NO_OUTPUT;
++ if (!xfer->rx_buf)
++ config |= QUP_CONFIG_NO_INPUT;
++ }
++
++ writel_relaxed(config, controller->base + QUP_CONFIG);
++
++ /* only write to OPERATIONAL_MASK when register is present */
++ if (!controller->qup_v1) {
++ u32 mask = 0;
++
++ /*
++ * mask INPUT and OUTPUT service flags to prevent IRQs on FIFO
++ * status change in BAM mode
++ */
++
++ if (spi_qup_is_dma_xfer(controller->mode))
++ mask = QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG;
++
++ writel_relaxed(mask, controller->base + QUP_OPERATIONAL_MASK);
++ }
++
++ return 0;
++}
++
++static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
+ unsigned long timeout)
+ {
++ struct spi_master *master = spi->master;
+ struct spi_qup *qup = spi_master_get_devdata(master);
+ dma_async_tx_callback rx_done = NULL, tx_done = NULL;
+ int ret;
+
++ ret = spi_qup_io_config(spi, xfer);
++ if (ret)
++ return ret;
++
+ /* before issuing the descriptors, set the QUP to run */
+ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+ if (ret) {
+@@ -467,12 +624,17 @@ unsigned long timeout)
+ return ret;
+ }
+
+-static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer,
++static int spi_qup_do_pio(struct spi_device *spi, struct spi_transfer *xfer,
+ unsigned long timeout)
+ {
++ struct spi_master *master = spi->master;
+ struct spi_qup *qup = spi_master_get_devdata(master);
+ int ret;
+
++ ret = spi_qup_io_config(spi, xfer);
++ if (ret)
++ return ret;
++
+ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+ if (ret) {
+ dev_warn(qup->dev, "cannot set RUN state\n");
+@@ -619,159 +781,6 @@ static int spi_qup_io_prep(struct spi_de
+ return 0;
+ }
+
+-/* prep qup for another spi transaction of specific type */
+-static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+-{
+- struct spi_qup *controller = spi_master_get_devdata(spi->master);
+- u32 config, iomode, control;
+- unsigned long flags;
+-
+- reinit_completion(&controller->done);
+- reinit_completion(&controller->dma_tx_done);
+-
+- spin_lock_irqsave(&controller->lock, flags);
+- controller->xfer = xfer;
+- controller->error = 0;
+- controller->rx_bytes = 0;
+- controller->tx_bytes = 0;
+- spin_unlock_irqrestore(&controller->lock, flags);
+-
+-
+- if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
+- dev_err(controller->dev, "cannot set RESET state\n");
+- return -EIO;
+- }
+-
+- switch (controller->mode) {
+- case QUP_IO_M_MODE_FIFO:
+- writel_relaxed(controller->n_words,
+- controller->base + QUP_MX_READ_CNT);
+- writel_relaxed(controller->n_words,
+- controller->base + QUP_MX_WRITE_CNT);
+- /* must be zero for FIFO */
+- writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
+- writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+- break;
+- case QUP_IO_M_MODE_BAM:
+- writel_relaxed(controller->n_words,
+- controller->base + QUP_MX_INPUT_CNT);
+- writel_relaxed(controller->n_words,
+- controller->base + QUP_MX_OUTPUT_CNT);
+- /* must be zero for BLOCK and BAM */
+- writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+- writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+- if (!controller->qup_v1) {
+- void __iomem *input_cnt;
+-
+- input_cnt = controller->base + QUP_MX_INPUT_CNT;
+- /*
+- * for DMA transfers, both QUP_MX_INPUT_CNT and
+- * QUP_MX_OUTPUT_CNT must be zero to all cases
+- * but one. That case is a non-balanced
+- * transfer when there is only a rx_buf.
+- */
+- if (xfer->tx_buf)
+- writel_relaxed(0, input_cnt);
+- else
+- writel_relaxed(controller->n_words,
+- input_cnt);
+-
+- writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+- }
+- break;
+- case QUP_IO_M_MODE_BLOCK:
+- writel_relaxed(controller->n_words,
+- controller->base + QUP_MX_INPUT_CNT);
+- writel_relaxed(controller->n_words,
+- controller->base + QUP_MX_OUTPUT_CNT);
+- /* must be zero for BLOCK and BAM */
+- writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+- writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+- break;
+- default:
+- dev_err(controller->dev, "unknown mode = %d\n",
+- controller->mode);
+- return -EIO;
+- }
+-
+- iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
+- /* Set input and output transfer mode */
+- iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
+-
+- if (!spi_qup_is_dma_xfer(controller->mode))
+- iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
+- else
+- iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
+-
+- iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
+- iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
+-
+- writel_relaxed(iomode, controller->base + QUP_IO_M_MODES);
+-
+- control = readl_relaxed(controller->base + SPI_IO_CONTROL);
+-
+- if (spi->mode & SPI_CPOL)
+- control |= SPI_IO_C_CLK_IDLE_HIGH;
+- else
+- control &= ~SPI_IO_C_CLK_IDLE_HIGH;
+-
+- writel_relaxed(control, controller->base + SPI_IO_CONTROL);
+-
+- config = readl_relaxed(controller->base + SPI_CONFIG);
+-
+- if (spi->mode & SPI_LOOP)
+- config |= SPI_CONFIG_LOOPBACK;
+- else
+- config &= ~SPI_CONFIG_LOOPBACK;
+-
+- if (spi->mode & SPI_CPHA)
+- config &= ~SPI_CONFIG_INPUT_FIRST;
+- else
+- config |= SPI_CONFIG_INPUT_FIRST;
+-
+- /*
+- * HS_MODE improves signal stability for spi-clk high rates,
+- * but is invalid in loop back mode.
+- */
+- if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(spi->mode & SPI_LOOP))
+- config |= SPI_CONFIG_HS_MODE;
+- else
+- config &= ~SPI_CONFIG_HS_MODE;
+-
+- writel_relaxed(config, controller->base + SPI_CONFIG);
+-
+- config = readl_relaxed(controller->base + QUP_CONFIG);
+- config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N);
+- config |= xfer->bits_per_word - 1;
+- config |= QUP_CONFIG_SPI_MODE;
+-
+- if (spi_qup_is_dma_xfer(controller->mode)) {
+- if (!xfer->tx_buf)
+- config |= QUP_CONFIG_NO_OUTPUT;
+- if (!xfer->rx_buf)
+- config |= QUP_CONFIG_NO_INPUT;
+- }
+-
+- writel_relaxed(config, controller->base + QUP_CONFIG);
+-
+- /* only write to OPERATIONAL_MASK when register is present */
+- if (!controller->qup_v1) {
+- u32 mask = 0;
+-
+- /*
+- * mask INPUT and OUTPUT service flags to prevent IRQs on FIFO
+- * status change in BAM mode
+- */
+-
+- if (spi_qup_is_dma_xfer(controller->mode))
+- mask = QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG;
+-
+- writel_relaxed(mask, controller->base + QUP_OPERATIONAL_MASK);
+- }
+-
+- return 0;
+-}
+-
+ static int spi_qup_transfer_one(struct spi_master *master,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+@@ -784,18 +793,14 @@ static int spi_qup_transfer_one(struct s
+ if (ret)
+ return ret;
+
+- ret = spi_qup_io_config(spi, xfer);
+- if (ret)
+- return ret;
+-
+ timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC);
+ timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
+ timeout = 100 * msecs_to_jiffies(timeout);
+
+ if (spi_qup_is_dma_xfer(controller->mode))
+- ret = spi_qup_do_dma(master, xfer, timeout);
++ ret = spi_qup_do_dma(spi, xfer, timeout);
+ else
+- ret = spi_qup_do_pio(master, xfer, timeout);
++ ret = spi_qup_do_pio(spi, xfer, timeout);
+
+ if (ret)
+ goto exit;
diff --git a/target/linux/ipq806x/patches-4.9/0014-spi-qup-allow-block-mode-to-generate-multiple-transa.patch b/target/linux/ipq806x/patches-4.9/0014-spi-qup-allow-block-mode-to-generate-multiple-transa.patch
new file mode 100644
index 0000000000..6c275f61d3
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0014-spi-qup-allow-block-mode-to-generate-multiple-transa.patch
@@ -0,0 +1,268 @@
+From 05a08cc5620df0fcf8e260feee04b9671705723e Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Tue, 26 Apr 2016 15:46:24 -0500
+Subject: [PATCH 14/37] spi: qup: allow block mode to generate multiple
+ transactions
+
+This let's you write more to the SPI bus than 64K-1 which is important
+if the block size of a SPI device is >= 64K or some other device wants
+to something larger.
+
+This has the benefit of completly removing spi_message from the spi-qup
+transactions
+
+Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
+---
+ drivers/spi/spi-qup.c | 120 ++++++++++++++++++++++++++++++-------------------
+ 1 file changed, 75 insertions(+), 45 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -120,7 +120,7 @@
+
+ #define SPI_NUM_CHIPSELECTS 4
+
+-#define SPI_MAX_DMA_XFER (SZ_64K - 64)
++#define SPI_MAX_XFER (SZ_64K - 64)
+
+ /* high speed mode is when bus rate is greater then 26MHz */
+ #define SPI_HS_MIN_RATE 26000000
+@@ -150,6 +150,8 @@ struct spi_qup {
+ int n_words;
+ int tx_bytes;
+ int rx_bytes;
++ const u8 *tx_buf;
++ u8 *rx_buf;
+ int qup_v1;
+
+ int mode;
+@@ -172,6 +174,12 @@ static inline bool spi_qup_is_dma_xfer(i
+ return false;
+ }
+
++/* get's the transaction size length */
++static inline unsigned spi_qup_len(struct spi_qup *controller)
++{
++ return controller->n_words * controller->w_size;
++}
++
+ static inline bool spi_qup_is_valid_state(struct spi_qup *controller)
+ {
+ u32 opstate = readl_relaxed(controller->base + QUP_STATE);
+@@ -224,10 +232,9 @@ static int spi_qup_set_state(struct spi_
+ return 0;
+ }
+
+-static void spi_qup_read_from_fifo(struct spi_qup *controller,
+- struct spi_transfer *xfer, u32 num_words)
++static void spi_qup_read_from_fifo(struct spi_qup *controller, u32 num_words)
+ {
+- u8 *rx_buf = xfer->rx_buf;
++ u8 *rx_buf = controller->rx_buf;
+ int i, shift, num_bytes;
+ u32 word;
+
+@@ -235,7 +242,7 @@ static void spi_qup_read_from_fifo(struc
+
+ word = readl_relaxed(controller->base + QUP_INPUT_FIFO);
+
+- num_bytes = min_t(int, xfer->len - controller->rx_bytes,
++ num_bytes = min_t(int, spi_qup_len(controller) - controller->rx_bytes,
+ controller->w_size);
+
+ if (!rx_buf) {
+@@ -257,13 +264,12 @@ static void spi_qup_read_from_fifo(struc
+ }
+ }
+
+-static void spi_qup_read(struct spi_qup *controller,
+- struct spi_transfer *xfer)
++static void spi_qup_read(struct spi_qup *controller)
+ {
+ u32 remainder, words_per_block, num_words;
+ bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
+
+- remainder = DIV_ROUND_UP(xfer->len - controller->rx_bytes,
++ remainder = DIV_ROUND_UP(spi_qup_len(controller) - controller->rx_bytes,
+ controller->w_size);
+ words_per_block = controller->in_blk_sz >> 2;
+
+@@ -284,7 +290,7 @@ static void spi_qup_read(struct spi_qup
+ }
+
+ /* read up to the maximum transfer size available */
+- spi_qup_read_from_fifo(controller, xfer, num_words);
++ spi_qup_read_from_fifo(controller, num_words);
+
+ remainder -= num_words;
+
+@@ -306,17 +312,16 @@ static void spi_qup_read(struct spi_qup
+
+ }
+
+-static void spi_qup_write_to_fifo(struct spi_qup *controller,
+- struct spi_transfer *xfer, u32 num_words)
++static void spi_qup_write_to_fifo(struct spi_qup *controller, u32 num_words)
+ {
+- const u8 *tx_buf = xfer->tx_buf;
++ const u8 *tx_buf = controller->tx_buf;
+ int i, num_bytes;
+ u32 word, data;
+
+ for (; num_words; num_words--) {
+ word = 0;
+
+- num_bytes = min_t(int, xfer->len - controller->tx_bytes,
++ num_bytes = min_t(int, spi_qup_len(controller) - controller->tx_bytes,
+ controller->w_size);
+ if (tx_buf)
+ for (i = 0; i < num_bytes; i++) {
+@@ -337,13 +342,12 @@ static void spi_qup_dma_done(void *data)
+ complete(done);
+ }
+
+-static void spi_qup_write(struct spi_qup *controller,
+- struct spi_transfer *xfer)
++static void spi_qup_write(struct spi_qup *controller)
+ {
+ bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
+ u32 remainder, words_per_block, num_words;
+
+- remainder = DIV_ROUND_UP(xfer->len - controller->tx_bytes,
++ remainder = DIV_ROUND_UP(spi_qup_len(controller) - controller->tx_bytes,
+ controller->w_size);
+ words_per_block = controller->out_blk_sz >> 2;
+
+@@ -363,7 +367,7 @@ static void spi_qup_write(struct spi_qup
+ num_words = 1;
+ }
+
+- spi_qup_write_to_fifo(controller, xfer, num_words);
++ spi_qup_write_to_fifo(controller, num_words);
+
+ remainder -= num_words;
+
+@@ -629,35 +633,61 @@ static int spi_qup_do_pio(struct spi_dev
+ {
+ struct spi_master *master = spi->master;
+ struct spi_qup *qup = spi_master_get_devdata(master);
+- int ret;
++ int ret, n_words, iterations, offset = 0;
+
+- ret = spi_qup_io_config(spi, xfer);
+- if (ret)
+- return ret;
++ n_words = qup->n_words;
++ iterations = n_words / SPI_MAX_XFER; /* round down */
+
+- ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+- if (ret) {
+- dev_warn(qup->dev, "cannot set RUN state\n");
+- return ret;
+- }
++ qup->rx_buf = xfer->rx_buf;
++ qup->tx_buf = xfer->tx_buf;
+
+- ret = spi_qup_set_state(qup, QUP_STATE_PAUSE);
+- if (ret) {
+- dev_warn(qup->dev, "cannot set PAUSE state\n");
+- return ret;
+- }
++ do {
++ if (iterations)
++ qup->n_words = SPI_MAX_XFER;
++ else
++ qup->n_words = n_words % SPI_MAX_XFER;
++
++ if (qup->tx_buf && offset)
++ qup->tx_buf = xfer->tx_buf + offset * SPI_MAX_XFER;
++
++ if (qup->rx_buf && offset)
++ qup->rx_buf = xfer->rx_buf + offset * SPI_MAX_XFER;
++
++ /* if the transaction is small enough, we need
++ * to fallback to FIFO mode */
++ if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32)))
++ qup->mode = QUP_IO_M_MODE_FIFO;
+
+- if (qup->mode == QUP_IO_M_MODE_FIFO)
+- spi_qup_write(qup, xfer);
++ ret = spi_qup_io_config(spi, xfer);
++ if (ret)
++ return ret;
+
+- ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+- if (ret) {
+- dev_warn(qup->dev, "cannot set RUN state\n");
+- return ret;
+- }
++ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
++ if (ret) {
++ dev_warn(qup->dev, "cannot set RUN state\n");
++ return ret;
++ }
+
+- if (!wait_for_completion_timeout(&qup->done, timeout))
+- return -ETIMEDOUT;
++ ret = spi_qup_set_state(qup, QUP_STATE_PAUSE);
++ if (ret) {
++ dev_warn(qup->dev, "cannot set PAUSE state\n");
++ return ret;
++ }
++
++ if (qup->mode == QUP_IO_M_MODE_FIFO)
++ spi_qup_write(qup);
++
++ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
++ if (ret) {
++ dev_warn(qup->dev, "cannot set RUN state\n");
++ return ret;
++ }
++
++ if (!wait_for_completion_timeout(&qup->done, timeout))
++ return -ETIMEDOUT;
++
++ offset++;
++ } while (iterations--);
+
+ return 0;
+ }
+@@ -722,17 +752,17 @@ static irqreturn_t spi_qup_qup_irq(int i
+ complete(&controller->dma_tx_done);
+ } else {
+ if (opflags & QUP_OP_IN_SERVICE_FLAG)
+- spi_qup_read(controller, xfer);
++ spi_qup_read(controller);
+
+ if (opflags & QUP_OP_OUT_SERVICE_FLAG)
+- spi_qup_write(controller, xfer);
++ spi_qup_write(controller);
+ }
+
+ /* re-read opflags as flags may have changed due to actions above */
+ if (opflags & QUP_OP_OUT_SERVICE_FLAG)
+ opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
+
+- if ((controller->rx_bytes == xfer->len &&
++ if ((controller->rx_bytes == spi_qup_len(controller) &&
+ (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error)
+ done = true;
+
+@@ -794,7 +824,7 @@ static int spi_qup_transfer_one(struct s
+ return ret;
+
+ timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC);
+- timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
++ timeout = DIV_ROUND_UP(min_t(unsigned long, SPI_MAX_XFER, xfer->len) * 8, timeout);
+ timeout = 100 * msecs_to_jiffies(timeout);
+
+ if (spi_qup_is_dma_xfer(controller->mode))
+@@ -983,7 +1013,7 @@ static int spi_qup_probe(struct platform
+ master->dev.of_node = pdev->dev.of_node;
+ master->auto_runtime_pm = true;
+ master->dma_alignment = dma_get_cache_alignment();
+- master->max_dma_len = SPI_MAX_DMA_XFER;
++ master->max_dma_len = SPI_MAX_XFER;
+
+ platform_set_drvdata(pdev, master);
+
diff --git a/target/linux/ipq806x/patches-4.9/0015-spi-qup-refactor-spi_qup_prep_sg-to-be-more-take-spe.patch b/target/linux/ipq806x/patches-4.9/0015-spi-qup-refactor-spi_qup_prep_sg-to-be-more-take-spe.patch
new file mode 100644
index 0000000000..90ffe863e1
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0015-spi-qup-refactor-spi_qup_prep_sg-to-be-more-take-spe.patch
@@ -0,0 +1,73 @@
+From a24914d34a4c6df4323c6d98950166600da79bc6 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Wed, 4 May 2016 16:33:42 -0500
+Subject: [PATCH 15/37] spi: qup: refactor spi_qup_prep_sg to be more take
+ specific sgl and nent
+
+This is in preparation for splitting DMA into multiple transacations,
+this contains no code changes just refactoring.
+
+Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
+---
+ drivers/spi/spi-qup.c | 28 +++++++++++-----------------
+ 1 file changed, 11 insertions(+), 17 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -379,27 +379,19 @@ static void spi_qup_write(struct spi_qup
+ } while (remainder);
+ }
+
+-static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer,
+- enum dma_transfer_direction dir,
+- dma_async_tx_callback callback,
+- void *data)
++static int spi_qup_prep_sg(struct spi_master *master, struct scatterlist *sgl,
++ unsigned int nents, enum dma_transfer_direction dir,
++ dma_async_tx_callback callback, void *data)
+ {
+ unsigned long flags = DMA_PREP_INTERRUPT | DMA_PREP_FENCE;
+ struct dma_async_tx_descriptor *desc;
+- struct scatterlist *sgl;
+ struct dma_chan *chan;
+ dma_cookie_t cookie;
+- unsigned int nents;
+
+- if (dir == DMA_MEM_TO_DEV) {
++ if (dir == DMA_MEM_TO_DEV)
+ chan = master->dma_tx;
+- nents = xfer->tx_sg.nents;
+- sgl = xfer->tx_sg.sgl;
+- } else {
++ else
+ chan = master->dma_rx;
+- nents = xfer->rx_sg.nents;
+- sgl = xfer->rx_sg.sgl;
+- }
+
+ desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags);
+ if (IS_ERR_OR_NULL(desc))
+@@ -602,8 +594,9 @@ unsigned long timeout)
+ }
+
+ if (xfer->rx_buf) {
+- ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done,
+- &qup->done);
++ ret = spi_qup_prep_sg(master, xfer->rx_sg.sgl,
++ xfer->rx_sg.nents, DMA_DEV_TO_MEM,
++ rx_done, &qup->done);
+ if (ret)
+ return ret;
+
+@@ -611,8 +604,9 @@ unsigned long timeout)
+ }
+
+ if (xfer->tx_buf) {
+- ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done,
+- &qup->dma_tx_done);
++ ret = spi_qup_prep_sg(master, xfer->tx_sg.sgl,
++ xfer->tx_sg.nents, DMA_MEM_TO_DEV,
++ tx_done, &qup->dma_tx_done);
+ if (ret)
+ return ret;
+
diff --git a/target/linux/ipq806x/patches-4.9/0016-spi-qup-allow-mulitple-DMA-transactions-per-spi-xfer.patch b/target/linux/ipq806x/patches-4.9/0016-spi-qup-allow-mulitple-DMA-transactions-per-spi-xfer.patch
new file mode 100644
index 0000000000..de324ffd6e
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0016-spi-qup-allow-mulitple-DMA-transactions-per-spi-xfer.patch
@@ -0,0 +1,166 @@
+From 6b2bb8803f19116bad41a271f9035d4c853f4553 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Thu, 5 May 2016 10:07:11 -0500
+Subject: [PATCH 16/37] spi: qup: allow mulitple DMA transactions per spi xfer
+
+Much like the block mode changes, we are breaking up DMA transactions
+into 64K chunks so we can reset the QUP engine.
+
+Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
+---
+ drivers/spi/spi-qup.c | 120 +++++++++++++++++++++++++++++++++++--------------
+ 1 file changed, 86 insertions(+), 34 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -566,6 +566,21 @@ static int spi_qup_io_config(struct spi_
+ return 0;
+ }
+
++static unsigned int spi_qup_sgl_get_size(struct scatterlist *sgl, unsigned int nents)
++{
++ struct scatterlist *sg;
++ int i;
++ unsigned int length = 0;
++
++ if (!nents)
++ return 0;
++
++ for_each_sg(sgl, sg, nents, i)
++ length += sg_dma_len(sg);
++
++ return length;
++}
++
+ static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
+ unsigned long timeout)
+ {
+@@ -573,53 +588,90 @@ unsigned long timeout)
+ struct spi_qup *qup = spi_master_get_devdata(master);
+ dma_async_tx_callback rx_done = NULL, tx_done = NULL;
+ int ret;
++ struct scatterlist *tx_sgl, *rx_sgl;
+
+- ret = spi_qup_io_config(spi, xfer);
+- if (ret)
+- return ret;
+-
+- /* before issuing the descriptors, set the QUP to run */
+- ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+- if (ret) {
+- dev_warn(qup->dev, "cannot set RUN state\n");
+- return ret;
+- }
+-
+- if (!qup->qup_v1) {
+- if (xfer->rx_buf)
+- rx_done = spi_qup_dma_done;
+-
+- if (xfer->tx_buf)
+- tx_done = spi_qup_dma_done;
+- }
+-
+- if (xfer->rx_buf) {
+- ret = spi_qup_prep_sg(master, xfer->rx_sg.sgl,
+- xfer->rx_sg.nents, DMA_DEV_TO_MEM,
+- rx_done, &qup->done);
+- if (ret)
+- return ret;
++ rx_sgl = xfer->rx_sg.sgl;
++ tx_sgl = xfer->tx_sg.sgl;
+
+- dma_async_issue_pending(master->dma_rx);
+- }
++ do {
++ int rx_nents = 0, tx_nents = 0;
+
+- if (xfer->tx_buf) {
+- ret = spi_qup_prep_sg(master, xfer->tx_sg.sgl,
+- xfer->tx_sg.nents, DMA_MEM_TO_DEV,
+- tx_done, &qup->dma_tx_done);
++ if (rx_sgl) {
++ rx_nents = sg_nents_for_len(rx_sgl, SPI_MAX_XFER);
++ if (rx_nents < 0)
++ rx_nents = sg_nents(rx_sgl);
++
++ qup->n_words = spi_qup_sgl_get_size(rx_sgl, rx_nents) /
++ qup->w_size;
++ }
++
++ if (tx_sgl) {
++ tx_nents = sg_nents_for_len(tx_sgl, SPI_MAX_XFER);
++ if (tx_nents < 0)
++ tx_nents = sg_nents(tx_sgl);
++
++ qup->n_words = spi_qup_sgl_get_size(tx_sgl, tx_nents) /
++ qup->w_size;
++ }
++
++
++ ret = spi_qup_io_config(spi, xfer);
+ if (ret)
+ return ret;
+
+- dma_async_issue_pending(master->dma_tx);
+- }
++ /* before issuing the descriptors, set the QUP to run */
++ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
++ if (ret) {
++ dev_warn(qup->dev, "cannot set RUN state\n");
++ return ret;
++ }
++
++ if (!qup->qup_v1) {
++ if (rx_sgl) {
++ rx_done = spi_qup_dma_done;
++ }
++
++ if (tx_sgl) {
++ tx_done = spi_qup_dma_done;
++ }
++ }
++
++ if (rx_sgl) {
++ ret = spi_qup_prep_sg(master, rx_sgl, rx_nents,
++ DMA_DEV_TO_MEM, rx_done,
++ &qup->done);
++ if (ret)
++ return ret;
++
++ dma_async_issue_pending(master->dma_rx);
++ }
++
++ if (tx_sgl) {
++ ret = spi_qup_prep_sg(master, tx_sgl, tx_nents,
++ DMA_MEM_TO_DEV, tx_done,
++ &qup->dma_tx_done);
++ if (ret)
++ return ret;
++
++ dma_async_issue_pending(master->dma_tx);
++ }
++
++ if (rx_sgl && !wait_for_completion_timeout(&qup->done, timeout)) {
++ pr_emerg(" rx timed out");
++ return -ETIMEDOUT;
++ }
++
++ if (tx_sgl && !wait_for_completion_timeout(&qup->dma_tx_done, timeout)) {
++ pr_emerg(" tx timed out\n");
++ return -ETIMEDOUT;
++ }
+
+- if (xfer->rx_buf && !wait_for_completion_timeout(&qup->done, timeout))
+- return -ETIMEDOUT;
++ for (; rx_sgl && rx_nents--; rx_sgl = sg_next(rx_sgl));
++ for (; tx_sgl && tx_nents--; tx_sgl = sg_next(tx_sgl));
+
+- if (xfer->tx_buf && !wait_for_completion_timeout(&qup->dma_tx_done, timeout))
+- ret = -ETIMEDOUT;
++ } while (rx_sgl || tx_sgl);
+
+- return ret;
++ return 0;
+ }
+
+ static int spi_qup_do_pio(struct spi_device *spi, struct spi_transfer *xfer,
diff --git a/target/linux/ipq806x/patches-4.9/0017-spi-qup-Fix-sg-nents-calculation.patch b/target/linux/ipq806x/patches-4.9/0017-spi-qup-Fix-sg-nents-calculation.patch
new file mode 100644
index 0000000000..d6d92b8d8e
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0017-spi-qup-Fix-sg-nents-calculation.patch
@@ -0,0 +1,86 @@
+From 5ffa559b35dd90469e1f7fc21a77c6a2d5a8ca0f Mon Sep 17 00:00:00 2001
+From: Varadarajan Narayanan <varada@codeaurora.org>
+Date: Wed, 25 May 2016 13:40:03 +0530
+Subject: [PATCH 17/37] spi: qup: Fix sg nents calculation
+
+lib/scatterlist.c:sg_nents_for_len() returns the number of SG
+entries that total up to greater than or equal to the given
+length. However, the spi-qup driver assumed that the returned
+nents is for a total less than or equal to the given length. The
+spi-qup driver requests nents for SPI_MAX_XFER, however the API
+returns nents for SPI_MAX_XFER+delta (actually SZ_64K).
+
+Based on this, spi_qup_do_dma() calculates n_words and programs
+that into QUP_MX_{IN|OUT}PUT_CNT register. The calculated
+n_words value is more than the maximum value that can fit in the
+the 16-bit COUNT field of the QUP_MX_{IN|OUT}PUT_CNT register.
+And, the field gets programmed to zero. Since the COUNT field is
+zero, the i/o doesn't start eventually resulting in the i/o
+timing out.
+
+Signed-off-by: Varadarajan Narayanan <varada@codeaurora.org>
+---
+ drivers/spi/spi-qup.c | 38 ++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 36 insertions(+), 2 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -581,6 +581,38 @@ static unsigned int spi_qup_sgl_get_size
+ return length;
+ }
+
++/**
++ * spi_qup_sg_nents_for_len - return total count of entries in scatterlist
++ * needed to satisfy the supplied length
++ * @sg: The scatterlist
++ * @len: The total required length
++ *
++ * Description:
++ * Determines the number of entries in sg that sum upto a maximum of
++ * the supplied length, taking into acount chaining as well
++ *
++ * Returns:
++ * the number of sg entries needed, negative error on failure
++ *
++ **/
++int spi_qup_sg_nents_for_len(struct scatterlist *sg, u64 len)
++{
++ int nents;
++ u64 total;
++
++ if (!len)
++ return 0;
++
++ for (nents = 0, total = 0; sg; sg = sg_next(sg)) {
++ nents++;
++ total += sg_dma_len(sg);
++ if (total > len)
++ return (nents - 1);
++ }
++
++ return -EINVAL;
++}
++
+ static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
+ unsigned long timeout)
+ {
+@@ -597,7 +629,8 @@ unsigned long timeout)
+ int rx_nents = 0, tx_nents = 0;
+
+ if (rx_sgl) {
+- rx_nents = sg_nents_for_len(rx_sgl, SPI_MAX_XFER);
++ rx_nents = spi_qup_sg_nents_for_len(rx_sgl,
++ SPI_MAX_XFER);
+ if (rx_nents < 0)
+ rx_nents = sg_nents(rx_sgl);
+
+@@ -606,7 +639,8 @@ unsigned long timeout)
+ }
+
+ if (tx_sgl) {
+- tx_nents = sg_nents_for_len(tx_sgl, SPI_MAX_XFER);
++ tx_nents = spi_qup_sg_nents_for_len(tx_sgl,
++ SPI_MAX_XFER);
+ if (tx_nents < 0)
+ tx_nents = sg_nents(tx_sgl);
+
diff --git a/target/linux/ipq806x/patches-4.9/0026-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch b/target/linux/ipq806x/patches-4.9/0026-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch
new file mode 100644
index 0000000000..3d6a827007
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0026-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch
@@ -0,0 +1,28 @@
+From 94aa51061597d9db86f3ad4d54eb4a560fa66f2f Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Wed, 13 Apr 2016 14:03:14 -0500
+Subject: [PATCH 26/37] cpufreq: dt: qcom: ipq4019: Add compat for qcom
+ ipq4019
+
+Instantiate cpufreq-dt-platdev driver for ipq4019 to support changing
+CPU frequencies.
+
+This depends on Viresh Kumar's patches in this series:
+http://comments.gmane.org/gmane.linux.power-management.general/73887
+
+Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
+---
+ drivers/cpufreq/cpufreq-dt-platdev.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/cpufreq/cpufreq-dt-platdev.c
++++ b/drivers/cpufreq/cpufreq-dt-platdev.c
+@@ -35,6 +35,8 @@ static const struct of_device_id machine
+
+ { .compatible = "marvell,berlin", },
+
++ { .compatible = "qcom,ipq4019", },
++
+ { .compatible = "samsung,exynos3250", },
+ { .compatible = "samsung,exynos4210", },
+ { .compatible = "samsung,exynos4212", },
diff --git a/target/linux/ipq806x/patches-4.9/0027-clk-ipq4019-report-accurate-fixed-clock-rates.patch b/target/linux/ipq806x/patches-4.9/0027-clk-ipq4019-report-accurate-fixed-clock-rates.patch
new file mode 100644
index 0000000000..87b25d55ee
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0027-clk-ipq4019-report-accurate-fixed-clock-rates.patch
@@ -0,0 +1,33 @@
+From 4f0328557a670d481f2609474b56890c28ab4694 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@qca.qualcomm.com>
+Date: Thu, 28 Apr 2016 12:55:08 -0500
+Subject: [PATCH 27/37] clk: ipq4019: report accurate fixed clock rates
+
+This looks like a copy-and-paste gone wrong, but update all
+the fixed clock rates to report the correct values.
+
+Signed-off-by: Matthew McClintock <mmcclint@qca.qualcomm.com>
+---
+ drivers/clk/qcom/gcc-ipq4019.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/clk/qcom/gcc-ipq4019.c
++++ b/drivers/clk/qcom/gcc-ipq4019.c
+@@ -1317,12 +1317,12 @@ static int gcc_ipq4019_probe(struct plat
+ {
+ struct device *dev = &pdev->dev;
+
+- clk_register_fixed_rate(dev, "fepll125", "xo", 0, 200000000);
+- clk_register_fixed_rate(dev, "fepll125dly", "xo", 0, 200000000);
+- clk_register_fixed_rate(dev, "fepllwcss2g", "xo", 0, 200000000);
+- clk_register_fixed_rate(dev, "fepllwcss5g", "xo", 0, 200000000);
++ clk_register_fixed_rate(dev, "fepll125", "xo", 0, 125000000);
++ clk_register_fixed_rate(dev, "fepll125dly", "xo", 0, 125000000);
++ clk_register_fixed_rate(dev, "fepllwcss2g", "xo", 0, 250000000);
++ clk_register_fixed_rate(dev, "fepllwcss5g", "xo", 0, 250000000);
+ clk_register_fixed_rate(dev, "fepll200", "xo", 0, 200000000);
+- clk_register_fixed_rate(dev, "fepll500", "xo", 0, 200000000);
++ clk_register_fixed_rate(dev, "fepll500", "xo", 0, 500000000);
+ clk_register_fixed_rate(dev, "ddrpllapss", "xo", 0, 666000000);
+
+ return qcom_cc_probe(pdev, &gcc_ipq4019_desc);
diff --git a/target/linux/ipq806x/patches-4.9/0028-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch b/target/linux/ipq806x/patches-4.9/0028-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch
new file mode 100644
index 0000000000..c2b0b44168
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0028-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch
@@ -0,0 +1,77 @@
+From ce6cb947b9535b2d81717925cb6a3bc9f8500f44 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Thu, 17 Mar 2016 15:01:09 -0500
+Subject: [PATCH 28/37] qcom: ipq4019: add cpu operating points for cpufreq
+ support
+
+This adds some operating points for cpu frequeny scaling
+
+Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi | 34 ++++++++++++++++++++++++++--------
+ 1 file changed, 26 insertions(+), 8 deletions(-)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -40,14 +40,7 @@
+ reg = <0x0>;
+ clocks = <&gcc GCC_APPS_CLK_SRC>;
+ clock-frequency = <0>;
+- operating-points = <
+- /* kHz uV (fixed) */
+- 48000 1100000
+- 200000 1100000
+- 500000 1100000
+- 666000 1100000
+- >;
+- clock-latency = <256000>;
++ operating-points-v2 = <&cpu0_opp_table>;
+ };
+
+ cpu@1 {
+@@ -59,6 +52,7 @@
+ reg = <0x1>;
+ clocks = <&gcc GCC_APPS_CLK_SRC>;
+ clock-frequency = <0>;
++ operating-points-v2 = <&cpu0_opp_table>;
+ };
+
+ cpu@2 {
+@@ -70,6 +64,7 @@
+ reg = <0x2>;
+ clocks = <&gcc GCC_APPS_CLK_SRC>;
+ clock-frequency = <0>;
++ operating-points-v2 = <&cpu0_opp_table>;
+ };
+
+ cpu@3 {
+@@ -81,6 +76,29 @@
+ reg = <0x3>;
+ clocks = <&gcc GCC_APPS_CLK_SRC>;
+ clock-frequency = <0>;
++ operating-points-v2 = <&cpu0_opp_table>;
++ };
++ };
++
++ cpu0_opp_table: opp_table0 {
++ compatible = "operating-points-v2";
++ opp-shared;
++
++ opp@48000000 {
++ opp-hz = /bits/ 64 <48000000>;
++ clock-latency-ns = <256000>;
++ };
++ opp@200000000 {
++ opp-hz = /bits/ 64 <200000000>;
++ clock-latency-ns = <256000>;
++ };
++ opp@500000000 {
++ opp-hz = /bits/ 64 <500000000>;
++ clock-latency-ns = <256000>;
++ };
++ opp@666000000 {
++ opp-hz = /bits/ 64 <666000000>;
++ clock-latency-ns = <256000>;
+ };
+ };
+
diff --git a/target/linux/ipq806x/patches-4.9/0029-qcom-ipq4019-turn-on-DMA-for-i2c.patch b/target/linux/ipq806x/patches-4.9/0029-qcom-ipq4019-turn-on-DMA-for-i2c.patch
new file mode 100644
index 0000000000..98dffc9b25
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0029-qcom-ipq4019-turn-on-DMA-for-i2c.patch
@@ -0,0 +1,23 @@
+From f033e344b8a02e98beaf16c1eb188bc175219756 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Mon, 28 Mar 2016 11:16:51 -0500
+Subject: [PATCH 29/37] qcom: ipq4019: turn on DMA for i2c
+
+These are the required nodes for i2c-qup to use DMA
+
+Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -179,6 +179,8 @@
+ clock-names = "iface", "core";
+ #address-cells = <1>;
+ #size-cells = <0>;
++ dmas = <&blsp_dma 9>, <&blsp_dma 8>;
++ dma-names = "rx", "tx";
+ status = "disabled";
+ };
+
diff --git a/target/linux/ipq806x/patches-4.9/0030-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch b/target/linux/ipq806x/patches-4.9/0030-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch
new file mode 100644
index 0000000000..7f79dba934
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0030-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch
@@ -0,0 +1,28 @@
+From 79f698655871f2eeceb1f9051e60d97bc434f52f Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@qca.qualcomm.com>
+Date: Fri, 29 Apr 2016 12:48:02 -0500
+Subject: [PATCH 30/37] qcom: ipq4019: use correct clock for i2c bus 0
+
+For the record the mapping is as follows:
+
+QUP0 = SPI QUP1
+QUP1 = SPI QUP2
+QUP2 = I2C QUP1
+QUP3 = I2C QUP2
+
+Signed-off-by: Matthew McClintock <mmcclint@qca.qualcomm.com>
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -175,7 +175,7 @@
+ reg = <0x78b7000 0x6000>;
+ interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gcc GCC_BLSP1_AHB_CLK>,
+- <&gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>;
++ <&gcc GCC_BLSP1_QUP1_I2C_APPS_CLK>;
+ clock-names = "iface", "core";
+ #address-cells = <1>;
+ #size-cells = <0>;
diff --git a/target/linux/ipq806x/patches-4.9/0031-qcom-ipq4019-enable-DMA-for-spi.patch b/target/linux/ipq806x/patches-4.9/0031-qcom-ipq4019-enable-DMA-for-spi.patch
new file mode 100644
index 0000000000..e7156181ae
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0031-qcom-ipq4019-enable-DMA-for-spi.patch
@@ -0,0 +1,23 @@
+From 20249a0d746265a6663208fa768614784553edac Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Mon, 11 Apr 2016 14:49:12 -0500
+Subject: [PATCH 31/37] qcom: ipq4019: enable DMA for spi
+
+These are the required nodes for spi-qup to use DMA
+
+Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -167,6 +167,8 @@
+ clock-names = "core", "iface";
+ #address-cells = <1>;
+ #size-cells = <0>;
++ dmas = <&blsp_dma 5>, <&blsp_dma 4>;
++ dma-names = "rx", "tx";
+ status = "disabled";
+ };
+
diff --git a/target/linux/ipq806x/patches-4.9/0032-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch b/target/linux/ipq806x/patches-4.9/0032-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch
new file mode 100644
index 0000000000..9263ffd5b8
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0032-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch
@@ -0,0 +1,108 @@
+From 4d7fe4171b01cbfc01e4a00f44b3e7f7a8013eb3 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Fri, 8 Apr 2016 15:26:10 -0500
+Subject: [PATCH 32/37] qcom: ipq4019: use v2 of the kpss bringup mechanism
+
+v1 was the incorrect choice here and sometimes the board would not come
+up properly
+
+Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi | 32 ++++++++++++++++++++++++--------
+ 1 file changed, 24 insertions(+), 8 deletions(-)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -34,7 +34,8 @@
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+- enable-method = "qcom,kpss-acc-v1";
++ enable-method = "qcom,kpss-acc-v2";
++ next-level-cache = <&L2>;
+ qcom,acc = <&acc0>;
+ qcom,saw = <&saw0>;
+ reg = <0x0>;
+@@ -46,7 +47,8 @@
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+- enable-method = "qcom,kpss-acc-v1";
++ enable-method = "qcom,kpss-acc-v2";
++ next-level-cache = <&L2>;
+ qcom,acc = <&acc1>;
+ qcom,saw = <&saw1>;
+ reg = <0x1>;
+@@ -58,7 +60,8 @@
+ cpu@2 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+- enable-method = "qcom,kpss-acc-v1";
++ enable-method = "qcom,kpss-acc-v2";
++ next-level-cache = <&L2>;
+ qcom,acc = <&acc2>;
+ qcom,saw = <&saw2>;
+ reg = <0x2>;
+@@ -70,7 +73,8 @@
+ cpu@3 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+- enable-method = "qcom,kpss-acc-v1";
++ enable-method = "qcom,kpss-acc-v2";
++ next-level-cache = <&L2>;
+ qcom,acc = <&acc3>;
+ qcom,saw = <&saw3>;
+ reg = <0x3>;
+@@ -100,6 +104,12 @@
+ opp-hz = /bits/ 64 <666000000>;
+ clock-latency-ns = <256000>;
+ };
++
++ L2: l2-cache {
++ compatible = "qcom,arch-cache";
++ cache-level = <2>;
++ qcom,saw = <&saw_l2>;
++ };
+ };
+
+ pmu {
+@@ -212,22 +222,22 @@
+ };
+
+ acc0: clock-controller@b088000 {
+- compatible = "qcom,kpss-acc-v1";
++ compatible = "qcom,kpss-acc-v2";
+ reg = <0x0b088000 0x1000>, <0xb008000 0x1000>;
+ };
+
+ acc1: clock-controller@b098000 {
+- compatible = "qcom,kpss-acc-v1";
++ compatible = "qcom,kpss-acc-v2";
+ reg = <0x0b098000 0x1000>, <0xb008000 0x1000>;
+ };
+
+ acc2: clock-controller@b0a8000 {
+- compatible = "qcom,kpss-acc-v1";
++ compatible = "qcom,kpss-acc-v2";
+ reg = <0x0b0a8000 0x1000>, <0xb008000 0x1000>;
+ };
+
+ acc3: clock-controller@b0b8000 {
+- compatible = "qcom,kpss-acc-v1";
++ compatible = "qcom,kpss-acc-v2";
+ reg = <0x0b0b8000 0x1000>, <0xb008000 0x1000>;
+ };
+
+@@ -255,6 +265,12 @@
+ regulator;
+ };
+
++ saw_l2: regulator@b012000 {
++ compatible = "qcom,saw2";
++ reg = <0xb012000 0x1000>;
++ regulator;
++ };
++
+ serial@78af000 {
+ compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
+ reg = <0x78af000 0x200>;
diff --git a/target/linux/ipq806x/patches-4.9/0033-dts-ipq4019-support-ARMv7-PMU.patch b/target/linux/ipq806x/patches-4.9/0033-dts-ipq4019-support-ARMv7-PMU.patch
new file mode 100644
index 0000000000..0ea66d9e4d
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0033-dts-ipq4019-support-ARMv7-PMU.patch
@@ -0,0 +1,28 @@
+From 25ee3761c072037b48246b992c3aec671c77a406 Mon Sep 17 00:00:00 2001
+From: Thomas Pedersen <twp@codeaurora.org>
+Date: Wed, 4 May 2016 12:25:41 -0700
+Subject: [PATCH 33/37] dts: ipq4019: support ARMv7 PMU
+
+Add support for cortex-a7-pmu present on ipq4019 SoCs.
+
+Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
+Signed-off-by: Matthew McClintock <mmcclint@qca.qualcomm.com>
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -118,6 +118,12 @@
+ IRQ_TYPE_LEVEL_HIGH)>;
+ };
+
++ pmu {
++ compatible = "arm,cortex-a7-pmu";
++ interrupts = <GIC_PPI 7 (GIC_CPU_MASK_SIMPLE(4) |
++ IRQ_TYPE_LEVEL_HIGH)>;
++ };
++
+ clocks {
+ sleep_clk: sleep_clk {
+ compatible = "fixed-clock";
diff --git a/target/linux/ipq806x/patches-4.9/0034-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches-4.9/0034-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch
new file mode 100644
index 0000000000..86a8a87a2a
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0034-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch
@@ -0,0 +1,488 @@
+From e8d6fd46f5f3c5860fa7fa98004de9bd97c0d869 Mon Sep 17 00:00:00 2001
+From: Senthilkumar N L <snlakshm@codeaurora.org>
+Date: Tue, 6 Jan 2015 12:52:23 +0530
+Subject: [PATCH 34/37] qcom: ipq4019: Add IPQ4019 USB HS/SS PHY drivers
+
+These drivers handles control and configuration of the HS
+and SS USB PHY transceivers.
+
+Signed-off-by: Senthilkumar N L <snlakshm@codeaurora.org>
+---
+ drivers/usb/phy/Kconfig | 11 ++
+ drivers/usb/phy/Makefile | 2 +
+ drivers/usb/phy/phy-qca-baldur.c | 262 ++++++++++++++++++++++++++++++++++++++
+ drivers/usb/phy/phy-qca-uniphy.c | 171 +++++++++++++++++++++++++
+ 4 files changed, 446 insertions(+)
+ create mode 100644 drivers/usb/phy/phy-qca-baldur.c
+ create mode 100644 drivers/usb/phy/phy-qca-uniphy.c
+
+--- a/drivers/usb/phy/Kconfig
++++ b/drivers/usb/phy/Kconfig
+@@ -194,6 +194,17 @@ config USB_MXS_PHY
+
+ MXS Phy is used by some of the i.MX SoCs, for example imx23/28/6x.
+
++config USB_IPQ4019_PHY
++ tristate "IPQ4019 PHY wrappers support"
++ depends on (USB || USB_GADGET) && ARCH_QCOM
++ select USB_PHY
++ help
++ Enable this to support the USB PHY transceivers on QCA961x chips.
++ It handles PHY initialization, clock management required after
++ resetting the hardware and power management.
++ This driver is required even for peripheral only or host only
++ mode configurations.
++
+ config USB_ULPI
+ bool "Generic ULPI Transceiver Driver"
+ depends on ARM || ARM64
+--- a/drivers/usb/phy/Makefile
++++ b/drivers/usb/phy/Makefile
+@@ -21,6 +21,8 @@ obj-$(CONFIG_USB_GPIO_VBUS) += phy-gpio
+ obj-$(CONFIG_USB_ISP1301) += phy-isp1301.o
+ obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o
+ obj-$(CONFIG_USB_QCOM_8X16_PHY) += phy-qcom-8x16-usb.o
++obj-$(CONFIG_USB_IPQ4019_PHY) += phy-qca-baldur.o
++obj-$(CONFIG_USB_IPQ4019_PHY) += phy-qca-uniphy.o
+ obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o
+ obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o
+ obj-$(CONFIG_USB_ULPI) += phy-ulpi.o
+--- /dev/null
++++ b/drivers/usb/phy/phy-qca-baldur.c
+@@ -0,0 +1,262 @@
++/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/regulator/consumer.h>
++#include <linux/usb/phy.h>
++#include <linux/reset.h>
++#include <linux/of_device.h>
++
++/**
++ * USB Hardware registers
++ */
++#define PHY_CTRL0_ADDR 0x000
++#define PHY_CTRL1_ADDR 0x004
++#define PHY_CTRL2_ADDR 0x008
++#define PHY_CTRL3_ADDR 0x00C
++#define PHY_CTRL4_ADDR 0x010
++#define PHY_MISC_ADDR 0x024
++#define PHY_IPG_ADDR 0x030
++
++#define PHY_CTRL0_EMU_ADDR 0x180
++#define PHY_CTRL1_EMU_ADDR 0x184
++#define PHY_CTRL2_EMU_ADDR 0x188
++#define PHY_CTRL3_EMU_ADDR 0x18C
++#define PHY_CTRL4_EMU_ADDR 0x190
++#define PHY_MISC_EMU_ADDR 0x1A4
++#define PHY_IPG_EMU_ADDR 0x1B0
++
++#define PHY_CTRL0_VAL 0xA4600015
++#define PHY_CTRL1_VAL 0x09500000
++#define PHY_CTRL2_VAL 0x00058180
++#define PHY_CTRL3_VAL 0x6DB6DCD6
++#define PHY_CTRL4_VAL 0x836DB6DB
++#define PHY_MISC_VAL 0x3803FB0C
++#define PHY_IPG_VAL 0x47323232
++
++#define PHY_CTRL0_EMU_VAL 0xb4000015
++#define PHY_CTRL1_EMU_VAL 0x09500000
++#define PHY_CTRL2_EMU_VAL 0x00058180
++#define PHY_CTRL3_EMU_VAL 0x6DB6DCD6
++#define PHY_CTRL4_EMU_VAL 0x836DB6DB
++#define PHY_MISC_EMU_VAL 0x3803FB0C
++#define PHY_IPG_EMU_VAL 0x47323232
++
++#define USB30_HS_PHY_HOST_MODE (0x01 << 21)
++#define USB20_HS_PHY_HOST_MODE (0x01 << 5)
++
++/* used to differentiate between USB3 HS and USB2 HS PHY */
++struct qca_baldur_hs_data {
++ unsigned int usb3_hs_phy;
++ unsigned int phy_config_offset;
++};
++
++struct qca_baldur_hs_phy {
++ struct device *dev;
++ struct usb_phy phy;
++
++ void __iomem *base;
++ void __iomem *qscratch_base;
++
++ struct reset_control *por_rst;
++ struct reset_control *srif_rst;
++
++ unsigned int host;
++ unsigned int emulation;
++ const struct qca_baldur_hs_data *data;
++};
++
++#define phy_to_dw_phy(x) container_of((x), struct qca_baldur_hs_phy, phy)
++
++static int qca_baldur_phy_read(struct usb_phy *x, u32 reg)
++{
++ struct qca_baldur_hs_phy *phy = phy_to_dw_phy(x);
++
++ return readl(phy->base + reg);
++}
++
++static int qca_baldur_phy_write(struct usb_phy *x, u32 val, u32 reg)
++{
++ struct qca_baldur_hs_phy *phy = phy_to_dw_phy(x);
++
++ writel(val, phy->base + reg);
++ return 0;
++}
++
++static int qca_baldur_hs_phy_init(struct usb_phy *x)
++{
++ struct qca_baldur_hs_phy *phy = phy_to_dw_phy(x);
++
++ /* assert HS PHY POR reset */
++ reset_control_assert(phy->por_rst);
++ msleep(10);
++
++ /* assert HS PHY SRIF reset */
++ reset_control_assert(phy->srif_rst);
++ msleep(10);
++
++ /* deassert HS PHY SRIF reset and program HS PHY registers */
++ reset_control_deassert(phy->srif_rst);
++ msleep(10);
++
++ if (!phy->emulation) {
++ /* perform PHY register writes */
++ writel(PHY_CTRL0_VAL, phy->base + PHY_CTRL0_ADDR);
++ writel(PHY_CTRL1_VAL, phy->base + PHY_CTRL1_ADDR);
++ writel(PHY_CTRL2_VAL, phy->base + PHY_CTRL2_ADDR);
++ writel(PHY_CTRL3_VAL, phy->base + PHY_CTRL3_ADDR);
++ writel(PHY_CTRL4_VAL, phy->base + PHY_CTRL4_ADDR);
++ writel(PHY_MISC_VAL, phy->base + PHY_MISC_ADDR);
++ writel(PHY_IPG_VAL, phy->base + PHY_IPG_ADDR);
++ } else {
++ /* perform PHY register writes */
++ writel(PHY_CTRL0_EMU_VAL, phy->base + PHY_CTRL0_EMU_ADDR);
++ writel(PHY_CTRL1_EMU_VAL, phy->base + PHY_CTRL1_EMU_ADDR);
++ writel(PHY_CTRL2_EMU_VAL, phy->base + PHY_CTRL2_EMU_ADDR);
++ writel(PHY_CTRL3_EMU_VAL, phy->base + PHY_CTRL3_EMU_ADDR);
++ writel(PHY_CTRL4_EMU_VAL, phy->base + PHY_CTRL4_EMU_ADDR);
++ writel(PHY_MISC_EMU_VAL, phy->base + PHY_MISC_EMU_ADDR);
++ writel(PHY_IPG_EMU_VAL, phy->base + PHY_IPG_EMU_ADDR);
++ }
++
++ msleep(10);
++
++ /* de-assert USB3 HS PHY POR reset */
++ reset_control_deassert(phy->por_rst);
++
++ return 0;
++}
++
++static int qca_baldur_hs_get_resources(struct qca_baldur_hs_phy *phy)
++{
++ struct platform_device *pdev = to_platform_device(phy->dev);
++ struct resource *res;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ phy->base = devm_ioremap_resource(phy->dev, res);
++ if (IS_ERR(phy->base))
++ return PTR_ERR(phy->base);
++
++ phy->por_rst = devm_reset_control_get(phy->dev, "por_rst");
++ if (IS_ERR(phy->por_rst))
++ return PTR_ERR(phy->por_rst);
++
++ phy->srif_rst = devm_reset_control_get(phy->dev, "srif_rst");
++ if (IS_ERR(phy->srif_rst))
++ return PTR_ERR(phy->srif_rst);
++
++ return 0;
++}
++
++static void qca_baldur_hs_put_resources(struct qca_baldur_hs_phy *phy)
++{
++ reset_control_assert(phy->srif_rst);
++ reset_control_assert(phy->por_rst);
++}
++
++static int qca_baldur_hs_remove(struct platform_device *pdev)
++{
++ struct qca_baldur_hs_phy *phy = platform_get_drvdata(pdev);
++
++ usb_remove_phy(&phy->phy);
++ return 0;
++}
++
++static void qca_baldur_hs_phy_shutdown(struct usb_phy *x)
++{
++ struct qca_baldur_hs_phy *phy = phy_to_dw_phy(x);
++
++ qca_baldur_hs_put_resources(phy);
++}
++
++static struct usb_phy_io_ops qca_baldur_io_ops = {
++ .read = qca_baldur_phy_read,
++ .write = qca_baldur_phy_write,
++};
++
++static const struct qca_baldur_hs_data usb3_hs_data = {
++ .usb3_hs_phy = 1,
++ .phy_config_offset = USB30_HS_PHY_HOST_MODE,
++};
++
++static const struct qca_baldur_hs_data usb2_hs_data = {
++ .usb3_hs_phy = 0,
++ .phy_config_offset = USB20_HS_PHY_HOST_MODE,
++};
++
++static const struct of_device_id qca_baldur_hs_id_table[] = {
++ { .compatible = "qca,baldur-usb3-hsphy", .data = &usb3_hs_data },
++ { .compatible = "qca,baldur-usb2-hsphy", .data = &usb2_hs_data },
++ { /* Sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, qca_baldur_hs_id_table);
++
++static int qca_baldur_hs_probe(struct platform_device *pdev)
++{
++ const struct of_device_id *match;
++ struct qca_baldur_hs_phy *phy;
++ int err;
++
++ match = of_match_device(qca_baldur_hs_id_table, &pdev->dev);
++ if (!match)
++ return -ENODEV;
++
++ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
++ if (!phy)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, phy);
++ phy->dev = &pdev->dev;
++
++ phy->data = match->data;
++
++ err = qca_baldur_hs_get_resources(phy);
++ if (err < 0) {
++ dev_err(&pdev->dev, "failed to request resources: %d\n", err);
++ return err;
++ }
++
++ phy->phy.dev = phy->dev;
++ phy->phy.label = "qca-baldur-hsphy";
++ phy->phy.init = qca_baldur_hs_phy_init;
++ phy->phy.shutdown = qca_baldur_hs_phy_shutdown;
++ phy->phy.type = USB_PHY_TYPE_USB2;
++ phy->phy.io_ops = &qca_baldur_io_ops;
++
++ err = usb_add_phy_dev(&phy->phy);
++ return err;
++}
++
++static struct platform_driver qca_baldur_hs_driver = {
++ .probe = qca_baldur_hs_probe,
++ .remove = qca_baldur_hs_remove,
++ .driver = {
++ .name = "qca-baldur-hsphy",
++ .owner = THIS_MODULE,
++ .of_match_table = qca_baldur_hs_id_table,
++ },
++};
++
++module_platform_driver(qca_baldur_hs_driver);
++
++MODULE_ALIAS("platform:qca-baldur-hsphy");
++MODULE_LICENSE("Dual BSD/GPL");
++MODULE_DESCRIPTION("USB3 QCA BALDUR HSPHY driver");
+--- /dev/null
++++ b/drivers/usb/phy/phy-qca-uniphy.c
+@@ -0,0 +1,171 @@
++/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/regulator/consumer.h>
++#include <linux/usb/phy.h>
++#include <linux/reset.h>
++#include <linux/of_device.h>
++
++struct qca_uni_ss_phy {
++ struct usb_phy phy;
++ struct device *dev;
++
++ void __iomem *base;
++
++ struct reset_control *por_rst;
++
++ unsigned int host;
++};
++
++#define phy_to_dw_phy(x) container_of((x), struct qca_uni_ss_phy, phy)
++
++/**
++ * Write register
++ *
++ * @base - PHY base virtual address.
++ * @offset - register offset.
++ */
++static u32 qca_uni_ss_read(void __iomem *base, u32 offset)
++{
++ u32 value;
++ value = readl_relaxed(base + offset);
++ return value;
++}
++
++/**
++ * Write register
++ *
++ * @base - PHY base virtual address.
++ * @offset - register offset.
++ * @val - value to write.
++ */
++static void qca_uni_ss_write(void __iomem *base, u32 offset, u32 val)
++{
++ writel(val, base + offset);
++ udelay(100);
++}
++
++static void qca_uni_ss_phy_shutdown(struct usb_phy *x)
++{
++ struct qca_uni_ss_phy *phy = phy_to_dw_phy(x);
++
++ /* assert SS PHY POR reset */
++ reset_control_assert(phy->por_rst);
++}
++
++static int qca_uni_ss_phy_init(struct usb_phy *x)
++{
++ struct qca_uni_ss_phy *phy = phy_to_dw_phy(x);
++
++ /* assert SS PHY POR reset */
++ reset_control_assert(phy->por_rst);
++
++ msleep(10);
++
++ msleep(10);
++
++ /* deassert SS PHY POR reset */
++ reset_control_deassert(phy->por_rst);
++
++ return 0;
++}
++
++static int qca_uni_ss_get_resources(struct platform_device *pdev,
++ struct qca_uni_ss_phy *phy)
++{
++ struct resource *res;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ phy->base = devm_ioremap_resource(phy->dev, res);
++ if (IS_ERR(phy->base))
++ return PTR_ERR(phy->base);
++
++ phy->por_rst = devm_reset_control_get(phy->dev, "por_rst");
++ if (IS_ERR(phy->por_rst))
++ return PTR_ERR(phy->por_rst);
++
++ return 0;
++}
++
++static int qca_uni_ss_remove(struct platform_device *pdev)
++{
++ struct qca_uni_ss_phy *phy = platform_get_drvdata(pdev);
++
++ usb_remove_phy(&phy->phy);
++ return 0;
++}
++
++static const struct of_device_id qca_uni_ss_id_table[] = {
++ { .compatible = "qca,uni-ssphy" },
++ { /* Sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, qca_uni_ss_id_table);
++
++static int qca_uni_ss_probe(struct platform_device *pdev)
++{
++ const struct of_device_id *match;
++ struct device_node *np = pdev->dev.of_node;
++ struct qca_uni_ss_phy *phy;
++ int ret;
++
++ match = of_match_device(qca_uni_ss_id_table, &pdev->dev);
++ if (!match)
++ return -ENODEV;
++
++ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
++ if (!phy)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, phy);
++ phy->dev = &pdev->dev;
++
++ ret = qca_uni_ss_get_resources(pdev, phy);
++ if (ret < 0) {
++ dev_err(&pdev->dev, "failed to request resources: %d\n", ret);
++ return ret;
++ }
++
++ phy->phy.dev = phy->dev;
++ phy->phy.label = "qca-uni-ssphy";
++ phy->phy.init = qca_uni_ss_phy_init;
++ phy->phy.shutdown = qca_uni_ss_phy_shutdown;
++ phy->phy.type = USB_PHY_TYPE_USB3;
++
++ ret = usb_add_phy_dev(&phy->phy);
++ return ret;
++}
++
++static struct platform_driver qca_uni_ss_driver = {
++ .probe = qca_uni_ss_probe,
++ .remove = qca_uni_ss_remove,
++ .driver = {
++ .name = "qca-uni-ssphy",
++ .owner = THIS_MODULE,
++ .of_match_table = qca_uni_ss_id_table,
++ },
++};
++
++module_platform_driver(qca_uni_ss_driver);
++
++MODULE_ALIAS("platform:qca-uni-ssphy");
++MODULE_LICENSE("Dual BSD/GPL");
++MODULE_DESCRIPTION("USB3 QCA UNI SSPHY driver");
diff --git a/target/linux/ipq806x/patches-4.9/0035-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch b/target/linux/ipq806x/patches-4.9/0035-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch
new file mode 100644
index 0000000000..61768bc7df
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0035-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch
@@ -0,0 +1,88 @@
+From d2ed553484fecdf02fa53bf431599412348afa95 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Thu, 17 Mar 2016 16:22:28 -0500
+Subject: [PATCH 35/37] qcom: ipq4019: add USB nodes to ipq4019 SoC device
+ tree
+
+This adds the SoC nodes to the ipq4019 device tree
+
+Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi | 67 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 67 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -313,5 +313,72 @@
+ compatible = "qcom,pshold";
+ reg = <0x4ab000 0x4>;
+ };
++
++ usb3_ss_phy: ssphy@9a000 {
++ compatible = "qca,uni-ssphy";
++ reg = <0x9a000 0x800>;
++ reg-names = "phy_base";
++ resets = <&gcc USB3_UNIPHY_PHY_ARES>;
++ reset-names = "por_rst";
++ status = "disabled";
++ };
++
++ usb3_hs_phy: hsphy@a6000 {
++ compatible = "qca,baldur-usb3-hsphy";
++ reg = <0xa6000 0x40>;
++ reg-names = "phy_base";
++ resets = <&gcc USB3_HSPHY_POR_ARES>, <&gcc USB3_HSPHY_S_ARES>;
++ reset-names = "por_rst", "srif_rst";
++ status = "disabled";
++ };
++
++ usb3@0 {
++ compatible = "qcom,dwc3";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ clocks = <&gcc GCC_USB3_MASTER_CLK>;
++ clock-names = "core";
++ ranges;
++ status = "disabled";
++
++ dwc3@8a00000 {
++ compatible = "snps,dwc3";
++ reg = <0x8a00000 0xf8000>;
++ interrupts = <0 132 0>;
++ usb-phy = <&usb3_hs_phy>, <&usb3_ss_phy>;
++ phy-names = "usb2-phy", "usb3-phy";
++ tx-fifo-resize;
++ dr_mode = "host";
++ };
++ };
++
++ usb2_hs_phy: hsphy@a8000 {
++ compatible = "qca,baldur-usb2-hsphy";
++ reg = <0xa8000 0x40>;
++ reg-names = "phy_base";
++ resets = <&gcc USB2_HSPHY_POR_ARES>, <&gcc USB2_HSPHY_S_ARES>;
++ reset-names = "por_rst", "srif_rst";
++ status = "disabled";
++ };
++
++ usb2@0 {
++ compatible = "qcom,dwc3";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ clocks = <&gcc GCC_USB2_MASTER_CLK>;
++ clock-names = "core";
++ ranges;
++ status = "disabled";
++
++ dwc3@6000000 {
++ compatible = "snps,dwc3";
++ reg = <0x6000000 0xf8000>;
++ interrupts = <0 136 0>;
++ usb-phy = <&usb2_hs_phy>;
++ phy-names = "usb2-phy";
++ tx-fifo-resize;
++ dr_mode = "host";
++ };
++ };
+ };
+ };
diff --git a/target/linux/ipq806x/patches-4.9/0036-qcom-ipq4019-enable-USB-bus-for-DK01.1-board.patch b/target/linux/ipq806x/patches-4.9/0036-qcom-ipq4019-enable-USB-bus-for-DK01.1-board.patch
new file mode 100644
index 0000000000..77be518081
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0036-qcom-ipq4019-enable-USB-bus-for-DK01.1-board.patch
@@ -0,0 +1,45 @@
+From c15caf58ff5c3b4093fa388b9c6228bb3c9c5413 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Mon, 21 Mar 2016 16:12:05 -0500
+Subject: [PATCH 36/37] qcom: ipq4019: enable USB bus for DK01.1 board
+
+This enables the USB block
+
+Change-Id: I384dd1874bba341713f384cf6199abd446e3f3c0
+Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi | 24 ++++++++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
+@@ -108,5 +108,29 @@
+ watchdog@b017000 {
+ status = "ok";
+ };
++
++ usb3_ss_phy: ssphy@0 {
++ status = "ok";
++ };
++
++ dummy_ss_phy: ssphy@1 {
++ status = "ok";
++ };
++
++ usb3_hs_phy: hsphy@a6000 {
++ status = "ok";
++ };
++
++ usb2_hs_phy: hsphy@a8000 {
++ status = "ok";
++ };
++
++ usb3: usb3@8a00000 {
++ status = "ok";
++ };
++
++ usb2: usb2@6000000 {
++ status = "ok";
++ };
+ };
+ };
diff --git a/target/linux/ipq806x/patches-4.9/0037-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch b/target/linux/ipq806x/patches-4.9/0037-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch
new file mode 100644
index 0000000000..21d7e39c58
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0037-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch
@@ -0,0 +1,258 @@
+From ff1ecc5bfc11377e82894d05aa45a92657ef8a06 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Mon, 21 Mar 2016 15:55:21 -0500
+Subject: [PATCH 37/37] dts: ipq4019: Add support for IPQ4019 DK04 board
+
+This is pretty similiar to a DK01 but has a bit more IO. Some notable
+differences are listed below however they are not in the device tree yet
+as we continue adding more support
+
+- second serial port
+- PCIe
+- NAND
+- SD/EMMC
+
+Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
+---
+ arch/arm/boot/dts/Makefile | 1 +
+ arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi | 12 +-
+ arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1-c1.dts | 21 +++
+ arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi | 163 +++++++++++++++++++++++
+ 4 files changed, 189 insertions(+), 8 deletions(-)
+ create mode 100644 arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1-c1.dts
+ create mode 100644 arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
+
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -617,6 +617,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \
+ qcom-apq8084-ifc6540.dtb \
+ qcom-apq8084-mtp.dtb \
+ qcom-ipq4019-ap.dk01.1-c1.dtb \
++ qcom-ipq4019-ap.dk04.1-c1.dtb \
+ qcom-ipq8064-ap148.dtb \
+ qcom-msm8660-surf.dtb \
+ qcom-msm8960-cdp.dtb \
+--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
+@@ -109,11 +109,7 @@
+ status = "ok";
+ };
+
+- usb3_ss_phy: ssphy@0 {
+- status = "ok";
+- };
+-
+- dummy_ss_phy: ssphy@1 {
++ usb3_ss_phy: ssphy@9a000 {
+ status = "ok";
+ };
+
+@@ -121,15 +117,15 @@
+ status = "ok";
+ };
+
+- usb2_hs_phy: hsphy@a8000 {
++ usb3@0 {
+ status = "ok";
+ };
+
+- usb3: usb3@8a00000 {
++ usb2_hs_phy: hsphy@a8000 {
+ status = "ok";
+ };
+
+- usb2: usb2@6000000 {
++ usb2@0{
+ status = "ok";
+ };
+ };
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1-c1.dts
+@@ -0,0 +1,21 @@
++/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ *
++ */
++
++#include "qcom-ipq4019-ap.dk04.1.dtsi"
++
++/ {
++ model = "Qualcomm Technologies, Inc. IPQ40xx/AP-DK04.1-C1";
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
+@@ -0,0 +1,163 @@
++/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ *
++ */
++
++#include "qcom-ipq4019.dtsi"
++
++/ {
++ model = "Qualcomm Technologies, Inc. IPQ4019/AP-DK04.1";
++ compatible = "qcom,ipq4019";
++
++ clocks {
++ xo: xo {
++ compatible = "fixed-clock";
++ clock-frequency = <48000000>;
++ #clock-cells = <0>;
++ };
++ };
++
++ soc {
++ timer {
++ compatible = "arm,armv7-timer";
++ interrupts = <1 2 0xf08>,
++ <1 3 0xf08>,
++ <1 4 0xf08>,
++ <1 1 0xf08>;
++ clock-frequency = <48000000>;
++ };
++
++ pinctrl@0x01000000 {
++ serial_0_pins: serial_pinmux {
++ mux {
++ pins = "gpio16", "gpio17";
++ function = "blsp_uart0";
++ bias-disable;
++ };
++ };
++
++ serial_1_pins: serial1_pinmux {
++ mux {
++ pins = "gpio8", "gpio9";
++ function = "blsp_uart1";
++ bias-disable;
++ };
++ };
++
++ spi_0_pins: spi_0_pinmux {
++ pinmux {
++ function = "blsp_spi0";
++ pins = "gpio13", "gpio14", "gpio15";
++ };
++ pinmux_cs {
++ function = "gpio";
++ pins = "gpio12";
++ };
++ pinconf {
++ pins = "gpio13", "gpio14", "gpio15";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ pinconf_cs {
++ pins = "gpio12";
++ drive-strength = <2>;
++ bias-disable;
++ output-high;
++ };
++ };
++
++ i2c_0_pins: i2c_0_pinmux {
++ pinmux {
++ function = "blsp_i2c0";
++ pins = "gpio10", "gpio11";
++ };
++ pinconf {
++ pins = "gpio10", "gpio11";
++ drive-strength = <16>;
++ bias-disable;
++ };
++ };
++ };
++
++ blsp_dma: dma@7884000 {
++ status = "ok";
++ };
++
++ spi_0: spi@78b5000 {
++ pinctrl-0 = <&spi_0_pins>;
++ pinctrl-names = "default";
++ status = "ok";
++ cs-gpios = <&tlmm 12 0>;
++
++ mx25l25635e@0 {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ reg = <0>;
++ compatible = "mx25l25635e";
++ spi-max-frequency = <24000000>;
++ };
++ };
++
++ i2c_0: i2c@78b7000 { /* BLSP1 QUP2 */
++ pinctrl-0 = <&i2c_0_pins>;
++ pinctrl-names = "default";
++
++ status = "ok";
++ };
++
++ serial@78af000 {
++ pinctrl-0 = <&serial_0_pins>;
++ pinctrl-names = "default";
++ status = "ok";
++ };
++
++ serial@78b0000 {
++ pinctrl-0 = <&serial_1_pins>;
++ pinctrl-names = "default";
++ status = "ok";
++ };
++
++ usb3_ss_phy: ssphy@9a000 {
++ status = "ok";
++ };
++
++ usb3_hs_phy: hsphy@a6000 {
++ status = "ok";
++ };
++
++ usb3: usb3@0 {
++ status = "ok";
++ };
++
++ usb2_hs_phy: hsphy@a8000 {
++ status = "ok";
++ };
++
++ usb2: usb2@6000000 {
++ status = "ok";
++ };
++
++ cryptobam: dma@8e04000 {
++ status = "ok";
++ };
++
++ crypto@8e3a000 {
++ status = "ok";
++ };
++
++ watchdog@b017000 {
++ status = "ok";
++ };
++ };
++};
diff --git a/target/linux/ipq806x/patches-4.9/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch b/target/linux/ipq806x/patches-4.9/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch
new file mode 100644
index 0000000000..bd79a89014
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch
@@ -0,0 +1,746 @@
+From patchwork Wed Nov 2 15:56:56 2016
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v9,1/3] clk: qcom: Add support for SMD-RPM Clocks
+From: Georgi Djakov <georgi.djakov@linaro.org>
+X-Patchwork-Id: 9409419
+Message-Id: <20161102155658.32203-2-georgi.djakov@linaro.org>
+To: sboyd@codeaurora.org, mturquette@baylibre.com
+Cc: linux-clk@vger.kernel.org, devicetree@vger.kernel.org,
+ robh+dt@kernel.org, mark.rutland@arm.com,
+ linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
+ georgi.djakov@linaro.org
+Date: Wed, 2 Nov 2016 17:56:56 +0200
+
+This adds initial support for clocks controlled by the Resource
+Power Manager (RPM) processor on some Qualcomm SoCs, which use
+the qcom_smd_rpm driver to communicate with RPM.
+Such platforms are msm8916, apq8084 and msm8974.
+
+The RPM is a dedicated hardware engine for managing the shared
+SoC resources in order to keep the lowest power profile. It
+communicates with other hardware subsystems via shared memory
+and accepts clock requests, aggregates the requests and turns
+the clocks on/off or scales them on demand.
+
+This driver is based on the codeaurora.org driver:
+https://www.codeaurora.org/cgit/quic/la/kernel/msm-3.10/tree/drivers/clk/qcom/clock-rpm.c
+
+Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
+---
+ .../devicetree/bindings/clock/qcom,rpmcc.txt | 36 ++
+ drivers/clk/qcom/Kconfig | 16 +
+ drivers/clk/qcom/Makefile | 1 +
+ drivers/clk/qcom/clk-smd-rpm.c | 571 +++++++++++++++++++++
+ include/dt-bindings/clock/qcom,rpmcc.h | 45 ++
+ 5 files changed, 669 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
+ create mode 100644 drivers/clk/qcom/clk-smd-rpm.c
+ create mode 100644 include/dt-bindings/clock/qcom,rpmcc.h
+
+--
+To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
+the body of a message to majordomo@vger.kernel.org
+More majordomo info at http://vger.kernel.org/majordomo-info.html
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
+@@ -0,0 +1,36 @@
++Qualcomm RPM Clock Controller Binding
++------------------------------------------------
++The RPM is a dedicated hardware engine for managing the shared
++SoC resources in order to keep the lowest power profile. It
++communicates with other hardware subsystems via shared memory
++and accepts clock requests, aggregates the requests and turns
++the clocks on/off or scales them on demand.
++
++Required properties :
++- compatible : shall contain only one of the following. The generic
++ compatible "qcom,rpmcc" should be also included.
++
++ "qcom,rpmcc-msm8916", "qcom,rpmcc"
++
++- #clock-cells : shall contain 1
++
++Example:
++ smd {
++ compatible = "qcom,smd";
++
++ rpm {
++ interrupts = <0 168 1>;
++ qcom,ipc = <&apcs 8 0>;
++ qcom,smd-edge = <15>;
++
++ rpm_requests {
++ compatible = "qcom,rpm-msm8916";
++ qcom,smd-channels = "rpm_requests";
++
++ rpmcc: clock-controller {
++ compatible = "qcom,rpmcc-msm8916", "qcom,rpmcc";
++ #clock-cells = <1>;
++ };
++ };
++ };
++ };
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -2,6 +2,9 @@
+ bool
+ select PM_GENERIC_DOMAINS if PM
+
++config QCOM_RPMCC
++ bool
++
+ config COMMON_CLK_QCOM
+ tristate "Support for Qualcomm's clock controllers"
+ depends on OF
+@@ -9,6 +12,19 @@
+ select REGMAP_MMIO
+ select RESET_CONTROLLER
+
++config QCOM_CLK_SMD_RPM
++ tristate "RPM over SMD based Clock Controller"
++ depends on COMMON_CLK_QCOM && QCOM_SMD_RPM
++ select QCOM_RPMCC
++ help
++ The RPM (Resource Power Manager) is a dedicated hardware engine for
++ managing the shared SoC resources in order to keep the lowest power
++ profile. It communicates with other hardware subsystems via shared
++ memory and accepts clock requests, aggregates the requests and turns
++ the clocks on/off or scales them on demand.
++ Say Y if you want to support the clocks exposed by the RPM on
++ platforms such as apq8016, apq8084, msm8974 etc.
++
+ config APQ_GCC_8084
+ tristate "APQ8084 Global Clock Controller"
+ select QCOM_GDSC
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -29,3 +29,4 @@
+ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
+ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
+ obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
++obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
+--- /dev/null
++++ b/drivers/clk/qcom/clk-smd-rpm.c
+@@ -0,0 +1,571 @@
++/*
++ * Copyright (c) 2016, Linaro Limited
++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/clk-provider.h>
++#include <linux/err.h>
++#include <linux/export.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/soc/qcom/smd-rpm.h>
++
++#include <dt-bindings/clock/qcom,rpmcc.h>
++#include <dt-bindings/mfd/qcom-rpm.h>
++
++#define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773
++#define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370
++#define QCOM_RPM_SMD_KEY_RATE 0x007a484b
++#define QCOM_RPM_SMD_KEY_ENABLE 0x62616e45
++#define QCOM_RPM_SMD_KEY_STATE 0x54415453
++#define QCOM_RPM_SCALING_ENABLE_ID 0x2
++
++#define __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, stat_id, \
++ key) \
++ static struct clk_smd_rpm _platform##_##_active; \
++ static struct clk_smd_rpm _platform##_##_name = { \
++ .rpm_res_type = (type), \
++ .rpm_clk_id = (r_id), \
++ .rpm_status_id = (stat_id), \
++ .rpm_key = (key), \
++ .peer = &_platform##_##_active, \
++ .rate = INT_MAX, \
++ .hw.init = &(struct clk_init_data){ \
++ .ops = &clk_smd_rpm_ops, \
++ .name = #_name, \
++ .parent_names = (const char *[]){ "xo_board" }, \
++ .num_parents = 1, \
++ }, \
++ }; \
++ static struct clk_smd_rpm _platform##_##_active = { \
++ .rpm_res_type = (type), \
++ .rpm_clk_id = (r_id), \
++ .rpm_status_id = (stat_id), \
++ .active_only = true, \
++ .rpm_key = (key), \
++ .peer = &_platform##_##_name, \
++ .rate = INT_MAX, \
++ .hw.init = &(struct clk_init_data){ \
++ .ops = &clk_smd_rpm_ops, \
++ .name = #_active, \
++ .parent_names = (const char *[]){ "xo_board" }, \
++ .num_parents = 1, \
++ }, \
++ }
++
++#define __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, \
++ stat_id, r, key) \
++ static struct clk_smd_rpm _platform##_##_active; \
++ static struct clk_smd_rpm _platform##_##_name = { \
++ .rpm_res_type = (type), \
++ .rpm_clk_id = (r_id), \
++ .rpm_status_id = (stat_id), \
++ .rpm_key = (key), \
++ .branch = true, \
++ .peer = &_platform##_##_active, \
++ .rate = (r), \
++ .hw.init = &(struct clk_init_data){ \
++ .ops = &clk_smd_rpm_branch_ops, \
++ .name = #_name, \
++ .parent_names = (const char *[]){ "xo_board" }, \
++ .num_parents = 1, \
++ }, \
++ }; \
++ static struct clk_smd_rpm _platform##_##_active = { \
++ .rpm_res_type = (type), \
++ .rpm_clk_id = (r_id), \
++ .rpm_status_id = (stat_id), \
++ .active_only = true, \
++ .rpm_key = (key), \
++ .branch = true, \
++ .peer = &_platform##_##_name, \
++ .rate = (r), \
++ .hw.init = &(struct clk_init_data){ \
++ .ops = &clk_smd_rpm_branch_ops, \
++ .name = #_active, \
++ .parent_names = (const char *[]){ "xo_board" }, \
++ .num_parents = 1, \
++ }, \
++ }
++
++#define DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id) \
++ __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \
++ 0, QCOM_RPM_SMD_KEY_RATE)
++
++#define DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, r) \
++ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, \
++ r_id, 0, r, QCOM_RPM_SMD_KEY_ENABLE)
++
++#define DEFINE_CLK_SMD_RPM_QDSS(_platform, _name, _active, type, r_id) \
++ __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \
++ 0, QCOM_RPM_SMD_KEY_STATE)
++
++#define DEFINE_CLK_SMD_RPM_XO_BUFFER(_platform, _name, _active, r_id) \
++ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \
++ QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \
++ QCOM_RPM_KEY_SOFTWARE_ENABLE)
++
++#define DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(_platform, _name, _active, r_id) \
++ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \
++ QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \
++ QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY)
++
++#define to_clk_smd_rpm(_hw) container_of(_hw, struct clk_smd_rpm, hw)
++
++struct clk_smd_rpm {
++ const int rpm_res_type;
++ const int rpm_key;
++ const int rpm_clk_id;
++ const int rpm_status_id;
++ const bool active_only;
++ bool enabled;
++ bool branch;
++ struct clk_smd_rpm *peer;
++ struct clk_hw hw;
++ unsigned long rate;
++ struct qcom_smd_rpm *rpm;
++};
++
++struct clk_smd_rpm_req {
++ __le32 key;
++ __le32 nbytes;
++ __le32 value;
++};
++
++struct rpm_cc {
++ struct qcom_rpm *rpm;
++ struct clk_hw_onecell_data data;
++ struct clk_hw *hws[];
++};
++
++struct rpm_smd_clk_desc {
++ struct clk_smd_rpm **clks;
++ size_t num_clks;
++};
++
++static DEFINE_MUTEX(rpm_smd_clk_lock);
++
++static int clk_smd_rpm_handoff(struct clk_smd_rpm *r)
++{
++ int ret;
++ struct clk_smd_rpm_req req = {
++ .key = cpu_to_le32(r->rpm_key),
++ .nbytes = cpu_to_le32(sizeof(u32)),
++ .value = cpu_to_le32(INT_MAX),
++ };
++
++ ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE,
++ r->rpm_res_type, r->rpm_clk_id, &req,
++ sizeof(req));
++ if (ret)
++ return ret;
++ ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE,
++ r->rpm_res_type, r->rpm_clk_id, &req,
++ sizeof(req));
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static int clk_smd_rpm_set_rate_active(struct clk_smd_rpm *r,
++ unsigned long rate)
++{
++ struct clk_smd_rpm_req req = {
++ .key = cpu_to_le32(r->rpm_key),
++ .nbytes = cpu_to_le32(sizeof(u32)),
++ .value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */
++ };
++
++ return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE,
++ r->rpm_res_type, r->rpm_clk_id, &req,
++ sizeof(req));
++}
++
++static int clk_smd_rpm_set_rate_sleep(struct clk_smd_rpm *r,
++ unsigned long rate)
++{
++ struct clk_smd_rpm_req req = {
++ .key = cpu_to_le32(r->rpm_key),
++ .nbytes = cpu_to_le32(sizeof(u32)),
++ .value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */
++ };
++
++ return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE,
++ r->rpm_res_type, r->rpm_clk_id, &req,
++ sizeof(req));
++}
++
++static void to_active_sleep(struct clk_smd_rpm *r, unsigned long rate,
++ unsigned long *active, unsigned long *sleep)
++{
++ *active = rate;
++
++ /*
++ * Active-only clocks don't care what the rate is during sleep. So,
++ * they vote for zero.
++ */
++ if (r->active_only)
++ *sleep = 0;
++ else
++ *sleep = *active;
++}
++
++static int clk_smd_rpm_prepare(struct clk_hw *hw)
++{
++ struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
++ struct clk_smd_rpm *peer = r->peer;
++ unsigned long this_rate = 0, this_sleep_rate = 0;
++ unsigned long peer_rate = 0, peer_sleep_rate = 0;
++ unsigned long active_rate, sleep_rate;
++ int ret = 0;
++
++ mutex_lock(&rpm_smd_clk_lock);
++
++ /* Don't send requests to the RPM if the rate has not been set. */
++ if (!r->rate)
++ goto out;
++
++ to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate);
++
++ /* Take peer clock's rate into account only if it's enabled. */
++ if (peer->enabled)
++ to_active_sleep(peer, peer->rate,
++ &peer_rate, &peer_sleep_rate);
++
++ active_rate = max(this_rate, peer_rate);
++
++ if (r->branch)
++ active_rate = !!active_rate;
++
++ ret = clk_smd_rpm_set_rate_active(r, active_rate);
++ if (ret)
++ goto out;
++
++ sleep_rate = max(this_sleep_rate, peer_sleep_rate);
++ if (r->branch)
++ sleep_rate = !!sleep_rate;
++
++ ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate);
++ if (ret)
++ /* Undo the active set vote and restore it */
++ ret = clk_smd_rpm_set_rate_active(r, peer_rate);
++
++out:
++ if (!ret)
++ r->enabled = true;
++
++ mutex_unlock(&rpm_smd_clk_lock);
++
++ return ret;
++}
++
++static void clk_smd_rpm_unprepare(struct clk_hw *hw)
++{
++ struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
++ struct clk_smd_rpm *peer = r->peer;
++ unsigned long peer_rate = 0, peer_sleep_rate = 0;
++ unsigned long active_rate, sleep_rate;
++ int ret;
++
++ mutex_lock(&rpm_smd_clk_lock);
++
++ if (!r->rate)
++ goto out;
++
++ /* Take peer clock's rate into account only if it's enabled. */
++ if (peer->enabled)
++ to_active_sleep(peer, peer->rate, &peer_rate,
++ &peer_sleep_rate);
++
++ active_rate = r->branch ? !!peer_rate : peer_rate;
++ ret = clk_smd_rpm_set_rate_active(r, active_rate);
++ if (ret)
++ goto out;
++
++ sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate;
++ ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate);
++ if (ret)
++ goto out;
++
++ r->enabled = false;
++
++out:
++ mutex_unlock(&rpm_smd_clk_lock);
++}
++
++static int clk_smd_rpm_set_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long parent_rate)
++{
++ struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
++ struct clk_smd_rpm *peer = r->peer;
++ unsigned long active_rate, sleep_rate;
++ unsigned long this_rate = 0, this_sleep_rate = 0;
++ unsigned long peer_rate = 0, peer_sleep_rate = 0;
++ int ret = 0;
++
++ mutex_lock(&rpm_smd_clk_lock);
++
++ if (!r->enabled)
++ goto out;
++
++ to_active_sleep(r, rate, &this_rate, &this_sleep_rate);
++
++ /* Take peer clock's rate into account only if it's enabled. */
++ if (peer->enabled)
++ to_active_sleep(peer, peer->rate,
++ &peer_rate, &peer_sleep_rate);
++
++ active_rate = max(this_rate, peer_rate);
++ ret = clk_smd_rpm_set_rate_active(r, active_rate);
++ if (ret)
++ goto out;
++
++ sleep_rate = max(this_sleep_rate, peer_sleep_rate);
++ ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate);
++ if (ret)
++ goto out;
++
++ r->rate = rate;
++
++out:
++ mutex_unlock(&rpm_smd_clk_lock);
++
++ return ret;
++}
++
++static long clk_smd_rpm_round_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long *parent_rate)
++{
++ /*
++ * RPM handles rate rounding and we don't have a way to
++ * know what the rate will be, so just return whatever
++ * rate is requested.
++ */
++ return rate;
++}
++
++static unsigned long clk_smd_rpm_recalc_rate(struct clk_hw *hw,
++ unsigned long parent_rate)
++{
++ struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
++
++ /*
++ * RPM handles rate rounding and we don't have a way to
++ * know what the rate will be, so just return whatever
++ * rate was set.
++ */
++ return r->rate;
++}
++
++static int clk_smd_rpm_enable_scaling(struct qcom_smd_rpm *rpm)
++{
++ int ret;
++ struct clk_smd_rpm_req req = {
++ .key = cpu_to_le32(QCOM_RPM_SMD_KEY_ENABLE),
++ .nbytes = cpu_to_le32(sizeof(u32)),
++ .value = cpu_to_le32(1),
++ };
++
++ ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_SLEEP_STATE,
++ QCOM_SMD_RPM_MISC_CLK,
++ QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req));
++ if (ret) {
++ pr_err("RPM clock scaling (sleep set) not enabled!\n");
++ return ret;
++ }
++
++ ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_ACTIVE_STATE,
++ QCOM_SMD_RPM_MISC_CLK,
++ QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req));
++ if (ret) {
++ pr_err("RPM clock scaling (active set) not enabled!\n");
++ return ret;
++ }
++
++ pr_debug("%s: RPM clock scaling is enabled\n", __func__);
++ return 0;
++}
++
++static const struct clk_ops clk_smd_rpm_ops = {
++ .prepare = clk_smd_rpm_prepare,
++ .unprepare = clk_smd_rpm_unprepare,
++ .set_rate = clk_smd_rpm_set_rate,
++ .round_rate = clk_smd_rpm_round_rate,
++ .recalc_rate = clk_smd_rpm_recalc_rate,
++};
++
++static const struct clk_ops clk_smd_rpm_branch_ops = {
++ .prepare = clk_smd_rpm_prepare,
++ .unprepare = clk_smd_rpm_unprepare,
++ .round_rate = clk_smd_rpm_round_rate,
++ .recalc_rate = clk_smd_rpm_recalc_rate,
++};
++
++/* msm8916 */
++DEFINE_CLK_SMD_RPM(msm8916, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0);
++DEFINE_CLK_SMD_RPM(msm8916, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1);
++DEFINE_CLK_SMD_RPM(msm8916, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0);
++DEFINE_CLK_SMD_RPM_QDSS(msm8916, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1);
++DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk1, bb_clk1_a, 1);
++DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk2, bb_clk2_a, 2);
++DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk1, rf_clk1_a, 4);
++DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk2, rf_clk2_a, 5);
++DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk1_pin, bb_clk1_a_pin, 1);
++DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk2_pin, bb_clk2_a_pin, 2);
++DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk1_pin, rf_clk1_a_pin, 4);
++DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk2_pin, rf_clk2_a_pin, 5);
++
++static struct clk_smd_rpm *msm8916_clks[] = {
++ [RPM_SMD_PCNOC_CLK] = &msm8916_pcnoc_clk,
++ [RPM_SMD_PCNOC_A_CLK] = &msm8916_pcnoc_a_clk,
++ [RPM_SMD_SNOC_CLK] = &msm8916_snoc_clk,
++ [RPM_SMD_SNOC_A_CLK] = &msm8916_snoc_a_clk,
++ [RPM_SMD_BIMC_CLK] = &msm8916_bimc_clk,
++ [RPM_SMD_BIMC_A_CLK] = &msm8916_bimc_a_clk,
++ [RPM_SMD_QDSS_CLK] = &msm8916_qdss_clk,
++ [RPM_SMD_QDSS_A_CLK] = &msm8916_qdss_a_clk,
++ [RPM_SMD_BB_CLK1] = &msm8916_bb_clk1,
++ [RPM_SMD_BB_CLK1_A] = &msm8916_bb_clk1_a,
++ [RPM_SMD_BB_CLK2] = &msm8916_bb_clk2,
++ [RPM_SMD_BB_CLK2_A] = &msm8916_bb_clk2_a,
++ [RPM_SMD_RF_CLK1] = &msm8916_rf_clk1,
++ [RPM_SMD_RF_CLK1_A] = &msm8916_rf_clk1_a,
++ [RPM_SMD_RF_CLK2] = &msm8916_rf_clk2,
++ [RPM_SMD_RF_CLK2_A] = &msm8916_rf_clk2_a,
++ [RPM_SMD_BB_CLK1_PIN] = &msm8916_bb_clk1_pin,
++ [RPM_SMD_BB_CLK1_A_PIN] = &msm8916_bb_clk1_a_pin,
++ [RPM_SMD_BB_CLK2_PIN] = &msm8916_bb_clk2_pin,
++ [RPM_SMD_BB_CLK2_A_PIN] = &msm8916_bb_clk2_a_pin,
++ [RPM_SMD_RF_CLK1_PIN] = &msm8916_rf_clk1_pin,
++ [RPM_SMD_RF_CLK1_A_PIN] = &msm8916_rf_clk1_a_pin,
++ [RPM_SMD_RF_CLK2_PIN] = &msm8916_rf_clk2_pin,
++ [RPM_SMD_RF_CLK2_A_PIN] = &msm8916_rf_clk2_a_pin,
++};
++
++static const struct rpm_smd_clk_desc rpm_clk_msm8916 = {
++ .clks = msm8916_clks,
++ .num_clks = ARRAY_SIZE(msm8916_clks),
++};
++
++static const struct of_device_id rpm_smd_clk_match_table[] = {
++ { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 },
++ { }
++};
++MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table);
++
++static int rpm_smd_clk_probe(struct platform_device *pdev)
++{
++ struct clk_hw **hws;
++ struct rpm_cc *rcc;
++ struct clk_hw_onecell_data *data;
++ int ret;
++ size_t num_clks, i;
++ struct qcom_smd_rpm *rpm;
++ struct clk_smd_rpm **rpm_smd_clks;
++ const struct rpm_smd_clk_desc *desc;
++
++ rpm = dev_get_drvdata(pdev->dev.parent);
++ if (!rpm) {
++ dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n");
++ return -ENODEV;
++ }
++
++ desc = of_device_get_match_data(&pdev->dev);
++ if (!desc)
++ return -EINVAL;
++
++ rpm_smd_clks = desc->clks;
++ num_clks = desc->num_clks;
++
++ rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks,
++ GFP_KERNEL);
++ if (!rcc)
++ return -ENOMEM;
++
++ hws = rcc->hws;
++ data = &rcc->data;
++ data->num = num_clks;
++
++ for (i = 0; i < num_clks; i++) {
++ if (!rpm_smd_clks[i]) {
++ continue;
++ }
++
++ rpm_smd_clks[i]->rpm = rpm;
++
++ ret = clk_smd_rpm_handoff(rpm_smd_clks[i]);
++ if (ret)
++ goto err;
++ }
++
++ ret = clk_smd_rpm_enable_scaling(rpm);
++ if (ret)
++ goto err;
++
++ for (i = 0; i < num_clks; i++) {
++ if (!rpm_smd_clks[i]) {
++ data->hws[i] = ERR_PTR(-ENOENT);
++ continue;
++ }
++
++ ret = devm_clk_hw_register(&pdev->dev, &rpm_smd_clks[i]->hw);
++ if (ret)
++ goto err;
++ }
++
++ ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
++ data);
++ if (ret)
++ goto err;
++
++ return 0;
++err:
++ dev_err(&pdev->dev, "Error registering SMD clock driver (%d)\n", ret);
++ return ret;
++}
++
++static int rpm_smd_clk_remove(struct platform_device *pdev)
++{
++ of_clk_del_provider(pdev->dev.of_node);
++ return 0;
++}
++
++static struct platform_driver rpm_smd_clk_driver = {
++ .driver = {
++ .name = "qcom-clk-smd-rpm",
++ .of_match_table = rpm_smd_clk_match_table,
++ },
++ .probe = rpm_smd_clk_probe,
++ .remove = rpm_smd_clk_remove,
++};
++
++static int __init rpm_smd_clk_init(void)
++{
++ return platform_driver_register(&rpm_smd_clk_driver);
++}
++core_initcall(rpm_smd_clk_init);
++
++static void __exit rpm_smd_clk_exit(void)
++{
++ platform_driver_unregister(&rpm_smd_clk_driver);
++}
++module_exit(rpm_smd_clk_exit);
++
++MODULE_DESCRIPTION("Qualcomm RPM over SMD Clock Controller Driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:qcom-clk-smd-rpm");
+--- /dev/null
++++ b/include/dt-bindings/clock/qcom,rpmcc.h
+@@ -0,0 +1,45 @@
++/*
++ * Copyright 2015 Linaro Limited
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#ifndef _DT_BINDINGS_CLK_MSM_RPMCC_H
++#define _DT_BINDINGS_CLK_MSM_RPMCC_H
++
++/* msm8916 */
++#define RPM_SMD_XO_CLK_SRC 0
++#define RPM_SMD_XO_A_CLK_SRC 1
++#define RPM_SMD_PCNOC_CLK 2
++#define RPM_SMD_PCNOC_A_CLK 3
++#define RPM_SMD_SNOC_CLK 4
++#define RPM_SMD_SNOC_A_CLK 5
++#define RPM_SMD_BIMC_CLK 6
++#define RPM_SMD_BIMC_A_CLK 7
++#define RPM_SMD_QDSS_CLK 8
++#define RPM_SMD_QDSS_A_CLK 9
++#define RPM_SMD_BB_CLK1 10
++#define RPM_SMD_BB_CLK1_A 11
++#define RPM_SMD_BB_CLK2 12
++#define RPM_SMD_BB_CLK2_A 13
++#define RPM_SMD_RF_CLK1 14
++#define RPM_SMD_RF_CLK1_A 15
++#define RPM_SMD_RF_CLK2 16
++#define RPM_SMD_RF_CLK2_A 17
++#define RPM_SMD_BB_CLK1_PIN 18
++#define RPM_SMD_BB_CLK1_A_PIN 19
++#define RPM_SMD_BB_CLK2_PIN 20
++#define RPM_SMD_BB_CLK2_A_PIN 21
++#define RPM_SMD_RF_CLK1_PIN 22
++#define RPM_SMD_RF_CLK1_A_PIN 23
++#define RPM_SMD_RF_CLK2_PIN 24
++#define RPM_SMD_RF_CLK2_A_PIN 25
++
++#endif
diff --git a/target/linux/ipq806x/patches-4.9/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch b/target/linux/ipq806x/patches-4.9/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch
new file mode 100644
index 0000000000..8a38ead567
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch
@@ -0,0 +1,586 @@
+From 872f91b5ea720c72f81fb46d353c43ecb3263ffa Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <georgi.djakov@linaro.org>
+Date: Wed, 2 Nov 2016 17:56:57 +0200
+Subject: clk: qcom: Add support for RPM Clocks
+
+This adds initial support for clocks controlled by the Resource
+Power Manager (RPM) processor on some Qualcomm SoCs, which use
+the qcom_rpm driver to communicate with RPM.
+Such platforms are apq8064 and msm8960.
+
+Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
+Acked-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ .../devicetree/bindings/clock/qcom,rpmcc.txt | 1 +
+ drivers/clk/qcom/Kconfig | 13 +
+ drivers/clk/qcom/Makefile | 1 +
+ drivers/clk/qcom/clk-rpm.c | 489 +++++++++++++++++++++
+ include/dt-bindings/clock/qcom,rpmcc.h | 24 +
+ 5 files changed, 528 insertions(+)
+ create mode 100644 drivers/clk/qcom/clk-rpm.c
+
+--- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
++++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
+@@ -11,6 +11,7 @@ Required properties :
+ compatible "qcom,rpmcc" should be also included.
+
+ "qcom,rpmcc-msm8916", "qcom,rpmcc"
++ "qcom,rpmcc-apq8064", "qcom,rpmcc"
+
+ - #clock-cells : shall contain 1
+
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -12,6 +12,19 @@ config COMMON_CLK_QCOM
+ select REGMAP_MMIO
+ select RESET_CONTROLLER
+
++config QCOM_CLK_RPM
++ tristate "RPM based Clock Controller"
++ depends on COMMON_CLK_QCOM && MFD_QCOM_RPM
++ select QCOM_RPMCC
++ help
++ The RPM (Resource Power Manager) is a dedicated hardware engine for
++ managing the shared SoC resources in order to keep the lowest power
++ profile. It communicates with other hardware subsystems via shared
++ memory and accepts clock requests, aggregates the requests and turns
++ the clocks on/off or scales them on demand.
++ Say Y if you want to support the clocks exposed by the RPM on
++ platforms such as ipq806x, msm8660, msm8960 etc.
++
+ config QCOM_CLK_SMD_RPM
+ tristate "RPM over SMD based Clock Controller"
+ depends on COMMON_CLK_QCOM && QCOM_SMD_RPM
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -23,3 +23,4 @@ obj-$(CONFIG_MSM_GCC_8974) += gcc-msm897
+ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
+ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
+ obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
++obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
+--- /dev/null
++++ b/drivers/clk/qcom/clk-rpm.c
+@@ -0,0 +1,489 @@
++/*
++ * Copyright (c) 2016, Linaro Limited
++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/clk-provider.h>
++#include <linux/err.h>
++#include <linux/export.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
++#include <linux/mfd/qcom_rpm.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++
++#include <dt-bindings/mfd/qcom-rpm.h>
++#include <dt-bindings/clock/qcom,rpmcc.h>
++
++#define QCOM_RPM_MISC_CLK_TYPE 0x306b6c63
++#define QCOM_RPM_SCALING_ENABLE_ID 0x2
++
++#define DEFINE_CLK_RPM(_platform, _name, _active, r_id) \
++ static struct clk_rpm _platform##_##_active; \
++ static struct clk_rpm _platform##_##_name = { \
++ .rpm_clk_id = (r_id), \
++ .peer = &_platform##_##_active, \
++ .rate = INT_MAX, \
++ .hw.init = &(struct clk_init_data){ \
++ .ops = &clk_rpm_ops, \
++ .name = #_name, \
++ .parent_names = (const char *[]){ "pxo_board" }, \
++ .num_parents = 1, \
++ }, \
++ }; \
++ static struct clk_rpm _platform##_##_active = { \
++ .rpm_clk_id = (r_id), \
++ .peer = &_platform##_##_name, \
++ .active_only = true, \
++ .rate = INT_MAX, \
++ .hw.init = &(struct clk_init_data){ \
++ .ops = &clk_rpm_ops, \
++ .name = #_active, \
++ .parent_names = (const char *[]){ "pxo_board" }, \
++ .num_parents = 1, \
++ }, \
++ }
++
++#define DEFINE_CLK_RPM_PXO_BRANCH(_platform, _name, _active, r_id, r) \
++ static struct clk_rpm _platform##_##_active; \
++ static struct clk_rpm _platform##_##_name = { \
++ .rpm_clk_id = (r_id), \
++ .active_only = true, \
++ .peer = &_platform##_##_active, \
++ .rate = (r), \
++ .branch = true, \
++ .hw.init = &(struct clk_init_data){ \
++ .ops = &clk_rpm_branch_ops, \
++ .name = #_name, \
++ .parent_names = (const char *[]){ "pxo_board" }, \
++ .num_parents = 1, \
++ }, \
++ }; \
++ static struct clk_rpm _platform##_##_active = { \
++ .rpm_clk_id = (r_id), \
++ .peer = &_platform##_##_name, \
++ .rate = (r), \
++ .branch = true, \
++ .hw.init = &(struct clk_init_data){ \
++ .ops = &clk_rpm_branch_ops, \
++ .name = #_active, \
++ .parent_names = (const char *[]){ "pxo_board" }, \
++ .num_parents = 1, \
++ }, \
++ }
++
++#define DEFINE_CLK_RPM_CXO_BRANCH(_platform, _name, _active, r_id, r) \
++ static struct clk_rpm _platform##_##_active; \
++ static struct clk_rpm _platform##_##_name = { \
++ .rpm_clk_id = (r_id), \
++ .peer = &_platform##_##_active, \
++ .rate = (r), \
++ .branch = true, \
++ .hw.init = &(struct clk_init_data){ \
++ .ops = &clk_rpm_branch_ops, \
++ .name = #_name, \
++ .parent_names = (const char *[]){ "cxo_board" }, \
++ .num_parents = 1, \
++ }, \
++ }; \
++ static struct clk_rpm _platform##_##_active = { \
++ .rpm_clk_id = (r_id), \
++ .active_only = true, \
++ .peer = &_platform##_##_name, \
++ .rate = (r), \
++ .branch = true, \
++ .hw.init = &(struct clk_init_data){ \
++ .ops = &clk_rpm_branch_ops, \
++ .name = #_active, \
++ .parent_names = (const char *[]){ "cxo_board" }, \
++ .num_parents = 1, \
++ }, \
++ }
++
++#define to_clk_rpm(_hw) container_of(_hw, struct clk_rpm, hw)
++
++struct clk_rpm {
++ const int rpm_clk_id;
++ const bool active_only;
++ unsigned long rate;
++ bool enabled;
++ bool branch;
++ struct clk_rpm *peer;
++ struct clk_hw hw;
++ struct qcom_rpm *rpm;
++};
++
++struct rpm_cc {
++ struct qcom_rpm *rpm;
++ struct clk_hw_onecell_data data;
++ struct clk_hw *hws[];
++};
++
++struct rpm_clk_desc {
++ struct clk_rpm **clks;
++ size_t num_clks;
++};
++
++static DEFINE_MUTEX(rpm_clk_lock);
++
++static int clk_rpm_handoff(struct clk_rpm *r)
++{
++ int ret;
++ u32 value = INT_MAX;
++
++ ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE,
++ r->rpm_clk_id, &value, 1);
++ if (ret)
++ return ret;
++ ret = qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE,
++ r->rpm_clk_id, &value, 1);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static int clk_rpm_set_rate_active(struct clk_rpm *r, unsigned long rate)
++{
++ u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */
++
++ return qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE,
++ r->rpm_clk_id, &value, 1);
++}
++
++static int clk_rpm_set_rate_sleep(struct clk_rpm *r, unsigned long rate)
++{
++ u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */
++
++ return qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE,
++ r->rpm_clk_id, &value, 1);
++}
++
++static void to_active_sleep(struct clk_rpm *r, unsigned long rate,
++ unsigned long *active, unsigned long *sleep)
++{
++ *active = rate;
++
++ /*
++ * Active-only clocks don't care what the rate is during sleep. So,
++ * they vote for zero.
++ */
++ if (r->active_only)
++ *sleep = 0;
++ else
++ *sleep = *active;
++}
++
++static int clk_rpm_prepare(struct clk_hw *hw)
++{
++ struct clk_rpm *r = to_clk_rpm(hw);
++ struct clk_rpm *peer = r->peer;
++ unsigned long this_rate = 0, this_sleep_rate = 0;
++ unsigned long peer_rate = 0, peer_sleep_rate = 0;
++ unsigned long active_rate, sleep_rate;
++ int ret = 0;
++
++ mutex_lock(&rpm_clk_lock);
++
++ /* Don't send requests to the RPM if the rate has not been set. */
++ if (!r->rate)
++ goto out;
++
++ to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate);
++
++ /* Take peer clock's rate into account only if it's enabled. */
++ if (peer->enabled)
++ to_active_sleep(peer, peer->rate,
++ &peer_rate, &peer_sleep_rate);
++
++ active_rate = max(this_rate, peer_rate);
++
++ if (r->branch)
++ active_rate = !!active_rate;
++
++ ret = clk_rpm_set_rate_active(r, active_rate);
++ if (ret)
++ goto out;
++
++ sleep_rate = max(this_sleep_rate, peer_sleep_rate);
++ if (r->branch)
++ sleep_rate = !!sleep_rate;
++
++ ret = clk_rpm_set_rate_sleep(r, sleep_rate);
++ if (ret)
++ /* Undo the active set vote and restore it */
++ ret = clk_rpm_set_rate_active(r, peer_rate);
++
++out:
++ if (!ret)
++ r->enabled = true;
++
++ mutex_unlock(&rpm_clk_lock);
++
++ return ret;
++}
++
++static void clk_rpm_unprepare(struct clk_hw *hw)
++{
++ struct clk_rpm *r = to_clk_rpm(hw);
++ struct clk_rpm *peer = r->peer;
++ unsigned long peer_rate = 0, peer_sleep_rate = 0;
++ unsigned long active_rate, sleep_rate;
++ int ret;
++
++ mutex_lock(&rpm_clk_lock);
++
++ if (!r->rate)
++ goto out;
++
++ /* Take peer clock's rate into account only if it's enabled. */
++ if (peer->enabled)
++ to_active_sleep(peer, peer->rate, &peer_rate,
++ &peer_sleep_rate);
++
++ active_rate = r->branch ? !!peer_rate : peer_rate;
++ ret = clk_rpm_set_rate_active(r, active_rate);
++ if (ret)
++ goto out;
++
++ sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate;
++ ret = clk_rpm_set_rate_sleep(r, sleep_rate);
++ if (ret)
++ goto out;
++
++ r->enabled = false;
++
++out:
++ mutex_unlock(&rpm_clk_lock);
++}
++
++static int clk_rpm_set_rate(struct clk_hw *hw,
++ unsigned long rate, unsigned long parent_rate)
++{
++ struct clk_rpm *r = to_clk_rpm(hw);
++ struct clk_rpm *peer = r->peer;
++ unsigned long active_rate, sleep_rate;
++ unsigned long this_rate = 0, this_sleep_rate = 0;
++ unsigned long peer_rate = 0, peer_sleep_rate = 0;
++ int ret = 0;
++
++ mutex_lock(&rpm_clk_lock);
++
++ if (!r->enabled)
++ goto out;
++
++ to_active_sleep(r, rate, &this_rate, &this_sleep_rate);
++
++ /* Take peer clock's rate into account only if it's enabled. */
++ if (peer->enabled)
++ to_active_sleep(peer, peer->rate,
++ &peer_rate, &peer_sleep_rate);
++
++ active_rate = max(this_rate, peer_rate);
++ ret = clk_rpm_set_rate_active(r, active_rate);
++ if (ret)
++ goto out;
++
++ sleep_rate = max(this_sleep_rate, peer_sleep_rate);
++ ret = clk_rpm_set_rate_sleep(r, sleep_rate);
++ if (ret)
++ goto out;
++
++ r->rate = rate;
++
++out:
++ mutex_unlock(&rpm_clk_lock);
++
++ return ret;
++}
++
++static long clk_rpm_round_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long *parent_rate)
++{
++ /*
++ * RPM handles rate rounding and we don't have a way to
++ * know what the rate will be, so just return whatever
++ * rate is requested.
++ */
++ return rate;
++}
++
++static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw,
++ unsigned long parent_rate)
++{
++ struct clk_rpm *r = to_clk_rpm(hw);
++
++ /*
++ * RPM handles rate rounding and we don't have a way to
++ * know what the rate will be, so just return whatever
++ * rate was set.
++ */
++ return r->rate;
++}
++
++static const struct clk_ops clk_rpm_ops = {
++ .prepare = clk_rpm_prepare,
++ .unprepare = clk_rpm_unprepare,
++ .set_rate = clk_rpm_set_rate,
++ .round_rate = clk_rpm_round_rate,
++ .recalc_rate = clk_rpm_recalc_rate,
++};
++
++static const struct clk_ops clk_rpm_branch_ops = {
++ .prepare = clk_rpm_prepare,
++ .unprepare = clk_rpm_unprepare,
++ .round_rate = clk_rpm_round_rate,
++ .recalc_rate = clk_rpm_recalc_rate,
++};
++
++/* apq8064 */
++DEFINE_CLK_RPM(apq8064, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK);
++DEFINE_CLK_RPM(apq8064, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK);
++DEFINE_CLK_RPM(apq8064, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK);
++DEFINE_CLK_RPM(apq8064, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK);
++DEFINE_CLK_RPM(apq8064, mmfab_clk, mmfab_a_clk, QCOM_RPM_MM_FABRIC_CLK);
++DEFINE_CLK_RPM(apq8064, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK);
++DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK);
++DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK);
++DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK);
++
++static struct clk_rpm *apq8064_clks[] = {
++ [RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk,
++ [RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk,
++ [RPM_CFPB_CLK] = &apq8064_cfpb_clk,
++ [RPM_CFPB_A_CLK] = &apq8064_cfpb_a_clk,
++ [RPM_DAYTONA_FABRIC_CLK] = &apq8064_daytona_clk,
++ [RPM_DAYTONA_FABRIC_A_CLK] = &apq8064_daytona_a_clk,
++ [RPM_EBI1_CLK] = &apq8064_ebi1_clk,
++ [RPM_EBI1_A_CLK] = &apq8064_ebi1_a_clk,
++ [RPM_MM_FABRIC_CLK] = &apq8064_mmfab_clk,
++ [RPM_MM_FABRIC_A_CLK] = &apq8064_mmfab_a_clk,
++ [RPM_MMFPB_CLK] = &apq8064_mmfpb_clk,
++ [RPM_MMFPB_A_CLK] = &apq8064_mmfpb_a_clk,
++ [RPM_SYS_FABRIC_CLK] = &apq8064_sfab_clk,
++ [RPM_SYS_FABRIC_A_CLK] = &apq8064_sfab_a_clk,
++ [RPM_SFPB_CLK] = &apq8064_sfpb_clk,
++ [RPM_SFPB_A_CLK] = &apq8064_sfpb_a_clk,
++ [RPM_QDSS_CLK] = &apq8064_qdss_clk,
++ [RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk,
++};
++
++static const struct rpm_clk_desc rpm_clk_apq8064 = {
++ .clks = apq8064_clks,
++ .num_clks = ARRAY_SIZE(apq8064_clks),
++};
++
++static const struct of_device_id rpm_clk_match_table[] = {
++ { .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 },
++ { }
++};
++MODULE_DEVICE_TABLE(of, rpm_clk_match_table);
++
++static int rpm_clk_probe(struct platform_device *pdev)
++{
++ struct clk_hw **hws;
++ struct rpm_cc *rcc;
++ struct clk_hw_onecell_data *data;
++ int ret;
++ size_t num_clks, i;
++ struct qcom_rpm *rpm;
++ struct clk_rpm **rpm_clks;
++ const struct rpm_clk_desc *desc;
++
++ rpm = dev_get_drvdata(pdev->dev.parent);
++ if (!rpm) {
++ dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n");
++ return -ENODEV;
++ }
++
++ desc = of_device_get_match_data(&pdev->dev);
++ if (!desc)
++ return -EINVAL;
++
++ rpm_clks = desc->clks;
++ num_clks = desc->num_clks;
++
++ rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks,
++ GFP_KERNEL);
++ if (!rcc)
++ return -ENOMEM;
++
++ hws = rcc->hws;
++ data = &rcc->data;
++ data->num = num_clks;
++
++ for (i = 0; i < num_clks; i++) {
++ if (!rpm_clks[i])
++ continue;
++
++ rpm_clks[i]->rpm = rpm;
++
++ ret = clk_rpm_handoff(rpm_clks[i]);
++ if (ret)
++ goto err;
++ }
++
++ for (i = 0; i < num_clks; i++) {
++ if (!rpm_clks[i]) {
++ data->hws[i] = ERR_PTR(-ENOENT);
++ continue;
++ }
++
++ ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw);
++ if (ret)
++ goto err;
++ }
++
++ ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
++ data);
++ if (ret)
++ goto err;
++
++ return 0;
++err:
++ dev_err(&pdev->dev, "Error registering RPM Clock driver (%d)\n", ret);
++ return ret;
++}
++
++static int rpm_clk_remove(struct platform_device *pdev)
++{
++ of_clk_del_provider(pdev->dev.of_node);
++ return 0;
++}
++
++static struct platform_driver rpm_clk_driver = {
++ .driver = {
++ .name = "qcom-clk-rpm",
++ .of_match_table = rpm_clk_match_table,
++ },
++ .probe = rpm_clk_probe,
++ .remove = rpm_clk_remove,
++};
++
++static int __init rpm_clk_init(void)
++{
++ return platform_driver_register(&rpm_clk_driver);
++}
++core_initcall(rpm_clk_init);
++
++static void __exit rpm_clk_exit(void)
++{
++ platform_driver_unregister(&rpm_clk_driver);
++}
++module_exit(rpm_clk_exit);
++
++MODULE_DESCRIPTION("Qualcomm RPM Clock Controller Driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:qcom-clk-rpm");
+--- a/include/dt-bindings/clock/qcom,rpmcc.h
++++ b/include/dt-bindings/clock/qcom,rpmcc.h
+@@ -14,6 +14,30 @@
+ #ifndef _DT_BINDINGS_CLK_MSM_RPMCC_H
+ #define _DT_BINDINGS_CLK_MSM_RPMCC_H
+
++/* apq8064 */
++#define RPM_PXO_CLK 0
++#define RPM_PXO_A_CLK 1
++#define RPM_CXO_CLK 2
++#define RPM_CXO_A_CLK 3
++#define RPM_APPS_FABRIC_CLK 4
++#define RPM_APPS_FABRIC_A_CLK 5
++#define RPM_CFPB_CLK 6
++#define RPM_CFPB_A_CLK 7
++#define RPM_QDSS_CLK 8
++#define RPM_QDSS_A_CLK 9
++#define RPM_DAYTONA_FABRIC_CLK 10
++#define RPM_DAYTONA_FABRIC_A_CLK 11
++#define RPM_EBI1_CLK 12
++#define RPM_EBI1_A_CLK 13
++#define RPM_MM_FABRIC_CLK 14
++#define RPM_MM_FABRIC_A_CLK 15
++#define RPM_MMFPB_CLK 16
++#define RPM_MMFPB_A_CLK 17
++#define RPM_SYS_FABRIC_CLK 18
++#define RPM_SYS_FABRIC_A_CLK 19
++#define RPM_SFPB_CLK 20
++#define RPM_SFPB_A_CLK 21
++
+ /* msm8916 */
+ #define RPM_SMD_XO_CLK_SRC 0
+ #define RPM_SMD_XO_A_CLK_SRC 1
diff --git a/target/linux/ipq806x/patches-4.9/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch b/target/linux/ipq806x/patches-4.9/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch
new file mode 100644
index 0000000000..e5c1870eab
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch
@@ -0,0 +1,94 @@
+From c260524aba53b57f72b5446ed553080df30b4d09 Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <georgi.djakov@linaro.org>
+Date: Wed, 23 Nov 2016 16:52:49 +0200
+Subject: clk: qcom: clk-rpm: Fix clk_hw references
+
+Fix the clk_hw references to the actual clocks and add a xlate function
+to return the hw pointers from the already existing static array.
+
+Reported-by: Michael Scott <michael.scott@linaro.org>
+Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/qcom/clk-rpm.c | 36 ++++++++++++++++++++++--------------
+ 1 file changed, 22 insertions(+), 14 deletions(-)
+
+--- a/drivers/clk/qcom/clk-rpm.c
++++ b/drivers/clk/qcom/clk-rpm.c
+@@ -127,8 +127,8 @@ struct clk_rpm {
+
+ struct rpm_cc {
+ struct qcom_rpm *rpm;
+- struct clk_hw_onecell_data data;
+- struct clk_hw *hws[];
++ struct clk_rpm **clks;
++ size_t num_clks;
+ };
+
+ struct rpm_clk_desc {
+@@ -391,11 +391,23 @@ static const struct of_device_id rpm_clk
+ };
+ MODULE_DEVICE_TABLE(of, rpm_clk_match_table);
+
++static struct clk_hw *qcom_rpm_clk_hw_get(struct of_phandle_args *clkspec,
++ void *data)
++{
++ struct rpm_cc *rcc = data;
++ unsigned int idx = clkspec->args[0];
++
++ if (idx >= rcc->num_clks) {
++ pr_err("%s: invalid index %u\n", __func__, idx);
++ return ERR_PTR(-EINVAL);
++ }
++
++ return rcc->clks[idx] ? &rcc->clks[idx]->hw : ERR_PTR(-ENOENT);
++}
++
+ static int rpm_clk_probe(struct platform_device *pdev)
+ {
+- struct clk_hw **hws;
+ struct rpm_cc *rcc;
+- struct clk_hw_onecell_data *data;
+ int ret;
+ size_t num_clks, i;
+ struct qcom_rpm *rpm;
+@@ -415,14 +427,12 @@ static int rpm_clk_probe(struct platform
+ rpm_clks = desc->clks;
+ num_clks = desc->num_clks;
+
+- rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks,
+- GFP_KERNEL);
++ rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc), GFP_KERNEL);
+ if (!rcc)
+ return -ENOMEM;
+
+- hws = rcc->hws;
+- data = &rcc->data;
+- data->num = num_clks;
++ rcc->clks = rpm_clks;
++ rcc->num_clks = num_clks;
+
+ for (i = 0; i < num_clks; i++) {
+ if (!rpm_clks[i])
+@@ -436,18 +446,16 @@ static int rpm_clk_probe(struct platform
+ }
+
+ for (i = 0; i < num_clks; i++) {
+- if (!rpm_clks[i]) {
+- data->hws[i] = ERR_PTR(-ENOENT);
++ if (!rpm_clks[i])
+ continue;
+- }
+
+ ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw);
+ if (ret)
+ goto err;
+ }
+
+- ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
+- data);
++ ret = of_clk_add_hw_provider(pdev->dev.of_node, qcom_rpm_clk_hw_get,
++ rcc);
+ if (ret)
+ goto err;
+
diff --git a/target/linux/ipq806x/patches-4.9/167-ARM-qcom_rpm_fix_support_for_smb208.patch b/target/linux/ipq806x/patches-4.9/167-ARM-qcom_rpm_fix_support_for_smb208.patch
new file mode 100644
index 0000000000..d46e97bc93
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/167-ARM-qcom_rpm_fix_support_for_smb208.patch
@@ -0,0 +1,44 @@
+
+In commit "regulator: qcom: Rework to single platform device" the smb208 regulator
+used in IPQ8064 was left out.
+
+Add it to that new framework and update Docs accordingly.
+
+Signed-off-by: Adrian Panella <ianchi74@outlook.com>
+
+--- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
++++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
+@@ -171,6 +171,9 @@ pm8018:
+ s1, s2, s3, s4, s5, , l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11,
+ l12, l14, lvs1
+
++smb208:
++ s1a, s1b, s2a, s2b
++
+ The content of each sub-node is defined by the standard binding for regulators -
+ see regulator.txt - with additional custom properties described below:
+
+--- a/drivers/regulator/qcom_rpm-regulator.c
++++ b/drivers/regulator/qcom_rpm-regulator.c
+@@ -933,12 +933,21 @@ static const struct rpm_regulator_data r
+ { }
+ };
+
++static const struct rpm_regulator_data rpm_smb208_regulators[] = {
++ { "s1a", QCOM_RPM_SMB208_S1a, &smb208_smps, "vin_s1a" },
++ { "s1b", QCOM_RPM_SMB208_S1b, &smb208_smps, "vin_s1b" },
++ { "s2a", QCOM_RPM_SMB208_S2a, &smb208_smps, "vin_s2a" },
++ { "s2b", QCOM_RPM_SMB208_S2b, &smb208_smps, "vin_s2b" },
++ { }
++};
++
+ static const struct of_device_id rpm_of_match[] = {
+ { .compatible = "qcom,rpm-pm8018-regulators",
+ .data = &rpm_pm8018_regulators },
+ { .compatible = "qcom,rpm-pm8058-regulators", .data = &rpm_pm8058_regulators },
+ { .compatible = "qcom,rpm-pm8901-regulators", .data = &rpm_pm8901_regulators },
+ { .compatible = "qcom,rpm-pm8921-regulators", .data = &rpm_pm8921_regulators },
++ { .compatible = "qcom,rpm-smb208-regulators", .data = &rpm_smb208_regulators },
+ { }
+ };
+ MODULE_DEVICE_TABLE(of, rpm_of_match);
diff --git a/target/linux/ipq806x/patches-4.9/307-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch b/target/linux/ipq806x/patches-4.9/307-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch
new file mode 100644
index 0000000000..7a7cff3e41
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/307-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch
@@ -0,0 +1,25 @@
+From dd43e356db678a564ad2cc111962d72ba3162a38 Mon Sep 17 00:00:00 2001
+From: Abhishek Sahu <absahu@codeaurora.org>
+Date: Wed, 18 Nov 2015 12:38:56 +0530
+Subject: ipq806x: gcc: Added the enable regs and mask for PRNG
+
+kernel got hanged while reading from /dev/hwrng at the
+time of PRNG clock enable
+
+Change-Id: I89856c7e19e6639508e6a2774304583a3ec91172
+Signed-off-by: Abhishek Sahu <absahu@codeaurora.org>
+---
+ drivers/clk/qcom/gcc-ipq806x.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/clk/qcom/gcc-ipq806x.c
++++ b/drivers/clk/qcom/gcc-ipq806x.c
+@@ -1153,6 +1153,8 @@ static struct clk_rcg prng_src = {
+ .parent_map = gcc_pxo_pll8_map,
+ },
+ .clkr = {
++ .enable_reg = 0x2e80,
++ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "prng_src",
+ .parent_names = gcc_pxo_pll8,
diff --git a/target/linux/ipq806x/patches-4.9/311-add-rpmcc-for-ipq806x.patch b/target/linux/ipq806x/patches-4.9/311-add-rpmcc-for-ipq806x.patch
new file mode 100644
index 0000000000..bb9dd87dd6
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/311-add-rpmcc-for-ipq806x.patch
@@ -0,0 +1,81 @@
+--- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
++++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
+@@ -12,6 +12,7 @@ Required properties :
+
+ "qcom,rpmcc-msm8916", "qcom,rpmcc"
+ "qcom,rpmcc-apq8064", "qcom,rpmcc"
++ "qcom,rpmcc-ipq806x", "qcom,rpmcc"
+
+ - #clock-cells : shall contain 1
+
+--- a/drivers/clk/qcom/clk-rpm.c
++++ b/drivers/clk/qcom/clk-rpm.c
+@@ -359,6 +359,16 @@ DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a
+ DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK);
+ DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK);
+
++/* ipq806x */
++DEFINE_CLK_RPM(ipq806x, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK);
++DEFINE_CLK_RPM(ipq806x, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK);
++DEFINE_CLK_RPM(ipq806x, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK);
++DEFINE_CLK_RPM(ipq806x, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK);
++DEFINE_CLK_RPM(ipq806x, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK);
++DEFINE_CLK_RPM(ipq806x, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK);
++DEFINE_CLK_RPM(ipq806x, nss_fabric_0_clk, nss_fabric_0_a_clk, QCOM_RPM_NSS_FABRIC_0_CLK);
++DEFINE_CLK_RPM(ipq806x, nss_fabric_1_clk, nss_fabric_1_a_clk, QCOM_RPM_NSS_FABRIC_1_CLK);
++
+ static struct clk_rpm *apq8064_clks[] = {
+ [RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk,
+ [RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk,
+@@ -380,13 +390,38 @@ static struct clk_rpm *apq8064_clks[] =
+ [RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk,
+ };
+
++static struct clk_rpm *ipq806x_clks[] = {
++ [RPM_APPS_FABRIC_CLK] = &ipq806x_afab_clk,
++ [RPM_APPS_FABRIC_A_CLK] = &ipq806x_afab_a_clk,
++ [RPM_CFPB_CLK] = &ipq806x_cfpb_clk,
++ [RPM_CFPB_A_CLK] = &ipq806x_cfpb_a_clk,
++ [RPM_DAYTONA_FABRIC_CLK] = &ipq806x_daytona_clk,
++ [RPM_DAYTONA_FABRIC_A_CLK] = &ipq806x_daytona_a_clk,
++ [RPM_EBI1_CLK] = &ipq806x_ebi1_clk,
++ [RPM_EBI1_A_CLK] = &ipq806x_ebi1_a_clk,
++ [RPM_SYS_FABRIC_CLK] = &ipq806x_sfab_clk,
++ [RPM_SYS_FABRIC_A_CLK] = &ipq806x_sfab_a_clk,
++ [RPM_SFPB_CLK] = &ipq806x_sfpb_clk,
++ [RPM_SFPB_A_CLK] = &ipq806x_sfpb_a_clk,
++ [RPM_NSS_FABRIC_0_CLK] = &ipq806x_nss_fabric_0_clk,
++ [RPM_NSS_FABRIC_0_A_CLK] = &ipq806x_nss_fabric_0_a_clk,
++ [RPM_NSS_FABRIC_1_CLK] = &ipq806x_nss_fabric_1_clk,
++ [RPM_NSS_FABRIC_1_A_CLK] = &ipq806x_nss_fabric_1_a_clk,
++};
++
+ static const struct rpm_clk_desc rpm_clk_apq8064 = {
+ .clks = apq8064_clks,
+ .num_clks = ARRAY_SIZE(apq8064_clks),
+ };
+
++static const struct rpm_clk_desc rpm_clk_ipq806x = {
++ .clks = ipq806x_clks,
++ .num_clks = ARRAY_SIZE(ipq806x_clks),
++};
++
+ static const struct of_device_id rpm_clk_match_table[] = {
+ { .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 },
++ { .compatible = "qcom,rpmcc-ipq806x", .data = &rpm_clk_ipq806x },
+ { }
+ };
+ MODULE_DEVICE_TABLE(of, rpm_clk_match_table);
+--- a/include/dt-bindings/clock/qcom,rpmcc.h
++++ b/include/dt-bindings/clock/qcom,rpmcc.h
+@@ -37,6 +37,10 @@
+ #define RPM_SYS_FABRIC_A_CLK 19
+ #define RPM_SFPB_CLK 20
+ #define RPM_SFPB_A_CLK 21
++#define RPM_NSS_FABRIC_0_CLK 22
++#define RPM_NSS_FABRIC_0_A_CLK 23
++#define RPM_NSS_FABRIC_1_CLK 24
++#define RPM_NSS_FABRIC_1_A_CLK 25
+
+ /* msm8916 */
+ #define RPM_SMD_XO_CLK_SRC 0
diff --git a/target/linux/ipq806x/patches-4.9/801-override-compiler-flags.patch b/target/linux/ipq806x/patches-4.9/801-override-compiler-flags.patch
new file mode 100644
index 0000000000..6591dbdf79
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/801-override-compiler-flags.patch
@@ -0,0 +1,11 @@
+--- a/arch/arm/Makefile
++++ b/arch/arm/Makefile
+@@ -65,7 +65,7 @@ KBUILD_CFLAGS += $(call cc-option,-fno-i
+ # macro, but instead defines a whole series of macros which makes
+ # testing for a specific architecture or later rather impossible.
+ arch-$(CONFIG_CPU_32v7M) =-D__LINUX_ARM_ARCH__=7 -march=armv7-m -Wa,-march=armv7-m
+-arch-$(CONFIG_CPU_32v7) =-D__LINUX_ARM_ARCH__=7 $(call cc-option,-march=armv7-a,-march=armv5t -Wa$(comma)-march=armv7-a)
++arch-$(CONFIG_CPU_32v7) =-D__LINUX_ARM_ARCH__=7 -mcpu=cortex-a15
+ arch-$(CONFIG_CPU_32v6) =-D__LINUX_ARM_ARCH__=6 $(call cc-option,-march=armv6,-march=armv5t -Wa$(comma)-march=armv6)
+ # Only override the compiler option if ARMv6. The ARMv6K extensions are
+ # always available in ARMv7
diff --git a/target/linux/ipq806x/patches-4.9/802-GPIO-add-named-gpio-exports.patch b/target/linux/ipq806x/patches-4.9/802-GPIO-add-named-gpio-exports.patch
new file mode 100644
index 0000000000..3434864d26
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/802-GPIO-add-named-gpio-exports.patch
@@ -0,0 +1,166 @@
+From 4267880319bc1a2270d352e0ded6d6386242a7ef Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 12 Aug 2014 20:49:27 +0200
+Subject: [PATCH 24/53] GPIO: add named gpio exports
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/gpio/gpiolib-of.c | 68 +++++++++++++++++++++++++++++++++++++++++
+ drivers/gpio/gpiolib-sysfs.c | 10 +++++-
+ include/asm-generic/gpio.h | 6 ++++
+ include/linux/gpio/consumer.h | 8 +++++
+ 4 files changed, 91 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpio/gpiolib-of.c
++++ b/drivers/gpio/gpiolib-of.c
+@@ -23,6 +23,8 @@
+ #include <linux/pinctrl/pinctrl.h>
+ #include <linux/slab.h>
+ #include <linux/gpio/machine.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
+
+ #include "gpiolib.h"
+
+@@ -538,3 +540,69 @@ void of_gpiochip_remove(struct gpio_chip
+ gpiochip_remove_pin_ranges(chip);
+ of_node_put(chip->of_node);
+ }
++
++static struct of_device_id gpio_export_ids[] = {
++ { .compatible = "gpio-export" },
++ { /* sentinel */ }
++};
++
++static int __init of_gpio_export_probe(struct platform_device *pdev)
++{
++ struct device_node *np = pdev->dev.of_node;
++ struct device_node *cnp;
++ u32 val;
++ int nb = 0;
++
++ for_each_child_of_node(np, cnp) {
++ const char *name = NULL;
++ int gpio;
++ bool dmc;
++ int max_gpio = 1;
++ int i;
++
++ of_property_read_string(cnp, "gpio-export,name", &name);
++
++ if (!name)
++ max_gpio = of_gpio_count(cnp);
++
++ for (i = 0; i < max_gpio; i++) {
++ unsigned flags = 0;
++ enum of_gpio_flags of_flags;
++
++ gpio = of_get_gpio_flags(cnp, i, &of_flags);
++
++ if (of_flags == OF_GPIO_ACTIVE_LOW)
++ flags |= GPIOF_ACTIVE_LOW;
++
++ if (!of_property_read_u32(cnp, "gpio-export,output", &val))
++ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
++ else
++ flags |= GPIOF_IN;
++
++ if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np)))
++ continue;
++
++ dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change");
++ gpio_export_with_name(gpio, dmc, name);
++ nb++;
++ }
++ }
++
++ dev_info(&pdev->dev, "%d gpio(s) exported\n", nb);
++
++ return 0;
++}
++
++static struct platform_driver gpio_export_driver = {
++ .driver = {
++ .name = "gpio-export",
++ .owner = THIS_MODULE,
++ .of_match_table = of_match_ptr(gpio_export_ids),
++ },
++};
++
++static int __init of_gpio_export_init(void)
++{
++ return platform_driver_probe(&gpio_export_driver, of_gpio_export_probe);
++}
++device_initcall(of_gpio_export_init);
+--- a/drivers/gpio/gpiolib-sysfs.c
++++ b/drivers/gpio/gpiolib-sysfs.c
+@@ -544,7 +544,7 @@ static struct class gpio_class = {
+ *
+ * Returns zero on success, else an error.
+ */
+-int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name)
+ {
+ struct gpio_chip *chip;
+ struct gpio_device *gdev;
+@@ -606,6 +606,8 @@ int gpiod_export(struct gpio_desc *desc,
+ offset = gpio_chip_hwgpio(desc);
+ if (chip->names && chip->names[offset])
+ ioname = chip->names[offset];
++ if (name)
++ ioname = name;
+
+ dev = device_create_with_groups(&gpio_class, &gdev->dev,
+ MKDEV(0, 0), data, gpio_groups,
+@@ -627,6 +629,12 @@ err_unlock:
+ gpiod_dbg(desc, "%s: status %d\n", __func__, status);
+ return status;
+ }
++EXPORT_SYMBOL_GPL(__gpiod_export);
++
++int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
++{
++ return __gpiod_export(desc, direction_may_change, NULL);
++}
+ EXPORT_SYMBOL_GPL(gpiod_export);
+
+ static int match_export(struct device *dev, const void *desc)
+--- a/include/asm-generic/gpio.h
++++ b/include/asm-generic/gpio.h
+@@ -126,6 +126,12 @@ static inline int gpio_export(unsigned g
+ return gpiod_export(gpio_to_desc(gpio), direction_may_change);
+ }
+
++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name);
++static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name)
++{
++ return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name);
++}
++
+ static inline int gpio_export_link(struct device *dev, const char *name,
+ unsigned gpio)
+ {
+--- a/include/linux/gpio/consumer.h
++++ b/include/linux/gpio/consumer.h
+@@ -427,6 +427,7 @@ static inline struct gpio_desc *devm_get
+
+ #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS)
+
++int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name);
+ int gpiod_export(struct gpio_desc *desc, bool direction_may_change);
+ int gpiod_export_link(struct device *dev, const char *name,
+ struct gpio_desc *desc);
+@@ -434,6 +435,13 @@ void gpiod_unexport(struct gpio_desc *de
+
+ #else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */
+
++static inline int _gpiod_export(struct gpio_desc *desc,
++ bool direction_may_change,
++ const char *name)
++{
++ return -ENOSYS;
++}
++
+ static inline int gpiod_export(struct gpio_desc *desc,
+ bool direction_may_change)
+ {
diff --git a/target/linux/ipq806x/patches-4.9/996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch b/target/linux/ipq806x/patches-4.9/996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch
new file mode 100644
index 0000000000..36490a2ebc
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch
@@ -0,0 +1,185 @@
+Author: Adrian Panella <ianchi74@outlook.com>
+Date: Fri Jun 10 19:10:15 2016 -0500
+
+generic: Mangle bootloader's kernel arguments
+
+The command-line arguments provided by the boot loader will be
+appended to a new device tree property: bootloader-args.
+If there is a property "append-rootblock" in DT under /chosen
+and a root= option in bootloaders command line it will be parsed
+and added to DT bootargs with the form: <append-rootblock>XX.
+Only command line ATAG will be processed, the rest of the ATAGs
+sent by bootloader will be ignored.
+This is usefull in dual boot systems, to get the current root partition
+without afecting the rest of the system.
+
+
+Signed-off-by: Adrian Panella <ianchi74@outlook.com>
+
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -1949,6 +1949,17 @@ config ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEN
+ The command-line arguments provided by the boot loader will be
+ appended to the the device tree bootargs property.
+
++config ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE
++ bool "Append rootblock parsing bootloader's kernel arguments"
++ help
++ The command-line arguments provided by the boot loader will be
++ appended to a new device tree property: bootloader-args.
++ If there is a property "append-rootblock" in DT under /chosen
++ and a root= option in bootloaders command line it will be parsed
++ and added to DT bootargs with the form: <append-rootblock>XX.
++ Only command line ATAG will be processed, the rest of the ATAGs
++ sent by bootloader will be ignored.
++
+ endchoice
+
+ config CMDLINE
+--- a/arch/arm/boot/compressed/atags_to_fdt.c
++++ b/arch/arm/boot/compressed/atags_to_fdt.c
+@@ -3,6 +3,8 @@
+
+ #if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND)
+ #define do_extend_cmdline 1
++#elif defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++#define do_extend_cmdline 1
+ #else
+ #define do_extend_cmdline 0
+ #endif
+@@ -66,6 +68,59 @@ static uint32_t get_cell_size(const void
+ return cell_size;
+ }
+
++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++
++static char *append_rootblock(char *dest, const char *str, int len, void *fdt)
++{
++ char *ptr, *end;
++ char *root="root=";
++ int i, l;
++ const char *rootblock;
++
++ //ARM doesn't have __HAVE_ARCH_STRSTR, so search manually
++ ptr = str - 1;
++
++ do {
++ //first find an 'r' at the begining or after a space
++ do {
++ ptr++;
++ ptr = strchr(ptr, 'r');
++ if(!ptr) return dest;
++
++ } while (ptr != str && *(ptr-1) != ' ');
++
++ //then check for the rest
++ for(i = 1; i <= 4; i++)
++ if(*(ptr+i) != *(root+i)) break;
++
++ } while (i != 5);
++
++ end = strchr(ptr, ' ');
++ end = end ? (end - 1) : (strchr(ptr, 0) - 1);
++
++ //find partition number (assumes format root=/dev/mtdXX | /dev/mtdblockXX | yy:XX )
++ for( i = 0; end >= ptr && *end >= '0' && *end <= '9'; end--, i++);
++ ptr = end + 1;
++
++ /* if append-rootblock property is set use it to append to command line */
++ rootblock = getprop(fdt, "/chosen", "append-rootblock", &l);
++ if(rootblock != NULL) {
++ if(*dest != ' ') {
++ *dest = ' ';
++ dest++;
++ len++;
++ }
++ if (len + l + i <= COMMAND_LINE_SIZE) {
++ memcpy(dest, rootblock, l);
++ dest += l - 1;
++ memcpy(dest, ptr, i);
++ dest += i;
++ }
++ }
++ return dest;
++}
++#endif
++
+ static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline)
+ {
+ char cmdline[COMMAND_LINE_SIZE];
+@@ -85,12 +140,21 @@ static void merge_fdt_bootargs(void *fdt
+
+ /* and append the ATAG_CMDLINE */
+ if (fdt_cmdline) {
++
++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++ //save original bootloader args
++ //and append ubi.mtd with root partition number to current cmdline
++ setprop_string(fdt, "/chosen", "bootloader-args", fdt_cmdline);
++ ptr = append_rootblock(ptr, fdt_cmdline, len, fdt);
++
++#else
+ len = strlen(fdt_cmdline);
+ if (ptr - cmdline + len + 2 < COMMAND_LINE_SIZE) {
+ *ptr++ = ' ';
+ memcpy(ptr, fdt_cmdline, len);
+ ptr += len;
+ }
++#endif
+ }
+ *ptr = '\0';
+
+@@ -147,7 +211,9 @@ int atags_to_fdt(void *atag_list, void *
+ else
+ setprop_string(fdt, "/chosen", "bootargs",
+ atag->u.cmdline.cmdline);
+- } else if (atag->hdr.tag == ATAG_MEM) {
++ }
++#ifndef CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE
++ else if (atag->hdr.tag == ATAG_MEM) {
+ if (memcount >= sizeof(mem_reg_property)/4)
+ continue;
+ if (!atag->u.mem.size)
+@@ -186,6 +252,10 @@ int atags_to_fdt(void *atag_list, void *
+ setprop(fdt, "/memory", "reg", mem_reg_property,
+ 4 * memcount * memsize);
+ }
++#else
++
++ }
++#endif
+
+ return fdt_pack(fdt);
+ }
+--- a/init/main.c
++++ b/init/main.c
+@@ -88,6 +88,10 @@
+ #include <asm/sections.h>
+ #include <asm/cacheflush.h>
+
++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++#include <linux/of.h>
++#endif
++
+ static int kernel_init(void *);
+
+ extern void init_IRQ(void);
+@@ -538,6 +542,18 @@ asmlinkage __visible void __init start_k
+ page_alloc_init();
+
+ pr_notice("Kernel command line: %s\n", boot_command_line);
++
++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++ //Show bootloader's original command line for reference
++ if(of_chosen) {
++ const char *prop = of_get_property(of_chosen, "bootloader-args", NULL);
++ if(prop)
++ pr_notice("Bootloader command line (ignored): %s\n", prop);
++ else
++ pr_notice("Bootloader command line not present\n");
++ }
++#endif
++
+ parse_early_param();
+ after_dashes = parse_args("Booting kernel",
+ static_command_line, __start___param,
diff --git a/target/linux/ipq806x/patches-4.9/999-dts.patch b/target/linux/ipq806x/patches-4.9/999-dts.patch
new file mode 100644
index 0000000000..3977d0b5d9
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/999-dts.patch
@@ -0,0 +1,5046 @@
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -573,92 +573,61 @@
+ omap4-var-stk-om44.dtb
+ dtb-$(CONFIG_SOC_AM43XX) += \
+ am43x-epos-evm.dtb \
+- am437x-cm-t43.dtb \
+- am437x-gp-evm.dtb \
++ am437x-sk-evm.dtb \
+ am437x-idk-evm.dtb \
+- am437x-sbc-t43.dtb \
+- am437x-sk-evm.dtb
++ am437x-gp-evm.dtb
+ dtb-$(CONFIG_SOC_OMAP5) += \
+ omap5-cm-t54.dtb \
+ omap5-igep0050.dtb \
+ omap5-sbc-t54.dtb \
+ omap5-uevm.dtb
+ dtb-$(CONFIG_SOC_DRA7XX) += \
+- am57xx-beagle-x15.dtb \
+- am57xx-beagle-x15-revb1.dtb \
+- am57xx-cl-som-am57x.dtb \
+- am57xx-sbc-am57x.dtb \
+- am572x-idk.dtb \
+ dra7-evm.dtb \
+- dra72-evm.dtb \
+- dra72-evm-revc.dtb
++ am57xx-beagle-x15.dtb \
++ dra72-evm.dtb
+ dtb-$(CONFIG_ARCH_ORION5X) += \
+- orion5x-kuroboxpro.dtb \
+ orion5x-lacie-d2-network.dtb \
+ orion5x-lacie-ethernet-disk-mini-v2.dtb \
+- orion5x-linkstation-lsgl.dtb \
+ orion5x-linkstation-lswtgl.dtb \
+ orion5x-lswsgl.dtb \
+ orion5x-maxtor-shared-storage-2.dtb \
+- orion5x-netgear-wnr854t.dtb \
+ orion5x-rd88f5182-nas.dtb
+ dtb-$(CONFIG_ARCH_PRIMA2) += \
+ prima2-evb.dtb
+-dtb-$(CONFIG_ARCH_OXNAS) += \
+- wd-mbwe.dtb
+ dtb-$(CONFIG_ARCH_QCOM) += \
+- qcom-apq8060-dragonboard.dtb \
+- qcom-apq8064-arrow-sd-600eval.dtb \
+ qcom-apq8064-cm-qs600.dtb \
+ qcom-apq8064-ifc6410.dtb \
+- qcom-apq8064-sony-xperia-yuga.dtb \
+- qcom-apq8064-asus-nexus7-flo.dtb \
+ qcom-apq8074-dragonboard.dtb \
+ qcom-apq8084-ifc6540.dtb \
+ qcom-apq8084-mtp.dtb \
+- qcom-ipq4019-ap.dk01.1-c1.dtb \
+- qcom-ipq4019-ap.dk04.1-c1.dtb \
+ qcom-ipq8064-ap148.dtb \
++ qcom-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-lge-nexus5-hammerhead.dtb \
+ qcom-msm8974-sony-xperia-honami.dtb
+ dtb-$(CONFIG_ARCH_REALVIEW) += \
+- arm-realview-pb1176.dtb \
+- arm-realview-pb11mp.dtb \
+- arm-realview-eb.dtb \
+- arm-realview-eb-bbrevd.dtb \
+- arm-realview-eb-11mp.dtb \
+- arm-realview-eb-11mp-bbrevd.dtb \
+- arm-realview-eb-11mp-ctrevb.dtb \
+- arm-realview-eb-11mp-bbrevd-ctrevb.dtb \
+- arm-realview-eb-a9mp.dtb \
+- arm-realview-eb-a9mp-bbrevd.dtb \
+- arm-realview-pba8.dtb \
+- arm-realview-pbx-a9.dtb
++ arm-realview-pb1176.dtb
+ dtb-$(CONFIG_ARCH_ROCKCHIP) += \
+- rk3036-evb.dtb \
+- rk3036-kylin.dtb \
+ rk3066a-bqcurie2.dtb \
+ rk3066a-marsboard.dtb \
+ rk3066a-rayeager.dtb \
+ rk3188-radxarock.dtb \
+- rk3228-evb.dtb \
+- rk3229-evb.dtb \
+ rk3288-evb-act8846.dtb \
+ rk3288-evb-rk808.dtb \
+- rk3288-fennec.dtb \
+ rk3288-firefly-beta.dtb \
+ rk3288-firefly.dtb \
+- rk3288-firefly-reload.dtb \
+- rk3288-miqi.dtb \
+ rk3288-popmetal.dtb \
+ rk3288-r89.dtb \
+ rk3288-rock2-square.dtb \
+- rk3288-veyron-brain.dtb \
+ rk3288-veyron-jaq.dtb \
+ rk3288-veyron-jerry.dtb \
+- rk3288-veyron-mickey.dtb \
+ rk3288-veyron-minnie.dtb \
+ rk3288-veyron-pinky.dtb \
+ rk3288-veyron-speedy.dtb
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -4,12 +4,9 @@
+ model = "Qualcomm IPQ8064/AP148";
+ compatible = "qcom,ipq8064-ap148", "qcom,ipq8064";
+
+- aliases {
+- serial0 = &gsbi4_serial;
+- };
+-
+- chosen {
+- stdout-path = "serial0:115200n8";
++ memory@0 {
++ reg = <0x42000000 0x1e000000>;
++ device_type = "memory";
+ };
+
+ reserved-memory {
+@@ -22,6 +19,15 @@
+ };
+ };
+
++ aliases {
++ serial0 = &uart4;
++ mdio-gpio0 = &mdio0;
++ };
++
++ chosen {
++ linux,stdout-path = "serial0:115200n8";
++ };
++
+ soc {
+ pinmux@800000 {
+ i2c4_pins: i2c4_pinmux {
+@@ -60,6 +66,25 @@
+ bias-bus-hold;
+ };
+ };
++
++ mdio0_pins: mdio0_pins {
++ mux {
++ pins = "gpio0", "gpio1";
++ function = "gpio";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
++
++ rgmii2_pins: rgmii2_pins {
++ mux {
++ pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32",
++ "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ;
++ function = "rgmii2";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
+ };
+
+ gsbi@16300000 {
+@@ -69,14 +94,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 {
+@@ -99,15 +122,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";
+ };
+ };
+ };
+@@ -117,23 +132,105 @@
+ };
+
+ sata@29000000 {
+- ports-implemented = <0x1>;
+ status = "ok";
+ };
+
++ phy@100f8800 { /* USB3 port 1 HS phy */
++ status = "ok";
++ };
++
++ phy@100f8830 { /* USB3 port 1 SS phy */
++ status = "ok";
++ };
++
++ phy@110f8800 { /* USB3 port 0 HS phy */
++ status = "ok";
++ };
++
++ phy@110f8830 { /* USB3 port 0 SS phy */
++ status = "ok";
++ };
++
++ usb30@0 {
++ status = "ok";
++ };
++
++ usb30@1 {
++ status = "ok";
++ };
++
++ pcie0: pci@1b500000 {
++ status = "ok";
++ phy-tx0-term-offset = <7>;
++ };
++
++ pcie1: pci@1b700000 {
++ status = "ok";
++ phy-tx0-term-offset = <7>;
++ };
++
+ nand@1ac00000 {
+ status = "ok";
+
+ pinctrl-0 = <&nand_pins>;
+ pinctrl-names = "default";
+
+- nandcs@0 {
+- compatible = "qcom,nandcs";
++ nand-ecc-strength = <4>;
++ nand-bus-width = <8>;
++
++ 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>;
++ };
++ };
++
++ 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>;
+
+- nand-ecc-strength = <4>;
+- nand-ecc-step-size = <512>;
+- nand-bus-width = <8>;
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
+ };
+ };
+ };
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-c2600.dts
+@@ -0,0 +1,501 @@
++#include "qcom-ipq8064-v1.0.dtsi"
++
++#include <dt-bindings/input/input.h>
++
++/ {
++ model = "TP-Link Archer C2600";
++ compatible = "tplink,c2600", "qcom,ipq8064";
++
++ memory@0 {
++ reg = <0x42000000 0x1e000000>;
++ device_type = "memory";
++ };
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ rsvd@41200000 {
++ reg = <0x41200000 0x300000>;
++ no-map;
++ };
++ };
++
++ aliases {
++ serial0 = &uart4;
++ mdio-gpio0 = &mdio0;
++
++ led-boot = &power;
++ led-failsafe = &general;
++ led-running = &power;
++ led-upgrade = &general;
++ };
++
++ chosen {
++ linux,stdout-path = "serial0:115200n8";
++ };
++
++ soc {
++ pinmux@800000 {
++ button_pins: button_pins {
++ mux {
++ pins = "gpio16", "gpio54", "gpio65";
++ function = "gpio";
++ drive-strength = <2>;
++ bias-pull-up;
++ };
++ };
++
++ i2c4_pins: i2c4_pinmux {
++ mux {
++ pins = "gpio12", "gpio13";
++ function = "gsbi4";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
++ led_pins: led_pins {
++ mux {
++ pins = "gpio6", "gpio7", "gpio8", "gpio9", "gpio26", "gpio33",
++ "gpio53", "gpio66";
++ function = "gpio";
++ drive-strength = <2>;
++ bias-pull-up;
++ };
++ };
++
++ spi_pins: spi_pins {
++ mux {
++ pins = "gpio18", "gpio19", "gpio21";
++ function = "gsbi5";
++ bias-pull-down;
++ };
++
++ data {
++ pins = "gpio18", "gpio19";
++ drive-strength = <10>;
++ };
++
++ cs {
++ pins = "gpio20";
++ function = "gpio";
++ drive-strength = <10>;
++ bias-pull-up;
++ };
++
++ clk {
++ pins = "gpio21";
++ drive-strength = <12>;
++ };
++ };
++
++ mdio0_pins: mdio0_pins {
++ mux {
++ pins = "gpio0", "gpio1";
++ function = "gpio";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
++
++ rgmii2_pins: rgmii2_pins {
++ mux {
++ pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32",
++ "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ;
++ function = "rgmii2";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
++
++ usb0_pwr_en_pin: usb0_pwr_en_pin {
++ mux {
++ pins = "gpio25";
++ function = "gpio";
++ drive-strength = <10>;
++ bias-pull-up;
++ output-high;
++ };
++ };
++
++ usb1_pwr_en_pin: usb1_pwr_en_pin {
++ mux {
++ pins = "gpio23";
++ function = "gpio";
++ drive-strength = <10>;
++ bias-pull-up;
++ output-high;
++ };
++ };
++ };
++
++ gsbi@16300000 {
++ qcom,mode = <GSBI_PROT_I2C_UART>;
++ status = "ok";
++ serial@16340000 {
++ status = "ok";
++ };
++ /*
++ * The i2c device on gsbi4 should not be enabled.
++ * On ipq806x designs gsbi4 i2c is meant for exclusive
++ * RPM usage. Turning this on in kernel manifests as
++ * i2c failure for the RPM.
++ */
++ };
++
++ gsbi5: gsbi@1a200000 {
++ qcom,mode = <GSBI_PROT_SPI>;
++ status = "ok";
++
++ spi5: spi@1a280000 {
++ status = "ok";
++
++ pinctrl-0 = <&spi_pins>;
++ pinctrl-names = "default";
++
++ cs-gpios = <&qcom_pinmux 20 GPIO_ACTIVE_HIGH>;
++
++ flash: m25p80@0 {
++ compatible = "jedec,spi-nor";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ spi-max-frequency = <50000000>;
++ reg = <0>;
++
++ SBL1@0 {
++ label = "SBL1";
++ reg = <0x0 0x20000>;
++ read-only;
++ };
++
++ MIBIB@20000 {
++ label = "MIBIB";
++ reg = <0x20000 0x20000>;
++ read-only;
++ };
++
++ SBL2@40000 {
++ label = "SBL2";
++ reg = <0x40000 0x20000>;
++ read-only;
++ };
++
++ SBL3@60000 {
++ label = "SBL3";
++ reg = <0x60000 0x30000>;
++ read-only;
++ };
++
++ DDRCONFIG@90000 {
++ label = "DDRCONFIG";
++ reg = <0x90000 0x10000>;
++ read-only;
++ };
++
++ SSD@a0000 {
++ label = "SSD";
++ reg = <0xa0000 0x10000>;
++ read-only;
++ };
++
++ TZ@b0000 {
++ label = "TZ";
++ reg = <0xb0000 0x30000>;
++ read-only;
++ };
++
++ RPM@e0000 {
++ label = "RPM";
++ reg = <0xe0000 0x20000>;
++ read-only;
++ };
++
++ fs-uboot@100000 {
++ label = "fs-uboot";
++ reg = <0x100000 0x70000>;
++ read-only;
++ };
++
++ uboot-env@170000 {
++ label = "uboot-env";
++ reg = <0x170000 0x40000>;
++ read-only;
++ };
++
++ radio@1b0000 {
++ label = "radio";
++ reg = <0x1b0000 0x40000>;
++ read-only;
++ };
++
++ os-image@1f0000 {
++ label = "os-image";
++ reg = <0x1f0000 0x200000>;
++ };
++
++ rootfs@3f0000 {
++ label = "rootfs";
++ reg = <0x3f0000 0x1b00000>;
++ };
++
++ defaultmac: default-mac@1ef0000 {
++ label = "default-mac";
++ reg = <0x1ef0000 0x00200>;
++ read-only;
++ };
++
++ pin@1ef0200 {
++ label = "pin";
++ reg = <0x1ef0200 0x00200>;
++ read-only;
++ };
++
++ product-info@1ef0400 {
++ label = "product-info";
++ reg = <0x1ef0400 0x0fc00>;
++ read-only;
++ };
++
++ partition-table@1f00000 {
++ label = "partition-table";
++ reg = <0x1f00000 0x10000>;
++ read-only;
++ };
++
++ soft-version@1f10000 {
++ label = "soft-version";
++ reg = <0x1f10000 0x10000>;
++ read-only;
++ };
++
++ support-list@1f20000 {
++ label = "support-list";
++ reg = <0x1f20000 0x10000>;
++ read-only;
++ };
++
++ profile@1f30000 {
++ label = "profile";
++ reg = <0x1f30000 0x10000>;
++ read-only;
++ };
++
++ default-config@1f40000 {
++ label = "default-config";
++ reg = <0x1f40000 0x10000>;
++ read-only;
++ };
++
++ user-config@1f50000 {
++ label = "user-config";
++ reg = <0x1f50000 0x40000>;
++ read-only;
++ };
++
++ qos-db@1f90000 {
++ label = "qos-db";
++ reg = <0x1f90000 0x40000>;
++ read-only;
++ };
++
++ usb-config@1fd0000 {
++ label = "usb-config";
++ reg = <0x1fd0000 0x10000>;
++ read-only;
++ };
++
++ log@1fe0000 {
++ label = "log";
++ reg = <0x1fe0000 0x20000>;
++ read-only;
++ };
++ };
++ };
++ };
++
++ 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";
++
++ pinctrl-0 = <&usb0_pwr_en_pin>;
++ pinctrl-names = "default";
++ };
++
++ usb30@1 {
++ status = "ok";
++
++ pinctrl-0 = <&usb1_pwr_en_pin>;
++ pinctrl-names = "default";
++ };
++
++ pcie0: pci@1b500000 {
++ status = "ok";
++ phy-tx0-term-offset = <7>;
++ };
++
++ pcie1: pci@1b700000 {
++ status = "ok";
++ phy-tx0-term-offset = <7>;
++ };
++
++ mdio0: mdio {
++ compatible = "virtual,mdio-gpio";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>;
++ 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>;
++ };
++ };
++
++ gmac1: ethernet@37200000 {
++ status = "ok";
++ phy-mode = "rgmii";
++ qcom,id = <1>;
++
++ pinctrl-0 = <&rgmii2_pins>;
++ pinctrl-names = "default";
++
++ mtd-mac-address = <&defaultmac 0x8>;
++ mtd-mac-address-increment = <1>;
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ gmac2: ethernet@37400000 {
++ status = "ok";
++ phy-mode = "sgmii";
++ qcom,id = <2>;
++
++ mtd-mac-address = <&defaultmac 0x8>;
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ rpm@108000 {
++ pinctrl-0 = <&i2c4_pins>;
++ pinctrl-names = "default";
++ };
++ };
++
++ gpio-keys {
++ compatible = "gpio-keys";
++ pinctrl-0 = <&button_pins>;
++ pinctrl-names = "default";
++
++ wifi {
++ label = "wifi";
++ gpios = <&qcom_pinmux 49 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_RFKILL>;
++ };
++
++ reset {
++ label = "reset";
++ gpios = <&qcom_pinmux 64 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_RESTART>;
++ };
++
++ wps {
++ label = "wps";
++ gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_WPS_BUTTON>;
++ };
++
++ ledswitch {
++ label = "ledswitch";
++ gpios = <&qcom_pinmux 16 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_LIGHTS_TOGGLE>;
++ };
++ };
++
++ gpio-leds {
++ compatible = "gpio-leds";
++ pinctrl-0 = <&led_pins>;
++ pinctrl-names = "default";
++
++ lan {
++ label = "c2600:white:lan";
++ gpios = <&qcom_pinmux 6 GPIO_ACTIVE_HIGH>;
++ };
++
++ usb4 {
++ label = "c2600:white:usb_4";
++ gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>;
++ };
++
++ usb2 {
++ label = "c2600:white:usb_2";
++ gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>;
++ };
++
++ wps {
++ label = "c2600:white:wps";
++ gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>;
++ };
++
++ wan_amber {
++ label = "c2600:amber:wan";
++ gpios = <&qcom_pinmux 26 GPIO_ACTIVE_LOW>;
++ };
++
++ wan_white {
++ label = "c2600:white:wan";
++ gpios = <&qcom_pinmux 33 GPIO_ACTIVE_LOW>;
++ };
++
++ power: power {
++ label = "c2600:white:power";
++ gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>;
++ default-state = "keep";
++ };
++
++ general: general {
++ label = "c2600:white:general";
++ gpios = <&qcom_pinmux 66 GPIO_ACTIVE_HIGH>;
++ };
++ };
++};
++
++&adm_dma {
++ status = "ok";
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-d7800.dts
+@@ -0,0 +1,406 @@
++#include "qcom-ipq8064-v1.0.dtsi"
++
++#include <dt-bindings/input/input.h>
++
++/ {
++ model = "Netgear Nighthawk X4 D7800";
++ compatible = "netgear,d7800", "qcom,ipq8064";
++
++ memory@0 {
++ reg = <0x42000000 0xe000000>;
++ device_type = "memory";
++ };
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ rsvd@41200000 {
++ reg = <0x41200000 0x300000>;
++ no-map;
++ };
++ };
++
++ aliases {
++ serial0 = &uart4;
++ mdio-gpio0 = &mdio0;
++
++ led-boot = &power_white;
++ led-failsafe = &power_amber;
++ led-running = &power_white;
++ led-upgrade = &power_amber;
++ };
++
++ chosen {
++ bootargs = "rootfstype=squashfs noinitrd";
++ linux,stdout-path = "serial0:115200n8";
++ };
++
++ soc {
++ pinmux@800000 {
++ button_pins: button_pins {
++ mux {
++ pins = "gpio6", "gpio54", "gpio65";
++ function = "gpio";
++ drive-strength = <2>;
++ bias-pull-up;
++ };
++ };
++
++ i2c4_pins: i2c4_pinmux {
++ mux {
++ pins = "gpio12", "gpio13";
++ function = "gsbi4";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
++ led_pins: led_pins {
++ mux {
++ pins = "gpio7", "gpio8", "gpio9", "gpio22", "gpio23",
++ "gpio24","gpio26", "gpio53", "gpio64";
++ function = "gpio";
++ drive-strength = <2>;
++ bias-pull-up;
++ };
++ };
++
++ mdio0_pins: mdio0_pins {
++ mux {
++ pins = "gpio0", "gpio1";
++ function = "gpio";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
++
++ nand_pins: nand_pins {
++ mux {
++ pins = "gpio34", "gpio35", "gpio36",
++ "gpio37", "gpio38", "gpio39",
++ "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ function = "nand";
++ drive-strength = <10>;
++ bias-disable;
++ };
++ pullups {
++ pins = "gpio39";
++ bias-pull-up;
++ };
++ hold {
++ pins = "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ bias-bus-hold;
++ };
++ };
++
++ 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;
++ };
++ };
++
++ usb0_pwr_en_pins: usb0_pwr_en_pins {
++ mux {
++ pins = "gpio15";
++ function = "gpio";
++ drive-strength = <12>;
++ bias-pull-down;
++ output-high;
++ };
++ };
++
++ usb1_pwr_en_pins: usb1_pwr_en_pins {
++ mux {
++ pins = "gpio16", "gpio68";
++ function = "gpio";
++ drive-strength = <12>;
++ bias-pull-down;
++ output-high;
++ };
++ };
++ };
++
++ gsbi@16300000 {
++ qcom,mode = <GSBI_PROT_I2C_UART>;
++ status = "ok";
++ serial@16340000 {
++ status = "ok";
++ };
++ /*
++ * The i2c device on gsbi4 should not be enabled.
++ * On ipq806x designs gsbi4 i2c is meant for exclusive
++ * RPM usage. Turning this on in kernel manifests as
++ * i2c failure for the RPM.
++ */
++ };
++
++ sata-phy@1b400000 {
++ status = "ok";
++ };
++
++ sata@29000000 {
++ status = "ok";
++ };
++
++ phy@100f8800 { /* USB3 port 1 HS phy */
++ status = "ok";
++ };
++
++ phy@100f8830 { /* USB3 port 1 SS phy */
++ status = "ok";
++ };
++
++ phy@110f8800 { /* USB3 port 0 HS phy */
++ status = "ok";
++ };
++
++ phy@110f8830 { /* USB3 port 0 SS phy */
++ status = "ok";
++ };
++
++ usb30@0 {
++ status = "ok";
++
++ pinctrl-0 = <&usb0_pwr_en_pins>;
++ pinctrl-names = "default";
++ };
++
++ usb30@1 {
++ status = "ok";
++
++ pinctrl-0 = <&usb1_pwr_en_pins>;
++ pinctrl-names = "default";
++ };
++
++ pcie0: pci@1b500000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 3 GPIO_ACTIVE_HIGH>;
++ pinctrl-0 = <&pcie0_pins>;
++ pinctrl-names = "default";
++ };
++
++ pcie1: pci@1b700000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 48 GPIO_ACTIVE_HIGH>;
++ pinctrl-0 = <&pcie1_pins>;
++ pinctrl-names = "default";
++ };
++
++ nand@1ac00000 {
++ status = "ok";
++
++ pinctrl-0 = <&nand_pins>;
++ pinctrl-names = "default";
++
++ nand-ecc-strength = <4>;
++ nand-bus-width = <8>;
++
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ qcadata@0 {
++ label = "qcadata";
++ reg = <0x0000000 0x0c80000>;
++ read-only;
++ };
++
++ APPSBL@c80000 {
++ label = "APPSBL";
++ reg = <0x0c80000 0x0500000>;
++ read-only;
++ };
++
++ APPSBLENV@1180000 {
++ label = "APPSBLENV";
++ reg = <0x1180000 0x0080000>;
++ read-only;
++ };
++
++ art: art@1200000 {
++ label = "art";
++ reg = <0x1200000 0x0140000>;
++ read-only;
++ };
++
++ artbak: art@1340000 {
++ label = "artbak";
++ reg = <0x1340000 0x0140000>;
++ read-only;
++ };
++
++ kernel@1480000 {
++ label = "kernel";
++ reg = <0x1480000 0x0200000>;
++ };
++
++ ubi@1680000 {
++ label = "ubi";
++ reg = <0x1680000 0x1E00000>;
++ };
++
++ netgear@3480000 {
++ label = "netgear";
++ reg = <0x3480000 0x4480000>;
++ read-only;
++ };
++
++ reserve@7900000 {
++ label = "reserve";
++ reg = <0x7900000 0x0700000>;
++ read-only;
++ };
++
++ firmware@1480000 {
++ label = "firmware";
++ reg = <0x1480000 0x2000000>;
++ };
++ };
++
++ mdio0: mdio {
++ compatible = "virtual,mdio-gpio";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>;
++ 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>;
++ };
++ };
++
++ gmac1: ethernet@37200000 {
++ status = "ok";
++ phy-mode = "rgmii";
++ phy-handle = <&phy4>;
++ qcom,id = <1>;
++
++ pinctrl-0 = <&rgmii2_pins>;
++ pinctrl-names = "default";
++
++ mtd-mac-address = <&art 6>;
++ };
++
++ gmac2: ethernet@37400000 {
++ status = "ok";
++ phy-mode = "sgmii";
++ qcom,id = <2>;
++
++ mtd-mac-address = <&art 0>;
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ rpm@108000 {
++ pinctrl-0 = <&i2c4_pins>;
++ pinctrl-names = "default";
++ };
++ };
++
++ gpio-keys {
++ compatible = "gpio-keys";
++ pinctrl-0 = <&button_pins>;
++ pinctrl-names = "default";
++
++ wifi {
++ label = "wifi";
++ gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_RFKILL>;
++ };
++
++ reset {
++ label = "reset";
++ gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_RESTART>;
++ };
++
++ wps {
++ label = "wps";
++ gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_WPS_BUTTON>;
++ };
++ };
++
++ gpio-leds {
++ compatible = "gpio-leds";
++ pinctrl-0 = <&led_pins>;
++ pinctrl-names = "default";
++
++ usb1 {
++ label = "d7800:white:usb1";
++ gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>;
++ };
++
++ usb2 {
++ label = "d7800:white:usb2";
++ gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>;
++ };
++
++ power_amber: power_amber {
++ label = "d7800:amber:power";
++ gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>;
++ };
++
++ wan_white {
++ label = "d7800:white:wan";
++ gpios = <&qcom_pinmux 22 GPIO_ACTIVE_HIGH>;
++ };
++
++ wan_amber {
++ label = "d7800:amber:wan";
++ gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>;
++ };
++
++ wps {
++ label = "d7800:white:wps";
++ gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>;
++ };
++
++ esata {
++ label = "d7800:white:esata";
++ gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>;
++ };
++
++ power_white: power_white {
++ label = "d7800:white:power";
++ gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>;
++ default-state = "keep";
++ };
++
++ wifi {
++ label = "d7800:white:wifi";
++ gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>;
++ };
++ };
++};
++
++&adm_dma {
++ status = "ok";
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
+@@ -0,0 +1,236 @@
++#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;
++ mdio-gpio0 = &mdio0;
++ };
++
++ 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;
++ };
++ };
++
++ mdio0_pins: mdio0_pins {
++ mux {
++ pins = "gpio0", "gpio1";
++ function = "gpio";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
++
++ rgmii0_pins: rgmii0_pins {
++ mux {
++ pins = "gpio2", "gpio66";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
++ };
++
++ gsbi2: gsbi@12480000 {
++ qcom,mode = <GSBI_PROT_I2C_UART>;
++ status = "ok";
++ uart2: serial@12490000 {
++ status = "ok";
++ };
++ };
++
++ gsbi5: gsbi@1a200000 {
++ qcom,mode = <GSBI_PROT_SPI>;
++ status = "ok";
++
++ spi4: spi@1a280000 {
++ status = "ok";
++ spi-max-frequency = <50000000>;
++
++ pinctrl-0 = <&spi_pins>;
++ pinctrl-names = "default";
++
++ cs-gpios = <&qcom_pinmux 20 0>;
++
++ flash: m25p80@0 {
++ compatible = "s25fl256s1";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ spi-max-frequency = <50000000>;
++ reg = <0>;
++ m25p,fast-read;
++
++ partition@0 {
++ label = "lowlevel_init";
++ reg = <0x0 0x1b0000>;
++ };
++
++ partition@1 {
++ label = "u-boot";
++ reg = <0x1b0000 0x80000>;
++ };
++
++ partition@2 {
++ label = "u-boot-env";
++ reg = <0x230000 0x40000>;
++ };
++
++ partition@3 {
++ label = "caldata";
++ reg = <0x270000 0x40000>;
++ };
++
++ partition@4 {
++ label = "firmware";
++ reg = <0x2b0000 0x1d50000>;
++ };
++ };
++ };
++ };
++
++ sata-phy@1b400000 {
++ status = "ok";
++ };
++
++ sata@29000000 {
++ status = "ok";
++ };
++
++ phy@100f8800 { /* USB3 port 1 HS phy */
++ status = "ok";
++ };
++
++ phy@100f8830 { /* USB3 port 1 SS phy */
++ status = "ok";
++ };
++
++ phy@110f8800 { /* USB3 port 0 HS phy */
++ status = "ok";
++ };
++
++ phy@110f8830 { /* USB3 port 0 SS phy */
++ status = "ok";
++ };
++
++ usb30@0 {
++ status = "ok";
++ };
++
++ usb30@1 {
++ status = "ok";
++ };
++
++ pcie0: pci@1b500000 {
++ status = "ok";
++ };
++
++ pcie1: pci@1b700000 {
++ status = "ok";
++ };
++
++ 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>;
++ };
++ };
++
++ 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
+@@ -1,11 +1,13 @@
+ /dts-v1/;
+
+ #include "skeleton.dtsi"
+-#include <dt-bindings/reset/qcom,gcc-ipq806x.h>
+ #include <dt-bindings/clock/qcom,gcc-ipq806x.h>
++#include <dt-bindings/mfd/qcom-rpm.h>
+ #include <dt-bindings/clock/qcom,lcc-ipq806x.h>
+ #include <dt-bindings/soc/qcom,gsbi.h>
++#include <dt-bindings/reset/qcom,gcc-ipq806x.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
++#include <dt-bindings/gpio/gpio.h>
+
+ / {
+ model = "Qualcomm IPQ8064";
+@@ -16,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";
+@@ -24,9 +26,18 @@
+ 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>;
++ cooling-min-state = <0>;
++ cooling-max-state = <10>;
++ #cooling-cells = <2>;
++ cpu-idle-states = <&CPU_SPC>;
+ };
+
+- cpu@1 {
++ cpu1: cpu@1 {
+ compatible = "qcom,krait";
+ enable-method = "qcom,kpss-acc-v1";
+ device_type = "cpu";
+@@ -34,11 +45,120 @@
+ 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>;
++ cooling-min-state = <0>;
++ cooling-max-state = <10>;
++ #cooling-cells = <2>;
++ cpu-idle-states = <&CPU_SPC>;
+ };
+
+ L2: l2-cache {
+ compatible = "cache";
+ cache-level = <2>;
++ qcom,saw = <&saw_l2>;
++ };
++
++ 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>;
++ };
++ };
++ };
++
++ 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";
++ };
++ };
+ };
+ };
+
+@@ -57,7 +177,7 @@
+ no-map;
+ };
+
+- smem@41000000 {
++ smem: smem@41000000 {
+ reg = <0x41000000 0x200000>;
+ no-map;
+ };
+@@ -67,13 +187,13 @@
+ cxo_board {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+- clock-frequency = <19200000>;
++ clock-frequency = <25000000>;
+ };
+
+ pxo_board {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+- clock-frequency = <27000000>;
++ clock-frequency = <25000000>;
+ };
+
+ sleep_clk: sleep_clk {
+@@ -83,6 +203,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>;
+@@ -104,6 +264,85 @@
+ 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>;
++ qcom,ipc = <&l2cc 0x8 2>;
++
++ interrupts = <0 19 0>,
++ <0 21 0>,
++ <0 22 0>;
++ interrupt-names = "ack",
++ "err",
++ "wakeup";
++
++ clocks = <&gcc RPM_MSG_RAM_H_CLK>;
++ clock-names = "ram";
++
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ rpmcc: clock-controller {
++ compatible = "qcom,rpmcc-ipq806x", "qcom,rpmcc";
++ #clock-cells = <1>;
++ };
++
++ regulators {
++ compatible = "qcom,rpm-smb208-regulators";
++
++ smb208_s1a: s1a {
++ regulator-min-microvolt = <1050000>;
++ regulator-max-microvolt = <1150000>;
++
++ qcom,switch-mode-frequency = <1200000>;
++
++ };
++
++ smb208_s1b: s1b {
++ regulator-min-microvolt = <1050000>;
++ regulator-max-microvolt = <1150000>;
++
++ qcom,switch-mode-frequency = <1200000>;
++ };
++
++ smb208_s2a: s2a {
++ regulator-min-microvolt = < 800000>;
++ regulator-max-microvolt = <1250000>;
++
++ qcom,switch-mode-frequency = <1200000>;
++ };
++
++ smb208_s2b: s2b {
++ regulator-min-microvolt = < 800000>;
++ regulator-max-microvolt = <1250000>;
++
++ qcom,switch-mode-frequency = <1200000>;
++ };
++ };
++ };
++
++ 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>;
+@@ -113,6 +352,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 {
+@@ -124,8 +391,7 @@
+ };
+
+ timer@200a000 {
+- compatible = "qcom,kpss-timer",
+- "qcom,kpss-wdt-ipq8064", "qcom,msm-timer";
++ compatible = "qcom,kpss-timer", "qcom,msm-timer";
+ interrupts = <1 1 0x301>,
+ <1 2 0x301>,
+ <1 3 0x301>,
+@@ -142,25 +408,44 @@
+ acc0: clock-controller@2088000 {
+ compatible = "qcom,kpss-acc-v1";
+ reg = <0x02088000 0x1000>, <0x02008000 0x1000>;
++ clock-output-names = "acpu0_aux";
+ };
+
+ acc1: clock-controller@2098000 {
+ compatible = "qcom,kpss-acc-v1";
+ reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
++ clock-output-names = "acpu1_aux";
+ };
+
++ l2cc: clock-controller@2011000 {
++ compatible = "qcom,kpss-gcc", "syscon";
++ reg = <0x2011000 0x1000>;
++ clock-output-names = "acpu_l2_aux";
++ };
++
+ 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>;
+@@ -174,7 +459,7 @@
+
+ syscon-tcsr = <&tcsr>;
+
+- serial@12490000 {
++ uart2: serial@12490000 {
+ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+ reg = <0x12490000 0x1000>,
+ <0x12480000 0x1000>;
+@@ -212,7 +497,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>;
+@@ -249,7 +534,7 @@
+
+ syscon-tcsr = <&tcsr>;
+
+- serial@1a240000 {
++ uart5: serial@1a240000 {
+ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+ reg = <0x1a240000 0x1000>,
+ <0x1a200000 0x1000>;
+@@ -328,8 +613,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 {
+@@ -344,10 +633,259 @@
+ #reset-cells = <1>;
+ };
+
++ sfpb_mutex_block: syscon@1200600 {
++ 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";
++
++ syscon-tcsr = <&tcsr 0xb0 1>;
++
++ 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";
++ snps,dis_u3_susphy_quirk;
++ };
++ };
++
++ usb3_1: usb30@1 {
++ compatible = "qcom,dwc3";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ clocks = <&gcc USB30_1_MASTER_CLK>;
++ clock-names = "core";
++
++ syscon-tcsr = <&tcsr 0xb0 0>;
++
++ 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";
++ snps,dis_u3_susphy_quirk;
++ };
++ };
++
++ 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 = <GIC_SPI 35 IRQ_TYPE_NONE>;
++ interrupt-names = "msi";
++ #interrupt-cells = <1>;
++ interrupt-map-mask = <0 0 0 0x7>;
++ interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
++ <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
++ <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
++ <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
++
++ clocks = <&gcc PCIE_A_CLK>,
++ <&gcc PCIE_H_CLK>,
++ <&gcc PCIE_PHY_CLK>,
++ <&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>,
++ <&gcc PCIE_EXT_RESET>;
++ reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
++
++ 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 = <GIC_SPI 57 IRQ_TYPE_NONE>;
++ interrupt-names = "msi";
++ #interrupt-cells = <1>;
++ interrupt-map-mask = <0 0 0 0x7>;
++ interrupt-map = <0 0 0 1 &intc 0 58 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
++ <0 0 0 2 &intc 0 59 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
++ <0 0 0 3 &intc 0 60 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
++ <0 0 0 4 &intc 0 61 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
++
++ clocks = <&gcc PCIE_1_A_CLK>,
++ <&gcc PCIE_1_H_CLK>,
++ <&gcc PCIE_1_PHY_CLK>,
++ <&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>,
++ <&gcc PCIE_1_EXT_RESET>;
++ reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
++
++ 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 = <GIC_SPI 71 IRQ_TYPE_NONE>;
++ interrupt-names = "msi";
++ #interrupt-cells = <1>;
++ interrupt-map-mask = <0 0 0 0x7>;
++ interrupt-map = <0 0 0 1 &intc 0 72 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
++ <0 0 0 2 &intc 0 73 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
++ <0 0 0 3 &intc 0 74 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
++ <0 0 0 4 &intc 0 75 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
++
++ clocks = <&gcc PCIE_2_A_CLK>,
++ <&gcc PCIE_2_H_CLK>,
++ <&gcc PCIE_2_PHY_CLK>,
++ <&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>,
++ <&gcc PCIE_2_EXT_RESET>;
++ reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
++
++ pinctrl-0 = <&pcie2_pins>;
++ pinctrl-names = "default";
++
++ perst-gpios = <&qcom_pinmux 63 GPIO_ACTIVE_LOW>;
++
++ status = "disabled";
++ };
++
+ adm_dma: dma@18300000 {
+ compatible = "qcom,adm";
+ reg = <0x18300000 0x100000>;
+- interrupts = <GIC_SPI 170 IRQ_TYPE_NONE>;
++ interrupts = <0 170 0>;
+ #dma-cells = <1>;
+
+ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>;
+@@ -365,7 +903,7 @@
+ };
+
+ nand@1ac00000 {
+- compatible = "qcom,ipq806x-nand";
++ compatible = "qcom,ebi2-nandc";
+ reg = <0x1ac00000 0x800>;
+
+ clocks = <&gcc EBI2_CLK>,
+@@ -380,5 +918,103 @@
+ 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 = <GIC_SPI 220 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "macirq";
++
++ qcom,nss-common = <&nss_common>;
++ qcom,qsgmii-csr = <&qsgmii_csr>;
++
++ clocks = <&gcc GMAC_CORE1_CLK>;
++ clock-names = "stmmaceth";
++
++ resets = <&gcc GMAC_CORE1_RESET>;
++ reset-names = "stmmaceth";
++
++ status = "disabled";
++ };
++
++ gmac1: ethernet@37200000 {
++ device_type = "network";
++ compatible = "qcom,ipq806x-gmac";
++ reg = <0x37200000 0x200000>;
++ interrupts = <GIC_SPI 223 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "macirq";
++
++ qcom,nss-common = <&nss_common>;
++ qcom,qsgmii-csr = <&qsgmii_csr>;
++
++ clocks = <&gcc GMAC_CORE2_CLK>;
++ clock-names = "stmmaceth";
++
++ resets = <&gcc GMAC_CORE2_RESET>;
++ reset-names = "stmmaceth";
++
++ status = "disabled";
++ };
++
++ gmac2: ethernet@37400000 {
++ device_type = "network";
++ compatible = "qcom,ipq806x-gmac";
++ reg = <0x37400000 0x200000>;
++ interrupts = <GIC_SPI 226 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "macirq";
++
++ qcom,nss-common = <&nss_common>;
++ qcom,qsgmii-csr = <&qsgmii_csr>;
++
++ clocks = <&gcc GMAC_CORE3_CLK>;
++ clock-names = "stmmaceth";
++
++ resets = <&gcc GMAC_CORE3_RESET>;
++ reset-names = "stmmaceth";
++
++ status = "disabled";
++ };
++
++ gmac3: ethernet@37600000 {
++ device_type = "network";
++ compatible = "qcom,ipq806x-gmac";
++ reg = <0x37600000 0x200000>;
++ interrupts = <GIC_SPI 229 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "macirq";
++
++ qcom,nss-common = <&nss_common>;
++ qcom,qsgmii-csr = <&qsgmii_csr>;
++
++ clocks = <&gcc GMAC_CORE4_CLK>;
++ clock-names = "stmmaceth";
++
++ resets = <&gcc GMAC_CORE4_RESET>;
++ reset-names = "stmmaceth";
++
++ status = "disabled";
++ };
++ };
++
++ sfpb_mutex: sfpb-mutex {
++ compatible = "qcom,sfpb-mutex";
++ syscon = <&sfpb_mutex_block 4 4>;
++
++ #hwlock-cells = <1>;
++ };
++
++ smem {
++ compatible = "qcom,smem";
++ memory-region = <&smem>;
++ hwlocks = <&sfpb_mutex 3>;
+ };
+ };
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-ea8500.dts
+@@ -0,0 +1,399 @@
++#include "qcom-ipq8064-v1.0.dtsi"
++
++#include <dt-bindings/input/input.h>
++
++/ {
++ model = "Linksys EA8500 WiFi Router";
++ compatible = "linksys,ea8500", "qcom,ipq8064";
++
++ memory@0 {
++ reg = <0x42000000 0x1e000000>;
++ device_type = "memory";
++ };
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ rsvd@41200000 {
++ reg = <0x41200000 0x300000>;
++ no-map;
++ };
++ };
++
++ aliases {
++ serial0 = &uart4;
++ mdio-gpio0 = &mdio0;
++
++ led-boot = &power;
++ led-failsafe = &power;
++ led-running = &power;
++ led-upgrade = &power;
++ };
++
++ chosen {
++ bootargs = "console=ttyMSM0,115200n8";
++ linux,stdout-path = "serial0:115200n8";
++ append-rootblock = "ubi.mtd="; /* append to bootargs adding the root deviceblock nbr from bootloader */
++ };
++
++ soc {
++ pinmux@800000 {
++ button_pins: button_pins {
++ mux {
++ pins = "gpio65", "gpio67", "gpio68";
++ function = "gpio";
++ drive-strength = <2>;
++ bias-pull-up;
++ };
++ };
++
++ i2c4_pins: i2c4_pinmux {
++ mux {
++ pins = "gpio12", "gpio13";
++ function = "gsbi4";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
++ led_pins: led_pins {
++ mux {
++ pins = "gpio6", "gpio53", "gpio54";
++ function = "gpio";
++ drive-strength = <2>;
++ bias-pull-up;
++ };
++ };
++
++ mdio0_pins: mdio0_pins {
++ mux {
++ pins = "gpio0", "gpio1";
++ function = "gpio";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
++
++ nand_pins: nand_pins {
++ mux {
++ pins = "gpio34", "gpio35", "gpio36",
++ "gpio37", "gpio38", "gpio39",
++ "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ function = "nand";
++ drive-strength = <10>;
++ bias-disable;
++ };
++ pullups {
++ pins = "gpio39";
++ bias-pull-up;
++ };
++ hold {
++ pins = "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ bias-bus-hold;
++ };
++ };
++
++ rgmii2_pins: rgmii2_pins {
++ mux {
++ pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32",
++ "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ;
++ function = "rgmii2";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
++ };
++
++ gsbi@16300000 {
++ qcom,mode = <GSBI_PROT_I2C_UART>;
++ status = "ok";
++ serial@16340000 {
++ status = "ok";
++ };
++ /*
++ * The i2c device on gsbi4 should not be enabled.
++ * On ipq806x designs gsbi4 i2c is meant for exclusive
++ * RPM usage. Turning this on in kernel manifests as
++ * i2c failure for the RPM.
++ */
++ };
++
++ sata-phy@1b400000 {
++ status = "ok";
++ };
++
++ sata@29000000 {
++ status = "ok";
++ };
++
++ phy@100f8800 { /* USB3 port 1 HS phy */
++ status = "ok";
++ };
++
++ phy@100f8830 { /* USB3 port 1 SS phy */
++ status = "ok";
++ };
++
++ phy@110f8800 { /* USB3 port 0 HS phy */
++ status = "ok";
++ };
++
++ phy@110f8830 { /* USB3 port 0 SS phy */
++ status = "ok";
++ };
++
++ usb30@0 {
++ status = "ok";
++ };
++
++ usb30@1 {
++ status = "ok";
++ };
++
++ pcie0: pci@1b500000 {
++ status = "ok";
++ phy-tx0-term-offset = <7>;
++ };
++
++ pcie1: pci@1b700000 {
++ status = "ok";
++ phy-tx0-term-offset = <7>;
++ };
++
++ pcie2: pci@1b900000 {
++ 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>;
++
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ SBL1@0 {
++ label = "SBL1";
++ reg = <0x0000000 0x0040000>;
++ read-only;
++ };
++
++ MIBIB@40000 {
++ label = "MIBIB";
++ reg = <0x0040000 0x0140000>;
++ read-only;
++ };
++
++ SBL2@180000 {
++ label = "SBL2";
++ reg = <0x0180000 0x0140000>;
++ read-only;
++ };
++
++ SBL3@2c0000 {
++ label = "SBL3";
++ reg = <0x02c0000 0x0280000>;
++ read-only;
++ };
++
++ DDRCONFIG@540000 {
++ label = "DDRCONFIG";
++ reg = <0x0540000 0x0120000>;
++ read-only;
++ };
++
++ SSD@660000 {
++ label = "SSD";
++ reg = <0x0660000 0x0120000>;
++ read-only;
++ };
++
++ TZ@780000 {
++ label = "TZ";
++ reg = <0x0780000 0x0280000>;
++ read-only;
++ };
++
++ RPM@a00000 {
++ label = "RPM";
++ reg = <0x0a00000 0x0280000>;
++ read-only;
++ };
++
++ art: art@c80000 {
++ label = "art";
++ reg = <0x0c80000 0x0140000>;
++ read-only;
++ };
++
++ APPSBL@dc0000 {
++ label = "APPSBL";
++ reg = <0x0dc0000 0x0100000>;
++ read-only;
++ };
++
++ u_env@ec0000 {
++ label = "u_env";
++ reg = <0x0ec0000 0x0040000>;
++ };
++
++ s_env@f00000 {
++ label = "s_env";
++ reg = <0x0f00000 0x0040000>;
++ };
++
++ devinfo@f40000 {
++ label = "devinfo";
++ reg = <0x0f40000 0x0040000>;
++ };
++
++ linux@f80000 {
++ label = "kernel1";
++ reg = <0x0f80000 0x2800000>; /* 3 MB spill to rootfs*/
++ };
++
++ rootfs@1280000 {
++ label = "rootfs1";
++ reg = <0x1280000 0x2500000>;
++ };
++
++ linux2@3780000 {
++ label = "kernel2";
++ reg = <0x3780000 0x2800000>;
++ };
++
++ rootfs2@3a80000 {
++ label = "rootfs2";
++ reg = <0x3a80000 0x2500000>;
++ };
++
++ syscfg@5f80000 {
++ label = "syscfg";
++ reg = <0x5f80000 0x2080000>;
++ };
++ };
++
++ mdio0: mdio {
++ compatible = "virtual,mdio-gpio";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>;
++ 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>;
++ };
++ };
++
++ gmac1: ethernet@37200000 {
++ status = "ok";
++ phy-mode = "rgmii";
++ qcom,id = <1>;
++ qcom,phy_mdio_addr = <4>;
++ qcom,poll_required = <1>;
++ qcom,rgmii_delay = <0>;
++ qcom,emulation = <0>;
++ pinctrl-0 = <&rgmii2_pins>;
++ pinctrl-names = "default";
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++ //lan
++ gmac2: ethernet@37400000 {
++ status = "ok";
++ phy-mode = "sgmii";
++ qcom,id = <2>;
++ qcom,phy_mdio_addr = <0>; /* none */
++ qcom,poll_required = <0>; /* no polling */
++ qcom,rgmii_delay = <0>;
++ qcom,emulation = <0>;
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ rpm@108000 {
++ pinctrl-0 = <&i2c4_pins>;
++ pinctrl-names = "default";
++ };
++
++ adm_dma: dma@18300000 {
++ status = "ok";
++ };
++ };
++
++ gpio-keys {
++ compatible = "gpio-keys";
++ pinctrl-0 = <&button_pins>;
++ pinctrl-names = "default";
++
++ wifi {
++ label = "wifi";
++ gpios = <&qcom_pinmux 67 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_RFKILL>;
++ };
++
++ reset {
++ label = "reset";
++ gpios = <&qcom_pinmux 68 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_RESTART >;
++ };
++
++ wps {
++ label = "wps";
++ gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_WPS_BUTTON>;
++ };
++ };
++
++ gpio-leds {
++ compatible = "gpio-leds";
++ pinctrl-0 = <&led_pins>;
++ pinctrl-names = "default";
++
++ wps {
++ label = "ea8500:green:wps";
++ gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>;
++ };
++
++ power: power {
++ label = "ea8500:white:power";
++ gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>;
++ default-state = "keep";
++ };
++
++ wifi {
++ label = "ea8500:green:wifi";
++ gpios = <&qcom_pinmux 54 GPIO_ACTIVE_HIGH>;
++ };
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-r7500.dts
+@@ -0,0 +1,373 @@
++#include "qcom-ipq8064-v1.0.dtsi"
++
++#include <dt-bindings/input/input.h>
++
++/ {
++ model = "Netgear Nighthawk X4 R7500";
++ compatible = "netgear,r7500", "qcom,ipq8064";
++
++ memory@0 {
++ reg = <0x42000000 0xe000000>;
++ device_type = "memory";
++ };
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ rsvd@41200000 {
++ reg = <0x41200000 0x300000>;
++ no-map;
++ };
++ };
++
++ aliases {
++ serial0 = &uart4;
++ mdio-gpio0 = &mdio0;
++
++ led-boot = &power_white;
++ led-failsafe = &power_amber;
++ led-running = &power_white;
++ led-upgrade = &power_amber;
++ };
++
++ chosen {
++ bootargs = "rootfstype=squashfs noinitrd";
++ linux,stdout-path = "serial0:115200n8";
++ };
++
++ soc {
++ pinmux@800000 {
++ button_pins: button_pins {
++ mux {
++ pins = "gpio6", "gpio54", "gpio65";
++ function = "gpio";
++ drive-strength = <2>;
++ bias-pull-up;
++ };
++ };
++
++ i2c4_pins: i2c4_pinmux {
++ mux {
++ pins = "gpio12", "gpio13";
++ function = "gsbi4";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
++ led_pins: led_pins {
++ mux {
++ pins = "gpio7", "gpio8", "gpio9", "gpio22", "gpio23",
++ "gpio24","gpio26", "gpio53", "gpio64";
++ function = "gpio";
++ drive-strength = <2>;
++ bias-pull-up;
++ };
++ };
++
++ mdio0_pins: mdio0_pins {
++ mux {
++ pins = "gpio0", "gpio1";
++ function = "gpio";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
++
++ nand_pins: nand_pins {
++ mux {
++ pins = "gpio34", "gpio35", "gpio36",
++ "gpio37", "gpio38", "gpio39",
++ "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ function = "nand";
++ drive-strength = <10>;
++ bias-disable;
++ };
++ pullups {
++ pins = "gpio39";
++ bias-pull-up;
++ };
++ hold {
++ pins = "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ bias-bus-hold;
++ };
++ };
++
++ rgmii2_pins: rgmii2_pins {
++ mux {
++ pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32",
++ "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ;
++ function = "rgmii2";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
++ };
++
++ gsbi@16300000 {
++ qcom,mode = <GSBI_PROT_I2C_UART>;
++ status = "ok";
++ serial@16340000 {
++ status = "ok";
++ };
++ /*
++ * The i2c device on gsbi4 should not be enabled.
++ * On ipq806x designs gsbi4 i2c is meant for exclusive
++ * RPM usage. Turning this on in kernel manifests as
++ * i2c failure for the RPM.
++ */
++ };
++
++ sata-phy@1b400000 {
++ status = "ok";
++ };
++
++ sata@29000000 {
++ status = "ok";
++ };
++
++ phy@100f8800 { /* USB3 port 1 HS phy */
++ status = "ok";
++ };
++
++ phy@100f8830 { /* USB3 port 1 SS phy */
++ status = "ok";
++ };
++
++ phy@110f8800 { /* USB3 port 0 HS phy */
++ status = "ok";
++ };
++
++ phy@110f8830 { /* USB3 port 0 SS phy */
++ status = "ok";
++ };
++
++ usb30@0 {
++ status = "ok";
++ };
++
++ usb30@1 {
++ status = "ok";
++ };
++
++ pcie0: pci@1b500000 {
++ status = "ok";
++ };
++
++ pcie1: pci@1b700000 {
++ status = "ok";
++ };
++
++ nand@1ac00000 {
++ status = "ok";
++
++ pinctrl-0 = <&nand_pins>;
++ pinctrl-names = "default";
++
++ nand-ecc-strength = <4>;
++ nand-bus-width = <8>;
++
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ qcadata@0 {
++ label = "qcadata";
++ reg = <0x0000000 0x0c80000>;
++ read-only;
++ };
++
++ APPSBL@c80000 {
++ label = "APPSBL";
++ reg = <0x0c80000 0x0500000>;
++ read-only;
++ };
++
++ APPSBLENV@1180000 {
++ label = "APPSBLENV";
++ reg = <0x1180000 0x0080000>;
++ read-only;
++ };
++
++ art: art@1200000 {
++ label = "art";
++ reg = <0x1200000 0x0140000>;
++ read-only;
++ };
++
++ kernel@1340000 {
++ label = "kernel";
++ reg = <0x1340000 0x0200000>;
++ };
++
++ ubi@1540000 {
++ label = "ubi";
++ reg = <0x1540000 0x1800000>;
++ };
++
++ netgear@2d40000 {
++ label = "netgear";
++ reg = <0x2d40000 0x0c00000>;
++ read-only;
++ };
++
++ reserve@3940000 {
++ label = "reserve";
++ reg = <0x3940000 0x46c0000>;
++ read-only;
++ };
++
++ firmware@1340000 {
++ label = "firmware";
++ reg = <0x1340000 0x1a00000>;
++ };
++
++ };
++
++ mdio0: mdio {
++ compatible = "virtual,mdio-gpio";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>;
++ 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>;
++ };
++ };
++
++ gmac1: ethernet@37200000 {
++ status = "ok";
++ phy-mode = "rgmii";
++ qcom,id = <1>;
++
++ pinctrl-0 = <&rgmii2_pins>;
++ pinctrl-names = "default";
++
++ mtd-mac-address = <&art 6>;
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ gmac2: ethernet@37400000 {
++ status = "ok";
++ phy-mode = "sgmii";
++ qcom,id = <2>;
++
++ mtd-mac-address = <&art 0>;
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ rpm@108000 {
++ pinctrl-0 = <&i2c4_pins>;
++ pinctrl-names = "default";
++ };
++ };
++
++ gpio-keys {
++ compatible = "gpio-keys";
++ pinctrl-0 = <&button_pins>;
++ pinctrl-names = "default";
++
++ wifi {
++ label = "wifi";
++ gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_RFKILL>;
++ };
++
++ reset {
++ label = "reset";
++ gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_RESTART>;
++ };
++
++ wps {
++ label = "wps";
++ gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_WPS_BUTTON>;
++ };
++ };
++
++ gpio-leds {
++ compatible = "gpio-leds";
++ pinctrl-0 = <&led_pins>;
++ pinctrl-names = "default";
++
++ usb1 {
++ label = "r7500:white:usb1";
++ gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>;
++ };
++
++ usb2 {
++ label = "r7500:white:usb2";
++ gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>;
++ };
++
++ power_amber: power_amber {
++ label = "r7500:amber:power";
++ gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>;
++ };
++
++ wan_white {
++ label = "r7500:white:wan";
++ gpios = <&qcom_pinmux 22 GPIO_ACTIVE_HIGH>;
++ };
++
++ wan_amber {
++ label = "r7500:amber:wan";
++ gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>;
++ };
++
++ wps {
++ label = "r7500:white:wps";
++ gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>;
++ };
++
++ esata {
++ label = "r7500:white:esata";
++ gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>;
++ };
++
++ power_white: power_white {
++ label = "r7500:white:power";
++ gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>;
++ default-state = "keep";
++ };
++
++ wifi {
++ label = "r7500:white:wifi";
++ gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>;
++ };
++ };
++};
++
++&adm_dma {
++ status = "ok";
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-r7500v2.dts
+@@ -0,0 +1,416 @@
++#include "qcom-ipq8064-v1.0.dtsi"
++
++#include <dt-bindings/input/input.h>
++
++/ {
++ model = "Netgear Nighthawk X4 R7500v2";
++ compatible = "netgear,r7500v2", "qcom,ipq8064";
++
++ memory@0 {
++ reg = <0x42000000 0x1e000000>;
++ device_type = "memory";
++ };
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ rsvd@41200000 {
++ reg = <0x41200000 0x300000>;
++ no-map;
++ };
++
++ rsvd@5fe00000 {
++ reg = <0x5fe00000 0x200000>;
++ reusable;
++ };
++ };
++
++ aliases {
++ serial0 = &uart4;
++ mdio-gpio0 = &mdio0;
++
++ led-boot = &power;
++ led-failsafe = &power;
++ led-running = &power;
++ led-upgrade = &power;
++ };
++
++ chosen {
++ bootargs = "rootfstype=squashfs noinitrd";
++ linux,stdout-path = "serial0:115200n8";
++ };
++
++ soc {
++ pinmux@800000 {
++ button_pins: button_pins {
++ mux {
++ pins = "gpio6", "gpio54", "gpio65";
++ function = "gpio";
++ drive-strength = <2>;
++ bias-pull-up;
++ };
++ };
++
++ i2c4_pins: i2c4_pinmux {
++ mux {
++ pins = "gpio12", "gpio13";
++ function = "gsbi4";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
++ led_pins: led_pins {
++ mux {
++ pins = "gpio7", "gpio8", "gpio9", "gpio22", "gpio23",
++ "gpio24","gpio26", "gpio53", "gpio64";
++ function = "gpio";
++ drive-strength = <2>;
++ bias-pull-up;
++ };
++ };
++
++ mdio0_pins: mdio0_pins {
++ mux {
++ pins = "gpio0", "gpio1";
++ function = "gpio";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
++
++ nand_pins: nand_pins {
++ mux {
++ pins = "gpio34", "gpio35", "gpio36",
++ "gpio37", "gpio38", "gpio39",
++ "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ function = "nand";
++ drive-strength = <10>;
++ bias-disable;
++ };
++ pullups {
++ pins = "gpio39";
++ bias-pull-up;
++ };
++ hold {
++ pins = "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ bias-bus-hold;
++ };
++ };
++
++ 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;
++ };
++ };
++
++ usb0_pwr_en_pins: usb0_pwr_en_pins {
++ mux {
++ pins = "gpio15";
++ function = "gpio";
++ drive-strength = <12>;
++ bias-pull-down;
++ output-high;
++ };
++ };
++
++ usb1_pwr_en_pins: usb1_pwr_en_pins {
++ mux {
++ pins = "gpio16", "gpio68";
++ function = "gpio";
++ drive-strength = <12>;
++ bias-pull-down;
++ output-high;
++ };
++ };
++ };
++
++ gsbi@16300000 {
++ qcom,mode = <GSBI_PROT_I2C_UART>;
++ status = "ok";
++ serial@16340000 {
++ status = "ok";
++ };
++ /*
++ * The i2c device on gsbi4 should not be enabled.
++ * On ipq806x designs gsbi4 i2c is meant for exclusive
++ * RPM usage. Turning this on in kernel manifests as
++ * i2c failure for the RPM.
++ */
++ };
++
++ sata-phy@1b400000 {
++ status = "ok";
++ };
++
++ sata@29000000 {
++ status = "ok";
++ };
++
++ phy@100f8800 { /* USB3 port 1 HS phy */
++ status = "ok";
++ };
++
++ phy@100f8830 { /* USB3 port 1 SS phy */
++ status = "ok";
++ };
++
++ phy@110f8800 { /* USB3 port 0 HS phy */
++ status = "ok";
++ };
++
++ phy@110f8830 { /* USB3 port 0 SS phy */
++ status = "ok";
++ };
++
++ usb30@0 {
++ status = "ok";
++
++ pinctrl-0 = <&usb0_pwr_en_pins>;
++ pinctrl-names = "default";
++ };
++
++ usb30@1 {
++ status = "ok";
++
++ pinctrl-0 = <&usb1_pwr_en_pins>;
++ pinctrl-names = "default";
++ };
++
++ pcie0: pci@1b500000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 3 GPIO_ACTIVE_LOW>;
++ pinctrl-0 = <&pcie0_pins>;
++ pinctrl-names = "default";
++ };
++
++ pcie1: pci@1b700000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 48 GPIO_ACTIVE_LOW>;
++ pinctrl-0 = <&pcie1_pins>;
++ pinctrl-names = "default";
++ };
++
++ nand@1ac00000 {
++ status = "ok";
++
++ pinctrl-0 = <&nand_pins>;
++ pinctrl-names = "default";
++
++ nand-ecc-strength = <4>;
++ nand-bus-width = <8>;
++
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ qcadata@0 {
++ label = "qcadata";
++ reg = <0x0000000 0x0c80000>;
++ read-only;
++ };
++
++ APPSBL@c80000 {
++ label = "APPSBL";
++ reg = <0x0c80000 0x0500000>;
++ read-only;
++ };
++
++ APPSBLENV@1180000 {
++ label = "APPSBLENV";
++ reg = <0x1180000 0x0080000>;
++ read-only;
++ };
++
++ art: art@1200000 {
++ label = "art";
++ reg = <0x1200000 0x0140000>;
++ read-only;
++ };
++
++ artbak: art@1340000 {
++ label = "artbak";
++ reg = <0x1340000 0x0140000>;
++ read-only;
++ };
++
++ kernel@1480000 {
++ label = "kernel";
++ reg = <0x1480000 0x0200000>;
++ };
++
++ ubi@1680000 {
++ label = "ubi";
++ reg = <0x1680000 0x1E00000>;
++ };
++
++ netgear@3480000 {
++ label = "netgear";
++ reg = <0x3480000 0x4480000>;
++ read-only;
++ };
++
++ reserve@7900000 {
++ label = "reserve";
++ reg = <0x7900000 0x0700000>;
++ read-only;
++ };
++
++ firmware@1480000 {
++ label = "firmware";
++ reg = <0x1480000 0x2000000>;
++ };
++
++ };
++
++ mdio0: mdio {
++ compatible = "virtual,mdio-gpio";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>;
++ pinctrl-0 = <&mdio0_pins>;
++ pinctrl-names = "default";
++
++ phy0: ethernet-phy@0 {
++ device_type = "ethernet-phy";
++ reg = <0>;
++ qca,ar8327-initvals = <
++ 0x00004 0x7600000 /* PAD0_MODE */
++ 0x00008 0x1000000 /* PAD5_MODE */
++ 0x0000c 0x80 /* PAD6_MODE */
++ 0x000e4 0xaa545 /* MAC_POWER_SEL */
++ 0x000e0 0xc74164de /* SGMII_CTRL */
++ 0x0007c 0x4e /* PORT0_STATUS */
++ 0x00094 0x4e /* PORT6_STATUS */
++ >;
++ };
++
++ phy4: ethernet-phy@4 {
++ device_type = "ethernet-phy";
++ reg = <4>;
++ };
++ };
++
++ gmac1: ethernet@37200000 {
++ status = "ok";
++ phy-mode = "rgmii";
++ qcom,id = <1>;
++
++ pinctrl-0 = <&rgmii2_pins>;
++ pinctrl-names = "default";
++
++ mtd-mac-address = <&art 6>;
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ gmac2: ethernet@37400000 {
++ status = "ok";
++ phy-mode = "sgmii";
++ qcom,id = <2>;
++
++ mtd-mac-address = <&art 0>;
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ rpm@108000 {
++ pinctrl-0 = <&i2c4_pins>;
++ pinctrl-names = "default";
++ };
++ };
++
++ gpio-keys {
++ compatible = "gpio-keys";
++ pinctrl-0 = <&button_pins>;
++ pinctrl-names = "default";
++
++ wifi {
++ label = "wifi";
++ gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_RFKILL>;
++ };
++
++ reset {
++ label = "reset";
++ gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_RESTART>;
++ };
++
++ wps {
++ label = "wps";
++ gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_WPS_BUTTON>;
++ };
++ };
++
++ gpio-leds {
++ compatible = "gpio-leds";
++ pinctrl-0 = <&led_pins>;
++ pinctrl-names = "default";
++
++ usb1 {
++ label = "r7500v2:amber:usb1";
++ gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>;
++ };
++
++ usb3 {
++ label = "r7500v2:amber:usb3";
++ gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>;
++ };
++
++ status {
++ label = "r7500v2:amber:status";
++ gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>;
++ };
++
++ internet {
++ label = "r7500v2:white:internet";
++ gpios = <&qcom_pinmux 22 GPIO_ACTIVE_HIGH>;
++ };
++
++ wan {
++ label = "r7500v2:white:wan";
++ gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>;
++ };
++
++ wps {
++ label = "r7500v2:white:wps";
++ gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>;
++ };
++
++ esata {
++ label = "r7500v2:white:esata";
++ gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>;
++ };
++
++ power: power {
++ label = "r7500v2:white:power";
++ gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>;
++ default-state = "keep";
++ };
++
++ wifi {
++ label = "r7500v2:white:wifi";
++ gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>;
++ };
++ };
++};
++
++&adm_dma {
++ status = "ok";
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-vr2600v.dts
+@@ -0,0 +1,425 @@
++#include "qcom-ipq8064-v1.0.dtsi"
++
++#include <dt-bindings/input/input.h>
++
++/ {
++ model = "TP-Link Archer VR2600v";
++ compatible = "tplink,vr2600v", "qcom,ipq8064";
++
++ memory@0 {
++ reg = <0x42000000 0x1e000000>;
++ device_type = "memory";
++ };
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ rsvd@41200000 {
++ reg = <0x41200000 0x300000>;
++ no-map;
++ };
++ };
++
++ aliases {
++ serial0 = &uart4;
++ mdio-gpio0 = &mdio0;
++
++ led-boot = &power;
++ led-failsafe = &general;
++ led-running = &power;
++ led-upgrade = &general;
++ };
++
++ chosen {
++ linux,stdout-path = "serial0:115200n8";
++ };
++
++ soc {
++ pinmux@800000 {
++ led_pins: led_pins {
++ mux {
++ pins = "gpio7", "gpio8", "gpio9", "gpio16", "gpio17",
++ "gpio26", "gpio53", "gpio56", "gpio66";
++ function = "gpio";
++ drive-strength = <2>;
++ bias-pull-up;
++ };
++ };
++
++ i2c4_pins: i2c4_pinmux {
++ mux {
++ pins = "gpio12", "gpio13";
++ function = "gsbi4";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
++ button_pins: button_pins {
++ mux {
++ pins = "gpio54", "gpio64", "gpio65", "gpio67", "gpio68";
++ function = "gpio";
++ drive-strength = <2>;
++ bias-pull-up;
++ };
++ };
++
++ spi_pins: spi_pins {
++ mux {
++ pins = "gpio18", "gpio19", "gpio21";
++ function = "gsbi5";
++ bias-pull-down;
++ };
++
++ data {
++ pins = "gpio18", "gpio19";
++ drive-strength = <10>;
++ };
++
++ cs {
++ pins = "gpio20";
++ drive-strength = <10>;
++ bias-pull-up;
++ };
++
++ clk {
++ pins = "gpio21";
++ drive-strength = <12>;
++ };
++ };
++
++ mdio0_pins: mdio0_pins {
++ mux {
++ pins = "gpio0", "gpio1";
++ function = "gpio";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
++
++ rgmii2_pins: rgmii2_pins {
++ mux {
++ pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32",
++ "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ;
++ function = "rgmii2";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
++ };
++
++ gsbi@16300000 {
++ qcom,mode = <GSBI_PROT_I2C_UART>;
++ status = "ok";
++ serial@16340000 {
++ status = "ok";
++ };
++ /*
++ * The i2c device on gsbi4 should not be enabled.
++ * On ipq806x designs gsbi4 i2c is meant for exclusive
++ * RPM usage. Turning this on in kernel manifests as
++ * i2c failure for the RPM.
++ */
++ };
++
++ gsbi5: gsbi@1a200000 {
++ qcom,mode = <GSBI_PROT_SPI>;
++ status = "ok";
++
++ spi4: spi@1a280000 {
++ status = "ok";
++
++ pinctrl-0 = <&spi_pins>;
++ pinctrl-names = "default";
++
++ cs-gpios = <&qcom_pinmux 20 GPIO_ACTIVE_HIGH>;
++
++ flash: W25Q128@0 {
++ compatible = "jedec,spi-nor";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ spi-max-frequency = <50000000>;
++ reg = <0>;
++
++ SBL1@0 {
++ label = "SBL1";
++ reg = <0x0 0x20000>;
++ read-only;
++ };
++
++ MIBIB@20000 {
++ label = "MIBIB";
++ reg = <0x20000 0x20000>;
++ read-only;
++ };
++
++ SBL2@40000 {
++ label = "SBL2";
++ reg = <0x40000 0x40000>;
++ read-only;
++ };
++
++ SBL3@80000 {
++ label = "SBL3";
++ reg = <0x80000 0x80000>;
++ read-only;
++ };
++
++ DDRCONFIG@100000 {
++ label = "DDRCONFIG";
++ reg = <0x100000 0x10000>;
++ read-only;
++ };
++
++ SSD@110000 {
++ label = "SSD";
++ reg = <0x110000 0x10000>;
++ read-only;
++ };
++
++ TZ@120000 {
++ label = "TZ";
++ reg = <0x120000 0x80000>;
++ read-only;
++ };
++
++ RPM@1a0000 {
++ label = "RPM";
++ reg = <0x1a0000 0x80000>;
++ read-only;
++ };
++
++ APPSBL@220000 {
++ label = "APPSBL";
++ reg = <0x220000 0x80000>;
++ read-only;
++ };
++
++ APPSBLENV@2a0000 {
++ label = "APPSBLENV";
++ reg = <0x2a0000 0x40000>;
++ read-only;
++ };
++
++ OLDART@2e0000 {
++ label = "OLDART";
++ reg = <0x2e0000 0x40000>;
++ read-only;
++ };
++
++ kernel@320000 {
++ label = "kernel";
++ reg = <0x320000 0x200000>;
++ };
++
++ rootfs@520000 {
++ label = "rootfs";
++ reg = <0x520000 0xa60000>;
++ };
++
++ defaultmac: default-mac@0xfaf100 {
++ label = "default-mac";
++ reg = <0xfaf100 0x00200>;
++ read-only;
++ };
++
++ ART@fc0000 {
++ label = "ART";
++ reg = <0xfc0000 0x40000>;
++ read-only;
++ };
++ };
++ };
++ };
++
++ phy@100f8800 { /* USB3 port 1 HS phy */
++ status = "ok";
++ };
++
++ phy@100f8830 { /* USB3 port 1 SS phy */
++ status = "ok";
++ };
++
++ phy@110f8800 { /* USB3 port 0 HS phy */
++ status = "ok";
++ };
++
++ phy@110f8830 { /* USB3 port 0 SS phy */
++ status = "ok";
++ };
++
++ usb30@0 {
++ status = "ok";
++ };
++
++ usb30@1 {
++ status = "ok";
++ };
++
++ pcie0: pci@1b500000 {
++ status = "ok";
++ phy-tx0-term-offset = <7>;
++ };
++
++ pcie1: pci@1b700000 {
++ status = "ok";
++ phy-tx0-term-offset = <7>;
++ };
++
++ mdio0: mdio {
++ compatible = "virtual,mdio-gpio";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>;
++ 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>;
++ };
++ };
++
++ gmac1: ethernet@37200000 {
++ status = "ok";
++ phy-mode = "rgmii";
++ qcom,id = <1>;
++
++ pinctrl-0 = <&rgmii2_pins>;
++ pinctrl-names = "default";
++
++ mtd-mac-address = <&defaultmac 0>;
++ mtd-mac-address-increment = <1>;
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ gmac2: ethernet@37400000 {
++ status = "ok";
++ phy-mode = "sgmii";
++ qcom,id = <2>;
++
++ mtd-mac-address = <&defaultmac 0>;
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ rpm@108000 {
++ pinctrl-0 = <&i2c4_pins>;
++ pinctrl-names = "default";
++ };
++ };
++
++ gpio-keys {
++ compatible = "gpio-keys";
++ pinctrl-0 = <&button_pins>;
++ pinctrl-names = "default";
++
++ wifi {
++ label = "wifi";
++ gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_RFKILL>;
++ };
++
++ reset {
++ label = "reset";
++ gpios = <&qcom_pinmux 64 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_RESTART>;
++ };
++
++ wps {
++ label = "wps";
++ gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_WPS_BUTTON>;
++ };
++
++ dect {
++ label = "dect";
++ gpios = <&qcom_pinmux 67 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_PHONE>;
++ };
++
++ ledswitch {
++ label = "ledswitch";
++ gpios = <&qcom_pinmux 68 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_LIGHTS_TOGGLE>;
++ };
++ };
++
++ gpio-leds {
++ compatible = "gpio-leds";
++ pinctrl-0 = <&led_pins>;
++ pinctrl-names = "default";
++
++ dsl {
++ label = "vr2600v:white:dsl";
++ gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>;
++ };
++
++ usb {
++ label = "vr2600v:white:usb";
++ gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>;
++ };
++
++ lan {
++ label = "vr2600v:white:lan";
++ gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>;
++ };
++
++ wlan2g {
++ label = "vr2600v:white:wlan2g";
++ gpios = <&qcom_pinmux 16 GPIO_ACTIVE_HIGH>;
++ };
++
++ wlan5g {
++ label = "vr2600v:white:wlan5g";
++ gpios = <&qcom_pinmux 17 GPIO_ACTIVE_HIGH>;
++ };
++
++ power: power {
++ label = "vr2600v:white:power";
++ gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>;
++ default-state = "keep";
++ };
++
++ phone {
++ label = "vr2600v:white:phone";
++ gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>;
++ };
++
++ wan {
++ label = "vr2600v:white:wan";
++ gpios = <&qcom_pinmux 56 GPIO_ACTIVE_HIGH>;
++ };
++
++ general: general {
++ label = "vr2600v:white:general";
++ gpios = <&qcom_pinmux 66 GPIO_ACTIVE_HIGH>;
++ };
++ };
++};
++
++&adm_dma {
++ status = "ok";
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8065.dtsi
+@@ -0,0 +1,153 @@
++#include "qcom-ipq8064.dtsi"
++
++/ {
++ model = "Qualcomm IPQ8065";
++ compatible = "qcom,ipq8065", "qcom,ipq8064";
++
++ qcom,pvs {
++ qcom,pvs-format-a;
++ qcom,speed0-pvs0-bin-v0 =
++ < 1725000000 1262500 >,
++ < 1400000000 1175000 >,
++ < 1000000000 1100000 >,
++ < 800000000 1050000 >,
++ < 600000000 1000000 >,
++ < 384000000 975000 >;
++ qcom,speed0-pvs1-bin-v0 =
++ < 1725000000 1225000 >,
++ < 1400000000 1150000 >,
++ < 1000000000 1075000 >,
++ < 800000000 1025000 >,
++ < 600000000 975000 >,
++ < 384000000 950000 >;
++ qcom,speed0-pvs2-bin-v0 =
++ < 1725000000 1200000 >,
++ < 1400000000 1125000 >,
++ < 1000000000 1050000 >,
++ < 800000000 1000000 >,
++ < 600000000 950000 >,
++ < 384000000 925000 >;
++ qcom,speed0-pvs3-bin-v0 =
++ < 1725000000 1175000 >,
++ < 1400000000 1100000 >,
++ < 1000000000 1025000 >,
++ < 800000000 975000 >,
++ < 600000000 925000 >,
++ < 384000000 900000 >;
++ qcom,speed0-pvs4-bin-v0 =
++ < 1725000000 1150000 >,
++ < 1400000000 1075000 >,
++ < 1000000000 1000000 >,
++ < 800000000 950000 >,
++ < 600000000 900000 >,
++ < 384000000 875000 >;
++ qcom,speed0-pvs5-bin-v0 =
++ < 1725000000 1100000 >,
++ < 1400000000 1025000 >,
++ < 1000000000 950000 >,
++ < 800000000 900000 >,
++ < 600000000 850000 >,
++ < 384000000 825000 >;
++ qcom,speed0-pvs6-bin-v0 =
++ < 1725000000 1050000 >,
++ < 1400000000 975000 >,
++ < 1000000000 900000 >,
++ < 800000000 850000 >,
++ < 600000000 800000 >,
++ < 384000000 775000 >;
++ };
++
++ soc: soc {
++
++ rpm@108000 {
++
++ regulators {
++
++ smb208_s2a: s2a {
++ regulator-min-microvolt = <775000>;
++ regulator-max-microvolt = <1275000>;
++ };
++
++ smb208_s2b: s2b {
++ regulator-min-microvolt = <775000>;
++ regulator-max-microvolt = <1275000>;
++ };
++ };
++ };
++
++ /* Temporary fixed regulator */
++ vsdcc_fixed: vsdcc-regulator {
++ compatible = "regulator-fixed";
++ regulator-name = "SDCC Power";
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++ regulator-always-on;
++ };
++
++ sdcc1bam:dma@12402000 {
++ compatible = "qcom,bam-v1.3.0";
++ reg = <0x12402000 0x8000>;
++ interrupts = <0 98 0>;
++ clocks = <&gcc SDC1_H_CLK>;
++ clock-names = "bam_clk";
++ #dma-cells = <1>;
++ qcom,ee = <0>;
++ };
++
++ sdcc3bam:dma@12182000 {
++ compatible = "qcom,bam-v1.3.0";
++ reg = <0x12182000 0x8000>;
++ interrupts = <0 96 0>;
++ clocks = <&gcc SDC3_H_CLK>;
++ clock-names = "bam_clk";
++ #dma-cells = <1>;
++ qcom,ee = <0>;
++ };
++
++ amba {
++ compatible = "arm,amba-bus";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ sdcc1: sdcc@12400000 {
++ status = "disabled";
++ compatible = "arm,pl18x", "arm,primecell";
++ arm,primecell-periphid = <0x00051180>;
++ reg = <0x12400000 0x2000>;
++ interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "cmd_irq";
++ clocks = <&gcc SDC1_CLK>, <&gcc SDC1_H_CLK>;
++ clock-names = "mclk", "apb_pclk";
++ bus-width = <8>;
++ max-frequency = <96000000>;
++ non-removable;
++ cap-sd-highspeed;
++ cap-mmc-highspeed;
++ vmmc-supply = <&vsdcc_fixed>;
++ dmas = <&sdcc1bam 2>, <&sdcc1bam 1>;
++ dma-names = "tx", "rx";
++ };
++
++ sdcc3: sdcc@12180000 {
++ compatible = "arm,pl18x", "arm,primecell";
++ arm,primecell-periphid = <0x00051180>;
++ status = "disabled";
++ reg = <0x12180000 0x2000>;
++ interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "cmd_irq";
++ clocks = <&gcc SDC3_CLK>, <&gcc SDC3_H_CLK>;
++ clock-names = "mclk", "apb_pclk";
++ bus-width = <8>;
++ cap-sd-highspeed;
++ cap-mmc-highspeed;
++ max-frequency = <192000000>;
++ #mmc-ddr-1_8v;
++ sd-uhs-sdr104;
++ sd-uhs-ddr50;
++ vqmmc-supply = <&vsdcc_fixed>;
++ dmas = <&sdcc3bam 2>, <&sdcc3bam 1>;
++ dma-names = "tx", "rx";
++ };
++ };
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts
+@@ -0,0 +1,388 @@
++#include "qcom-ipq8065-v1.0.dtsi"
++
++#include <dt-bindings/input/input.h>
++
++/ {
++ model = "ZyXEL NBG6817";
++ compatible = "zyxel,nbg6817", "qcom,ipq8065";
++
++ memory@0 {
++ reg = <0x42000000 0x1e000000>;
++ device_type = "memory";
++ };
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ rsvd@41200000 {
++ reg = <0x41200000 0x300000>;
++ no-map;
++ };
++ };
++
++ aliases {
++ serial0 = &uart4;
++ mdio-gpio0 = &mdio0;
++ sdcc1 = &sdcc1;
++
++ led-boot = &power;
++ led-failsafe = &power;
++ led-running = &power;
++ led-upgrade = &power;
++ };
++
++ chosen {
++ bootargs = "root=/dev/mmcblk0p5 rootfstype=squashfs,ext4 rootwait noinitrd";
++ linux,stdout-path = "serial0:115200n8";
++ };
++
++ soc {
++ pinmux@800000 {
++ button_pins: button_pins {
++ mux {
++ pins = "gpio6", "gpio54", "gpio65";
++ function = "gpio";
++ drive-strength = <2>;
++ bias-pull-up;
++ };
++ };
++
++ i2c4_pins: i2c4_pinmux {
++ mux {
++ pins = "gpio12", "gpio13";
++ function = "gsbi4";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
++ led_pins: led_pins {
++ mux {
++ pins = "gpio9", "gpio26", "gpio33", "gpio64";
++ function = "gpio";
++ drive-strength = <2>;
++ bias-pull-down;
++ };
++ };
++
++ mdio0_pins: mdio0_pins {
++ mux {
++ pins = "gpio0", "gpio1";
++ function = "gpio";
++ drive-strength = <8>;
++ bias-disable;
++ };
++
++ clk {
++ pins = "gpio1";
++ input-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;
++ };
++
++ tx {
++ pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32" ;
++ input-disable;
++ };
++ };
++
++ spi_pins: spi_pins {
++ mux {
++ pins = "gpio18", "gpio19", "gpio21";
++ function = "gsbi5";
++ drive-strength = <10>;
++ bias-none;
++ };
++
++ cs {
++ pins = "gpio20";
++ drive-strength = <12>;
++ };
++ };
++
++ usb0_pwr_en_pins: usb0_pwr_en_pins {
++ mux {
++ pins = "gpio16", "gpio17";
++ function = "gpio";
++ drive-strength = <12>;
++ };
++
++ pwr {
++ pins = "gpio17";
++ bias-pull-down;
++ output-high;
++ };
++
++ ovc {
++ pins = "gpio16";
++ bias-pull-up;
++ };
++ };
++
++ usb1_pwr_en_pins: usb1_pwr_en_pins {
++ mux {
++ pins = "gpio14", "gpio15";
++ function = "gpio";
++ drive-strength = <12>;
++ };
++
++ pwr {
++ pins = "gpio14";
++ bias-pull-down;
++ output-high;
++ };
++
++ ovc {
++ pins = "gpio15";
++ bias-pull-up;
++ };
++ };
++ };
++
++ gsbi@16300000 {
++ qcom,mode = <GSBI_PROT_I2C_UART>;
++ status = "ok";
++ serial@16340000 {
++ status = "ok";
++ };
++ /*
++ * The i2c device on gsbi4 should not be enabled.
++ * On ipq806x designs gsbi4 i2c is meant for exclusive
++ * RPM usage. Turning this on in kernel manifests as
++ * i2c failure for the RPM.
++ */
++ };
++
++ gsbi5: gsbi@1a200000 {
++ qcom,mode = <GSBI_PROT_SPI>;
++ status = "ok";
++
++ spi4: spi@1a280000 {
++ status = "ok";
++
++ pinctrl-0 = <&spi_pins>;
++ pinctrl-names = "default";
++
++ cs-gpios = <&qcom_pinmux 20 GPIO_ACTIVE_HIGH>;
++
++ flash: m25p80@0 {
++ compatible = "jedec,spi-nor";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ spi-max-frequency = <51200000>;
++ reg = <0>;
++
++ linux,part-probe = "qcom-smem";
++ };
++ };
++ };
++
++ 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";
++
++ pinctrl-0 = <&usb0_pwr_en_pins>;
++ pinctrl-names = "default";
++ };
++
++ usb30@1 {
++ status = "ok";
++
++ pinctrl-0 = <&usb1_pwr_en_pins>;
++ pinctrl-names = "default";
++ };
++
++ pcie0: pci@1b500000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 3 GPIO_ACTIVE_LOW>;
++ pinctrl-0 = <&pcie0_pins>;
++ pinctrl-names = "default";
++ };
++
++ pcie1: pci@1b700000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 48 GPIO_ACTIVE_LOW>;
++ pinctrl-0 = <&pcie1_pins>;
++ pinctrl-names = "default";
++ };
++
++ mdio0: mdio {
++ compatible = "virtual,mdio-gpio";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>;
++ pinctrl-0 = <&mdio0_pins>;
++ pinctrl-names = "default";
++
++ phy0: ethernet-phy@0 {
++ device_type = "ethernet-phy";
++ reg = <0>;
++ qca,ar8327-initvals = <
++ 0x00004 0x7600000 /* PAD0_MODE */
++ 0x00008 0x1000000 /* PAD5_MODE */
++ 0x0000c 0x80 /* PAD6_MODE */
++ 0x000e4 0xaa545 /* MAC_POWER_SEL */
++ 0x000e0 0xc74164de /* SGMII_CTRL */
++ 0x0007c 0x4e /* PORT0_STATUS */
++ 0x00094 0x4e /* PORT6_STATUS */
++ 0x00970 0x1e864443 /* QM_PORT0_CTRL0 */
++ 0x00974 0x000001c6 /* QM_PORT0_CTRL1 */
++ 0x00978 0x19008643 /* QM_PORT1_CTRL0 */
++ 0x0097c 0x000001c6 /* QM_PORT1_CTRL1 */
++ 0x00980 0x19008643 /* QM_PORT2_CTRL0 */
++ 0x00984 0x000001c6 /* QM_PORT2_CTRL1 */
++ 0x00988 0x19008643 /* QM_PORT3_CTRL0 */
++ 0x0098c 0x000001c6 /* QM_PORT3_CTRL1 */
++ 0x00990 0x19008643 /* QM_PORT4_CTRL0 */
++ 0x00994 0x000001c6 /* QM_PORT4_CTRL1 */
++ 0x00998 0x1e864443 /* QM_PORT5_CTRL0 */
++ 0x0099c 0x000001c6 /* QM_PORT5_CTRL1 */
++ 0x009a0 0x1e864443 /* QM_PORT6_CTRL0 */
++ 0x009a4 0x000001c6 /* QM_PORT6_CTRL1 */
++ >;
++ };
++
++ phy4: ethernet-phy@4 {
++ device_type = "ethernet-phy";
++ reg = <4>;
++ qca,ar8327-initvals = <
++ 0x000e4 0x6a545 /* MAC_POWER_SEL */
++ 0x0000c 0x80 /* PAD6_MODE */
++ >;
++ };
++ };
++
++ gmac1: ethernet@37200000 {
++ status = "ok";
++ phy-mode = "rgmii";
++ qcom,id = <1>;
++ qcom,phy_mdio_addr = <4>;
++ qcom,poll_required = <0>;
++ qcom,rgmii_delay = <1>;
++ qcom,phy_mii_type = <0>;
++ qcom,emulation = <0>;
++ qcom,irq = <255>;
++ mdiobus = <&mdio0>;
++
++ pinctrl-0 = <&rgmii2_pins>;
++ pinctrl-names = "default";
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ gmac2: ethernet@37400000 {
++ status = "ok";
++ phy-mode = "sgmii";
++ qcom,id = <2>;
++ qcom,phy_mdio_addr = <0>; /* none */
++ qcom,poll_required = <0>; /* no polling */
++ qcom,rgmii_delay = <0>;
++ qcom,phy_mii_type = <1>;
++ qcom,emulation = <0>;
++ qcom,irq = <258>;
++ mdiobus = <&mdio0>;
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ rpm@108000 {
++ pinctrl-0 = <&i2c4_pins>;
++ pinctrl-names = "default";
++ };
++
++ amba {
++ sdcc1: sdcc@12400000 {
++ status = "okay";
++ };
++ };
++ };
++
++ gpio-keys {
++ compatible = "gpio-keys";
++ pinctrl-0 = <&button_pins>;
++ pinctrl-names = "default";
++
++ wifi {
++ label = "wifi";
++ gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_RFKILL>;
++ };
++
++ reset {
++ label = "reset";
++ gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_RESTART>;
++ };
++
++ wps {
++ label = "wps";
++ gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_WPS_BUTTON>;
++ };
++ };
++
++ gpio-leds {
++ compatible = "gpio-leds";
++ pinctrl-0 = <&led_pins>;
++ pinctrl-names = "default";
++
++ internet {
++ label = "nbg6817:white:internet";
++ gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>;
++ };
++
++ power: power {
++ label = "nbg6817:white:power";
++ gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>;
++ default-state = "keep";
++ };
++
++ wifi2g {
++ label = "nbg6817:amber:wifi2g";
++ gpios = <&qcom_pinmux 33 GPIO_ACTIVE_HIGH>;
++ };
++
++ /* wifi2g amber from the manual is missing */
++
++ wifi5g {
++ label = "nbg6817:amber:wifi5g";
++ gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>;
++ };
++
++ /* wifi5g amber from the manual is missing */
++ };
++};
++
++&adm_dma {
++ status = "ok";
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8065-r7800.dts
+@@ -0,0 +1,566 @@
++#include "qcom-ipq8065-v1.0.dtsi"
++
++#include <dt-bindings/input/input.h>
++
++/ {
++ model = "Netgear Nighthawk X4S R7800";
++ compatible = "netgear,r7800", "qcom,ipq8065", "qcom,ipq8064";
++
++ memory@0 {
++ reg = <0x42000000 0x1e000000>;
++ device_type = "memory";
++ };
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ rsvd@41200000 {
++ reg = <0x41200000 0x300000>;
++ no-map;
++ };
++
++ rsvd@5fe00000 {
++ reg = <0x5fe00000 0x200000>;
++ reusable;
++ };
++ };
++
++ aliases {
++ serial0 = &uart4;
++ mdio-gpio0 = &mdio0;
++
++ led-boot = &power_white;
++ led-failsafe = &power_amber;
++ led-running = &power_white;
++ led-upgrade = &power_amber;
++ };
++
++ chosen {
++ linux,stdout-path = "serial0:115200n8";
++ };
++
++ soc {
++ pinmux@800000 {
++ button_pins: button_pins {
++ mux {
++ pins = "gpio6", "gpio54", "gpio65";
++ function = "gpio";
++ drive-strength = <2>;
++ bias-pull-up;
++ };
++ };
++
++ i2c4_pins: i2c4_pinmux {
++ mux {
++ pins = "gpio12", "gpio13";
++ function = "gsbi4";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
++ led_pins: led_pins {
++ pins = "gpio7", "gpio8", "gpio9", "gpio22", "gpio23",
++ "gpio24","gpio26", "gpio53", "gpio64";
++ function = "gpio";
++ drive-strength = <2>;
++ bias-pull-down;
++ };
++
++ nand_pins: nand_pins {
++ mux {
++ pins = "gpio34", "gpio35", "gpio36",
++ "gpio37", "gpio38", "gpio39",
++ "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ function = "nand";
++ drive-strength = <10>;
++ bias-disable;
++ };
++ pullups {
++ pins = "gpio39";
++ bias-pull-up;
++ };
++ hold {
++ pins = "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ bias-bus-hold;
++ };
++ };
++
++ mdio0_pins: mdio0_pins {
++ mux {
++ pins = "gpio0", "gpio1";
++ function = "gpio";
++ drive-strength = <8>;
++ bias-disable;
++ };
++
++ clk {
++ pins = "gpio1";
++ input-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;
++ };
++
++ tx {
++ pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32" ;
++ input-disable;
++ };
++ };
++
++ spi_pins: spi_pins {
++ mux {
++ pins = "gpio18", "gpio19", "gpio21";
++ function = "gsbi5";
++ bias-pull-down;
++ };
++
++ data {
++ pins = "gpio18", "gpio19";
++ drive-strength = <10>;
++ };
++
++ cs {
++ pins = "gpio20";
++ drive-strength = <10>;
++ bias-pull-up;
++ };
++
++ clk {
++ pins = "gpio21";
++ drive-strength = <12>;
++ };
++ };
++
++ spi6_pins: spi6_pins {
++ mux {
++ pins = "gpio55", "gpio56", "gpio58";
++ function = "gsbi6";
++ bias-pull-down;
++ };
++
++ mosi {
++ pins = "gpio55";
++ drive-strength = <12>;
++ };
++
++ miso {
++ pins = "gpio56";
++ drive-strength = <14>;
++ };
++
++ cs {
++ pins = "gpio57";
++ drive-strength = <12>;
++ bias-pull-up;
++ };
++
++ clk {
++ pins = "gpio58";
++ drive-strength = <12>;
++ };
++
++ reset {
++ pins = "gpio33";
++ drive-strength = <10>;
++ bias-pull-down;
++ output-high;
++ };
++ };
++
++ usb0_pwr_en_pins: usb0_pwr_en_pins {
++ mux {
++ pins = "gpio15";
++ function = "gpio";
++ drive-strength = <12>;
++ bias-pull-down;
++ output-high;
++ };
++ };
++
++ usb1_pwr_en_pins: usb1_pwr_en_pins {
++ mux {
++ pins = "gpio16", "gpio68";
++ function = "gpio";
++ drive-strength = <12>;
++ bias-pull-down;
++ output-high;
++ };
++ };
++ };
++
++ gsbi@16300000 {
++ qcom,mode = <GSBI_PROT_I2C_UART>;
++ status = "ok";
++ serial@16340000 {
++ status = "ok";
++ };
++ /*
++ * The i2c device on gsbi4 should not be enabled.
++ * On ipq806x designs gsbi4 i2c is meant for exclusive
++ * RPM usage. Turning this on in kernel manifests as
++ * i2c failure for the RPM.
++ */
++ };
++
++ gsbi5: gsbi@1a200000 {
++ qcom,mode = <GSBI_PROT_SPI>;
++ status = "ok";
++
++ spi5: spi@1a280000 {
++ status = "ok";
++
++ pinctrl-0 = <&spi_pins>;
++ pinctrl-names = "default";
++
++ cs-gpios = <&qcom_pinmux 20 GPIO_ACTIVE_HIGH>;
++
++ flash: m25p80@0 {
++ compatible = "jedec,spi-nor";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ spi-max-frequency = <50000000>;
++ reg = <0>;
++
++ linux,part-probe = "qcom-smem";
++ };
++ };
++ };
++
++ gsbi6: gsbi@16500000 {
++ qcom,mode = <GSBI_PROT_SPI>;
++ status = "ok";
++ spi6: spi@16580000 {
++ status = "ok";
++
++ pinctrl-0 = <&spi6_pins>;
++ pinctrl-names = "default";
++
++ cs-gpios = <&qcom_pinmux 57 GPIO_ACTIVE_HIGH>;
++
++ spi-nor@0 {
++ compatible = "jedec,spi-nor";
++ reg = <0>;
++ spi-max-frequency = <6000000>;
++ };
++ };
++ };
++
++ sata-phy@1b400000 {
++ status = "ok";
++ };
++
++ sata@29000000 {
++ ports-implemented = <0x1>;
++ 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";
++
++ pinctrl-0 = <&usb0_pwr_en_pins>;
++ pinctrl-names = "default";
++ };
++
++ usb30@1 {
++ status = "ok";
++
++ pinctrl-0 = <&usb1_pwr_en_pins>;
++ pinctrl-names = "default";
++ };
++
++ pcie0: pci@1b500000 {
++ status = "ok";
++ phy-tx0-term-offset = <7>;
++ };
++
++ pcie1: pci@1b700000 {
++ status = "ok";
++ phy-tx0-term-offset = <7>;
++ };
++
++ nand@1ac00000 {
++ status = "ok";
++
++ pinctrl-0 = <&nand_pins>;
++ pinctrl-names = "default";
++
++ nand-ecc-strength = <4>;
++ nand-ecc-step-size = <512>;
++ nand-bus-width = <8>;
++
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ qcadata@0 {
++ label = "qcadata";
++ reg = <0x0000000 0x0c80000>;
++ read-only;
++ };
++
++ APPSBL@c80000 {
++ label = "APPSBL";
++ reg = <0x0c80000 0x0500000>;
++ read-only;
++ };
++
++ APPSBLENV@1180000 {
++ label = "APPSBLENV";
++ reg = <0x1180000 0x0080000>;
++ read-only;
++ };
++
++ art: art@1200000 {
++ label = "art";
++ reg = <0x1200000 0x0140000>;
++ read-only;
++ };
++
++ artbak: art@1340000 {
++ label = "artbak";
++ reg = <0x1340000 0x0140000>;
++ read-only;
++ };
++
++ kernel@1480000 {
++ label = "kernel";
++ reg = <0x1480000 0x0200000>;
++ };
++
++ ubi@1680000 {
++ label = "ubi";
++ reg = <0x1680000 0x1E00000>;
++ };
++
++ netgear@3480000 {
++ label = "netgear";
++ reg = <0x3480000 0x4480000>;
++ read-only;
++ };
++
++ reserve@7900000 {
++ label = "reserve";
++ reg = <0x7900000 0x0700000>;
++ read-only;
++ };
++
++ firmware@1480000 {
++ label = "firmware";
++ reg = <0x1480000 0x2000000>;
++ };
++ };
++
++ mdio0: mdio {
++ compatible = "virtual,mdio-gpio";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>;
++ pinctrl-0 = <&mdio0_pins>;
++ pinctrl-names = "default";
++
++
++ phy0: ethernet-phy@0 {
++ device_type = "ethernet-phy";
++ reg = <0>;
++ qca,ar8327-initvals = <
++ 0x00004 0x7600000 /* PAD0_MODE */
++ 0x00008 0x1000000 /* PAD5_MODE */
++ 0x0000c 0x80 /* PAD6_MODE */
++ 0x000e4 0xaa545 /* MAC_POWER_SEL */
++ 0x000e0 0xc74164de /* SGMII_CTRL */
++ 0x0007c 0x4e /* PORT0_STATUS */
++ 0x00094 0x4e /* PORT6_STATUS */
++ 0x00970 0x1e864443 /* QM_PORT0_CTRL0 */
++ 0x00974 0x000001c6 /* QM_PORT0_CTRL1 */
++ 0x00978 0x19008643 /* QM_PORT1_CTRL0 */
++ 0x0097c 0x000001c6 /* QM_PORT1_CTRL1 */
++ 0x00980 0x19008643 /* QM_PORT2_CTRL0 */
++ 0x00984 0x000001c6 /* QM_PORT2_CTRL1 */
++ 0x00988 0x19008643 /* QM_PORT3_CTRL0 */
++ 0x0098c 0x000001c6 /* QM_PORT3_CTRL1 */
++ 0x00990 0x19008643 /* QM_PORT4_CTRL0 */
++ 0x00994 0x000001c6 /* QM_PORT4_CTRL1 */
++ 0x00998 0x1e864443 /* QM_PORT5_CTRL0 */
++ 0x0099c 0x000001c6 /* QM_PORT5_CTRL1 */
++ 0x009a0 0x1e864443 /* QM_PORT6_CTRL0 */
++ 0x009a4 0x000001c6 /* QM_PORT6_CTRL1 */
++ >;
++ qca,ar8327-vlans = <
++ 0x1 0x5e /* VLAN1 Ports 1/2/3/4/6 */
++ 0x2 0x21 /* VLAN2 Ports 0/5 */
++ >;
++ };
++
++ phy4: ethernet-phy@4 {
++ device_type = "ethernet-phy";
++ reg = <4>;
++ qca,ar8327-initvals = <
++ 0x000e4 0x6a545 /* MAC_POWER_SEL */
++ 0x0000c 0x80 /* PAD6_MODE */
++ >;
++ };
++ };
++
++ gmac1: ethernet@37200000 {
++ status = "ok";
++ phy-mode = "rgmii";
++ qcom,id = <1>;
++ qcom,phy_mdio_addr = <4>;
++ qcom,poll_required = <0>;
++ qcom,rgmii_delay = <1>;
++ qcom,phy_mii_type = <0>;
++ qcom,emulation = <0>;
++ qcom,irq = <255>;
++ mdiobus = <&mdio0>;
++
++ pinctrl-0 = <&rgmii2_pins>;
++ pinctrl-names = "default";
++
++ mtd-mac-address = <&art 6>;
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ gmac2: ethernet@37400000 {
++ status = "ok";
++ phy-mode = "sgmii";
++ qcom,id = <2>;
++ qcom,phy_mdio_addr = <0>; /* none */
++ qcom,poll_required = <0>; /* no polling */
++ qcom,rgmii_delay = <0>;
++ qcom,phy_mii_type = <1>;
++ qcom,emulation = <0>;
++ qcom,irq = <258>;
++ mdiobus = <&mdio0>;
++
++ mtd-mac-address = <&art 0>;
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ rpm@108000 {
++ pinctrl-0 = <&i2c4_pins>;
++ pinctrl-names = "default";
++ };
++ };
++
++ gpio-keys {
++ compatible = "gpio-keys";
++ pinctrl-0 = <&button_pins>;
++ pinctrl-names = "default";
++
++ wifi {
++ label = "wifi";
++ gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_RFKILL>;
++ debounce-interval = <60>;
++ wakeup-source;
++ };
++
++ reset {
++ label = "reset";
++ gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_RESTART>;
++ debounce-interval = <60>;
++ wakeup-source;
++ };
++
++ wps {
++ label = "wps";
++ gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>;
++ linux,code = <KEY_WPS_BUTTON>;
++ debounce-interval = <60>;
++ wakeup-source;
++ };
++ };
++
++ gpio-leds {
++ compatible = "gpio-leds";
++ pinctrl-0 = <&led_pins>;
++ pinctrl-names = "default";
++
++ power_white: power_white {
++ label = "r7800:white:power";
++ gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>;
++ default-state = "keep";
++ };
++
++ power_amber: power_amber {
++ label = "r7800:amber:power";
++ gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>;
++ };
++
++ wan_white {
++ label = "r7800:white:wan";
++ gpios = <&qcom_pinmux 22 GPIO_ACTIVE_HIGH>;
++ };
++
++ wan_amber {
++ label = "r7800:amber:wan";
++ gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>;
++ };
++
++ usb1 {
++ label = "r7800:white:usb1";
++ gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>;
++ };
++
++ usb2 {
++ label = "r7800:white:usb2";
++ gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>;
++ };
++
++ esata {
++ label = "r7800:white:esata";
++ gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>;
++ };
++
++ wifi {
++ label = "r7800:white:wifi";
++ gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>;
++ };
++
++ wps {
++ label = "r7800:white:wps";
++ gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>;
++ };
++ };
++};
++
++&adm_dma {
++ status = "ok";
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8065-v1.0.dtsi
+@@ -0,0 +1 @@
++#include "qcom-ipq8065.dtsi"