diff --git a/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp b/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp index ccf1e3a4f6..bec3eb9719 100644 --- a/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp +++ b/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp @@ -59,33 +59,38 @@ static pci_x86_module_info* sPCIx86Module; static void sdhci_register_dump(uint8_t slot, struct registers* regs) { +#ifdef TRACE_SDHCI TRACE("Register values for slot %d:\n", slot); TRACE("system_address: %d\n", regs->system_address); TRACE("%d blocks of size %d\n", regs->block_count, regs->block_size); TRACE("argument: %d\n", regs->argument); TRACE("transfer_mode: %d\n", regs->transfer_mode); - TRACE("command: %d\n", regs->command); + TRACE("command: %d\n", regs->command.Bits()); TRACE("response:"); for (int i = 0; i < 8; i++) - TRACE(" %d", regs->response[i]); - TRACE("\nbuffer_data_port: %d\n", regs->buffer_data_port); - TRACE("present_state: %d\n", regs->present_state); - TRACE("power_control: %d\n", regs->power_control); + dprintf(" %d", regs->response[i]); + dprintf("\n"); + TRACE("buffer_data_port: %d\n", regs->buffer_data_port); + TRACE("present_state: %x\n", regs->present_state.Bits()); + TRACE("power_control: %d\n", regs->power_control.Bits()); TRACE("host_control: %d\n", regs->host_control); TRACE("wakeup_control: %d\n", regs->wakeup_control); TRACE("block_gap_control: %d\n", regs->block_gap_control); - TRACE("clock_control: %d\n", regs->clock_control); - TRACE("software_reset: %d\n", regs->software_reset); + TRACE("clock_control: %x\n", regs->clock_control.Bits()); + TRACE("software_reset: %d\n", regs->software_reset.Bits()); TRACE("timeout_control: %d\n", regs->timeout_control); TRACE("interrupt_status: %x enable: %x signal: %x\n", regs->interrupt_status, regs->interrupt_status_enable, regs->interrupt_signal_enable); TRACE("auto_cmd12_error_status: %d\n", regs->auto_cmd12_error_status); - TRACE("capabilities: %lld\n", regs->capabilities); + TRACE("capabilities: %lld\n", regs->capabilities.Bits()); TRACE("max_current_capabilities: %lld\n", regs->max_current_capabilities); TRACE("slot_interrupt_status: %d\n", regs->slot_interrupt_status); - TRACE("host_controller_version %x\n", regs->host_controller_version); + TRACE("host_controller_version spec %x vendor %x\n", + regs->host_controller_version.specVersion, + regs->host_controller_version.vendorVersion); +#endif } @@ -93,73 +98,85 @@ static void sdhci_reset(struct registers* regs) { // if card is not present then no point of reseting the registers - if (!(regs->present_state & SDHCI_CARD_DETECT)) + if (!regs->present_state.IsCardInserted()) return; // enabling software reset all - regs->software_reset |= SDHCI_SOFTWARE_RESET_ALL; - - // waiting for clock and power to get off - while (regs->clock_control != 0 && regs->power_control != 0); + regs->software_reset.ResetAll(); } static void -sdhci_set_clock(struct registers* regs, uint16_t base_clock_div) +sdhci_set_clock(struct registers* regs) { - uint32_t clock_control = regs->clock_control; - int base_clock = SDHCI_BASE_CLOCK_FREQ(regs->capabilities); + int base_clock = regs->capabilities.BaseClockFrequency(); + // Try to get as close to 400kHz as possible, but not faster + int divider = base_clock * 1000 / 400; - TRACE("SDCLK frequency: %dMHz\n", base_clock); + if (regs->host_controller_version.specVersion <= 1) { + // Old controller only support power of two dividers up to 256, + // round to next power of two up to 256 + if (divider > 256) + divider = 256; - // clearing previous frequency - clock_control &= SDHCI_CLR_FREQ_SEL; - clock_control |= base_clock_div; + divider--; + divider |= divider >> 1; + divider |= divider >> 2; + divider |= divider >> 4; + divider++; + } - // enabling internal clock - clock_control |= SDHCI_INTERNAL_CLOCK_ENABLE; - regs->clock_control = clock_control; + divider = regs->clock_control.SetDivider(divider); - // waiting till internal clock gets stable - while (!(regs->clock_control & SDHCI_INTERNAL_CLOCK_STABLE)); + // Log the value after possible rounding by SetDivider (only even values + // are allowed). + TRACE("SDCLK frequency: %dMHz / %d = %dkHz\n", base_clock, divider, + base_clock * 1000 / divider); - regs->clock_control |= SDHCI_SD_CLOCK_ENABLE; // enabling the SD clock + // We have set the divider, now we can enable the internal clock. + regs->clock_control.EnableInternal(); + + // wait until internal clock is stabilized + while (!(regs->clock_control.InternalStable())); + + regs->clock_control.EnablePLL(); + while (!(regs->clock_control.InternalStable())); + + // Finally, route the clock to the SD card + regs->clock_control.EnableSD(); } static void sdhci_stop_clock(struct registers* regs) { - regs->clock_control &= SDHCI_SD_CLOCK_DISABLE; + regs->clock_control.DisableSD(); } static void sdhci_set_power(struct registers* _regs) { - uint16_t command = _regs->command; + uint8_t supportedVoltages = _regs->capabilities.SupportedVoltages(); + if ((supportedVoltages & Capabilities::k3v3) != 0) + _regs->power_control.SetVoltage(PowerControl::k3v3); + else if ((supportedVoltages & Capabilities::k3v0) != 0) + _regs->power_control.SetVoltage(PowerControl::k3v0); + else if ((supportedVoltages & Capabilities::k1v8) != 0) + _regs->power_control.SetVoltage(PowerControl::k1v8); + else { + _regs->power_control.PowerOff(); + ERROR("No voltage is supported\n"); + return; + } - if (SDHCI_VOLTAGE_SUPPORTED(_regs->capabilities)) - if (SDHCI_VOLTAGE_SUPPORTED_33(_regs->capabilities)) - _regs->power_control |= SDHCI_VOLTAGE_SUPPORT_33; - else if (SDHCI_VOLTAGE_SUPPORTED_30(_regs->capabilities)) - _regs->power_control |= SDHCI_VOLTAGE_SUPPORT_30; - else - _regs->power_control |= SDHCI_VOLTAGE_SUPPORT_18; - else - TRACE("No voltage is supported\n"); - - if (SDHCI_CARD_INSERTED(_regs->present_state) == 0) { + if (!_regs->present_state.IsCardInserted()) { TRACE("Card not inserted\n"); return; } - _regs->power_control |= SDHCI_BUS_POWER_ON; - TRACE("Executed CMD0\n"); - - command = SDHCI_RESPONSE_R1 | SDHCI_CMD_CRC_EN - | SDHCI_CMD_INDEX_EN | SDHCI_CMD_0; - _regs->command |= command; + TRACE("Execute CMD0\n"); + _regs->command.SendCommand(0, false); DELAY(1000); } @@ -204,6 +221,8 @@ init_bus(device_node* node, void** bus_cookie) || gDeviceManager->get_attr_uint8(node, BAR_INDEX, &bar, false) < B_OK) return -1; + TRACE("Controller has %d slots, first bar is %d\n", slot + 1, bar); + bus->node = node; bus->pci = pci; bus->device = device; @@ -272,9 +291,8 @@ init_bus(device_node* node, void** bus_cookie) | SDHCI_INT_BUS_POWER | SDHCI_INT_END_BIT; sdhci_register_dump(slot, _regs); - sdhci_set_clock(_regs, SDHCI_BASE_CLOCK_DIV_128); + sdhci_set_clock(_regs); sdhci_set_power(_regs); - sdhci_register_dump(slot, _regs); *bus_cookie = bus; return status; @@ -287,10 +305,8 @@ sdhci_error_interrupt_recovery(struct registers* _regs) _regs->interrupt_signal_enable &= ~(SDHCI_INT_CMD_CMP | SDHCI_INT_TRANS_CMP | SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM); - if (_regs->interrupt_status & 7) { - _regs->software_reset |= 1 << 1; - while (_regs->command); - } + if (_regs->interrupt_status & 7) + _regs->software_reset.ResetTransaction(); int16_t erorr_status = _regs->interrupt_status; _regs->interrupt_status &= ~(erorr_status); @@ -303,11 +319,11 @@ sdhci_generic_interrupt(void* data) TRACE("interrupt function called\n"); sdhci_pci_mmc_bus_info* bus = (sdhci_pci_mmc_bus_info*)data; - uint16_t intmask, card_present; + uint32_t intmask, card_present; intmask = bus->_regs->slot_interrupt_status; - if (intmask == 0 || intmask == 0xffffffff) { + if ((intmask == 0) || (intmask == 0xffffffff)) { TRACE("invalid command interrupt\n"); return B_UNHANDLED_INTERRUPT; @@ -335,8 +351,8 @@ sdhci_generic_interrupt(void* data) // handling command interrupt if (intmask & SDHCI_INT_CMD_MASK) { - TRACE("interrupt status error: %d\n", bus->_regs->interrupt_status); bus->_regs->interrupt_status |= (intmask & SDHCI_INT_CMD_MASK); + // TODO do something with the interrupt TRACE("Command interrupt handled\n"); return B_HANDLED_INTERRUPT; diff --git a/src/add-ons/kernel/busses/mmc/sdhci_pci.h b/src/add-ons/kernel/busses/mmc/sdhci_pci.h index ee94e99fbc..2c7680c2a6 100644 --- a/src/add-ons/kernel/busses/mmc/sdhci_pci.h +++ b/src/add-ons/kernel/busses/mmc/sdhci_pci.h @@ -20,37 +20,101 @@ #define SDHCI_DEVICE_TYPE_ITEM "sdhci/type" #define SDHCI_BUS_TYPE_NAME "bus/sdhci/v1" -#define SDHCI_CARD_DETECT 1 << 16 -#define SDHCI_SOFTWARE_RESET_ALL 1 << 0 +class Command { + public: + uint16_t Bits() { return fBits; } -#define SDHCI_BASE_CLOCK_FREQ(x) ((x >> 8) & 63) -#define SDHCI_VOLTAGE_SUPPORTED(x) ((x >> 24) & 7) -#define SDHCI_VOLTAGE_SUPPORTED_33(x) ((x >> 24) & 1) -#define SDHCI_VOLTAGE_SUPPORTED_30(x) ((x >> 24) & 3) -#define SDHCI_VOLTAGE_SUPPORT_33 7 << 1 -#define SDHCI_VOLTAGE_SUPPORT_30 6 << 1 -#define SDHCI_VOLTAGE_SUPPORT_18 5 << 1 -#define SDHCI_CARD_INSERTED(x) ((x >> 16) & 1) -#define SDHCI_BASE_CLOCK_DIV_1 0 << 8 -#define SDHCI_BASE_CLOCK_DIV_2 1 << 8 -#define SDHCI_BASE_CLOCK_DIV_4 2 << 8 -#define SDHCI_BASE_CLOCK_DIV_8 4 << 8 -#define SDHCI_BASE_CLOCK_DIV_16 8 << 8 -#define SDHCI_BASE_CLOCK_DIV_32 16 << 8 -#define SDHCI_BASE_CLOCK_DIV_64 32 << 8 -#define SDHCI_BASE_CLOCK_DIV_128 64 << 8 -#define SDHCI_BASE_CLOCK_DIV_256 128 << 8 -#define SDHCI_INTERNAL_CLOCK_ENABLE 1 << 0 -#define SDHCI_INTERNAL_CLOCK_STABLE 1 << 1 -#define SDHCI_SD_CLOCK_ENABLE 1 << 2 -#define SDHCI_SD_CLOCK_DISABLE ~(1 << 2) -#define SDHCI_CLR_FREQ_SEL ~(255 << 8) -#define SDHCI_BUS_POWER_ON 1 + void SendCommand(uint8_t command, bool data) + { + fBits = (command << 8) | (data << 5); + } + + private: + volatile uint16_t fBits; +} __attribute__((packed)); #define SDHCI_RESPONSE_R1 2 #define SDHCI_CMD_CRC_EN 1 << 3 #define SDHCI_CMD_INDEX_EN 1 << 4 -#define SDHCI_CMD_0 ~(63 << 8) + + +class PresentState { + public: + uint32_t Bits() { return fBits; } + + bool IsCardInserted() { return fBits & (1 << 16); } + + private: + volatile uint32_t fBits; +} __attribute__((packed)); + + +class PowerControl { + public: + uint8_t Bits() { return fBits; } + + void SetVoltage(int voltage) { + fBits |= voltage | kBusPowerOn; + } + void PowerOff() { fBits &= ~kBusPowerOn; } + + static const uint8_t k3v3 = 7 << 1; + static const uint8_t k3v0 = 6 << 1; + static const uint8_t k1v8 = 5 << 1; + private: + volatile uint8_t fBits; + + static const uint8_t kBusPowerOn = 1; +} __attribute__((packed)); + + +class ClockControl +{ + public: + uint16_t Bits() { return fBits; } + + uint16_t SetDivider(uint16_t divider) { + if (divider == 1) + divider = 0; + else + divider /= 2; + uint16_t bits = fBits & ~0xffc0; + bits |= divider << 8; + bits |= (divider >> 8) & 0xc0; + fBits = bits; + + return divider == 0 ? 1 : divider * 2; + } + + void EnableInternal() { fBits |= 1 << 0; } + bool InternalStable() { return fBits & (1 << 1); } + void EnableSD() { fBits |= 1 << 2; } + void DisableSD() { fBits &= ~(1 << 2); } + void EnablePLL() { fBits |= 1 << 3; } + private: + volatile uint16_t fBits; +} __attribute__((packed)); + + +class SoftwareReset { + public: + uint8_t Bits() { return fBits; } + + void ResetAll() { + fBits |= 1; + while(fBits & 1); + } + + void ResetTransaction() { + fBits |= 2; + while(fBits & 2); + } + + private: + volatile uint8_t fBits; +} __attribute__((packed)); + + /* Interrupt registers */ #define SDHCI_INT_CMD_CMP 0x00000001 // command complete enable #define SDHCI_INT_TRANS_CMP 0x00000002 // transfer complete enable @@ -68,40 +132,87 @@ #define SDHCI_INT_CMD_MASK (SDHCI_INT_CMD_CMP | SDHCI_INT_CMD_ERROR_MASK) +class Capabilities +{ + public: + uint64_t Bits() { return fBits; } + + uint8_t SupportedVoltages() { return (fBits >> 24) & 7; } + uint8_t BaseClockFrequency() { return (fBits >> 8) & 0xFF; } + + static const uint8_t k3v3 = 1; + static const uint8_t k3v0 = 2; + static const uint8_t k1v8 = 4; + + private: + const uint64_t fBits; +} __attribute__((packed)); + + +class HostControllerVersion { + public: + const uint8_t specVersion; + const uint8_t vendorVersion; +} __attribute__((packed)); + + struct registers { + // SD command generation volatile uint32_t system_address; volatile uint16_t block_size; volatile uint16_t block_count; volatile uint32_t argument; volatile uint16_t transfer_mode; - volatile uint16_t command; + Command command; + + // Response volatile uint16_t response[8]; + + // Buffer Data Port volatile uint32_t buffer_data_port; - volatile uint32_t present_state; + + // Host control 1 + PresentState present_state; volatile uint8_t host_control; - volatile uint8_t power_control; + PowerControl power_control; volatile uint8_t block_gap_control; volatile uint8_t wakeup_control; - volatile uint16_t clock_control; + ClockControl clock_control; volatile uint8_t timeout_control; - volatile uint8_t software_reset; + SoftwareReset software_reset; + + // Interrupt control volatile uint32_t interrupt_status; volatile uint32_t interrupt_status_enable; volatile uint32_t interrupt_signal_enable; volatile uint16_t auto_cmd12_error_status; + + // Host control 2 volatile uint16_t host_control_2; - volatile uint64_t capabilities; + + // Capabilities + Capabilities capabilities; volatile uint64_t max_current_capabilities; + + // Force event volatile uint16_t force_event_acmd_status; volatile uint16_t force_event_error_status; + + // ADMA2 volatile uint8_t adma_error_status; volatile uint8_t padding[3]; volatile uint64_t adma_system_address; + + // Preset values volatile uint64_t preset_value[2]; volatile uint32_t :32; volatile uint16_t uhs2_preset_value; volatile uint16_t :16; + + // ADMA3 volatile uint64_t adma3_id_address; + + // UHS-II volatile uint16_t uhs2_block_size; volatile uint16_t :16; volatile uint32_t uhs2_block_count; @@ -121,6 +232,8 @@ struct registers { volatile uint32_t uhs2_error_interrupt_status_enable; volatile uint32_t uhs2_error_interrupt_signal_enable; volatile uint8_t padding3[16]; + + // Pointers volatile uint16_t uhs2_settings_pointer; volatile uint16_t uhs2_host_capabilities_pointer; volatile uint16_t uhs2_test_pointer; @@ -128,8 +241,10 @@ struct registers { volatile uint16_t vendor_specific_pointer; volatile uint16_t reserved_specific_pointer; volatile uint8_t padding4[16]; + + // Common area volatile uint16_t slot_interrupt_status; - volatile uint16_t host_controller_version; + HostControllerVersion host_controller_version; } __attribute__((packed)); typedef void* sdhci_mmc_bus; @@ -142,7 +257,7 @@ typedef void* sdhci_mmc_bus; static void sdhci_register_dump(uint8_t, struct registers*); static void sdhci_reset(struct registers*); -static void sdhci_set_clock(struct registers*, uint16_t); +static void sdhci_set_clock(struct registers*); static void sdhci_set_power(struct registers*); static void sdhci_stop_clock(struct registers*); void sdhci_error_interrupt_recovery(struct registers*);