xref: /haiku/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp (revision dedbe94e4647dac61656832e08895d08ec33674c)
125b6a6f1Skrish_iyer /*
2*dedbe94eSAdrien 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 
37*dedbe94eSAdrien 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 
65*dedbe94eSAdrien Destugues void
66*dedbe94eSAdrien Destugues MMCBus::Rescan()
67*dedbe94eSAdrien Destugues {
68*dedbe94eSAdrien Destugues 	// Just wake up the thread for a scan
69*dedbe94eSAdrien Destugues 	release_sem(fSemaphore);
70*dedbe94eSAdrien Destugues }
71*dedbe94eSAdrien Destugues 
72*dedbe94eSAdrien 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 
92*dedbe94eSAdrien Destugues 	while (bus->fStatus != B_SHUTTING_DOWN) {
93*dedbe94eSAdrien Destugues 		// wait for bus to signal a card is inserted
94*dedbe94eSAdrien Destugues 		acquire_sem(bus->fSemaphore);
95*dedbe94eSAdrien Destugues 		TRACE("Scanning the bus\n");
96*dedbe94eSAdrien 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
101*dedbe94eSAdrien 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) {
108*dedbe94eSAdrien Destugues 			ERROR("Card does not support voltage range (expected %x, "
109*dedbe94eSAdrien 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)
123*dedbe94eSAdrien Destugues 				ERROR("SD card reports error %x\n", cardStatus);
124ff76d2dfSAdrien Destugues 			if ((cardStatus & (1 << 5)) == 0)
125*dedbe94eSAdrien 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 
135*dedbe94eSAdrien Destugues 		// FIXME this should be asked to each card, when there are multiple
136*dedbe94eSAdrien Destugues 		// ones. So ACMD41 should be moved inside the probing loop below?
137*dedbe94eSAdrien Destugues 		uint8_t cardType = CARD_TYPE_SD;
138*dedbe94eSAdrien Destugues 
139518af33fSAdrien Destugues 		if (ocr & hcs != 0)
140*dedbe94eSAdrien Destugues 			cardType = CARD_TYPE_SDHC;
141518af33fSAdrien Destugues 		if (ocr & (1 << 29) != 0)
142*dedbe94eSAdrien 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 
149*dedbe94eSAdrien Destugues 		// iterate CMD2/CMD3 to assign an RCA to all cards and publish devices
150*dedbe94eSAdrien Destugues 		// for each of them
151ff76d2dfSAdrien Destugues 		uint32_t cid[4];
152*dedbe94eSAdrien 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 
157*dedbe94eSAdrien Destugues 			if ((response & 0xFF00) != 0x500) {
158*dedbe94eSAdrien Destugues 				TRACE("Card did not enter data state\n");
159*dedbe94eSAdrien Destugues 				// This probably means there are no more cards to scan on the
160*dedbe94eSAdrien Destugues 				// bus, so exit the loop.
161*dedbe94eSAdrien Destugues 				break;
162*dedbe94eSAdrien Destugues 			}
163ff76d2dfSAdrien Destugues 
164*dedbe94eSAdrien Destugues 			// The card now has an RCA and it entered the data phase, which
165*dedbe94eSAdrien Destugues 			// means our initializing job is over, we can pass it on to the
166*dedbe94eSAdrien Destugues 			// mmc_disk driver.
167ff76d2dfSAdrien Destugues 
168*dedbe94eSAdrien Destugues 			uint32_t vendor = cid[3] & 0xFFFFFF;
169*dedbe94eSAdrien Destugues 			char name[6] = {cid[2] >> 24, cid[2] >> 16, cid[2] >> 8, cid[2],
170*dedbe94eSAdrien Destugues 				cid[1] >> 24, 0};
171*dedbe94eSAdrien Destugues 			uint32_t serial = (cid[1] << 16) | (cid[0] >> 16);
172*dedbe94eSAdrien Destugues 			uint16_t revision = (cid[1] >> 20) & 0xF;
173*dedbe94eSAdrien Destugues 			revision *= 100;
174*dedbe94eSAdrien Destugues 			revision += (cid[1] >> 16) & 0xF;
175*dedbe94eSAdrien Destugues 			uint8_t month = cid[0] & 0xF;
176*dedbe94eSAdrien Destugues 			uint16_t year = 2000 + ((cid[0] >> 4) & 0xFF);
177*dedbe94eSAdrien Destugues 			uint16_t rca = response >> 16;
178ff76d2dfSAdrien Destugues 
179*dedbe94eSAdrien Destugues 			device_attr attrs[] = {
180*dedbe94eSAdrien Destugues 				{ B_DEVICE_BUS, B_STRING_TYPE, {string: "mmc" }},
181*dedbe94eSAdrien Destugues 				{ B_DEVICE_VENDOR_ID, B_UINT32_TYPE, {ui32: vendor}},
182*dedbe94eSAdrien Destugues 				{ B_DEVICE_ID, B_STRING_TYPE, {string: name}},
183*dedbe94eSAdrien Destugues 				{ B_DEVICE_UNIQUE_ID, B_UINT32_TYPE, {ui32: serial}},
184*dedbe94eSAdrien Destugues 				{ "mmc/revision", B_UINT16_TYPE, {ui16: revision}},
185*dedbe94eSAdrien Destugues 				{ "mmc/month", B_UINT8_TYPE, {ui8: month}},
186*dedbe94eSAdrien Destugues 				{ "mmc/year", B_UINT16_TYPE, {ui16: year}},
187*dedbe94eSAdrien Destugues 				{ "mmc/rca", B_UINT16_TYPE, {ui16: rca}},
188*dedbe94eSAdrien Destugues 				{ "mmc/type", B_UINT8_TYPE, {ui8: cardType}},
189*dedbe94eSAdrien Destugues 				{}
190*dedbe94eSAdrien Destugues 			};
191*dedbe94eSAdrien Destugues 
192*dedbe94eSAdrien Destugues 			// publish child device for the card
193*dedbe94eSAdrien Destugues 			gDeviceManager->register_node(bus->fNode, MMC_BUS_MODULE_NAME,
194*dedbe94eSAdrien Destugues 				attrs, NULL, NULL);
195*dedbe94eSAdrien Destugues 		}
196*dedbe94eSAdrien Destugues 
197*dedbe94eSAdrien Destugues 		// FIXME we also need to unpublish devices that are gone. Probably need
198*dedbe94eSAdrien Destugues 		// to "ping" all RCAs somehow? Or is there an interrupt we can look for
199*dedbe94eSAdrien Destugues 		// to detect added/removed cards?
200*dedbe94eSAdrien Destugues 	}
201*dedbe94eSAdrien Destugues 
202*dedbe94eSAdrien Destugues 	TRACE("poller thread terminating");
203*dedbe94eSAdrien Destugues 	return B_OK;
204ff76d2dfSAdrien Destugues }
205