]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm64/cavium/thunder_pcie_pem.c
Since contrib/libc++'s ancestry was never correct, subversion 1.8 and
[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         DEVMETHOD(pcib_map_msi,                 thunder_common_map_msi),
169         DEVMETHOD(pcib_alloc_msix,              thunder_common_alloc_msix),
170         DEVMETHOD(pcib_release_msix,            thunder_common_release_msix),
171         DEVMETHOD(pcib_alloc_msi,               thunder_common_alloc_msi),
172         DEVMETHOD(pcib_release_msi,             thunder_common_release_msi),
173         DEVMETHOD_END
174 };
175
176 static driver_t thunder_pem_driver = {
177         "pcib",
178         thunder_pem_methods,
179         sizeof(struct thunder_pem_softc),
180 };
181
182 static int
183 thunder_pem_maxslots(device_t dev)
184 {
185
186         /* max slots per bus acc. to standard */
187         return (PCI_SLOTMAX);
188 }
189
190 static int
191 thunder_pem_read_ivar(device_t dev, device_t child, int index,
192     uintptr_t *result)
193 {
194         struct thunder_pem_softc *sc;
195         int secondary_bus = 0;
196
197         sc = device_get_softc(dev);
198
199         if (index == PCIB_IVAR_BUS) {
200                 secondary_bus = thunder_pem_config_reg_read(sc, PCIERC_CFG006);
201                 *result = PCIERC_CFG006_SEC_BUS(secondary_bus);
202                 return (0);
203         }
204         if (index == PCIB_IVAR_DOMAIN) {
205                 *result = sc->id;
206                 return (0);
207         }
208
209         return (ENOENT);
210 }
211
212 static int
213 thunder_pem_write_ivar(device_t dev, device_t child, int index,
214     uintptr_t value)
215 {
216
217         return (ENOENT);
218 }
219
220 static int
221 thunder_pem_identify(device_t dev)
222 {
223         struct thunder_pem_softc *sc;
224         u_long start;
225
226         sc = device_get_softc(dev);
227         start = rman_get_start(sc->reg);
228
229         /* Calculate PEM designations from its address */
230         sc->node = (start >> SLI_NODE_SHIFT) & SLI_NODE_MASK;
231         sc->id = ((start >> SLI_ID_SHIFT) & SLI_ID_MASK) +
232             (SLI_PEMS_PER_NODE * sc->node);
233         sc->sli = sc->id % SLI_PEMS_PER_GROUP;
234         sc->sli_group = (sc->id / SLI_PEMS_PER_GROUP) % SLI_GROUPS_PER_NODE;
235         sc->sli_window_base = SLI_BASE |
236             (((uint64_t)sc->node) << SLI_NODE_SHIFT) |
237             ((uint64_t)sc->sli_group << SLI_GROUP_SHIFT);
238         sc->sli_window_base += SLI_WINDOW_SPACING * sc->sli;
239
240         return (0);
241 }
242
243 static void
244 thunder_pem_slix_s2m_regx_acc_modify(struct thunder_pem_softc *sc,
245     int sli_group, int slix)
246 {
247         uint64_t regval;
248         bus_space_handle_t handle = 0;
249
250         KASSERT(slix >= 0 && slix <= SLI_ACC_REG_CNT, ("Invalid SLI index"));
251
252         if (sli_group == 0)
253                 handle = sli0_s2m_regx_base;
254         else if (sli_group == 1)
255                 handle = sli1_s2m_regx_base;
256         else
257                 device_printf(sc->dev, "SLI group is not correct\n");
258
259         if (handle) {
260                 /* Clear lower 32-bits of the SLIx register */
261                 regval = bus_space_read_8(sc->reg_bst, handle,
262                     PEM_CFG_SLIX_TO_REG(slix));
263                 regval &= ~(0xFFFFFFFFUL);
264                 bus_space_write_8(sc->reg_bst, handle,
265                     PEM_CFG_SLIX_TO_REG(slix), regval);
266         }
267 }
268
269 static int
270 thunder_pem_link_init(struct thunder_pem_softc *sc)
271 {
272         uint64_t regval;
273
274         /* check whether PEM is safe to access. */
275         regval = bus_space_read_8(sc->reg_bst, sc->reg_bsh, PEM_ON_REG);
276         if ((regval & PEM_CFG_LINK_MASK) != PEM_CFG_LINK_RDY) {
277                 device_printf(sc->dev, "PEM%d is not ON\n", sc->id);
278                 return (ENXIO);
279         }
280
281         regval = bus_space_read_8(sc->reg_bst, sc->reg_bsh, PEM_CTL_STATUS);
282         regval |= PEM_LINK_ENABLE;
283         bus_space_write_8(sc->reg_bst, sc->reg_bsh, PEM_CTL_STATUS, regval);
284
285         /* Wait 1ms as per Cavium specification */
286         DELAY(1000);
287
288         regval = thunder_pem_config_reg_read(sc, PCIERC_CFG032);
289
290         if (((regval & PEM_LINK_DLLA) == 0) || ((regval & PEM_LINK_LT) != 0)) {
291                 device_printf(sc->dev, "PCIe RC: Port %d Link Timeout\n",
292                     sc->id);
293                 return (ENXIO);
294         }
295
296         return (0);
297 }
298
299 static int
300 thunder_pem_init(struct thunder_pem_softc *sc)
301 {
302         int i, retval = 0;
303
304         retval = thunder_pem_link_init(sc);
305         if (retval) {
306                 device_printf(sc->dev, "%s failed\n", __func__);
307                 return retval;
308         }
309
310         retval = bus_space_map(sc->reg_bst, sc->sli_window_base,
311             SLI_WINDOW_SIZE, 0, &sc->pem_sli_base);
312         if (retval) {
313                 device_printf(sc->dev,
314                     "Unable to map RC%d pem_addr base address", sc->id);
315                 return (ENOMEM);
316         }
317
318         /* To support 32-bit PCIe devices, set S2M_REGx_ACC[BA]=0x0 */
319         for (i = 0; i < SLI_ACC_REG_CNT; i++) {
320                 thunder_pem_slix_s2m_regx_acc_modify(sc, sc->sli_group, i);
321         }
322
323         return (retval);
324 }
325
326 static uint64_t
327 thunder_pem_config_reg_read(struct thunder_pem_softc *sc, int reg)
328 {
329         uint64_t data;
330
331         /* Write to ADDR register */
332         bus_space_write_8(sc->reg_bst, sc->reg_bsh, PEM_CFG_RD,
333             PEM_CFG_RD_REG_ALIGN(reg));
334         bus_space_barrier(sc->reg_bst, sc->reg_bsh, PEM_CFG_RD, 8,
335             BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
336         /* Read from DATA register */
337         data = PEM_CFG_RD_REG_DATA(bus_space_read_8(sc->reg_bst, sc->reg_bsh,
338             PEM_CFG_RD));
339
340         return (data);
341 }
342
343 static uint32_t
344 thunder_pem_read_config(device_t dev, u_int bus, u_int slot,
345     u_int func, u_int reg, int bytes)
346 {
347         uint64_t offset;
348         uint32_t data;
349         struct thunder_pem_softc *sc;
350         bus_space_tag_t t;
351         bus_space_handle_t h;
352
353         if ((bus > PCI_BUSMAX) || (slot > PCI_SLOTMAX) ||
354             (func > PCI_FUNCMAX) || (reg > PCIE_REGMAX))
355                 return (~0U);
356
357         sc = device_get_softc(dev);
358
359         /* Calculate offset */
360         offset = (bus << PEM_BUS_SHIFT) | (slot << PEM_SLOT_SHIFT) |
361             (func << PEM_FUNC_SHIFT) | reg;
362         t = sc->reg_bst;
363         h = sc->pem_sli_base;
364
365         switch (bytes) {
366         case 1:
367                 data = bus_space_read_1(t, h, offset);
368                 break;
369         case 2:
370                 data = le16toh(bus_space_read_2(t, h, offset));
371                 break;
372         case 4:
373                 data = le32toh(bus_space_read_4(t, h, offset));
374                 break;
375         default:
376                 return (~0U);
377         }
378
379         return (data);
380 }
381
382 static void
383 thunder_pem_write_config(device_t dev, u_int bus, u_int slot,
384     u_int func, u_int reg, uint32_t val, int bytes)
385 {
386         uint64_t offset;
387         struct thunder_pem_softc *sc;
388         bus_space_tag_t t;
389         bus_space_handle_t h;
390
391         if ((bus > PCI_BUSMAX) || (slot > PCI_SLOTMAX) ||
392             (func > PCI_FUNCMAX) || (reg > PCIE_REGMAX))
393                 return;
394
395         sc = device_get_softc(dev);
396
397         /* Calculate offset */
398         offset = (bus << PEM_BUS_SHIFT) | (slot << PEM_SLOT_SHIFT) |
399             (func << PEM_FUNC_SHIFT) | reg;
400         t = sc->reg_bst;
401         h = sc->pem_sli_base;
402
403         switch (bytes) {
404         case 1:
405                 bus_space_write_1(t, h, offset, val);
406                 break;
407         case 2:
408                 bus_space_write_2(t, h, offset, htole16(val));
409                 break;
410         case 4:
411                 bus_space_write_4(t, h, offset, htole32(val));
412                 break;
413         default:
414                 return;
415         }
416 }
417
418 static struct resource *
419 thunder_pem_alloc_resource(device_t dev, device_t child, int type, int *rid,
420     u_long start, u_long end, u_long count, u_int flags)
421 {
422         struct thunder_pem_softc *sc = device_get_softc(dev);
423         struct rman *rm = NULL;
424         struct resource *res;
425         device_t parent_dev;
426
427         switch (type) {
428         case SYS_RES_IOPORT:
429                 rm = &sc->io_rman;
430                 break;
431         case SYS_RES_MEMORY:
432                 rm = &sc->mem_rman;
433                 break;
434         default:
435                 /* Find parent device. On ThunderX we know an exact path. */
436                 parent_dev = device_get_parent(device_get_parent(dev));
437                 return (BUS_ALLOC_RESOURCE(parent_dev, dev, type, rid, start,
438                     end, count, flags));
439         };
440
441         if ((start == 0UL) && (end == ~0UL)) {
442                 device_printf(dev,
443                     "Cannot allocate resource with unspecified range\n");
444                 goto fail;
445         }
446
447         /* Translate PCI address to host PHYS */
448         if (range_addr_is_pci(sc->ranges, start, count) == 0)
449                 goto fail;
450         start = range_addr_pci_to_phys(sc->ranges, start);
451         end = start + count - 1;
452
453         if (bootverbose) {
454                 device_printf(dev,
455                     "rman_reserve_resource: start=%#lx, end=%#lx, count=%#lx\n",
456                     start, end, count);
457         }
458
459         res = rman_reserve_resource(rm, start, end, count, flags, child);
460         if (res == NULL)
461                 goto fail;
462
463         rman_set_rid(res, *rid);
464
465         if (flags & RF_ACTIVE)
466                 if (bus_activate_resource(child, type, *rid, res)) {
467                         rman_release_resource(res);
468                         goto fail;
469                 }
470
471         return (res);
472
473 fail:
474         if (bootverbose) {
475                 device_printf(dev, "%s FAIL: type=%d, rid=%d, "
476                     "start=%016lx, end=%016lx, count=%016lx, flags=%x\n",
477                     __func__, type, *rid, start, end, count, flags);
478         }
479
480         return (NULL);
481 }
482
483 static int
484 thunder_pem_release_resource(device_t dev, device_t child, int type, int rid,
485     struct resource *res)
486 {
487         device_t parent_dev;
488
489         /* Find parent device. On ThunderX we know an exact path. */
490         parent_dev = device_get_parent(device_get_parent(dev));
491
492         if ((type != SYS_RES_MEMORY) && (type != SYS_RES_IOPORT))
493                 return (BUS_RELEASE_RESOURCE(parent_dev, child,
494                     type, rid, res));
495
496         return (rman_release_resource(res));
497 }
498
499 static int
500 thunder_pem_probe(device_t dev)
501 {
502         uint16_t pci_vendor_id;
503         uint16_t pci_device_id;
504
505         pci_vendor_id = pci_get_vendor(dev);
506         pci_device_id = pci_get_device(dev);
507
508         if ((pci_vendor_id == THUNDER_PEM_VENDOR_ID) &&
509             (pci_device_id == THUNDER_PEM_DEVICE_ID)) {
510                 device_set_desc_copy(dev, THUNDER_PEM_DESC);
511                 return (0);
512         }
513
514         return (ENXIO);
515 }
516
517 static int
518 thunder_pem_attach(device_t dev)
519 {
520         struct thunder_pem_softc *sc;
521         int error;
522         int rid;
523
524         sc = device_get_softc(dev);
525         sc->dev = dev;
526
527         /* Allocate memory for BAR(0) */
528         rid = PCIR_BAR(0);
529         sc->reg = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
530             &rid, RF_ACTIVE);
531         if (sc->reg == NULL) {
532                 device_printf(dev, "Failed to allocate resource\n");
533                 return (ENXIO);
534         }
535         sc->reg_bst = rman_get_bustag(sc->reg);
536         sc->reg_bsh = rman_get_bushandle(sc->reg);
537
538         /* Map SLI, do it only once */
539         if (!sli0_s2m_regx_base) {
540                 bus_space_map(sc->reg_bst, SLIX_S2M_REGX_ACC,
541                     SLIX_S2M_REGX_ACC_SIZE, 0, &sli0_s2m_regx_base);
542         }
543         if (!sli1_s2m_regx_base) {
544                 bus_space_map(sc->reg_bst, SLIX_S2M_REGX_ACC +
545                     SLIX_S2M_REGX_ACC_SPACING, SLIX_S2M_REGX_ACC_SIZE, 0,
546                     &sli1_s2m_regx_base);
547         }
548
549         if ((sli0_s2m_regx_base == 0) || (sli1_s2m_regx_base == 0)) {
550                 device_printf(dev,
551                     "bus_space_map failed to map slix_s2m_regx_base\n");
552                 goto fail;
553         }
554
555         /* Identify PEM */
556         if (thunder_pem_identify(dev) != 0)
557                 goto fail;
558
559         /* Initialize rman and allocate regions */
560         sc->mem_rman.rm_type = RMAN_ARRAY;
561         sc->mem_rman.rm_descr = "PEM PCIe Memory";
562         error = rman_init(&sc->mem_rman);
563         if (error != 0) {
564                 device_printf(dev, "memory rman_init() failed. error = %d\n",
565                     error);
566                 goto fail;
567         }
568         sc->io_rman.rm_type = RMAN_ARRAY;
569         sc->io_rman.rm_descr = "PEM PCIe IO";
570         error = rman_init(&sc->io_rman);
571         if (error != 0) {
572                 device_printf(dev, "IO rman_init() failed. error = %d\n",
573                     error);
574                 goto fail_mem;
575         }
576
577         /* Fill memory window */
578         sc->ranges[0].pci_base = PCI_MEMORY_BASE;
579         sc->ranges[0].size = PCI_MEMORY_SIZE;
580         sc->ranges[0].phys_base = sc->sli_window_base + SLI_PCI_OFFSET +
581             sc->ranges[0].pci_base;
582         rman_manage_region(&sc->mem_rman, sc->ranges[0].phys_base,
583             sc->ranges[0].phys_base + sc->ranges[0].size - 1);
584
585         /* Fill IO window */
586         sc->ranges[1].pci_base = PCI_IO_BASE;
587         sc->ranges[1].size = PCI_IO_SIZE;
588         sc->ranges[1].phys_base = sc->sli_window_base + SLI_PCI_OFFSET +
589             sc->ranges[1].pci_base;
590         rman_manage_region(&sc->io_rman, sc->ranges[1].phys_base,
591             sc->ranges[1].phys_base + sc->ranges[1].size - 1);
592
593         if (thunder_pem_init(sc)) {
594                 device_printf(dev, "Failure during PEM init\n");
595                 goto fail_io;
596         }
597
598         device_add_child(dev, "pci", -1);
599
600         return (bus_generic_attach(dev));
601
602 fail_io:
603         rman_fini(&sc->io_rman);
604 fail_mem:
605         rman_fini(&sc->mem_rman);
606 fail:
607         bus_free_resource(dev, SYS_RES_MEMORY, sc->reg);
608         return (ENXIO);
609 }
610
611 static void
612 thunder_pem_release_all(device_t dev)
613 {
614         struct thunder_pem_softc *sc;
615
616         sc = device_get_softc(dev);
617
618         rman_fini(&sc->io_rman);
619         rman_fini(&sc->mem_rman);
620
621         if (sc->reg != NULL)
622                 bus_free_resource(dev, SYS_RES_MEMORY, sc->reg);
623 }
624
625 static int
626 thunder_pem_detach(device_t dev)
627 {
628
629         thunder_pem_release_all(dev);
630
631         return (0);
632 }
633
634 static devclass_t thunder_pem_devclass;
635
636 DRIVER_MODULE(thunder_pem, pci, thunder_pem_driver, thunder_pem_devclass, 0, 0);
637 MODULE_DEPEND(thunder_pem, pci, 1, 1, 1);