blob: 56f32aeebd0a40bbcd522533813aa48cc01382b9 [file] [log] [blame]
Jingoo Han4b1ced82013-07-31 17:14:10 +09001/*
2 * PCIe host controller driver for Samsung EXYNOS SoCs
3 *
4 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
5 * http://www.samsung.com
6 *
7 * Author: Jingoo Han <jg1.han@samsung.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/clk.h>
15#include <linux/delay.h>
16#include <linux/gpio.h>
17#include <linux/interrupt.h>
18#include <linux/kernel.h>
Paul Gortmakercaf55482016-08-22 17:59:47 -040019#include <linux/init.h>
Niyas Ahmed S T32784782017-02-01 10:13:06 +053020#include <linux/of_device.h>
Jingoo Han4b1ced82013-07-31 17:14:10 +090021#include <linux/of_gpio.h>
22#include <linux/pci.h>
23#include <linux/platform_device.h>
Jaehoon Chunge7cd7ef2017-02-13 17:26:13 +090024#include <linux/phy/phy.h>
Jingoo Han4b1ced82013-07-31 17:14:10 +090025#include <linux/resource.h>
26#include <linux/signal.h>
27#include <linux/types.h>
28
29#include "pcie-designware.h"
30
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +053031#define to_exynos_pcie(x) dev_get_drvdata((x)->dev)
Jingoo Han4b1ced82013-07-31 17:14:10 +090032
Jingoo Han4b1ced82013-07-31 17:14:10 +090033/* PCIe ELBI registers */
34#define PCIE_IRQ_PULSE 0x000
Jaehoon Chung2681c0e2017-01-16 15:31:37 +090035#define IRQ_INTA_ASSERT BIT(0)
36#define IRQ_INTB_ASSERT BIT(2)
37#define IRQ_INTC_ASSERT BIT(4)
38#define IRQ_INTD_ASSERT BIT(6)
Jingoo Han4b1ced82013-07-31 17:14:10 +090039#define PCIE_IRQ_LEVEL 0x004
40#define PCIE_IRQ_SPECIAL 0x008
41#define PCIE_IRQ_EN_PULSE 0x00c
42#define PCIE_IRQ_EN_LEVEL 0x010
Jaehoon Chung2681c0e2017-01-16 15:31:37 +090043#define IRQ_MSI_ENABLE BIT(2)
Jingoo Han4b1ced82013-07-31 17:14:10 +090044#define PCIE_IRQ_EN_SPECIAL 0x014
45#define PCIE_PWR_RESET 0x018
46#define PCIE_CORE_RESET 0x01c
Jaehoon Chung2681c0e2017-01-16 15:31:37 +090047#define PCIE_CORE_RESET_ENABLE BIT(0)
Jingoo Han4b1ced82013-07-31 17:14:10 +090048#define PCIE_STICKY_RESET 0x020
49#define PCIE_NONSTICKY_RESET 0x024
50#define PCIE_APP_INIT_RESET 0x028
51#define PCIE_APP_LTSSM_ENABLE 0x02c
52#define PCIE_ELBI_RDLH_LINKUP 0x064
53#define PCIE_ELBI_LTSSM_ENABLE 0x1
54#define PCIE_ELBI_SLV_AWMISC 0x11c
55#define PCIE_ELBI_SLV_ARMISC 0x120
Jaehoon Chung2681c0e2017-01-16 15:31:37 +090056#define PCIE_ELBI_SLV_DBI_ENABLE BIT(21)
Jingoo Han4b1ced82013-07-31 17:14:10 +090057
Niyas Ahmed S T32784782017-02-01 10:13:06 +053058struct exynos_pcie_mem_res {
59 void __iomem *elbi_base; /* DT 0th resource: PCIe CTRL */
Niyas Ahmed S T32784782017-02-01 10:13:06 +053060};
61
62struct exynos_pcie_clk_res {
63 struct clk *clk;
64 struct clk *bus_clk;
65};
66
67struct exynos_pcie {
Bjorn Helgaasb2e6d302017-02-21 15:13:30 -060068 struct dw_pcie *pci;
Niyas Ahmed S T32784782017-02-01 10:13:06 +053069 struct exynos_pcie_mem_res *mem_res;
70 struct exynos_pcie_clk_res *clk_res;
71 const struct exynos_pcie_ops *ops;
72 int reset_gpio;
Jaehoon Chunge7cd7ef2017-02-13 17:26:13 +090073
Jaehoon Chunge7cd7ef2017-02-13 17:26:13 +090074 struct phy *phy;
Niyas Ahmed S T32784782017-02-01 10:13:06 +053075};
76
77struct exynos_pcie_ops {
78 int (*get_mem_resources)(struct platform_device *pdev,
79 struct exynos_pcie *ep);
80 int (*get_clk_resources)(struct exynos_pcie *ep);
81 int (*init_clk_resources)(struct exynos_pcie *ep);
82 void (*deinit_clk_resources)(struct exynos_pcie *ep);
83};
84
85static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev,
86 struct exynos_pcie *ep)
Seungwon Jeon058dd012013-08-29 21:35:56 +090087{
Bjorn Helgaasb2e6d302017-02-21 15:13:30 -060088 struct dw_pcie *pci = ep->pci;
89 struct device *dev = pci->dev;
Niyas Ahmed S T32784782017-02-01 10:13:06 +053090 struct resource *res;
Niyas Ahmed S T32784782017-02-01 10:13:06 +053091
92 ep->mem_res = devm_kzalloc(dev, sizeof(*ep->mem_res), GFP_KERNEL);
93 if (!ep->mem_res)
94 return -ENOMEM;
95
96 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
97 ep->mem_res->elbi_base = devm_ioremap_resource(dev, res);
98 if (IS_ERR(ep->mem_res->elbi_base))
99 return PTR_ERR(ep->mem_res->elbi_base);
100
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530101 return 0;
Seungwon Jeon058dd012013-08-29 21:35:56 +0900102}
103
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530104static int exynos5440_pcie_get_clk_resources(struct exynos_pcie *ep)
Seungwon Jeon058dd012013-08-29 21:35:56 +0900105{
Bjorn Helgaasb2e6d302017-02-21 15:13:30 -0600106 struct dw_pcie *pci = ep->pci;
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530107 struct device *dev = pci->dev;
Jingoo Han4b1ced82013-07-31 17:14:10 +0900108
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530109 ep->clk_res = devm_kzalloc(dev, sizeof(*ep->clk_res), GFP_KERNEL);
110 if (!ep->clk_res)
111 return -ENOMEM;
112
113 ep->clk_res->clk = devm_clk_get(dev, "pcie");
114 if (IS_ERR(ep->clk_res->clk)) {
115 dev_err(dev, "Failed to get pcie rc clock\n");
116 return PTR_ERR(ep->clk_res->clk);
117 }
118
119 ep->clk_res->bus_clk = devm_clk_get(dev, "pcie_bus");
120 if (IS_ERR(ep->clk_res->bus_clk)) {
121 dev_err(dev, "Failed to get pcie bus clock\n");
122 return PTR_ERR(ep->clk_res->bus_clk);
123 }
124
125 return 0;
126}
127
128static int exynos5440_pcie_init_clk_resources(struct exynos_pcie *ep)
129{
Bjorn Helgaasb2e6d302017-02-21 15:13:30 -0600130 struct dw_pcie *pci = ep->pci;
131 struct device *dev = pci->dev;
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530132 int ret;
133
134 ret = clk_prepare_enable(ep->clk_res->clk);
135 if (ret) {
136 dev_err(dev, "cannot enable pcie rc clock");
137 return ret;
138 }
139
140 ret = clk_prepare_enable(ep->clk_res->bus_clk);
141 if (ret) {
142 dev_err(dev, "cannot enable pcie bus clock");
143 goto err_bus_clk;
144 }
145
146 return 0;
147
148err_bus_clk:
149 clk_disable_unprepare(ep->clk_res->clk);
150
151 return ret;
152}
153
154static void exynos5440_pcie_deinit_clk_resources(struct exynos_pcie *ep)
155{
156 clk_disable_unprepare(ep->clk_res->bus_clk);
157 clk_disable_unprepare(ep->clk_res->clk);
158}
159
160static const struct exynos_pcie_ops exynos5440_pcie_ops = {
161 .get_mem_resources = exynos5440_pcie_get_mem_resources,
162 .get_clk_resources = exynos5440_pcie_get_clk_resources,
163 .init_clk_resources = exynos5440_pcie_init_clk_resources,
164 .deinit_clk_resources = exynos5440_pcie_deinit_clk_resources,
165};
166
Jaehoon Chungd6da7d92017-01-16 15:31:35 +0900167static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900168{
Jaehoon Chungd6da7d92017-01-16 15:31:35 +0900169 writel(val, base + reg);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900170}
171
Jaehoon Chungd6da7d92017-01-16 15:31:35 +0900172static u32 exynos_pcie_readl(void __iomem *base, u32 reg)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900173{
Jaehoon Chungd6da7d92017-01-16 15:31:35 +0900174 return readl(base + reg);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900175}
176
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900177static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900178{
179 u32 val;
180
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530181 val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_AWMISC);
Jaehoon Chung92004a062017-01-16 15:31:38 +0900182 if (on)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900183 val |= PCIE_ELBI_SLV_DBI_ENABLE;
Jaehoon Chung92004a062017-01-16 15:31:38 +0900184 else
Jingoo Han4b1ced82013-07-31 17:14:10 +0900185 val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530186 exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900187}
188
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900189static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900190{
191 u32 val;
192
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530193 val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_ARMISC);
Jaehoon Chung92004a062017-01-16 15:31:38 +0900194 if (on)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900195 val |= PCIE_ELBI_SLV_DBI_ENABLE;
Jaehoon Chung92004a062017-01-16 15:31:38 +0900196 else
Jingoo Han4b1ced82013-07-31 17:14:10 +0900197 val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530198 exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900199}
200
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900201static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900202{
203 u32 val;
204
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530205 val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900206 val &= ~PCIE_CORE_RESET_ENABLE;
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530207 exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
208 exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_PWR_RESET);
209 exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_STICKY_RESET);
210 exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_NONSTICKY_RESET);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900211}
212
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900213static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900214{
215 u32 val;
216
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530217 val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900218 val |= PCIE_CORE_RESET_ENABLE;
219
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530220 exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
221 exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_STICKY_RESET);
222 exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET);
223 exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET);
224 exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900225}
226
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900227static void exynos_pcie_assert_reset(struct exynos_pcie *ep)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900228{
Bjorn Helgaasb2e6d302017-02-21 15:13:30 -0600229 struct dw_pcie *pci = ep->pci;
230 struct device *dev = pci->dev;
Jingoo Han4b1ced82013-07-31 17:14:10 +0900231
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900232 if (ep->reset_gpio >= 0)
233 devm_gpio_request_one(dev, ep->reset_gpio,
Jingoo Han4b1ced82013-07-31 17:14:10 +0900234 GPIOF_OUT_INIT_HIGH, "RESET");
Jingoo Han4b1ced82013-07-31 17:14:10 +0900235}
236
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900237static int exynos_pcie_establish_link(struct exynos_pcie *ep)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900238{
Bjorn Helgaasb2e6d302017-02-21 15:13:30 -0600239 struct dw_pcie *pci = ep->pci;
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530240 struct pcie_port *pp = &pci->pp;
241 struct device *dev = pci->dev;
Jingoo Han4b1ced82013-07-31 17:14:10 +0900242
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530243 if (dw_pcie_link_up(pci)) {
Bjorn Helgaasfae68d62016-10-06 13:33:41 -0500244 dev_err(dev, "Link already up\n");
Jingoo Han4b1ced82013-07-31 17:14:10 +0900245 return 0;
246 }
247
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900248 exynos_pcie_assert_core_reset(ep);
Jaehoon Chunge7cd7ef2017-02-13 17:26:13 +0900249
Jaehoon Chung83f4f3f2017-12-27 18:43:27 +0900250 phy_reset(ep->phy);
Jaehoon Chunge7cd7ef2017-02-13 17:26:13 +0900251
Jaehoon Chung83f4f3f2017-12-27 18:43:27 +0900252 exynos_pcie_writel(ep->mem_res->elbi_base, 1,
253 PCIE_PWR_RESET);
Jaehoon Chunge7cd7ef2017-02-13 17:26:13 +0900254
Jaehoon Chung83f4f3f2017-12-27 18:43:27 +0900255 phy_power_on(ep->phy);
256 phy_init(ep->phy);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900257
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900258 exynos_pcie_deassert_core_reset(ep);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900259 dw_pcie_setup_rc(pp);
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900260 exynos_pcie_assert_reset(ep);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900261
262 /* assert LTSSM enable */
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530263 exynos_pcie_writel(ep->mem_res->elbi_base, PCIE_ELBI_LTSSM_ENABLE,
Seungwon Jeon058dd012013-08-29 21:35:56 +0900264 PCIE_APP_LTSSM_ENABLE);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900265
266 /* check if the link is up or not */
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530267 if (!dw_pcie_wait_for_link(pci))
Joao Pinto886bc5c2016-03-10 14:44:35 -0600268 return 0;
Jingoo Han4b1ced82013-07-31 17:14:10 +0900269
Jaehoon Chung83f4f3f2017-12-27 18:43:27 +0900270 phy_power_off(ep->phy);
Joao Pinto886bc5c2016-03-10 14:44:35 -0600271 return -ETIMEDOUT;
Jingoo Han4b1ced82013-07-31 17:14:10 +0900272}
273
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900274static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900275{
276 u32 val;
Jingoo Han4b1ced82013-07-31 17:14:10 +0900277
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530278 val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_PULSE);
279 exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_PULSE);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900280}
281
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900282static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900283{
284 u32 val;
Jingoo Han4b1ced82013-07-31 17:14:10 +0900285
286 /* enable INTX interrupt */
287 val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
Jaehoon Chung01d06a92015-03-25 14:13:12 +0900288 IRQ_INTC_ASSERT | IRQ_INTD_ASSERT;
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530289 exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_PULSE);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900290}
291
292static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
293{
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900294 struct exynos_pcie *ep = arg;
Jingoo Han4b1ced82013-07-31 17:14:10 +0900295
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900296 exynos_pcie_clear_irq_pulse(ep);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900297 return IRQ_HANDLED;
298}
299
Jingoo Hanf342d942013-09-06 15:54:59 +0900300static irqreturn_t exynos_pcie_msi_irq_handler(int irq, void *arg)
301{
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900302 struct exynos_pcie *ep = arg;
Bjorn Helgaasb2e6d302017-02-21 15:13:30 -0600303 struct dw_pcie *pci = ep->pci;
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530304 struct pcie_port *pp = &pci->pp;
Jingoo Hanf342d942013-09-06 15:54:59 +0900305
Lucas Stach7f4f16e2014-03-28 17:52:58 +0100306 return dw_handle_msi_irq(pp);
Jingoo Hanf342d942013-09-06 15:54:59 +0900307}
308
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900309static void exynos_pcie_msi_init(struct exynos_pcie *ep)
Jingoo Hanf342d942013-09-06 15:54:59 +0900310{
Bjorn Helgaasb2e6d302017-02-21 15:13:30 -0600311 struct dw_pcie *pci = ep->pci;
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530312 struct pcie_port *pp = &pci->pp;
Jingoo Hanf342d942013-09-06 15:54:59 +0900313 u32 val;
Jingoo Hanf342d942013-09-06 15:54:59 +0900314
315 dw_pcie_msi_init(pp);
316
317 /* enable MSI interrupt */
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530318 val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_EN_LEVEL);
Jingoo Hanf342d942013-09-06 15:54:59 +0900319 val |= IRQ_MSI_ENABLE;
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530320 exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_LEVEL);
Jingoo Hanf342d942013-09-06 15:54:59 +0900321}
322
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900323static void exynos_pcie_enable_interrupts(struct exynos_pcie *ep)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900324{
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900325 exynos_pcie_enable_irq_pulse(ep);
Jingoo Hanf342d942013-09-06 15:54:59 +0900326
327 if (IS_ENABLED(CONFIG_PCI_MSI))
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900328 exynos_pcie_msi_init(ep);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900329}
330
Kishon Vijay Abraham Ia509d7d2017-03-13 19:13:26 +0530331static u32 exynos_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base,
332 u32 reg, size_t size)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900333{
Bjorn Helgaasb2e6d302017-02-21 15:13:30 -0600334 struct exynos_pcie *ep = to_exynos_pcie(pci);
Bjorn Helgaas446fc232016-08-17 14:17:58 -0500335 u32 val;
336
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900337 exynos_pcie_sideband_dbi_r_mode(ep, true);
Kishon Vijay Abraham Ia509d7d2017-03-13 19:13:26 +0530338 dw_pcie_read(base + reg, size, &val);
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900339 exynos_pcie_sideband_dbi_r_mode(ep, false);
Bjorn Helgaas446fc232016-08-17 14:17:58 -0500340 return val;
Jingoo Han4b1ced82013-07-31 17:14:10 +0900341}
342
Kishon Vijay Abraham Ia509d7d2017-03-13 19:13:26 +0530343static void exynos_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base,
344 u32 reg, size_t size, u32 val)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900345{
Bjorn Helgaasb2e6d302017-02-21 15:13:30 -0600346 struct exynos_pcie *ep = to_exynos_pcie(pci);
Bjorn Helgaascc08e822016-10-06 13:33:39 -0500347
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900348 exynos_pcie_sideband_dbi_w_mode(ep, true);
Kishon Vijay Abraham Ia509d7d2017-03-13 19:13:26 +0530349 dw_pcie_write(base + reg, size, val);
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900350 exynos_pcie_sideband_dbi_w_mode(ep, false);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900351}
352
353static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
354 u32 *val)
355{
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530356 struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
Bjorn Helgaasb2e6d302017-02-21 15:13:30 -0600357 struct exynos_pcie *ep = to_exynos_pcie(pci);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900358 int ret;
359
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900360 exynos_pcie_sideband_dbi_r_mode(ep, true);
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530361 ret = dw_pcie_read(pci->dbi_base + where, size, val);
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900362 exynos_pcie_sideband_dbi_r_mode(ep, false);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900363 return ret;
364}
365
366static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
367 u32 val)
368{
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530369 struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
Bjorn Helgaasb2e6d302017-02-21 15:13:30 -0600370 struct exynos_pcie *ep = to_exynos_pcie(pci);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900371 int ret;
372
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900373 exynos_pcie_sideband_dbi_w_mode(ep, true);
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530374 ret = dw_pcie_write(pci->dbi_base + where, size, val);
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900375 exynos_pcie_sideband_dbi_w_mode(ep, false);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900376 return ret;
377}
378
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530379static int exynos_pcie_link_up(struct dw_pcie *pci)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900380{
Bjorn Helgaasb2e6d302017-02-21 15:13:30 -0600381 struct exynos_pcie *ep = to_exynos_pcie(pci);
Bjorn Helgaascc08e822016-10-06 13:33:39 -0500382 u32 val;
Jingoo Han4b1ced82013-07-31 17:14:10 +0900383
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530384 val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_RDLH_LINKUP);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900385 if (val == PCIE_ELBI_LTSSM_ENABLE)
386 return 1;
387
388 return 0;
389}
390
Bjorn Andersson4a301762017-07-15 23:39:45 -0700391static int exynos_pcie_host_init(struct pcie_port *pp)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900392{
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530393 struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
Bjorn Helgaasb2e6d302017-02-21 15:13:30 -0600394 struct exynos_pcie *ep = to_exynos_pcie(pci);
Bjorn Helgaascc08e822016-10-06 13:33:39 -0500395
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900396 exynos_pcie_establish_link(ep);
397 exynos_pcie_enable_interrupts(ep);
Bjorn Andersson4a301762017-07-15 23:39:45 -0700398
399 return 0;
Jingoo Han4b1ced82013-07-31 17:14:10 +0900400}
401
Jisheng Zhang4ab2e7c2017-06-05 16:53:46 +0800402static const struct dw_pcie_host_ops exynos_pcie_host_ops = {
Jingoo Han4b1ced82013-07-31 17:14:10 +0900403 .rd_own_conf = exynos_pcie_rd_own_conf,
404 .wr_own_conf = exynos_pcie_wr_own_conf,
Jingoo Han4b1ced82013-07-31 17:14:10 +0900405 .host_init = exynos_pcie_host_init,
406};
407
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900408static int __init exynos_add_pcie_port(struct exynos_pcie *ep,
Jingoo Han70b3e892014-10-22 13:58:49 +0900409 struct platform_device *pdev)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900410{
Bjorn Helgaasb2e6d302017-02-21 15:13:30 -0600411 struct dw_pcie *pci = ep->pci;
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530412 struct pcie_port *pp = &pci->pp;
413 struct device *dev = &pdev->dev;
Jingoo Han4b1ced82013-07-31 17:14:10 +0900414 int ret;
415
416 pp->irq = platform_get_irq(pdev, 1);
Fabio Estevam1df5a482017-08-31 14:52:01 -0300417 if (pp->irq < 0) {
Bjorn Helgaasfae68d62016-10-06 13:33:41 -0500418 dev_err(dev, "failed to get irq\n");
Fabio Estevam1df5a482017-08-31 14:52:01 -0300419 return pp->irq;
Jingoo Han4b1ced82013-07-31 17:14:10 +0900420 }
Bjorn Helgaasfae68d62016-10-06 13:33:41 -0500421 ret = devm_request_irq(dev, pp->irq, exynos_pcie_irq_handler,
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900422 IRQF_SHARED, "exynos-pcie", ep);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900423 if (ret) {
Bjorn Helgaasfae68d62016-10-06 13:33:41 -0500424 dev_err(dev, "failed to request irq\n");
Jingoo Han4b1ced82013-07-31 17:14:10 +0900425 return ret;
426 }
427
Jingoo Hanf342d942013-09-06 15:54:59 +0900428 if (IS_ENABLED(CONFIG_PCI_MSI)) {
429 pp->msi_irq = platform_get_irq(pdev, 0);
Fabio Estevam1df5a482017-08-31 14:52:01 -0300430 if (pp->msi_irq < 0) {
Bjorn Helgaasfae68d62016-10-06 13:33:41 -0500431 dev_err(dev, "failed to get msi irq\n");
Fabio Estevam1df5a482017-08-31 14:52:01 -0300432 return pp->msi_irq;
Jingoo Hanf342d942013-09-06 15:54:59 +0900433 }
434
Bjorn Helgaasfae68d62016-10-06 13:33:41 -0500435 ret = devm_request_irq(dev, pp->msi_irq,
Jingoo Hanf342d942013-09-06 15:54:59 +0900436 exynos_pcie_msi_irq_handler,
Grygorii Strashko8ff0ef92015-12-10 21:18:20 +0200437 IRQF_SHARED | IRQF_NO_THREAD,
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900438 "exynos-pcie", ep);
Jingoo Hanf342d942013-09-06 15:54:59 +0900439 if (ret) {
Bjorn Helgaasfae68d62016-10-06 13:33:41 -0500440 dev_err(dev, "failed to request msi irq\n");
Jingoo Hanf342d942013-09-06 15:54:59 +0900441 return ret;
442 }
443 }
444
Jingoo Han4b1ced82013-07-31 17:14:10 +0900445 pp->root_bus_nr = -1;
446 pp->ops = &exynos_pcie_host_ops;
447
Jingoo Han4b1ced82013-07-31 17:14:10 +0900448 ret = dw_pcie_host_init(pp);
449 if (ret) {
Bjorn Helgaasfae68d62016-10-06 13:33:41 -0500450 dev_err(dev, "failed to initialize host\n");
Jingoo Han4b1ced82013-07-31 17:14:10 +0900451 return ret;
452 }
453
454 return 0;
455}
456
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530457static const struct dw_pcie_ops dw_pcie_ops = {
Kishon Vijay Abraham Ia509d7d2017-03-13 19:13:26 +0530458 .read_dbi = exynos_pcie_read_dbi,
459 .write_dbi = exynos_pcie_write_dbi,
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530460 .link_up = exynos_pcie_link_up,
461};
462
Jingoo Han4b1ced82013-07-31 17:14:10 +0900463static int __init exynos_pcie_probe(struct platform_device *pdev)
464{
Bjorn Helgaasfae68d62016-10-06 13:33:41 -0500465 struct device *dev = &pdev->dev;
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530466 struct dw_pcie *pci;
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900467 struct exynos_pcie *ep;
Bjorn Helgaasfae68d62016-10-06 13:33:41 -0500468 struct device_node *np = dev->of_node;
Jingoo Han4b1ced82013-07-31 17:14:10 +0900469 int ret;
470
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900471 ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL);
472 if (!ep)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900473 return -ENOMEM;
Jingoo Han4b1ced82013-07-31 17:14:10 +0900474
Kishon Vijay Abraham I442ec4c2017-02-15 18:48:14 +0530475 pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
476 if (!pci)
477 return -ENOMEM;
478
479 pci->dev = dev;
480 pci->ops = &dw_pcie_ops;
Jingoo Han4b1ced82013-07-31 17:14:10 +0900481
Guenter Roeckc0464062017-02-25 02:08:12 -0800482 ep->pci = pci;
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530483 ep->ops = (const struct exynos_pcie_ops *)
484 of_device_get_match_data(dev);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900485
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900486 ep->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900487
Jaehoon Chunge7cd7ef2017-02-13 17:26:13 +0900488 ep->phy = devm_of_phy_get(dev, np, NULL);
489 if (IS_ERR(ep->phy)) {
490 if (PTR_ERR(ep->phy) == -EPROBE_DEFER)
491 return PTR_ERR(ep->phy);
Jaehoon Chung83f4f3f2017-12-27 18:43:27 +0900492
493 ep->phy = NULL;
494 }
Jaehoon Chunge7cd7ef2017-02-13 17:26:13 +0900495
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530496 if (ep->ops && ep->ops->get_mem_resources) {
497 ret = ep->ops->get_mem_resources(pdev, ep);
498 if (ret)
499 return ret;
Wei Yongjunf8db3c92013-09-29 10:29:11 +0800500 }
Jingoo Han4b1ced82013-07-31 17:14:10 +0900501
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530502 if (ep->ops && ep->ops->get_clk_resources) {
503 ret = ep->ops->get_clk_resources(ep);
504 if (ret)
505 return ret;
506 ret = ep->ops->init_clk_resources(ep);
507 if (ret)
508 return ret;
Wei Yongjunf8db3c92013-09-29 10:29:11 +0800509 }
Jingoo Han4b1ced82013-07-31 17:14:10 +0900510
Bjorn Helgaasb2e6d302017-02-21 15:13:30 -0600511 platform_set_drvdata(pdev, ep);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900512
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900513 ret = exynos_add_pcie_port(ep, pdev);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900514 if (ret < 0)
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530515 goto fail_probe;
Jingoo Han4b1ced82013-07-31 17:14:10 +0900516
Jingoo Han4b1ced82013-07-31 17:14:10 +0900517 return 0;
518
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530519fail_probe:
Jaehoon Chung83f4f3f2017-12-27 18:43:27 +0900520 phy_exit(ep->phy);
Jaehoon Chunge7cd7ef2017-02-13 17:26:13 +0900521
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530522 if (ep->ops && ep->ops->deinit_clk_resources)
523 ep->ops->deinit_clk_resources(ep);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900524 return ret;
525}
526
527static int __exit exynos_pcie_remove(struct platform_device *pdev)
528{
Jaehoon Chung4e0a90b382017-01-16 15:31:34 +0900529 struct exynos_pcie *ep = platform_get_drvdata(pdev);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900530
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530531 if (ep->ops && ep->ops->deinit_clk_resources)
532 ep->ops->deinit_clk_resources(ep);
Jingoo Han4b1ced82013-07-31 17:14:10 +0900533
534 return 0;
535}
536
537static const struct of_device_id exynos_pcie_of_match[] = {
Niyas Ahmed S T32784782017-02-01 10:13:06 +0530538 {
539 .compatible = "samsung,exynos5440-pcie",
540 .data = &exynos5440_pcie_ops
541 },
Jingoo Han4b1ced82013-07-31 17:14:10 +0900542 {},
543};
Jingoo Han4b1ced82013-07-31 17:14:10 +0900544
545static struct platform_driver exynos_pcie_driver = {
546 .remove = __exit_p(exynos_pcie_remove),
547 .driver = {
548 .name = "exynos-pcie",
Sachin Kamateb363092013-10-21 14:36:43 +0530549 .of_match_table = exynos_pcie_of_match,
Jingoo Han4b1ced82013-07-31 17:14:10 +0900550 },
551};
552
553/* Exynos PCIe driver does not allow module unload */
554
Jingoo Han70b3e892014-10-22 13:58:49 +0900555static int __init exynos_pcie_init(void)
Jingoo Han4b1ced82013-07-31 17:14:10 +0900556{
557 return platform_driver_probe(&exynos_pcie_driver, exynos_pcie_probe);
558}
Jingoo Han70b3e892014-10-22 13:58:49 +0900559subsys_initcall(exynos_pcie_init);