diff options
Diffstat (limited to 'roms/openbios/drivers/lsi.c')
-rw-r--r-- | roms/openbios/drivers/lsi.c | 775 |
1 files changed, 775 insertions, 0 deletions
diff --git a/roms/openbios/drivers/lsi.c b/roms/openbios/drivers/lsi.c new file mode 100644 index 000000000..51a541c4f --- /dev/null +++ b/roms/openbios/drivers/lsi.c @@ -0,0 +1,775 @@ +/* + * 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 sd_private_t; +typedef struct lsi_table lsi_table_t; +typedef struct lsi_private lsi_private_t; + +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]; + lsi_private_t *lsi; +}; + +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; +}; + +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]; +}; + +#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; + lsi_private_t *lsi = (*sd)->lsi; + + 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(lsi, *sd, sect_offset)) { + DPRINTF("ob_sd_read_blocks: error\n"); + RET(0); + } + while (n && pos < spb * 512) { + memcpy(dest, (uint8_t *)&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; + phandle_t ph; + + PUSH(find_ih_method("sd-private", my_self())); + fword("execute"); + *sd = cell2pointer(POP()); + +#ifdef CONFIG_DEBUG_LSI + { + char *args; + + fword("my-args"); + args = pop_fstr_copy(); + DPRINTF("opening drive args %s\n", 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) +{ + phandle_t aliases; + + DPRINTF("add_alias dev \"%s\" = alias \"%s\"\n", device, alias); + + aliases = find_dev("/aliases"); + set_property(aliases, alias, device, strlen(device) + 1); +} + +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; + + BIND_NODE_METHODS(ph, ob_lsi); + + lsi = malloc(sizeof(lsi_private_t)); + if (!lsi) { + DPRINTF("Can't allocate LSI private structure\n"); + return -1; + } + + /* Buffer for commands */ + 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); + + /* 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; + + lsi->sd[id].lsi = lsi; + + 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"); + + PUSH(pointer2cell(&lsi->sd[id])); + feval("value sd-private"); + + BIND_NODE_METHODS(get_cur_dev(), ob_sd); + fword("finish-device"); + + snprintf(nodebuff, sizeof(nodebuff), "%s/sd@%d", + get_path_from_ph(ph), id); + + 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; +} |