xref: /haiku/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp (revision e46898932ce906c782f9f400b6d2d588ccb0af81)
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