diff --git a/src/add-ons/kernel/busses/ata/Jamfile b/src/add-ons/kernel/busses/ata/Jamfile new file mode 100644 index 0000000000..04cda2b93a --- /dev/null +++ b/src/add-ons/kernel/busses/ata/Jamfile @@ -0,0 +1,8 @@ +SubDir HAIKU_TOP src add-ons kernel busses ide ; + +SubInclude HAIKU_TOP src add-ons kernel busses ide generic_ide_pci ; +SubInclude HAIKU_TOP src add-ons kernel busses ide ide_isa ; +SubInclude HAIKU_TOP src add-ons kernel busses ide it8211 ; +SubInclude HAIKU_TOP src add-ons kernel busses ide promise_tx2 ; +SubInclude HAIKU_TOP src add-ons kernel busses ide silicon_image_3112 ; +SubInclude HAIKU_TOP src add-ons kernel busses ide legacy_sata ; diff --git a/src/add-ons/kernel/busses/ata/generic_ide_pci/Jamfile b/src/add-ons/kernel/busses/ata/generic_ide_pci/Jamfile new file mode 100644 index 0000000000..d41131f65a --- /dev/null +++ b/src/add-ons/kernel/busses/ata/generic_ide_pci/Jamfile @@ -0,0 +1,7 @@ +SubDir HAIKU_TOP src add-ons kernel busses ide generic_ide_pci ; + +UsePrivateHeaders drivers kernel ; + +KernelAddon generic_ide_pci : + generic_ide_pci.c + ; diff --git a/src/add-ons/kernel/busses/ata/generic_ide_pci/generic_ide_pci.c b/src/add-ons/kernel/busses/ata/generic_ide_pci/generic_ide_pci.c new file mode 100644 index 0000000000..1118d07173 --- /dev/null +++ b/src/add-ons/kernel/busses/ata/generic_ide_pci/generic_ide_pci.c @@ -0,0 +1,247 @@ +/* + * Copyright 2002/03, Thomas Kurschel. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +/*! Generic PCI bus mastering IDE driver. */ + +#include +#include +#include + +#include + +#define GENERIC_IDE_PCI_CONTROLLER_MODULE_NAME "busses/ide/generic_ide_pci/driver_v1" +#define GENERIC_IDE_PCI_CHANNEL_MODULE_NAME "busses/ide/generic_ide_pci/channel/v1" + +#define IDE_PCI_CONTROLLER_TYPE_NAME "ide pci controller" + +ide_for_controller_interface *ide; +static ide_adapter_interface *ide_adapter; +device_manager_info *pnp; + + +static void +set_channel(void *cookie, ide_channel channel) +{ + ide_adapter->set_channel((ide_adapter_channel_info *)cookie, channel); +} + + +static status_t +write_command_block_regs(void *channel_cookie, ide_task_file *tf, ide_reg_mask mask) +{ + return ide_adapter->write_command_block_regs((ide_adapter_channel_info *)channel_cookie, tf, mask); +} + + +static status_t +read_command_block_regs(void *channel_cookie, ide_task_file *tf, ide_reg_mask mask) +{ + return ide_adapter->read_command_block_regs((ide_adapter_channel_info *)channel_cookie, tf, mask); +} + + +static uint8 +get_altstatus(void *channel_cookie) +{ + return ide_adapter->get_altstatus((ide_adapter_channel_info *)channel_cookie); +} + + +static status_t +write_device_control(void *channel_cookie, uint8 val) +{ + return ide_adapter->write_device_control((ide_adapter_channel_info *)channel_cookie, val); +} + + +static status_t +write_pio(void *channel_cookie, uint16 *data, int count, bool force_16bit) +{ + return ide_adapter->write_pio((ide_adapter_channel_info *)channel_cookie, data, count, force_16bit); +} + + +static status_t +read_pio(void *channel_cookie, uint16 *data, int count, bool force_16bit) +{ + return ide_adapter->read_pio((ide_adapter_channel_info *)channel_cookie, data, count, force_16bit); +} + + +static status_t +prepare_dma(void *channel_cookie, + const physical_entry *sg_list, size_t sg_list_count, + bool to_device) +{ + return ide_adapter->prepare_dma((ide_adapter_channel_info *)channel_cookie, sg_list, sg_list_count, to_device); +} + + +static status_t +start_dma(void *channel_cookie) +{ + return ide_adapter->start_dma((ide_adapter_channel_info *)channel_cookie); +} + + +static status_t +finish_dma(void *channel_cookie) +{ + return ide_adapter->finish_dma((ide_adapter_channel_info *)channel_cookie); +} + + +static status_t +init_channel(device_node *node, void **channel_cookie) +{ + return ide_adapter->init_channel(node, + (ide_adapter_channel_info **)channel_cookie, + sizeof(ide_adapter_channel_info), ide_adapter->inthand); +} + + +static void +uninit_channel(void *channel_cookie) +{ + ide_adapter->uninit_channel((ide_adapter_channel_info *)channel_cookie); +} + + +static void +channel_removed(void *channel_cookie) +{ + ide_adapter->channel_removed((ide_adapter_channel_info *)channel_cookie); +} + + +static status_t +init_controller(device_node *node, ide_adapter_controller_info **cookie) +{ + return ide_adapter->init_controller(node, cookie, + sizeof( ide_adapter_controller_info)); +} + + +static void +uninit_controller(ide_adapter_controller_info *controller) +{ + ide_adapter->uninit_controller(controller); +} + + +static void +controller_removed(ide_adapter_controller_info *controller) +{ + return ide_adapter->controller_removed(controller); +} + + +static status_t +probe_controller(device_node *parent) +{ + return ide_adapter->probe_controller(parent, + GENERIC_IDE_PCI_CONTROLLER_MODULE_NAME, "generic_ide_pci", + "Generic IDE PCI Controller", + GENERIC_IDE_PCI_CHANNEL_MODULE_NAME, + true, + true, // assume that command queuing works + 1, // assume 16 bit alignment is enough + 0xffff, // boundary is on 64k according to spec + 0x10000, // up to 64k per S/G block according to spec + true); // by default, compatibility mode is used +} + + +static float +supports_device(device_node *parent) +{ + const char *bus; + uint16 baseClass, subClass; + + // make sure parent is an PCI IDE mass storage host adapter device node + if (pnp->get_attr_string(parent, B_DEVICE_BUS, &bus, false) != B_OK + || pnp->get_attr_uint16(parent, B_DEVICE_TYPE, &baseClass, false) != B_OK + || pnp->get_attr_uint16(parent, B_DEVICE_SUB_TYPE, &subClass, false) != B_OK) + return -1; + + if (strcmp(bus, "pci") || baseClass != PCI_mass_storage) + return 0.0f; + + if (subClass == PCI_ide) + return 0.3f; + + // vendor 105a: Promise Technology, Inc.; device 4d69: 20269 (Ultra133TX2) + // has subClass set to PCI_mass_storage_other, and there are others as well. + if (subClass == PCI_mass_storage_other) + return 0.3f; + + return 0.0f; +} + + +module_dependency module_dependencies[] = { + { IDE_FOR_CONTROLLER_MODULE_NAME, (module_info **)&ide }, + { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&pnp }, + { IDE_ADAPTER_MODULE_NAME, (module_info **)&ide_adapter }, + {} +}; + + +// exported interface +static ide_controller_interface channel_interface = { + { + { + GENERIC_IDE_PCI_CHANNEL_MODULE_NAME, + 0, + NULL + }, + + NULL, // supports device + NULL, // register device + init_channel, + uninit_channel, + NULL, // register child devices + NULL, // rescan + channel_removed, + }, + + &set_channel, + + &write_command_block_regs, + &read_command_block_regs, + + &get_altstatus, + &write_device_control, + + &write_pio, + &read_pio, + + &prepare_dma, + &start_dma, + &finish_dma, +}; + + +static driver_module_info controller_interface = { + { + GENERIC_IDE_PCI_CONTROLLER_MODULE_NAME, + 0, + NULL + }, + + supports_device, + probe_controller, + (status_t (*)(device_node *, void **)) init_controller, + (void (*)(void *)) uninit_controller, + NULL, // register child devices + NULL, // rescan + (void (*)(void *)) controller_removed, +}; + +module_info *modules[] = { + (module_info *)&controller_interface, + (module_info *)&channel_interface, + NULL +}; diff --git a/src/add-ons/kernel/busses/ata/ide_isa/Jamfile b/src/add-ons/kernel/busses/ata/ide_isa/Jamfile new file mode 100644 index 0000000000..0d95c01cdb --- /dev/null +++ b/src/add-ons/kernel/busses/ata/ide_isa/Jamfile @@ -0,0 +1,12 @@ +SubDir HAIKU_TOP src add-ons kernel busses ide ide_isa ; + +UsePrivateHeaders drivers kernel ; + +# disable debug output, if debugging is disabled +if $(DEBUG) = 0 { + SubDirCcFlags [ FDefines DEBUG_MAX_LEVEL_FLOW=0 DEBUG_MAX_LEVEL_INFO=0 ] ; +} + +KernelAddon ide_isa : + ide_isa.c + ; diff --git a/src/add-ons/kernel/busses/ata/ide_isa/ide_isa.c b/src/add-ons/kernel/busses/ata/ide_isa/ide_isa.c new file mode 100644 index 0000000000..5887a13063 --- /dev/null +++ b/src/add-ons/kernel/busses/ata/ide_isa/ide_isa.c @@ -0,0 +1,461 @@ +/* + * Copyright 2002/03, Thomas Kurschel. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +/* + IDE ISA controller driver + + This is a testing-only driver. In reality, you want to use + the IDE PCI controller driver, but at least under Bochs, there's not + much choice as PCI support is very limited there. +*/ + + +#include +#include +#include + +#include +#include +#include +#include + + +//#define TRACE_IDE_ISA +#ifdef TRACE_IDE_ISA +# define TRACE(x...) dprintf("ide_isa: " x) +#else +# define TRACE(x...) ; +#endif + + +#define IDE_ISA_MODULE_NAME "busses/ide/ide_isa/driver_v1" + +// private node item: +// io address of command block +#define IDE_ISA_COMMAND_BLOCK_BASE "ide_isa/command_block_base" +// io address of control block +#define IDE_ISA_CONTROL_BLOCK_BASE "ide_isa/control_block_base" +// interrupt number +#define IDE_ISA_INTNUM "ide_isa/irq" + + +ide_for_controller_interface *ide; +device_manager_info *pnp; + + +// info about one channel +typedef struct channel_info { + isa2_module_info *isa; + uint16 command_block_base; // io address command block + uint16 control_block_base; // io address control block + int intnum; // interrupt number + + uint32 lost; // != 0 if device got removed, i.e. if it must not + // be accessed anymore + + ide_channel ide_channel; + device_node *node; +} channel_info; + + +/*! publish node of an ide channel */ +static status_t +publish_channel(device_node *parent, uint16 command_block_base, + uint16 control_block_base, uint8 intnum, const char *name) +{ + device_attr attrs[] = { + { B_DEVICE_FIXED_CHILD, B_STRING_TYPE, { string: IDE_FOR_CONTROLLER_MODULE_NAME }}, + + // properties of this controller for ide bus manager + { IDE_CONTROLLER_MAX_DEVICES_ITEM, B_UINT8_TYPE, { ui8: 2 }}, + { IDE_CONTROLLER_CAN_DMA_ITEM, B_UINT8_TYPE, { ui8: 0 }}, + { IDE_CONTROLLER_CAN_CQ_ITEM, B_UINT8_TYPE, { ui8: 1 }}, + { IDE_CONTROLLER_CONTROLLER_NAME_ITEM, B_STRING_TYPE, { string: name }}, + + // DMA properties; the 16 bit alignment is not necessary as + // the ide bus manager handles that very efficiently, but why + // not use the block device manager for doing that? + { B_DMA_ALIGNMENT, B_UINT32_TYPE, { ui32: 1 }}, + + // private data to identify device + { IDE_ISA_COMMAND_BLOCK_BASE, B_UINT16_TYPE, { ui16: command_block_base }}, + { IDE_ISA_CONTROL_BLOCK_BASE, B_UINT16_TYPE, { ui16: control_block_base }}, + { IDE_ISA_INTNUM, B_UINT8_TYPE, { ui8: intnum }}, + { NULL } + }; + io_resource resources[3] = { + { B_IO_PORT, command_block_base, 8 }, + { B_IO_PORT, control_block_base, 1 }, + {} + }; + + TRACE("publishing %s, resources %#x %#x %d\n", + name, command_block_base, control_block_base, intnum); + + return pnp->register_node(parent, IDE_ISA_MODULE_NAME, attrs, resources, + NULL); +} + + +// #pragma mark - + + +static void +set_channel(void *cookie, ide_channel ideChannel) +{ + channel_info *channel = cookie; + channel->ide_channel = ideChannel; +} + + +static status_t +write_command_block_regs(void *channel_cookie, ide_task_file *tf, + ide_reg_mask mask) +{ + channel_info *channel = channel_cookie; + uint16 ioaddr = channel->command_block_base; + int i; + + if (channel->lost) + return B_ERROR; + + for (i = 0; i < 7; i++) { + if (((1 << (i-7)) & mask) != 0) { + TRACE("write_command_block_regs(): %x->HI(%x)\n", + tf->raw.r[i + 7], i); + channel->isa->write_io_8(ioaddr + 1 + i, tf->raw.r[i + 7]); + } + + if (((1 << i) & mask) != 0 ) { + TRACE("write_comamnd_block_regs(): %x->LO(%x)\n", tf->raw.r[i], i); + channel->isa->write_io_8(ioaddr + 1 + i, tf->raw.r[i]); + } + } + + return B_OK; +} + + +static status_t +read_command_block_regs(void *channel_cookie, ide_task_file *tf, + ide_reg_mask mask) +{ + channel_info *channel = channel_cookie; + uint16 ioaddr = channel->command_block_base; + int i; + + if (channel->lost) + return B_ERROR; + + for (i = 0; i < 7; i++) { + if (((1 << i) & mask) != 0) { + tf->raw.r[i] = channel->isa->read_io_8(ioaddr + 1 + i); + TRACE("read_command_block_regs(%x): %x\n", i, (int)tf->raw.r[i]); + } + } + + return B_OK; +} + + +static uint8 +get_altstatus(void *channel_cookie) +{ + channel_info *channel = channel_cookie; + uint16 altstatusaddr = channel->control_block_base; + + if (channel->lost) + return B_ERROR; + + return channel->isa->read_io_8(altstatusaddr); +} + + +static status_t +write_device_control(void *channel_cookie, uint8 val) +{ + channel_info *channel = channel_cookie; + uint16 device_control_addr = channel->control_block_base; + + TRACE("write_device_control(%x)\n", (int)val); + + if (channel->lost) + return B_ERROR; + + channel->isa->write_io_8(device_control_addr, val); + + return B_OK; +} + + +static status_t +write_pio_16(void *channel_cookie, uint16 *data, int count, bool force_16bit) +{ + channel_info *channel = channel_cookie; + uint16 ioaddr = channel->command_block_base; + + if (channel->lost) + return B_ERROR; + + // Bochs doesn't support 32 bit accesses; + // no real performance impact as this driver is for Bochs only anyway + force_16bit = true; + + if ((count & 1) != 0 || force_16bit) { + for (; count > 0; --count) + channel->isa->write_io_16(ioaddr, *(data++)); + } else { + uint32 *cur_data = (uint32 *)data; + + for (; count > 0; count -= 2) + channel->isa->write_io_32(ioaddr, *(cur_data++)); + } + + return B_OK; +} + + +static status_t +read_pio_16(void *channel_cookie, uint16 *data, int count, bool force_16bit) +{ + channel_info *channel = channel_cookie; + uint16 ioaddr = channel->command_block_base; + + if (channel->lost) + return B_ERROR; + + force_16bit = true; + + if ((count & 1) != 0 || force_16bit) { + for (; count > 0; --count) + *(data++) = channel->isa->read_io_16(ioaddr); + } else { + uint32 *cur_data = (uint32 *)data; + + for (; count > 0; count -= 2) + *(cur_data++) = channel->isa->read_io_32(ioaddr); + } + + return B_OK; +} + + +static int32 +inthand(void *arg) +{ + channel_info *channel = (channel_info *)arg; + uint8 status; + + TRACE("interrupt handler()\n"); + + if (channel->lost) + return B_UNHANDLED_INTERRUPT; + + // acknowledge IRQ + status = channel->isa->read_io_8(channel->command_block_base + 7); + + return ide->irq_handler(channel->ide_channel, status); +} + + +static status_t +prepare_dma(void *channel_cookie, const physical_entry *sg_list, + size_t sg_list_count, bool write) +{ + return B_NOT_ALLOWED; +} + + +static status_t +start_dma(void *channel_cookie) +{ + return B_NOT_ALLOWED; +} + + +static status_t +finish_dma(void *channel_cookie) +{ + return B_NOT_ALLOWED; +} + + +// #pragma mark - + + +static float +supports_device(device_node *parent) +{ + const char *bus; + + // make sure parent is really the ISA bus manager + if (pnp->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) + return B_ERROR; + + if (strcmp(bus, "isa")) + return 0.0; + + // we assume that every modern PC has an IDE controller, so no + // further testing is done (well - I don't really know how to detect the + // controller, but who cares ;) + + return 0.6; +} + + +static status_t +init_channel(device_node *node, void **_cookie) +{ + channel_info *channel; + device_node *parent; + isa2_module_info *isa; + uint16 command_block_base, control_block_base; + uint8 irq; + status_t res; + + TRACE("ISA-IDE: channel init\n"); + + // get device data + if (pnp->get_attr_uint16(node, IDE_ISA_COMMAND_BLOCK_BASE, &command_block_base, false) != B_OK + || pnp->get_attr_uint16(node, IDE_ISA_CONTROL_BLOCK_BASE, &control_block_base, false) != B_OK + || pnp->get_attr_uint8(node, IDE_ISA_INTNUM, &irq, false) != B_OK) + return B_ERROR; + + parent = pnp->get_parent_node(node); + pnp->get_driver(parent, (driver_module_info **)&isa, NULL); + pnp->put_node(parent); + + channel = (channel_info *)malloc(sizeof(channel_info)); + if (channel == NULL) + return B_NO_MEMORY; + + TRACE("ISA-IDE: channel init, resources %#x %#x %d\n", + command_block_base, control_block_base, irq); + + channel->isa = isa; + channel->node = node; + channel->lost = false; + channel->command_block_base = command_block_base; + channel->control_block_base = control_block_base; + channel->intnum = irq; + channel->ide_channel = NULL; + + res = install_io_interrupt_handler(channel->intnum, + inthand, channel, 0); + + if (res < 0) { + TRACE("ISA-IDE: couldn't install irq handler for int %d\n", irq); + goto err; + } + + // enable interrupts so the channel is ready to run + write_device_control(channel, ide_devctrl_bit3); + + *_cookie = channel; + return B_OK; + +err: + free(channel); + return res; +} + + +static void +uninit_channel(void *channel_cookie) +{ + channel_info *channel = channel_cookie; + + TRACE("ISA-IDE: channel uninit\n"); + + // disable IRQs + write_device_control(channel, ide_devctrl_bit3 | ide_devctrl_nien); + + // catch spurious interrupt + // (some controllers generate an IRQ when you _disable_ interrupts, + // they are delayed by less then 40 µs, so 1 ms is safe) + snooze(1000); + + remove_io_interrupt_handler(channel->intnum, inthand, channel); + + free(channel); +} + + +static status_t +register_device(device_node *parent) +{ + status_t primaryStatus; + status_t secondaryStatus; + TRACE("register_device()\n"); + + // our parent device is the isa bus and all device drivers are Universal, + // so the pnp_manager tries each ISA driver in turn + primaryStatus = publish_channel(parent, 0x1f0, 0x3f6, 14, + "primary IDE channel"); + secondaryStatus = publish_channel(parent, 0x170, 0x376, 15, + "secondary IDE channel"); + + if (primaryStatus == B_OK || secondaryStatus == B_OK) + return B_OK; + + return primaryStatus; +} + + +static void +channel_removed(void *cookie) +{ + channel_info *channel = cookie; + TRACE("channel_removed()\n"); + + // disable access instantly + atomic_or(&channel->lost, 1); +} + + +module_dependency module_dependencies[] = { + { IDE_FOR_CONTROLLER_MODULE_NAME, (module_info **)&ide }, + { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&pnp }, + {} +}; + +// exported interface +static ide_controller_interface sISAControllerInterface = { + { + { + IDE_ISA_MODULE_NAME, + 0, + NULL + }, + + supports_device, + register_device, + init_channel, + uninit_channel, + NULL, // register child devices + NULL, // rescan + channel_removed, + }, + + &set_channel, + + &write_command_block_regs, + &read_command_block_regs, + + &get_altstatus, + &write_device_control, + + &write_pio_16, + &read_pio_16, + + &prepare_dma, + &start_dma, + &finish_dma, +}; + +module_info *modules[] = { + (module_info *)&sISAControllerInterface, + NULL +}; diff --git a/src/add-ons/kernel/busses/ata/it8211/Jamfile b/src/add-ons/kernel/busses/ata/it8211/Jamfile new file mode 100644 index 0000000000..423a654ed2 --- /dev/null +++ b/src/add-ons/kernel/busses/ata/it8211/Jamfile @@ -0,0 +1,8 @@ +SubDir HAIKU_TOP src add-ons kernel busses ide it8211 ; + +UsePrivateHeaders kernel ; +UsePrivateHeaders drivers ; + +KernelAddon it8211 : + it8211.c + ; diff --git a/src/add-ons/kernel/busses/ata/it8211/it8211.c b/src/add-ons/kernel/busses/ata/it8211/it8211.c new file mode 100644 index 0000000000..f48a75abc0 --- /dev/null +++ b/src/add-ons/kernel/busses/ata/it8211/it8211.c @@ -0,0 +1,242 @@ +/* + * Copyright 2008, Michael Lotz. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#include +#include +#include + +#include + +#define IT8211_CONTROLLER_MODULE_NAME "busses/ide/it8211/driver_v1" +#define IT8211_CHANNEL_MODULE_NAME "busses/ide/it8211/channel/v1" + +#define PCI_VENDOR_ITE 0x1283 +#define PCI_DEVICE_ITE_IT8211 0x8211 + +static ide_adapter_interface *sIDEAdapter; +static device_manager_info *sDeviceManager; + + +static void +it8211_set_channel(void *channelCookie, ide_channel channel) +{ + sIDEAdapter->set_channel((ide_adapter_channel_info *)channelCookie, + channel); +} + + +static status_t +it8211_write_command_block_regs(void *channelCookie, ide_task_file *taskFile, + ide_reg_mask registerMask) +{ + return sIDEAdapter->write_command_block_regs( + (ide_adapter_channel_info *)channelCookie, taskFile, registerMask); +} + + +static status_t +it8211_read_command_block_regs(void *channelCookie, ide_task_file *taskFile, + ide_reg_mask registerMask) +{ + return sIDEAdapter->read_command_block_regs( + (ide_adapter_channel_info *)channelCookie, taskFile, registerMask); +} + + +static uint8 +it8211_get_altstatus(void *channelCookie) +{ + return sIDEAdapter->get_altstatus((ide_adapter_channel_info *)channelCookie); +} + + +static status_t +it8211_write_device_control(void *channelCookie, uint8 value) +{ + return sIDEAdapter->write_device_control( + (ide_adapter_channel_info *)channelCookie, value); +} + + +static status_t +it8211_write_pio(void *channelCookie, uint16 *data, int count, bool force16bit) +{ + return sIDEAdapter->write_pio( + (ide_adapter_channel_info *)channelCookie, data, count, force16bit); +} + + +static status_t +it8211_read_pio(void *channelCookie, uint16 *data, int count, bool force16bit) +{ + return sIDEAdapter->read_pio( + (ide_adapter_channel_info *)channelCookie, data, count, force16bit); +} + + +static status_t +it8211_prepare_dma(void *channelCookie, const physical_entry *sgList, + size_t sgListCount, bool toDevice) +{ + return sIDEAdapter->prepare_dma((ide_adapter_channel_info *)channelCookie, + sgList, sgListCount, toDevice); +} + + +static status_t +it8211_start_dma(void *channelCookie) +{ + return sIDEAdapter->start_dma((ide_adapter_channel_info *)channelCookie); +} + + +static status_t +it8211_finish_dma(void *channelCookie) +{ + return sIDEAdapter->finish_dma((ide_adapter_channel_info *)channelCookie); +} + + +static status_t +init_channel(device_node *node, void **channelCookie) +{ + return sIDEAdapter->init_channel(node, + (ide_adapter_channel_info **)channelCookie, + sizeof(ide_adapter_channel_info), sIDEAdapter->inthand); +} + + +static void +uninit_channel(void *channelCookie) +{ + sIDEAdapter->uninit_channel((ide_adapter_channel_info *)channelCookie); +} + + +static void +channel_removed(void *channelCookie) +{ + sIDEAdapter->channel_removed((ide_adapter_channel_info *)channelCookie); +} + + +static status_t +init_controller(device_node *node, void **cookie) +{ + return sIDEAdapter->init_controller(node, + (ide_adapter_controller_info **)cookie, + sizeof(ide_adapter_controller_info)); +} + + +static void +uninit_controller(void *cookie) +{ + sIDEAdapter->uninit_controller((ide_adapter_controller_info *)cookie); +} + + +static void +controller_removed(void *cookie) +{ + sIDEAdapter->controller_removed((ide_adapter_controller_info *)cookie); +} + + +static status_t +probe_controller(device_node *parent) +{ + return sIDEAdapter->probe_controller(parent, + IT8211_CONTROLLER_MODULE_NAME, "it8211", + "ITE IT8211", IT8211_CHANNEL_MODULE_NAME, + true, /* DMA */ + true, /* command queuing */ + 1, /* 16 bit alignment */ + 0xffff, /* 64k boundary */ + 0x10000, /* up to 64k per scatter/gather block */ + false); /* no compatibility mode */ +} + + +static float +supports_device(device_node *parent) +{ + const char *bus; + uint16 vendorID; + uint16 deviceID; + + if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false) != B_OK + || sDeviceManager->get_attr_uint16(parent, B_DEVICE_VENDOR_ID, &vendorID, false) != B_OK + || sDeviceManager->get_attr_uint16(parent, B_DEVICE_ID, &deviceID, false) != B_OK) { + return -1.0f; + } + + if (strcmp(bus, "pci") != 0 || vendorID != PCI_VENDOR_ITE + || deviceID != PCI_DEVICE_ITE_IT8211) + return 0.0f; + + return 1.0f; +} + + +module_dependency module_dependencies[] = { + { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager }, + { IDE_ADAPTER_MODULE_NAME, (module_info **)&sIDEAdapter }, + {} +}; + + +// exported interface +static ide_controller_interface sChannelInterface = { + { + { + IT8211_CHANNEL_MODULE_NAME, + 0, + NULL + }, + + NULL, // supports device + NULL, // register device + &init_channel, + &uninit_channel, + NULL, // register child devices + NULL, // rescan + &channel_removed + }, + + &it8211_set_channel, + &it8211_write_command_block_regs, + &it8211_read_command_block_regs, + &it8211_get_altstatus, + &it8211_write_device_control, + &it8211_write_pio, + &it8211_read_pio, + &it8211_prepare_dma, + &it8211_start_dma, + &it8211_finish_dma +}; + + +static driver_module_info sControllerInterface = { + { + IT8211_CONTROLLER_MODULE_NAME, + 0, + NULL + }, + + &supports_device, + &probe_controller, + &init_controller, + &uninit_controller, + NULL, // register child devices + NULL, // rescan + &controller_removed +}; + + +module_info *modules[] = { + (module_info *)&sControllerInterface, + (module_info *)&sChannelInterface, + NULL +}; diff --git a/src/add-ons/kernel/busses/ata/legacy_sata/Jamfile b/src/add-ons/kernel/busses/ata/legacy_sata/Jamfile new file mode 100644 index 0000000000..f1d6c09699 --- /dev/null +++ b/src/add-ons/kernel/busses/ata/legacy_sata/Jamfile @@ -0,0 +1,8 @@ +SubDir HAIKU_TOP src add-ons kernel busses ide legacy_sata ; + +UsePrivateHeaders drivers kernel ; + +KernelAddon legacy_sata : + legacy_sata.c + ; + diff --git a/src/add-ons/kernel/busses/ata/legacy_sata/legacy_sata.c b/src/add-ons/kernel/busses/ata/legacy_sata/legacy_sata.c new file mode 100644 index 0000000000..15d37b5650 --- /dev/null +++ b/src/add-ons/kernel/busses/ata/legacy_sata/legacy_sata.c @@ -0,0 +1,403 @@ +/* + * Copyright 2007, Ithamar R. Adema. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#include +#include +#include +#include +#include +#include + + +#define DRIVER_PRETTY_NAME "Legacy SATA" +#define CONTROLLER_NAME DRIVER_PRETTY_NAME +#define CONTROLLER_MODULE_NAME "busses/ide/legacy_sata/driver_v1" +#define CHANNEL_MODULE_NAME "busses/ide/legacy_sata/channel/v1" + +#define TRACE(a...) dprintf(DRIVER_PRETTY_NAME ": " a) +#define FLOW(a...) dprintf(DRIVER_PRETTY_NAME ": " a) + +#define PCI_vendor_VIA 0x1106 +#define PCI_device_VIA6420 0x3149 +#define PCI_device_VIA6421 0x3249 +#define PCI_device_VIA8237A 0x0591 + +#define PCI_vendor_ALI 0x10b9 +#define PCI_device_ALI5289 0x5289 +#define PCI_device_ALI5287 0x5287 +#define PCI_device_ALI5281 0x5281 + +#define PCI_vendor_NVIDIA 0x10de +#define PCI_device_NF2PROS1 0x008e +#define PCI_device_NF3PROS1 0x00e3 +#define PCI_device_NF3PROS2 0x00ee +#define PCI_device_MCP4S1 0x0036 +#define PCI_device_MCP4S2 0x003e +#define PCI_device_CK804S1 0x0054 +#define PCI_device_CK804S2 0x0055 +#define PCI_device_MCP51S1 0x0266 +#define PCI_device_MCP51S2 0x0267 +#define PCI_device_MCP55S1 0x037e +#define PCI_device_MCP55S2 0x037f +#define PCI_device_MCP61S1 0x03e7 +#define PCI_device_MCP61S2 0x03f6 +#define PCI_device_MCP61S3 0x03f7 + +#define ID(v,d) (((v)<< 16) | (d)) + +/* XXX: To be moved to PCI.h */ +#define PCI_command_interrupt 0x400 + +static const char * const kChannelNames[] = { + "Primary Channel", "Secondary Channel", + "Tertiary Channel", "Quaternary Channel" +}; + +static ide_for_controller_interface* ide; +static ide_adapter_interface* ide_adapter; +static device_manager_info* dm; + + +static float +controller_supports(device_node *parent) +{ + uint16 vendor_id; + uint16 device_id; + status_t res; + const char *bus = NULL; + + // get the bus (should be PCI) + if (dm->get_attr_string(parent, B_DEVICE_BUS, &bus, false) != B_OK) + return B_ERROR; + if (strcmp(bus, "pci") != 0) { + return B_ERROR; + } + + // get vendor and device ID + if ((res=dm->get_attr_uint16(parent, B_DEVICE_VENDOR_ID, &vendor_id, false)) != B_OK + || (res=dm->get_attr_uint16(parent, B_DEVICE_ID, &device_id, false)) != B_OK) { + return res; + } + + switch (ID(vendor_id, device_id)) { + /* VIA SATA chipsets */ + case ID(PCI_vendor_VIA, PCI_device_VIA6420): + case ID(PCI_vendor_VIA, PCI_device_VIA6421): + case ID(PCI_vendor_VIA, PCI_device_VIA8237A): + break; + + /* ALI SATA chipsets */ + case ID(PCI_vendor_ALI, PCI_device_ALI5281): + case ID(PCI_vendor_ALI, PCI_device_ALI5287): + case ID(PCI_vendor_ALI, PCI_device_ALI5289): + break; + + /* NVidia NForce chipsets */ + case ID(PCI_vendor_NVIDIA, PCI_device_NF2PROS1): + case ID(PCI_vendor_NVIDIA, PCI_device_NF3PROS1): + case ID(PCI_vendor_NVIDIA, PCI_device_NF3PROS2): + case ID(PCI_vendor_NVIDIA, PCI_device_MCP4S1): + case ID(PCI_vendor_NVIDIA, PCI_device_MCP4S2): + case ID(PCI_vendor_NVIDIA, PCI_device_CK804S1): + case ID(PCI_vendor_NVIDIA, PCI_device_CK804S2): + case ID(PCI_vendor_NVIDIA, PCI_device_MCP51S1): + case ID(PCI_vendor_NVIDIA, PCI_device_MCP51S2): + case ID(PCI_vendor_NVIDIA, PCI_device_MCP55S1): + case ID(PCI_vendor_NVIDIA, PCI_device_MCP55S2): + case ID(PCI_vendor_NVIDIA, PCI_device_MCP61S1): + case ID(PCI_vendor_NVIDIA, PCI_device_MCP61S2): + case ID(PCI_vendor_NVIDIA, PCI_device_MCP61S3): + break; + + default: + return 0.0f; + } + + TRACE("controller found! vendor 0x%04x, device 0x%04x\n", vendor_id, device_id); + + return 0.8f; +} + + +static status_t +controller_probe(device_node *parent) +{ + device_node *controller_node; + device_node *channels[4]; + uint16 command_block_base[4]; + uint16 control_block_base[4]; + pci_device_module_info *pci; + uint8 num_channels = 2; + uint32 bus_master_base; + pci_device *device = NULL; + uint16 device_id; + uint16 vendor_id; + uint8 int_num; + status_t res; + uint8 index; + + TRACE("controller_probe\n"); + + dm->get_driver(parent, (driver_module_info **)&pci, (void **)&device); + + device_id = pci->read_pci_config(device, PCI_device_id, 2); + vendor_id = pci->read_pci_config(device, PCI_vendor_id, 2); + int_num = pci->read_pci_config(device, PCI_interrupt_line, 1); + bus_master_base = pci->read_pci_config(device, PCI_base_registers + 16, 4); + + /* Default PCI assigments */ + command_block_base[0] = pci->read_pci_config(device, PCI_base_registers + 0, 4); + control_block_base[0] = pci->read_pci_config(device, PCI_base_registers + 4, 4); + command_block_base[1] = pci->read_pci_config(device, PCI_base_registers + 8, 4); + control_block_base[1] = pci->read_pci_config(device, PCI_base_registers + 12, 4); + + /* enable PCI interrupt */ + pci->write_pci_config(device, PCI_command, 2, + pci->read_pci_config(device, PCI_command, 2) & ~PCI_command_interrupt); + + if (vendor_id == PCI_vendor_NVIDIA) { + /* enable control access */ + pci->write_pci_config(device, 0x50, 1, + pci->read_pci_config(device, 0x50, 1) | 0x04); + } + + switch (ID(vendor_id, device_id)) { + case ID(PCI_vendor_VIA,PCI_device_VIA6421): + /* newer SATA chips has resources in one BAR for each channel */ + num_channels = 4; + command_block_base[0] = pci->read_pci_config(device, PCI_base_registers + 0, 4); + control_block_base[0] = command_block_base[0] + 8; + command_block_base[1] = pci->read_pci_config(device, PCI_base_registers + 4, 4); + control_block_base[1] = command_block_base[1] + 8; + command_block_base[2] = pci->read_pci_config(device, PCI_base_registers + 8, 4); + control_block_base[2] = command_block_base[2] + 8; + command_block_base[3] = pci->read_pci_config(device, PCI_base_registers + 12, 4); + control_block_base[3] = command_block_base[3] + 8; + break; + + case ID(PCI_vendor_ALI, PCI_device_ALI5287): + num_channels = 4; + command_block_base[2] = pci->read_pci_config(device, PCI_base_registers + 0, 4) + 8; + control_block_base[2] = pci->read_pci_config(device, PCI_base_registers + 4, 4) + 4; + command_block_base[3] = pci->read_pci_config(device, PCI_base_registers + 8, 4) + 8; + control_block_base[3] = pci->read_pci_config(device, PCI_base_registers + 12, 4) + 4; + break; + } + + bus_master_base &= PCI_address_io_mask; + for (index = 0; index < num_channels; index++) { + command_block_base[index] &= PCI_address_io_mask; + control_block_base[index] &= PCI_address_io_mask; + } + + res = ide_adapter->detect_controller(pci, device, parent, bus_master_base, + CONTROLLER_MODULE_NAME, "" /* XXX: unused: controller_driver_type*/, CONTROLLER_NAME, true, + true, 1, 0xffff, 0x10000, &controller_node); + // don't register if controller is already registered! + // (happens during rescan; registering new channels would kick out old channels) + if (res != B_OK) + goto err; + if (controller_node == NULL) { + res = B_IO_ERROR; + goto err; + } + + // ignore errors during registration of channels - could be a simple rescan collision + + for (index = 0; index < num_channels; index++) { + res = ide_adapter->detect_channel(pci, device, controller_node, CHANNEL_MODULE_NAME, + true, command_block_base[index], control_block_base[index], bus_master_base, + int_num, index, kChannelNames[index], &channels[index], false); + + dprintf("%s: %s\n", kChannelNames[index], strerror(res)); + } + + + TRACE("controller_probe success\n"); + return B_OK; + +err: + TRACE("controller_probe failed (%s)\n", strerror(res)); + return res; +} + + +static status_t +controller_init(device_node *node, void **controller_cookie) +{ + return ide_adapter->init_controller( + node, (ide_adapter_controller_info**)controller_cookie, + sizeof(ide_adapter_controller_info)); +} + + +static void +controller_uninit(void *controller_cookie) +{ + ide_adapter->uninit_controller(controller_cookie); +} + + +static void +controller_removed(void *controller_cookie) +{ + ide_adapter->controller_removed( + (ide_adapter_controller_info*)controller_cookie); +} + + +static status_t +channel_init(device_node *node, void **channel_cookie) +{ + return ide_adapter->init_channel(node, + (ide_adapter_channel_info**)channel_cookie, + sizeof(ide_adapter_channel_info), + ide_adapter->inthand); +} + + +static void +channel_uninit(void *channel_cookie) +{ + ide_adapter->uninit_channel(channel_cookie); +} + + +static void +channel_removed(void *channel_cookie) +{ + ide_adapter->channel_removed(channel_cookie); +} + + +static void +channel_set(void *cookie, ide_channel channel) +{ + ide_adapter->set_channel((ide_adapter_channel_info *)cookie, channel); +} + + +static status_t +task_file_write(void *channel_cookie, ide_task_file *tf, ide_reg_mask mask) +{ + return ide_adapter->write_command_block_regs(channel_cookie,tf,mask); +} + + +static status_t +task_file_read(void *channel_cookie, ide_task_file *tf, ide_reg_mask mask) +{ + return ide_adapter->read_command_block_regs(channel_cookie,tf,mask); +} + + +static uint8 +altstatus_read(void *channel_cookie) +{ + return ide_adapter->get_altstatus(channel_cookie); +} + + +static status_t +device_control_write(void *channel_cookie, uint8 val) +{ + return ide_adapter->write_device_control(channel_cookie,val); +} + + +static status_t +pio_write(void *channel_cookie, uint16 *data, int count, bool force_16bit) +{ + return ide_adapter->write_pio(channel_cookie,data,count,force_16bit); +} + + +static status_t +pio_read(void *channel_cookie, uint16 *data, int count, bool force_16bit) +{ + return ide_adapter->read_pio(channel_cookie,data,count,force_16bit); +} + + +static status_t +dma_prepare(void *channel_cookie, const physical_entry *sg_list, size_t sg_list_count, bool write) +{ + return ide_adapter->prepare_dma(channel_cookie,sg_list,sg_list_count,write); +} + + +static status_t +dma_start(void *channel_cookie) +{ + return ide_adapter->start_dma(channel_cookie); +} + + +static status_t +dma_finish(void *channel_cookie) +{ + return ide_adapter->finish_dma(channel_cookie); +} + + +module_dependency module_dependencies[] = { + { IDE_FOR_CONTROLLER_MODULE_NAME, (module_info **)&ide }, + { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&dm }, + { IDE_ADAPTER_MODULE_NAME, (module_info **)&ide_adapter }, + {} +}; + +static ide_controller_interface sChannelInterface = { + { + { + CHANNEL_MODULE_NAME, + 0, + NULL + }, + + NULL, + NULL, + channel_init, + channel_uninit, + NULL, + NULL, + channel_removed, + }, + + channel_set, + + task_file_write, + task_file_read, + + altstatus_read, + device_control_write, + + pio_write, + pio_read, + + dma_prepare, + dma_start, + dma_finish, +}; + +static driver_module_info sControllerInterface = { + { + CONTROLLER_MODULE_NAME, + 0, + NULL + }, + + .init_driver = &controller_init, + .uninit_driver = &controller_uninit, + .supports_device = &controller_supports, + .register_device = &controller_probe, + .device_removed = &controller_removed, +}; + +module_info *modules[] = { + (module_info *)&sControllerInterface, + (module_info *)&sChannelInterface, + NULL +}; diff --git a/src/add-ons/kernel/busses/ata/promise_tx2/Jamfile b/src/add-ons/kernel/busses/ata/promise_tx2/Jamfile new file mode 100644 index 0000000000..31d0221092 --- /dev/null +++ b/src/add-ons/kernel/busses/ata/promise_tx2/Jamfile @@ -0,0 +1,7 @@ +SubDir HAIKU_TOP src add-ons kernel busses ide promise_tx2 ; + +UsePrivateHeaders drivers kernel ; + +KernelAddon promise_tx2 : + promise_tx2.c + ; diff --git a/src/add-ons/kernel/busses/ata/promise_tx2/promise_tx2.c b/src/add-ons/kernel/busses/ata/promise_tx2/promise_tx2.c new file mode 100644 index 0000000000..6bc100e3d8 --- /dev/null +++ b/src/add-ons/kernel/busses/ata/promise_tx2/promise_tx2.c @@ -0,0 +1,377 @@ +/* + * Copyright 2002-04, Thomas Kurschel. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +/* + Promise TX2 series IDE controller driver +*/ + +#include +#include +#include + +#include + +#define debug_level_flow 0 +#define debug_level_error 3 +#define debug_level_info 3 + +#define DEBUG_MSG_PREFIX "PROMISE TX2 -- " + +#include "wrapper.h" + +#define PROMISE_TX2_CONTROLLER_MODULE_NAME "busses/ide/promise_tx2/device_v1" +#define PROMISE_TX2_CHANNEL_MODULE_NAME "busses/ide/promise_tx2/channel/v1" + + +static ide_for_controller_interface *ide; +static ide_adapter_interface *ide_adapter; +static device_manager_info *pnp; + + +static status_t +write_command_block_regs(void *channel_cookie, ide_task_file *tf, ide_reg_mask mask) +{ + return ide_adapter->write_command_block_regs((ide_adapter_channel_info *)channel_cookie, tf, mask); +} + + +static status_t +read_command_block_regs(void *channel_cookie, ide_task_file *tf, ide_reg_mask mask) +{ + return ide_adapter->read_command_block_regs((ide_adapter_channel_info *)channel_cookie, tf, mask); +} + + +static uint8 +get_altstatus(void *channel_cookie) +{ + return ide_adapter->get_altstatus((ide_adapter_channel_info *)channel_cookie); +} + + +static status_t +write_device_control(void *channel_cookie, uint8 val) +{ + return ide_adapter->write_device_control((ide_adapter_channel_info *)channel_cookie, val); +} + + +static status_t +write_pio(void *channel_cookie, uint16 *data, int count, bool force_16bit) +{ + return ide_adapter->write_pio((ide_adapter_channel_info *)channel_cookie, data, count, force_16bit); +} + + +static status_t +read_pio(void *channel_cookie, uint16 *data, int count, bool force_16bit) +{ + return ide_adapter->read_pio((ide_adapter_channel_info *)channel_cookie, data, count, force_16bit); +} + + +static int32 +inthand(void *arg) +{ + ide_adapter_channel_info *channel = (ide_adapter_channel_info *)arg; + pci_device_module_info *pci = channel->pci; + pci_device device = channel->device; + ide_bm_status bm_status; + uint8 status; + + SHOW_FLOW0( 3, "" ); + + if (channel->lost) + return B_UNHANDLED_INTERRUPT; + + // the controller always tells us whether it generated the IRQ, so ask it first + pci->write_io_8(device, channel->bus_master_base + 1, 0x0b); + if ((pci->read_io_8(device, channel->bus_master_base + 3) & 0x20) == 0) + return B_UNHANDLED_INTERRUPT; + + if (channel->dmaing) { + // in DMA mode, there is a safe test + // in PIO mode, this doesn't work + *(uint8 *)&bm_status = pci->read_io_8( device, + channel->bus_master_base + ide_bm_status_reg ); + + if (!bm_status.interrupt) + return B_UNHANDLED_INTERRUPT; + } + + // acknowledge IRQ + status = pci->read_io_8(device, channel->command_block_base + 7); + + return ide->irq_handler(channel->ide_channel, status); +} + + +static status_t +prepare_dma(void *channel_cookie, const physical_entry *sg_list, + size_t sg_list_count, bool to_device) +{ + return ide_adapter->prepare_dma((ide_adapter_channel_info *)channel_cookie, sg_list, sg_list_count, to_device); +} + + +static status_t +start_dma(void *channel_cookie) +{ + return ide_adapter->start_dma((ide_adapter_channel_info *)channel_cookie); +} + + +static status_t +finish_dma(void *channel_cookie) +{ + return ide_adapter->finish_dma((ide_adapter_channel_info *)channel_cookie); +} + + +static status_t +init_channel(device_node_handle node, ide_channel ide_channel, + void **channel_cookie) +{ + return ide_adapter->init_channel(node, ide_channel, (ide_adapter_channel_info **)channel_cookie, + sizeof(ide_adapter_channel_info), inthand); +} + + +static status_t +uninit_channel(void *channel_cookie) +{ + return ide_adapter->uninit_channel((ide_adapter_channel_info *)channel_cookie); +} + + +static void channel_removed(device_node_handle node, void *channel_cookie) +{ + return ide_adapter->channel_removed(node, (ide_adapter_channel_info *)channel_cookie); +} + + +static status_t +init_controller(device_node_handle node, void *user_cookie, + ide_adapter_controller_info **cookie) +{ + return ide_adapter->init_controller(node, user_cookie, cookie, + sizeof(ide_adapter_controller_info)); +} + + +static status_t +uninit_controller(ide_adapter_controller_info *controller) +{ + return ide_adapter->uninit_controller(controller); +} + + +static void +controller_removed(device_node_handle node, ide_adapter_controller_info *controller) +{ + return ide_adapter->controller_removed(node, controller); +} + + +// publish node of ide controller + +static status_t +publish_controller(device_node_handle parent, uint16 bus_master_base, uint8 intnum, + io_resource_handle *resources, device_node_handle *node) +{ + device_attr attrs[] = { + // info about ourself and our consumer + { B_DRIVER_MODULE, B_STRING_TYPE, { string: PROMISE_TX2_CONTROLLER_MODULE_NAME }}, + + // properties of this controller for ide bus manager + // there are always max. 2 devices + { IDE_CONTROLLER_MAX_DEVICES_ITEM, B_UINT8_TYPE, { ui8: 2 }}, + // of course we can DMA + { IDE_CONTROLLER_CAN_DMA_ITEM, B_UINT8_TYPE, { ui8: 1 }}, + // command queuing always works + { IDE_CONTROLLER_CAN_CQ_ITEM, B_UINT8_TYPE, { ui8: 1 }}, + // choose any name here + { IDE_CONTROLLER_CONTROLLER_NAME_ITEM, B_STRING_TYPE, { string: "Promise TX2" }}, + + // DMA properties + // some say it must be dword-aligned, others that it can be byte-aligned; + // stay on the safe side + { B_DMA_ALIGNMENT, B_UINT32_TYPE, { ui32: 3 }}, + // one S/G block must not cross 64K boundary + { B_DMA_BOUNDARY, B_UINT32_TYPE, { ui32: 0xffff }}, + // size of S/G block is 16 bits with zero being 64K + { B_DMA_MAX_SEGMENT_BLOCKS, B_UINT32_TYPE, { ui32: 0x10000 }}, + { B_DMA_MAX_SEGMENT_COUNT, B_UINT32_TYPE, + { ui32: IDE_ADAPTER_MAX_SG_COUNT }}, + + // private data to find controller + { IDE_ADAPTER_BUS_MASTER_BASE, B_UINT16_TYPE, { ui16: bus_master_base }}, + // store interrupt in controller node + { IDE_ADAPTER_INTNUM, B_UINT8_TYPE, { ui8: intnum }}, + { NULL } + }; + + SHOW_FLOW0(2, ""); + + return pnp->register_device(parent, attrs, resources, node); +} + + +// detect pure IDE controller, i.e. without channels + +static status_t +detect_controller(pci_device_module_info *pci, pci_device pci_device, + device_node_handle parent, uint16 bus_master_base, int8 intnum, + device_node_handle *node) +{ + io_resource_handle resource_handles[2]; + + SHOW_FLOW0(3, ""); + + if ((bus_master_base & PCI_address_space) != 1) + return B_OK; + + bus_master_base &= ~PCI_address_space; + + { + io_resource resources[2] = { + { IO_PORT, bus_master_base, 16 }, + {} + }; + + if (pnp->acquire_io_resources(resources, resource_handles) != B_OK) + return B_ERROR; + } + + return publish_controller(parent, bus_master_base, intnum, resource_handles, node); +} + + +static status_t +probe_controller(device_node_handle parent) +{ + pci_device_module_info *pci; + pci_device device; + uint16 command_block_base[2]; + uint16 control_block_base[2]; + uint16 bus_master_base; + device_node_handle controller_node, channels[2]; + uint8 intnum; + status_t res; + + SHOW_FLOW0(3, ""); + + if (pnp->init_driver(parent, NULL, (driver_module_info **)&pci, (void **)&device) != B_OK) + return B_ERROR; + + command_block_base[0] = pci->read_pci_config(device, PCI_base_registers, 4); + control_block_base[0] = pci->read_pci_config(device, PCI_base_registers + 4, 4); + command_block_base[1] = pci->read_pci_config(device, PCI_base_registers + 8, 4); + control_block_base[1] = pci->read_pci_config(device, PCI_base_registers + 12, 4); + bus_master_base = pci->read_pci_config(device, PCI_base_registers + 16, 4); + intnum = pci->read_pci_config(device, PCI_interrupt_line, 1); + + command_block_base[0] &= PCI_address_io_mask; + control_block_base[0] &= PCI_address_io_mask; + command_block_base[1] &= PCI_address_io_mask; + control_block_base[1] &= PCI_address_io_mask; + bus_master_base &= PCI_address_io_mask; + + res = detect_controller(pci, device, parent, bus_master_base, intnum, &controller_node); + if (res != B_OK || controller_node == NULL) + goto err; + + ide_adapter->detect_channel(pci, device, controller_node, + PROMISE_TX2_CHANNEL_MODULE_NAME, true, + command_block_base[0], control_block_base[0], bus_master_base, intnum, + 0, "Primary Channel", &channels[0], false); + + ide_adapter->detect_channel(pci, device, controller_node, + PROMISE_TX2_CHANNEL_MODULE_NAME, true, + command_block_base[1], control_block_base[1], bus_master_base, intnum, + 1, "Secondary Channel", &channels[1], false); + + pnp->uninit_driver(parent); + + return B_OK; + +err: + pnp->uninit_driver(parent); + return res; +} + + +static status_t +std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + case B_MODULE_UNINIT: + return B_OK; + + default: + return B_ERROR; + } +} + + +module_dependency module_dependencies[] = { + { IDE_FOR_CONTROLLER_MODULE_NAME, (module_info **)&ide }, + { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&pnp }, + { IDE_ADAPTER_MODULE_NAME, (module_info **)&ide_adapter }, + {} +}; + + +// exported interface +static ide_controller_interface channel_interface = { + { + { + PROMISE_TX2_CHANNEL_MODULE_NAME, + 0, + std_ops + }, + + NULL, // supported devices + NULL, + (status_t (*)( device_node_handle , void *, void ** )) init_channel, + uninit_channel, + channel_removed + }, + + &write_command_block_regs, + &read_command_block_regs, + + &get_altstatus, + &write_device_control, + + &write_pio, + &read_pio, + + &prepare_dma, + &start_dma, + &finish_dma, +}; + + +static driver_module_info controller_interface = { + { + PROMISE_TX2_CONTROLLER_MODULE_NAME, + 0, + std_ops + }, + + NULL, + probe_controller, + (status_t (*)(device_node_handle, void *, void **)) init_controller, + (status_t (*)(void *)) uninit_controller, + (void (*)(device_node_handle, void *)) controller_removed +}; + +module_info *modules[] = { + (module_info *)&controller_interface, + (module_info *)&channel_interface, + NULL +}; diff --git a/src/add-ons/kernel/busses/ata/promise_tx2/wrapper.h b/src/add-ons/kernel/busses/ata/promise_tx2/wrapper.h new file mode 100644 index 0000000000..89752da5df --- /dev/null +++ b/src/add-ons/kernel/busses/ata/promise_tx2/wrapper.h @@ -0,0 +1,90 @@ +#ifndef _WRAPPER_H +#define _WRAPPER_H + +#include +#include + + +// benaphores + +#define INIT_BEN(x, prefix) (mutex_init_etc(x, prefix, MUTEX_FLAG_CLONE_NAME), \ + B_OK) +#define DELETE_BEN(x) mutex_destroy(x) +#define ACQUIRE_BEN(x) mutex_lock(x) +#define RELEASE_BEN(x) mutex_unlock(x) + +// debug output + +#ifdef DEBUG_WAIT_ON_MSG +# define DEBUG_WAIT snooze( DEBUG_WAIT_ON_MSG ); +#else +# define DEBUG_WAIT +#endif + +#ifdef DEBUG_WAIT_ON_ERROR +# define DEBUG_WAIT_ERROR snooze( DEBUG_WAIT_ON_ERROR ); +#else +# define DEBUG_WAIT_ERROR +#endif + +#ifndef DEBUG_MAX_LEVEL_FLOW +# define DEBUG_MAX_LEVEL_FLOW 4 +#endif + +#ifndef DEBUG_MAX_LEVEL_INFO +# define DEBUG_MAX_LEVEL_INFO 4 +#endif + +#ifndef DEBUG_MAX_LEVEL_ERROR +# define DEBUG_MAX_LEVEL_ERROR 4 +#endif + +#ifndef DEBUG_MSG_PREFIX +# define DEBUG_MSG_PREFIX "" +#endif + +#ifndef debug_level_flow +# define debug_level_flow 3 +#endif + +#ifndef debug_level_info +# define debug_level_info 2 +#endif + +#ifndef debug_level_error +# define debug_level_error 1 +#endif + +#define FUNC_NAME DEBUG_MSG_PREFIX __FUNCTION__ ": " + +#define SHOW_FLOW(seriousness, format, param...) \ + do { if( seriousness <= debug_level_flow && seriousness <= DEBUG_MAX_LEVEL_FLOW ) { \ + dprintf( "%s"##format"\n", FUNC_NAME, param ); DEBUG_WAIT \ + }} while( 0 ) + +#define SHOW_FLOW0(seriousness, format) \ + do { if( seriousness <= debug_level_flow && seriousness <= DEBUG_MAX_LEVEL_FLOW ) { \ + dprintf( "%s"##format"\n", FUNC_NAME); DEBUG_WAIT \ + }} while( 0 ) + +#define SHOW_INFO(seriousness, format, param...) \ + do { if( seriousness <= debug_level_info && seriousness <= DEBUG_MAX_LEVEL_INFO ) { \ + dprintf( "%s"##format"\n", FUNC_NAME, param ); DEBUG_WAIT \ + }} while( 0 ) + +#define SHOW_INFO0(seriousness, format) \ + do { if( seriousness <= debug_level_info && seriousness <= DEBUG_MAX_LEVEL_INFO ) { \ + dprintf( "%s"##format"\n", FUNC_NAME); DEBUG_WAIT \ + }} while( 0 ) + +#define SHOW_ERROR(seriousness, format, param...) \ + do { if( seriousness <= debug_level_error && seriousness <= DEBUG_MAX_LEVEL_ERROR ) { \ + dprintf( "%s"##format"\n", FUNC_NAME, param ); DEBUG_WAIT_ERROR \ + }} while( 0 ) + +#define SHOW_ERROR0(seriousness, format) \ + do { if( seriousness <= debug_level_error && seriousness <= DEBUG_MAX_LEVEL_ERROR ) { \ + dprintf( "%s"##format"\n", FUNC_NAME); DEBUG_WAIT_ERROR \ + }} while( 0 ) + +#endif /* _BENAPHORE_H */ diff --git a/src/add-ons/kernel/busses/ata/silicon_image_3112/Jamfile b/src/add-ons/kernel/busses/ata/silicon_image_3112/Jamfile new file mode 100644 index 0000000000..092ef5502b --- /dev/null +++ b/src/add-ons/kernel/busses/ata/silicon_image_3112/Jamfile @@ -0,0 +1,7 @@ +SubDir HAIKU_TOP src add-ons kernel busses ide silicon_image_3112 ; + +UsePrivateHeaders drivers kernel ; + +KernelAddon silicon_image_3112 : + silicon_image_3112.c + ; diff --git a/src/add-ons/kernel/busses/ata/silicon_image_3112/silicon_image_3112.c b/src/add-ons/kernel/busses/ata/silicon_image_3112/silicon_image_3112.c new file mode 100644 index 0000000000..bebacc349e --- /dev/null +++ b/src/add-ons/kernel/busses/ata/silicon_image_3112/silicon_image_3112.c @@ -0,0 +1,895 @@ +/* + * Copyright 2006, Marcus Overhagen. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#include +#include + +#include +#include +#include + +#include + +#define TRACE(x...) dprintf("si-3112: " x) +//#define FLOW(x...) dprintf("si-3112: " x) +#define FLOW(x...) + + +#define DRIVER_PRETTY_NAME "Silicon Image SATA" +#define CONTROLLER_NAME DRIVER_PRETTY_NAME +#define CONTROLLER_MODULE_NAME "busses/ide/silicon_image_3112/driver_v1" +#define CHANNEL_MODULE_NAME "busses/ide/silicon_image_3112/channel/v1" + +enum asic_type { + ASIC_SI3112 = 0, + ASIC_SI3114 = 1, +}; + +#if 0 +static const char *adapter_asic_names[] = { + "si3112", + "si3114", + NULL +}; +#endif + +static const struct { + uint16 channel_count; + uint32 mmio_bar_size; +} kASICData[2] = { + { 2, 0x200 }, + { 4, 0x400 }, +}; + +static const struct { + uint16 cmd; + uint16 ctl; + uint16 bmdma; + uint16 sien; + uint16 stat; + const char *name; +} kControllerChannelData[] = { + { 0x80, 0x8A, 0x00, 0x148, 0xA0, "Primary Channel" }, + { 0xC0, 0xCA, 0x08, 0x1C8, 0xE0, "Secondary Channel" }, + { 0x280, 0x28A, 0x200, 0x348, 0x2A0, "Tertiary Channel" }, + { 0x2C0, 0x2CA, 0x208, 0x3C8, 0x2E0, "Quaternary Channel" }, +}; + +#define SI_SYSCFG 0x48 +#define SI_BMDMA2 0x200 +#define SI_INT_STEERING 0x02 +#define SI_MASK_2PORT ((1 << 22) | (1 << 23)) +#define SI_MASK_4PORT ((1 << 22) | (1 << 23) | (1 << 24) | (1 << 25)) + +struct channel_data; + +typedef struct controller_data { + pci_device_module_info *pci; + pci_device *device; + + device_node *node; + + struct channel_data *channel[4]; // XXX only for interrupt workaround + int channel_count; // XXX only for interrupt workaround + + uint32 asic_index; + uint32 int_num; + + area_id mmio_area; + uint32 mmio_addr; + + uint32 lost; // != 0 if device got removed, i.e. if it must not + // be accessed anymore +} controller_data; + + +typedef struct channel_data { + pci_device_module_info *pci; + device_node * node; + pci_device * device; + ide_channel ide_channel; + + volatile uint8 * task_file; + volatile uint8 * control_block; + volatile uint8 * command_block; + volatile uint8 * dev_ctrl; + volatile uint8 * bm_status_reg; + volatile uint8 * bm_command_reg; + volatile uint32 * bm_prdt_address; + + volatile uint32 * stat; + + area_id prd_area; + prd_entry * prdt; + void * prdt_phys; + uint32 dma_active; + uint32 lost; +} channel_data; + + +static ide_for_controller_interface * ide; +static ide_adapter_interface * ide_adapter; +static device_manager_info * dm; + +static status_t device_control_write(void *channel_cookie, uint8 val); +static int32 handle_interrupt(void *arg); + + +static float +controller_supports(device_node *parent) +{ + const char *bus; + uint16 vendorID; + uint16 deviceID; + + // get the bus (should be PCI) + if (dm->get_attr_string(parent, B_DEVICE_BUS, &bus, false) != B_OK + || strcmp(bus, "pci") != 0) + return B_ERROR; + + // get vendor and device ID + if (dm->get_attr_uint16(parent, B_DEVICE_VENDOR_ID, &vendorID, false) != B_OK + || dm->get_attr_uint16(parent, B_DEVICE_ID, &deviceID, false) != B_OK) { + return B_ERROR; + } + + #define ID(v, d) (((v) << 16) | (d)) + switch (ID(vendorID, deviceID)) { + case ID(0x1095, 0x0240): + case ID(0x1095, 0x3112): + case ID(0x1095, 0x3114): + case ID(0x1095, 0x3512): + case ID(0x1002, 0x436e): // ATI + case ID(0x1002, 0x4379): // ATI + case ID(0x1002, 0x437a): // ATI + break; + default: + return 0.0f; + } + + TRACE("controller found! vendor 0x%04x, device 0x%04x\n", vendorID, deviceID); + + return 0.8f; +} + + +static status_t +controller_probe(device_node *parent) +{ + pci_device *device; + pci_device_module_info *pci; + uint32 mmioBase; + uint16 deviceID; + uint16 vendorID; + uint8 interruptNumber; + int asicIndex; + + TRACE("controller_probe\n"); + + dm->get_driver(parent, (driver_module_info **)&pci, (void **)&device); + + deviceID = pci->read_pci_config(device, PCI_device_id, 2); + vendorID = pci->read_pci_config(device, PCI_vendor_id, 2); + interruptNumber = pci->read_pci_config(device, PCI_interrupt_line, 1); + mmioBase = pci->read_pci_config(device, PCI_base_registers + 20, 4) + & PCI_address_io_mask; + + switch (ID(vendorID, deviceID)) { + case ID(0x1095, 0x0240): + case ID(0x1095, 0x3112): + case ID(0x1095, 0x3512): + case ID(0x1002, 0x436e): // ATI + case ID(0x1002, 0x4379): // ATI + case ID(0x1002, 0x437a): // ATI + asicIndex = ASIC_SI3112; + break; + case ID(0x1095, 0x3114): + asicIndex = ASIC_SI3114; + break; + default: + TRACE("unsupported asic\n"); + return B_ERROR; + } + + if (!mmioBase) { + TRACE("mmio not configured\n"); + return B_ERROR; + } + + if (interruptNumber == 0 || interruptNumber == 0xff) { + TRACE("irq not configured\n"); + return B_ERROR; + } + + { + io_resource resources[2] = { + { B_IO_MEMORY, mmioBase, kASICData[asicIndex].mmio_bar_size }, + {} + }; + device_attr attrs[] = { + // properties of this controller for ide bus manager + // there are always max. 2 devices + // (unless this is a Compact Flash Card with a built-in IDE controller, + // which has exactly 1 device) + { IDE_CONTROLLER_MAX_DEVICES_ITEM, B_UINT8_TYPE, { ui8: kASICData[asicIndex].channel_count }}, + // of course we can DMA + { IDE_CONTROLLER_CAN_DMA_ITEM, B_UINT8_TYPE, { ui8: true }}, + // command queuing always works + { IDE_CONTROLLER_CAN_CQ_ITEM, B_UINT8_TYPE, { ui8: true }}, + // choose any name here + { IDE_CONTROLLER_CONTROLLER_NAME_ITEM, B_STRING_TYPE, { string: CONTROLLER_NAME }}, + + // DMA properties + // data must be word-aligned; + // warning: some controllers are more picky! + { B_DMA_ALIGNMENT, B_UINT32_TYPE, { ui32: 1}}, + // one S/G block must not cross 64K boundary + { B_DMA_BOUNDARY, B_UINT32_TYPE, { ui32: 0xffff }}, + // max size of S/G block is 16 bits with zero being 64K + { B_DMA_MAX_SEGMENT_BLOCKS, B_UINT32_TYPE, { ui32: 0x10000 }}, + { B_DMA_MAX_SEGMENT_COUNT, B_UINT32_TYPE, + { ui32: IDE_ADAPTER_MAX_SG_COUNT }}, + + // private data to find controller + { "silicon_image_3112/asic_index", B_UINT32_TYPE, { ui32: asicIndex }}, + { "silicon_image_3112/mmio_base", B_UINT32_TYPE, { ui32: mmioBase }}, + { "silicon_image_3112/int_num", B_UINT32_TYPE, { ui32: interruptNumber }}, + { NULL } + }; + + TRACE("publishing controller\n"); + + return dm->register_node(parent, CONTROLLER_MODULE_NAME, attrs, + resources, NULL); + } +} + + +static status_t +controller_init(device_node *node, void **_controllerCookie) +{ + controller_data *controller; + device_node *parent; + pci_device_module_info *pci; + pci_device *device; + uint32 asicIndex; + uint32 mmioBase; + uint32 mmioAddr; + area_id mmioArea; + uint32 interruptNumber; + status_t res; + uint32 temp; + int i; + + TRACE("controller_init\n"); + + if (dm->get_attr_uint32(node, "silicon_image_3112/asic_index", &asicIndex, false) != B_OK) + return B_ERROR; + if (dm->get_attr_uint32(node, "silicon_image_3112/mmio_base", &mmioBase, false) != B_OK) + return B_ERROR; + if (dm->get_attr_uint32(node, "silicon_image_3112/int_num", &interruptNumber, false) != B_OK) + return B_ERROR; + + controller = malloc(sizeof(controller_data)); + if (!controller) + return B_NO_MEMORY; + + FLOW("controller %p\n", controller); + + mmioArea = map_physical_memory("Silicon Image SATA regs", + (void *)mmioBase, kASICData[asicIndex].mmio_bar_size, + B_ANY_KERNEL_ADDRESS, 0, (void **)&mmioAddr); + if (mmioArea < B_OK) { + TRACE("controller_init: mapping memory failed\n"); + free(controller); + return B_ERROR; + } + + parent = dm->get_parent_node(node); + dm->get_driver(parent, (driver_module_info **)&pci, (void **)&device); + dm->put_node(parent); + + TRACE("asic %ld\n", asicIndex); + TRACE("int_num %ld\n", interruptNumber); + TRACE("mmio_addr %p\n", (void *)mmioAddr); + + controller->pci = pci; + controller->device = device; + controller->node = node; + controller->asic_index = asicIndex; + controller->int_num = interruptNumber; + controller->mmio_area = mmioArea; + controller->mmio_addr = mmioAddr; + controller->lost = 0; + + controller->channel_count = kASICData[asicIndex].channel_count; + for (i = 0; i < kASICData[asicIndex].channel_count; i++) + controller->channel[i] = 0; + + if (asicIndex == ASIC_SI3114) { + // I put a magic spell on you + temp = *(volatile uint32 *)(mmioAddr + SI_BMDMA2); + *(volatile uint32 *)(mmioAddr + SI_BMDMA2) = temp | SI_INT_STEERING; + *(volatile uint32 *)(mmioAddr + SI_BMDMA2); // flush + } + + // disable all sata phy interrupts + for (i = 0; i < kASICData[asicIndex].channel_count; i++) + *(volatile uint32 *)(mmioAddr + kControllerChannelData[i].sien) = 0; + *(volatile uint32 *)(mmioAddr + kControllerChannelData[0].sien); // flush + + // install interrupt handler + res = install_io_interrupt_handler(interruptNumber, handle_interrupt, + controller, 0); + if (res < B_OK) { + TRACE("controller_init: installing interrupt handler failed\n"); + delete_area(mmioArea); + free(controller); + return res; + } + + // unmask interrupts + temp = *(volatile uint32 *)(mmioAddr + SI_SYSCFG); + temp &= (asicIndex == ASIC_SI3114) ? (~SI_MASK_4PORT) : (~SI_MASK_2PORT); + *(volatile uint32 *)(mmioAddr + SI_SYSCFG) = temp; + *(volatile uint32 *)(mmioAddr + SI_SYSCFG); // flush + + *_controllerCookie = controller; + + TRACE("controller_init success\n"); + return B_OK; +} + + +static void +controller_uninit(void *controllerCookie) +{ + controller_data *controller = controllerCookie; + uint32 temp; + + TRACE("controller_uninit enter\n"); + + // disable interrupts + temp = *(volatile uint32 *)(controller->mmio_addr + SI_SYSCFG); + temp |= (controller->asic_index == ASIC_SI3114) ? SI_MASK_4PORT : SI_MASK_2PORT; + *(volatile uint32 *)(controller->mmio_addr + SI_SYSCFG) = temp; + *(volatile uint32 *)(controller->mmio_addr + SI_SYSCFG); // flush + + remove_io_interrupt_handler(controller->int_num, handle_interrupt, + controller); + + delete_area(controller->mmio_area); + free(controller); + + TRACE("controller_uninit leave\n"); +} + + +static status_t +controller_register_channels(void *cookie) +{ + controller_data *controller = cookie; + int index; + + // publish channel nodes + for (index = 0; index < kASICData[controller->asic_index].channel_count; + index++) { + device_attr attrs[] = { + { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: DRIVER_PRETTY_NAME }}, +// { PNP_DRIVER_CONNECTION, B_STRING_TYPE, { string: kControllerChannelData[channelIndex].name }}, + { B_DEVICE_FIXED_CHILD, B_STRING_TYPE, { string: IDE_FOR_CONTROLLER_MODULE_NAME }}, + + // private data to identify channel + { IDE_CONTROLLER_CAN_DMA_ITEM, B_UINT8_TYPE, { ui8: true }}, + { "silicon_image_3112/chan_index", B_UINT32_TYPE, { ui32: index }}, + { NULL } + }; + + TRACE("publishing %s\n", kControllerChannelData[index].name); + + dm->register_node(controller->node, CHANNEL_MODULE_NAME, attrs, + NULL, NULL); + } + + return B_OK; +} + + +static void +controller_removed(void *controllerCookie) +{ + controller_data *controller = controllerCookie; + controller->lost = 1; +} + + +// #pragma mark - + + +static status_t +channel_init(device_node *node, void **_channelCookie) +{ + controller_data *controller; + device_node *parent; + channel_data *channel; + physical_entry entry; + size_t prdtSize; + uint32 channelIndex; + + TRACE("channel_init enter\n"); + + channel = malloc(sizeof(channel_data)); + if (!channel) + return B_NO_MEMORY; + + if (dm->get_attr_uint32(node, "silicon_image_3112/chan_index", &channelIndex, false) != B_OK) + goto err; + +#if 0 + if (1 /* debug */){ + uint8 bus, device, function; + uint16 vendorID, deviceID; + dm->get_attr_uint8(node, PCI_DEVICE_BUS_ITEM, &bus, true); + dm->get_attr_uint8(node, PCI_DEVICE_DEVICE_ITEM, &device, true); + dm->get_attr_uint8(node, PCI_DEVICE_FUNCTION_ITEM, &function, true); + dm->get_attr_uint16(node, PCI_DEVICE_VENDOR_ID_ITEM, &vendorID, true); + dm->get_attr_uint16(node, PCI_DEVICE_DEVICE_ID_ITEM, &deviceID, true); + TRACE("bus %3d, device %2d, function %2d: vendor %04x, device %04x\n", + bus, device, function, vendorID, deviceID); + } +#endif + + TRACE("channel_index %ld\n", channelIndex); + TRACE("channel name: %s\n", kControllerChannelData[channelIndex].name); + + TRACE("channel %p\n", channel); + + parent = dm->get_parent_node(node); + dm->get_driver(parent, NULL, (void **)&controller); + dm->put_node(parent); + + TRACE("controller %p\n", controller); + TRACE("mmio_addr %p\n", (void *)controller->mmio_addr); + + // PRDT must be contiguous, dword-aligned and must not cross 64K boundary + prdtSize = (IDE_ADAPTER_MAX_SG_COUNT * sizeof(prd_entry) + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1); + channel->prd_area = create_area("prd", (void **)&channel->prdt, + B_ANY_KERNEL_ADDRESS, prdtSize, B_CONTIGUOUS, 0); + if (channel->prd_area < B_OK) { + TRACE("creating prd_area failed\n"); + goto err; + } + + get_memory_map(channel->prdt, prdtSize, &entry, 1); + channel->prdt_phys = entry.address; + + channel->pci = controller->pci; + channel->device = controller->device; + channel->node = node; + channel->ide_channel = NULL; + + channel->task_file = (volatile uint8 *)(controller->mmio_addr + kControllerChannelData[channelIndex].cmd + 1); + channel->control_block = (volatile uint8 *)(controller->mmio_addr + kControllerChannelData[channelIndex].ctl); + channel->command_block = (volatile uint8 *)(controller->mmio_addr + kControllerChannelData[channelIndex].cmd); + channel->dev_ctrl = (volatile uint8 *)(controller->mmio_addr + kControllerChannelData[channelIndex].ctl); + channel->bm_prdt_address = (volatile uint32 *)(controller->mmio_addr + kControllerChannelData[channelIndex].bmdma + IDE_BM_PRDT_ADDRESS); + channel->bm_status_reg = (volatile uint8 *)(controller->mmio_addr + kControllerChannelData[channelIndex].bmdma + IDE_BM_STATUS_REG); + channel->bm_command_reg = (volatile uint8 *)(controller->mmio_addr + kControllerChannelData[channelIndex].bmdma + IDE_BM_COMMAND_REG); + channel->stat = (volatile uint32 *)(controller->mmio_addr + kControllerChannelData[channelIndex].stat); + + channel->lost = 0; + channel->dma_active = 0; + + controller->channel[channelIndex] = channel; + + // enable interrupts so the channel is ready to run + device_control_write(channel, ide_devctrl_bit3); + + *_channelCookie = channel; + + TRACE("channel_init leave\n"); + return B_OK; + +err: + free(channel); + return B_ERROR; +} + + +static void +channel_uninit(void *channelCookie) +{ + channel_data *channel = channelCookie; + + TRACE("channel_uninit enter\n"); + + // disable IRQs + device_control_write(channel, ide_devctrl_bit3 | ide_devctrl_nien); + + // catch spurious interrupt + // (some controllers generate an IRQ when you _disable_ interrupts, + // they are delayed by less then 40 µs, so 1 ms is safe) + snooze(1000); + + delete_area(channel->prd_area); + free(channel); + + TRACE("channel_uninit leave\n"); +} + + +static void +channel_removed(void *channelCookie) +{ + channel_data *channel = channelCookie; + channel->lost = 1; +} + + +static void +set_channel(void *channelCookie, ide_channel ideChannel) +{ + channel_data *channel = channelCookie; + channel->ide_channel = ideChannel; +} + + +static status_t +task_file_write(void *channelCookie, ide_task_file *tf, ide_reg_mask mask) +{ + channel_data *channel = channelCookie; + int i; + + FLOW("task_file_write\n"); + + if (channel->lost) + return B_ERROR; + + for (i = 0; i < 7; i++) { + if( ((1 << (i+7)) & mask) != 0 ) { + FLOW("%x->HI(%x)\n", tf->raw.r[i + 7], i ); + channel->task_file[i] = tf->raw.r[i + 7]; + } + + if (((1 << i) & mask) != 0) { + FLOW("%x->LO(%x)\n", tf->raw.r[i], i ); + channel->task_file[i] = tf->raw.r[i]; + } + } + *channel->dev_ctrl; // read altstatus to flush + + return B_OK; +} + + +static status_t +task_file_read(void *channelCookie, ide_task_file *tf, ide_reg_mask mask) +{ + channel_data *channel = channelCookie; + int i; + + FLOW("task_file_read\n"); + + if (channel->lost) + return B_ERROR; + + for (i = 0; i < 7; i++) { + if (((1 << i) & mask) != 0) { + tf->raw.r[i] = channel->task_file[i]; + FLOW("%x: %x\n", i, (int)tf->raw.r[i] ); + } + } + + return B_OK; +} + + +static uint8 +altstatus_read(void *channelCookie) +{ + channel_data *channel = channelCookie; + + FLOW("altstatus_read\n"); + + if (channel->lost) + return 0x01; // Error bit + + return *channel->dev_ctrl; +} + + +static status_t +device_control_write(void *channelCookie, uint8 val) +{ + channel_data *channel = channelCookie; + + FLOW("device_control_write 0x%x\n", val); + + if (channel->lost) + return B_ERROR; + + *channel->dev_ctrl = val; + *channel->dev_ctrl; // read altstatus to flush + + return B_OK; +} + + +static status_t +pio_write(void *channelCookie, uint16 *data, int count, bool force_16bit) +{ + channel_data *channel = channelCookie; + if (channel->lost) + return B_ERROR; + + FLOW("pio_write force_16bit = %d, (count & 1) = %d\n", force_16bit, (count & 1)); + + // The data port is only 8 bit wide in the command register block. + + if ((count & 1) != 0 || force_16bit) { + volatile uint16 * base = (volatile uint16 *)channel->command_block; + for ( ; count > 0; --count) + *base = *(data++); + } else { + volatile uint32 * base = (volatile uint32 *)channel->command_block; + uint32 *cur_data = (uint32 *)data; + + for ( ; count > 0; count -= 2 ) + *base = *(cur_data++); + } + *channel->dev_ctrl; // read altstatus to flush + + return B_OK; +} + + +static status_t +pio_read(void *channelCookie, uint16 *data, int count, bool force_16bit) +{ + channel_data *channel = channelCookie; + if (channel->lost) + return B_ERROR; + + FLOW("pio_read force_16bit = %d, (count & 1) = %d\n", force_16bit, (count & 1)); + + // The data port is only 8 bit wide in the command register block. + // We are memory mapped and read using 16 or 32 bit access from this 8 bit location. + + if ((count & 1) != 0 || force_16bit) { + volatile uint16 * base = (volatile uint16 *)channel->command_block; + for ( ; count > 0; --count) + *(data++) = *base; + } else { + volatile uint32 * base = (volatile uint32 *)channel->command_block; + uint32 *cur_data = (uint32 *)data; + + for ( ; count > 0; count -= 2 ) + *(cur_data++) = *base; + } + + return B_OK; +} + + +static status_t +dma_prepare(void *channelCookie, const physical_entry *sg_list, + size_t sg_list_count, bool write) +{ + channel_data *channel = channelCookie; + pci_device_module_info *pci = channel->pci; + pci_device *device = channel->device; + prd_entry *prd = channel->prdt; + uint8 command; + uint8 status; + uint32 temp; + int i; + + FLOW("dma_prepare enter\n"); + + for (i = sg_list_count - 1, prd = channel->prdt; i >= 0; + --i, ++prd, ++sg_list ) { + prd->address = B_HOST_TO_LENDIAN_INT32(pci->ram_address(device, + sg_list->address)); + + // 0 means 64K - this is done automatically by discarding upper 16 bits + prd->count = B_HOST_TO_LENDIAN_INT16((uint16)sg_list->size); + prd->EOT = i == 0; + + FLOW("%x, %x, %d\n", (int)prd->address, prd->count, prd->EOT); + } + + // XXX move this to chan init? + temp = (*channel->bm_prdt_address) & 3; + temp |= B_HOST_TO_LENDIAN_INT32(pci->ram_address(device, + (void *)channel->prdt_phys)) & ~3; + *channel->bm_prdt_address = temp; + + *channel->dev_ctrl; // read altstatus to flush + + // reset interrupt and error signal + status = *channel->bm_status_reg | IDE_BM_STATUS_INTERRUPT + | IDE_BM_STATUS_ERROR; + *channel->bm_status_reg = status; + + *channel->dev_ctrl; // read altstatus to flush + + // set data direction + command = *channel->bm_command_reg; + if (write) + command &= ~IDE_BM_COMMAND_READ_FROM_DEVICE; + else + command |= IDE_BM_COMMAND_READ_FROM_DEVICE; + + *channel->bm_command_reg = command; + + *channel->dev_ctrl; // read altstatus to flush + + FLOW("dma_prepare leave\n"); + + return B_OK; +} + + +static status_t +dma_start(void *channelCookie) +{ + channel_data *channel = channelCookie; + uint8 command; + + FLOW("dma_start enter\n"); + + command = *channel->bm_command_reg | IDE_BM_COMMAND_START_STOP; + channel->dma_active = true; + *channel->bm_command_reg = command; + + *channel->dev_ctrl; // read altstatus to flush + + FLOW("dma_start leave\n"); + + return B_OK; +} + + +static status_t +dma_finish(void *channelCookie) +{ + channel_data *channel = channelCookie; + uint8 command; + uint8 status; + + FLOW("dma_finish enter\n"); + + command = *channel->bm_command_reg & ~IDE_BM_COMMAND_START_STOP; + channel->dma_active = false; + + *channel->bm_command_reg = command; + + status = *channel->bm_status_reg; + + *channel->bm_status_reg = status | IDE_BM_STATUS_INTERRUPT + | IDE_BM_STATUS_ERROR; + + *channel->dev_ctrl; // read altstatus to flush + + if ((status & IDE_BM_STATUS_ERROR) != 0) { + FLOW("dma_finish: failed\n"); + return B_ERROR; + } + + if ((status & IDE_BM_STATUS_INTERRUPT) == 0) { + if ((status & IDE_BM_STATUS_ACTIVE) != 0) { + TRACE("dma_finish: transfer aborted\n"); + return B_ERROR; + } else { + TRACE("dma_finish: buffer underrun\n"); + return B_DEV_DATA_UNDERRUN; + } + } else { + if ((status & IDE_BM_STATUS_ACTIVE) != 0) { + TRACE("dma_finish: buffer too large\n"); + return B_DEV_DATA_OVERRUN; + } else { + FLOW("dma_finish leave\n"); + return B_OK; + } + } +} + + +static int32 +handle_interrupt(void *arg) +{ + controller_data *controller = arg; + uint8 status; + int32 result, ret; + int i; + +// FLOW("handle_interrupt\n"); + + result = B_UNHANDLED_INTERRUPT; + + for (i = 0; i < controller->channel_count; i++) { + channel_data *channel = controller->channel[i]; + if (!channel || channel->lost) + continue; + + if (channel->dma_active) { + if ((*channel->bm_status_reg & IDE_BM_STATUS_INTERRUPT) == 0) + continue; + } else { + // for PIO, read the "Task File Configuration + Status" Interrupt + // status + if (!(*channel->stat & (1 << 11))) + continue; + } + + // acknowledge IRQ + status = *(channel->command_block + 7); + ret = ide->irq_handler(channel->ide_channel, status); + if (ret == B_INVOKE_SCHEDULER || result == B_UNHANDLED_INTERRUPT) + result = ret; + } + + return result; +} + + +module_dependency module_dependencies[] = { + { IDE_FOR_CONTROLLER_MODULE_NAME, (module_info **)&ide }, + { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&dm }, + { IDE_ADAPTER_MODULE_NAME, (module_info **)&ide_adapter }, + {} +}; + + +static ide_controller_interface sChannelInterface = { + { + { + CHANNEL_MODULE_NAME, + 0, + NULL + }, + + .supports_device = NULL, + .register_device = NULL, + .init_driver = &channel_init, + .uninit_driver = &channel_uninit, + .register_child_devices = NULL, + .device_removed = &channel_removed, + }, + + .set_channel = &set_channel, + .write_command_block_regs = &task_file_write, + .read_command_block_regs = &task_file_read, + .get_altstatus = &altstatus_read, + .write_device_control = &device_control_write, + .write_pio = &pio_write, + .read_pio = &pio_read, + .prepare_dma = &dma_prepare, + .start_dma = &dma_start, + .finish_dma = &dma_finish, +}; + + +static driver_module_info sControllerInterface = { + { + CONTROLLER_MODULE_NAME, + 0, + NULL + }, + + .supports_device = &controller_supports, + .register_device = &controller_probe, + .init_driver = &controller_init, + .uninit_driver = &controller_uninit, + .register_child_devices = &controller_register_channels, + .device_removed = &controller_removed, +}; + +module_info *modules[] = { + (module_info *)&sControllerInterface, + (module_info *)&sChannelInterface, + NULL +};