123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Rockchip DesignWare based PCIe host controller driver
- *
- * Copyright (c) 2021 Rockchip, Inc.
- */
- #include <common.h>
- #include <clk.h>
- #include <dm.h>
- #include <generic-phy.h>
- #include <pci.h>
- #include <power-domain.h>
- #include <reset.h>
- #include <syscon.h>
- #include <asm/arch-rockchip/clock.h>
- #include <asm/global_data.h>
- #include <asm/io.h>
- #include <asm-generic/gpio.h>
- #include <dm/device_compat.h>
- #include <linux/iopoll.h>
- #include <linux/delay.h>
- #include <power/regulator.h>
- #include "pcie_dw_common.h"
- DECLARE_GLOBAL_DATA_PTR;
- /**
- * struct rk_pcie - RK DW PCIe controller state
- *
- * @vpcie3v3: The 3.3v power supply for slot
- * @apb_base: The base address of vendor regs
- * @rst_gpio: The #PERST signal for slot
- */
- struct rk_pcie {
- /* Must be first member of the struct */
- struct pcie_dw dw;
- struct udevice *vpcie3v3;
- void *apb_base;
- struct phy phy;
- struct clk_bulk clks;
- struct reset_ctl_bulk rsts;
- struct gpio_desc rst_gpio;
- };
- /* Parameters for the waiting for iATU enabled routine */
- #define PCIE_CLIENT_GENERAL_DEBUG 0x104
- #define PCIE_CLIENT_HOT_RESET_CTRL 0x180
- #define PCIE_LTSSM_ENABLE_ENHANCE BIT(4)
- #define PCIE_CLIENT_LTSSM_STATUS 0x300
- #define SMLH_LINKUP BIT(16)
- #define RDLH_LINKUP BIT(17)
- #define PCIE_CLIENT_DBG_FIFO_MODE_CON 0x310
- #define PCIE_CLIENT_DBG_FIFO_PTN_HIT_D0 0x320
- #define PCIE_CLIENT_DBG_FIFO_PTN_HIT_D1 0x324
- #define PCIE_CLIENT_DBG_FIFO_TRN_HIT_D0 0x328
- #define PCIE_CLIENT_DBG_FIFO_TRN_HIT_D1 0x32c
- #define PCIE_CLIENT_DBG_FIFO_STATUS 0x350
- #define PCIE_CLIENT_DBG_TRANSITION_DATA 0xffff0000
- #define PCIE_CLIENT_DBF_EN 0xffff0003
- /* Parameters for the waiting for #perst signal */
- #define MACRO_US 1000
- static int rk_pcie_read(void __iomem *addr, int size, u32 *val)
- {
- if ((uintptr_t)addr & (size - 1)) {
- *val = 0;
- return -EOPNOTSUPP;
- }
- if (size == 4) {
- *val = readl(addr);
- } else if (size == 2) {
- *val = readw(addr);
- } else if (size == 1) {
- *val = readb(addr);
- } else {
- *val = 0;
- return -ENODEV;
- }
- return 0;
- }
- static int rk_pcie_write(void __iomem *addr, int size, u32 val)
- {
- if ((uintptr_t)addr & (size - 1))
- return -EOPNOTSUPP;
- if (size == 4)
- writel(val, addr);
- else if (size == 2)
- writew(val, addr);
- else if (size == 1)
- writeb(val, addr);
- else
- return -ENODEV;
- return 0;
- }
- static u32 __rk_pcie_read_apb(struct rk_pcie *rk_pcie, void __iomem *base,
- u32 reg, size_t size)
- {
- int ret;
- u32 val;
- ret = rk_pcie_read(base + reg, size, &val);
- if (ret)
- dev_err(rk_pcie->dw.dev, "Read APB address failed\n");
- return val;
- }
- static void __rk_pcie_write_apb(struct rk_pcie *rk_pcie, void __iomem *base,
- u32 reg, size_t size, u32 val)
- {
- int ret;
- ret = rk_pcie_write(base + reg, size, val);
- if (ret)
- dev_err(rk_pcie->dw.dev, "Write APB address failed\n");
- }
- /**
- * rk_pcie_readl_apb() - Read vendor regs
- *
- * @rk_pcie: Pointer to the PCI controller state
- * @reg: Offset of regs
- */
- static inline u32 rk_pcie_readl_apb(struct rk_pcie *rk_pcie, u32 reg)
- {
- return __rk_pcie_read_apb(rk_pcie, rk_pcie->apb_base, reg, 0x4);
- }
- /**
- * rk_pcie_writel_apb() - Write vendor regs
- *
- * @rk_pcie: Pointer to the PCI controller state
- * @reg: Offset of regs
- * @val: Value to be writen
- */
- static inline void rk_pcie_writel_apb(struct rk_pcie *rk_pcie, u32 reg,
- u32 val)
- {
- __rk_pcie_write_apb(rk_pcie, rk_pcie->apb_base, reg, 0x4, val);
- }
- /**
- * rk_pcie_configure() - Configure link capabilities and speed
- *
- * @rk_pcie: Pointer to the PCI controller state
- * @cap_speed: The capabilities and speed to configure
- *
- * Configure the link capabilities and speed in the PCIe root complex.
- */
- static void rk_pcie_configure(struct rk_pcie *pci, u32 cap_speed)
- {
- dw_pcie_dbi_write_enable(&pci->dw, true);
- clrsetbits_le32(pci->dw.dbi_base + PCIE_LINK_CAPABILITY,
- TARGET_LINK_SPEED_MASK, cap_speed);
- clrsetbits_le32(pci->dw.dbi_base + PCIE_LINK_CTL_2,
- TARGET_LINK_SPEED_MASK, cap_speed);
- dw_pcie_dbi_write_enable(&pci->dw, false);
- }
- static void rk_pcie_enable_debug(struct rk_pcie *rk_pcie)
- {
- rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_DBG_FIFO_PTN_HIT_D0,
- PCIE_CLIENT_DBG_TRANSITION_DATA);
- rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_DBG_FIFO_PTN_HIT_D1,
- PCIE_CLIENT_DBG_TRANSITION_DATA);
- rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_DBG_FIFO_TRN_HIT_D0,
- PCIE_CLIENT_DBG_TRANSITION_DATA);
- rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_DBG_FIFO_TRN_HIT_D1,
- PCIE_CLIENT_DBG_TRANSITION_DATA);
- rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_DBG_FIFO_MODE_CON,
- PCIE_CLIENT_DBF_EN);
- }
- static void rk_pcie_debug_dump(struct rk_pcie *rk_pcie)
- {
- u32 loop;
- debug("ltssm = 0x%x\n",
- rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_LTSSM_STATUS));
- for (loop = 0; loop < 64; loop++)
- debug("fifo_status = 0x%x\n",
- rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_DBG_FIFO_STATUS));
- }
- static inline void rk_pcie_link_status_clear(struct rk_pcie *rk_pcie)
- {
- rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_GENERAL_DEBUG, 0x0);
- }
- static inline void rk_pcie_disable_ltssm(struct rk_pcie *rk_pcie)
- {
- rk_pcie_writel_apb(rk_pcie, 0x0, 0xc0008);
- }
- static inline void rk_pcie_enable_ltssm(struct rk_pcie *rk_pcie)
- {
- rk_pcie_writel_apb(rk_pcie, 0x0, 0xc000c);
- }
- static int is_link_up(struct rk_pcie *priv)
- {
- u32 val;
- val = rk_pcie_readl_apb(priv, PCIE_CLIENT_LTSSM_STATUS);
- if ((val & (RDLH_LINKUP | SMLH_LINKUP)) == 0x30000 &&
- (val & GENMASK(5, 0)) == 0x11)
- return 1;
- return 0;
- }
- /**
- * rk_pcie_link_up() - Wait for the link to come up
- *
- * @rk_pcie: Pointer to the PCI controller state
- * @cap_speed: Desired link speed
- *
- * Return: 1 (true) for active line and negetive (false) for no link (timeout)
- */
- static int rk_pcie_link_up(struct rk_pcie *priv, u32 cap_speed)
- {
- int retries;
- if (is_link_up(priv)) {
- printf("PCI Link already up before configuration!\n");
- return 1;
- }
- /* DW pre link configurations */
- rk_pcie_configure(priv, cap_speed);
- /* Rest the device */
- if (dm_gpio_is_valid(&priv->rst_gpio)) {
- dm_gpio_set_value(&priv->rst_gpio, 0);
- /*
- * Minimal is 100ms from spec but we see
- * some wired devices need much more, such as 600ms.
- * Add a enough delay to cover all cases.
- */
- udelay(MACRO_US * 1000);
- dm_gpio_set_value(&priv->rst_gpio, 1);
- }
- rk_pcie_disable_ltssm(priv);
- rk_pcie_link_status_clear(priv);
- rk_pcie_enable_debug(priv);
- /* Enable LTSSM */
- rk_pcie_enable_ltssm(priv);
- for (retries = 0; retries < 5; retries++) {
- if (is_link_up(priv)) {
- dev_info(priv->dw.dev, "PCIe Link up, LTSSM is 0x%x\n",
- rk_pcie_readl_apb(priv, PCIE_CLIENT_LTSSM_STATUS));
- rk_pcie_debug_dump(priv);
- return 0;
- }
- dev_info(priv->dw.dev, "PCIe Linking... LTSSM is 0x%x\n",
- rk_pcie_readl_apb(priv, PCIE_CLIENT_LTSSM_STATUS));
- rk_pcie_debug_dump(priv);
- udelay(MACRO_US * 1000);
- }
- dev_err(priv->dw.dev, "PCIe-%d Link Fail\n", dev_seq(priv->dw.dev));
- /* Link maybe in Gen switch recovery but we need to wait more 1s */
- udelay(MACRO_US * 1000);
- return -EIO;
- }
- static int rockchip_pcie_init_port(struct udevice *dev)
- {
- int ret;
- u32 val;
- struct rk_pcie *priv = dev_get_priv(dev);
- /* Set power and maybe external ref clk input */
- if (priv->vpcie3v3) {
- ret = regulator_set_value(priv->vpcie3v3, 3300000);
- if (ret) {
- dev_err(priv->dw.dev, "failed to enable vpcie3v3 (ret=%d)\n",
- ret);
- return ret;
- }
- }
- udelay(MACRO_US * 1000);
- ret = generic_phy_init(&priv->phy);
- if (ret) {
- dev_err(dev, "failed to init phy (ret=%d)\n", ret);
- return ret;
- }
- ret = generic_phy_power_on(&priv->phy);
- if (ret) {
- dev_err(dev, "failed to power on phy (ret=%d)\n", ret);
- goto err_exit_phy;
- }
- ret = reset_deassert_bulk(&priv->rsts);
- if (ret) {
- dev_err(dev, "failed to deassert resets (ret=%d)\n", ret);
- goto err_power_off_phy;
- }
- ret = clk_enable_bulk(&priv->clks);
- if (ret) {
- dev_err(dev, "failed to enable clks (ret=%d)\n", ret);
- goto err_deassert_bulk;
- }
- /* LTSSM EN ctrl mode */
- val = rk_pcie_readl_apb(priv, PCIE_CLIENT_HOT_RESET_CTRL);
- val |= PCIE_LTSSM_ENABLE_ENHANCE | (PCIE_LTSSM_ENABLE_ENHANCE << 16);
- rk_pcie_writel_apb(priv, PCIE_CLIENT_HOT_RESET_CTRL, val);
- /* Set RC mode */
- rk_pcie_writel_apb(priv, 0x0, 0xf00040);
- pcie_dw_setup_host(&priv->dw);
- ret = rk_pcie_link_up(priv, LINK_SPEED_GEN_3);
- if (ret < 0)
- goto err_link_up;
- return 0;
- err_link_up:
- clk_disable_bulk(&priv->clks);
- err_deassert_bulk:
- reset_assert_bulk(&priv->rsts);
- err_power_off_phy:
- generic_phy_power_off(&priv->phy);
- err_exit_phy:
- generic_phy_exit(&priv->phy);
- return ret;
- }
- static int rockchip_pcie_parse_dt(struct udevice *dev)
- {
- struct rk_pcie *priv = dev_get_priv(dev);
- int ret;
- priv->dw.dbi_base = (void *)dev_read_addr_index(dev, 0);
- if (!priv->dw.dbi_base)
- return -ENODEV;
- dev_dbg(dev, "DBI address is 0x%p\n", priv->dw.dbi_base);
- priv->apb_base = (void *)dev_read_addr_index(dev, 1);
- if (!priv->apb_base)
- return -ENODEV;
- dev_dbg(dev, "APB address is 0x%p\n", priv->apb_base);
- ret = gpio_request_by_name(dev, "reset-gpios", 0,
- &priv->rst_gpio, GPIOD_IS_OUT);
- if (ret) {
- dev_err(dev, "failed to find reset-gpios property\n");
- return ret;
- }
- ret = reset_get_bulk(dev, &priv->rsts);
- if (ret) {
- dev_err(dev, "Can't get reset: %d\n", ret);
- return ret;
- }
- ret = clk_get_bulk(dev, &priv->clks);
- if (ret) {
- dev_err(dev, "Can't get clock: %d\n", ret);
- return ret;
- }
- ret = device_get_supply_regulator(dev, "vpcie3v3-supply",
- &priv->vpcie3v3);
- if (ret && ret != -ENOENT) {
- dev_err(dev, "failed to get vpcie3v3 supply (ret=%d)\n", ret);
- return ret;
- }
- ret = generic_phy_get_by_index(dev, 0, &priv->phy);
- if (ret) {
- dev_err(dev, "failed to get pcie phy (ret=%d)\n", ret);
- return ret;
- }
- return 0;
- }
- /**
- * rockchip_pcie_probe() - Probe the PCIe bus for active link
- *
- * @dev: A pointer to the device being operated on
- *
- * Probe for an active link on the PCIe bus and configure the controller
- * to enable this port.
- *
- * Return: 0 on success, else -ENODEV
- */
- static int rockchip_pcie_probe(struct udevice *dev)
- {
- struct rk_pcie *priv = dev_get_priv(dev);
- struct udevice *ctlr = pci_get_controller(dev);
- struct pci_controller *hose = dev_get_uclass_priv(ctlr);
- int ret = 0;
- priv->dw.first_busno = dev_seq(dev);
- priv->dw.dev = dev;
- ret = rockchip_pcie_parse_dt(dev);
- if (ret)
- return ret;
- ret = rockchip_pcie_init_port(dev);
- if (ret)
- return ret;
- dev_info(dev, "PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n",
- dev_seq(dev), pcie_dw_get_link_speed(&priv->dw),
- pcie_dw_get_link_width(&priv->dw),
- hose->first_busno);
- return pcie_dw_prog_outbound_atu_unroll(&priv->dw,
- PCIE_ATU_REGION_INDEX0,
- PCIE_ATU_TYPE_MEM,
- priv->dw.mem.phys_start,
- priv->dw.mem.bus_start,
- priv->dw.mem.size);
- }
- static const struct dm_pci_ops rockchip_pcie_ops = {
- .read_config = pcie_dw_read_config,
- .write_config = pcie_dw_write_config,
- };
- static const struct udevice_id rockchip_pcie_ids[] = {
- { .compatible = "rockchip,rk3568-pcie" },
- { }
- };
- U_BOOT_DRIVER(rockchip_dw_pcie) = {
- .name = "pcie_dw_rockchip",
- .id = UCLASS_PCI,
- .of_match = rockchip_pcie_ids,
- .ops = &rockchip_pcie_ops,
- .probe = rockchip_pcie_probe,
- .priv_auto = sizeof(struct rk_pcie),
- };
|