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
20device_manager_info* gDeviceManager;
21i2c_for_controller_interface* gI2c;
22acpi_module_info* gACPI;
23
24
25static void
26enable_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
40static int32
41pch_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
94static void
95set_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
103static status_t
104exec_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
257static acpi_status
258pch_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
283static status_t
284acpi_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
305acpi_status
306pch_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
360static status_t
361scan_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
371static status_t
372acquire_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
380static void
381release_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
392static status_t
393init_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%" B_PRIx32 "\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
490err:
491	if (bus->registersArea >= 0)
492		delete_area(bus->registersArea);
493	return status;
494}
495
496
497static void
498uninit_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
514module_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
522static 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
547module_info* modules[] = {
548	(module_info* )&gPchI2cAcpiDevice,
549	(module_info* )&gPchI2cPciDevice,
550	(module_info* )&sPchI2cDeviceModule,
551	NULL
552};
553