diff options
Diffstat (limited to 'arch/arm/mach-tegra/pcie.c')
-rw-r--r-- | arch/arm/mach-tegra/pcie.c | 237 |
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 |