ipq806x: clean up patches, port missing patches from 4.4
authorJohn Crispin <john@phrozen.org>
Mon, 13 Mar 2017 12:30:10 +0000 (13:30 +0100)
committerFelix Fietkau <nbd@nbd.name>
Mon, 13 Mar 2017 12:40:55 +0000 (13:40 +0100)
Signed-off-by: John Crispin <john@phrozen.org>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
102 files changed:
target/linux/ipq806x/patches-4.9/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch
target/linux/ipq806x/patches-4.9/0002-dmaengine-Add-ADM-driver.patch
target/linux/ipq806x/patches-4.9/0003-spi-qup-Make-sure-mode-is-only-determined-once.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0004-spi-qup-Fix-transaction-done-signaling.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0005-spi-qup-Fix-DMA-mode-to-work-correctly.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0006-spi-qup-Fix-block-mode-to-work-correctly.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0006-spi-qup-Make-sure-mode-is-only-determined-once.patch [deleted file]
target/linux/ipq806x/patches-4.9/0007-spi-qup-Fix-transaction-done-signaling.patch [deleted file]
target/linux/ipq806x/patches-4.9/0007-spi-qup-properly-detect-extra-interrupts.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0008-spi-qup-Fix-DMA-mode-to-work-correctly.patch [deleted file]
target/linux/ipq806x/patches-4.9/0008-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0009-spi-qup-Fix-block-mode-to-work-correctly.patch [deleted file]
target/linux/ipq806x/patches-4.9/0009-spi-qup-refactor-spi_qup_io_config-in-two-functions.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0010-spi-qup-call-io_config-in-mode-specific-function.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0010-spi-qup-properly-detect-extra-interrupts.patch [deleted file]
target/linux/ipq806x/patches-4.9/0011-spi-qup-allow-block-mode-to-generate-multiple-transa.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0011-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch [deleted file]
target/linux/ipq806x/patches-4.9/0012-spi-qup-refactor-spi_qup_io_config-in-two-functions.patch [deleted file]
target/linux/ipq806x/patches-4.9/0012-spi-qup-refactor-spi_qup_prep_sg-to-be-more-take-spe.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0013-spi-qup-allow-mulitple-DMA-transactions-per-spi-xfer.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0013-spi-qup-call-io_config-in-mode-specific-function.patch [deleted file]
target/linux/ipq806x/patches-4.9/0014-spi-qup-Fix-sg-nents-calculation.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0014-spi-qup-allow-block-mode-to-generate-multiple-transa.patch [deleted file]
target/linux/ipq806x/patches-4.9/0015-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0015-spi-qup-refactor-spi_qup_prep_sg-to-be-more-take-spe.patch [deleted file]
target/linux/ipq806x/patches-4.9/0016-clk-ipq4019-report-accurate-fixed-clock-rates.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0016-spi-qup-allow-mulitple-DMA-transactions-per-spi-xfer.patch [deleted file]
target/linux/ipq806x/patches-4.9/0017-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0017-spi-qup-Fix-sg-nents-calculation.patch [deleted file]
target/linux/ipq806x/patches-4.9/0018-qcom-ipq4019-turn-on-DMA-for-i2c.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0019-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0020-qcom-ipq4019-enable-DMA-for-spi.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0021-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0022-dts-ipq4019-support-ARMv7-PMU.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0023-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0024-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0025-qcom-ipq4019-enable-USB-bus-for-DK01.1-board.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0026-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch [deleted file]
target/linux/ipq806x/patches-4.9/0026-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0027-clk-ipq4019-report-accurate-fixed-clock-rates.patch [deleted file]
target/linux/ipq806x/patches-4.9/0027-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0028-clk-qcom-Add-support-for-RPM-Clocks.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0028-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch [deleted file]
target/linux/ipq806x/patches-4.9/0029-clk-qcom-clk-rpm-Fix-clk_hw-references.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0029-qcom-ipq4019-turn-on-DMA-for-i2c.patch [deleted file]
target/linux/ipq806x/patches-4.9/0030-clk-Disable-i2c-device-on-gsbi4.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0030-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch [deleted file]
target/linux/ipq806x/patches-4.9/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0031-qcom-ipq4019-enable-DMA-for-spi.patch [deleted file]
target/linux/ipq806x/patches-4.9/0032-phy-add-qcom-dwc3-phy.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0032-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch [deleted file]
target/linux/ipq806x/patches-4.9/0033-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0033-dts-ipq4019-support-ARMv7-PMU.patch [deleted file]
target/linux/ipq806x/patches-4.9/0034-ARM-Add-Krait-L2-register-accessor-functions.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0034-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch [deleted file]
target/linux/ipq806x/patches-4.9/0035-clk-mux-Split-out-register-accessors-for-reuse.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0035-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch [deleted file]
target/linux/ipq806x/patches-4.9/0036-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0036-qcom-ipq4019-enable-USB-bus-for-DK01.1-board.patch [deleted file]
target/linux/ipq806x/patches-4.9/0037-clk-Add-safe-switch-hook.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0037-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch [deleted file]
target/linux/ipq806x/patches-4.9/0038-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0039-clk-qcom-Add-HFPLL-driver.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0040-clk-qcom-Add-IPQ806X-s-HFPLLs.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0041-clk-qcom-Add-support-for-Krait-clocks.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0042-clk-qcom-Add-KPSS-ACC-GCC-driver.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0043-clk-qcom-Add-Krait-clock-controller-driver.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0044-clk-qcom-krait-Remove-CLK_IS_ROOT.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0045-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0046-cpufreq-qcom-Remove-platform-data.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0047-mtd-nand-Create-a-BBT-flag-to-access-bad-block-marke.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0048-PM-OPP-HACK-Allow-to-set-regulator-without-opp_list.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0049-PM-OPP-Support-adjusting-OPP-voltages-at-runtime.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0050-OPP-Allow-notifiers-to-call-dev_pm_opp_get_-voltage-.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0051-PM-OPP-Add-a-helper-to-get-an-opp-regulator-for-devi.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0052-PM-OPP-Update-the-voltage-tolerance-when-adjusting-t.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0053-regulator-add-smb208-support.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0054-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0055-cpufreq-dt-Add-L2-frequency-scaling-support.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0056-cpufreq-dt-Add-missing-rcu-locks.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0057-clk-qcom-Add-regmap-mux-div-clocks-support.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0058-clk-qcom-Always-add-factor-clock-for-xo-clocks.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0059-ARM-cpuidle-Add-cpuidle-support-for-QCOM-cpus.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0060-HACK-arch-arm-force-ZRELADDR-on-arch-qcom.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0062-ipq806x-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0063-ipq806x-clk-gcc-add-tsens-child-node.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0064-clk-clk-rpm-fixes.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0065-arm-override-compiler-flags.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0066-GPIO-add-named-gpio-exports.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0067-generic-Mangle-bootloader-s-kernel-arguments.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/0068-spi-add-gpio-cs-support.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch [deleted file]
target/linux/ipq806x/patches-4.9/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch [deleted file]
target/linux/ipq806x/patches-4.9/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch [deleted file]
target/linux/ipq806x/patches-4.9/167-ARM-qcom_rpm_fix_support_for_smb208.patch [deleted file]
target/linux/ipq806x/patches-4.9/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch [deleted file]
target/linux/ipq806x/patches-4.9/307-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch [deleted file]
target/linux/ipq806x/patches-4.9/311-add-rpmcc-for-ipq806x.patch [deleted file]
target/linux/ipq806x/patches-4.9/801-override-compiler-flags.patch [deleted file]
target/linux/ipq806x/patches-4.9/802-GPIO-add-named-gpio-exports.patch [deleted file]
target/linux/ipq806x/patches-4.9/996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch [deleted file]

index fb629aea5591921f0eec397893626cb1ad4dd3fd..2a36a85b825be0eea67600e94ddcd172beb862b5 100644 (file)
@@ -1,7 +1,7 @@
-From f275ae09b82aa423d2ea5a2be3ea315c8fcf6143 Mon Sep 17 00:00:00 2001
+From 28d0ed88f536dd639adf1b0c7c08e04be3c8f294 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
+Subject: [PATCH 01/69] dtbindings: qcom_adm: Fix channel specifiers
 
 Original patch from Andy Gross.
 
@@ -17,7 +17,7 @@ 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 ++++++----------
+ Documentation/devicetree/bindings/dma/qcom_adm.txt | 16 ++++++----------
  1 file changed, 6 insertions(+), 10 deletions(-)
 
 --- a/Documentation/devicetree/bindings/dma/qcom_adm.txt
index 4b56fed0bbb67f25f03ac8fde185edde7461ae09..212a90290526a3179c9d3dac6afd58cbe68b18f3 100644 (file)
@@ -1,7 +1,7 @@
-From 1d32bf93c8e83db0aca04d2961badef7e86d663b Mon Sep 17 00:00:00 2001
+From 563fa24db4e529c5a3311928d73a8a90531ee527 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
+Subject: [PATCH 02/69] dmaengine: Add ADM driver
 
 Original patch by Andy Gross.
 
@@ -18,9 +18,9 @@ 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 +++++++++++++++++++++++++++++++++++++++++++
+ 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
 
