xref: /haiku/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp (revision ff76d2df8e0e9b7fcc166957bcc1db0ea07b9724)
125b6a6f1Skrish_iyer /*
225b6a6f1Skrish_iyer  * Copyright 2018 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 
10*ff76d2dfSAdrien Destugues #include <Errors.h>
11*ff76d2dfSAdrien Destugues 
12*ff76d2dfSAdrien Destugues #include <stdint.h>
13*ff76d2dfSAdrien 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*ff76d2dfSAdrien Destugues 	fWorkerThread(0)
2225b6a6f1Skrish_iyer {
2325b6a6f1Skrish_iyer 	CALLED();
24*ff76d2dfSAdrien Destugues 
25*ff76d2dfSAdrien 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 	}
36*ff76d2dfSAdrien Destugues 
37*ff76d2dfSAdrien Destugues 	// TODO enumerate the bus (in a separate thread?)
38*ff76d2dfSAdrien Destugues 	fWorkerThread = spawn_kernel_thread(WorkerThread, "SD bus controller",
39*ff76d2dfSAdrien Destugues 		B_NORMAL_PRIORITY, this);
40*ff76d2dfSAdrien Destugues 	resume_thread(fWorkerThread);
4125b6a6f1Skrish_iyer }
4225b6a6f1Skrish_iyer 
4325b6a6f1Skrish_iyer 
4425b6a6f1Skrish_iyer MMCBus::~MMCBus()
4525b6a6f1Skrish_iyer {
4625b6a6f1Skrish_iyer 	CALLED();
47*ff76d2dfSAdrien Destugues 
48*ff76d2dfSAdrien Destugues 	// stop worker thread
49*ff76d2dfSAdrien Destugues 	fStatus = B_SHUTTING_DOWN;
50*ff76d2dfSAdrien Destugues 
51*ff76d2dfSAdrien Destugues 	status_t result;
52*ff76d2dfSAdrien Destugues 	if (fWorkerThread != 0)
53*ff76d2dfSAdrien Destugues 		wait_for_thread(fWorkerThread, &result);
54*ff76d2dfSAdrien 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 }
63*ff76d2dfSAdrien Destugues 
64*ff76d2dfSAdrien Destugues 
65*ff76d2dfSAdrien Destugues status_t
66*ff76d2dfSAdrien Destugues MMCBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response)
67*ff76d2dfSAdrien Destugues {
68*ff76d2dfSAdrien Destugues 	return fController->execute_command(fCookie, command, argument, response);
69*ff76d2dfSAdrien Destugues }
70*ff76d2dfSAdrien Destugues 
71*ff76d2dfSAdrien Destugues 
72*ff76d2dfSAdrien Destugues status_t
73*ff76d2dfSAdrien Destugues MMCBus::WorkerThread(void* cookie)
74*ff76d2dfSAdrien Destugues {
75*ff76d2dfSAdrien Destugues 	TRACE("worker thread spawned.\n");
76*ff76d2dfSAdrien Destugues 
77*ff76d2dfSAdrien Destugues 	MMCBus* bus = (MMCBus*)cookie;
78*ff76d2dfSAdrien Destugues 	uint32_t response;
79*ff76d2dfSAdrien Destugues 
80*ff76d2dfSAdrien Destugues 	// FIXME wait for bus to signal a card is inserted
81*ff76d2dfSAdrien Destugues 	// We assume the bus defaults to 400kHz clock and has already powered on
82*ff76d2dfSAdrien Destugues 	// cards.
83*ff76d2dfSAdrien Destugues 
84*ff76d2dfSAdrien Destugues 	// Reset all cards on the bus
85*ff76d2dfSAdrien Destugues 	bus->ExecuteCommand(0, 0, NULL);
86*ff76d2dfSAdrien Destugues 
87*ff76d2dfSAdrien Destugues 	// Probe the voltage range
88*ff76d2dfSAdrien Destugues 	// FIXME MMC cards will not reply to this! They expect CMD1 instead
89*ff76d2dfSAdrien Destugues 	// SD v1 cards will also not reply, but we can proceed to ACMD41
90*ff76d2dfSAdrien Destugues 	// If ACMD41 also does not work, it may be an SDIO card, too
91*ff76d2dfSAdrien Destugues 	uint32_t probe = (1 << 8) | 0x55;
92*ff76d2dfSAdrien Destugues 	bus->ExecuteCommand(8, probe, &response);
93*ff76d2dfSAdrien Destugues 	if (response != probe) {
94*ff76d2dfSAdrien Destugues 		ERROR("Card does not support voltage range (expected %x, reply %x)\n",
95*ff76d2dfSAdrien Destugues 			probe, response);
96*ff76d2dfSAdrien Destugues 		// TODO what now?
97*ff76d2dfSAdrien Destugues 	}
98*ff76d2dfSAdrien Destugues 
99*ff76d2dfSAdrien Destugues 	// Probe OCR, waiting for card to become ready
100*ff76d2dfSAdrien Destugues 	uint32_t ocr;
101*ff76d2dfSAdrien Destugues 	do {
102*ff76d2dfSAdrien Destugues 		uint32_t cardStatus;
103*ff76d2dfSAdrien Destugues 		while (bus->ExecuteCommand(55, 0, &cardStatus)
104*ff76d2dfSAdrien Destugues 			== B_BUSY) {
105*ff76d2dfSAdrien Destugues 			// FIXME we shouldn't get here if we handle CMD8 failure properly
106*ff76d2dfSAdrien Destugues 			ERROR("Card locked after CMD8...\n");
107*ff76d2dfSAdrien Destugues 			snooze(1000000);
108*ff76d2dfSAdrien Destugues 		}
109*ff76d2dfSAdrien Destugues 		if ((cardStatus & 0xFFFF8000) != 0)
110*ff76d2dfSAdrien Destugues 			ERROR("SD card reports error\n");
111*ff76d2dfSAdrien Destugues 		if ((cardStatus & (1 << 5)) == 0)
112*ff76d2dfSAdrien Destugues 			ERROR("Card did not enter ACMD mode");
113*ff76d2dfSAdrien Destugues 
114*ff76d2dfSAdrien Destugues 		bus->ExecuteCommand(41, (1 << 30) | 0xFF8000, &ocr);
115*ff76d2dfSAdrien Destugues 
116*ff76d2dfSAdrien Destugues 		if ((ocr & (1 << 31)) == 0) {
117*ff76d2dfSAdrien Destugues 			TRACE("Card is busy\n");
118*ff76d2dfSAdrien Destugues 			snooze(100000);
119*ff76d2dfSAdrien Destugues 		}
120*ff76d2dfSAdrien Destugues 	} while (((ocr & (1 << 31)) == 0));
121*ff76d2dfSAdrien Destugues 
122*ff76d2dfSAdrien Destugues 	if (ocr & (1 << 30))
123*ff76d2dfSAdrien Destugues 		TRACE("Card is SDHC");
124*ff76d2dfSAdrien Destugues 	if (ocr & (1 << 29))
125*ff76d2dfSAdrien Destugues 		TRACE("Card supports UHS-II");
126*ff76d2dfSAdrien Destugues 	if (ocr & (1 << 24))
127*ff76d2dfSAdrien Destugues 		TRACE("Card supports 1.8v");
128*ff76d2dfSAdrien Destugues 	TRACE("Voltage range: %x\n", ocr & 0xFFFFFF);
129*ff76d2dfSAdrien Destugues 
130*ff76d2dfSAdrien Destugues 	// TODO send CMD11 to switch to low voltage mode if card supports it?
131*ff76d2dfSAdrien Destugues 
132*ff76d2dfSAdrien Destugues 	uint32_t cid[4];
133*ff76d2dfSAdrien Destugues 	bus->ExecuteCommand(2, 0, cid);
134*ff76d2dfSAdrien Destugues 
135*ff76d2dfSAdrien Destugues 	TRACE("Manufacturer: %02x%c%c\n", cid[3] >> 16, cid[3] >> 8, cid[3]);
136*ff76d2dfSAdrien Destugues 	TRACE("Name: %c%c%c%c%c\n", cid[2] >> 24, cid[2] >> 16, cid[2] >> 8,
137*ff76d2dfSAdrien Destugues 		cid[2], cid[1] >> 24);
138*ff76d2dfSAdrien Destugues 	TRACE("Revision: %d.%d\n", (cid[1] >> 20) & 0xF, (cid[1] >> 16) & 0xF);
139*ff76d2dfSAdrien Destugues 	TRACE("Serial number: %x\n", (cid[1] << 16) | (cid[0] >> 16));
140*ff76d2dfSAdrien Destugues 	TRACE("Date: %d/%d\n", cid[0] & 0xF, 2000 + ((cid[0] >> 4) & 0xFF));
141*ff76d2dfSAdrien Destugues 
142*ff76d2dfSAdrien Destugues 	bus->ExecuteCommand(3, 0, &response);
143*ff76d2dfSAdrien Destugues 
144*ff76d2dfSAdrien Destugues 	TRACE("RCA: %x Status: %x\n", response >> 16, response & 0xFFFF);
145*ff76d2dfSAdrien Destugues 
146*ff76d2dfSAdrien Destugues 	if ((response & 0xFF00) != 0x5000)
147*ff76d2dfSAdrien Destugues 		ERROR("Card did not enter data state\n");
148*ff76d2dfSAdrien Destugues 
149*ff76d2dfSAdrien Destugues 	// The card now has an RCA and it entered the data phase, which means our
150*ff76d2dfSAdrien Destugues 	// initializing job is over, we can pass it on to the mmc_disk driver.
151*ff76d2dfSAdrien Destugues 
152*ff76d2dfSAdrien Destugues 	// TODO publish child device for the card
153*ff76d2dfSAdrien Destugues 	// TODO fill it with attributes from the CID
154*ff76d2dfSAdrien Destugues 
155*ff76d2dfSAdrien Destugues 	// TODO iterate CMD2/CMD3 to assign an RCA to all cards (and publish devices
156*ff76d2dfSAdrien Destugues 	// for each of them)
157*ff76d2dfSAdrien Destugues }
158