123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * SiFive FU740 DesignWare PCIe Controller
- *
- * Copyright (C) 2020-2021 SiFive, Inc.
- *
- * Based in early part on the i.MX6 PCIe host controller shim which is:
- *
- * Copyright (C) 2013 Kosagi
- * http://www.kosagi.com
- *
- * Based on driver from author: Alan Mikhak <amikhak@wirelessfabric.com>
- */
- #include <asm/io.h>
- #include <asm-generic/gpio.h>
- #include <clk.h>
- #include <common.h>
- #include <dm.h>
- #include <dm/device_compat.h>
- #include <generic-phy.h>
- #include <linux/bitops.h>
- #include <linux/log2.h>
- #include <pci.h>
- #include <pci_ep.h>
- #include <pci_ids.h>
- #include <regmap.h>
- #include <reset.h>
- #include <syscon.h>
- #include "pcie_dw_common.h"
- struct pcie_sifive {
- /* Must be first member of the struct */
- struct pcie_dw dw;
- /* private control regs */
- void __iomem *priv_base;
- /* reset, power, clock resources */
- int sys_int_pin;
- struct gpio_desc pwren_gpio;
- struct gpio_desc reset_gpio;
- struct clk aux_ck;
- struct reset_ctl reset;
- };
- enum pcie_sifive_devtype {
- SV_PCIE_UNKNOWN_TYPE = 0,
- SV_PCIE_ENDPOINT_TYPE = 1,
- SV_PCIE_HOST_TYPE = 3
- };
- #define ASSERTION_DELAY 100
- #define PCIE_PERST_ASSERT 0x0
- #define PCIE_PERST_DEASSERT 0x1
- #define PCIE_PHY_RESET 0x1
- #define PCIE_PHY_RESET_DEASSERT 0x0
- #define GPIO_LOW 0x0
- #define GPIO_HIGH 0x1
- #define PCIE_PHY_SEL 0x1
- #define sv_info(sv, fmt, arg...) printf(fmt, ## arg)
- #define sv_warn(sv, fmt, arg...) printf(fmt, ## arg)
- #define sv_debug(sv, fmt, arg...) debug(fmt, ## arg)
- #define sv_err(sv, fmt, arg...) printf(fmt, ## arg)
- /* Doorbell Interface */
- #define DBI_OFFSET 0x0
- #define DBI_SIZE 0x1000
- #define PL_OFFSET 0x700
- #define PHY_DEBUG_R0 (PL_OFFSET + 0x28)
- #define PHY_DEBUG_R1 (PL_OFFSET + 0x2c)
- #define PHY_DEBUG_R1_LINK_UP (0x1 << 4)
- #define PHY_DEBUG_R1_LINK_IN_TRAINING (0x1 << 29)
- #define PCIE_MISC_CONTROL_1 0x8bc
- #define DBI_RO_WR_EN BIT(0)
- /* pcie reset */
- #define PCIEX8MGMT_PERST_N 0x0
- /* LTSSM */
- #define PCIEX8MGMT_APP_LTSSM_ENABLE 0x10
- #define LTSSM_ENABLE_BIT BIT(0)
- /* phy reset */
- #define PCIEX8MGMT_APP_HOLD_PHY_RST 0x18
- /* device type */
- #define PCIEX8MGMT_DEVICE_TYPE 0x708
- #define DEVICE_TYPE_EP 0x0
- #define DEVICE_TYPE_RC 0x4
- /* phy control registers*/
- #define PCIEX8MGMT_PHY0_CR_PARA_ADDR 0x860
- #define PCIEX8MGMT_PHY0_CR_PARA_RD_EN 0x870
- #define PCIEX8MGMT_PHY0_CR_PARA_RD_DATA 0x878
- #define PCIEX8MGMT_PHY0_CR_PARA_SEL 0x880
- #define PCIEX8MGMT_PHY0_CR_PARA_WR_DATA 0x888
- #define PCIEX8MGMT_PHY0_CR_PARA_WR_EN 0x890
- #define PCIEX8MGMT_PHY0_CR_PARA_ACK 0x898
- #define PCIEX8MGMT_PHY1_CR_PARA_ADDR 0x8a0
- #define PCIEX8MGMT_PHY1_CR_PARA_RD_EN 0x8b0
- #define PCIEX8MGMT_PHY1_CR_PARA_RD_DATA 0x8b8
- #define PCIEX8MGMT_PHY1_CR_PARA_SEL 0x8c0
- #define PCIEX8MGMT_PHY1_CR_PARA_WR_DATA 0x8c8
- #define PCIEX8MGMT_PHY1_CR_PARA_WR_EN 0x8d0
- #define PCIEX8MGMT_PHY1_CR_PARA_ACK 0x8d8
- #define PCIEX8MGMT_LANE_NUM 8
- #define PCIEX8MGMT_LANE 0x1008
- #define PCIEX8MGMT_LANE_OFF 0x100
- #define PCIEX8MGMT_TERM_MODE 0x0e21
- #define PCIE_CAP_BASE 0x70
- #define PCI_CONFIG(r) (DBI_OFFSET + (r))
- #define PCIE_CAPABILITIES(r) PCI_CONFIG(PCIE_CAP_BASE + (r))
- /* Link capability */
- #define PF0_PCIE_CAP_LINK_CAP PCIE_CAPABILITIES(0xc)
- #define PCIE_LINK_CAP_MAX_SPEED_MASK 0xf
- #define PCIE_LINK_CAP_MAX_SPEED_GEN1 BIT(0)
- #define PCIE_LINK_CAP_MAX_SPEED_GEN2 BIT(1)
- #define PCIE_LINK_CAP_MAX_SPEED_GEN3 BIT(2)
- #define PCIE_LINK_CAP_MAX_SPEED_GEN4 BIT(3)
- static enum pcie_sifive_devtype pcie_sifive_get_devtype(struct pcie_sifive *sv)
- {
- u32 val;
- val = readl(sv->priv_base + PCIEX8MGMT_DEVICE_TYPE);
- switch (val) {
- case DEVICE_TYPE_RC:
- return SV_PCIE_HOST_TYPE;
- case DEVICE_TYPE_EP:
- return SV_PCIE_ENDPOINT_TYPE;
- default:
- return SV_PCIE_UNKNOWN_TYPE;
- }
- }
- static void pcie_sifive_priv_set_state(struct pcie_sifive *sv, u32 reg,
- u32 bits, int state)
- {
- u32 val;
- val = readl(sv->priv_base + reg);
- val = state ? (val | bits) : (val & !bits);
- writel(val, sv->priv_base + reg);
- }
- static void pcie_sifive_assert_reset(struct pcie_sifive *sv)
- {
- dm_gpio_set_value(&sv->reset_gpio, GPIO_LOW);
- writel(PCIE_PERST_ASSERT, sv->priv_base + PCIEX8MGMT_PERST_N);
- mdelay(ASSERTION_DELAY);
- }
- static void pcie_sifive_power_on(struct pcie_sifive *sv)
- {
- dm_gpio_set_value(&sv->pwren_gpio, GPIO_HIGH);
- mdelay(ASSERTION_DELAY);
- }
- static void pcie_sifive_deassert_reset(struct pcie_sifive *sv)
- {
- writel(PCIE_PERST_DEASSERT, sv->priv_base + PCIEX8MGMT_PERST_N);
- dm_gpio_set_value(&sv->reset_gpio, GPIO_HIGH);
- mdelay(ASSERTION_DELAY);
- }
- static int pcie_sifive_setphy(const u8 phy, const u8 write,
- const u16 addr, const u16 wrdata,
- u16 *rddata, struct pcie_sifive *sv)
- {
- unsigned char ack = 0;
- if (!(phy == 0 || phy == 1))
- return -2;
- /* setup phy para */
- writel(addr, sv->priv_base +
- (phy ? PCIEX8MGMT_PHY1_CR_PARA_ADDR :
- PCIEX8MGMT_PHY0_CR_PARA_ADDR));
- if (write)
- writel(wrdata, sv->priv_base +
- (phy ? PCIEX8MGMT_PHY1_CR_PARA_WR_DATA :
- PCIEX8MGMT_PHY0_CR_PARA_WR_DATA));
- /* enable access if write */
- if (write)
- writel(1, sv->priv_base +
- (phy ? PCIEX8MGMT_PHY1_CR_PARA_WR_EN :
- PCIEX8MGMT_PHY0_CR_PARA_WR_EN));
- else
- writel(1, sv->priv_base +
- (phy ? PCIEX8MGMT_PHY1_CR_PARA_RD_EN :
- PCIEX8MGMT_PHY0_CR_PARA_RD_EN));
- /* wait for wait_idle */
- do {
- u32 val;
- val = readl(sv->priv_base +
- (phy ? PCIEX8MGMT_PHY1_CR_PARA_ACK :
- PCIEX8MGMT_PHY0_CR_PARA_ACK));
- if (val) {
- ack = 1;
- if (!write)
- readl(sv->priv_base +
- (phy ? PCIEX8MGMT_PHY1_CR_PARA_RD_DATA :
- PCIEX8MGMT_PHY0_CR_PARA_RD_DATA));
- mdelay(1);
- }
- } while (!ack);
- /* clear */
- if (write)
- writel(0, sv->priv_base +
- (phy ? PCIEX8MGMT_PHY1_CR_PARA_WR_EN :
- PCIEX8MGMT_PHY0_CR_PARA_WR_EN));
- else
- writel(0, sv->priv_base +
- (phy ? PCIEX8MGMT_PHY1_CR_PARA_RD_EN :
- PCIEX8MGMT_PHY0_CR_PARA_RD_EN));
- while (readl(sv->priv_base +
- (phy ? PCIEX8MGMT_PHY1_CR_PARA_ACK :
- PCIEX8MGMT_PHY0_CR_PARA_ACK))) {
- /* wait for ~wait_idle */
- }
- return 0;
- }
- static void pcie_sifive_init_phy(struct pcie_sifive *sv)
- {
- int lane;
- /* enable phy cr_para_sel interfaces */
- writel(PCIE_PHY_SEL, sv->priv_base + PCIEX8MGMT_PHY0_CR_PARA_SEL);
- writel(PCIE_PHY_SEL, sv->priv_base + PCIEX8MGMT_PHY1_CR_PARA_SEL);
- mdelay(1);
- /* set PHY AC termination mode */
- for (lane = 0; lane < PCIEX8MGMT_LANE_NUM; lane++) {
- pcie_sifive_setphy(0, 1,
- PCIEX8MGMT_LANE +
- (PCIEX8MGMT_LANE_OFF * lane),
- PCIEX8MGMT_TERM_MODE, NULL, sv);
- pcie_sifive_setphy(1, 1,
- PCIEX8MGMT_LANE +
- (PCIEX8MGMT_LANE_OFF * lane),
- PCIEX8MGMT_TERM_MODE, NULL, sv);
- }
- }
- static int pcie_sifive_check_link(struct pcie_sifive *sv)
- {
- u32 val;
- val = readl(sv->dw.dbi_base + PHY_DEBUG_R1);
- return (val & PHY_DEBUG_R1_LINK_UP) &&
- !(val & PHY_DEBUG_R1_LINK_IN_TRAINING);
- }
- static void pcie_sifive_force_gen1(struct pcie_sifive *sv)
- {
- u32 val, linkcap;
- /*
- * Force Gen1 operation when starting the link. In case the link is
- * started in Gen2 mode, there is a possibility the devices on the
- * bus will not be detected at all. This happens with PCIe switches.
- */
- /* ctrl_ro_wr_enable */
- val = readl(sv->dw.dbi_base + PCIE_MISC_CONTROL_1);
- val |= DBI_RO_WR_EN;
- writel(val, sv->dw.dbi_base + PCIE_MISC_CONTROL_1);
- /* configure link cap */
- linkcap = readl(sv->dw.dbi_base + PF0_PCIE_CAP_LINK_CAP);
- linkcap |= PCIE_LINK_CAP_MAX_SPEED_MASK;
- writel(linkcap, sv->dw.dbi_base + PF0_PCIE_CAP_LINK_CAP);
- /* ctrl_ro_wr_disable */
- val &= ~DBI_RO_WR_EN;
- writel(val, sv->dw.dbi_base + PCIE_MISC_CONTROL_1);
- }
- static void pcie_sifive_print_phy_debug(struct pcie_sifive *sv)
- {
- sv_err(sv, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n",
- readl(sv->dw.dbi_base + PHY_DEBUG_R0),
- readl(sv->dw.dbi_base + PHY_DEBUG_R1));
- }
- static int pcie_sifive_wait_for_link(struct pcie_sifive *sv)
- {
- u32 val;
- int timeout;
- /* Wait for the link to train */
- mdelay(20);
- timeout = 20;
- do {
- mdelay(1);
- } while (--timeout && !pcie_sifive_check_link(sv));
- val = readl(sv->dw.dbi_base + PHY_DEBUG_R1);
- if (!(val & PHY_DEBUG_R1_LINK_UP) ||
- (val & PHY_DEBUG_R1_LINK_IN_TRAINING)) {
- sv_info(sv, "Failed to negotiate PCIe link!\n");
- pcie_sifive_print_phy_debug(sv);
- writel(PCIE_PHY_RESET,
- sv->priv_base + PCIEX8MGMT_APP_HOLD_PHY_RST);
- return -ETIMEDOUT;
- }
- return 0;
- }
- static int pcie_sifive_start_link(struct pcie_sifive *sv)
- {
- if (pcie_sifive_check_link(sv))
- return -EALREADY;
- pcie_sifive_force_gen1(sv);
- /* set ltssm */
- pcie_sifive_priv_set_state(sv, PCIEX8MGMT_APP_LTSSM_ENABLE,
- LTSSM_ENABLE_BIT, 1);
- return 0;
- }
- static int pcie_sifive_init_port(struct udevice *dev,
- enum pcie_sifive_devtype mode)
- {
- struct pcie_sifive *sv = dev_get_priv(dev);
- int ret;
- /* Power on reset */
- pcie_sifive_assert_reset(sv);
- pcie_sifive_power_on(sv);
- pcie_sifive_deassert_reset(sv);
- /* Enable pcieauxclk */
- ret = clk_enable(&sv->aux_ck);
- if (ret)
- dev_err(dev, "unable to enable pcie_aux clock\n");
- /*
- * assert hold_phy_rst (hold the controller LTSSM in reset
- * after power_up_rst_n for register programming with cr_para)
- */
- writel(PCIE_PHY_RESET, sv->priv_base + PCIEX8MGMT_APP_HOLD_PHY_RST);
- /* deassert power_up_rst_n */
- ret = reset_deassert(&sv->reset);
- if (ret < 0) {
- dev_err(dev, "failed to deassert reset");
- return -EINVAL;
- }
- pcie_sifive_init_phy(sv);
- /* disable pcieauxclk */
- clk_disable(&sv->aux_ck);
- /* deassert hold_phy_rst */
- writel(PCIE_PHY_RESET_DEASSERT,
- sv->priv_base + PCIEX8MGMT_APP_HOLD_PHY_RST);
- /* enable pcieauxclk */
- clk_enable(&sv->aux_ck);
- /* Set desired mode while core is not operational */
- if (mode == SV_PCIE_HOST_TYPE)
- writel(DEVICE_TYPE_RC,
- sv->priv_base + PCIEX8MGMT_DEVICE_TYPE);
- else
- writel(DEVICE_TYPE_EP,
- sv->priv_base + PCIEX8MGMT_DEVICE_TYPE);
- /* Confirm desired mode from operational core */
- if (pcie_sifive_get_devtype(sv) != mode)
- return -EINVAL;
- pcie_dw_setup_host(&sv->dw);
- if (pcie_sifive_start_link(sv) == -EALREADY)
- sv_info(sv, "PCIe link is already up\n");
- else if (pcie_sifive_wait_for_link(sv) == -ETIMEDOUT)
- return -ETIMEDOUT;
- return 0;
- }
- static int pcie_sifive_probe(struct udevice *dev)
- {
- struct pcie_sifive *sv = dev_get_priv(dev);
- struct udevice *parent = pci_get_controller(dev);
- struct pci_controller *hose = dev_get_uclass_priv(parent);
- int err;
- sv->dw.first_busno = dev_seq(dev);
- sv->dw.dev = dev;
- err = pcie_sifive_init_port(dev, SV_PCIE_HOST_TYPE);
- if (err) {
- sv_info(sv, "Failed to init port.\n");
- return err;
- }
- printf("PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n",
- dev_seq(dev), pcie_dw_get_link_speed(&sv->dw),
- pcie_dw_get_link_width(&sv->dw),
- hose->first_busno);
- return pcie_dw_prog_outbound_atu_unroll(&sv->dw,
- PCIE_ATU_REGION_INDEX0,
- PCIE_ATU_TYPE_MEM,
- sv->dw.mem.phys_start,
- sv->dw.mem.bus_start,
- sv->dw.mem.size);
- }
- static void __iomem *get_fdt_addr(struct udevice *dev, const char *name)
- {
- fdt_addr_t addr;
- addr = dev_read_addr_name(dev, name);
- return (addr == FDT_ADDR_T_NONE) ? NULL : (void __iomem *)addr;
- }
- static int pcie_sifive_of_to_plat(struct udevice *dev)
- {
- struct pcie_sifive *sv = dev_get_priv(dev);
- int err;
- /* get designware DBI base addr */
- sv->dw.dbi_base = get_fdt_addr(dev, "dbi");
- if (!sv->dw.dbi_base)
- return -EINVAL;
- /* get private control base addr */
- sv->priv_base = get_fdt_addr(dev, "mgmt");
- if (!sv->priv_base)
- return -EINVAL;
- gpio_request_by_name(dev, "pwren-gpios", 0, &sv->pwren_gpio,
- GPIOD_IS_OUT);
- if (!dm_gpio_is_valid(&sv->pwren_gpio)) {
- sv_info(sv, "pwren_gpio is invalid\n");
- return -EINVAL;
- }
- gpio_request_by_name(dev, "reset-gpios", 0, &sv->reset_gpio,
- GPIOD_IS_OUT);
- if (!dm_gpio_is_valid(&sv->reset_gpio)) {
- sv_info(sv, "reset_gpio is invalid\n");
- return -EINVAL;
- }
- err = clk_get_by_index(dev, 0, &sv->aux_ck);
- if (err) {
- sv_info(sv, "clk_get_by_index(aux_ck) failed: %d\n", err);
- return err;
- }
- err = reset_get_by_index(dev, 0, &sv->reset);
- if (err) {
- sv_info(sv, "reset_get_by_index(reset) failed: %d\n", err);
- return err;
- }
- return 0;
- }
- static const struct dm_pci_ops pcie_sifive_ops = {
- .read_config = pcie_dw_read_config,
- .write_config = pcie_dw_write_config,
- };
- static const struct udevice_id pcie_sifive_ids[] = {
- { .compatible = "sifive,fu740-pcie" },
- {}
- };
- U_BOOT_DRIVER(pcie_sifive) = {
- .name = "pcie_sifive",
- .id = UCLASS_PCI,
- .of_match = pcie_sifive_ids,
- .ops = &pcie_sifive_ops,
- .of_to_plat = pcie_sifive_of_to_plat,
- .probe = pcie_sifive_probe,
- .priv_auto = sizeof(struct pcie_sifive),
- };
|