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