xref: /haiku/src/add-ons/kernel/busses/i2c/pch/pch_i2c.cpp (revision 4c07199d8201fcf267e90be0d24b76799d03cea6)
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 #include <bus/PCI.h>
15 
16 
17 #include "pch_i2c.h"
18 
19 
20 device_manager_info* gDeviceManager;
21 i2c_for_controller_interface* gI2c;
22 acpi_module_info* gACPI;
23 
24 
25 static void
26 enable_device(pch_i2c_sim_info* bus, bool enable)
27 {
28 	uint32 status = enable ? 1 : 0;
29 	for (int tries = 100; tries >= 0; tries--) {
30 		write32(bus->registers + PCH_IC_ENABLE, status);
31 		if ((read32(bus->registers + PCH_IC_ENABLE_STATUS) & 1) == status)
32 			return;
33 		snooze(25);
34 	}
35 
36 	ERROR("enable_device failed\n");
37 }
38 
39 
40 static int32
41 pch_i2c_interrupt_handler(pch_i2c_sim_info* bus)
42 {
43 	int32 handled = B_HANDLED_INTERRUPT;
44 
45 	// Check if this interrupt is ours
46 	uint32 enable = read32(bus->registers + PCH_IC_ENABLE);
47 	if (enable == 0)
48 		return B_UNHANDLED_INTERRUPT;
49 
50 	uint32 status = read32(bus->registers + PCH_IC_INTR_STAT);
51 	if ((status & PCH_IC_INTR_STAT_RX_UNDER) != 0)
52 		write32(bus->registers + PCH_IC_CLR_RX_UNDER, 0);
53 	if ((status & PCH_IC_INTR_STAT_RX_OVER) != 0)
54 		write32(bus->registers + PCH_IC_CLR_RX_OVER, 0);
55 	if ((status & PCH_IC_INTR_STAT_TX_OVER) != 0)
56 		write32(bus->registers + PCH_IC_CLR_TX_OVER, 0);
57 	if ((status & PCH_IC_INTR_STAT_RD_REQ) != 0)
58 		write32(bus->registers + PCH_IC_CLR_RD_REQ, 0);
59 	if ((status & PCH_IC_INTR_STAT_TX_ABRT) != 0)
60 		write32(bus->registers + PCH_IC_CLR_TX_ABRT, 0);
61 	if ((status & PCH_IC_INTR_STAT_RX_DONE) != 0)
62 		write32(bus->registers + PCH_IC_CLR_RX_DONE, 0);
63 	if ((status & PCH_IC_INTR_STAT_ACTIVITY) != 0)
64 		write32(bus->registers + PCH_IC_CLR_ACTIVITY, 0);
65 	if ((status & PCH_IC_INTR_STAT_STOP_DET) != 0)
66 		write32(bus->registers + PCH_IC_CLR_STOP_DET, 0);
67 	if ((status & PCH_IC_INTR_STAT_START_DET) != 0)
68 		write32(bus->registers + PCH_IC_CLR_START_DET, 0);
69 	if ((status & PCH_IC_INTR_STAT_GEN_CALL) != 0)
70 		write32(bus->registers + PCH_IC_CLR_GEN_CALL, 0);
71 
72 	TRACE("pch_i2c_interrupt_handler %" B_PRIx32 "\n", status);
73 
74 	if ((status & ~PCH_IC_INTR_STAT_ACTIVITY) == 0)
75 		return handled;
76 	/*if ((status & PCH_IC_INTR_STAT_TX_ABRT) != 0)
77 		tx error */
78 	if ((status & PCH_IC_INTR_STAT_RX_FULL) != 0)
79 		ConditionVariable::NotifyAll(&bus->readwait, B_OK);
80 	if ((status & PCH_IC_INTR_STAT_TX_EMPTY) != 0)
81 		ConditionVariable::NotifyAll(&bus->writewait, B_OK);
82 	if ((status & PCH_IC_INTR_STAT_STOP_DET) != 0) {
83 		bus->busy = 0;
84 		ConditionVariable::NotifyAll(&bus->busy, B_OK);
85 	}
86 
87 	return handled;
88 }
89 
90 
91 //	#pragma mark -
92 
93 
94 static void
95 set_sim(i2c_bus_cookie cookie, i2c_bus sim)
96 {
97 	CALLED();
98 	pch_i2c_sim_info* bus = (pch_i2c_sim_info*)cookie;
99 	bus->sim = sim;
100 }
101 
102 
103 static status_t
104 exec_command(i2c_bus_cookie cookie, i2c_op op, i2c_addr slaveAddress,
105 	const void *cmdBuffer, size_t cmdLength, void* dataBuffer,
106 	size_t dataLength)
107 {
108 	CALLED();
109 	pch_i2c_sim_info* bus = (pch_i2c_sim_info*)cookie;
110 
111 	if (atomic_test_and_set(&bus->busy, 1, 0) != 0)
112 		return B_BUSY;
113 
114 	TRACE("exec_command: acquired busy flag\n");
115 
116 	uint32 status = 0;
117 	for (int tries = 100; tries >= 0; tries--) {
118 		status = read32(bus->registers + PCH_IC_STATUS);
119 		if ((status & PCH_IC_STATUS_ACTIVITY) == 0)
120 			break;
121 		snooze(1000);
122 	}
123 
124 	if ((status & PCH_IC_STATUS_ACTIVITY) != 0) {
125 		bus->busy = 0;
126 		return B_BUSY;
127 	}
128 
129 	TRACE("exec_command: write slave address\n");
130 
131 	enable_device(bus, false);
132 	write32(bus->registers + PCH_IC_CON,
133 		read32(bus->registers + PCH_IC_CON) & ~PCH_IC_CON_10BIT_ADDR_MASTER);
134 	write32(bus->registers + PCH_IC_TAR, slaveAddress);
135 
136 	write32(bus->registers + PCH_IC_INTR_MASK, 0);
137 	read32(bus->registers + PCH_IC_CLR_INTR);
138 
139 	enable_device(bus, true);
140 
141 	read32(bus->registers + PCH_IC_CLR_INTR);
142 	write32(bus->registers + PCH_IC_INTR_MASK, PCH_IC_INTR_STAT_TX_EMPTY);
143 
144 	// wait for write
145 	// wait_lock
146 
147 	if (cmdLength > 0) {
148 		TRACE("exec_command: write command buffer\n");
149 		uint16 txLimit = bus->tx_fifo_depth
150 			- read32(bus->registers + PCH_IC_TXFLR);
151 		if (cmdLength > txLimit) {
152 			ERROR("exec_command can't write, cmd too long %" B_PRIuSIZE
153 				" (max %d)\n", cmdLength, txLimit);
154 			bus->busy = 0;
155 			return B_BAD_VALUE;
156 		}
157 
158 		uint8* buffer = (uint8*)cmdBuffer;
159 		for (size_t i = 0; i < cmdLength; i++) {
160 			uint32 cmd = buffer[i];
161 			if (i == cmdLength - 1 && dataLength == 0 && IS_STOP_OP(op))
162 				cmd |= PCH_IC_DATA_CMD_STOP;
163 			write32(bus->registers + PCH_IC_DATA_CMD, cmd);
164 		}
165 	}
166 
167 	TRACE("exec_command: processing buffer %" B_PRIuSIZE " bytes\n",
168 		dataLength);
169 	uint16 txLimit = bus->tx_fifo_depth
170 		- read32(bus->registers + PCH_IC_TXFLR);
171 	uint8* buffer = (uint8*)dataBuffer;
172 	size_t readPos = 0;
173 	size_t i = 0;
174 	while (i < dataLength) {
175 		uint32 cmd = PCH_IC_DATA_CMD_READ;
176 		if (IS_WRITE_OP(op))
177 			cmd = buffer[i];
178 
179 		if (i == 0 && cmdLength > 0 && IS_READ_OP(op))
180 			cmd |= PCH_IC_DATA_CMD_RESTART;
181 
182 		if (i == (dataLength - 1) && IS_STOP_OP(op))
183 			cmd |= PCH_IC_DATA_CMD_STOP;
184 
185 		write32(bus->registers + PCH_IC_DATA_CMD, cmd);
186 
187 		if (IS_READ_OP(op) && IS_BLOCK_OP(op) && readPos == 0)
188 			txLimit = 1;
189 		txLimit--;
190 		i++;
191 
192 		// here read the data if needed
193 		while (IS_READ_OP(op) && (txLimit == 0 || i == dataLength)) {
194 			write32(bus->registers + PCH_IC_INTR_MASK,
195 				PCH_IC_INTR_STAT_RX_FULL);
196 
197 			// sleep until wake up by intr handler
198 			struct ConditionVariable condition;
199 			condition.Publish(&bus->readwait, "pch_i2c");
200 			ConditionVariableEntry variableEntry;
201 			status_t status = variableEntry.Wait(&bus->readwait,
202 				B_RELATIVE_TIMEOUT, 500000L);
203 			condition.Unpublish();
204 			if (status != B_OK)
205 				ERROR("exec_command timed out waiting for read\n");
206 			uint32 rxBytes = read32(bus->registers + PCH_IC_RXFLR);
207 			if (rxBytes == 0) {
208 				ERROR("exec_command timed out reading %" B_PRIuSIZE " bytes\n",
209 					dataLength - readPos);
210 				bus->busy = 0;
211 				return B_ERROR;
212 			}
213 			for (; rxBytes > 0; rxBytes--) {
214 				uint32 read = read32(bus->registers + PCH_IC_DATA_CMD);
215 				if (readPos < dataLength)
216 					buffer[readPos++] = read;
217 			}
218 
219 			if (IS_BLOCK_OP(op) && readPos > 0 && dataLength > buffer[0])
220 				dataLength = buffer[0] + 1;
221 			if (readPos >= dataLength)
222 				break;
223 
224 			TRACE("exec_command %" B_PRIuSIZE" bytes to be read\n",
225 				dataLength - readPos);
226 			txLimit = bus->tx_fifo_depth
227 				- read32(bus->registers + PCH_IC_TXFLR);
228 		}
229 	}
230 
231 	status_t err = B_OK;
232 	if (IS_STOP_OP(op) && IS_WRITE_OP(op)) {
233 		TRACE("exec_command: waiting busy condition\n");
234 		while (bus->busy == 1) {
235 			write32(bus->registers + PCH_IC_INTR_MASK,
236 				PCH_IC_INTR_STAT_STOP_DET);
237 
238 			// sleep until wake up by intr handler
239 			struct ConditionVariable condition;
240 			condition.Publish(&bus->busy, "pch_i2c");
241 			ConditionVariableEntry variableEntry;
242 			err = variableEntry.Wait(&bus->busy, B_RELATIVE_TIMEOUT,
243 				500000L);
244 			condition.Unpublish();
245 			if (err != B_OK)
246 				ERROR("exec_command timed out waiting for busy\n");
247 		}
248 	}
249 	TRACE("exec_command: processing done\n");
250 
251 	bus->busy = 0;
252 
253 	return err;
254 }
255 
256 
257 static acpi_status
258 pch_i2c_scan_parse_callback(ACPI_RESOURCE *res, void *context)
259 {
260 	struct pch_i2c_crs* crs = (struct pch_i2c_crs*)context;
261 
262 	if (res->Type == ACPI_RESOURCE_TYPE_SERIAL_BUS &&
263 	    res->Data.CommonSerialBus.Type == ACPI_RESOURCE_SERIAL_TYPE_I2C) {
264 		crs->i2c_addr = B_LENDIAN_TO_HOST_INT16(
265 			res->Data.I2cSerialBus.SlaveAddress);
266 		return AE_CTRL_TERMINATE;
267 	} else if (res->Type == ACPI_RESOURCE_TYPE_IRQ) {
268 		crs->irq = res->Data.Irq.Interrupts[0];
269 		crs->irq_triggering = res->Data.Irq.Triggering;
270 		crs->irq_polarity = res->Data.Irq.Polarity;
271 		crs->irq_shareable = res->Data.Irq.Shareable;
272 	} else if (res->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
273 		crs->irq = res->Data.ExtendedIrq.Interrupts[0];
274 		crs->irq_triggering = res->Data.ExtendedIrq.Triggering;
275 		crs->irq_polarity = res->Data.ExtendedIrq.Polarity;
276 		crs->irq_shareable = res->Data.ExtendedIrq.Shareable;
277 	}
278 
279 	return B_OK;
280 }
281 
282 
283 static status_t
284 acpi_GetInteger(acpi_handle acpiCookie,
285 	const char* path, int64* number)
286 {
287 	acpi_data buf;
288 	acpi_object_type object;
289 	buf.pointer = &object;
290 	buf.length = sizeof(acpi_object_type);
291 
292 	// Assume that what we've been pointed at is an Integer object, or
293 	// a method that will return an Integer.
294 	status_t status = gACPI->evaluate_method(acpiCookie, path, NULL, &buf);
295 	if (status == B_OK) {
296 		if (object.object_type == ACPI_TYPE_INTEGER)
297 			*number = object.integer.integer;
298 		else
299 			status = B_BAD_VALUE;
300 	}
301 	return status;
302 }
303 
304 
305 acpi_status
306 pch_i2c_scan_bus_callback(acpi_handle object, uint32 nestingLevel,
307 	void *context, void** returnValue)
308 {
309 	pch_i2c_sim_info* bus = (pch_i2c_sim_info*)context;
310 	TRACE("pch_i2c_scan_bus_callback %p\n", object);
311 
312 	// skip absent devices
313 	int64 sta;
314 	status_t status = acpi_GetInteger(object, "_STA", &sta);
315 	if (status == B_OK && (sta & ACPI_STA_DEVICE_PRESENT) == 0)
316 		return B_OK;
317 
318 	// Attach devices for I2C resources
319 	struct pch_i2c_crs crs;
320 	status = gACPI->walk_resources(object, (ACPI_STRING)"_CRS",
321 		pch_i2c_scan_parse_callback, &crs);
322 	if (status != B_OK) {
323 		ERROR("Error while getting I2C devices\n");
324 		return status;
325 	}
326 
327 	TRACE("pch_i2c_scan_bus_callback deviceAddress %x\n", crs.i2c_addr);
328 
329 	acpi_data buffer;
330 	buffer.pointer = NULL;
331 	buffer.length = ACPI_ALLOCATE_BUFFER;
332 	status = gACPI->ns_handle_to_pathname(object, &buffer);
333 	if (status != B_OK) {
334 		ERROR("pch_i2c_scan_bus_callback ns_handle_to_pathname failed\n");
335 		return status;
336 	}
337 
338 	char* hid = NULL;
339 	char* cidList[8] = { NULL };
340 	status = gACPI->get_device_info((const char*)buffer.pointer, &hid,
341 		(char**)&cidList, 8, NULL, NULL);
342 	if (status != B_OK) {
343 		ERROR("pch_i2c_scan_bus_callback get_device_info failed\n");
344 		return status;
345 	}
346 
347 	status = gI2c->register_device(bus->sim, crs.i2c_addr, hid, cidList,
348 		object);
349 	free(hid);
350 	for (int i = 0; cidList[i] != NULL; i++)
351 		free(cidList[i]);
352 	free(buffer.pointer);
353 
354 	TRACE("pch_i2c_scan_bus_callback registered device: %s\n", strerror(status));
355 
356 	return status;
357 }
358 
359 
360 static status_t
361 scan_bus(i2c_bus_cookie cookie)
362 {
363 	CALLED();
364 	pch_i2c_sim_info* bus = (pch_i2c_sim_info*)cookie;
365 	if (bus->scan_bus != NULL)
366 		return bus->scan_bus(bus);
367 	return B_OK;
368 }
369 
370 
371 static status_t
372 acquire_bus(i2c_bus_cookie cookie)
373 {
374 	CALLED();
375 	pch_i2c_sim_info* bus = (pch_i2c_sim_info*)cookie;
376 	return mutex_lock(&bus->lock);
377 }
378 
379 
380 static void
381 release_bus(i2c_bus_cookie cookie)
382 {
383 	CALLED();
384 	pch_i2c_sim_info* bus = (pch_i2c_sim_info*)cookie;
385 	mutex_unlock(&bus->lock);
386 }
387 
388 
389 //	#pragma mark -
390 
391 
392 static status_t
393 init_bus(device_node* node, void** bus_cookie)
394 {
395 	CALLED();
396 	status_t status = B_OK;
397 
398 	driver_module_info* driver;
399 	pch_i2c_sim_info* bus;
400 	device_node* parent = gDeviceManager->get_parent_node(node);
401 	gDeviceManager->get_driver(parent, &driver, (void**)&bus);
402 	gDeviceManager->put_node(parent);
403 
404 	TRACE_ALWAYS("init_bus() addr 0x%" B_PRIxPHYSADDR " size 0x%" B_PRIx64
405 		" irq 0x%x\n", bus->base_addr, bus->map_size, bus->irq);
406 
407 	bus->registersArea = map_physical_memory("PCHI2C memory mapped registers",
408 		bus->base_addr, bus->map_size, B_ANY_KERNEL_ADDRESS,
409 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
410 		(void **)&bus->registers);
411 	// init bus
412 	bus->capabilities = read32(bus->registers + PCH_SUP_CAPABLITIES);
413 	TRACE_ALWAYS("init_bus() 0x%" B_PRIx32 " (0x%" B_PRIx32 ")\n",
414 		(bus->capabilities >> PCH_SUP_CAPABLITIES_TYPE_SHIFT)
415 			& PCH_SUP_CAPABLITIES_TYPE_MASK,
416 		bus->capabilities);
417 	if (((bus->capabilities >> PCH_SUP_CAPABLITIES_TYPE_SHIFT)
418 			& PCH_SUP_CAPABLITIES_TYPE_MASK) != 0) {
419 		status = B_ERROR;
420 		ERROR("init_bus() device type not supported\n");
421 		goto err;
422 	}
423 
424 	write32(bus->registers + PCH_SUP_RESETS, 0);
425 	write32(bus->registers + PCH_SUP_RESETS,
426 		PCH_SUP_RESETS_FUNC | PCH_SUP_RESETS_IDMA);
427 
428 	if (bus->ss_hcnt == 0)
429 		bus->ss_hcnt = read32(bus->registers + PCH_IC_SS_SCL_HCNT);
430 	if (bus->ss_lcnt == 0)
431 		bus->ss_lcnt = read32(bus->registers + PCH_IC_SS_SCL_LCNT);
432 	if (bus->fs_hcnt == 0)
433 		bus->fs_hcnt = read32(bus->registers + PCH_IC_FS_SCL_HCNT);
434 	if (bus->fs_lcnt == 0)
435 		bus->fs_lcnt = read32(bus->registers + PCH_IC_FS_SCL_LCNT);
436 	if (bus->sda_hold_time == 0)
437 		bus->sda_hold_time = read32(bus->registers + PCH_IC_SDA_HOLD);
438 	TRACE_ALWAYS("init_bus() 0x%04" B_PRIx16 " 0x%04" B_PRIx16 " 0x%04" B_PRIx16
439 		" 0x%04" B_PRIx16 " 0x%08" B_PRIx32 "\n", bus->ss_hcnt, bus->ss_lcnt,
440 		bus->fs_hcnt, bus->fs_lcnt, bus->sda_hold_time);
441 
442 	enable_device(bus, false);
443 
444 	write32(bus->registers + PCH_IC_SS_SCL_HCNT, bus->ss_hcnt);
445 	write32(bus->registers + PCH_IC_SS_SCL_LCNT, bus->ss_lcnt);
446 	write32(bus->registers + PCH_IC_FS_SCL_HCNT, bus->fs_hcnt);
447 	write32(bus->registers + PCH_IC_FS_SCL_LCNT, bus->fs_lcnt);
448 	if (bus->hs_hcnt > 0)
449 		write32(bus->registers + PCH_IC_HS_SCL_HCNT, bus->hs_hcnt);
450 	if (bus->hs_lcnt > 0)
451 		write32(bus->registers + PCH_IC_HS_SCL_LCNT, bus->hs_lcnt);
452 	{
453 		uint32 reg = read32(bus->registers + PCH_IC_COMP_VERSION);
454 		if (reg >= PCH_IC_COMP_VERSION_MIN)
455 			write32(bus->registers + PCH_IC_SDA_HOLD, bus->sda_hold_time);
456 	}
457 
458 	{
459 		bus->tx_fifo_depth = 32;
460 		bus->rx_fifo_depth = 32;
461 		uint32 reg = read32(bus->registers + PCH_IC_COMP_PARAM1);
462 		uint8 rx_fifo_depth = PCH_IC_COMP_PARAM1_RX(reg);
463 		uint8 tx_fifo_depth = PCH_IC_COMP_PARAM1_TX(reg);
464 		if (rx_fifo_depth > 1 && rx_fifo_depth < bus->rx_fifo_depth)
465 			bus->rx_fifo_depth = rx_fifo_depth;
466 		if (tx_fifo_depth > 1 && tx_fifo_depth < bus->tx_fifo_depth)
467 			bus->tx_fifo_depth = tx_fifo_depth;
468 		write32(bus->registers + PCH_IC_RX_TL, 0);
469 		write32(bus->registers + PCH_IC_TX_TL, bus->tx_fifo_depth / 2);
470 	}
471 
472 	bus->masterConfig = PCH_IC_CON_MASTER | PCH_IC_CON_SLAVE_DISABLE |
473 	    PCH_IC_CON_RESTART_EN | PCH_IC_CON_SPEED_FAST;
474 	write32(bus->registers + PCH_IC_CON, bus->masterConfig);
475 
476 	write32(bus->registers + PCH_IC_INTR_MASK, 0);
477 	read32(bus->registers + PCH_IC_CLR_INTR);
478 
479 	status = install_io_interrupt_handler(bus->irq,
480 		(interrupt_handler)pch_i2c_interrupt_handler, bus, 0);
481 	if (status != B_OK) {
482 		ERROR("install interrupt handler failed\n");
483 		goto err;
484 	}
485 
486 	mutex_init(&bus->lock, "pch_i2c");
487 	*bus_cookie = bus;
488 	return status;
489 
490 err:
491 	if (bus->registersArea >= 0)
492 		delete_area(bus->registersArea);
493 	return status;
494 }
495 
496 
497 static void
498 uninit_bus(void* bus_cookie)
499 {
500 	pch_i2c_sim_info* bus = (pch_i2c_sim_info*)bus_cookie;
501 
502 	mutex_destroy(&bus->lock);
503 	remove_io_interrupt_handler(bus->irq,
504 		(interrupt_handler)pch_i2c_interrupt_handler, bus);
505 	if (bus->registersArea >= 0)
506 		delete_area(bus->registersArea);
507 
508 }
509 
510 
511 //	#pragma mark -
512 
513 
514 module_dependency module_dependencies[] = {
515 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
516 	{ B_ACPI_MODULE_NAME, (module_info**)&gACPI },
517 	{ I2C_FOR_CONTROLLER_MODULE_NAME, (module_info**)&gI2c },
518 	{}
519 };
520 
521 
522 static i2c_sim_interface sPchI2cDeviceModule = {
523 	{
524 		{
525 			PCH_I2C_SIM_MODULE_NAME,
526 			0,
527 			NULL
528 		},
529 
530 		NULL,	// supports device
531 		NULL,	// register device
532 		init_bus,
533 		uninit_bus,
534 		NULL,	// register child devices
535 		NULL,	// rescan
536 		NULL, 	// device removed
537 	},
538 
539 	set_sim,
540 	exec_command,
541 	scan_bus,
542 	acquire_bus,
543 	release_bus,
544 };
545 
546 
547 module_info* modules[] = {
548 	(module_info* )&gPchI2cAcpiDevice,
549 	(module_info* )&gPchI2cPciDevice,
550 	(module_info* )&sPchI2cDeviceModule,
551 	NULL
552 };
553