* Copyright 2018-2020 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* B Krishnan Iyer, krishnaniyer97@gmail.com
*/
#include "mmc_bus.h"
#include <Errors.h>
#include <stdint.h>
MMCBus::MMCBus(device_node* node)
:
fNode(node),
fController(NULL),
fCookie(NULL),
fStatus(B_OK),
fWorkerThread(-1),
fActiveDevice(0)
{
CALLED();
device_node* parent = gDeviceManager->get_parent_node(node);
fStatus = gDeviceManager->get_driver(parent,
(driver_module_info**)&fController, &fCookie);
gDeviceManager->put_node(parent);
if (fStatus != B_OK) {
ERROR("Not able to establish the bus %s\n",
strerror(fStatus));
return;
}
fScanSemaphore = create_sem(0, "MMC bus scan");
fLockSemaphore = create_sem(1, "MMC bus lock");
fWorkerThread = spawn_kernel_thread(_WorkerThread, "SD bus controller",
B_NORMAL_PRIORITY, this);
resume_thread(fWorkerThread);
fController->set_scan_semaphore(fCookie, fScanSemaphore);
}
MMCBus::~MMCBus()
{
CALLED();
fStatus = B_SHUTTING_DOWN;
delete_sem(fScanSemaphore);
delete_sem(fLockSemaphore);
status_t result;
if (fWorkerThread != 0)
wait_for_thread(fWorkerThread, &result);
}
status_t
MMCBus::InitCheck()
{
return fStatus;
}
void
MMCBus::Rescan()
{
release_sem(fScanSemaphore);
}
status_t
MMCBus::ExecuteCommand(uint16_t rca, uint8_t command, uint32_t argument,
uint32_t* response)
{
status_t status = _ActivateDevice(rca);
if (status != B_OK)
return status;
return fController->execute_command(fCookie, command, argument, response);
}
status_t
MMCBus::DoIO(uint16_t rca, uint8_t command, IOOperation* operation,
bool offsetAsSectors)
{
status_t status = _ActivateDevice(rca);
if (status != B_OK)
return status;
return fController->do_io(fCookie, command, operation, offsetAsSectors);
}
void
MMCBus::SetClock(int frequency)
{
fController->set_clock(fCookie, frequency);
}
void
MMCBus::SetBusWidth(int width)
{
fController->set_bus_width(fCookie, width);
}
status_t
MMCBus::_ActivateDevice(uint16_t rca)
{
if (fActiveDevice == rca)
return B_OK;
uint32_t response;
status_t result;
result = fController->execute_command(fCookie, SD_SELECT_DESELECT_CARD,
((uint32)rca) << 16, &response);
if (result == B_OK)
fActiveDevice = rca;
return result;
}
void MMCBus::_AcquireScanSemaphore()
{
release_sem(fLockSemaphore);
acquire_sem(fScanSemaphore);
acquire_sem(fLockSemaphore);
}
status_t
MMCBus::_WorkerThread(void* cookie)
{
MMCBus* bus = (MMCBus*)cookie;
uint32_t response;
acquire_sem(bus->fLockSemaphore);
status_t result;
do {
bus->_AcquireScanSemaphore();
if (bus->fStatus == B_SHUTTING_DOWN) {
release_sem(bus->fLockSemaphore);
return B_OK;
}
TRACE("Reset the bus...\n");
result = bus->ExecuteCommand(0, SD_GO_IDLE_STATE, 0, NULL);
TRACE("CMD0 result: %s\n", strerror(result));
} while (result != B_OK);
snooze(30000);
while (bus->fStatus != B_SHUTTING_DOWN) {
TRACE("Scanning the bus\n");
bus->SetClock(400);
bus->SetBusWidth(1);
enum {
HOST_27_36V = 1,
};
static const uint8 kVoltageCheckPattern = 0xAA;
uint32_t probe = (HOST_27_36V << 8) | kVoltageCheckPattern;
uint32_t hcs = 1 << 30;
if (bus->ExecuteCommand(0, SD_SEND_IF_COND, probe, &response) != B_OK) {
TRACE("Card does not implement CMD8, may be a V1 SD card\n");
hcs = 0;
} else if (response != probe) {
ERROR("Card does not support voltage range (expected %x, "
"reply %x)\n", probe, response);
}
uint32_t ocr;
do {
uint32_t cardStatus;
while (bus->ExecuteCommand(0, SD_APP_CMD, 0, &cardStatus)
== B_BUSY) {
ERROR("Card locked after CMD8...\n");
snooze(1000000);
}
if ((cardStatus & 0xFFFF8000) != 0)
ERROR("SD card reports error %x\n", cardStatus);
if ((cardStatus & (1 << 5)) == 0)
ERROR("Card did not enter ACMD mode\n");
bus->ExecuteCommand(0, SD_SEND_OP_COND, hcs | 0xFF8000, &ocr);
if ((ocr & (1 << 31)) == 0) {
TRACE("Card is busy\n");
snooze(100000);
}
} while (((ocr & (1 << 31)) == 0));
uint8_t cardType = CARD_TYPE_SD;
if ((ocr & hcs) != 0)
cardType = CARD_TYPE_SDHC;
if ((ocr & (1 << 29)) != 0)
cardType = CARD_TYPE_UHS2;
if ((ocr & (1 << 24)) != 0)
TRACE("Card supports 1.8v");
TRACE("Voltage range: %x\n", ocr & 0xFFFFFF);
uint32_t cid[4];
if (bus->ExecuteCommand(0, SD_ALL_SEND_CID, 0, cid) == B_OK) {
bus->ExecuteCommand(0, SD_SEND_RELATIVE_ADDR, 0, &response);
TRACE("RCA: %x Status: %x\n", response >> 16, response & 0xFFFF);
if ((response & 0xFF00) != 0x500) {
TRACE("Card did not enter data state\n");
break;
}
uint32_t vendor = cid[3] & 0xFFFFFF;
char name[6] = {(char)(cid[2] >> 24), (char)(cid[2] >> 16),
(char)(cid[2] >> 8), (char)cid[2], (char)(cid[1] >> 24), 0};
uint32_t serial = (cid[1] << 16) | (cid[0] >> 16);
uint16_t revision = (cid[1] >> 20) & 0xF;
revision *= 100;
revision += (cid[1] >> 16) & 0xF;
uint8_t month = cid[0] & 0xF;
uint16_t year = 2000 + ((cid[0] >> 4) & 0xFF);
uint16_t rca = response >> 16;
device_attr attrs[] = {
{ B_DEVICE_BUS, B_STRING_TYPE, {.string = "mmc" }},
{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "mmc device" }},
{ B_DEVICE_VENDOR_ID, B_UINT32_TYPE, {.ui32 = vendor}},
{ B_DEVICE_ID, B_STRING_TYPE, {.string = name}},
{ B_DEVICE_UNIQUE_ID, B_UINT32_TYPE, {.ui32 = serial}},
{ "mmc/revision", B_UINT16_TYPE, {.ui16 = revision}},
{ "mmc/month", B_UINT8_TYPE, {.ui8 = month}},
{ "mmc/year", B_UINT16_TYPE, {.ui16 = year}},
{ kMmcRcaAttribute, B_UINT16_TYPE, {.ui16 = rca}},
{ kMmcTypeAttribute, B_UINT8_TYPE, {.ui8 = cardType}},
{}
};
gDeviceManager->register_node(bus->fNode, MMC_BUS_MODULE_NAME,
attrs, NULL, NULL);
}
bus->SetClock(25000);
bus->_AcquireScanSemaphore();
}
release_sem(bus->fLockSemaphore);
TRACE("poller thread terminating");
return B_OK;
}