blob: 072fd7ecc29f2758ccb082ac5443335f8407be58 [file] [log] [blame]
Bjorn Helgaas8cfab3c2018-01-26 12:50:27 -06001// SPDX-License-Identifier: GPL-2.0
Thomas Petazzoni1c52a512016-04-26 10:31:46 +02002/*
3 * PCIe host controller driver for Marvell Armada-8K SoCs
4 *
5 * Armada-8K PCIe Glue Layer Source Code
6 *
7 * Copyright (C) 2016 Marvell Technology Group Ltd.
8 *
Paul Gortmaker0e6f98c2016-07-02 19:13:21 -04009 * Author: Yehuda Yitshak <yehuday@marvell.com>
10 * Author: Shadi Ammouri <shadi@marvell.com>
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020011 */
12
13#include <linux/clk.h>
14#include <linux/delay.h>
15#include <linux/interrupt.h>
16#include <linux/kernel.h>
Paul Gortmaker0e6f98c2016-07-02 19:13:21 -040017#include <linux/init.h>
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020018#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#include <linux/of_irq.h>
25
26#include "pcie-designware.h"
27
28struct armada8k_pcie {
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +053029 struct dw_pcie *pci;
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020030 struct clk *clk;
Gregory CLEMENT2435cdd2018-02-28 17:35:30 +010031 struct clk *clk_reg;
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020032};
33
34#define PCIE_VENDOR_REGS_OFFSET 0x8000
35
Bjorn Helgaas74e69072016-10-06 13:29:59 -050036#define PCIE_GLOBAL_CONTROL_REG (PCIE_VENDOR_REGS_OFFSET + 0x0)
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020037#define PCIE_APP_LTSSM_EN BIT(2)
38#define PCIE_DEVICE_TYPE_SHIFT 4
39#define PCIE_DEVICE_TYPE_MASK 0xF
40#define PCIE_DEVICE_TYPE_RC 0x4 /* Root complex */
41
Bjorn Helgaas74e69072016-10-06 13:29:59 -050042#define PCIE_GLOBAL_STATUS_REG (PCIE_VENDOR_REGS_OFFSET + 0x8)
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020043#define PCIE_GLB_STS_RDLH_LINK_UP BIT(1)
44#define PCIE_GLB_STS_PHY_LINK_UP BIT(9)
45
Bjorn Helgaas74e69072016-10-06 13:29:59 -050046#define PCIE_GLOBAL_INT_CAUSE1_REG (PCIE_VENDOR_REGS_OFFSET + 0x1C)
47#define PCIE_GLOBAL_INT_MASK1_REG (PCIE_VENDOR_REGS_OFFSET + 0x20)
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020048#define PCIE_INT_A_ASSERT_MASK BIT(9)
49#define PCIE_INT_B_ASSERT_MASK BIT(10)
50#define PCIE_INT_C_ASSERT_MASK BIT(11)
51#define PCIE_INT_D_ASSERT_MASK BIT(12)
52
Bjorn Helgaas74e69072016-10-06 13:29:59 -050053#define PCIE_ARCACHE_TRC_REG (PCIE_VENDOR_REGS_OFFSET + 0x50)
54#define PCIE_AWCACHE_TRC_REG (PCIE_VENDOR_REGS_OFFSET + 0x54)
55#define PCIE_ARUSER_REG (PCIE_VENDOR_REGS_OFFSET + 0x5C)
56#define PCIE_AWUSER_REG (PCIE_VENDOR_REGS_OFFSET + 0x60)
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020057/*
58 * AR/AW Cache defauls: Normal memory, Write-Back, Read / Write
59 * allocate
60 */
61#define ARCACHE_DEFAULT_VALUE 0x3511
62#define AWCACHE_DEFAULT_VALUE 0x5311
63
64#define DOMAIN_OUTER_SHAREABLE 0x2
65#define AX_USER_DOMAIN_MASK 0x3
66#define AX_USER_DOMAIN_SHIFT 4
67
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +053068#define to_armada8k_pcie(x) dev_get_drvdata((x)->dev)
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020069
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +053070static int armada8k_pcie_link_up(struct dw_pcie *pci)
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020071{
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020072 u32 reg;
73 u32 mask = PCIE_GLB_STS_RDLH_LINK_UP | PCIE_GLB_STS_PHY_LINK_UP;
74
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +053075 reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_STATUS_REG);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020076
77 if ((reg & mask) == mask)
78 return 1;
79
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +053080 dev_dbg(pci->dev, "No link detected (Global-Status: 0x%08x).\n", reg);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020081 return 0;
82}
83
Bjorn Helgaasb2d6fd72016-10-06 13:30:00 -050084static void armada8k_pcie_establish_link(struct armada8k_pcie *pcie)
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020085{
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +053086 struct dw_pcie *pci = pcie->pci;
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020087 u32 reg;
88
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +053089 if (!dw_pcie_link_up(pci)) {
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020090 /* Disable LTSSM state machine to enable configuration */
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +053091 reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020092 reg &= ~(PCIE_APP_LTSSM_EN);
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +053093 dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020094 }
95
96 /* Set the device to root complex mode */
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +053097 reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +020098 reg &= ~(PCIE_DEVICE_TYPE_MASK << PCIE_DEVICE_TYPE_SHIFT);
99 reg |= PCIE_DEVICE_TYPE_RC << PCIE_DEVICE_TYPE_SHIFT;
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530100 dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200101
102 /* Set the PCIe master AxCache attributes */
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530103 dw_pcie_writel_dbi(pci, PCIE_ARCACHE_TRC_REG, ARCACHE_DEFAULT_VALUE);
104 dw_pcie_writel_dbi(pci, PCIE_AWCACHE_TRC_REG, AWCACHE_DEFAULT_VALUE);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200105
106 /* Set the PCIe master AxDomain attributes */
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530107 reg = dw_pcie_readl_dbi(pci, PCIE_ARUSER_REG);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200108 reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT);
109 reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT;
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530110 dw_pcie_writel_dbi(pci, PCIE_ARUSER_REG, reg);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200111
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530112 reg = dw_pcie_readl_dbi(pci, PCIE_AWUSER_REG);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200113 reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT);
114 reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT;
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530115 dw_pcie_writel_dbi(pci, PCIE_AWUSER_REG, reg);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200116
117 /* Enable INT A-D interrupts */
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530118 reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200119 reg |= PCIE_INT_A_ASSERT_MASK | PCIE_INT_B_ASSERT_MASK |
120 PCIE_INT_C_ASSERT_MASK | PCIE_INT_D_ASSERT_MASK;
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530121 dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG, reg);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200122
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530123 if (!dw_pcie_link_up(pci)) {
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200124 /* Configuration done. Start LTSSM */
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530125 reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200126 reg |= PCIE_APP_LTSSM_EN;
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530127 dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200128 }
129
130 /* Wait until the link becomes active again */
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530131 if (dw_pcie_wait_for_link(pci))
132 dev_err(pci->dev, "Link not up after reconfiguration\n");
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200133}
134
Bjorn Andersson4a301762017-07-15 23:39:45 -0700135static int armada8k_pcie_host_init(struct pcie_port *pp)
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200136{
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530137 struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
138 struct armada8k_pcie *pcie = to_armada8k_pcie(pci);
Bjorn Helgaasb2d6fd72016-10-06 13:30:00 -0500139
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200140 dw_pcie_setup_rc(pp);
Bjorn Helgaasb2d6fd72016-10-06 13:30:00 -0500141 armada8k_pcie_establish_link(pcie);
Bjorn Andersson4a301762017-07-15 23:39:45 -0700142
143 return 0;
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200144}
145
146static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg)
147{
Bjorn Helgaasb2d6fd72016-10-06 13:30:00 -0500148 struct armada8k_pcie *pcie = arg;
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530149 struct dw_pcie *pci = pcie->pci;
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200150 u32 val;
151
152 /*
153 * Interrupts are directly handled by the device driver of the
154 * PCI device. However, they are also latched into the PCIe
155 * controller, so we simply discard them.
156 */
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530157 val = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_CAUSE1_REG);
158 dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_CAUSE1_REG, val);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200159
160 return IRQ_HANDLED;
161}
162
Jisheng Zhang4ab2e7c2017-06-05 16:53:46 +0800163static const struct dw_pcie_host_ops armada8k_pcie_host_ops = {
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200164 .host_init = armada8k_pcie_host_init,
165};
166
Bjorn Helgaasb2d6fd72016-10-06 13:30:00 -0500167static int armada8k_add_pcie_port(struct armada8k_pcie *pcie,
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200168 struct platform_device *pdev)
169{
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530170 struct dw_pcie *pci = pcie->pci;
171 struct pcie_port *pp = &pci->pp;
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200172 struct device *dev = &pdev->dev;
173 int ret;
174
175 pp->root_bus_nr = -1;
176 pp->ops = &armada8k_pcie_host_ops;
177
178 pp->irq = platform_get_irq(pdev, 0);
Fabio Estevam0fe5f1c2017-08-31 14:52:03 -0300179 if (pp->irq < 0) {
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200180 dev_err(dev, "failed to get irq for port\n");
Fabio Estevam0fe5f1c2017-08-31 14:52:03 -0300181 return pp->irq;
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200182 }
183
184 ret = devm_request_irq(dev, pp->irq, armada8k_pcie_irq_handler,
Bjorn Helgaasb2d6fd72016-10-06 13:30:00 -0500185 IRQF_SHARED, "armada8k-pcie", pcie);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200186 if (ret) {
187 dev_err(dev, "failed to request irq %d\n", pp->irq);
188 return ret;
189 }
190
191 ret = dw_pcie_host_init(pp);
192 if (ret) {
193 dev_err(dev, "failed to initialize host: %d\n", ret);
194 return ret;
195 }
196
197 return 0;
198}
199
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530200static const struct dw_pcie_ops dw_pcie_ops = {
201 .link_up = armada8k_pcie_link_up,
202};
203
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200204static int armada8k_pcie_probe(struct platform_device *pdev)
205{
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530206 struct dw_pcie *pci;
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200207 struct armada8k_pcie *pcie;
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200208 struct device *dev = &pdev->dev;
209 struct resource *base;
210 int ret;
211
212 pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
213 if (!pcie)
214 return -ENOMEM;
215
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530216 pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
217 if (!pci)
218 return -ENOMEM;
219
220 pci->dev = dev;
221 pci->ops = &dw_pcie_ops;
222
Guenter Roeckc0464062017-02-25 02:08:12 -0800223 pcie->pci = pci;
224
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200225 pcie->clk = devm_clk_get(dev, NULL);
226 if (IS_ERR(pcie->clk))
227 return PTR_ERR(pcie->clk);
228
Fabio Estevame2e5d7b2017-07-22 17:25:19 -0300229 ret = clk_prepare_enable(pcie->clk);
230 if (ret)
231 return ret;
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200232
Gregory CLEMENT2435cdd2018-02-28 17:35:30 +0100233 pcie->clk_reg = devm_clk_get(dev, "reg");
234 if (pcie->clk_reg == ERR_PTR(-EPROBE_DEFER)) {
235 ret = -EPROBE_DEFER;
236 goto fail;
237 }
238 if (!IS_ERR(pcie->clk_reg)) {
239 ret = clk_prepare_enable(pcie->clk_reg);
240 if (ret)
241 goto fail_clkreg;
242 }
243
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200244 /* Get the dw-pcie unit configuration/control registers base. */
245 base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl");
Lorenzo Pieralisi53dfa172017-04-19 17:49:04 +0100246 pci->dbi_base = devm_pci_remap_cfg_resource(dev, base);
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530247 if (IS_ERR(pci->dbi_base)) {
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200248 dev_err(dev, "couldn't remap regs base %p\n", base);
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530249 ret = PTR_ERR(pci->dbi_base);
Gregory CLEMENT2435cdd2018-02-28 17:35:30 +0100250 goto fail_clkreg;
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200251 }
252
Kishon Vijay Abraham I9bcf0a62017-02-15 18:48:11 +0530253 platform_set_drvdata(pdev, pcie);
254
Bjorn Helgaasb2d6fd72016-10-06 13:30:00 -0500255 ret = armada8k_add_pcie_port(pcie, pdev);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200256 if (ret)
Gregory CLEMENT2435cdd2018-02-28 17:35:30 +0100257 goto fail_clkreg;
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200258
259 return 0;
260
Gregory CLEMENT2435cdd2018-02-28 17:35:30 +0100261fail_clkreg:
262 clk_disable_unprepare(pcie->clk_reg);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200263fail:
Gregory CLEMENT5dcd7f12018-02-28 17:35:29 +0100264 clk_disable_unprepare(pcie->clk);
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200265
266 return ret;
267}
268
269static const struct of_device_id armada8k_pcie_of_match[] = {
270 { .compatible = "marvell,armada8k-pcie", },
271 {},
272};
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200273
274static struct platform_driver armada8k_pcie_driver = {
275 .probe = armada8k_pcie_probe,
276 .driver = {
277 .name = "armada8k-pcie",
278 .of_match_table = of_match_ptr(armada8k_pcie_of_match),
Brian Norrisa5f40e82017-04-20 15:36:25 -0500279 .suppress_bind_attrs = true,
Thomas Petazzoni1c52a512016-04-26 10:31:46 +0200280 },
281};
Paul Gortmaker0e6f98c2016-07-02 19:13:21 -0400282builtin_platform_driver(armada8k_pcie_driver);