xref: /freebsd/sys/dev/mpr/mpr_pci.c (revision fcb560670601b2a4d87bb31d7531c8dcc37ee71b)
1 /*-
2  * Copyright (c) 2009 Yahoo! Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 /* PCI/PCI-X/PCIe bus interface for the LSI MPT2 controllers */
31 
32 /* TODO Move headers to mprvar */
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/module.h>
38 #include <sys/bus.h>
39 #include <sys/conf.h>
40 #include <sys/malloc.h>
41 #include <sys/sysctl.h>
42 #include <sys/uio.h>
43 
44 #include <machine/bus.h>
45 #include <machine/resource.h>
46 #include <sys/rman.h>
47 
48 #include <dev/pci/pcireg.h>
49 #include <dev/pci/pcivar.h>
50 #include <dev/pci/pci_private.h>
51 
52 #include <dev/mpr/mpi/mpi2_type.h>
53 #include <dev/mpr/mpi/mpi2.h>
54 #include <dev/mpr/mpi/mpi2_ioc.h>
55 #include <dev/mpr/mpi/mpi2_cnfg.h>
56 #include <dev/mpr/mpi/mpi2_tool.h>
57 
58 #include <sys/queue.h>
59 #include <sys/kthread.h>
60 #include <dev/mpr/mpr_ioctl.h>
61 #include <dev/mpr/mprvar.h>
62 
63 static int	mpr_pci_probe(device_t);
64 static int	mpr_pci_attach(device_t);
65 static int	mpr_pci_detach(device_t);
66 static int	mpr_pci_suspend(device_t);
67 static int	mpr_pci_resume(device_t);
68 static void	mpr_pci_free(struct mpr_softc *);
69 static int	mpr_alloc_msix(struct mpr_softc *sc, int msgs);
70 static int	mpr_alloc_msi(struct mpr_softc *sc, int msgs);
71 
72 static device_method_t mpr_methods[] = {
73 	DEVMETHOD(device_probe,		mpr_pci_probe),
74 	DEVMETHOD(device_attach,	mpr_pci_attach),
75 	DEVMETHOD(device_detach,	mpr_pci_detach),
76 	DEVMETHOD(device_suspend,	mpr_pci_suspend),
77 	DEVMETHOD(device_resume,	mpr_pci_resume),
78 	DEVMETHOD(bus_print_child,	bus_generic_print_child),
79 	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
80 	{ 0, 0 }
81 };
82 
83 static driver_t mpr_pci_driver = {
84 	"mpr",
85 	mpr_methods,
86 	sizeof(struct mpr_softc)
87 };
88 
89 static devclass_t	mpr_devclass;
90 DRIVER_MODULE(mpr, pci, mpr_pci_driver, mpr_devclass, 0, 0);
91 MODULE_DEPEND(mpr, cam, 1, 1, 1);
92 
93 struct mpr_ident {
94 	uint16_t	vendor;
95 	uint16_t	device;
96 	uint16_t	subvendor;
97 	uint16_t	subdevice;
98 	u_int		flags;
99 	const char	*desc;
100 } mpr_identifiers[] = {
101 	{ MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3004,
102 	    0xffff, 0xffff, 0, "LSI SAS3004" },
103 	{ MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3008,
104 	    0xffff, 0xffff, 0, "LSI SAS3008" },
105 	{ MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_1,
106 	    0xffff, 0xffff, 0, "LSI SAS3108_1" },
107 	{ MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_2,
108 	    0xffff, 0xffff, 0, "LSI SAS3108_2" },
109 	{ MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_5,
110 	    0xffff, 0xffff, 0, "LSI SAS3108_5" },
111 	{ MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_6,
112 	    0xffff, 0xffff, 0, "LSI SAS3108_6" },
113 	{ 0, 0, 0, 0, 0, NULL }
114 };
115 
116 static struct mpr_ident *
117 mpr_find_ident(device_t dev)
118 {
119 	struct mpr_ident *m;
120 
121 	for (m = mpr_identifiers; m->vendor != 0; m++) {
122 		if (m->vendor != pci_get_vendor(dev))
123 			continue;
124 		if (m->device != pci_get_device(dev))
125 			continue;
126 		if ((m->subvendor != 0xffff) &&
127 		    (m->subvendor != pci_get_subvendor(dev)))
128 			continue;
129 		if ((m->subdevice != 0xffff) &&
130 		    (m->subdevice != pci_get_subdevice(dev)))
131 			continue;
132 		return (m);
133 	}
134 
135 	return (NULL);
136 }
137 
138 static int
139 mpr_pci_probe(device_t dev)
140 {
141 	struct mpr_ident *id;
142 
143 	if ((id = mpr_find_ident(dev)) != NULL) {
144 		device_set_desc(dev, id->desc);
145 		return (BUS_PROBE_DEFAULT);
146 	}
147 	return (ENXIO);
148 }
149 
150 static int
151 mpr_pci_attach(device_t dev)
152 {
153 	struct mpr_softc *sc;
154 	struct mpr_ident *m;
155 	int error;
156 
157 	sc = device_get_softc(dev);
158 	bzero(sc, sizeof(*sc));
159 	sc->mpr_dev = dev;
160 	m = mpr_find_ident(dev);
161 	sc->mpr_flags = m->flags;
162 
163 	/* Twiddle basic PCI config bits for a sanity check */
164 	pci_enable_busmaster(dev);
165 
166 	/* Allocate the System Interface Register Set */
167 	sc->mpr_regs_rid = PCIR_BAR(1);
168 	if ((sc->mpr_regs_resource = bus_alloc_resource_any(dev,
169 	    SYS_RES_MEMORY, &sc->mpr_regs_rid, RF_ACTIVE)) == NULL) {
170 		mpr_printf(sc, "Cannot allocate PCI registers\n");
171 		return (ENXIO);
172 	}
173 	sc->mpr_btag = rman_get_bustag(sc->mpr_regs_resource);
174 	sc->mpr_bhandle = rman_get_bushandle(sc->mpr_regs_resource);
175 
176 	/* Allocate the parent DMA tag */
177 	if (bus_dma_tag_create( bus_get_dma_tag(dev),	/* parent */
178 				1, 0,			/* algnmnt, boundary */
179 				BUS_SPACE_MAXADDR,	/* lowaddr */
180 				BUS_SPACE_MAXADDR,	/* highaddr */
181 				NULL, NULL,		/* filter, filterarg */
182 				BUS_SPACE_MAXSIZE_32BIT,/* maxsize */
183 				BUS_SPACE_UNRESTRICTED,	/* nsegments */
184 				BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
185 				0,			/* flags */
186 				NULL, NULL,		/* lockfunc, lockarg */
187 				&sc->mpr_parent_dmat)) {
188 		mpr_printf(sc, "Cannot allocate parent DMA tag\n");
189 		mpr_pci_free(sc);
190 		return (ENOMEM);
191 	}
192 
193 	if ((error = mpr_attach(sc)) != 0)
194 		mpr_pci_free(sc);
195 
196 	return (error);
197 }
198 
199 int
200 mpr_pci_setup_interrupts(struct mpr_softc *sc)
201 {
202 	device_t dev;
203 	int i, error, msgs;
204 
205 	dev = sc->mpr_dev;
206 	error = ENXIO;
207 	if ((sc->disable_msix == 0) &&
208 	    ((msgs = pci_msix_count(dev)) >= MPR_MSI_COUNT))
209 		error = mpr_alloc_msix(sc, MPR_MSI_COUNT);
210 	if ((error != 0) && (sc->disable_msi == 0) &&
211 	    ((msgs = pci_msi_count(dev)) >= MPR_MSI_COUNT))
212 		error = mpr_alloc_msi(sc, MPR_MSI_COUNT);
213 
214 	if (error != 0) {
215 		sc->mpr_flags |= MPR_FLAGS_INTX;
216 		sc->mpr_irq_rid[0] = 0;
217 		sc->mpr_irq[0] = bus_alloc_resource_any(dev, SYS_RES_IRQ,
218 		    &sc->mpr_irq_rid[0],  RF_SHAREABLE | RF_ACTIVE);
219 		if (sc->mpr_irq[0] == NULL) {
220 			mpr_printf(sc, "Cannot allocate INTx interrupt\n");
221 			return (ENXIO);
222 		}
223 		error = bus_setup_intr(dev, sc->mpr_irq[0],
224 		    INTR_TYPE_BIO | INTR_MPSAFE, NULL, mpr_intr, sc,
225 		    &sc->mpr_intrhand[0]);
226 		if (error)
227 			mpr_printf(sc, "Cannot setup INTx interrupt\n");
228 	} else {
229 		sc->mpr_flags |= MPR_FLAGS_MSI;
230 		for (i = 0; i < MPR_MSI_COUNT; i++) {
231 			sc->mpr_irq_rid[i] = i + 1;
232 			sc->mpr_irq[i] = bus_alloc_resource_any(dev,
233 			    SYS_RES_IRQ, &sc->mpr_irq_rid[i], RF_ACTIVE);
234 			if (sc->mpr_irq[i] == NULL) {
235 				mpr_printf(sc,
236 				    "Cannot allocate MSI interrupt\n");
237 				return (ENXIO);
238 			}
239 			error = bus_setup_intr(dev, sc->mpr_irq[i],
240 			    INTR_TYPE_BIO | INTR_MPSAFE, NULL, mpr_intr_msi,
241 			    sc, &sc->mpr_intrhand[i]);
242 			if (error) {
243 				mpr_printf(sc,
244 				    "Cannot setup MSI interrupt %d\n", i);
245 				break;
246 			}
247 		}
248 	}
249 
250 	return (error);
251 }
252 
253 static int
254 mpr_pci_detach(device_t dev)
255 {
256 	struct mpr_softc *sc;
257 	int error;
258 
259 	sc = device_get_softc(dev);
260 
261 	if ((error = mpr_free(sc)) != 0)
262 		return (error);
263 
264 	mpr_pci_free(sc);
265 	return (0);
266 }
267 
268 static void
269 mpr_pci_free(struct mpr_softc *sc)
270 {
271 	int i;
272 
273 	if (sc->mpr_parent_dmat != NULL) {
274 		bus_dma_tag_destroy(sc->mpr_parent_dmat);
275 	}
276 
277 	if (sc->mpr_flags & MPR_FLAGS_MSI) {
278 		for (i = 0; i < MPR_MSI_COUNT; i++) {
279 			if (sc->mpr_irq[i] != NULL) {
280 				bus_teardown_intr(sc->mpr_dev, sc->mpr_irq[i],
281 				    sc->mpr_intrhand[i]);
282 				bus_release_resource(sc->mpr_dev, SYS_RES_IRQ,
283 				    sc->mpr_irq_rid[i], sc->mpr_irq[i]);
284 			}
285 		}
286 		pci_release_msi(sc->mpr_dev);
287 	}
288 
289 	if (sc->mpr_flags & MPR_FLAGS_INTX) {
290 		bus_teardown_intr(sc->mpr_dev, sc->mpr_irq[0],
291 		    sc->mpr_intrhand[0]);
292 		bus_release_resource(sc->mpr_dev, SYS_RES_IRQ,
293 		    sc->mpr_irq_rid[0], sc->mpr_irq[0]);
294 	}
295 
296 	if (sc->mpr_regs_resource != NULL) {
297 		bus_release_resource(sc->mpr_dev, SYS_RES_MEMORY,
298 		    sc->mpr_regs_rid, sc->mpr_regs_resource);
299 	}
300 
301 	return;
302 }
303 
304 static int
305 mpr_pci_suspend(device_t dev)
306 {
307 	return (EINVAL);
308 }
309 
310 static int
311 mpr_pci_resume(device_t dev)
312 {
313 	return (EINVAL);
314 }
315 
316 static int
317 mpr_alloc_msix(struct mpr_softc *sc, int msgs)
318 {
319 	int error;
320 
321 	error = pci_alloc_msix(sc->mpr_dev, &msgs);
322 	return (error);
323 }
324 
325 static int
326 mpr_alloc_msi(struct mpr_softc *sc, int msgs)
327 {
328 	int error;
329 
330 	error = pci_alloc_msi(sc->mpr_dev, &msgs);
331 	return (error);
332 }
333 
334 int
335 mpr_pci_restore(struct mpr_softc *sc)
336 {
337 	struct pci_devinfo *dinfo;
338 
339 	mpr_dprint(sc, MPR_TRACE, "%s\n", __func__);
340 
341 	dinfo = device_get_ivars(sc->mpr_dev);
342 	if (dinfo == NULL) {
343 		mpr_dprint(sc, MPR_FAULT, "%s: NULL dinfo\n", __func__);
344 		return (EINVAL);
345 	}
346 
347 	pci_cfg_restore(sc->mpr_dev, dinfo);
348 	return (0);
349 }
350 
351