diff --git a/target/linux/ipq806x/patches-4.9/0003-spi-qup-Make-sure-mode-is-only-determined-once.patch b/target/linux/ipq806x/patches-4.9/0003-spi-qup-Make-sure-mode-is-only-determined-once.patch
new file mode 100644 (file)
index 0000000..fd9df44
--- /dev/null
@@ -0,0 +1,208 @@
+From 57c4d2626bcb990a2e677b4f769a88c3d8e0911d 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 03/69] 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/0004-spi-qup-Fix-transaction-done-signaling.patch b/target/linux/ipq806x/patches-4.9/0004-spi-qup-Fix-transaction-done-signaling.patch
new file mode 100644 (file)
index 0000000..5881ffa
--- /dev/null
@@ -0,0 +1,29 @@
+From fbdf80d138f8c7fda8e598287109fb90446d557d 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 04/69] 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/0005-spi-qup-Fix-DMA-mode-to-work-correctly.patch b/target/linux/ipq806x/patches-4.9/0005-spi-qup-Fix-DMA-mode-to-work-correctly.patch
new file mode 100644 (file)
index 0000000..20ab5c1
--- /dev/null
@@ -0,0 +1,219 @@
+From 1204ea49f3f7ded898d1ee202776093715a9ecf6 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 05/69] 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/0006-spi-qup-Fix-block-mode-to-work-correctly.patch b/target/linux/ipq806x/patches-4.9/0006-spi-qup-Fix-block-mode-to-work-correctly.patch
new file mode 100644 (file)
index 0000000..e7d0d65
--- /dev/null
@@ -0,0 +1,310 @@
+From b56c1e35cc550fd014fa601ca56b964d88fd44a9 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 06/69] 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/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
deleted file mode 100644 (file)
index 9d99699..0000000
+++ /dev/null
@@ -1,208 +0,0 @@
-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
deleted file mode 100644 (file)
index b42f8cd..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-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/0007-spi-qup-properly-detect-extra-interrupts.patch b/target/linux/ipq806x/patches-4.9/0007-spi-qup-properly-detect-extra-interrupts.patch
new file mode 100644 (file)
index 0000000..8a3b027
--- /dev/null
@@ -0,0 +1,61 @@
+From 0f32f976ebaa7d8643fcd9419f12bc801ba14407 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Thu, 10 Mar 2016 16:44:55 -0600
+Subject: [PATCH 07/69] 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/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
deleted file mode 100644 (file)
index d950446..0000000
+++ /dev/null
@@ -1,219 +0,0 @@
-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/0008-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch b/target/linux/ipq806x/patches-4.9/0008-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch
new file mode 100644 (file)
index 0000000..9893596
--- /dev/null
@@ -0,0 +1,26 @@
+From 9864f39695aefe0831b3c6e86c0dff30489ad580 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Thu, 10 Mar 2016 16:48:27 -0600
+Subject: [PATCH 08/69] 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/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
deleted file mode 100644 (file)
index 2f316b1..0000000
+++ /dev/null
@@ -1,310 +0,0 @@
-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/0009-spi-qup-refactor-spi_qup_io_config-in-two-functions.patch b/target/linux/ipq806x/patches-4.9/0009-spi-qup-refactor-spi_qup_io_config-in-two-functions.patch
new file mode 100644 (file)
index 0000000..36c225b
--- /dev/null
@@ -0,0 +1,202 @@
+From e06f04d55752e460d8f332f28317aebc27ab1b17 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Tue, 26 Apr 2016 12:57:46 -0500
+Subject: [PATCH 09/69] 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/0010-spi-qup-call-io_config-in-mode-specific-function.patch b/target/linux/ipq806x/patches-4.9/0010-spi-qup-call-io_config-in-mode-specific-function.patch
new file mode 100644 (file)
index 0000000..dfbed62
--- /dev/null
@@ -0,0 +1,391 @@
+From afe108e638a2dd441b11cd2c7b1e0658bb47b5e8 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Tue, 26 Apr 2016 13:14:45 -0500
+Subject: [PATCH 10/69] 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/0010-spi-qup-properly-detect-extra-interrupts.patch b/target/linux/ipq806x/patches-4.9/0010-spi-qup-properly-detect-extra-interrupts.patch
deleted file mode 100644 (file)
index 74ef25a..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-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-allow-block-mode-to-generate-multiple-transa.patch b/target/linux/ipq806x/patches-4.9/0011-spi-qup-allow-block-mode-to-generate-multiple-transa.patch
new file mode 100644 (file)
index 0000000..39a1aec
--- /dev/null
@@ -0,0 +1,268 @@
+From 6858a6a75f1ed364764afba938d77bbb57f80559 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Tue, 26 Apr 2016 15:46:24 -0500
+Subject: [PATCH 11/69] 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/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
deleted file mode 100644 (file)
index da1569f..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-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
deleted file mode 100644 (file)
index 7beb5d9..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-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/0012-spi-qup-refactor-spi_qup_prep_sg-to-be-more-take-spe.patch b/target/linux/ipq806x/patches-4.9/0012-spi-qup-refactor-spi_qup_prep_sg-to-be-more-take-spe.patch
new file mode 100644 (file)
index 0000000..990cccd
--- /dev/null
@@ -0,0 +1,73 @@
+From fca27bd516d30e33b9373a8c61ca4431077e479e Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Wed, 4 May 2016 16:33:42 -0500
+Subject: [PATCH 12/69] 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/0013-spi-qup-allow-mulitple-DMA-transactions-per-spi-xfer.patch b/target/linux/ipq806x/patches-4.9/0013-spi-qup-allow-mulitple-DMA-transactions-per-spi-xfer.patch
new file mode 100644 (file)
index 0000000..13e199c
--- /dev/null
@@ -0,0 +1,166 @@
+From 028f915b20ec343dda88f1bcc99f07f6b428b4aa Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Thu, 5 May 2016 10:07:11 -0500
+Subject: [PATCH 13/69] 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/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
deleted file mode 100644 (file)
index 3aebaac..0000000
+++ /dev/null
@@ -1,391 +0,0 @@
-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-Fix-sg-nents-calculation.patch b/target/linux/ipq806x/patches-4.9/0014-spi-qup-Fix-sg-nents-calculation.patch
new file mode 100644 (file)
index 0000000..2d321f1
--- /dev/null
@@ -0,0 +1,86 @@
+From f5913e137c3dac4972ac0ddd5f248924d02d3dcb Mon Sep 17 00:00:00 2001
+From: Varadarajan Narayanan <varada@codeaurora.org>
+Date: Wed, 25 May 2016 13:40:03 +0530
+Subject: [PATCH 14/69] 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/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
deleted file mode 100644 (file)
index 6c275f6..0000000
+++ /dev/null
@@ -1,268 +0,0 @@
-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-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch b/target/linux/ipq806x/patches-4.9/0015-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch
new file mode 100644 (file)
index 0000000..06b61a0
--- /dev/null
@@ -0,0 +1,27 @@
+From 5543e93f51d5e23f9b3a7fe11a722c91fc410485 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Wed, 13 Apr 2016 14:03:14 -0500
+Subject: [PATCH 15/69] 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/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
deleted file mode 100644 (file)
index 90ffe86..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-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-clk-ipq4019-report-accurate-fixed-clock-rates.patch b/target/linux/ipq806x/patches-4.9/0016-clk-ipq4019-report-accurate-fixed-clock-rates.patch
new file mode 100644 (file)
index 0000000..6f76653
--- /dev/null
@@ -0,0 +1,33 @@
+From 5e2df5f44e35d79fff2ab8bbb8a726ad5de78a3e 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 16/69] 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/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
deleted file mode 100644 (file)
index de324ff..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-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-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch b/target/linux/ipq806x/patches-4.9/0017-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch
new file mode 100644 (file)
index 0000000..7cbd6a4
--- /dev/null
@@ -0,0 +1,77 @@
+From 18c3b42575a154343831aec0637aab00e19440e1 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Thu, 17 Mar 2016 15:01:09 -0500
+Subject: [PATCH 17/69] 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/0017-spi-qup-Fix-sg-nents-calculation.patch b/target/linux/ipq806x/patches-4.9/0017-spi-qup-Fix-sg-nents-calculation.patch
deleted file mode 100644 (file)
index d6d92b8..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-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/0018-qcom-ipq4019-turn-on-DMA-for-i2c.patch b/target/linux/ipq806x/patches-4.9/0018-qcom-ipq4019-turn-on-DMA-for-i2c.patch
new file mode 100644 (file)
index 0000000..42bb4a6
--- /dev/null
@@ -0,0 +1,23 @@
+From 71f82049dca86bc89b9da07e051e4ed492820233 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Mon, 28 Mar 2016 11:16:51 -0500
+Subject: [PATCH 18/69] 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/0019-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch b/target/linux/ipq806x/patches-4.9/0019-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch
new file mode 100644 (file)
index 0000000..54ee571
--- /dev/null
@@ -0,0 +1,28 @@
+From 7292bf171cdf2fb48607058f12ddd0d812a87428 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 19/69] 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/0020-qcom-ipq4019-enable-DMA-for-spi.patch b/target/linux/ipq806x/patches-4.9/0020-qcom-ipq4019-enable-DMA-for-spi.patch
new file mode 100644 (file)
index 0000000..c1fa5c7
--- /dev/null
@@ -0,0 +1,23 @@
+From 4593e768393b9589f0a8987eaf57316c214865fe Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Mon, 11 Apr 2016 14:49:12 -0500
+Subject: [PATCH 20/69] 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/0021-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch b/target/linux/ipq806x/patches-4.9/0021-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch
new file mode 100644 (file)
index 0000000..887ebf3
--- /dev/null
@@ -0,0 +1,108 @@
+From 644ad7209637b02a0ca6d72f0715a9f52532fc70 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Fri, 8 Apr 2016 15:26:10 -0500
+Subject: [PATCH 21/69] 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/0022-dts-ipq4019-support-ARMv7-PMU.patch b/target/linux/ipq806x/patches-4.9/0022-dts-ipq4019-support-ARMv7-PMU.patch
new file mode 100644 (file)
index 0000000..e5dbba7
--- /dev/null
@@ -0,0 +1,28 @@
+From 47f00399b195e0987c67006b329587bef0692bf4 Mon Sep 17 00:00:00 2001
+From: Thomas Pedersen <twp@codeaurora.org>
+Date: Wed, 4 May 2016 12:25:41 -0700
+Subject: [PATCH 22/69] 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/0023-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches-4.9/0023-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch
new file mode 100644 (file)
index 0000000..e8f4bfc
--- /dev/null
@@ -0,0 +1,488 @@
+From 04d3f9be0ce80fac99d4ca1f46faf3605258ca1f 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 23/69] 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/0024-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch b/target/linux/ipq806x/patches-4.9/0024-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch
new file mode 100644 (file)
index 0000000..cea6bd0
--- /dev/null
@@ -0,0 +1,87 @@
+From 0fba6eceb6e16fa8fd5834d65fcb771fa263a44b Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Thu, 17 Mar 2016 16:22:28 -0500
+Subject: [PATCH 24/69] 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/0025-qcom-ipq4019-enable-USB-bus-for-DK01.1-board.patch b/target/linux/ipq806x/patches-4.9/0025-qcom-ipq4019-enable-USB-bus-for-DK01.1-board.patch
new file mode 100644 (file)
index 0000000..975f42f
--- /dev/null
@@ -0,0 +1,45 @@
+From ae5e11c926f710595d0080e51bd10e704776669d Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Mon, 21 Mar 2016 16:12:05 -0500
+Subject: [PATCH 25/69] 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/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
deleted file mode 100644 (file)
index 3d6a827..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-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/0026-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch b/target/linux/ipq806x/patches-4.9/0026-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch
new file mode 100644 (file)
index 0000000..32c52b0
--- /dev/null
@@ -0,0 +1,258 @@
+From ec3e465ecf3f7dd26f2e22170e4c5f4b9979df5d Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint@codeaurora.org>
+Date: Mon, 21 Mar 2016 15:55:21 -0500
+Subject: [PATCH 26/69] 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/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
deleted file mode 100644 (file)
index 87b25d5..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-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/0027-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch b/target/linux/ipq806x/patches-4.9/0027-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch
new file mode 100644 (file)
index 0000000..f7ff1da
--- /dev/null
@@ -0,0 +1,731 @@
+From 41ee71bae788e1858c0a387d010c342e6bb3f4b0 Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <georgi.djakov@linaro.org>
+Date: Wed, 2 Nov 2016 17:56:56 +0200
+Subject: [PATCH 27/69] clk: qcom: Add support for SMD-RPM Clocks
+
+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
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
+@@ -0,0 +1,36 @@
++Qualcomm RPM Clock Controller Binding
++------------------------------------------------
++The RPM is a dedicated hardware engine for managing the shared
++SoC resources in order to keep the lowest power profile. It
++communicates with other hardware subsystems via shared memory
++and accepts clock requests, aggregates the requests and turns
++the clocks on/off or scales them on demand.
++
++Required properties :
++- compatible : shall contain only one of the following. The generic
++               compatible "qcom,rpmcc" should be also included.
++
++                      "qcom,rpmcc-msm8916", "qcom,rpmcc"
++
++- #clock-cells : shall contain 1
++
++Example:
++      smd {
++              compatible = "qcom,smd";
++
++              rpm {
++                      interrupts = <0 168 1>;
++                      qcom,ipc = <&apcs 8 0>;
++                      qcom,smd-edge = <15>;
++
++                      rpm_requests {
++                              compatible = "qcom,rpm-msm8916";
++                              qcom,smd-channels = "rpm_requests";
++
++                              rpmcc: clock-controller {
++                                      compatible = "qcom,rpmcc-msm8916", "qcom,rpmcc";
++                                      #clock-cells = <1>;
++                              };
++                      };
++              };
++      };
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -2,6 +2,9 @@ config QCOM_GDSC
+       bool
+       select PM_GENERIC_DOMAINS if PM
++config QCOM_RPMCC
++      bool
++
+ config COMMON_CLK_QCOM
+       tristate "Support for Qualcomm's clock controllers"
+       depends on OF
+@@ -9,6 +12,19 @@ config COMMON_CLK_QCOM
+       select REGMAP_MMIO
+       select RESET_CONTROLLER
++config QCOM_CLK_SMD_RPM
++      tristate "RPM over SMD based Clock Controller"
++      depends on COMMON_CLK_QCOM && QCOM_SMD_RPM
++      select QCOM_RPMCC
++      help
++        The RPM (Resource Power Manager) is a dedicated hardware engine for
++        managing the shared SoC resources in order to keep the lowest power
++        profile. It communicates with other hardware subsystems via shared
++        memory and accepts clock requests, aggregates the requests and turns
++        the clocks on/off or scales them on demand.
++        Say Y if you want to support the clocks exposed by the RPM on
++        platforms such as apq8016, apq8084, msm8974 etc.
++
+ config APQ_GCC_8084
+       tristate "APQ8084 Global Clock Controller"
+       select QCOM_GDSC
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -29,3 +29,4 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm896
+ 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/0028-clk-qcom-Add-support-for-RPM-Clocks.patch b/target/linux/ipq806x/patches-4.9/0028-clk-qcom-Add-support-for-RPM-Clocks.patch
new file mode 100644 (file)
index 0000000..72b392d
--- /dev/null
@@ -0,0 +1,587 @@
+From 21e7116c9d639f3283d4cec286fed1e703832b43 Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <georgi.djakov@linaro.org>
+Date: Wed, 2 Nov 2016 17:56:57 +0200
+Subject: [PATCH 28/69] 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
+@@ -29,4 +29,5 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm896
+ 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_RPM) += clk-rpm.o
+ obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-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/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
deleted file mode 100644 (file)
index c2b0b44..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-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-clk-qcom-clk-rpm-Fix-clk_hw-references.patch b/target/linux/ipq806x/patches-4.9/0029-clk-qcom-clk-rpm-Fix-clk_hw-references.patch
new file mode 100644 (file)
index 0000000..1b1b558
--- /dev/null
@@ -0,0 +1,94 @@
+From 7028c21deb2c6205bb896cc3719414de3d6d6a6e Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <georgi.djakov@linaro.org>
+Date: Wed, 23 Nov 2016 16:52:49 +0200
+Subject: [PATCH 29/69] 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/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
deleted file mode 100644 (file)
index 98dffc9..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-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-clk-Disable-i2c-device-on-gsbi4.patch b/target/linux/ipq806x/patches-4.9/0030-clk-Disable-i2c-device-on-gsbi4.patch
new file mode 100644 (file)
index 0000000..a4fe548
--- /dev/null
@@ -0,0 +1,40 @@
+From 0c974b87829e007dc4fae94e20d488204e20e662 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Thu, 9 Mar 2017 08:16:10 +0100
+Subject: [PATCH 30/69] clk: Disable i2c device on gsbi4
+
+This patch was not annotated and comes from the v4.4 tree.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/clk/qcom/gcc-ipq806x.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/drivers/clk/qcom/gcc-ipq806x.c
++++ b/drivers/clk/qcom/gcc-ipq806x.c
+@@ -294,7 +294,7 @@ static struct clk_rcg gsbi1_uart_src = {
+                       .parent_names = gcc_pxo_pll8,
+                       .num_parents = 2,
+                       .ops = &clk_rcg_ops,
+-                      .flags = CLK_SET_PARENT_GATE,
++                      .flags = CLK_SET_PARENT_GATE | CLK_IGNORE_UNUSED,
+               },
+       },
+ };
+@@ -312,7 +312,7 @@ static struct clk_branch gsbi1_uart_clk
+                       },
+                       .num_parents = 1,
+                       .ops = &clk_branch_ops,
+-                      .flags = CLK_SET_RATE_PARENT,
++                      .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+               },
+       },
+ };
+@@ -890,6 +890,7 @@ static struct clk_branch gsbi1_h_clk = {
+               .hw.init = &(struct clk_init_data){
+                       .name = "gsbi1_h_clk",
+                       .ops = &clk_branch_ops,
+++                     .flags = CLK_IGNORE_UNUSED,
+               },
+       },
+ };
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
deleted file mode 100644 (file)
index 7f79dba..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-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-mtd-add-SMEM-parser-for-QCOM-platforms.patch b/target/linux/ipq806x/patches-4.9/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch
new file mode 100644 (file)
index 0000000..d956a3b
--- /dev/null
@@ -0,0 +1,275 @@
+From d8eeb4de90e968ba32d956728c866f20752cf2c3 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Thu, 9 Mar 2017 08:18:08 +0100
+Subject: [PATCH 31/69] mtd: add SMEM parser for QCOM platforms
+
+On QCOM platforms using MTD devices storage (such as IPQ806x), SMEM is
+used to store partition layout. This new parser can now be used to read
+SMEM and use it to register an MTD layout according to its content.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+Signed-off-by: Ram Chandra Jangir <rjangi@codeaurora.org>
+---
+ drivers/mtd/Kconfig          |   7 ++
+ drivers/mtd/Makefile         |   1 +
+ drivers/mtd/qcom_smem_part.c | 228 +++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 236 insertions(+)
+ create mode 100644 drivers/mtd/qcom_smem_part.c
+
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -190,6 +190,13 @@ config MTD_MYLOADER_PARTS
+         You will still need the parsing functions to be called by the driver
+         for your particular device. It won't happen automatically.
++config MTD_QCOM_SMEM_PARTS
++      tristate "QCOM SMEM partitioning support"
++      depends on QCOM_SMEM
++      help
++        This provides partitions parser for QCOM devices using SMEM
++        such as IPQ806x.
++
+ comment "User Modules And Translation Layers"
+ #
+--- /dev/null
++++ b/drivers/mtd/qcom_smem_part.c
+@@ -0,0 +1,228 @@
++/*
++ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/kernel.h>
++#include <linux/device.h>
++#include <linux/slab.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/spi/spi.h>
++#include <linux/module.h>
++
++#include <linux/soc/qcom/smem.h>
++
++/* Processor/host identifier for the application processor */
++#define SMEM_HOST_APPS                        0
++
++/* SMEM items index */
++#define SMEM_AARM_PARTITION_TABLE     9
++#define SMEM_BOOT_FLASH_TYPE          421
++#define SMEM_BOOT_FLASH_BLOCK_SIZE    424
++
++/* SMEM Flash types */
++#define SMEM_FLASH_NAND                       2
++#define SMEM_FLASH_SPI                        6
++
++#define SMEM_PART_NAME_SZ             16
++#define SMEM_PARTS_MAX                        32
++
++struct smem_partition {
++      char name[SMEM_PART_NAME_SZ];
++      __le32 start;
++      __le32 size;
++      __le32 attr;
++};
++
++struct smem_partition_table {
++      u8 magic[8];
++      __le32 version;
++      __le32 len;
++      struct smem_partition parts[SMEM_PARTS_MAX];
++};
++
++/* SMEM Magic values in partition table */
++static const u8 SMEM_PTABLE_MAGIC[] = {
++      0xaa, 0x73, 0xee, 0x55,
++      0xdb, 0xbd, 0x5e, 0xe3,
++};
++
++static int qcom_smem_get_flash_blksz(u64 **smem_blksz)
++{
++      size_t size;
++
++      *smem_blksz = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_BLOCK_SIZE,
++                                  &size);
++
++      if (IS_ERR(*smem_blksz)) {
++              pr_err("Unable to read flash blksz from SMEM\n");
++              return -ENOENT;
++      }
++
++      if (size != sizeof(**smem_blksz)) {
++              pr_err("Invalid flash blksz size in SMEM\n");
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static int qcom_smem_get_flash_type(u64 **smem_flash_type)
++{
++      size_t size;
++
++      *smem_flash_type = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_TYPE,
++                                      &size);
++
++      if (IS_ERR(*smem_flash_type)) {
++              pr_err("Unable to read flash type from SMEM\n");
++              return -ENOENT;
++      }
++
++      if (size != sizeof(**smem_flash_type)) {
++              pr_err("Invalid flash type size in SMEM\n");
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static int qcom_smem_get_flash_partitions(struct smem_partition_table **pparts)
++{
++      size_t size;
++
++      *pparts = qcom_smem_get(SMEM_HOST_APPS, SMEM_AARM_PARTITION_TABLE,
++                              &size);
++
++      if (IS_ERR(*pparts)) {
++              pr_err("Unable to read partition table from SMEM\n");
++              return -ENOENT;
++      }
++
++      return 0;
++}
++
++static int of_dev_node_match(struct device *dev, void *data)
++{
++      return dev->of_node == data;
++}
++
++static bool is_spi_device(struct device_node *np)
++{
++      struct device *dev;
++
++      dev = bus_find_device(&spi_bus_type, NULL, np, of_dev_node_match);
++      if (!dev)
++              return false;
++
++      put_device(dev);
++      return true;
++}
++
++static int parse_qcom_smem_partitions(struct mtd_info *master,
++                                    const struct mtd_partition **pparts,
++                                    struct mtd_part_parser_data *data)
++{
++      struct smem_partition_table *smem_parts;
++      u64 *smem_flash_type, *smem_blksz;
++      struct mtd_partition *mtd_parts;
++      struct device_node *of_node = master->dev.of_node;
++      int i, ret;
++
++      /*
++       * SMEM will only store the partition table of the boot device.
++       * If this is not the boot device, do not return any partition.
++       */
++      ret = qcom_smem_get_flash_type(&smem_flash_type);
++      if (ret < 0)
++              return ret;
++
++      if ((*smem_flash_type == SMEM_FLASH_NAND && !mtd_type_is_nand(master))
++          || (*smem_flash_type == SMEM_FLASH_SPI && !is_spi_device(of_node)))
++              return 0;
++
++      /*
++       * Just for sanity purpose, make sure the block size in SMEM matches the
++       * block size of the MTD device
++       */
++      ret = qcom_smem_get_flash_blksz(&smem_blksz);
++      if (ret < 0)
++              return ret;
++
++      if (*smem_blksz != master->erasesize) {
++              pr_err("SMEM block size differs from MTD block size\n");
++              return -EINVAL;
++      }
++
++      /* Get partition pointer from SMEM */
++      ret = qcom_smem_get_flash_partitions(&smem_parts);
++      if (ret < 0)
++              return ret;
++
++      if (memcmp(SMEM_PTABLE_MAGIC, smem_parts->magic,
++                 sizeof(SMEM_PTABLE_MAGIC))) {
++              pr_err("SMEM partition magic invalid\n");
++              return -EINVAL;
++      }
++
++      /* Allocate and populate the mtd structures */
++      mtd_parts = kcalloc(le32_to_cpu(smem_parts->len), sizeof(*mtd_parts),
++                          GFP_KERNEL);
++      if (!mtd_parts)
++              return -ENOMEM;
++
++      for (i = 0; i < smem_parts->len; i++) {
++              struct smem_partition *s_part = &smem_parts->parts[i];
++              struct mtd_partition *m_part = &mtd_parts[i];
++
++              m_part->name = s_part->name;
++              m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz);
++              m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz);
++
++              /*
++               * The last SMEM partition may have its size marked as
++               * something like 0xffffffff, which means "until the end of the
++               * flash device". In this case, truncate it.
++               */
++              if (m_part->offset + m_part->size > master->size)
++                      m_part->size = master->size - m_part->offset;
++      }
++
++      *pparts = mtd_parts;
++
++      return smem_parts->len;
++}
++
++static struct mtd_part_parser qcom_smem_parser = {
++      .owner = THIS_MODULE,
++      .parse_fn = parse_qcom_smem_partitions,
++      .name = "qcom-smem",
++};
++
++static int __init qcom_smem_parser_init(void)
++{
++      register_mtd_parser(&qcom_smem_parser);
++      return 0;
++}
++
++static void __exit qcom_smem_parser_exit(void)
++{
++      deregister_mtd_parser(&qcom_smem_parser);
++}
++
++module_init(qcom_smem_parser_init);
++module_exit(qcom_smem_parser_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Mathieu Olivari <mathieu@codeaurora.org>");
++MODULE_DESCRIPTION("Parsing code for SMEM based partition tables");
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_AR7_PARTS)  += ar7part.o
+ obj-$(CONFIG_MTD_BCM63XX_PARTS)       += bcm63xxpart.o
+ obj-$(CONFIG_MTD_BCM47XX_PARTS)       += bcm47xxpart.o
+ obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o
++obj-$(CONFIG_MTD_QCOM_SMEM_PARTS) += qcom_smem_part.o
+ # 'Users' - code which presents functionality to userspace.
+ obj-$(CONFIG_MTD_BLKDEVS)     += mtd_blkdevs.o
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
deleted file mode 100644 (file)
index e715618..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-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-phy-add-qcom-dwc3-phy.patch b/target/linux/ipq806x/patches-4.9/0032-phy-add-qcom-dwc3-phy.patch
new file mode 100644 (file)
index 0000000..314631d
--- /dev/null
@@ -0,0 +1,526 @@
+From b9004f4fd23e4c614d71c972f3a9311665480e29 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Thu, 9 Mar 2017 08:19:18 +0100
+Subject: [PATCH 32/69] phy: add qcom dwc3 phy
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ drivers/phy/Kconfig         |  12 ++
+ drivers/phy/Makefile        |   1 +
+ drivers/phy/phy-qcom-dwc3.c | 484 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 497 insertions(+)
+ create mode 100644 drivers/phy/phy-qcom-dwc3.c
+
+--- a/drivers/phy/Kconfig
++++ b/drivers/phy/Kconfig
+@@ -489,4 +489,16 @@ config PHY_NS2_PCIE
+       help
+         Enable this to support the Broadcom Northstar2 PCIe PHY.
+         If unsure, say N.
++
++config PHY_QCOM_DWC3
++      tristate "QCOM DWC3 USB PHY support"
++      depends on ARCH_QCOM
++      depends on HAS_IOMEM
++      depends on OF
++      select GENERIC_PHY
++      help
++        This option enables support for the Synopsis PHYs present inside the
++        Qualcomm USB3.0 DWC3 controller.  This driver supports both HS and SS
++        PHY controllers.
++
+ endmenu
+--- a/drivers/phy/Makefile
++++ b/drivers/phy/Makefile
+@@ -60,3 +60,4 @@ obj-$(CONFIG_PHY_PISTACHIO_USB)              += phy-
+ obj-$(CONFIG_PHY_CYGNUS_PCIE)         += phy-bcm-cygnus-pcie.o
+ obj-$(CONFIG_ARCH_TEGRA) += tegra/
+ obj-$(CONFIG_PHY_NS2_PCIE)            += phy-bcm-ns2-pcie.o
++obj-$(CONFIG_PHY_QCOM_DWC3)           += phy-qcom-dwc3.o
+--- /dev/null
++++ b/drivers/phy/phy-qcom-dwc3.c
+@@ -0,0 +1,484 @@
++/* Copyright (c) 2014-2015, Code Aurora Forum. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++* This program is distributed in the hope that it will be useful,
++* but WITHOUT ANY WARRANTY; without even the implied warranty of
++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++* GNU General Public License for more details.
++*/
++
++#include <linux/clk.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/phy/phy.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++
++/**
++ *  USB QSCRATCH Hardware registers
++ */
++#define QSCRATCH_GENERAL_CFG          (0x08)
++#define HSUSB_PHY_CTRL_REG            (0x10)
++
++/* PHY_CTRL_REG */
++#define HSUSB_CTRL_DMSEHV_CLAMP                       BIT(24)
++#define HSUSB_CTRL_USB2_SUSPEND                       BIT(23)
++#define HSUSB_CTRL_UTMI_CLK_EN                        BIT(21)
++#define HSUSB_CTRL_UTMI_OTG_VBUS_VALID                BIT(20)
++#define HSUSB_CTRL_USE_CLKCORE                        BIT(18)
++#define HSUSB_CTRL_DPSEHV_CLAMP                       BIT(17)
++#define HSUSB_CTRL_COMMONONN                  BIT(11)
++#define HSUSB_CTRL_ID_HV_CLAMP                        BIT(9)
++#define HSUSB_CTRL_OTGSESSVLD_CLAMP           BIT(8)
++#define HSUSB_CTRL_CLAMP_EN                   BIT(7)
++#define HSUSB_CTRL_RETENABLEN                 BIT(1)
++#define HSUSB_CTRL_POR                                BIT(0)
++
++/* QSCRATCH_GENERAL_CFG */
++#define HSUSB_GCFG_XHCI_REV           BIT(2)
++
++/**
++ *  USB QSCRATCH Hardware registers
++ */
++#define SSUSB_PHY_CTRL_REG            (0x00)
++#define SSUSB_PHY_PARAM_CTRL_1                (0x04)
++#define SSUSB_PHY_PARAM_CTRL_2                (0x08)
++#define CR_PROTOCOL_DATA_IN_REG               (0x0c)
++#define CR_PROTOCOL_DATA_OUT_REG      (0x10)
++#define CR_PROTOCOL_CAP_ADDR_REG      (0x14)
++#define CR_PROTOCOL_CAP_DATA_REG      (0x18)
++#define CR_PROTOCOL_READ_REG          (0x1c)
++#define CR_PROTOCOL_WRITE_REG         (0x20)
++
++/* PHY_CTRL_REG */
++#define SSUSB_CTRL_REF_USE_PAD                BIT(28)
++#define SSUSB_CTRL_TEST_POWERDOWN     BIT(27)
++#define SSUSB_CTRL_LANE0_PWR_PRESENT  BIT(24)
++#define SSUSB_CTRL_SS_PHY_EN          BIT(8)
++#define SSUSB_CTRL_SS_PHY_RESET               BIT(7)
++
++/* SSPHY control registers */
++#define SSPHY_CTRL_RX_OVRD_IN_HI(lane)        (0x1006 + 0x100 * lane)
++#define SSPHY_CTRL_TX_OVRD_DRV_LO(lane)       (0x1002 + 0x100 * lane)
++
++/* RX OVRD IN HI bits */
++#define RX_OVRD_IN_HI_RX_RESET_OVRD           BIT(13)
++#define RX_OVRD_IN_HI_RX_RX_RESET             BIT(12)
++#define RX_OVRD_IN_HI_RX_EQ_OVRD              BIT(11)
++#define RX_OVRD_IN_HI_RX_EQ_MASK              0x0700
++#define RX_OVRD_IN_HI_RX_EQ_SHIFT             8
++#define RX_OVRD_IN_HI_RX_EQ_EN_OVRD           BIT(7)
++#define RX_OVRD_IN_HI_RX_EQ_EN                        BIT(6)
++#define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD      BIT(5)
++#define RX_OVRD_IN_HI_RX_LOS_FILTER_MASK      0x0018
++#define RX_OVRD_IN_HI_RX_RATE_OVRD            BIT(2)
++#define RX_OVRD_IN_HI_RX_RATE_MASK            0x0003
++
++/* TX OVRD DRV LO register bits */
++#define TX_OVRD_DRV_LO_AMPLITUDE_MASK 0x007F
++#define TX_OVRD_DRV_LO_PREEMPH_MASK   0x3F80
++#define TX_OVRD_DRV_LO_PREEMPH_SHIFT  7
++#define TX_OVRD_DRV_LO_EN             BIT(14)
++
++/* SS CAP register bits */
++#define SS_CR_CAP_ADDR_REG            BIT(0)
++#define SS_CR_CAP_DATA_REG            BIT(0)
++#define SS_CR_READ_REG                        BIT(0)
++#define SS_CR_WRITE_REG                       BIT(0)
++
++struct qcom_dwc3_usb_phy {
++      void __iomem            *base;
++      struct device           *dev;
++      struct clk              *xo_clk;
++      struct clk              *ref_clk;
++};
++
++struct qcom_dwc3_phy_drvdata {
++      struct phy_ops  ops;
++      u32             clk_rate;
++};
++
++/**
++ * Write register and read back masked value to confirm it is written
++ *
++ * @base - QCOM DWC3 PHY base virtual address.
++ * @offset - register offset.
++ * @mask - register bitmask specifying what should be updated
++ * @val - value to write.
++ */
++static inline void qcom_dwc3_phy_write_readback(
++      struct qcom_dwc3_usb_phy *phy_dwc3, u32 offset,
++      const u32 mask, u32 val)
++{
++      u32 write_val, tmp = readl(phy_dwc3->base + offset);
++
++      tmp &= ~mask;           /* retain other bits */
++      write_val = tmp | val;
++
++      writel(write_val, phy_dwc3->base + offset);
++
++      /* Read back to see if val was written */
++      tmp = readl(phy_dwc3->base + offset);
++      tmp &= mask;            /* clear other bits */
++
++      if (tmp != val)
++              dev_err(phy_dwc3->dev, "write: %x to QSCRATCH: %x FAILED\n",
++                      val, offset);
++}
++
++static int wait_for_latch(void __iomem *addr)
++{
++      u32 retry = 10;
++
++      while (true) {
++              if (!readl(addr))
++                      break;
++
++              if (--retry == 0)
++                      return -ETIMEDOUT;
++
++              usleep_range(10, 20);
++      }
++
++      return 0;
++}
++
++/**
++ * Write SSPHY register
++ *
++ * @base - QCOM DWC3 PHY base virtual address.
++ * @addr - SSPHY address to write.
++ * @val - value to write.
++ */
++static int qcom_dwc3_ss_write_phycreg(struct qcom_dwc3_usb_phy *phy_dwc3,
++                                      u32 addr, u32 val)
++{
++      int ret;
++
++      writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG);
++      writel(SS_CR_CAP_ADDR_REG, phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG);
++
++      ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG);
++      if (ret)
++              goto err_wait;
++
++      writel(val, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG);
++      writel(SS_CR_CAP_DATA_REG, phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG);
++
++      ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG);
++      if (ret)
++              goto err_wait;
++
++      writel(SS_CR_WRITE_REG, phy_dwc3->base + CR_PROTOCOL_WRITE_REG);
++
++      ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_WRITE_REG);
++
++err_wait:
++      if (ret)
++              dev_err(phy_dwc3->dev, "timeout waiting for latch\n");
++      return ret;
++}
++
++/**
++ * Read SSPHY register.
++ *
++ * @base - QCOM DWC3 PHY base virtual address.
++ * @addr - SSPHY address to read.
++ */
++static int qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr, u32 *val)
++{
++      int ret;
++
++      writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
++      writel(SS_CR_CAP_ADDR_REG, base + CR_PROTOCOL_CAP_ADDR_REG);
++
++      ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG);
++      if (ret)
++              goto err_wait;
++
++      /*
++       * Due to hardware bug, first read of SSPHY register might be
++       * incorrect. Hence as workaround, SW should perform SSPHY register
++       * read twice, but use only second read and ignore first read.
++       */
++      writel(SS_CR_READ_REG, base + CR_PROTOCOL_READ_REG);
++
++      ret = wait_for_latch(base + CR_PROTOCOL_READ_REG);
++      if (ret)
++              goto err_wait;
++
++      /* throwaway read */
++      readl(base + CR_PROTOCOL_DATA_OUT_REG);
++
++      writel(SS_CR_READ_REG, base + CR_PROTOCOL_READ_REG);
++
++      ret = wait_for_latch(base + CR_PROTOCOL_READ_REG);
++      if (ret)
++              goto err_wait;
++
++      *val = readl(base + CR_PROTOCOL_DATA_OUT_REG);
++
++err_wait:
++      return ret;
++}
++
++static int qcom_dwc3_phy_power_on(struct phy *phy)
++{
++      int ret;
++      struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
++
++      ret = clk_prepare_enable(phy_dwc3->xo_clk);
++      if (ret)
++              return ret;
++
++      ret = clk_prepare_enable(phy_dwc3->ref_clk);
++      if (ret)
++              clk_disable_unprepare(phy_dwc3->xo_clk);
++
++      return ret;
++}
++
++static int qcom_dwc3_phy_power_off(struct phy *phy)
++{
++      struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
++
++      clk_disable_unprepare(phy_dwc3->ref_clk);
++      clk_disable_unprepare(phy_dwc3->xo_clk);
++
++      return 0;
++}
++
++static int qcom_dwc3_hs_phy_init(struct phy *phy)
++{
++      struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
++      u32 val;
++
++      /*
++       * HSPHY Initialization: Enable UTMI clock, select 19.2MHz fsel
++       * enable clamping, and disable RETENTION (power-on default is ENABLED)
++       */
++      val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP |
++              HSUSB_CTRL_RETENABLEN  | HSUSB_CTRL_COMMONONN |
++              HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP |
++              HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_UTMI_OTG_VBUS_VALID |
++              HSUSB_CTRL_UTMI_CLK_EN | HSUSB_CTRL_CLAMP_EN | 0x70;
++
++      /* use core clock if external reference is not present */
++      if (!phy_dwc3->xo_clk)
++              val |= HSUSB_CTRL_USE_CLKCORE;
++
++      writel(val, phy_dwc3->base + HSUSB_PHY_CTRL_REG);
++      usleep_range(2000, 2200);
++
++      /* Disable (bypass) VBUS and ID filters */
++      writel(HSUSB_GCFG_XHCI_REV, phy_dwc3->base + QSCRATCH_GENERAL_CFG);
++
++      return 0;
++}
++
++static int qcom_dwc3_ss_phy_init(struct phy *phy)
++{
++      struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
++      int ret;
++      u32 data = 0;
++
++      /* reset phy */
++      data = readl(phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++      writel(data | SSUSB_CTRL_SS_PHY_RESET,
++              phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++      usleep_range(2000, 2200);
++      writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++
++      /* clear REF_PAD if we don't have XO clk */
++      if (!phy_dwc3->xo_clk)
++              data &= ~SSUSB_CTRL_REF_USE_PAD;
++      else
++              data |= SSUSB_CTRL_REF_USE_PAD;
++
++      writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++
++      /* wait for ref clk to become stable, this can take up to 30ms */
++      msleep(30);
++
++      data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT;
++      writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++
++      /*
++       * Fix RX Equalization setting as follows
++       * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
++       * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
++       * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
++       * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
++       */
++      ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base,
++                      SSPHY_CTRL_RX_OVRD_IN_HI(0), &data);
++      if (ret)
++              goto err_phy_trans;
++
++      data &= ~RX_OVRD_IN_HI_RX_EQ_EN;
++      data |= RX_OVRD_IN_HI_RX_EQ_EN_OVRD;
++      data &= ~RX_OVRD_IN_HI_RX_EQ_MASK;
++      data |= 0x3 << RX_OVRD_IN_HI_RX_EQ_SHIFT;
++      data |= RX_OVRD_IN_HI_RX_EQ_OVRD;
++      ret = qcom_dwc3_ss_write_phycreg(phy_dwc3,
++              SSPHY_CTRL_RX_OVRD_IN_HI(0), data);
++      if (ret)
++              goto err_phy_trans;
++
++      /*
++       * Set EQ and TX launch amplitudes as follows
++       * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
++       * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
++       * LANE0.TX_OVRD_DRV_LO.EN set to 1.
++       */
++      ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base,
++              SSPHY_CTRL_TX_OVRD_DRV_LO(0), &data);
++      if (ret)
++              goto err_phy_trans;
++
++      data &= ~TX_OVRD_DRV_LO_PREEMPH_MASK;
++      data |= 0x16 << TX_OVRD_DRV_LO_PREEMPH_SHIFT;
++      data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK;
++      data |= 0x7f;
++      data |= TX_OVRD_DRV_LO_EN;
++      ret = qcom_dwc3_ss_write_phycreg(phy_dwc3,
++              SSPHY_CTRL_TX_OVRD_DRV_LO(0), data);
++      if (ret)
++              goto err_phy_trans;
++
++      /*
++       * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows
++       * TX_FULL_SWING [26:20] amplitude to 127
++       * TX_DEEMPH_3_5DB [13:8] to 22
++       * LOS_BIAS [2:0] to 0x5
++       */
++      qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1,
++                                 0x07f03f07, 0x07f01605);
++
++err_phy_trans:
++      return ret;
++}
++
++static int qcom_dwc3_ss_phy_exit(struct phy *phy)
++{
++      struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
++
++      /* Sequence to put SSPHY in low power state:
++       * 1. Clear REF_PHY_EN in PHY_CTRL_REG
++       * 2. Clear REF_USE_PAD in PHY_CTRL_REG
++       * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention
++       */
++      qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
++              SSUSB_CTRL_SS_PHY_EN, 0x0);
++      qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
++              SSUSB_CTRL_REF_USE_PAD, 0x0);
++      qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
++              0x0, SSUSB_CTRL_TEST_POWERDOWN);
++
++      return 0;
++}
++
++static const struct qcom_dwc3_phy_drvdata qcom_dwc3_hs_drvdata = {
++      .ops = {
++              .init           = qcom_dwc3_hs_phy_init,
++              .power_on       = qcom_dwc3_phy_power_on,
++              .power_off      = qcom_dwc3_phy_power_off,
++              .owner          = THIS_MODULE,
++      },
++      .clk_rate = 60000000,
++};
++
++static const struct qcom_dwc3_phy_drvdata qcom_dwc3_ss_drvdata = {
++      .ops = {
++              .init           = qcom_dwc3_ss_phy_init,
++              .exit           = qcom_dwc3_ss_phy_exit,
++              .power_on       = qcom_dwc3_phy_power_on,
++              .power_off      = qcom_dwc3_phy_power_off,
++              .owner          = THIS_MODULE,
++      },
++      .clk_rate = 125000000,
++};
++
++static const struct of_device_id qcom_dwc3_phy_table[] = {
++      { .compatible = "qcom,dwc3-hs-usb-phy", .data = &qcom_dwc3_hs_drvdata },
++      { .compatible = "qcom,dwc3-ss-usb-phy", .data = &qcom_dwc3_ss_drvdata },
++      { /* Sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, qcom_dwc3_phy_table);
++
++static int qcom_dwc3_phy_probe(struct platform_device *pdev)
++{
++      struct qcom_dwc3_usb_phy        *phy_dwc3;
++      struct phy_provider             *phy_provider;
++      struct phy                      *generic_phy;
++      struct resource                 *res;
++      const struct of_device_id *match;
++      const struct qcom_dwc3_phy_drvdata *data;
++
++      phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL);
++      if (!phy_dwc3)
++              return -ENOMEM;
++
++      match = of_match_node(qcom_dwc3_phy_table, pdev->dev.of_node);
++      data = match->data;
++
++      phy_dwc3->dev = &pdev->dev;
++
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      phy_dwc3->base = devm_ioremap_resource(phy_dwc3->dev, res);
++      if (IS_ERR(phy_dwc3->base))
++              return PTR_ERR(phy_dwc3->base);
++
++      phy_dwc3->ref_clk = devm_clk_get(phy_dwc3->dev, "ref");
++      if (IS_ERR(phy_dwc3->ref_clk)) {
++              dev_dbg(phy_dwc3->dev, "cannot get reference clock\n");
++              return PTR_ERR(phy_dwc3->ref_clk);
++      }
++
++      clk_set_rate(phy_dwc3->ref_clk, data->clk_rate);
++
++      phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo");
++      if (IS_ERR(phy_dwc3->xo_clk)) {
++              dev_dbg(phy_dwc3->dev, "cannot get TCXO clock\n");
++              phy_dwc3->xo_clk = NULL;
++      }
++
++      generic_phy = devm_phy_create(phy_dwc3->dev, pdev->dev.of_node,
++                                    &data->ops);
++
++      if (IS_ERR(generic_phy))
++              return PTR_ERR(generic_phy);
++
++      phy_set_drvdata(generic_phy, phy_dwc3);
++      platform_set_drvdata(pdev, phy_dwc3);
++
++      phy_provider = devm_of_phy_provider_register(phy_dwc3->dev,
++                      of_phy_simple_xlate);
++
++      if (IS_ERR(phy_provider))
++              return PTR_ERR(phy_provider);
++
++      return 0;
++}
++
++static struct platform_driver qcom_dwc3_phy_driver = {
++      .probe          = qcom_dwc3_phy_probe,
++      .driver         = {
++              .name   = "qcom-dwc3-usb-phy",
++              .owner  = THIS_MODULE,
++              .of_match_table = qcom_dwc3_phy_table,
++      },
++};
++
++module_platform_driver(qcom_dwc3_phy_driver);
++
++MODULE_ALIAS("platform:phy-qcom-dwc3");
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
++MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
++MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver");
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
deleted file mode 100644 (file)
index 9263ffd..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-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-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch b/target/linux/ipq806x/patches-4.9/0033-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch
new file mode 100644 (file)
index 0000000..a6c7953
--- /dev/null
@@ -0,0 +1,29 @@
+From 48051ece78136e4235a2415a52797db56f8a4478 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Tue, 21 Apr 2015 19:09:07 -0700
+Subject: [PATCH 33/69] ARM: qcom: automatically select PCI_DOMAINS if PCI is
+ enabled
+
+If multiple PCIe devices are present in the system, the kernel will
+panic at boot time when trying to scan the PCI buses. This happens on
+IPQ806x based platforms, which has 3 PCIe ports.
+
+Enabling this option allows the kernel to assign the pci-domains
+according to the device-tree content. This allows multiple PCIe
+controllers to coexist in the system.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/mach-qcom/Kconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm/mach-qcom/Kconfig
++++ b/arch/arm/mach-qcom/Kconfig
+@@ -6,6 +6,7 @@ menuconfig ARCH_QCOM
+       select ARM_AMBA
+       select PINCTRL
+       select QCOM_SCM if SMP
++      select PCI_DOMAINS if PCI
+       help
+         Support for Qualcomm's devicetree based systems.
diff --git a/target/linux/ipq806x/patches-4.9/0033-dts-ipq4019-support-ARMv7-PMU.patch b/target/linux/ipq806x/patches-4.9/0033-dts-ipq4019-support-ARMv7-PMU.patch
deleted file mode 100644 (file)
index 0ea66d9..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-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-ARM-Add-Krait-L2-register-accessor-functions.patch b/target/linux/ipq806x/patches-4.9/0034-ARM-Add-Krait-L2-register-accessor-functions.patch
new file mode 100644 (file)
index 0000000..8c69b97
--- /dev/null
@@ -0,0 +1,133 @@
+From 1d6b12f73c98ecf252743936a53242356cc50a34 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 20 Mar 2015 23:45:20 -0700
+Subject: [PATCH 34/69] ARM: Add Krait L2 register accessor functions
+
+Krait CPUs have a handful of L2 cache controller registers that
+live behind a cp15 based indirection register. First you program
+the indirection register (l2cpselr) to point the L2 'window'
+register (l2cpdr) at what you want to read/write.  Then you
+read/write the 'window' register to do what you want. The
+l2cpselr register is not banked per-cpu so we must lock around
+accesses to it to prevent other CPUs from re-pointing l2cpdr
+underneath us.
+
+Cc: Mark Rutland <mark.rutland@arm.com>
+Cc: Russell King <linux@arm.linux.org.uk>
+Cc: Courtney Cavin <courtney.cavin@sonymobile.com>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ arch/arm/common/Kconfig                   |  3 ++
+ arch/arm/common/Makefile                  |  1 +
+ arch/arm/common/krait-l2-accessors.c      | 58 +++++++++++++++++++++++++++++++
+ arch/arm/include/asm/krait-l2-accessors.h | 20 +++++++++++
+ 4 files changed, 82 insertions(+)
+ create mode 100644 arch/arm/common/krait-l2-accessors.c
+ create mode 100644 arch/arm/include/asm/krait-l2-accessors.h
+
+--- a/arch/arm/common/Kconfig
++++ b/arch/arm/common/Kconfig
+@@ -9,6 +9,9 @@ config DMABOUNCE
+       bool
+       select ZONE_DMA
++config KRAIT_L2_ACCESSORS
++      bool
++
+ config SHARP_LOCOMO
+       bool
+--- a/arch/arm/common/Makefile
++++ b/arch/arm/common/Makefile
+@@ -7,6 +7,7 @@ obj-y                          += firmware.o
+ obj-$(CONFIG_ICST)            += icst.o
+ obj-$(CONFIG_SA1111)          += sa1111.o
+ obj-$(CONFIG_DMABOUNCE)               += dmabounce.o
++obj-$(CONFIG_KRAIT_L2_ACCESSORS) += krait-l2-accessors.o
+ obj-$(CONFIG_SHARP_LOCOMO)    += locomo.o
+ obj-$(CONFIG_SHARP_PARAM)     += sharpsl_param.o
+ obj-$(CONFIG_SHARP_SCOOP)     += scoop.o
+--- /dev/null
++++ b/arch/arm/common/krait-l2-accessors.c
+@@ -0,0 +1,58 @@
++/*
++ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/spinlock.h>
++#include <linux/export.h>
++
++#include <asm/barrier.h>
++#include <asm/krait-l2-accessors.h>
++
++static DEFINE_RAW_SPINLOCK(krait_l2_lock);
++
++void krait_set_l2_indirect_reg(u32 addr, u32 val)
++{
++      unsigned long flags;
++
++      raw_spin_lock_irqsave(&krait_l2_lock, flags);
++      /*
++       * Select the L2 window by poking l2cpselr, then write to the window
++       * via l2cpdr.
++       */
++      asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr));
++      isb();
++      asm volatile ("mcr p15, 3, %0, c15, c0, 7 @ l2cpdr" : : "r" (val));
++      isb();
++
++      raw_spin_unlock_irqrestore(&krait_l2_lock, flags);
++}
++EXPORT_SYMBOL(krait_set_l2_indirect_reg);
++
++u32 krait_get_l2_indirect_reg(u32 addr)
++{
++      u32 val;
++      unsigned long flags;
++
++      raw_spin_lock_irqsave(&krait_l2_lock, flags);
++      /*
++       * Select the L2 window by poking l2cpselr, then read from the window
++       * via l2cpdr.
++       */
++      asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr));
++      isb();
++      asm volatile ("mrc p15, 3, %0, c15, c0, 7 @ l2cpdr" : "=r" (val));
++
++      raw_spin_unlock_irqrestore(&krait_l2_lock, flags);
++
++      return val;
++}
++EXPORT_SYMBOL(krait_get_l2_indirect_reg);
+--- /dev/null
++++ b/arch/arm/include/asm/krait-l2-accessors.h
+@@ -0,0 +1,20 @@
++/*
++ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#ifndef __ASMARM_KRAIT_L2_ACCESSORS_H
++#define __ASMARM_KRAIT_L2_ACCESSORS_H
++
++extern void krait_set_l2_indirect_reg(u32 addr, u32 val);
++extern u32 krait_get_l2_indirect_reg(u32 addr);
++
++#endif
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
deleted file mode 100644 (file)
index 86a8a87..0000000
+++ /dev/null
@@ -1,488 +0,0 @@
-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-clk-mux-Split-out-register-accessors-for-reuse.patch b/target/linux/ipq806x/patches-4.9/0035-clk-mux-Split-out-register-accessors-for-reuse.patch
new file mode 100644 (file)
index 0000000..426cabd
--- /dev/null
@@ -0,0 +1,192 @@
+From 9d381d65eae163d8f50d97a3ad9033bba176f62b Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 20 Mar 2015 23:45:21 -0700
+Subject: [PATCH 35/69] clk: mux: Split out register accessors for reuse
+
+We want to reuse the logic in clk-mux.c for other clock drivers
+that don't use readl as register accessors. Fortunately, there
+really isn't much to the mux code besides the table indirection
+and quirk flags if you assume any bit shifting and masking has
+been done already. Pull that logic out into reusable functions
+that operate on an optional table and some flags so that other
+drivers can use the same logic.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
+---
+ drivers/clk/clk-mux.c        | 76 ++++++++++++++++++++++++++++----------------
+ include/linux/clk-provider.h | 11 +++++--
+ 2 files changed, 57 insertions(+), 30 deletions(-)
+
+--- a/drivers/clk/clk-mux.c
++++ b/drivers/clk/clk-mux.c
+@@ -26,35 +26,27 @@
+  * parent - parent is adjustable through clk_set_parent
+  */
+-static u8 clk_mux_get_parent(struct clk_hw *hw)
++#define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw)
++
++unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val,
++                              unsigned int *table, unsigned long flags)
+ {
+       struct clk_mux *mux = to_clk_mux(hw);
+       int num_parents = clk_hw_get_num_parents(hw);
+-      u32 val;
+-      /*
+-       * FIXME need a mux-specific flag to determine if val is bitwise or numeric
+-       * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
+-       * to 0x7 (index starts at one)
+-       * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
+-       * val = 0x4 really means "bit 2, index starts at bit 0"
+-       */
+-      val = clk_readl(mux->reg) >> mux->shift;
+-      val &= mux->mask;
+-
+-      if (mux->table) {
++      if (table) {
+               int i;
+               for (i = 0; i < num_parents; i++)
+-                      if (mux->table[i] == val)
++                      if (table[i] == val)
+                               return i;
+               return -EINVAL;
+       }
+-      if (val && (mux->flags & CLK_MUX_INDEX_BIT))
++      if (val && (flags & CLK_MUX_INDEX_BIT))
+               val = ffs(val) - 1;
+-      if (val && (mux->flags & CLK_MUX_INDEX_ONE))
++      if (val && (flags & CLK_MUX_INDEX_ONE))
+               val--;
+       if (val >= num_parents)
+@@ -62,23 +54,53 @@ static u8 clk_mux_get_parent(struct clk_
+       return val;
+ }
++EXPORT_SYMBOL_GPL(clk_mux_get_parent);
+-static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
++static u8 _clk_mux_get_parent(struct clk_hw *hw)
+ {
+       struct clk_mux *mux = to_clk_mux(hw);
+       u32 val;
+-      unsigned long flags = 0;
+-      if (mux->table) {
+-              index = mux->table[index];
++      /*
++       * FIXME need a mux-specific flag to determine if val is bitwise or numeric
++       * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
++       * to 0x7 (index starts at one)
++       * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
++       * val = 0x4 really means "bit 2, index starts at bit 0"
++       */
++      val = clk_readl(mux->reg) >> mux->shift;
++      val &= mux->mask;
++
++      return clk_mux_get_parent(hw, val, mux->table, mux->flags);
++}
++
++unsigned int clk_mux_reindex(u8 index, unsigned int *table,
++                           unsigned long flags)
++{
++      unsigned int val = index;
++
++      if (table) {
++              val = table[val];
+       } else {
+-              if (mux->flags & CLK_MUX_INDEX_BIT)
+-                      index = 1 << index;
++              if (flags & CLK_MUX_INDEX_BIT)
++                      val = 1 << index;
+-              if (mux->flags & CLK_MUX_INDEX_ONE)
+-                      index++;
++              if (flags & CLK_MUX_INDEX_ONE)
++                      val++;
+       }
++      return val;
++}
++EXPORT_SYMBOL_GPL(clk_mux_reindex);
++
++static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
++{
++      struct clk_mux *mux = to_clk_mux(hw);
++      u32 val;
++      unsigned long flags = 0;
++
++      index = clk_mux_reindex(index, mux->table, mux->flags);
++
+       if (mux->lock)
+               spin_lock_irqsave(mux->lock, flags);
+       else
+@@ -102,14 +124,14 @@ static int clk_mux_set_parent(struct clk
+ }
+ const struct clk_ops clk_mux_ops = {
+-      .get_parent = clk_mux_get_parent,
++      .get_parent = _clk_mux_get_parent,
+       .set_parent = clk_mux_set_parent,
+       .determine_rate = __clk_mux_determine_rate,
+ };
+ EXPORT_SYMBOL_GPL(clk_mux_ops);
+ const struct clk_ops clk_mux_ro_ops = {
+-      .get_parent = clk_mux_get_parent,
++      .get_parent = _clk_mux_get_parent,
+ };
+ EXPORT_SYMBOL_GPL(clk_mux_ro_ops);
+@@ -117,7 +139,7 @@ struct clk_hw *clk_hw_register_mux_table
+               const char * const *parent_names, u8 num_parents,
+               unsigned long flags,
+               void __iomem *reg, u8 shift, u32 mask,
+-              u8 clk_mux_flags, u32 *table, spinlock_t *lock)
++              u8 clk_mux_flags, unsigned int *table, spinlock_t *lock)
+ {
+       struct clk_mux *mux;
+       struct clk_hw *hw;
+--- a/include/linux/clk-provider.h
++++ b/include/linux/clk-provider.h
+@@ -466,7 +466,7 @@ void clk_hw_unregister_divider(struct cl
+ struct clk_mux {
+       struct clk_hw   hw;
+       void __iomem    *reg;
+-      u32             *table;
++      unsigned int    *table;
+       u32             mask;
+       u8              shift;
+       u8              flags;
+@@ -484,6 +484,11 @@ struct clk_mux {
+ extern const struct clk_ops clk_mux_ops;
+ extern const struct clk_ops clk_mux_ro_ops;
++unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val,
++                              unsigned int *table, unsigned long flags);
++unsigned int clk_mux_reindex(u8 index, unsigned int *table,
++                           unsigned long flags);
++
+ struct clk *clk_register_mux(struct device *dev, const char *name,
+               const char * const *parent_names, u8 num_parents,
+               unsigned long flags,
+@@ -499,12 +504,12 @@ struct clk *clk_register_mux_table(struc
+               const char * const *parent_names, u8 num_parents,
+               unsigned long flags,
+               void __iomem *reg, u8 shift, u32 mask,
+-              u8 clk_mux_flags, u32 *table, spinlock_t *lock);
++              u8 clk_mux_flags, unsigned int *table, spinlock_t *lock);
+ struct clk_hw *clk_hw_register_mux_table(struct device *dev, const char *name,
+               const char * const *parent_names, u8 num_parents,
+               unsigned long flags,
+               void __iomem *reg, u8 shift, u32 mask,
+-              u8 clk_mux_flags, u32 *table, spinlock_t *lock);
++              u8 clk_mux_flags, unsigned int *table, spinlock_t *lock);
+ void clk_unregister_mux(struct clk *clk);
+ void clk_hw_unregister_mux(struct clk_hw *hw);
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
deleted file mode 100644 (file)
index 61768bc..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-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-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch b/target/linux/ipq806x/patches-4.9/0036-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch
new file mode 100644 (file)
index 0000000..f4a47c7
--- /dev/null
@@ -0,0 +1,107 @@
+From 88e1d6d9c113fe50810d1b03eb1fdbf015e5d1bd Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 20 Mar 2015 23:45:22 -0700
+Subject: [PATCH 36/69] clk: Avoid sending high rates to downstream clocks
+ during set_rate
+
+If a clock is on and we call clk_set_rate() on it we may get into
+a situation where the clock temporarily increases in rate
+dramatically while we walk the tree and call .set_rate() ops. For
+example, consider a case where a PLL feeds into a divider.
+Initially the divider is set to divide by 1 and the PLL is
+running fairly slow (100MHz). The downstream consumer of the
+divider output can only handle rates =< 400 MHz, but the divider
+can only choose between divisors of 1 and 4.
+
+ +-----+   +----------------+
+ | PLL |-->| div 1 or div 4 |---> consumer device
+ +-----+   +----------------+
+
+To achieve a rate of 400MHz on the output of the divider, we
+would have to set the rate of the PLL to 1.6 GHz and then divide
+it by 4. The current code would set the PLL to 1.6GHz first while
+the divider is still set to 1, thus causing the downstream
+consumer of the clock to receive a few clock cycles of 1.6GHz
+clock (far beyond it's maximum acceptable rate). We should be
+changing the divider first before increasing the PLL rate to
+avoid this problem.
+
+Therefore, set the rate of any child clocks that are increasing
+in rate from their current rate so that they can increase their
+dividers if necessary. We assume that there isn't such a thing as
+minimum rate requirements.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+
+Conflicts:
+       drivers/clk/clk.c
+---
+ drivers/clk/clk.c | 22 +++++++++++++++-------
+ 1 file changed, 15 insertions(+), 7 deletions(-)
+
+--- a/drivers/clk/clk.c
++++ b/drivers/clk/clk.c
+@@ -1466,12 +1466,12 @@ static struct clk_core *clk_propagate_ra
+  * walk down a subtree and set the new rates notifying the rate
+  * change on the way
+  */
+-static void clk_change_rate(struct clk_core *core)
++static void
++clk_change_rate(struct clk_core *core, unsigned long best_parent_rate)
+ {
+       struct clk_core *child;
+       struct hlist_node *tmp;
+       unsigned long old_rate;
+-      unsigned long best_parent_rate = 0;
+       bool skip_set_rate = false;
+       struct clk_core *old_parent;
+       struct clk_core *parent = NULL;
+@@ -1523,6 +1523,7 @@ static void clk_change_rate(struct clk_c
+       trace_clk_set_rate_complete(core, core->new_rate);
+       core->rate = clk_recalc(core, best_parent_rate);
++      core->rate = core->new_rate;
+       if (core->flags & CLK_SET_RATE_UNGATE) {
+               unsigned long flags;
+@@ -1550,12 +1551,13 @@ static void clk_change_rate(struct clk_c
+               /* Skip children who will be reparented to another clock */
+               if (child->new_parent && child->new_parent != core)
+                       continue;
+-              clk_change_rate(child);
++              if (child->new_rate != child->rate)
++                      clk_change_rate(child, core->new_rate);
+       }
+-      /* handle the new child who might not be in core->children yet */
+-      if (core->new_child)
+-              clk_change_rate(core->new_child);
++      /* handle the new child who might not be in clk->children yet */
++      if (core->new_child && core->new_child->new_rate != core->new_child->rate)
++              clk_change_rate(core->new_child, core->new_rate);
+ }
+ static int clk_core_set_rate_nolock(struct clk_core *core,
+@@ -1563,6 +1565,7 @@ static int clk_core_set_rate_nolock(stru
+ {
+       struct clk_core *top, *fail_clk;
+       unsigned long rate = req_rate;
++      unsigned long parent_rate;
+       if (!core)
+               return 0;
+@@ -1588,8 +1591,13 @@ static int clk_core_set_rate_nolock(stru
+               return -EBUSY;
+       }
++      if (top->parent)
++              parent_rate = top->parent->rate;
++      else
++              parent_rate = 0;
++
+       /* change the rates */
+-      clk_change_rate(top);
++      clk_change_rate(top, parent_rate);
+       core->req_rate = req_rate;
diff --git a/target/linux/ipq806x/patches-4.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
deleted file mode 100644 (file)
index 77be518..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-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-clk-Add-safe-switch-hook.patch b/target/linux/ipq806x/patches-4.9/0037-clk-Add-safe-switch-hook.patch
new file mode 100644 (file)
index 0000000..bff605f
--- /dev/null
@@ -0,0 +1,158 @@
+From a1adfb782789ae9b25c928dfe3d639288563a86c Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 20 Mar 2015 23:45:23 -0700
+Subject: [PATCH 37/69] clk: Add safe switch hook
+
+Sometimes clocks can't accept their parent source turning off
+while the source is reprogrammed to a different rate. Most
+notably CPU clocks require a way to switch away from the current
+PLL they're running on, reprogram that PLL to a new rate, and
+then switch back to the PLL with the new rate once they're done.
+Add a hook that drivers can implement allowing them to return a
+'safe parent' and 'safe frequency' that they can switch their
+parent to while the upstream source is reprogrammed to support
+this.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
+---
+ drivers/clk/clk.c            | 73 +++++++++++++++++++++++++++++++++++++++++---
+ include/linux/clk-provider.h |  2 ++
+ 2 files changed, 70 insertions(+), 5 deletions(-)
+
+--- a/drivers/clk/clk.c
++++ b/drivers/clk/clk.c
+@@ -51,9 +51,13 @@ struct clk_core {
+       struct clk_core         **parents;
+       u8                      num_parents;
+       u8                      new_parent_index;
++      u8                      safe_parent_index;
+       unsigned long           rate;
+       unsigned long           req_rate;
++      unsigned long           old_rate;
+       unsigned long           new_rate;
++      unsigned long           safe_freq;
++      struct clk_core         *safe_parent;
+       struct clk_core         *new_parent;
+       struct clk_core         *new_child;
+       unsigned long           flags;
+@@ -1310,7 +1314,9 @@ out:
+ static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate,
+                            struct clk_core *new_parent, u8 p_index)
+ {
+-      struct clk_core *child;
++      struct clk_core *child, *parent;
++      struct clk_hw *parent_hw;
++      unsigned long safe_freq = 0;
+       core->new_rate = new_rate;
+       core->new_parent = new_parent;
+@@ -1320,6 +1326,23 @@ static void clk_calc_subtree(struct clk_
+       if (new_parent && new_parent != core->parent)
+               new_parent->new_child = core;
++      if (core->ops->get_safe_parent) {
++              parent_hw = core->ops->get_safe_parent(core->hw, &safe_freq);
++              if (parent_hw) {
++                      parent = parent_hw->core;
++                      p_index = clk_fetch_parent_index(core, parent);
++                      core->safe_parent_index = p_index;
++                      core->safe_parent = parent;
++                      if (safe_freq)
++                              core->safe_freq = safe_freq;
++                      else
++                              core->safe_freq = 0;
++              }
++      } else {
++              core->safe_parent = NULL;
++              core->safe_freq = 0;
++      }
++
+       hlist_for_each_entry(child, &core->children, child_node) {
+               child->new_rate = clk_recalc(child, new_rate);
+               clk_calc_subtree(child, child->new_rate, NULL, 0);
+@@ -1432,14 +1455,51 @@ static struct clk_core *clk_propagate_ra
+                                                 unsigned long event)
+ {
+       struct clk_core *child, *tmp_clk, *fail_clk = NULL;
++      struct clk_core *old_parent;
+       int ret = NOTIFY_DONE;
+-      if (core->rate == core->new_rate)
++      if (core->rate == core->new_rate && event != POST_RATE_CHANGE)
+               return NULL;
++      switch (event) {
++      case PRE_RATE_CHANGE:
++              if (core->safe_parent) {
++                      if (core->safe_freq)
++                              core->ops->set_rate_and_parent(core->hw,
++                                              core->safe_freq,
++                                              core->safe_parent->rate,
++                                              core->safe_parent_index);
++                      else
++                              core->ops->set_parent(core->hw,
++                                              core->safe_parent_index);
++              }
++              core->old_rate = core->rate;
++              break;
++      case POST_RATE_CHANGE:
++              if (core->safe_parent) {
++                      old_parent = __clk_set_parent_before(core,
++                                                           core->new_parent);
++                      if (core->ops->set_rate_and_parent) {
++                              core->ops->set_rate_and_parent(core->hw,
++                                              core->new_rate,
++                                              core->new_parent ?
++                                              core->new_parent->rate : 0,
++                                              core->new_parent_index);
++                      } else if (core->ops->set_parent) {
++                              core->ops->set_parent(core->hw,
++                                              core->new_parent_index);
++                      }
++                      __clk_set_parent_after(core, core->new_parent,
++                                             old_parent);
++              }
++              break;
++      }
++
+       if (core->notifier_count) {
+-              ret = __clk_notify(core, event, core->rate, core->new_rate);
+-              if (ret & NOTIFY_STOP_MASK)
++              if (event != POST_RATE_CHANGE || core->old_rate != core->rate)
++                      ret = __clk_notify(core, event, core->old_rate,
++                                         core->new_rate);
++              if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE)
+                       fail_clk = core;
+       }
+@@ -1495,7 +1555,8 @@ clk_change_rate(struct clk_core *core, u
+               clk_enable_unlock(flags);
+       }
+-      if (core->new_parent && core->new_parent != core->parent) {
++      if (core->new_parent && core->new_parent != core->parent &&
++          !core->safe_parent) {
+               old_parent = __clk_set_parent_before(core, core->new_parent);
+               trace_clk_set_parent(core, core->new_parent);
+@@ -1601,6 +1662,8 @@ static int clk_core_set_rate_nolock(stru
+       core->req_rate = req_rate;
++      clk_propagate_rate_change(top, POST_RATE_CHANGE);
++
+       return 0;
+ }
+--- a/include/linux/clk-provider.h
++++ b/include/linux/clk-provider.h
+@@ -206,6 +206,8 @@ struct clk_ops {
+                                         struct clk_rate_request *req);
+       int             (*set_parent)(struct clk_hw *hw, u8 index);
+       u8              (*get_parent)(struct clk_hw *hw);
++      struct clk_hw   *(*get_safe_parent)(struct clk_hw *hw,
++                                          unsigned long *safe_freq);
+       int             (*set_rate)(struct clk_hw *hw, unsigned long rate,
+                                   unsigned long parent_rate);
+       int             (*set_rate_and_parent)(struct clk_hw *hw,
diff --git a/target/linux/ipq806x/patches-4.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
deleted file mode 100644 (file)
index 21d7e39..0000000
+++ /dev/null
@@ -1,258 +0,0 @@
-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/0038-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch b/target/linux/ipq806x/patches-4.9/0038-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch
new file mode 100644 (file)
index 0000000..e3797cf
--- /dev/null
@@ -0,0 +1,340 @@
+From f044ffe2d612dcaa2de36c918aaab79c8db1482e Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 20 Mar 2015 23:45:24 -0700
+Subject: [PATCH 38/69] clk: qcom: Add support for High-Frequency PLLs (HFPLLs)
+
+HFPLLs are the main frequency source for Krait CPU clocks. Add
+support for changing the rate of these PLLs.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/qcom/Makefile    |   1 +
+ drivers/clk/qcom/clk-hfpll.c | 253 +++++++++++++++++++++++++++++++++++++++++++
+ drivers/clk/qcom/clk-hfpll.h |  54 +++++++++
+ 3 files changed, 308 insertions(+)
+ create mode 100644 drivers/clk/qcom/clk-hfpll.c
+ create mode 100644 drivers/clk/qcom/clk-hfpll.h
+
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -9,6 +9,7 @@ clk-qcom-y += clk-rcg2.o
+ clk-qcom-y += clk-branch.o
+ clk-qcom-y += clk-regmap-divider.o
+ clk-qcom-y += clk-regmap-mux.o
++clk-qcom-y += clk-hfpll.o
+ clk-qcom-y += reset.o
+ clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
+--- /dev/null
++++ b/drivers/clk/qcom/clk-hfpll.c
+@@ -0,0 +1,253 @@
++/*
++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++#include <linux/kernel.h>
++#include <linux/export.h>
++#include <linux/regmap.h>
++#include <linux/delay.h>
++#include <linux/err.h>
++#include <linux/clk-provider.h>
++#include <linux/spinlock.h>
++
++#include "clk-regmap.h"
++#include "clk-hfpll.h"
++
++#define PLL_OUTCTRL   BIT(0)
++#define PLL_BYPASSNL  BIT(1)
++#define PLL_RESET_N   BIT(2)
++
++/* Initialize a HFPLL at a given rate and enable it. */
++static void __clk_hfpll_init_once(struct clk_hw *hw)
++{
++      struct clk_hfpll *h = to_clk_hfpll(hw);
++      struct hfpll_data const *hd = h->d;
++      struct regmap *regmap = h->clkr.regmap;
++
++      if (likely(h->init_done))
++              return;
++
++      /* Configure PLL parameters for integer mode. */
++      if (hd->config_val)
++              regmap_write(regmap, hd->config_reg, hd->config_val);
++      regmap_write(regmap, hd->m_reg, 0);
++      regmap_write(regmap, hd->n_reg, 1);
++
++      if (hd->user_reg) {
++              u32 regval = hd->user_val;
++              unsigned long rate;
++
++              rate = clk_hw_get_rate(hw);
++
++              /* Pick the right VCO. */
++              if (hd->user_vco_mask && rate > hd->low_vco_max_rate)
++                      regval |= hd->user_vco_mask;
++              regmap_write(regmap, hd->user_reg, regval);
++      }
++
++      if (hd->droop_reg)
++              regmap_write(regmap, hd->droop_reg, hd->droop_val);
++
++      h->init_done = true;
++}
++
++static void __clk_hfpll_enable(struct clk_hw *hw)
++{
++      struct clk_hfpll *h = to_clk_hfpll(hw);
++      struct hfpll_data const *hd = h->d;
++      struct regmap *regmap = h->clkr.regmap;
++      u32 val;
++
++      __clk_hfpll_init_once(hw);
++
++      /* Disable PLL bypass mode. */
++      regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL);
++
++      /*
++       * H/W requires a 5us delay between disabling the bypass and
++       * de-asserting the reset. Delay 10us just to be safe.
++       */
++      udelay(10);
++
++      /* De-assert active-low PLL reset. */
++      regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N);
++
++      /* Wait for PLL to lock. */
++      if (hd->status_reg) {
++              do {
++                      regmap_read(regmap, hd->status_reg, &val);
++              } while (!(val & BIT(hd->lock_bit)));
++      } else {
++              udelay(60);
++      }
++
++      /* Enable PLL output. */
++      regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL);
++}
++
++/* Enable an already-configured HFPLL. */
++static int clk_hfpll_enable(struct clk_hw *hw)
++{
++      unsigned long flags;
++      struct clk_hfpll *h = to_clk_hfpll(hw);
++      struct hfpll_data const *hd = h->d;
++      struct regmap *regmap = h->clkr.regmap;
++      u32 mode;
++
++      spin_lock_irqsave(&h->lock, flags);
++      regmap_read(regmap, hd->mode_reg, &mode);
++      if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)))
++              __clk_hfpll_enable(hw);
++      spin_unlock_irqrestore(&h->lock, flags);
++
++      return 0;
++}
++
++static void __clk_hfpll_disable(struct clk_hfpll *h)
++{
++      struct hfpll_data const *hd = h->d;
++      struct regmap *regmap = h->clkr.regmap;
++
++      /*
++       * Disable the PLL output, disable test mode, enable the bypass mode,
++       * and assert the reset.
++       */
++      regmap_update_bits(regmap, hd->mode_reg,
++                      PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0);
++}
++
++static void clk_hfpll_disable(struct clk_hw *hw)
++{
++      struct clk_hfpll *h = to_clk_hfpll(hw);
++      unsigned long flags;
++
++      spin_lock_irqsave(&h->lock, flags);
++      __clk_hfpll_disable(h);
++      spin_unlock_irqrestore(&h->lock, flags);
++}
++
++static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate,
++                               unsigned long *parent_rate)
++{
++      struct clk_hfpll *h = to_clk_hfpll(hw);
++      struct hfpll_data const *hd = h->d;
++      unsigned long rrate;
++
++      rate = clamp(rate, hd->min_rate, hd->max_rate);
++
++      rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate;
++      if (rrate > hd->max_rate)
++              rrate -= *parent_rate;
++
++      return rrate;
++}
++
++/*
++ * For optimization reasons, assumes no downstream clocks are actively using
++ * it.
++ */
++static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate,
++                            unsigned long parent_rate)
++{
++      struct clk_hfpll *h = to_clk_hfpll(hw);
++      struct hfpll_data const *hd = h->d;
++      struct regmap *regmap = h->clkr.regmap;
++      unsigned long flags;
++      u32 l_val, val;
++      bool enabled;
++
++      l_val = rate / parent_rate;
++
++      spin_lock_irqsave(&h->lock, flags);
++
++      enabled = __clk_is_enabled(hw->clk);
++      if (enabled)
++              __clk_hfpll_disable(h);
++
++      /* Pick the right VCO. */
++      if (hd->user_reg && hd->user_vco_mask) {
++              regmap_read(regmap, hd->user_reg, &val);
++              if (rate <= hd->low_vco_max_rate)
++                      val &= ~hd->user_vco_mask;
++              else
++                      val |= hd->user_vco_mask;
++              regmap_write(regmap, hd->user_reg, val);
++      }
++
++      regmap_write(regmap, hd->l_reg, l_val);
++
++      if (enabled)
++              __clk_hfpll_enable(hw);
++
++      spin_unlock_irqrestore(&h->lock, flags);
++
++      return 0;
++}
++
++static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw,
++                                         unsigned long parent_rate)
++{
++      struct clk_hfpll *h = to_clk_hfpll(hw);
++      struct hfpll_data const *hd = h->d;
++      struct regmap *regmap = h->clkr.regmap;
++      u32 l_val;
++
++      regmap_read(regmap, hd->l_reg, &l_val);
++
++      return l_val * parent_rate;
++}
++
++static void clk_hfpll_init(struct clk_hw *hw)
++{
++      struct clk_hfpll *h = to_clk_hfpll(hw);
++      struct hfpll_data const *hd = h->d;
++      struct regmap *regmap = h->clkr.regmap;
++      u32 mode, status;
++
++      regmap_read(regmap, hd->mode_reg, &mode);
++      if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) {
++              __clk_hfpll_init_once(hw);
++              return;
++      }
++
++      if (hd->status_reg) {
++              regmap_read(regmap, hd->status_reg, &status);
++              if (!(status & BIT(hd->lock_bit))) {
++                      WARN(1, "HFPLL %s is ON, but not locked!\n",
++                                      __clk_get_name(hw->clk));
++                      clk_hfpll_disable(hw);
++                      __clk_hfpll_init_once(hw);
++              }
++      }
++}
++
++static int hfpll_is_enabled(struct clk_hw *hw)
++{
++      struct clk_hfpll *h = to_clk_hfpll(hw);
++      struct hfpll_data const *hd = h->d;
++      struct regmap *regmap = h->clkr.regmap;
++      u32 mode;
++
++      regmap_read(regmap, hd->mode_reg, &mode);
++      mode &= 0x7;
++      return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL);
++}
++
++const struct clk_ops clk_ops_hfpll = {
++      .enable = clk_hfpll_enable,
++      .disable = clk_hfpll_disable,
++      .is_enabled = hfpll_is_enabled,
++      .round_rate = clk_hfpll_round_rate,
++      .set_rate = clk_hfpll_set_rate,
++      .recalc_rate = clk_hfpll_recalc_rate,
++      .init = clk_hfpll_init,
++};
++EXPORT_SYMBOL_GPL(clk_ops_hfpll);
+--- /dev/null
++++ b/drivers/clk/qcom/clk-hfpll.h
+@@ -0,0 +1,54 @@
++/*
++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++#ifndef __QCOM_CLK_HFPLL_H__
++#define __QCOM_CLK_HFPLL_H__
++
++#include <linux/clk-provider.h>
++#include <linux/spinlock.h>
++#include "clk-regmap.h"
++
++struct hfpll_data {
++      u32 mode_reg;
++      u32 l_reg;
++      u32 m_reg;
++      u32 n_reg;
++      u32 user_reg;
++      u32 droop_reg;
++      u32 config_reg;
++      u32 status_reg;
++      u8  lock_bit;
++
++      u32 droop_val;
++      u32 config_val;
++      u32 user_val;
++      u32 user_vco_mask;
++      unsigned long low_vco_max_rate;
++
++      unsigned long min_rate;
++      unsigned long max_rate;
++};
++
++struct clk_hfpll {
++      struct hfpll_data const *d;
++      int init_done;
++
++      struct clk_regmap clkr;
++      spinlock_t lock;
++};
++
++#define to_clk_hfpll(_hw) \
++      container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr)
++
++extern const struct clk_ops clk_ops_hfpll;
++
++#endif
diff --git a/target/linux/ipq806x/patches-4.9/0039-clk-qcom-Add-HFPLL-driver.patch b/target/linux/ipq806x/patches-4.9/0039-clk-qcom-Add-HFPLL-driver.patch
new file mode 100644 (file)
index 0000000..92266f7
--- /dev/null
@@ -0,0 +1,195 @@
+From 23f680d03e5894f494572a5162d21328bd86890c Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 20 Mar 2015 23:45:25 -0700
+Subject: [PATCH 39/69] clk: qcom: Add HFPLL driver
+
+On some devices (MSM8974 for example), the HFPLLs are
+instantiated within the Krait processor subsystem as separate
+register regions. Add a driver for these PLLs so that we can
+provide HFPLL clocks for use by the system.
+
+Cc: <devicetree@vger.kernel.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
+---
+ .../devicetree/bindings/clock/qcom,hfpll.txt       |  40 ++++++++
+ drivers/clk/qcom/Kconfig                           |   8 ++
+ drivers/clk/qcom/Makefile                          |   1 +
+ drivers/clk/qcom/hfpll.c                           | 106 +++++++++++++++++++++
+ 4 files changed, 155 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/clock/qcom,hfpll.txt
+ create mode 100644 drivers/clk/qcom/hfpll.c
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/clock/qcom,hfpll.txt
+@@ -0,0 +1,40 @@
++High-Frequency PLL (HFPLL)
++
++PROPERTIES
++
++- compatible:
++      Usage: required
++      Value type: <string>
++      Definition: must be "qcom,hfpll"
++
++- reg:
++      Usage: required
++      Value type: <prop-encoded-array>
++      Definition: address and size of HPLL registers. An optional second
++                  element specifies the address and size of the alias
++                  register region.
++
++- clock-output-names:
++      Usage: required
++      Value type: <string>
++      Definition: Name of the PLL. Typically hfpllX where X is a CPU number
++                  starting at 0. Otherwise hfpll_Y where Y is more specific
++                  such as "l2".
++
++Example:
++
++1) An HFPLL for the L2 cache.
++
++      clock-controller@f9016000 {
++              compatible = "qcom,hfpll";
++              reg = <0xf9016000 0x30>;
++              clock-output-names = "hfpll_l2";
++      };
++
++2) An HFPLL for CPU0. This HFPLL has the alias register region.
++
++      clock-controller@f908a000 {
++              compatible = "qcom,hfpll";
++              reg = <0xf908a000 0x30>, <0xf900a000 0x30>;
++              clock-output-names = "hfpll0";
++      };
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -179,3 +179,11 @@ config MSM_MMCC_8996
+         Support for the multimedia clock controller on msm8996 devices.
+         Say Y if you want to support multimedia devices such as display,
+         graphics, video encode/decode, camera, etc.
++
++config QCOM_HFPLL
++      tristate "High-Frequency PLL (HFPLL) Clock Controller"
++      depends on COMMON_CLK_QCOM
++      help
++        Support for the high-frequency PLLs present on Qualcomm devices.
++        Say Y if you want to support CPU frequency scaling on devices
++        such as MSM8974, APQ8084, etc.
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -32,3 +32,4 @@ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8
+ obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
+ obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
+ obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
++obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
+--- /dev/null
++++ b/drivers/clk/qcom/hfpll.c
+@@ -0,0 +1,106 @@
++/*
++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/of.h>
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/regmap.h>
++
++#include "clk-regmap.h"
++#include "clk-hfpll.h"
++
++static const struct hfpll_data hdata = {
++      .mode_reg = 0x00,
++      .l_reg = 0x04,
++      .m_reg = 0x08,
++      .n_reg = 0x0c,
++      .user_reg = 0x10,
++      .config_reg = 0x14,
++      .config_val = 0x430405d,
++      .status_reg = 0x1c,
++      .lock_bit = 16,
++
++      .user_val = 0x8,
++      .user_vco_mask = 0x100000,
++      .low_vco_max_rate = 1248000000,
++      .min_rate = 537600000UL,
++      .max_rate = 2900000000UL,
++};
++
++static const struct of_device_id qcom_hfpll_match_table[] = {
++      { .compatible = "qcom,hfpll" },
++      { }
++};
++MODULE_DEVICE_TABLE(of, qcom_hfpll_match_table);
++
++static const struct regmap_config hfpll_regmap_config = {
++      .reg_bits       = 32,
++      .reg_stride     = 4,
++      .val_bits       = 32,
++      .max_register   = 0x30,
++      .fast_io        = true,
++};
++
++static int qcom_hfpll_probe(struct platform_device *pdev)
++{
++      struct resource *res;
++      struct device *dev = &pdev->dev;
++      void __iomem *base;
++      struct regmap *regmap;
++      struct clk_hfpll *h;
++      struct clk_init_data init = {
++              .parent_names = (const char *[]){ "xo" },
++              .num_parents = 1,
++              .ops = &clk_ops_hfpll,
++      };
++
++      h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL);
++      if (!h)
++              return -ENOMEM;
++
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      base = devm_ioremap_resource(dev, res);
++      if (IS_ERR(base))
++              return PTR_ERR(base);
++
++      regmap = devm_regmap_init_mmio(&pdev->dev, base, &hfpll_regmap_config);
++      if (IS_ERR(regmap))
++              return PTR_ERR(regmap);
++
++      if (of_property_read_string_index(dev->of_node, "clock-output-names",
++                                                0, &init.name))
++              return -ENODEV;
++
++      h->d = &hdata;
++      h->clkr.hw.init = &init;
++      spin_lock_init(&h->lock);
++
++      return devm_clk_register_regmap(&pdev->dev, &h->clkr);
++}
++
++static struct platform_driver qcom_hfpll_driver = {
++      .probe          = qcom_hfpll_probe,
++      .driver         = {
++              .name   = "qcom-hfpll",
++              .of_match_table = qcom_hfpll_match_table,
++      },
++};
++module_platform_driver(qcom_hfpll_driver);
++
++MODULE_DESCRIPTION("QCOM HFPLL Clock Driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:qcom-hfpll");
diff --git a/target/linux/ipq806x/patches-4.9/0040-clk-qcom-Add-IPQ806X-s-HFPLLs.patch b/target/linux/ipq806x/patches-4.9/0040-clk-qcom-Add-IPQ806X-s-HFPLLs.patch
new file mode 100644 (file)
index 0000000..8a51af7
--- /dev/null
@@ -0,0 +1,118 @@
+From 0dfdf84ee3982e88a62123b3de1c094d2c0829af Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 20 Mar 2015 23:45:27 -0700
+Subject: [PATCH 40/69] clk: qcom: Add IPQ806X's HFPLLs
+
+Describe the HFPLLs present on IPQ806X devices.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/qcom/gcc-ipq806x.c | 83 ++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 83 insertions(+)
+
+--- a/drivers/clk/qcom/gcc-ipq806x.c
++++ b/drivers/clk/qcom/gcc-ipq806x.c
+@@ -30,6 +30,7 @@
+ #include "clk-pll.h"
+ #include "clk-rcg.h"
+ #include "clk-branch.h"
++#include "clk-hfpll.h"
+ #include "reset.h"
+ static struct clk_pll pll0 = {
+@@ -113,6 +114,85 @@ static struct clk_regmap pll8_vote = {
+       },
+ };
++static struct hfpll_data hfpll0_data = {
++      .mode_reg = 0x3200,
++      .l_reg = 0x3208,
++      .m_reg = 0x320c,
++      .n_reg = 0x3210,
++      .config_reg = 0x3204,
++      .status_reg = 0x321c,
++      .config_val = 0x7845c665,
++      .droop_reg = 0x3214,
++      .droop_val = 0x0108c000,
++      .min_rate = 600000000UL,
++      .max_rate = 1800000000UL,
++};
++
++static struct clk_hfpll hfpll0 = {
++      .d = &hfpll0_data,
++      .clkr.hw.init = &(struct clk_init_data){
++              .parent_names = (const char *[]){ "pxo" },
++              .num_parents = 1,
++              .name = "hfpll0",
++              .ops = &clk_ops_hfpll,
++              .flags = CLK_IGNORE_UNUSED,
++      },
++      .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock),
++};
++
++static struct hfpll_data hfpll1_data = {
++      .mode_reg = 0x3240,
++      .l_reg = 0x3248,
++      .m_reg = 0x324c,
++      .n_reg = 0x3250,
++      .config_reg = 0x3244,
++      .status_reg = 0x325c,
++      .config_val = 0x7845c665,
++      .droop_reg = 0x3314,
++      .droop_val = 0x0108c000,
++      .min_rate = 600000000UL,
++      .max_rate = 1800000000UL,
++};
++
++static struct clk_hfpll hfpll1 = {
++      .d = &hfpll1_data,
++      .clkr.hw.init = &(struct clk_init_data){
++              .parent_names = (const char *[]){ "pxo" },
++              .num_parents = 1,
++              .name = "hfpll1",
++              .ops = &clk_ops_hfpll,
++              .flags = CLK_IGNORE_UNUSED,
++      },
++      .lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock),
++};
++
++static struct hfpll_data hfpll_l2_data = {
++      .mode_reg = 0x3300,
++      .l_reg = 0x3308,
++      .m_reg = 0x330c,
++      .n_reg = 0x3310,
++      .config_reg = 0x3304,
++      .status_reg = 0x331c,
++      .config_val = 0x7845c665,
++      .droop_reg = 0x3314,
++      .droop_val = 0x0108c000,
++      .min_rate = 600000000UL,
++      .max_rate = 1800000000UL,
++};
++
++static struct clk_hfpll hfpll_l2 = {
++      .d = &hfpll_l2_data,
++      .clkr.hw.init = &(struct clk_init_data){
++              .parent_names = (const char *[]){ "pxo" },
++              .num_parents = 1,
++              .name = "hfpll_l2",
++              .ops = &clk_ops_hfpll,
++              .flags = CLK_IGNORE_UNUSED,
++      },
++      .lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock),
++};
++
++
+ static struct clk_pll pll14 = {
+       .l_reg = 0x31c4,
+       .m_reg = 0x31c8,
+@@ -2801,6 +2881,9 @@ static struct clk_regmap *gcc_ipq806x_cl
+       [UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr,
+       [NSSTCM_CLK_SRC] = &nss_tcm_src.clkr,
+       [NSSTCM_CLK] = &nss_tcm_clk.clkr,
++      [PLL9] = &hfpll0.clkr,
++      [PLL10] = &hfpll1.clkr,
++      [PLL12] = &hfpll_l2.clkr,
+ };
+ static const struct qcom_reset_map gcc_ipq806x_resets[] = {
diff --git a/target/linux/ipq806x/patches-4.9/0041-clk-qcom-Add-support-for-Krait-clocks.patch b/target/linux/ipq806x/patches-4.9/0041-clk-qcom-Add-support-for-Krait-clocks.patch
new file mode 100644 (file)
index 0000000..902e49d
--- /dev/null
@@ -0,0 +1,263 @@
+From b9747125a8e7633ed2701a70e32dbb0442193774 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 20 Mar 2015 23:45:28 -0700
+Subject: [PATCH 41/69] clk: qcom: Add support for Krait clocks
+
+The Krait clocks are made up of a series of muxes and a divider
+that choose between a fixed rate clock and dedicated HFPLLs for
+each CPU. Instead of using mmio accesses to remux parents, the
+Krait implementation exposes the remux control via cp15
+registers. Support these clocks.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/qcom/Kconfig     |   4 ++
+ drivers/clk/qcom/Makefile    |   1 +
+ drivers/clk/qcom/clk-krait.c | 167 +++++++++++++++++++++++++++++++++++++++++++
+ drivers/clk/qcom/clk-krait.h |  49 +++++++++++++
+ 4 files changed, 221 insertions(+)
+ create mode 100644 drivers/clk/qcom/clk-krait.c
+ create mode 100644 drivers/clk/qcom/clk-krait.h
+
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -187,3 +187,7 @@ config QCOM_HFPLL
+         Support for the high-frequency PLLs present on Qualcomm devices.
+         Say Y if you want to support CPU frequency scaling on devices
+         such as MSM8974, APQ8084, etc.
++
++config KRAIT_CLOCKS
++      bool
++      select KRAIT_L2_ACCESSORS
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -9,6 +9,7 @@ clk-qcom-y += clk-rcg2.o
+ clk-qcom-y += clk-branch.o
+ clk-qcom-y += clk-regmap-divider.o
+ clk-qcom-y += clk-regmap-mux.o
++clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
+ clk-qcom-y += clk-hfpll.o
+ clk-qcom-y += reset.o
+ clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
+--- /dev/null
++++ b/drivers/clk/qcom/clk-krait.c
+@@ -0,0 +1,167 @@
++/*
++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/io.h>
++#include <linux/delay.h>
++#include <linux/err.h>
++#include <linux/clk-provider.h>
++#include <linux/spinlock.h>
++
++#include <asm/krait-l2-accessors.h>
++
++#include "clk-krait.h"
++
++/* Secondary and primary muxes share the same cp15 register */
++static DEFINE_SPINLOCK(krait_clock_reg_lock);
++
++#define LPL_SHIFT     8
++static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel)
++{
++      unsigned long flags;
++      u32 regval;
++
++      spin_lock_irqsave(&krait_clock_reg_lock, flags);
++      regval = krait_get_l2_indirect_reg(mux->offset);
++      regval &= ~(mux->mask << mux->shift);
++      regval |= (sel & mux->mask) << mux->shift;
++      if (mux->lpl) {
++              regval &= ~(mux->mask << (mux->shift + LPL_SHIFT));
++              regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT);
++      }
++      krait_set_l2_indirect_reg(mux->offset, regval);
++      spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
++
++      /* Wait for switch to complete. */
++      mb();
++      udelay(1);
++}
++
++static int krait_mux_set_parent(struct clk_hw *hw, u8 index)
++{
++      struct krait_mux_clk *mux = to_krait_mux_clk(hw);
++      u32 sel;
++
++      sel = clk_mux_reindex(index, mux->parent_map, 0);
++      mux->en_mask = sel;
++      /* Don't touch mux if CPU is off as it won't work */
++      if (__clk_is_enabled(hw->clk))
++              __krait_mux_set_sel(mux, sel);
++      return 0;
++}
++
++static u8 krait_mux_get_parent(struct clk_hw *hw)
++{
++      struct krait_mux_clk *mux = to_krait_mux_clk(hw);
++      u32 sel;
++
++      sel = krait_get_l2_indirect_reg(mux->offset);
++      sel >>= mux->shift;
++      sel &= mux->mask;
++      mux->en_mask = sel;
++
++      return clk_mux_get_parent(hw, sel, mux->parent_map, 0);
++}
++
++static struct clk_hw *krait_mux_get_safe_parent(struct clk_hw *hw,
++                                              unsigned long *safe_freq)
++{
++      int i;
++      struct krait_mux_clk *mux = to_krait_mux_clk(hw);
++      int num_parents = clk_hw_get_num_parents(hw);
++
++      i = mux->safe_sel;
++      for (i = 0; i < num_parents; i++)
++              if (mux->safe_sel == mux->parent_map[i])
++                      break;
++
++      return clk_hw_get_parent_by_index(hw, i);
++}
++
++static int krait_mux_enable(struct clk_hw *hw)
++{
++      struct krait_mux_clk *mux = to_krait_mux_clk(hw);
++
++      __krait_mux_set_sel(mux, mux->en_mask);
++
++      return 0;
++}
++
++static void krait_mux_disable(struct clk_hw *hw)
++{
++      struct krait_mux_clk *mux = to_krait_mux_clk(hw);
++
++      __krait_mux_set_sel(mux, mux->safe_sel);
++}
++
++const struct clk_ops krait_mux_clk_ops = {
++      .enable = krait_mux_enable,
++      .disable = krait_mux_disable,
++      .set_parent = krait_mux_set_parent,
++      .get_parent = krait_mux_get_parent,
++      .determine_rate = __clk_mux_determine_rate_closest,
++      .get_safe_parent = krait_mux_get_safe_parent,
++};
++EXPORT_SYMBOL_GPL(krait_mux_clk_ops);
++
++/* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */
++static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate,
++                                unsigned long *parent_rate)
++{
++      *parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), rate * 2);
++      return DIV_ROUND_UP(*parent_rate, 2);
++}
++
++static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate,
++                      unsigned long parent_rate)
++{
++      struct krait_div2_clk *d = to_krait_div2_clk(hw);
++      unsigned long flags;
++      u32 val;
++      u32 mask = BIT(d->width) - 1;
++
++      if (d->lpl)
++              mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift;
++
++      spin_lock_irqsave(&krait_clock_reg_lock, flags);
++      val = krait_get_l2_indirect_reg(d->offset);
++      val &= ~mask;
++      krait_set_l2_indirect_reg(d->offset, val);
++      spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
++
++      return 0;
++}
++
++static unsigned long
++krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
++{
++      struct krait_div2_clk *d = to_krait_div2_clk(hw);
++      u32 mask = BIT(d->width) - 1;
++      u32 div;
++
++      div = krait_get_l2_indirect_reg(d->offset);
++      div >>= d->shift;
++      div &= mask;
++      div = (div + 1) * 2;
++
++      return DIV_ROUND_UP(parent_rate, div);
++}
++
++const struct clk_ops krait_div2_clk_ops = {
++      .round_rate = krait_div2_round_rate,
++      .set_rate = krait_div2_set_rate,
++      .recalc_rate = krait_div2_recalc_rate,
++};
++EXPORT_SYMBOL_GPL(krait_div2_clk_ops);
+--- /dev/null
++++ b/drivers/clk/qcom/clk-krait.h
+@@ -0,0 +1,49 @@
++/*
++ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#ifndef __QCOM_CLK_KRAIT_H
++#define __QCOM_CLK_KRAIT_H
++
++#include <linux/clk-provider.h>
++
++struct krait_mux_clk {
++      unsigned int    *parent_map;
++      bool            has_safe_parent;
++      u8              safe_sel;
++      u32             offset;
++      u32             mask;
++      u32             shift;
++      u32             en_mask;
++      bool            lpl;
++
++      struct clk_hw   hw;
++};
++
++#define to_krait_mux_clk(_hw) container_of(_hw, struct krait_mux_clk, hw)
++
++extern const struct clk_ops krait_mux_clk_ops;
++
++struct krait_div2_clk {
++      u32             offset;
++      u8              width;
++      u32             shift;
++      bool            lpl;
++
++      struct clk_hw   hw;
++};
++
++#define to_krait_div2_clk(_hw) container_of(_hw, struct krait_div2_clk, hw)
++
++extern const struct clk_ops krait_div2_clk_ops;
++
++#endif
diff --git a/target/linux/ipq806x/patches-4.9/0042-clk-qcom-Add-KPSS-ACC-GCC-driver.patch b/target/linux/ipq806x/patches-4.9/0042-clk-qcom-Add-KPSS-ACC-GCC-driver.patch
new file mode 100644 (file)
index 0000000..c499436
--- /dev/null
@@ -0,0 +1,196 @@
+From 6039eb63fabdd6871fc70940aa98102665c78eed Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 20 Mar 2015 23:45:29 -0700
+Subject: [PATCH 42/69] clk: qcom: Add KPSS ACC/GCC driver
+
+The ACC and GCC regions present in KPSSv1 contain registers to
+control clocks and power to each Krait CPU and L2. For CPUfreq
+purposes probe these devices and expose a mux clock that chooses
+between PXO and PLL8.
+
+Cc: <devicetree@vger.kernel.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ .../devicetree/bindings/arm/msm/qcom,kpss-acc.txt  |  7 ++
+ .../devicetree/bindings/arm/msm/qcom,kpss-gcc.txt  | 28 +++++++
+ drivers/clk/qcom/Kconfig                           |  8 ++
+ drivers/clk/qcom/Makefile                          |  1 +
+ drivers/clk/qcom/kpss-xcc.c                        | 95 ++++++++++++++++++++++
+ 5 files changed, 139 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
+ create mode 100644 drivers/clk/qcom/kpss-xcc.c
+
+--- a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
++++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
+@@ -21,10 +21,17 @@ PROPERTIES
+                   the register region. An optional second element specifies
+                   the base address and size of the alias register region.
++- clock-output-names:
++      Usage: optional
++      Value type: <string>
++      Definition: Name of the output clock. Typically acpuX_aux where X is a
++                  CPU number starting at 0.
++
+ Example:
+       clock-controller@2088000 {
+               compatible = "qcom,kpss-acc-v2";
+               reg = <0x02088000 0x1000>,
+                     <0x02008000 0x1000>;
++              clock-output-names = "acpu0_aux";
+       };
+--- /dev/null
++++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
+@@ -0,0 +1,28 @@
++Krait Processor Sub-system (KPSS) Global Clock Controller (GCC)
++
++PROPERTIES
++
++- compatible:
++      Usage: required
++      Value type: <string>
++      Definition: should be one of:
++                      "qcom,kpss-gcc"
++
++- reg:
++      Usage: required
++      Value type: <prop-encoded-array>
++      Definition: base address and size of the register region
++
++- clock-output-names:
++      Usage: required
++      Value type: <string>
++      Definition: Name of the output clock. Typically acpu_l2_aux indicating
++                  an L2 cache auxiliary clock.
++
++Example:
++
++      l2cc: clock-controller@2011000 {
++              compatible = "qcom,kpss-gcc";
++              reg = <0x2011000 0x1000>;
++              clock-output-names = "acpu_l2_aux";
++      };
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -188,6 +188,14 @@ config QCOM_HFPLL
+         Say Y if you want to support CPU frequency scaling on devices
+         such as MSM8974, APQ8084, etc.
++config KPSS_XCC
++      tristate "KPSS Clock Controller"
++      depends on COMMON_CLK_QCOM
++      help
++        Support for the Krait ACC and GCC clock controllers. Say Y
++        if you want to support CPU frequency scaling on devices such
++        as MSM8960, APQ8064, etc.
++
+ config KRAIT_CLOCKS
+       bool
+       select KRAIT_L2_ACCESSORS
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -33,4 +33,5 @@ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8
+ obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
+ obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
+ obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
++obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
+ obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
+--- /dev/null
++++ b/drivers/clk/qcom/kpss-xcc.c
+@@ -0,0 +1,95 @@
++/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++
++static const char *aux_parents[] = {
++      "pll8_vote",
++      "pxo",
++};
++
++static unsigned int aux_parent_map[] = {
++      3,
++      0,
++};
++
++static const struct of_device_id kpss_xcc_match_table[] = {
++      { .compatible = "qcom,kpss-acc-v1", .data = (void *)1UL },
++      { .compatible = "qcom,kpss-gcc" },
++      {}
++};
++MODULE_DEVICE_TABLE(of, kpss_xcc_match_table);
++
++static int kpss_xcc_driver_probe(struct platform_device *pdev)
++{
++      const struct of_device_id *id;
++      struct clk *clk;
++      struct resource *res;
++      void __iomem *base;
++      const char *name;
++
++      id = of_match_device(kpss_xcc_match_table, &pdev->dev);
++      if (!id)
++              return -ENODEV;
++
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      base = devm_ioremap_resource(&pdev->dev, res);
++      if (IS_ERR(base))
++              return PTR_ERR(base);
++
++      if (id->data) {
++              if (of_property_read_string_index(pdev->dev.of_node,
++                                      "clock-output-names", 0, &name))
++                      return -ENODEV;
++              base += 0x14;
++      } else {
++              name = "acpu_l2_aux";
++              base += 0x28;
++      }
++
++      clk = clk_register_mux_table(&pdev->dev, name, aux_parents,
++                                   ARRAY_SIZE(aux_parents), 0, base, 0, 0x3,
++                                   0, aux_parent_map, NULL);
++
++      platform_set_drvdata(pdev, clk);
++
++      return PTR_ERR_OR_ZERO(clk);
++}
++
++static int kpss_xcc_driver_remove(struct platform_device *pdev)
++{
++      clk_unregister_mux(platform_get_drvdata(pdev));
++      return 0;
++}
++
++static struct platform_driver kpss_xcc_driver = {
++      .probe = kpss_xcc_driver_probe,
++      .remove = kpss_xcc_driver_remove,
++      .driver = {
++              .name = "kpss-xcc",
++              .of_match_table = kpss_xcc_match_table,
++      },
++};
++module_platform_driver(kpss_xcc_driver);
++
++MODULE_DESCRIPTION("Krait Processor Sub System (KPSS) Clock Driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:kpss-xcc");
diff --git a/target/linux/ipq806x/patches-4.9/0043-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches-4.9/0043-clk-qcom-Add-Krait-clock-controller-driver.patch
new file mode 100644 (file)
index 0000000..7e2e41e
--- /dev/null
@@ -0,0 +1,426 @@
+From 7fb5976eb0231a06f484a6bde5e5fbfee7ee4f4a Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 20 Mar 2015 23:45:30 -0700
+Subject: [PATCH 43/69] clk: qcom: Add Krait clock controller driver
+
+The Krait CPU clocks are made up of a primary mux and secondary
+mux for each CPU and the L2, controlled via cp15 accessors. For
+Kraits within KPSSv1 each secondary mux accepts a different aux
+source, but on KPSSv2 each secondary mux accepts the same aux
+source.
+
+Cc: <devicetree@vger.kernel.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ .../devicetree/bindings/clock/qcom,krait-cc.txt    |  22 ++
+ drivers/clk/qcom/Kconfig                           |   8 +
+ drivers/clk/qcom/Makefile                          |   1 +
+ drivers/clk/qcom/krait-cc.c                        | 352 +++++++++++++++++++++
+ 4 files changed, 383 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
+ create mode 100644 drivers/clk/qcom/krait-cc.c
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
+@@ -0,0 +1,22 @@
++Krait Clock Controller
++
++PROPERTIES
++
++- compatible:
++      Usage: required
++      Value type: <string>
++      Definition: must be one of:
++                      "qcom,krait-cc-v1"
++                      "qcom,krait-cc-v2"
++
++- #clock-cells:
++      Usage: required
++      Value type: <u32>
++      Definition: must be 1
++
++Example:
++
++      kraitcc: clock-controller {
++              compatible = "qcom,krait-cc-v1";
++              #clock-cells = <1>;
++      };
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -196,6 +196,14 @@ config KPSS_XCC
+         if you want to support CPU frequency scaling on devices such
+         as MSM8960, APQ8064, etc.
++config KRAITCC
++      tristate "Krait Clock Controller"
++      depends on COMMON_CLK_QCOM && ARM
++      select KRAIT_CLOCKS
++      help
++        Support for the Krait CPU clocks on Qualcomm devices.
++        Say Y if you want to support CPU frequency scaling.
++
+ config KRAIT_CLOCKS
+       bool
+       select KRAIT_L2_ACCESSORS
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -35,3 +35,4 @@ obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
+ obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
+ obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
+ obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
++obj-$(CONFIG_KRAITCC) += krait-cc.o
+--- /dev/null
++++ b/drivers/clk/qcom/krait-cc.c
+@@ -0,0 +1,352 @@
++/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/slab.h>
++
++#include "clk-krait.h"
++
++static unsigned int sec_mux_map[] = {
++      2,
++      0,
++};
++
++static unsigned int pri_mux_map[] = {
++      1,
++      2,
++      0,
++};
++
++static int
++krait_add_div(struct device *dev, int id, const char *s, unsigned offset)
++{
++      struct krait_div2_clk *div;
++      struct clk_init_data init = {
++              .num_parents = 1,
++              .ops = &krait_div2_clk_ops,
++              .flags = CLK_SET_RATE_PARENT,
++      };
++      const char *p_names[1];
++      struct clk *clk;
++
++      div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
++      if (!div)
++              return -ENOMEM;
++
++      div->width = 2;
++      div->shift = 6;
++      div->lpl = id >= 0;
++      div->offset = offset;
++      div->hw.init = &init;
++
++      init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
++      if (!init.name)
++              return -ENOMEM;
++
++      init.parent_names = p_names;
++      p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
++      if (!p_names[0]) {
++              kfree(init.name);
++              return -ENOMEM;
++      }
++
++      clk = devm_clk_register(dev, &div->hw);
++      kfree(p_names[0]);
++      kfree(init.name);
++
++      return PTR_ERR_OR_ZERO(clk);
++}
++
++static int
++krait_add_sec_mux(struct device *dev, int id, const char *s, unsigned offset,
++                bool unique_aux)
++{
++      struct krait_mux_clk *mux;
++      static const char *sec_mux_list[] = {
++              "acpu_aux",
++              "qsb",
++      };
++      struct clk_init_data init = {
++              .parent_names = sec_mux_list,
++              .num_parents = ARRAY_SIZE(sec_mux_list),
++              .ops = &krait_mux_clk_ops,
++              .flags = CLK_SET_RATE_PARENT,
++      };
++      struct clk *clk;
++
++      mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
++      if (!mux)
++              return -ENOMEM;
++
++      mux->offset = offset;
++      mux->lpl = id >= 0;
++      mux->has_safe_parent = true;
++      mux->safe_sel = 2;
++      mux->mask = 0x3;
++      mux->shift = 2;
++      mux->parent_map = sec_mux_map;
++      mux->hw.init = &init;
++
++      init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
++      if (!init.name)
++              return -ENOMEM;
++
++      if (unique_aux) {
++              sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s);
++              if (!sec_mux_list[0]) {
++                      clk = ERR_PTR(-ENOMEM);
++                      goto err_aux;
++              }
++      }
++
++      clk = devm_clk_register(dev, &mux->hw);
++
++      if (unique_aux)
++              kfree(sec_mux_list[0]);
++err_aux:
++      kfree(init.name);
++      return PTR_ERR_OR_ZERO(clk);
++}
++
++static struct clk *
++krait_add_pri_mux(struct device *dev, int id, const char *s, unsigned offset)
++{
++      struct krait_mux_clk *mux;
++      const char *p_names[3];
++      struct clk_init_data init = {
++              .parent_names = p_names,
++              .num_parents = ARRAY_SIZE(p_names),
++              .ops = &krait_mux_clk_ops,
++              .flags = CLK_SET_RATE_PARENT,
++      };
++      struct clk *clk;
++
++      mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
++      if (!mux)
++              return ERR_PTR(-ENOMEM);
++
++      mux->has_safe_parent = true;
++      mux->safe_sel = 0;
++      mux->mask = 0x3;
++      mux->shift = 0;
++      mux->offset = offset;
++      mux->lpl = id >= 0;
++      mux->parent_map = pri_mux_map;
++      mux->hw.init = &init;
++
++      init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s);
++      if (!init.name)
++              return ERR_PTR(-ENOMEM);
++
++      p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
++      if (!p_names[0]) {
++              clk = ERR_PTR(-ENOMEM);
++              goto err_p0;
++      }
++
++      p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
++      if (!p_names[1]) {
++              clk = ERR_PTR(-ENOMEM);
++              goto err_p1;
++      }
++
++      p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
++      if (!p_names[2]) {
++              clk = ERR_PTR(-ENOMEM);
++              goto err_p2;
++      }
++
++      clk = devm_clk_register(dev, &mux->hw);
++
++      kfree(p_names[2]);
++err_p2:
++      kfree(p_names[1]);
++err_p1:
++      kfree(p_names[0]);
++err_p0:
++      kfree(init.name);
++      return clk;
++}
++
++/* id < 0 for L2, otherwise id == physical CPU number */
++static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux)
++{
++      int ret;
++      unsigned offset;
++      void *p = NULL;
++      const char *s;
++      struct clk *clk;
++
++      if (id >= 0) {
++              offset = 0x4501 + (0x1000 * id);
++              s = p = kasprintf(GFP_KERNEL, "%d", id);
++              if (!s)
++                      return ERR_PTR(-ENOMEM);
++      } else {
++              offset = 0x500;
++              s = "_l2";
++      }
++
++      ret = krait_add_div(dev, id, s, offset);
++      if (ret) {
++              clk = ERR_PTR(ret);
++              goto err;
++      }
++
++      ret = krait_add_sec_mux(dev, id, s, offset, unique_aux);
++      if (ret) {
++              clk = ERR_PTR(ret);
++              goto err;
++      }
++
++      clk = krait_add_pri_mux(dev, id, s, offset);
++err:
++      kfree(p);
++      return clk;
++}
++
++static struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data)
++{
++      unsigned int idx = clkspec->args[0];
++      struct clk **clks = data;
++
++      if (idx >= 5) {
++              pr_err("%s: invalid clock index %d\n", __func__, idx);
++              return ERR_PTR(-EINVAL);
++      }
++
++      return clks[idx] ? : ERR_PTR(-ENODEV);
++}
++
++static const struct of_device_id krait_cc_match_table[] = {
++      { .compatible = "qcom,krait-cc-v1", (void *)1UL },
++      { .compatible = "qcom,krait-cc-v2" },
++      {}
++};
++MODULE_DEVICE_TABLE(of, krait_cc_match_table);
++
++static int krait_cc_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      const struct of_device_id *id;
++      unsigned long cur_rate, aux_rate;
++      int cpu;
++      struct clk *clk;
++      struct clk **clks;
++      struct clk *l2_pri_mux_clk;
++
++      id = of_match_device(krait_cc_match_table, dev);
++      if (!id)
++              return -ENODEV;
++
++      /* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */
++      clk = clk_register_fixed_rate(dev, "qsb", NULL, CLK_IS_ROOT, 1);
++      if (IS_ERR(clk))
++              return PTR_ERR(clk);
++
++      if (!id->data) {
++              clk = clk_register_fixed_factor(dev, "acpu_aux",
++                                              "gpll0_vote", 0, 1, 2);
++              if (IS_ERR(clk))
++                      return PTR_ERR(clk);
++      }
++
++      /* Krait configurations have at most 4 CPUs and one L2 */
++      clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL);
++      if (!clks)
++              return -ENOMEM;
++
++      for_each_possible_cpu(cpu) {
++              clk = krait_add_clks(dev, cpu, id->data);
++              if (IS_ERR(clk))
++                      return PTR_ERR(clk);
++              clks[cpu] = clk;
++      }
++
++      l2_pri_mux_clk = krait_add_clks(dev, -1, id->data);
++      if (IS_ERR(l2_pri_mux_clk))
++              return PTR_ERR(l2_pri_mux_clk);
++      clks[4] = l2_pri_mux_clk;
++
++      /*
++       * We don't want the CPU or L2 clocks to be turned off at late init
++       * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the
++       * refcount of these clocks. Any cpufreq/hotplug manager can assume
++       * that the clocks have already been prepared and enabled by the time
++       * they take over.
++       */
++      for_each_online_cpu(cpu) {
++              clk_prepare_enable(l2_pri_mux_clk);
++              WARN(clk_prepare_enable(clks[cpu]),
++                      "Unable to turn on CPU%d clock", cpu);
++      }
++
++      /*
++       * Force reinit of HFPLLs and muxes to overwrite any potential
++       * incorrect configuration of HFPLLs and muxes by the bootloader.
++       * While at it, also make sure the cores are running at known rates
++       * and print the current rate.
++       *
++       * The clocks are set to aux clock rate first to make sure the
++       * secondary mux is not sourcing off of QSB. The rate is then set to
++       * two different rates to force a HFPLL reinit under all
++       * circumstances.
++       */
++      cur_rate = clk_get_rate(l2_pri_mux_clk);
++      aux_rate = 384000000;
++      if (cur_rate == 1) {
++              pr_info("L2 @ QSB rate. Forcing new rate.\n");
++              cur_rate = aux_rate;
++      }
++      clk_set_rate(l2_pri_mux_clk, aux_rate);
++      clk_set_rate(l2_pri_mux_clk, 2);
++      clk_set_rate(l2_pri_mux_clk, cur_rate);
++      pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000);
++      for_each_possible_cpu(cpu) {
++              clk = clks[cpu];
++              cur_rate = clk_get_rate(clk);
++              if (cur_rate == 1) {
++                      pr_info("CPU%d @ QSB rate. Forcing new rate.\n", cpu);
++                      cur_rate = aux_rate;
++              }
++              clk_set_rate(clk, aux_rate);
++              clk_set_rate(clk, 2);
++              clk_set_rate(clk, cur_rate);
++              pr_info("CPU%d @ %lu KHz\n", cpu, clk_get_rate(clk) / 1000);
++      }
++
++      of_clk_add_provider(dev->of_node, krait_of_get, clks);
++
++      return 0;
++}
++
++static struct platform_driver krait_cc_driver = {
++      .probe = krait_cc_probe,
++      .driver = {
++              .name = "krait-cc",
++              .of_match_table = krait_cc_match_table,
++      },
++};
++module_platform_driver(krait_cc_driver);
++
++MODULE_DESCRIPTION("Krait CPU Clock Driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:krait-cc");
diff --git a/target/linux/ipq806x/patches-4.9/0044-clk-qcom-krait-Remove-CLK_IS_ROOT.patch b/target/linux/ipq806x/patches-4.9/0044-clk-qcom-krait-Remove-CLK_IS_ROOT.patch
new file mode 100644 (file)
index 0000000..c3761ee
--- /dev/null
@@ -0,0 +1,23 @@
+From 58f8215f1d9397f9130657cc2c15a956bd99210e Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <georgi.djakov@linaro.org>
+Date: Wed, 13 Jul 2016 15:22:25 +0300
+Subject: [PATCH 44/69] clk: qcom: krait: Remove CLK_IS_ROOT
+
+The flag CLK_IS_ROOT is no-op now. Remove it.
+
+Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
+---
+ drivers/clk/qcom/krait-cc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/clk/qcom/krait-cc.c
++++ b/drivers/clk/qcom/krait-cc.c
+@@ -258,7 +258,7 @@ static int krait_cc_probe(struct platfor
+               return -ENODEV;
+       /* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */
+-      clk = clk_register_fixed_rate(dev, "qsb", NULL, CLK_IS_ROOT, 1);
++      clk = clk_register_fixed_rate(dev, "qsb", NULL, 0, 1);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
diff --git a/target/linux/ipq806x/patches-4.9/0045-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch b/target/linux/ipq806x/patches-4.9/0045-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch
new file mode 100644 (file)
index 0000000..f5b5ea0
--- /dev/null
@@ -0,0 +1,295 @@
+From 42eea6bc2858ab9649cf6931455e391e48939685 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 20 Mar 2015 23:45:31 -0700
+Subject: [PATCH 45/69] cpufreq: Add module to register cpufreq on Krait CPUs
+
+Register a cpufreq-generic device whenever we detect that a
+"qcom,krait" compatible CPU is present in DT.
+
+Cc: <devicetree@vger.kernel.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ .../devicetree/bindings/arm/msm/qcom,pvs.txt       |  38 ++++
+ drivers/cpufreq/Kconfig.arm                        |   9 +
+ drivers/cpufreq/Makefile                           |   1 +
+ drivers/cpufreq/qcom-cpufreq.c                     | 204 +++++++++++++++++++++
+ 4 files changed, 252 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
+ create mode 100644 drivers/cpufreq/qcom-cpufreq.c
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
+@@ -0,0 +1,38 @@
++Qualcomm Process Voltage Scaling Tables
++
++The node name is required to be "qcom,pvs". There shall only be one
++such node present in the root of the tree.
++
++PROPERTIES
++
++- qcom,pvs-format-a or qcom,pvs-format-b:
++      Usage: required
++      Value type: <empty>
++      Definition: Indicates the format of qcom,speedX-pvsY-bin-vZ properties.
++                  If qcom,pvs-format-a is used the table is two columns
++                  (frequency and voltage in that order). If qcom,pvs-format-b                     is used the table is three columns (frequency, voltage,
++                  and current in that order).
++
++- qcom,speedX-pvsY-bin-vZ:
++      Usage: required
++      Value type: <prop-encoded-array>
++      Definition: The PVS table corresponding to the speed bin X, pvs bin Y,
++                  and version Z.
++Example:
++
++      qcom,pvs {
++              qcom,pvs-format-a;
++              qcom,speed0-pvs0-bin-v0 =
++                      <  384000000  950000 >,
++                      <  486000000  975000 >,
++                      <  594000000 1000000 >,
++                      <  702000000 1025000 >,
++                      <  810000000 1075000 >,
++                      <  918000000 1100000 >,
++                      < 1026000000 1125000 >,
++                      < 1134000000 1175000 >,
++                      < 1242000000 1200000 >,
++                      < 1350000000 1225000 >,
++                      < 1458000000 1237500 >,
++                      < 1512000000 1250000 >;
++      };
+--- a/drivers/cpufreq/Kconfig.arm
++++ b/drivers/cpufreq/Kconfig.arm
+@@ -88,6 +88,15 @@ config ARM_OMAP2PLUS_CPUFREQ
+       depends on ARCH_OMAP2PLUS
+       default ARCH_OMAP2PLUS
++config ARM_QCOM_CPUFREQ
++      tristate "Qualcomm based"
++      depends on ARCH_QCOM
++      select PM_OPP
++      help
++        This adds the CPUFreq driver for Qualcomm SoC based boards.
++
++        If in doubt, say N.
++
+ config ARM_S3C_CPUFREQ
+       bool
+       help
+--- a/drivers/cpufreq/Makefile
++++ b/drivers/cpufreq/Makefile
+@@ -62,6 +62,7 @@ obj-$(CONFIG_ARM_MT8173_CPUFREQ)     += mt81
+ obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ)   += omap-cpufreq.o
+ obj-$(CONFIG_ARM_PXA2xx_CPUFREQ)      += pxa2xx-cpufreq.o
+ obj-$(CONFIG_PXA3xx)                  += pxa3xx-cpufreq.o
++obj-$(CONFIG_ARM_QCOM_CPUFREQ)                += qcom-cpufreq.o
+ obj-$(CONFIG_ARM_S3C24XX_CPUFREQ)     += s3c24xx-cpufreq.o
+ obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o
+ obj-$(CONFIG_ARM_S3C2410_CPUFREQ)     += s3c2410-cpufreq.o
+--- /dev/null
++++ b/drivers/cpufreq/qcom-cpufreq.c
+@@ -0,0 +1,204 @@
++/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/cpu.h>
++#include <linux/err.h>
++#include <linux/init.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pm_opp.h>
++#include <linux/slab.h>
++#include <linux/cpufreq-dt.h>
++
++static void __init get_krait_bin_format_a(int *speed, int *pvs, int *pvs_ver)
++{
++      void __iomem *base;
++      u32 pte_efuse;
++
++      *speed = *pvs = *pvs_ver = 0;
++
++      base = ioremap(0x007000c0, 4);
++      if (!base) {
++              pr_warn("Unable to read efuse data. Defaulting to 0!\n");
++              return;
++      }
++
++      pte_efuse = readl_relaxed(base);
++      iounmap(base);
++
++      *speed = pte_efuse & 0xf;
++      if (*speed == 0xf)
++              *speed = (pte_efuse >> 4) & 0xf;
++
++      if (*speed == 0xf) {
++              *speed = 0;
++              pr_warn("Speed bin: Defaulting to %d\n", *speed);
++      } else {
++              pr_info("Speed bin: %d\n", *speed);
++      }
++
++      *pvs = (pte_efuse >> 10) & 0x7;
++      if (*pvs == 0x7)
++              *pvs = (pte_efuse >> 13) & 0x7;
++
++      if (*pvs == 0x7) {
++              *pvs = 0;
++              pr_warn("PVS bin: Defaulting to %d\n", *pvs);
++      } else {
++              pr_info("PVS bin: %d\n", *pvs);
++      }
++}
++
++static void __init get_krait_bin_format_b(int *speed, int *pvs, int *pvs_ver)
++{
++      u32 pte_efuse, redundant_sel;
++      void __iomem *base;
++
++      *speed = 0;
++      *pvs = 0;
++      *pvs_ver = 0;
++
++      base = ioremap(0xfc4b80b0, 8);
++      if (!base) {
++              pr_warn("Unable to read efuse data. Defaulting to 0!\n");
++              return;
++      }
++
++      pte_efuse = readl_relaxed(base);
++      redundant_sel = (pte_efuse >> 24) & 0x7;
++      *speed = pte_efuse & 0x7;
++      /* 4 bits of PVS are in efuse register bits 31, 8-6. */
++      *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7);
++      *pvs_ver = (pte_efuse >> 4) & 0x3;
++
++      switch (redundant_sel) {
++      case 1:
++              *speed = (pte_efuse >> 27) & 0xf;
++              break;
++      case 2:
++              *pvs = (pte_efuse >> 27) & 0xf;
++              break;
++      }
++
++      /* Check SPEED_BIN_BLOW_STATUS */
++      if (pte_efuse & BIT(3)) {
++              pr_info("Speed bin: %d\n", *speed);
++      } else {
++              pr_warn("Speed bin not set. Defaulting to 0!\n");
++              *speed = 0;
++      }
++
++      /* Check PVS_BLOW_STATUS */
++      pte_efuse = readl_relaxed(base + 0x4) & BIT(21);
++      if (pte_efuse) {
++              pr_info("PVS bin: %d\n", *pvs);
++      } else {
++              pr_warn("PVS bin not set. Defaulting to 0!\n");
++              *pvs = 0;
++      }
++
++      pr_info("PVS version: %d\n", *pvs_ver);
++      iounmap(base);
++}
++
++static int __init qcom_cpufreq_populate_opps(void)
++{
++      int len, rows, cols, i, k, speed, pvs, pvs_ver;
++      char table_name[] = "qcom,speedXX-pvsXX-bin-vXX";
++      struct device_node *np;
++      struct device *dev;
++      int cpu = 0;
++
++      np = of_find_node_by_name(NULL, "qcom,pvs");
++      if (!np)
++              return -ENODEV;
++
++      if (of_property_read_bool(np, "qcom,pvs-format-a")) {
++              get_krait_bin_format_a(&speed, &pvs, &pvs_ver);
++              cols = 2;
++      } else if (of_property_read_bool(np, "qcom,pvs-format-b")) {
++              get_krait_bin_format_b(&speed, &pvs, &pvs_ver);
++              cols = 3;
++      } else {
++              return -ENODEV;
++      }
++
++      snprintf(table_name, sizeof(table_name),
++                      "qcom,speed%d-pvs%d-bin-v%d", speed, pvs, pvs_ver);
++
++      if (!of_find_property(np, table_name, &len))
++              return -EINVAL;
++
++      len /= sizeof(u32);
++      if (len % cols || len == 0)
++              return -EINVAL;
++
++      rows = len / cols;
++
++      for (i = 0, k = 0; i < rows; i++) {
++              u32 freq, volt;
++
++              of_property_read_u32_index(np, table_name, k++, &freq);
++              of_property_read_u32_index(np, table_name, k++, &volt);
++              while (k % cols)
++                      k++; /* Skip uA entries if present */
++              for (cpu = 0; cpu < num_possible_cpus(); cpu++) {
++                      dev = get_cpu_device(cpu);
++                      if (!dev)
++                              return -ENODEV;
++                      if (dev_pm_opp_add(dev, freq, volt))
++                              pr_warn("failed to add OPP %u\n", freq);
++              }
++      }
++
++      return 0;
++}
++
++static int __init qcom_cpufreq_driver_init(void)
++{
++      struct cpufreq_dt_platform_data pdata = { .independent_clocks = true };
++      struct platform_device_info devinfo = {
++              .name = "cpufreq-dt",
++              .data = &pdata,
++              .size_data = sizeof(pdata),
++      };
++      struct device *cpu_dev;
++      struct device_node *np;
++      int ret;
++
++      cpu_dev = get_cpu_device(0);
++      if (!cpu_dev)
++              return -ENODEV;
++
++      np = of_node_get(cpu_dev->of_node);
++      if (!np)
++              return -ENOENT;
++
++      if (!of_device_is_compatible(np, "qcom,krait")) {
++              of_node_put(np);
++              return -ENODEV;
++      }
++      of_node_put(np);
++
++      ret = qcom_cpufreq_populate_opps();
++      if (ret)
++              return ret;
++
++      return PTR_ERR_OR_ZERO(platform_device_register_full(&devinfo));
++}
++module_init(qcom_cpufreq_driver_init);
++
++MODULE_DESCRIPTION("Qualcomm CPUfreq driver");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/ipq806x/patches-4.9/0046-cpufreq-qcom-Remove-platform-data.patch b/target/linux/ipq806x/patches-4.9/0046-cpufreq-qcom-Remove-platform-data.patch
new file mode 100644 (file)
index 0000000..aba2d19
--- /dev/null
@@ -0,0 +1,46 @@
+From f3a327717565cadc8ce5c148860ce0baeb4fbe20 Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <georgi.djakov@linaro.org>
+Date: Thu, 14 Jul 2016 14:48:21 +0300
+Subject: [PATCH 46/69] cpufreq: qcom: Remove platform data
+
+Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
+---
+ drivers/cpufreq/qcom-cpufreq.c | 12 +++---------
+ 1 file changed, 3 insertions(+), 9 deletions(-)
+
+--- a/drivers/cpufreq/qcom-cpufreq.c
++++ b/drivers/cpufreq/qcom-cpufreq.c
+@@ -20,7 +20,6 @@
+ #include <linux/platform_device.h>
+ #include <linux/pm_opp.h>
+ #include <linux/slab.h>
+-#include <linux/cpufreq-dt.h>
+ static void __init get_krait_bin_format_a(int *speed, int *pvs, int *pvs_ver)
+ {
+@@ -168,12 +167,6 @@ static int __init qcom_cpufreq_populate_
+ static int __init qcom_cpufreq_driver_init(void)
+ {
+-      struct cpufreq_dt_platform_data pdata = { .independent_clocks = true };
+-      struct platform_device_info devinfo = {
+-              .name = "cpufreq-dt",
+-              .data = &pdata,
+-              .size_data = sizeof(pdata),
+-      };
+       struct device *cpu_dev;
+       struct device_node *np;
+       int ret;
+@@ -196,9 +189,10 @@ static int __init qcom_cpufreq_driver_in
+       if (ret)
+               return ret;
+-      return PTR_ERR_OR_ZERO(platform_device_register_full(&devinfo));
++      return PTR_ERR_OR_ZERO(platform_device_register_simple("cpufreq-dt", -1,
++                                                             NULL, 0));
+ }
+-module_init(qcom_cpufreq_driver_init);
++late_initcall(qcom_cpufreq_driver_init);
+ MODULE_DESCRIPTION("Qualcomm CPUfreq driver");
+ MODULE_LICENSE("GPL v2");
diff --git a/target/linux/ipq806x/patches-4.9/0047-mtd-nand-Create-a-BBT-flag-to-access-bad-block-marke.patch b/target/linux/ipq806x/patches-4.9/0047-mtd-nand-Create-a-BBT-flag-to-access-bad-block-marke.patch
new file mode 100644 (file)
index 0000000..f70821e
--- /dev/null
@@ -0,0 +1,72 @@
+From c7c6a0f50f9ac3620c611ce06ba1f9fafea0444e Mon Sep 17 00:00:00 2001
+From: Archit Taneja <architt@codeaurora.org>
+Date: Mon, 3 Aug 2015 10:38:14 +0530
+Subject: [PATCH 47/69] mtd: nand: Create a BBT flag to access bad block
+ markers in raw mode
+
+Some controllers can access the factory bad block marker from OOB only
+when they read it in raw mode. When ECC is enabled, these controllers
+discard reading/writing bad block markers, preventing access to them
+altogether.
+
+The bbt driver assumes MTD_OPS_PLACE_OOB when scanning for bad blocks.
+This results in the nand driver's ecc->read_oob() op to be called, which
+works with ECC enabled.
+
+Create a new BBT option flag that tells nand_bbt to force the mode to
+MTD_OPS_RAW. This would result in the correct op being called for the
+underlying nand controller driver.
+
+Reviewed-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Archit Taneja <architt@codeaurora.org>
+---
+ drivers/mtd/nand/nand_base.c | 6 +++++-
+ drivers/mtd/nand/nand_bbt.c  | 6 +++++-
+ include/linux/mtd/bbm.h      | 6 ++++++
+ 3 files changed, 16 insertions(+), 2 deletions(-)
+
+--- a/drivers/mtd/nand/nand_base.c
++++ b/drivers/mtd/nand/nand_base.c
+@@ -414,7 +414,11 @@ static int nand_default_block_markbad(st
+       } else {
+               ops.len = ops.ooblen = 1;
+       }
+-      ops.mode = MTD_OPS_PLACE_OOB;
++
++      if (unlikely(chip->bbt_options & NAND_BBT_ACCESS_BBM_RAW))
++              ops.mode = MTD_OPS_RAW;
++      else
++              ops.mode = MTD_OPS_PLACE_OOB;
+       /* Write to first/last page(s) if necessary */
+       if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
+--- a/drivers/mtd/nand/nand_bbt.c
++++ b/drivers/mtd/nand/nand_bbt.c
+@@ -420,7 +420,11 @@ static int scan_block_fast(struct mtd_in
+       ops.oobbuf = buf;
+       ops.ooboffs = 0;
+       ops.datbuf = NULL;
+-      ops.mode = MTD_OPS_PLACE_OOB;
++
++      if (unlikely(bd->options & NAND_BBT_ACCESS_BBM_RAW))
++              ops.mode = MTD_OPS_RAW;
++      else
++              ops.mode = MTD_OPS_PLACE_OOB;
+       for (j = 0; j < numpages; j++) {
+               /*
+--- a/include/linux/mtd/bbm.h
++++ b/include/linux/mtd/bbm.h
+@@ -116,6 +116,12 @@ struct nand_bbt_descr {
+ #define NAND_BBT_NO_OOB_BBM   0x00080000
+ /*
++ * Force MTD_OPS_RAW mode when trying to access bad block markes from OOB. To
++ * be used by controllers which can access BBM only when ECC is disabled, i.e,
++ * when in RAW access mode
++ */
++#define NAND_BBT_ACCESS_BBM_RAW        0x00100000
++/*
+  * Flag set by nand_create_default_bbt_descr(), marking that the nand_bbt_descr
+  * was allocated dynamicaly and must be freed in nand_release(). Has no meaning
+  * in nand_chip.bbt_options.
diff --git a/target/linux/ipq806x/patches-4.9/0048-PM-OPP-HACK-Allow-to-set-regulator-without-opp_list.patch b/target/linux/ipq806x/patches-4.9/0048-PM-OPP-HACK-Allow-to-set-regulator-without-opp_list.patch
new file mode 100644 (file)
index 0000000..da9c0db
--- /dev/null
@@ -0,0 +1,27 @@
+From 5c294df1715d673f94f3b0a6e1ea3a426ca35e6e Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <georgi.djakov@linaro.org>
+Date: Thu, 28 Apr 2016 16:20:12 +0300
+Subject: [PATCH 48/69] PM / OPP: HACK: Allow to set regulator without opp_list
+
+Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
+---
+ drivers/base/power/opp/core.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/base/power/opp/core.c
++++ b/drivers/base/power/opp/core.c
+@@ -1339,12 +1339,13 @@ struct opp_table *dev_pm_opp_set_regulat
+               ret = -ENOMEM;
+               goto unlock;
+       }
+-
++#if 0
+       /* This should be called before OPPs are initialized */
+       if (WARN_ON(!list_empty(&opp_table->opp_list))) {
+               ret = -EBUSY;
+               goto err;
+       }
++#endif
+       /* Already have a regulator set */
+       if (WARN_ON(!IS_ERR(opp_table->regulator))) {
diff --git a/target/linux/ipq806x/patches-4.9/0049-PM-OPP-Support-adjusting-OPP-voltages-at-runtime.patch b/target/linux/ipq806x/patches-4.9/0049-PM-OPP-Support-adjusting-OPP-voltages-at-runtime.patch
new file mode 100644 (file)
index 0000000..e2a4eed
--- /dev/null
@@ -0,0 +1,147 @@
+From c949f08cf20fe82971fbdb4015daa38210da492e Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 18 Sep 2015 17:52:06 -0700
+Subject: [PATCH 49/69] PM / OPP: Support adjusting OPP voltages at runtime
+
+On some SoCs the Adaptive Voltage Scaling (AVS) technique is
+employed to optimize the operating voltage of a device. At a
+given frequency, the hardware monitors dynamic factors and either
+makes a suggestion for how much to adjust a voltage for the
+current frequency, or it automatically adjusts the voltage
+without software intervention. Add an API to the OPP library for
+the former case, so that AVS type devices can update the voltages
+for an OPP when the hardware determines the voltage should
+change. The assumption is that drivers like CPUfreq or devfreq
+will register for the OPP notifiers and adjust the voltage
+according to suggestions that AVS makes.
+
+Cc: Nishanth Menon <nm@ti.com>
+Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
+Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
+---
+ drivers/base/power/opp/core.c | 77 +++++++++++++++++++++++++++++++++++++++++++
+ include/linux/pm_opp.h        | 11 +++++++
+ 2 files changed, 88 insertions(+)
+
+--- a/drivers/base/power/opp/core.c
++++ b/drivers/base/power/opp/core.c
+@@ -1521,6 +1521,83 @@ unlock:
+ }
+ /**
++ * dev_pm_opp_adjust_voltage() - helper to change the voltage of an OPP
++ * @dev:              device for which we do this operation
++ * @freq:             OPP frequency to adjust voltage of
++ * @u_volt:           new OPP voltage
++ *
++ * Change the voltage of an OPP with an RCU operation.
++ *
++ * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the
++ * copy operation, returns 0 if no modifcation was done OR modification was
++ * successful.
++ *
++ * Locking: The internal device_opp and opp structures are RCU protected.
++ * Hence this function internally uses RCU updater strategy with mutex locks to
++ * keep the integrity of the internal data structures. Callers should ensure
++ * that this function is *NOT* called under RCU protection or in contexts where
++ * mutex locking or synchronize_rcu() blocking calls cannot be used.
++ */
++int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
++                            unsigned long u_volt)
++{
++      struct opp_table *opp_table;
++      struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV);
++      int r = 0;
++
++      /* keep the node allocated */
++      new_opp = kmalloc(sizeof(*new_opp), GFP_KERNEL);
++      if (!new_opp)
++              return -ENOMEM;
++
++      mutex_lock(&opp_table_lock);
++
++      /* Find the opp_table */
++      opp_table = _find_opp_table(dev);
++      if (IS_ERR(opp_table)) {
++              r = PTR_ERR(opp_table);
++              dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r);
++              goto unlock;
++      }
++
++      /* Do we have the frequency? */
++      list_for_each_entry(tmp_opp, &opp_table->opp_list, node) {
++              if (tmp_opp->rate == freq) {
++                      opp = tmp_opp;
++                      break;
++              }
++      }
++      if (IS_ERR(opp)) {
++              r = PTR_ERR(opp);
++              goto unlock;
++      }
++
++      /* Is update really needed? */
++      if (opp->u_volt == u_volt)
++              goto unlock;
++      /* copy the old data over */
++      *new_opp = *opp;
++
++      /* plug in new node */
++      new_opp->u_volt = u_volt;
++
++      list_replace_rcu(&opp->node, &new_opp->node);
++      mutex_unlock(&opp_table_lock);
++      call_srcu(&opp_table->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu);
++
++      /* Notify the change of the OPP */
++      srcu_notifier_call_chain(&opp_table->srcu_head, OPP_EVENT_ADJUST_VOLTAGE,
++                               new_opp);
++
++      return 0;
++
++unlock:
++      mutex_unlock(&opp_table_lock);
++      kfree(new_opp);
++      return r;
++}
++
++/**
+  * dev_pm_opp_enable() - Enable a specific OPP
+  * @dev:      device for which we do this operation
+  * @freq:     OPP frequency to enable
+--- a/include/linux/pm_opp.h
++++ b/include/linux/pm_opp.h
+@@ -23,6 +23,7 @@ struct opp_table;
+ enum dev_pm_opp_event {
+       OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE,
++      OPP_EVENT_ADJUST_VOLTAGE,
+ };
+ #if defined(CONFIG_PM_OPP)
+@@ -53,6 +54,9 @@ int dev_pm_opp_add(struct device *dev, u
+                  unsigned long u_volt);
+ void dev_pm_opp_remove(struct device *dev, unsigned long freq);
++int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
++                            unsigned long u_volt);
++
+ int dev_pm_opp_enable(struct device *dev, unsigned long freq);
+ int dev_pm_opp_disable(struct device *dev, unsigned long freq);
+@@ -139,6 +143,13 @@ static inline void dev_pm_opp_remove(str
+ {
+ }
++static inline int
++dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
++                        unsigned long u_volt)
++{
++      return 0;
++}
++
+ static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq)
+ {
+       return 0;
diff --git a/target/linux/ipq806x/patches-4.9/0050-OPP-Allow-notifiers-to-call-dev_pm_opp_get_-voltage-.patch b/target/linux/ipq806x/patches-4.9/0050-OPP-Allow-notifiers-to-call-dev_pm_opp_get_-voltage-.patch
new file mode 100644 (file)
index 0000000..7b41157
--- /dev/null
@@ -0,0 +1,107 @@
+From 4a17bbfcf72c94b37079e39a7c1e1e8653f7fe92 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 18 Sep 2015 17:52:07 -0700
+Subject: [PATCH 50/69] OPP: Allow notifiers to call dev_pm_opp_get_{voltage,
+ freq} RCU-free
+
+We pass the dev_pm_opp structure to OPP notifiers but the users
+of the notifier need to surround calls to dev_pm_opp_get_*() with
+RCU read locks to avoid lockdep warnings. The notifier is already
+called with the dev_opp's srcu lock held, so it should be safe to
+assume the devm_pm_opp structure is already protected inside the
+notifier. Update the lockdep check for this.
+
+Cc: Krzysztof Kozlowski <k.kozlowski@samsung.com>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
+Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
+---
+ drivers/base/power/opp/core.c | 19 ++++++++++---------
+ 1 file changed, 10 insertions(+), 9 deletions(-)
+
+--- a/drivers/base/power/opp/core.c
++++ b/drivers/base/power/opp/core.c
+@@ -32,9 +32,10 @@ LIST_HEAD(opp_tables);
+ /* Lock to allow exclusive modification to the device and opp lists */
+ DEFINE_MUTEX(opp_table_lock);
+-#define opp_rcu_lockdep_assert()                                      \
++#define opp_rcu_lockdep_assert(s)                                     \
+ do {                                                                  \
+       RCU_LOCKDEP_WARN(!rcu_read_lock_held() &&                       \
++                       !(s && srcu_read_lock_held(s)) &&              \
+                        !lockdep_is_held(&opp_table_lock),             \
+                        "Missing rcu_read_lock() or "                  \
+                        "opp_table_lock protection");                  \
+@@ -72,7 +73,7 @@ struct opp_table *_find_opp_table(struct
+ {
+       struct opp_table *opp_table;
+-      opp_rcu_lockdep_assert();
++      opp_rcu_lockdep_assert(NULL);
+       if (IS_ERR_OR_NULL(dev)) {
+               pr_err("%s: Invalid parameters\n", __func__);
+@@ -106,7 +107,7 @@ unsigned long dev_pm_opp_get_voltage(str
+       struct dev_pm_opp *tmp_opp;
+       unsigned long v = 0;
+-      opp_rcu_lockdep_assert();
++      opp_rcu_lockdep_assert(NULL);
+       tmp_opp = rcu_dereference(opp);
+       if (IS_ERR_OR_NULL(tmp_opp))
+@@ -138,7 +139,7 @@ unsigned long dev_pm_opp_get_freq(struct
+       struct dev_pm_opp *tmp_opp;
+       unsigned long f = 0;
+-      opp_rcu_lockdep_assert();
++      opp_rcu_lockdep_assert(NULL);
+       tmp_opp = rcu_dereference(opp);
+       if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available)
+@@ -172,7 +173,7 @@ bool dev_pm_opp_is_turbo(struct dev_pm_o
+ {
+       struct dev_pm_opp *tmp_opp;
+-      opp_rcu_lockdep_assert();
++      opp_rcu_lockdep_assert(NULL);
+       tmp_opp = rcu_dereference(opp);
+       if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) {
+@@ -300,7 +301,7 @@ struct dev_pm_opp *dev_pm_opp_get_suspen
+ {
+       struct opp_table *opp_table;
+-      opp_rcu_lockdep_assert();
++      opp_rcu_lockdep_assert(NULL);
+       opp_table = _find_opp_table(dev);
+       if (IS_ERR(opp_table) || !opp_table->suspend_opp ||
+@@ -380,7 +381,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_
+       struct opp_table *opp_table;
+       struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
+-      opp_rcu_lockdep_assert();
++      opp_rcu_lockdep_assert(NULL);
+       opp_table = _find_opp_table(dev);
+       if (IS_ERR(opp_table)) {
+@@ -444,7 +445,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_
+ {
+       struct opp_table *opp_table;
+-      opp_rcu_lockdep_assert();
++      opp_rcu_lockdep_assert(NULL);
+       if (!dev || !freq) {
+               dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq);
+@@ -486,7 +487,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_
+       struct opp_table *opp_table;
+       struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
+-      opp_rcu_lockdep_assert();
++      opp_rcu_lockdep_assert(NULL);
+       if (!dev || !freq) {
+               dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq);
diff --git a/target/linux/ipq806x/patches-4.9/0051-PM-OPP-Add-a-helper-to-get-an-opp-regulator-for-devi.patch b/target/linux/ipq806x/patches-4.9/0051-PM-OPP-Add-a-helper-to-get-an-opp-regulator-for-devi.patch
new file mode 100644 (file)
index 0000000..fc1a36e
--- /dev/null
@@ -0,0 +1,52 @@
+From d06ca5e7a3cf726f5be5ffd96e93ccd798b8c09a Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <georgi.djakov@linaro.org>
+Date: Thu, 12 May 2016 14:41:33 +0300
+Subject: [PATCH 51/69] PM / OPP: Add a helper to get an opp regulator for
+ device
+
+Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
+---
+ drivers/base/power/opp/core.c | 21 +++++++++++++++++++++
+ include/linux/pm_opp.h        |  1 +
+ 2 files changed, 22 insertions(+)
+
+--- a/drivers/base/power/opp/core.c
++++ b/drivers/base/power/opp/core.c
+@@ -151,6 +151,27 @@ unsigned long dev_pm_opp_get_freq(struct
+ }
+ EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq);
++struct regulator *dev_pm_opp_get_regulator(struct device *dev)
++{
++      struct opp_table *opp_table;
++      struct regulator *reg;
++
++      rcu_read_lock();
++
++      opp_table = _find_opp_table(dev);
++      if (IS_ERR(opp_table)) {
++              rcu_read_unlock();
++              return ERR_CAST(opp_table);
++      }
++
++      reg = opp_table->regulator;
++
++      rcu_read_unlock();
++
++      return reg;
++}
++EXPORT_SYMBOL_GPL(dev_pm_opp_get_regulator);
++
+ /**
+  * dev_pm_opp_is_turbo() - Returns if opp is turbo OPP or not
+  * @opp: opp for which turbo mode is being verified
+--- a/include/linux/pm_opp.h
++++ b/include/linux/pm_opp.h
+@@ -31,6 +31,7 @@ enum dev_pm_opp_event {
+ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp);
+ unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp);
++struct regulator *dev_pm_opp_get_regulator(struct device *dev);
+ bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp);
diff --git a/target/linux/ipq806x/patches-4.9/0052-PM-OPP-Update-the-voltage-tolerance-when-adjusting-t.patch b/target/linux/ipq806x/patches-4.9/0052-PM-OPP-Update-the-voltage-tolerance-when-adjusting-t.patch
new file mode 100644 (file)
index 0000000..9065911
--- /dev/null
@@ -0,0 +1,38 @@
+From 4533c285c2aedce6d4434d7b877066de3b1ecb33 Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <georgi.djakov@linaro.org>
+Date: Thu, 25 Aug 2016 18:43:35 +0300
+Subject: [PATCH 52/69] PM / OPP: Update the voltage tolerance when adjusting
+ the OPP
+
+When the voltage is adjusted, the voltage tolerance is not updated.
+This can lead to situations where the voltage min value is greater
+than the voltage max value. The final result is triggering a BUG()
+in the regulator core.
+Fix this by updating the voltage tolerance values too.
+
+Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
+---
+ drivers/base/power/opp/core.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/base/power/opp/core.c
++++ b/drivers/base/power/opp/core.c
+@@ -1566,6 +1566,7 @@ int dev_pm_opp_adjust_voltage(struct dev
+       struct opp_table *opp_table;
+       struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV);
+       int r = 0;
++      unsigned long tol;
+       /* keep the node allocated */
+       new_opp = kmalloc(sizeof(*new_opp), GFP_KERNEL);
+@@ -1602,6 +1603,10 @@ int dev_pm_opp_adjust_voltage(struct dev
+       /* plug in new node */
+       new_opp->u_volt = u_volt;
++      tol = u_volt * opp_table->voltage_tolerance_v1 / 100;
++      new_opp->u_volt = u_volt;
++      new_opp->u_volt_min = u_volt - tol;
++      new_opp->u_volt_max = u_volt + tol;
+       list_replace_rcu(&opp->node, &new_opp->node);
+       mutex_unlock(&opp_table_lock);
diff --git a/target/linux/ipq806x/patches-4.9/0053-regulator-add-smb208-support.patch b/target/linux/ipq806x/patches-4.9/0053-regulator-add-smb208-support.patch
new file mode 100644 (file)
index 0000000..0d2862c
--- /dev/null
@@ -0,0 +1,55 @@
+From ef10381ca4d01848ebedb4afb2c78feb8052f103 Mon Sep 17 00:00:00 2001
+From: Adrian Panella <ianchi74@outlook.com>
+Date: Thu, 9 Mar 2017 08:26:54 +0100
+Subject: [PATCH 53/69] regulator: add smb208 support
+
+Signed-off-by: Adrian Panella <ianchi74@outlook.com>
+---
+ Documentation/devicetree/bindings/mfd/qcom-rpm.txt | 4 ++++
+ drivers/regulator/qcom_rpm-regulator.c             | 9 +++++++++
+ 2 files changed, 13 insertions(+)
+
+--- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
++++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
+@@ -61,6 +61,7 @@ Regulator nodes are identified by their
+                   "qcom,rpm-pm8901-regulators"
+                   "qcom,rpm-pm8921-regulators"
+                   "qcom,rpm-pm8018-regulators"
++                  "qcom,rpm-smb208-regulators"
+ - vdd_l0_l1_lvs-supply:
+ - vdd_l2_l11_l12-supply:
+@@ -171,6 +172,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/0054-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch b/target/linux/ipq806x/patches-4.9/0054-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch
new file mode 100644 (file)
index 0000000..8c1b75a
--- /dev/null
@@ -0,0 +1,144 @@
+From 10577f74c35bd395951d1b2382c8d821089b5745 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 18 Sep 2015 17:52:08 -0700
+Subject: [PATCH 54/69] cpufreq-dt: Handle OPP voltage adjust events
+
+On some SoCs the Adaptive Voltage Scaling (AVS) technique is
+employed to optimize the operating voltage of a device. At a
+given frequency, the hardware monitors dynamic factors and either
+makes a suggestion for how much to adjust a voltage for the
+current frequency, or it automatically adjusts the voltage
+without software intervention.
+
+In the former case, an AVS driver will call
+dev_pm_opp_modify_voltage() and update the voltage for the
+particular OPP the CPUs are using. Add an OPP notifier to
+cpufreq-dt so that we can adjust the voltage of the CPU when AVS
+updates the OPP.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
+Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
+---
+ drivers/cpufreq/cpufreq-dt.c | 68 ++++++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 65 insertions(+), 3 deletions(-)
+
+--- a/drivers/cpufreq/cpufreq-dt.c
++++ b/drivers/cpufreq/cpufreq-dt.c
+@@ -32,6 +32,9 @@ struct private_data {
+       struct device *cpu_dev;
+       struct thermal_cooling_device *cdev;
+       const char *reg_name;
++      struct notifier_block opp_nb;
++      struct mutex lock;
++      unsigned long opp_freq;
+ };
+ static struct freq_attr *cpufreq_dt_attr[] = {
+@@ -43,9 +46,16 @@ static struct freq_attr *cpufreq_dt_attr
+ static int set_target(struct cpufreq_policy *policy, unsigned int index)
+ {
+       struct private_data *priv = policy->driver_data;
++      int ret;
++      unsigned long target_freq = policy->freq_table[index].frequency * 1000;
++
++      mutex_lock(&priv->lock);
++      ret = dev_pm_opp_set_rate(priv->cpu_dev, target_freq);
++      if (!ret)
++              priv->opp_freq = target_freq;
++      mutex_unlock(&priv->lock);
+-      return dev_pm_opp_set_rate(priv->cpu_dev,
+-                                 policy->freq_table[index].frequency * 1000);
++      return ret;
+ }
+ /*
+@@ -86,6 +96,39 @@ node_put:
+       return name;
+ }
++static int opp_notifier(struct notifier_block *nb, unsigned long event,
++                      void *data)
++{
++      struct dev_pm_opp *opp = data;
++      struct private_data *priv = container_of(nb, struct private_data,
++                                               opp_nb);
++      struct device *cpu_dev = priv->cpu_dev;
++      struct regulator *cpu_reg;
++      unsigned long volt, freq;
++      int ret = 0;
++
++      if (event == OPP_EVENT_ADJUST_VOLTAGE) {
++              cpu_reg = dev_pm_opp_get_regulator(cpu_dev);
++              if (IS_ERR(cpu_reg)) {
++                      ret = PTR_ERR(cpu_reg);
++                      goto out;
++              }
++              volt = dev_pm_opp_get_voltage(opp);
++              freq = dev_pm_opp_get_freq(opp);
++
++              mutex_lock(&priv->lock);
++              if (freq == priv->opp_freq) {
++                      ret = regulator_set_voltage_triplet(cpu_reg, volt, volt, volt);
++              }
++              mutex_unlock(&priv->lock);
++              if (ret)
++                      dev_err(cpu_dev, "failed to scale voltage: %d\n", ret);
++      }
++
++out:
++      return notifier_from_errno(ret);
++}
++
+ static int resources_available(void)
+ {
+       struct device *cpu_dev;
+@@ -153,6 +196,7 @@ static int cpufreq_init(struct cpufreq_p
+       bool fallback = false;
+       const char *name;
+       int ret;
++      struct srcu_notifier_head *opp_srcu_head;
+       cpu_dev = get_cpu_device(policy->cpu);
+       if (!cpu_dev) {
+@@ -239,13 +283,29 @@ static int cpufreq_init(struct cpufreq_p
+               goto out_free_opp;
+       }
++      mutex_init(&priv->lock);
++
++      rcu_read_lock();
++      opp_srcu_head = dev_pm_opp_get_notifier(cpu_dev);
++      if (IS_ERR(opp_srcu_head)) {
++              ret = PTR_ERR(opp_srcu_head);
++              rcu_read_unlock();
++              goto out_free_priv;
++      }
++
++      priv->opp_nb.notifier_call = opp_notifier;
++      ret = srcu_notifier_chain_register(opp_srcu_head, &priv->opp_nb);
++      rcu_read_unlock();
++      if (ret)
++              goto out_free_priv;
++
+       priv->reg_name = name;
+       priv->opp_table = opp_table;
+       ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
+       if (ret) {
+               dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
+-              goto out_free_priv;
++              goto out_unregister_nb;
+       }
+       priv->cpu_dev = cpu_dev;
+@@ -284,6 +344,8 @@ static int cpufreq_init(struct cpufreq_p
+ out_free_cpufreq_table:
+       dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
++out_unregister_nb:
++      srcu_notifier_chain_unregister(opp_srcu_head, &priv->opp_nb);
+ out_free_priv:
+       kfree(priv);
+ out_free_opp:
diff --git a/target/linux/ipq806x/patches-4.9/0055-cpufreq-dt-Add-L2-frequency-scaling-support.patch b/target/linux/ipq806x/patches-4.9/0055-cpufreq-dt-Add-L2-frequency-scaling-support.patch
new file mode 100644 (file)
index 0000000..e2d6233
--- /dev/null
@@ -0,0 +1,90 @@
+From 0759cdff49f1cf361bf503c13f7bcb33da43ab95 Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <georgi.djakov@linaro.org>
+Date: Tue, 8 Sep 2015 11:24:41 +0300
+Subject: [PATCH 55/69] cpufreq-dt: Add L2 frequency scaling support
+
+Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
+---
+ drivers/cpufreq/cpufreq-dt.c | 41 ++++++++++++++++++++++++++++++++++++++++-
+ include/linux/cpufreq.h      |  2 ++
+ 2 files changed, 42 insertions(+), 1 deletion(-)
+
+--- a/drivers/cpufreq/cpufreq-dt.c
++++ b/drivers/cpufreq/cpufreq-dt.c
+@@ -48,11 +48,41 @@ static int set_target(struct cpufreq_pol
+       struct private_data *priv = policy->driver_data;
+       int ret;
+       unsigned long target_freq = policy->freq_table[index].frequency * 1000;
++      struct clk *l2_clk = policy->l2_clk;
++      unsigned int l2_freq;
++      unsigned long new_l2_freq = 0;
+       mutex_lock(&priv->lock);
+       ret = dev_pm_opp_set_rate(priv->cpu_dev, target_freq);
+-      if (!ret)
++
++      if (!ret) {
++              if (!IS_ERR(l2_clk) && policy->l2_rate[0] && policy->l2_rate[1] &&
++                              policy->l2_rate[2]) {
++                      static unsigned long krait_l2[CONFIG_NR_CPUS] = { };
++                      int cpu, ret = 0;
++
++                      if (target_freq >= policy->l2_rate[2])
++                              new_l2_freq = policy->l2_rate[2];
++                      else if (target_freq >= policy->l2_rate[1])
++                              new_l2_freq = policy->l2_rate[1];
++                      else
++                              new_l2_freq = policy->l2_rate[0];
++
++                      krait_l2[policy->cpu] = new_l2_freq;
++                      for_each_present_cpu(cpu)
++                              new_l2_freq = max(new_l2_freq, krait_l2[cpu]);
++
++                      l2_freq = clk_get_rate(l2_clk);
++
++                      if (l2_freq != new_l2_freq) {
++                              /* scale l2 with the core */
++                              ret = clk_set_rate(l2_clk, new_l2_freq);
++                      }
++              }
++
+               priv->opp_freq = target_freq;
++      }
++
+       mutex_unlock(&priv->lock);
+       return ret;
+@@ -197,6 +227,8 @@ static int cpufreq_init(struct cpufreq_p
+       const char *name;
+       int ret;
+       struct srcu_notifier_head *opp_srcu_head;
++      struct device_node *l2_np;
++      struct clk *l2_clk = NULL;
+       cpu_dev = get_cpu_device(policy->cpu);
+       if (!cpu_dev) {
+@@ -318,6 +350,13 @@ static int cpufreq_init(struct cpufreq_p
+               policy->suspend_freq = dev_pm_opp_get_freq(suspend_opp) / 1000;
+       rcu_read_unlock();
++      l2_clk = clk_get(cpu_dev, "l2");
++      if (!IS_ERR(l2_clk))
++              policy->l2_clk = l2_clk;
++      l2_np = of_find_node_by_name(NULL, "qcom,l2");
++      if (l2_np)
++              of_property_read_u32_array(l2_np, "qcom,l2-rates", policy->l2_rate, 3);
++
+       ret = cpufreq_table_validate_and_show(policy, freq_table);
+       if (ret) {
+               dev_err(cpu_dev, "%s: invalid frequency table: %d\n", __func__,
+--- a/include/linux/cpufreq.h
++++ b/include/linux/cpufreq.h
+@@ -73,6 +73,8 @@ struct cpufreq_policy {
+       unsigned int            cpu;    /* cpu managing this policy, must be online */
+       struct clk              *clk;
++      struct clk              *l2_clk; /* L2 clock */
++      unsigned int            l2_rate[3]; /* L2 bus clock rate thresholds */
+       struct cpufreq_cpuinfo  cpuinfo;/* see above */
+       unsigned int            min;    /* in kHz */
diff --git a/target/linux/ipq806x/patches-4.9/0056-cpufreq-dt-Add-missing-rcu-locks.patch b/target/linux/ipq806x/patches-4.9/0056-cpufreq-dt-Add-missing-rcu-locks.patch
new file mode 100644 (file)
index 0000000..c0eb2eb
--- /dev/null
@@ -0,0 +1,23 @@
+From 001a8dcb56ced58c518aaa10a4f0ba5e878705b6 Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <georgi.djakov@linaro.org>
+Date: Tue, 17 May 2016 16:15:43 +0300
+Subject: [PATCH 56/69] cpufreq-dt: Add missing rcu locks
+
+Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
+---
+ drivers/cpufreq/cpufreq-dt.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/cpufreq/cpufreq-dt.c
++++ b/drivers/cpufreq/cpufreq-dt.c
+@@ -143,8 +143,10 @@ static int opp_notifier(struct notifier_
+                       ret = PTR_ERR(cpu_reg);
+                       goto out;
+               }
++              rcu_read_lock();
+               volt = dev_pm_opp_get_voltage(opp);
+               freq = dev_pm_opp_get_freq(opp);
++              rcu_read_unlock();
+               mutex_lock(&priv->lock);
+               if (freq == priv->opp_freq) {
diff --git a/target/linux/ipq806x/patches-4.9/0057-clk-qcom-Add-regmap-mux-div-clocks-support.patch b/target/linux/ipq806x/patches-4.9/0057-clk-qcom-Add-regmap-mux-div-clocks-support.patch
new file mode 100644 (file)
index 0000000..c5cdc79
--- /dev/null
@@ -0,0 +1,372 @@
+From f72c5aa18281c44945fea6181d0d816a7605505c Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <georgi.djakov@linaro.org>
+Date: Wed, 18 Mar 2015 17:23:29 +0200
+Subject: [PATCH 57/69] clk: qcom: Add regmap mux-div clocks support
+
+Add support for hardware that can switch both parent clocks and divider
+at the same time. This avoids generating intermediate frequencies from
+either the old parent clock and new divider or new parent clock and
+old divider combinations.
+
+Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
+---
+ drivers/clk/qcom/Makefile             |   1 +
+ drivers/clk/qcom/clk-regmap-mux-div.c | 272 ++++++++++++++++++++++++++++++++++
+ drivers/clk/qcom/clk-regmap-mux-div.h |  65 ++++++++
+ 3 files changed, 338 insertions(+)
+ create mode 100644 drivers/clk/qcom/clk-regmap-mux-div.c
+ create mode 100644 drivers/clk/qcom/clk-regmap-mux-div.h
+
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -9,6 +9,7 @@ clk-qcom-y += clk-rcg2.o
+ clk-qcom-y += clk-branch.o
+ clk-qcom-y += clk-regmap-divider.o
+ clk-qcom-y += clk-regmap-mux.o
++clk-qcom-y += clk-regmap-mux-div.o
+ clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
+ clk-qcom-y += clk-hfpll.o
+ clk-qcom-y += reset.o
+--- /dev/null
++++ b/drivers/clk/qcom/clk-regmap-mux-div.c
+@@ -0,0 +1,272 @@
++/*
++ * Copyright (c) 2015, Linaro Limited
++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/bitops.h>
++#include <linux/delay.h>
++#include <linux/export.h>
++#include <linux/kernel.h>
++#include <linux/regmap.h>
++
++#include "clk-regmap-mux-div.h"
++
++#define CMD_RCGR                      0x0
++#define CMD_RCGR_UPDATE                       BIT(0)
++#define CMD_RCGR_DIRTY_CFG            BIT(4)
++#define CMD_RCGR_ROOT_OFF             BIT(31)
++#define CFG_RCGR                      0x4
++
++#define to_clk_regmap_mux_div(_hw) \
++      container_of(to_clk_regmap(_hw), struct clk_regmap_mux_div, clkr)
++
++int __mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div)
++{
++      int ret, count;
++      u32 val, mask;
++      const char *name = clk_hw_get_name(&md->clkr.hw);
++
++      val = (div << md->hid_shift) | (src << md->src_shift);
++      mask = ((BIT(md->hid_width) - 1) << md->hid_shift) |
++             ((BIT(md->src_width) - 1) << md->src_shift);
++
++      ret = regmap_update_bits(md->clkr.regmap, CFG_RCGR + md->reg_offset,
++                               mask, val);
++      if (ret)
++              return ret;
++
++      ret = regmap_update_bits(md->clkr.regmap, CMD_RCGR + md->reg_offset,
++                               CMD_RCGR_UPDATE, CMD_RCGR_UPDATE);
++      if (ret)
++              return ret;
++
++      /* Wait for update to take effect */
++      for (count = 500; count > 0; count--) {
++              ret = regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset,
++                                &val);
++              if (ret)
++                      return ret;
++              if (!(val & CMD_RCGR_UPDATE))
++                      return 0;
++              udelay(1);
++      }
++
++      pr_err("%s: RCG did not update its configuration", name);
++      return -EBUSY;
++}
++
++static void __mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src,
++                                u32 *div)
++{
++      u32 val, __div, __src;
++      const char *name = clk_hw_get_name(&md->clkr.hw);
++
++      regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, &val);
++
++      if (val & CMD_RCGR_DIRTY_CFG) {
++              pr_err("%s: RCG configuration is pending\n", name);
++              return;
++      }
++
++      regmap_read(md->clkr.regmap, CFG_RCGR + md->reg_offset, &val);
++      __src = (val >> md->src_shift);
++      __src &= BIT(md->src_width) - 1;
++      *src = __src;
++
++      __div = (val >> md->hid_shift);
++      __div &= BIT(md->hid_width) - 1;
++      *div = __div;
++}
++
++static int mux_div_enable(struct clk_hw *hw)
++{
++      struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++
++      return __mux_div_set_src_div(md, md->src, md->div);
++}
++
++static inline bool is_better_rate(unsigned long req, unsigned long best,
++                                unsigned long new)
++{
++      return (req <= new && new < best) || (best < req && best < new);
++}
++
++static int mux_div_determine_rate(struct clk_hw *hw,
++                                struct clk_rate_request *req)
++{
++      struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++      unsigned int i, div, max_div;
++      unsigned long actual_rate, best_rate = 0;
++      unsigned long req_rate = req->rate;
++
++      for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
++              struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
++              unsigned long parent_rate = clk_hw_get_rate(parent);
++
++              max_div = BIT(md->hid_width) - 1;
++              for (div = 1; div < max_div; div++) {
++                      parent_rate = mult_frac(req_rate, div, 2);
++                      parent_rate = clk_hw_round_rate(parent, parent_rate);
++                      actual_rate = mult_frac(parent_rate, 2, div);
++
++                      if (is_better_rate(req_rate, best_rate, actual_rate)) {
++                              best_rate = actual_rate;
++                              req->rate = best_rate;
++                              req->best_parent_rate = parent_rate;
++                              req->best_parent_hw = parent;
++                      }
++
++                      if (actual_rate < req_rate || best_rate <= req_rate)
++                              break;
++              }
++      }
++
++      if (!best_rate)
++              return -EINVAL;
++
++      return 0;
++}
++
++static int __mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
++                                       unsigned long prate, u32 src)
++{
++      struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++      int ret;
++      u32 div, max_div, best_src = 0, best_div = 0;
++      unsigned int i;
++      unsigned long actual_rate, best_rate = 0;
++
++      for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
++              struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
++              unsigned long parent_rate = clk_hw_get_rate(parent);
++
++              max_div = BIT(md->hid_width) - 1;
++              for (div = 1; div < max_div; div++) {
++                      parent_rate = mult_frac(rate, div, 2);
++                      parent_rate = clk_hw_round_rate(parent, parent_rate);
++                      actual_rate = mult_frac(parent_rate, 2, div);
++
++                      if (is_better_rate(rate, best_rate, actual_rate)) {
++                              best_rate = actual_rate;
++                              best_src = md->parent_map[i].cfg;
++                              best_div = div - 1;
++                      }
++
++                      if (actual_rate < rate || best_rate <= rate)
++                              break;
++              }
++      }
++
++      ret = __mux_div_set_src_div(md, best_src, best_div);
++      if (!ret) {
++              md->div = best_div;
++              md->src = best_src;
++      }
++
++      return ret;
++}
++
++static u8 mux_div_get_parent(struct clk_hw *hw)
++{
++      struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++      const char *name = clk_hw_get_name(hw);
++      u32 i, div, src = 0;
++
++      __mux_div_get_src_div(md, &src, &div);
++
++      for (i = 0; i < clk_hw_get_num_parents(hw); i++)
++              if (src == md->parent_map[i].cfg)
++                      return i;
++
++      pr_err("%s: Can't find parent with src %d\n", name, src);
++      return 0;
++}
++
++static int mux_div_set_parent(struct clk_hw *hw, u8 index)
++{
++      struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++
++      return __mux_div_set_src_div(md, md->parent_map[index].cfg, md->div);
++}
++
++static int mux_div_set_rate(struct clk_hw *hw,
++                          unsigned long rate, unsigned long prate)
++{
++      struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++
++      return __mux_div_set_rate_and_parent(hw, rate, prate, md->src);
++}
++
++static int mux_div_set_rate_and_parent(struct clk_hw *hw,  unsigned long rate,
++                                     unsigned long prate, u8 index)
++{
++      struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++
++      return __mux_div_set_rate_and_parent(hw, rate, prate,
++                                           md->parent_map[index].cfg);
++}
++
++static unsigned long mux_div_recalc_rate(struct clk_hw *hw, unsigned long prate)
++{
++      struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++      u32 div, src;
++      int i, num_parents = clk_hw_get_num_parents(hw);
++      const char *name = clk_hw_get_name(hw);
++
++      __mux_div_get_src_div(md, &src, &div);
++      for (i = 0; i < num_parents; i++)
++              if (src == md->parent_map[i].cfg) {
++                      struct clk_hw *p = clk_hw_get_parent_by_index(hw, i);
++                      unsigned long parent_rate = clk_hw_get_rate(p);
++
++                      return mult_frac(parent_rate, 2, div + 1);
++              }
++
++      pr_err("%s: Can't find parent %d\n", name, src);
++      return 0;
++}
++
++static struct clk_hw *mux_div_get_safe_parent(struct clk_hw *hw,
++                                            unsigned long *safe_freq)
++{
++      int i;
++      struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++
++      if (md->safe_freq)
++              *safe_freq = md->safe_freq;
++
++      for (i = 0; i < clk_hw_get_num_parents(hw); i++)
++              if (md->safe_src == md->parent_map[i].cfg)
++                      break;
++
++      return clk_hw_get_parent_by_index(hw, i);
++}
++
++static void mux_div_disable(struct clk_hw *hw)
++{
++      struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++
++      __mux_div_set_src_div(md, md->safe_src, md->safe_div);
++}
++
++const struct clk_ops clk_regmap_mux_div_ops = {
++      .enable = mux_div_enable,
++      .disable = mux_div_disable,
++      .get_parent = mux_div_get_parent,
++      .set_parent = mux_div_set_parent,
++      .set_rate = mux_div_set_rate,
++      .set_rate_and_parent = mux_div_set_rate_and_parent,
++      .determine_rate = mux_div_determine_rate,
++      .recalc_rate = mux_div_recalc_rate,
++      .get_safe_parent = mux_div_get_safe_parent,
++};
++EXPORT_SYMBOL_GPL(clk_regmap_mux_div_ops);
+--- /dev/null
++++ b/drivers/clk/qcom/clk-regmap-mux-div.h
+@@ -0,0 +1,65 @@
++/*
++ * Copyright (c) 2015, Linaro Limited
++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#ifndef __QCOM_CLK_REGMAP_MUX_DIV_H__
++#define __QCOM_CLK_REGMAP_MUX_DIV_H__
++
++#include <linux/clk-provider.h>
++#include "clk-rcg.h"
++#include "clk-regmap.h"
++
++/**
++ * struct mux_div_clk - combined mux/divider clock
++ * @reg_offset: offset of the mux/divider register
++ * @hid_width:        number of bits in half integer divider
++ * @hid_shift:        lowest bit of hid value field
++ * @src_width:        number of bits in source select
++ * @src_shift:        lowest bit of source select field
++ * @div:      the divider raw configuration value
++ * @src:      the mux index which will be used if the clock is enabled
++ * @safe_src: the safe source mux value we switch to, while the main PLL is
++ *            reconfigured
++ * @safe_div: the safe divider value that we set, while the main PLL is
++ *            reconfigured
++ * @safe_freq:        When switching rates from A to B, the mux div clock will
++ *            instead switch from A -> safe_freq -> B. This allows the
++ *            mux_div clock to change rates while enabled, even if this
++ *            behavior is not supported by the parent clocks.
++ *            If changing the rate of parent A also causes the rate of
++ *            parent B to change, then safe_freq must be defined.
++ *            safe_freq is expected to have a source clock which is always
++ *            on and runs at only one rate.
++ * @parent_map:       pointer to parent_map struct
++ * @clkr:     handle between common and hardware-specific interfaces
++ */
++
++struct clk_regmap_mux_div {
++      u32                             reg_offset;
++      u32                             hid_width;
++      u32                             hid_shift;
++      u32                             src_width;
++      u32                             src_shift;
++      u32                             div;
++      u32                             src;
++      u32                             safe_src;
++      u32                             safe_div;
++      unsigned long                   safe_freq;
++      const struct parent_map         *parent_map;
++      struct clk_regmap               clkr;
++};
++
++extern const struct clk_ops clk_regmap_mux_div_ops;
++int __mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div);
++
++#endif
diff --git a/target/linux/ipq806x/patches-4.9/0058-clk-qcom-Always-add-factor-clock-for-xo-clocks.patch b/target/linux/ipq806x/patches-4.9/0058-clk-qcom-Always-add-factor-clock-for-xo-clocks.patch
new file mode 100644 (file)
index 0000000..f679cf7
--- /dev/null
@@ -0,0 +1,38 @@
+From 6081776c1eef63e3083387bb9ec2bf7edf92428b Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <georgi.djakov@linaro.org>
+Date: Wed, 2 Nov 2016 17:56:58 +0200
+Subject: [PATCH 58/69] clk: qcom: Always add factor clock for xo clocks
+
+Currently the RPM/RPM-SMD clock drivers do not register the xo clocks,
+so we should always add factor clock. When we later add xo clocks support
+into the drivers, we should update this function to skip registration.
+By doing so we avoid any DT dependencies.
+
+Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
+---
+ drivers/clk/qcom/common.c | 15 ++++++---------
+ 1 file changed, 6 insertions(+), 9 deletions(-)
+
+--- a/drivers/clk/qcom/common.c
++++ b/drivers/clk/qcom/common.c
+@@ -153,15 +153,12 @@ int qcom_cc_register_board_clk(struct de
+                              const char *name, unsigned long rate)
+ {
+       bool add_factor = true;
+-      struct device_node *node;
+-      /* The RPM clock driver will add the factor clock if present */
+-      if (IS_ENABLED(CONFIG_QCOM_RPMCC)) {
+-              node = of_find_compatible_node(NULL, NULL, "qcom,rpmcc");
+-              if (of_device_is_available(node))
+-                      add_factor = false;
+-              of_node_put(node);
+-      }
++      /*
++       * TODO: The RPM clock driver currently does not support the xo clock.
++       * When xo is added to the RPM clock driver, we should change this
++       * function to skip registration of xo factor clocks.
++       */
+       return _qcom_cc_register_board_clk(dev, path, name, rate, add_factor);
+ }
diff --git a/target/linux/ipq806x/patches-4.9/0059-ARM-cpuidle-Add-cpuidle-support-for-QCOM-cpus.patch b/target/linux/ipq806x/patches-4.9/0059-ARM-cpuidle-Add-cpuidle-support-for-QCOM-cpus.patch
new file mode 100644 (file)
index 0000000..b734986
--- /dev/null
@@ -0,0 +1,29 @@
+From 04ca10340f1b4d92e849724d322a7ca225d11539 Mon Sep 17 00:00:00 2001
+From: Lina Iyer <lina.iyer@linaro.org>
+Date: Wed, 25 Mar 2015 14:25:29 -0600
+Subject: [PATCH 59/69] ARM: cpuidle: Add cpuidle support for QCOM cpus
+
+Define ARM_QCOM_CPUIDLE config item to enable cpuidle support.
+
+Cc: Stephen Boyd <sboyd@codeaurora.org>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Cc: Kevin Hilman <khilman@linaro.org>
+Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
+Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
+---
+ drivers/cpuidle/Kconfig.arm | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/cpuidle/Kconfig.arm
++++ b/drivers/cpuidle/Kconfig.arm
+@@ -74,3 +74,10 @@ config ARM_MVEBU_V7_CPUIDLE
+       depends on ARCH_MVEBU && !ARM64
+       help
+         Select this to enable cpuidle on Armada 370, 38x and XP processors.
++
++config ARM_QCOM_CPUIDLE
++      bool "CPU Idle Driver for QCOM processors"
++      depends on ARCH_QCOM
++      select ARM_CPUIDLE
++      help
++        Select this to enable cpuidle on QCOM processors.
diff --git a/target/linux/ipq806x/patches-4.9/0060-HACK-arch-arm-force-ZRELADDR-on-arch-qcom.patch b/target/linux/ipq806x/patches-4.9/0060-HACK-arch-arm-force-ZRELADDR-on-arch-qcom.patch
new file mode 100644 (file)
index 0000000..5f04d56
--- /dev/null
@@ -0,0 +1,62 @@
+From fa71139b55e114aa8c3c4823ff8ee7d49ee810d4 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Wed, 29 Apr 2015 15:21:46 -0700
+Subject: [PATCH 60/69] HACK: arch: arm: force ZRELADDR on arch-qcom
+
+ARCH_QCOM is using the ARCH_MULTIPLATFORM option, as now recommended
+on most ARM architectures. This automatically calculate ZRELADDR by
+masking PHYS_OFFSET with 0xf8000000.
+
+However, on IPQ806x, the first ~20MB of RAM is reserved for the hardware
+network accelerators, and the bootloader removes this section from the
+layout passed from the ATAGS (when used).
+
+For newer bootloader, when DT is used, this is not a problem, we just
+reserve this memory in the device tree. But if the bootloader doesn't
+have DT support, then ATAGS have to be used. In this case, the ARM
+decompressor will position the kernel in this low mem, which will not be
+in the RAM section mapped by the bootloader, which means the kernel will
+freeze in the middle of the boot process trying to map the memory.
+
+As a work around, this patch allows disabling AUTO_ZRELADDR when
+ARCH_QCOM is selected. It makes the zImage usage possible on bootloaders
+which don't support device-tree, which is the case on certain early
+IPQ806x based designs.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/Kconfig                 | 2 +-
+ arch/arm/Makefile                | 2 ++
+ arch/arm/mach-qcom/Makefile.boot | 1 +
+ 3 files changed, 4 insertions(+), 1 deletion(-)
+ create mode 100644 arch/arm/mach-qcom/Makefile.boot
+
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -332,7 +332,7 @@ config ARCH_MULTIPLATFORM
+       depends on MMU
+       select ARM_HAS_SG_CHAIN
+       select ARM_PATCH_PHYS_VIRT
+-      select AUTO_ZRELADDR
++      select AUTO_ZRELADDR if !ARCH_QCOM
+       select CLKSRC_OF
+       select COMMON_CLK
+       select GENERIC_CLOCKEVENTS
+--- a/arch/arm/Makefile
++++ b/arch/arm/Makefile
+@@ -251,9 +251,11 @@ MACHINE  := arch/arm/mach-$(word 1,$(mac
+ else
+ MACHINE  :=
+ endif
++ifeq ($(CONFIG_ARCH_QCOM),)
+ ifeq ($(CONFIG_ARCH_MULTIPLATFORM),y)
+ MACHINE  :=
+ endif
++endif
+ machdirs := $(patsubst %,arch/arm/mach-%/,$(machine-y))
+ platdirs := $(patsubst %,arch/arm/plat-%/,$(sort $(plat-y)))
+--- /dev/null
++++ b/arch/arm/mach-qcom/Makefile.boot
+@@ -0,0 +1 @@
++zreladdr-y+= 0x42208000
diff --git a/target/linux/ipq806x/patches-4.9/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch b/target/linux/ipq806x/patches-4.9/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch
new file mode 100644 (file)
index 0000000..a4a9575
--- /dev/null
@@ -0,0 +1,23 @@
+From 5001f2e1a325b68dbf225bd17f69a4d3d975cca5 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Thu, 9 Mar 2017 09:31:44 +0100
+Subject: [PATCH 61/69] mtd: "rootfs" conflicts with OpenWrt auto mounting
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/mtd/qcom_smem_part.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/mtd/qcom_smem_part.c
++++ b/drivers/mtd/qcom_smem_part.c
+@@ -189,6 +189,10 @@ static int parse_qcom_smem_partitions(st
+               m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz);
+               m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz);
++              /* "rootfs" conflicts with OpenWrt auto mounting */
++              if (mtd_type_is_nand(master) && !strcmp(m_part->name, "rootfs"))
++                      m_part->name = "ubi";
++
+               /*
+                * The last SMEM partition may have its size marked as
+                * something like 0xffffffff, which means "until the end of the
diff --git a/target/linux/ipq806x/patches-4.9/0062-ipq806x-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch b/target/linux/ipq806x/patches-4.9/0062-ipq806x-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch
new file mode 100644 (file)
index 0000000..a3e0a1d
--- /dev/null
@@ -0,0 +1,25 @@
+From a16fcf911a020e46439a3bb3e702463fc3159831 Mon Sep 17 00:00:00 2001
+From: Abhishek Sahu <absahu@codeaurora.org>
+Date: Wed, 18 Nov 2015 12:38:56 +0530
+Subject: [PATCH 62/69] 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
+@@ -1234,6 +1234,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/0063-ipq806x-clk-gcc-add-tsens-child-node.patch b/target/linux/ipq806x/patches-4.9/0063-ipq806x-clk-gcc-add-tsens-child-node.patch
new file mode 100644 (file)
index 0000000..9f154ae
--- /dev/null
@@ -0,0 +1,46 @@
+From 3064376aa3e8dae03dc2c5c3c064e2283c4337d8 Mon Sep 17 00:00:00 2001
+From: Pavel Kubelun <be.dissent@gmail.com>
+Date: Tue, 22 Nov 2016 17:37:56 +0300
+Subject: [PATCH 63/69] ipq806x: clk: gcc: add tsens child node
+
+Thermal sensors in ipq806x are inside a Global clock controller.
+Add a child node into it to be used by the TSENS driver.
+
+Signed-off-by: Pavel Kubelun <be.dissent@gmail.com>
+---
+ drivers/clk/qcom/gcc-ipq806x.c | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+--- a/drivers/clk/qcom/gcc-ipq806x.c
++++ b/drivers/clk/qcom/gcc-ipq806x.c
+@@ -970,7 +970,7 @@ static struct clk_branch gsbi1_h_clk = {
+               .hw.init = &(struct clk_init_data){
+                       .name = "gsbi1_h_clk",
+                       .ops = &clk_branch_ops,
+-+                     .flags = CLK_IGNORE_UNUSED,
++                      .flags = CLK_IGNORE_UNUSED,
+               },
+       },
+ };
+@@ -3073,6 +3073,7 @@ MODULE_DEVICE_TABLE(of, gcc_ipq806x_matc
+ static int gcc_ipq806x_probe(struct platform_device *pdev)
+ {
+       struct device *dev = &pdev->dev;
++      struct platform_device *tsens;
+       struct regmap *regmap;
+       int ret;
+@@ -3102,6 +3103,13 @@ static int gcc_ipq806x_probe(struct plat
+       regmap_write(regmap, 0x3cf8, 8);
+       regmap_write(regmap, 0x3d18, 8);
++      tsens = platform_device_register_data(&pdev->dev, "qcom-tsens", -1,
++                                            NULL, 0);
++      if (IS_ERR(tsens))
++              return PTR_ERR(tsens);
++
++      platform_set_drvdata(pdev, tsens);
++
+       return 0;
+ }
diff --git a/target/linux/ipq806x/patches-4.9/0064-clk-clk-rpm-fixes.patch b/target/linux/ipq806x/patches-4.9/0064-clk-clk-rpm-fixes.patch
new file mode 100644 (file)
index 0000000..b803488
--- /dev/null
@@ -0,0 +1,93 @@
+From d30840e2b1cf79d90392e6051b0c0b6006d29d8b Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Thu, 9 Mar 2017 09:32:40 +0100
+Subject: [PATCH 64/69] clk: clk-rpm fixes
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ .../devicetree/bindings/clock/qcom,rpmcc.txt       |  1 +
+ drivers/clk/qcom/clk-rpm.c                         | 35 ++++++++++++++++++++++
+ include/dt-bindings/clock/qcom,rpmcc.h             |  4 +++
+ 3 files changed, 40 insertions(+)
+
+--- 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/0065-arm-override-compiler-flags.patch b/target/linux/ipq806x/patches-4.9/0065-arm-override-compiler-flags.patch
new file mode 100644 (file)
index 0000000..e5af7ff
--- /dev/null
@@ -0,0 +1,21 @@
+From 4d8e29642661397a339ac3485f212c6360445421 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Thu, 9 Mar 2017 09:33:32 +0100
+Subject: [PATCH 65/69] arm: override compiler flags
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ arch/arm/Makefile | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- 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/0066-GPIO-add-named-gpio-exports.patch b/target/linux/ipq806x/patches-4.9/0066-GPIO-add-named-gpio-exports.patch
new file mode 100644 (file)
index 0000000..4aa8000
--- /dev/null
@@ -0,0 +1,166 @@
+From a37b0c9113647b2120cf1a18cfc46afdb3f1fccc Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 12 Aug 2014 20:49:27 +0200
+Subject: [PATCH 66/69] 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/0067-generic-Mangle-bootloader-s-kernel-arguments.patch b/target/linux/ipq806x/patches-4.9/0067-generic-Mangle-bootloader-s-kernel-arguments.patch
new file mode 100644 (file)
index 0000000..123d353
--- /dev/null
@@ -0,0 +1,189 @@
+From 71270226b14733a4b1f2cde58ea9265caa50b38d Mon Sep 17 00:00:00 2001
+From: Adrian Panella <ianchi74@outlook.com>
+Date: Thu, 9 Mar 2017 09:37:17 +0100
+Subject: [PATCH 67/69] 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>
+---
+ arch/arm/Kconfig                        | 11 +++++
+ arch/arm/boot/compressed/atags_to_fdt.c | 72 ++++++++++++++++++++++++++++++++-
+ init/main.c                             | 16 ++++++++
+ 3 files changed, 98 insertions(+), 1 deletion(-)
+
+--- 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/0068-spi-add-gpio-cs-support.patch b/target/linux/ipq806x/patches-4.9/0068-spi-add-gpio-cs-support.patch
new file mode 100644 (file)
index 0000000..0c03bc9
--- /dev/null
@@ -0,0 +1,71 @@
+From b9c998eb7735df8000cf48d77f9271823e8e73da Mon Sep 17 00:00:00 2001
+From: Ram Chandra Jangir <rjangi@codeaurora.org>
+Date: Thu, 9 Mar 2017 09:39:05 +0100
+Subject: [PATCH 68/69] spi: add gpio cs support
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/spi/spi-qup.c | 38 ++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 38 insertions(+)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -24,6 +24,7 @@
+ #include <linux/spi/spi.h>
+ #include <linux/dmaengine.h>
+ #include <linux/dma-mapping.h>
++#include <linux/gpio.h>
+ #define QUP_CONFIG                    0x0000
+ #define QUP_STATE                     0x0004
+@@ -1019,6 +1020,38 @@ err_tx:
+       return ret;
+ }
++static void spi_qup_set_cs(struct spi_device *spi, bool val)
++{
++      struct spi_qup *controller;
++      u32 spi_ioc;
++      u32 spi_ioc_orig;
++
++      controller = spi_master_get_devdata(spi->master);
++      spi_ioc = readl_relaxed(controller->base + SPI_IO_CONTROL);
++      spi_ioc_orig = spi_ioc;
++      if (!val)
++              spi_ioc |= SPI_IO_C_FORCE_CS;
++      else
++              spi_ioc &= ~SPI_IO_C_FORCE_CS;
++
++      if (spi_ioc != spi_ioc_orig)
++              writel_relaxed(spi_ioc, controller->base + SPI_IO_CONTROL);
++}
++
++static int spi_qup_setup(struct spi_device *spi)
++{
++      if (spi->cs_gpio >= 0) {
++              if (spi->mode & SPI_CS_HIGH)
++                      gpio_set_value(spi->cs_gpio, 0);
++              else
++                      gpio_set_value(spi->cs_gpio, 1);
++
++              udelay(10);
++      }
++
++      return 0;
++}
++
+ static int spi_qup_probe(struct platform_device *pdev)
+ {
+       struct spi_master *master;
+@@ -1115,6 +1148,11 @@ static int spi_qup_probe(struct platform
+       if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1"))
+               controller->qup_v1 = 1;
++      if (!controller->qup_v1)
++              master->set_cs = spi_qup_set_cs;
++      else
++              master->setup = spi_qup_setup;
++
+       spin_lock_init(&controller->lock);
+       init_completion(&controller->done);
+       init_completion(&controller->dma_tx_done);
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
deleted file mode 100644 (file)
index 5c22b79..0000000
+++ /dev/null
@@ -1,746 +0,0 @@
-From patchwork Wed Nov  2 15:56:56 2016
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-Subject: [v9,1/3] clk: qcom: Add support for SMD-RPM Clocks
-From: Georgi Djakov <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 @@ config QCOM_GDSC
-       bool
-       select PM_GENERIC_DOMAINS if PM
-+config QCOM_RPMCC
-+      bool
-+
- config COMMON_CLK_QCOM
-       tristate "Support for Qualcomm's clock controllers"
-       depends on OF
-@@ -9,6 +12,19 @@ config COMMON_CLK_QCOM
-       select REGMAP_MMIO
-       select RESET_CONTROLLER
-+config QCOM_CLK_SMD_RPM
-+      tristate "RPM over SMD based Clock Controller"
-+      depends on COMMON_CLK_QCOM && QCOM_SMD_RPM
-+      select QCOM_RPMCC
-+      help
-+        The RPM (Resource Power Manager) is a dedicated hardware engine for
-+        managing the shared SoC resources in order to keep the lowest power
-+        profile. It communicates with other hardware subsystems via shared
-+        memory and accepts clock requests, aggregates the requests and turns
-+        the clocks on/off or scales them on demand.
-+        Say Y if you want to support the clocks exposed by the RPM on
-+        platforms such as apq8016, apq8084, msm8974 etc.
-+
- config APQ_GCC_8084
-       tristate "APQ8084 Global Clock Controller"
-       select QCOM_GDSC
---- a/drivers/clk/qcom/Makefile
-+++ b/drivers/clk/qcom/Makefile
-@@ -29,3 +29,4 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm896
- 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
deleted file mode 100644 (file)
index 4933ffb..0000000
+++ /dev/null
@@ -1,586 +0,0 @@
-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
-@@ -30,3 +30,4 @@ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8
- 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
-+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
deleted file mode 100644 (file)
index e5c1870..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-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
deleted file mode 100644 (file)
index d46e97b..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-
-In commit "regulator: qcom: Rework to single platform device" the smb208 regulator
-used in IPQ8064 was left out.
-
-Add it to that new framework and update Docs accordingly.
-
-Signed-off-by: Adrian Panella <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/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch b/target/linux/ipq806x/patches-4.9/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch
deleted file mode 100644 (file)
index c308560..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-From b12e230f09d4481424e6a5d7e2ae566b6954e83f Mon Sep 17 00:00:00 2001
-From: Mathieu Olivari <mathieu@codeaurora.org>
-Date: Wed, 29 Apr 2015 15:21:46 -0700
-Subject: [PATCH] HACK: arch: arm: force ZRELADDR on arch-qcom
-
-ARCH_QCOM is using the ARCH_MULTIPLATFORM option, as now recommended
-on most ARM architectures. This automatically calculate ZRELADDR by
-masking PHYS_OFFSET with 0xf8000000.
-
-However, on IPQ806x, the first ~20MB of RAM is reserved for the hardware
-network accelerators, and the bootloader removes this section from the
-layout passed from the ATAGS (when used).
-
-For newer bootloader, when DT is used, this is not a problem, we just
-reserve this memory in the device tree. But if the bootloader doesn't
-have DT support, then ATAGS have to be used. In this case, the ARM
-decompressor will position the kernel in this low mem, which will not be
-in the RAM section mapped by the bootloader, which means the kernel will
-freeze in the middle of the boot process trying to map the memory.
-
-As a work around, this patch allows disabling AUTO_ZRELADDR when
-ARCH_QCOM is selected. It makes the zImage usage possible on bootloaders
-which don't support device-tree, which is the case on certain early
-IPQ806x based designs.
-
-Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
----
- arch/arm/Kconfig                 | 2 +-
- arch/arm/Makefile                | 2 ++
- arch/arm/mach-qcom/Makefile.boot | 1 +
- 3 files changed, 4 insertions(+), 1 deletion(-)
- create mode 100644 arch/arm/mach-qcom/Makefile.boot
-
---- a/arch/arm/Kconfig
-+++ b/arch/arm/Kconfig
-@@ -332,7 +332,7 @@ config ARCH_MULTIPLATFORM
-       depends on MMU
-       select ARM_HAS_SG_CHAIN
-       select ARM_PATCH_PHYS_VIRT
--      select AUTO_ZRELADDR
-+      select AUTO_ZRELADDR if !ARCH_QCOM
-       select CLKSRC_OF
-       select COMMON_CLK
-       select GENERIC_CLOCKEVENTS
---- a/arch/arm/Makefile
-+++ b/arch/arm/Makefile
-@@ -251,9 +251,11 @@ MACHINE  := arch/arm/mach-$(word 1,$(mac
- else
- MACHINE  :=
- endif
-+ifeq ($(CONFIG_ARCH_QCOM),)
- ifeq ($(CONFIG_ARCH_MULTIPLATFORM),y)
- MACHINE  :=
- endif
-+endif
- machdirs := $(patsubst %,arch/arm/mach-%/,$(machine-y))
- platdirs := $(patsubst %,arch/arm/plat-%/,$(sort $(plat-y)))
---- /dev/null
-+++ b/arch/arm/mach-qcom/Makefile.boot
-@@ -0,0 +1 @@
-+zreladdr-y+= 0x42208000
diff --git a/target/linux/ipq806x/patches-4.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
deleted file mode 100644 (file)
index 7a7cff3..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-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
deleted file mode 100644 (file)
index bb9dd87..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
---- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
-+++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
-@@ -12,6 +12,7 @@ Required properties :
-                       "qcom,rpmcc-msm8916", "qcom,rpmcc"
-                       "qcom,rpmcc-apq8064", "qcom,rpmcc"
-+                      "qcom,rpmcc-ipq806x", "qcom,rpmcc"
- - #clock-cells : shall contain 1
---- a/drivers/clk/qcom/clk-rpm.c
-+++ b/drivers/clk/qcom/clk-rpm.c
-@@ -359,6 +359,16 @@ DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a
- DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK);
- DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK);
-+/* ipq806x */
-+DEFINE_CLK_RPM(ipq806x, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK);
-+DEFINE_CLK_RPM(ipq806x, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK);
-+DEFINE_CLK_RPM(ipq806x, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK);
-+DEFINE_CLK_RPM(ipq806x, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK);
-+DEFINE_CLK_RPM(ipq806x, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK);
-+DEFINE_CLK_RPM(ipq806x, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK);
-+DEFINE_CLK_RPM(ipq806x, nss_fabric_0_clk, nss_fabric_0_a_clk, QCOM_RPM_NSS_FABRIC_0_CLK);
-+DEFINE_CLK_RPM(ipq806x, nss_fabric_1_clk, nss_fabric_1_a_clk, QCOM_RPM_NSS_FABRIC_1_CLK);
-+
- static struct clk_rpm *apq8064_clks[] = {
-       [RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk,
-       [RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk,
-@@ -380,13 +390,38 @@ static struct clk_rpm *apq8064_clks[] =
-       [RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk,
- };
-+static struct clk_rpm *ipq806x_clks[] = {
-+      [RPM_APPS_FABRIC_CLK] = &ipq806x_afab_clk,
-+      [RPM_APPS_FABRIC_A_CLK] = &ipq806x_afab_a_clk,
-+      [RPM_CFPB_CLK] = &ipq806x_cfpb_clk,
-+      [RPM_CFPB_A_CLK] = &ipq806x_cfpb_a_clk,
-+      [RPM_DAYTONA_FABRIC_CLK] = &ipq806x_daytona_clk,
-+      [RPM_DAYTONA_FABRIC_A_CLK] = &ipq806x_daytona_a_clk,
-+      [RPM_EBI1_CLK] = &ipq806x_ebi1_clk,
-+      [RPM_EBI1_A_CLK] = &ipq806x_ebi1_a_clk,
-+      [RPM_SYS_FABRIC_CLK] = &ipq806x_sfab_clk,
-+      [RPM_SYS_FABRIC_A_CLK] = &ipq806x_sfab_a_clk,
-+      [RPM_SFPB_CLK] = &ipq806x_sfpb_clk,
-+      [RPM_SFPB_A_CLK] = &ipq806x_sfpb_a_clk,
-+      [RPM_NSS_FABRIC_0_CLK] = &ipq806x_nss_fabric_0_clk,
-+      [RPM_NSS_FABRIC_0_A_CLK] = &ipq806x_nss_fabric_0_a_clk,
-+      [RPM_NSS_FABRIC_1_CLK] = &ipq806x_nss_fabric_1_clk,
-+      [RPM_NSS_FABRIC_1_A_CLK] = &ipq806x_nss_fabric_1_a_clk,
-+};
-+
- static const struct rpm_clk_desc rpm_clk_apq8064 = {
-       .clks = apq8064_clks,
-       .num_clks = ARRAY_SIZE(apq8064_clks),
- };
-+static const struct rpm_clk_desc rpm_clk_ipq806x = {
-+      .clks = ipq806x_clks,
-+      .num_clks = ARRAY_SIZE(ipq806x_clks),
-+};
-+
- static const struct of_device_id rpm_clk_match_table[] = {
-       { .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 },
-+      { .compatible = "qcom,rpmcc-ipq806x", .data = &rpm_clk_ipq806x },
-       { }
- };
- MODULE_DEVICE_TABLE(of, rpm_clk_match_table);
---- a/include/dt-bindings/clock/qcom,rpmcc.h
-+++ b/include/dt-bindings/clock/qcom,rpmcc.h
-@@ -37,6 +37,10 @@
- #define RPM_SYS_FABRIC_A_CLK                  19
- #define RPM_SFPB_CLK                          20
- #define RPM_SFPB_A_CLK                                21
-+#define RPM_NSS_FABRIC_0_CLK                          22
-+#define RPM_NSS_FABRIC_0_A_CLK                                23
-+#define RPM_NSS_FABRIC_1_CLK                          24
-+#define RPM_NSS_FABRIC_1_A_CLK                                25
- /* msm8916 */
- #define RPM_SMD_XO_CLK_SRC                            0
diff --git a/target/linux/ipq806x/patches-4.9/801-override-compiler-flags.patch b/target/linux/ipq806x/patches-4.9/801-override-compiler-flags.patch
deleted file mode 100644 (file)
index 6591dbd..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
---- 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
deleted file mode 100644 (file)
index 3434864..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-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
deleted file mode 100644 (file)
index 36490a2..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-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,