1/*
2 * Copyright 2020, J��r��me Duval, jerome.duval@gmail.com.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <new>
8#include <stdio.h>
9#include <string.h>
10
11#include <ACPI.h>
12#include <ByteOrder.h>
13#include <condition_variable.h>
14
15#include "pch_i2c.h"
16
17
18typedef struct {
19	pch_i2c_sim_info info;
20	acpi_device_module_info* acpi;
21	acpi_device device;
22
23} pch_i2c_acpi_sim_info;
24
25
26static status_t
27pch_i2c_acpi_set_powerstate(pch_i2c_acpi_sim_info* info, uint8 power)
28{
29	status_t status = info->acpi->evaluate_method(info->device,
30		power == 1 ? "_PS0" : "_PS3", NULL, NULL);
31	return status;
32}
33
34
35
36static acpi_status
37pch_i2c_scan_parse_callback(ACPI_RESOURCE *res, void *context)
38{
39	struct pch_i2c_crs* crs = (struct pch_i2c_crs*)context;
40
41	if (res->Type == ACPI_RESOURCE_TYPE_IRQ) {
42		crs->irq = res->Data.Irq.Interrupts[0];
43		crs->irq_triggering = res->Data.Irq.Triggering;
44		crs->irq_polarity = res->Data.Irq.Polarity;
45		crs->irq_shareable = res->Data.Irq.Shareable;
46	} else if (res->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
47		crs->irq = res->Data.ExtendedIrq.Interrupts[0];
48		crs->irq_triggering = res->Data.ExtendedIrq.Triggering;
49		crs->irq_polarity = res->Data.ExtendedIrq.Polarity;
50		crs->irq_shareable = res->Data.ExtendedIrq.Shareable;
51	} else if (res->Type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
52		crs->addr_bas = res->Data.FixedMemory32.Address;
53		crs->addr_len = res->Data.FixedMemory32.AddressLength;
54	}
55
56	return B_OK;
57}
58
59
60//	#pragma mark -
61
62
63static status_t
64acpi_scan_bus(i2c_bus_cookie cookie)
65{
66	CALLED();
67	pch_i2c_acpi_sim_info* bus = (pch_i2c_acpi_sim_info*)cookie;
68
69	bus->acpi->walk_namespace(bus->device, ACPI_TYPE_DEVICE, 1,
70		pch_i2c_scan_bus_callback, NULL, bus, NULL);
71
72	return B_OK;
73}
74
75
76static status_t
77register_child_devices(void* cookie)
78{
79	CALLED();
80
81	pch_i2c_acpi_sim_info* bus = (pch_i2c_acpi_sim_info*)cookie;
82	device_node* node = bus->info.driver_node;
83
84	char prettyName[25];
85	sprintf(prettyName, "PCH I2C Controller");
86
87	device_attr attrs[] = {
88		// properties of this controller for i2c bus manager
89		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
90			{ .string = prettyName }},
91		{ B_DEVICE_FIXED_CHILD, B_STRING_TYPE,
92			{ .string = I2C_FOR_CONTROLLER_MODULE_NAME }},
93
94		// private data to identify the device
95		{ NULL }
96	};
97
98	return gDeviceManager->register_node(node, PCH_I2C_SIM_MODULE_NAME,
99		attrs, NULL, NULL);
100}
101
102
103static status_t
104init_device(device_node* node, void** device_cookie)
105{
106	CALLED();
107	status_t status = B_OK;
108
109	pch_i2c_acpi_sim_info* bus = (pch_i2c_acpi_sim_info*)calloc(1,
110		sizeof(pch_i2c_acpi_sim_info));
111	if (bus == NULL)
112		return B_NO_MEMORY;
113
114	acpi_device_module_info* acpi;
115	acpi_device device;
116	{
117		device_node* acpiParent = gDeviceManager->get_parent_node(node);
118		gDeviceManager->get_driver(acpiParent, (driver_module_info**)&acpi,
119			(void**)&device);
120		gDeviceManager->put_node(acpiParent);
121	}
122
123	bus->acpi = acpi;
124	bus->device = device;
125	bus->info.driver_node = node;
126	bus->info.scan_bus = acpi_scan_bus;
127
128	// Attach devices for I2C resources
129	struct pch_i2c_crs crs;
130	status = acpi->walk_resources(device, (ACPI_STRING)"_CRS",
131		pch_i2c_scan_parse_callback, &crs);
132	if (status != B_OK) {
133		ERROR("Error while getting I2C devices\n");
134		free(bus);
135		return status;
136	}
137	if (crs.addr_bas == 0 || crs.addr_len == 0) {
138		TRACE("skipping non configured I2C devices\n");
139		free(bus);
140		return B_BAD_VALUE;
141	}
142
143	bus->info.base_addr = crs.addr_bas;
144	bus->info.map_size = crs.addr_len;
145	bus->info.irq = crs.irq;
146
147	pch_i2c_acpi_set_powerstate(bus, 1);
148
149	*device_cookie = bus;
150	return B_OK;
151}
152
153
154static void
155uninit_device(void* device_cookie)
156{
157	pch_i2c_acpi_sim_info* bus = (pch_i2c_acpi_sim_info*)device_cookie;
158	free(bus);
159}
160
161
162static status_t
163register_device(device_node* parent)
164{
165	device_attr attrs[] = {
166		{B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "PCH I2C ACPI"}},
167		{}
168	};
169
170	return gDeviceManager->register_node(parent,
171		PCH_I2C_ACPI_DEVICE_MODULE_NAME, attrs, NULL, NULL);
172}
173
174
175static float
176supports_device(device_node* parent)
177{
178	CALLED();
179	const char* bus;
180
181	// make sure parent is a PCH I2C ACPI device node
182	if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)
183		< B_OK) {
184		return -1;
185	}
186
187	if (strcmp(bus, "acpi") != 0)
188		return 0.0f;
189
190	TRACE("found an acpi node\n");
191
192	// check whether it's really a device
193	uint32 device_type;
194	if (gDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM,
195			&device_type, false) != B_OK
196		|| device_type != ACPI_TYPE_DEVICE) {
197		return 0.0;
198	}
199	TRACE("found an acpi device\n");
200
201	// check whether it's a PCH I2C device
202	const char *name;
203	if (gDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &name,
204		false) != B_OK) {
205		return 0.0;
206	}
207	TRACE("found an acpi device hid %s\n", name);
208
209	if (strcmp(name, "INT33C2") == 0
210		|| strcmp(name, "INT33C3") == 0
211		|| strcmp(name, "INT3432") == 0
212		|| strcmp(name, "INT3433") == 0
213		|| strcmp(name, "INT3442") == 0
214		|| strcmp(name, "INT3443") == 0
215		|| strcmp(name, "INT3444") == 0
216		|| strcmp(name, "INT3445") == 0
217		|| strcmp(name, "INT3446") == 0
218		|| strcmp(name, "INT3447") == 0
219		|| strcmp(name, "80860AAC") == 0
220		|| strcmp(name, "80865AAC") == 0
221		|| strcmp(name, "80860F41") == 0
222		|| strcmp(name, "808622C1") == 0) {
223		TRACE("PCH I2C device found! name %s\n", name);
224		return 0.6f;
225	}
226
227	return 0.0f;
228}
229
230
231//	#pragma mark -
232
233
234driver_module_info gPchI2cAcpiDevice = {
235	{
236		PCH_I2C_ACPI_DEVICE_MODULE_NAME,
237		0,
238		NULL
239	},
240
241	supports_device,
242	register_device,
243	init_device,
244	uninit_device,
245	register_child_devices,
246	NULL,	// rescan
247	NULL,	// device removed
248};
249
250
251