1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer,
12 * without modification.
13 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
14 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
15 * redistribution must be conditioned upon including a substantially
16 * similar Disclaimer requirement for further binary redistribution.
17 *
18 * NO WARRANTY
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
22 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
24 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
27 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29 * THE POSSIBILITY OF SUCH DAMAGES.
30 */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 /*
36 * Broadcom Common PCI/PCIe Support.
37 *
38 * This base driver implementation is shared by the bhnd_pcib (root complex)
39 * and bhnd_pci_hostb (host bridge) drivers.
40 */
41
42 #include <sys/param.h>
43 #include <sys/malloc.h>
44 #include <sys/kernel.h>
45 #include <sys/bus.h>
46 #include <sys/module.h>
47 #include <sys/systm.h>
48
49 #include <machine/bus.h>
50 #include <sys/rman.h>
51 #include <machine/resource.h>
52
53 #include <dev/bhnd/bhnd.h>
54 #include <dev/mdio/mdio.h>
55
56 #include "bhnd_pcireg.h"
57 #include "bhnd_pcivar.h"
58
59 static int bhnd_pcie_mdio_wait_idle(struct bhnd_pci_softc *sc);
60 static int bhnd_pcie_mdio_ioctl(struct bhnd_pci_softc *sc, uint32_t cmd);
61 static int bhnd_pcie_mdio_enable(struct bhnd_pci_softc *sc);
62 static void bhnd_pcie_mdio_disable(struct bhnd_pci_softc *sc);
63 static int bhnd_pcie_mdio_cmd_write(struct bhnd_pci_softc *sc,
64 uint32_t cmd);
65 static int bhnd_pcie_mdio_cmd_read(struct bhnd_pci_softc *sc, uint32_t cmd,
66 uint16_t *data_read);
67
68 static struct bhnd_device_quirk bhnd_pci_quirks[];
69 static struct bhnd_device_quirk bhnd_pcie_quirks[];
70
71 #define BHND_PCI_QUIRKS bhnd_pci_quirks
72 #define BHND_PCIE_QUIRKS bhnd_pcie_quirks
73 #define BHND_PCI_DEV(_core, _desc, ...) \
74 { BHND_DEVICE(BCM, _core, _desc, BHND_ ## _core ## _QUIRKS, \
75 ## __VA_ARGS__), BHND_PCI_REGFMT_ ## _core }
76
77 static const struct bhnd_pci_device {
78 struct bhnd_device device;
79 bhnd_pci_regfmt_t regfmt; /**< register format */
80 } bhnd_pci_devs[] = {
81 BHND_PCI_DEV(PCI, "Host-PCI bridge", BHND_DF_HOSTB),
82 BHND_PCI_DEV(PCI, "PCI-BHND bridge", BHND_DF_SOC),
83 BHND_PCI_DEV(PCIE, "PCIe-G1 Host-PCI bridge", BHND_DF_HOSTB),
84 BHND_PCI_DEV(PCIE, "PCIe-G1 PCI-BHND bridge", BHND_DF_SOC),
85 { BHND_DEVICE_END, 0 }
86 };
87
88 /* Device quirks tables */
89 static struct bhnd_device_quirk bhnd_pci_quirks[] = { BHND_DEVICE_QUIRK_END };
90 static struct bhnd_device_quirk bhnd_pcie_quirks[] = {
91 BHND_CORE_QUIRK(HWREV_GTE(10), BHND_PCI_QUIRK_SD_C22_EXTADDR),
92
93 BHND_DEVICE_QUIRK_END
94 };
95
96 #define BHND_PCIE_MDIO_CTL_DELAY 10 /**< usec delay required between
97 * MDIO_CTL/MDIO_DATA accesses. */
98 #define BHND_PCIE_MDIO_RETRY_DELAY 2000 /**< usec delay before retrying
99 * BHND_PCIE_MDIOCTL_DONE. */
100 #define BHND_PCIE_MDIO_RETRY_COUNT 200 /**< number of times to loop waiting
101 * for BHND_PCIE_MDIOCTL_DONE. */
102
103 #define BHND_PCI_READ_4(_sc, _reg) \
104 bhnd_bus_read_4((_sc)->mem_res, (_reg))
105 #define BHND_PCI_WRITE_4(_sc, _reg, _val) \
106 bhnd_bus_write_4((_sc)->mem_res, (_reg), (_val))
107
108 #define BHND_PCIE_ASSERT(sc) \
109 KASSERT(bhnd_get_class(sc->dev) == BHND_DEVCLASS_PCIE, \
110 ("not a pcie device!"));
111
112 int
113 bhnd_pci_generic_probe(device_t dev)
114 {
115 const struct bhnd_device *id;
116
117 id = bhnd_device_lookup(dev, &bhnd_pci_devs[0].device,
118 sizeof(bhnd_pci_devs[0]));
119 if (id == NULL)
120 return (ENXIO);
121
122 bhnd_set_custom_core_desc(dev, id->desc);
123 return (BUS_PROBE_DEFAULT);
124 }
125
126 int
127 bhnd_pci_generic_attach(device_t dev)
128 {
129 struct bhnd_pci_softc *sc;
130 int error;
131
132 sc = device_get_softc(dev);
133 sc->dev = dev;
134 sc->quirks = bhnd_device_quirks(dev, &bhnd_pci_devs[0].device,
135 sizeof(bhnd_pci_devs[0]));
136
137 /* Allocate bus resources */
138 sc->mem_res = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
139 RF_ACTIVE);
140 if (sc->mem_res == NULL)
141 return (ENXIO);
142
143 BHND_PCI_LOCK_INIT(sc);
144
145 /* Probe and attach children */
146 if ((error = bus_generic_attach(dev)))
147 goto cleanup;
148
149 return (0);
150
151 cleanup:
152 bhnd_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res);
153 BHND_PCI_LOCK_DESTROY(sc);
154
155 return (error);
156 }
157
158 int
159 bhnd_pci_generic_detach(device_t dev)
160 {
161 struct bhnd_pci_softc *sc;
162 int error;
163
164 sc = device_get_softc(dev);
165
166 if ((error = bus_generic_detach(dev)))
167 return (error);
168
169 bhnd_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res);
170
171 BHND_PCI_LOCK_DESTROY(sc);
172
173 return (0);
174 }
175
176 static struct resource_list *
177 bhnd_pci_get_resource_list(device_t dev, device_t child)
178 {
179 struct bhnd_pci_devinfo *dinfo;
180
181 if (device_get_parent(child) != dev)
182 return (NULL);
183
184 dinfo = device_get_ivars(child);
185 return (&dinfo->resources);
186 }
187
188 static device_t
189 bhnd_pci_add_child(device_t dev, u_int order, const char *name, int unit)
190 {
191 struct bhnd_pci_devinfo *dinfo;
192 device_t child;
193
194 child = device_add_child_ordered(dev, order, name, unit);
195 if (child == NULL)
196 return (NULL);
197
198 dinfo = malloc(sizeof(struct bhnd_pci_devinfo), M_DEVBUF, M_NOWAIT);
199 if (dinfo == NULL) {
200 device_delete_child(dev, child);
201 return (NULL);
202 }
203
204 resource_list_init(&dinfo->resources);
205
206 device_set_ivars(child, dinfo);
207 return (child);
208 }
209
210 static void
211 bhnd_pci_child_deleted(device_t dev, device_t child)
212 {
213 struct bhnd_pci_devinfo *dinfo;
214
215 if (device_get_parent(child) != dev)
216 return;
217
218 dinfo = device_get_ivars(child);
219 if (dinfo != NULL) {
220 resource_list_free(&dinfo->resources);
221 free(dinfo, M_DEVBUF);
222 }
223
224 device_set_ivars(child, NULL);
225 }
226
227 int
228 bhnd_pci_generic_suspend(device_t dev)
229 {
230 return (bus_generic_suspend(dev));
231 }
232
233 int
234 bhnd_pci_generic_resume(device_t dev)
235 {
236 return (bus_generic_resume(dev));
237 }
238
239 /**
240 * Read a 32-bit PCIe TLP/DLLP/PLP protocol register.
241 *
242 * @param sc The bhndb_pci driver state.
243 * @param addr The protocol register offset.
244 */
245 uint32_t
246 bhnd_pcie_read_proto_reg(struct bhnd_pci_softc *sc, uint32_t addr)
247 {
248 uint32_t val;
249
250 BHND_PCIE_ASSERT(sc);
251
252 BHND_PCI_LOCK(sc);
253 BHND_PCI_WRITE_4(sc, BHND_PCIE_IND_ADDR, addr);
254 val = BHND_PCI_READ_4(sc, BHND_PCIE_IND_DATA);
255 BHND_PCI_UNLOCK(sc);
256
257 return (val);
258 }
259
260 /**
261 * Write a 32-bit PCIe TLP/DLLP/PLP protocol register value.
262 *
263 * @param sc The bhndb_pci driver state.
264 * @param addr The protocol register offset.
265 * @param val The value to write to @p addr.
266 */
267 void
268 bhnd_pcie_write_proto_reg(struct bhnd_pci_softc *sc, uint32_t addr,
269 uint32_t val)
270 {
271 BHND_PCIE_ASSERT(sc);
272
273 BHND_PCI_LOCK(sc);
274 BHND_PCI_WRITE_4(sc, BHND_PCIE_IND_ADDR, addr);
275 BHND_PCI_WRITE_4(sc, BHND_PCIE_IND_DATA, val);
276 BHND_PCI_UNLOCK(sc);
277 }
278
279 /* Spin until the MDIO device reports itself as idle, or timeout is reached. */
280 static int
281 bhnd_pcie_mdio_wait_idle(struct bhnd_pci_softc *sc)
282 {
283 uint32_t ctl;
284
285 /* Spin waiting for the BUSY flag to clear */
286 for (int i = 0; i < BHND_PCIE_MDIO_RETRY_COUNT; i++) {
287 ctl = BHND_PCI_READ_4(sc, BHND_PCIE_MDIO_CTL);
288 if ((ctl & BHND_PCIE_MDIOCTL_DONE))
289 return (0);
290
291 DELAY(BHND_PCIE_MDIO_RETRY_DELAY);
292 }
293
294 return (ETIMEDOUT);
295 }
296
297 /**
298 * Write an MDIO IOCTL and wait for completion.
299 */
300 static int
301 bhnd_pcie_mdio_ioctl(struct bhnd_pci_softc *sc, uint32_t cmd)
302 {
303 BHND_PCI_LOCK_ASSERT(sc, MA_OWNED);
304
305 BHND_PCI_WRITE_4(sc, BHND_PCIE_MDIO_CTL, cmd);
306 DELAY(BHND_PCIE_MDIO_CTL_DELAY);
307 return (0);
308 }
309
310 /**
311 * Enable MDIO device
312 */
313 static int
314 bhnd_pcie_mdio_enable(struct bhnd_pci_softc *sc)
315 {
316 uint32_t ctl;
317
318 BHND_PCIE_ASSERT(sc);
319
320 /* Enable MDIO clock and preamble mode */
321 ctl = BHND_PCIE_MDIOCTL_PREAM_EN|BHND_PCIE_MDIOCTL_DIVISOR_VAL;
322 return (bhnd_pcie_mdio_ioctl(sc, ctl));
323 }
324
325 /**
326 * Disable MDIO device.
327 */
328 static void
329 bhnd_pcie_mdio_disable(struct bhnd_pci_softc *sc)
330 {
331 if (bhnd_pcie_mdio_ioctl(sc, 0))
332 device_printf(sc->dev, "failed to disable MDIO clock\n");
333 }
334
335 /**
336 * Issue a write command and wait for completion
337 */
338 static int
339 bhnd_pcie_mdio_cmd_write(struct bhnd_pci_softc *sc, uint32_t cmd)
340 {
341 int error;
342
343 BHND_PCI_LOCK_ASSERT(sc, MA_OWNED);
344
345 cmd |= BHND_PCIE_MDIODATA_START|BHND_PCIE_MDIODATA_TA|BHND_PCIE_MDIODATA_CMD_WRITE;
346
347 BHND_PCI_WRITE_4(sc, BHND_PCIE_MDIO_DATA, cmd);
348 DELAY(BHND_PCIE_MDIO_CTL_DELAY);
349
350 if ((error = bhnd_pcie_mdio_wait_idle(sc)))
351 return (error);
352
353 return (0);
354 }
355
356 /**
357 * Issue an MDIO read command, wait for completion, and return
358 * the result in @p data_read.
359 */
360 static int
361 bhnd_pcie_mdio_cmd_read(struct bhnd_pci_softc *sc, uint32_t cmd,
362 uint16_t *data_read)
363 {
364 int error;
365
366 BHND_PCI_LOCK_ASSERT(sc, MA_OWNED);
367
368 cmd |= BHND_PCIE_MDIODATA_START|BHND_PCIE_MDIODATA_TA|BHND_PCIE_MDIODATA_CMD_READ;
369 BHND_PCI_WRITE_4(sc, BHND_PCIE_MDIO_DATA, cmd);
370 DELAY(BHND_PCIE_MDIO_CTL_DELAY);
371
372 if ((error = bhnd_pcie_mdio_wait_idle(sc)))
373 return (error);
374
375 *data_read = (BHND_PCI_READ_4(sc, BHND_PCIE_MDIO_DATA) &
376 BHND_PCIE_MDIODATA_DATA_MASK);
377 return (0);
378 }
379
380 int
381 bhnd_pcie_mdio_read(struct bhnd_pci_softc *sc, int phy, int reg)
382 {
383 uint32_t cmd;
384 uint16_t val;
385 int error;
386
387 /* Enable MDIO access */
388 BHND_PCI_LOCK(sc);
389 bhnd_pcie_mdio_enable(sc);
390
391 /* Issue the read */
392 cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg);
393 error = bhnd_pcie_mdio_cmd_read(sc, cmd, &val);
394
395 /* Disable MDIO access */
396 bhnd_pcie_mdio_disable(sc);
397 BHND_PCI_UNLOCK(sc);
398
399 if (error)
400 return (~0U);
401
402 return (val);
403 }
404
405 int
406 bhnd_pcie_mdio_write(struct bhnd_pci_softc *sc, int phy, int reg, int val)
407 {
408 uint32_t cmd;
409 int error;
410
411 /* Enable MDIO access */
412 BHND_PCI_LOCK(sc);
413 bhnd_pcie_mdio_enable(sc);
414
415 /* Issue the write */
416 cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg) | (val & BHND_PCIE_MDIODATA_DATA_MASK);
417 error = bhnd_pcie_mdio_cmd_write(sc, cmd);
418
419 /* Disable MDIO access */
420 bhnd_pcie_mdio_disable(sc);
421 BHND_PCI_UNLOCK(sc);
422
423 return (error);
424 }
425
426 int
427 bhnd_pcie_mdio_read_ext(struct bhnd_pci_softc *sc, int phy, int devaddr,
428 int reg)
429 {
430 uint32_t cmd;
431 uint16_t val;
432 int error;
433
434 if (devaddr == MDIO_DEVADDR_NONE)
435 return (bhnd_pcie_mdio_read(sc, phy, reg));
436
437 /* Extended register access is only supported for the SerDes device,
438 * using the non-standard C22 extended address mechanism */
439 if (!(sc->quirks & BHND_PCI_QUIRK_SD_C22_EXTADDR) ||
440 phy != BHND_PCIE_PHYADDR_SD)
441 {
442 return (~0U);
443 }
444
445 /* Enable MDIO access */
446 BHND_PCI_LOCK(sc);
447 bhnd_pcie_mdio_enable(sc);
448
449 /* Write the block address to the address extension register */
450 cmd = BHND_PCIE_MDIODATA_ADDR(phy, BHND_PCIE_SD_ADDREXT) | devaddr;
451 if ((error = bhnd_pcie_mdio_cmd_write(sc, cmd)))
452 goto cleanup;
453
454 /* Issue the read */
455 cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg);
456 error = bhnd_pcie_mdio_cmd_read(sc, cmd, &val);
457
458 cleanup:
459 bhnd_pcie_mdio_disable(sc);
460 BHND_PCI_UNLOCK(sc);
461
462 if (error)
463 return (~0U);
464
465 return (val);
466 }
467
468 int
469 bhnd_pcie_mdio_write_ext(struct bhnd_pci_softc *sc, int phy, int devaddr,
470 int reg, int val)
471 {
472 uint32_t cmd;
473 int error;
474
475 if (devaddr == MDIO_DEVADDR_NONE)
476 return (bhnd_pcie_mdio_write(sc, phy, reg, val));
477
478 /* Extended register access is only supported for the SerDes device,
479 * using the non-standard C22 extended address mechanism */
480 if (!(sc->quirks & BHND_PCI_QUIRK_SD_C22_EXTADDR) ||
481 phy != BHND_PCIE_PHYADDR_SD)
482 {
483 return (~0U);
484 }
485
486 /* Enable MDIO access */
487 BHND_PCI_LOCK(sc);
488 bhnd_pcie_mdio_enable(sc);
489
490 /* Write the block address to the address extension register */
491 cmd = BHND_PCIE_MDIODATA_ADDR(phy, BHND_PCIE_SD_ADDREXT) | devaddr;
492 if ((error = bhnd_pcie_mdio_cmd_write(sc, cmd)))
493 goto cleanup;
494
495 /* Issue the write */
496 cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg) |
497 (val & BHND_PCIE_MDIODATA_DATA_MASK);
498 error = bhnd_pcie_mdio_cmd_write(sc, cmd);
499
500 cleanup:
501 bhnd_pcie_mdio_disable(sc);
502 BHND_PCI_UNLOCK(sc);
503
504 return (error);
505 }
506
507 static device_method_t bhnd_pci_methods[] = {
508 /* Device interface */
509 DEVMETHOD(device_probe, bhnd_pci_generic_probe),
510 DEVMETHOD(device_attach, bhnd_pci_generic_attach),
511 DEVMETHOD(device_detach, bhnd_pci_generic_detach),
512 DEVMETHOD(device_suspend, bhnd_pci_generic_suspend),
513 DEVMETHOD(device_resume, bhnd_pci_generic_resume),
514
515 /* Bus interface */
516 DEVMETHOD(bus_add_child, bhnd_pci_add_child),
517 DEVMETHOD(bus_child_deleted, bhnd_pci_child_deleted),
518 DEVMETHOD(bus_print_child, bus_generic_print_child),
519 DEVMETHOD(bus_get_resource_list, bhnd_pci_get_resource_list),
520 DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
521 DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource),
522 DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource),
523
524 DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource),
525 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
526 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
527 DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource),
528 DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource),
529
530 DEVMETHOD_END
531 };
532
533 DEFINE_CLASS_0(bhnd_pci, bhnd_pci_driver, bhnd_pci_methods, sizeof(struct bhnd_pci_softc));
534 MODULE_DEPEND(bhnd_pci, bhnd, 1, 1, 1);
535 MODULE_DEPEND(bhnd_pci, pci, 1, 1, 1);
536 MODULE_VERSION(bhnd_pci, 1);
Cache object: f816d8506495a4a1dee3134bf0bbca42
|