From 5fcded5e857cf66c9592e4be28c4dab4520c9177 Mon Sep 17 00:00:00 2001 From: Sergio Paracuellos Date: Thu, 9 Apr 2020 13:16:52 +0200 Subject: [PATCH] staging: mt7621-pci: properly power off dual-ported pcie phy Pcie phy for pcie0 and pcie1 is shared using a dual ported one. Current code was assuming that if nothing is connected in pcie0 it won't be also nothing connected in pcie1. This assumtion is wrong for some devices such us 'Mikrotik rbm33g' and 'ZyXEL LTE3301-PLUS' where only connecting a card to the second bus on the phy is possible. For such devices kernel hangs in the same point because of the wrong poweroff of the phy getting the following trace: mt7621-pci-phy 1e149000.pcie-phy: PHY for 0xbe149000 (dual port = 1) mt7621-pci-phy 1e14a000.pcie-phy: PHY for 0xbe14a000 (dual port = 0) mt7621-pci-phy 1e149000.pcie-phy: Xtal is 40MHz mt7621-pci-phy 1e14a000.pcie-phy: Xtal is 40MHz mt7621-pci 1e140000.pcie: pcie0 no card, disable it (RST & CLK) [hangs] The wrong assumption is located in the 'mt7621_pcie_init_ports' function where we are just making a power off of the phy for slots 0 and 2 if nothing is connected in them. Hence, only poweroff the phy if nothing is connected in both slot 0 and slot 1 avoiding the kernel to hang. Fixes: 5737cfe87a9c ("staging: mt7621-pci: avoid to poweroff the phy for slot one") Signed-off-by: Sergio Paracuellos Link: https://lore.kernel.org/r/20200409111652.30964-1-sergio.paracuellos@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/mt7621-pci/pci-mt7621.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) --- a/drivers/staging/mt7621-pci/pci-mt7621.c +++ b/drivers/staging/mt7621-pci/pci-mt7621.c @@ -502,17 +502,25 @@ static void mt7621_pcie_init_ports(struc mt7621_pcie_reset_ep_deassert(pcie); + tmp = NULL; list_for_each_entry(port, &pcie->ports, list) { u32 slot = port->slot; if (!mt7621_pcie_port_is_linkup(port)) { dev_err(dev, "pcie%d no card, disable it (RST & CLK)\n", slot); - if (slot != 1) - phy_power_off(port->phy); mt7621_control_assert(port); mt7621_pcie_port_clk_disable(port); port->enabled = false; + + if (slot == 0) { + tmp = port; + continue; + } + + if (slot == 1 && tmp && !tmp->enabled) + phy_power_off(tmp->phy); + } } }