Merge branch 'pci/host-rockchip' into next

* pci/host-rockchip:
  PCI: rockchip: Set vendor ID from local core config space
  PCI: rockchip: Fix rockchip_pcie_probe() error path to free resource list
  PCI: rockchip: Mark PM functions as __maybe_unused
  PCI: rockchip: Use readl_poll_timeout() instead of open-coding it
  PCI: rockchip: Disable RC's ASPM L0s based on DT "aspm-no-l0s"
  PCI: rockchip: Add system PM support
This commit is contained in:
Bjorn Helgaas 2017-02-21 15:15:44 -06:00
commit bcea623c65
2 changed files with 138 additions and 39 deletions

View file

@ -43,6 +43,8 @@ Required properties:
- interrupt-map-mask and interrupt-map: standard PCI properties
Optional Property:
- aspm-no-l0s: RC won't support ASPM L0s. This property is needed if
using 24MHz OSC for RC's PHY.
- ep-gpios: contain the entry for pre-reset gpio
- num-lanes: number of lanes to use
- vpcie3v3-supply: The phandle to the 3.3v regulator to use for PCIe.

View file

@ -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
@ -120,6 +125,7 @@
#define PCIE_CORE_INT_CT BIT(11)
#define PCIE_CORE_INT_UTC BIT(18)
#define PCIE_CORE_INT_MMVC BIT(19)
#define PCIE_CORE_CONFIG_VENDOR (PCIE_CORE_CTRL_MGMT_BASE + 0x44)
#define PCIE_CORE_INT_MASK (PCIE_CORE_CTRL_MGMT_BASE + 0x210)
#define PCIE_RC_BAR_CONF (PCIE_CORE_CTRL_MGMT_BASE + 0x300)
@ -133,13 +139,14 @@
PCIE_CORE_INT_MMVC)
#define PCIE_RC_CONFIG_BASE 0xa00000
#define PCIE_RC_CONFIG_VENDOR (PCIE_RC_CONFIG_BASE + 0x00)
#define PCIE_RC_CONFIG_RID_CCR (PCIE_RC_CONFIG_BASE + 0x08)
#define PCIE_RC_CONFIG_SCC_SHIFT 16
#define PCIE_RC_CONFIG_DCR (PCIE_RC_CONFIG_BASE + 0xc4)
#define PCIE_RC_CONFIG_DCR_CSPL_SHIFT 18
#define PCIE_RC_CONFIG_DCR_CSPL_LIMIT 0xff
#define PCIE_RC_CONFIG_DCR_CPLS_SHIFT 26
#define PCIE_RC_CONFIG_LINK_CAP (PCIE_RC_CONFIG_BASE + 0xcc)
#define PCIE_RC_CONFIG_LINK_CAP_L0S BIT(10)
#define PCIE_RC_CONFIG_LCS (PCIE_RC_CONFIG_BASE + 0xd0)
#define PCIE_RC_CONFIG_L1_SUBSTATE_CTRL2 (PCIE_RC_CONFIG_BASE + 0x90c)
#define PCIE_RC_CONFIG_THP_CAP (PCIE_RC_CONFIG_BASE + 0x274)
@ -167,9 +174,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 +187,12 @@
#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 PCIE_LINK_UP(x) \
(((x) & PCIE_CLIENT_LINK_STATUS_MASK) == PCIE_CLIENT_LINK_STATUS_UP)
#define PCIE_LINK_IS_GEN2(x) \
(((x) & PCIE_CORE_PL_CONF_SPEED_MASK) == PCIE_CORE_PL_CONF_SPEED_5G)
#define RC_REGION_0_ADDR_TRANS_H 0x00000000
#define RC_REGION_0_ADDR_TRANS_L 0x00000000
@ -211,7 +226,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;
};
@ -449,7 +466,6 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip)
struct device *dev = rockchip->dev;
int err;
u32 status;
unsigned long timeout;
gpiod_set_value(rockchip->ep_gpio, 0);
@ -590,23 +606,12 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip)
gpiod_set_value(rockchip->ep_gpio, 1);
/* 500ms timeout value should be enough for Gen1/2 training */
timeout = jiffies + msecs_to_jiffies(500);
for (;;) {
status = rockchip_pcie_read(rockchip,
PCIE_CLIENT_BASIC_STATUS1);
if ((status & PCIE_CLIENT_LINK_STATUS_MASK) ==
PCIE_CLIENT_LINK_STATUS_UP) {
dev_dbg(dev, "PCIe link training gen1 pass!\n");
break;
}
if (time_after(jiffies, timeout)) {
dev_err(dev, "PCIe link training gen1 timeout!\n");
return -ETIMEDOUT;
}
msleep(20);
err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_BASIC_STATUS1,
status, PCIE_LINK_UP(status), 20,
500 * USEC_PER_MSEC);
if (err) {
dev_err(dev, "PCIe link training gen1 timeout!\n");
return -ETIMEDOUT;
}
if (rockchip->link_gen == 2) {
@ -618,22 +623,11 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip)
status |= PCI_EXP_LNKCTL_RL;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS);
timeout = jiffies + msecs_to_jiffies(500);
for (;;) {
status = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL);
if ((status & PCIE_CORE_PL_CONF_SPEED_MASK) ==
PCIE_CORE_PL_CONF_SPEED_5G) {
dev_dbg(dev, "PCIe link training gen2 pass!\n");
break;
}
if (time_after(jiffies, timeout)) {
dev_dbg(dev, "PCIe link training gen2 timeout, fall back to gen1!\n");
break;
}
msleep(20);
}
err = readl_poll_timeout(rockchip->apb_base + PCIE_CORE_CTRL,
status, PCIE_LINK_IS_GEN2(status), 20,
500 * USEC_PER_MSEC);
if (err)
dev_dbg(dev, "PCIe link training gen2 timeout, fall back to gen1!\n");
}
/* Check the final link width from negotiated lane counter from MGMT */
@ -643,7 +637,7 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip)
dev_dbg(dev, "current link width is x%d\n", status);
rockchip_pcie_write(rockchip, ROCKCHIP_VENDOR_ID,
PCIE_RC_CONFIG_VENDOR);
PCIE_CORE_CONFIG_VENDOR);
rockchip_pcie_write(rockchip,
PCI_CLASS_BRIDGE_PCI << PCIE_RC_CONFIG_SCC_SHIFT,
PCIE_RC_CONFIG_RID_CCR);
@ -653,6 +647,13 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip)
status &= ~PCIE_RC_CONFIG_THP_CAP_NEXT_MASK;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_THP_CAP);
/* Clear L0s from RC's link cap */
if (of_property_read_bool(dev->of_node, "aspm-no-l0s")) {
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LINK_CAP);
status &= ~PCIE_RC_CONFIG_LINK_CAP_L0S;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LINK_CAP);
}
rockchip_pcie_write(rockchip, 0x0, PCIE_RC_BAR_CONF);
rockchip_pcie_write(rockchip,
@ -1186,6 +1187,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 __maybe_unused 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 __maybe_unused 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 +1289,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);
@ -1262,7 +1343,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
err = devm_request_pci_bus_resources(dev, &res);
if (err)
goto err_vpcie;
goto err_free_res;
/* Get the I/O and memory ranges from DT */
resource_list_for_each_entry(win, &res) {
@ -1295,11 +1376,19 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
err = rockchip_cfg_atu(rockchip);
if (err)
goto err_vpcie;
goto err_free_res;
rockchip->msg_region = devm_ioremap(rockchip->dev,
rockchip->msg_bus_addr, SZ_1M);
if (!rockchip->msg_region) {
err = -ENOMEM;
goto err_free_res;
}
bus = pci_scan_root_bus(&pdev->dev, 0, &rockchip_pcie_ops, rockchip, &res);
if (!bus) {
err = -ENOMEM;
goto err_vpcie;
goto err_free_res;
}
pci_bus_size_bridges(bus);
@ -1310,6 +1399,8 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
pci_bus_add_devices(bus);
return err;
err_free_res:
pci_free_resource_list(&res);
err_vpcie:
if (!IS_ERR(rockchip->vpcie3v3))
regulator_disable(rockchip->vpcie3v3);
@ -1329,6 +1420,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 +1434,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,