1991554f2SKenneth D. Merry /*-
2991554f2SKenneth D. Merry * Copyright (c) 2009 Yahoo! Inc.
3991554f2SKenneth D. Merry * All rights reserved.
4991554f2SKenneth D. Merry *
5991554f2SKenneth D. Merry * Redistribution and use in source and binary forms, with or without
6991554f2SKenneth D. Merry * modification, are permitted provided that the following conditions
7991554f2SKenneth D. Merry * are met:
8991554f2SKenneth D. Merry * 1. Redistributions of source code must retain the above copyright
9991554f2SKenneth D. Merry * notice, this list of conditions and the following disclaimer.
10991554f2SKenneth D. Merry * 2. Redistributions in binary form must reproduce the above copyright
11991554f2SKenneth D. Merry * notice, this list of conditions and the following disclaimer in the
12991554f2SKenneth D. Merry * documentation and/or other materials provided with the distribution.
13991554f2SKenneth D. Merry *
14991554f2SKenneth D. Merry * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15991554f2SKenneth D. Merry * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16991554f2SKenneth D. Merry * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17991554f2SKenneth D. Merry * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18991554f2SKenneth D. Merry * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19991554f2SKenneth D. Merry * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20991554f2SKenneth D. Merry * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21991554f2SKenneth D. Merry * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22991554f2SKenneth D. Merry * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23991554f2SKenneth D. Merry * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24991554f2SKenneth D. Merry * SUCH DAMAGE.
25991554f2SKenneth D. Merry */
26991554f2SKenneth D. Merry
27991554f2SKenneth D. Merry #include <sys/cdefs.h>
28a2c14879SStephen McConnell /* PCI/PCI-X/PCIe bus interface for the Avago Tech (LSI) MPT3 controllers */
29991554f2SKenneth D. Merry
30991554f2SKenneth D. Merry /* TODO Move headers to mprvar */
31991554f2SKenneth D. Merry #include <sys/types.h>
32991554f2SKenneth D. Merry #include <sys/param.h>
33991554f2SKenneth D. Merry #include <sys/systm.h>
34991554f2SKenneth D. Merry #include <sys/kernel.h>
35991554f2SKenneth D. Merry #include <sys/module.h>
36991554f2SKenneth D. Merry #include <sys/bus.h>
37991554f2SKenneth D. Merry #include <sys/conf.h>
38991554f2SKenneth D. Merry #include <sys/malloc.h>
39991554f2SKenneth D. Merry #include <sys/sysctl.h>
40991554f2SKenneth D. Merry #include <sys/uio.h>
41991554f2SKenneth D. Merry
42991554f2SKenneth D. Merry #include <machine/bus.h>
43991554f2SKenneth D. Merry #include <machine/resource.h>
44991554f2SKenneth D. Merry #include <sys/rman.h>
45991554f2SKenneth D. Merry
46991554f2SKenneth D. Merry #include <dev/pci/pcireg.h>
47991554f2SKenneth D. Merry #include <dev/pci/pcivar.h>
48991554f2SKenneth D. Merry #include <dev/pci/pci_private.h>
49991554f2SKenneth D. Merry
50991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_type.h>
51991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2.h>
52991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_ioc.h>
53991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_cnfg.h>
54991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_tool.h>
5567feec50SStephen McConnell #include <dev/mpr/mpi/mpi2_pci.h>
56991554f2SKenneth D. Merry
57991554f2SKenneth D. Merry #include <sys/queue.h>
58991554f2SKenneth D. Merry #include <sys/kthread.h>
59991554f2SKenneth D. Merry #include <dev/mpr/mpr_ioctl.h>
60991554f2SKenneth D. Merry #include <dev/mpr/mprvar.h>
61991554f2SKenneth D. Merry
62991554f2SKenneth D. Merry static int mpr_pci_probe(device_t);
63991554f2SKenneth D. Merry static int mpr_pci_attach(device_t);
64991554f2SKenneth D. Merry static int mpr_pci_detach(device_t);
65991554f2SKenneth D. Merry static int mpr_pci_suspend(device_t);
66991554f2SKenneth D. Merry static int mpr_pci_resume(device_t);
67991554f2SKenneth D. Merry static void mpr_pci_free(struct mpr_softc *);
68991554f2SKenneth D. Merry static int mpr_alloc_msix(struct mpr_softc *sc, int msgs);
69991554f2SKenneth D. Merry static int mpr_alloc_msi(struct mpr_softc *sc, int msgs);
70252b2b4fSScott Long static int mpr_pci_alloc_interrupts(struct mpr_softc *sc);
71991554f2SKenneth D. Merry
72991554f2SKenneth D. Merry static device_method_t mpr_methods[] = {
73991554f2SKenneth D. Merry DEVMETHOD(device_probe, mpr_pci_probe),
74991554f2SKenneth D. Merry DEVMETHOD(device_attach, mpr_pci_attach),
75991554f2SKenneth D. Merry DEVMETHOD(device_detach, mpr_pci_detach),
76991554f2SKenneth D. Merry DEVMETHOD(device_suspend, mpr_pci_suspend),
77991554f2SKenneth D. Merry DEVMETHOD(device_resume, mpr_pci_resume),
78991554f2SKenneth D. Merry DEVMETHOD(bus_print_child, bus_generic_print_child),
79991554f2SKenneth D. Merry DEVMETHOD(bus_driver_added, bus_generic_driver_added),
80991554f2SKenneth D. Merry { 0, 0 }
81991554f2SKenneth D. Merry };
82991554f2SKenneth D. Merry
83991554f2SKenneth D. Merry static driver_t mpr_pci_driver = {
84991554f2SKenneth D. Merry "mpr",
85991554f2SKenneth D. Merry mpr_methods,
86991554f2SKenneth D. Merry sizeof(struct mpr_softc)
87991554f2SKenneth D. Merry };
88991554f2SKenneth D. Merry
89991554f2SKenneth D. Merry struct mpr_ident {
90991554f2SKenneth D. Merry uint16_t vendor;
91991554f2SKenneth D. Merry uint16_t device;
92991554f2SKenneth D. Merry uint16_t subvendor;
93991554f2SKenneth D. Merry uint16_t subdevice;
94991554f2SKenneth D. Merry u_int flags;
95991554f2SKenneth D. Merry const char *desc;
96991554f2SKenneth D. Merry } mpr_identifiers[] = {
97991554f2SKenneth D. Merry { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3004,
98a2c14879SStephen McConnell 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3004" },
99991554f2SKenneth D. Merry { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3008,
100a2c14879SStephen McConnell 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3008" },
101991554f2SKenneth D. Merry { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_1,
102a2c14879SStephen McConnell 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3108_1" },
103991554f2SKenneth D. Merry { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_2,
104a2c14879SStephen McConnell 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3108_2" },
105991554f2SKenneth D. Merry { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_5,
106a2c14879SStephen McConnell 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3108_5" },
107991554f2SKenneth D. Merry { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_6,
108a2c14879SStephen McConnell 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3108_6" },
10967feec50SStephen McConnell { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3216,
11067feec50SStephen McConnell 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3216" },
11167feec50SStephen McConnell { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3224,
11267feec50SStephen McConnell 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3224" },
1132bbc5fcbSStephen McConnell { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3316_1,
1142bbc5fcbSStephen McConnell 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3316_1" },
1152bbc5fcbSStephen McConnell { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3316_2,
1162bbc5fcbSStephen McConnell 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3316_2" },
1172bbc5fcbSStephen McConnell { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3324_1,
1182bbc5fcbSStephen McConnell 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3324_1" },
1192bbc5fcbSStephen McConnell { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3324_2,
1202bbc5fcbSStephen McConnell 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3324_2" },
12167feec50SStephen McConnell { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3408,
122b618318aSScott Long 0xffff, 0xffff, MPR_FLAGS_GEN35_IOC,
123b618318aSScott Long "Avago Technologies (LSI) SAS3408" },
12467feec50SStephen McConnell { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3416,
125b618318aSScott Long 0xffff, 0xffff, MPR_FLAGS_GEN35_IOC,
126b618318aSScott Long "Avago Technologies (LSI) SAS3416" },
12767feec50SStephen McConnell { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3508,
128b618318aSScott Long 0xffff, 0xffff, MPR_FLAGS_GEN35_IOC,
129b618318aSScott Long "Avago Technologies (LSI) SAS3508" },
13067feec50SStephen McConnell { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3508_1,
131b618318aSScott Long 0xffff, 0xffff, MPR_FLAGS_GEN35_IOC,
132b618318aSScott Long "Avago Technologies (LSI) SAS3508_1" },
13367feec50SStephen McConnell { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3516,
134b618318aSScott Long 0xffff, 0xffff, MPR_FLAGS_GEN35_IOC,
135b618318aSScott Long "Avago Technologies (LSI) SAS3516" },
13667feec50SStephen McConnell { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3516_1,
137b618318aSScott Long 0xffff, 0xffff, MPR_FLAGS_GEN35_IOC,
138b618318aSScott Long "Avago Technologies (LSI) SAS3516_1" },
13967feec50SStephen McConnell { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3616,
140b618318aSScott Long 0xffff, 0xffff, MPR_FLAGS_GEN35_IOC,
141b618318aSScott Long "Avago Technologies (LSI) SAS3616" },
14267feec50SStephen McConnell { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3708,
143b618318aSScott Long 0xffff, 0xffff, MPR_FLAGS_GEN35_IOC,
144b618318aSScott Long "Avago Technologies (LSI) SAS3708" },
14567feec50SStephen McConnell { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3716,
146b618318aSScott Long 0xffff, 0xffff, MPR_FLAGS_GEN35_IOC,
147b618318aSScott Long "Avago Technologies (LSI) SAS3716" },
148f36649b7SKashyap D Desai { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_INVALID0_SAS3816,
149f36649b7SKashyap D Desai 0xffff, 0xffff, (MPR_FLAGS_GEN35_IOC | MPR_FLAGS_SEA_IOC),
150f36649b7SKashyap D Desai "Broadcom Inc. (LSI) INVALID0 SAS3816" },
151f36649b7SKashyap D Desai { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_CFG_SEC_SAS3816,
152f36649b7SKashyap D Desai 0xffff, 0xffff, (MPR_FLAGS_GEN35_IOC | MPR_FLAGS_SEA_IOC),
153f36649b7SKashyap D Desai "Broadcom Inc. (LSI) CFG SEC SAS3816" },
154f36649b7SKashyap D Desai { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_HARD_SEC_SAS3816,
155f36649b7SKashyap D Desai 0xffff, 0xffff, (MPR_FLAGS_GEN35_IOC | MPR_FLAGS_SEA_IOC),
156f36649b7SKashyap D Desai "Broadcom Inc. (LSI) HARD SEC SAS3816" },
157f36649b7SKashyap D Desai { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_INVALID1_SAS3816,
158f36649b7SKashyap D Desai 0xffff, 0xffff, (MPR_FLAGS_GEN35_IOC | MPR_FLAGS_SEA_IOC),
159f36649b7SKashyap D Desai "Broadcom Inc. (LSI) INVALID1 SAS3816" },
160f36649b7SKashyap D Desai { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_INVALID0_SAS3916,
161f36649b7SKashyap D Desai 0xffff, 0xffff, (MPR_FLAGS_GEN35_IOC | MPR_FLAGS_SEA_IOC),
162f36649b7SKashyap D Desai "Broadcom Inc. (LSI) INVALID0 SAS3916" },
163f36649b7SKashyap D Desai { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_CFG_SEC_SAS3916,
164f36649b7SKashyap D Desai 0xffff, 0xffff, (MPR_FLAGS_GEN35_IOC | MPR_FLAGS_SEA_IOC),
165f36649b7SKashyap D Desai "Broadcom Inc. (LSI) CFG SEC SAS3916" },
166f36649b7SKashyap D Desai { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_HARD_SEC_SAS3916,
167f36649b7SKashyap D Desai 0xffff, 0xffff, (MPR_FLAGS_GEN35_IOC | MPR_FLAGS_SEA_IOC),
168f36649b7SKashyap D Desai "Broadcom Inc. (LSI) HARD SEC SAS3916" },
169f36649b7SKashyap D Desai { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_INVALID1_SAS3916,
170f36649b7SKashyap D Desai 0xffff, 0xffff, (MPR_FLAGS_GEN35_IOC | MPR_FLAGS_SEA_IOC),
171f36649b7SKashyap D Desai "Broadcom Inc. (LSI) INVALID1 SAS3916" },
172991554f2SKenneth D. Merry { 0, 0, 0, 0, 0, NULL }
173991554f2SKenneth D. Merry };
174991554f2SKenneth D. Merry
175*d48ddc5fSJohn Baldwin DRIVER_MODULE(mpr, pci, mpr_pci_driver, 0, 0);
1760dc34160SWarner Losh MODULE_PNP_INFO("U16:vendor;U16:device;U16:subvendor;U16:subdevice;D:#", pci,
1770dc34160SWarner Losh mpr, mpr_identifiers, nitems(mpr_identifiers) - 1);
1780dc34160SWarner Losh
1790dc34160SWarner Losh MODULE_DEPEND(mpr, cam, 1, 1, 1);
1800dc34160SWarner Losh
181991554f2SKenneth D. Merry static struct mpr_ident *
mpr_find_ident(device_t dev)182991554f2SKenneth D. Merry mpr_find_ident(device_t dev)
183991554f2SKenneth D. Merry {
184991554f2SKenneth D. Merry struct mpr_ident *m;
185991554f2SKenneth D. Merry
186991554f2SKenneth D. Merry for (m = mpr_identifiers; m->vendor != 0; m++) {
187991554f2SKenneth D. Merry if (m->vendor != pci_get_vendor(dev))
188991554f2SKenneth D. Merry continue;
189991554f2SKenneth D. Merry if (m->device != pci_get_device(dev))
190991554f2SKenneth D. Merry continue;
191991554f2SKenneth D. Merry if ((m->subvendor != 0xffff) &&
192991554f2SKenneth D. Merry (m->subvendor != pci_get_subvendor(dev)))
193991554f2SKenneth D. Merry continue;
194991554f2SKenneth D. Merry if ((m->subdevice != 0xffff) &&
195991554f2SKenneth D. Merry (m->subdevice != pci_get_subdevice(dev)))
196991554f2SKenneth D. Merry continue;
197991554f2SKenneth D. Merry return (m);
198991554f2SKenneth D. Merry }
199991554f2SKenneth D. Merry
200991554f2SKenneth D. Merry return (NULL);
201991554f2SKenneth D. Merry }
202991554f2SKenneth D. Merry
203991554f2SKenneth D. Merry static int
mpr_pci_probe(device_t dev)204991554f2SKenneth D. Merry mpr_pci_probe(device_t dev)
205991554f2SKenneth D. Merry {
206991554f2SKenneth D. Merry struct mpr_ident *id;
207991554f2SKenneth D. Merry
208991554f2SKenneth D. Merry if ((id = mpr_find_ident(dev)) != NULL) {
209991554f2SKenneth D. Merry device_set_desc(dev, id->desc);
210991554f2SKenneth D. Merry return (BUS_PROBE_DEFAULT);
211991554f2SKenneth D. Merry }
212991554f2SKenneth D. Merry return (ENXIO);
213991554f2SKenneth D. Merry }
214991554f2SKenneth D. Merry
215991554f2SKenneth D. Merry static int
mpr_pci_attach(device_t dev)216991554f2SKenneth D. Merry mpr_pci_attach(device_t dev)
217991554f2SKenneth D. Merry {
21874c781edSScott Long bus_dma_template_t t;
219991554f2SKenneth D. Merry struct mpr_softc *sc;
220991554f2SKenneth D. Merry struct mpr_ident *m;
22167feec50SStephen McConnell int error, i;
222991554f2SKenneth D. Merry
223991554f2SKenneth D. Merry sc = device_get_softc(dev);
224991554f2SKenneth D. Merry bzero(sc, sizeof(*sc));
225991554f2SKenneth D. Merry sc->mpr_dev = dev;
226991554f2SKenneth D. Merry m = mpr_find_ident(dev);
227991554f2SKenneth D. Merry sc->mpr_flags = m->flags;
228991554f2SKenneth D. Merry
229f36649b7SKashyap D Desai switch (m->device) {
230f36649b7SKashyap D Desai case MPI26_MFGPAGE_DEVID_INVALID0_SAS3816:
231f36649b7SKashyap D Desai case MPI26_MFGPAGE_DEVID_INVALID1_SAS3816:
232f36649b7SKashyap D Desai case MPI26_MFGPAGE_DEVID_INVALID0_SAS3916:
233f36649b7SKashyap D Desai case MPI26_MFGPAGE_DEVID_INVALID1_SAS3916:
234f36649b7SKashyap D Desai mpr_printf(sc, "HBA is in Non Secure mode\n");
235f36649b7SKashyap D Desai return (ENXIO);
236f36649b7SKashyap D Desai case MPI26_MFGPAGE_DEVID_CFG_SEC_SAS3816:
237f36649b7SKashyap D Desai case MPI26_MFGPAGE_DEVID_CFG_SEC_SAS3916:
238f36649b7SKashyap D Desai mpr_printf(sc, "HBA is in Configurable Secure mode\n");
239f36649b7SKashyap D Desai break;
240f36649b7SKashyap D Desai default:
241f36649b7SKashyap D Desai break;
242f36649b7SKashyap D Desai }
243f36649b7SKashyap D Desai
244252b2b4fSScott Long mpr_get_tunables(sc);
245252b2b4fSScott Long
246991554f2SKenneth D. Merry /* Twiddle basic PCI config bits for a sanity check */
247991554f2SKenneth D. Merry pci_enable_busmaster(dev);
248991554f2SKenneth D. Merry
24967feec50SStephen McConnell for (i = 0; i < PCI_MAXMAPS_0; i++) {
25067feec50SStephen McConnell sc->mpr_regs_rid = PCIR_BAR(i);
25167feec50SStephen McConnell
252991554f2SKenneth D. Merry if ((sc->mpr_regs_resource = bus_alloc_resource_any(dev,
25367feec50SStephen McConnell SYS_RES_MEMORY, &sc->mpr_regs_rid, RF_ACTIVE)) != NULL)
25467feec50SStephen McConnell break;
25567feec50SStephen McConnell }
25667feec50SStephen McConnell
25767feec50SStephen McConnell if (sc->mpr_regs_resource == NULL) {
258991554f2SKenneth D. Merry mpr_printf(sc, "Cannot allocate PCI registers\n");
259991554f2SKenneth D. Merry return (ENXIO);
260991554f2SKenneth D. Merry }
26167feec50SStephen McConnell
262991554f2SKenneth D. Merry sc->mpr_btag = rman_get_bustag(sc->mpr_regs_resource);
263991554f2SKenneth D. Merry sc->mpr_bhandle = rman_get_bushandle(sc->mpr_regs_resource);
264991554f2SKenneth D. Merry
265991554f2SKenneth D. Merry /* Allocate the parent DMA tag */
266f5ead205SScott Long bus_dma_template_init(&t, bus_get_dma_tag(dev));
267f5ead205SScott Long if (bus_dma_template_tag(&t, &sc->mpr_parent_dmat)) {
268991554f2SKenneth D. Merry mpr_printf(sc, "Cannot allocate parent DMA tag\n");
269991554f2SKenneth D. Merry mpr_pci_free(sc);
270991554f2SKenneth D. Merry return (ENOMEM);
271991554f2SKenneth D. Merry }
272991554f2SKenneth D. Merry
273252b2b4fSScott Long if (((error = mpr_pci_alloc_interrupts(sc)) != 0) ||
274252b2b4fSScott Long ((error = mpr_attach(sc)) != 0))
275991554f2SKenneth D. Merry mpr_pci_free(sc);
276991554f2SKenneth D. Merry
277991554f2SKenneth D. Merry return (error);
278991554f2SKenneth D. Merry }
279991554f2SKenneth D. Merry
280252b2b4fSScott Long /*
281252b2b4fSScott Long * Allocate, but don't assign interrupts early. Doing it before requesting
282252b2b4fSScott Long * the IOCFacts message informs the firmware that we want to do MSI-X
283252b2b4fSScott Long * multiqueue. We might not use all of the available messages, but there's
284252b2b4fSScott Long * no reason to re-alloc if we don't.
285252b2b4fSScott Long */
286252b2b4fSScott Long int
mpr_pci_alloc_interrupts(struct mpr_softc * sc)287252b2b4fSScott Long mpr_pci_alloc_interrupts(struct mpr_softc *sc)
288252b2b4fSScott Long {
289252b2b4fSScott Long device_t dev;
290252b2b4fSScott Long int error, msgs;
291252b2b4fSScott Long
292252b2b4fSScott Long dev = sc->mpr_dev;
293252b2b4fSScott Long error = 0;
2942068b2aaSScott Long msgs = 0;
295252b2b4fSScott Long
2963c5ac992SScott Long if (sc->disable_msix == 0) {
2973c5ac992SScott Long msgs = pci_msix_count(dev);
2983c5ac992SScott Long mpr_dprint(sc, MPR_INIT, "Counted %d MSI-X messages\n", msgs);
2993c5ac992SScott Long msgs = min(msgs, sc->max_msix);
3003c5ac992SScott Long msgs = min(msgs, MPR_MSIX_MAX);
3013c5ac992SScott Long msgs = min(msgs, 1); /* XXX */
3023c5ac992SScott Long if (msgs != 0) {
3037eed4c18SScott Long mpr_dprint(sc, MPR_INIT, "Attempting to allocate %d "
3047eed4c18SScott Long "MSI-X messages\n", msgs);
3053c5ac992SScott Long error = mpr_alloc_msix(sc, msgs);
3063c5ac992SScott Long }
3073c5ac992SScott Long }
3083c5ac992SScott Long if (((error != 0) || (msgs == 0)) && (sc->disable_msi == 0)) {
3093c5ac992SScott Long msgs = pci_msi_count(dev);
3103c5ac992SScott Long mpr_dprint(sc, MPR_INIT, "Counted %d MSI messages\n", msgs);
3113c5ac992SScott Long msgs = min(msgs, MPR_MSI_MAX);
3123c5ac992SScott Long if (msgs != 0) {
3137eed4c18SScott Long mpr_dprint(sc, MPR_INIT, "Attempting to allocated %d "
3147eed4c18SScott Long "MSI messages\n", MPR_MSI_MAX);
3153c5ac992SScott Long error = mpr_alloc_msi(sc, MPR_MSI_MAX);
3163c5ac992SScott Long }
3173c5ac992SScott Long }
3183c5ac992SScott Long if ((error != 0) || (msgs == 0)) {
3193d96cd78SScott Long /*
3203d96cd78SScott Long * If neither MSI or MSI-X are available, assume legacy INTx.
3213d96cd78SScott Long * This also implies that there will be only 1 queue.
3223d96cd78SScott Long */
3233c5ac992SScott Long mpr_dprint(sc, MPR_INIT, "Falling back to legacy INTx\n");
3243d96cd78SScott Long sc->mpr_flags |= MPR_FLAGS_INTX;
3253d96cd78SScott Long msgs = 1;
3263c5ac992SScott Long } else
3273d96cd78SScott Long sc->mpr_flags |= MPR_FLAGS_MSI;
328252b2b4fSScott Long
329252b2b4fSScott Long sc->msi_msgs = msgs;
3303d96cd78SScott Long mpr_dprint(sc, MPR_INIT, "Allocated %d interrupts\n", msgs);
3313d96cd78SScott Long
332252b2b4fSScott Long return (error);
333252b2b4fSScott Long }
334252b2b4fSScott Long
335991554f2SKenneth D. Merry int
mpr_pci_setup_interrupts(struct mpr_softc * sc)336991554f2SKenneth D. Merry mpr_pci_setup_interrupts(struct mpr_softc *sc)
337991554f2SKenneth D. Merry {
338991554f2SKenneth D. Merry device_t dev;
339bec09074SScott Long struct mpr_queue *q;
3403d96cd78SScott Long void *ihandler;
3413d96cd78SScott Long int i, error, rid, initial_rid;
342991554f2SKenneth D. Merry
343991554f2SKenneth D. Merry dev = sc->mpr_dev;
344991554f2SKenneth D. Merry error = ENXIO;
345991554f2SKenneth D. Merry
3463d96cd78SScott Long if (sc->mpr_flags & MPR_FLAGS_INTX) {
3473d96cd78SScott Long initial_rid = 0;
3483d96cd78SScott Long ihandler = mpr_intr;
3493d96cd78SScott Long } else if (sc->mpr_flags & MPR_FLAGS_MSI) {
3503d96cd78SScott Long initial_rid = 1;
3513d96cd78SScott Long ihandler = mpr_intr_msi;
352991554f2SKenneth D. Merry } else {
3533d96cd78SScott Long mpr_dprint(sc, MPR_ERROR|MPR_INIT,
3543d96cd78SScott Long "Unable to set up interrupts\n");
3553d96cd78SScott Long return (EINVAL);
3563d96cd78SScott Long }
3573d96cd78SScott Long
3583d96cd78SScott Long for (i = 0; i < sc->msi_msgs; i++) {
359bec09074SScott Long q = &sc->queues[i];
3603d96cd78SScott Long rid = i + initial_rid;
361bec09074SScott Long q->irq_rid = rid;
362bec09074SScott Long q->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
363bec09074SScott Long &q->irq_rid, RF_ACTIVE);
364bec09074SScott Long if (q->irq == NULL) {
3653d96cd78SScott Long mpr_dprint(sc, MPR_ERROR|MPR_INIT,
3663d96cd78SScott Long "Cannot allocate interrupt RID %d\n", rid);
3673c5ac992SScott Long sc->msi_msgs = i;
3683d96cd78SScott Long break;
369991554f2SKenneth D. Merry }
370bec09074SScott Long error = bus_setup_intr(dev, q->irq,
371bec09074SScott Long INTR_TYPE_BIO | INTR_MPSAFE, NULL, ihandler,
372bec09074SScott Long sc, &q->intrhand);
373991554f2SKenneth D. Merry if (error) {
3743d96cd78SScott Long mpr_dprint(sc, MPR_ERROR|MPR_INIT,
3753d96cd78SScott Long "Cannot setup interrupt RID %d\n", rid);
3763c5ac992SScott Long sc->msi_msgs = i;
377991554f2SKenneth D. Merry break;
378991554f2SKenneth D. Merry }
379991554f2SKenneth D. Merry }
380991554f2SKenneth D. Merry
3813d96cd78SScott Long mpr_dprint(sc, MPR_INIT, "Set up %d interrupts\n", sc->msi_msgs);
382991554f2SKenneth D. Merry return (error);
383991554f2SKenneth D. Merry }
384991554f2SKenneth D. Merry
385991554f2SKenneth D. Merry static int
mpr_pci_detach(device_t dev)386991554f2SKenneth D. Merry mpr_pci_detach(device_t dev)
387991554f2SKenneth D. Merry {
388991554f2SKenneth D. Merry struct mpr_softc *sc;
389991554f2SKenneth D. Merry int error;
390991554f2SKenneth D. Merry
391991554f2SKenneth D. Merry sc = device_get_softc(dev);
392991554f2SKenneth D. Merry
393991554f2SKenneth D. Merry if ((error = mpr_free(sc)) != 0)
394991554f2SKenneth D. Merry return (error);
395991554f2SKenneth D. Merry
396991554f2SKenneth D. Merry mpr_pci_free(sc);
397991554f2SKenneth D. Merry return (0);
398991554f2SKenneth D. Merry }
399991554f2SKenneth D. Merry
400bec09074SScott Long void
mpr_pci_free_interrupts(struct mpr_softc * sc)401bec09074SScott Long mpr_pci_free_interrupts(struct mpr_softc *sc)
402bec09074SScott Long {
403bec09074SScott Long struct mpr_queue *q;
404bec09074SScott Long int i;
405bec09074SScott Long
406bec09074SScott Long if (sc->queues == NULL)
407bec09074SScott Long return;
408bec09074SScott Long
409bec09074SScott Long for (i = 0; i < sc->msi_msgs; i++) {
410bec09074SScott Long q = &sc->queues[i];
411bec09074SScott Long if (q->irq != NULL) {
412bec09074SScott Long bus_teardown_intr(sc->mpr_dev, q->irq,
413bec09074SScott Long q->intrhand);
414bec09074SScott Long bus_release_resource(sc->mpr_dev, SYS_RES_IRQ,
415bec09074SScott Long q->irq_rid, q->irq);
416bec09074SScott Long }
417bec09074SScott Long }
418bec09074SScott Long }
419bec09074SScott Long
420991554f2SKenneth D. Merry static void
mpr_pci_free(struct mpr_softc * sc)421991554f2SKenneth D. Merry mpr_pci_free(struct mpr_softc *sc)
422991554f2SKenneth D. Merry {
423991554f2SKenneth D. Merry
424991554f2SKenneth D. Merry if (sc->mpr_parent_dmat != NULL) {
425991554f2SKenneth D. Merry bus_dma_tag_destroy(sc->mpr_parent_dmat);
426991554f2SKenneth D. Merry }
427991554f2SKenneth D. Merry
428bec09074SScott Long mpr_pci_free_interrupts(sc);
429991554f2SKenneth D. Merry
4303d96cd78SScott Long if (sc->mpr_flags & MPR_FLAGS_MSI)
4313d96cd78SScott Long pci_release_msi(sc->mpr_dev);
432991554f2SKenneth D. Merry
433991554f2SKenneth D. Merry if (sc->mpr_regs_resource != NULL) {
434991554f2SKenneth D. Merry bus_release_resource(sc->mpr_dev, SYS_RES_MEMORY,
435991554f2SKenneth D. Merry sc->mpr_regs_rid, sc->mpr_regs_resource);
436991554f2SKenneth D. Merry }
437991554f2SKenneth D. Merry
438991554f2SKenneth D. Merry return;
439991554f2SKenneth D. Merry }
440991554f2SKenneth D. Merry
441991554f2SKenneth D. Merry static int
mpr_pci_suspend(device_t dev)442991554f2SKenneth D. Merry mpr_pci_suspend(device_t dev)
443991554f2SKenneth D. Merry {
444991554f2SKenneth D. Merry return (EINVAL);
445991554f2SKenneth D. Merry }
446991554f2SKenneth D. Merry
447991554f2SKenneth D. Merry static int
mpr_pci_resume(device_t dev)448991554f2SKenneth D. Merry mpr_pci_resume(device_t dev)
449991554f2SKenneth D. Merry {
450991554f2SKenneth D. Merry return (EINVAL);
451991554f2SKenneth D. Merry }
452991554f2SKenneth D. Merry
453991554f2SKenneth D. Merry static int
mpr_alloc_msix(struct mpr_softc * sc,int msgs)454991554f2SKenneth D. Merry mpr_alloc_msix(struct mpr_softc *sc, int msgs)
455991554f2SKenneth D. Merry {
456991554f2SKenneth D. Merry int error;
457991554f2SKenneth D. Merry
458991554f2SKenneth D. Merry error = pci_alloc_msix(sc->mpr_dev, &msgs);
459991554f2SKenneth D. Merry return (error);
460991554f2SKenneth D. Merry }
461991554f2SKenneth D. Merry
462991554f2SKenneth D. Merry static int
mpr_alloc_msi(struct mpr_softc * sc,int msgs)463991554f2SKenneth D. Merry mpr_alloc_msi(struct mpr_softc *sc, int msgs)
464991554f2SKenneth D. Merry {
465991554f2SKenneth D. Merry int error;
466991554f2SKenneth D. Merry
467991554f2SKenneth D. Merry error = pci_alloc_msi(sc->mpr_dev, &msgs);
468991554f2SKenneth D. Merry return (error);
469991554f2SKenneth D. Merry }
470991554f2SKenneth D. Merry
471991554f2SKenneth D. Merry int
mpr_pci_restore(struct mpr_softc * sc)472991554f2SKenneth D. Merry mpr_pci_restore(struct mpr_softc *sc)
473991554f2SKenneth D. Merry {
474991554f2SKenneth D. Merry struct pci_devinfo *dinfo;
475991554f2SKenneth D. Merry
476991554f2SKenneth D. Merry mpr_dprint(sc, MPR_TRACE, "%s\n", __func__);
477991554f2SKenneth D. Merry
478991554f2SKenneth D. Merry dinfo = device_get_ivars(sc->mpr_dev);
479991554f2SKenneth D. Merry if (dinfo == NULL) {
480991554f2SKenneth D. Merry mpr_dprint(sc, MPR_FAULT, "%s: NULL dinfo\n", __func__);
481991554f2SKenneth D. Merry return (EINVAL);
482991554f2SKenneth D. Merry }
483991554f2SKenneth D. Merry
484991554f2SKenneth D. Merry pci_cfg_restore(sc->mpr_dev, dinfo);
485991554f2SKenneth D. Merry return (0);
486991554f2SKenneth D. Merry }
487