125b6a6f1Skrish_iyer /* 2dedbe94eSAdrien Destugues * Copyright 2018-2020 Haiku, Inc. All rights reserved. 325b6a6f1Skrish_iyer * Distributed under the terms of the MIT License. 425b6a6f1Skrish_iyer * 525b6a6f1Skrish_iyer * Authors: 625b6a6f1Skrish_iyer * B Krishnan Iyer, krishnaniyer97@gmail.com 725b6a6f1Skrish_iyer */ 825b6a6f1Skrish_iyer #include "mmc_bus.h" 925b6a6f1Skrish_iyer 10ff76d2dfSAdrien Destugues #include <Errors.h> 11ff76d2dfSAdrien Destugues 12ff76d2dfSAdrien Destugues #include <stdint.h> 13ff76d2dfSAdrien Destugues 1425b6a6f1Skrish_iyer 1525b6a6f1Skrish_iyer MMCBus::MMCBus(device_node* node) 1625b6a6f1Skrish_iyer : 1725b6a6f1Skrish_iyer fNode(node), 1825b6a6f1Skrish_iyer fController(NULL), 1925b6a6f1Skrish_iyer fCookie(NULL), 2025b6a6f1Skrish_iyer fStatus(B_OK), 21ff76d2dfSAdrien Destugues fWorkerThread(0) 2225b6a6f1Skrish_iyer { 2325b6a6f1Skrish_iyer CALLED(); 24ff76d2dfSAdrien Destugues 25ff76d2dfSAdrien Destugues // Get the parent info, it includes the API to send commands to the hardware 2625b6a6f1Skrish_iyer device_node* parent = gDeviceManager->get_parent_node(node); 2725b6a6f1Skrish_iyer fStatus = gDeviceManager->get_driver(parent, 2825b6a6f1Skrish_iyer (driver_module_info**)&fController, &fCookie); 2925b6a6f1Skrish_iyer gDeviceManager->put_node(parent); 3025b6a6f1Skrish_iyer 3125b6a6f1Skrish_iyer if (fStatus != B_OK) { 3225b6a6f1Skrish_iyer ERROR("Not able to establish the bus %s\n", 3325b6a6f1Skrish_iyer strerror(fStatus)); 3425b6a6f1Skrish_iyer return; 3525b6a6f1Skrish_iyer } 36ff76d2dfSAdrien Destugues 37dedbe94eSAdrien Destugues fSemaphore = create_sem(0, "MMC bus scan"); 38ff76d2dfSAdrien Destugues fWorkerThread = spawn_kernel_thread(WorkerThread, "SD bus controller", 39ff76d2dfSAdrien Destugues B_NORMAL_PRIORITY, this); 40ff76d2dfSAdrien Destugues resume_thread(fWorkerThread); 4125b6a6f1Skrish_iyer } 4225b6a6f1Skrish_iyer 4325b6a6f1Skrish_iyer 4425b6a6f1Skrish_iyer MMCBus::~MMCBus() 4525b6a6f1Skrish_iyer { 4625b6a6f1Skrish_iyer CALLED(); 47ff76d2dfSAdrien Destugues 48ff76d2dfSAdrien Destugues // stop worker thread 49ff76d2dfSAdrien Destugues fStatus = B_SHUTTING_DOWN; 50ff76d2dfSAdrien Destugues 51ff76d2dfSAdrien Destugues status_t result; 52ff76d2dfSAdrien Destugues if (fWorkerThread != 0) 53ff76d2dfSAdrien Destugues wait_for_thread(fWorkerThread, &result); 54ff76d2dfSAdrien Destugues // TODO power off cards, stop clock, etc if needed. 5525b6a6f1Skrish_iyer } 5625b6a6f1Skrish_iyer 5725b6a6f1Skrish_iyer 5825b6a6f1Skrish_iyer status_t 5925b6a6f1Skrish_iyer MMCBus::InitCheck() 6025b6a6f1Skrish_iyer { 6125b6a6f1Skrish_iyer return fStatus; 6225b6a6f1Skrish_iyer } 63ff76d2dfSAdrien Destugues 64ff76d2dfSAdrien Destugues 65dedbe94eSAdrien Destugues void 66dedbe94eSAdrien Destugues MMCBus::Rescan() 67dedbe94eSAdrien Destugues { 68dedbe94eSAdrien Destugues // Just wake up the thread for a scan 69dedbe94eSAdrien Destugues release_sem(fSemaphore); 70dedbe94eSAdrien Destugues } 71dedbe94eSAdrien Destugues 72dedbe94eSAdrien Destugues 73ff76d2dfSAdrien Destugues status_t 74ff76d2dfSAdrien Destugues MMCBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response) 75ff76d2dfSAdrien Destugues { 76ff76d2dfSAdrien Destugues return fController->execute_command(fCookie, command, argument, response); 77ff76d2dfSAdrien Destugues } 78ff76d2dfSAdrien Destugues 79ff76d2dfSAdrien Destugues 80ff76d2dfSAdrien Destugues status_t 81ff76d2dfSAdrien Destugues MMCBus::WorkerThread(void* cookie) 82ff76d2dfSAdrien Destugues { 83ff76d2dfSAdrien Destugues MMCBus* bus = (MMCBus*)cookie; 84ff76d2dfSAdrien Destugues uint32_t response; 85ff76d2dfSAdrien Destugues 86ff76d2dfSAdrien Destugues // We assume the bus defaults to 400kHz clock and has already powered on 87ff76d2dfSAdrien Destugues // cards. 88ff76d2dfSAdrien Destugues 89ff76d2dfSAdrien Destugues // Reset all cards on the bus 90ff76d2dfSAdrien Destugues bus->ExecuteCommand(0, 0, NULL); 91ff76d2dfSAdrien Destugues 92dedbe94eSAdrien Destugues while (bus->fStatus != B_SHUTTING_DOWN) { 93dedbe94eSAdrien Destugues // wait for bus to signal a card is inserted 94dedbe94eSAdrien Destugues acquire_sem(bus->fSemaphore); 95dedbe94eSAdrien Destugues TRACE("Scanning the bus\n"); 96dedbe94eSAdrien Destugues 97ff76d2dfSAdrien Destugues // Probe the voltage range 98ff76d2dfSAdrien Destugues // FIXME MMC cards will not reply to this! They expect CMD1 instead 99ff76d2dfSAdrien Destugues // SD v1 cards will also not reply, but we can proceed to ACMD41 100ff76d2dfSAdrien Destugues // If ACMD41 also does not work, it may be an SDIO card, too 101dedbe94eSAdrien Destugues uint32_t probe = (1 << 8) | 0xAA; 102518af33fSAdrien Destugues uint32_t hcs = 1 << 30; 103518af33fSAdrien Destugues if (bus->ExecuteCommand(8, probe, &response) != B_OK) { 104518af33fSAdrien Destugues TRACE("Card does not implement CMD8, may be a V1 SD card\n"); 105518af33fSAdrien Destugues // Do not check for SDHC support in this case 106518af33fSAdrien Destugues hcs = 0; 107518af33fSAdrien Destugues } else if (response != probe) { 108dedbe94eSAdrien Destugues ERROR("Card does not support voltage range (expected %x, " 109dedbe94eSAdrien Destugues "reply %x)\n", probe, response); 110ff76d2dfSAdrien Destugues // TODO what now? 111ff76d2dfSAdrien Destugues } 112ff76d2dfSAdrien Destugues 113ff76d2dfSAdrien Destugues // Probe OCR, waiting for card to become ready 114ff76d2dfSAdrien Destugues uint32_t ocr; 115ff76d2dfSAdrien Destugues do { 116ff76d2dfSAdrien Destugues uint32_t cardStatus; 117ff76d2dfSAdrien Destugues while (bus->ExecuteCommand(55, 0, &cardStatus) 118ff76d2dfSAdrien Destugues == B_BUSY) { 119ff76d2dfSAdrien Destugues ERROR("Card locked after CMD8...\n"); 120ff76d2dfSAdrien Destugues snooze(1000000); 121ff76d2dfSAdrien Destugues } 122ff76d2dfSAdrien Destugues if ((cardStatus & 0xFFFF8000) != 0) 123dedbe94eSAdrien Destugues ERROR("SD card reports error %x\n", cardStatus); 124ff76d2dfSAdrien Destugues if ((cardStatus & (1 << 5)) == 0) 125dedbe94eSAdrien Destugues ERROR("Card did not enter ACMD mode\n"); 126ff76d2dfSAdrien Destugues 127518af33fSAdrien Destugues bus->ExecuteCommand(41, hcs | 0xFF8000, &ocr); 128ff76d2dfSAdrien Destugues 129ff76d2dfSAdrien Destugues if ((ocr & (1 << 31)) == 0) { 130ff76d2dfSAdrien Destugues TRACE("Card is busy\n"); 131ff76d2dfSAdrien Destugues snooze(100000); 132ff76d2dfSAdrien Destugues } 133ff76d2dfSAdrien Destugues } while (((ocr & (1 << 31)) == 0)); 134ff76d2dfSAdrien Destugues 135dedbe94eSAdrien Destugues // FIXME this should be asked to each card, when there are multiple 136dedbe94eSAdrien Destugues // ones. So ACMD41 should be moved inside the probing loop below? 137dedbe94eSAdrien Destugues uint8_t cardType = CARD_TYPE_SD; 138dedbe94eSAdrien Destugues 139518af33fSAdrien Destugues if (ocr & hcs != 0) 140dedbe94eSAdrien Destugues cardType = CARD_TYPE_SDHC; 141518af33fSAdrien Destugues if (ocr & (1 << 29) != 0) 142dedbe94eSAdrien Destugues cardType = CARD_TYPE_UHS2; 143518af33fSAdrien Destugues if (ocr & (1 << 24) != 0) 144ff76d2dfSAdrien Destugues TRACE("Card supports 1.8v"); 145ff76d2dfSAdrien Destugues TRACE("Voltage range: %x\n", ocr & 0xFFFFFF); 146ff76d2dfSAdrien Destugues 147ff76d2dfSAdrien Destugues // TODO send CMD11 to switch to low voltage mode if card supports it? 148ff76d2dfSAdrien Destugues 149dedbe94eSAdrien Destugues // iterate CMD2/CMD3 to assign an RCA to all cards and publish devices 150dedbe94eSAdrien Destugues // for each of them 151ff76d2dfSAdrien Destugues uint32_t cid[4]; 152dedbe94eSAdrien Destugues while (bus->ExecuteCommand(2, 0, cid) == B_OK) { 153ff76d2dfSAdrien Destugues bus->ExecuteCommand(3, 0, &response); 154ff76d2dfSAdrien Destugues 155ff76d2dfSAdrien Destugues TRACE("RCA: %x Status: %x\n", response >> 16, response & 0xFFFF); 156ff76d2dfSAdrien Destugues 157dedbe94eSAdrien Destugues if ((response & 0xFF00) != 0x500) { 158dedbe94eSAdrien Destugues TRACE("Card did not enter data state\n"); 159dedbe94eSAdrien Destugues // This probably means there are no more cards to scan on the 160dedbe94eSAdrien Destugues // bus, so exit the loop. 161dedbe94eSAdrien Destugues break; 162dedbe94eSAdrien Destugues } 163ff76d2dfSAdrien Destugues 164dedbe94eSAdrien Destugues // The card now has an RCA and it entered the data phase, which 165dedbe94eSAdrien Destugues // means our initializing job is over, we can pass it on to the 166dedbe94eSAdrien Destugues // mmc_disk driver. 167ff76d2dfSAdrien Destugues 168dedbe94eSAdrien Destugues uint32_t vendor = cid[3] & 0xFFFFFF; 169dedbe94eSAdrien Destugues char name[6] = {cid[2] >> 24, cid[2] >> 16, cid[2] >> 8, cid[2], 170dedbe94eSAdrien Destugues cid[1] >> 24, 0}; 171dedbe94eSAdrien Destugues uint32_t serial = (cid[1] << 16) | (cid[0] >> 16); 172dedbe94eSAdrien Destugues uint16_t revision = (cid[1] >> 20) & 0xF; 173dedbe94eSAdrien Destugues revision *= 100; 174dedbe94eSAdrien Destugues revision += (cid[1] >> 16) & 0xF; 175dedbe94eSAdrien Destugues uint8_t month = cid[0] & 0xF; 176dedbe94eSAdrien Destugues uint16_t year = 2000 + ((cid[0] >> 4) & 0xFF); 177dedbe94eSAdrien Destugues uint16_t rca = response >> 16; 178ff76d2dfSAdrien Destugues 179dedbe94eSAdrien Destugues device_attr attrs[] = { 180dedbe94eSAdrien Destugues { B_DEVICE_BUS, B_STRING_TYPE, {string: "mmc" }}, 18162eaf4c0SAdrien Destugues { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "mmc device" }}, 182dedbe94eSAdrien Destugues { B_DEVICE_VENDOR_ID, B_UINT32_TYPE, {ui32: vendor}}, 183dedbe94eSAdrien Destugues { B_DEVICE_ID, B_STRING_TYPE, {string: name}}, 184dedbe94eSAdrien Destugues { B_DEVICE_UNIQUE_ID, B_UINT32_TYPE, {ui32: serial}}, 185dedbe94eSAdrien Destugues { "mmc/revision", B_UINT16_TYPE, {ui16: revision}}, 186dedbe94eSAdrien Destugues { "mmc/month", B_UINT8_TYPE, {ui8: month}}, 187dedbe94eSAdrien Destugues { "mmc/year", B_UINT16_TYPE, {ui16: year}}, 188*e4689893SAdrien Destugues { kMmcRcaAttribute, B_UINT16_TYPE, {ui16: rca}}, 189*e4689893SAdrien Destugues { kMmcTypeAttribute, B_UINT8_TYPE, {ui8: cardType}}, 190dedbe94eSAdrien Destugues {} 191dedbe94eSAdrien Destugues }; 192dedbe94eSAdrien Destugues 193dedbe94eSAdrien Destugues // publish child device for the card 194dedbe94eSAdrien Destugues gDeviceManager->register_node(bus->fNode, MMC_BUS_MODULE_NAME, 195dedbe94eSAdrien Destugues attrs, NULL, NULL); 196dedbe94eSAdrien Destugues } 197dedbe94eSAdrien Destugues 198dedbe94eSAdrien Destugues // FIXME we also need to unpublish devices that are gone. Probably need 199dedbe94eSAdrien Destugues // to "ping" all RCAs somehow? Or is there an interrupt we can look for 200dedbe94eSAdrien Destugues // to detect added/removed cards? 201dedbe94eSAdrien Destugues } 202dedbe94eSAdrien Destugues 203dedbe94eSAdrien Destugues TRACE("poller thread terminating"); 204dedbe94eSAdrien Destugues return B_OK; 205ff76d2dfSAdrien Destugues } 206