]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm64/cavium/thunder_pcie_pem.c
MFV r293415:
[FreeBSD/FreeBSD.git] / sys / arm64 / cavium / thunder_pcie_pem.c
1 /*-
2  * Copyright (c) 2015 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Semihalf under
6  * the sponsorship of the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 /* PCIe external MAC root complex driver (PEM) for Cavium Thunder SOC */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/bus.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/module.h>
41 #include <sys/rman.h>
42 #include <sys/endian.h>
43
44 #include <dev/pci/pcivar.h>
45 #include <dev/pci/pcireg.h>
46
47 #include <machine/bus.h>
48 #include <machine/resource.h>
49 #include <machine/smp.h>
50 #include <machine/intr.h>
51
52 #include "thunder_pcie_common.h"
53 #include "pcib_if.h"
54
55 #define THUNDER_PEM_DEVICE_ID           0xa020
56 #define THUNDER_PEM_VENDOR_ID           0x177d
57 #define THUNDER_PEM_DESC                "ThunderX PEM"
58
59 /* ThunderX specific defines */
60 #define THUNDER_PEMn_REG_BASE(unit)     (0x87e0c0000000UL | ((unit) << 24))
61 #define PCIERC_CFG002                   0x08
62 #define PCIERC_CFG006                   0x18
63 #define PCIERC_CFG032                   0x80
64 #define PCIERC_CFG006_SEC_BUS(reg)      (((reg) >> 8) & 0xFF)
65 #define PEM_CFG_RD_REG_ALIGN(reg)       ((reg) & ~0x3)
66 #define PEM_CFG_RD_REG_DATA(val)        (((val) >> 32) & 0xFFFFFFFF)
67 #define PEM_CFG_RD                      0x30
68 #define PEM_CFG_LINK_MASK               0x3
69 #define PEM_CFG_LINK_RDY                0x3
70 #define PEM_CFG_SLIX_TO_REG(slix)       ((slix) << 4)
71 #define SBNUM_OFFSET                    0x8
72 #define SBNUM_MASK                      0xFF
73 #define PEM_ON_REG                      0x420
74 #define PEM_CTL_STATUS                  0x0
75 #define PEM_LINK_ENABLE                 (1 << 4)
76 #define PEM_LINK_DLLA                   (1 << 29)
77 #define PEM_LINK_LT                     (1 << 27)
78 #define PEM_BUS_SHIFT                   (24)
79 #define PEM_SLOT_SHIFT                  (19)
80 #define PEM_FUNC_SHIFT                  (16)
81 #define SLIX_S2M_REGX_ACC               0x874001000000UL
82 #define SLIX_S2M_REGX_ACC_SIZE          0x1000
83 #define SLIX_S2M_REGX_ACC_SPACING       0x001000000000UL
84 #define SLI_BASE                        0x880000000000UL
85 #define SLI_WINDOW_SPACING              0x004000000000UL
86 #define SLI_WINDOW_SIZE                 0x0000FF000000UL
87 #define SLI_PCI_OFFSET                  0x001000000000UL
88 #define SLI_NODE_SHIFT                  (44)
89 #define SLI_NODE_MASK                   (3)
90 #define SLI_GROUP_SHIFT                 (40)
91 #define SLI_ID_SHIFT                    (24)
92 #define SLI_ID_MASK                     (7)
93 #define SLI_PEMS_PER_GROUP              (3)
94 #define SLI_GROUPS_PER_NODE             (2)
95 #define SLI_PEMS_PER_NODE               (SLI_PEMS_PER_GROUP * SLI_GROUPS_PER_NODE)
96 #define SLI_ACC_REG_CNT                 (256)
97
98 /*
99  * Each PEM device creates its own bus with
100  * own address translation, so we can adjust bus addresses
101  * as we want. To support 32-bit cards let's assume
102  * PCI window assignment looks as following:
103  *
104  * 0x00000000 - 0x000FFFFF      IO
105  * 0x00100000 - 0xFFFFFFFF      Memory
106  */
107 #define PCI_IO_BASE             0x00000000UL
108 #define PCI_IO_SIZE             0x00100000UL
109 #define PCI_MEMORY_BASE         PCI_IO_SIZE
110 #define PCI_MEMORY_SIZE         0xFFF00000UL
111
112 struct thunder_pem_softc {
113         device_t                dev;
114         struct resource         *reg;
115         bus_space_tag_t         reg_bst;
116         bus_space_handle_t      reg_bsh;
117         struct pcie_range       ranges[MAX_RANGES_TUPLES];
118         struct rman             mem_rman;
119         struct rman             io_rman;
120         bus_space_handle_t      pem_sli_base;
121         uint32_t                node;
122         uint32_t                id;
123         uint32_t                sli;
124         uint32_t                sli_group;
125         uint64_t                sli_window_base;
126 };
127
128 static struct resource * thunder_pem_alloc_resource(device_t, device_t, int,
129     int *, u_long, u_long, u_long, u_int);
130 static int thunder_pem_attach(device_t);
131 static int thunder_pem_detach(device_t);
132 static uint64_t thunder_pem_config_reg_read(struct thunder_pem_softc *, int);
133 static int thunder_pem_link_init(struct thunder_pem_softc *);
134 static int thunder_pem_maxslots(device_t);
135 static int thunder_pem_probe(device_t);
136 static uint32_t thunder_pem_read_config(device_t, u_int, u_int, u_int, u_int,
137     int);
138 static int thunder_pem_read_ivar(device_t, device_t, int, uintptr_t *);
139 static void thunder_pem_release_all(device_t);
140 static int thunder_pem_release_resource(device_t, device_t, int, int,
141     struct resource *);
142 static void thunder_pem_slix_s2m_regx_acc_modify(struct thunder_pem_softc *,
143     int, int);
144 static void thunder_pem_write_config(device_t, u_int, u_int, u_int, u_int,
145     uint32_t, int);
146 static int thunder_pem_write_ivar(device_t, device_t, int, uintptr_t);
147
148 /* Global handlers for SLI interface */
149 static bus_space_handle_t sli0_s2m_regx_base = 0;
150 static bus_space_handle_t sli1_s2m_regx_base = 0;
151
152 static device_method_t thunder_pem_methods[] = {
153         /* Device interface */
154         DEVMETHOD(device_probe,                 thunder_pem_probe),
155         DEVMETHOD(device_attach,                thunder_pem_attach),
156         DEVMETHOD(device_detach,                thunder_pem_detach),
157         DEVMETHOD(pcib_maxslots,                thunder_pem_maxslots),
158         DEVMETHOD(pcib_read_config,             thunder_pem_read_config),
159         DEVMETHOD(pcib_write_config,            thunder_pem_write_config),
160         DEVMETHOD(bus_read_ivar,                thunder_pem_read_ivar),
161         DEVMETHOD(bus_write_ivar,               thunder_pem_write_ivar),
162         DEVMETHOD(bus_alloc_resource,           thunder_pem_alloc_resource),
163         DEVMETHOD(bus_release_resource,         thunder_pem_release_resource),
164         DEVMETHOD(bus_activate_resource,        bus_generic_activate_resource),
165         DEVMETHOD(bus_deactivate_resource,      bus_generic_deactivate_resource),
166         DEVMETHOD(bus_setup_intr,               bus_generic_setup_intr),
167         DEVMETHOD(bus_teardown_intr,            bus_generic_teardown_intr),
168
169         DEVMETHOD(pcib_map_msi,                 arm_map_msi),
170         DEVMETHOD(pcib_alloc_msix,              arm_alloc_msix),
171         DEVMETHOD(pcib_release_msix,            arm_release_msix),
172         DEVMETHOD(pcib_alloc_msi,               arm_alloc_msi),
173         DEVMETHOD(pcib_release_msi,             arm_release_msi),
174         DEVMETHOD_END
175 };
176
177 static driver_t thunder_pem_driver = {
178         "pcib",
179         thunder_pem_methods,
180         sizeof(struct thunder_pem_softc),
181 };
182
183 static int
184 thunder_pem_maxslots(device_t dev)
185 {
186
187 #if 0
188         /* max slots per bus acc. to standard */
189         return (PCI_SLOTMAX);
190 #else
191         /*
192          * ARM64TODO Workaround - otherwise an em(4) interface appears to be
193          * present on every PCI function on the bus to which it is connected
194          */
195         return (0);
196 #endif
197 }
198
199 static int
200 thunder_pem_read_ivar(device_t dev, device_t child, int index,
201     uintptr_t *result)
202 {
203         struct thunder_pem_softc *sc;
204         int secondary_bus = 0;
205
206         sc = device_get_softc(dev);
207
208         if (index == PCIB_IVAR_BUS) {
209                 secondary_bus = thunder_pem_config_reg_read(sc, PCIERC_CFG006);
210                 *result = PCIERC_CFG006_SEC_BUS(secondary_bus);
211                 return (0);
212         }
213         if (index == PCIB_IVAR_DOMAIN) {
214                 *result = sc->id;
215                 return (0);
216         }
217
218         return (ENOENT);
219 }
220
221 static int
222 thunder_pem_write_ivar(device_t dev, device_t child, int index,
223     uintptr_t value)
224 {
225
226         return (ENOENT);
227 }
228
229 static int
230 thunder_pem_identify(device_t dev)
231 {
232         struct thunder_pem_softc *sc;
233         u_long start;
234
235         sc = device_get_softc(dev);
236         start = rman_get_start(sc->reg);
237
238         /* Calculate PEM designations from its address */
239         sc->node = (start >> SLI_NODE_SHIFT) & SLI_NODE_MASK;
240         sc->id = ((start >> SLI_ID_SHIFT) & SLI_ID_MASK) +
241             (SLI_PEMS_PER_NODE * sc->node);
242         sc->sli = sc->id % SLI_PEMS_PER_GROUP;
243         sc->sli_group = (sc->id / SLI_PEMS_PER_GROUP) % SLI_GROUPS_PER_NODE;
244         sc->sli_window_base = SLI_BASE |
245             (((uint64_t)sc->node) << SLI_NODE_SHIFT) |
246             ((uint64_t)sc->sli_group << SLI_GROUP_SHIFT);
247         sc->sli_window_base += SLI_WINDOW_SPACING * sc->sli;
248
249         return (0);
250 }
251
252 static void
253 thunder_pem_slix_s2m_regx_acc_modify(struct thunder_pem_softc *sc,
254     int sli_group, int slix)
255 {
256         uint64_t regval;
257         bus_space_handle_t handle = 0;
258
259         KASSERT(slix >= 0 && slix <= SLI_ACC_REG_CNT, ("Invalid SLI index"));
260
261         if (sli_group == 0)
262                 handle = sli0_s2m_regx_base;
263         else if (sli_group == 1)
264                 handle = sli1_s2m_regx_base;
265         else
266                 device_printf(sc->dev, "SLI group is not correct\n");
267
268         if (handle) {
269                 /* Clear lower 32-bits of the SLIx register */
270                 regval = bus_space_read_8(sc->reg_bst, handle,
271                     PEM_CFG_SLIX_TO_REG(slix));
272                 regval &= ~(0xFFFFFFFFUL);
273                 bus_space_write_8(sc->reg_bst, handle,
274                     PEM_CFG_SLIX_TO_REG(slix), regval);
275         }
276 }
277
278 static int
279 thunder_pem_link_init(struct thunder_pem_softc *sc)
280 {
281         uint64_t regval;
282
283         /* check whether PEM is safe to access. */
284         regval = bus_space_read_8(sc->reg_bst, sc->reg_bsh, PEM_ON_REG);
285         if ((regval & PEM_CFG_LINK_MASK) != PEM_CFG_LINK_RDY) {
286                 device_printf(sc->dev, "PEM%d is not ON\n", sc->id);
287                 return (ENXIO);
288         }
289
290         regval = bus_space_read_8(sc->reg_bst, sc->reg_bsh, PEM_CTL_STATUS);
291         regval |= PEM_LINK_ENABLE;
292         bus_space_write_8(sc->reg_bst, sc->reg_bsh, PEM_CTL_STATUS, regval);
293
294         /* Wait 1ms as per Cavium specification */
295         DELAY(1000);
296
297         regval = thunder_pem_config_reg_read(sc, PCIERC_CFG032);
298
299         if (((regval & PEM_LINK_DLLA) == 0) || ((regval & PEM_LINK_LT) != 0)) {
300                 device_printf(sc->dev, "PCIe RC: Port %d Link Timeout\n",
301                     sc->id);
302                 return (ENXIO);
303         }
304
305         return (0);
306 }
307
308 static int
309 thunder_pem_init(struct thunder_pem_softc *sc)
310 {
311         int i, retval = 0;
312
313         retval = thunder_pem_link_init(sc);
314         if (retval) {
315                 device_printf(sc->dev, "%s failed\n", __func__);
316                 return retval;
317         }
318
319         retval = bus_space_map(sc->reg_bst, sc->sli_window_base,
320             SLI_WINDOW_SIZE, 0, &sc->pem_sli_base);
321         if (retval) {
322                 device_printf(sc->dev,
323                     "Unable to map RC%d pem_addr base address", sc->id);
324                 return (ENOMEM);
325         }
326
327         /* To support 32-bit PCIe devices, set S2M_REGx_ACC[BA]=0x0 */
328         for (i = 0; i < SLI_ACC_REG_CNT; i++) {
329                 thunder_pem_slix_s2m_regx_acc_modify(sc, sc->sli_group, i);
330         }
331
332         return (retval);
333 }
334
335 static uint64_t
336 thunder_pem_config_reg_read(struct thunder_pem_softc *sc, int reg)
337 {
338         uint64_t data;
339
340         /* Write to ADDR register */
341         bus_space_write_8(sc->reg_bst, sc->reg_bsh, PEM_CFG_RD,
342             PEM_CFG_RD_REG_ALIGN(reg));
343         bus_space_barrier(sc->reg_bst, sc->reg_bsh, PEM_CFG_RD, 8,
344             BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
345         /* Read from DATA register */
346         data = PEM_CFG_RD_REG_DATA(bus_space_read_8(sc->reg_bst, sc->reg_bsh,
347             PEM_CFG_RD));
348
349         return (data);
350 }
351
352 static uint32_t
353 thunder_pem_read_config(device_t dev, u_int bus, u_int slot,
354     u_int func, u_int reg, int bytes)
355 {
356         uint64_t offset;
357         uint32_t data;
358         struct thunder_pem_softc *sc;
359         bus_space_tag_t t;
360         bus_space_handle_t h;
361
362         if ((bus > PCI_BUSMAX) || (slot > PCI_SLOTMAX) ||
363             (func > PCI_FUNCMAX) || (reg > PCIE_REGMAX))
364                 return (~0U);
365
366         sc = device_get_softc(dev);
367
368         /* Calculate offset */
369         offset = (bus << PEM_BUS_SHIFT) | (slot << PEM_SLOT_SHIFT) |
370             (func << PEM_FUNC_SHIFT) | reg;
371         t = sc->reg_bst;
372         h = sc->pem_sli_base;
373
374         switch (bytes) {
375         case 1:
376                 data = bus_space_read_1(t, h, offset);
377                 break;
378         case 2:
379                 data = le16toh(bus_space_read_2(t, h, offset));
380                 break;
381         case 4:
382                 data = le32toh(bus_space_read_4(t, h, offset));
383                 break;
384         default:
385                 return (~0U);
386         }
387
388         return (data);
389 }
390
391 static void
392 thunder_pem_write_config(device_t dev, u_int bus, u_int slot,
393     u_int func, u_int reg, uint32_t val, int bytes)
394 {
395         uint64_t offset;
396         struct thunder_pem_softc *sc;
397         bus_space_tag_t t;
398         bus_space_handle_t h;
399
400         if ((bus > PCI_BUSMAX) || (slot > PCI_SLOTMAX) ||
401             (func > PCI_FUNCMAX) || (reg > PCIE_REGMAX))
402                 return;
403
404         sc = device_get_softc(dev);
405
406         /* Calculate offset */
407         offset = (bus << PEM_BUS_SHIFT) | (slot << PEM_SLOT_SHIFT) |
408             (func << PEM_FUNC_SHIFT) | reg;
409         t = sc->reg_bst;
410         h = sc->pem_sli_base;
411
412         switch (bytes) {
413         case 1:
414                 bus_space_write_1(t, h, offset, val);
415                 break;
416         case 2:
417                 bus_space_write_2(t, h, offset, htole16(val));
418                 break;
419         case 4:
420                 bus_space_write_4(t, h, offset, htole32(val));
421                 break;
422         default:
423                 return;
424         }
425 }
426
427 static struct resource *
428 thunder_pem_alloc_resource(device_t dev, device_t child, int type, int *rid,
429     u_long start, u_long end, u_long count, u_int flags)
430 {
431         struct thunder_pem_softc *sc = device_get_softc(dev);
432         struct rman *rm = NULL;
433         struct resource *res;
434         device_t parent_dev;
435
436         switch (type) {
437         case SYS_RES_IOPORT:
438                 rm = &sc->io_rman;
439                 break;
440         case SYS_RES_MEMORY:
441                 rm = &sc->mem_rman;
442                 break;
443         default:
444                 /* Find parent device. On ThunderX we know an exact path. */
445                 parent_dev = device_get_parent(device_get_parent(dev));
446                 return (BUS_ALLOC_RESOURCE(parent_dev, dev, type, rid, start,
447                     end, count, flags));
448         };
449
450         if ((start == 0UL) && (end == ~0UL)) {
451                 device_printf(dev,
452                     "Cannot allocate resource with unspecified range\n");
453                 goto fail;
454         }
455
456         /* Translate PCI address to host PHYS */
457         if (range_addr_is_pci(sc->ranges, start, count) == 0)
458                 goto fail;
459         start = range_addr_pci_to_phys(sc->ranges, start);
460         end = start + count - 1;
461
462         if (bootverbose) {
463                 device_printf(dev,
464                     "rman_reserve_resource: start=%#lx, end=%#lx, count=%#lx\n",
465                     start, end, count);
466         }
467
468         res = rman_reserve_resource(rm, start, end, count, flags, child);
469         if (res == NULL)
470                 goto fail;
471
472         rman_set_rid(res, *rid);
473
474         if (flags & RF_ACTIVE)
475                 if (bus_activate_resource(child, type, *rid, res)) {
476                         rman_release_resource(res);
477                         goto fail;
478                 }
479
480         return (res);
481
482 fail:
483         if (bootverbose) {
484                 device_printf(dev, "%s FAIL: type=%d, rid=%d, "
485                     "start=%016lx, end=%016lx, count=%016lx, flags=%x\n",
486                     __func__, type, *rid, start, end, count, flags);
487         }
488
489         return (NULL);
490 }
491
492 static int
493 thunder_pem_release_resource(device_t dev, device_t child, int type, int rid,
494     struct resource *res)
495 {
496         device_t parent_dev;
497
498         /* Find parent device. On ThunderX we know an exact path. */
499         parent_dev = device_get_parent(device_get_parent(dev));
500
501         if ((type != SYS_RES_MEMORY) && (type != SYS_RES_IOPORT))
502                 return (BUS_RELEASE_RESOURCE(parent_dev, child,
503                     type, rid, res));
504
505         return (rman_release_resource(res));
506 }
507
508 static int
509 thunder_pem_probe(device_t dev)
510 {
511         uint16_t pci_vendor_id;
512         uint16_t pci_device_id;
513
514         pci_vendor_id = pci_get_vendor(dev);
515         pci_device_id = pci_get_device(dev);
516
517         if ((pci_vendor_id == THUNDER_PEM_VENDOR_ID) &&
518             (pci_device_id == THUNDER_PEM_DEVICE_ID)) {
519                 device_set_desc_copy(dev, THUNDER_PEM_DESC);
520                 return (0);
521         }
522
523         return (ENXIO);
524 }
525
526 static int
527 thunder_pem_attach(device_t dev)
528 {
529         struct thunder_pem_softc *sc;
530         int error;
531         int rid;
532
533         sc = device_get_softc(dev);
534         sc->dev = dev;
535
536         /* Allocate memory for BAR(0) */
537         rid = PCIR_BAR(0);
538         sc->reg = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
539             &rid, RF_ACTIVE);
540         if (sc->reg == NULL) {
541                 device_printf(dev, "Failed to allocate resource\n");
542                 return (ENXIO);
543         }
544         sc->reg_bst = rman_get_bustag(sc->reg);
545         sc->reg_bsh = rman_get_bushandle(sc->reg);
546
547         /* Map SLI, do it only once */
548         if (!sli0_s2m_regx_base) {
549                 bus_space_map(sc->reg_bst, SLIX_S2M_REGX_ACC,
550                     SLIX_S2M_REGX_ACC_SIZE, 0, &sli0_s2m_regx_base);
551         }
552         if (!sli1_s2m_regx_base) {
553                 bus_space_map(sc->reg_bst, SLIX_S2M_REGX_ACC +
554                     SLIX_S2M_REGX_ACC_SPACING, SLIX_S2M_REGX_ACC_SIZE, 0,
555                     &sli1_s2m_regx_base);
556         }
557
558         if ((sli0_s2m_regx_base == 0) || (sli1_s2m_regx_base == 0)) {
559                 device_printf(dev,
560                     "bus_space_map failed to map slix_s2m_regx_base\n");
561                 goto fail;
562         }
563
564         /* Identify PEM */
565         if (thunder_pem_identify(dev) != 0)
566                 goto fail;
567
568         /* Initialize rman and allocate regions */
569         sc->mem_rman.rm_type = RMAN_ARRAY;
570         sc->mem_rman.rm_descr = "PEM PCIe Memory";
571         error = rman_init(&sc->mem_rman);
572         if (error != 0) {
573                 device_printf(dev, "memory rman_init() failed. error = %d\n",
574                     error);
575                 goto fail;
576         }
577         sc->io_rman.rm_type = RMAN_ARRAY;
578         sc->io_rman.rm_descr = "PEM PCIe IO";
579         error = rman_init(&sc->io_rman);
580         if (error != 0) {
581                 device_printf(dev, "IO rman_init() failed. error = %d\n",
582                     error);
583                 goto fail_mem;
584         }
585
586         /* Fill memory window */
587         sc->ranges[0].pci_base = PCI_MEMORY_BASE;
588         sc->ranges[0].size = PCI_MEMORY_SIZE;
589         sc->ranges[0].phys_base = sc->sli_window_base + SLI_PCI_OFFSET +
590             sc->ranges[0].pci_base;
591         rman_manage_region(&sc->mem_rman, sc->ranges[0].phys_base,
592             sc->ranges[0].phys_base + sc->ranges[0].size - 1);
593
594         /* Fill IO window */
595         sc->ranges[1].pci_base = PCI_IO_BASE;
596         sc->ranges[1].size = PCI_IO_SIZE;
597         sc->ranges[1].phys_base = sc->sli_window_base + SLI_PCI_OFFSET +
598             sc->ranges[1].pci_base;
599         rman_manage_region(&sc->io_rman, sc->ranges[1].phys_base,
600             sc->ranges[1].phys_base + sc->ranges[1].size - 1);
601
602         if (thunder_pem_init(sc)) {
603                 device_printf(dev, "Failure during PEM init\n");
604                 goto fail_io;
605         }
606
607         device_add_child(dev, "pci", -1);
608
609         return (bus_generic_attach(dev));
610
611 fail_io:
612         rman_fini(&sc->io_rman);
613 fail_mem:
614         rman_fini(&sc->mem_rman);
615 fail:
616         bus_free_resource(dev, SYS_RES_MEMORY, sc->reg);
617         return (ENXIO);
618 }
619
620 static void
621 thunder_pem_release_all(device_t dev)
622 {
623         struct thunder_pem_softc *sc;
624
625         sc = device_get_softc(dev);
626
627         rman_fini(&sc->io_rman);
628         rman_fini(&sc->mem_rman);
629
630         if (sc->reg != NULL)
631                 bus_free_resource(dev, SYS_RES_MEMORY, sc->reg);
632 }
633
634 static int
635 thunder_pem_detach(device_t dev)
636 {
637
638         thunder_pem_release_all(dev);
639
640         return (0);
641 }
642
643 static devclass_t thunder_pem_devclass;
644
645 DRIVER_MODULE(thunder_pem, pci, thunder_pem_driver, thunder_pem_devclass, 0, 0);
646 MODULE_DEPEND(thunder_pem, pci, 1, 1, 1);