vga_pci.c revision 232472
1/*-
2 * Copyright (c) 2005 John Baldwin <jhb@FreeBSD.org>
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 * 3. Neither the name of the author nor the names of any co-contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
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#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sys/dev/pci/vga_pci.c 232472 2012-03-03 18:08:57Z jhb $");
32
33/*
34 * Simple driver for PCI VGA display devices.  Drivers such as agp(4) and
35 * drm(4) should attach as children of this device.
36 *
37 * XXX: The vgapci name is a hack until we somehow merge the isa vga driver
38 * in or rename it.
39 */
40
41#include <sys/param.h>
42#include <sys/bus.h>
43#include <sys/kernel.h>
44#include <sys/module.h>
45#include <sys/rman.h>
46#include <sys/sysctl.h>
47#include <sys/systm.h>
48
49#include <dev/pci/pcireg.h>
50#include <dev/pci/pcivar.h>
51
52struct vga_resource {
53	struct resource	*vr_res;
54	int	vr_refs;
55};
56
57struct vga_pci_softc {
58	device_t	vga_msi_child;	/* Child driver using MSI. */
59	struct vga_resource vga_res[PCIR_MAX_BAR_0 + 1];
60};
61
62SYSCTL_DECL(_hw_pci);
63
64int vga_pci_default_unit = -1;
65TUNABLE_INT("hw.pci.default_vgapci_unit", &vga_pci_default_unit);
66SYSCTL_INT(_hw_pci, OID_AUTO, default_vgapci_unit, CTLFLAG_RDTUN,
67    &vga_pci_default_unit, -1, "Default VGA-compatible display");
68
69static int
70vga_pci_probe(device_t dev)
71{
72	device_t bdev;
73	int unit;
74	uint16_t bctl;
75
76	switch (pci_get_class(dev)) {
77	case PCIC_DISPLAY:
78		break;
79	case PCIC_OLD:
80		if (pci_get_subclass(dev) != PCIS_OLD_VGA)
81			return (ENXIO);
82		break;
83	default:
84		return (ENXIO);
85	}
86
87	/* Probe default display. */
88	unit = device_get_unit(dev);
89	bdev = device_get_parent(device_get_parent(dev));
90	bctl = pci_read_config(bdev, PCIR_BRIDGECTL_1, 2);
91	if (vga_pci_default_unit < 0 && (bctl & PCIB_BCR_VGA_ENABLE) != 0)
92		vga_pci_default_unit = unit;
93	if (vga_pci_default_unit == unit)
94		device_set_flags(dev, 1);
95
96	device_set_desc(dev, "VGA-compatible display");
97	return (BUS_PROBE_GENERIC);
98}
99
100static int
101vga_pci_attach(device_t dev)
102{
103
104	bus_generic_probe(dev);
105
106	/* Always create a drm child for now to make it easier on drm. */
107	device_add_child(dev, "drm", -1);
108	bus_generic_attach(dev);
109	return (0);
110}
111
112static int
113vga_pci_suspend(device_t dev)
114{
115
116	return (bus_generic_suspend(dev));
117}
118
119static int
120vga_pci_resume(device_t dev)
121{
122
123	return (bus_generic_resume(dev));
124}
125
126/* Bus interface. */
127
128static int
129vga_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
130{
131
132	return (BUS_READ_IVAR(device_get_parent(dev), dev, which, result));
133}
134
135static int
136vga_pci_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
137{
138
139	return (EINVAL);
140}
141
142static int
143vga_pci_setup_intr(device_t dev, device_t child, struct resource *irq,
144    int flags, driver_filter_t *filter, driver_intr_t *intr, void *arg,
145    void **cookiep)
146{
147	return (BUS_SETUP_INTR(device_get_parent(dev), dev, irq, flags,
148	    filter, intr, arg, cookiep));
149}
150
151static int
152vga_pci_teardown_intr(device_t dev, device_t child, struct resource *irq,
153    void *cookie)
154{
155	return (BUS_TEARDOWN_INTR(device_get_parent(dev), dev, irq, cookie));
156}
157
158static struct resource *
159vga_pci_alloc_resource(device_t dev, device_t child, int type, int *rid,
160    u_long start, u_long end, u_long count, u_int flags)
161{
162	struct vga_pci_softc *sc;
163	int bar;
164
165	switch (type) {
166	case SYS_RES_MEMORY:
167	case SYS_RES_IOPORT:
168		/*
169		 * For BARs, we cache the resource so that we only allocate it
170		 * from the PCI bus once.
171		 */
172		bar = PCI_RID2BAR(*rid);
173		if (bar < 0 || bar > PCIR_MAX_BAR_0)
174			return (NULL);
175		sc = device_get_softc(dev);
176		if (sc->vga_res[bar].vr_res == NULL)
177			sc->vga_res[bar].vr_res = bus_alloc_resource(dev, type,
178			    rid, start, end, count, flags);
179		if (sc->vga_res[bar].vr_res != NULL)
180			sc->vga_res[bar].vr_refs++;
181		return (sc->vga_res[bar].vr_res);
182	}
183	return (bus_alloc_resource(dev, type, rid, start, end, count, flags));
184}
185
186static int
187vga_pci_release_resource(device_t dev, device_t child, int type, int rid,
188    struct resource *r)
189{
190	struct vga_pci_softc *sc;
191	int bar, error;
192
193	switch (type) {
194	case SYS_RES_MEMORY:
195	case SYS_RES_IOPORT:
196		/*
197		 * For BARs, we release the resource from the PCI bus
198		 * when the last child reference goes away.
199		 */
200		bar = PCI_RID2BAR(rid);
201		if (bar < 0 || bar > PCIR_MAX_BAR_0)
202			return (EINVAL);
203		sc = device_get_softc(dev);
204		if (sc->vga_res[bar].vr_res == NULL)
205			return (EINVAL);
206		KASSERT(sc->vga_res[bar].vr_res == r,
207		    ("vga_pci resource mismatch"));
208		if (sc->vga_res[bar].vr_refs > 1) {
209			sc->vga_res[bar].vr_refs--;
210			return (0);
211		}
212		KASSERT(sc->vga_res[bar].vr_refs > 0,
213		    ("vga_pci resource reference count underflow"));
214		error = bus_release_resource(dev, type, rid, r);
215		if (error == 0) {
216			sc->vga_res[bar].vr_res = NULL;
217			sc->vga_res[bar].vr_refs = 0;
218		}
219		return (error);
220	}
221
222	return (bus_release_resource(dev, type, rid, r));
223}
224
225/* PCI interface. */
226
227static uint32_t
228vga_pci_read_config(device_t dev, device_t child, int reg, int width)
229{
230
231	return (pci_read_config(dev, reg, width));
232}
233
234static void
235vga_pci_write_config(device_t dev, device_t child, int reg,
236    uint32_t val, int width)
237{
238
239	pci_write_config(dev, reg, val, width);
240}
241
242static int
243vga_pci_enable_busmaster(device_t dev, device_t child)
244{
245
246	return (pci_enable_busmaster(dev));
247}
248
249static int
250vga_pci_disable_busmaster(device_t dev, device_t child)
251{
252
253	return (pci_disable_busmaster(dev));
254}
255
256static int
257vga_pci_enable_io(device_t dev, device_t child, int space)
258{
259
260	device_printf(dev, "child %s requested pci_enable_io\n",
261	    device_get_nameunit(child));
262	return (pci_enable_io(dev, space));
263}
264
265static int
266vga_pci_disable_io(device_t dev, device_t child, int space)
267{
268
269	device_printf(dev, "child %s requested pci_disable_io\n",
270	    device_get_nameunit(child));
271	return (pci_disable_io(dev, space));
272}
273
274static int
275vga_pci_get_vpd_ident(device_t dev, device_t child, const char **identptr)
276{
277
278	return (pci_get_vpd_ident(dev, identptr));
279}
280
281static int
282vga_pci_get_vpd_readonly(device_t dev, device_t child, const char *kw,
283    const char **vptr)
284{
285
286	return (pci_get_vpd_readonly(dev, kw, vptr));
287}
288
289static int
290vga_pci_set_powerstate(device_t dev, device_t child, int state)
291{
292
293	device_printf(dev, "child %s requested pci_set_powerstate\n",
294	    device_get_nameunit(child));
295	return (pci_set_powerstate(dev, state));
296}
297
298static int
299vga_pci_get_powerstate(device_t dev, device_t child)
300{
301
302	device_printf(dev, "child %s requested pci_get_powerstate\n",
303	    device_get_nameunit(child));
304	return (pci_get_powerstate(dev));
305}
306
307static int
308vga_pci_assign_interrupt(device_t dev, device_t child)
309{
310
311	device_printf(dev, "child %s requested pci_assign_interrupt\n",
312	    device_get_nameunit(child));
313	return (PCI_ASSIGN_INTERRUPT(device_get_parent(dev), dev));
314}
315
316static int
317vga_pci_find_cap(device_t dev, device_t child, int capability,
318    int *capreg)
319{
320
321	return (pci_find_cap(dev, capability, capreg));
322}
323
324static int
325vga_pci_find_extcap(device_t dev, device_t child, int capability,
326    int *capreg)
327{
328
329	return (pci_find_extcap(dev, capability, capreg));
330}
331
332static int
333vga_pci_find_htcap(device_t dev, device_t child, int capability,
334    int *capreg)
335{
336
337	return (pci_find_htcap(dev, capability, capreg));
338}
339
340static int
341vga_pci_alloc_msi(device_t dev, device_t child, int *count)
342{
343	struct vga_pci_softc *sc;
344	int error;
345
346	sc = device_get_softc(dev);
347	if (sc->vga_msi_child != NULL)
348		return (EBUSY);
349	error = pci_alloc_msi(dev, count);
350	if (error == 0)
351		sc->vga_msi_child = child;
352	return (error);
353}
354
355static int
356vga_pci_alloc_msix(device_t dev, device_t child, int *count)
357{
358	struct vga_pci_softc *sc;
359	int error;
360
361	sc = device_get_softc(dev);
362	if (sc->vga_msi_child != NULL)
363		return (EBUSY);
364	error = pci_alloc_msix(dev, count);
365	if (error == 0)
366		sc->vga_msi_child = child;
367	return (error);
368}
369
370static int
371vga_pci_remap_msix(device_t dev, device_t child, int count,
372    const u_int *vectors)
373{
374	struct vga_pci_softc *sc;
375
376	sc = device_get_softc(dev);
377	if (sc->vga_msi_child != child)
378		return (ENXIO);
379	return (pci_remap_msix(dev, count, vectors));
380}
381
382static int
383vga_pci_release_msi(device_t dev, device_t child)
384{
385	struct vga_pci_softc *sc;
386	int error;
387
388	sc = device_get_softc(dev);
389	if (sc->vga_msi_child != child)
390		return (ENXIO);
391	error = pci_release_msi(dev);
392	if (error == 0)
393		sc->vga_msi_child = NULL;
394	return (error);
395}
396
397static int
398vga_pci_msi_count(device_t dev, device_t child)
399{
400
401	return (pci_msi_count(dev));
402}
403
404static int
405vga_pci_msix_count(device_t dev, device_t child)
406{
407
408	return (pci_msix_count(dev));
409}
410
411static device_method_t vga_pci_methods[] = {
412	/* Device interface */
413	DEVMETHOD(device_probe,		vga_pci_probe),
414	DEVMETHOD(device_attach,	vga_pci_attach),
415	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
416	DEVMETHOD(device_suspend,	vga_pci_suspend),
417	DEVMETHOD(device_resume,	vga_pci_resume),
418
419	/* Bus interface */
420	DEVMETHOD(bus_read_ivar,	vga_pci_read_ivar),
421	DEVMETHOD(bus_write_ivar,	vga_pci_write_ivar),
422	DEVMETHOD(bus_setup_intr,	vga_pci_setup_intr),
423	DEVMETHOD(bus_teardown_intr,	vga_pci_teardown_intr),
424	DEVMETHOD(bus_alloc_resource,	vga_pci_alloc_resource),
425	DEVMETHOD(bus_release_resource,	vga_pci_release_resource),
426	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
427	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
428
429	/* PCI interface */
430	DEVMETHOD(pci_read_config,	vga_pci_read_config),
431	DEVMETHOD(pci_write_config,	vga_pci_write_config),
432	DEVMETHOD(pci_enable_busmaster,	vga_pci_enable_busmaster),
433	DEVMETHOD(pci_disable_busmaster, vga_pci_disable_busmaster),
434	DEVMETHOD(pci_enable_io,	vga_pci_enable_io),
435	DEVMETHOD(pci_disable_io,	vga_pci_disable_io),
436	DEVMETHOD(pci_get_vpd_ident,	vga_pci_get_vpd_ident),
437	DEVMETHOD(pci_get_vpd_readonly,	vga_pci_get_vpd_readonly),
438	DEVMETHOD(pci_get_powerstate,	vga_pci_get_powerstate),
439	DEVMETHOD(pci_set_powerstate,	vga_pci_set_powerstate),
440	DEVMETHOD(pci_assign_interrupt,	vga_pci_assign_interrupt),
441	DEVMETHOD(pci_find_cap,		vga_pci_find_cap),
442	DEVMETHOD(pci_find_extcap,	vga_pci_find_extcap),
443	DEVMETHOD(pci_find_htcap,	vga_pci_find_htcap),
444	DEVMETHOD(pci_alloc_msi,	vga_pci_alloc_msi),
445	DEVMETHOD(pci_alloc_msix,	vga_pci_alloc_msix),
446	DEVMETHOD(pci_remap_msix,	vga_pci_remap_msix),
447	DEVMETHOD(pci_release_msi,	vga_pci_release_msi),
448	DEVMETHOD(pci_msi_count,	vga_pci_msi_count),
449	DEVMETHOD(pci_msix_count,	vga_pci_msix_count),
450
451	{ 0, 0 }
452};
453
454static driver_t vga_pci_driver = {
455	"vgapci",
456	vga_pci_methods,
457	sizeof(struct vga_pci_softc),
458};
459
460static devclass_t vga_devclass;
461
462DRIVER_MODULE(vgapci, pci, vga_pci_driver, vga_devclass, 0, 0);
463