Copy ide busses into new ata busses folder, so they can be modified without breaking ide.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30486 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Marcus Overhagen
2009-04-29 20:01:38 +00:00
parent 58f0d01b6d
commit 587382f9d6
14 changed files with 2772 additions and 0 deletions
+8
View File
@@ -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 ;
@@ -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
;
@@ -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 <KernelExport.h>
#include <stdlib.h>
#include <string.h>
#include <ide_adapter.h>
#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
};
@@ -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
;
@@ -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 <KernelExport.h>
#include <stdlib.h>
#include <string.h>
#include <bus/ISA.h>
#include <bus/IDE.h>
#include <ide_types.h>
#include <device_manager.h>
//#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
};
@@ -0,0 +1,8 @@
SubDir HAIKU_TOP src add-ons kernel busses ide it8211 ;
UsePrivateHeaders kernel ;
UsePrivateHeaders drivers ;
KernelAddon it8211 :
it8211.c
;
@@ -0,0 +1,242 @@
/*
* Copyright 2008, Michael Lotz. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <KernelExport.h>
#include <stdlib.h>
#include <string.h>
#include <ide_adapter.h>
#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
};
@@ -0,0 +1,8 @@
SubDir HAIKU_TOP src add-ons kernel busses ide legacy_sata ;
UsePrivateHeaders drivers kernel ;
KernelAddon 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 <KernelExport.h>
#include <stdlib.h>
#include <string.h>
#include <device_manager.h>
#include <bus/IDE.h>
#include <ide_adapter.h>
#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
};
@@ -0,0 +1,7 @@
SubDir HAIKU_TOP src add-ons kernel busses ide promise_tx2 ;
UsePrivateHeaders drivers kernel ;
KernelAddon 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 <KernelExport.h>
#include <stdlib.h>
#include <string.h>
#include <bus/ide/ide_adapter.h>
#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
};
@@ -0,0 +1,90 @@
#ifndef _WRAPPER_H
#define _WRAPPER_H
#include <KernelExport.h>
#include <lock.h>
// 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 */
@@ -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
;
@@ -0,0 +1,895 @@
/*
* Copyright 2006, Marcus Overhagen. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <stdlib.h>
#include <string.h>
#include <KernelExport.h>
#include <device_manager.h>
#include <bus/IDE.h>
#include <ide_adapter.h>
#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
};