Skip to content

Commit e7e21b3

Browse files
paul-walmsley-sifiveLorenzo Pieralisi
authored andcommitted
PCI: fu740: Add SiFive FU740 PCIe host controller driver
Add driver for the SiFive FU740 PCIe host controller. This controller is based on the DesignWare PCIe core. Co-developed-by: Henry Styles <hes@sifive.com> Co-developed-by: Erik Danie <erik.danie@sifive.com> Co-developed-by: Greentime Hu <greentime.hu@sifive.com> Link: https://lore.kernel.org/r/20210504105940.100004-6-greentime.hu@sifive.com Signed-off-by: Paul Walmsley <paul.walmsley@sifive.com> Signed-off-by: Henry Styles <hes@sifive.com> Signed-off-by: Erik Danie <erik.danie@sifive.com> Signed-off-by: Greentime Hu <greentime.hu@sifive.com> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
1 parent 43cea11 commit e7e21b3

3 files changed

Lines changed: 319 additions & 0 deletions

File tree

drivers/pci/controller/dwc/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,4 +318,13 @@ config PCIE_AL
318318
required only for DT-based platforms. ACPI platforms with the
319319
Annapurna Labs PCIe controller don't need to enable this.
320320

321+
config PCIE_FU740
322+
bool "SiFive FU740 PCIe host controller"
323+
depends on PCI_MSI_IRQ_DOMAIN
324+
depends on SOC_SIFIVE || COMPILE_TEST
325+
select PCIE_DW_HOST
326+
help
327+
Say Y here if you want PCIe controller support for the SiFive
328+
FU740.
329+
321330
endmenu

drivers/pci/controller/dwc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o
55
obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o
66
obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
77
obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
8+
obj-$(CONFIG_PCIE_FU740) += pcie-fu740.o
89
obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
910
obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
1011
obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
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+
34+
struct 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+
82+
static 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+
90+
static 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+
98+
static 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+
109+
static 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+
116+
static 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+
157+
static 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+
180+
static 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+
190+
static 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+
234+
static const struct dw_pcie_host_ops fu740_pcie_host_ops = {
235+
.host_init = fu740_pcie_host_init,
236+
};
237+
238+
static const struct dw_pcie_ops dw_pcie_ops = {
239+
.start_link = fu740_pcie_start_link,
240+
};
241+
242+
static 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+
286+
static 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+
294+
static const struct of_device_id fu740_pcie_of_match[] = {
295+
{ .compatible = "sifive,fu740-pcie", },
296+
{},
297+
};
298+
299+
static 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+
309+
builtin_platform_driver(fu740_pcie_driver);

0 commit comments

Comments
 (0)