brintos

brintos / linux-shallow public Read only

0
0
Text · 8.5 KiB · b5c599c Raw
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