123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * pcie_uniphier.c - Socionext UniPhier PCIe driver
- * Copyright 2019-2021 Socionext, Inc.
- */
- #include <clk.h>
- #include <common.h>
- #include <dm.h>
- #include <dm/device_compat.h>
- #include <generic-phy.h>
- #include <linux/bitfield.h>
- #include <linux/bitops.h>
- #include <linux/compat.h>
- #include <linux/delay.h>
- #include <linux/io.h>
- #include <pci.h>
- #include <reset.h>
- DECLARE_GLOBAL_DATA_PTR;
- /* DBI registers */
- #define PCIE_LINK_STATUS_REG 0x0080
- #define PCIE_LINK_STATUS_WIDTH_MASK GENMASK(25, 20)
- #define PCIE_LINK_STATUS_SPEED_MASK GENMASK(19, 16)
- #define PCIE_MISC_CONTROL_1_OFF 0x08BC
- #define PCIE_DBI_RO_WR_EN BIT(0)
- /* DBI iATU registers */
- #define PCIE_ATU_VIEWPORT 0x0900
- #define PCIE_ATU_REGION_INBOUND BIT(31)
- #define PCIE_ATU_REGION_OUTBOUND 0
- #define PCIE_ATU_REGION_INDEX_MASK GENMASK(3, 0)
- #define PCIE_ATU_CR1 0x0904
- #define PCIE_ATU_TYPE_MEM 0
- #define PCIE_ATU_TYPE_IO 2
- #define PCIE_ATU_TYPE_CFG0 4
- #define PCIE_ATU_TYPE_CFG1 5
- #define PCIE_ATU_CR2 0x0908
- #define PCIE_ATU_ENABLE BIT(31)
- #define PCIE_ATU_MATCH_MODE BIT(30)
- #define PCIE_ATU_BAR_NUM_MASK GENMASK(10, 8)
- #define PCIE_ATU_LOWER_BASE 0x090C
- #define PCIE_ATU_UPPER_BASE 0x0910
- #define PCIE_ATU_LIMIT 0x0914
- #define PCIE_ATU_LOWER_TARGET 0x0918
- #define PCIE_ATU_BUS(x) FIELD_PREP(GENMASK(31, 24), x)
- #define PCIE_ATU_DEV(x) FIELD_PREP(GENMASK(23, 19), x)
- #define PCIE_ATU_FUNC(x) FIELD_PREP(GENMASK(18, 16), x)
- #define PCIE_ATU_UPPER_TARGET 0x091C
- /* Link Glue registers */
- #define PCL_PINCTRL0 0x002c
- #define PCL_PERST_PLDN_REGEN BIT(12)
- #define PCL_PERST_NOE_REGEN BIT(11)
- #define PCL_PERST_OUT_REGEN BIT(8)
- #define PCL_PERST_PLDN_REGVAL BIT(4)
- #define PCL_PERST_NOE_REGVAL BIT(3)
- #define PCL_PERST_OUT_REGVAL BIT(0)
- #define PCL_MODE 0x8000
- #define PCL_MODE_REGEN BIT(8)
- #define PCL_MODE_REGVAL BIT(0)
- #define PCL_APP_READY_CTRL 0x8008
- #define PCL_APP_LTSSM_ENABLE BIT(0)
- #define PCL_APP_PM0 0x8078
- #define PCL_SYS_AUX_PWR_DET BIT(8)
- #define PCL_STATUS_LINK 0x8140
- #define PCL_RDLH_LINK_UP BIT(1)
- #define PCL_XMLH_LINK_UP BIT(0)
- #define LINK_UP_TIMEOUT_MS 100
- struct uniphier_pcie_priv {
- void *base;
- void *dbi_base;
- void *cfg_base;
- fdt_size_t cfg_size;
- struct fdt_resource link_res;
- struct fdt_resource dbi_res;
- struct fdt_resource cfg_res;
- struct clk clk;
- struct reset_ctl rst;
- struct phy phy;
- struct pci_region io;
- struct pci_region mem;
- };
- static int pcie_dw_get_link_speed(struct uniphier_pcie_priv *priv)
- {
- u32 val = readl(priv->dbi_base + PCIE_LINK_STATUS_REG);
- return FIELD_GET(PCIE_LINK_STATUS_SPEED_MASK, val);
- }
- static int pcie_dw_get_link_width(struct uniphier_pcie_priv *priv)
- {
- u32 val = readl(priv->dbi_base + PCIE_LINK_STATUS_REG);
- return FIELD_GET(PCIE_LINK_STATUS_WIDTH_MASK, val);
- }
- static void pcie_dw_prog_outbound_atu(struct uniphier_pcie_priv *priv,
- int index, int type, u64 cpu_addr,
- u64 pci_addr, u32 size)
- {
- writel(PCIE_ATU_REGION_OUTBOUND
- | FIELD_PREP(PCIE_ATU_REGION_INDEX_MASK, index),
- priv->dbi_base + PCIE_ATU_VIEWPORT);
- writel(lower_32_bits(cpu_addr),
- priv->dbi_base + PCIE_ATU_LOWER_BASE);
- writel(upper_32_bits(cpu_addr),
- priv->dbi_base + PCIE_ATU_UPPER_BASE);
- writel(lower_32_bits(cpu_addr + size - 1),
- priv->dbi_base + PCIE_ATU_LIMIT);
- writel(lower_32_bits(pci_addr),
- priv->dbi_base + PCIE_ATU_LOWER_TARGET);
- writel(upper_32_bits(pci_addr),
- priv->dbi_base + PCIE_ATU_UPPER_TARGET);
- writel(type, priv->dbi_base + PCIE_ATU_CR1);
- writel(PCIE_ATU_ENABLE, priv->dbi_base + PCIE_ATU_CR2);
- }
- static int uniphier_pcie_addr_valid(pci_dev_t bdf, int first_busno)
- {
- /* accept only device {0,1} on first bus */
- if ((PCI_BUS(bdf) != first_busno) || (PCI_DEV(bdf) > 1))
- return -EINVAL;
- return 0;
- }
- static int uniphier_pcie_conf_address(const struct udevice *dev, pci_dev_t bdf,
- uint offset, void **paddr)
- {
- struct uniphier_pcie_priv *priv = dev_get_priv(dev);
- u32 busdev;
- int seq = dev_seq(dev);
- int ret;
- ret = uniphier_pcie_addr_valid(bdf, seq);
- if (ret)
- return ret;
- if ((PCI_BUS(bdf) == seq) && !PCI_DEV(bdf)) {
- *paddr = (void *)(priv->dbi_base + offset);
- return 0;
- }
- busdev = PCIE_ATU_BUS(PCI_BUS(bdf) - seq)
- | PCIE_ATU_DEV(PCI_DEV(bdf))
- | PCIE_ATU_FUNC(PCI_FUNC(bdf));
- pcie_dw_prog_outbound_atu(priv, 0,
- PCIE_ATU_TYPE_CFG0, (u64)priv->cfg_base,
- busdev, priv->cfg_size);
- *paddr = (void *)(priv->cfg_base + offset);
- return 0;
- }
- static int uniphier_pcie_read_config(const struct udevice *dev, pci_dev_t bdf,
- uint offset, ulong *valp,
- enum pci_size_t size)
- {
- return pci_generic_mmap_read_config(dev, uniphier_pcie_conf_address,
- bdf, offset, valp, size);
- }
- static int uniphier_pcie_write_config(struct udevice *dev, pci_dev_t bdf,
- uint offset, ulong val,
- enum pci_size_t size)
- {
- return pci_generic_mmap_write_config(dev, uniphier_pcie_conf_address,
- bdf, offset, val, size);
- }
- static void uniphier_pcie_ltssm_enable(struct uniphier_pcie_priv *priv,
- bool enable)
- {
- u32 val;
- val = readl(priv->base + PCL_APP_READY_CTRL);
- if (enable)
- val |= PCL_APP_LTSSM_ENABLE;
- else
- val &= ~PCL_APP_LTSSM_ENABLE;
- writel(val, priv->base + PCL_APP_READY_CTRL);
- }
- static int uniphier_pcie_link_up(struct uniphier_pcie_priv *priv)
- {
- u32 val, mask;
- val = readl(priv->base + PCL_STATUS_LINK);
- mask = PCL_RDLH_LINK_UP | PCL_XMLH_LINK_UP;
- return (val & mask) == mask;
- }
- static int uniphier_pcie_wait_link(struct uniphier_pcie_priv *priv)
- {
- unsigned long timeout;
- timeout = get_timer(0) + LINK_UP_TIMEOUT_MS;
- while (get_timer(0) < timeout) {
- if (uniphier_pcie_link_up(priv))
- return 0;
- }
- return -ETIMEDOUT;
- }
- static int uniphier_pcie_establish_link(struct uniphier_pcie_priv *priv)
- {
- if (uniphier_pcie_link_up(priv))
- return 0;
- uniphier_pcie_ltssm_enable(priv, true);
- return uniphier_pcie_wait_link(priv);
- }
- static void uniphier_pcie_init_rc(struct uniphier_pcie_priv *priv)
- {
- u32 val;
- /* set RC mode */
- val = readl(priv->base + PCL_MODE);
- val |= PCL_MODE_REGEN;
- val &= ~PCL_MODE_REGVAL;
- writel(val, priv->base + PCL_MODE);
- /* use auxiliary power detection */
- val = readl(priv->base + PCL_APP_PM0);
- val |= PCL_SYS_AUX_PWR_DET;
- writel(val, priv->base + PCL_APP_PM0);
- /* assert PERST# */
- val = readl(priv->base + PCL_PINCTRL0);
- val &= ~(PCL_PERST_NOE_REGVAL | PCL_PERST_OUT_REGVAL
- | PCL_PERST_PLDN_REGVAL);
- val |= PCL_PERST_NOE_REGEN | PCL_PERST_OUT_REGEN
- | PCL_PERST_PLDN_REGEN;
- writel(val, priv->base + PCL_PINCTRL0);
- uniphier_pcie_ltssm_enable(priv, false);
- mdelay(100);
- /* deassert PERST# */
- val = readl(priv->base + PCL_PINCTRL0);
- val |= PCL_PERST_OUT_REGVAL | PCL_PERST_OUT_REGEN;
- writel(val, priv->base + PCL_PINCTRL0);
- }
- static void uniphier_pcie_setup_rc(struct uniphier_pcie_priv *priv,
- struct pci_controller *hose)
- {
- /* Store the IO and MEM windows settings for future use by the ATU */
- priv->io.phys_start = hose->regions[0].phys_start; /* IO base */
- priv->io.bus_start = hose->regions[0].bus_start; /* IO_bus_addr */
- priv->io.size = hose->regions[0].size; /* IO size */
- priv->mem.phys_start = hose->regions[1].phys_start; /* MEM base */
- priv->mem.bus_start = hose->regions[1].bus_start; /* MEM_bus_addr */
- priv->mem.size = hose->regions[1].size; /* MEM size */
- /* outbound: IO */
- pcie_dw_prog_outbound_atu(priv, 0,
- PCIE_ATU_TYPE_IO, priv->io.phys_start,
- priv->io.bus_start, priv->io.size);
- /* outbound: MEM */
- pcie_dw_prog_outbound_atu(priv, 1,
- PCIE_ATU_TYPE_MEM, priv->mem.phys_start,
- priv->mem.bus_start, priv->mem.size);
- }
- static int uniphier_pcie_probe(struct udevice *dev)
- {
- struct uniphier_pcie_priv *priv = dev_get_priv(dev);
- struct udevice *ctlr = pci_get_controller(dev);
- struct pci_controller *hose = dev_get_uclass_priv(ctlr);
- int ret;
- priv->base = map_physmem(priv->link_res.start,
- fdt_resource_size(&priv->link_res),
- MAP_NOCACHE);
- priv->dbi_base = map_physmem(priv->dbi_res.start,
- fdt_resource_size(&priv->dbi_res),
- MAP_NOCACHE);
- priv->cfg_size = fdt_resource_size(&priv->cfg_res);
- priv->cfg_base = map_physmem(priv->cfg_res.start,
- priv->cfg_size, MAP_NOCACHE);
- ret = clk_enable(&priv->clk);
- if (ret) {
- dev_err(dev, "Failed to enable clk: %d\n", ret);
- return ret;
- }
- ret = reset_deassert(&priv->rst);
- if (ret) {
- dev_err(dev, "Failed to deassert reset: %d\n", ret);
- goto out_clk_release;
- }
- ret = generic_phy_init(&priv->phy);
- if (ret) {
- dev_err(dev, "Failed to initialize phy: %d\n", ret);
- goto out_reset_release;
- }
- ret = generic_phy_power_on(&priv->phy);
- if (ret) {
- dev_err(dev, "Failed to power on phy: %d\n", ret);
- goto out_phy_exit;
- }
- uniphier_pcie_init_rc(priv);
- /* set DBI to read only */
- writel(0, priv->dbi_base + PCIE_MISC_CONTROL_1_OFF);
- uniphier_pcie_setup_rc(priv, hose);
- if (uniphier_pcie_establish_link(priv)) {
- printf("PCIE-%d: Link down\n", dev_seq(dev));
- } else {
- printf("PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n",
- dev_seq(dev), pcie_dw_get_link_speed(priv),
- pcie_dw_get_link_width(priv), hose->first_busno);
- }
- return 0;
- out_phy_exit:
- generic_phy_exit(&priv->phy);
- out_reset_release:
- reset_release_all(&priv->rst, 1);
- out_clk_release:
- clk_release_all(&priv->clk, 1);
- return ret;
- }
- static int uniphier_pcie_of_to_plat(struct udevice *dev)
- {
- struct uniphier_pcie_priv *priv = dev_get_priv(dev);
- const void *fdt = gd->fdt_blob;
- int node = dev_of_offset(dev);
- int ret;
- ret = fdt_get_named_resource(fdt, node, "reg", "reg-names",
- "link", &priv->link_res);
- if (ret) {
- dev_err(dev, "Failed to get link regs: %d\n", ret);
- return ret;
- }
- ret = fdt_get_named_resource(fdt, node, "reg", "reg-names",
- "dbi", &priv->dbi_res);
- if (ret) {
- dev_err(dev, "Failed to get dbi regs: %d\n", ret);
- return ret;
- }
- ret = fdt_get_named_resource(fdt, node, "reg", "reg-names",
- "config", &priv->cfg_res);
- if (ret) {
- dev_err(dev, "Failed to get config regs: %d\n", ret);
- return ret;
- }
- ret = clk_get_by_index(dev, 0, &priv->clk);
- if (ret) {
- dev_err(dev, "Failed to get clocks property: %d\n", ret);
- return ret;
- }
- ret = reset_get_by_index(dev, 0, &priv->rst);
- if (ret) {
- dev_err(dev, "Failed to get resets property: %d\n", ret);
- return ret;
- }
- ret = generic_phy_get_by_index(dev, 0, &priv->phy);
- if (ret) {
- dev_err(dev, "Failed to get phy property: %d\n", ret);
- return ret;
- }
- return 0;
- }
- static const struct dm_pci_ops uniphier_pcie_ops = {
- .read_config = uniphier_pcie_read_config,
- .write_config = uniphier_pcie_write_config,
- };
- static const struct udevice_id uniphier_pcie_ids[] = {
- { .compatible = "socionext,uniphier-pcie", },
- { /* Sentinel */ }
- };
- U_BOOT_DRIVER(pcie_uniphier) = {
- .name = "uniphier-pcie",
- .id = UCLASS_PCI,
- .of_match = uniphier_pcie_ids,
- .probe = uniphier_pcie_probe,
- .ops = &uniphier_pcie_ops,
- .of_to_plat = uniphier_pcie_of_to_plat,
- .priv_auto = sizeof(struct uniphier_pcie_priv),
- };
|