* 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 <ata_adapter.h>
#include <bus/ISA.h>
#ifdef TRACE_IDE_ISA
# define TRACE(x...) dprintf("ide_isa: " x)
#else
# define TRACE(x...) ;
#endif
#define ATA_ISA_MODULE_NAME "busses/ata/ide_isa/driver_v1"
#define ATA_ISA_COMMAND_BLOCK_BASE "ide_isa/command_block_base"
#define ATA_ISA_CONTROL_BLOCK_BASE "ide_isa/control_block_base"
#define ATA_ISA_INTNUM "ide_isa/irq"
ata_for_controller_interface *sATA;
device_manager_info *sDeviceManager;
typedef struct channel_info {
isa2_module_info *isa;
uint16 command_block_base;
uint16 control_block_base;
int intnum;
uint32 lost;
ata_channel ataChannel;
device_node *node;
} channel_info;
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 = ATA_FOR_CONTROLLER_MODULE_NAME }},
{ ATA_CONTROLLER_MAX_DEVICES_ITEM, B_UINT8_TYPE, { .ui8 = 2 }},
{ ATA_CONTROLLER_CAN_DMA_ITEM, B_UINT8_TYPE, { .ui8 = 0 }},
{ ATA_CONTROLLER_CONTROLLER_NAME_ITEM, B_STRING_TYPE, { .string = name }},
{ B_DMA_ALIGNMENT, B_UINT32_TYPE, { .ui32 = 1 }},
{ B_DMA_HIGH_ADDRESS, B_UINT64_TYPE, { .ui64 = 0x100000000LL }},
{ ATA_ISA_COMMAND_BLOCK_BASE, B_UINT16_TYPE, { .ui16 = command_block_base }},
{ ATA_ISA_CONTROL_BLOCK_BASE, B_UINT16_TYPE, { .ui16 = control_block_base }},
{ ATA_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 sDeviceManager->register_node(parent, ATA_ISA_MODULE_NAME, attrs, resources,
NULL);
}
static void
set_channel(void *cookie, ata_channel ataChannel)
{
channel_info *channel = cookie;
channel->ataChannel = ataChannel;
}
static status_t
write_command_block_regs(void *channel_cookie, ata_task_file *tf,
ata_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, ata_task_file *tf,
ata_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;
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;
status = channel->isa->read_io_8(channel->command_block_base + 7);
return sATA->interrupt_handler(channel->ataChannel, 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;
}
static float
supports_device(device_node *parent)
{
const char *bus;
if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
return B_ERROR;
if (strcmp(bus, "isa"))
return 0.0;
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");
if (sDeviceManager->get_attr_uint16(node, ATA_ISA_COMMAND_BLOCK_BASE, &command_block_base, false) != B_OK
|| sDeviceManager->get_attr_uint16(node, ATA_ISA_CONTROL_BLOCK_BASE, &control_block_base, false) != B_OK
|| sDeviceManager->get_attr_uint8(node, ATA_ISA_INTNUM, &irq, false) != B_OK)
return B_ERROR;
parent = sDeviceManager->get_parent_node(node);
sDeviceManager->get_driver(parent, (driver_module_info **)&isa, NULL);
sDeviceManager->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->ataChannel = 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;
}
write_device_control(channel, ATA_DEVICE_CONTROL_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");
write_device_control(channel, ATA_DEVICE_CONTROL_BIT3 | ATA_DEVICE_CONTROL_DISABLE_INTS);
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");
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");
atomic_or((int32*)&channel->lost, 1);
}
module_dependency module_dependencies[] = {
{ ATA_FOR_CONTROLLER_MODULE_NAME, (module_info **)&sATA },
{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
{}
};
static ata_controller_interface sISAControllerInterface = {
{
{
ATA_ISA_MODULE_NAME,
0,
NULL
},
supports_device,
register_device,
init_channel,
uninit_channel,
NULL,
NULL,
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
};