OsdHardware.c revision 170143
167760Smsmith/*-
280071Smsmith * Copyright (c) 2000, 2001 Michael Smith
367760Smsmith * Copyright (c) 2000 BSDi
467760Smsmith * All rights reserved.
567760Smsmith *
667760Smsmith * Redistribution and use in source and binary forms, with or without
767760Smsmith * modification, are permitted provided that the following conditions
867760Smsmith * are met:
967760Smsmith * 1. Redistributions of source code must retain the above copyright
1067760Smsmith *    notice, this list of conditions and the following disclaimer.
1167760Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1267760Smsmith *    notice, this list of conditions and the following disclaimer in the
1367760Smsmith *    documentation and/or other materials provided with the distribution.
1467760Smsmith *
1567760Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1667760Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1767760Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1867760Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1967760Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2067760Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2167760Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2267760Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2367760Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2467760Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2567760Smsmith * SUCH DAMAGE.
2667760Smsmith */
2767760Smsmith
2867760Smsmith/*
2967760Smsmith * 6.7 : Hardware Abstraction
3067760Smsmith */
3167760Smsmith
32148318Snjl#include <sys/cdefs.h>
33148318Snjl__FBSDID("$FreeBSD: head/sys/dev/acpica/Osd/OsdHardware.c 170143 2007-05-31 00:52:32Z njl $");
34148318Snjl
35150003Sobrien#include <contrib/dev/acpica/acpi.h>
3667760Smsmith
37170143Snjl#include <sys/bus.h>
38157245Snjl#include <sys/kernel.h>
3967760Smsmith#include <machine/bus.h>
4067760Smsmith#include <machine/pci_cfgreg.h>
41170143Snjl#include <dev/acpica/acpivar.h>
42114246Snjl#include <dev/pci/pcireg.h>
4367760Smsmith
4467760Smsmith/*
4567760Smsmith * ACPICA's rather gung-ho approach to hardware resource ownership is a little
46157452Snjl * troublesome insofar as there is no easy way for us to know in advance
4767760Smsmith * exactly which I/O resources it's going to want to use.
48157452Snjl *
4967760Smsmith * In order to deal with this, we ignore resource ownership entirely, and simply
5067760Smsmith * use the native I/O space accessor functionality.  This is Evil, but it works.
5167760Smsmith *
5267760Smsmith * XXX use an intermediate #define for the tag/handle
5367760Smsmith */
5467760Smsmith
5584446Sdfr#ifdef __i386__
5667760Smsmith#define ACPI_BUS_SPACE_IO	I386_BUS_SPACE_IO
5767760Smsmith#define ACPI_BUS_HANDLE		0
5884446Sdfr#endif
5984446Sdfr#ifdef __ia64__
6084446Sdfr#define ACPI_BUS_SPACE_IO	IA64_BUS_SPACE_IO
6184446Sdfr#define ACPI_BUS_HANDLE		0
6284446Sdfr#endif
63115427Speter#ifdef __amd64__
64115427Speter#define ACPI_BUS_SPACE_IO	AMD64_BUS_SPACE_IO
65115427Speter#define ACPI_BUS_HANDLE		0
66115427Speter#endif
6767760Smsmith
68157245Snjl/*
69157245Snjl * Some BIOS vendors use AML to read/write directly to IO space.  This
70157245Snjl * can cause a problem if such accesses interfere with the OS's access to
71157245Snjl * the same ports.  Windows XP and newer systems block accesses to certain
72157245Snjl * IO ports.  We print a message or block accesses based on a tunable.
73157245Snjl */
74157245Snjlstatic int illegal_bios_ports[] = {
75157245Snjl	0x000, 0x00f,	/* DMA controller 1 */
76157245Snjl	0x020, 0x021,	/* PIC */
77157245Snjl	0x040, 0x043,	/* Timer 1 */
78157245Snjl	0x048, 0x04b,	/* Timer 2 failsafe */
79157245Snjl	0x070, 0x071,	/* CMOS and RTC */
80157245Snjl	0x074, 0x076,	/* Extended CMOS */
81157245Snjl	0x081, 0x083,	/* DMA1 page registers */
82157245Snjl	0x087, 0x087,	/* DMA1 ch0 low page */
83157245Snjl	0x089, 0x08b,	/* DMA2 ch2 (0x89), ch3 low page (0x8a, 0x8b) */
84157245Snjl	0x08f, 0x091,	/* DMA2 low page refresh (0x8f) */
85157245Snjl			/* Arb ctrl port, card select feedback (0x90, 0x91) */
86157245Snjl	0x093, 0x094,	/* System board setup */
87157245Snjl	0x096, 0x097,	/* POS channel select */
88157245Snjl	0x0a0, 0x0a1,	/* PIC (cascaded) */
89157245Snjl	0x0c0, 0x0df,	/* ISA DMA */
90157245Snjl	0x4d0, 0x4d1,	/* PIC ELCR (edge/level control) */
91157245Snjl	0xcf8, 0xcff,	/* PCI config space. Microsoft adds 0xd00 also but
92157245Snjl			   that seems incorrect. */
93157245Snjl	-1, -1
94157245Snjl};
95157245Snjl
96157245Snjl/* Block accesses to bad IO port addresses or just print a warning. */
97157245Snjlstatic int block_bad_io;
98157245SnjlTUNABLE_INT("debug.acpi.block_bad_io", &block_bad_io);
99157245Snjl
100157245Snjl/*
101157245Snjl * Look up bad ports in our table.  Returns 0 if ok, 1 if marked bad but
102157245Snjl * access is still allowed, or -1 to deny access.
103157245Snjl */
104157245Snjlstatic int
105157245Snjlacpi_os_check_port(UINT32 addr, UINT32 width)
106157245Snjl{
107157245Snjl	int error, *port;
108157245Snjl
109157245Snjl	error = 0;
110157245Snjl	for (port = illegal_bios_ports; *port != -1; port += 2) {
111157245Snjl		if ((addr >= port[0] && addr <= port[1]) ||
112157452Snjl		    (addr < port[0] && addr + (width / 8) > port[0])) {
113157245Snjl			if (block_bad_io)
114157245Snjl			    error = -1;
115157245Snjl			else
116157245Snjl			    error = 1;
117157245Snjl			break;
118157245Snjl		}
119157245Snjl	}
120157245Snjl
121157245Snjl	return (error);
122157245Snjl}
123157245Snjl
12480071SmsmithACPI_STATUS
125128225SnjlAcpiOsReadPort(ACPI_IO_ADDRESS InPort, UINT32 *Value, UINT32 Width)
12667760Smsmith{
127157245Snjl    int error;
128157245Snjl
129157245Snjl    error = acpi_os_check_port(InPort, Width);
130157245Snjl    if (error != 0) {
131162597Shrs	if (bootverbose)
132162597Shrs		printf("acpi: bad read from port 0x%03x (%d)\n",
133162597Shrs			(int)InPort, Width);
134157245Snjl	if (error == -1)
135157245Snjl	    return (AE_BAD_PARAMETER);
136157245Snjl    }
137157245Snjl
13880071Smsmith    switch (Width) {
13980071Smsmith    case 8:
140128225Snjl        *(u_int8_t *)Value = bus_space_read_1(ACPI_BUS_SPACE_IO,
141128225Snjl	    ACPI_BUS_HANDLE, InPort);
14280071Smsmith        break;
14380071Smsmith    case 16:
144128225Snjl        *(u_int16_t *)Value = bus_space_read_2(ACPI_BUS_SPACE_IO,
145128225Snjl	    ACPI_BUS_HANDLE, InPort);
14680071Smsmith        break;
14780071Smsmith    case 32:
148128225Snjl        *(u_int32_t *)Value = bus_space_read_4(ACPI_BUS_SPACE_IO,
149128225Snjl	    ACPI_BUS_HANDLE, InPort);
15080071Smsmith        break;
15180071Smsmith    default:
15280071Smsmith        /* debug trap goes here */
15392666Speter	break;
15480071Smsmith    }
15567760Smsmith
156128225Snjl    return (AE_OK);
15767760Smsmith}
15867760Smsmith
15967760SmsmithACPI_STATUS
160128225SnjlAcpiOsWritePort(ACPI_IO_ADDRESS OutPort, UINT32	Value, UINT32 Width)
16167760Smsmith{
162157245Snjl    int error;
163157245Snjl
164157245Snjl    error = acpi_os_check_port(OutPort, Width);
165157245Snjl    if (error != 0) {
166162597Shrs	if (bootverbose)
167162597Shrs		printf("acpi: bad write to port 0x%03x (%d), val %#x\n",
168162597Shrs			(int)OutPort, Width, Value);
169157245Snjl	if (error == -1)
170157245Snjl	    return (AE_BAD_PARAMETER);
171157245Snjl    }
172157245Snjl
17380071Smsmith    switch (Width) {
17480071Smsmith    case 8:
17580071Smsmith        bus_space_write_1(ACPI_BUS_SPACE_IO, ACPI_BUS_HANDLE, OutPort, Value);
17680071Smsmith        break;
17780071Smsmith    case 16:
17880071Smsmith        bus_space_write_2(ACPI_BUS_SPACE_IO, ACPI_BUS_HANDLE, OutPort, Value);
17980071Smsmith        break;
18080071Smsmith    case 32:
18180071Smsmith        bus_space_write_4(ACPI_BUS_SPACE_IO, ACPI_BUS_HANDLE, OutPort, Value);
18280071Smsmith        break;
18380071Smsmith    default:
18480071Smsmith        /* debug trap goes here */
18592666Speter	break;
18680071Smsmith    }
18767760Smsmith
188128225Snjl    return (AE_OK);
18967760Smsmith}
19067760Smsmith
19167760SmsmithACPI_STATUS
192128225SnjlAcpiOsReadPciConfiguration(ACPI_PCI_ID *PciId, UINT32 Register, void *Value,
193128225Snjl    UINT32 Width)
19467760Smsmith{
19580071Smsmith    u_int32_t	byte_width = Width / 8;
19680071Smsmith    u_int32_t	val;
19767760Smsmith
19867760Smsmith    if (!pci_cfgregopen())
199128225Snjl        return (AE_NOT_EXIST);
20067760Smsmith
201128225Snjl    val = pci_cfgregread(PciId->Bus, PciId->Device, PciId->Function, Register,
202128225Snjl	byte_width);
20380071Smsmith    switch (Width) {
20480071Smsmith    case 8:
20580071Smsmith	*(u_int8_t *)Value = val & 0xff;
20680071Smsmith	break;
20780071Smsmith    case 16:
20880071Smsmith	*(u_int16_t *)Value = val & 0xffff;
20980071Smsmith	break;
21080071Smsmith    case 32:
21180071Smsmith	*(u_int32_t *)Value = val;
21280071Smsmith	break;
21380071Smsmith    default:
21480071Smsmith	/* debug trap goes here */
21592666Speter	break;
21680071Smsmith    }
217157452Snjl
218128225Snjl    return (AE_OK);
21967760Smsmith}
22067760Smsmith
22180071Smsmith
22267760SmsmithACPI_STATUS
223128225SnjlAcpiOsWritePciConfiguration (ACPI_PCI_ID *PciId, UINT32 Register,
224128225Snjl    ACPI_INTEGER Value, UINT32 Width)
22567760Smsmith{
22680071Smsmith    u_int32_t	byte_width = Width / 8;
22767760Smsmith
22867760Smsmith    if (!pci_cfgregopen())
229128225Snjl    	return (AE_NOT_EXIST);
23067760Smsmith
231128225Snjl    pci_cfgregwrite(PciId->Bus, PciId->Device, PciId->Function, Register,
232128225Snjl	Value, byte_width);
23380071Smsmith
234128225Snjl    return (AE_OK);
23567760Smsmith}
236114246Snjl
237114246Snjl/*
238114246Snjl * Depth-first recursive case for finding the bus, given the slot/function.
239114246Snjl */
240114246Snjlstatic int
241114246Snjlacpi_bus_number(ACPI_HANDLE root, ACPI_HANDLE curr, ACPI_PCI_ID *PciId)
242114246Snjl{
243114246Snjl    ACPI_HANDLE parent;
244128225Snjl    ACPI_STATUS status;
245114246Snjl    ACPI_OBJECT_TYPE type;
246114246Snjl    UINT32 adr;
247114246Snjl    int bus, slot, func, class, subclass, header;
248114246Snjl
249128225Snjl    /* Try to get the _BBN object of the root, otherwise assume it is 0. */
250114246Snjl    bus = 0;
251114246Snjl    if (root == curr) {
252128225Snjl	status = acpi_GetInteger(root, "_BBN", &bus);
253128225Snjl	if (ACPI_FAILURE(status) && bootverbose)
254128225Snjl	    printf("acpi_bus_number: root bus has no _BBN, assuming 0\n");
255114246Snjl	return (bus);
256114246Snjl    }
257128225Snjl    status = AcpiGetParent(curr, &parent);
258128225Snjl    if (ACPI_FAILURE(status))
259128225Snjl	return (bus);
260157452Snjl
261128225Snjl    /* First, recurse up the tree until we find the host bus. */
262114246Snjl    bus = acpi_bus_number(root, parent, PciId);
263114246Snjl
264128225Snjl    /* Validate parent bus device type. */
265114246Snjl    if (ACPI_FAILURE(AcpiGetType(parent, &type)) || type != ACPI_TYPE_DEVICE) {
266128225Snjl	printf("acpi_bus_number: not a device, type %d\n", type);
267128225Snjl	return (bus);
268114246Snjl    }
269128225Snjl
270128225Snjl    /* Get the parent's slot and function. */
271128225Snjl    status = acpi_GetInteger(parent, "_ADR", &adr);
272170143Snjl    if (ACPI_FAILURE(status))
273128225Snjl	return (bus);
274114246Snjl    slot = ACPI_HIWORD(adr);
275114246Snjl    func = ACPI_LOWORD(adr);
276114246Snjl
277114246Snjl    /* Is this a PCI-PCI or Cardbus-PCI bridge? */
278114246Snjl    class = pci_cfgregread(bus, slot, func, PCIR_CLASS, 1);
279114246Snjl    if (class != PCIC_BRIDGE)
280128225Snjl	return (bus);
281114246Snjl    subclass = pci_cfgregread(bus, slot, func, PCIR_SUBCLASS, 1);
282128225Snjl
283128225Snjl    /* Find the header type, masking off the multifunction bit. */
284119539Sjhb    header = pci_cfgregread(bus, slot, func, PCIR_HDRTYPE, 1) & PCIM_HDRTYPE;
285119539Sjhb    if (header == PCIM_HDRTYPE_BRIDGE && subclass == PCIS_BRIDGE_PCI)
286128225Snjl	bus = pci_cfgregread(bus, slot, func, PCIR_SECBUS_1, 1);
287170143Snjl    else if (header == PCIM_HDRTYPE_CARDBUS && subclass == PCIS_BRIDGE_CARDBUS)
288128225Snjl	bus = pci_cfgregread(bus, slot, func, PCIR_SECBUS_2, 1);
289114246Snjl    return (bus);
290114246Snjl}
291114246Snjl
292114246Snjl/*
293114246Snjl * Find the bus number for a device
294114246Snjl *
295114246Snjl * rhandle: handle for the root bus
296114246Snjl * chandle: handle for the device
297114246Snjl * PciId: pointer to device slot and function, we fill out bus
298114246Snjl */
299114246Snjlvoid
300128225SnjlAcpiOsDerivePciId(ACPI_HANDLE rhandle, ACPI_HANDLE chandle, ACPI_PCI_ID **PciId)
301114246Snjl{
302114246Snjl    ACPI_HANDLE parent;
303128225Snjl    ACPI_STATUS status;
304114246Snjl    int bus;
305114246Snjl
306114246Snjl    if (pci_cfgregopen() == 0)
307128225Snjl	panic("AcpiOsDerivePciId unable to initialize pci bus");
308114246Snjl
309114246Snjl    /* Try to read _BBN for bus number if we're at the root */
310114246Snjl    bus = 0;
311114246Snjl    if (rhandle == chandle) {
312128225Snjl	status = acpi_GetInteger(rhandle, "_BBN", &bus);
313128225Snjl	if (ACPI_FAILURE(status) && bootverbose)
314128225Snjl	    printf("AcpiOsDerivePciId: root bus has no _BBN, assuming 0\n");
315114246Snjl    }
316128225Snjl
317114246Snjl    /*
318114246Snjl     * Get the parent handle and call the recursive case.  It is not
319114246Snjl     * clear why we seem to be getting a chandle that points to a child
320114246Snjl     * of the desired slot/function but passing in the parent handle
321114246Snjl     * here works.
322114246Snjl     */
323114246Snjl    if (ACPI_SUCCESS(AcpiGetParent(chandle, &parent)))
324128225Snjl	bus = acpi_bus_number(rhandle, parent, *PciId);
325114246Snjl    (*PciId)->Bus = bus;
326114246Snjl    if (bootverbose) {
327170143Snjl	printf("AcpiOsDerivePciId: %s -> bus %d dev %d func %d\n",
328170143Snjl	    acpi_name(chandle), (*PciId)->Bus, (*PciId)->Device,
329170143Snjl	    (*PciId)->Function);
330114246Snjl    }
331114246Snjl}
332