xref: /freebsd/sys/riscv/sifive/fu740_pci_dw.c (revision 397e83df75e0fcd0d3fcb95ae4d794cb7600fc89)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright 2021 Jessica Clarke <jrtc27@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28 
29 /* SiFive FU740 DesignWare PCIe driver */
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/bus.h>
34 #include <sys/gpio.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/rman.h>
38 
39 #include <machine/bus.h>
40 #include <machine/intr.h>
41 #include <machine/resource.h>
42 
43 #include <dev/clk/clk.h>
44 #include <dev/hwreset/hwreset.h>
45 #include <dev/gpio/gpiobusvar.h>
46 #include <dev/ofw/ofw_bus.h>
47 #include <dev/ofw/ofw_bus_subr.h>
48 #include <dev/ofw/ofw_pci.h>
49 #include <dev/ofw/ofwpci.h>
50 #include <dev/pci/pcivar.h>
51 #include <dev/pci/pcireg.h>
52 #include <dev/pci/pcib_private.h>
53 #include <dev/pci/pci_dw.h>
54 
55 #include "pcib_if.h"
56 #include "pci_dw_if.h"
57 
58 #define	FUDW_PHYS		2
59 #define	FUDW_LANES_PER_PHY	4
60 
61 #define	FUDW_MGMT_PERST_N			0x0
62 #define	FUDW_MGMT_LTSSM_EN			0x10
63 #define	FUDW_MGMT_HOLD_PHY_RST			0x18
64 #define	FUDW_MGMT_DEVICE_TYPE			0x708
65 #define	 FUDW_MGMT_DEVICE_TYPE_RC			0x4
66 #define	FUDW_MGMT_PHY_CR_PARA_REG(_n, _r)	\
67     (0x860 + (_n) * 0x40 + FUDW_MGMT_PHY_CR_PARA_##_r)
68 #define	 FUDW_MGMT_PHY_CR_PARA_ADDR			0x0
69 #define	 FUDW_MGMT_PHY_CR_PARA_READ_EN			0x10
70 #define	 FUDW_MGMT_PHY_CR_PARA_READ_DATA		0x18
71 #define	 FUDW_MGMT_PHY_CR_PARA_SEL			0x20
72 #define	 FUDW_MGMT_PHY_CR_PARA_WRITE_DATA		0x28
73 #define	 FUDW_MGMT_PHY_CR_PARA_WRITE_EN			0x30
74 #define	 FUDW_MGMT_PHY_CR_PARA_ACK			0x38
75 
76 #define	FUDW_MGMT_PHY_LANE(_n)			(0x1008 + (_n) * 0x100)
77 #define	 FUDW_MGMT_PHY_LANE_CDR_TRACK_EN		(1 << 0)
78 #define	 FUDW_MGMT_PHY_LANE_LOS_THRESH			(1 << 5)
79 #define	 FUDW_MGMT_PHY_LANE_TERM_EN			(1 << 9)
80 #define	 FUDW_MGMT_PHY_LANE_TERM_ACDC			(1 << 10)
81 #define	 FUDW_MGMT_PHY_LANE_EN				(1 << 11)
82 #define	 FUDW_MGMT_PHY_LANE_INIT					\
83     (FUDW_MGMT_PHY_LANE_CDR_TRACK_EN | FUDW_MGMT_PHY_LANE_LOS_THRESH |	\
84      FUDW_MGMT_PHY_LANE_TERM_EN | FUDW_MGMT_PHY_LANE_TERM_ACDC |	\
85      FUDW_MGMT_PHY_LANE_EN)
86 
87 #define	FUDW_DBI_PORT_DBG1			0x72c
88 #define	 FUDW_DBI_PORT_DBG1_LINK_UP			(1 << 4)
89 #define	 FUDW_DBI_PORT_DBG1_LINK_IN_TRAINING		(1 << 29)
90 
91 struct fupci_softc {
92 	struct pci_dw_softc	dw_sc;
93 	device_t		dev;
94 	struct resource		*mgmt_res;
95 	gpio_pin_t		porst_pin;
96 	gpio_pin_t		pwren_pin;
97 	clk_t			pcie_aux_clk;
98 	hwreset_t		pcie_aux_rst;
99 };
100 
101 #define	FUDW_MGMT_READ(_sc, _o)		bus_read_4((_sc)->mgmt_res, (_o))
102 #define	FUDW_MGMT_WRITE(_sc, _o, _v)	bus_write_4((_sc)->mgmt_res, (_o), (_v))
103 
104 static struct ofw_compat_data compat_data[] = {
105 	{ "sifive,fu740-pcie",	1 },
106 	{ NULL,			0 },
107 };
108 
109 /* Currently unused; included for completeness */
110 static int __unused
111 fupci_phy_read(struct fupci_softc *sc, int phy, uint32_t reg, uint32_t *val)
112 {
113 	unsigned timeout;
114 	uint32_t ack;
115 
116 	FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ADDR), reg);
117 	FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, READ_EN), 1);
118 
119 	timeout = 10;
120 	do {
121 		ack = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ACK));
122 		if (ack != 0)
123 			break;
124 		DELAY(10);
125 	} while (--timeout > 0);
126 
127 	if (timeout == 0) {
128 		device_printf(sc->dev, "Timeout waiting for read ACK\n");
129 		return (ETIMEDOUT);
130 	}
131 
132 	*val = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, READ_DATA));
133 	FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, READ_EN), 0);
134 
135 	timeout = 10;
136 	do {
137 		ack = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ACK));
138 		if (ack == 0)
139 			break;
140 		DELAY(10);
141 	} while (--timeout > 0);
142 
143 	if (timeout == 0) {
144 		device_printf(sc->dev, "Timeout waiting for read un-ACK\n");
145 		return (ETIMEDOUT);
146 	}
147 
148 	return (0);
149 }
150 
151 static int
152 fupci_phy_write(struct fupci_softc *sc, int phy, uint32_t reg, uint32_t val)
153 {
154 	unsigned timeout;
155 	uint32_t ack;
156 
157 	FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ADDR), reg);
158 	FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, WRITE_DATA), val);
159 	FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, WRITE_EN), 1);
160 
161 	timeout = 10;
162 	do {
163 		ack = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ACK));
164 		if (ack != 0)
165 			break;
166 		DELAY(10);
167 	} while (--timeout > 0);
168 
169 	if (timeout == 0) {
170 		device_printf(sc->dev, "Timeout waiting for write ACK\n");
171 		return (ETIMEDOUT);
172 	}
173 
174 	FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, WRITE_EN), 0);
175 
176 	timeout = 10;
177 	do {
178 		ack = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ACK));
179 		if (ack == 0)
180 			break;
181 		DELAY(10);
182 	} while (--timeout > 0);
183 
184 	if (timeout == 0) {
185 		device_printf(sc->dev, "Timeout waiting for write un-ACK\n");
186 		return (ETIMEDOUT);
187 	}
188 
189 	return (0);
190 }
191 
192 static int
193 fupci_phy_init(struct fupci_softc *sc)
194 {
195 	device_t dev;
196 	int error, phy, lane;
197 
198 	dev = sc->dev;
199 
200 	/* Assert core power-on reset (active low) */
201 	error = gpio_pin_set_active(sc->porst_pin, false);
202 	if (error != 0) {
203 		device_printf(dev, "Cannot assert power-on reset: %d\n",
204 		    error);
205 		return (error);
206 	}
207 
208 	/* Assert PERST_N */
209 	FUDW_MGMT_WRITE(sc, FUDW_MGMT_PERST_N, 0);
210 
211 	/* Enable power */
212 	error = gpio_pin_set_active(sc->pwren_pin, true);
213 	if (error != 0) {
214 		device_printf(dev, "Cannot enable power: %d\n", error);
215 		return (error);
216 	}
217 
218 	/* Hold PERST for 100ms as per the PCIe spec */
219 	DELAY(100);
220 
221 	/* Deassert PERST_N */
222 	FUDW_MGMT_WRITE(sc, FUDW_MGMT_PERST_N, 1);
223 
224 	/* Deassert core power-on reset (active low) */
225 	error = gpio_pin_set_active(sc->porst_pin, true);
226 	if (error != 0) {
227 		device_printf(dev, "Cannot deassert power-on reset: %d\n",
228 		    error);
229 		return (error);
230 	}
231 
232 	/* Enable the aux clock */
233 	error = clk_enable(sc->pcie_aux_clk);
234 	if (error != 0) {
235 		device_printf(dev, "Cannot enable aux clock: %d\n", error);
236 		return (error);
237 	}
238 
239 	/* Hold LTSSM in reset whilst initialising the PHYs */
240 	FUDW_MGMT_WRITE(sc, FUDW_MGMT_HOLD_PHY_RST, 1);
241 
242 	/* Deassert the aux reset */
243 	error = hwreset_deassert(sc->pcie_aux_rst);
244 	if (error != 0) {
245 		device_printf(dev, "Cannot deassert aux reset: %d\n", error);
246 		return (error);
247 	}
248 
249 	/* Enable control register interface */
250 	for (phy = 0; phy < FUDW_PHYS; ++phy)
251 		FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, SEL), 1);
252 
253 	/* Wait for enable to take effect */
254 	DELAY(1);
255 
256 	/* Initialise lane configuration */
257 	for (phy = 0; phy < FUDW_PHYS; ++phy) {
258 		for (lane = 0; lane < FUDW_LANES_PER_PHY; ++lane)
259 			fupci_phy_write(sc, phy, FUDW_MGMT_PHY_LANE(lane),
260 			    FUDW_MGMT_PHY_LANE_INIT);
261 	}
262 
263 	/* Disable the aux clock whilst taking the LTSSM out of reset */
264 	error = clk_disable(sc->pcie_aux_clk);
265 	if (error != 0) {
266 		device_printf(dev, "Cannot disable aux clock: %d\n", error);
267 		return (error);
268 	}
269 
270 	/* Take LTSSM out of reset */
271 	FUDW_MGMT_WRITE(sc, FUDW_MGMT_HOLD_PHY_RST, 0);
272 
273 	/* Enable the aux clock again */
274 	error = clk_enable(sc->pcie_aux_clk);
275 	if (error != 0) {
276 		device_printf(dev, "Cannot re-enable aux clock: %d\n", error);
277 		return (error);
278 	}
279 
280 	/* Put the controller in Root Complex mode */
281 	FUDW_MGMT_WRITE(sc, FUDW_MGMT_DEVICE_TYPE, FUDW_MGMT_DEVICE_TYPE_RC);
282 
283 	return (0);
284 }
285 
286 static void
287 fupci_dbi_protect(struct fupci_softc *sc, bool protect)
288 {
289 	uint32_t reg;
290 
291 	reg = pci_dw_dbi_rd4(sc->dev, DW_MISC_CONTROL_1);
292 	if (protect)
293 		reg &= ~DBI_RO_WR_EN;
294 	else
295 		reg |= DBI_RO_WR_EN;
296 	pci_dw_dbi_wr4(sc->dev, DW_MISC_CONTROL_1, reg);
297 }
298 
299 static int
300 fupci_init(struct fupci_softc *sc)
301 {
302 	/* Enable 32-bit I/O window */
303 	fupci_dbi_protect(sc, false);
304 	pci_dw_dbi_wr2(sc->dev, PCIR_IOBASEL_1,
305 	    (PCIM_BRIO_32 << 8) | PCIM_BRIO_32);
306 	fupci_dbi_protect(sc, true);
307 
308 	/* Enable LTSSM */
309 	FUDW_MGMT_WRITE(sc, FUDW_MGMT_LTSSM_EN, 1);
310 
311 	return (0);
312 }
313 
314 static int
315 fupci_probe(device_t dev)
316 {
317 	if (!ofw_bus_status_okay(dev))
318 		return (ENXIO);
319 
320 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
321 		return (ENXIO);
322 
323 	device_set_desc(dev, "SiFive FU740 PCIe Controller");
324 
325 	return (BUS_PROBE_DEFAULT);
326 }
327 
328 static int
329 fupci_attach(device_t dev)
330 {
331 	struct fupci_softc *sc;
332 	phandle_t node;
333 	int error, rid;
334 
335 	sc = device_get_softc(dev);
336 	node = ofw_bus_get_node(dev);
337 	sc->dev = dev;
338 
339 	rid = 0;
340 	error = ofw_bus_find_string_index(node, "reg-names", "dbi", &rid);
341 	if (error != 0) {
342 		device_printf(dev, "Cannot get DBI memory: %d\n", error);
343 		goto fail;
344 	}
345 	sc->dw_sc.dbi_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
346 	    RF_ACTIVE);
347 	if (sc->dw_sc.dbi_res == NULL) {
348 		device_printf(dev, "Cannot allocate DBI memory\n");
349 		error = ENXIO;
350 		goto fail;
351 	}
352 
353 	rid = 0;
354 	error = ofw_bus_find_string_index(node, "reg-names", "mgmt", &rid);
355 	if (error != 0) {
356 		device_printf(dev, "Cannot get management space memory: %d\n",
357 		    error);
358 		goto fail;
359 	}
360 	sc->mgmt_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
361 	    RF_ACTIVE);
362 	if (sc->mgmt_res == NULL) {
363 		device_printf(dev, "Cannot allocate management space memory\n");
364 		error = ENXIO;
365 		goto fail;
366 	}
367 
368 	error = gpio_pin_get_by_ofw_property(dev, node, "reset-gpios",
369 	    &sc->porst_pin);
370 	/* Old U-Boot device tree uses perstn-gpios */
371 	if (error == ENOENT)
372 		error = gpio_pin_get_by_ofw_property(dev, node, "perstn-gpios",
373 		    &sc->porst_pin);
374 	if (error != 0) {
375 		device_printf(dev, "Cannot get power-on reset GPIO: %d\n",
376 		    error);
377 		goto fail;
378 	}
379 	error = gpio_pin_setflags(sc->porst_pin, GPIO_PIN_OUTPUT);
380 	if (error != 0) {
381 		device_printf(dev, "Cannot configure power-on reset GPIO: %d\n",
382 		    error);
383 		goto fail;
384 	}
385 
386 	error = gpio_pin_get_by_ofw_property(dev, node, "pwren-gpios",
387 	    &sc->pwren_pin);
388 	if (error != 0) {
389 		device_printf(dev, "Cannot get power enable GPIO: %d\n",
390 		    error);
391 		goto fail;
392 	}
393 	error = gpio_pin_setflags(sc->pwren_pin, GPIO_PIN_OUTPUT);
394 	if (error != 0) {
395 		device_printf(dev, "Cannot configure power enable GPIO: %d\n",
396 		    error);
397 		goto fail;
398 	}
399 
400 	error = clk_get_by_ofw_name(dev, node, "pcie_aux", &sc->pcie_aux_clk);
401 	/* Old U-Boot device tree uses pcieaux */
402 	if (error == ENOENT)
403 		error = clk_get_by_ofw_name(dev, node, "pcieaux",
404 		    &sc->pcie_aux_clk);
405 	if (error != 0) {
406 		device_printf(dev, "Cannot get aux clock: %d\n", error);
407 		goto fail;
408 	}
409 
410 	error = hwreset_get_by_ofw_idx(dev, node, 0, &sc->pcie_aux_rst);
411 	if (error != 0) {
412 		device_printf(dev, "Cannot get aux reset: %d\n", error);
413 		goto fail;
414 	}
415 
416 	error = fupci_phy_init(sc);
417 	if (error != 0)
418 		goto fail;
419 
420 	error = pci_dw_init(dev);
421 	if (error != 0)
422 		goto fail;
423 
424 	error = fupci_init(sc);
425 	if (error != 0)
426 		goto fail;
427 
428 	return (bus_generic_attach(dev));
429 
430 fail:
431 	/* XXX Cleanup */
432 	return (error);
433 }
434 
435 static int
436 fupci_get_link(device_t dev, bool *status)
437 {
438 	uint32_t reg;
439 
440 	reg = pci_dw_dbi_rd4(dev, FUDW_DBI_PORT_DBG1);
441 	*status = (reg & FUDW_DBI_PORT_DBG1_LINK_UP) != 0 &&
442 	    (reg & FUDW_DBI_PORT_DBG1_LINK_IN_TRAINING) == 0;
443 
444 	return (0);
445 }
446 
447 static device_method_t fupci_methods[] = {
448 	/* Device interface */
449 	DEVMETHOD(device_probe,			fupci_probe),
450 	DEVMETHOD(device_attach,		fupci_attach),
451 
452 	/* PCI DW interface  */
453 	DEVMETHOD(pci_dw_get_link,		fupci_get_link),
454 
455 	DEVMETHOD_END
456 };
457 
458 DEFINE_CLASS_1(pcib, fupci_driver, fupci_methods,
459     sizeof(struct fupci_softc), pci_dw_driver);
460 DRIVER_MODULE(fu740_pci_dw, simplebus, fupci_driver, NULL, NULL);
461