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 <[email protected]>
This commit is contained in:
krish_iyer
2018-08-05 20:56:54 +00:00
committed by Alexander von Gluck IV
parent 42cc463169
commit 25b6a6f19b
14 changed files with 1118 additions and 3 deletions
+1
View File
@@ -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 ;
@@ -0,0 +1,8 @@
SubDir HAIKU_TOP src add-ons kernel bus_managers mmc ;
UsePrivateKernelHeaders ;
KernelAddon mmc_bus :
mmc_module.cpp
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, [email protected]
*/
#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;
}
@@ -0,0 +1,53 @@
/*
* Copyright 2018 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* B Krishnan Iyer, [email protected]
*/
#ifndef MMC_BUS_H
#define MMC_BUS_H
#include <new>
#include <stdio.h>
#include <string.h>
#include <lock.h>
#include <util/AutoLock.h>
#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*/
@@ -0,0 +1,137 @@
/*
* Copyright 2018 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* B Krishnan Iyer, [email protected]
*/
#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
};
+1
View File
@@ -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 ;
+7
View File
@@ -0,0 +1,7 @@
SubDir HAIKU_TOP src add-ons kernel busses mmc ;
SubDirC++Flags -fno-rtti ;
KernelAddon sdhci_pci :
sdhci_pci.cpp
;
+534
View File
@@ -0,0 +1,534 @@
/*
* Copyright 2018 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* B Krishnan Iyer, [email protected]
*/
#include <new>
#include <stdio.h>
#include <string.h>
#include <bus/PCI.h>
#include <PCI_x86.h>
#include <KernelExport.h>
#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**)&regs);
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
};
+129
View File
@@ -0,0 +1,129 @@
/*
* Copyright 2018 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* B Krishnan Iyer, [email protected]
*/
#ifndef _SDHCI_PCI_H
#define _SDHCI_PCI_H
#include <device_manager.h>
#include <KernelExport.h>
#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*/
+2 -1
View File
@@ -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 ;
@@ -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
;
@@ -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 <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "mmc_disk.h"
#include <drivers/device_manager.h>
#include <drivers/KernelExport.h>
#include <drivers/Drivers.h>
#include <kernel/OS.h>
// #include <fs/devfs.h>
#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
};
@@ -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 <device_manager.h>
#include <KernelExport.h>
#define SDHCI_DEVICE_TYPE_ITEM "sdhci/type"
typedef struct {
device_node* node;
status_t media_status;
} mmc_disk_driver_info;
#endif /*_MMC_DISK_H*/
@@ -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);