* Copyright 2018-2025 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* B Krishnan Iyer, krishnaniyer97@gmail.com
* Adrien Destugues, pulkomandy@pulkomandy.tk
* Ron Ben Aroya, sed4906birdie@gmail.com
*/
#include <algorithm>
#include <new>
#include <stdio.h>
#include <string.h>
#include <bus/PCI.h>
#include <ACPI.h>
#include "acpi.h"
#include <KernelExport.h>
#include "IOSchedulerSimple.h"
#include "mmc.h"
#include "sdhci.h"
#ifdef TRACE_SDHCI
# define TRACE(x...) dprintf("\33[33msdhci:\33[0m " x)
#else
# define TRACE(x...) ;
#endif
#define TRACE_ALWAYS(x...) dprintf("\33[33msdhci:\33[0m " x)
#define ERROR(x...) dprintf("\33[33msdhci:\33[0m " x)
#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
#define SDHCI_DEVICE_MODULE_NAME "busses/mmc/sdhci/driver_v1"
#define SDHCI_ACPI_MMC_BUS_MODULE_NAME "busses/mmc/sdhci/acpi/device/v1"
static acpi_status
sdhci_acpi_scan_parse_callback(ACPI_RESOURCE *res, void *context)
{
struct sdhci_crs* crs = (struct sdhci_crs*)context;
if (res->Type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
crs->addr_bas = res->Data.FixedMemory32.Address;
crs->addr_len = res->Data.FixedMemory32.AddressLength;
} else if (res->Type == ACPI_RESOURCE_TYPE_IRQ) {
crs->irq = res->Data.Irq.Interrupt;
} else if (res->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
crs->irq = res->Data.ExtendedIrq.Interrupt;
}
return B_OK;
}
status_t
init_bus_acpi(device_node* node, void** bus_cookie)
{
CALLED();
acpi_device_module_info* acpi;
acpi_device device;
device_node* parent = gDeviceManager->get_parent_node(node);
device_node* acpiParent = gDeviceManager->get_parent_node(parent);
gDeviceManager->get_driver(acpiParent, (driver_module_info**)&acpi,
(void**)&device);
gDeviceManager->put_node(acpiParent);
gDeviceManager->put_node(parent);
TRACE("Register SD bus\n");
struct sdhci_crs crs;
if(acpi->walk_resources(device, (ACPI_STRING)"_CRS",
sdhci_acpi_scan_parse_callback, &crs) != B_OK) {
ERROR("Couldn't scan ACPI register set\n");
return B_IO_ERROR;
}
TRACE("addr: %" B_PRIx32 " len: %" B_PRIx32 "\n", crs.addr_bas, crs.addr_len);
if (crs.addr_bas == 0 || crs.addr_len == 0) {
ERROR("No registers to map\n");
return B_IO_ERROR;
}
area_id regs_area;
struct registers* _regs;
regs_area = map_physical_memory("sdhc_regs_map",
crs.addr_bas, crs.addr_len, B_ANY_KERNEL_BLOCK_ADDRESS,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&_regs);
if (regs_area < B_OK) {
ERROR("Could not map registers\n");
return B_BAD_VALUE;
}
uint8_t irq = crs.irq;
TRACE("irq interrupt line: %d\n", irq);
SdhciBus* bus = new(std::nothrow) SdhciBus(_regs, irq, true);
status_t status = B_NO_MEMORY;
if (bus != NULL)
status = bus->InitCheck();
if (status != B_OK) {
if (bus != NULL)
delete bus;
else
delete_area(regs_area);
return status;
}
*bus_cookie = bus;
return status;
}
status_t
register_child_devices_acpi(void* cookie)
{
CALLED();
SdhciDevice* context = (SdhciDevice*)cookie;
device_node* parent = gDeviceManager->get_parent_node(context->fNode);
acpi_device_module_info* acpi;
acpi_device* device;
gDeviceManager->get_driver(parent, (driver_module_info**)&acpi,
(void**)&device);
TRACE("register_child_devices\n");
char prettyName[25];
sprintf(prettyName, "SDHC bus");
device_attr attrs[] = {
{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = prettyName } },
{ B_DEVICE_FIXED_CHILD, B_STRING_TYPE,
{.string = MMC_BUS_MODULE_NAME} },
{ B_DEVICE_BUS, B_STRING_TYPE, {.string = "mmc"} },
{ B_DMA_ALIGNMENT, B_UINT32_TYPE, { .ui32 = 511 }},
{ B_DMA_HIGH_ADDRESS, B_UINT64_TYPE, { .ui64 = 0x100000000LL }},
{ B_DMA_BOUNDARY, B_UINT32_TYPE, { .ui32 = (1 << 19) - 1 }},
{ B_DMA_MAX_SEGMENT_COUNT, B_UINT32_TYPE, { .ui32 = 1 }},
{ B_DMA_MAX_SEGMENT_BLOCKS, B_UINT32_TYPE, { .ui32 = (1 << 10) - 1 }},
{ NULL }
};
device_node* node;
if (gDeviceManager->register_node(context->fNode,
SDHCI_ACPI_MMC_BUS_MODULE_NAME, attrs, NULL,
&node) != B_OK)
return B_BAD_VALUE;
return B_OK;
}
float
supports_device_acpi(device_node* parent)
{
const char* hid;
const char* uid;
const char* cid;
uint32 type;
if (gDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM, &type, false)
|| type != ACPI_TYPE_DEVICE) {
return 0.0f;
}
if (gDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid, false)) {
TRACE("No hid attribute\n");
return 0.0f;
}
if (gDeviceManager->get_attr_string(parent, ACPI_DEVICE_UID_ITEM, &uid, false)) {
TRACE("No uid attribute\n");
return 0.0f;
}
if (gDeviceManager->get_attr_string(parent, ACPI_DEVICE_CID_ITEM, &cid, false)) {
TRACE("No cid attribute\n");
return 0.0f;
}
if (strcmp(cid, "PNP0D40") != 0) {
return 0.0f;
}
acpi_device_module_info* acpi;
acpi_device* device;
gDeviceManager->get_driver(parent, (driver_module_info**)&acpi,
(void**)&device);
TRACE("SDHCI Device found! hid: %s, uid: %s\n", hid, uid);
return 0.8f;
}
mmc_bus_interface gSDHCIACPIDeviceModule = {
.info = {
.info = {
.name = SDHCI_ACPI_MMC_BUS_MODULE_NAME,
},
.init_driver = init_bus_acpi,
.uninit_driver = uninit_bus,
.device_removed = bus_removed,
},
.set_clock = set_clock,
.execute_command = execute_command,
.do_io = do_io,
.set_scan_semaphore = set_scan_semaphore,
.set_bus_width = set_bus_width,
};