Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/riscv/sifive/fu740_pci_dw.c
39482 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright 2021 Jessica Clarke <[email protected]>
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
/* Deassert core power-on reset (active low) */
219
error = gpio_pin_set_active(sc->porst_pin, true);
220
if (error != 0) {
221
device_printf(dev, "Cannot deassert power-on reset: %d\n",
222
error);
223
return (error);
224
}
225
226
/* Enable the aux clock */
227
error = clk_enable(sc->pcie_aux_clk);
228
if (error != 0) {
229
device_printf(dev, "Cannot enable aux clock: %d\n", error);
230
return (error);
231
}
232
233
/* Hold LTSSM in reset whilst initialising the PHYs */
234
FUDW_MGMT_WRITE(sc, FUDW_MGMT_HOLD_PHY_RST, 1);
235
236
/* Deassert the aux reset */
237
error = hwreset_deassert(sc->pcie_aux_rst);
238
if (error != 0) {
239
device_printf(dev, "Cannot deassert aux reset: %d\n", error);
240
return (error);
241
}
242
243
/* Enable control register interface */
244
for (phy = 0; phy < FUDW_PHYS; ++phy)
245
FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, SEL), 1);
246
247
/* Wait for enable to take effect */
248
DELAY(1);
249
250
/* Initialise lane configuration */
251
for (phy = 0; phy < FUDW_PHYS; ++phy) {
252
for (lane = 0; lane < FUDW_LANES_PER_PHY; ++lane)
253
fupci_phy_write(sc, phy, FUDW_MGMT_PHY_LANE(lane),
254
FUDW_MGMT_PHY_LANE_INIT);
255
}
256
257
/* Disable the aux clock whilst taking the LTSSM out of reset */
258
error = clk_disable(sc->pcie_aux_clk);
259
if (error != 0) {
260
device_printf(dev, "Cannot disable aux clock: %d\n", error);
261
return (error);
262
}
263
264
/* Take LTSSM out of reset */
265
FUDW_MGMT_WRITE(sc, FUDW_MGMT_HOLD_PHY_RST, 0);
266
267
/* Enable the aux clock again */
268
error = clk_enable(sc->pcie_aux_clk);
269
if (error != 0) {
270
device_printf(dev, "Cannot re-enable aux clock: %d\n", error);
271
return (error);
272
}
273
274
/* Put the controller in Root Complex mode */
275
FUDW_MGMT_WRITE(sc, FUDW_MGMT_DEVICE_TYPE, FUDW_MGMT_DEVICE_TYPE_RC);
276
277
/* Hold PERST for 100ms as per the PCIe spec */
278
DELAY(100000);
279
280
/* Deassert PERST_N */
281
FUDW_MGMT_WRITE(sc, FUDW_MGMT_PERST_N, 1);
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
bus_attach_children(dev);
429
return (0);
430
431
fail:
432
/* XXX Cleanup */
433
return (error);
434
}
435
436
static int
437
fupci_get_link(device_t dev, bool *status)
438
{
439
uint32_t reg;
440
441
reg = pci_dw_dbi_rd4(dev, FUDW_DBI_PORT_DBG1);
442
*status = (reg & FUDW_DBI_PORT_DBG1_LINK_UP) != 0 &&
443
(reg & FUDW_DBI_PORT_DBG1_LINK_IN_TRAINING) == 0;
444
445
return (0);
446
}
447
448
static device_method_t fupci_methods[] = {
449
/* Device interface */
450
DEVMETHOD(device_probe, fupci_probe),
451
DEVMETHOD(device_attach, fupci_attach),
452
453
/* PCI DW interface */
454
DEVMETHOD(pci_dw_get_link, fupci_get_link),
455
456
DEVMETHOD_END
457
};
458
459
DEFINE_CLASS_1(pcib, fupci_driver, fupci_methods,
460
sizeof(struct fupci_softc), pci_dw_driver);
461
DRIVER_MODULE(fu740_pci_dw, simplebus, fupci_driver, NULL, NULL);
462
463