vga_pci.c revision 235846
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 235846 2012-05-23 17:09:14Z kib $");
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	device_add_child(dev, "drmn", -1);
109	bus_generic_attach(dev);
110	return (0);
111}
112
113static int
114vga_pci_suspend(device_t dev)
115{
116
117	return (bus_generic_suspend(dev));
118}
119
120static int
121vga_pci_resume(device_t dev)
122{
123
124	return (bus_generic_resume(dev));
125}
126
127/* Bus interface. */
128
129static int
130vga_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
131{
132
133	return (BUS_READ_IVAR(device_get_parent(dev), dev, which, result));
134}
135
136static int
137vga_pci_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
138{
139
140	return (EINVAL);
141}
142
143static int
144vga_pci_setup_intr(device_t dev, device_t child, struct resource *irq,
145    int flags, driver_filter_t *filter, driver_intr_t *intr, void *arg,
146    void **cookiep)
147{
148	return (BUS_SETUP_INTR(device_get_parent(dev), dev, irq, flags,
149	    filter, intr, arg, cookiep));
150}
151
152static int
153vga_pci_teardown_intr(device_t dev, device_t child, struct resource *irq,
154    void *cookie)
155{
156	return (BUS_TEARDOWN_INTR(device_get_parent(dev), dev, irq, cookie));
157}
158
159static struct resource *
160vga_pci_alloc_resource(device_t dev, device_t child, int type, int *rid,
161    u_long start, u_long end, u_long count, u_int flags)
162{
163	struct vga_pci_softc *sc;
164	int bar;
165
166	switch (type) {
167	case SYS_RES_MEMORY:
168	case SYS_RES_IOPORT:
169		/*
170		 * For BARs, we cache the resource so that we only allocate it
171		 * from the PCI bus once.
172		 */
173		bar = PCI_RID2BAR(*rid);
174		if (bar < 0 || bar > PCIR_MAX_BAR_0)
175			return (NULL);
176		sc = device_get_softc(dev);
177		if (sc->vga_res[bar].vr_res == NULL)
178			sc->vga_res[bar].vr_res = bus_alloc_resource(dev, type,
179			    rid, start, end, count, flags);
180		if (sc->vga_res[bar].vr_res != NULL)
181			sc->vga_res[bar].vr_refs++;
182		return (sc->vga_res[bar].vr_res);
183	}
184	return (bus_alloc_resource(dev, type, rid, start, end, count, flags));
185}
186
187static int
188vga_pci_release_resource(device_t dev, device_t child, int type, int rid,
189    struct resource *r)
190{
191	struct vga_pci_softc *sc;
192	int bar, error;
193
194	switch (type) {
195	case SYS_RES_MEMORY:
196	case SYS_RES_IOPORT:
197		/*
198		 * For BARs, we release the resource from the PCI bus
199		 * when the last child reference goes away.
200		 */
201		bar = PCI_RID2BAR(rid);
202		if (bar < 0 || bar > PCIR_MAX_BAR_0)
203			return (EINVAL);
204		sc = device_get_softc(dev);
205		if (sc->vga_res[bar].vr_res == NULL)
206			return (EINVAL);
207		KASSERT(sc->vga_res[bar].vr_res == r,
208		    ("vga_pci resource mismatch"));
209		if (sc->vga_res[bar].vr_refs > 1) {
210			sc->vga_res[bar].vr_refs--;
211			return (0);
212		}
213		KASSERT(sc->vga_res[bar].vr_refs > 0,
214		    ("vga_pci resource reference count underflow"));
215		error = bus_release_resource(dev, type, rid, r);
216		if (error == 0) {
217			sc->vga_res[bar].vr_res = NULL;
218			sc->vga_res[bar].vr_refs = 0;
219		}
220		return (error);
221	}
222
223	return (bus_release_resource(dev, type, rid, r));
224}
225
226/* PCI interface. */
227
228static uint32_t
229vga_pci_read_config(device_t dev, device_t child, int reg, int width)
230{
231
232	return (pci_read_config(dev, reg, width));
233}
234
235static void
236vga_pci_write_config(device_t dev, device_t child, int reg,
237    uint32_t val, int width)
238{
239
240	pci_write_config(dev, reg, val, width);
241}
242
243static int
244vga_pci_enable_busmaster(device_t dev, device_t child)
245{
246
247	return (pci_enable_busmaster(dev));
248}
249
250static int
251vga_pci_disable_busmaster(device_t dev, device_t child)
252{
253
254	return (pci_disable_busmaster(dev));
255}
256
257static int
258vga_pci_enable_io(device_t dev, device_t child, int space)
259{
260
261	device_printf(dev, "child %s requested pci_enable_io\n",
262	    device_get_nameunit(child));
263	return (pci_enable_io(dev, space));
264}
265
266static int
267vga_pci_disable_io(device_t dev, device_t child, int space)
268{
269
270	device_printf(dev, "child %s requested pci_disable_io\n",
271	    device_get_nameunit(child));
272	return (pci_disable_io(dev, space));
273}
274
275static int
276vga_pci_get_vpd_ident(device_t dev, device_t child, const char **identptr)
277{
278
279	return (pci_get_vpd_ident(dev, identptr));
280}
281
282static int
283vga_pci_get_vpd_readonly(device_t dev, device_t child, const char *kw,
284    const char **vptr)
285{
286
287	return (pci_get_vpd_readonly(dev, kw, vptr));
288}
289
290static int
291vga_pci_set_powerstate(device_t dev, device_t child, int state)
292{
293
294	device_printf(dev, "child %s requested pci_set_powerstate\n",
295	    device_get_nameunit(child));
296	return (pci_set_powerstate(dev, state));
297}
298
299static int
300vga_pci_get_powerstate(device_t dev, device_t child)
301{
302
303	device_printf(dev, "child %s requested pci_get_powerstate\n",
304	    device_get_nameunit(child));
305	return (pci_get_powerstate(dev));
306}
307
308static int
309vga_pci_assign_interrupt(device_t dev, device_t child)
310{
311
312	device_printf(dev, "child %s requested pci_assign_interrupt\n",
313	    device_get_nameunit(child));
314	return (PCI_ASSIGN_INTERRUPT(device_get_parent(dev), dev));
315}
316
317static int
318vga_pci_find_cap(device_t dev, device_t child, int capability,
319    int *capreg)
320{
321
322	return (pci_find_cap(dev, capability, capreg));
323}
324
325static int
326vga_pci_find_extcap(device_t dev, device_t child, int capability,
327    int *capreg)
328{
329
330	return (pci_find_extcap(dev, capability, capreg));
331}
332
333static int
334vga_pci_find_htcap(device_t dev, device_t child, int capability,
335    int *capreg)
336{
337
338	return (pci_find_htcap(dev, capability, capreg));
339}
340
341static int
342vga_pci_alloc_msi(device_t dev, device_t child, int *count)
343{
344	struct vga_pci_softc *sc;
345	int error;
346
347	sc = device_get_softc(dev);
348	if (sc->vga_msi_child != NULL)
349		return (EBUSY);
350	error = pci_alloc_msi(dev, count);
351	if (error == 0)
352		sc->vga_msi_child = child;
353	return (error);
354}
355
356static int
357vga_pci_alloc_msix(device_t dev, device_t child, int *count)
358{
359	struct vga_pci_softc *sc;
360	int error;
361
362	sc = device_get_softc(dev);
363	if (sc->vga_msi_child != NULL)
364		return (EBUSY);
365	error = pci_alloc_msix(dev, count);
366	if (error == 0)
367		sc->vga_msi_child = child;
368	return (error);
369}
370
371static int
372vga_pci_remap_msix(device_t dev, device_t child, int count,
373    const u_int *vectors)
374{
375	struct vga_pci_softc *sc;
376
377	sc = device_get_softc(dev);
378	if (sc->vga_msi_child != child)
379		return (ENXIO);
380	return (pci_remap_msix(dev, count, vectors));
381}
382
383static int
384vga_pci_release_msi(device_t dev, device_t child)
385{
386	struct vga_pci_softc *sc;
387	int error;
388
389	sc = device_get_softc(dev);
390	if (sc->vga_msi_child != child)
391		return (ENXIO);
392	error = pci_release_msi(dev);
393	if (error == 0)
394		sc->vga_msi_child = NULL;
395	return (error);
396}
397
398static int
399vga_pci_msi_count(device_t dev, device_t child)
400{
401
402	return (pci_msi_count(dev));
403}
404
405static int
406vga_pci_msix_count(device_t dev, device_t child)
407{
408
409	return (pci_msix_count(dev));
410}
411
412static device_method_t vga_pci_methods[] = {
413	/* Device interface */
414	DEVMETHOD(device_probe,		vga_pci_probe),
415	DEVMETHOD(device_attach,	vga_pci_attach),
416	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
417	DEVMETHOD(device_suspend,	vga_pci_suspend),
418	DEVMETHOD(device_resume,	vga_pci_resume),
419
420	/* Bus interface */
421	DEVMETHOD(bus_read_ivar,	vga_pci_read_ivar),
422	DEVMETHOD(bus_write_ivar,	vga_pci_write_ivar),
423	DEVMETHOD(bus_setup_intr,	vga_pci_setup_intr),
424	DEVMETHOD(bus_teardown_intr,	vga_pci_teardown_intr),
425	DEVMETHOD(bus_alloc_resource,	vga_pci_alloc_resource),
426	DEVMETHOD(bus_release_resource,	vga_pci_release_resource),
427	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
428	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
429
430	/* PCI interface */
431	DEVMETHOD(pci_read_config,	vga_pci_read_config),
432	DEVMETHOD(pci_write_config,	vga_pci_write_config),
433	DEVMETHOD(pci_enable_busmaster,	vga_pci_enable_busmaster),
434	DEVMETHOD(pci_disable_busmaster, vga_pci_disable_busmaster),
435	DEVMETHOD(pci_enable_io,	vga_pci_enable_io),
436	DEVMETHOD(pci_disable_io,	vga_pci_disable_io),
437	DEVMETHOD(pci_get_vpd_ident,	vga_pci_get_vpd_ident),
438	DEVMETHOD(pci_get_vpd_readonly,	vga_pci_get_vpd_readonly),
439	DEVMETHOD(pci_get_powerstate,	vga_pci_get_powerstate),
440	DEVMETHOD(pci_set_powerstate,	vga_pci_set_powerstate),
441	DEVMETHOD(pci_assign_interrupt,	vga_pci_assign_interrupt),
442	DEVMETHOD(pci_find_cap,		vga_pci_find_cap),
443	DEVMETHOD(pci_find_extcap,	vga_pci_find_extcap),
444	DEVMETHOD(pci_find_htcap,	vga_pci_find_htcap),
445	DEVMETHOD(pci_alloc_msi,	vga_pci_alloc_msi),
446	DEVMETHOD(pci_alloc_msix,	vga_pci_alloc_msix),
447	DEVMETHOD(pci_remap_msix,	vga_pci_remap_msix),
448	DEVMETHOD(pci_release_msi,	vga_pci_release_msi),
449	DEVMETHOD(pci_msi_count,	vga_pci_msi_count),
450	DEVMETHOD(pci_msix_count,	vga_pci_msix_count),
451
452	{ 0, 0 }
453};
454
455static driver_t vga_pci_driver = {
456	"vgapci",
457	vga_pci_methods,
458	sizeof(struct vga_pci_softc),
459};
460
461static devclass_t vga_devclass;
462
463DRIVER_MODULE(vgapci, pci, vga_pci_driver, vga_devclass, 0, 0);
464