|
@@ -20,6 +20,7 @@
|
|
|
#include <linux/gpio/consumer.h>
|
|
|
#include <linux/init.h>
|
|
|
#include <linux/interrupt.h>
|
|
|
+#include <linux/iopoll.h>
|
|
|
#include <linux/irq.h>
|
|
|
#include <linux/irqchip/chained_irq.h>
|
|
|
#include <linux/irqdomain.h>
|
|
@@ -55,6 +56,10 @@
|
|
|
#define PCIE_CLIENT_MODE_RC HIWORD_UPDATE_BIT(0x0040)
|
|
|
#define PCIE_CLIENT_GEN_SEL_1 HIWORD_UPDATE(0x0080, 0)
|
|
|
#define PCIE_CLIENT_GEN_SEL_2 HIWORD_UPDATE_BIT(0x0080)
|
|
|
+#define PCIE_CLIENT_DEBUG_OUT_0 (PCIE_CLIENT_BASE + 0x3c)
|
|
|
+#define PCIE_CLIENT_DEBUG_LTSSM_MASK GENMASK(5, 0)
|
|
|
+#define PCIE_CLIENT_DEBUG_LTSSM_L1 0x18
|
|
|
+#define PCIE_CLIENT_DEBUG_LTSSM_L2 0x19
|
|
|
#define PCIE_CLIENT_BASIC_STATUS1 (PCIE_CLIENT_BASE + 0x48)
|
|
|
#define PCIE_CLIENT_LINK_STATUS_UP 0x00300000
|
|
|
#define PCIE_CLIENT_LINK_STATUS_MASK 0x00300000
|
|
@@ -167,9 +172,11 @@
|
|
|
#define IB_ROOT_PORT_REG_SIZE_SHIFT 3
|
|
|
#define AXI_WRAPPER_IO_WRITE 0x6
|
|
|
#define AXI_WRAPPER_MEM_WRITE 0x2
|
|
|
+#define AXI_WRAPPER_NOR_MSG 0xc
|
|
|
|
|
|
#define MAX_AXI_IB_ROOTPORT_REGION_NUM 3
|
|
|
#define MIN_AXI_ADDR_BITS_PASSED 8
|
|
|
+#define PCIE_RC_SEND_PME_OFF 0x11960
|
|
|
#define ROCKCHIP_VENDOR_ID 0x1d87
|
|
|
#define PCIE_ECAM_BUS(x) (((x) & 0xff) << 20)
|
|
|
#define PCIE_ECAM_DEV(x) (((x) & 0x1f) << 15)
|
|
@@ -178,6 +185,9 @@
|
|
|
#define PCIE_ECAM_ADDR(bus, dev, func, reg) \
|
|
|
(PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEV(dev) | \
|
|
|
PCIE_ECAM_FUNC(func) | PCIE_ECAM_REG(reg))
|
|
|
+#define PCIE_LINK_IS_L2(x) \
|
|
|
+ (((x) & PCIE_CLIENT_DEBUG_LTSSM_MASK) == \
|
|
|
+ PCIE_CLIENT_DEBUG_LTSSM_L2)
|
|
|
|
|
|
#define RC_REGION_0_ADDR_TRANS_H 0x00000000
|
|
|
#define RC_REGION_0_ADDR_TRANS_L 0x00000000
|
|
@@ -211,7 +221,9 @@ struct rockchip_pcie {
|
|
|
u32 io_size;
|
|
|
int offset;
|
|
|
phys_addr_t io_bus_addr;
|
|
|
+ void __iomem *msg_region;
|
|
|
u32 mem_size;
|
|
|
+ phys_addr_t msg_bus_addr;
|
|
|
phys_addr_t mem_bus_addr;
|
|
|
};
|
|
|
|
|
@@ -1186,6 +1198,85 @@ static int rockchip_cfg_atu(struct rockchip_pcie *rockchip)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /* assign message regions */
|
|
|
+ rockchip_pcie_prog_ob_atu(rockchip, reg_no + 1 + offset,
|
|
|
+ AXI_WRAPPER_NOR_MSG,
|
|
|
+ 20 - 1, 0, 0);
|
|
|
+
|
|
|
+ rockchip->msg_bus_addr = rockchip->mem_bus_addr +
|
|
|
+ ((reg_no + offset) << 20);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int rockchip_pcie_wait_l2(struct rockchip_pcie *rockchip)
|
|
|
+{
|
|
|
+ u32 value;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ /* send PME_TURN_OFF message */
|
|
|
+ writel(0x0, rockchip->msg_region + PCIE_RC_SEND_PME_OFF);
|
|
|
+
|
|
|
+ /* read LTSSM and wait for falling into L2 link state */
|
|
|
+ err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_DEBUG_OUT_0,
|
|
|
+ value, PCIE_LINK_IS_L2(value), 20,
|
|
|
+ jiffies_to_usecs(5 * HZ));
|
|
|
+ if (err) {
|
|
|
+ dev_err(rockchip->dev, "PCIe link enter L2 timeout!\n");
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int rockchip_pcie_suspend_noirq(struct device *dev)
|
|
|
+{
|
|
|
+ struct rockchip_pcie *rockchip = dev_get_drvdata(dev);
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* disable core and cli int since we don't need to ack PME_ACK */
|
|
|
+ rockchip_pcie_write(rockchip, (PCIE_CLIENT_INT_CLI << 16) |
|
|
|
+ PCIE_CLIENT_INT_CLI, PCIE_CLIENT_INT_MASK);
|
|
|
+ rockchip_pcie_write(rockchip, (u32)PCIE_CORE_INT, PCIE_CORE_INT_MASK);
|
|
|
+
|
|
|
+ ret = rockchip_pcie_wait_l2(rockchip);
|
|
|
+ if (ret) {
|
|
|
+ rockchip_pcie_enable_interrupts(rockchip);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ phy_power_off(rockchip->phy);
|
|
|
+ phy_exit(rockchip->phy);
|
|
|
+
|
|
|
+ clk_disable_unprepare(rockchip->clk_pcie_pm);
|
|
|
+ clk_disable_unprepare(rockchip->hclk_pcie);
|
|
|
+ clk_disable_unprepare(rockchip->aclk_perf_pcie);
|
|
|
+ clk_disable_unprepare(rockchip->aclk_pcie);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int rockchip_pcie_resume_noirq(struct device *dev)
|
|
|
+{
|
|
|
+ struct rockchip_pcie *rockchip = dev_get_drvdata(dev);
|
|
|
+ int err;
|
|
|
+
|
|
|
+ clk_prepare_enable(rockchip->clk_pcie_pm);
|
|
|
+ clk_prepare_enable(rockchip->hclk_pcie);
|
|
|
+ clk_prepare_enable(rockchip->aclk_perf_pcie);
|
|
|
+ clk_prepare_enable(rockchip->aclk_pcie);
|
|
|
+
|
|
|
+ err = rockchip_pcie_init_port(rockchip);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ err = rockchip_cfg_atu(rockchip);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ /* Need this to enter L1 again */
|
|
|
+ rockchip_pcie_update_txcredit_mui(rockchip);
|
|
|
+ rockchip_pcie_enable_interrupts(rockchip);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1209,6 +1300,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
|
|
|
if (!rockchip)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
+ platform_set_drvdata(pdev, rockchip);
|
|
|
rockchip->dev = dev;
|
|
|
|
|
|
err = rockchip_pcie_parse_dt(rockchip);
|
|
@@ -1296,6 +1388,14 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
|
|
|
err = rockchip_cfg_atu(rockchip);
|
|
|
if (err)
|
|
|
goto err_vpcie;
|
|
|
+
|
|
|
+ rockchip->msg_region = devm_ioremap(rockchip->dev,
|
|
|
+ rockchip->msg_bus_addr, SZ_1M);
|
|
|
+ if (!rockchip->msg_region) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto err_vpcie;
|
|
|
+ }
|
|
|
+
|
|
|
bus = pci_scan_root_bus(&pdev->dev, 0, &rockchip_pcie_ops, rockchip, &res);
|
|
|
if (!bus) {
|
|
|
err = -ENOMEM;
|
|
@@ -1329,6 +1429,11 @@ err_aclk_pcie:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static const struct dev_pm_ops rockchip_pcie_pm_ops = {
|
|
|
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_pcie_suspend_noirq,
|
|
|
+ rockchip_pcie_resume_noirq)
|
|
|
+};
|
|
|
+
|
|
|
static const struct of_device_id rockchip_pcie_of_match[] = {
|
|
|
{ .compatible = "rockchip,rk3399-pcie", },
|
|
|
{}
|
|
@@ -1338,6 +1443,7 @@ static struct platform_driver rockchip_pcie_driver = {
|
|
|
.driver = {
|
|
|
.name = "rockchip-pcie",
|
|
|
.of_match_table = rockchip_pcie_of_match,
|
|
|
+ .pm = &rockchip_pcie_pm_ops,
|
|
|
},
|
|
|
.probe = rockchip_pcie_probe,
|
|
|
|