Hi Vidya, Wow, there's a lot of nice work here! Thanks for that! On Tue, Mar 26, 2019 at 08:43:26PM +0530, Vidya Sagar wrote: > Add support for Synopsys DesignWare core IP based PCIe host controller > present in Tegra194 SoC. General comments: - There are a few multi-line comments that don't match the prevailing style: /* * Text... */ - Comments and dev_info()/dev_err() messages are inconsistent about starting with upper-case or lower-case letters. - Use "MSI", "IRQ", "PCIe", "CPU", etc in comments and messages. - There are a few functions that use "&pdev->dev" many times; can you add a "struct device *dev = &pdev->dev" to reduce the redundancy? > +#include "../../pcie/portdrv.h" What's this for? I didn't see any obvious uses of things from portdrv.h, but I didn't actually try to build without it. > +struct tegra_pcie_dw { > + struct device *dev; > + struct resource *appl_res; > + struct resource *dbi_res; > + struct resource *atu_dma_res; > + void __iomem *appl_base; > + struct clk *core_clk; > + struct reset_control *core_apb_rst; > + struct reset_control *core_rst; > + struct dw_pcie pci; > + enum dw_pcie_device_mode mode; > + > + bool disable_clock_request; > + bool power_down_en; > + u8 init_link_width; > + bool link_state; > + u32 msi_ctrl_int; > + u32 num_lanes; > + u32 max_speed; > + u32 init_speed; > + bool cdm_check; > + u32 cid; > + int pex_wake; > + bool update_fc_fixup; > + int n_gpios; > + int *gpios; > +#if defined(CONFIG_PCIEASPM) > + u32 cfg_link_cap_l1sub; > + u32 event_cntr_ctrl; > + u32 event_cntr_data; > + u32 aspm_cmrt; > + u32 aspm_pwr_on_t; > + u32 aspm_l0s_enter_lat; > + u32 disabled_aspm_states; > +#endif The above could be indented the same as the rest of the struct? > + struct regulator *pex_ctl_reg; > + > + int phy_count; > + struct phy **phy; > + > + struct dentry *debugfs; > +}; > +static void apply_bad_link_workaround(struct pcie_port *pp) > +{ > + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); > + struct tegra_pcie_dw *pcie = dw_pcie_to_tegra_pcie(pci); > + u16 val; > + > + /* > + * NOTE:- Since this scenario is uncommon and link as > + * such is not stable anyway, not waiting to confirm > + * if link is really transiting to Gen-2 speed s/transiting/transitioning/ I think there are other uses of "transit" when you mean "transition". > +static int tegra_pcie_dw_rd_own_conf(struct pcie_port *pp, int where, int size, > + u32 *val) > +{ > + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); > + > + /* > + * This is an endpoint mode specific register happen to appear even > + * when controller is operating in root port mode and system hangs > + * when it is accessed with link being in ASPM-L1 state. > + * So skip accessing it altogether > + */ > + if (where == PORT_LOGIC_MSIX_DOORBELL) { > + *val = 0x00000000; > + return PCIBIOS_SUCCESSFUL; > + } else { > + return dw_pcie_read(pci->dbi_base + where, size, val); > + } > +} > + > +static int tegra_pcie_dw_wr_own_conf(struct pcie_port *pp, int where, int size, > + u32 val) > +{ > + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); > + > + /* This is EP specific register and system hangs when it is > + * accessed with link being in ASPM-L1 state. > + * So skip accessing it altogether > + */ > + if (where == PORT_LOGIC_MSIX_DOORBELL) > + return PCIBIOS_SUCCESSFUL; > + else > + return dw_pcie_write(pci->dbi_base + where, size, val); These two functions are almost identical and they could look more similar. This one has the wrong multi-line comment style, uses "EP" instead of "endpoint", etc. Use this style for the "if" since the first case is really an error case: if (where == PORT_LOGIC_MSIX_DOORBELL) { ... return ...; } return dw_pcie_...(); > +static int tegra_pcie_dw_host_init(struct pcie_port *pp) > +{ > + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); > + struct tegra_pcie_dw *pcie = dw_pcie_to_tegra_pcie(pci); > + int count = 200; > + u32 val, tmp, offset; > + u16 val_w; > + > +#if defined(CONFIG_PCIEASPM) > + pcie->cfg_link_cap_l1sub = > + dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS) + > + PCI_L1SS_CAP; > +#endif > + val = dw_pcie_readl_dbi(pci, PCI_IO_BASE); > + val &= ~(IO_BASE_IO_DECODE | IO_BASE_IO_DECODE_BIT8); > + dw_pcie_writel_dbi(pci, PCI_IO_BASE, val); > + > + val = dw_pcie_readl_dbi(pci, PCI_PREF_MEMORY_BASE); > + val |= CFG_PREF_MEM_LIMIT_BASE_MEM_DECODE; > + val |= CFG_PREF_MEM_LIMIT_BASE_MEM_LIMIT_DECODE; > + dw_pcie_writel_dbi(pci, PCI_PREF_MEMORY_BASE, val); > + > + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0); > + > + /* Configure FTS */ > + val = dw_pcie_readl_dbi(pci, PORT_LOGIC_ACK_F_ASPM_CTRL); > + val &= ~(N_FTS_MASK << N_FTS_SHIFT); > + val |= N_FTS_VAL << N_FTS_SHIFT; > + dw_pcie_writel_dbi(pci, PORT_LOGIC_ACK_F_ASPM_CTRL, val); > + > + val = dw_pcie_readl_dbi(pci, PORT_LOGIC_GEN2_CTRL); > + val &= ~FTS_MASK; > + val |= FTS_VAL; > + dw_pcie_writel_dbi(pci, PORT_LOGIC_GEN2_CTRL, val); > + > + /* Enable as 0xFFFF0001 response for CRS */ > + val = dw_pcie_readl_dbi(pci, PORT_LOGIC_AMBA_ERROR_RESPONSE_DEFAULT); > + val &= ~(AMBA_ERROR_RESPONSE_CRS_MASK << AMBA_ERROR_RESPONSE_CRS_SHIFT); > + val |= (AMBA_ERROR_RESPONSE_CRS_OKAY_FFFF0001 << > + AMBA_ERROR_RESPONSE_CRS_SHIFT); > + dw_pcie_writel_dbi(pci, PORT_LOGIC_AMBA_ERROR_RESPONSE_DEFAULT, val); > + > + /* Set MPS to 256 in DEV_CTL */ > + val = dw_pcie_readl_dbi(pci, CFG_DEV_STATUS_CONTROL); > + val &= ~PCI_EXP_DEVCTL_PAYLOAD; > + val |= (1 << CFG_DEV_STATUS_CONTROL_MPS_SHIFT); > + dw_pcie_writel_dbi(pci, CFG_DEV_STATUS_CONTROL, val); > + > + /* Configure Max Speed from DT */ > + val = dw_pcie_readl_dbi(pci, CFG_LINK_CAP); > + val &= ~PCI_EXP_LNKCAP_SLS; > + val |= pcie->max_speed; > + dw_pcie_writel_dbi(pci, CFG_LINK_CAP, val); > + > + val = dw_pcie_readw_dbi(pci, CFG_LINK_CONTROL_2); > + val &= ~PCI_EXP_LNKCTL2_TLS; > + val |= pcie->init_speed; > + dw_pcie_writew_dbi(pci, CFG_LINK_CONTROL_2, val); > + > + /* Configure Max lane width from DT */ > + val = dw_pcie_readl_dbi(pci, CFG_LINK_CAP); > + val &= ~PCI_EXP_LNKCAP_MLW; > + val |= (pcie->num_lanes << PCI_EXP_LNKSTA_NLW_SHIFT); > + dw_pcie_writel_dbi(pci, CFG_LINK_CAP, val); > + > + config_gen3_gen4_eq_presets(pcie); > + > +#if defined(CONFIG_PCIEASPM) > + /* Enable ASPM counters */ > + val = EVENT_COUNTER_ENABLE_ALL << EVENT_COUNTER_ENABLE_SHIFT; > + val |= EVENT_COUNTER_GROUP_5 << EVENT_COUNTER_GROUP_SEL_SHIFT; > + dw_pcie_writel_dbi(pci, pcie->event_cntr_ctrl, val); > + > + /* Program T_cmrt and T_pwr_on values */ > + val = dw_pcie_readl_dbi(pci, pcie->cfg_link_cap_l1sub); > + val &= ~(PCI_L1SS_CAP_CM_RESTORE_TIME | PCI_L1SS_CAP_P_PWR_ON_VALUE); > + val |= (pcie->aspm_cmrt << PCI_L1SS_CAP_CM_RTM_SHIFT); > + val |= (pcie->aspm_pwr_on_t << PCI_L1SS_CAP_PWRN_VAL_SHIFT); > + dw_pcie_writel_dbi(pci, pcie->cfg_link_cap_l1sub, val); > + > + /* Program L0s and L1 entrance latencies */ > + val = dw_pcie_readl_dbi(pci, PORT_LOGIC_ACK_F_ASPM_CTRL); > + val &= ~L0S_ENTRANCE_LAT_MASK; > + val |= (pcie->aspm_l0s_enter_lat << L0S_ENTRANCE_LAT_SHIFT); > + val |= ENTER_ASPM; > + dw_pcie_writel_dbi(pci, PORT_LOGIC_ACK_F_ASPM_CTRL, val); > + > + /* Program what ASPM states sould get advertised */ s/sould/should/ > + if (pcie->disabled_aspm_states & 0x1) > + disable_aspm_l0s(pcie); /* Disable L0s */ > + if (pcie->disabled_aspm_states & 0x2) { > + disable_aspm_l10(pcie); /* Disable L1 */ > + disable_aspm_l11(pcie); /* Disable L1.1 */ > + disable_aspm_l12(pcie); /* Disable L1.2 */ > + } > + if (pcie->disabled_aspm_states & 0x4) > + disable_aspm_l11(pcie); /* Disable L1.1 */ > + if (pcie->disabled_aspm_states & 0x8) > + disable_aspm_l12(pcie); /* Disable L1.2 */ > +#endif > + val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF); > + val &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL; > + dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); > + > + if (pcie->update_fc_fixup) { > + val = dw_pcie_readl_dbi(pci, CFG_TIMER_CTRL_MAX_FUNC_NUM_OFF); > + val |= 0x1 << CFG_TIMER_CTRL_ACK_NAK_SHIFT; > + dw_pcie_writel_dbi(pci, CFG_TIMER_CTRL_MAX_FUNC_NUM_OFF, val); > + } > + > + /* CDM check enable */ > + if (pcie->cdm_check) { > + val = dw_pcie_readl_dbi(pci, > + PORT_LOGIC_PL_CHK_REG_CONTROL_STATUS); > + val |= PORT_LOGIC_PL_CHK_REG_CHK_REG_CONTINUOUS; > + val |= PORT_LOGIC_PL_CHK_REG_CHK_REG_START; > + dw_pcie_writel_dbi(pci, PORT_LOGIC_PL_CHK_REG_CONTROL_STATUS, > + val); > + } > + > + dw_pcie_setup_rc(pp); > + > + clk_set_rate(pcie->core_clk, GEN4_CORE_CLK_FREQ); > + > + /* assert RST */ > + val = readl(pcie->appl_base + APPL_PINMUX); > + val &= ~APPL_PINMUX_PEX_RST; > + writel(val, pcie->appl_base + APPL_PINMUX); > + > + usleep_range(100, 200); > + > + /* enable LTSSM */ > + val = readl(pcie->appl_base + APPL_CTRL); > + val |= APPL_CTRL_LTSSM_EN; > + writel(val, pcie->appl_base + APPL_CTRL); > + > + /* de-assert RST */ > + val = readl(pcie->appl_base + APPL_PINMUX); > + val |= APPL_PINMUX_PEX_RST; > + writel(val, pcie->appl_base + APPL_PINMUX); > + > + msleep(100); > + > + val_w = dw_pcie_readw_dbi(pci, CFG_LINK_STATUS); > + while (!(val_w & PCI_EXP_LNKSTA_DLLLA)) { > + if (!count) { > + val = readl(pcie->appl_base + APPL_DEBUG); > + val &= APPL_DEBUG_LTSSM_STATE_MASK; > + val >>= APPL_DEBUG_LTSSM_STATE_SHIFT; > + tmp = readl(pcie->appl_base + APPL_LINK_STATUS); > + tmp &= APPL_LINK_STATUS_RDLH_LINK_UP; > + if (val == 0x11 && !tmp) { > + dev_info(pci->dev, "link is down in DLL"); > + dev_info(pci->dev, > + "trying again with DLFE disabled\n"); > + /* disable LTSSM */ > + val = readl(pcie->appl_base + APPL_CTRL); > + val &= ~APPL_CTRL_LTSSM_EN; > + writel(val, pcie->appl_base + APPL_CTRL); > + > + reset_control_assert(pcie->core_rst); > + reset_control_deassert(pcie->core_rst); > + > + offset = > + dw_pcie_find_ext_capability(pci, > + PCI_EXT_CAP_ID_DLF) > + + PCI_DLF_CAP; This capability offset doesn't change, does it? Could it be computed outside the loop? > + val = dw_pcie_readl_dbi(pci, offset); > + val &= ~DL_FEATURE_EXCHANGE_EN; > + dw_pcie_writel_dbi(pci, offset, val); > + > + tegra_pcie_dw_host_init(&pcie->pci.pp); This looks like some sort of "wait for link up" retry loop, but a recursive call seems a little unusual. My 5 second analysis is that the loop could run this 200 times, and you sure don't want the possibility of a 200-deep call chain. Is there way to split out the host init from the link-up polling? > + return 0; > + } > + dev_info(pci->dev, "link is down\n"); > + return 0; > + } > + dev_dbg(pci->dev, "polling for link up\n"); > + usleep_range(1000, 2000); > + val_w = dw_pcie_readw_dbi(pci, CFG_LINK_STATUS); > + count--; > + } > + dev_info(pci->dev, "link is up\n"); > + > + tegra_pcie_enable_interrupts(pp); > + > + return 0; > +} > +static void tegra_pcie_dw_scan_bus(struct pcie_port *pp) > +{ > + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); > + struct tegra_pcie_dw *pcie = dw_pcie_to_tegra_pcie(pci); > + u32 speed; > + > + if (!tegra_pcie_dw_link_up(pci)) > + return; > + > + speed = (dw_pcie_readw_dbi(pci, CFG_LINK_STATUS) & PCI_EXP_LNKSTA_CLS); > + clk_set_rate(pcie->core_clk, pcie_gen_freq[speed - 1]); I don't understand what's happening here. This is named tegra_pcie_dw_scan_bus(), but it doesn't actually scan anything. Maybe it's just a bad name for the dw_pcie_host_ops hook (ks_pcie_v3_65_scan_bus() is the only other .scan_bus() implementation, and it doesn't scan anything either). dw_pcie_host_init() calls pci_scan_root_bus_bridge(), which actually *does* scan the bus, but it does it before calling pp->ops->scan_bus(). I'd say by the time we get to pci_scan_root_bus_bridge(), the device-specific init should be all done and we should be using only generic PCI core interfaces. Maybe this stuff could/should be done in the ->host_init() hook? The code between ->host_init() and ->scan_bus() is all generic code with no device-specific stuff, so I don't know why we need both hooks. > +static int tegra_pcie_enable_phy(struct tegra_pcie_dw *pcie) > +{ > + int phy_count = pcie->phy_count; > + int ret; > + int i; > + > + for (i = 0; i < phy_count; i++) { > + ret = phy_init(pcie->phy[i]); > + if (ret < 0) > + goto err_phy_init; > + > + ret = phy_power_on(pcie->phy[i]); > + if (ret < 0) { > + phy_exit(pcie->phy[i]); > + goto err_phy_power_on; > + } > + } > + > + return 0; > + > + while (i >= 0) { > + phy_power_off(pcie->phy[i]); > +err_phy_power_on: > + phy_exit(pcie->phy[i]); > +err_phy_init: > + i--; > + } Wow, jumping into the middle of that loop is clever ;) Can't decide what I think of it, but it's certainly clever! > + return ret; > +} > + > +static int tegra_pcie_dw_parse_dt(struct tegra_pcie_dw *pcie) > +{ > + struct device_node *np = pcie->dev->of_node; > + int ret; > + > +#if defined(CONFIG_PCIEASPM) > + ret = of_property_read_u32(np, "nvidia,event-cntr-ctrl", > + &pcie->event_cntr_ctrl); > + if (ret < 0) { > + dev_err(pcie->dev, "fail to read event-cntr-ctrl: %d\n", ret); > + return ret; > + } The fact that you return error here if DT lacks the "nvidia,event-cntr-ctrl" property, but only if CONFIG_PCIEASPM=y, means that you have a revlock between the DT and the kernel: if you update the kernel to enable CONFIG_PCIEASPM, you may also have to update your DT. Maybe that's OK, but I think it'd be nicer if you always required the presence of these properties, even if you only actually *use* them when CONFIG_PCIEASPM=y. > +static int tegra_pcie_dw_probe(struct platform_device *pdev) > +{ > + struct tegra_pcie_dw *pcie; > + struct pcie_port *pp; > + struct dw_pcie *pci; > + struct phy **phy; > + struct resource *dbi_res; > + struct resource *atu_dma_res; > + const struct of_device_id *match; > + const struct tegra_pcie_of_data *data; > + char *name; > + int ret, i; > + > + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); > + if (!pcie) > + return -ENOMEM; > + > + pci = &pcie->pci; > + pci->dev = &pdev->dev; > + pci->ops = &tegra_dw_pcie_ops; > + pp = &pci->pp; > + pcie->dev = &pdev->dev; > + > + match = of_match_device(of_match_ptr(tegra_pcie_dw_of_match), > + &pdev->dev); > + if (!match) > + return -EINVAL; Logically could be the first thing in the function since it doesn't depend on anything. > + data = (struct tegra_pcie_of_data *)match->data; > + pcie->mode = (enum dw_pcie_device_mode)data->mode; > + > + ret = tegra_pcie_dw_parse_dt(pcie); > + if (ret < 0) { > + dev_err(pcie->dev, "device tree parsing failed: %d\n", ret); > + return ret; > + } > + > + if (gpio_is_valid(pcie->pex_wake)) { > + ret = devm_gpio_request(pcie->dev, pcie->pex_wake, > + "pcie_wake"); > + if (ret < 0) { > + if (ret == -EBUSY) { > + dev_err(pcie->dev, > + "pex_wake already in use\n"); > + pcie->pex_wake = -EINVAL; This looks strange. "pex_wake == -EINVAL" doesn't look right, and you're about to pass it to gpio_direction_input(), which looks wrong. > + } else { > + dev_err(pcie->dev, > + "pcie_wake gpio_request failed %d\n", > + ret); > + return ret; > + } > + } > + > + ret = gpio_direction_input(pcie->pex_wake); > + if (ret < 0) { > + dev_err(pcie->dev, > + "setting pcie_wake input direction failed %d\n", > + ret); > + return ret; > + } > + device_init_wakeup(pcie->dev, true); > + } > + > + pcie->pex_ctl_reg = devm_regulator_get(&pdev->dev, "vddio-pex-ctl"); > + if (IS_ERR(pcie->pex_ctl_reg)) { > + dev_err(&pdev->dev, "fail to get regulator: %ld\n", > + PTR_ERR(pcie->pex_ctl_reg)); > + return PTR_ERR(pcie->pex_ctl_reg); > + } > + > + pcie->core_clk = devm_clk_get(&pdev->dev, "core_clk"); > + if (IS_ERR(pcie->core_clk)) { > + dev_err(&pdev->dev, "Failed to get core clock\n"); > + return PTR_ERR(pcie->core_clk); > + } > + > + pcie->appl_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, > + "appl"); > + if (!pcie->appl_res) { > + dev_err(&pdev->dev, "missing appl space\n"); > + return PTR_ERR(pcie->appl_res); > + } > + pcie->appl_base = devm_ioremap_resource(&pdev->dev, pcie->appl_res); > + if (IS_ERR(pcie->appl_base)) { > + dev_err(&pdev->dev, "mapping appl space failed\n"); > + return PTR_ERR(pcie->appl_base); > + } > + > + pcie->core_apb_rst = devm_reset_control_get(pcie->dev, "core_apb_rst"); > + if (IS_ERR(pcie->core_apb_rst)) { > + dev_err(pcie->dev, "PCIE : core_apb_rst reset is missing\n"); This error message looks different from the others ("PCIE :" prefix). > + return PTR_ERR(pcie->core_apb_rst); > + } > + > + phy = devm_kcalloc(pcie->dev, pcie->phy_count, sizeof(*phy), > + GFP_KERNEL); > + if (!phy) > + return PTR_ERR(phy); > + > + for (i = 0; i < pcie->phy_count; i++) { > + name = kasprintf(GFP_KERNEL, "pcie-p2u-%u", i); > + if (!name) { > + dev_err(pcie->dev, "failed to create p2u string\n"); > + return -ENOMEM; > + } > + phy[i] = devm_phy_get(pcie->dev, name); > + kfree(name); > + if (IS_ERR(phy[i])) { > + ret = PTR_ERR(phy[i]); > + dev_err(pcie->dev, "phy_get error: %d\n", ret); > + return ret; > + } > + } > + > + pcie->phy = phy; > + > + dbi_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); > + if (!dbi_res) { > + dev_err(&pdev->dev, "missing config space\n"); > + return PTR_ERR(dbi_res); > + } > + pcie->dbi_res = dbi_res; > + > + pci->dbi_base = devm_ioremap_resource(&pdev->dev, dbi_res); > + if (IS_ERR(pci->dbi_base)) { > + dev_err(&pdev->dev, "mapping dbi space failed\n"); > + return PTR_ERR(pci->dbi_base); > + } > + > + /* Tegra HW locates DBI2 at a fixed offset from DBI */ > + pci->dbi_base2 = pci->dbi_base + 0x1000; > + > + atu_dma_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, > + "atu_dma"); > + if (!atu_dma_res) { > + dev_err(&pdev->dev, "missing atu_dma space\n"); > + return PTR_ERR(atu_dma_res); > + } > + pcie->atu_dma_res = atu_dma_res; > + pci->atu_base = devm_ioremap_resource(&pdev->dev, atu_dma_res); > + if (IS_ERR(pci->atu_base)) { > + dev_err(&pdev->dev, "mapping atu space failed\n"); > + return PTR_ERR(pci->atu_base); > + } > + > + pcie->core_rst = devm_reset_control_get(pcie->dev, "core_rst"); > + if (IS_ERR(pcie->core_rst)) { > + dev_err(pcie->dev, "PCIE : core_rst reset is missing\n"); Different message format again. > + return PTR_ERR(pcie->core_rst); > + } > + > + pp->irq = platform_get_irq_byname(pdev, "intr"); > + if (!pp->irq) { > + dev_err(pcie->dev, "failed to get intr interrupt\n"); > + return -ENODEV; > + } > + > + ret = devm_request_irq(&pdev->dev, pp->irq, tegra_pcie_irq_handler, > + IRQF_SHARED, "tegra-pcie-intr", pcie); > + if (ret) { > + dev_err(pcie->dev, "failed to request \"intr\" irq\n"); It'd be nice to include the actual IRQ, i.e., "IRQ %d", pp->irq. > + return ret; > + } > + > + platform_set_drvdata(pdev, pcie); > + > + if (pcie->mode == DW_PCIE_RC_TYPE) { > + ret = tegra_pcie_config_rp(pcie); > + if (ret == -ENOMEDIUM) > + ret = 0; > + } > + > + return ret; > +} > +static void tegra_pcie_downstream_dev_to_D0(struct tegra_pcie_dw *pcie) > +{ > + struct pci_dev *pdev = NULL; Unnecessary initialization. > + struct pci_bus *child; > + struct pcie_port *pp = &pcie->pci.pp; > + > + list_for_each_entry(child, &pp->bus->children, node) { > + /* Bring downstream devices to D0 if they are not already in */ > + if (child->parent == pp->bus) { > + pdev = pci_get_slot(child, PCI_DEVFN(0, 0)); > + pci_dev_put(pdev); > + if (!pdev) > + break; I don't really like this dance with iterating over the bus children, comparing parent to pp->bus, pci_get_slot(), pci_dev_put(), etc. I guess the idea is to bring only the directly-downstream devices to D0, not to do it for things deeper in the hierarchy? Is this some Tegra-specific wrinkle? I don't think other drivers do this. I see that an earlier patch added "bus" to struct pcie_port. I think it would be better to somehow connect to the pci_host_bridge struct. Several other drivers already do this; see uses of pci_host_bridge_from_priv(). That would give you the bus, as well as flags like no_ext_tags, native_aer, etc, which this driver, being a host bridge driver that's responsible for this part of the firmware/OS interface, may conceivably need. Rather than pci_get_slot(), couldn't you iterate over bus->devices and just skip the non-PCI_DEVFN(0, 0) devices? > + > + if (pci_set_power_state(pdev, PCI_D0)) > + dev_err(pcie->dev, "D0 transition failed\n"); > + } > + } > +} > +static int tegra_pcie_dw_remove(struct platform_device *pdev) > +{ > + struct tegra_pcie_dw *pcie = platform_get_drvdata(pdev); > + > + if (pcie->mode == DW_PCIE_RC_TYPE) { Return early if it's not RC and unindent the rest of the function. > + if (!pcie->link_state && pcie->power_down_en) > + return 0; > + > + debugfs_remove_recursive(pcie->debugfs); > + pm_runtime_put_sync(pcie->dev); > + pm_runtime_disable(pcie->dev); > + } > + > + return 0; > +} > +static int tegra_pcie_dw_runtime_suspend(struct device *dev) > +{ > + struct tegra_pcie_dw *pcie = dev_get_drvdata(dev); > + > + tegra_pcie_downstream_dev_to_D0(pcie); > + > + pci_stop_root_bus(pcie->pci.pp.bus); > + pci_remove_root_bus(pcie->pci.pp.bus); Why are you calling these? No other drivers do this except in their .remove() methods. Is there something special about Tegra, or is this something the other drivers *should* be doing? > + tegra_pcie_dw_pme_turnoff(pcie); > + > + reset_control_assert(pcie->core_rst); > + tegra_pcie_disable_phy(pcie); > + reset_control_assert(pcie->core_apb_rst); > + clk_disable_unprepare(pcie->core_clk); > + regulator_disable(pcie->pex_ctl_reg); > + config_plat_gpio(pcie, 0); > + > + if (pcie->cid != CTRL_5) > + tegra_pcie_bpmp_set_ctrl_state(pcie, false); > + > + return 0; > +} > + > +static int tegra_pcie_dw_runtime_resume(struct device *dev) > +{ > + struct tegra_pcie_dw *pcie = dev_get_drvdata(dev); > + struct dw_pcie *pci = &pcie->pci; > + struct pcie_port *pp = &pci->pp; > + int ret = 0; > + > + ret = tegra_pcie_config_controller(pcie, false); > + if (ret < 0) > + return ret; > + > + /* program to use MPS of 256 whereever possible */ s/whereever/wherever/ > + pcie_bus_config = PCIE_BUS_SAFE; > + > + pp->root_bus_nr = -1; > + pp->ops = &tegra_pcie_dw_host_ops; > + > + /* Disable MSI interrupts for PME messages */ Superfluous comment; it repeats the function name. > +static int tegra_pcie_dw_suspend_noirq(struct device *dev) > +{ > + struct tegra_pcie_dw *pcie = dev_get_drvdata(dev); > + int ret = 0; > + > + if (!pcie->link_state) > + return 0; > + > + /* save MSI interrutp vector*/ s/interrutp/interrupt/ s/vector/vector / > +static int tegra_pcie_dw_resume_noirq(struct device *dev) > +{ > + struct tegra_pcie_dw *pcie = dev_get_drvdata(dev); > + int ret; > + > + if (!pcie->link_state) > + return 0; > + > + if (gpio_is_valid(pcie->pex_wake) && device_may_wakeup(dev)) { > + ret = disable_irq_wake(gpio_to_irq(pcie->pex_wake)); > + if (ret < 0) > + dev_err(dev, "disable wake irq failed: %d\n", ret); > + } > + > + ret = tegra_pcie_config_controller(pcie, true); > + if (ret < 0) > + return ret; > + > + ret = tegra_pcie_dw_host_init(&pcie->pci.pp); > + if (ret < 0) { > + dev_err(dev, "failed to init host: %d\n", ret); > + goto fail_host_init; > + } > + > + /* restore MSI interrutp vector*/ s/interrutp/interrupt/ s/vector/vector / > +static void tegra_pcie_dw_shutdown(struct platform_device *pdev) > +{ > + struct tegra_pcie_dw *pcie = platform_get_drvdata(pdev); > + > + if (pcie->mode == DW_PCIE_RC_TYPE) { if (pcie->mode != DW_PCIE_RC_TYPE) return; Then you can unindent the whole function. > + if (!pcie->link_state && pcie->power_down_en) > + return; > + > + debugfs_remove_recursive(pcie->debugfs); > + tegra_pcie_downstream_dev_to_D0(pcie); > + > + /* Disable interrupts */ Superfluous comment. > + disable_irq(pcie->pci.pp.irq);