xref: /haiku/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp (revision 74b60970787e901984a6bc0518cbe2d58b092589)
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),
21*74b60970SAnarchos 	fWorkerThread(-1),
22*74b60970SAnarchos 	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 
38*74b60970SAnarchos 	fScanSemaphore = create_sem(0, "MMC bus scan");
39*74b60970SAnarchos 	fLockSemaphore = create_sem(1, "MMC bus lock");
40*74b60970SAnarchos 	fWorkerThread = spawn_kernel_thread(_WorkerThread, "SD bus controller",
41ff76d2dfSAdrien Destugues 		B_NORMAL_PRIORITY, this);
42ff76d2dfSAdrien Destugues 	resume_thread(fWorkerThread);
4325b6a6f1Skrish_iyer }
4425b6a6f1Skrish_iyer 
4525b6a6f1Skrish_iyer 
4625b6a6f1Skrish_iyer MMCBus::~MMCBus()
4725b6a6f1Skrish_iyer {
4825b6a6f1Skrish_iyer 	CALLED();
49ff76d2dfSAdrien Destugues 
50ff76d2dfSAdrien Destugues 	// stop worker thread
51ff76d2dfSAdrien Destugues 	fStatus = B_SHUTTING_DOWN;
52ff76d2dfSAdrien Destugues 
53ff76d2dfSAdrien Destugues 	status_t result;
54ff76d2dfSAdrien Destugues 	if (fWorkerThread != 0)
55ff76d2dfSAdrien Destugues 		wait_for_thread(fWorkerThread, &result);
56ff76d2dfSAdrien Destugues 	// TODO power off cards, stop clock, etc if needed.
57*74b60970SAnarchos 
58*74b60970SAnarchos 	delete_sem(fLockSemaphore);
59*74b60970SAnarchos 	delete_sem(fScanSemaphore);
6025b6a6f1Skrish_iyer }
6125b6a6f1Skrish_iyer 
6225b6a6f1Skrish_iyer 
6325b6a6f1Skrish_iyer status_t
6425b6a6f1Skrish_iyer MMCBus::InitCheck()
6525b6a6f1Skrish_iyer {
6625b6a6f1Skrish_iyer 	return fStatus;
6725b6a6f1Skrish_iyer }
68ff76d2dfSAdrien Destugues 
69ff76d2dfSAdrien Destugues 
70dedbe94eSAdrien Destugues void
71dedbe94eSAdrien Destugues MMCBus::Rescan()
72dedbe94eSAdrien Destugues {
73dedbe94eSAdrien Destugues 	// Just wake up the thread for a scan
74*74b60970SAnarchos 	release_sem(fScanSemaphore);
75dedbe94eSAdrien Destugues }
76dedbe94eSAdrien Destugues 
77dedbe94eSAdrien Destugues 
78ff76d2dfSAdrien Destugues status_t
79ff76d2dfSAdrien Destugues MMCBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response)
80ff76d2dfSAdrien Destugues {
81*74b60970SAnarchos 	status_t status = _ActivateDevice(0);
82*74b60970SAnarchos 	if (status != B_OK)
83*74b60970SAnarchos 		return status;
84ff76d2dfSAdrien Destugues 	return fController->execute_command(fCookie, command, argument, response);
85ff76d2dfSAdrien Destugues }
86ff76d2dfSAdrien Destugues 
87ff76d2dfSAdrien Destugues 
88ff76d2dfSAdrien Destugues status_t
89*74b60970SAnarchos MMCBus::Read(uint16_t rca, off_t position, void* buffer, size_t* length)
90*74b60970SAnarchos {
91*74b60970SAnarchos 	status_t status = _ActivateDevice(rca);
92*74b60970SAnarchos 	if (status != B_OK)
93*74b60970SAnarchos 		return status;
94*74b60970SAnarchos 	return fController->read_naive(fCookie, position, buffer, length);
95*74b60970SAnarchos }
96*74b60970SAnarchos 
97*74b60970SAnarchos 
98*74b60970SAnarchos status_t
99*74b60970SAnarchos MMCBus::_ActivateDevice(uint16_t rca)
100*74b60970SAnarchos {
101*74b60970SAnarchos 	// Do nothing if the device is already activated
102*74b60970SAnarchos 	if (fActiveDevice == rca)
103*74b60970SAnarchos 		return B_OK;
104*74b60970SAnarchos 
105*74b60970SAnarchos 	uint32_t response;
106*74b60970SAnarchos 	status_t result;
107*74b60970SAnarchos 	result = fController->execute_command(fCookie, SD_SELECT_DESELECT_CARD,
108*74b60970SAnarchos 		((uint32)rca) << 16, &response);
109*74b60970SAnarchos 
110*74b60970SAnarchos 	if (result == B_OK)
111*74b60970SAnarchos 		fActiveDevice = rca;
112*74b60970SAnarchos 
113*74b60970SAnarchos 	return result;
114*74b60970SAnarchos }
115*74b60970SAnarchos 
116*74b60970SAnarchos 
117*74b60970SAnarchos status_t
118*74b60970SAnarchos MMCBus::_WorkerThread(void* cookie)
119ff76d2dfSAdrien Destugues {
120ff76d2dfSAdrien Destugues 	MMCBus* bus = (MMCBus*)cookie;
121ff76d2dfSAdrien Destugues 	uint32_t response;
122ff76d2dfSAdrien Destugues 
123*74b60970SAnarchos 	acquire_sem(bus->fLockSemaphore);
124*74b60970SAnarchos 
125ff76d2dfSAdrien Destugues 	// We assume the bus defaults to 400kHz clock and has already powered on
126ff76d2dfSAdrien Destugues 	// cards.
127ff76d2dfSAdrien Destugues 
128ff76d2dfSAdrien Destugues 	// Reset all cards on the bus
129*74b60970SAnarchos 	bus->ExecuteCommand(SD_GO_IDLE_STATE, 0, NULL);
130ff76d2dfSAdrien Destugues 
131dedbe94eSAdrien Destugues 	while (bus->fStatus != B_SHUTTING_DOWN) {
132*74b60970SAnarchos 		release_sem(bus->fLockSemaphore);
133dedbe94eSAdrien Destugues 		// wait for bus to signal a card is inserted
134*74b60970SAnarchos 		// Most of the time the thread will be waiting here, with
135*74b60970SAnarchos 		// fLockSemaphore released
136*74b60970SAnarchos 		acquire_sem(bus->fScanSemaphore);
137*74b60970SAnarchos 		acquire_sem(bus->fLockSemaphore);
138*74b60970SAnarchos 
139dedbe94eSAdrien Destugues 		TRACE("Scanning the bus\n");
140dedbe94eSAdrien Destugues 
141ff76d2dfSAdrien Destugues 		// Probe the voltage range
142*74b60970SAnarchos 		enum {
143*74b60970SAnarchos 			// Table 4-40 in physical layer specification v8.00
144*74b60970SAnarchos 			// All other values are currently reserved
145*74b60970SAnarchos 			HOST_27_36V = 1, //Host supplied voltage 2.7-3.6V
146*74b60970SAnarchos 		};
147*74b60970SAnarchos 
148*74b60970SAnarchos 		// An arbitrary value, we just need to check that the response
149*74b60970SAnarchos 		// containts the same.
150*74b60970SAnarchos 		static const uint8 kVoltageCheckPattern = 0xAA;
151*74b60970SAnarchos 
152ff76d2dfSAdrien Destugues 		// FIXME MMC cards will not reply to this! They expect CMD1 instead
153ff76d2dfSAdrien Destugues 		// SD v1 cards will also not reply, but we can proceed to ACMD41
154ff76d2dfSAdrien Destugues 		// If ACMD41 also does not work, it may be an SDIO card, too
155*74b60970SAnarchos 		uint32_t probe = (HOST_27_36V << 8) | kVoltageCheckPattern;
156518af33fSAdrien Destugues 		uint32_t hcs = 1 << 30;
157*74b60970SAnarchos 		if (bus->ExecuteCommand(SD_SEND_IF_COND, probe, &response) != B_OK) {
158518af33fSAdrien Destugues 			TRACE("Card does not implement CMD8, may be a V1 SD card\n");
159518af33fSAdrien Destugues 			// Do not check for SDHC support in this case
160518af33fSAdrien Destugues 			hcs = 0;
161518af33fSAdrien Destugues 		} else if (response != probe) {
162dedbe94eSAdrien Destugues 			ERROR("Card does not support voltage range (expected %x, "
163dedbe94eSAdrien Destugues 				"reply %x)\n", probe, response);
164ff76d2dfSAdrien Destugues 			// TODO what now?
165ff76d2dfSAdrien Destugues 		}
166ff76d2dfSAdrien Destugues 
167ff76d2dfSAdrien Destugues 		// Probe OCR, waiting for card to become ready
168ff76d2dfSAdrien Destugues 		uint32_t ocr;
169ff76d2dfSAdrien Destugues 		do {
170ff76d2dfSAdrien Destugues 			uint32_t cardStatus;
171*74b60970SAnarchos 			while (bus->ExecuteCommand(SD_APP_CMD, 0, &cardStatus)
172ff76d2dfSAdrien Destugues 					== B_BUSY) {
173ff76d2dfSAdrien Destugues 				ERROR("Card locked after CMD8...\n");
174ff76d2dfSAdrien Destugues 				snooze(1000000);
175ff76d2dfSAdrien Destugues 			}
176ff76d2dfSAdrien Destugues 			if ((cardStatus & 0xFFFF8000) != 0)
177dedbe94eSAdrien Destugues 				ERROR("SD card reports error %x\n", cardStatus);
178ff76d2dfSAdrien Destugues 			if ((cardStatus & (1 << 5)) == 0)
179dedbe94eSAdrien Destugues 				ERROR("Card did not enter ACMD mode\n");
180ff76d2dfSAdrien Destugues 
181*74b60970SAnarchos 			bus->ExecuteCommand(SD_SEND_OP_COND, hcs | 0xFF8000, &ocr);
182ff76d2dfSAdrien Destugues 
183ff76d2dfSAdrien Destugues 			if ((ocr & (1 << 31)) == 0) {
184ff76d2dfSAdrien Destugues 				TRACE("Card is busy\n");
185ff76d2dfSAdrien Destugues 				snooze(100000);
186ff76d2dfSAdrien Destugues 			}
187ff76d2dfSAdrien Destugues 		} while (((ocr & (1 << 31)) == 0));
188ff76d2dfSAdrien Destugues 
189dedbe94eSAdrien Destugues 		// FIXME this should be asked to each card, when there are multiple
190dedbe94eSAdrien Destugues 		// ones. So ACMD41 should be moved inside the probing loop below?
191dedbe94eSAdrien Destugues 		uint8_t cardType = CARD_TYPE_SD;
192dedbe94eSAdrien Destugues 
193eb92a834SAdrien Destugues 		if ((ocr & hcs) != 0)
194dedbe94eSAdrien Destugues 			cardType = CARD_TYPE_SDHC;
195eb92a834SAdrien Destugues 		if ((ocr & (1 << 29)) != 0)
196dedbe94eSAdrien Destugues 			cardType = CARD_TYPE_UHS2;
197eb92a834SAdrien Destugues 		if ((ocr & (1 << 24)) != 0)
198ff76d2dfSAdrien Destugues 			TRACE("Card supports 1.8v");
199ff76d2dfSAdrien Destugues 		TRACE("Voltage range: %x\n", ocr & 0xFFFFFF);
200ff76d2dfSAdrien Destugues 
201ff76d2dfSAdrien Destugues 		// TODO send CMD11 to switch to low voltage mode if card supports it?
202ff76d2dfSAdrien Destugues 
203*74b60970SAnarchos 		// We use CMD2 (ALL_SEND_CID) and CMD3 (SEND_RELATIVE_ADDR) to assign
204*74b60970SAnarchos 		// an RCA to all cards. Initially all cards have an RCA of 0 and will
205*74b60970SAnarchos 		// all receive CMD2. But only ne of them will reply (they do collision
206*74b60970SAnarchos 		// detection while sending the CID in reply). We assign a new RCA to
207*74b60970SAnarchos 		// that first card, and repeat the process with the remaining ones
208*74b60970SAnarchos 		// until no one answers to CMD2. Then we know all cards have an RCA
209*74b60970SAnarchos 		// (and a matching published device on our side).
210ff76d2dfSAdrien Destugues 		uint32_t cid[4];
211*74b60970SAnarchos 
212*74b60970SAnarchos 		while (bus->ExecuteCommand(SD_ALL_SEND_CID, 0, cid) == B_OK) {
213*74b60970SAnarchos 			bus->ExecuteCommand(SD_SEND_RELATIVE_ADDR, 0, &response);
214ff76d2dfSAdrien Destugues 
215ff76d2dfSAdrien Destugues 			TRACE("RCA: %x Status: %x\n", response >> 16, response & 0xFFFF);
216ff76d2dfSAdrien Destugues 
217dedbe94eSAdrien Destugues 			if ((response & 0xFF00) != 0x500) {
218dedbe94eSAdrien Destugues 				TRACE("Card did not enter data state\n");
219dedbe94eSAdrien Destugues 				// This probably means there are no more cards to scan on the
220dedbe94eSAdrien Destugues 				// bus, so exit the loop.
221dedbe94eSAdrien Destugues 				break;
222dedbe94eSAdrien Destugues 			}
223ff76d2dfSAdrien Destugues 
224dedbe94eSAdrien Destugues 			// The card now has an RCA and it entered the data phase, which
225dedbe94eSAdrien Destugues 			// means our initializing job is over, we can pass it on to the
226dedbe94eSAdrien Destugues 			// mmc_disk driver.
227ff76d2dfSAdrien Destugues 
228dedbe94eSAdrien Destugues 			uint32_t vendor = cid[3] & 0xFFFFFF;
229eb92a834SAdrien Destugues 			char name[6] = {(char)(cid[2] >> 24), (char)(cid[2] >> 16),
230eb92a834SAdrien Destugues 				(char)(cid[2] >> 8), (char)cid[2], (char)(cid[1] >> 24), 0};
231dedbe94eSAdrien Destugues 			uint32_t serial = (cid[1] << 16) | (cid[0] >> 16);
232dedbe94eSAdrien Destugues 			uint16_t revision = (cid[1] >> 20) & 0xF;
233dedbe94eSAdrien Destugues 			revision *= 100;
234dedbe94eSAdrien Destugues 			revision += (cid[1] >> 16) & 0xF;
235dedbe94eSAdrien Destugues 			uint8_t month = cid[0] & 0xF;
236dedbe94eSAdrien Destugues 			uint16_t year = 2000 + ((cid[0] >> 4) & 0xFF);
237dedbe94eSAdrien Destugues 			uint16_t rca = response >> 16;
238ff76d2dfSAdrien Destugues 
239dedbe94eSAdrien Destugues 			device_attr attrs[] = {
240dedbe94eSAdrien Destugues 				{ B_DEVICE_BUS, B_STRING_TYPE, {string: "mmc" }},
24162eaf4c0SAdrien Destugues 				{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "mmc device" }},
242dedbe94eSAdrien Destugues 				{ B_DEVICE_VENDOR_ID, B_UINT32_TYPE, {ui32: vendor}},
243dedbe94eSAdrien Destugues 				{ B_DEVICE_ID, B_STRING_TYPE, {string: name}},
244dedbe94eSAdrien Destugues 				{ B_DEVICE_UNIQUE_ID, B_UINT32_TYPE, {ui32: serial}},
245dedbe94eSAdrien Destugues 				{ "mmc/revision", B_UINT16_TYPE, {ui16: revision}},
246dedbe94eSAdrien Destugues 				{ "mmc/month", B_UINT8_TYPE, {ui8: month}},
247dedbe94eSAdrien Destugues 				{ "mmc/year", B_UINT16_TYPE, {ui16: year}},
248e4689893SAdrien Destugues 				{ kMmcRcaAttribute, B_UINT16_TYPE, {ui16: rca}},
249e4689893SAdrien Destugues 				{ kMmcTypeAttribute, B_UINT8_TYPE, {ui8: cardType}},
250dedbe94eSAdrien Destugues 				{}
251dedbe94eSAdrien Destugues 			};
252dedbe94eSAdrien Destugues 
253dedbe94eSAdrien Destugues 			// publish child device for the card
254dedbe94eSAdrien Destugues 			gDeviceManager->register_node(bus->fNode, MMC_BUS_MODULE_NAME,
255dedbe94eSAdrien Destugues 				attrs, NULL, NULL);
256dedbe94eSAdrien Destugues 		}
257dedbe94eSAdrien Destugues 
258dedbe94eSAdrien Destugues 		// FIXME we also need to unpublish devices that are gone. Probably need
259dedbe94eSAdrien Destugues 		// to "ping" all RCAs somehow? Or is there an interrupt we can look for
260dedbe94eSAdrien Destugues 		// to detect added/removed cards?
261dedbe94eSAdrien Destugues 	}
262dedbe94eSAdrien Destugues 
263*74b60970SAnarchos 	release_sem(bus->fLockSemaphore);
264*74b60970SAnarchos 
265dedbe94eSAdrien Destugues 	TRACE("poller thread terminating");
266dedbe94eSAdrien Destugues 	return B_OK;
267ff76d2dfSAdrien Destugues }
268