From 25b6a6f19b13680a759cffecebf60d0b7e76d3d3 Mon Sep 17 00:00:00 2001 From: krish_iyer Date: Sat, 28 Apr 2018 19:24:09 +0530 Subject: [PATCH] SDHCI MMC Driver 1. SDHCI PCI Bus: Discovering SDHC device from the PCI bus and registers a child node(MMC bus) to which slots are attached. SDHC registers are mapped by MMUIO, they are binded in a structure(struct* registers). A pin based interrupt handler is also installed, which triggers the handler function and interrupts are being taken care of. Added API's to set-up and the clock for SD/MMC card, change frequency and reset the registers. 2. Device Manager: Currently, busses subdir lists are har- dcoded and in order to load the driver. We hard coded the bus dir under PCI devices. 3. MMC Disk Driver: In order to register the slots under /dev/disk/mmc and hence data transfer and other operations can be done. 4. MMC Bus Manager: Setted up a bus manager to create an object to do a particular for eg certain data transfer and get freed until another operation is requested. Change-Id: I369354da6b79adc6b6dfb08fe160334af1392a34 Reviewed-on: https://review.haiku-os.org/318 Reviewed-by: Alexander von Gluck IV --- src/add-ons/kernel/bus_managers/Jamfile | 1 + src/add-ons/kernel/bus_managers/mmc/Jamfile | 8 + .../kernel/bus_managers/mmc/mmc_bus.cpp | 43 ++ src/add-ons/kernel/bus_managers/mmc/mmc_bus.h | 53 ++ .../kernel/bus_managers/mmc/mmc_module.cpp | 137 +++++ src/add-ons/kernel/busses/Jamfile | 1 + src/add-ons/kernel/busses/mmc/Jamfile | 7 + src/add-ons/kernel/busses/mmc/sdhci_pci.cpp | 534 ++++++++++++++++++ src/add-ons/kernel/busses/mmc/sdhci_pci.h | 129 +++++ src/add-ons/kernel/drivers/disk/Jamfile | 3 +- src/add-ons/kernel/drivers/disk/mmc/Jamfile | 8 + .../kernel/drivers/disk/mmc/mmc_disk.cpp | 157 +++++ .../kernel/drivers/disk/mmc/mmc_disk.h | 24 + .../kernel/device_manager/device_manager.cpp | 16 +- 14 files changed, 1118 insertions(+), 3 deletions(-) create mode 100644 src/add-ons/kernel/bus_managers/mmc/Jamfile create mode 100644 src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp create mode 100644 src/add-ons/kernel/bus_managers/mmc/mmc_bus.h create mode 100644 src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp create mode 100644 src/add-ons/kernel/busses/mmc/Jamfile create mode 100644 src/add-ons/kernel/busses/mmc/sdhci_pci.cpp create mode 100644 src/add-ons/kernel/busses/mmc/sdhci_pci.h create mode 100644 src/add-ons/kernel/drivers/disk/mmc/Jamfile create mode 100644 src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp create mode 100644 src/add-ons/kernel/drivers/disk/mmc/mmc_disk.h diff --git a/src/add-ons/kernel/bus_managers/Jamfile b/src/add-ons/kernel/bus_managers/Jamfile index e997c36156..386b887d09 100644 --- a/src/add-ons/kernel/bus_managers/Jamfile +++ b/src/add-ons/kernel/bus_managers/Jamfile @@ -7,6 +7,7 @@ SubInclude HAIKU_TOP src add-ons kernel bus_managers fdt ; SubInclude HAIKU_TOP src add-ons kernel bus_managers firewire ; SubInclude HAIKU_TOP src add-ons kernel bus_managers ata ; SubInclude HAIKU_TOP src add-ons kernel bus_managers isa ; +SubInclude HAIKU_TOP src add-ons kernel bus_managers mmc ; SubInclude HAIKU_TOP src add-ons kernel bus_managers pci ; SubInclude HAIKU_TOP src add-ons kernel bus_managers ps2 ; SubInclude HAIKU_TOP src add-ons kernel bus_managers random ; diff --git a/src/add-ons/kernel/bus_managers/mmc/Jamfile b/src/add-ons/kernel/bus_managers/mmc/Jamfile new file mode 100644 index 0000000000..fbf3722826 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/mmc/Jamfile @@ -0,0 +1,8 @@ +SubDir HAIKU_TOP src add-ons kernel bus_managers mmc ; + +UsePrivateKernelHeaders ; + +KernelAddon mmc_bus : + mmc_module.cpp + mmc_bus.cpp + ; diff --git a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp new file mode 100644 index 0000000000..ffdc09cdb8 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp @@ -0,0 +1,43 @@ +/* + * Copyright 2018 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * B Krishnan Iyer, krishnaniyer97@gmail.com + */ +#include "mmc_bus.h" + + +MMCBus::MMCBus(device_node* node) + : + fNode(node), + fController(NULL), + fCookie(NULL), + fStatus(B_OK), + fDriverCookie(NULL) +{ + CALLED(); + device_node* parent = gDeviceManager->get_parent_node(node); + fStatus = gDeviceManager->get_driver(parent, + (driver_module_info**)&fController, &fCookie); + gDeviceManager->put_node(parent); + + if (fStatus != B_OK) { + ERROR("Not able to establish the bus %s\n", + strerror(fStatus)); + return; + } +} + + +MMCBus::~MMCBus() +{ + CALLED(); +} + + +status_t +MMCBus::InitCheck() +{ + return fStatus; +} diff --git a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h new file mode 100644 index 0000000000..efdc97efec --- /dev/null +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h @@ -0,0 +1,53 @@ +/* + * Copyright 2018 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * B Krishnan Iyer, krishnaniyer97@gmail.com + */ +#ifndef MMC_BUS_H +#define MMC_BUS_H + + +#include +#include +#include + +#include +#include +#include "../../busses/mmc/sdhci_pci.h" + + +#define MMCBUS_TRACE +#ifdef MMCBUS_TRACE +# define TRACE(x...) dprintf("\33[33mmmc_bus:\33[0m " x) +#else +# define TRACE(x...) +#endif +#define TRACE_ALWAYS(x...) dprintf("\33[33mmmc_bus:\33[0m " x) +#define ERROR(x...) dprintf("\33[33mmmc_bus:\33[0m " x) +#define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__) + +extern device_manager_info *gDeviceManager; + + +class MMCBus; + +class MMCBus { +public: + + MMCBus(device_node *node); + ~MMCBus(); + status_t InitCheck(); + +private: + + device_node* fNode; + sdhci_mmc_bus_interface* fController; + void* fCookie; + status_t fStatus; + void* fDriverCookie; +}; + + +#endif /*MMC_BUS_H*/ \ No newline at end of file diff --git a/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp b/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp new file mode 100644 index 0000000000..7ada05ce64 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp @@ -0,0 +1,137 @@ +/* + * Copyright 2018 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * B Krishnan Iyer, krishnaniyer97@gmail.com + */ +#include "mmc_bus.h" + + +device_manager_info* gDeviceManager = NULL; + + +static status_t +mmc_bus_init(device_node* node, void** _device) +{ + CALLED(); + MMCBus* device = new(std::nothrow) MMCBus(node); + if (device == NULL) { + ERROR("Unable to allocate MMC bus\n"); + return B_NO_MEMORY; + } + + status_t result = device->InitCheck(); + if (result != B_OK) { + TRACE("failed to set up mmc bus device object\n"); + return result; + } + TRACE("MMC bus object created\n"); + + *_device = device; + return B_OK; +} + + +static void +mmc_bus_uninit(void* _device) +{ + CALLED(); + MMCBus* device = (MMCBus*)_device; + delete device; +} + + +static void +mmc_bus_removed(void* _device) +{ + CALLED(); +} + + +status_t +mmc_bus_added_device(device_node* parent) +{ + CALLED(); + uint16 deviceType; + if (gDeviceManager->get_attr_uint16(parent, + SDHCI_DEVICE_TYPE_ITEM, &deviceType, true) != B_OK) { + TRACE("device is missing\n"); + return B_ERROR; + } + + TRACE("MMC bus device added\n"); + + device_attr attributes[] = { + { B_DEVICE_BUS, B_STRING_TYPE, { string: "mmc bus"}}, + { SDHCI_DEVICE_TYPE_ITEM, B_UINT16_TYPE, + { ui16: deviceType }}, + { NULL } + }; + + return gDeviceManager->register_node(parent, MMC_BUS_MODULE_NAME, + attributes, NULL, NULL); +} + + +static status_t +std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + // Nothing to do + case B_MODULE_UNINIT: + return B_OK; + + default: + break; + } + + return B_ERROR; +} + + +driver_module_info mmc_bus_device_module = { + { + MMC_BUS_MODULE_NAME, + 0, + std_ops + }, + NULL, // supported devices + NULL, // register node + mmc_bus_init, + mmc_bus_uninit, + NULL, // register child devices + NULL, // rescan + mmc_bus_removed, + NULL, // suspend + NULL // resume +}; + + +driver_module_info mmc_bus_controller_module = { + { + SDHCI_BUS_CONTROLLER_MODULE_NAME, + 0, + &std_ops + }, + + NULL, // supported devices + mmc_bus_added_device, + NULL, + NULL, + NULL +}; + + +module_dependency module_dependencies[] = { + { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&gDeviceManager }, + {} +}; + + +module_info* modules[] = { + (module_info*)&mmc_bus_controller_module, + (module_info*)&mmc_bus_device_module, + NULL +}; diff --git a/src/add-ons/kernel/busses/Jamfile b/src/add-ons/kernel/busses/Jamfile index 402cbec5f8..7b549f0e04 100644 --- a/src/add-ons/kernel/busses/Jamfile +++ b/src/add-ons/kernel/busses/Jamfile @@ -2,6 +2,7 @@ SubDir HAIKU_TOP src add-ons kernel busses ; SubInclude HAIKU_TOP src add-ons kernel busses ata ; SubInclude HAIKU_TOP src add-ons kernel busses agp_gart ; +SubInclude HAIKU_TOP src add-ons kernel busses mmc ; SubInclude HAIKU_TOP src add-ons kernel busses random ; SubInclude HAIKU_TOP src add-ons kernel busses scsi ; SubInclude HAIKU_TOP src add-ons kernel busses usb ; diff --git a/src/add-ons/kernel/busses/mmc/Jamfile b/src/add-ons/kernel/busses/mmc/Jamfile new file mode 100644 index 0000000000..ef5298fb2f --- /dev/null +++ b/src/add-ons/kernel/busses/mmc/Jamfile @@ -0,0 +1,7 @@ +SubDir HAIKU_TOP src add-ons kernel busses mmc ; + +SubDirC++Flags -fno-rtti ; + +KernelAddon sdhci_pci : + sdhci_pci.cpp +; diff --git a/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp b/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp new file mode 100644 index 0000000000..35f31d3557 --- /dev/null +++ b/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp @@ -0,0 +1,534 @@ +/* + * Copyright 2018 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * B Krishnan Iyer, krishnaniyer97@gmail.com + */ +#include +#include +#include + +#include +#include + +#include + +#include "sdhci_pci.h" + + +#define TRACE_SDHCI +#ifdef TRACE_SDHCI +# define TRACE(x...) dprintf("\33[33msdhci_pci:\33[0m " x) +#else +# define TRACE(x...) ; +#endif +#define TRACE_ALWAYS(x...) dprintf("\33[33msdhci_pci:\33[0m " x) +#define ERROR(x...) dprintf("\33[33msdhci_pci:\33[0m " x) +#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__) + + +#define SDHCI_PCI_DEVICE_MODULE_NAME "busses/mmc/sdhci_pci/driver_v1" +#define SDHCI_PCI_MMC_BUS_MODULE_NAME "busses/mmc/sdhci_pci/device/v1" + +#define SDHCI_PCI_CONTROLLER_TYPE_NAME "sdhci pci controller" + +#define SLOTS_COUNT "device/slots_count" +#define SLOT_NUMBER "device/slot" +#define BAR_INDEX "device/bar" + +typedef struct { + pci_device_module_info* pci; + pci_device* device; + addr_t base_addr; + uint8 irq; + sdhci_mmc_bus mmc_bus; + area_id regs_area; + device_node* node; + pci_info info; + struct registers* _regs; + +} sdhci_pci_mmc_bus_info; + + +device_manager_info* gDeviceManager; +device_module_info* gSDHCIDeviceController; +static pci_x86_module_info* sPCIx86Module; + + +static void +sdhci_register_dump(uint8_t slot, struct registers* regs) +{ + TRACE("Register values for slot: %d\n", slot); + TRACE("system_address: %d\n", regs->system_address); + TRACE("block_size: %d\n", regs->block_size); + TRACE("block_count: %d\n", regs->block_count); + TRACE("argument: %d\n", regs->argument); + TRACE("transfer_mode: %d\n", regs->transfer_mode); + TRACE("command: %d\n", regs->command); + TRACE("response0: %d\n", regs->response0); + TRACE("response2: %d\n", regs->response2); + TRACE("response4: %d\n", regs->response4); + TRACE("response6: %d\n", regs->response6); + TRACE("buffer_data_port: %d\n", regs->buffer_data_port); + TRACE("present_state: %d\n", regs->present_state); + TRACE("power_control: %d\n", regs->power_control); + 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("timeout_control: %d\n", regs->timeout_control); + TRACE("interrupt_status: %d\n", regs->interrupt_status); + TRACE("interrupt_status_enable: %d\n", regs->interrupt_status_enable); + TRACE("interrupt_signal_enable: %d\n", regs->interrupt_signal_enable); + TRACE("auto_cmd12_error_status: %d\n", regs->auto_cmd12_error_status); + TRACE("capabilities: %d\n", regs->capabilities); + TRACE("capabilities_rsvd: %d\n", regs->capabilities_rsvd); + TRACE("max_current_capabilities: %d\n", + regs->max_current_capabilities); + TRACE("max_current_capabilities_rsvd: %d\n", + regs->max_current_capabilities_rsvd); + TRACE("slot_interrupt_status: %d\n", regs->slot_interrupt_status); + TRACE("host_control_version %d\n", regs->host_control_version); +} + + +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)) + 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); +} + + +static void +sdhci_set_clock(struct registers* regs, uint16_t base_clock_div) +{ + uint32_t clock_control = regs->clock_control; + int base_clock = SDHCI_BASE_CLOCK_FREQ(regs->capabilities); + + TRACE("SDCLK frequency: %dMHz\n", base_clock); + + // clearing previous frequency + clock_control &= SDHCI_CLR_FREQ_SEL; + clock_control |= base_clock_div; + + // enabling internal clock + clock_control |= SDHCI_INTERNAL_CLOCK_ENABLE; + regs->clock_control = clock_control; + + // waiting till internal clock gets stable + while (!(regs->clock_control & SDHCI_INTERNAL_CLOCK_STABLE)); + + regs->clock_control |= SDHCI_SD_CLOCK_ENABLE; // enabling the SD clock +} + + +static void +sdhci_stop_clock(struct registers* regs) +{ + regs->clock_control &= SDHCI_SD_CLOCK_DISABLE; +} + + +static void +sdhci_set_power(struct registers* _regs) +{ + uint16_t command = _regs->command; + + 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) { + 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; + + DELAY(1000); +} + + +static status_t +init_bus(device_node* node, void** bus_cookie) +{ + CALLED(); + status_t status = B_OK; + area_id regs_area; + volatile uint32_t* regs; + uint8_t bar, slot; + + sdhci_pci_mmc_bus_info* bus = new(std::nothrow) sdhci_pci_mmc_bus_info; + if (bus == NULL) + return B_NO_MEMORY; + + pci_info* pciInfo = &bus->info; + pci_device_module_info* pci; + pci_device* device; + + device_node* parent = gDeviceManager->get_parent_node(node); + device_node* pciParent = gDeviceManager->get_parent_node(parent); + gDeviceManager->get_driver(pciParent, (driver_module_info**)&pci, + (void**)&device); + gDeviceManager->put_node(pciParent); + gDeviceManager->put_node(parent); + + if (get_module(B_PCI_X86_MODULE_NAME, (module_info**)&sPCIx86Module) + != B_OK) { + sPCIx86Module = NULL; + TRACE("PCIx86Module not loaded\n"); + } + + int msiCount = sPCIx86Module->get_msi_count(pciInfo->bus, + pciInfo->device, pciInfo->function); + + TRACE("interrupts count: %d\n",msiCount); + + if (gDeviceManager->get_attr_uint8(node, SLOT_NUMBER, &slot, false) < B_OK + || gDeviceManager->get_attr_uint8(node, BAR_INDEX, &bar, false) < B_OK) + return -1; + + bus->node = node; + bus->pci = pci; + bus->device = device; + + pci->get_pci_info(device, pciInfo); + + // legacy interrupt + bus->base_addr = pciInfo->u.h0.base_registers[bar]; + + // enable bus master and io + uint16 pcicmd = pci->read_pci_config(device, PCI_command, 2); + pcicmd &= ~(PCI_command_int_disable | PCI_command_io); + pcicmd |= PCI_command_master | PCI_command_memory; + pci->write_pci_config(device, PCI_command, 2, pcicmd); + + TRACE("init_bus() %p node %p pci %p device %p\n", bus, node, + bus->pci, bus->device); + + // mapping the registers by MMUIO method + int bar_size = pciInfo->u.h0.base_register_sizes[bar]; + + regs_area = map_physical_memory("sdhc_regs_map", + pciInfo->u.h0.base_registers[bar], + pciInfo->u.h0.base_register_sizes[bar], B_ANY_KERNEL_BLOCK_ADDRESS, + B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)®s); + + if (regs_area < B_OK) { + TRACE("mapping failed"); + return B_BAD_VALUE; + } + + bus->regs_area = regs_area; + struct registers* _regs = (struct registers*)regs; + bus->_regs = _regs; + sdhci_reset(_regs); + bus->irq = pciInfo->u.h0.interrupt_line; + + TRACE("irq interrupt line: %d\n",bus->irq); + + if (bus->irq == 0 || bus->irq == 0xff) { + TRACE("PCI IRQ not assigned\n"); + if (sPCIx86Module != NULL) { + put_module(B_PCI_X86_MODULE_NAME); + sPCIx86Module = NULL; + } + delete bus; + return B_ERROR; + } + + status = install_io_interrupt_handler(bus->irq, + sdhci_generic_interrupt, bus, 0); + + if (status != B_OK) { + TRACE("can't install interrupt handler\n"); + return status; + } + TRACE("interrupt handler installed\n"); + + _regs->interrupt_status_enable = SDHCI_INT_CMD_CMP + | SDHCI_INT_TRANS_CMP | SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM + | SDHCI_INT_TIMEOUT | SDHCI_INT_CRC | SDHCI_INT_INDEX + | SDHCI_INT_BUS_POWER | SDHCI_INT_END_BIT; + _regs->interrupt_signal_enable = SDHCI_INT_CMD_CMP + | SDHCI_INT_TRANS_CMP | SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM + | SDHCI_INT_TIMEOUT | SDHCI_INT_CRC | SDHCI_INT_INDEX + | SDHCI_INT_BUS_POWER | SDHCI_INT_END_BIT; + + sdhci_register_dump(slot, _regs); + sdhci_set_clock(_regs, SDHCI_BASE_CLOCK_DIV_128); + sdhci_set_power(_regs); + sdhci_register_dump(slot, _regs); + + *bus_cookie = bus; + return status; +} + + +void +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); + } + + int16_t erorr_status = _regs->interrupt_status; + _regs->interrupt_status &= ~(erorr_status); +} + + +int32 +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; + + intmask = bus->_regs->slot_interrupt_status; + + if (intmask == 0 || intmask == 0xffffffff) { + TRACE("invalid command interrupt\n"); + + return B_UNHANDLED_INTERRUPT; + } + + // handling card presence interrupt + if (intmask & (SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM)) { + card_present = ((intmask & SDHCI_INT_CARD_INS) != 0); + bus->_regs->interrupt_status_enable &= ~(SDHCI_INT_CARD_INS + | SDHCI_INT_CARD_REM); + bus->_regs->interrupt_signal_enable &= ~(SDHCI_INT_CARD_INS + | SDHCI_INT_CARD_REM); + + bus->_regs->interrupt_status_enable |= card_present + ? SDHCI_INT_CARD_REM : SDHCI_INT_CARD_INS; + bus->_regs->interrupt_signal_enable |= card_present + ? SDHCI_INT_CARD_REM : SDHCI_INT_CARD_INS; + + bus->_regs->interrupt_status |= (intmask & + (SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM)); + TRACE("Card presence interrupt handled\n"); + + return B_HANDLED_INTERRUPT; + } + + // 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); + TRACE("Command interrupt handled\n"); + + return B_HANDLED_INTERRUPT; + } + + // handling bus power interrupt + if (intmask & SDHCI_INT_BUS_POWER) { + bus->_regs->interrupt_status |= SDHCI_INT_BUS_POWER; + TRACE("card is consuming too much power\n"); + + return B_HANDLED_INTERRUPT; + } + + intmask &= ~(SDHCI_INT_BUS_POWER | SDHCI_INT_CARD_INS + |SDHCI_INT_CARD_REM | SDHCI_INT_CMD_MASK); + +} + + +static void +uninit_bus(void* bus_cookie) +{ + sdhci_pci_mmc_bus_info* bus = (sdhci_pci_mmc_bus_info*)bus_cookie; + delete bus; +} + + +static void +bus_removed(void* bus_cookie) +{ + return; +} + + +static status_t +register_child_devices(void* cookie) +{ + CALLED(); + device_node* node = (device_node*)cookie; + device_node* parent = gDeviceManager->get_parent_node(node); + pci_device_module_info* pci; + pci_device* device; + uint8 slots_count, bar, slotsInfo; + + gDeviceManager->get_driver(parent, (driver_module_info**)&pci, + (void**)&device); + uint16 pciSubDeviceId = pci->read_pci_config(device, PCI_subsystem_id, 2); + slotsInfo = pci->read_pci_config(device, SDHCI_PCI_SLOT_INFO, 1); + bar = SDHCI_PCI_SLOT_INFO_FIRST_BASE_INDEX(slotsInfo); + slots_count = SDHCI_PCI_SLOTS(slotsInfo); + + char prettyName[25]; + + if (slots_count > 6 || bar > 5) { + TRACE("Invalid slots count: %d or BAR count: %d \n", slots_count, bar); + return B_BAD_VALUE; + } + + for (uint8_t slot = 0; slot <= slots_count; slot++) { + + bar = bar + slot; + sprintf(prettyName, "SDHC bus %" B_PRIu16 " slot %" + B_PRIu8, pciSubDeviceId, slot); + device_attr attrs[] = { + // properties of this controller for SDHCI bus manager + { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, + { string: prettyName }}, + { B_DEVICE_FIXED_CHILD, B_STRING_TYPE, + {string: SDHCI_BUS_CONTROLLER_MODULE_NAME}}, + {SDHCI_DEVICE_TYPE_ITEM, B_UINT16_TYPE, + { ui16: pciSubDeviceId}}, + {B_DEVICE_BUS, B_STRING_TYPE,{string: "mmc"}}, + {SLOT_NUMBER, B_UINT8_TYPE, + { ui8: slot}}, + {BAR_INDEX, B_UINT8_TYPE, + { ui8: bar}}, + { NULL } + }; + if (gDeviceManager->register_node(node, SDHCI_PCI_MMC_BUS_MODULE_NAME, + attrs, NULL, &node) != B_OK) + return B_BAD_VALUE; + } + return B_OK; +} + + +static status_t +init_device(device_node* node, void** device_cookie) +{ + CALLED(); + *device_cookie = node; + return B_OK; +} + + +static status_t +register_device(device_node* parent) +{ + device_attr attrs[] = { + {B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "SDHC PCI controller"}}, + {} + }; + + return gDeviceManager->register_node(parent, SDHCI_PCI_DEVICE_MODULE_NAME, + attrs, NULL, NULL); +} + + +static float +supports_device(device_node* parent) +{ + CALLED(); + const char* bus; + uint16 type, subType; + uint8 pciSubDeviceId; + + // make sure parent is a PCI SDHCI device node + if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false) + != B_OK || gDeviceManager->get_attr_uint16(parent, B_DEVICE_SUB_TYPE, + &subType, false) < B_OK || gDeviceManager->get_attr_uint16(parent, + B_DEVICE_TYPE, &type, false) < B_OK) + return -1; + + if (strcmp(bus, "pci") != 0) + return 0.0f; + + if (type == PCI_base_peripheral) { + if (subType != PCI_sd_host) + return 0.0f; + + pci_device_module_info* pci; + pci_device* device; + gDeviceManager->get_driver(parent, (driver_module_info**)&pci, + (void**)&device); + pciSubDeviceId = pci->read_pci_config(device, PCI_revision, 1); + TRACE("SDHCI Device found! Subtype: 0x%04x, type: 0x%04x\n", + subType, type); + return 0.8f; + } + + return 0.0f; +} + + +module_dependency module_dependencies[] = { + { SDHCI_BUS_CONTROLLER_MODULE_NAME, (module_info**)&gSDHCIDeviceController}, + { B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager }, + {} +}; + + +static sdhci_mmc_bus_interface gSDHCIPCIDeviceModule = { + { + { + SDHCI_PCI_MMC_BUS_MODULE_NAME, + 0, + NULL + }, + NULL, // supports device + NULL, // register device + init_bus, + uninit_bus, + NULL, // register child devices + NULL, // rescan + bus_removed, + } +}; + + +static driver_module_info sSDHCIDevice = { + { + SDHCI_PCI_DEVICE_MODULE_NAME, + 0, + NULL + }, + supports_device, + register_device, + init_device, + NULL, // uninit + register_child_devices, + NULL, // rescan + NULL, // device removed +}; + + +module_info* modules[] = { + (module_info* )&sSDHCIDevice, + (module_info* )&gSDHCIPCIDeviceModule, + NULL +}; diff --git a/src/add-ons/kernel/busses/mmc/sdhci_pci.h b/src/add-ons/kernel/busses/mmc/sdhci_pci.h new file mode 100644 index 0000000000..1d493f3906 --- /dev/null +++ b/src/add-ons/kernel/busses/mmc/sdhci_pci.h @@ -0,0 +1,129 @@ +/* + * Copyright 2018 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * B Krishnan Iyer, krishnaniyer97@gmail.com + */ +#ifndef _SDHCI_PCI_H +#define _SDHCI_PCI_H + + +#include +#include + + +#define SDHCI_PCI_SLOT_INFO 0x40 +#define SDHCI_PCI_SLOTS(x) ((( x >> 4) & 7)) +#define SDHCI_PCI_SLOT_INFO_FIRST_BASE_INDEX(x) (( x ) & 7) + +#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 + +#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 +#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) +/* Interrupt registers */ +#define SDHCI_INT_CMD_CMP 0x00000001 // command complete enable +#define SDHCI_INT_TRANS_CMP 0x00000002 // transfer complete enable +#define SDHCI_INT_CARD_INS 0x00000040 // card insertion enable +#define SDHCI_INT_CARD_REM 0x00000080 // card removal enable +#define SDHCI_INT_TIMEOUT 0x00010000 // Timeout error +#define SDHCI_INT_CRC 0x00020000 // CRC error +#define SDHCI_INT_END_BIT 0x00040000 // end bit error +#define SDHCI_INT_INDEX 0x00080000 // index error +#define SDHCI_INT_BUS_POWER 0x00800000 // power fail + +#define SDHCI_INT_CMD_ERROR_MASK (SDHCI_INT_TIMEOUT | \ + SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX) + +#define SDHCI_INT_CMD_MASK (SDHCI_INT_CMD_CMP | SDHCI_INT_CMD_ERROR_MASK) + + +struct registers { + 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; + volatile uint32_t response0; + volatile uint32_t response2; + volatile uint32_t response4; + volatile uint32_t response6; + volatile uint32_t buffer_data_port; + volatile uint32_t present_state; + volatile uint8_t host_control; + volatile uint8_t power_control; + volatile uint8_t block_gap_control; + volatile uint8_t wakeup_control; + volatile uint16_t clock_control; + volatile uint8_t timeout_control; + volatile uint8_t software_reset; + volatile uint32_t interrupt_status; + volatile uint32_t interrupt_status_enable; + volatile uint32_t interrupt_signal_enable; + volatile uint16_t auto_cmd12_error_status; + volatile uint16_t : 16; + volatile uint32_t capabilities; + volatile uint32_t capabilities_rsvd; + volatile uint32_t max_current_capabilities; + volatile uint32_t max_current_capabilities_rsvd; + volatile uint64_t padding[21]; + volatile uint32_t : 32; + volatile uint16_t slot_interrupt_status; + volatile uint16_t host_control_version; +} __attribute__((packed)); + +typedef void* sdhci_mmc_bus; + +#define DELAY(n) snooze(n) + +#define SDHCI_BUS_CONTROLLER_MODULE_NAME "bus_managers/mmc_bus/driver_v1" + +#define MMC_BUS_MODULE_NAME "bus_managers/mmc_bus/device/v1" + +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_power(struct registers*); +static void sdhci_stop_clock(struct registers*); +void sdhci_error_interrupt_recovery(struct registers*); + +status_t sdhci_generic_interrupt(void*); + +typedef struct { + driver_module_info info; + +} sdhci_mmc_bus_interface; + + +#endif /*_SDHCI_PCI_H*/ diff --git a/src/add-ons/kernel/drivers/disk/Jamfile b/src/add-ons/kernel/drivers/disk/Jamfile index d6ce88a04a..9dfc9029e1 100644 --- a/src/add-ons/kernel/drivers/disk/Jamfile +++ b/src/add-ons/kernel/drivers/disk/Jamfile @@ -2,6 +2,7 @@ SubDir HAIKU_TOP src add-ons kernel drivers disk ; SubInclude HAIKU_TOP src add-ons kernel drivers disk floppy ; SubInclude HAIKU_TOP src add-ons kernel drivers disk norflash ; +SubInclude HAIKU_TOP src add-ons kernel drivers disk mmc ; SubInclude HAIKU_TOP src add-ons kernel drivers disk scsi ; SubInclude HAIKU_TOP src add-ons kernel drivers disk usb ; -SubInclude HAIKU_TOP src add-ons kernel drivers disk virtual ; +SubInclude HAIKU_TOP src add-ons kernel drivers disk virtual ; \ No newline at end of file diff --git a/src/add-ons/kernel/drivers/disk/mmc/Jamfile b/src/add-ons/kernel/drivers/disk/mmc/Jamfile new file mode 100644 index 0000000000..bb603575c9 --- /dev/null +++ b/src/add-ons/kernel/drivers/disk/mmc/Jamfile @@ -0,0 +1,8 @@ +SubDir HAIKU_TOP src add-ons kernel drivers disk mmc ; + +UsePrivateKernelHeaders ; +SubDirHdrs $(HAIKU_TOP) src system kernel device_manager ; + +KernelAddon mmc_disk : + mmc_disk.cpp +; diff --git a/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp b/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp new file mode 100644 index 0000000000..ccccc3550a --- /dev/null +++ b/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp @@ -0,0 +1,157 @@ +/* + * Copyright 2018 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * B Krishnan Iyer, krishnaniyer97@gmail.com + */ +#include +#include +#include +#include +#include "mmc_disk.h" + +#include +#include +#include +#include + +// #include + +#define TRACE_MMC_DISK +#ifdef TRACE_MMC_DISK +# define TRACE(x...) dprintf("mmc_disk: " x) +#else +# define TRACE(x...) ; +#endif +#define ERROR(x...) dprintf("\33[33mmc_disk:\33[0m " x) +#define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__) + +#define MMC_DISK_DRIVER_MODULE_NAME "drivers/disk/mmc/mmc_disk/driver_v1" +#define MMC_DISK_DEVICE_MODULE_NAME "drivers/disk/mmc/mmc_disk/device_v1" +#define MMC_DEVICE_ID_GENERATOR "mmc/device_id" + +static device_manager_info* sDeviceManager; + + +static float +mmc_disk_supports_device(device_node* parent) +{ + CALLED(); + const char* bus; + // uint16 deviceType; + + if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, + true) != B_OK) + return -1; + + if (strcmp(bus, "mmc") != 0) { + TRACE("bus value %s, parent: %p\n",bus, parent); + return 0.0; + } + // Used to check the device type but later we have attached an + // attribute(mmc) to the bus. Not a compulsion to use this condition + #if 0 + if (sDeviceManager->get_attr_uint16(parent, SDHCI_DEVICE_TYPE_ITEM, + &deviceType, true) != B_OK) + { + TRACE(" Inavlid device type, bus found: %s and attr val %d\n", + bus, deviceType); + return 0.0; + } + #endif + TRACE("sdhci device found, parent: %p\n", parent); + + return 0.8; +} + + +static status_t +mmc_disk_register_device(device_node* node) +{ + CALLED(); + + device_attr attrs[] = { + { NULL } + }; + + return sDeviceManager->register_node(node + , MMC_DISK_DRIVER_MODULE_NAME, attrs, NULL, NULL); +} + + +static status_t +mmc_disk_init_driver(device_node* node, void** cookie) +{ + CALLED(); + mmc_disk_driver_info* info = (mmc_disk_driver_info*)malloc( + sizeof(mmc_disk_driver_info)); + + if (info == NULL) + return B_NO_MEMORY; + + memset(info, 0, sizeof(*info)); + + info->node = node; + + *cookie = info; + return B_OK; +} + + +static void +mmc_disk_uninit_driver(void* _cookie) +{ + CALLED(); + mmc_disk_driver_info* info = (mmc_disk_driver_info*)_cookie; + free(info); +} + + +static status_t +mmc_disk_register_child_devices(void* _cookie) +{ + CALLED(); + mmc_disk_driver_info* info = (mmc_disk_driver_info*)_cookie; + status_t status; + + int32 id = sDeviceManager->create_id(MMC_DEVICE_ID_GENERATOR); + if (id < 0) + return id; + + char name[64]; + snprintf(name, sizeof(name), "disk/mmc/%" B_PRId32 "/raw", id); + + status = sDeviceManager->publish_device(info->node, name, + MMC_DISK_DEVICE_MODULE_NAME); + + return status; +} + + +module_dependency module_dependencies[] = { + {B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager}, + {} +}; + + +struct driver_module_info sMMCDiskDriver = { + { + MMC_DISK_DRIVER_MODULE_NAME, + 0, + NULL + }, + mmc_disk_supports_device, + mmc_disk_register_device, + mmc_disk_init_driver, + mmc_disk_uninit_driver, + mmc_disk_register_child_devices, + NULL, // mmc_disk_rescan_child_devices, + NULL, +}; + + +module_info* modules[] = { + (module_info*)&sMMCDiskDriver, + NULL +}; diff --git a/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.h b/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.h new file mode 100644 index 0000000000..990e104aff --- /dev/null +++ b/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.h @@ -0,0 +1,24 @@ +/* + * Copyright 2018 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * B Krishnan Iyer, krishnaniyer97@gmail.com + */ +#ifndef _MMC_DISK_H +#define _MMC_DISK_H + + +#include +#include + + +#define SDHCI_DEVICE_TYPE_ITEM "sdhci/type" + +typedef struct { + device_node* node; + status_t media_status; +} mmc_disk_driver_info; + + +#endif /*_MMC_DISK_H*/ \ No newline at end of file diff --git a/src/system/kernel/device_manager/device_manager.cpp b/src/system/kernel/device_manager/device_manager.cpp index f42a03094d..233f9b6fd4 100644 --- a/src/system/kernel/device_manager/device_manager.cpp +++ b/src/system/kernel/device_manager/device_manager.cpp @@ -42,7 +42,6 @@ # define TRACE(a) ; #endif - #define DEVICE_MANAGER_ROOT_NAME "system/devices_root/driver_v1" #define DEVICE_MANAGER_GENERIC_NAME "system/devices_generic/driver_v1" @@ -1538,7 +1537,9 @@ device_node::_GetNextDriverPath(void*& cookie, KPath& _path) if (get_attr_uint16(this, B_DEVICE_TYPE, &type, false) != B_OK || get_attr_uint16(this, B_DEVICE_SUB_TYPE, &subType, false) != B_OK) + { generic = true; + } get_attr_uint16(this, B_DEVICE_INTERFACE, &interface, false); @@ -1599,6 +1600,16 @@ device_node::_GetNextDriverPath(void*& cookie, KPath& _path) break; } break; + case PCI_base_peripheral: + switch (subType) { + case PCI_sd_host: + _AddPath(*stack, "busses", "mmc"); + break; + default: + _AddPath(*stack, "drivers"); + break; + } + break; default: if (sRootNode == this) { _AddPath(*stack, "busses/pci"); @@ -1885,7 +1896,8 @@ device_node::Probe(const char* devicePath, uint32 updateCycle) // Check if this node matches the device path // TODO: maybe make this extendible via settings file? if (!strcmp(devicePath, "disk")) { - matches = type == PCI_mass_storage; + matches = type == PCI_mass_storage + && (type == PCI_base_peripheral || subType == PCI_sd_host); } else if (!strcmp(devicePath, "audio")) { matches = type == PCI_multimedia && (subType == PCI_audio || subType == PCI_hd_audio);