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