This is to enable booting PReP machines in OpenBIOS.
Signed-off-by: Mark Cave-Ayland mark.cave-ayland@ilande.co.uk --- config/examples/ppc_config.xml | 1 + drivers/build.xml | 1 + drivers/lsi.c | 770 +++++++++++++++++++++++++++++++++++++++++ drivers/pci.c | 19 + drivers/pci_database.c | 8 + drivers/pci_database.h | 1 + include/drivers/drivers.h | 4 + include/drivers/pci.h | 3 + 8 files changed, 807 insertions(+) create mode 100644 drivers/lsi.c
diff --git a/config/examples/ppc_config.xml b/config/examples/ppc_config.xml index 4c14eb6..0ac3817 100644 --- a/config/examples/ppc_config.xml +++ b/config/examples/ppc_config.xml @@ -83,3 +83,4 @@ <option name="CONFIG_DRIVER_USB" type="boolean" value="true"/> <option name="CONFIG_DEBUG_USB" type="boolean" value="false"/> <option name="CONFIG_USB_HID" type="boolean" value="true"/> + <option name="CONFIG_DRIVER_LSI_53C810" type="boolean" value="true"/> diff --git a/drivers/build.xml b/drivers/build.xml index bd1abd3..de84e38 100644 --- a/drivers/build.xml +++ b/drivers/build.xml @@ -26,6 +26,7 @@ <object source="usbhid.c" condition="USB_HID"/> <object source="usbohci.c" condition="DRIVER_USB"/> <object source="usbohci_rh.c" condition="DRIVER_USB"/> + <object source="lsi.c" condition="DRIVER_LSI_53C810"/> </library>
<dictionary name="openbios" target="forth"> diff --git a/drivers/lsi.c b/drivers/lsi.c new file mode 100644 index 0000000..1f3f038 --- /dev/null +++ b/drivers/lsi.c @@ -0,0 +1,770 @@ +/* + * OpenBIOS LSI driver + * + * Copyright (C) 2018 Mark Cave-Ayland mark.cave-ayland@ilande.co.uk + * + * Based upon drivers/esp.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 + * + */ + +#include "config.h" +#include "libc/byteorder.h" +#include "libc/vsprintf.h" +#include "libopenbios/bindings.h" +#include "drivers/drivers.h" +#include "scsi.h" + +typedef struct sd_private { + unsigned int bs; + const char *media_str[2]; + uint32_t sectors; + uint8_t media; + uint8_t id; + uint8_t present; + char model[40]; +} sd_private_t; + +typedef struct lsi_table { + uint32_t id; + uint32_t id_addr; + uint32_t msg_out_len; + uint32_t msg_out_ptr; + uint32_t cmd_len; + uint32_t cmd_ptr; + uint32_t data_in_len; + uint32_t data_in_ptr; + uint32_t status_len; + uint32_t status_ptr; + uint32_t msg_in_len; + uint32_t msg_in_ptr; +} lsi_table_t; + +typedef struct lsi_private { + volatile uint8_t *mmio; + uint32_t *scripts; + uint32_t *scripts_iova; + lsi_table_t *table; + lsi_table_t *table_iova; + volatile uint8_t *buffer; + volatile uint8_t *buffer_iova; + sd_private_t sd[8]; +} lsi_private_t; + +static lsi_private_t *global_lsi; + +#ifdef CONFIG_DEBUG_LSI +#define DPRINTF(fmt, args...) \ + do { printk(fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) +#endif + +/* DECLARE data structures for the nodes. */ +DECLARE_UNNAMED_NODE(ob_sd, INSTALL_OPEN, sizeof(sd_private_t *)); +DECLARE_UNNAMED_NODE(ob_lsi, INSTALL_OPEN, sizeof(lsi_private_t **)); + +#ifdef CONFIG_DEBUG_LSI +static void dump_drive(sd_private_t *drive) +{ + printk("SCSI DRIVE @%lx:\n", (unsigned long)drive); + printk("id: %d\n", drive->id); + printk("media: %s\n", drive->media_str[0]); + printk("media: %s\n", drive->media_str[1]); + printk("model: %s\n", drive->model); + printk("sectors: %d\n", drive->sectors); + printk("present: %d\n", drive->present); + printk("bs: %d\n", drive->bs); +} +#endif + +#define PHASE_DO 0 +#define PHASE_DI 1 +#define PHASE_CMD 2 +#define PHASE_ST 3 +#define PHASE_MO 6 +#define PHASE_MI 7 + +#define LSI_DSTAT 0x0c +#define LSI_DSA 0x10 +#define LSI_ISTAT0 0x14 +#define LSI_DSP 0x2c +#define LSI_SIST0 0x42 +#define LSI_SIST1 0x43 + +#define LSI_ISTAT0_DIP 0x01 +#define LSI_ISTAT0_SIP 0x02 + +/* Indirection table */ +#define LSI_TABLE_OFFSET(x) (((uintptr_t)&(x)) - ((uintptr_t)lsi->table)) + +#define LSI_TABLE_MSG_OUT_OFFSET 0x0 +#define LSI_TABLE_CMD_OFFSET 0x2 +#define LSI_TABLE_DATA_OFFSET 0x20 +#define LSI_TABLE_STATUS_OFFSET 0x10 +#define LSI_TABLE_MSG_IN_OFFSET 0x12 + +static void +init_scripts(lsi_private_t *lsi) +{ + /* Initialise SCRIPTS for the commands we are interested in */ + + /* 1 - INQUIRY / READ CAPACITY */ + + /* 1.0 Select with ATN */ + lsi->scripts[0x0] = __cpu_to_le32(0x47000000); + lsi->scripts[0x1] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->id)); + + /* 1.1 Select LUN */ + lsi->scripts[0x2] = __cpu_to_le32(0x10000000 | (PHASE_MO << 24)); + lsi->scripts[0x3] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->msg_out_len)); + + /* 1.2 Send command */ + lsi->scripts[0x4] = __cpu_to_le32(0x10000000 | (PHASE_CMD << 24)); + lsi->scripts[0x5] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->cmd_len)); + + /* 1.3 Data in */ + lsi->scripts[0x6] = __cpu_to_le32(0x10000000 | (PHASE_DI << 24)); + lsi->scripts[0x7] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->data_in_len)); + + /* 1.4 Status */ + lsi->scripts[0x8] = __cpu_to_le32(0x10000000 | (PHASE_ST << 24)); + lsi->scripts[0x9] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->status_len)); + + /* 1.5 Message in */ + lsi->scripts[0xa] = __cpu_to_le32(0x10000000 | (PHASE_MI << 24)); + lsi->scripts[0xb] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->msg_in_len)); + + /* 1.6 Wait disconnect */ + lsi->scripts[0xc] = __cpu_to_le32(0x48000000); + lsi->scripts[0xd] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->id)); + + /* 1.7 Interrupt */ + lsi->scripts[0xe] = __cpu_to_le32(0x98080000); + lsi->scripts[0xf] = 0x0; + + + /* 2 - TEST UNIT READY */ + + /* 2.0 Select with ATN */ + lsi->scripts[0x10] = __cpu_to_le32(0x47000000); + lsi->scripts[0x11] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->id)); + + /* 2.1 Select LUN */ + lsi->scripts[0x12] = __cpu_to_le32(0x10000000 | (PHASE_MO << 24)); + lsi->scripts[0x13] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->msg_out_len)); + + /* 2.2 Send command */ + lsi->scripts[0x14] = __cpu_to_le32(0x10000000 | (PHASE_CMD << 24)); + lsi->scripts[0x15] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->cmd_len)); + + /* 2.3 Status */ + lsi->scripts[0x16] = __cpu_to_le32(0x10000000 | (PHASE_ST << 24)); + lsi->scripts[0x17] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->status_len)); + + /* 2.4 Message in */ + lsi->scripts[0x18] = __cpu_to_le32(0x10000000 | (PHASE_MI << 24)); + lsi->scripts[0x19] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->msg_in_len)); + + /* 2.5 Wait disconnect */ + lsi->scripts[0x1a] = __cpu_to_le32(0x48000000); + lsi->scripts[0x1b] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->id)); + + /* 2.6 Interrupt */ + lsi->scripts[0x1c] = __cpu_to_le32(0x98080000); + lsi->scripts[0x1d] = 0x0; + + + /* 3 - READ 10 */ + + /* 3.0 Select with ATN */ + lsi->scripts[0x20] = __cpu_to_le32(0x47000000); + lsi->scripts[0x21] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->id)); + + /* 3.1 Select LUN */ + lsi->scripts[0x22] = __cpu_to_le32(0x10000000 | (PHASE_MO << 24)); + lsi->scripts[0x23] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->msg_out_len)); + + /* 3.2 Send command */ + lsi->scripts[0x24] = __cpu_to_le32(0x10000000 | (PHASE_CMD << 24)); + lsi->scripts[0x25] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->cmd_len)); + + /* 3.3 Message in */ + lsi->scripts[0x26] = __cpu_to_le32(0x10000000 | (PHASE_MI << 24)); + lsi->scripts[0x27] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->msg_in_len)); + + /* 3.6 Interrupt */ + lsi->scripts[0x28] = __cpu_to_le32(0x98080000); + lsi->scripts[0x29] = 0x0; + + /* 3.7 Wait reselect */ + lsi->scripts[0x2a] = __cpu_to_le32(0x50000000); + lsi->scripts[0x2b] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->id)); + + /* 3.8 Message in */ + lsi->scripts[0x2c] = __cpu_to_le32(0x10000000 | (PHASE_MI << 24)); + lsi->scripts[0x2d] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->msg_in_len)); + + /* 3.9 Data in */ + lsi->scripts[0x2e] = __cpu_to_le32(0x10000000 | (PHASE_DI << 24)); + lsi->scripts[0x2f] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->data_in_len)); + + /* 3.10 Wait disconnect */ + lsi->scripts[0x30] = __cpu_to_le32(0x48000000); + lsi->scripts[0x31] = __cpu_to_le32(LSI_TABLE_OFFSET(lsi->table->id)); + + /* 3.11 Interrupt */ + lsi->scripts[0x32] = __cpu_to_le32(0x98080000); + lsi->scripts[0x33] = 0x0; +} + +static void +init_table(lsi_private_t *lsi) +{ + uint32_t dsa; + + /* Initialise indirect table */ + lsi->table->msg_out_ptr = __cpu_to_le32((uintptr_t)&lsi->buffer_iova[LSI_TABLE_MSG_OUT_OFFSET]); + lsi->table->cmd_ptr = __cpu_to_le32((uintptr_t)&lsi->buffer_iova[LSI_TABLE_CMD_OFFSET]); + lsi->table->data_in_ptr = __cpu_to_le32((uintptr_t)&lsi->buffer_iova[LSI_TABLE_DATA_OFFSET]); + lsi->table->status_ptr = __cpu_to_le32((uintptr_t)&lsi->buffer_iova[LSI_TABLE_STATUS_OFFSET]); + lsi->table->msg_in_ptr = __cpu_to_le32((uintptr_t)&lsi->buffer_iova[LSI_TABLE_MSG_IN_OFFSET]); + + /* Set the DSA to point to the base of our data table */ + dsa = (uintptr_t)lsi->table_iova; + lsi->mmio[LSI_DSA] = dsa & 0xff; + lsi->mmio[LSI_DSA + 1] = (dsa >> 8) & 0xff; + lsi->mmio[LSI_DSA + 2] = (dsa >> 16) & 0xff; + lsi->mmio[LSI_DSA + 3] = (dsa >> 24) & 0xff; +} + +static unsigned int +lsi_interrupt_status(lsi_private_t *lsi) +{ + uint32_t istat, sist0, sist1, dstat; + + /* Wait for interrupt status */ + while ((istat = lsi->mmio[LSI_ISTAT0]) == 0); + + if (istat & LSI_ISTAT0_SIP) { + /* If SCSI interrupt, clear SCSI interrupt registers */ + sist0 = lsi->mmio[LSI_SIST0]; + sist1 = lsi->mmio[LSI_SIST1]; + + if (sist0 != 0 || sist1 != 0) { + return 1; + } + } + + if (istat & LSI_ISTAT0_DIP) { + /* If DMA interrupt, clear DMA interrupt register */ + dstat = lsi->mmio[LSI_DSTAT]; + + if ((dstat & 0x7f) != 0x4) { + return 1; + } + } + + return 0; +} + +static unsigned int +inquiry(lsi_private_t *lsi, sd_private_t *sd) +{ + const char *media[2] = { "UNKNOWN", "UNKNOWN"}; + uint8_t *buffer; + + // Setup command = Inquiry + memset((uint8_t *)&lsi->buffer[LSI_TABLE_CMD_OFFSET], 0, 7); + lsi->buffer[LSI_TABLE_MSG_OUT_OFFSET] = 0x80; + lsi->table->msg_out_len = __cpu_to_le32(0x1); + + lsi->buffer[LSI_TABLE_CMD_OFFSET] = INQUIRY; + lsi->table->cmd_len = __cpu_to_le32(0x6); + + lsi->buffer[LSI_TABLE_CMD_OFFSET + 4] = 36; + lsi->table->data_in_len = __cpu_to_le32(36); + + lsi->table->status_len = __cpu_to_le32(0x1); + lsi->table->msg_in_len = __cpu_to_le32(0x1); + + lsi->table->id = __cpu_to_le32((sd->id << 16)); + lsi->table->id_addr = __cpu_to_le32(&lsi->scripts_iova[0x2]); + + /* Write DSP to start DMA engine */ + uint32_t dsp = (uintptr_t)lsi->scripts_iova; + lsi->mmio[LSI_DSP] = dsp & 0xff; + lsi->mmio[LSI_DSP + 1] = (dsp >> 8) & 0xff; + lsi->mmio[LSI_DSP + 2] = (dsp >> 16) & 0xff; + lsi->mmio[LSI_DSP + 3] = (dsp >> 24) & 0xff; + + if (lsi_interrupt_status(lsi)) { + sd->present = 0; + sd->media = -1; + return 0; + } + + buffer = (uint8_t *)&lsi->buffer[LSI_TABLE_DATA_OFFSET]; + sd->present = 1; + sd->media = buffer[0]; + + switch (sd->media) { + case TYPE_DISK: + media[0] = "disk"; + media[1] = "hd"; + break; + case TYPE_ROM: + media[0] = "cdrom"; + media[1] = "cd"; + break; + } + sd->media_str[0] = media[0]; + sd->media_str[1] = media[1]; + memcpy(sd->model, &buffer[16], 16); + sd->model[17] = '\0'; + + return 1; +} + +static unsigned int +read_capacity(lsi_private_t *lsi, sd_private_t *sd) +{ + uint8_t *buffer; + + // Setup command = Read Capacity + memset((uint8_t *)&lsi->buffer[LSI_TABLE_CMD_OFFSET], 0, 11); + lsi->buffer[LSI_TABLE_MSG_OUT_OFFSET] = 0x80; + lsi->table->msg_out_len = __cpu_to_le32(0x1); + + lsi->buffer[LSI_TABLE_CMD_OFFSET] = READ_CAPACITY; + lsi->table->cmd_len = __cpu_to_le32(0x11); + + lsi->table->data_in_len = __cpu_to_le32(0x8); + + lsi->table->status_len = __cpu_to_le32(0x1); + lsi->table->msg_in_len = __cpu_to_le32(0x1); + + lsi->table->id = __cpu_to_le32((sd->id << 16)); + lsi->table->id_addr = __cpu_to_le32(&lsi->scripts_iova[0x2]); + + /* Write DSP to start DMA engine */ + uint32_t dsp = (uintptr_t)lsi->scripts_iova; + lsi->mmio[LSI_DSP] = dsp & 0xff; + lsi->mmio[LSI_DSP + 1] = (dsp >> 8) & 0xff; + lsi->mmio[LSI_DSP + 2] = (dsp >> 16) & 0xff; + lsi->mmio[LSI_DSP + 3] = (dsp >> 24) & 0xff; + + if (lsi_interrupt_status(lsi)) { + sd->sectors = 0; + sd->bs = 0; + DPRINTF("read_capacity id %d failed\n", sd->id); + return 0; + } + + buffer = (uint8_t *)&lsi->buffer[LSI_TABLE_DATA_OFFSET]; + sd->bs = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; + sd->sectors = ((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]) * (sd->bs / 512); + + DPRINTF("read_capacity id %d bs %d sectors %d\n", sd->id, sd->bs, + sd->sectors); + return 1; +} + +static unsigned int +test_unit_ready(lsi_private_t *lsi, sd_private_t *sd) +{ + /* Setup command = Test Unit Ready */ + memset((uint8_t *)&lsi->buffer[LSI_TABLE_CMD_OFFSET], 0, 7); + lsi->buffer[LSI_TABLE_MSG_OUT_OFFSET] = 0x80; + lsi->table->msg_out_len = __cpu_to_le32(0x1); + + lsi->buffer[LSI_TABLE_CMD_OFFSET] = TEST_UNIT_READY; + lsi->table->cmd_len = __cpu_to_le32(0x6); + + lsi->table->status_len = __cpu_to_le32(0x1); + lsi->table->msg_in_len = __cpu_to_le32(0x1); + + lsi->table->id = __cpu_to_le32((sd->id << 16)); + lsi->table->id_addr = __cpu_to_le32(&lsi->scripts_iova[0x12]); + + /* Write DSP to start DMA engine */ + uint32_t dsp = (uintptr_t)&lsi->scripts_iova[0x10]; + lsi->mmio[LSI_DSP] = dsp & 0xff; + lsi->mmio[LSI_DSP + 1] = (dsp >> 8) & 0xff; + lsi->mmio[LSI_DSP + 2] = (dsp >> 16) & 0xff; + lsi->mmio[LSI_DSP + 3] = (dsp >> 24) & 0xff; + + if (lsi_interrupt_status(lsi)) { + DPRINTF("test_unit_ready id %d failed\n", sd->id); + return 0; + } + + DPRINTF("test_unit_ready id %d success\n", sd->id); + return 1; +} + +static void +ob_lsi_dma_alloc(__attribute__((unused)) lsi_private_t **lsi) +{ + call_parent_method("dma-alloc"); +} + +static void +ob_lsi_dma_free(__attribute__((unused)) lsi_private_t **lsi) +{ + call_parent_method("dma-free"); +} + +static void +ob_lsi_dma_map_in(__attribute__((unused)) lsi_private_t **lsi) +{ + call_parent_method("dma-map-in"); +} + +static void +ob_lsi_dma_map_out(__attribute__((unused)) lsi_private_t **lsi) +{ + call_parent_method("dma-map-out"); +} + +static void +ob_lsi_dma_sync(__attribute__((unused)) lsi_private_t **lsi) +{ + call_parent_method("dma-sync"); +} + +// offset is in sectors +static int +ob_sd_read_sector(lsi_private_t *lsi, sd_private_t *sd, int offset) +{ + uint32_t dsp; + + DPRINTF("ob_sd_read_sector id %d sector=%d\n", + sd->id, offset); + + // Setup command = Read(10) + memset((uint8_t *)&lsi->buffer[LSI_TABLE_CMD_OFFSET], 0, 10); + lsi->buffer[LSI_TABLE_MSG_OUT_OFFSET] = 0x80; + lsi->table->msg_out_len = __cpu_to_le32(0x1); + + lsi->buffer[LSI_TABLE_CMD_OFFSET] = READ_10; + lsi->buffer[LSI_TABLE_CMD_OFFSET + 2] = (offset >> 24) & 0xff; + lsi->buffer[LSI_TABLE_CMD_OFFSET + 3] = (offset >> 16) & 0xff;; + lsi->buffer[LSI_TABLE_CMD_OFFSET + 4] = (offset >> 8) & 0xff; + lsi->buffer[LSI_TABLE_CMD_OFFSET + 5] = offset & 0xff; + lsi->buffer[LSI_TABLE_CMD_OFFSET + 7] = 0; + lsi->buffer[LSI_TABLE_CMD_OFFSET + 8] = 1; + lsi->table->cmd_len = __cpu_to_le32(0xa); + + lsi->table->data_in_len = __cpu_to_le32(sd->bs); + + lsi->table->status_len = __cpu_to_le32(0x1); + lsi->table->msg_in_len = __cpu_to_le32(0x2); + + lsi->table->id = __cpu_to_le32((sd->id << 16)); + lsi->table->id_addr = __cpu_to_le32(&lsi->scripts_iova[0x22]); + + /* Write DSP to start DMA engine */ + dsp = (uintptr_t)&lsi->scripts_iova[0x20]; + lsi->mmio[LSI_DSP] = dsp & 0xff; + lsi->mmio[LSI_DSP + 1] = (dsp >> 8) & 0xff; + lsi->mmio[LSI_DSP + 2] = (dsp >> 16) & 0xff; + lsi->mmio[LSI_DSP + 3] = (dsp >> 24) & 0xff; + + if (lsi_interrupt_status(lsi)) { + return 1; + } + + // Reslect and data transfer + lsi->table->msg_in_len = __cpu_to_le32(0x1); + + lsi->table->data_in_len = __cpu_to_le32(sd->bs); + + /* Write DSP to start DMA engine */ + dsp = (uintptr_t)&lsi->scripts_iova[0x2a]; + lsi->mmio[LSI_DSP] = dsp & 0xff; + lsi->mmio[LSI_DSP + 1] = (dsp >> 8) & 0xff; + lsi->mmio[LSI_DSP + 2] = (dsp >> 16) & 0xff; + lsi->mmio[LSI_DSP + 3] = (dsp >> 24) & 0xff; + + if (lsi_interrupt_status(lsi)) { + return 1; + } + + return 0; +} + +static void +ob_sd_read_blocks(sd_private_t **sd) +{ + cell n = POP(), cnt = n; + ucell blk = POP(); + char *dest = (char*)POP(); + int pos, spb, sect_offset; + + DPRINTF("ob_sd_read_blocks id %d %lx block=%d n=%d\n", (*sd)->id, (unsigned long)dest, blk, n ); + + if ((*sd)->bs == 0) { + PUSH(0); + return; + } + spb = (*sd)->bs / 512; + while (n) { + sect_offset = blk / spb; + pos = (blk - sect_offset * spb) * 512; + + if (ob_sd_read_sector(global_lsi, *sd, sect_offset)) { + DPRINTF("ob_sd_read_blocks: error\n"); + RET(0); + } + while (n && pos < spb * 512) { + memcpy(dest, (uint8_t *)&global_lsi->buffer[LSI_TABLE_DATA_OFFSET] + pos, 512); + pos += 512; + dest += 512; + n--; + blk++; + } + } + PUSH(cnt); +} + +static void +ob_sd_block_size(__attribute__((unused))sd_private_t **sd) +{ + PUSH(512); +} + +static void +ob_sd_open(__attribute__((unused))sd_private_t **sd) +{ + int ret = 1, id; + phandle_t ph; + + fword("my-unit"); + id = POP(); + *sd = &global_lsi->sd[id]; + +#ifdef CONFIG_DEBUG_LSI + { + char *args; + + fword("my-args"); + args = pop_fstr_copy(); + DPRINTF("opening drive %d args %s\n", id, args); + free(args); + } +#endif + + selfword("open-deblocker"); + + /* interpose disk-label */ + ph = find_dev("/packages/disk-label"); + fword("my-args"); + PUSH_ph( ph ); + fword("interpose"); + + RET ( -ret ); +} + +static void +ob_sd_close(__attribute__((unused)) sd_private_t **sd) +{ + selfword("close-deblocker"); +} + +NODE_METHODS(ob_sd) = { + { "open", ob_sd_open }, + { "close", ob_sd_close }, + { "read-blocks", ob_sd_read_blocks }, + { "block-size", ob_sd_block_size }, +}; + +static void +ob_lsi_decodeunit(__attribute__((unused)) lsi_private_t **lsi_p) +{ + /* ( str len -- id ) */ + fword("parse-hex"); +} + +static void +ob_lsi_encodeunit(__attribute__((unused)) lsi_private_t **lsi_p) +{ + /* ( id -- str len ) */ + fword("pocket"); + fword("tohexstr"); +} + +static void +ob_lsi_open(__attribute__((unused)) lsi_private_t **lsi_p) +{ + PUSH(-1); +} + +static void +ob_lsi_close(__attribute__((unused)) lsi_private_t **lsi_p) +{ + return; +} + +NODE_METHODS(ob_lsi) = { + { "open" , ob_lsi_open }, + { "close" , ob_lsi_close }, + { "decode-unit", ob_lsi_decodeunit }, + { "encode-unit", ob_lsi_encodeunit }, + { "dma-alloc", ob_lsi_dma_alloc }, + { "dma-free", ob_lsi_dma_free }, + { "dma-map-in", ob_lsi_dma_map_in }, + { "dma-map-out", ob_lsi_dma_map_out }, + { "dma-sync", ob_lsi_dma_sync }, +}; + +static void +add_alias(const char *device, const char *alias) +{ + DPRINTF("add_alias dev "%s" = alias "%s"\n", device, alias); + push_str("/aliases"); + fword("find-device"); + push_str(device); + fword("encode-string"); + push_str(alias); + fword("property"); +} + +int +ob_lsi_init(const char *path, uint64_t mmio, uint64_t ram) +{ + int id, diskcount = 0, cdcount = 0, *counter_ptr; + char nodebuff[256], aliasbuff[256]; + phandle_t ph = get_cur_dev(); + lsi_private_t *lsi; + int i; + ucell addr; + + REGISTER_NODE_METHODS(ob_lsi, path); + + lsi = malloc(sizeof(lsi_private_t)); + if (!lsi) { + DPRINTF("Can't allocate LSI private structure\n"); + return -1; + } + + global_lsi = lsi; + + /* Buffer for commands */ + fword("my-self"); + push_str(path); + feval("open-dev to my-self"); + + PUSH(0x1000); + feval("dma-alloc"); + addr = POP(); + lsi->buffer = cell2pointer(addr); + + PUSH(addr); + PUSH(0x1000); + PUSH(0); + feval("dma-map-in"); + addr = POP(); + lsi->buffer_iova = cell2pointer(addr); + + PUSH(0x40 * sizeof(uint32_t)); + feval("dma-alloc"); + addr = POP(); + lsi->scripts = cell2pointer(addr); + + PUSH(addr); + PUSH(0x40 * sizeof(uint32_t)); + PUSH(0); + feval("dma-map-in"); + addr = POP(); + lsi->scripts_iova = cell2pointer(addr); + + PUSH(sizeof(lsi_table_t)); + feval("dma-alloc"); + addr = POP(); + lsi->table = cell2pointer(addr); + + PUSH(addr); + PUSH(sizeof(lsi_table_t)); + PUSH(0); + feval("dma-map-in"); + addr = POP(); + lsi->table_iova = cell2pointer(addr); + + set_int_property(ph, "#address-cells", 1); + set_int_property(ph, "#size-cells", 0); + feval("to my-self"); + + /* Initialise SCRIPTS */ + lsi->mmio = (uint8_t *)(uint32_t)mmio; + init_scripts(lsi); + init_table(lsi); + + /* Scan the SCSI bus */ + for (id = 0; id < 8; id++) { + lsi->sd[id].id = id; + if (!inquiry(lsi, &lsi->sd[id])) { + DPRINTF("Unit %d not present\n", id); + continue; + } + + /* Clear Unit Attention condition from reset */ + for (i = 0; i < 5; i++) { + if (test_unit_ready(lsi, &lsi->sd[id])) { + break; + } + } + if (i == 5) { + DPRINTF("Unit %d present but won't become ready\n", id); + continue; + } + DPRINTF("Unit %d present\n", id); + read_capacity(lsi, &lsi->sd[id]); + +#ifdef CONFIG_DEBUG_LSI + dump_drive(&lsi->sd[id]); +#endif + } + + for (id = 0; id < 8; id++) { + if (!lsi->sd[id].present) + continue; + fword("new-device"); + push_str("sd"); + fword("device-name"); + push_str("block"); + fword("device-type"); + fword("is-deblocker"); + PUSH(id); + fword("encode-int"); + PUSH(0); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); + fword("finish-device"); + snprintf(nodebuff, sizeof(nodebuff), "%s/sd@%d", + get_path_from_ph(ph), id); + REGISTER_NODE_METHODS(ob_sd, nodebuff); + if (lsi->sd[id].media == TYPE_ROM) { + counter_ptr = &cdcount; + } else { + counter_ptr = &diskcount; + } + if (*counter_ptr == 0) { + add_alias(nodebuff, lsi->sd[id].media_str[0]); + add_alias(nodebuff, lsi->sd[id].media_str[1]); + } + snprintf(aliasbuff, sizeof(aliasbuff), "%s%d", + lsi->sd[id].media_str[0], *counter_ptr); + add_alias(nodebuff, aliasbuff); + snprintf(aliasbuff, sizeof(aliasbuff), "%s%d", + lsi->sd[id].media_str[1], *counter_ptr); + add_alias(nodebuff, aliasbuff); + } + + return 0; +} diff --git a/drivers/pci.c b/drivers/pci.c index e48a791..f16aeb9 100644 --- a/drivers/pci.c +++ b/drivers/pci.c @@ -1159,6 +1159,25 @@ int usb_ohci_config_cb(const pci_config_t *config) return 0; }
+int lsi53c810_config_cb(const pci_config_t *config) +{ +#ifdef CONFIG_DRIVER_LSI_53C810 + uint64_t mmio, ram; + + /* Enable PCI bus mastering */ + ob_pci_enable_bus_master(config); + + /* Map PCI memory BAR 1: LSI MMIO */ + mmio = ob_pci_map(config->assigned[1], 0x400); + + /* Map PCI memory BAR 2: LSI RAM */ + ram = ob_pci_map(config->assigned[2], 0x400); + + ob_lsi_init(config->path, mmio, ram); +#endif + return 0; +} + void ob_pci_enable_bus_master(const pci_config_t *config) { /* Enable bus mastering for the PCI device */ diff --git a/drivers/pci_database.c b/drivers/pci_database.c index 43e59e0..8288acd 100644 --- a/drivers/pci_database.c +++ b/drivers/pci_database.c @@ -55,6 +55,14 @@ static const pci_dev_t scsi_devices[] = { NULL, NULL, }, { + /* lsi53c810 controller */ + PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C810, + NULL, "lsi53c810", NULL, + "pci1000,1\0", + 0, 0, 0, + lsi53c810_config_cb, NULL, + }, + { 0xFFFF, 0xFFFF, NULL, NULL, NULL, NULL, -1, -1, -1, diff --git a/drivers/pci_database.h b/drivers/pci_database.h index ac72284..6f5eb39 100644 --- a/drivers/pci_database.h +++ b/drivers/pci_database.h @@ -43,6 +43,7 @@ extern int usb_ohci_config_cb(const pci_config_t *config); extern int rtl8139_config_cb(const pci_config_t *config); extern int sungem_config_cb (const pci_config_t *config); extern int sunhme_config_cb(const pci_config_t *config); +extern int lsi53c810_config_cb(const pci_config_t *config);
static inline int pci_compat_len(const pci_dev_t *dev) { diff --git a/include/drivers/drivers.h b/include/drivers/drivers.h index 64824e4..38efcc8 100644 --- a/include/drivers/drivers.h +++ b/include/drivers/drivers.h @@ -60,6 +60,10 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels); int ob_esp_init(unsigned int slot, uint64_t base, unsigned long espoffset, unsigned long dmaoffset); #endif +#ifdef CONFIG_DRIVER_LSI_53C810 +/* drivers/lsi.c */ +int ob_lsi_init(const char *path, uint64_t mmio, uint64_t ram); +#endif #ifdef CONFIG_DRIVER_OBIO /* drivers/obio.c */ int ob_obio_init(uint64_t slavio_base, unsigned long fd_offset, diff --git a/include/drivers/pci.h b/include/drivers/pci.h index b70eb3a..fc7573a 100644 --- a/include/drivers/pci.h +++ b/include/drivers/pci.h @@ -182,6 +182,9 @@ extern const pci_arch_t *arch;
/* Vendors and devices. */
+#define PCI_VENDOR_ID_LSI_LOGIC 0x1000 +#define PCI_DEVICE_ID_LSI_53C810 0x0001 + #define PCI_VENDOR_ID_ATI 0x1002 #define PCI_DEVICE_ID_ATI_RAGE128_PF 0x5046