summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/pcie.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/pcie.c')
-rw-r--r--arch/arm/mach-tegra/pcie.c237
1 files changed, 138 insertions, 99 deletions
diff --git a/arch/arm/mach-tegra/pcie.c b/arch/arm/mach-tegra/pcie.c
index ec386a51721c..04d3a5db3f7e 100644
--- a/arch/arm/mach-tegra/pcie.c
+++ b/arch/arm/mach-tegra/pcie.c
@@ -105,6 +105,7 @@
#define AFI_CONFIGURATION 0xac
#define AFI_CONFIGURATION_EN_FPCI (1 << 0)
+#define AFI_CONFIGURATION_DFPCI_RSPPASSPW (1 << 2)
#define AFI_FPCI_ERROR_MASKS 0xb0
@@ -155,6 +156,12 @@
#define RP_VEND_XP 0x00000F00
#define RP_VEND_XP_DL_UP (1 << 30)
+#define RP_TXBA1 0x00000E1C
+#define RP_TXBA1_CM_OVER_PW_BURST_MASK (0xF << 4)
+#define RP_TXBA1_CM_OVER_PW_BURST_INIT_VAL (0x4 << 4)
+#define RP_TXBA1_PW_OVER_CM_BURST_MASK (0xF)
+#define RP_TXBA1_PW_OVER_CM_BURST_INIT_VAL (0x4)
+
#define RP_LINK_CONTROL_STATUS 0x00000090
#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000
@@ -300,7 +307,7 @@ struct tegra_pcie_info {
void __iomem *reg_clk_base;
void __iomem *regs;
- struct resource res_mmio;
+ struct resource *res_mmio;
int power_rails_enabled;
int pcie_power_enabled;
struct work_struct hotplug_detect;
@@ -311,20 +318,13 @@ struct tegra_pcie_info {
struct clk *pcie_xclk;
struct clk *pll_e;
struct tegra_pci_platform_data *plat_data;
-};
+}tegra_pcie;
-#define pmc_writel(value, reg) \
- __raw_writel(value, (u32)reg_pmc_base + (reg))
-#define pmc_readl(reg) \
- __raw_readl((u32)reg_pmc_base + (reg))
-
-static struct tegra_pcie_info tegra_pcie = {
- .res_mmio = {
- .name = "PCI IO",
- .start = MMIO_BASE,
- .end = MMIO_BASE + MMIO_SIZE - 1,
- .flags = IORESOURCE_MEM,
- },
+struct resource tegra_pcie_res_mmio = {
+ .name = "PCI IO",
+ .start = MMIO_BASE,
+ .end = MMIO_BASE + MMIO_SIZE - 1,
+ .flags = IORESOURCE_MEM,
};
static struct resource pcie_io_space;
@@ -639,9 +639,6 @@ static void tegra_pcie_hotplug_init(void)
static void tegra_pcie_attach(void)
{
- /* this hardcode is just to bypass the check in resume */
- if (!is_dock_conn_at_boot)
- tegra_pcie.num_ports = 1;
#ifdef CONFIG_PM
tegra_pcie_resume(NULL);
#endif
@@ -880,8 +877,11 @@ static void tegra_pcie_enable_controller(void)
/* Take the PCIe interface module out of reset */
tegra_periph_reset_deassert(tegra_pcie.pcie_xclk);
+ /* WAR avoid hang on CPU read/write while gpu transfers in progress */
+ val = afi_readl(AFI_CONFIGURATION) | AFI_CONFIGURATION_DFPCI_RSPPASSPW;
+
/* Finally enable PCIe */
- val = afi_readl(AFI_CONFIGURATION) | AFI_CONFIGURATION_EN_FPCI;
+ val |= AFI_CONFIGURATION_EN_FPCI;
afi_writel(val, AFI_CONFIGURATION);
val = (AFI_INTR_EN_INI_SLVERR | AFI_INTR_EN_INI_DECERR |
@@ -902,8 +902,12 @@ static void tegra_pcie_enable_controller(void)
static int tegra_pcie_enable_regulators(void)
{
- if (tegra_pcie.power_rails_enabled)
+ if (tegra_pcie.power_rails_enabled) {
+ pr_debug("PCIE: Already power rails enabled");
return 0;
+ }
+ tegra_pcie.power_rails_enabled = 1;
+
if (tegra_pcie.regulator_hvdd == NULL) {
printk(KERN_INFO "PCIE.C: %s : regulator hvdd_pex\n",
__func__);
@@ -948,16 +952,17 @@ static int tegra_pcie_enable_regulators(void)
if (tegra_pcie.regulator_avdd_plle)
regulator_enable(tegra_pcie.regulator_avdd_plle);
- tegra_pcie.power_rails_enabled = 1;
-
return 0;
}
static int tegra_pcie_disable_regulators(void)
{
int err = 0;
- if (tegra_pcie.power_rails_enabled == 0)
+
+ if (tegra_pcie.power_rails_enabled == 0) {
+ pr_debug("PCIE: Already power rails disabled");
goto err_exit;
+ }
if (tegra_pcie.regulator_hvdd)
err = regulator_disable(tegra_pcie.regulator_hvdd);
if (err)
@@ -985,30 +990,92 @@ static int tegra_pcie_power_regate(void)
return clk_enable(tegra_pcie.pll_e);
}
-#ifdef CONFIG_PM
+static int tegra_pcie_map_resources(void)
+{
+ int err;
+
+ /* Allocate config space virtual memory */
+ tegra_pcie.regs = ioremap_nocache(TEGRA_PCIE_BASE, PCIE_IOMAP_SZ);
+ if (tegra_pcie.regs == NULL) {
+ pr_err("PCIE: Failed to map PCI/AFI registers\n");
+ return -ENOMEM;
+ }
+
+ err = request_resource(&iomem_resource, &tegra_pcie_res_mmio);
+ if (err) {
+ pr_err("PCIE: Failed to request resources: %d\n", err);
+ return err;
+ }
+ tegra_pcie.res_mmio = &tegra_pcie_res_mmio;
+
+ /* Allocate downstream IO virtual memory */
+ tegra_pcie_io_base = ioremap_nocache(tegra_pcie_res_mmio.start,
+ resource_size(&tegra_pcie_res_mmio));
+ if (tegra_pcie_io_base == NULL) {
+ pr_err("PCIE: Failed to map IO\n");
+ return -ENOMEM;
+ }
+ return err;
+}
+
+void tegra_pcie_unmap_resources(void)
+{
+ if (tegra_pcie_io_base) {
+ iounmap(tegra_pcie_io_base);
+ tegra_pcie_io_base = 0;
+ }
+ if (tegra_pcie.res_mmio) {
+ release_resource(tegra_pcie.res_mmio);
+ tegra_pcie.res_mmio = 0;
+ }
+ if (tegra_pcie.regs) {
+ iounmap(tegra_pcie.regs);
+ tegra_pcie.regs = 0;
+ }
+}
+static int tegra_pcie_power_off(void);
+
static int tegra_pcie_power_on(void)
{
int err = 0;
- if (tegra_pcie.pcie_power_enabled)
- return 0;
+
+ if (tegra_pcie.pcie_power_enabled) {
+ pr_debug("PCIE: Already powered on");
+ goto err_exit;
+ }
+ tegra_pcie.pcie_power_enabled = 1;
+
err = tegra_pcie_enable_regulators();
- if (err)
+ if (err) {
+ pr_err("PCIE: Failed to enable regulators\n");
goto err_exit;
+ }
err = tegra_pcie_power_regate();
- if (err)
+ if (err) {
+ pr_err("PCIE: Failed to power regate\n");
+ goto err_exit;
+ }
+ err = tegra_pcie_map_resources();
+ if (err) {
+ pr_err("PCIE: Failed to map resources\n");
goto err_exit;
+ }
- tegra_pcie.pcie_power_enabled = 1;
err_exit:
+ if (err)
+ tegra_pcie_power_off();
return err;
}
-#endif
static int tegra_pcie_power_off(void)
{
int err = 0;
- if (tegra_pcie.pcie_power_enabled == 0)
- return 0;
+
+ if (tegra_pcie.pcie_power_enabled == 0) {
+ pr_debug("PCIE: Already powered off");
+ goto err_exit;
+ }
+ tegra_pcie_unmap_resources();
if (tegra_pcie.pll_e)
clk_disable(tegra_pcie.pll_e);
@@ -1047,81 +1114,42 @@ error_exit:
static void tegra_pcie_clocks_put(void)
{
- clk_put(tegra_pcie.pll_e);
- clk_put(tegra_pcie.pcie_xclk);
+ if (tegra_pcie.pll_e)
+ clk_put(tegra_pcie.pll_e);
+ if (tegra_pcie.pcie_xclk)
+ clk_put(tegra_pcie.pcie_xclk);
}
static int tegra_pcie_get_resources(void)
{
- struct resource *res_mmio = 0;
int err;
tegra_pcie.power_rails_enabled = 0;
- err = tegra_pcie_enable_regulators();
- if (err) {
- pr_err("PCIE: failed to enable power rails %d\n", err);
- goto err_pwr_on_rail;
- }
- tegra_unpowergate_partition(TEGRA_POWERGATE_PCIE);
+ tegra_pcie.pcie_power_enabled = 0;
err = tegra_pcie_clocks_get();
if (err) {
pr_err("PCIE: failed to get clocks: %d\n", err);
- return err;
+ goto err_clk_get;
}
-
- err = tegra_pcie_power_regate();
+ err = tegra_pcie_power_on();
if (err) {
- pr_err("PCIE: failed to power up: %d\n", err);
+ pr_err("PCIE: Failed to power on: %d\n", err);
goto err_pwr_on;
}
-
- /* Allocate config space virtual memory */
- tegra_pcie.regs = ioremap_nocache(TEGRA_PCIE_BASE, PCIE_IOMAP_SZ);
- if (tegra_pcie.regs == NULL) {
- pr_err("PCIE: Failed to map PCI/AFI registers\n");
- err = -ENOMEM;
- goto err_map_reg;
- }
- res_mmio = &tegra_pcie.res_mmio;
-
- err = request_resource(&iomem_resource, res_mmio);
- if (err) {
- pr_err("PCIE: Failed to request resources: %d\n", err);
- goto err_req_io;
- }
-
- /* Allocate downstream IO virtual memory */
- tegra_pcie_io_base = ioremap_nocache(res_mmio->start,
- resource_size(res_mmio));
- if (tegra_pcie_io_base == NULL) {
- pr_err("PCIE: Failed to map IO\n");
- err = -ENOMEM;
- goto err_map_io;
- }
-
err = request_irq(INT_PCIE_INTR, tegra_pcie_isr,
- IRQF_SHARED, "PCIE", &tegra_pcie);
+ IRQF_SHARED, "PCIE", &tegra_pcie);
if (err) {
pr_err("PCIE: Failed to register IRQ: %d\n", err);
- goto err_irq;
+ goto err_pwr_on;
}
set_irq_flags(INT_PCIE_INTR, IRQF_VALID);
-
return 0;
-err_irq:
- iounmap(tegra_pcie_io_base);
-err_map_io:
- release_resource(&tegra_pcie.res_mmio);
-err_req_io:
- iounmap(tegra_pcie.regs);
-err_map_reg:
- tegra_pcie_power_off();
err_pwr_on:
+ tegra_pcie_power_off();
+err_clk_get:
tegra_pcie_clocks_put();
-err_pwr_on_rail:
- tegra_pcie_disable_regulators();
return err;
}
@@ -1205,6 +1233,7 @@ static void tegra_pcie_enable_aspm_l1_support(int index)
static void tegra_pcie_add_port(int index, u32 offset, u32 reset_reg)
{
struct tegra_pcie_port *pp;
+ unsigned int data;
pp = tegra_pcie.port + tegra_pcie.num_ports;
@@ -1219,6 +1248,18 @@ static void tegra_pcie_add_port(int index, u32 offset, u32 reset_reg)
}
tegra_pcie_enable_clock_clamp(index);
tegra_pcie_enable_aspm_l1_support(index);
+
+ /*
+ * Initialize TXBA1 register to fix the unfair arbitration
+ * between downstream reads and completions to upstream reads
+ */
+ data = rp_readl(RP_TXBA1, index);
+ data &= ~(RP_TXBA1_PW_OVER_CM_BURST_MASK);
+ data |= RP_TXBA1_PW_OVER_CM_BURST_INIT_VAL;
+ data &= ~(RP_TXBA1_CM_OVER_PW_BURST_MASK);
+ data |= RP_TXBA1_CM_OVER_PW_BURST_INIT_VAL;
+ rp_writel(data, RP_TXBA1, index);
+
tegra_pcie.num_ports++;
pp->index = index;
memset(pp->res, 0, sizeof(pp->res));
@@ -1254,7 +1295,6 @@ static int tegra_pcie_init(void)
tegra_pcie_add_port(port, rp_offset, ctrl_offset);
}
- tegra_pcie.pcie_power_enabled = 1;
if (tegra_pcie.plat_data->use_dock_detect) {
unsigned int irq;
@@ -1294,7 +1334,6 @@ err_irq:
static int tegra_pcie_probe(struct platform_device *pdev)
{
int ret;
- struct pci_dev *dev = NULL;
tegra_pcie.plat_data = pdev->dev.platform_data;
dev_dbg(&pdev->dev, "PCIE.C: %s : _port_status[0] %d\n",
@@ -1305,23 +1344,14 @@ static int tegra_pcie_probe(struct platform_device *pdev)
__func__, tegra_pcie.plat_data->port_status[2]);
ret = tegra_pcie_init();
- /* disable async PM of pci devices to ensure right order */
- /* suspend/resume calls of tegra and bus driver */
- for_each_pci_dev(dev)
- device_disable_async_suspend(&dev->dev);
-
return ret;
}
#ifdef CONFIG_PM
static int tegra_pcie_suspend(struct device *dev)
{
- int ret = 0;
struct pci_dev *pdev = NULL;
- if (!tegra_pcie.num_ports)
- return ret;
-
for_each_pci_dev(pdev) {
pci_remove_bus_device(pdev);
break;
@@ -1329,6 +1359,8 @@ static int tegra_pcie_suspend(struct device *dev)
/* disable read/write registers before powering off */
is_pcie_noirq_op = true;
+ /* reset number of ports since fresh initialization occurs in resume */
+ tegra_pcie.num_ports = 0;
return tegra_pcie_power_off();
}
@@ -1358,24 +1390,31 @@ static int tegra_pcie_resume(struct device *dev)
int port, rp_offset = 0;
int ctrl_offset = AFI_PEX0_CTRL;
- if (!tegra_pcie.num_ports)
- return ret;
+ /* return w/o resume if cardhu dock is not connected */
+ if (gpio_get_value(tegra_pcie.plat_data->gpio))
+ goto exit;
ret = tegra_pcie_power_on();
+ if (ret) {
+ pr_err("PCIE: Failed to power on: %d\n", ret);
+ return ret;
+ }
/* enable read/write registers after powering on */
is_pcie_noirq_op = false;
tegra_pcie_enable_controller();
tegra_pcie_setup_translations();
msi_enable = false;
- /* reset number of ports before adding port */
- tegra_pcie.num_ports = 0;
for (port = 0; port < MAX_PCIE_SUPPORTED_PORTS; port++) {
ctrl_offset += (port * 8);
rp_offset = (rp_offset + 0x1000) * port;
if (tegra_pcie.plat_data->port_status[port])
tegra_pcie_add_port(port, rp_offset, ctrl_offset);
}
+ if (!tegra_pcie.num_ports) {
+ tegra_pcie_power_off();
+ goto exit;
+ }
tegra_pcie_hotplug_init();
while ((bus = pci_find_next_bus(bus)) != NULL) {
@@ -1395,8 +1434,8 @@ static int tegra_pcie_resume(struct device *dev)
pci_enable_bridges(bus);
pci_bus_add_devices(bus);
}
-
- return ret;
+exit:
+ return 0;
}
#endif