ipq806x: add platform usb support
authorJohn Crispin <john@openwrt.org>
Fri, 3 Apr 2015 19:06:30 +0000 (19:06 +0000)
committerJohn Crispin <john@openwrt.org>
Fri, 3 Apr 2015 19:06:30 +0000 (19:06 +0000)
This change adds DWC3 QCOM USB phys and TCSR drivers. These are
cherry-picked from the following LKML threads:
*dwc3 qcom: https://lkml.org/lkml/2014/9/12/599
*tcsr: https://lkml.org/lkml/2015/2/9/579

We're also adding an additional patch to add the corresponding dev nodes
in the IPQ806x and AP148 dts files.

Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
SVN-Revision: 45261

target/linux/ipq806x/patches-3.18/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/102-soc-qcom-gsbi-Add-support-for-ADM-CRCI-muxing.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/103-ARM-DT-ipq8064-Add-TCSR-support.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/700-add-gmac-dts-suport.patch

diff --git a/target/linux/ipq806x/patches-3.18/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches-3.18/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch
new file mode 100644 (file)
index 0000000..25803b8
--- /dev/null
@@ -0,0 +1,511 @@
+--- a/drivers/phy/Kconfig
++++ b/drivers/phy/Kconfig
+@@ -256,4 +256,15 @@ config PHY_STIH41X_USB
+         Enable this to support the USB transceiver that is part of
+         STMicroelectronics STiH41x SoC series.
++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
+@@ -31,3 +31,4 @@ obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) +=
+ obj-$(CONFIG_PHY_XGENE)                       += phy-xgene.o
+ obj-$(CONFIG_PHY_STIH407_USB)         += phy-stih407-usb.o
+ obj-$(CONFIG_PHY_STIH41X_USB)         += phy-stih41x-usb.o
++obj-$(CONFIG_PHY_QCOM_DWC3)           += phy-qcom-dwc3.o
+--- /dev/null
++++ b/drivers/phy/phy-qcom-dwc3.c
+@@ -0,0 +1,483 @@
++/* Copyright (c) 2013-2014, Code Aurora Forum. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/clk.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/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)
++
++struct qcom_dwc3_usb_phy {
++      void __iomem            *base;
++      struct device           *dev;
++      struct phy *phy;
++
++      int (*phy_init)(struct qcom_dwc3_usb_phy *phy_dwc3);
++      int (*phy_exit)(struct qcom_dwc3_usb_phy *phy_dwc3);
++
++      struct clk              *xo_clk;
++      struct clk              *ref_clk;
++};
++
++/**
++ * 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(void __iomem *base, u32 addr, u32 val)
++{
++      int ret;
++
++      writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
++      writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG);
++
++      ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG);
++      if (ret)
++              goto err_wait;
++
++      writel(val, base + CR_PROTOCOL_DATA_IN_REG);
++      writel(0x1, base + CR_PROTOCOL_CAP_DATA_REG);
++
++      ret = wait_for_latch(base + CR_PROTOCOL_CAP_DATA_REG);
++      if (ret)
++              goto err_wait;
++
++      writel(0x1, base + CR_PROTOCOL_WRITE_REG);
++
++      ret = wait_for_latch(base + CR_PROTOCOL_WRITE_REG);
++
++err_wait:
++      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;
++      bool first_read = true;
++
++      writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
++      writel(0x1, 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.
++       */
++retry:
++      writel(0x1, base + CR_PROTOCOL_READ_REG);
++
++      ret = wait_for_latch(base + CR_PROTOCOL_READ_REG);
++      if (ret)
++              goto err_wait;
++
++      if (first_read) {
++              readl(base + CR_PROTOCOL_DATA_OUT_REG);
++              first_read = false;
++              goto retry;
++      }
++
++      *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 qcom_dwc3_usb_phy *phy_dwc3)
++{
++      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 qcom_dwc3_usb_phy *phy_dwc3)
++{
++      int ret;
++      u32 data = 0;
++
++      /* reset phy */
++      data = readl_relaxed(phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++      writel_relaxed(data | SSUSB_CTRL_SS_PHY_RESET,
++              phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++      usleep_range(2000, 2200);
++      writel_relaxed(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_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++      msleep(30);
++
++      data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT;
++      writel_relaxed(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->base,
++              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->base,
++              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 qcom_dwc3_usb_phy *phy_dwc3)
++{
++      /* Sequence to put SSPHY in low power state:
++       * 1. Clear REF_PHY_EN in PHY_CTRL_REG
++       * 2. Clear REF_USE_PAD in PHY_CTRL_REG
++       * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention
++       * 4. Disable SSPHY ref clk
++       */
++      qcom_dwc3_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 int qcom_dwc3_phy_init(struct phy *phy)
++{
++      struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
++
++      if (phy_dwc3->phy_init)
++              return phy_dwc3->phy_init(phy_dwc3);
++
++      return 0;
++}
++
++static int qcom_dwc3_phy_exit(struct phy *phy)
++{
++      struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
++
++      if (phy_dwc3->phy_exit)
++              return qcom_dwc3_ss_phy_exit(phy_dwc3);
++
++      return 0;
++}
++
++static struct phy_ops qcom_dwc3_phy_ops = {
++      .init           = qcom_dwc3_phy_init,
++      .exit           = qcom_dwc3_phy_exit,
++      .power_on       = qcom_dwc3_phy_power_on,
++      .power_off      = qcom_dwc3_phy_power_off,
++      .owner          = THIS_MODULE,
++};
++
++static const struct of_device_id qcom_dwc3_phy_table[] = {
++      { .compatible = "qcom,dwc3-hs-usb-phy", },
++      { .compatible = "qcom,dwc3-ss-usb-phy", },
++      { /* 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 resource                 *res;
++
++      phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL);
++      if (!phy_dwc3)
++              return -ENOMEM;
++
++      platform_set_drvdata(pdev, phy_dwc3);
++
++      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);
++      }
++
++      if (of_device_is_compatible(pdev->dev.of_node,
++                      "qcom,dwc3-hs-usb-phy")) {
++              clk_set_rate(phy_dwc3->ref_clk, 60000000);
++              phy_dwc3->phy_init = qcom_dwc3_hs_phy_init;
++      } else if (of_device_is_compatible(pdev->dev.of_node,
++                      "qcom,dwc3-ss-usb-phy")) {
++              phy_dwc3->phy_init = qcom_dwc3_ss_phy_init;
++              phy_dwc3->phy_exit = qcom_dwc3_ss_phy_exit;
++              clk_set_rate(phy_dwc3->ref_clk, 125000000);
++      } else {
++              dev_err(phy_dwc3->dev, "Unknown phy\n");
++              return -EINVAL;
++      }
++
++      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;
++      }
++
++      phy_dwc3->phy = devm_phy_create(phy_dwc3->dev, NULL, &qcom_dwc3_phy_ops,
++                                      NULL);
++
++      if (IS_ERR(phy_dwc3->phy))
++              return PTR_ERR(phy_dwc3->phy);
++
++      phy_set_drvdata(phy_dwc3->phy, 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-3.18/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch b/target/linux/ipq806x/patches-3.18/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch
new file mode 100644 (file)
index 0000000..f1b4164
--- /dev/null
@@ -0,0 +1,125 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -112,5 +112,29 @@
+               sata@29000000 {
+                       status = "ok";
+               };
++
++              phy@100f8800 {          /* USB3 port 1 HS phy */
++                      status = "ok";
++              };
++
++              phy@100f8830 {          /* USB3 port 1 SS phy */
++                      status = "ok";
++              };
++
++              phy@110f8800 {          /* USB3 port 0 HS phy */
++                      status = "ok";
++              };
++
++              phy@110f8830 {          /* USB3 port 0 SS phy */
++                      status = "ok";
++              };
++
++              usb30@0 {
++                      status = "ok";
++              };
++
++              usb30@1 {
++                      status = "ok";
++              };
+       };
+ };
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -291,5 +291,90 @@
+                       #clock-cells = <1>;
+                       #reset-cells = <1>;
+               };
++
++              hs_phy_1: phy@100f8800 {
++                      compatible = "qcom,dwc3-hs-usb-phy";
++                      reg = <0x100f8800 0x30>;
++                      clocks = <&gcc USB30_1_UTMI_CLK>;
++                      clock-names = "ref";
++                      #phy-cells = <0>;
++
++                      status = "disabled";
++              };
++
++              ss_phy_1: phy@100f8830 {
++                      compatible = "qcom,dwc3-ss-usb-phy";
++                      reg = <0x100f8830 0x30>;
++                      clocks = <&gcc USB30_1_MASTER_CLK>;
++                      clock-names = "ref";
++                      #phy-cells = <0>;
++
++                      status = "disabled";
++              };
++
++              hs_phy_0: phy@110f8800 {
++                      compatible = "qcom,dwc3-hs-usb-phy";
++                      reg = <0x110f8800 0x30>;
++                      clocks = <&gcc USB30_0_UTMI_CLK>;
++                      clock-names = "ref";
++                      #phy-cells = <0>;
++
++                      status = "disabled";
++              };
++
++              ss_phy_0: phy@110f8830 {
++                      compatible = "qcom,dwc3-ss-usb-phy";
++                      reg = <0x110f8830 0x30>;
++                      clocks = <&gcc USB30_0_MASTER_CLK>;
++                      clock-names = "ref";
++                      #phy-cells = <0>;
++
++                      status = "disabled";
++              };
++
++              usb3_0: usb30@0 {
++                      compatible = "qcom,dwc3";
++                      #address-cells = <1>;
++                      #size-cells = <1>;
++                      clocks = <&gcc USB30_0_MASTER_CLK>;
++                      clock-names = "core";
++
++                      ranges;
++
++                      status = "disabled";
++
++                      dwc3@11000000 {
++                              compatible = "snps,dwc3";
++                              reg = <0x11000000 0xcd00>;
++                              interrupts = <0 110 0x4>;
++                              phys = <&hs_phy_0>, <&ss_phy_0>;
++                              phy-names = "usb2-phy", "usb3-phy";
++                              tx-fifo-resize;
++                              dr_mode = "host";
++                      };
++              };
++
++              usb3_1: usb30@1 {
++                      compatible = "qcom,dwc3";
++                      #address-cells = <1>;
++                      #size-cells = <1>;
++                      clocks = <&gcc USB30_1_MASTER_CLK>;
++                      clock-names = "core";
++
++                      ranges;
++
++                      status = "disabled";
++
++                      dwc3@10000000 {
++                              compatible = "snps,dwc3";
++                              reg = <0x10000000 0xcd00>;
++                              interrupts = <0 205 0x4>;
++                              phys = <&hs_phy_1>, <&ss_phy_1>;
++                              phy-names = "usb2-phy", "usb3-phy";
++                              tx-fifo-resize;
++                              dr_mode = "host";
++                      };
++              };
++
+       };
+ };
diff --git a/target/linux/ipq806x/patches-3.18/102-soc-qcom-gsbi-Add-support-for-ADM-CRCI-muxing.patch b/target/linux/ipq806x/patches-3.18/102-soc-qcom-gsbi-Add-support-for-ADM-CRCI-muxing.patch
new file mode 100644 (file)
index 0000000..f37ebeb
--- /dev/null
@@ -0,0 +1,258 @@
+diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt
+index 4ce24d4..186e5f4 100644
+--- a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt
++++ b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt
+@@ -6,7 +6,8 @@ configuration settings.  The mode setting will govern the input/output mode of
+ the 4 GSBI IOs.
+ Required properties:
+-- compatible: must contain "qcom,gsbi-v1.0.0" for APQ8064/IPQ8064
++- compatible: Should contain "qcom,gsbi-v1.0.0"
++- cell-index: Should contain the GSBI index
+ - reg: Address range for GSBI registers
+ - clocks: required clock
+ - clock-names: must contain "iface" entry
+@@ -16,6 +17,8 @@ Required properties:
+ Optional properties:
+ - qcom,crci : indicates CRCI MUX value for QUP CRCI ports.  Please reference
+   dt-bindings/soc/qcom,gsbi.h for valid CRCI mux values.
++- syscon-tcsr: indicates phandle of TCSR syscon node.  Required if child uses
++  dma.
+ Required properties if child node exists:
+ - #address-cells: Must be 1
+@@ -39,6 +42,7 @@ Example for APQ8064:
+       gsbi4@16300000 {
+               compatible = "qcom,gsbi-v1.0.0";
++              cell-index = <4>;
+               reg = <0x16300000 0x100>;
+               clocks = <&gcc GSBI4_H_CLK>;
+               clock-names = "iface";
+@@ -48,6 +52,8 @@ Example for APQ8064:
+               qcom,mode = <GSBI_PROT_I2C_UART>;
+               qcom,crci = <GSBI_CRCI_QUP>;
++              syscon-tcsr = <&tcsr>;
++
+               /* child nodes go under here */
+               i2c_qup4: i2c@16380000 {
+@@ -76,3 +82,9 @@ Example for APQ8064:
+               };
+       };
++      tcsr: syscon@1a400000 {
++              compatible = "qcom,apq8064-tcsr", "syscon";
++              reg = <0x1a400000 0x100>;
++      };
++
++
+diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
+index 7bd2c94..460b2db 100644
+--- a/drivers/soc/qcom/Kconfig
++++ b/drivers/soc/qcom/Kconfig
+@@ -4,6 +4,7 @@
+ config QCOM_GSBI
+         tristate "QCOM General Serial Bus Interface"
+         depends on ARCH_QCOM
++        select MFD_SYSCON
+         help
+           Say y here to enable GSBI support.  The GSBI provides control
+           functions for connecting the underlying serial UART, SPI, and I2C
+diff --git a/drivers/soc/qcom/qcom_gsbi.c b/drivers/soc/qcom/qcom_gsbi.c
+index 729425d..09c669e 100644
+--- a/drivers/soc/qcom/qcom_gsbi.c
++++ b/drivers/soc/qcom/qcom_gsbi.c
+@@ -18,22 +18,129 @@
+ #include <linux/of.h>
+ #include <linux/of_platform.h>
+ #include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/mfd/syscon.h>
++#include <dt-bindings/soc/qcom,gsbi.h>
+ #define GSBI_CTRL_REG         0x0000
+ #define GSBI_PROTOCOL_SHIFT   4
++#define MAX_GSBI              12
++
++#define TCSR_ADM_CRCI_BASE    0x70
++
++struct crci_config {
++      u32 num_rows;
++      const u32 (*array)[MAX_GSBI];
++};
++
++static const u32 crci_ipq8064[][MAX_GSBI] = {
++      {
++              0x000003, 0x00000c, 0x000030, 0x0000c0,
++              0x000300, 0x000c00, 0x003000, 0x00c000,
++              0x030000, 0x0c0000, 0x300000, 0xc00000
++      },
++      {
++              0x000003, 0x00000c, 0x000030, 0x0000c0,
++              0x000300, 0x000c00, 0x003000, 0x00c000,
++              0x030000, 0x0c0000, 0x300000, 0xc00000
++      },
++};
++
++static const struct crci_config config_ipq8064 = {
++      .num_rows = ARRAY_SIZE(crci_ipq8064),
++      .array = crci_ipq8064,
++};
++
++static const unsigned int crci_apq8064[][MAX_GSBI] = {
++      {
++              0x001800, 0x006000, 0x000030, 0x0000c0,
++              0x000300, 0x000400, 0x000000, 0x000000,
++              0x000000, 0x000000, 0x000000, 0x000000
++      },
++      {
++              0x000000, 0x000000, 0x000000, 0x000000,
++              0x000000, 0x000020, 0x0000c0, 0x000000,
++              0x000000, 0x000000, 0x000000, 0x000000
++      },
++};
++
++static const struct crci_config config_apq8064 = {
++      .num_rows = ARRAY_SIZE(crci_apq8064),
++      .array = crci_apq8064,
++};
++
++static const unsigned int crci_msm8960[][MAX_GSBI] = {
++      {
++              0x000003, 0x00000c, 0x000030, 0x0000c0,
++              0x000300, 0x000400, 0x000000, 0x000000,
++              0x000000, 0x000000, 0x000000, 0x000000
++      },
++      {
++              0x000000, 0x000000, 0x000000, 0x000000,
++              0x000000, 0x000020, 0x0000c0, 0x000300,
++              0x001800, 0x006000, 0x000000, 0x000000
++      },
++};
++
++static const struct crci_config config_msm8960 = {
++      .num_rows = ARRAY_SIZE(crci_msm8960),
++      .array = crci_msm8960,
++};
++
++static const unsigned int crci_msm8660[][MAX_GSBI] = {
++      {       /* ADM 0 - B */
++              0x000003, 0x00000c, 0x000030, 0x0000c0,
++              0x000300, 0x000c00, 0x003000, 0x00c000,
++              0x030000, 0x0c0000, 0x300000, 0xc00000
++      },
++      {       /* ADM 0 - B */
++              0x000003, 0x00000c, 0x000030, 0x0000c0,
++              0x000300, 0x000c00, 0x003000, 0x00c000,
++              0x030000, 0x0c0000, 0x300000, 0xc00000
++      },
++      {       /* ADM 1 - A */
++              0x000003, 0x00000c, 0x000030, 0x0000c0,
++              0x000300, 0x000c00, 0x003000, 0x00c000,
++              0x030000, 0x0c0000, 0x300000, 0xc00000
++      },
++      {       /* ADM 1 - B */
++              0x000003, 0x00000c, 0x000030, 0x0000c0,
++              0x000300, 0x000c00, 0x003000, 0x00c000,
++              0x030000, 0x0c0000, 0x300000, 0xc00000
++      },
++};
++
++static const struct crci_config config_msm8660 = {
++      .num_rows = ARRAY_SIZE(crci_msm8660),
++      .array = crci_msm8660,
++};
+ struct gsbi_info {
+       struct clk *hclk;
+       u32 mode;
+       u32 crci;
++      struct regmap *tcsr;
++};
++
++static const struct of_device_id tcsr_dt_match[] = {
++      { .compatible = "qcom,tcsr-ipq8064", .data = &config_ipq8064},
++      { .compatible = "qcom,tcsr-apq8064", .data = &config_apq8064},
++      { .compatible = "qcom,tcsr-msm8960", .data = &config_msm8960},
++      { .compatible = "qcom,tcsr-msm8660", .data = &config_msm8660},
++      { },
+ };
+ static int gsbi_probe(struct platform_device *pdev)
+ {
+       struct device_node *node = pdev->dev.of_node;
++      struct device_node *tcsr_node;
++      const struct of_device_id *match;
+       struct resource *res;
+       void __iomem *base;
+       struct gsbi_info *gsbi;
++      int i;
++      u32 mask, gsbi_num;
++      const struct crci_config *config = NULL;
+       gsbi = devm_kzalloc(&pdev->dev, sizeof(*gsbi), GFP_KERNEL);
+@@ -45,6 +152,32 @@ static int gsbi_probe(struct platform_device *pdev)
+       if (IS_ERR(base))
+               return PTR_ERR(base);
++      /* get the tcsr node and setup the config and regmap */
++      gsbi->tcsr = syscon_regmap_lookup_by_phandle(node, "syscon-tcsr");
++
++      if (!IS_ERR(gsbi->tcsr)) {
++              tcsr_node = of_parse_phandle(node, "syscon-tcsr", 0);
++              if (tcsr_node) {
++                      match = of_match_node(tcsr_dt_match, tcsr_node);
++                      if (match)
++                              config = match->data;
++                      else
++                              dev_warn(&pdev->dev, "no matching TCSR\n");
++
++                      of_node_put(tcsr_node);
++              }
++      }
++
++      if (of_property_read_u32(node, "cell-index", &gsbi_num)) {
++              dev_err(&pdev->dev, "missing cell-index\n");
++              return -EINVAL;
++      }
++
++      if (gsbi_num < 1 || gsbi_num > MAX_GSBI) {
++              dev_err(&pdev->dev, "invalid cell-index\n");
++              return -EINVAL;
++      }
++
+       if (of_property_read_u32(node, "qcom,mode", &gsbi->mode)) {
+               dev_err(&pdev->dev, "missing mode configuration\n");
+               return -EINVAL;
+@@ -64,6 +197,25 @@ static int gsbi_probe(struct platform_device *pdev)
+       writel_relaxed((gsbi->mode << GSBI_PROTOCOL_SHIFT) | gsbi->crci,
+                               base + GSBI_CTRL_REG);
++      /*
++       * modify tcsr to reflect mode and ADM CRCI mux
++       * Each gsbi contains a pair of bits, one for RX and one for TX
++       * SPI mode requires both bits cleared, otherwise they are set
++       */
++      if (config) {
++              for (i = 0; i < config->num_rows; i++) {
++                      mask = config->array[i][gsbi_num - 1];
++
++                      if (gsbi->mode == GSBI_PROT_SPI)
++                              regmap_update_bits(gsbi->tcsr,
++                                      TCSR_ADM_CRCI_BASE + 4 * i, mask, 0);
++                      else
++                              regmap_update_bits(gsbi->tcsr,
++                                      TCSR_ADM_CRCI_BASE + 4 * i, mask, mask);
++
++              }
++      }
++
+       /* make sure the gsbi control write is not reordered */
+       wmb();
+-- 
+1.9.1
+
diff --git a/target/linux/ipq806x/patches-3.18/103-ARM-DT-ipq8064-Add-TCSR-support.patch b/target/linux/ipq806x/patches-3.18/103-ARM-DT-ipq8064-Add-TCSR-support.patch
new file mode 100644 (file)
index 0000000..322e1d9
--- /dev/null
@@ -0,0 +1,65 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -132,6 +132,7 @@
+               gsbi2: gsbi@12480000 {
+                       compatible = "qcom,gsbi-v1.0.0";
++                      cell-index = <2>;
+                       reg = <0x12480000 0x100>;
+                       clocks = <&gcc GSBI2_H_CLK>;
+                       clock-names = "iface";
+@@ -140,6 +141,8 @@
+                       ranges;
+                       status = "disabled";
++                      syscon-tcsr = <&tcsr>;
++
+                       uart2: serial@12490000 {
+                               compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+                               reg = <0x12490000 0x1000>,
+@@ -167,6 +170,7 @@
+               gsbi4: gsbi@16300000 {
+                       compatible = "qcom,gsbi-v1.0.0";
++                      cell-index = <4>;
+                       reg = <0x16300000 0x100>;
+                       clocks = <&gcc GSBI4_H_CLK>;
+                       clock-names = "iface";
+@@ -175,6 +179,8 @@
+                       ranges;
+                       status = "disabled";
++                      syscon-tcsr = <&tcsr>;
++
+                       uart4: serial@16340000 {
+                               compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+                               reg = <0x16340000 0x1000>,
+@@ -201,6 +207,7 @@
+               gsbi5: gsbi@1a200000 {
+                       compatible = "qcom,gsbi-v1.0.0";
++                      cell-index = <5>;
+                       reg = <0x1a200000 0x100>;
+                       clocks = <&gcc GSBI5_H_CLK>;
+                       clock-names = "iface";
+@@ -209,6 +216,8 @@
+                       ranges;
+                       status = "disabled";
++                      syscon-tcsr = <&tcsr>;
++
+                       uart5: serial@1a240000 {
+                               compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+                               reg = <0x1a240000 0x1000>,
+@@ -279,6 +288,11 @@
+                       status = "disabled";
+               };
++              tcsr: syscon@1a400000 {
++                      compatible = "qcom,tcsr-ipq8064", "syscon";
++                      reg = <0x1a400000 0x100>;
++              };
++
+               qcom,ssbi@500000 {
+                       compatible = "qcom,ssbi";
+                       reg = <0x00500000 0x1000>;
index 89ebe66946f0deac63f200f821aa2da4b5146347..348f24b921ac2a80ca960f8a88465e6129732e6e 100644 (file)
@@ -1,7 +1,7 @@
 --- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
 +++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
-@@ -18,8 +18,15 @@
-               bootargs = "console=ttyMSM0,115200 root=/dev/mtdblock12 rootfstype=squashfs,jffs2";
+@@ -22,8 +22,15 @@
+               linux,stdout-path = "serial0:115200n8";
        };
  
 +      aliases {
@@ -16,7 +16,7 @@
                        i2c4_pins: i2c4_pinmux {
                                pins = "gpio12", "gpio13";
                                function = "gsbi4";
-@@ -34,6 +41,25 @@
+@@ -38,6 +45,25 @@
                                        bias-none;
                                };
                        };
                };
  
                gsbi@16300000 {
-@@ -72,6 +98,7 @@
+@@ -76,6 +102,7 @@
                                        #size-cells = <1>;
                                        spi-max-frequency = <50000000>;
                                        reg = <0>;
 +                                      m25p,fast-read;
  
                                        partition@0 {
-                                               label = "0:SBL1";
-@@ -148,5 +175,66 @@
-               sata@29000000 {
+                                               label = "lowlevel_init";
+@@ -136,5 +163,66 @@
+               usb30@1 {
                        status = "ok";
                };
 +
  
  / {
        model = "Qualcomm IPQ8064";
-@@ -279,5 +280,42 @@
-                       #clock-cells = <1>;
-                       #reset-cells = <1>;
+@@ -390,5 +391,41 @@
+                       };
                };
-+
 +              nss-gmac-common {
 +                      reg = <0x03000000 0x0000FFFF 0x1bb00000 0x0000FFFF 0x00900000 0x00004000>;
 +                      reg-names = "nss_reg_base" , "qsgmii_reg_base", "clk_ctl_base";