PCI: rockchip: Idle inactive PHY(s)

Check the status of all lanes and idle the inactive one(s).

Tested-by: Jeffy Chen <jeffy.chen@rock-chips.com>
Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
[bhelgaas: always set lanes_map, even for legacy_phy case]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Brian Norris <briannorris@chromium.org>
Acked-by: Kishon Vijay Abraham I <kishon@ti.com>
This commit is contained in:
Shawn Lin 2017-07-19 17:55:15 +08:00 committed by Bjorn Helgaas
parent 90a7612d07
commit f06c6c41e6

View file

@ -15,6 +15,7 @@
* (at your option) any later version.
*/
#include <linux/bitrev.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
@ -112,6 +113,9 @@
#define PCIE_CORE_TXCREDIT_CFG1_MUI_SHIFT 16
#define PCIE_CORE_TXCREDIT_CFG1_MUI_ENCODE(x) \
(((x) >> 3) << PCIE_CORE_TXCREDIT_CFG1_MUI_SHIFT)
#define PCIE_CORE_LANE_MAP (PCIE_CORE_CTRL_MGMT_BASE + 0x200)
#define PCIE_CORE_LANE_MAP_MASK 0x0000000f
#define PCIE_CORE_LANE_MAP_REVERSE BIT(16)
#define PCIE_CORE_INT_STATUS (PCIE_CORE_CTRL_MGMT_BASE + 0x20c)
#define PCIE_CORE_INT_PRFPE BIT(0)
#define PCIE_CORE_INT_CRFPE BIT(1)
@ -230,6 +234,7 @@ struct rockchip_pcie {
struct regulator *vpcie0v9; /* 0.9V power supply */
struct gpio_desc *ep_gpio;
u32 lanes;
u8 lanes_map;
u8 root_bus_nr;
int link_gen;
struct device *dev;
@ -302,6 +307,24 @@ static int rockchip_pcie_valid_device(struct rockchip_pcie *rockchip,
return 1;
}
static u8 rockchip_pcie_lane_map(struct rockchip_pcie *rockchip)
{
u32 val;
u8 map;
if (rockchip->legacy_phy)
return GENMASK(MAX_LANE_NUM - 1, 0);
val = rockchip_pcie_read(rockchip, PCIE_CORE_LANE_MAP);
map = val & PCIE_CORE_LANE_MAP_MASK;
/* The link may be using a reverse-indexed mapping. */
if (val & PCIE_CORE_LANE_MAP_REVERSE)
map = bitrev8(map) >> 4;
return map;
}
static int rockchip_pcie_rd_own_conf(struct rockchip_pcie *rockchip,
int where, int size, u32 *val)
{
@ -698,6 +721,15 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip)
PCIE_CORE_PL_CONF_LANE_SHIFT);
dev_dbg(dev, "current link width is x%d\n", status);
/* Power off unused lane(s) */
rockchip->lanes_map = rockchip_pcie_lane_map(rockchip);
for (i = 0; i < MAX_LANE_NUM; i++) {
if (!(rockchip->lanes_map & BIT(i))) {
dev_dbg(dev, "idling lane %d\n", i);
phy_power_off(rockchip->phys[i]);
}
}
rockchip_pcie_write(rockchip, ROCKCHIP_VENDOR_ID,
PCIE_CORE_CONFIG_VENDOR);
rockchip_pcie_write(rockchip,
@ -1349,7 +1381,9 @@ static int __maybe_unused rockchip_pcie_suspend_noirq(struct device *dev)
}
for (i = 0; i < MAX_LANE_NUM; i++) {
phy_power_off(rockchip->phys[i]);
/* inactive lanes are already powered off */
if (rockchip->lanes_map & BIT(i))
phy_power_off(rockchip->phys[i]);
phy_exit(rockchip->phys[i]);
}
@ -1597,7 +1631,9 @@ static int rockchip_pcie_remove(struct platform_device *pdev)
irq_domain_remove(rockchip->irq_domain);
for (i = 0; i < MAX_LANE_NUM; i++) {
phy_power_off(rockchip->phys[i]);
/* inactive lanes are already powered off */
if (rockchip->lanes_map & BIT(i))
phy_power_off(rockchip->phys[i]);
phy_exit(rockchip->phys[i]);
}