xref: /haiku/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp (revision db6fcb750a1afb5fdc752322972adf6044d3b4c4)
1 /*
2  * Copyright 2018-2019 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		B Krishnan Iyer, krishnaniyer97@gmail.com
7  */
8 #include <new>
9 #include <stdio.h>
10 #include <string.h>
11 
12 #include <bus/PCI.h>
13 #include <PCI_x86.h>
14 
15 #include <KernelExport.h>
16 
17 #include "mmc.h"
18 #include "sdhci_pci.h"
19 
20 
21 #define TRACE_SDHCI
22 #ifdef TRACE_SDHCI
23 #	define TRACE(x...) dprintf("\33[33msdhci_pci:\33[0m " x)
24 #else
25 #	define TRACE(x...) ;
26 #endif
27 #define TRACE_ALWAYS(x...)	dprintf("\33[33msdhci_pci:\33[0m " x)
28 #define ERROR(x...)			dprintf("\33[33msdhci_pci:\33[0m " x)
29 #define CALLED(x...)		TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
30 
31 
32 #define SDHCI_PCI_DEVICE_MODULE_NAME "busses/mmc/sdhci_pci/driver_v1"
33 #define SDHCI_PCI_MMC_BUS_MODULE_NAME "busses/mmc/sdhci_pci/device/v1"
34 
35 #define SLOTS_COUNT				"device/slots_count"
36 #define SLOT_NUMBER				"device/slot"
37 #define BAR_INDEX				"device/bar"
38 
39 
40 class SdhciBus {
41 	public:
42 							SdhciBus(struct registers* registers, uint8_t irq);
43 							~SdhciBus();
44 
45 		void				EnableInterrupts(uint32_t mask);
46 		status_t			ExecuteCommand(uint8_t command, uint32_t argument,
47 								uint32_t* response);
48 		int32				HandleInterrupt();
49 		status_t			InitCheck();
50 		void				Reset();
51 		void				SetClock(int kilohertz);
52 
53 	private:
54 		void				DumpRegisters(uint8_t slot);
55 		bool				PowerOn();
56 		void				RecoverError();
57 
58 	private:
59 		struct registers*	fRegisters;
60 		uint8_t				fIrq;
61 		sem_id				fSemaphore;
62 		status_t			fStatus;
63 };
64 
65 
66 device_manager_info* gDeviceManager;
67 device_module_info* gMMCBusController;
68 static pci_x86_module_info* sPCIx86Module;
69 
70 
71 static int32
72 sdhci_generic_interrupt(void* data)
73 {
74 	SdhciBus* bus = (SdhciBus*)data;
75 	return bus->HandleInterrupt();
76 }
77 
78 
79 SdhciBus::SdhciBus(struct registers* registers, uint8_t irq)
80 	:
81 	fRegisters(registers),
82 	fIrq(irq),
83 	fSemaphore(0)
84 {
85 	if (irq == 0 || irq == 0xff) {
86 		ERROR("PCI IRQ not assigned\n");
87 		fStatus = B_BAD_DATA;
88 		return;
89 	}
90 
91 	fSemaphore = create_sem(0, "SDHCI command");
92 
93 	fStatus = install_io_interrupt_handler(fIrq,
94 		sdhci_generic_interrupt, this, 0);
95 
96 	if (fStatus != B_OK) {
97 		ERROR("can't install interrupt handler\n");
98 		return;
99 	}
100 
101 	// First of all, we have to make sure we are in a sane state. The easiest
102 	// way is to reset everything.
103 	Reset();
104 
105 	// Then we configure the clock to the frequency needed for initialization
106 	SetClock(400);
107 
108 	// And we turn on the power supply to the card
109 	// FIXME maybe this should only be done when a card is inserted?
110 	if (!PowerOn()) {
111 		ERROR("Failed to power on the card\n");
112 		fStatus = B_NO_INIT;
113 		return;
114 	}
115 
116 	// FIXME do we need all these? Wouldn't card insertion/removal and command
117 	// completion be enough?
118 	EnableInterrupts(SDHCI_INT_CMD_CMP
119 		| SDHCI_INT_TRANS_CMP | SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM
120 		| SDHCI_INT_TIMEOUT | SDHCI_INT_CRC | SDHCI_INT_INDEX
121 		| SDHCI_INT_BUS_POWER | SDHCI_INT_END_BIT);
122 
123 	fRegisters->interrupt_status_enable |= SDHCI_INT_ERROR;
124 }
125 
126 
127 SdhciBus::~SdhciBus()
128 {
129 	if (fSemaphore != 0)
130 		delete_sem(fSemaphore);
131 
132 	EnableInterrupts(0);
133 	if (fIrq != 0)
134 		remove_io_interrupt_handler(fIrq, sdhci_generic_interrupt, this);
135 
136 	area_id regs_area = area_for(fRegisters);
137 	delete_area(regs_area);
138 }
139 
140 
141 void
142 SdhciBus::DumpRegisters(uint8_t slot)
143 {
144 #ifdef TRACE_SDHCI
145 	TRACE("Register values for slot %d:\n", slot);
146 	TRACE("system_address: %d\n", fRegisters->system_address);
147 	TRACE("%d blocks of size %d\n", fRegisters->block_count,
148 		fRegisters->block_size);
149 	TRACE("argument: %x\n", fRegisters->argument);
150 	TRACE("transfer_mode: %d\n", fRegisters->transfer_mode);
151 	TRACE("command: %x\n", fRegisters->command.Bits());
152 	TRACE("response:");
153 	for (int i = 0; i < 4; i++)
154 		dprintf(" %d", fRegisters->response[i]);
155 	dprintf("\n");
156 	TRACE("buffer_data_port: %d\n", fRegisters->buffer_data_port);
157 	TRACE("present_state: %x\n", fRegisters->present_state.Bits());
158 	TRACE("power_control: %d\n", fRegisters->power_control.Bits());
159 	TRACE("host_control: %d\n", fRegisters->host_control);
160 	TRACE("wakeup_control: %d\n", fRegisters->wakeup_control);
161 	TRACE("block_gap_control: %d\n", fRegisters->block_gap_control);
162 	TRACE("clock_control: %x\n", fRegisters->clock_control.Bits());
163 	TRACE("software_reset: %d\n", fRegisters->software_reset.Bits());
164 	TRACE("timeout_control: %d\n", fRegisters->timeout_control);
165 	TRACE("interrupt_status: %x enable: %x signal: %x\n",
166 		fRegisters->interrupt_status, fRegisters->interrupt_status_enable,
167 		fRegisters->interrupt_signal_enable);
168 	TRACE("auto_cmd12_error_status: %d\n", fRegisters->auto_cmd12_error_status);
169 	TRACE("capabilities: %lld\n", fRegisters->capabilities.Bits());
170 	TRACE("max_current_capabilities: %lld\n",
171 		fRegisters->max_current_capabilities);
172 	TRACE("slot_interrupt_status: %d\n", fRegisters->slot_interrupt_status);
173 	TRACE("host_controller_version spec %x vendor %x\n",
174 		fRegisters->host_controller_version.specVersion,
175 		fRegisters->host_controller_version.vendorVersion);
176 #endif
177 }
178 
179 
180 void
181 SdhciBus::EnableInterrupts(uint32_t mask)
182 {
183 	fRegisters->interrupt_status_enable = mask;
184 	fRegisters->interrupt_signal_enable = mask;
185 }
186 
187 
188 status_t
189 SdhciBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response)
190 {
191 	TRACE("ExecuteCommand(%d, %x)\n", command, argument);
192 	if (fRegisters->present_state.CommandInhibit()) {
193 		ERROR("Execution aborted, command inhibit\n");
194 		return B_BUSY;
195 	}
196 
197 	fRegisters->argument = argument;
198 
199 	uint32_t replyType;
200 
201 	switch(command) {
202 		case 0:
203 			replyType = Command::kNoReplyType;
204 			break;
205 		case 2:
206 			replyType = Command::kR2Type;
207 			break;
208 		case 55:
209 			replyType = Command::kR1Type;
210 			break;
211 		case 41: // ACMD
212 			replyType = Command::kR3Type;
213 			break;
214 		case 3:
215 			replyType = Command::kR6Type;
216 			break;
217 		case 8:
218 			replyType = Command::kR7Type;
219 			break;
220 		default:
221 			ERROR("Unknown command\n");
222 			return B_BAD_DATA;
223 	}
224 
225 	fRegisters->command.SendCommand(command, replyType);
226 	acquire_sem(fSemaphore);
227 
228 	if (fRegisters->interrupt_status & SDHCI_INT_ERROR) {
229 		fRegisters->interrupt_status |= SDHCI_INT_ERROR;
230 		ERROR("Command execution failed\n");
231 		// TODO look at errors in interrupt_status register for more details
232 		// and return a more appropriate error code
233 		return B_ERROR;
234 	}
235 
236 	if (fRegisters->present_state.CommandInhibit()) {
237 		TRACE("Command execution failed, card stalled\n");
238 		// Clear the stall
239 		fRegisters->software_reset.ResetCommandLine();
240 		return B_ERROR;
241 	}
242 
243 	if (replyType == Command::kNoReplyType) {
244 		// No response
245 	} else if (replyType == Command::kR2Type) {
246 		// 128 bit response
247 		response[0] = fRegisters->response[0];
248 		response[1] = fRegisters->response[1];
249 		response[2] = fRegisters->response[2];
250 		response[3] = fRegisters->response[3];
251 	} else {
252 		// 32 bit response
253 		*response = fRegisters->response[0];
254 	}
255 
256 	ERROR("Command execution complete\n");
257 	return B_OK;
258 }
259 
260 
261 status_t
262 SdhciBus::InitCheck()
263 {
264 	return fStatus;
265 }
266 
267 
268 void
269 SdhciBus::Reset()
270 {
271 	fRegisters->software_reset.ResetAll();
272 }
273 
274 
275 void
276 SdhciBus::SetClock(int kilohertz)
277 {
278 	int base_clock = fRegisters->capabilities.BaseClockFrequency();
279 	// Try to get as close to 400kHz as possible, but not faster
280 	int divider = base_clock * 1000 / kilohertz;
281 
282 	if (fRegisters->host_controller_version.specVersion <= 1) {
283 		// Old controller only support power of two dividers up to 256,
284 		// round to next power of two up to 256
285 		if (divider > 256)
286 			divider = 256;
287 
288 		divider--;
289 		divider |= divider >> 1;
290 		divider |= divider >> 2;
291 		divider |= divider >> 4;
292 		divider++;
293 	}
294 
295 	divider = fRegisters->clock_control.SetDivider(divider);
296 
297 	// Log the value after possible rounding by SetDivider (only even values
298 	// are allowed).
299 	TRACE("SDCLK frequency: %dMHz / %d = %dkHz\n", base_clock, divider,
300 		base_clock * 1000 / divider);
301 
302 	// We have set the divider, now we can enable the internal clock.
303 	fRegisters->clock_control.EnableInternal();
304 
305 	// wait until internal clock is stabilized
306 	while (!(fRegisters->clock_control.InternalStable()));
307 
308 	fRegisters->clock_control.EnablePLL();
309 	while (!(fRegisters->clock_control.InternalStable()));
310 
311 	// Finally, route the clock to the SD card
312 	fRegisters->clock_control.EnableSD();
313 }
314 
315 
316 static void
317 sdhci_stop_clock(struct registers* regs)
318 {
319 	regs->clock_control.DisableSD();
320 }
321 
322 
323 bool
324 SdhciBus::PowerOn()
325 {
326 	if (!fRegisters->present_state.IsCardInserted()) {
327 		TRACE("Card not inserted, not powering on for now\n");
328 		return false;
329 	}
330 
331 	uint8_t supportedVoltages = fRegisters->capabilities.SupportedVoltages();
332 	if ((supportedVoltages & Capabilities::k3v3) != 0)
333 		fRegisters->power_control.SetVoltage(PowerControl::k3v3);
334 	else if ((supportedVoltages & Capabilities::k3v0) != 0)
335 		fRegisters->power_control.SetVoltage(PowerControl::k3v0);
336 	else if ((supportedVoltages & Capabilities::k1v8) != 0)
337 		fRegisters->power_control.SetVoltage(PowerControl::k1v8);
338 	else {
339 		fRegisters->power_control.PowerOff();
340 		ERROR("No voltage is supported\n");
341 		return false;
342 	}
343 
344 	return true;
345 }
346 
347 
348 static status_t
349 init_bus(device_node* node, void** bus_cookie)
350 {
351 	CALLED();
352 
353 	// Get the PCI driver and device
354 	pci_device_module_info* pci;
355 	pci_device* device;
356 
357 	device_node* parent = gDeviceManager->get_parent_node(node);
358 	device_node* pciParent = gDeviceManager->get_parent_node(parent);
359 	gDeviceManager->get_driver(pciParent, (driver_module_info**)&pci,
360 	        (void**)&device);
361 	gDeviceManager->put_node(pciParent);
362 	gDeviceManager->put_node(parent);
363 
364 	if (get_module(B_PCI_X86_MODULE_NAME, (module_info**)&sPCIx86Module)
365 	    != B_OK) {
366 	    sPCIx86Module = NULL;
367 		ERROR("PCIx86Module not loaded\n");
368 		// FIXME try probing FDT as well
369 		return -1;
370 	}
371 
372 	uint8_t bar, slot;
373 	if (gDeviceManager->get_attr_uint8(node, SLOT_NUMBER, &slot, false) < B_OK
374 		|| gDeviceManager->get_attr_uint8(node, BAR_INDEX, &bar, false) < B_OK)
375 		return -1;
376 
377 	TRACE("Register SD bus at slot %d, using bar %d\n", slot + 1, bar);
378 
379 	pci_info pciInfo;
380 	pci->get_pci_info(device, &pciInfo);
381 	int msiCount = sPCIx86Module->get_msi_count(pciInfo.bus,
382 		pciInfo.device, pciInfo.function);
383 	TRACE("interrupts count: %d\n",msiCount);
384 	// FIXME if available, use MSI rather than good old IRQ...
385 
386 	// enable bus master and io
387 	uint16 pcicmd = pci->read_pci_config(device, PCI_command, 2);
388 	pcicmd &= ~(PCI_command_int_disable | PCI_command_io);
389 	pcicmd |= PCI_command_master | PCI_command_memory;
390 	pci->write_pci_config(device, PCI_command, 2, pcicmd);
391 
392 	// map the slot registers
393 	area_id	regs_area;
394 	struct registers* _regs;
395 	regs_area = map_physical_memory("sdhc_regs_map",
396 		pciInfo.u.h0.base_registers[bar],
397 		pciInfo.u.h0.base_register_sizes[bar], B_ANY_KERNEL_BLOCK_ADDRESS,
398 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&_regs);
399 
400 	if (regs_area < B_OK) {
401 		ERROR("Could not map registers\n");
402 		return B_BAD_VALUE;
403 	}
404 
405 	// the interrupt is shared between all busses in an SDHC controller, but
406 	// they each register an handler. Not a problem, we will just test the
407 	// interrupt registers for all busses one after the other and find no
408 	// interrupts on the idle busses.
409 	uint8_t irq = pciInfo.u.h0.interrupt_line;
410 	TRACE("irq interrupt line: %d\n", irq);
411 
412 	SdhciBus* bus = new(std::nothrow) SdhciBus(_regs, irq);
413 
414 	status_t status = B_NO_MEMORY;
415 	if (bus != NULL)
416 		status = bus->InitCheck();
417 
418 	if (status != B_OK) {
419 		if (sPCIx86Module != NULL) {
420 			put_module(B_PCI_X86_MODULE_NAME);
421 			sPCIx86Module = NULL;
422 		}
423 
424 		if (bus != NULL)
425 			delete bus;
426 		else
427 			delete_area(regs_area);
428 		return status;
429 	}
430 
431 	// Store the created object as a cookie, allowing users of the bus to
432 	// locate it.
433 	*bus_cookie = bus;
434 
435 	return status;
436 }
437 
438 
439 static void
440 uninit_bus(void* bus_cookie)
441 {
442 	SdhciBus* bus = (SdhciBus*)bus_cookie;
443 	delete bus;
444 
445 	// FIXME do we need to put() the PCI module here?
446 }
447 
448 
449 void
450 SdhciBus::RecoverError()
451 {
452 	fRegisters->interrupt_signal_enable &= ~(SDHCI_INT_CMD_CMP
453 		| SDHCI_INT_TRANS_CMP | SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM);
454 
455 	if (fRegisters->interrupt_status & 7)
456 		fRegisters->software_reset.ResetCommandLine();
457 
458 	int16_t error_status = fRegisters->interrupt_status;
459 	fRegisters->interrupt_status &= ~(error_status);
460 }
461 
462 
463 int32
464 SdhciBus::HandleInterrupt()
465 {
466 	uint32_t intmask = fRegisters->slot_interrupt_status;
467 
468 	if ((intmask == 0) || (intmask == 0xffffffff)) {
469 		return B_UNHANDLED_INTERRUPT;
470 	}
471 
472 	TRACE("interrupt function called\n");
473 
474 	// FIXME use the global "slot interrupt" register to quickly decide if an
475 	// interrupt is targetted to this slot
476 
477 	// handling card presence interrupt
478 	if (intmask & (SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM)) {
479 		uint32_t card_present = ((intmask & SDHCI_INT_CARD_INS) != 0);
480 		fRegisters->interrupt_status_enable &= ~(SDHCI_INT_CARD_INS
481 			| SDHCI_INT_CARD_REM);
482 		fRegisters->interrupt_signal_enable &= ~(SDHCI_INT_CARD_INS
483 			| SDHCI_INT_CARD_REM);
484 
485 		fRegisters->interrupt_status_enable |= card_present
486 		 	? SDHCI_INT_CARD_REM : SDHCI_INT_CARD_INS;
487 		fRegisters->interrupt_signal_enable |= card_present
488 			? SDHCI_INT_CARD_REM : SDHCI_INT_CARD_INS;
489 
490 		fRegisters->interrupt_status |= (intmask &
491 			(SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM));
492 		TRACE("Card presence interrupt handled\n");
493 
494 		return B_HANDLED_INTERRUPT;
495 	}
496 
497 	// handling command interrupt
498 	if (intmask & SDHCI_INT_CMD_MASK) {
499 		fRegisters->interrupt_status |= (intmask & SDHCI_INT_CMD_MASK);
500 		// Notify the thread
501 		release_sem_etc(fSemaphore, 1, B_DO_NOT_RESCHEDULE);
502 		TRACE("Command interrupt handled\n");
503 
504 		return B_HANDLED_INTERRUPT;
505 	}
506 
507 	// handling bus power interrupt
508 	if (intmask & SDHCI_INT_BUS_POWER) {
509 		fRegisters->interrupt_status |= SDHCI_INT_BUS_POWER;
510 		TRACE("card is consuming too much power\n");
511 
512 		return B_HANDLED_INTERRUPT;
513 	}
514 
515 	intmask = fRegisters->slot_interrupt_status;
516 	if (intmask != 0) {
517 		ERROR("Remaining interrupts at end of handler: %x\n", intmask);
518 	}
519 
520 	return B_UNHANDLED_INTERRUPT;
521 }
522 
523 
524 static void
525 bus_removed(void* bus_cookie)
526 {
527 	return;
528 }
529 
530 
531 static status_t
532 register_child_devices(void* cookie)
533 {
534 	CALLED();
535 	device_node* node = (device_node*)cookie;
536 	device_node* parent = gDeviceManager->get_parent_node(node);
537 	pci_device_module_info* pci;
538 	pci_device* device;
539 	uint8 slots_count, bar, slotsInfo;
540 
541 	gDeviceManager->get_driver(parent, (driver_module_info**)&pci,
542 		(void**)&device);
543 	slotsInfo = pci->read_pci_config(device, SDHCI_PCI_SLOT_INFO, 1);
544 	bar = SDHCI_PCI_SLOT_INFO_FIRST_BASE_INDEX(slotsInfo);
545 	slots_count = SDHCI_PCI_SLOTS(slotsInfo);
546 
547 	char prettyName[25];
548 
549 	if (slots_count > 6 || bar > 5) {
550 		ERROR("Invalid slots count: %d or BAR count: %d \n", slots_count, bar);
551 		return B_BAD_VALUE;
552 	}
553 
554 	for (uint8_t slot = 0; slot <= slots_count; slot++) {
555 
556 		bar = bar + slot;
557 		sprintf(prettyName, "SDHC bus %" B_PRIu8, slot);
558 		device_attr attrs[] = {
559 			// properties of this controller for SDHCI bus manager
560 			{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: prettyName } },
561 			{ B_DEVICE_FIXED_CHILD, B_STRING_TYPE,
562 				{string: MMC_BUS_MODULE_NAME} },
563 			{ B_DEVICE_BUS, B_STRING_TYPE, {string: "mmc"} },
564 			{ SLOT_NUMBER, B_UINT8_TYPE, { ui8: slot} },
565 			{ BAR_INDEX, B_UINT8_TYPE, { ui8: bar} },
566 			{ NULL }
567 		};
568 		if (gDeviceManager->register_node(node, SDHCI_PCI_MMC_BUS_MODULE_NAME,
569 				attrs, NULL, &node) != B_OK)
570 			return B_BAD_VALUE;
571 	}
572 	return B_OK;
573 }
574 
575 
576 static status_t
577 init_device(device_node* node, void** device_cookie)
578 {
579 	CALLED();
580 	*device_cookie = node;
581 	return B_OK;
582 }
583 
584 
585 static status_t
586 register_device(device_node* parent)
587 {
588 	device_attr attrs[] = {
589 		{B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "SD Host Controller"}},
590 		{}
591 	};
592 
593 	return gDeviceManager->register_node(parent, SDHCI_PCI_DEVICE_MODULE_NAME,
594 		attrs, NULL, NULL);
595 }
596 
597 
598 static float
599 supports_device(device_node* parent)
600 {
601 	CALLED();
602 	const char* bus;
603 	uint16 type, subType;
604 	uint8 pciSubDeviceId;
605 
606 	// make sure parent is a PCI SDHCI device node
607 	if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)
608 		!= B_OK || gDeviceManager->get_attr_uint16(parent, B_DEVICE_SUB_TYPE,
609 		&subType, false) < B_OK || gDeviceManager->get_attr_uint16(parent,
610 		B_DEVICE_TYPE, &type, false) < B_OK) {
611 		ERROR("Could not find required attribute device/bus\n");
612 		return -1;
613 	}
614 
615 	if (strcmp(bus, "pci") != 0)
616 		return 0.0f;
617 
618 	if (type == PCI_base_peripheral) {
619 		if (subType != PCI_sd_host)
620 			return 0.0f;
621 
622 		pci_device_module_info* pci;
623 		pci_device* device;
624 		gDeviceManager->get_driver(parent, (driver_module_info**)&pci,
625 			(void**)&device);
626 		pciSubDeviceId = pci->read_pci_config(device, PCI_revision, 1);
627 		TRACE("SDHCI Device found! Subtype: 0x%04x, type: 0x%04x\n",
628 			subType, type);
629 		return 0.8f;
630 	}
631 
632 	return 0.0f;
633 }
634 
635 
636 static status_t
637 set_clock(void* controller, uint32_t kilohertz)
638 {
639 	SdhciBus* bus = (SdhciBus*)controller;
640 	bus->SetClock(kilohertz);
641 	return B_OK;
642 }
643 
644 
645 static status_t
646 execute_command(void* controller, uint8_t command, uint32_t argument,
647 	uint32_t* response)
648 {
649 	SdhciBus* bus = (SdhciBus*)controller;
650 	return bus->ExecuteCommand(command, argument, response);
651 }
652 
653 
654 module_dependency module_dependencies[] = {
655 	{ MMC_BUS_MODULE_NAME, (module_info**)&gMMCBusController},
656 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
657 	{}
658 };
659 
660 
661 // Device node registered for each SD slot. It implements the MMC operations so
662 // the bus manager can use it to communicate with SD cards.
663 static mmc_bus_interface gSDHCIPCIDeviceModule = {
664 	{
665 		{
666 			SDHCI_PCI_MMC_BUS_MODULE_NAME,
667 			0,
668 			NULL
669 		},
670 		NULL,	// supports device
671 		NULL,	// register device
672 		init_bus,
673 		uninit_bus,
674 		NULL,	// register child devices
675 		NULL,	// rescan
676 		bus_removed,
677 	},
678 
679 	set_clock,
680 	execute_command,
681 };
682 
683 
684 // Root device that binds to the PCI bus. It will register an mmc_bus_interface
685 // node for each SD slot in the device.
686 static driver_module_info sSDHCIDevice = {
687 	{
688 		SDHCI_PCI_DEVICE_MODULE_NAME,
689 		0,
690 		NULL
691 	},
692 	supports_device,
693 	register_device,
694 	init_device,
695 	NULL,	// uninit
696 	register_child_devices,
697 	NULL,	// rescan
698 	NULL,	// device removed
699 };
700 
701 
702 module_info* modules[] = {
703 	(module_info* )&sSDHCIDevice,
704 	(module_info* )&gSDHCIPCIDeviceModule,
705 	NULL
706 };
707