xref: /haiku/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp (revision 215b685f7fd7d2f9a04260bb164be31ddc001ea2)
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),
2174b60970SAnarchos 	fWorkerThread(-1),
2274b60970SAnarchos 	fActiveDevice(0)
2325b6a6f1Skrish_iyer {
2425b6a6f1Skrish_iyer 	CALLED();
25ff76d2dfSAdrien Destugues 
26ff76d2dfSAdrien Destugues 	// Get the parent info, it includes the API to send commands to the hardware
2725b6a6f1Skrish_iyer 	device_node* parent = gDeviceManager->get_parent_node(node);
2825b6a6f1Skrish_iyer 	fStatus = gDeviceManager->get_driver(parent,
2925b6a6f1Skrish_iyer 		(driver_module_info**)&fController, &fCookie);
3025b6a6f1Skrish_iyer 	gDeviceManager->put_node(parent);
3125b6a6f1Skrish_iyer 
3225b6a6f1Skrish_iyer 	if (fStatus != B_OK) {
3325b6a6f1Skrish_iyer 		ERROR("Not able to establish the bus %s\n",
3425b6a6f1Skrish_iyer 			strerror(fStatus));
3525b6a6f1Skrish_iyer 		return;
3625b6a6f1Skrish_iyer 	}
37ff76d2dfSAdrien Destugues 
3874b60970SAnarchos 	fScanSemaphore = create_sem(0, "MMC bus scan");
3974b60970SAnarchos 	fLockSemaphore = create_sem(1, "MMC bus lock");
4074b60970SAnarchos 	fWorkerThread = spawn_kernel_thread(_WorkerThread, "SD bus controller",
41ff76d2dfSAdrien Destugues 		B_NORMAL_PRIORITY, this);
42ff76d2dfSAdrien Destugues 	resume_thread(fWorkerThread);
4324136793SAdrien Destugues 
4424136793SAdrien Destugues 	fController->set_scan_semaphore(fCookie, fScanSemaphore);
4525b6a6f1Skrish_iyer }
4625b6a6f1Skrish_iyer 
4725b6a6f1Skrish_iyer 
4825b6a6f1Skrish_iyer MMCBus::~MMCBus()
4925b6a6f1Skrish_iyer {
5025b6a6f1Skrish_iyer 	CALLED();
51ff76d2dfSAdrien Destugues 
52a59f6b7aSAdrien Destugues 	// Tell the worker thread we want to stop
53ff76d2dfSAdrien Destugues 	fStatus = B_SHUTTING_DOWN;
54ff76d2dfSAdrien Destugues 
55a59f6b7aSAdrien Destugues 	// Delete the semaphores (this will unlock the worker thread if it was
56a59f6b7aSAdrien Destugues 	// waiting on them)
57a59f6b7aSAdrien Destugues 	delete_sem(fScanSemaphore);
58a59f6b7aSAdrien Destugues 	delete_sem(fLockSemaphore);
59a59f6b7aSAdrien Destugues 
60a59f6b7aSAdrien Destugues 	// Wait for the worker thread to terminate
61ff76d2dfSAdrien Destugues 	status_t result;
62ff76d2dfSAdrien Destugues 	if (fWorkerThread != 0)
63ff76d2dfSAdrien Destugues 		wait_for_thread(fWorkerThread, &result);
6474b60970SAnarchos 
65a59f6b7aSAdrien Destugues 	// TODO power off cards, stop clock, etc if needed.
6625b6a6f1Skrish_iyer }
6725b6a6f1Skrish_iyer 
6825b6a6f1Skrish_iyer 
6925b6a6f1Skrish_iyer status_t
7025b6a6f1Skrish_iyer MMCBus::InitCheck()
7125b6a6f1Skrish_iyer {
7225b6a6f1Skrish_iyer 	return fStatus;
7325b6a6f1Skrish_iyer }
74ff76d2dfSAdrien Destugues 
75ff76d2dfSAdrien Destugues 
76dedbe94eSAdrien Destugues void
77dedbe94eSAdrien Destugues MMCBus::Rescan()
78dedbe94eSAdrien Destugues {
79dedbe94eSAdrien Destugues 	// Just wake up the thread for a scan
8074b60970SAnarchos 	release_sem(fScanSemaphore);
81dedbe94eSAdrien Destugues }
82dedbe94eSAdrien Destugues 
83dedbe94eSAdrien Destugues 
84ff76d2dfSAdrien Destugues status_t
85522c141dSAdrien Destugues MMCBus::ExecuteCommand(uint16_t rca, uint8_t command, uint32_t argument,
86522c141dSAdrien Destugues 	uint32_t* response)
87ff76d2dfSAdrien Destugues {
88522c141dSAdrien Destugues 	status_t status = _ActivateDevice(rca);
8974b60970SAnarchos 	if (status != B_OK)
9074b60970SAnarchos 		return status;
91ff76d2dfSAdrien Destugues 	return fController->execute_command(fCookie, command, argument, response);
92ff76d2dfSAdrien Destugues }
93ff76d2dfSAdrien Destugues 
94ff76d2dfSAdrien Destugues 
95ff76d2dfSAdrien Destugues status_t
96d1fee57dSAdrien Destugues MMCBus::DoIO(uint16_t rca, uint8_t command, IOOperation* operation,
97d1fee57dSAdrien Destugues 	bool offsetAsSectors)
9874b60970SAnarchos {
9974b60970SAnarchos 	status_t status = _ActivateDevice(rca);
10074b60970SAnarchos 	if (status != B_OK)
10174b60970SAnarchos 		return status;
102d1fee57dSAdrien Destugues 	return fController->do_io(fCookie, command, operation, offsetAsSectors);
10374b60970SAnarchos }
10474b60970SAnarchos 
10574b60970SAnarchos 
106522c141dSAdrien Destugues void
107522c141dSAdrien Destugues MMCBus::SetClock(int frequency)
108522c141dSAdrien Destugues {
109522c141dSAdrien Destugues 	fController->set_clock(fCookie, frequency);
110522c141dSAdrien Destugues }
111522c141dSAdrien Destugues 
112522c141dSAdrien Destugues 
11334552f8eSAdrien Destugues void
11434552f8eSAdrien Destugues MMCBus::SetBusWidth(int width)
11534552f8eSAdrien Destugues {
11634552f8eSAdrien Destugues 	fController->set_bus_width(fCookie, width);
11734552f8eSAdrien Destugues }
11834552f8eSAdrien Destugues 
11934552f8eSAdrien Destugues 
12074b60970SAnarchos status_t
12174b60970SAnarchos MMCBus::_ActivateDevice(uint16_t rca)
12274b60970SAnarchos {
12374b60970SAnarchos 	// Do nothing if the device is already activated
12474b60970SAnarchos 	if (fActiveDevice == rca)
12574b60970SAnarchos 		return B_OK;
12674b60970SAnarchos 
12774b60970SAnarchos 	uint32_t response;
12874b60970SAnarchos 	status_t result;
12974b60970SAnarchos 	result = fController->execute_command(fCookie, SD_SELECT_DESELECT_CARD,
13074b60970SAnarchos 		((uint32)rca) << 16, &response);
13174b60970SAnarchos 
13274b60970SAnarchos 	if (result == B_OK)
13374b60970SAnarchos 		fActiveDevice = rca;
13474b60970SAnarchos 
13574b60970SAnarchos 	return result;
13674b60970SAnarchos }
13774b60970SAnarchos 
13874b60970SAnarchos 
13924136793SAdrien Destugues void MMCBus::_AcquireScanSemaphore()
14024136793SAdrien Destugues {
14124136793SAdrien Destugues 	release_sem(fLockSemaphore);
14224136793SAdrien Destugues 	acquire_sem(fScanSemaphore);
14324136793SAdrien Destugues 	acquire_sem(fLockSemaphore);
14424136793SAdrien Destugues }
14524136793SAdrien Destugues 
14624136793SAdrien Destugues 
14774b60970SAnarchos status_t
14874b60970SAnarchos MMCBus::_WorkerThread(void* cookie)
149ff76d2dfSAdrien Destugues {
150ff76d2dfSAdrien Destugues 	MMCBus* bus = (MMCBus*)cookie;
151ff76d2dfSAdrien Destugues 	uint32_t response;
152ff76d2dfSAdrien Destugues 
15374b60970SAnarchos 	acquire_sem(bus->fLockSemaphore);
15474b60970SAnarchos 
155ff76d2dfSAdrien Destugues 	// We assume the bus defaults to 400kHz clock and has already powered on
156ff76d2dfSAdrien Destugues 	// cards.
157ff76d2dfSAdrien Destugues 
158ff76d2dfSAdrien Destugues 	// Reset all cards on the bus
15924136793SAdrien Destugues 	// This does not work if the bus has not been powered on yet (the command
16024136793SAdrien Destugues 	// will timeout), in that case we wait until asked to scan again when a
16124136793SAdrien Destugues 	// card has been inserted and powered on.
16224136793SAdrien Destugues 	status_t result;
16324136793SAdrien Destugues 	do {
16424136793SAdrien Destugues 		bus->_AcquireScanSemaphore();
16534552f8eSAdrien Destugues 
166a59f6b7aSAdrien Destugues 		// Check if we need to exit early (possible if the parent device did
167a59f6b7aSAdrien Destugues 		// not manage initialize itself correctly)
168a59f6b7aSAdrien Destugues 		if (bus->fStatus == B_SHUTTING_DOWN) {
169a59f6b7aSAdrien Destugues 			release_sem(bus->fLockSemaphore);
170a59f6b7aSAdrien Destugues 			return B_OK;
171a59f6b7aSAdrien Destugues 		}
172a59f6b7aSAdrien Destugues 
17324136793SAdrien Destugues 		TRACE("Reset the bus...\n");
174522c141dSAdrien Destugues 		result = bus->ExecuteCommand(0, SD_GO_IDLE_STATE, 0, NULL);
17524136793SAdrien Destugues 		TRACE("CMD0 result: %s\n", strerror(result));
17624136793SAdrien Destugues 	} while (result != B_OK);
17724136793SAdrien Destugues 
17824136793SAdrien Destugues 	// Need to wait at least 8 clock cycles after CMD0 before sending the next
179522c141dSAdrien Destugues 	// command. With the default 400kHz clock that would be 20 microseconds,
18034552f8eSAdrien Destugues 	// but we need to wait at least 20ms here, otherwise the next command times
18134552f8eSAdrien Destugues 	// out
1825ec64c5cSAdrien Destugues 	snooze(30000);
183ff76d2dfSAdrien Destugues 
184dedbe94eSAdrien Destugues 	while (bus->fStatus != B_SHUTTING_DOWN) {
185dedbe94eSAdrien Destugues 		TRACE("Scanning the bus\n");
186dedbe94eSAdrien Destugues 
18734552f8eSAdrien Destugues 		// Use the low speed clock and 1bit bus width for scanning
188522c141dSAdrien Destugues 		bus->SetClock(400);
18934552f8eSAdrien Destugues 		bus->SetBusWidth(1);
190522c141dSAdrien Destugues 
191ff76d2dfSAdrien Destugues 		// Probe the voltage range
19274b60970SAnarchos 		enum {
19374b60970SAnarchos 			// Table 4-40 in physical layer specification v8.00
19474b60970SAnarchos 			// All other values are currently reserved
19574b60970SAnarchos 			HOST_27_36V = 1, //Host supplied voltage 2.7-3.6V
19674b60970SAnarchos 		};
19774b60970SAnarchos 
19874b60970SAnarchos 		// An arbitrary value, we just need to check that the response
19974b60970SAnarchos 		// containts the same.
20074b60970SAnarchos 		static const uint8 kVoltageCheckPattern = 0xAA;
20174b60970SAnarchos 
202ff76d2dfSAdrien Destugues 		// FIXME MMC cards will not reply to this! They expect CMD1 instead
203ff76d2dfSAdrien Destugues 		// SD v1 cards will also not reply, but we can proceed to ACMD41
204ff76d2dfSAdrien Destugues 		// If ACMD41 also does not work, it may be an SDIO card, too
20574b60970SAnarchos 		uint32_t probe = (HOST_27_36V << 8) | kVoltageCheckPattern;
206518af33fSAdrien Destugues 		uint32_t hcs = 1 << 30;
207522c141dSAdrien Destugues 		if (bus->ExecuteCommand(0, SD_SEND_IF_COND, probe, &response) != B_OK) {
208518af33fSAdrien Destugues 			TRACE("Card does not implement CMD8, may be a V1 SD card\n");
209518af33fSAdrien Destugues 			// Do not check for SDHC support in this case
210518af33fSAdrien Destugues 			hcs = 0;
211518af33fSAdrien Destugues 		} else if (response != probe) {
212dedbe94eSAdrien Destugues 			ERROR("Card does not support voltage range (expected %x, "
213dedbe94eSAdrien Destugues 				"reply %x)\n", probe, response);
21424136793SAdrien Destugues 			// TODO we should power off the bus in this case.
215ff76d2dfSAdrien Destugues 		}
216ff76d2dfSAdrien Destugues 
217ff76d2dfSAdrien Destugues 		// Probe OCR, waiting for card to become ready
21824136793SAdrien Destugues 		// We keep repeating ACMD41 until the card replies that it is
21924136793SAdrien Destugues 		// initialized.
220ff76d2dfSAdrien Destugues 		uint32_t ocr;
221ff76d2dfSAdrien Destugues 		do {
222ff76d2dfSAdrien Destugues 			uint32_t cardStatus;
223522c141dSAdrien Destugues 			while (bus->ExecuteCommand(0, SD_APP_CMD, 0, &cardStatus)
224ff76d2dfSAdrien Destugues 					== B_BUSY) {
225ff76d2dfSAdrien Destugues 				ERROR("Card locked after CMD8...\n");
226ff76d2dfSAdrien Destugues 				snooze(1000000);
227ff76d2dfSAdrien Destugues 			}
228ff76d2dfSAdrien Destugues 			if ((cardStatus & 0xFFFF8000) != 0)
229dedbe94eSAdrien Destugues 				ERROR("SD card reports error %x\n", cardStatus);
230ff76d2dfSAdrien Destugues 			if ((cardStatus & (1 << 5)) == 0)
231dedbe94eSAdrien Destugues 				ERROR("Card did not enter ACMD mode\n");
232ff76d2dfSAdrien Destugues 
233522c141dSAdrien Destugues 			bus->ExecuteCommand(0, SD_SEND_OP_COND, hcs | 0xFF8000, &ocr);
234ff76d2dfSAdrien Destugues 
235ff76d2dfSAdrien Destugues 			if ((ocr & (1 << 31)) == 0) {
236ff76d2dfSAdrien Destugues 				TRACE("Card is busy\n");
237ff76d2dfSAdrien Destugues 				snooze(100000);
238ff76d2dfSAdrien Destugues 			}
239ff76d2dfSAdrien Destugues 		} while (((ocr & (1 << 31)) == 0));
240ff76d2dfSAdrien Destugues 
241dedbe94eSAdrien Destugues 		// FIXME this should be asked to each card, when there are multiple
242dedbe94eSAdrien Destugues 		// ones. So ACMD41 should be moved inside the probing loop below?
243dedbe94eSAdrien Destugues 		uint8_t cardType = CARD_TYPE_SD;
244dedbe94eSAdrien Destugues 
245eb92a834SAdrien Destugues 		if ((ocr & hcs) != 0)
246dedbe94eSAdrien Destugues 			cardType = CARD_TYPE_SDHC;
247eb92a834SAdrien Destugues 		if ((ocr & (1 << 29)) != 0)
248dedbe94eSAdrien Destugues 			cardType = CARD_TYPE_UHS2;
249eb92a834SAdrien Destugues 		if ((ocr & (1 << 24)) != 0)
250ff76d2dfSAdrien Destugues 			TRACE("Card supports 1.8v");
251ff76d2dfSAdrien Destugues 		TRACE("Voltage range: %x\n", ocr & 0xFFFFFF);
252ff76d2dfSAdrien Destugues 
253ff76d2dfSAdrien Destugues 		// TODO send CMD11 to switch to low voltage mode if card supports it?
254ff76d2dfSAdrien Destugues 
25574b60970SAnarchos 		// We use CMD2 (ALL_SEND_CID) and CMD3 (SEND_RELATIVE_ADDR) to assign
25674b60970SAnarchos 		// an RCA to all cards. Initially all cards have an RCA of 0 and will
25774b60970SAnarchos 		// all receive CMD2. But only ne of them will reply (they do collision
25874b60970SAnarchos 		// detection while sending the CID in reply). We assign a new RCA to
25974b60970SAnarchos 		// that first card, and repeat the process with the remaining ones
26074b60970SAnarchos 		// until no one answers to CMD2. Then we know all cards have an RCA
26174b60970SAnarchos 		// (and a matching published device on our side).
262ff76d2dfSAdrien Destugues 		uint32_t cid[4];
26374b60970SAnarchos 
264522c141dSAdrien Destugues 		while (bus->ExecuteCommand(0, SD_ALL_SEND_CID, 0, cid) == B_OK) {
265522c141dSAdrien Destugues 			bus->ExecuteCommand(0, SD_SEND_RELATIVE_ADDR, 0, &response);
266ff76d2dfSAdrien Destugues 
267ff76d2dfSAdrien Destugues 			TRACE("RCA: %x Status: %x\n", response >> 16, response & 0xFFFF);
268ff76d2dfSAdrien Destugues 
269dedbe94eSAdrien Destugues 			if ((response & 0xFF00) != 0x500) {
270dedbe94eSAdrien Destugues 				TRACE("Card did not enter data state\n");
271dedbe94eSAdrien Destugues 				// This probably means there are no more cards to scan on the
272dedbe94eSAdrien Destugues 				// bus, so exit the loop.
273dedbe94eSAdrien Destugues 				break;
274dedbe94eSAdrien Destugues 			}
275ff76d2dfSAdrien Destugues 
276dedbe94eSAdrien Destugues 			// The card now has an RCA and it entered the data phase, which
277dedbe94eSAdrien Destugues 			// means our initializing job is over, we can pass it on to the
278dedbe94eSAdrien Destugues 			// mmc_disk driver.
279ff76d2dfSAdrien Destugues 
280dedbe94eSAdrien Destugues 			uint32_t vendor = cid[3] & 0xFFFFFF;
281eb92a834SAdrien Destugues 			char name[6] = {(char)(cid[2] >> 24), (char)(cid[2] >> 16),
282eb92a834SAdrien Destugues 				(char)(cid[2] >> 8), (char)cid[2], (char)(cid[1] >> 24), 0};
283dedbe94eSAdrien Destugues 			uint32_t serial = (cid[1] << 16) | (cid[0] >> 16);
284dedbe94eSAdrien Destugues 			uint16_t revision = (cid[1] >> 20) & 0xF;
285dedbe94eSAdrien Destugues 			revision *= 100;
286dedbe94eSAdrien Destugues 			revision += (cid[1] >> 16) & 0xF;
287dedbe94eSAdrien Destugues 			uint8_t month = cid[0] & 0xF;
288dedbe94eSAdrien Destugues 			uint16_t year = 2000 + ((cid[0] >> 4) & 0xFF);
289dedbe94eSAdrien Destugues 			uint16_t rca = response >> 16;
290ff76d2dfSAdrien Destugues 
291dedbe94eSAdrien Destugues 			device_attr attrs[] = {
292*215b685fSX512 				{ B_DEVICE_BUS, B_STRING_TYPE, {.string = "mmc" }},
293*215b685fSX512 				{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "mmc device" }},
294*215b685fSX512 				{ B_DEVICE_VENDOR_ID, B_UINT32_TYPE, {.ui32 = vendor}},
295*215b685fSX512 				{ B_DEVICE_ID, B_STRING_TYPE, {.string = name}},
296*215b685fSX512 				{ B_DEVICE_UNIQUE_ID, B_UINT32_TYPE, {.ui32 = serial}},
297*215b685fSX512 				{ "mmc/revision", B_UINT16_TYPE, {.ui16 = revision}},
298*215b685fSX512 				{ "mmc/month", B_UINT8_TYPE, {.ui8 = month}},
299*215b685fSX512 				{ "mmc/year", B_UINT16_TYPE, {.ui16 = year}},
300*215b685fSX512 				{ kMmcRcaAttribute, B_UINT16_TYPE, {.ui16 = rca}},
301*215b685fSX512 				{ kMmcTypeAttribute, B_UINT8_TYPE, {.ui8 = cardType}},
302dedbe94eSAdrien Destugues 				{}
303dedbe94eSAdrien Destugues 			};
304dedbe94eSAdrien Destugues 
305dedbe94eSAdrien Destugues 			// publish child device for the card
306dedbe94eSAdrien Destugues 			gDeviceManager->register_node(bus->fNode, MMC_BUS_MODULE_NAME,
307dedbe94eSAdrien Destugues 				attrs, NULL, NULL);
308dedbe94eSAdrien Destugues 		}
309dedbe94eSAdrien Destugues 
310522c141dSAdrien Destugues 		// TODO if there is a single card active, check if it supports CMD6
311522c141dSAdrien Destugues 		// (spec version 1.10 or later in SCR). If it does, check if CMD6 can
312522c141dSAdrien Destugues 		// enable high speed mode, use that to go to 50MHz instead of 25.
313522c141dSAdrien Destugues 		bus->SetClock(25000);
314522c141dSAdrien Destugues 
315dedbe94eSAdrien Destugues 		// FIXME we also need to unpublish devices that are gone. Probably need
316dedbe94eSAdrien Destugues 		// to "ping" all RCAs somehow? Or is there an interrupt we can look for
317dedbe94eSAdrien Destugues 		// to detect added/removed cards?
31824136793SAdrien Destugues 
31924136793SAdrien Destugues 		// Wait for the next scan request
32024136793SAdrien Destugues 		// The thread will spend most of its time waiting here
32124136793SAdrien Destugues 		bus->_AcquireScanSemaphore();
322dedbe94eSAdrien Destugues 	}
323dedbe94eSAdrien Destugues 
32474b60970SAnarchos 	release_sem(bus->fLockSemaphore);
32574b60970SAnarchos 
326dedbe94eSAdrien Destugues 	TRACE("poller thread terminating");
327dedbe94eSAdrien Destugues 	return B_OK;
328ff76d2dfSAdrien Destugues }
329