350 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * PCIe host controller driver for Marvell Armada-8K SoCs4 *5 * Armada-8K PCIe Glue Layer Source Code6 *7 * Copyright (C) 2016 Marvell Technology Group Ltd.8 *9 * Author: Yehuda Yitshak <yehuday@marvell.com>10 * Author: Shadi Ammouri <shadi@marvell.com>11 */12 13#include <linux/clk.h>14#include <linux/delay.h>15#include <linux/interrupt.h>16#include <linux/kernel.h>17#include <linux/init.h>18#include <linux/of.h>19#include <linux/pci.h>20#include <linux/phy/phy.h>21#include <linux/platform_device.h>22#include <linux/resource.h>23#include <linux/of_pci.h>24 25#include "pcie-designware.h"26 27#define ARMADA8K_PCIE_MAX_LANES PCIE_LNK_X428 29struct armada8k_pcie {30 struct dw_pcie *pci;31 struct clk *clk;32 struct clk *clk_reg;33 struct phy *phy[ARMADA8K_PCIE_MAX_LANES];34 unsigned int phy_count;35};36 37#define PCIE_VENDOR_REGS_OFFSET 0x800038 39#define PCIE_GLOBAL_CONTROL_REG (PCIE_VENDOR_REGS_OFFSET + 0x0)40#define PCIE_APP_LTSSM_EN BIT(2)41#define PCIE_DEVICE_TYPE_SHIFT 442#define PCIE_DEVICE_TYPE_MASK 0xF43#define PCIE_DEVICE_TYPE_RC 0x4 /* Root complex */44 45#define PCIE_GLOBAL_STATUS_REG (PCIE_VENDOR_REGS_OFFSET + 0x8)46#define PCIE_GLB_STS_RDLH_LINK_UP BIT(1)47#define PCIE_GLB_STS_PHY_LINK_UP BIT(9)48 49#define PCIE_GLOBAL_INT_CAUSE1_REG (PCIE_VENDOR_REGS_OFFSET + 0x1C)50#define PCIE_GLOBAL_INT_MASK1_REG (PCIE_VENDOR_REGS_OFFSET + 0x20)51#define PCIE_INT_A_ASSERT_MASK BIT(9)52#define PCIE_INT_B_ASSERT_MASK BIT(10)53#define PCIE_INT_C_ASSERT_MASK BIT(11)54#define PCIE_INT_D_ASSERT_MASK BIT(12)55 56#define PCIE_ARCACHE_TRC_REG (PCIE_VENDOR_REGS_OFFSET + 0x50)57#define PCIE_AWCACHE_TRC_REG (PCIE_VENDOR_REGS_OFFSET + 0x54)58#define PCIE_ARUSER_REG (PCIE_VENDOR_REGS_OFFSET + 0x5C)59#define PCIE_AWUSER_REG (PCIE_VENDOR_REGS_OFFSET + 0x60)60/*61 * AR/AW Cache defaults: Normal memory, Write-Back, Read / Write62 * allocate63 */64#define ARCACHE_DEFAULT_VALUE 0x351165#define AWCACHE_DEFAULT_VALUE 0x531166 67#define DOMAIN_OUTER_SHAREABLE 0x268#define AX_USER_DOMAIN_MASK 0x369#define AX_USER_DOMAIN_SHIFT 470 71#define to_armada8k_pcie(x) dev_get_drvdata((x)->dev)72 73static void armada8k_pcie_disable_phys(struct armada8k_pcie *pcie)74{75 int i;76 77 for (i = 0; i < ARMADA8K_PCIE_MAX_LANES; i++) {78 phy_power_off(pcie->phy[i]);79 phy_exit(pcie->phy[i]);80 }81}82 83static int armada8k_pcie_enable_phys(struct armada8k_pcie *pcie)84{85 int ret;86 int i;87 88 for (i = 0; i < ARMADA8K_PCIE_MAX_LANES; i++) {89 ret = phy_init(pcie->phy[i]);90 if (ret)91 return ret;92 93 ret = phy_set_mode_ext(pcie->phy[i], PHY_MODE_PCIE,94 pcie->phy_count);95 if (ret) {96 phy_exit(pcie->phy[i]);97 return ret;98 }99 100 ret = phy_power_on(pcie->phy[i]);101 if (ret) {102 phy_exit(pcie->phy[i]);103 return ret;104 }105 }106 107 return 0;108}109 110static int armada8k_pcie_setup_phys(struct armada8k_pcie *pcie)111{112 struct dw_pcie *pci = pcie->pci;113 struct device *dev = pci->dev;114 struct device_node *node = dev->of_node;115 int ret = 0;116 int i;117 118 for (i = 0; i < ARMADA8K_PCIE_MAX_LANES; i++) {119 pcie->phy[i] = devm_of_phy_get_by_index(dev, node, i);120 if (IS_ERR(pcie->phy[i])) {121 if (PTR_ERR(pcie->phy[i]) != -ENODEV)122 return PTR_ERR(pcie->phy[i]);123 124 pcie->phy[i] = NULL;125 continue;126 }127 128 pcie->phy_count++;129 }130 131 /* Old bindings miss the PHY handle, so just warn if there is no PHY */132 if (!pcie->phy_count)133 dev_warn(dev, "No available PHY\n");134 135 ret = armada8k_pcie_enable_phys(pcie);136 if (ret)137 dev_err(dev, "Failed to initialize PHY(s) (%d)\n", ret);138 139 return ret;140}141 142static int armada8k_pcie_link_up(struct dw_pcie *pci)143{144 u32 reg;145 u32 mask = PCIE_GLB_STS_RDLH_LINK_UP | PCIE_GLB_STS_PHY_LINK_UP;146 147 reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_STATUS_REG);148 149 if ((reg & mask) == mask)150 return 1;151 152 dev_dbg(pci->dev, "No link detected (Global-Status: 0x%08x).\n", reg);153 return 0;154}155 156static int armada8k_pcie_start_link(struct dw_pcie *pci)157{158 u32 reg;159 160 /* Start LTSSM */161 reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG);162 reg |= PCIE_APP_LTSSM_EN;163 dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg);164 165 return 0;166}167 168static int armada8k_pcie_host_init(struct dw_pcie_rp *pp)169{170 u32 reg;171 struct dw_pcie *pci = to_dw_pcie_from_pp(pp);172 173 if (!dw_pcie_link_up(pci)) {174 /* Disable LTSSM state machine to enable configuration */175 reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG);176 reg &= ~(PCIE_APP_LTSSM_EN);177 dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg);178 }179 180 /* Set the device to root complex mode */181 reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG);182 reg &= ~(PCIE_DEVICE_TYPE_MASK << PCIE_DEVICE_TYPE_SHIFT);183 reg |= PCIE_DEVICE_TYPE_RC << PCIE_DEVICE_TYPE_SHIFT;184 dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg);185 186 /* Set the PCIe master AxCache attributes */187 dw_pcie_writel_dbi(pci, PCIE_ARCACHE_TRC_REG, ARCACHE_DEFAULT_VALUE);188 dw_pcie_writel_dbi(pci, PCIE_AWCACHE_TRC_REG, AWCACHE_DEFAULT_VALUE);189 190 /* Set the PCIe master AxDomain attributes */191 reg = dw_pcie_readl_dbi(pci, PCIE_ARUSER_REG);192 reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT);193 reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT;194 dw_pcie_writel_dbi(pci, PCIE_ARUSER_REG, reg);195 196 reg = dw_pcie_readl_dbi(pci, PCIE_AWUSER_REG);197 reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT);198 reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT;199 dw_pcie_writel_dbi(pci, PCIE_AWUSER_REG, reg);200 201 /* Enable INT A-D interrupts */202 reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG);203 reg |= PCIE_INT_A_ASSERT_MASK | PCIE_INT_B_ASSERT_MASK |204 PCIE_INT_C_ASSERT_MASK | PCIE_INT_D_ASSERT_MASK;205 dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG, reg);206 207 return 0;208}209 210static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg)211{212 struct armada8k_pcie *pcie = arg;213 struct dw_pcie *pci = pcie->pci;214 u32 val;215 216 /*217 * Interrupts are directly handled by the device driver of the218 * PCI device. However, they are also latched into the PCIe219 * controller, so we simply discard them.220 */221 val = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_CAUSE1_REG);222 dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_CAUSE1_REG, val);223 224 return IRQ_HANDLED;225}226 227static const struct dw_pcie_host_ops armada8k_pcie_host_ops = {228 .init = armada8k_pcie_host_init,229};230 231static int armada8k_add_pcie_port(struct armada8k_pcie *pcie,232 struct platform_device *pdev)233{234 struct dw_pcie *pci = pcie->pci;235 struct dw_pcie_rp *pp = &pci->pp;236 struct device *dev = &pdev->dev;237 int ret;238 239 pp->ops = &armada8k_pcie_host_ops;240 241 pp->irq = platform_get_irq(pdev, 0);242 if (pp->irq < 0)243 return pp->irq;244 245 ret = devm_request_irq(dev, pp->irq, armada8k_pcie_irq_handler,246 IRQF_SHARED, "armada8k-pcie", pcie);247 if (ret) {248 dev_err(dev, "failed to request irq %d\n", pp->irq);249 return ret;250 }251 252 ret = dw_pcie_host_init(pp);253 if (ret) {254 dev_err(dev, "failed to initialize host: %d\n", ret);255 return ret;256 }257 258 return 0;259}260 261static const struct dw_pcie_ops dw_pcie_ops = {262 .link_up = armada8k_pcie_link_up,263 .start_link = armada8k_pcie_start_link,264};265 266static int armada8k_pcie_probe(struct platform_device *pdev)267{268 struct dw_pcie *pci;269 struct armada8k_pcie *pcie;270 struct device *dev = &pdev->dev;271 struct resource *base;272 int ret;273 274 pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);275 if (!pcie)276 return -ENOMEM;277 278 pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);279 if (!pci)280 return -ENOMEM;281 282 pci->dev = dev;283 pci->ops = &dw_pcie_ops;284 285 pcie->pci = pci;286 287 pcie->clk = devm_clk_get(dev, NULL);288 if (IS_ERR(pcie->clk))289 return PTR_ERR(pcie->clk);290 291 ret = clk_prepare_enable(pcie->clk);292 if (ret)293 return ret;294 295 pcie->clk_reg = devm_clk_get(dev, "reg");296 if (pcie->clk_reg == ERR_PTR(-EPROBE_DEFER)) {297 ret = -EPROBE_DEFER;298 goto fail;299 }300 if (!IS_ERR(pcie->clk_reg)) {301 ret = clk_prepare_enable(pcie->clk_reg);302 if (ret)303 goto fail_clkreg;304 }305 306 /* Get the dw-pcie unit configuration/control registers base. */307 base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl");308 pci->dbi_base = devm_pci_remap_cfg_resource(dev, base);309 if (IS_ERR(pci->dbi_base)) {310 ret = PTR_ERR(pci->dbi_base);311 goto fail_clkreg;312 }313 314 ret = armada8k_pcie_setup_phys(pcie);315 if (ret)316 goto fail_clkreg;317 318 platform_set_drvdata(pdev, pcie);319 320 ret = armada8k_add_pcie_port(pcie, pdev);321 if (ret)322 goto disable_phy;323 324 return 0;325 326disable_phy:327 armada8k_pcie_disable_phys(pcie);328fail_clkreg:329 clk_disable_unprepare(pcie->clk_reg);330fail:331 clk_disable_unprepare(pcie->clk);332 333 return ret;334}335 336static const struct of_device_id armada8k_pcie_of_match[] = {337 { .compatible = "marvell,armada8k-pcie", },338 {},339};340 341static struct platform_driver armada8k_pcie_driver = {342 .probe = armada8k_pcie_probe,343 .driver = {344 .name = "armada8k-pcie",345 .of_match_table = armada8k_pcie_of_match,346 .suppress_bind_attrs = true,347 },348};349builtin_platform_driver(armada8k_pcie_driver);350