* Copyright 2018-2024 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_pci:\33[0m " x)
#else
# define TRACE(x...) ;
#endif
#define TRACE_ALWAYS(x...) dprintf("\33[33msdhci_pci:\33[0m " x)
#define ERROR(x...) dprintf("\33[33msdhci_pci:\33[0m " x)
#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
#define SDHCI_DEVICE_MODULE_NAME "busses/mmc/sdhci/driver_v1"
#define SDHCI_PCI_MMC_BUS_MODULE_NAME "busses/mmc/sdhci/pci/device/v1"
#define SLOT_NUMBER "device/slot"
#define BAR_INDEX "device/bar"
#define SDHCI_PCI_SLOT_INFO 0x40
#define SDHCI_PCI_SLOTS(x) ((((x) >> 4) & 0x7) + 1)
#define SDHCI_PCI_SLOT_INFO_FIRST_BASE_INDEX(x) ((x) & 0x7)
#define SDHCI_PCI_RICOH_MODE_KEY 0xf9
#define SDHCI_PCI_RICOH_MODE 0x150
#define SDHCI_PCI_RICOH_MODE_SD20 0x10
status_t
init_bus_pci(device_node* node, void** bus_cookie)
{
CALLED();
pci_device_module_info* pci;
pci_device* device;
device_node* parent = gDeviceManager->get_parent_node(node);
device_node* pciParent = gDeviceManager->get_parent_node(parent);
gDeviceManager->get_driver(pciParent, (driver_module_info**)&pci,
(void**)&device);
gDeviceManager->put_node(pciParent);
gDeviceManager->put_node(parent);
uint8_t bar, slot;
if (gDeviceManager->get_attr_uint8(node, SLOT_NUMBER, &slot, false) < B_OK
|| gDeviceManager->get_attr_uint8(node, BAR_INDEX, &bar, false) < B_OK)
return B_BAD_TYPE;
TRACE("Register SD bus at slot %d, using bar %d\n", slot + 1, bar);
pci_info pciInfo;
pci->get_pci_info(device, &pciInfo);
for (; bar < 6 && slot > 0; bar++, slot--) {
if ((pciInfo.u.h0.base_register_flags[bar] & PCI_address_type)
== PCI_address_type_64) {
bar++;
}
}
phys_addr_t physicalAddress = pciInfo.u.h0.base_registers[bar];
uint64 barSize = pciInfo.u.h0.base_register_sizes[bar];
if ((pciInfo.u.h0.base_register_flags[bar] & PCI_address_type)
== PCI_address_type_64) {
physicalAddress |= (uint64)pciInfo.u.h0.base_registers[bar + 1] << 32;
barSize |= (uint64)pciInfo.u.h0.base_register_sizes[bar + 1] << 32;
}
if (physicalAddress == 0 || barSize == 0) {
ERROR("No registers to map\n");
return B_IO_ERROR;
}
uint16 pcicmd = pci->read_pci_config(device, PCI_command, 2);
pcicmd &= ~(PCI_command_int_disable | PCI_command_io);
pcicmd |= PCI_command_master | PCI_command_memory;
pci->write_pci_config(device, PCI_command, 2, pcicmd);
area_id regs_area;
struct registers* _regs;
regs_area = map_physical_memory("sdhc_regs_map",
physicalAddress, barSize, 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 = pciInfo.u.h0.interrupt_line;
TRACE("irq interrupt line: %d\n", irq);
SdhciBus* bus = new(std::nothrow) SdhciBus(_regs, irq, false);
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_pci(void* cookie)
{
CALLED();
SdhciDevice* context = (SdhciDevice*)cookie;
device_node* parent = gDeviceManager->get_parent_node(context->fNode);
pci_device_module_info* pci;
pci_device* device;
gDeviceManager->get_driver(parent, (driver_module_info**)&pci,
(void**)&device);
uint8 slotsInfo = pci->read_pci_config(device, SDHCI_PCI_SLOT_INFO, 1);
uint8 bar = SDHCI_PCI_SLOT_INFO_FIRST_BASE_INDEX(slotsInfo);
uint8 slotsCount = SDHCI_PCI_SLOTS(slotsInfo);
TRACE("register_child_devices: %u, %u\n", bar, slotsCount);
char prettyName[25];
if (slotsCount > 6 || bar > 5) {
ERROR("Invalid slots count: %d or BAR count: %d \n", slotsCount, bar);
return B_BAD_VALUE;
}
for (uint8_t slot = 0; slot < slotsCount; slot++) {
sprintf(prettyName, "SDHC bus %" B_PRIu8, slot);
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 }},
{ SLOT_NUMBER, B_UINT8_TYPE, { .ui8 = slot} },
{ BAR_INDEX, B_UINT8_TYPE, { .ui8 = bar} },
{ NULL }
};
device_node* node;
if (gDeviceManager->register_node(context->fNode,
SDHCI_PCI_MMC_BUS_MODULE_NAME, attrs, NULL,
&node) != B_OK)
return B_BAD_VALUE;
}
return B_OK;
}
status_t
init_device_pci(device_node* node, SdhciDevice* context)
{
pci_device_module_info* pci;
pci_device* device;
uint16 vendorId, deviceId;
device_node* pciParent = gDeviceManager->get_parent_node(context->fNode);
gDeviceManager->get_driver(pciParent, (driver_module_info**)&pci,
(void**)&device);
gDeviceManager->put_node(pciParent);
if (gDeviceManager->get_attr_uint16(node, B_DEVICE_VENDOR_ID,
&vendorId, true) != B_OK
|| gDeviceManager->get_attr_uint16(node, B_DEVICE_ID, &deviceId,
true) != B_OK) {
panic("No vendor or device id attribute\n");
return B_OK;
}
if (vendorId == 0x1180 && deviceId == 0xe823) {
pci->write_pci_config(device, SDHCI_PCI_RICOH_MODE_KEY, 1, 0xfc);
context->fRicohOriginalMode = pci->read_pci_config(device,
SDHCI_PCI_RICOH_MODE, 1);
pci->write_pci_config(device, SDHCI_PCI_RICOH_MODE, 1,
SDHCI_PCI_RICOH_MODE_SD20);
pci->write_pci_config(device, SDHCI_PCI_RICOH_MODE_KEY, 1, 0);
deviceId = pci->read_pci_config(device, 2, 2);
TRACE("Device ID after Ricoh quirk: %x\n", deviceId);
}
return B_OK;
}
void
uninit_device_pci(SdhciDevice* context, device_node* pciParent)
{
pci_device_module_info* pci;
pci_device* device;
uint16 vendorId, deviceId;
gDeviceManager->get_driver(pciParent, (driver_module_info**)&pci,
(void**)&device);
if (gDeviceManager->get_attr_uint16(context->fNode, B_DEVICE_VENDOR_ID,
&vendorId, true) != B_OK
|| gDeviceManager->get_attr_uint16(context->fNode, B_DEVICE_ID,
&deviceId, false) != B_OK) {
ERROR("No vendor or device id attribute\n");
} else if (vendorId == 0x1180 && deviceId == 0xe823) {
pci->write_pci_config(device, SDHCI_PCI_RICOH_MODE_KEY, 1, 0xfc);
pci->write_pci_config(device, SDHCI_PCI_RICOH_MODE, 1,
context->fRicohOriginalMode);
pci->write_pci_config(device, SDHCI_PCI_RICOH_MODE_KEY, 1, 0);
}
}
float
supports_device_pci(device_node* parent)
{
uint16 type, subType;
uint16 vendorId, deviceId;
if (gDeviceManager->get_attr_uint16(parent, B_DEVICE_VENDOR_ID, &vendorId,
false) != B_OK
|| gDeviceManager->get_attr_uint16(parent, B_DEVICE_ID, &deviceId,
false) != B_OK) {
TRACE("No vendor or device id attribute\n");
return 0.0f;
}
TRACE("supports_device(vid:%04x pid:%04x)\n", vendorId, deviceId);
if (gDeviceManager->get_attr_uint16(parent, B_DEVICE_SUB_TYPE, &subType,
false) < B_OK
|| gDeviceManager->get_attr_uint16(parent, B_DEVICE_TYPE, &type,
false) < B_OK) {
TRACE("Could not find type/subtype attributes\n");
return -1;
}
if (type == PCI_base_peripheral) {
if (subType != PCI_sd_host) {
if (vendorId != 0x1180
|| (deviceId != 0xe823 && deviceId != 0xe822)) {
TRACE("Not the right subclass, and not a Ricoh device\n");
return 0.0f;
}
}
pci_device_module_info* pci;
pci_device* device;
gDeviceManager->get_driver(parent, (driver_module_info**)&pci,
(void**)&device);
TRACE("SDHCI Device found! Subtype: 0x%04x, type: 0x%04x\n",
subType, type);
return 0.8f;
}
return 0.0f;
}
mmc_bus_interface gSDHCIPCIDeviceModule = {
.info = {
.info = {
.name = SDHCI_PCI_MMC_BUS_MODULE_NAME,
},
.init_driver = init_bus_pci,
.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,
};