* [PATCH v2 1/6] resource: Add device-managed request/release_resource()
2014-08-26 14:57 [PATCH v2 0/6] PCI: tegra: Fixes and Tegra124 support Thierry Reding
@ 2014-08-26 14:57 ` Thierry Reding
2014-08-26 14:57 ` [PATCH v2 2/6] PCI: tegra: Implement a proper resource hierarchy Thierry Reding
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Thierry Reding @ 2014-08-26 14:57 UTC (permalink / raw)
To: Bjorn Helgaas; +Cc: Stephen Warren, linux-pci, linux-tegra, linux-kernel
From: Thierry Reding <treding@nvidia.com>
Provide device-managed implementations of the request_resource() and
release_resource() functions. Upon failure to request a resource, the
new devm_request_resource() function will output an error message for
consistent error reporting.
Acked-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v2:
- use more natural flow for error handling
- don't needlessly check for NULL pointers
Documentation/driver-model/devres.txt | 2 +
include/linux/ioport.h | 5 +++
kernel/resource.c | 70 +++++++++++++++++++++++++++++++++++
3 files changed, 77 insertions(+)
diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
index d14710b04439..befc3fe12ba6 100644
--- a/Documentation/driver-model/devres.txt
+++ b/Documentation/driver-model/devres.txt
@@ -264,8 +264,10 @@ IIO
IO region
devm_release_mem_region()
devm_release_region()
+ devm_release_resource()
devm_request_mem_region()
devm_request_region()
+ devm_request_resource()
IOMAP
devm_ioport_map()
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 142ec544167c..2c5250222278 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -215,6 +215,11 @@ static inline int __deprecated check_region(resource_size_t s,
/* Wrappers for managed devices */
struct device;
+
+extern int devm_request_resource(struct device *dev, struct resource *root,
+ struct resource *new);
+extern void devm_release_resource(struct device *dev, struct resource *new);
+
#define devm_request_region(dev,start,n,name) \
__devm_request_region(dev, &ioport_resource, (start), (n), (name))
#define devm_request_mem_region(dev,start,n,name) \
diff --git a/kernel/resource.c b/kernel/resource.c
index 60c5a3856ab7..46322019ab7d 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -1245,6 +1245,76 @@ int release_mem_region_adjustable(struct resource *parent,
/*
* Managed region resource
*/
+static void devm_resource_release(struct device *dev, void *ptr)
+{
+ struct resource **r = ptr;
+
+ release_resource(*r);
+}
+
+/**
+ * devm_request_resource() - request and reserve an I/O or memory resource
+ * @dev: device for which to request the resource
+ * @root: root of the resource tree from which to request the resource
+ * @new: descriptor of the resource to request
+ *
+ * This is a device-managed version of request_resource(). There is usually
+ * no need to release resources requested by this function explicitly since
+ * that will be taken care of when the device is unbound from its driver.
+ * If for some reason the resource needs to be released explicitly, because
+ * of ordering issues for example, drivers must call devm_release_resource()
+ * rather than the regular release_resource().
+ *
+ * When a conflict is detected between any existing resources and the newly
+ * requested resource, an error message will be printed.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int devm_request_resource(struct device *dev, struct resource *root,
+ struct resource *new)
+{
+ struct resource *conflict, **ptr;
+
+ ptr = devres_alloc(devm_resource_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ *ptr = new;
+
+ conflict = request_resource_conflict(root, new);
+ if (conflict) {
+ dev_err(dev, "resource collision: %pR conflicts with %s %pR\n",
+ new, conflict->name, conflict);
+ devres_free(ptr);
+ return -EBUSY;
+ }
+
+ devres_add(dev, ptr);
+ return 0;
+}
+EXPORT_SYMBOL(devm_request_resource);
+
+static int devm_resource_match(struct device *dev, void *res, void *data)
+{
+ struct resource **ptr = res;
+
+ return *ptr == data;
+}
+
+/**
+ * devm_release_resource() - release a previously requested resource
+ * @dev: device for which to release the resource
+ * @new: descriptor of the resource to release
+ *
+ * Releases a resource previously requested using devm_request_resource().
+ */
+void devm_release_resource(struct device *dev, struct resource *new)
+{
+ WARN_ON(devres_release(dev, devm_resource_release, devm_resource_match,
+ new));
+}
+EXPORT_SYMBOL(devm_release_resource);
+
struct region_devres {
struct resource *parent;
resource_size_t start;
--
2.0.4
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 2/6] PCI: tegra: Implement a proper resource hierarchy
2014-08-26 14:57 [PATCH v2 0/6] PCI: tegra: Fixes and Tegra124 support Thierry Reding
2014-08-26 14:57 ` [PATCH v2 1/6] resource: Add device-managed request/release_resource() Thierry Reding
@ 2014-08-26 14:57 ` Thierry Reding
2014-08-26 14:57 ` [PATCH v2 3/6] PCI: tegra: Clear CLKREQ# enable on port disable Thierry Reding
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Thierry Reding @ 2014-08-26 14:57 UTC (permalink / raw)
To: Bjorn Helgaas; +Cc: Stephen Warren, linux-pci, linux-tegra, linux-kernel
From: Thierry Reding <treding@nvidia.com>
Currently the resource hierarchy generated from the PCIe host bridge is
completely flat:
$ cat /proc/iomem
00000000-00000fff : /pcie-controller@00003000/pci@1,0
00003000-000037ff : pads
00003800-000039ff : afi
10000000-1fffffff : cs
28000000-28003fff : r8169
28004000-28004fff : r8169
...
The host bridge driver doesn't request all the resources that are used.
Windows allocated to each of the root ports aren't tracked, so there is
no way for resources allocated to individual devices to be matched up
with the correct parent resource by the PCI core.
This patch addresses this in two steps. It first takes the union of all
regions associated with the PCIe host bridge (control registers, root
port registers, configuration space, I/O and prefetchable as well as
non-prefetchable memory regions) and uses it as the new root of the
resource hierarchy.
Subsequently, regions are allocated from within this new root resource
so that the resource tree looks much more like what's expected:
# cat /proc/iomem
00000000-3fffffff : /pcie-controller@00003000
00000000-00000fff : /pcie-controller@00003000/pci@1,0
00003000-000037ff : pads
00003800-000039ff : afi
10000000-1fffffff : cs
20000000-27ffffff : non-prefetchable
28000000-3fffffff : prefetchable
28000000-280fffff : PCI Bus 0000:01
28000000-28003fff : 0000:01:00.0
28000000-28003fff : r8169
28004000-28004fff : 0000:01:00.0
28004000-28004fff : r8169
...
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
drivers/pci/host/pci-tegra.c | 32 +++++++++++++++++++++++++++++---
1 file changed, 29 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c
index 0fb0fdb223d5..83fa33e16650 100644
--- a/drivers/pci/host/pci-tegra.c
+++ b/drivers/pci/host/pci-tegra.c
@@ -253,6 +253,7 @@ struct tegra_pcie {
struct list_head buses;
struct resource *cs;
+ struct resource all;
struct resource io;
struct resource mem;
struct resource prefetch;
@@ -626,6 +627,15 @@ DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable);
static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
{
struct tegra_pcie *pcie = sys_to_pcie(sys);
+ int err;
+
+ err = devm_request_resource(pcie->dev, &pcie->all, &pcie->mem);
+ if (err < 0)
+ return err;
+
+ err = devm_request_resource(pcie->dev, &pcie->all, &pcie->prefetch);
+ if (err)
+ return err;
pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset);
pci_add_resource_offset(&sys->resources, &pcie->prefetch,
@@ -1514,6 +1524,12 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
struct resource res;
int err;
+ memset(&pcie->all, 0, sizeof(pcie->all));
+ pcie->all.flags = IORESOURCE_MEM;
+ pcie->all.name = np->full_name;
+ pcie->all.start = ~0;
+ pcie->all.end = 0;
+
if (of_pci_range_parser_init(&parser, np)) {
dev_err(pcie->dev, "missing \"ranges\" property\n");
return -EINVAL;
@@ -1525,21 +1541,31 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
switch (res.flags & IORESOURCE_TYPE_BITS) {
case IORESOURCE_IO:
memcpy(&pcie->io, &res, sizeof(res));
- pcie->io.name = "I/O";
+ pcie->io.name = np->full_name;
break;
case IORESOURCE_MEM:
if (res.flags & IORESOURCE_PREFETCH) {
memcpy(&pcie->prefetch, &res, sizeof(res));
- pcie->prefetch.name = "PREFETCH";
+ pcie->prefetch.name = "prefetchable";
} else {
memcpy(&pcie->mem, &res, sizeof(res));
- pcie->mem.name = "MEM";
+ pcie->mem.name = "non-prefetchable";
}
break;
}
+
+ if (res.start <= pcie->all.start)
+ pcie->all.start = res.start;
+
+ if (res.end >= pcie->all.end)
+ pcie->all.end = res.end;
}
+ err = devm_request_resource(pcie->dev, &iomem_resource, &pcie->all);
+ if (err < 0)
+ return err;
+
err = of_pci_parse_bus_range(np, &pcie->busn);
if (err < 0) {
dev_err(pcie->dev, "failed to parse ranges property: %d\n",
--
2.0.4
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 6/6] PCI: tegra: Add Tegra124 support
2014-08-26 14:57 [PATCH v2 0/6] PCI: tegra: Fixes and Tegra124 support Thierry Reding
` (4 preceding siblings ...)
2014-08-26 14:57 ` [PATCH v2 5/6] PCI: tegra: Make sure the PCIe PLL is really reset Thierry Reding
@ 2014-08-26 14:57 ` Thierry Reding
2014-08-26 15:08 ` [PATCH v2 0/6] PCI: tegra: Fixes and " Thierry Reding
6 siblings, 0 replies; 8+ messages in thread
From: Thierry Reding @ 2014-08-26 14:57 UTC (permalink / raw)
To: Bjorn Helgaas; +Cc: Stephen Warren, linux-pci, linux-tegra, linux-kernel
From: Thierry Reding <treding@nvidia.com>
The PCIe controller on Tegra124 has two root ports that can be used in a
x4/x1 or x2/x1 configuration and can run at PCIe 2.0 link speeds (up to
5 GT/s). The PHY programming has been moved into a separate controller,
so the driver now needs to request an external PHY referenced using the
device tree.
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
.../bindings/pci/nvidia,tegra20-pcie.txt | 25 ++-
drivers/pci/host/pci-tegra.c | 211 ++++++++++++++++++---
2 files changed, 204 insertions(+), 32 deletions(-)
diff --git a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
index 0823362548dc..bc0b09b0dfb5 100644
--- a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
+++ b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
@@ -1,7 +1,10 @@
NVIDIA Tegra PCIe controller
Required properties:
-- compatible: "nvidia,tegra20-pcie" or "nvidia,tegra30-pcie"
+- compatible: Must be one of:
+ - "nvidia,tegra20-pcie"
+ - "nvidia,tegra30-pcie"
+ - "nvidia,tegra124-pcie"
- device_type: Must be "pci"
- reg: A list of physical base address and length for each set of controller
registers. Must contain an entry for each entry in the reg-names property.
@@ -57,6 +60,11 @@ Required properties:
- afi
- pcie_x
+Required properties on Tegra124 and later:
+- phys: Must contain an entry for each entry in phy-names.
+- phy-names: Must include the following entries:
+ - pcie
+
Power supplies for Tegra20:
- avdd-pex-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
- vdd-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
@@ -84,6 +92,21 @@ Power supplies for Tegra30:
- avdd-pexb-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
- vdd-pexb-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
+Power supplies for Tegra124:
+- Required:
+ - avddio-pex-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
+ - vddio-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
+ - avdd-pex-pll-supply: Power supply for dedicated (internal) PCIe PLL. Must
+ supply 1.05 V.
+ - hvdd-pex-supply: High-voltage supply for PCIe I/O and PCIe output clocks.
+ Must supply 3.3 V.
+ - hvdd-pex-plle-supply: High-voltage supply for PLLE (shared with USB3).
+ Must supply 3.3 V.
+ - vddio-pex-ctl-supply: Power supply for PCIe control I/O partition. Must
+ supply 2.8-3.3 V.
+ - avdd-plle-supply: Power supply for PLLE (shared with USB3). Must
+ supply 1.05 V.
+
Root ports are defined as subnodes of the PCIe controller node.
Required properties:
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c
index 0ce43764dd36..a04525a93487 100644
--- a/drivers/pci/host/pci-tegra.c
+++ b/drivers/pci/host/pci-tegra.c
@@ -38,6 +38,7 @@
#include <linux/of_pci.h>
#include <linux/of_platform.h>
#include <linux/pci.h>
+#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/sizes.h>
@@ -115,13 +116,20 @@
#define AFI_INTR_CODE 0xb8
#define AFI_INTR_CODE_MASK 0xf
-#define AFI_INTR_AXI_SLAVE_ERROR 1
-#define AFI_INTR_AXI_DECODE_ERROR 2
+#define AFI_INTR_INI_SLAVE_ERROR 1
+#define AFI_INTR_INI_DECODE_ERROR 2
#define AFI_INTR_TARGET_ABORT 3
#define AFI_INTR_MASTER_ABORT 4
#define AFI_INTR_INVALID_WRITE 5
#define AFI_INTR_LEGACY 6
#define AFI_INTR_FPCI_DECODE_ERROR 7
+#define AFI_INTR_AXI_DECODE_ERROR 8
+#define AFI_INTR_FPCI_TIMEOUT 9
+#define AFI_INTR_PE_PRSNT_SENSE 10
+#define AFI_INTR_PE_CLKREQ_SENSE 11
+#define AFI_INTR_CLKCLAMP_SENSE 12
+#define AFI_INTR_RDY4PD_SENSE 13
+#define AFI_INTR_P2P_ERROR 14
#define AFI_INTR_SIGNATURE 0xbc
#define AFI_UPPER_FPCI_ADDRESS 0xc0
@@ -152,8 +160,10 @@
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK (0xf << 20)
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE (0x0 << 20)
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420 (0x0 << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1 (0x0 << 20)
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL (0x1 << 20)
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222 (0x1 << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1 (0x1 << 20)
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 20)
#define AFI_FUSE 0x104
@@ -165,12 +175,21 @@
#define AFI_PEX_CTRL_RST (1 << 0)
#define AFI_PEX_CTRL_CLKREQ_EN (1 << 1)
#define AFI_PEX_CTRL_REFCLK_EN (1 << 3)
+#define AFI_PEX_CTRL_OVERRIDE_EN (1 << 4)
+
+#define AFI_PLLE_CONTROL 0x160
+#define AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL (1 << 9)
+#define AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN (1 << 1)
#define AFI_PEXBIAS_CTRL_0 0x168
#define RP_VEND_XP 0x00000F00
#define RP_VEND_XP_DL_UP (1 << 30)
+#define RP_PRIV_MISC 0x00000FE0
+#define RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xE << 0)
+#define RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xF << 0)
+
#define RP_LINK_CONTROL_STATUS 0x00000090
#define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000
#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000
@@ -197,6 +216,7 @@
#define PADS_REFCLK_CFG0 0x000000C8
#define PADS_REFCLK_CFG1 0x000000CC
+#define PADS_REFCLK_BIAS 0x000000D0
/*
* Fields in PADS_REFCLK_CFG*. Those registers form an array of 16-bit
@@ -236,6 +256,7 @@ struct tegra_pcie_soc_data {
bool has_pex_bias_ctrl;
bool has_intr_prsnt_sense;
bool has_cml_clk;
+ bool has_gen2;
};
static inline struct tegra_msi *to_tegra_msi(struct msi_chip *chip)
@@ -268,6 +289,8 @@ struct tegra_pcie {
struct reset_control *afi_rst;
struct reset_control *pcie_xrst;
+ struct phy *phy;
+
struct tegra_msi msi;
struct list_head ports;
@@ -562,6 +585,8 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
if (soc->has_pex_clkreq_en)
value |= AFI_PEX_CTRL_CLKREQ_EN;
+ value |= AFI_PEX_CTRL_OVERRIDE_EN;
+
afi_writel(port->pcie, value, ctrl);
tegra_pcie_port_reset(port);
@@ -699,9 +724,15 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg)
"Target abort",
"Master abort",
"Invalid write",
+ "Legacy interrupt",
"Response decoding error",
"AXI response decoding error",
"Transaction timeout",
+ "Slot present pin change",
+ "Slot clock request change",
+ "TMS clock ramp change",
+ "TMS ready for power down",
+ "Peer2Peer error",
};
struct tegra_pcie *pcie = arg;
u32 code, signature;
@@ -807,30 +838,27 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
afi_writel(pcie, 0, AFI_MSI_BAR_SZ);
}
-static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
+static int tegra_pcie_pll_wait(struct tegra_pcie *pcie, unsigned long timeout)
{
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
- struct tegra_pcie_port *port;
- unsigned int timeout;
- unsigned long value;
-
- /* power down PCIe slot clock bias pad */
- if (soc->has_pex_bias_ctrl)
- afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0);
+ u32 value;
- /* configure mode and disable all ports */
- value = afi_readl(pcie, AFI_PCIE_CONFIG);
- value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK;
- value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar_config;
+ timeout = jiffies + msecs_to_jiffies(timeout);
- list_for_each_entry(port, &pcie->ports, list)
- value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index);
+ while (time_before(jiffies, timeout)) {
+ value = pads_readl(pcie, soc->pads_pll_ctl);
+ if (value & PADS_PLL_CTL_LOCKDET)
+ return 0;
+ }
- afi_writel(pcie, value, AFI_PCIE_CONFIG);
+ return -ETIMEDOUT;
+}
- value = afi_readl(pcie, AFI_FUSE);
- value |= AFI_FUSE_PCIE_T0_GEN2_DIS;
- afi_writel(pcie, value, AFI_FUSE);
+static int tegra_pcie_phy_enable(struct tegra_pcie *pcie)
+{
+ const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+ u32 value;
+ int err;
/* initialize internal PHY, enable up to 16 PCIE lanes */
pads_writel(pcie, 0x0, PADS_CTL_SEL);
@@ -868,15 +896,11 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
pads_writel(pcie, PADS_REFCLK_CFG_VALUE, PADS_REFCLK_CFG1);
/* wait for the PLL to lock */
- timeout = 300;
- do {
- value = pads_readl(pcie, soc->pads_pll_ctl);
- usleep_range(1000, 2000);
- if (--timeout == 0) {
- pr_err("Tegra PCIe error: timeout waiting for PLL\n");
- return -EBUSY;
- }
- } while (!(value & PADS_PLL_CTL_LOCKDET));
+ err = tegra_pcie_pll_wait(pcie, 500);
+ if (err < 0) {
+ dev_err(pcie->dev, "PLL failed to lock: %d\n", err);
+ return err;
+ }
/* turn off IDDQ override */
value = pads_readl(pcie, PADS_CTL);
@@ -888,6 +912,58 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
value |= PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L;
pads_writel(pcie, value, PADS_CTL);
+ return 0;
+}
+
+static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
+{
+ const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+ struct tegra_pcie_port *port;
+ unsigned long value;
+ int err;
+
+ /* enable PLL power down */
+ if (pcie->phy) {
+ value = afi_readl(pcie, AFI_PLLE_CONTROL);
+ value &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL;
+ value |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN;
+ afi_writel(pcie, value, AFI_PLLE_CONTROL);
+ }
+
+ /* power down PCIe slot clock bias pad */
+ if (soc->has_pex_bias_ctrl)
+ afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0);
+
+ /* configure mode and disable all ports */
+ value = afi_readl(pcie, AFI_PCIE_CONFIG);
+ value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK;
+ value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar_config;
+
+ list_for_each_entry(port, &pcie->ports, list)
+ value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index);
+
+ afi_writel(pcie, value, AFI_PCIE_CONFIG);
+
+ if (soc->has_gen2) {
+ value = afi_readl(pcie, AFI_FUSE);
+ value &= ~AFI_FUSE_PCIE_T0_GEN2_DIS;
+ afi_writel(pcie, value, AFI_FUSE);
+ } else {
+ value = afi_readl(pcie, AFI_FUSE);
+ value |= AFI_FUSE_PCIE_T0_GEN2_DIS;
+ afi_writel(pcie, value, AFI_FUSE);
+ }
+
+ if (!pcie->phy)
+ err = tegra_pcie_phy_enable(pcie);
+ else
+ err = phy_power_on(pcie->phy);
+
+ if (err < 0) {
+ dev_err(pcie->dev, "failed to power on PHY: %d\n", err);
+ return err;
+ }
+
/* take the PCIe interface module out of reset */
reset_control_deassert(pcie->pcie_xrst);
@@ -921,6 +997,10 @@ static void tegra_pcie_power_off(struct tegra_pcie *pcie)
/* TODO: disable and unprepare clocks? */
+ err = phy_power_off(pcie->phy);
+ if (err < 0)
+ dev_warn(pcie->dev, "failed to power off PHY: %d\n", err);
+
reset_control_assert(pcie->pcie_xrst);
reset_control_assert(pcie->afi_rst);
reset_control_assert(pcie->pex_rst);
@@ -1042,6 +1122,19 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
return err;
}
+ pcie->phy = devm_phy_optional_get(pcie->dev, "pcie");
+ if (IS_ERR(pcie->phy)) {
+ err = PTR_ERR(pcie->phy);
+ dev_err(&pdev->dev, "failed to get PHY: %d\n", err);
+ return err;
+ }
+
+ err = phy_init(pcie->phy);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to initialize PHY: %d\n", err);
+ return err;
+ }
+
err = tegra_pcie_power_on(pcie);
if (err) {
dev_err(&pdev->dev, "failed to power up: %d\n", err);
@@ -1100,10 +1193,17 @@ poweroff:
static int tegra_pcie_put_resources(struct tegra_pcie *pcie)
{
+ int err;
+
if (pcie->irq > 0)
free_irq(pcie->irq, pcie);
tegra_pcie_power_off(pcie);
+
+ err = phy_exit(pcie->phy);
+ if (err < 0)
+ dev_err(pcie->dev, "failed to teardown PHY: %d\n", err);
+
return 0;
}
@@ -1349,7 +1449,19 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
{
struct device_node *np = pcie->dev->of_node;
- if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
+ if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
+ switch (lanes) {
+ case 0x0000104:
+ dev_info(pcie->dev, "4x1, 1x1 configuration\n");
+ *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1;
+ return 0;
+
+ case 0x0000102:
+ dev_info(pcie->dev, "2x1, 1x1 configuration\n");
+ *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1;
+ return 0;
+ }
+ } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
switch (lanes) {
case 0x00000204:
dev_info(pcie->dev, "4x1, 2x1 configuration\n");
@@ -1457,7 +1569,23 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
struct device_node *np = pcie->dev->of_node;
unsigned int i = 0;
- if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
+ if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
+ pcie->num_supplies = 7;
+
+ pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
+ sizeof(*pcie->supplies),
+ GFP_KERNEL);
+ if (!pcie->supplies)
+ return -ENOMEM;
+
+ pcie->supplies[i++].supply = "avddio-pex";
+ pcie->supplies[i++].supply = "vddio-pex";
+ pcie->supplies[i++].supply = "avdd-pex-pll";
+ pcie->supplies[i++].supply = "hvdd-pex";
+ pcie->supplies[i++].supply = "hvdd-pex-plle";
+ pcie->supplies[i++].supply = "vddio-pex-ctl";
+ pcie->supplies[i++].supply = "avdd-plle";
+ } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
bool need_pexa = false, need_pexb = false;
/* VDD_PEXA and AVDD_PEXA supply lanes 0 to 3 */
@@ -1679,6 +1807,12 @@ static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port)
unsigned int retries = 3;
unsigned long value;
+ /* override presence detection */
+ value = readl(port->base + RP_PRIV_MISC);
+ value &= ~RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT;
+ value |= RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT;
+ writel(value, port->base + RP_PRIV_MISC);
+
do {
unsigned int timeout = TEGRA_PCIE_LINKUP_TIMEOUT;
@@ -1759,6 +1893,7 @@ static const struct tegra_pcie_soc_data tegra20_pcie_data = {
.has_pex_bias_ctrl = false,
.has_intr_prsnt_sense = false,
.has_cml_clk = false,
+ .has_gen2 = false,
};
static const struct tegra_pcie_soc_data tegra30_pcie_data = {
@@ -1770,9 +1905,23 @@ static const struct tegra_pcie_soc_data tegra30_pcie_data = {
.has_pex_bias_ctrl = true,
.has_intr_prsnt_sense = true,
.has_cml_clk = true,
+ .has_gen2 = false,
+};
+
+static const struct tegra_pcie_soc_data tegra124_pcie_data = {
+ .num_ports = 2,
+ .msi_base_shift = 8,
+ .pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
+ .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN,
+ .has_pex_clkreq_en = true,
+ .has_pex_bias_ctrl = true,
+ .has_intr_prsnt_sense = true,
+ .has_cml_clk = true,
+ .has_gen2 = true,
};
static const struct of_device_id tegra_pcie_of_match[] = {
+ { .compatible = "nvidia,tegra124-pcie", .data = &tegra124_pcie_data },
{ .compatible = "nvidia,tegra30-pcie", .data = &tegra30_pcie_data },
{ .compatible = "nvidia,tegra20-pcie", .data = &tegra20_pcie_data },
{ },
--
2.0.4
^ permalink raw reply related [flat|nested] 8+ messages in thread