1/*	$NetBSD: route80h.c,v 1.1.1.2 2018/08/16 18:17:47 jmcneill Exp $	*/
2
3#include <efi.h>
4#include <efilib.h>
5
6/* this example program changes the Reserved Page Route (RPR) bit on ICH10's General
7 * Control And Status Register (GCS) from LPC to PCI.  In practical terms, it routes
8 * outb to port 80h to the PCI bus. */
9
10#define GCS_OFFSET_ADDR 0x3410
11#define GCS_RPR_SHIFT 2
12#define GCS_RPR_PCI 1
13#define GCS_RPR_LPC 0
14
15#define VENDOR_ID_INTEL 0x8086
16#define DEVICE_ID_LPCIF 0x3a16
17#define DEVICE_ID_COUGARPOINT_LPCIF 0x1c56
18
19static EFI_HANDLE ImageHandle;
20
21typedef struct {
22	uint16_t vendor_id;	/* 00-01 */
23	uint16_t device_id;	/* 02-03 */
24	char pad[0xEB];		/* 04-EF */
25	uint32_t rcba;		/* F0-F3 */
26	uint32_t reserved[3];	/* F4-FF */
27} lpcif_t;
28
29static inline void set_bit(volatile uint32_t *flag, int bit, int value)
30{
31	uint32_t val = *flag;
32	Print(L"current value is 0x%2x\n", val);
33
34	if (value) {
35		val |= (1 << bit);
36	} else {
37		val &= ~(1 << bit);
38	}
39	Print(L"setting value to 0x%2x\n", val);
40	*flag = val;
41	val = *flag;
42	Print(L"new value is 0x%2x\n", val);
43}
44
45static int is_device(EFI_PCI_IO *pciio, uint16_t vendor_id, uint16_t device_id)
46{
47	lpcif_t lpcif;
48	EFI_STATUS rc;
49
50	rc = uefi_call_wrapper(pciio->Pci.Read, 5, pciio, EfiPciIoWidthUint16, 0, 2, &lpcif);
51	if (EFI_ERROR(rc))
52		return 0;
53
54	if (vendor_id == lpcif.vendor_id && device_id == lpcif.device_id)
55		return 1;
56	return 0;
57}
58
59static EFI_STATUS find_pci_device(uint16_t vendor_id, uint16_t device_id,
60				EFI_PCI_IO **pciio)
61{
62	EFI_STATUS rc;
63	EFI_HANDLE *Handles;
64	UINTN NoHandles, i;
65
66	if (!pciio)
67		return EFI_INVALID_PARAMETER;
68
69	rc = LibLocateHandle(ByProtocol, &PciIoProtocol, NULL, &NoHandles,
70			     &Handles);
71	if (EFI_ERROR(rc))
72		return rc;
73
74	for (i = 0; i < NoHandles; i++) {
75		void *pciio_tmp = NULL;
76		rc = uefi_call_wrapper(BS->OpenProtocol, 6, Handles[i],
77				    &PciIoProtocol, &pciio_tmp, ImageHandle,
78				    NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
79		if (EFI_ERROR(rc))
80			continue;
81		*pciio = pciio_tmp;
82		if (!is_device(*pciio, vendor_id, device_id)) {
83			*pciio = NULL;
84			continue;
85		}
86
87		return EFI_SUCCESS;
88	}
89	return EFI_NOT_FOUND;
90}
91
92EFI_STATUS
93efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab)
94{
95	InitializeLib(image_handle, systab);
96	EFI_PCI_IO *pciio = NULL;
97	lpcif_t lpcif;
98	EFI_STATUS rc = EFI_SUCCESS;
99	struct {
100		uint16_t vendor;
101		uint16_t device;
102	} devices[] = {
103		{ VENDOR_ID_INTEL, DEVICE_ID_LPCIF },
104		{ VENDOR_ID_INTEL, DEVICE_ID_COUGARPOINT_LPCIF },
105		{ 0, 0 }
106	};
107	int i;
108
109	ImageHandle = image_handle;
110	for (i = 0; devices[i].vendor != 0; i++) {
111		rc = find_pci_device(devices[i].vendor, devices[i].device, &pciio);
112		if (EFI_ERROR(rc))
113			continue;
114	}
115
116	if (rc == EFI_NOT_FOUND) {
117		Print(L"Device not found.\n");
118		return rc;
119	} else if (EFI_ERROR(rc)) {
120		return rc;
121	}
122
123	rc = uefi_call_wrapper(pciio->Pci.Read, 5, pciio, EfiPciIoWidthUint32,
124		EFI_FIELD_OFFSET(lpcif_t, rcba), 1, &lpcif.rcba);
125	if (EFI_ERROR(rc))
126		return rc;
127	if (!(lpcif.rcba & 1)) {
128		Print(L"rcrb is not mapped, cannot route port 80h\n");
129		return EFI_UNSUPPORTED;
130	}
131	lpcif.rcba &= ~1UL;
132
133	Print(L"rcba: 0x%8x\n", lpcif.rcba, lpcif.rcba);
134	set_bit((uint32_t *)(intptr_t)(lpcif.rcba + GCS_OFFSET_ADDR),
135		     GCS_RPR_SHIFT, GCS_RPR_PCI);
136
137	return EFI_SUCCESS;
138}
139