summaryrefslogtreecommitdiffstats
path: root/sys/dev/pst/pst-iop.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/pst/pst-iop.c')
-rw-r--r--sys/dev/pst/pst-iop.c499
1 files changed, 499 insertions, 0 deletions
diff --git a/sys/dev/pst/pst-iop.c b/sys/dev/pst/pst-iop.c
new file mode 100644
index 0000000..dd372ef
--- /dev/null
+++ b/sys/dev/pst/pst-iop.c
@@ -0,0 +1,499 @@
+/*-
+ * Copyright (c) 2001,2002,2003 Søren Schmidt <sos@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/bio.h>
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <machine/stdarg.h>
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <pci/pcivar.h>
+#include <pci/pcireg.h>
+
+#include "dev/pst/pst-iop.h"
+
+struct iop_request {
+ struct i2o_single_reply *reply;
+ u_int32_t mfa;
+};
+
+/* local vars */
+MALLOC_DEFINE(M_PSTIOP, "PSTIOP", "Promise SuperTrak IOP driver");
+
+int
+iop_init(struct iop_softc *sc)
+{
+ int mfa, timeout = 10000;
+
+ while ((mfa = sc->reg->iqueue) == 0xffffffff && --timeout)
+ DELAY(1000);
+ if (!timeout) {
+ printf("pstiop: no free mfa\n");
+ return 0;
+ }
+ iop_free_mfa(sc, mfa);
+
+ sc->reg->oqueue_intr_mask = 0xffffffff;
+
+ if (!iop_reset(sc)) {
+ printf("pstiop: no reset response\n");
+ return 0;
+ }
+
+ if (!iop_init_outqueue(sc)) {
+ printf("pstiop: init outbound queue failed\n");
+ return 0;
+ }
+
+ /* register iop_attach to be run when interrupts are enabled */
+ if (!(sc->iop_delayed_attach = (struct intr_config_hook *)
+ malloc(sizeof(struct intr_config_hook),
+ M_PSTIOP, M_NOWAIT | M_ZERO))) {
+ printf("pstiop: malloc of delayed attach hook failed\n");
+ return 0;
+ }
+ sc->iop_delayed_attach->ich_func = (void *)iop_attach;
+ sc->iop_delayed_attach->ich_arg = (void *)sc;
+ if (config_intrhook_establish(sc->iop_delayed_attach)) {
+ printf("pstiop: config_intrhook_establish failed\n");
+ free(sc->iop_delayed_attach, M_PSTIOP);
+ }
+ return 1;
+}
+
+void
+iop_attach(struct iop_softc *sc)
+{
+ int i;
+
+ if (sc->iop_delayed_attach) {
+ config_intrhook_disestablish(sc->iop_delayed_attach);
+ free(sc->iop_delayed_attach, M_PSTIOP);
+ sc->iop_delayed_attach = NULL;
+ }
+
+ if (!iop_get_lct(sc)) {
+ printf("pstiop: get LCT failed\n");
+ return;
+ }
+
+ /* figure out what devices are here and config as needed */
+ for (i = 0; sc->lct[i].entry_size == I2O_LCT_ENTRYSIZE; i++) {
+#ifdef PSTDEBUG
+ struct i2o_get_param_reply *reply;
+
+ printf("pstiop: LCT entry %d ", i);
+ printf("class=%04x ", sc->lct[i].class);
+ printf("sub=%04x ", sc->lct[i].sub_class);
+ printf("localtid=%04x ", sc->lct[i].local_tid);
+ printf("usertid=%04x ", sc->lct[i].user_tid);
+ printf("parentid=%04x\n", sc->lct[i].parent_tid);
+
+ if ((reply = iop_get_util_params(sc, sc->lct[i].local_tid,
+ I2O_PARAMS_OPERATION_FIELD_GET,
+ I2O_UTIL_DEVICE_IDENTITY_GROUP_NO))) {
+ struct i2o_device_identity *ident =
+ (struct i2o_device_identity *)reply->result;
+ printf("pstiop: vendor=<%.16s> product=<%.16s>\n",
+ ident->vendor, ident->product);
+ printf("pstiop: description=<%.16s> revision=<%.8s>\n",
+ ident->description, ident->revision);
+ contigfree(reply, PAGE_SIZE, M_PSTIOP);
+ }
+#endif
+
+ if (sc->lct[i].user_tid != I2O_TID_NONE &&
+ sc->lct[i].user_tid != I2O_TID_HOST)
+ continue;
+
+ switch (sc->lct[i].class) {
+ case I2O_CLASS_DDM:
+ if (sc->lct[i].sub_class == I2O_SUBCLASS_ISM)
+ sc->ism = sc->lct[i].local_tid;
+ break;
+
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ pst_add_raid(sc, &sc->lct[i]);
+ break;
+ }
+ }
+
+ /* setup and enable interrupts */
+ bus_setup_intr(sc->dev, sc->r_irq, INTR_TYPE_BIO|INTR_ENTROPY|INTR_MPSAFE,
+ iop_intr, sc, &sc->handle);
+ sc->reg->oqueue_intr_mask = 0x0;
+}
+
+void
+iop_intr(void *data)
+{
+ struct iop_softc *sc = (struct iop_softc *)data;
+ struct i2o_single_reply *reply;
+ u_int32_t mfa;
+
+ /* we might get more than one finished request pr interrupt */
+ mtx_lock(&sc->mtx);
+ while (1) {
+ if ((mfa = sc->reg->oqueue) == 0xffffffff)
+ if ((mfa = sc->reg->oqueue) == 0xffffffff)
+ break;
+
+ reply = (struct i2o_single_reply *)(sc->obase + (mfa - sc->phys_obase));
+
+ /* if this is an event register reply, shout! */
+ if (reply->function == I2O_UTIL_EVENT_REGISTER) {
+ struct i2o_util_event_reply_message *event =
+ (struct i2o_util_event_reply_message *)reply;
+
+ printf("pstiop: EVENT!! idx=%08x data=%08x\n",
+ event->event_mask, event->event_data[0]);
+ break;
+ }
+
+ /* if reply is a failurenotice we need to free the original mfa */
+ if (reply->message_flags & I2O_MESSAGE_FLAGS_FAIL)
+ iop_free_mfa(sc,((struct i2o_fault_reply *)(reply))->preserved_mfa);
+
+ /* reply->initiator_context points to the service routine */
+ ((void (*)(struct iop_softc *, u_int32_t, struct i2o_single_reply *))
+ (reply->initiator_context))(sc, mfa, reply);
+ }
+ mtx_unlock(&sc->mtx);
+}
+
+int
+iop_reset(struct iop_softc *sc)
+{
+ struct i2o_exec_iop_reset_message *msg;
+ int mfa, timeout = 5000;
+ volatile u_int32_t reply = 0;
+
+ mfa = iop_get_mfa(sc);
+ msg = (struct i2o_exec_iop_reset_message *)(sc->ibase + mfa);
+ bzero(msg, sizeof(struct i2o_exec_iop_reset_message));
+ msg->version_offset = 0x1;
+ msg->message_flags = 0x0;
+ msg->message_size = sizeof(struct i2o_exec_iop_reset_message) >> 2;
+ msg->target_address = I2O_TID_IOP;
+ msg->initiator_address = I2O_TID_HOST;
+ msg->function = I2O_EXEC_IOP_RESET;
+ msg->status_word_low_addr = vtophys(&reply);
+ msg->status_word_high_addr = 0;
+
+ sc->reg->iqueue = mfa;
+
+ while (--timeout && !reply)
+ DELAY(1000);
+
+ /* wait for iqueue ready */
+ timeout = 10000;
+ while ((mfa = sc->reg->iqueue) == 0xffffffff && --timeout)
+ DELAY(1000);
+
+ iop_free_mfa(sc, mfa);
+ return reply;
+}
+
+int
+iop_init_outqueue(struct iop_softc *sc)
+{
+ struct i2o_exec_init_outqueue_message *msg;
+ int i, mfa, timeout = 5000;
+ volatile u_int32_t reply = 0;
+
+ if (!(sc->obase = contigmalloc(I2O_IOP_OUTBOUND_FRAME_COUNT *
+ I2O_IOP_OUTBOUND_FRAME_SIZE,
+ M_PSTIOP, M_NOWAIT,
+ 0x00010000, 0xFFFFFFFF,
+ PAGE_SIZE, 0))) {
+ printf("pstiop: contigmalloc of outqueue buffers failed!\n");
+ return 0;
+ }
+ sc->phys_obase = vtophys(sc->obase);
+ mfa = iop_get_mfa(sc);
+ msg = (struct i2o_exec_init_outqueue_message *)(sc->ibase + mfa);
+ bzero(msg, sizeof(struct i2o_exec_init_outqueue_message));
+ msg->version_offset = 0x61;
+ msg->message_flags = 0x0;
+ msg->message_size = sizeof(struct i2o_exec_init_outqueue_message) >> 2;
+ msg->target_address = I2O_TID_IOP;
+ msg->initiator_address = I2O_TID_HOST;
+ msg->function = I2O_EXEC_OUTBOUND_INIT;
+ msg->host_pagesize = PAGE_SIZE;
+ msg->init_code = 0x00; /* SOS XXX should be 0x80 == OS */
+ msg->queue_framesize = I2O_IOP_OUTBOUND_FRAME_SIZE / sizeof(u_int32_t);
+ msg->sgl[0].flags = I2O_SGL_SIMPLE | I2O_SGL_END | I2O_SGL_EOB;
+ msg->sgl[0].count = sizeof(reply);
+ msg->sgl[0].phys_addr[0] = vtophys(&reply);
+ msg->sgl[1].flags = I2O_SGL_END | I2O_SGL_EOB;
+ msg->sgl[1].count = 1;
+ msg->sgl[1].phys_addr[0] = 0;
+
+ sc->reg->iqueue = mfa;
+
+ /* wait for init to complete */
+ while (--timeout && reply != I2O_EXEC_OUTBOUND_INIT_COMPLETE)
+ DELAY(1000);
+
+ if (!timeout) {
+ printf("pstiop: timeout waiting for init-complete response\n");
+ iop_free_mfa(sc, mfa);
+ return 0;
+ }
+
+ /* now init our oqueue bufs */
+ for (i = 0; i < I2O_IOP_OUTBOUND_FRAME_COUNT; i++) {
+ sc->reg->oqueue = sc->phys_obase + (i * I2O_IOP_OUTBOUND_FRAME_SIZE);
+ DELAY(1000);
+ }
+
+ return 1;
+}
+
+int
+iop_get_lct(struct iop_softc *sc)
+{
+ struct i2o_exec_get_lct_message *msg;
+ struct i2o_get_lct_reply *reply;
+ int mfa;
+#define ALLOCSIZE (PAGE_SIZE + (256 * sizeof(struct i2o_lct_entry)))
+
+ if (!(reply = contigmalloc(ALLOCSIZE, M_PSTIOP, M_NOWAIT | M_ZERO,
+ 0x00010000, 0xFFFFFFFF, PAGE_SIZE, 0)))
+ return 0;
+
+ mfa = iop_get_mfa(sc);
+ msg = (struct i2o_exec_get_lct_message *)(sc->ibase + mfa);
+ bzero(msg, sizeof(struct i2o_exec_get_lct_message));
+ msg->version_offset = 0x61;
+ msg->message_flags = 0x0;
+ msg->message_size = sizeof(struct i2o_exec_get_lct_message) >> 2;
+ msg->target_address = I2O_TID_IOP;
+ msg->initiator_address = I2O_TID_HOST;
+ msg->function = I2O_EXEC_LCT_NOTIFY;
+ msg->class = I2O_CLASS_MATCH_ANYCLASS;
+ msg->last_change_id = 0;
+
+ msg->sgl.flags = I2O_SGL_SIMPLE | I2O_SGL_END | I2O_SGL_EOB;
+ msg->sgl.count = ALLOCSIZE;
+ msg->sgl.phys_addr[0] = vtophys(reply);
+
+ if (iop_queue_wait_msg(sc, mfa, (struct i2o_basic_message *)msg)) {
+ contigfree(reply, ALLOCSIZE, M_PSTIOP);
+ return 0;
+ }
+ if (!(sc->lct = malloc(reply->table_size * sizeof(struct i2o_lct_entry),
+ M_PSTIOP, M_NOWAIT | M_ZERO))) {
+ contigfree(reply, ALLOCSIZE, M_PSTIOP);
+ return 0;
+ }
+ bcopy(&reply->entry[0], sc->lct,
+ reply->table_size * sizeof(struct i2o_lct_entry));
+ sc->lct_count = reply->table_size;
+ contigfree(reply, ALLOCSIZE, M_PSTIOP);
+ return 1;
+}
+
+struct i2o_get_param_reply *
+iop_get_util_params(struct iop_softc *sc, int target, int operation, int group)
+{
+ struct i2o_util_get_param_message *msg;
+ struct i2o_get_param_operation *param;
+ struct i2o_get_param_reply *reply;
+ int mfa;
+
+ if (!(param = contigmalloc(PAGE_SIZE, M_PSTIOP, M_NOWAIT | M_ZERO,
+ 0x00010000, 0xFFFFFFFF, PAGE_SIZE, 0)))
+ return NULL;
+
+ if (!(reply = contigmalloc(PAGE_SIZE, M_PSTIOP, M_NOWAIT | M_ZERO,
+ 0x00010000, 0xFFFFFFFF, PAGE_SIZE, 0)))
+ return NULL;
+
+ mfa = iop_get_mfa(sc);
+ msg = (struct i2o_util_get_param_message *)(sc->ibase + mfa);
+ bzero(msg, sizeof(struct i2o_util_get_param_message));
+ msg->version_offset = 0x51;
+ msg->message_flags = 0x0;
+ msg->message_size = sizeof(struct i2o_util_get_param_message) >> 2;
+ msg->target_address = target;
+ msg->initiator_address = I2O_TID_HOST;
+ msg->function = I2O_UTIL_PARAMS_GET;
+ msg->operation_flags = 0;
+
+ param->operation_count = 1;
+ param->operation[0].operation = operation;
+ param->operation[0].group = group;
+ param->operation[0].field_count = 0xffff;
+
+ msg->sgl[0].flags = I2O_SGL_SIMPLE | I2O_SGL_DIR | I2O_SGL_EOB;
+ msg->sgl[0].count = sizeof(struct i2o_get_param_operation);
+ msg->sgl[0].phys_addr[0] = vtophys(param);
+
+ msg->sgl[1].flags = I2O_SGL_SIMPLE | I2O_SGL_END | I2O_SGL_EOB;
+ msg->sgl[1].count = PAGE_SIZE;
+ msg->sgl[1].phys_addr[0] = vtophys(reply);
+
+ if (iop_queue_wait_msg(sc, mfa, (struct i2o_basic_message *)msg) ||
+ reply->error_info_size) {
+ contigfree(reply, PAGE_SIZE, M_PSTIOP);
+ reply = NULL;
+ }
+ contigfree(param, PAGE_SIZE, M_PSTIOP);
+ return reply;
+}
+
+u_int32_t
+iop_get_mfa(struct iop_softc *sc)
+{
+ u_int32_t mfa;
+ int timeout = 10000;
+
+ while ((mfa = sc->reg->iqueue) == 0xffffffff && timeout) {
+ DELAY(1000);
+ timeout--;
+ }
+ if (!timeout)
+ printf("pstiop: no free mfa\n");
+ return mfa;
+}
+
+void
+iop_free_mfa(struct iop_softc *sc, int mfa)
+{
+ struct i2o_basic_message *msg = (struct i2o_basic_message *)(sc->ibase+mfa);
+
+ bzero(msg, sizeof(struct i2o_basic_message));
+ msg->version = 0x01;
+ msg->message_flags = 0x0;
+ msg->message_size = sizeof(struct i2o_basic_message) >> 2;
+ msg->target_address = I2O_TID_IOP;
+ msg->initiator_address = I2O_TID_HOST;
+ msg->function = I2O_UTIL_NOP;
+ sc->reg->iqueue = mfa;
+}
+
+static void
+iop_done(struct iop_softc *sc, u_int32_t mfa, struct i2o_single_reply *reply)
+{
+ struct iop_request *request =
+ (struct iop_request *)reply->transaction_context;
+
+ request->reply = reply;
+ request->mfa = mfa;
+ wakeup(request);
+}
+
+int
+iop_queue_wait_msg(struct iop_softc *sc, int mfa, struct i2o_basic_message *msg)
+{
+ struct i2o_single_reply *reply;
+ struct iop_request request;
+ u_int32_t out_mfa;
+ int status, timeout = 10000;
+
+ mtx_lock(&sc->mtx);
+ if (!(sc->reg->oqueue_intr_mask & 0x08)) {
+ msg->transaction_context = (u_int32_t)&request;
+ msg->initiator_context = (u_int32_t)iop_done;
+ sc->reg->iqueue = mfa;
+ if (msleep(&request, &sc->mtx, PRIBIO, "pstwt", 10 * hz)) {
+ printf("pstiop: timeout waiting for message response\n");
+ iop_free_mfa(sc, mfa);
+ mtx_unlock(&sc->mtx);
+ return -1;
+ }
+ status = request.reply->status;
+ sc->reg->oqueue = request.mfa;
+ }
+ else {
+ sc->reg->iqueue = mfa;
+ while (--timeout && ((out_mfa = sc->reg->oqueue) == 0xffffffff))
+ DELAY(1000);
+ if (!timeout) {
+ printf("pstiop: timeout waiting for message response\n");
+ iop_free_mfa(sc, mfa);
+ mtx_unlock(&sc->mtx);
+ return -1;
+ }
+ reply = (struct i2o_single_reply *)(sc->obase+(out_mfa-sc->phys_obase));
+ status = reply->status;
+ sc->reg->oqueue = out_mfa;
+ }
+ mtx_unlock(&sc->mtx);
+ return status;
+}
+
+int
+iop_create_sgl(struct i2o_basic_message *msg, caddr_t data, int count, int dir)
+{
+ struct i2o_sgl *sgl = (struct i2o_sgl *)((int32_t *)msg + msg->offset);
+ u_int32_t sgl_count, sgl_phys;
+ int i = 0;
+
+ if (((uintptr_t)data & 3) || (count & 3)) {
+ printf("pstiop: non aligned DMA transfer attempted\n");
+ return 0;
+ }
+ if (!count) {
+ printf("pstiop: zero length DMA transfer attempted\n");
+ return 0;
+ }
+
+ sgl_count = min(count, (PAGE_SIZE - ((uintptr_t)data & PAGE_MASK)));
+ sgl_phys = vtophys(data);
+ sgl->flags = dir | I2O_SGL_PAGELIST | I2O_SGL_EOB | I2O_SGL_END;
+ sgl->count = count;
+ data += sgl_count;
+ count -= sgl_count;
+
+ while (count) {
+ sgl->phys_addr[i] = sgl_phys;
+ sgl_phys = vtophys(data);
+ data += min(count, PAGE_SIZE);
+ count -= min(count, PAGE_SIZE);
+ if (++i >= I2O_SGL_MAX_SEGS) {
+ printf("pstiop: too many segments in SGL\n");
+ return 0;
+ }
+ }
+ sgl->phys_addr[i] = sgl_phys;
+ msg->message_size += i;
+ return 1;
+}
OpenPOWER on IntegriCloud