diff options
author | John Crispin <john@phrozen.org> | 2017-02-16 12:25:25 +0100 |
---|---|---|
committer | John Crispin <john@phrozen.org> | 2017-02-16 20:25:32 +0100 |
commit | bb255f74290d889b65a563bac7a4be0427fdbec8 (patch) | |
tree | 94eba9e702b4ed6203ee4aecb1bcd5778dde01b1 /target/linux/ipq806x/patches-4.9 | |
parent | 04c4b6f7fd1c48c09333ba0051ad24a6905491a0 (diff) | |
download | upstream-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')
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" |