linux/drivers/pci/controller/dwc/pcie-fu740.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * FU740 DesignWare PCIe Controller integration
   4 * Copyright (C) 2019-2021 SiFive, Inc.
   5 * Paul Walmsley
   6 * Greentime Hu
   7 *
   8 * Based in part on the i.MX6 PCIe host controller shim which is:
   9 *
  10 * Copyright (C) 2013 Kosagi
  11 *              https://www.kosagi.com
  12 */
  13
  14#include <linux/clk.h>
  15#include <linux/delay.h>
  16#include <linux/gpio.h>
  17#include <linux/gpio/consumer.h>
  18#include <linux/kernel.h>
  19#include <linux/mfd/syscon.h>
  20#include <linux/module.h>
  21#include <linux/pci.h>
  22#include <linux/platform_device.h>
  23#include <linux/regulator/consumer.h>
  24#include <linux/resource.h>
  25#include <linux/types.h>
  26#include <linux/interrupt.h>
  27#include <linux/iopoll.h>
  28#include <linux/reset.h>
  29
  30#include "pcie-designware.h"
  31
  32#define to_fu740_pcie(x)        dev_get_drvdata((x)->dev)
  33
  34struct fu740_pcie {
  35        struct dw_pcie pci;
  36        void __iomem *mgmt_base;
  37        struct gpio_desc *reset;
  38        struct gpio_desc *pwren;
  39        struct clk *pcie_aux;
  40        struct reset_control *rst;
  41};
  42
  43#define SIFIVE_DEVICESRESETREG          0x28
  44
  45#define PCIEX8MGMT_PERST_N              0x0
  46#define PCIEX8MGMT_APP_LTSSM_ENABLE     0x10
  47#define PCIEX8MGMT_APP_HOLD_PHY_RST     0x18
  48#define PCIEX8MGMT_DEVICE_TYPE          0x708
  49#define PCIEX8MGMT_PHY0_CR_PARA_ADDR    0x860
  50#define PCIEX8MGMT_PHY0_CR_PARA_RD_EN   0x870
  51#define PCIEX8MGMT_PHY0_CR_PARA_RD_DATA 0x878
  52#define PCIEX8MGMT_PHY0_CR_PARA_SEL     0x880
  53#define PCIEX8MGMT_PHY0_CR_PARA_WR_DATA 0x888
  54#define PCIEX8MGMT_PHY0_CR_PARA_WR_EN   0x890
  55#define PCIEX8MGMT_PHY0_CR_PARA_ACK     0x898
  56#define PCIEX8MGMT_PHY1_CR_PARA_ADDR    0x8a0
  57#define PCIEX8MGMT_PHY1_CR_PARA_RD_EN   0x8b0
  58#define PCIEX8MGMT_PHY1_CR_PARA_RD_DATA 0x8b8
  59#define PCIEX8MGMT_PHY1_CR_PARA_SEL     0x8c0
  60#define PCIEX8MGMT_PHY1_CR_PARA_WR_DATA 0x8c8
  61#define PCIEX8MGMT_PHY1_CR_PARA_WR_EN   0x8d0
  62#define PCIEX8MGMT_PHY1_CR_PARA_ACK     0x8d8
  63
  64#define PCIEX8MGMT_PHY_CDR_TRACK_EN     BIT(0)
  65#define PCIEX8MGMT_PHY_LOS_THRSHLD      BIT(5)
  66#define PCIEX8MGMT_PHY_TERM_EN          BIT(9)
  67#define PCIEX8MGMT_PHY_TERM_ACDC        BIT(10)
  68#define PCIEX8MGMT_PHY_EN               BIT(11)
  69#define PCIEX8MGMT_PHY_INIT_VAL         (PCIEX8MGMT_PHY_CDR_TRACK_EN|\
  70                                         PCIEX8MGMT_PHY_LOS_THRSHLD|\
  71                                         PCIEX8MGMT_PHY_TERM_EN|\
  72                                         PCIEX8MGMT_PHY_TERM_ACDC|\
  73                                         PCIEX8MGMT_PHY_EN)
  74
  75#define PCIEX8MGMT_PHY_LANEN_DIG_ASIC_RX_OVRD_IN_3      0x1008
  76#define PCIEX8MGMT_PHY_LANE_OFF         0x100
  77#define PCIEX8MGMT_PHY_LANE0_BASE       (PCIEX8MGMT_PHY_LANEN_DIG_ASIC_RX_OVRD_IN_3 + 0x100 * 0)
  78#define PCIEX8MGMT_PHY_LANE1_BASE       (PCIEX8MGMT_PHY_LANEN_DIG_ASIC_RX_OVRD_IN_3 + 0x100 * 1)
  79#define PCIEX8MGMT_PHY_LANE2_BASE       (PCIEX8MGMT_PHY_LANEN_DIG_ASIC_RX_OVRD_IN_3 + 0x100 * 2)
  80#define PCIEX8MGMT_PHY_LANE3_BASE       (PCIEX8MGMT_PHY_LANEN_DIG_ASIC_RX_OVRD_IN_3 + 0x100 * 3)
  81
  82static void fu740_pcie_assert_reset(struct fu740_pcie *afp)
  83{
  84        /* Assert PERST_N GPIO */
  85        gpiod_set_value_cansleep(afp->reset, 0);
  86        /* Assert controller PERST_N */
  87        writel_relaxed(0x0, afp->mgmt_base + PCIEX8MGMT_PERST_N);
  88}
  89
  90static void fu740_pcie_deassert_reset(struct fu740_pcie *afp)
  91{
  92        /* Deassert controller PERST_N */
  93        writel_relaxed(0x1, afp->mgmt_base + PCIEX8MGMT_PERST_N);
  94        /* Deassert PERST_N GPIO */
  95        gpiod_set_value_cansleep(afp->reset, 1);
  96}
  97
  98static void fu740_pcie_power_on(struct fu740_pcie *afp)
  99{
 100        gpiod_set_value_cansleep(afp->pwren, 1);
 101        /*
 102         * Ensure that PERST has been asserted for at least 100 ms.
 103         * Section 2.2 of PCI Express Card Electromechanical Specification
 104         * Revision 3.0
 105         */
 106        msleep(100);
 107}
 108
 109static void fu740_pcie_drive_reset(struct fu740_pcie *afp)
 110{
 111        fu740_pcie_assert_reset(afp);
 112        fu740_pcie_power_on(afp);
 113        fu740_pcie_deassert_reset(afp);
 114}
 115
 116static void fu740_phyregwrite(const uint8_t phy, const uint16_t addr,
 117                              const uint16_t wrdata, struct fu740_pcie *afp)
 118{
 119        struct device *dev = afp->pci.dev;
 120        void __iomem *phy_cr_para_addr;
 121        void __iomem *phy_cr_para_wr_data;
 122        void __iomem *phy_cr_para_wr_en;
 123        void __iomem *phy_cr_para_ack;
 124        int ret, val;
 125
 126        /* Setup */
 127        if (phy) {
 128                phy_cr_para_addr = afp->mgmt_base + PCIEX8MGMT_PHY1_CR_PARA_ADDR;
 129                phy_cr_para_wr_data = afp->mgmt_base + PCIEX8MGMT_PHY1_CR_PARA_WR_DATA;
 130                phy_cr_para_wr_en = afp->mgmt_base + PCIEX8MGMT_PHY1_CR_PARA_WR_EN;
 131                phy_cr_para_ack = afp->mgmt_base + PCIEX8MGMT_PHY1_CR_PARA_ACK;
 132        } else {
 133                phy_cr_para_addr = afp->mgmt_base + PCIEX8MGMT_PHY0_CR_PARA_ADDR;
 134                phy_cr_para_wr_data = afp->mgmt_base + PCIEX8MGMT_PHY0_CR_PARA_WR_DATA;
 135                phy_cr_para_wr_en = afp->mgmt_base + PCIEX8MGMT_PHY0_CR_PARA_WR_EN;
 136                phy_cr_para_ack = afp->mgmt_base + PCIEX8MGMT_PHY0_CR_PARA_ACK;
 137        }
 138
 139        writel_relaxed(addr, phy_cr_para_addr);
 140        writel_relaxed(wrdata, phy_cr_para_wr_data);
 141        writel_relaxed(1, phy_cr_para_wr_en);
 142
 143        /* Wait for wait_idle */
 144        ret = readl_poll_timeout(phy_cr_para_ack, val, val, 10, 5000);
 145        if (ret)
 146                dev_warn(dev, "Wait for wait_idle state failed!\n");
 147
 148        /* Clear */
 149        writel_relaxed(0, phy_cr_para_wr_en);
 150
 151        /* Wait for ~wait_idle */
 152        ret = readl_poll_timeout(phy_cr_para_ack, val, !val, 10, 5000);
 153        if (ret)
 154                dev_warn(dev, "Wait for !wait_idle state failed!\n");
 155}
 156
 157static void fu740_pcie_init_phy(struct fu740_pcie *afp)
 158{
 159        /* Enable phy cr_para_sel interfaces */
 160        writel_relaxed(0x1, afp->mgmt_base + PCIEX8MGMT_PHY0_CR_PARA_SEL);
 161        writel_relaxed(0x1, afp->mgmt_base + PCIEX8MGMT_PHY1_CR_PARA_SEL);
 162
 163        /*
 164         * Wait 10 cr_para cycles to guarantee that the registers are ready
 165         * to be edited.
 166         */
 167        ndelay(10);
 168
 169        /* Set PHY AC termination mode */
 170        fu740_phyregwrite(0, PCIEX8MGMT_PHY_LANE0_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp);
 171        fu740_phyregwrite(0, PCIEX8MGMT_PHY_LANE1_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp);
 172        fu740_phyregwrite(0, PCIEX8MGMT_PHY_LANE2_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp);
 173        fu740_phyregwrite(0, PCIEX8MGMT_PHY_LANE3_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp);
 174        fu740_phyregwrite(1, PCIEX8MGMT_PHY_LANE0_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp);
 175        fu740_phyregwrite(1, PCIEX8MGMT_PHY_LANE1_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp);
 176        fu740_phyregwrite(1, PCIEX8MGMT_PHY_LANE2_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp);
 177        fu740_phyregwrite(1, PCIEX8MGMT_PHY_LANE3_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp);
 178}
 179
 180static int fu740_pcie_start_link(struct dw_pcie *pci)
 181{
 182        struct device *dev = pci->dev;
 183        struct fu740_pcie *afp = dev_get_drvdata(dev);
 184
 185        /* Enable LTSSM */
 186        writel_relaxed(0x1, afp->mgmt_base + PCIEX8MGMT_APP_LTSSM_ENABLE);
 187        return 0;
 188}
 189
 190static int fu740_pcie_host_init(struct pcie_port *pp)
 191{
 192        struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
 193        struct fu740_pcie *afp = to_fu740_pcie(pci);
 194        struct device *dev = pci->dev;
 195        int ret;
 196
 197        /* Power on reset */
 198        fu740_pcie_drive_reset(afp);
 199
 200        /* Enable pcieauxclk */
 201        ret = clk_prepare_enable(afp->pcie_aux);
 202        if (ret) {
 203                dev_err(dev, "unable to enable pcie_aux clock\n");
 204                return ret;
 205        }
 206
 207        /*
 208         * Assert hold_phy_rst (hold the controller LTSSM in reset after
 209         * power_up_rst_n for register programming with cr_para)
 210         */
 211        writel_relaxed(0x1, afp->mgmt_base + PCIEX8MGMT_APP_HOLD_PHY_RST);
 212
 213        /* Deassert power_up_rst_n */
 214        ret = reset_control_deassert(afp->rst);
 215        if (ret) {
 216                dev_err(dev, "unable to deassert pcie_power_up_rst_n\n");
 217                return ret;
 218        }
 219
 220        fu740_pcie_init_phy(afp);
 221
 222        /* Disable pcieauxclk */
 223        clk_disable_unprepare(afp->pcie_aux);
 224        /* Clear hold_phy_rst */
 225        writel_relaxed(0x0, afp->mgmt_base + PCIEX8MGMT_APP_HOLD_PHY_RST);
 226        /* Enable pcieauxclk */
 227        ret = clk_prepare_enable(afp->pcie_aux);
 228        /* Set RC mode */
 229        writel_relaxed(0x4, afp->mgmt_base + PCIEX8MGMT_DEVICE_TYPE);
 230
 231        return 0;
 232}
 233
 234static const struct dw_pcie_host_ops fu740_pcie_host_ops = {
 235        .host_init = fu740_pcie_host_init,
 236};
 237
 238static const struct dw_pcie_ops dw_pcie_ops = {
 239        .start_link = fu740_pcie_start_link,
 240};
 241
 242static int fu740_pcie_probe(struct platform_device *pdev)
 243{
 244        struct device *dev = &pdev->dev;
 245        struct dw_pcie *pci;
 246        struct fu740_pcie *afp;
 247
 248        afp = devm_kzalloc(dev, sizeof(*afp), GFP_KERNEL);
 249        if (!afp)
 250                return -ENOMEM;
 251        pci = &afp->pci;
 252        pci->dev = dev;
 253        pci->ops = &dw_pcie_ops;
 254        pci->pp.ops = &fu740_pcie_host_ops;
 255
 256        /* SiFive specific region: mgmt */
 257        afp->mgmt_base = devm_platform_ioremap_resource_byname(pdev, "mgmt");
 258        if (IS_ERR(afp->mgmt_base))
 259                return PTR_ERR(afp->mgmt_base);
 260
 261        /* Fetch GPIOs */
 262        afp->reset = devm_gpiod_get_optional(dev, "reset-gpios", GPIOD_OUT_LOW);
 263        if (IS_ERR(afp->reset))
 264                return dev_err_probe(dev, PTR_ERR(afp->reset), "unable to get reset-gpios\n");
 265
 266        afp->pwren = devm_gpiod_get_optional(dev, "pwren-gpios", GPIOD_OUT_LOW);
 267        if (IS_ERR(afp->pwren))
 268                return dev_err_probe(dev, PTR_ERR(afp->pwren), "unable to get pwren-gpios\n");
 269
 270        /* Fetch clocks */
 271        afp->pcie_aux = devm_clk_get(dev, "pcie_aux");
 272        if (IS_ERR(afp->pcie_aux))
 273                return dev_err_probe(dev, PTR_ERR(afp->pcie_aux),
 274                                             "pcie_aux clock source missing or invalid\n");
 275
 276        /* Fetch reset */
 277        afp->rst = devm_reset_control_get_exclusive(dev, NULL);
 278        if (IS_ERR(afp->rst))
 279                return dev_err_probe(dev, PTR_ERR(afp->rst), "unable to get reset\n");
 280
 281        platform_set_drvdata(pdev, afp);
 282
 283        return dw_pcie_host_init(&pci->pp);
 284}
 285
 286static void fu740_pcie_shutdown(struct platform_device *pdev)
 287{
 288        struct fu740_pcie *afp = platform_get_drvdata(pdev);
 289
 290        /* Bring down link, so bootloader gets clean state in case of reboot */
 291        fu740_pcie_assert_reset(afp);
 292}
 293
 294static const struct of_device_id fu740_pcie_of_match[] = {
 295        { .compatible = "sifive,fu740-pcie", },
 296        {},
 297};
 298
 299static struct platform_driver fu740_pcie_driver = {
 300        .driver = {
 301                   .name = "fu740-pcie",
 302                   .of_match_table = fu740_pcie_of_match,
 303                   .suppress_bind_attrs = true,
 304        },
 305        .probe = fu740_pcie_probe,
 306        .shutdown = fu740_pcie_shutdown,
 307};
 308
 309builtin_platform_driver(fu740_pcie_driver);
 310