xref: /haiku/src/add-ons/kernel/busses/mmc/sdhci_acpi.cpp (revision a03edeab4d237c1cdb6490da30f160b3115cbe43)
11cdb3bebSSED4906 /*
21cdb3bebSSED4906  * Copyright 2018-2024 Haiku, Inc. All rights reserved.
31cdb3bebSSED4906  * Distributed under the terms of the MIT License.
41cdb3bebSSED4906  *
51cdb3bebSSED4906  * Authors:
61cdb3bebSSED4906  *		B Krishnan Iyer, krishnaniyer97@gmail.com
71cdb3bebSSED4906  *		Adrien Destugues, pulkomandy@pulkomandy.tk
81cdb3bebSSED4906  *		Ron Ben Aroya, sed4906birdie@gmail.com
91cdb3bebSSED4906  */
101cdb3bebSSED4906 #include <algorithm>
111cdb3bebSSED4906 #include <new>
121cdb3bebSSED4906 #include <stdio.h>
131cdb3bebSSED4906 #include <string.h>
141cdb3bebSSED4906 
151cdb3bebSSED4906 #include <bus/PCI.h>
161cdb3bebSSED4906 #include <ACPI.h>
171cdb3bebSSED4906 #include "acpi.h"
181cdb3bebSSED4906 
191cdb3bebSSED4906 #include <KernelExport.h>
201cdb3bebSSED4906 
211cdb3bebSSED4906 #include "IOSchedulerSimple.h"
221cdb3bebSSED4906 #include "mmc.h"
231cdb3bebSSED4906 #include "sdhci.h"
241cdb3bebSSED4906 
251cdb3bebSSED4906 
26*a03edeabSAugustin Cavalier //#define TRACE_SDHCI
271cdb3bebSSED4906 #ifdef TRACE_SDHCI
281cdb3bebSSED4906 #	define TRACE(x...) dprintf("\33[33msdhci:\33[0m " x)
291cdb3bebSSED4906 #else
301cdb3bebSSED4906 #	define TRACE(x...) ;
311cdb3bebSSED4906 #endif
321cdb3bebSSED4906 #define TRACE_ALWAYS(x...)	dprintf("\33[33msdhci:\33[0m " x)
331cdb3bebSSED4906 #define ERROR(x...)			dprintf("\33[33msdhci:\33[0m " x)
341cdb3bebSSED4906 #define CALLED(x...)		TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
351cdb3bebSSED4906 
361cdb3bebSSED4906 
371cdb3bebSSED4906 #define SDHCI_DEVICE_MODULE_NAME "busses/mmc/sdhci/driver_v1"
381cdb3bebSSED4906 #define SDHCI_ACPI_MMC_BUS_MODULE_NAME "busses/mmc/sdhci/acpi/device/v1"
391cdb3bebSSED4906 
401cdb3bebSSED4906 static acpi_status
sdhci_acpi_scan_parse_callback(ACPI_RESOURCE * res,void * context)411cdb3bebSSED4906 sdhci_acpi_scan_parse_callback(ACPI_RESOURCE *res, void *context)
421cdb3bebSSED4906 {
431cdb3bebSSED4906 	struct sdhci_crs* crs = (struct sdhci_crs*)context;
441cdb3bebSSED4906 
451cdb3bebSSED4906 	if (res->Type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
461cdb3bebSSED4906 		crs->addr_bas = res->Data.FixedMemory32.Address;
471cdb3bebSSED4906 		crs->addr_len = res->Data.FixedMemory32.AddressLength;
481cdb3bebSSED4906 	} else if (res->Type == ACPI_RESOURCE_TYPE_IRQ) {
491cdb3bebSSED4906 		crs->irq = res->Data.Irq.Interrupt;
501cdb3bebSSED4906 		//crs->irq_triggering = res->Data.Irq.Triggering;
511cdb3bebSSED4906 		//crs->irq_polarity = res->Data.Irq.Polarity;
521cdb3bebSSED4906 		//crs->irq_shareable = res->Data.Irq.Shareable;
531cdb3bebSSED4906 	} else if (res->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
541cdb3bebSSED4906 		crs->irq = res->Data.ExtendedIrq.Interrupt;
551cdb3bebSSED4906 		//crs->irq_triggering = res->Data.ExtendedIrq.Triggering;
561cdb3bebSSED4906 		//crs->irq_polarity = res->Data.ExtendedIrq.Polarity;
571cdb3bebSSED4906 		//crs->irq_shareable = res->Data.ExtendedIrq.Shareable;
581cdb3bebSSED4906 	}
591cdb3bebSSED4906 
601cdb3bebSSED4906 	return B_OK;
611cdb3bebSSED4906 }
621cdb3bebSSED4906 
631cdb3bebSSED4906 status_t
init_bus_acpi(device_node * node,void ** bus_cookie)641cdb3bebSSED4906 init_bus_acpi(device_node* node, void** bus_cookie)
651cdb3bebSSED4906 {
661cdb3bebSSED4906 	CALLED();
671cdb3bebSSED4906 
681cdb3bebSSED4906 	// Get the ACPI driver and device
691cdb3bebSSED4906 	acpi_device_module_info* acpi;
701cdb3bebSSED4906 	acpi_device device;
711cdb3bebSSED4906 
721cdb3bebSSED4906 	device_node* parent = gDeviceManager->get_parent_node(node);
731cdb3bebSSED4906 	device_node* acpiParent = gDeviceManager->get_parent_node(parent);
741cdb3bebSSED4906 	gDeviceManager->get_driver(acpiParent, (driver_module_info**)&acpi,
751cdb3bebSSED4906 			(void**)&device);
761cdb3bebSSED4906 	gDeviceManager->put_node(acpiParent);
771cdb3bebSSED4906 	gDeviceManager->put_node(parent);
781cdb3bebSSED4906 
791cdb3bebSSED4906 	// Ignore invalid bars
801cdb3bebSSED4906 	TRACE("Register SD bus\n");
811cdb3bebSSED4906 
821cdb3bebSSED4906 	struct sdhci_crs crs;
831cdb3bebSSED4906 	if(acpi->walk_resources(device, (ACPI_STRING)"_CRS",
841cdb3bebSSED4906 			sdhci_acpi_scan_parse_callback, &crs) != B_OK) {
851cdb3bebSSED4906 		ERROR("Couldn't scan ACPI register set\n");
861cdb3bebSSED4906 		return B_IO_ERROR;
871cdb3bebSSED4906 	}
881cdb3bebSSED4906 
891cdb3bebSSED4906 	TRACE("addr: %" B_PRIx32 " len: %" B_PRIx32 "\n", crs.addr_bas, crs.addr_len);
901cdb3bebSSED4906 
911cdb3bebSSED4906 	if (crs.addr_bas == 0 || crs.addr_len == 0) {
921cdb3bebSSED4906 		ERROR("No registers to map\n");
931cdb3bebSSED4906 		return B_IO_ERROR;
941cdb3bebSSED4906 	}
951cdb3bebSSED4906 
961cdb3bebSSED4906 	// map the slot registers
971cdb3bebSSED4906 	area_id	regs_area;
981cdb3bebSSED4906 	struct registers* _regs;
991cdb3bebSSED4906 	regs_area = map_physical_memory("sdhc_regs_map",
1001cdb3bebSSED4906 		crs.addr_bas, crs.addr_len, B_ANY_KERNEL_BLOCK_ADDRESS,
1011cdb3bebSSED4906 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&_regs);
1021cdb3bebSSED4906 
1031cdb3bebSSED4906 	if (regs_area < B_OK) {
1041cdb3bebSSED4906 		ERROR("Could not map registers\n");
1051cdb3bebSSED4906 		return B_BAD_VALUE;
1061cdb3bebSSED4906 	}
1071cdb3bebSSED4906 
1081cdb3bebSSED4906 	// the interrupt is shared between all busses in an SDHC controller, but
1091cdb3bebSSED4906 	// they each register an handler. Not a problem, we will just test the
1101cdb3bebSSED4906 	// interrupt registers for all busses one after the other and find no
1111cdb3bebSSED4906 	// interrupts on the idle busses.
1121cdb3bebSSED4906 	uint8_t irq = crs.irq;
1131cdb3bebSSED4906 	TRACE("irq interrupt line: %d\n", irq);
1141cdb3bebSSED4906 
1151cdb3bebSSED4906 	SdhciBus* bus = new(std::nothrow) SdhciBus(_regs, irq, true);
1161cdb3bebSSED4906 
1171cdb3bebSSED4906 	status_t status = B_NO_MEMORY;
1181cdb3bebSSED4906 	if (bus != NULL)
1191cdb3bebSSED4906 		status = bus->InitCheck();
1201cdb3bebSSED4906 
1211cdb3bebSSED4906 	if (status != B_OK) {
1221cdb3bebSSED4906 		if (bus != NULL)
1231cdb3bebSSED4906 			delete bus;
1241cdb3bebSSED4906 		else
1251cdb3bebSSED4906 			delete_area(regs_area);
1261cdb3bebSSED4906 		return status;
1271cdb3bebSSED4906 	}
1281cdb3bebSSED4906 
1291cdb3bebSSED4906 	// Store the created object as a cookie, allowing users of the bus to
1301cdb3bebSSED4906 	// locate it.
1311cdb3bebSSED4906 	*bus_cookie = bus;
1321cdb3bebSSED4906 
1331cdb3bebSSED4906 	return status;
1341cdb3bebSSED4906 }
1351cdb3bebSSED4906 
1361cdb3bebSSED4906 status_t
register_child_devices_acpi(void * cookie)1371cdb3bebSSED4906 register_child_devices_acpi(void* cookie)
1381cdb3bebSSED4906 {
1391cdb3bebSSED4906 	CALLED();
1401cdb3bebSSED4906 	SdhciDevice* context = (SdhciDevice*)cookie;
1411cdb3bebSSED4906 	device_node* parent = gDeviceManager->get_parent_node(context->fNode);
1421cdb3bebSSED4906 	acpi_device_module_info* acpi;
1431cdb3bebSSED4906 	acpi_device* device;
1441cdb3bebSSED4906 
1451cdb3bebSSED4906 	gDeviceManager->get_driver(parent, (driver_module_info**)&acpi,
1461cdb3bebSSED4906 		(void**)&device);
1471cdb3bebSSED4906 
1481cdb3bebSSED4906 	TRACE("register_child_devices\n");
1491cdb3bebSSED4906 
1501cdb3bebSSED4906 	char prettyName[25];
1511cdb3bebSSED4906 
1521cdb3bebSSED4906 	sprintf(prettyName, "SDHC bus");
1531cdb3bebSSED4906 	device_attr attrs[] = {
1541cdb3bebSSED4906 		// properties of this controller for mmc bus manager
1551cdb3bebSSED4906 		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = prettyName } },
1561cdb3bebSSED4906 		{ B_DEVICE_FIXED_CHILD, B_STRING_TYPE,
1571cdb3bebSSED4906 			{.string = MMC_BUS_MODULE_NAME} },
1581cdb3bebSSED4906 		{ B_DEVICE_BUS, B_STRING_TYPE, {.string = "mmc"} },
1591cdb3bebSSED4906 
1601cdb3bebSSED4906 		// DMA properties
1611cdb3bebSSED4906 		// The high alignment is to force access only to complete sectors
1621cdb3bebSSED4906 		// These constraints could be removed by using ADMA which allows
1631cdb3bebSSED4906 		// use of the full 64bit address space and can do scatter-gather.
1641cdb3bebSSED4906 		{ B_DMA_ALIGNMENT, B_UINT32_TYPE, { .ui32 = 511 }},
1651cdb3bebSSED4906 		{ B_DMA_HIGH_ADDRESS, B_UINT64_TYPE, { .ui64 = 0x100000000LL }},
1661cdb3bebSSED4906 		{ B_DMA_BOUNDARY, B_UINT32_TYPE, { .ui32 = (1 << 19) - 1 }},
1671cdb3bebSSED4906 		{ B_DMA_MAX_SEGMENT_COUNT, B_UINT32_TYPE, { .ui32 = 1 }},
1681cdb3bebSSED4906 		{ B_DMA_MAX_SEGMENT_BLOCKS, B_UINT32_TYPE, { .ui32 = (1 << 10) - 1 }},
1691cdb3bebSSED4906 
1701cdb3bebSSED4906 		// private data to identify device
1711cdb3bebSSED4906 		{ NULL }
1721cdb3bebSSED4906 	};
1731cdb3bebSSED4906 	device_node* node;
1741cdb3bebSSED4906 	if (gDeviceManager->register_node(context->fNode,
1751cdb3bebSSED4906 			SDHCI_ACPI_MMC_BUS_MODULE_NAME, attrs, NULL,
1761cdb3bebSSED4906 			&node) != B_OK)
1771cdb3bebSSED4906 		return B_BAD_VALUE;
1781cdb3bebSSED4906 	return B_OK;
1791cdb3bebSSED4906 }
1801cdb3bebSSED4906 
1811cdb3bebSSED4906 float
supports_device_acpi(device_node * parent)1821cdb3bebSSED4906 supports_device_acpi(device_node* parent)
1831cdb3bebSSED4906 {
1841cdb3bebSSED4906 	const char* hid;
1851cdb3bebSSED4906 	const char* uid;
1861cdb3bebSSED4906 	uint32 type;
1871cdb3bebSSED4906 
1881cdb3bebSSED4906 	if (gDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM, &type, false)
1891cdb3bebSSED4906 		|| type != ACPI_TYPE_DEVICE) {
1901cdb3bebSSED4906 		return 0.0f;
1911cdb3bebSSED4906 	}
1921cdb3bebSSED4906 
1931cdb3bebSSED4906 	if (gDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid, false)) {
1941cdb3bebSSED4906 		TRACE("No hid attribute\n");
1951cdb3bebSSED4906 		return 0.0f;
1961cdb3bebSSED4906 	}
1971cdb3bebSSED4906 
1981cdb3bebSSED4906 	if (gDeviceManager->get_attr_string(parent, ACPI_DEVICE_UID_ITEM, &uid, false)) {
1991cdb3bebSSED4906 		TRACE("No uid attribute\n");
2001cdb3bebSSED4906 		return 0.0f;
2011cdb3bebSSED4906 	}
2021cdb3bebSSED4906 
2031cdb3bebSSED4906 	TRACE("supports_device(hid:%s uid:%s)\n", hid, uid);
2041cdb3bebSSED4906 
2051cdb3bebSSED4906 	if (!(strcmp(hid, "80860F14") == 0
2061cdb3bebSSED4906 			||	strcmp(hid, "80860F16") == 0
2071cdb3bebSSED4906 			||	strcmp(hid, "80865ACA") == 0
2081cdb3bebSSED4906 			||	strcmp(hid, "80865AD0") == 0
2091cdb3bebSSED4906 			||	strcmp(hid, "INT33C6") == 0
2101cdb3bebSSED4906 			||	strcmp(hid, "INT3436") == 0
2111cdb3bebSSED4906 			||	strcmp(hid, "INT344D") == 0
2121cdb3bebSSED4906 			||	strcmp(hid, "INT33BB") == 0
2131cdb3bebSSED4906 			||	strcmp(hid, "NXP0003") == 0
2141cdb3bebSSED4906 			||	strcmp(hid, "RKCP0D40") == 0
2151cdb3bebSSED4906 			||	strcmp(hid, "PNP0D40") == 0))
2161cdb3bebSSED4906 		return 0.0f;
2171cdb3bebSSED4906 
2181cdb3bebSSED4906 	acpi_device_module_info* acpi;
2191cdb3bebSSED4906 	acpi_device* device;
2201cdb3bebSSED4906 	gDeviceManager->get_driver(parent, (driver_module_info**)&acpi,
2211cdb3bebSSED4906 		(void**)&device);
2221cdb3bebSSED4906 	TRACE("SDHCI Device found! hid: %s, uid: %s\n", hid, uid);
2231cdb3bebSSED4906 
2241cdb3bebSSED4906 	return 0.8f;
2251cdb3bebSSED4906 }
2261cdb3bebSSED4906 
2271cdb3bebSSED4906 // Device node registered for each SD slot. It implements the MMC operations so
2281cdb3bebSSED4906 // the bus manager can use it to communicate with SD cards.
2291cdb3bebSSED4906 mmc_bus_interface gSDHCIACPIDeviceModule = {
2301cdb3bebSSED4906 	.info = {
2311cdb3bebSSED4906 		.info = {
2321cdb3bebSSED4906 			.name = SDHCI_ACPI_MMC_BUS_MODULE_NAME,
2331cdb3bebSSED4906 		},
2341cdb3bebSSED4906 
2351cdb3bebSSED4906 		.init_driver = init_bus_acpi,
2361cdb3bebSSED4906 		.uninit_driver = uninit_bus,
2371cdb3bebSSED4906 		.device_removed = bus_removed,
2381cdb3bebSSED4906 	},
2391cdb3bebSSED4906 
2401cdb3bebSSED4906 	.set_clock = set_clock,
2411cdb3bebSSED4906 	.execute_command = execute_command,
2421cdb3bebSSED4906 	.do_io = do_io,
2431cdb3bebSSED4906 	.set_scan_semaphore = set_scan_semaphore,
2441cdb3bebSSED4906 	.set_bus_width = set_bus_width,
2451cdb3bebSSED4906 };
246