xref: /haiku/src/add-ons/kernel/busses/i2c/pch/pch_i2c_acpi.cpp (revision 3af8011358bd4c624a0979336d48dabb466171ed)
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 
18 typedef 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 
26 static status_t
27 pch_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 
36 static acpi_status
37 pch_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 
63 static status_t
64 acpi_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 
76 static status_t
77 register_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 
103 static status_t
104 init_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 
154 static void
155 uninit_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 
162 static status_t
163 register_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 
175 static float
176 supports_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 
234 driver_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