From fba47bd7286c747745ca1469f652cc9db25a09ca Mon Sep 17 00:00:00 2001 From: "Ithamar R. Adema" Date: Sat, 11 Aug 2007 15:03:30 +0000 Subject: [PATCH] Initial work for a driver supporting the pre-AHCI SATA controllers. Currently implemented: VIA & ALI. nForce is on the TODO list ;) git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21894 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../kernel/busses/ide/legacy_sata/Jamfile | 8 + .../busses/ide/legacy_sata/legacy_sata.c | 389 ++++++++++++++++++ 2 files changed, 397 insertions(+) create mode 100644 src/add-ons/kernel/busses/ide/legacy_sata/Jamfile create mode 100644 src/add-ons/kernel/busses/ide/legacy_sata/legacy_sata.c diff --git a/src/add-ons/kernel/busses/ide/legacy_sata/Jamfile b/src/add-ons/kernel/busses/ide/legacy_sata/Jamfile new file mode 100644 index 0000000000..f87d486d94 --- /dev/null +++ b/src/add-ons/kernel/busses/ide/legacy_sata/Jamfile @@ -0,0 +1,8 @@ +SubDir HAIKU_TOP src add-ons kernel busses ide legacy_sata ; + +UsePrivateHeaders kernel ; + +KernelAddon legacy_sata : + legacy_sata.c + ; + diff --git a/src/add-ons/kernel/busses/ide/legacy_sata/legacy_sata.c b/src/add-ons/kernel/busses/ide/legacy_sata/legacy_sata.c new file mode 100644 index 0000000000..2e405be3c5 --- /dev/null +++ b/src/add-ons/kernel/busses/ide/legacy_sata/legacy_sata.c @@ -0,0 +1,389 @@ +/* + * Copyright 2007, Ithamar R. Adema. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#include +#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/device_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 ID(v,d) (((v)<< 16) | (d)) + +/* XXX: To be moved to PCI.h */ +#define PCI_command_interrupt 0x400 + +static ide_for_controller_interface* ide; +static ide_adapter_interface* ide_adapter; +static device_manager_info* dm; + +static float +controller_supports(device_node_handle parent, bool *_noConnection) +{ + uint16 vendor_id; + uint16 device_id; + status_t res; + char *bus; + + // get the bus (should be PCI) + if (dm->get_attr_string(parent, B_DRIVER_BUS, &bus, false) != B_OK) + return B_ERROR; + if (strcmp(bus, "pci") != 0) { + free(bus); + return B_ERROR; + } + free(bus); + + // get vendor and device ID + if ((res=dm->get_attr_uint16(parent, PCI_DEVICE_VENDOR_ID_ITEM, &vendor_id, false)) != B_OK + || (res=dm->get_attr_uint16(parent, PCI_DEVICE_DEVICE_ID_ITEM, &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; + + 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_handle parent) +{ + device_node_handle controller_node; + device_node_handle channels[2]; + uint16 command_block_base[2]; + uint16 control_block_base[2]; + pci_device_module_info *pci; + uint8 num_channels = 2; + uint32 bus_master_base; + pci_device device; + uint16 device_id; + uint16 vendor_id; + uint8 int_num; + status_t res; + + TRACE("controller_probe\n"); + + if ((res=dm->init_driver(parent, NULL, (driver_module_info **)&pci, (void **)&device)) != B_OK) + return res; + + 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); + + /* enable PCI interrupt */ + pci->write_pci_config(device, PCI_command, + pci->read_pci_config(device, PCI_command, 2) & ~PCI_command_interrupt, 2); + + + 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[3] = pci->read_pci_config(device, PCI_base_registers + 0, 4 ) + 8; + control_block_base[3] = pci->read_pci_config(device, PCI_base_registers + 4, 4) + 4; + command_block_base[4] = pci->read_pci_config(device, PCI_base_registers + 8, 4) + 8; + control_block_base[4] = pci->read_pci_config(device, PCI_base_registers + 12, 4) + 4; + /* Intentional fall-through */ + + default: + /* 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); + } + + 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 + ide_adapter->detect_channel(pci, device, controller_node, CHANNEL_MODULE_NAME, + true, command_block_base[0], control_block_base[0], bus_master_base, + int_num, true, "Primary Channel", &channels[0], false); + + ide_adapter->detect_channel(pci, device, controller_node, CHANNEL_MODULE_NAME, + true, command_block_base[1], control_block_base[1], bus_master_base, + int_num, false, "Secondary Channel", &channels[1], false); + + if (num_channels == 4) { + ide_adapter->detect_channel(pci, device, controller_node, CHANNEL_MODULE_NAME, + true, command_block_base[2], control_block_base[2], bus_master_base, + int_num, true, "Tertiary Channel", &channels[2], false); + + ide_adapter->detect_channel(pci, device, controller_node, CHANNEL_MODULE_NAME, + true, command_block_base[3], control_block_base[3], bus_master_base, + int_num, false, "Quaternary Channel", &channels[3], false); + } + + dm->uninit_driver(parent); + + TRACE("controller_probe success\n"); + return B_OK; + +err: + dm->uninit_driver(parent); + TRACE("controller_probe failed (%s)\n", strerror(res)); + return res; +} + + + +static status_t +controller_init(device_node_handle node, void *user_cookie, void **controller_cookie) +{ + return ide_adapter->init_controller( + node,user_cookie, + (ide_adapter_controller_info**)controller_cookie, + sizeof(ide_adapter_controller_info)); +} + + +static status_t +controller_uninit(void *controller_cookie) +{ + return ide_adapter->uninit_controller(controller_cookie); +} + +static void +controller_removed(device_node_handle node, void *controller_cookie) +{ + ide_adapter->controller_removed( + node, + (ide_adapter_controller_info*)controller_cookie); +} + + +static void +controller_get_paths(const char ***_bus, const char ***_device) +{ + static const char *kBus[] = { "pci", NULL }; + static const char *kDevice[] = { "drivers/dev/disk/sata", NULL }; + + *_bus = kBus; + *_device = kDevice; +} + + +static status_t +channel_init(device_node_handle node, void *user_cookie, void **channel_cookie) +{ + return ide_adapter->init_channel( + node, + user_cookie, + (ide_adapter_channel_info**)channel_cookie, + sizeof(ide_adapter_channel_info), + ide_adapter->inthand); +} + + +static status_t +channel_uninit(void *channel_cookie) +{ + return ide_adapter->uninit_channel(channel_cookie); +} + + +static void +channel_removed(device_node_handle node, void *channel_cookie) +{ + ide_adapter->channel_removed(node,channel_cookie); +} + + +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); +} + + +static status_t +std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + return B_OK; + 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 **)&dm }, + { IDE_ADAPTER_MODULE_NAME, (module_info **)&ide_adapter }, + {} +}; + +static ide_controller_interface channel_interface = { + { + { + CHANNEL_MODULE_NAME, + 0, + std_ops + }, + + .supports_device = NULL, + .register_device = NULL, + .init_driver = channel_init, + .uninit_driver = channel_uninit, + .device_removed = channel_removed, + .device_cleanup = NULL, + .get_supported_paths = NULL, + }, + + .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 controller_interface = { + { + CONTROLLER_MODULE_NAME, + 0, + std_ops + }, + + .supports_device = controller_supports, + .register_device = controller_probe, + .init_driver = controller_init, + .uninit_driver = controller_uninit, + .device_removed = controller_removed, + .device_cleanup = NULL, + .get_supported_paths = controller_get_paths, +}; + +module_info *modules[] = { + (module_info *)&controller_interface, + (module_info *)&channel_interface, + NULL +};