|
@@ -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]);
|
|
|
}
|
|
|
|