diff options
Diffstat (limited to 'drivers/pci/controller/dwc/pcie-dw-rockchip.c')
-rw-r--r-- | drivers/pci/controller/dwc/pcie-dw-rockchip.c | 106 |
1 files changed, 65 insertions, 41 deletions
diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index c624b7ebd118..93171a392879 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -8,6 +8,7 @@ * Author: Simon Xue <xxm@rock-chips.com> */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/gpio/consumer.h> #include <linux/irqchip/chained_irq.h> @@ -21,6 +22,7 @@ #include <linux/regmap.h> #include <linux/reset.h> +#include "../../pci.h" #include "pcie-designware.h" /* @@ -33,26 +35,36 @@ #define to_rockchip_pcie(x) dev_get_drvdata((x)->dev) -#define PCIE_CLIENT_RC_MODE HIWORD_UPDATE_BIT(0x40) -#define PCIE_CLIENT_EP_MODE HIWORD_UPDATE(0xf0, 0x0) -#define PCIE_CLIENT_ENABLE_LTSSM HIWORD_UPDATE_BIT(0xc) -#define PCIE_CLIENT_DISABLE_LTSSM HIWORD_UPDATE(0x0c, 0x8) -#define PCIE_CLIENT_INTR_STATUS_MISC 0x10 -#define PCIE_CLIENT_INTR_MASK_MISC 0x24 -#define PCIE_SMLH_LINKUP BIT(16) -#define PCIE_RDLH_LINKUP BIT(17) -#define PCIE_LINKUP (PCIE_SMLH_LINKUP | PCIE_RDLH_LINKUP) -#define PCIE_RDLH_LINK_UP_CHGED BIT(1) -#define PCIE_LINK_REQ_RST_NOT_INT BIT(2) -#define PCIE_L0S_ENTRY 0x11 -#define PCIE_CLIENT_GENERAL_CONTROL 0x0 +/* General Control Register */ +#define PCIE_CLIENT_GENERAL_CON 0x0 +#define PCIE_CLIENT_RC_MODE HIWORD_UPDATE_BIT(0x40) +#define PCIE_CLIENT_EP_MODE HIWORD_UPDATE(0xf0, 0x0) +#define PCIE_CLIENT_ENABLE_LTSSM HIWORD_UPDATE_BIT(0xc) +#define PCIE_CLIENT_DISABLE_LTSSM HIWORD_UPDATE(0x0c, 0x8) + +/* Interrupt Status Register Related to Legacy Interrupt */ #define PCIE_CLIENT_INTR_STATUS_LEGACY 0x8 + +/* Interrupt Status Register Related to Miscellaneous Operation */ +#define PCIE_CLIENT_INTR_STATUS_MISC 0x10 +#define PCIE_RDLH_LINK_UP_CHGED BIT(1) +#define PCIE_LINK_REQ_RST_NOT_INT BIT(2) + +/* Interrupt Mask Register Related to Legacy Interrupt */ #define PCIE_CLIENT_INTR_MASK_LEGACY 0x1c -#define PCIE_CLIENT_GENERAL_DEBUG 0x104 + +/* Interrupt Mask Register Related to Miscellaneous Operation */ +#define PCIE_CLIENT_INTR_MASK_MISC 0x24 + +/* Hot Reset Control Register */ #define PCIE_CLIENT_HOT_RESET_CTRL 0x180 +#define PCIE_LTSSM_ENABLE_ENHANCE BIT(4) + +/* LTSSM Status Register */ #define PCIE_CLIENT_LTSSM_STATUS 0x300 -#define PCIE_LTSSM_ENABLE_ENHANCE BIT(4) -#define PCIE_LTSSM_STATUS_MASK GENMASK(5, 0) +#define PCIE_LINKUP 0x3 +#define PCIE_LINKUP_MASK GENMASK(17, 16) +#define PCIE_LTSSM_STATUS_MASK GENMASK(5, 0) struct rockchip_pcie { struct dw_pcie pci; @@ -144,8 +156,8 @@ static int rockchip_pcie_init_irq_domain(struct rockchip_pcie *rockchip) return -EINVAL; } - rockchip->irq_domain = irq_domain_add_linear(intc, PCI_NUM_INTX, - &intx_domain_ops, rockchip); + rockchip->irq_domain = irq_domain_create_linear(of_fwnode_handle(intc), PCI_NUM_INTX, + &intx_domain_ops, rockchip); of_node_put(intc); if (!rockchip->irq_domain) { dev_err(dev, "failed to get a INTx IRQ domain\n"); @@ -163,25 +175,36 @@ static u32 rockchip_pcie_get_ltssm(struct rockchip_pcie *rockchip) static void rockchip_pcie_enable_ltssm(struct rockchip_pcie *rockchip) { rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_ENABLE_LTSSM, - PCIE_CLIENT_GENERAL_CONTROL); + PCIE_CLIENT_GENERAL_CON); } static void rockchip_pcie_disable_ltssm(struct rockchip_pcie *rockchip) { rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_DISABLE_LTSSM, - PCIE_CLIENT_GENERAL_CONTROL); + PCIE_CLIENT_GENERAL_CON); } -static int rockchip_pcie_link_up(struct dw_pcie *pci) +static bool rockchip_pcie_link_up(struct dw_pcie *pci) { struct rockchip_pcie *rockchip = to_rockchip_pcie(pci); u32 val = rockchip_pcie_get_ltssm(rockchip); - if ((val & PCIE_LINKUP) == PCIE_LINKUP && - (val & PCIE_LTSSM_STATUS_MASK) == PCIE_L0S_ENTRY) - return 1; + return FIELD_GET(PCIE_LINKUP_MASK, val) == PCIE_LINKUP; +} - return 0; +static void rockchip_pcie_enable_l0s(struct dw_pcie *pci) +{ + u32 cap, lnkcap; + + /* Enable L0S capability for all SoCs */ + cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + if (cap) { + lnkcap = dw_pcie_readl_dbi(pci, cap + PCI_EXP_LNKCAP); + lnkcap |= PCI_EXP_LNKCAP_ASPM_L0S; + dw_pcie_dbi_ro_wr_en(pci); + dw_pcie_writel_dbi(pci, cap + PCI_EXP_LNKCAP, lnkcap); + dw_pcie_dbi_ro_wr_dis(pci); + } } static int rockchip_pcie_start_link(struct dw_pcie *pci) @@ -202,7 +225,7 @@ static int rockchip_pcie_start_link(struct dw_pcie *pci) * We need more extra time as before, rather than setting just * 100us as we don't know how long should the device need to reset. */ - msleep(100); + msleep(PCIE_T_PVPERL_MS); gpiod_set_value_cansleep(rockchip->rst_gpio, 1); return 0; @@ -233,6 +256,8 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp) irq_set_chained_handler_and_data(irq, rockchip_pcie_intx_handler, rockchip); + rockchip_pcie_enable_l0s(pci); + return 0; } @@ -263,16 +288,14 @@ static void rockchip_pcie_ep_hide_broken_ats_cap_rk3588(struct dw_pcie_ep *ep) dev_err(dev, "failed to hide ATS capability\n"); } -static void rockchip_pcie_ep_pre_init(struct dw_pcie_ep *ep) -{ - rockchip_pcie_ep_hide_broken_ats_cap_rk3588(ep); -} - static void rockchip_pcie_ep_init(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); enum pci_barno bar; + rockchip_pcie_enable_l0s(pci); + rockchip_pcie_ep_hide_broken_ats_cap_rk3588(ep); + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) dw_pcie_ep_reset_bar(pci, bar); }; @@ -342,7 +365,6 @@ rockchip_pcie_get_features(struct dw_pcie_ep *ep) static const struct dw_pcie_ep_ops rockchip_pcie_ep_ops = { .init = rockchip_pcie_ep_init, - .pre_init = rockchip_pcie_ep_pre_init, .raise_irq = rockchip_pcie_raise_irq, .get_features = rockchip_pcie_get_features, }; @@ -410,8 +432,8 @@ static int rockchip_pcie_phy_init(struct rockchip_pcie *rockchip) static void rockchip_pcie_phy_deinit(struct rockchip_pcie *rockchip) { - phy_exit(rockchip->phy); phy_power_off(rockchip->phy); + phy_exit(rockchip->phy); } static const struct dw_pcie_ops dw_pcie_ops = { @@ -426,7 +448,7 @@ static irqreturn_t rockchip_pcie_rc_sys_irq_thread(int irq, void *arg) struct dw_pcie *pci = &rockchip->pci; struct dw_pcie_rp *pp = &pci->pp; struct device *dev = pci->dev; - u32 reg, val; + u32 reg; reg = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_MISC); rockchip_pcie_writel_apb(rockchip, reg, PCIE_CLIENT_INTR_STATUS_MISC); @@ -435,8 +457,7 @@ static irqreturn_t rockchip_pcie_rc_sys_irq_thread(int irq, void *arg) dev_dbg(dev, "LTSSM_STATUS: %#x\n", rockchip_pcie_get_ltssm(rockchip)); if (reg & PCIE_RDLH_LINK_UP_CHGED) { - val = rockchip_pcie_get_ltssm(rockchip); - if ((val & PCIE_LINKUP) == PCIE_LINKUP) { + if (rockchip_pcie_link_up(pci)) { dev_dbg(dev, "Received Link up event. Starting enumeration!\n"); /* Rescan the bus to enumerate endpoint devices */ pci_lock_rescan_remove(); @@ -453,7 +474,7 @@ static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg) struct rockchip_pcie *rockchip = arg; struct dw_pcie *pci = &rockchip->pci; struct device *dev = pci->dev; - u32 reg, val; + u32 reg; reg = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_MISC); rockchip_pcie_writel_apb(rockchip, reg, PCIE_CLIENT_INTR_STATUS_MISC); @@ -467,8 +488,7 @@ static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg) } if (reg & PCIE_RDLH_LINK_UP_CHGED) { - val = rockchip_pcie_get_ltssm(rockchip); - if ((val & PCIE_LINKUP) == PCIE_LINKUP) { + if (rockchip_pcie_link_up(pci)) { dev_dbg(dev, "link up\n"); dw_pcie_ep_linkup(&pci->ep); } @@ -505,7 +525,7 @@ static int rockchip_pcie_configure_rc(struct platform_device *pdev, rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL); rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_RC_MODE, - PCIE_CLIENT_GENERAL_CONTROL); + PCIE_CLIENT_GENERAL_CON); pp = &rockchip->pci.pp; pp->ops = &rockchip_pcie_host_ops; @@ -551,7 +571,7 @@ static int rockchip_pcie_configure_ep(struct platform_device *pdev, rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL); rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_EP_MODE, - PCIE_CLIENT_GENERAL_CONTROL); + PCIE_CLIENT_GENERAL_CON); rockchip->pci.ep.ops = &rockchip_pcie_ep_ops; rockchip->pci.ep.page_size = SZ_64K; @@ -601,6 +621,10 @@ static int rockchip_pcie_probe(struct platform_device *pdev) rockchip->pci.ops = &dw_pcie_ops; rockchip->data = data; + /* Default N_FTS value (210) is broken, override it to 255 */ + rockchip->pci.n_fts[0] = 255; /* Gen1 */ + rockchip->pci.n_fts[1] = 255; /* Gen2+ */ + ret = rockchip_pcie_resource_get(pdev, rockchip); if (ret) return ret; |