aboutsummaryrefslogtreecommitdiffstats
path: root/roms/openbios/drivers/lsi.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/openbios/drivers/lsi.c')
-rw-r--r--roms/openbios/drivers/lsi.c775
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;
+}