SILI - Silicon Image 3132 driver, initial commit
authorMatthew Dillon <dillon@apollo.backplane.com>
Wed, 17 Jun 2009 06:43:24 +0000 (23:43 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Wed, 17 Jun 2009 06:43:24 +0000 (23:43 -0700)
This driver was derived from the AHCI driver and retooled extensively
for the Silicon Image chipset.  It retains all the cool features from the
AHCI driver including port multiplier (PM) support, NCQ, and hot-plug for
both direct-attached and PM-attached targets.

This initial commit is very alpha and the driver has significant PM-related
bugs.  It is not yet ready for prime time.

sys/dev/disk/sili/Makefile [new file with mode: 0644]
sys/dev/disk/sili/atascsi.h [new file with mode: 0644]
sys/dev/disk/sili/fixme [new file with mode: 0755]
sys/dev/disk/sili/pmreg.h [new file with mode: 0644]
sys/dev/disk/sili/sili.c [new file with mode: 0644]
sys/dev/disk/sili/sili.h [new file with mode: 0644]
sys/dev/disk/sili/sili_attach.c [new file with mode: 0644]
sys/dev/disk/sili/sili_cam.c [new file with mode: 0644]
sys/dev/disk/sili/sili_dragonfly.c [new file with mode: 0644]
sys/dev/disk/sili/sili_dragonfly.h [new file with mode: 0644]
sys/dev/disk/sili/sili_pm.c [new file with mode: 0644]

diff --git a/sys/dev/disk/sili/Makefile b/sys/dev/disk/sili/Makefile
new file mode 100644 (file)
index 0000000..4b1d8bb
--- /dev/null
@@ -0,0 +1,10 @@
+# Native AHCI driver, ported from OpenBSD
+#
+KMOD= sili
+
+.PATH: ${.CURDIR}
+
+SRCS= sili_dragonfly.c sili_attach.c sili_cam.c sili_pm.c sili.c \
+      bus_if.h device_if.h pci_if.h opt_scsi.h opt_cam.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/disk/sili/atascsi.h b/sys/dev/disk/sili/atascsi.h
new file mode 100644 (file)
index 0000000..ae512ff
--- /dev/null
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2007 David Gwynne <dlg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $OpenBSD: atascsi.h,v 1.33 2009/02/16 21:19:06 miod Exp $
+ */
+
+struct atascsi;
+struct scsi_link;
+
+/*
+ * ATA commands
+ */
+
+#define ATA_C_SATA_FEATURE_ENA 0x10
+#define ATA_C_READDMA_EXT      0x25
+#define ATA_C_READ_LOG_EXT     0x2f
+#define ATA_C_WRITEDMA_EXT     0x35
+#define ATA_C_READ_FPDMA       0x60
+#define ATA_C_WRITE_FPDMA      0x61
+#define ATA_C_SATA_FEATURE_DIS 0x90
+#define ATA_C_PACKET           0xa0
+#define ATA_C_ATAPI_IDENTIFY   0xa1
+#define ATA_C_READDMA          0xc8
+#define ATA_C_WRITEDMA         0xca
+#define ATA_C_READ_PM          0xe4
+#define ATA_C_WRITE_PM         0xe8
+#define ATA_C_FLUSH_CACHE      0xe7
+#define ATA_C_FLUSH_CACHE_EXT  0xea /* lba48 */
+#define ATA_C_IDENTIFY         0xec
+#define ATA_C_SET_FEATURES     0xef
+#define ATA_C_SEC_FREEZE_LOCK  0xf5
+
+/*
+ * ATA SATA FEATURES subcommands
+ */
+#define ATA_SATAFT_NONZDMA     0x01    /* DMA non-zero buffer offset */
+#define ATA_SATAFT_DMAAAOPT    0x02    /* DMA AA optimization */
+#define ATA_SATAFT_DEVIPS      0x03    /* Device-initiated pwr state*/
+#define ATA_SATAFT_INORDER     0x04    /* in-order data delivery */
+#define ATA_SATAFT_ASYNCNOTIFY 0x05    /* Async notification */
+
+/*
+ * ATA SET FEATURES subcommands
+ */
+#define ATA_SF_WRITECACHE_EN   0x02
+#define ATA_SF_LOOKAHEAD_EN    0xaa
+
+struct ata_identify {
+       u_int16_t       config;         /*   0 */
+       u_int16_t       ncyls;          /*   1 */
+       u_int16_t       reserved1;      /*   2 */
+       u_int16_t       nheads;         /*   3 */
+       u_int16_t       track_size;     /*   4 */
+       u_int16_t       sector_size;    /*   5 */
+       u_int16_t       nsectors;       /*   6 */
+       u_int16_t       reserved2[3];   /*   7 vendor unique */
+       u_int8_t        serial[20];     /*  10 */
+       u_int16_t       buffer_type;    /*  20 */
+       u_int16_t       buffer_size;    /*  21 */
+       u_int16_t       ecc;            /*  22 */
+       u_int8_t        firmware[8];    /*  23 */
+       u_int8_t        model[40];      /*  27 */
+       u_int16_t       multi;          /*  47 */
+       u_int16_t       dwcap;          /*  48 */
+       u_int16_t       cap;            /*  49 */
+       u_int16_t       reserved3;      /*  50 */
+       u_int16_t       piomode;        /*  51 */
+       u_int16_t       dmamode;        /*  52 */
+       u_int16_t       validinfo;      /*  53 */
+       u_int16_t       curcyls;        /*  54 */
+       u_int16_t       curheads;       /*  55 */
+       u_int16_t       cursectrk;      /*  56 */
+       u_int16_t       curseccp[2];    /*  57 */
+       u_int16_t       mult2;          /*  59 */
+       u_int16_t       addrsec[2];     /*  60 */
+       u_int16_t       worddma;        /*  62 */
+       u_int16_t       dworddma;       /*  63 */
+       u_int16_t       advpiomode;     /*  64 */
+       u_int16_t       minmwdma;       /*  65 */
+       u_int16_t       recmwdma;       /*  66 */
+       u_int16_t       minpio;         /*  67 */
+       u_int16_t       minpioflow;     /*  68 */
+       u_int16_t       reserved4[2];   /*  69 */
+       u_int16_t       typtime[2];     /*  71 */
+       u_int16_t       reserved5[2];   /*  73 */
+       u_int16_t       qdepth;         /*  75 */
+       u_int16_t       satacap;        /*  76 */
+       u_int16_t       reserved6;      /*  77 */
+       u_int16_t       satafsup;       /*  78 */
+       u_int16_t       satafen;        /*  79 */
+       u_int16_t       majver;         /*  80 */
+       u_int16_t       minver;         /*  81 */
+       u_int16_t       cmdset82;       /*  82 */
+       u_int16_t       cmdset83;       /*  83 */
+       u_int16_t       cmdset84;       /*  84 */
+       u_int16_t       features85;     /*  85 */
+       u_int16_t       features86;     /*  86 */
+       u_int16_t       features87;     /*  87 */
+#define ATA_ID_F87_WWN         (1<<8)
+       u_int16_t       ultradma;       /*  88 */
+       u_int16_t       erasetime;      /*  89 */
+       u_int16_t       erasetimex;     /*  90 */
+       u_int16_t       apm;            /*  91 */
+       u_int16_t       masterpw;       /*  92 */
+       u_int16_t       hwreset;        /*  93 */
+       u_int16_t       acoustic;       /*  94 */
+       u_int16_t       stream_min;     /*  95 */
+       u_int16_t       stream_xfer_d;  /*  96 */
+       u_int16_t       stream_lat;     /*  97 */
+       u_int16_t       streamperf[2];  /*  98 */
+       u_int16_t       addrsecxt[4];   /* 100 */
+       u_int16_t       stream_xfer_p;  /* 104 */
+       u_int16_t       padding1;       /* 105 */
+       u_int16_t       phys_sect_sz;   /* 106 */
+       u_int16_t       seek_delay;     /* 107 */
+       u_int16_t       naa_ieee_oui;   /* 108 */
+       u_int16_t       ieee_oui_uid;   /* 109 */
+       u_int16_t       uid_mid;        /* 110 */
+       u_int16_t       uid_low;        /* 111 */
+       u_int16_t       resv_wwn[4];    /* 112 */
+       u_int16_t       incits;         /* 116 */
+       u_int16_t       words_lsec[2];  /* 117 */
+       u_int16_t       cmdset119;      /* 119 */
+       u_int16_t       features120;    /* 120 */
+       u_int16_t       padding2[6];
+       u_int16_t       rmsn;           /* 127 */
+       u_int16_t       securestatus;   /* 128 */
+#define ATA_SECURE_LOCKED              (1<<2)
+#define ATA_SECURE_FROZEN              (1<<3)
+       u_int16_t       vendor[31];     /* 129 */
+       u_int16_t       padding3[16];   /* 160 */
+       u_int16_t       curmedser[30];  /* 176 */
+       u_int16_t       sctsupport;     /* 206 */
+       u_int16_t       padding4[48];   /* 207 */
+       u_int16_t       integrity;      /* 255 */
+} __packed;
+
+/*
+ * IDENTIFY DEVICE data
+ */
+#define ATA_IDENTIFY_SECURITY          (1 << 1)
+#define ATA_IDENTIFY_WRITECACHE                (1 << 5)
+#define ATA_IDENTIFY_LOOKAHEAD         (1 << 6)
+
+/*
+ * Frame Information Structures
+ */
+
+#define ATA_FIS_LENGTH         20
+
+struct ata_fis_h2d {
+       u_int8_t                type;
+#define ATA_FIS_TYPE_H2D               0x27
+       u_int8_t                flags;
+#define ATA_H2D_FLAGS_CMD              (1<<7)
+       u_int8_t                command;
+       u_int8_t                features;
+#define ATA_H2D_FEATURES_DMA           (1<<0)
+#define ATA_H2D_FEATURES_DIR           (1<<2)
+#define ATA_H2D_FEATURES_DIR_READ      (1<<2)
+#define ATA_H2D_FEATURES_DIR_WRITE     (0<<2)
+
+       u_int8_t                lba_low;
+       u_int8_t                lba_mid;
+       u_int8_t                lba_high;
+       u_int8_t                device;
+#define ATA_H2D_DEVICE_LBA             0x40
+
+       u_int8_t                lba_low_exp;
+       u_int8_t                lba_mid_exp;
+       u_int8_t                lba_high_exp;
+       u_int8_t                features_exp;
+
+       u_int8_t                sector_count;
+       u_int8_t                sector_count_exp;
+       u_int8_t                reserved0;
+       u_int8_t                control;
+#define ATA_FIS_CONTROL_SRST   0x04
+#define ATA_FIS_CONTROL_4BIT   0x08
+
+       u_int8_t                reserved1;
+       u_int8_t                reserved2;
+       u_int8_t                reserved3;
+       u_int8_t                reserved4;
+} __packed;
+
+struct ata_fis_d2h {
+       u_int8_t                type;
+#define ATA_FIS_TYPE_D2H               0x34
+       u_int8_t                flags;
+#define ATA_D2H_FLAGS_INTR             (1<<6)
+       u_int8_t                status;
+       u_int8_t                error;
+
+       u_int8_t                lba_low;
+       u_int8_t                lba_mid;
+       u_int8_t                lba_high;
+       u_int8_t                device;
+
+       u_int8_t                lba_low_exp;
+       u_int8_t                lba_mid_exp;
+       u_int8_t                lba_high_exp;
+       u_int8_t                reserved0;
+
+       u_int8_t                sector_count;
+       u_int8_t                sector_count_exp;
+       u_int8_t                reserved1;
+       u_int8_t                reserved2;
+
+       u_int8_t                reserved3;
+       u_int8_t                reserved4;
+       u_int8_t                reserved5;
+       u_int8_t                reserved6;
+} __packed;
+
+/*
+ * SATA log page 10h -
+ * looks like a D2H FIS, with errored tag number in first byte.
+ */
+struct ata_log_page_10h {
+       struct ata_fis_d2h      err_regs;
+#define ATA_LOG_10H_TYPE_NOTQUEUED     0x80
+#define ATA_LOG_10H_TYPE_TAG_MASK      0x1f
+       u_int8_t                reserved[256 - sizeof(struct ata_fis_d2h)];
+       u_int8_t                vendor_specific[255];
+       u_int8_t                checksum;
+} __packed;
+
+/*
+ * SATA registers
+ */
+
+#define SATA_SStatus_DET               0x00f
+#define SATA_SStatus_DET_NODEV         0x000
+#define SATA_SStatus_DET_NOPHY         0x001
+#define SATA_SStatus_DET_DEV           0x003
+#define SATA_SStatus_DET_OFFLINE       0x008
+
+#define SATA_SStatus_SPD               0x0f0
+#define SATA_SStatus_SPD_NONE          0x000
+#define SATA_SStatus_SPD_1_5           0x010
+#define SATA_SStatus_SPD_3_0           0x020
+
+#define SATA_SStatus_IPM               0xf00
+#define SATA_SStatus_IPM_NODEV         0x000
+#define SATA_SStatus_IPM_ACTIVE                0x100
+#define SATA_SStatus_IPM_PARTIAL       0x200
+#define SATA_SStatus_IPM_SLUMBER       0x600
+
+#define SATA_SIGNATURE_PORT_MULTIPLIER 0x96690101
+#define SATA_SIGNATURE_ATAPI           0xeb140101
+#define SATA_SIGNATURE_DISK            0x00000101
+
+/*
+ * ATA interface
+ */
+
+struct sili_port;
+
+struct ata_port {
+       struct ata_identify     at_identify;    /* only if ATA_PORT_T_DISK */
+       struct sili_port        *at_sili_port;
+       int                     at_type;
+#define ATA_PORT_T_NONE                        0
+#define ATA_PORT_T_DISK                        1
+#define ATA_PORT_T_ATAPI               2
+#define ATA_PORT_T_PM                  3
+       int                     at_features;
+#define ATA_PORT_F_WCACHE              (1 << 0)
+#define ATA_PORT_F_RAHEAD              (1 << 1)
+#define ATA_PORT_F_FRZLCK              (1 << 2)
+#define ATA_PORT_F_RESCAN              (1 << 3) /* re-check on bus scan */
+#define ATA_PORT_F_READLOG             (1 << 4) /* must read log page */
+       int                     at_probe;
+#define ATA_PROBE_NEED_INIT            0
+#define ATA_PROBE_NEED_HARD_RESET      1
+#define ATA_PROBE_NEED_SOFT_RESET      2
+#define ATA_PROBE_NEED_IDENT           3
+#define ATA_PROBE_GOOD                 4
+#define ATA_PROBE_FAILED               7
+       int                     at_ncqdepth;
+       u_int64_t               at_capacity;    /* only if ATA_PORT_T_DISK */
+       int                     at_target;      /* port multiplier port */
+       char                    at_name[16];
+};
+
+struct ata_xfer {
+       struct ata_fis_h2d      *fis;
+       struct ata_fis_d2h      *rfis;
+       u_int8_t                *packetcmd;
+       u_int8_t                tag;
+
+       void                    *data;
+       size_t                  datalen;
+       size_t                  resid;
+
+       void                    (*complete)(struct ata_xfer *);
+       u_int                   timeout;
+
+       int                     flags;
+#define ATA_F_READ                     (1<<0)
+#define ATA_F_WRITE                    (1<<1)
+#define ATA_F_NOWAIT                   (1<<2)
+#define ATA_F_POLL                     (1<<3)
+#define ATA_F_PIO                      (1<<4)
+#define ATA_F_PACKET                   (1<<5)
+#define ATA_F_NCQ                      (1<<6)
+#define ATA_F_TIMEOUT_RUNNING          (1<<7)
+#define ATA_F_TIMEOUT_DESIRED          (1<<8)
+#define ATA_F_TIMEOUT_EXPIRED          (1<<9)
+#define ATA_FMT_FLAGS                  "\020"                          \
+                                       "\012EXPIRED"                   \
+                                       "\011DESIRED" "\010TRUNNING"    \
+                                       "\007NCQ" "\006PACKET"          \
+                                       "\005PIO" "\004POLL" "\003NOWAIT" \
+                                       "\002WRITE" "\001READ"
+
+       volatile int            state;
+#define ATA_S_SETUP                    0
+#define ATA_S_PENDING                  1
+#define ATA_S_COMPLETE                 2
+#define ATA_S_ERROR                    3
+#define ATA_S_TIMEOUT                  4
+#define ATA_S_ONCHIP                   5
+#define ATA_S_PUT                      6
+
+       void                    *atascsi_private;
+       struct ata_port         *at;    /* NULL if direct-attached */
+};
diff --git a/sys/dev/disk/sili/fixme b/sys/dev/disk/sili/fixme
new file mode 100755 (executable)
index 0000000..9d2ce1d
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/csh
+#
+
+foreach i ( *.c *.h )
+       sed -e 's/AHCI/SILI/g' < $i > $i.new
+       mv $i.new $i
+end
diff --git a/sys/dev/disk/sili/pmreg.h b/sys/dev/disk/sili/pmreg.h
new file mode 100644 (file)
index 0000000..2a2da5d
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com>
+ *
+ * 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.
+ * 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. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``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
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+ */
+
+/*
+ * SATA port multiplier registers
+ */
+#define SATA_PMREG_SSTS                0       /* use SATA_PM_SSTS_ bit defs */
+#define SATA_PMREG_SERR                1       /* use SATA_PM_SERR_ bit defs */
+#define SATA_PMREG_SCTL                2       /* use SATA_PM_SCTL_ bit defs */
+#define SATA_PMREG_SACT                3       /* (not implemented on PM) */
+
+#define  SATA_PM_SSTS_DET              0xf /* Device Detection */
+#define  SATA_PM_SSTS_DET_NONE         0x0
+#define  SATA_PM_SSTS_DET_DEV_NE       0x1
+#define  SATA_PM_SSTS_DET_DEV          0x3
+#define  SATA_PM_SSTS_DET_PHYOFFLINE   0x4
+#define  SATA_PM_SSTS_SPD              0xf0 /* Current Interface Speed */
+#define  SATA_PM_SSTS_SPD_NONE         0x00
+#define  SATA_PM_SSTS_SPD_GEN1         0x10
+#define  SATA_PM_SSTS_SPD_GEN2         0x20
+#define  SATA_PM_SSTS_IPM              0xf00 /* Interface Power Management */
+#define  SATA_PM_SSTS_IPM_NONE         0x000
+#define  SATA_PM_SSTS_IPM_ACTIVE       0x100
+#define  SATA_PM_SSTS_IPM_PARTIAL      0x200
+#define  SATA_PM_SSTS_IPM_SLUMBER      0x600
+
+#define  SATA_PM_SCTL_DET              0xf /* Device Detection */
+#define  SATA_PM_SCTL_DET_NONE         0x0
+#define  SATA_PM_SCTL_DET_INIT         0x1
+#define  SATA_PM_SCTL_DET_DISABLE      0x4
+#define  SATA_PM_SCTL_SPD              0xf0 /* Speed Allowed */
+#define  SATA_PM_SCTL_SPD_ANY          0x00
+#define  SATA_PM_SCTL_SPD_GEN1         0x10
+#define  SATA_PM_SCTL_SPD_GEN2         0x20
+#define  SATA_PM_SCTL_IPM              0xf00 /* Interface Power Management */
+#define  SATA_PM_SCTL_IPM_NONE         0x000
+#define  SATA_PM_SCTL_IPM_NOPARTIAL    0x100
+#define  SATA_PM_SCTL_IPM_NOSLUMBER    0x200
+#define  SATA_PM_SCTL_IPM_DISABLED     0x300
+#define         SATA_PM_SCTL_SPM               0xf000  /* Select Power Management */
+#define         SATA_PM_SCTL_SPM_NONE          0x0000
+#define         SATA_PM_SCTL_SPM_NOPARTIAL     0x1000
+#define         SATA_PM_SCTL_SPM_NOSLUMBER     0x2000
+#define         SATA_PM_SCTL_SPM_DISABLED      0x3000
+#define  SATA_PM_SCTL_PMP              0xf0000 /* Set PM port for xmit FISes */
+#define  SATA_PM_SCTL_PMP_SHIFT                16
+
+#define  SATA_PM_SERR_ERR_I            (1<<0) /* Recovered Data Integrity */
+#define  SATA_PM_SERR_ERR_M            (1<<1) /* Recovered Communications */
+#define  SATA_PM_SERR_ERR_T            (1<<8) /* Transient Data Integrity */
+#define  SATA_PM_SERR_ERR_C            (1<<9) /* Persistent Comm/Data */
+#define  SATA_PM_SERR_ERR_P            (1<<10) /* Protocol */
+#define  SATA_PM_SERR_ERR_E            (1<<11) /* Internal */
+#define  SATA_PM_SERR_DIAG_N           (1<<16) /* PhyRdy Change */
+#define  SATA_PM_SERR_DIAG_I           (1<<17) /* Phy Internal Error */
+#define  SATA_PM_SERR_DIAG_W           (1<<18) /* Comm Wake */
+#define  SATA_PM_SERR_DIAG_B           (1<<19) /* 10B to 8B Decode Error */
+#define  SATA_PM_SERR_DIAG_D           (1<<20) /* Disparity Error */
+#define  SATA_PM_SERR_DIAG_C           (1<<21) /* CRC Error */
+#define  SATA_PM_SERR_DIAG_H           (1<<22) /* Handshake Error */
+#define  SATA_PM_SERR_DIAG_S           (1<<23) /* Link Sequence Error */
+#define  SATA_PM_SERR_DIAG_T           (1<<24) /* Transport State Trans Err */
+#define  SATA_PM_SERR_DIAG_F           (1<<25) /* Unknown FIS Type */
+#define  SATA_PM_SERR_DIAG_X           (1<<26) /* Exchanged */
+
+#define  SATA_PFMT_SERR        "\020"  \
+                       "\033DIAG.X" "\032DIAG.F" "\031DIAG.T" "\030DIAG.S" \
+                       "\027DIAG.H" "\026DIAG.C" "\025DIAG.D" "\024DIAG.B" \
+                       "\023DIAG.W" "\022DIAG.I" "\021DIAG.N"              \
+                       "\014ERR.E" "\013ERR.P" "\012ERR.C" "\011ERR.T"     \
+                       "\002ERR.M" "\001ERR.I"
+
+/*
+ * AHCI port multiplier revision information SCR[1] (see ahci_pm_read)
+ *
+ * Rev 1.1 is the one that should support async notification.
+ */
+#define SATA_PMREV_PM1_0       0x00000002
+#define SATA_PMREV_PM1_1       0x00000004
+#define SATA_PFMT_PM_REV       "\20" "\003PM1.1" "\002PM1.0"
+
+/*
+ * GSCR[64] and GSCR[96] - Port Multiplier features available and features
+ *                        enabled.
+ */
+#define SATA_PMREG_FEA         64
+#define SATA_PMREG_FEAEN       96              /* (features enabled) */
+#define SATA_PMFEA_BIST                0x00000001      /* BIST Support */
+#define SATA_PMFEA_PMREQ       0x00000002      /* Can issue PMREQp to host */
+#define SATA_PMFEA_DYNSSC      0x00000004      /* Dynamic SSC transmit enab */
+#define SATA_PMFEA_ASYNCNOTIFY 0x00000008      /* Async notification */
+
+#define SATA_PFMT_PM_FEA       "\20"                   \
+                               "\004AsyncNotify"       \
+                               "\003DynamicSSC"        \
+                               "\002PMREQ"             \
+                               "\001BIST"
+
+/*
+ * Enable generation of async notify events for individual targets
+ * via the PMEENA register.  Each bit in PMEINFO is a wire-or of all
+ * SERROR bits for that target.  To enable a new notification event
+ * the SERROR bits in PMSERROR_REGNO must be cleared.
+ */
+#define SATA_PMREG_EINFO       32              /* error info 16 ports */
+#define SATA_PMREG_EEENA       33              /* error info enable 16 ports */
diff --git a/sys/dev/disk/sili/sili.c b/sys/dev/disk/sili/sili.c
new file mode 100644 (file)
index 0000000..607e5c1
--- /dev/null
@@ -0,0 +1,2655 @@
+/*
+ * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com>
+ *
+ * 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.
+ * 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. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``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
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+ *
+ *
+ * Copyright (c) 2006 David Gwynne <dlg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ *
+ *
+ * $OpenBSD: sili.c,v 1.147 2009/02/16 21:19:07 miod Exp $
+ */
+
+#include "sili.h"
+
+void   sili_port_interrupt_enable(struct sili_port *ap);
+void   sili_port_interrupt_redisable(struct sili_port *ap);
+void   sili_port_interrupt_reenable(struct sili_port *ap);
+
+int    sili_load_prb(struct sili_ccb *);
+void   sili_unload_prb(struct sili_ccb *);
+static void sili_load_prb_callback(void *info, bus_dma_segment_t *segs,
+                                   int nsegs, int error);
+void   sili_start(struct sili_ccb *);
+int    sili_port_softreset(struct sili_port *ap);
+int    sili_port_pmprobe(struct sili_port *ap);
+int    sili_port_hardreset(struct sili_port *ap, int hard);
+void   sili_port_hardstop(struct sili_port *ap);
+void   sili_port_listen(struct sili_port *ap);
+
+static void sili_ata_cmd_timeout_unserialized(void *);
+static int sili_core_timeout(struct sili_ccb *ccb);
+void   sili_quick_timeout(struct sili_ccb *ccb);
+void   sili_check_active_timeouts(struct sili_port *ap);
+
+void   sili_beg_exclusive_access(struct sili_port *ap, struct ata_port *at);
+void   sili_end_exclusive_access(struct sili_port *ap, struct ata_port *at);
+void   sili_issue_pending_commands(struct sili_port *ap, struct sili_ccb *ccb);
+
+int    sili_port_read_ncq_error(struct sili_port *, int);
+
+struct sili_dmamem *sili_dmamem_alloc(struct sili_softc *, bus_dma_tag_t tag);
+void   sili_dmamem_free(struct sili_softc *, struct sili_dmamem *);
+static void sili_dmamem_saveseg(void *info, bus_dma_segment_t *segs, int nsegs, int error);
+
+static void sili_dummy_done(struct ata_xfer *xa);
+static void sili_empty_done(struct sili_ccb *ccb);
+static void sili_ata_cmd_done(struct sili_ccb *ccb);
+
+/* Wait for all bits in _b to be cleared */
+#define sili_pwait_clr(_ap, _r, _b) \
+       sili_pwait_eq((_ap), SILI_PWAIT_TIMEOUT, (_r), (_b), 0)
+#define sili_pwait_clr_to(_ap, _to,  _r, _b) \
+       sili_pwait_eq((_ap), _to, (_r), (_b), 0)
+
+/* Wait for all bits in _b to be set */
+#define sili_pwait_set(_ap, _r, _b) \
+       sili_pwait_eq((_ap), SILI_PWAIT_TIMEOUT, (_r), (_b), (_b))
+#define sili_pwait_set_to(_ap, _to, _r, _b) \
+       sili_pwait_eq((_ap), _to, (_r), (_b), (_b))
+
+#define SILI_PWAIT_TIMEOUT     1000
+
+/*
+ * Initialize the global SILI hardware.  This code does not set up any of
+ * its ports.
+ */
+int
+sili_init(struct sili_softc *sc)
+{
+       DPRINTF(SILI_D_VERBOSE, " GHC 0x%b",
+               sili_read(sc, SILI_REG_GHC), SILI_FMT_GHC);
+
+       /*
+        * Reset the entire chip.  This also resets all ports.
+        *
+        * The spec doesn't say anything about how long we have to
+        * wait, so wait 10ms.
+        */
+       sili_write(sc, SILI_REG_GCTL, SILI_REG_GCTL_GRESET);
+       sili_os_sleep(10);
+       sili_write(sc, SILI_REG_GCTL, 0);
+       sili_os_sleep(10);
+
+       return (0);
+}
+
+/*
+ * Allocate and initialize an SILI port.
+ */
+int
+sili_port_alloc(struct sili_softc *sc, u_int port)
+{
+       struct sili_port        *ap;
+       struct ata_port         *at;
+       struct sili_ccb         *ccb;
+       int     rc = ENOMEM;
+       int     error;
+       int     i;
+
+       ap = kmalloc(sizeof(*ap), M_DEVBUF, M_WAITOK | M_ZERO);
+
+       ksnprintf(ap->ap_name, sizeof(ap->ap_name), "%s%d.%d",
+                 device_get_name(sc->sc_dev),
+                 device_get_unit(sc->sc_dev),
+                 port);
+       sc->sc_ports[port] = ap;
+       kprintf("%s: allocate port\n", PORTNAME(ap));
+
+       /*
+        * Allocate enough so we never have to reallocate, it makes
+        * it easier.
+        *
+        * ap_pmcount will be reduced by the scan if we encounter the
+        * port multiplier port prior to target 15.
+        */
+       if (ap->ap_ata == NULL) {
+               ap->ap_ata = kmalloc(sizeof(*ap->ap_ata) * SILI_MAX_PMPORTS,
+                                    M_DEVBUF, M_INTWAIT | M_ZERO);
+               for (i = 0; i < SILI_MAX_PMPORTS; ++i) {
+                       at = &ap->ap_ata[i];
+                       at->at_sili_port = ap;
+                       at->at_target = i;
+                       at->at_probe = ATA_PROBE_NEED_INIT;
+                       at->at_features |= ATA_PORT_F_RESCAN;
+                       ksnprintf(at->at_name, sizeof(at->at_name),
+                                 "%s.%d", ap->ap_name, i);
+               }
+       }
+       if (bus_space_subregion(sc->sc_piot, sc->sc_pioh,
+                               SILI_PORT_REGION(port), SILI_PORT_SIZE,
+                               &ap->ap_ioh) != 0) {
+               device_printf(sc->sc_dev,
+                             "unable to create register window for port %d\n",
+                             port);
+               goto freeport;
+       }
+
+       ap->ap_sc = sc;
+       ap->ap_num = port;
+       ap->ap_probe = ATA_PROBE_NEED_INIT;
+       TAILQ_INIT(&ap->ap_ccb_free);
+       TAILQ_INIT(&ap->ap_ccb_pending);
+       lockinit(&ap->ap_ccb_lock, "silipo", 0, 0);
+
+       /* Disable port interrupts */
+       sili_pwrite(ap, SILI_PREG_INT_DISABLE, SILI_PREG_INT_MASK);
+
+       /*
+        * Reset the port.  This is similar to a Device Reset but far
+        * more invasive.  We use Device Reset in our hardreset function.
+        * This function also does the same OOB initialization sequence
+        * that Device Reset does.
+        *
+        * NOTE: SILI_PREG_STATUS_READY will not be asserted unless and until
+        *       a device is connected to the port, so we can't use it to
+        *       verify that the port exists.
+        */
+       sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_RESET);
+       if (sili_pread(ap, SILI_PREG_STATUS) & SILI_PREG_STATUS_READY) {
+               device_printf(sc->sc_dev,
+                             "Port %d will not go into reset\n", port);
+               goto freeport;
+       }
+       sili_os_sleep(10);
+       sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_RESUME);
+       sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_RESET);
+
+       /*
+        * Allocate the SGE Table
+        */
+       ap->ap_dmamem_sget = sili_dmamem_alloc(sc, sc->sc_tag_sget);
+       if (ap->ap_dmamem_sget == NULL) {
+               kprintf("%s: NOSGET\n", PORTNAME(ap));
+               goto freeport;
+       }
+
+       /*
+        * Set up the SGE table base address
+        */
+       ap->ap_sget = (struct sili_sge *)SILI_DMA_KVA(ap->ap_dmamem_sget);
+
+       /*
+        * Allocate a CCB for each command slot
+        */
+       ap->ap_ccbs = kmalloc(sizeof(struct sili_ccb) * sc->sc_ncmds, M_DEVBUF,
+                             M_WAITOK | M_ZERO);
+       if (ap->ap_ccbs == NULL) {
+               device_printf(sc->sc_dev,
+                             "unable to allocate command list for port %d\n",
+                             port);
+               goto freeport;
+       }
+
+       /*
+        * Port control register setup.
+        */
+       sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_NOAUTOCC);
+       sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_32BITDMA |
+                                          SILI_PREG_CTL_PMA |
+                                          SILI_PREG_CTL_NOAUTOCC);
+
+       /*
+        * Most structures are in the port BAR.  Assign convenient
+        * pointers in the CCBs
+        */
+
+       for (i = 0; i < sc->sc_ncmds; i++) {
+               ccb = &ap->ap_ccbs[i];
+
+               error = bus_dmamap_create(sc->sc_tag_data, BUS_DMA_ALLOCNOW,
+                                         &ccb->ccb_dmamap);
+               if (error) {
+                       device_printf(sc->sc_dev,
+                                     "unable to create dmamap for port %d "
+                                     "ccb %d\n", port, i);
+                       goto freeport;
+               }
+
+               /*
+                * NOTE: fis and rfis overload the same structure.
+                */
+               callout_init(&ccb->ccb_timeout);
+               ccb->ccb_slot = i;
+               ccb->ccb_port = ap;
+               ccb->ccb_prb = bus_space_kva(sc->sc_piot, ap->ap_ioh,
+                                            SILI_PREG_LRAM_SLOT(i));
+               ccb->ccb_sge = &ap->ap_sget[i * SILI_MAX_SGET];
+               ccb->ccb_sge_paddr = SILI_DMA_DVA(ap->ap_dmamem_sget) +
+                                   sizeof(*ccb->ccb_sge) * SILI_MAX_SGET * i;
+               ccb->ccb_xa.fis = &ccb->ccb_prb->prb_h2d;
+               ccb->ccb_xa.rfis = &ccb->ccb_prb->prb_d2h;
+               ccb->ccb_xa.packetcmd = prb_packet(ccb->ccb_prb);
+               ccb->ccb_xa.tag = i;
+
+               ccb->ccb_xa.state = ATA_S_COMPLETE;
+
+               /*
+                * Reserve CCB[1] as the error CCB.  It doesn't matter
+                * which one we use for the Sili controllers.
+                */
+               if (i == 1)
+                       ap->ap_err_ccb = ccb;
+               else
+                       sili_put_ccb(ccb);
+       }
+       kprintf("%s: start port\n", PORTNAME(ap));
+       sili_os_start_port(ap);
+       return(0);
+freeport:
+       sili_port_free(sc, port);
+       return (rc);
+}
+
+/*
+ * [re]initialize an idle port.  No CCBs should be active.
+ *
+ * If at is NULL we are initializing a directly connected port, otherwise
+ * we are indirectly initializing a port multiplier port.
+ *
+ * This function is called during the initial port allocation sequence
+ * and is also called on hot-plug insertion.  We take no chances and
+ * use a hardreset instead of a softreset.
+ *
+ * This function is the only way to move a failed port back to active
+ * status.
+ *
+ * Returns 0 if a device is successfully detected.
+ */
+int
+sili_port_init(struct sili_port *ap, struct ata_port *atx)
+{
+       u_int32_t data;
+       int rc;
+
+       /*
+        * Clear all notification bits
+        */
+       if (atx == NULL && (ap->ap_sc->sc_flags & SILI_F_SSNTF))
+               sili_pwrite(ap, SILI_PREG_SNTF, -1);
+
+       /*
+        * Make sure the port is out of continuous COMRESET mode.
+        */
+       data = SILI_PREG_SCTL_SPM_NONE |
+              SILI_PREG_SCTL_IPM_NONE |
+              SILI_PREG_SCTL_SPD_NONE |
+              SILI_PREG_SCTL_DET_NONE;
+       if (SiliForceGen1 & (1 << ap->ap_num)) {
+               data &= ~SILI_PREG_SCTL_SPD_NONE;
+               data |= SILI_PREG_SCTL_SPD_GEN1;
+       }
+       sili_pwrite(ap, SILI_PREG_SCTL, data);
+
+       /*
+        * Hard-reset the port.  If a device is detected but it is busy
+        * we try a second time, this time cycling the phy as well.
+        *
+        * XXX note: hard reset mode 2 (cycling the PHY) is not reliable.
+        */
+       if (atx)
+               atx->at_probe = ATA_PROBE_NEED_HARD_RESET;
+       else
+               ap->ap_probe = ATA_PROBE_NEED_HARD_RESET;
+
+       rc = sili_port_reset(ap, atx, 1);
+#if 0
+       rc = sili_port_reset(ap, atx, 1);
+       if (rc == EBUSY) {
+               rc = sili_port_reset(ap, atx, 2);
+       }
+#endif
+
+       switch (rc) {
+       case ENODEV:
+               /*
+                * We had problems talking to the device on the port.
+                */
+               if (atx) {
+                       sili_pm_read(ap, atx->at_target,
+                                    SATA_PMREG_SSTS, &data);
+
+                       switch(data & SATA_PM_SSTS_DET) {
+                       case SATA_PM_SSTS_DET_DEV_NE:
+                               kprintf("%s: Device not communicating\n",
+                                       ATANAME(ap, atx));
+                               break;
+                       case SATA_PM_SSTS_DET_PHYOFFLINE:
+                               kprintf("%s: PHY offline\n",
+                                       ATANAME(ap, atx));
+                               break;
+                       default:
+                               kprintf("%s: No device detected\n",
+                                       ATANAME(ap, atx));
+                               break;
+                       }
+               } else {
+                       data = sili_pread(ap, SILI_PREG_SSTS);
+
+                       switch(data & SATA_PM_SSTS_DET) {
+                       case SILI_PREG_SSTS_DET_DEV_NE:
+                               kprintf("%s: Device not communicating\n",
+                                       ATANAME(ap, atx));
+                               break;
+                       case SILI_PREG_SSTS_DET_OFFLINE:
+                               kprintf("%s: PHY offline\n",
+                                       ATANAME(ap, atx));
+                               break;
+                       default:
+                               kprintf("%s: No device detected\n",
+                                       ATANAME(ap, atx));
+                               break;
+                       }
+               }
+               break;
+
+       case EBUSY:
+               /*
+                * The device on the port is still telling us its busy,
+                * which means that it is not properly handling a SATA
+                * port COMRESET.
+                *
+                * It may be possible to softreset the device using CLO
+                * and a device reset command.
+                */
+               if (atx) {
+                       kprintf("%s: Device on port is bricked, giving up\n",
+                               ATANAME(ap, atx));
+               } else {
+                       kprintf("%s: Device on port is bricked, "
+                               "trying softreset\n", PORTNAME(ap));
+
+                       rc = sili_port_reset(ap, atx, 0);
+                       if (rc) {
+                               kprintf("%s: Unable unbrick device\n",
+                                       PORTNAME(ap));
+                       } else {
+                               kprintf("%s: Successfully unbricked\n",
+                                       PORTNAME(ap));
+                       }
+               }
+               break;
+
+       default:
+               break;
+       }
+
+       /*
+        * Command transfers can only be enabled if a device was successfully
+        * detected.
+        *
+        * Allocate or deallocate the ap_ata array here too.
+        */
+       if (atx == NULL) {
+               switch(ap->ap_type) {
+               case ATA_PORT_T_NONE:
+                       ap->ap_pmcount = 0;
+                       break;
+               case ATA_PORT_T_PM:
+                       /* already set */
+                       break;
+               default:
+                       ap->ap_pmcount = 1;
+                       break;
+               }
+       }
+
+       /*
+        * Flush interrupts on the port. XXX
+        *
+        * Enable interrupts on the port whether a device is sitting on
+        * it or not, to handle hot-plug events.
+        */
+       if (atx == NULL) {
+#if 0
+               sili_pwrite(ap, SILI_PREG_IS, sili_pread(ap, SILI_PREG_IS));
+               sili_write(ap->ap_sc, SILI_REG_IS, 1 << ap->ap_num);
+#endif
+               sili_port_interrupt_enable(ap);
+       }
+       return(rc);
+}
+
+/*
+ * Handle an errored port.  This routine is called when the only
+ * commands left on the queue are expired, meaning we can safely
+ * go through a port init to clear its state.
+ *
+ * We complete the expired CCBs and then restart the queue.
+ */
+static
+void
+sili_port_reinit(struct sili_port *ap)
+{
+       struct sili_ccb *ccb;
+       struct ata_port *at;
+       int slot;
+       int target;
+       u_int32_t data;
+       int reentrant;
+
+       reentrant = (ap->ap_flags & AP_F_ERR_CCB_RESERVED) ? 1 : 0;
+
+       kprintf("%s: PORT REINIT AFTER ERROR reentrant=%d\n",
+               PORTNAME(ap), reentrant);
+
+       /*
+        * Clear port resume, clear bits 16:13 in the port device status
+        * register.  This is from the data sheet.
+        *
+        * Data sheet does not specify a delay but it seems prudent.
+        */
+       sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_RESUME);
+       sili_os_sleep(10);
+       for (target = 0; target < SILI_MAX_PMPORTS; ++target) {
+               data = sili_pread(ap, SILI_PREG_PM_STATUS(target));
+               data &= ~(SILI_PREG_PM_STATUS_SERVICE |
+                         SILI_PREG_PM_STATUS_LEGACY |
+                         SILI_PREG_PM_STATUS_NATIVE |
+                         SILI_PREG_PM_STATUS_VBSY);
+               sili_pwrite(ap, SILI_PREG_PM_STATUS(target), data);
+               sili_pwrite(ap, SILI_PREG_PM_QACTIVE(target), 0);
+       }
+
+       /*
+        * Issue a Port Initialize and wait for it to clear.  This flushes
+        * commands but does not reset the port.  Then wait for port ready.
+        */
+       sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_INIT);
+       if (sili_pwait_clr(ap, SILI_PREG_STATUS, SILI_PREG_CTL_INIT)) {
+               kprintf("%s: Unable to reinit, port failed\n",
+                       PORTNAME(ap));
+       }
+       if (sili_pwait_set(ap, SILI_PREG_STATUS, SILI_PREG_STATUS_READY)) {
+               kprintf("%s: Unable to reinit, port will not come ready\n",
+                       PORTNAME(ap));
+       }
+
+       /*
+        * Read the LOG ERROR page for targets that returned a specific
+        * D2H FIS with ERR set.
+        */
+       if (reentrant == 0) {
+               for (target = 0; target < SILI_MAX_PMPORTS; ++target) {
+                       at = &ap->ap_ata[target];
+                       if (at->at_features & ATA_PORT_F_READLOG) {
+                               kprintf("%s: READ LOG ERROR PAGE\n",
+                                       ATANAME(ap, at));
+                               at->at_features &= ~ATA_PORT_F_READLOG;
+                               sili_port_read_ncq_error(ap, target);
+                       }
+               }
+       }
+
+       /*
+        * Finally clean out the expired commands, we've probed the error
+        * status (or hopefully probed the error status).  Well, ok,
+        * we probably didn't XXX.
+        */
+       while (ap->ap_expired) {
+               slot = ffs(ap->ap_expired) - 1;
+               ap->ap_expired &= ~(1 << slot);
+               KKASSERT(ap->ap_active & (1 << slot));
+               ap->ap_active &= ~(1 << slot);
+               --ap->ap_active_cnt;
+               ccb = &ap->ap_ccbs[slot];
+               ccb->ccb_xa.state = ATA_S_TIMEOUT;
+               kprintf("%s: reinit: kill slot %d\n",
+                       ATANAME(ap, ccb->ccb_xa.at), ccb->ccb_slot);
+               ccb->ccb_done(ccb);
+               ccb->ccb_xa.complete(&ccb->ccb_xa);
+       }
+
+       /*
+        * Wow.  All done.  We can get the port moving again.
+        */
+       if (reentrant) {
+               kprintf("%s: reinit called reentrantly, skip end\n",
+                       PORTNAME(ap));
+       } else if (ap->ap_probe == ATA_PROBE_FAILED) {
+               kprintf("%s: reinit failed, port is dead\n", PORTNAME(ap));
+               while ((ccb = TAILQ_FIRST(&ap->ap_ccb_pending)) != NULL) {
+                       TAILQ_REMOVE(&ap->ap_ccb_pending, ccb, ccb_entry);
+                       ccb->ccb_xa.flags &= ~ATA_F_TIMEOUT_DESIRED;
+                       ccb->ccb_xa.state = ATA_S_TIMEOUT;
+                       ccb->ccb_done(ccb);
+                       ccb->ccb_xa.complete(&ccb->ccb_xa);
+               }
+       } else {
+               kprintf("%s: reinit succeeded probe=%d type=%d\n", PORTNAME(ap), ap->ap_probe, ap->ap_type);
+               sili_issue_pending_commands(ap, NULL);
+       }
+       return;
+}
+
+/*
+ * Enable or re-enable interrupts on a port.
+ *
+ * This routine is called from the port initialization code or from the
+ * helper thread as the real interrupt may be forced to turn off certain
+ * interrupt sources.
+ */
+void
+sili_port_interrupt_enable(struct sili_port *ap)
+{
+       u_int32_t data;
+
+       data =  SILI_PREG_INT_CCOMPLETE | SILI_PREG_INT_CERROR |
+               SILI_PREG_INT_PHYRDYCHG | SILI_PREG_INT_DEVEXCHG |
+               SILI_PREG_INT_DECODE | SILI_PREG_INT_CRC |
+               SILI_PREG_INT_HANDSHK | SILI_PREG_INT_PMCHANGE;
+       if (ap->ap_sc->sc_flags & SILI_F_SSNTF)
+               data |= SILI_PREG_INT_SDB;
+       sili_pwrite(ap, SILI_PREG_INT_ENABLE, data);
+}
+
+void
+sili_port_interrupt_redisable(struct sili_port *ap)
+{
+       u_int32_t data;
+
+       data = sili_read(ap->ap_sc, SILI_REG_GCTL);
+       data &= SILI_REG_GINT_PORTMASK;
+       data &= ~(1 << ap->ap_num);
+       sili_write(ap->ap_sc, SILI_REG_GCTL, data);
+}
+
+void
+sili_port_interrupt_reenable(struct sili_port *ap)
+{
+       u_int32_t data;
+
+       data = sili_read(ap->ap_sc, SILI_REG_GCTL);
+       data &= SILI_REG_GINT_PORTMASK;
+       data |= (1 << ap->ap_num);
+       sili_write(ap->ap_sc, SILI_REG_GCTL, data);
+}
+
+/*
+ * Run the port / target state machine from a main context.
+ *
+ * The state machine for the port is always run.
+ *
+ * If atx is non-NULL run the state machine for a particular target.
+ * If atx is NULL run the state machine for all targets.
+ */
+void
+sili_port_state_machine(struct sili_port *ap, int initial)
+{
+       struct ata_port *at;
+       u_int32_t data;
+       int target;
+       int didsleep;
+       int loop;
+
+       /*
+        * State machine for port.  Note that CAM is not yet associated
+        * during the initial parallel probe and the port's probe state
+        * will not get past ATA_PROBE_NEED_IDENT.
+        */
+       {
+               if (initial == 0 && ap->ap_probe <= ATA_PROBE_NEED_HARD_RESET) {
+                       kprintf("%s: Waiting 10 seconds on insertion\n",
+                               PORTNAME(ap));
+                       sili_os_sleep(10000);
+                       initial = 1;
+               }
+               if (ap->ap_probe == ATA_PROBE_NEED_INIT)
+                       sili_port_init(ap, NULL);
+               if (ap->ap_probe == ATA_PROBE_NEED_HARD_RESET)
+                       sili_port_reset(ap, NULL, 1);
+               if (ap->ap_probe == ATA_PROBE_NEED_SOFT_RESET)
+                       sili_port_reset(ap, NULL, 0);
+               if (ap->ap_probe == ATA_PROBE_NEED_IDENT)
+                       sili_cam_probe(ap, NULL);
+       }
+       if (ap->ap_type != ATA_PORT_T_PM) {
+               if (ap->ap_probe == ATA_PROBE_FAILED) {
+                       sili_cam_changed(ap, NULL, 0);
+               } else if (ap->ap_probe >= ATA_PROBE_NEED_IDENT) {
+                       sili_cam_changed(ap, NULL, 1);
+               }
+               return;
+       }
+
+       /*
+        * Port Multiplier state machine.
+        *
+        * Get a mask of changed targets and combine with any runnable
+        * states already present.
+        */
+       for (loop = 0; ;++loop) {
+               if (sili_pm_read(ap, 15, SATA_PMREG_EINFO, &data)) {
+                       kprintf("%s: PM unable to read hot-plug bitmap\n",
+                               PORTNAME(ap));
+                       break;
+               }
+
+               /*
+                * Do at least one loop, then stop if no more state changes
+                * have occured.  The PM might not generate a new
+                * notification until we clear the entire bitmap.
+                */
+               if (loop && data == 0)
+                       break;
+
+               /*
+                * New devices showing up in the bitmap require some spin-up
+                * time before we start probing them.  Reset didsleep.  The
+                * first new device we detect will sleep before probing.
+                *
+                * This only applies to devices whos change bit is set in
+                * the data, and does not apply to the initial boot-time
+                * probe.
+                */
+               didsleep = 0;
+
+               for (target = 0; target < ap->ap_pmcount; ++target) {
+                       at = &ap->ap_ata[target];
+
+                       /*
+                        * Check the target state for targets behind the PM
+                        * which have changed state.  This will adjust
+                        * at_probe and set ATA_PORT_F_RESCAN
+                        *
+                        * We want to wait at least 10 seconds before probing
+                        * a newly inserted device.  If the check status
+                        * indicates a device is present and in need of a
+                        * hard reset, we make sure we have slept before
+                        * continuing.
+                        *
+                        * We also need to wait at least 1 second for the
+                        * PHY state to change after insertion, if we
+                        * haven't already waited the 10 seconds.
+                        *
+                        * NOTE: When pm_check_good finds a good port it
+                        *       typically starts us in probe state
+                        *       NEED_HARD_RESET rather than INIT.
+                        */
+                       if (data & (1 << target)) {
+                               if (initial == 0 && didsleep == 0)
+                                       sili_os_sleep(1000);
+                               sili_pm_check_good(ap, target);
+                               if (initial == 0 && didsleep == 0 &&
+                                   at->at_probe <= ATA_PROBE_NEED_HARD_RESET
+                               ) {
+                                       didsleep = 1;
+                                       kprintf("%s: Waiting 10 seconds on insertion\n", PORTNAME(ap));
+                                       sili_os_sleep(10000);
+                               }
+                       }
+
+                       /*
+                        * Report hot-plug events before the probe state
+                        * really gets hot.  Only actual events are reported
+                        * here to reduce spew.
+                        */
+                       if (data & (1 << target)) {
+                               kprintf("%s: HOTPLUG (PM) - ", ATANAME(ap, at));
+                               switch(at->at_probe) {
+                               case ATA_PROBE_NEED_INIT:
+                               case ATA_PROBE_NEED_HARD_RESET:
+                                       kprintf("Device inserted\n");
+                                       break;
+                               case ATA_PROBE_FAILED:
+                                       kprintf("Device removed\n");
+                                       break;
+                               default:
+                                       kprintf("Device probe in progress\n");
+                                       break;
+                               }
+                       }
+
+                       /*
+                        * Run through the state machine as necessary if
+                        * the port is not marked failed.
+                        *
+                        * The state machine may stop at NEED_IDENT if
+                        * CAM is not yet attached.
+                        *
+                        * Acquire exclusive access to the port while we
+                        * are doing this.  This prevents command-completion
+                        * from queueing commands for non-polled targets
+                        * inbetween our probe steps.  We need to do this
+                        * because the reset probes can generate severe PHY
+                        * and protocol errors and soft-brick the port.
+                        */
+                       if (at->at_probe != ATA_PROBE_FAILED &&
+                           at->at_probe != ATA_PROBE_GOOD) {
+                               sili_beg_exclusive_access(ap, at);
+                               if (at->at_probe == ATA_PROBE_NEED_INIT)
+                                       sili_port_init(ap, at);
+                               if (at->at_probe == ATA_PROBE_NEED_HARD_RESET)
+                                       sili_port_reset(ap, at, 1);
+                               if (at->at_probe == ATA_PROBE_NEED_SOFT_RESET)
+                                       sili_port_reset(ap, at, 0);
+                               if (at->at_probe == ATA_PROBE_NEED_IDENT)
+                                       sili_cam_probe(ap, at);
+                               sili_end_exclusive_access(ap, at);
+                       }
+
+                       /*
+                        * Add or remove from CAM
+                        */
+                       if (at->at_features & ATA_PORT_F_RESCAN) {
+                               at->at_features &= ~ATA_PORT_F_RESCAN;
+                               if (at->at_probe == ATA_PROBE_FAILED) {
+                                       sili_cam_changed(ap, at, 0);
+                               } else if (at->at_probe >= ATA_PROBE_NEED_IDENT) {
+                                       sili_cam_changed(ap, at, 1);
+                               }
+                       }
+                       data &= ~(1 << target);
+               }
+               if (data) {
+                       kprintf("%s: WARNING (PM): extra bits set in "
+                               "EINFO: %08x\n", PORTNAME(ap), data);
+                       while (target < SILI_MAX_PMPORTS) {
+                               sili_pm_check_good(ap, target);
+                               ++target;
+                       }
+               }
+       }
+}
+
+/*
+ * De-initialize and detach a port.
+ */
+void
+sili_port_free(struct sili_softc *sc, u_int port)
+{
+       struct sili_port                *ap = sc->sc_ports[port];
+       struct sili_ccb                 *ccb;
+
+       /*
+        * Ensure port is disabled and its interrupts are all flushed.
+        */
+       if (ap->ap_sc) {
+               sili_os_stop_port(ap);
+               sili_pwrite(ap, SILI_PREG_INT_DISABLE, SILI_PREG_INT_MASK);
+               sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_RESET);
+               sili_write(ap->ap_sc, SILI_REG_GCTL,
+                       sili_read(ap->ap_sc, SILI_REG_GCTL) &
+                       ~SILI_REG_GINT_PORTST(ap->ap_num));
+       }
+
+       if (ap->ap_ccbs) {
+               while ((ccb = sili_get_ccb(ap)) != NULL) {
+                       if (ccb->ccb_dmamap) {
+                               bus_dmamap_destroy(sc->sc_tag_data,
+                                                  ccb->ccb_dmamap);
+                               ccb->ccb_dmamap = NULL;
+                       }
+               }
+               if ((ccb = ap->ap_err_ccb) != NULL) {
+                       if (ccb->ccb_dmamap) {
+                               bus_dmamap_destroy(sc->sc_tag_data,
+                                                  ccb->ccb_dmamap);
+                               ccb->ccb_dmamap = NULL;
+                       }
+                       ap->ap_err_ccb = NULL;
+               }
+               kfree(ap->ap_ccbs, M_DEVBUF);
+               ap->ap_ccbs = NULL;
+       }
+
+       if (ap->ap_dmamem_sget) {
+               sili_dmamem_free(sc, ap->ap_dmamem_sget);
+               ap->ap_dmamem_sget = NULL;
+       }
+       if (ap->ap_ata) {
+               kfree(ap->ap_ata, M_DEVBUF);
+               ap->ap_ata = NULL;
+       }
+
+       /* bus_space(9) says we dont free the subregions handle */
+
+       kfree(ap, M_DEVBUF);
+       sc->sc_ports[port] = NULL;
+}
+
+/*
+ * Reset a port.
+ *
+ * If hard is 0 perform a softreset of the port.
+ * If hard is 1 perform a hard reset of the port.
+ * If hard is 2 perform a hard reset of the port and cycle the phy.
+ *
+ * If at is non-NULL an indirect port via a port-multiplier is being
+ * reset, otherwise a direct port is being reset.
+ *
+ * NOTE: Indirect ports can only be soft-reset.
+ */
+int
+sili_port_reset(struct sili_port *ap, struct ata_port *at, int hard)
+{
+       int rc;
+
+       if (hard) {
+               if (at)
+                       rc = sili_pm_hardreset(ap, at->at_target, hard);
+               else
+                       rc = sili_port_hardreset(ap, hard);
+       } else {
+               if (at)
+                       rc = sili_pm_softreset(ap, at->at_target);
+               else
+                       rc = sili_port_softreset(ap);
+       }
+       return(rc);
+}
+
+/*
+ * SILI soft reset, Section 10.4.1
+ *
+ * (at) will be NULL when soft-resetting a directly-attached device, and
+ * non-NULL when soft-resetting a device through a port multiplier.
+ *
+ * This function keeps port communications intact and attempts to generate
+ * a reset to the connected device using device commands.
+ */
+int
+sili_port_softreset(struct sili_port *ap)
+{
+       struct sili_ccb         *ccb = NULL;
+       struct sili_prb         *prb;
+       int                     error;
+       u_int32_t               sig;
+
+       error = EIO;
+
+       kprintf("%s: START SOFTRESET\n", PORTNAME(ap));
+
+       DPRINTF(SILI_D_VERBOSE, "%s: soft reset\n", PORTNAME(ap));
+
+       crit_enter();
+       ap->ap_state = AP_S_NORMAL;
+
+       /*
+        * Prep the special soft-reset SII command.
+        */
+       ccb = sili_get_err_ccb(ap);
+       ccb->ccb_done = sili_empty_done;
+       ccb->ccb_xa.flags = ATA_F_POLL;
+       ccb->ccb_xa.complete = sili_dummy_done;
+       ccb->ccb_xa.at = NULL;
+
+       prb = ccb->ccb_prb;
+       bzero(&prb->prb_h2d, sizeof(prb->prb_h2d));
+       prb->prb_h2d.flags = 0;
+       prb->prb_control = SILI_PRB_CTRL_SOFTRESET;
+       prb->prb_override = 0;
+
+       ccb->ccb_xa.state = ATA_S_PENDING;
+       ccb->ccb_xa.flags = 0;
+
+                               /* XXX */
+       if (sili_poll(ccb, 8000, sili_quick_timeout) != ATA_S_COMPLETE) {
+               kprintf("%s: First FIS failed\n", PORTNAME(ap));
+               goto err;
+       }
+
+       sig = (prb->prb_d2h.lba_high << 24) |
+             (prb->prb_d2h.lba_mid << 16) |
+             (prb->prb_d2h.lba_low << 8) |
+             (prb->prb_d2h.sector_count);
+       kprintf("%s: SOFTRESET SIGNATURE %08x\n", PORTNAME(ap), sig);
+
+       /*
+        * If the softreset is trying to clear a BSY condition after a
+        * normal portreset we assign the port type.
+        *
+        * If the softreset is being run first as part of the ccb error
+        * processing code then report if the device signature changed
+        * unexpectedly.
+        */
+       if (ap->ap_type == ATA_PORT_T_NONE) {
+               ap->ap_type = sili_port_signature(ap, NULL, sig);
+       } else {
+               if (sili_port_signature(ap, NULL, sig) != ap->ap_type) {
+                       kprintf("%s: device signature unexpectedly "
+                               "changed\n", PORTNAME(ap));
+                       error = EBUSY; /* XXX */
+               }
+       }
+       error = 0;
+err:
+       if (ccb != NULL) {
+               sili_put_err_ccb(ccb);
+       }
+
+       /*
+        * If we failed to softreset make the port quiescent, otherwise
+        * make sure the port's start/stop state matches what it was on
+        * entry.
+        *
+        * Don't kill the port if the softreset is on a port multiplier
+        * target, that would kill all the targets!
+        */
+       kprintf("%s: END SOFTRESET %d prob=%d state=%d\n", PORTNAME(ap), error, ap->ap_probe, ap->ap_state);
+       if (error) {
+               sili_port_hardstop(ap);
+               /* ap_probe set to failed */
+       } else {
+               ap->ap_probe = ATA_PROBE_NEED_IDENT;
+       }
+       crit_exit();
+
+       if (bootverbose)
+               kprintf("%s: END SOFTRESET\n", PORTNAME(ap));
+
+       return (error);
+}
+
+/*
+ * SILI port reset, Section 10.4.2
+ *
+ * This function does a hard reset of the port.  Note that the device
+ * connected to the port could still end-up hung.
+ */
+int
+sili_port_hardreset(struct sili_port *ap, int hard)
+{
+       u_int32_t r;
+       int     error;
+       int     loop;
+
+       DPRINTF(SILI_D_VERBOSE, "%s: port reset\n", PORTNAME(ap));
+
+       ap->ap_state = AP_S_NORMAL;
+       error = 0;
+
+       /*
+        * Issue Device Reset.
+        *
+        * NOTE:  Unlike Port Reset, the port ready signal will not
+        *        go active unless a device is established to be on
+        *        the port.
+        */
+       sili_pwrite(ap, SILI_PREG_SERR, -1);
+       sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_PMA);
+       sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_RESUME);
+       sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_DEVRESET);
+       if (sili_pwait_clr(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_DEVRESET)) {
+               kprintf("%s: hardreset failed to clear\n", PORTNAME(ap));
+       }
+
+       /*
+        * Try to determine if there is a device on the port.
+        *
+        * Give the device 3/10 second to at least be detected.
+        */
+       loop = 300;
+       while (loop > 0) {
+               r = sili_pread(ap, SILI_PREG_SSTS);
+               if (r & SILI_PREG_SSTS_DET)
+                       break;
+               loop -= sili_os_softsleep();
+       }
+       if (loop <= 0) {
+               if (bootverbose) {
+                       kprintf("%s: Port appears to be unplugged\n",
+                               PORTNAME(ap));
+               }
+               error = ENODEV;
+       }
+
+       /*
+        * There is something on the port.  Give the device 3 seconds
+        * to fully negotiate.
+        */
+       if (error == 0 &&
+           sili_pwait_eq(ap, 3000, SILI_PREG_SSTS,
+                         SILI_PREG_SSTS_DET, SILI_PREG_SSTS_DET_DEV)) {
+               if (bootverbose) {
+                       kprintf("%s: Device may be powered down\n",
+                               PORTNAME(ap));
+               }
+               error = ENODEV;
+       }
+
+       /*
+        * Wait for the port to become ready.
+        *
+        * This can take more then a second, give it 3 seconds.  If we
+        * succeed give the device another 3ms after that.
+        *
+        * NOTE: Port multipliers can do two things here.  First they can
+        *       return device-ready if a device is on target 0 and also
+        *       return the signature for that device.  If there is no
+        *       device on target 0 then BSY/DRQ is never cleared and
+        *       it never comes ready.
+        */
+       if (error == 0 && sili_pwait_set_to(ap, 3000, SILI_PREG_STATUS,
+                                           SILI_PREG_STATUS_READY)) {
+               /*
+                * The device is bricked or its a port multiplier and will
+                * not unbusy until we do the pmprobe CLO softreset sequence.
+                */
+               error = sili_port_pmprobe(ap);
+               if (error) {
+                       kprintf("%s: Device will not come ready\n",
+                               PORTNAME(ap));
+               } else {
+                       ap->ap_type = ATA_PORT_T_PM;
+               }
+       } else if (error == 0) {
+               /*
+                * The sili's hardreset doesn't return a signature (does it)?
+                * In anycase, set the type so the signature gets set by
+                * the softreset stage.
+                */
+               error = sili_port_pmprobe(ap);
+               if (error) {
+                       ap->ap_type = ATA_PORT_T_NONE;
+                       error = 0;
+               } else {
+                       ap->ap_type = ATA_PORT_T_PM;
+                       kprintf("%s: Port multiplier detected\n",
+                               PORTNAME(ap));
+               }
+       }
+
+       /*
+        * hard-stop the port if we failed.  This will set ap_probe
+        * to FAILED.
+        */
+       if (error) {
+               sili_port_hardstop(ap);
+               /* ap_probe set to failed */
+       } else {
+               if (ap->ap_type == ATA_PORT_T_PM)
+                       ap->ap_probe = ATA_PROBE_GOOD;
+               else
+                       ap->ap_probe = ATA_PROBE_NEED_SOFT_RESET;
+       }
+       return (error);
+}
+
+/*
+ * SILI port multiplier probe.  This routine is run by the hardreset code
+ * if it gets past the device detect.
+ *
+ * All we do here is call sili_pm_softreset().  The Sili chip does all the
+ * hard work for us.
+ *
+ * Return 0 on success, non-zero on failure.
+ */
+int
+sili_port_pmprobe(struct sili_port *ap)
+{
+       struct ata_port *at;
+       int error;
+       int i;
+
+       /*
+        * If we don't support port multipliers don't try to detect one.
+        */
+       if ((ap->ap_sc->sc_flags & SILI_F_SPM) == 0)
+               return (ENODEV);
+
+       /*
+        * The port may be unhappy from its hardreset if there's a PM
+        * but no device at target 0.  If we try to shove the softreset
+        * for target 15 down its throat it will pop a gasket.
+        *
+        * Reiniting the port.. kind of a soft reset of its command
+        * processor which otherwise does not effect the port registers,
+        * seems to fix the problem.
+        */
+       sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_PMA);
+       sili_port_reinit(ap);
+       ap->ap_state = AP_S_NORMAL;
+       error = sili_pm_softreset(ap, 15);
+       if (error == 0) {
+               ap->ap_ata[15].at_probe = ATA_PROBE_GOOD;
+       } else {
+               error = EBUSY;
+       }
+
+       kprintf("PMPROBE3 %d\n", error);
+
+       if (error == 0 && sili_pm_identify(ap)) {
+               kprintf("%s: PM - cannot identify port multiplier\n",
+                       PORTNAME(ap));
+               error = EBUSY;
+       }
+       kprintf("PMPROBE3 %d %d %d\n", error, ap->ap_probe, ap->ap_state);
+
+       /*
+        * If we probed the PM reset the state for the targets behind
+        * it so they get probed by the state machine.
+        */
+       if (error == 0) {
+               for (i = 0; i < SILI_MAX_PMPORTS; ++i) {
+                       at = &ap->ap_ata[i];
+                       at->at_probe = ATA_PROBE_NEED_INIT;
+                       at->at_features |= ATA_PORT_F_RESCAN;
+                       at->at_features &= ~ATA_PORT_F_READLOG;
+               }
+       }
+
+       /*
+        * If we failed turn off PMA, otherwise identify the port multiplier.
+        * CAM will iterate the devices.
+        */
+       if (error)
+               sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_PMA);
+       return(error);
+}
+
+/*
+ * Hard-stop on hot-swap device removal.  See 10.10.1
+ *
+ * Place the port in a mode that will allow it to detect hot-swap insertions.
+ * This is a bit imprecise because just setting-up SCTL to DET_INIT doesn't
+ * seem to do the job.
+ */
+void
+sili_port_hardstop(struct sili_port *ap)
+{
+       struct sili_ccb *ccb;
+       struct ata_port *at;
+       int i;
+       int slot;
+
+       ap->ap_state = AP_S_FATAL_ERROR;
+       ap->ap_probe = ATA_PROBE_FAILED;
+       ap->ap_type = ATA_PORT_T_NONE;
+
+       /*
+        * Clean up AT sub-ports on SATA port.
+        */
+       for (i = 0; ap->ap_ata && i < SILI_MAX_PMPORTS; ++i) {
+               at = &ap->ap_ata[i];
+               at->at_type = ATA_PORT_T_NONE;
+               at->at_probe = ATA_PROBE_FAILED;
+               at->at_features &= ~ATA_PORT_F_READLOG;
+       }
+
+       /*
+        * Kill the port.  Don't bother waiting for it to transition
+        * back up.
+        */
+       sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_RESET);
+       if (sili_pread(ap, SILI_PREG_STATUS) & SILI_PREG_STATUS_READY) {
+               kprintf("%s: Port will not go into reset\n",
+                       PORTNAME(ap));
+       }
+       sili_os_sleep(10);
+       sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_RESUME);
+       sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_RESET);
+
+       /*
+        * Turn off port-multiplier control bit
+        */
+       sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_PMA);
+
+       /*
+        * Clean up the command list.
+        */
+       while (ap->ap_active) {
+               slot = ffs(ap->ap_active) - 1;
+               ap->ap_active &= ~(1 << slot);
+               ap->ap_expired &= ~(1 << slot);
+               --ap->ap_active_cnt;
+               ccb = &ap->ap_ccbs[slot];
+               if (ccb->ccb_xa.flags & ATA_F_TIMEOUT_RUNNING) {
+                       callout_stop(&ccb->ccb_timeout);
+                       ccb->ccb_xa.flags &= ~ATA_F_TIMEOUT_RUNNING;
+               }
+               ccb->ccb_xa.flags &= ~(ATA_F_TIMEOUT_DESIRED |
+                                      ATA_F_TIMEOUT_EXPIRED);
+               ccb->ccb_xa.state = ATA_S_TIMEOUT;
+               ccb->ccb_done(ccb);
+               ccb->ccb_xa.complete(&ccb->ccb_xa);
+       }
+       while ((ccb = TAILQ_FIRST(&ap->ap_ccb_pending)) != NULL) {
+               TAILQ_REMOVE(&ap->ap_ccb_pending, ccb, ccb_entry);
+               ccb->ccb_xa.state = ATA_S_TIMEOUT;
+               ccb->ccb_xa.flags &= ~ATA_F_TIMEOUT_DESIRED;
+               ccb->ccb_done(ccb);
+               ccb->ccb_xa.complete(&ccb->ccb_xa);
+       }
+       KKASSERT(ap->ap_active_cnt == 0);
+
+       /*
+        * Put the port into a listen mode, we want to get insertion/removal
+        * events.
+        */
+       sili_port_listen(ap);
+}
+
+/*
+ * Place port into a listen mode for hotplug events only.  The port has
+ * already been reset and the command processor may not be ready due
+ * to the lack of a device.
+ */
+void
+sili_port_listen(struct sili_port *ap)
+{
+       u_int32_t data;
+
+#if 1
+       data = SILI_PREG_SCTL_SPM_NONE |
+              SILI_PREG_SCTL_IPM_NONE |
+              SILI_PREG_SCTL_SPD_NONE |
+              SILI_PREG_SCTL_DET_INIT;
+       if (SiliForceGen1 & (1 << ap->ap_num)) {
+               data &= ~SILI_PREG_SCTL_SPD_NONE;
+               data |= SILI_PREG_SCTL_SPD_GEN1;
+       }
+#endif
+       sili_pwrite(ap, SILI_PREG_SERR, -1);
+       sili_pwrite(ap, SILI_PREG_INT_ENABLE, SILI_PREG_INT_PHYRDYCHG |
+                                             SILI_PREG_INT_DEVEXCHG);
+}
+
+/*
+ * Figure out what type of device is connected to the port, ATAPI or
+ * DISK.
+ */
+int
+sili_port_signature(struct sili_port *ap, struct ata_port *at, u_int32_t sig)
+{
+       if (bootverbose)
+               kprintf("%s: sig %08x\n", ATANAME(ap, at), sig);
+       if ((sig & 0xffff0000) == (SATA_SIGNATURE_ATAPI & 0xffff0000)) {
+               return(ATA_PORT_T_ATAPI);
+       } else if ((sig & 0xffff0000) ==
+                (SATA_SIGNATURE_PORT_MULTIPLIER & 0xffff0000)) {
+               return(ATA_PORT_T_PM);
+       } else {
+               return(ATA_PORT_T_DISK);
+       }
+}
+
+/*
+ * Load the DMA descriptor table for a CCB's buffer.
+ *
+ * NOTE: ATA_F_PIO is auto-selected by sili part.
+ */
+int
+sili_load_prb(struct sili_ccb *ccb)
+{
+       struct sili_port                *ap = ccb->ccb_port;
+       struct sili_softc               *sc = ap->ap_sc;
+       struct ata_xfer                 *xa = &ccb->ccb_xa;
+       struct sili_prb                 *prb = ccb->ccb_prb;
+       struct sili_sge                 *sge;
+       bus_dmamap_t                    dmap = ccb->ccb_dmamap;
+       int                             error;
+
+       /*
+        * Set up the PRB.  Set up the SGE that links to our
+        * SGE array.  We do not use the limited number of SGE's
+        * in the Sili's LRAM.
+        */
+       prb->prb_xfer_count = 0;
+       prb->prb_control = 0;
+       prb->prb_override = 0;
+       sge = (ccb->ccb_xa.flags & ATA_F_PACKET) ?
+               &prb->prb_sge_packet : &prb->prb_sge_normal;
+       if (xa->datalen == 0) {
+               sge->sge_flags = SILI_SGE_FLAGS_TRM | SILI_SGE_FLAGS_DRD;
+               sge->sge_count = 0;
+               return (0);
+       }
+
+       if (ccb->ccb_xa.flags & ATA_F_READ)
+               prb->prb_control |= SILI_PRB_CTRL_READ;
+       if (ccb->ccb_xa.flags & ATA_F_WRITE)
+               prb->prb_control |= SILI_PRB_CTRL_WRITE;
+       sge->sge_flags = SILI_SGE_FLAGS_LNK;
+       sge->sge_count = 0;
+       sge->sge_paddr = ccb->ccb_sge_paddr;
+
+       /*
+        * Load our external sge array.
+        */
+       error = bus_dmamap_load(sc->sc_tag_data, dmap,
+                               xa->data, xa->datalen,
+                               sili_load_prb_callback,
+                               ccb,
+                               ((xa->flags & ATA_F_NOWAIT) ?
+                                   BUS_DMA_NOWAIT : BUS_DMA_WAITOK));
+       if (error != 0) {
+               kprintf("%s: error %d loading dmamap\n", PORTNAME(ap), error);
+               return (1);
+       }
+
+       bus_dmamap_sync(sc->sc_tag_data, dmap,
+                       (xa->flags & ATA_F_READ) ?
+                           BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
+
+       return (0);
+
+#ifdef DIAGNOSTIC
+diagerr:
+       bus_dmamap_unload(sc->sc_tag_data, dmap);
+       return (1);
+#endif
+}
+
+/*
+ * Callback from BUSDMA system to load the segment list.
+ *
+ * The scatter/gather table is loaded by the sili chip in blocks of
+ * four SGE's.  If a continuance is required the last entry in each
+ * block must point to the next block.
+ */
+static
+void
+sili_load_prb_callback(void *info, bus_dma_segment_t *segs, int nsegs,
+                       int error)
+{
+       struct sili_ccb *ccb = info;
+       struct sili_sge *sge;
+       int sgi;
+
+       KKASSERT(nsegs <= SILI_MAX_SGET);
+
+       sgi = 0;
+       sge = ccb->ccb_sge;
+       while (nsegs) {
+               if ((sgi & 3) == 3) {
+                       sge->sge_paddr = htole64(ccb->ccb_sge_paddr +
+                                                sizeof(*sge) * (sgi + 1));
+                       sge->sge_count = 0;
+                       sge->sge_flags = SILI_SGE_FLAGS_LNK;
+               } else {
+                       sge->sge_paddr = htole64(segs->ds_addr);
+                       sge->sge_count = htole32(segs->ds_len);
+                       sge->sge_flags = 0;
+                       --nsegs;
+                       ++segs;
+               }
+               ++sge;
+               ++sgi;
+       }
+       --sge;
+       sge->sge_flags |= SILI_SGE_FLAGS_TRM;
+}
+
+void
+sili_unload_prb(struct sili_ccb *ccb)
+{
+       struct sili_port                *ap = ccb->ccb_port;
+       struct sili_softc               *sc = ap->ap_sc;
+       struct ata_xfer                 *xa = &ccb->ccb_xa;
+       bus_dmamap_t                    dmap = ccb->ccb_dmamap;
+
+       if (xa->datalen != 0) {
+               bus_dmamap_sync(sc->sc_tag_data, dmap,
+                               (xa->flags & ATA_F_READ) ?
+                               BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
+
+               bus_dmamap_unload(sc->sc_tag_data, dmap);
+
+               if (ccb->ccb_xa.flags & ATA_F_NCQ)
+                       xa->resid = 0;
+               else
+                       xa->resid = xa->datalen -
+                                   le32toh(ccb->ccb_prb->prb_xfer_count);
+       }
+}
+
+/*
+ * Start a command and poll for completion.
+ *
+ * timeout is in ms and only counts once the command gets on-chip.
+ *
+ * Returns ATA_S_* state, compare against ATA_S_COMPLETE to determine
+ * that no error occured.
+ *
+ * NOTE: If the caller specifies a NULL timeout function the caller is
+ *      responsible for clearing hardware state on failure, but we will
+ *      deal with removing the ccb from any pending queue.
+ *
+ * NOTE: NCQ should never be used with this function.
+ *
+ * NOTE: If the port is in a failed state and stopped we do not try
+ *      to activate the ccb.
+ */
+int
+sili_poll(struct sili_ccb *ccb, int timeout,
+         void (*timeout_fn)(struct sili_ccb *))
+{
+       struct sili_port *ap = ccb->ccb_port;
+
+       if (ccb->ccb_port->ap_state == AP_S_FATAL_ERROR) {
+               ccb->ccb_xa.state = ATA_S_ERROR;
+               return(ccb->ccb_xa.state);
+       }
+
+       sili_start(ccb);
+
+       do {
+               sili_port_intr(ap, 1);
+               switch(ccb->ccb_xa.state) {
+               case ATA_S_ONCHIP:
+                       timeout -= sili_os_softsleep();
+                       break;
+               case ATA_S_PENDING:
+                       /*
+                        * The packet can get stuck on the pending queue
+                        * if the port refuses to come ready.
+                        */
+                       if (AP_F_EXCLUSIVE_ACCESS)
+                               timeout -= sili_os_softsleep();
+                       else
+                               sili_os_softsleep();
+                       sili_check_active_timeouts(ap);
+                       break;
+               default:
+                       return (ccb->ccb_xa.state);
+               }
+       } while (timeout > 0);
+
+       kprintf("%s: Poll timeout slot %d\n",
+               ATANAME(ap, ccb->ccb_xa.at),
+               ccb->ccb_slot);
+
+       timeout_fn(ccb);
+
+       return(ccb->ccb_xa.state);
+}
+
+/*
+ * When polling we have to check if the currently active CCB(s)
+ * have timed out as the callout will be deadlocked while we
+ * hold the port lock.
+ */
+void
+sili_check_active_timeouts(struct sili_port *ap)
+{
+       struct sili_ccb *ccb;
+       u_int32_t mask;
+       int tag;
+
+       mask = ap->ap_active;
+       while (mask) {
+               tag = ffs(mask) - 1;
+               mask &= ~(1 << tag);
+               ccb = &ap->ap_ccbs[tag];
+               if (ccb->ccb_xa.flags & ATA_F_TIMEOUT_EXPIRED) {
+                       sili_core_timeout(ccb);
+               }
+       }
+}
+
+static
+__inline
+void
+sili_start_timeout(struct sili_ccb *ccb)
+{
+       if (ccb->ccb_xa.flags & ATA_F_TIMEOUT_DESIRED) {
+               ccb->ccb_xa.flags |= ATA_F_TIMEOUT_RUNNING;
+               callout_reset(&ccb->ccb_timeout,
+                             (ccb->ccb_xa.timeout * hz + 999) / 1000,
+                             sili_ata_cmd_timeout_unserialized, ccb);
+       }
+}
+
+void
+sili_start(struct sili_ccb *ccb)
+{
+       struct sili_port                *ap = ccb->ccb_port;
+#if 0
+       struct sili_softc               *sc = ap->ap_sc;
+#endif
+
+       KKASSERT(ccb->ccb_xa.state == ATA_S_PENDING);
+
+       /*
+        * Sync our SGE table and PRB
+        */
+       bus_dmamap_sync(ap->ap_dmamem_sget->adm_tag,
+                       ap->ap_dmamem_sget->adm_map,
+                       BUS_DMASYNC_PREWRITE);
+
+       /*
+        * XXX dmamap for PRB XXX  BUS_DMASYNC_PREWRITE
+        */
+
+       /*
+        * Controller will update shared memory!
+        * XXX bus_dmamap_sync ... BUS_DMASYNC_PREREAD ...
+        */
+       /* Prepare RFIS area for write by controller */
+
+       /*
+        * There's no point trying to optimize this, it only shaves a few
+        * nanoseconds so just queue the command and call our generic issue.
+        */
+       sili_issue_pending_commands(ap, ccb);
+}
+
+/*
+ * While holding the port lock acquire exclusive access to the port.
+ *
+ * This is used when running the state machine to initialize and identify
+ * targets over a port multiplier.  Setting exclusive access prevents
+ * sili_port_intr() from activating any requests sitting on the pending
+ * queue.
+ */
+void
+sili_beg_exclusive_access(struct sili_port *ap, struct ata_port *at)
+{
+       KKASSERT((ap->ap_flags & AP_F_EXCLUSIVE_ACCESS) == 0);
+       ap->ap_flags |= AP_F_EXCLUSIVE_ACCESS;
+       while (ap->ap_active) {
+               sili_port_intr(ap, 1);
+               sili_os_softsleep();
+       }
+}
+
+void
+sili_end_exclusive_access(struct sili_port *ap, struct ata_port *at)
+{
+       KKASSERT((ap->ap_flags & AP_F_EXCLUSIVE_ACCESS) != 0);
+       ap->ap_flags &= ~AP_F_EXCLUSIVE_ACCESS;
+       sili_issue_pending_commands(ap, NULL);
+}
+
+/*
+ * If ccb is not NULL enqueue and/or issue it.
+ *
+ * If ccb is NULL issue whatever we can from the queue.  However, nothing
+ * new is issued if the exclusive access flag is set or expired ccb's are
+ * present.
+ *
+ * If existing commands are still active (ap_active) we can only
+ * issue matching new commands.
+ */
+void
+sili_issue_pending_commands(struct sili_port *ap, struct sili_ccb *ccb)
+{
+       /*
+        * Enqueue the ccb.
+        *
+        * If just running the queue and in exclusive access mode we
+        * just return.  Also in this case if there are any expired ccb's
+        * we want to clear the queue so the port can be safely stopped.
+        *
+        * XXX sili chip - expiration needs to be per-target if PM supports
+        *      FBSS?
+        */
+       if (ccb) {
+               TAILQ_INSERT_TAIL(&ap->ap_ccb_pending, ccb, ccb_entry);
+       } else if ((ap->ap_flags & AP_F_EXCLUSIVE_ACCESS) || ap->ap_expired) {
+               return;
+       }
+
+       /*
+        * Pull the next ccb off the queue and run it if possible.
+        * If the port is not ready to accept commands enable the
+        * ready interrupt instead of starting a new command.
+        *
+        * XXX limit ncqdepth for attached devices behind PM
+        */
+       while ((ccb = TAILQ_FIRST(&ap->ap_ccb_pending)) != NULL) {
+               if ((sili_pread(ap, SILI_PREG_STATUS) &
+                   SILI_PREG_STATUS_READY) == 0) {
+                       kprintf("%s: slot %d NOT READY\n",
+                               ATANAME(ap, ccb->ccb_xa.at), ccb->ccb_slot);
+                       sili_pwrite(ap, SILI_PREG_INT_ENABLE,
+                                   SILI_PREG_INT_READY);
+                       break;
+               }
+               TAILQ_REMOVE(&ap->ap_ccb_pending, ccb, ccb_entry);
+               ccb->ccb_xa.state = ATA_S_ONCHIP;
+               ap->ap_active |= 1 << ccb->ccb_slot;
+               ap->ap_active_cnt++;
+               sili_pwrite(ap, SILI_PREG_CMD_FIFO, ccb->ccb_slot);
+               sili_start_timeout(ccb);
+       }
+}
+
+void
+sili_intr(void *arg)
+{
+       struct sili_softc       *sc = arg;
+       struct sili_port        *ap;
+       u_int32_t               gint;
+       int                     port;
+
+       /*
+        * Check if the master enable is up, and whether any interrupts are
+        * pending.
+        *
+        * Clear the ints we got.
+        */
+       if ((sc->sc_flags & SILI_F_INT_GOOD) == 0)
+               return;
+       gint = sili_read(sc, SILI_REG_GINT);
+       if (gint == 0 || gint == 0xffffffff)
+               return;
+       sili_write(sc, SILI_REG_GINT, gint);
+
+       /*
+        * Process interrupts for each port in a non-blocking fashion.
+        */
+       while (gint & SILI_REG_GINT_PORTMASK) {
+               port = ffs(gint) - 1;
+               ap = sc->sc_ports[port];
+               if (ap) {
+                       if (sili_os_lock_port_nb(ap) == 0) {
+                               sili_port_intr(ap, 0);
+                               sili_os_unlock_port(ap);
+                       } else {
+                               sili_port_interrupt_redisable(ap);
+                               sili_os_signal_port_thread(ap, AP_SIGF_PORTINT);
+                       }
+               }
+               gint &= ~(1 << port);
+       }
+}
+
+/*
+ * Core called from helper thread.
+ */
+void
+sili_port_thread_core(struct sili_port *ap, int mask)
+{
+       /*
+        * Process any expired timedouts.
+        */
+       sili_os_lock_port(ap);
+       if (mask & AP_SIGF_TIMEOUT) {
+               sili_check_active_timeouts(ap);
+       }
+
+       /*
+        * Process port interrupts which require a higher level of
+        * intervention.
+        */
+       if (mask & AP_SIGF_PORTINT) {
+               sili_port_intr(ap, 1);
+               sili_port_interrupt_reenable(ap);
+               sili_os_unlock_port(ap);
+       } else {
+               sili_os_unlock_port(ap);
+       }
+}
+
+/*
+ * Core per-port interrupt handler.
+ *
+ * If blockable is 0 we cannot call sili_os_sleep() at all and we can only
+ * deal with normal command completions which do not require blocking.
+ */
+void
+sili_port_intr(struct sili_port *ap, int blockable)
+{
+       struct sili_softc       *sc = ap->ap_sc;
+       u_int32_t               is;
+       int                     slot;
+       struct sili_ccb         *ccb = NULL;
+       struct ata_port         *ccb_at = NULL;
+#ifdef DIAGNOSTIC
+       u_int32_t               tmp;
+#endif
+       u_int32_t               active;
+       const u_int32_t         blockable_mask = SILI_PREG_IST_PHYRDYCHG |
+                                                SILI_PREG_IST_DEVEXCHG |
+                                                SILI_PREG_IST_CERROR |
+                                                SILI_PREG_IST_DECODE |
+                                                SILI_PREG_IST_CRC |
+                                                SILI_PREG_IST_HANDSHK;
+       const u_int32_t         fatal_mask     = SILI_PREG_IST_PHYRDYCHG |
+                                                SILI_PREG_IST_DEVEXCHG |
+                                                SILI_PREG_IST_DECODE |
+                                                SILI_PREG_IST_CRC |
+                                                SILI_PREG_IST_HANDSHK;
+
+       enum { NEED_NOTHING, NEED_HOTPLUG_INSERT,
+              NEED_HOTPLUG_REMOVE } need = NEED_NOTHING;
+
+       /*
+        * NOTE: CCOMPLETE was automatically cleared when we read INT_STATUS.
+        */
+       is = sili_pread(ap, SILI_PREG_INT_STATUS);
+       is &= SILI_PREG_IST_MASK;
+       if (is & SILI_PREG_IST_CCOMPLETE)
+               sili_pwrite(ap, SILI_PREG_INT_STATUS, SILI_PREG_IST_CCOMPLETE);
+
+       /*
+        * If we can't block then we can't handle these here.  Disable
+        * the interrupts in question so we don't live-lock, the helper
+        * thread will re-enable them.
+        *
+        * If the port is in a completely failed state we do not want
+        * to drop through to failed-command-processing if blockable is 0,
+        * just let the thread deal with it all.
+        *
+        * Otherwise we fall through and still handle DHRS and any commands
+        * which completed normally.  Even if we are errored we haven't
+        * stopped the port yet so CI/SACT are still good.
+        */
+       if (blockable == 0) {
+               if (ap->ap_state == AP_S_FATAL_ERROR) {
+                       sili_port_interrupt_redisable(ap);
+                       sili_os_signal_port_thread(ap, AP_SIGF_PORTINT);
+                       /*is &= ~blockable_mask;*/
+                       return;
+               }
+               if (is & blockable_mask) {
+                       sili_port_interrupt_redisable(ap);
+                       sili_os_signal_port_thread(ap, AP_SIGF_PORTINT);
+                       /*is &= ~blockable_mask;*/
+                       return;
+               }
+       }
+
+       if (is & SILI_PREG_IST_CERROR) {
+               /*
+                * Command failed (blockable).
+                *
+                * This stops command processing.  We can extract the PM
+                * target from the PMP field in SILI_PREG_CONTEXT.  The
+                * tag is not necessarily valid so don't use that.
+                *
+                * We must then expire all CCB's for that target and resume
+                * processing if any other targets have active commands.
+                * Particular error codes can be recovered by reading the LOG
+                * page.
+                *
+                * The expire handling code will do the rest, which is
+                * basically to reset the port once the only active
+                * commands remaining are all expired.
+                */
+               u_int32_t error;
+               int       target;
+               int       resume = 1;
+
+               target = (sili_pread(ap, SILI_PREG_CONTEXT) >>
+                         SILI_PREG_CONTEXT_PMPORT_SHIFT) &
+                         SILI_PREG_CONTEXT_PMPORT_MASK;
+               sili_pwrite(ap, SILI_PREG_INT_STATUS, SILI_PREG_IST_CERROR);
+               active = ap->ap_active & ~ap->ap_expired;
+               error = sili_pread(ap, SILI_PREG_CERROR);
+               kprintf("%s.%d target error %d active=%08x hactive=%08x "
+                       "SERR=%b\n",
+                       PORTNAME(ap), target, error,
+                       active, sili_pread(ap, SILI_PREG_SLOTST),
+                       sili_pread(ap, SILI_PREG_SERR), SILI_PFMT_SERR);
+
+               while (active) {
+                       slot = ffs(active) - 1;
+                       ccb = &ap->ap_ccbs[slot];
+                       if ((ccb_at = ccb->ccb_xa.at) == NULL)
+                               ccb_at = &ap->ap_ata[0];
+                       if (target == ccb_at->at_target) {
+                               kprintf("%s kill ccb slot %d\n",
+                                       ATANAME(ap, ccb->ccb_xa.at), slot);
+                               if (ccb->ccb_xa.flags & ATA_F_NCQ &&
+                                   (error == SILI_PREG_CERROR_DEVICE ||
+                                    error == SILI_PREG_CERROR_SDBERROR)) {
+                                       ccb_at->at_features |= ATA_PORT_F_READLOG;
+                               }
+                               if (sili_core_timeout(ccb) == 0)
+                                       resume = 0;
+                       }
+                       active &= ~(1 << slot);
+               }
+
+               /*
+                * Resume will be 0 if the timeout reinited and restarted
+                * the port.  Otherwise we resume the port to allow other
+                * commands to complete.
+                */
+               kprintf("%s.%d remain=%08x resume=%d\n",
+                       PORTNAME(ap), target,
+                       ap->ap_active & ~ap->ap_expired, resume);
+               if (resume)
+                       sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_RESUME);
+       }
+
+       /*
+        * Device notification to us (non-blocking)
+        *
+        * This is interrupt status SILIPREG_IST_SDB
+        *
+        * NOTE!  On some parts notification bits can get set without
+        *        generating an interrupt.  It is unclear whether this is
+        *        a bug in the PM (sending a DTOH device setbits with 'N' set
+        *        and 'I' not set), or a bug in the host controller.
+        *
+        *        It only seems to occur under load.
+        */
+       if (sc->sc_flags & SILI_F_SSNTF) {
+               u_int32_t data;
+               const char *xstr;
+
+               data = sili_pread(ap, SILI_PREG_SNTF);
+               if (is & SILI_PREG_IST_SDB) {
+                       sili_pwrite(ap, SILI_PREG_INT_STATUS,
+                                   SILI_PREG_IST_SDB);
+                       is &= ~SILI_PREG_IST_SDB;
+                       xstr = " (no SDBS!)";
+               } else {
+                       xstr = "";
+               }
+               if (data) {
+                       kprintf("%s: NOTIFY %08x%s\n",
+                               PORTNAME(ap), data, xstr);
+                       sili_pwrite(ap, SILI_PREG_SNTF, data);
+                       sili_cam_changed(ap, NULL, -1);
+               }
+       }
+
+       /*
+        * Port change (hot-plug) (blockable).
+        *
+        * A PCS interrupt will occur on hot-plug once communication is
+        * established.
+        *
+        * A PRCS interrupt will occur on hot-unplug (and possibly also
+        * on hot-plug).
+        *
+        * XXX We can then check the CPS (Cold Presence State) bit, if
+        * supported, to determine if a device is plugged in or not and do
+        * the right thing.
+        *
+        * WARNING:  A PCS interrupt is cleared by clearing DIAG_X, and
+        *           can also occur if an unsolicited COMINIT is received.
+        *           If this occurs command processing is automatically
+        *           stopped (CR goes inactive) and the port must be stopped
+        *           and restarted.
+        */
+       if (is & (SILI_PREG_IST_PHYRDYCHG | SILI_PREG_IST_DEVEXCHG)) {
+               /* XXX */
+               sili_pwrite(ap, SILI_PREG_SERR,
+                       (SILI_PREG_SERR_DIAG_N | SILI_PREG_SERR_DIAG_X));
+               sili_pwrite(ap, SILI_PREG_INT_STATUS,
+                   is & (SILI_PREG_IST_PHYRDYCHG | SILI_PREG_IST_DEVEXCHG));
+
+               is &= ~(SILI_PREG_IST_PHYRDYCHG | SILI_PREG_IST_DEVEXCHG);
+               kprintf("%s: Port change\n", PORTNAME(ap));
+
+               switch (sili_pread(ap, SILI_PREG_SSTS) & SILI_PREG_SSTS_DET) {
+               case SILI_PREG_SSTS_DET_DEV:
+                       if (ap->ap_type == ATA_PORT_T_NONE &&
+                           ap->ap_probe == ATA_PROBE_FAILED) {
+                               need = NEED_HOTPLUG_INSERT;
+                               goto fatal;
+                       }
+                       break;
+               default:
+                       kprintf("%s: Device lost\n", PORTNAME(ap));
+                       if (ap->ap_type != ATA_PORT_T_NONE) {
+                               need = NEED_HOTPLUG_REMOVE;
+                               goto fatal;
+                       }
+                       break;
+               }
+       }
+
+       /*
+        * Check for remaining errors - they are fatal. (blockable)
+        */
+       if (is & fatal_mask) {
+               u_int32_t serr;
+
+               sili_pwrite(ap, SILI_PREG_INT_STATUS, is & fatal_mask);
+
+               serr = sili_pread(ap, SILI_PREG_SERR);
+               kprintf("%s: Unrecoverable errors (IS: %b, SERR: %b), "
+                       "disabling port.\n",
+                       PORTNAME(ap),
+                       is, SILI_PFMT_INT_STATUS,
+                       serr, SILI_PFMT_SERR
+               );
+               is &= ~fatal_mask;
+               /* XXX try recovery first */
+               goto fatal;
+       }
+
+       /*
+        * Fail all outstanding commands if we know the port won't recover.
+        *
+        * We may have a ccb_at if the failed command is known and was
+        * being sent to a device over a port multiplier (PM).  In this
+        * case if the port itself has not completely failed we fail just
+        * the commands related to that target.
+        */
+       if (ap->ap_state == AP_S_FATAL_ERROR && ap->ap_active) {
+fatal:
+               kprintf("%s: Interrupt, fatal error\n", PORTNAME(ap));
+               ap->ap_state = AP_S_FATAL_ERROR;
+/*failall:*/
+               /*
+                * Error all the active slots.  If running across a PM
+                * try to error out just the slots related to the target.
+                */
+               active = ap->ap_active & ~ap->ap_expired;
+
+               while (active) {
+                       slot = ffs(active) - 1;
+                       kprintf("%s: Killing slot %d\n", PORTNAME(ap), slot);
+                       active &= ~(1 << slot);
+                       ccb = &ap->ap_ccbs[slot];
+                       sili_core_timeout(ccb);
+               }
+       }
+
+       /*
+        * CCB completion (non blocking).
+        *
+        * CCB completion is detected by noticing the slot bit in
+        * the port slot status register has cleared while the bit
+        * is still set in our ap_active variable.
+        *
+        * When completing expired events we must remember to reinit
+        * the port once everything is clear.
+        */
+       active = ap->ap_active & ~sili_pread(ap, SILI_PREG_SLOTST);
+
+       while (active) {
+               slot = ffs(active) - 1;
+               ccb = &ap->ap_ccbs[slot];
+
+               DPRINTF(SILI_D_INTR, "%s: slot %d is complete%s\n",
+                   PORTNAME(ap), slot, ccb->ccb_xa.state == ATA_S_ERROR ?
+                   " (error)" : "");
+
+               active &= ~(1 << slot);
+
+               /*
+                * XXX sync POSTREAD for return data?
+                */
+               ap->ap_active &= ~(1 << ccb->ccb_slot);
+               --ap->ap_active_cnt;
+
+               /*
+                * Complete the ccb.  If the ccb was marked expired it
+                * may or may not have been cleared from the port,
+                * make sure we mark it as having timed out.
+                */
+               if (ap->ap_expired & (1 << ccb->ccb_slot)) {
+                       ap->ap_expired &= ~(1 << ccb->ccb_slot);
+                       ccb->ccb_xa.state = ATA_S_TIMEOUT;
+                       ccb->ccb_done(ccb);
+                       ccb->ccb_xa.complete(&ccb->ccb_xa);
+               } else {
+                       if (ccb->ccb_xa.state == ATA_S_ONCHIP)
+                               ccb->ccb_xa.state = ATA_S_COMPLETE;
+                       ccb->ccb_done(ccb);
+               }
+       }
+       if (is & SILI_PREG_IST_READY) {
+               is &= ~SILI_PREG_IST_READY;
+               sili_pwrite(ap, SILI_PREG_INT_DISABLE, SILI_PREG_INT_READY);
+               sili_pwrite(ap, SILI_PREG_INT_STATUS, SILI_PREG_IST_READY);
+       }
+
+       /*
+        * If we had expired commands and were waiting for
+        * remaining commands to complete, and they have now
+        * completed, we can reinit the port.
+        *
+        * This will also clean out the expired commands.
+        * The timeout code also calls sili_port_reinit() if
+        * the only commands remaining after a timeout are all
+        * now expired commands.
+        *
+        * Otherwise just reissue.
+        */
+       if (ap->ap_expired && ap->ap_active == ap->ap_expired)
+               sili_port_reinit(ap);
+       else
+               sili_issue_pending_commands(ap, NULL);
+
+       /*
+        * Cleanup.  Will not be set if non-blocking.
+        */
+       switch(need) {
+       case NEED_HOTPLUG_INSERT:
+               /*
+                * A hot-plug insertion event has occured and all
+                * outstanding commands have already been revoked.
+                *
+                * Don't recurse if this occurs while we are
+                * resetting the port.
+                *
+                * Place the port in a continuous COMRESET state
+                * until the INIT code gets to it.
+                */
+               kprintf("%s: HOTPLUG - Device inserted\n",
+                       PORTNAME(ap));
+               ap->ap_probe = ATA_PROBE_NEED_INIT;
+               sili_cam_changed(ap, NULL, -1);
+               break;
+       case NEED_HOTPLUG_REMOVE:
+               /*
+                * A hot-plug removal event has occured and all
+                * outstanding commands have already been revoked.
+                *
+                * Don't recurse if this occurs while we are
+                * resetting the port.
+                */
+               kprintf("%s: HOTPLUG - Device removed\n",
+                       PORTNAME(ap));
+               sili_port_hardstop(ap);
+               /* ap_probe set to failed */
+               sili_cam_changed(ap, NULL, -1);
+               break;
+       default:
+               break;
+       }
+}
+
+struct sili_ccb *
+sili_get_ccb(struct sili_port *ap)
+{
+       struct sili_ccb                 *ccb;
+
+       lockmgr(&ap->ap_ccb_lock, LK_EXCLUSIVE);
+       ccb = TAILQ_FIRST(&ap->ap_ccb_free);
+       if (ccb != NULL) {
+               KKASSERT(ccb->ccb_xa.state == ATA_S_PUT);
+               TAILQ_REMOVE(&ap->ap_ccb_free, ccb, ccb_entry);
+               ccb->ccb_xa.state = ATA_S_SETUP;
+               ccb->ccb_xa.at = NULL;
+       }
+       lockmgr(&ap->ap_ccb_lock, LK_RELEASE);
+
+       return (ccb);
+}
+
+void
+sili_put_ccb(struct sili_ccb *ccb)
+{
+       struct sili_port                *ap = ccb->ccb_port;
+
+#ifdef DIAGNOSTIC
+       if (ccb->ccb_xa.state != ATA_S_COMPLETE &&
+           ccb->ccb_xa.state != ATA_S_TIMEOUT &&
+           ccb->ccb_xa.state != ATA_S_ERROR) {
+               kprintf("%s: invalid ata_xfer state %02x in sili_put_ccb, "
+                       "slot %d\n",
+                       PORTNAME(ccb->ccb_port), ccb->ccb_xa.state,
+                       ccb->ccb_slot);
+       }
+#endif
+
+       ccb->ccb_xa.state = ATA_S_PUT;
+       lockmgr(&ap->ap_ccb_lock, LK_EXCLUSIVE);
+       TAILQ_INSERT_TAIL(&ap->ap_ccb_free, ccb, ccb_entry);
+       lockmgr(&ap->ap_ccb_lock, LK_RELEASE);
+}
+
+struct sili_ccb *
+sili_get_err_ccb(struct sili_port *ap)
+{
+       struct sili_ccb *err_ccb;
+
+       KKASSERT(sili_pread(ap, SILI_PREG_CI) == 0);
+       KKASSERT((ap->ap_flags & AP_F_ERR_CCB_RESERVED) == 0);
+       ap->ap_flags |= AP_F_ERR_CCB_RESERVED;
+
+#ifdef DIAGNOSTIC
+       KKASSERT(ap->ap_err_busy == 0);
+       ap->ap_err_busy = 1;
+#endif
+       /*
+        * Grab a CCB to use for error recovery.  This should never fail, as
+        * we ask atascsi to reserve one for us at init time.
+        */
+       err_ccb = ap->ap_err_ccb;
+       KKASSERT(err_ccb != NULL);
+       err_ccb->ccb_xa.flags = 0;
+       err_ccb->ccb_done = sili_empty_done;
+
+       return err_ccb;
+}
+
+void
+sili_put_err_ccb(struct sili_ccb *ccb)
+{
+       struct sili_port *ap = ccb->ccb_port;
+
+#ifdef DIAGNOSTIC
+       KKASSERT(ap->ap_err_busy);
+#endif
+       KKASSERT((ap->ap_flags & AP_F_ERR_CCB_RESERVED) != 0);
+
+       KKASSERT(ccb == ap->ap_err_ccb);
+
+#ifdef DIAGNOSTIC
+       ap->ap_err_busy = 0;
+#endif
+       ap->ap_flags &= ~AP_F_ERR_CCB_RESERVED;
+}
+
+/*
+ * Read log page to get NCQ error.
+ */
+int
+sili_port_read_ncq_error(struct sili_port *ap, int target)
+{
+       struct sili_ccb         *ccb;
+       struct ata_fis_h2d      *fis;
+       int                     rc = EIO;
+
+       DPRINTF(SILI_D_VERBOSE, "%s: read log page\n", PORTNAME(ap));
+
+       /* Prep error CCB for READ LOG EXT, page 10h, 1 sector. */
+       ccb = sili_get_err_ccb(ap);
+       ccb->ccb_done = sili_empty_done;
+       ccb->ccb_xa.flags = ATA_F_NOWAIT | ATA_F_READ | ATA_F_POLL;
+       ccb->ccb_xa.data = ap->ap_err_scratch;
+       ccb->ccb_xa.datalen = 512;
+       ccb->ccb_xa.complete = sili_dummy_done;
+       ccb->ccb_xa.at = &ap->ap_ata[target];
+       fis = &ccb->ccb_prb->prb_h2d;
+       bzero(fis, sizeof(*fis));
+
+       fis->type = ATA_FIS_TYPE_H2D;
+       fis->flags = ATA_H2D_FLAGS_CMD | target;
+       fis->command = ATA_C_READ_LOG_EXT;
+       fis->lba_low = 0x10;            /* queued error log page (10h) */
+       fis->sector_count = 1;          /* number of sectors (1) */
+       fis->sector_count_exp = 0;
+       fis->lba_mid = 0;               /* starting offset */
+       fis->lba_mid_exp = 0;
+       fis->device = 0;
+
+       if (sili_load_prb(ccb) != 0) {
+               rc = ENOMEM;    /* XXX caller must abort all commands */
+       } else {
+               ccb->ccb_xa.state = ATA_S_PENDING;
+               rc = sili_poll(ccb, 1000, sili_quick_timeout);
+       }
+
+       /* Abort our command, if it failed, by stopping command DMA. */
+       if (rc) {
+               kprintf("%s: log page read failed, slot %d was still active.\n",
+                       ATANAME(ap, ccb->ccb_xa.at), ccb->ccb_slot);
+       }
+
+       /* Done with the error CCB now. */
+       sili_unload_prb(ccb);
+       sili_put_err_ccb(ccb);
+
+       /* Extract failed register set and tags from the scratch space. */
+       if (rc == 0) {
+               struct ata_log_page_10h         *log;
+               int                             err_slot;
+
+               log = (struct ata_log_page_10h *)ap->ap_err_scratch;
+               if (log->err_regs.type & ATA_LOG_10H_TYPE_NOTQUEUED) {
+                       /* Not queued bit was set - wasn't an NCQ error? */
+                       kprintf("%s: read NCQ error page, but not an NCQ "
+                               "error?\n",
+                               PORTNAME(ap));
+                       rc = ESRCH;
+               } else {
+                       /* Copy back the log record as a D2H register FIS. */
+                       err_slot = log->err_regs.type &
+                                  ATA_LOG_10H_TYPE_TAG_MASK;
+                       ccb = &ap->ap_ccbs[err_slot];
+                       if (ap->ap_expired & (1 << ccb->ccb_slot)) {
+                               kprintf("%s: read NCQ error page ok\n",
+                                       ATANAME(ap, ccb->ccb_xa.at));
+                               memcpy(&ccb->ccb_prb->prb_d2h, &log->err_regs,
+                                       sizeof(struct ata_fis_d2h));
+                               ccb->ccb_prb->prb_d2h.type = ATA_FIS_TYPE_D2H;
+                               ccb->ccb_prb->prb_d2h.flags = 0;
+                       } else {
+                               kprintf("%s: error log slot %d did not match a failed ccb!\n", ATANAME(ccb->ccb_port, ccb->ccb_xa.at), err_slot);
+                       }
+               }
+       }
+
+       return (rc);
+}
+
+/*
+ * Allocate memory for various structures DMAd by hardware.  The maximum
+ * number of segments for these tags is 1 so the DMA memory will have a
+ * single physical base address.
+ */
+struct sili_dmamem *
+sili_dmamem_alloc(struct sili_softc *sc, bus_dma_tag_t tag)
+{
+       struct sili_dmamem *adm;
+       int     error;
+
+       adm = kmalloc(sizeof(*adm), M_DEVBUF, M_INTWAIT | M_ZERO);
+
+       error = bus_dmamem_alloc(tag, (void **)&adm->adm_kva,
+                                BUS_DMA_ZERO, &adm->adm_map);
+       if (error == 0) {
+               adm->adm_tag = tag;
+               error = bus_dmamap_load(tag, adm->adm_map,
+                                       adm->adm_kva,
+                                       bus_dma_tag_getmaxsize(tag),
+                                       sili_dmamem_saveseg, &adm->adm_busaddr,
+                                       0);
+       }
+       if (error) {
+               if (adm->adm_map) {
+                       bus_dmamap_destroy(tag, adm->adm_map);
+                       adm->adm_map = NULL;
+                       adm->adm_tag = NULL;
+                       adm->adm_kva = NULL;
+               }
+               kfree(adm, M_DEVBUF);
+               adm = NULL;
+       }
+       return (adm);
+}
+
+static
+void
+sili_dmamem_saveseg(void *info, bus_dma_segment_t *segs, int nsegs, int error)
+{
+       KKASSERT(error == 0);
+       KKASSERT(nsegs == 1);
+       *(bus_addr_t *)info = segs->ds_addr;
+}
+
+
+void
+sili_dmamem_free(struct sili_softc *sc, struct sili_dmamem *adm)
+{
+       if (adm->adm_map) {
+               bus_dmamap_unload(adm->adm_tag, adm->adm_map);
+               bus_dmamap_destroy(adm->adm_tag, adm->adm_map);
+               adm->adm_map = NULL;
+               adm->adm_tag = NULL;
+               adm->adm_kva = NULL;
+       }
+       kfree(adm, M_DEVBUF);
+}
+
+u_int32_t
+sili_read(struct sili_softc *sc, bus_size_t r)
+{
+       bus_space_barrier(sc->sc_iot, sc->sc_ioh, r, 4,
+                         BUS_SPACE_BARRIER_READ);
+       return (bus_space_read_4(sc->sc_iot, sc->sc_ioh, r));
+}
+
+void
+sili_write(struct sili_softc *sc, bus_size_t r, u_int32_t v)
+{
+       bus_space_write_4(sc->sc_iot, sc->sc_ioh, r, v);
+       bus_space_barrier(sc->sc_iot, sc->sc_ioh, r, 4,
+                         BUS_SPACE_BARRIER_WRITE);
+}
+
+u_int32_t
+sili_pread(struct sili_port *ap, bus_size_t r)
+{
+       bus_space_barrier(ap->ap_sc->sc_iot, ap->ap_ioh, r, 4,
+                         BUS_SPACE_BARRIER_READ);
+       return (bus_space_read_4(ap->ap_sc->sc_iot, ap->ap_ioh, r));
+}
+
+void
+sili_pwrite(struct sili_port *ap, bus_size_t r, u_int32_t v)
+{
+       bus_space_write_4(ap->ap_sc->sc_iot, ap->ap_ioh, r, v);
+       bus_space_barrier(ap->ap_sc->sc_iot, ap->ap_ioh, r, 4,
+                         BUS_SPACE_BARRIER_WRITE);
+}
+
+/*
+ * Wait up to (timeout) milliseconds for the masked port register to
+ * match the target.
+ *
+ * Timeout is in milliseconds.
+ */
+int
+sili_pwait_eq(struct sili_port *ap, int timeout,
+             bus_size_t r, u_int32_t mask, u_int32_t target)
+{
+       int     t;
+
+       /*
+        * Loop hard up to 100uS
+        */
+       for (t = 0; t < 100; ++t) {
+               if ((sili_pread(ap, r) & mask) == target)
+                       return (0);
+               sili_os_hardsleep(1);   /* us */
+       }
+
+       do {
+               timeout -= sili_os_softsleep();
+               if ((sili_pread(ap, r) & mask) == target)
+                       return (0);
+       } while (timeout > 0);
+       return (1);
+}
+
+int
+sili_wait_ne(struct sili_softc *sc, bus_size_t r, u_int32_t mask,
+            u_int32_t target)
+{
+       int     t;
+
+       /*
+        * Loop hard up to 100uS
+        */
+       for (t = 0; t < 100; ++t) {
+               if ((sili_read(sc, r) & mask) != target)
+                       return (0);
+               sili_os_hardsleep(1);   /* us */
+       }
+
+       /*
+        * And one millisecond the slow way
+        */
+       t = 1000;
+       do {
+               t -= sili_os_softsleep();
+               if ((sili_read(sc, r) & mask) != target)
+                       return (0);
+       } while (t > 0);
+
+       return (1);
+}
+
+
+/*
+ * Acquire an ata transfer.
+ *
+ * Pass a NULL at for direct-attached transfers, and a non-NULL at for
+ * targets that go through the port multiplier.
+ */
+struct ata_xfer *
+sili_ata_get_xfer(struct sili_port *ap, struct ata_port *at)
+{
+       struct sili_ccb         *ccb;
+
+       ccb = sili_get_ccb(ap);
+       if (ccb == NULL) {
+               DPRINTF(SILI_D_XFER, "%s: sili_ata_get_xfer: NULL ccb\n",
+                   PORTNAME(ap));
+               return (NULL);
+       }
+
+       DPRINTF(SILI_D_XFER, "%s: sili_ata_get_xfer got slot %d\n",
+           PORTNAME(ap), ccb->ccb_slot);
+
+       bzero(ccb->ccb_xa.fis, sizeof(*ccb->ccb_xa.fis));
+       ccb->ccb_xa.at = at;
+       ccb->ccb_xa.fis->type = ATA_FIS_TYPE_H2D;
+
+       return (&ccb->ccb_xa);
+}
+
+void
+sili_ata_put_xfer(struct ata_xfer *xa)
+{
+       struct sili_ccb                 *ccb = (struct sili_ccb *)xa;
+
+       DPRINTF(SILI_D_XFER, "sili_ata_put_xfer slot %d\n", ccb->ccb_slot);
+
+       sili_put_ccb(ccb);
+}
+
+int
+sili_ata_cmd(struct ata_xfer *xa)
+{
+       struct sili_ccb                 *ccb = (struct sili_ccb *)xa;
+
+       KKASSERT(xa->state == ATA_S_SETUP);
+
+       if (ccb->ccb_port->ap_state == AP_S_FATAL_ERROR)
+               goto failcmd;
+#if 0
+       kprintf("%s: started std command %b ccb %d ccb_at %p %d\n",
+               ATANAME(ccb->ccb_port, ccb->ccb_xa.at),
+               sili_pread(ccb->ccb_port, SILI_PREG_CMD), SILI_PFMT_CMD,
+               ccb->ccb_slot,
+               ccb->ccb_xa.at,
+               ccb->ccb_xa.at ? ccb->ccb_xa.at->at_target : -1);
+#endif
+
+       ccb->ccb_done = sili_ata_cmd_done;
+
+       if (sili_load_prb(ccb) != 0)
+               goto failcmd;
+
+       xa->state = ATA_S_PENDING;
+
+       if (xa->flags & ATA_F_POLL)
+               return (sili_poll(ccb, xa->timeout, sili_ata_cmd_timeout));
+
+       crit_enter();
+       KKASSERT((xa->flags & ATA_F_TIMEOUT_EXPIRED) == 0);
+       xa->flags |= ATA_F_TIMEOUT_DESIRED;
+       sili_start(ccb);
+       crit_exit();
+       return (xa->state);
+
+failcmd:
+       crit_enter();
+       xa->state = ATA_S_ERROR;
+       xa->complete(xa);
+       crit_exit();
+       return (ATA_S_ERROR);
+}
+
+static void
+sili_ata_cmd_done(struct sili_ccb *ccb)
+{
+       struct ata_xfer                 *xa = &ccb->ccb_xa;
+
+       /*
+        * NOTE: callout does not lock port and may race us modifying
+        * the flags, so make sure its stopped.
+        */
+       if (xa->flags & ATA_F_TIMEOUT_RUNNING) {
+               callout_stop(&ccb->ccb_timeout);
+               xa->flags &= ~ATA_F_TIMEOUT_RUNNING;
+       }
+       xa->flags &= ~(ATA_F_TIMEOUT_DESIRED | ATA_F_TIMEOUT_EXPIRED);
+
+       KKASSERT(xa->state != ATA_S_ONCHIP);
+       sili_unload_prb(ccb);
+
+#ifdef DIAGNOSTIC
+       else if (xa->state != ATA_S_ERROR && xa->state != ATA_S_TIMEOUT)
+               kprintf("%s: invalid ata_xfer state %02x in sili_ata_cmd_done, "
+                       "slot %d\n",
+                       PORTNAME(ccb->ccb_port), xa->state, ccb->ccb_slot);
+#endif
+       if (xa->state != ATA_S_TIMEOUT)
+               xa->complete(xa);
+}
+
+/*
+ * Timeout from callout, MPSAFE - nothing can mess with the CCB's flags
+ * while the callout is runing.
+ *
+ * We can't safely get the port lock here or delay, we could block
+ * the callout thread.
+ */
+static void
+sili_ata_cmd_timeout_unserialized(void *arg)
+{
+       struct sili_ccb         *ccb = arg;
+       struct sili_port        *ap = ccb->ccb_port;
+
+       ccb->ccb_xa.flags &= ~ATA_F_TIMEOUT_RUNNING;
+       ccb->ccb_xa.flags |= ATA_F_TIMEOUT_EXPIRED;
+       sili_os_signal_port_thread(ap, AP_SIGF_TIMEOUT);
+}
+
+void
+sili_ata_cmd_timeout(struct sili_ccb *ccb)
+{
+       sili_core_timeout(ccb);
+}
+
+/*
+ * Timeout code, typically called when the port command processor is running.
+ *
+ * Returns 0 if all timeout processing completed, non-zero if it is still
+ * in progress.
+ */
+static
+int
+sili_core_timeout(struct sili_ccb *ccb)
+{
+       struct ata_xfer         *xa = &ccb->ccb_xa;
+       struct sili_port        *ap = ccb->ccb_port;
+       struct ata_port         *at;
+
+       at = ccb->ccb_xa.at;
+
+       kprintf("%s: CMD TIMEOUT state=%d slot=%d\n"
+               "\t active=%08x\n"
+               "\texpired=%08x\n"
+               "\thactive=%08x\n",
+               ATANAME(ap, at),
+               ccb->ccb_xa.state, ccb->ccb_slot,
+               ap->ap_active,
+               ap->ap_expired,
+               sili_pread(ap, SILI_PREG_SLOTST)
+       );
+
+       /*
+        * NOTE: Timeout will not be running if the command was polled.
+        *       If we got here at least one of these flags should be set.
+        *
+        *       However, it might be running if we are called from the
+        *       interrupt error handling code.
+        */
+       KKASSERT(xa->flags & (ATA_F_POLL | ATA_F_TIMEOUT_DESIRED |
+                             ATA_F_TIMEOUT_RUNNING));
+       if (xa->flags & ATA_F_TIMEOUT_RUNNING) {
+               callout_stop(&ccb->ccb_timeout);
+               xa->flags &= ~ATA_F_TIMEOUT_RUNNING;
+       }
+       xa->flags &= ~ATA_F_TIMEOUT_EXPIRED;
+
+       if (ccb->ccb_xa.state == ATA_S_PENDING) {
+               TAILQ_REMOVE(&ap->ap_ccb_pending, ccb, ccb_entry);
+               ccb->ccb_xa.state = ATA_S_TIMEOUT;
+               ccb->ccb_done(ccb);
+               xa->complete(xa);
+               sili_issue_pending_commands(ap, NULL);
+               return(1);
+       }
+       if (ccb->ccb_xa.state != ATA_S_ONCHIP) {
+               kprintf("%s: Unexpected state during timeout: %d\n",
+                       ATANAME(ap, at), ccb->ccb_xa.state);
+               return(1);
+       }
+
+       /*
+        * We can't process timeouts while other commands are running.
+        */
+       ap->ap_expired |= 1 << ccb->ccb_slot;
+
+       if (ap->ap_active != ap->ap_expired) {
+               kprintf("%s: Deferred timeout until its safe, slot %d\n",
+                       ATANAME(ap, at), ccb->ccb_slot);
+               return(1);
+       }
+
+       /*
+        * We have to issue a Port reinit.  We don't read an error log
+        * page for timeouts.  Reiniting the port will clear all pending
+        * commands.
+        */
+       sili_port_reinit(ap);
+       return(0);
+}
+
+/*
+ * Used by the softreset, pmprobe, and read_ncq_error only, in very
+ * specialized, controlled circumstances.
+ */
+void
+sili_quick_timeout(struct sili_ccb *ccb)
+{
+       struct sili_port *ap = ccb->ccb_port;
+
+       switch (ccb->ccb_xa.state) {
+       case ATA_S_PENDING:
+               TAILQ_REMOVE(&ap->ap_ccb_pending, ccb, ccb_entry);
+               ccb->ccb_xa.state = ATA_S_TIMEOUT;
+               break;
+       case ATA_S_ONCHIP:
+               KKASSERT((ap->ap_active & ~ap->ap_expired) ==
+                        (1 << ccb->ccb_slot));
+               ccb->ccb_xa.state = ATA_S_TIMEOUT;
+               ap->ap_active &= ~(1 << ccb->ccb_slot);
+               KKASSERT(ap->ap_active_cnt > 0);
+               --ap->ap_active_cnt;
+               sili_port_reinit(ap);
+               break;
+       default:
+               panic("%s: sili_quick_timeout: ccb in bad state %d",
+                     ATANAME(ap, ccb->ccb_xa.at), ccb->ccb_xa.state);
+       }
+}
+
+static void
+sili_dummy_done(struct ata_xfer *xa)
+{
+}
+
+static void
+sili_empty_done(struct sili_ccb *ccb)
+{
+}
diff --git a/sys/dev/disk/sili/sili.h b/sys/dev/disk/sili/sili.h
new file mode 100644 (file)
index 0000000..c1580cb
--- /dev/null
@@ -0,0 +1,895 @@
+/*
+ * Copyright (c) 2006 David Gwynne <dlg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $OpenBSD: sili.c,v 1.147 2009/02/16 21:19:07 miod Exp $
+ */
+
+#if defined(__DragonFly__)
+#include "sili_dragonfly.h"
+#else
+#error "build for OS unknown"
+#endif
+#include "pmreg.h"
+#include "atascsi.h"
+
+/* change to SILI_DEBUG for dmesg spam */
+#define NO_SILI_DEBUG
+
+#ifdef SILI_DEBUG
+#define DPRINTF(m, f...) do { if ((silidebug & (m)) == (m)) kprintf(f); } \
+    while (0)
+#define SILI_D_TIMEOUT         0x00
+#define SILI_D_VERBOSE         0x01
+#define SILI_D_INTR            0x02
+#define SILI_D_XFER            0x08
+int silidebug = SILI_D_VERBOSE;
+#else
+#define DPRINTF(m, f...)
+#endif
+
+/*
+ * BAR0 - Global Registers             128-byte aligned, 128-byte region
+ * BAR1 - Port Registers and LRAM      32KB-aligned
+ * BAR2 - Indirect I/O registers       (we don't use this)
+ */
+
+/*
+ * Port-N Slot Status.
+ *
+ * NOTE: Mirrors SILI_PREG_SLOTST
+ */
+#define SILI_REG_SLOTST(n)     (0x0000 + ((n) * 4))
+#define SILI_REG_SLOTST_ATTN   0x80000000      /* attention required */
+
+#define SILI_REG_GCTL          0x0040          /* Global Control       */
+#define SILI_REG_GCTL_PORTEN(n)        (1 << (n))      /* Port interrupt ena   */
+#define SILI_REG_GCTL_300CAP   0x01000000      /* 3Gb/s capable (R)    */
+#define SILI_REG_GCTL_I2C_IEN  0x20000000      /* I2C Int enable       */
+#define SILI_REG_GCTL_MSIACK   0x40000000      /* MSI Ack W1C          */
+#define SILI_REG_GCTL_GRESET   0x80000000      /* global reset */
+
+#define SILI_REG_GINT          0x0044          /* Global Interrupt Status */
+#define SILI_REG_GINT_I2C      0x20000000      /* I2C Int Status       */
+#define SILI_REG_GINT_PORTST(n)        (1 << (n))      /* Port interrupt stat  */
+#define SILI_REG_GINT_PORTMASK 0x0000FFFF
+
+/*
+ * Most bits in phyconf should not be modified.  Setting the low four bits
+ * to 1's, all channel Tx outputs spread spectrum clocking.
+ */
+#define SILI_REG_PHYCONF       0x0048          /* PHY Configuration */
+#define SILI_REG_PHYCONF_ALLSS 0x000F          /* spread spectrum tx */
+
+/*
+ * BIST_CTL_TEN -      Enable data paths for running data loopback BIST
+ * BIST_CTL_TPAT -     Select repeating pattern (1) or pseudo-random
+ *                     pattern (0)
+ * BIST_CTL_RXSEL-     Select the rx port for pattern comparison
+ * BIST_CTL_TXSEL-     Select the tx ports that transmit loopback data
+ */
+#define SILI_REG_BIST_CTL      0x0050
+#define SILI_REG_BIST_CTL_TEN  0x80000000
+#define SILI_REG_BIST_CTL_TPAT 0x40000000
+#define SILI_REG_BIST_CTL_RXSEL(n) ((n) << 16)
+#define SILI_REG_BIST_CTL_TXSEL(n) (1 << (n))
+
+#define SILI_REG_BIST_PATTERN  0x0054  /* 32 bit pattern */
+
+/*
+ * GOOD is set to 1 on BIST initiation, and reset to 0 on the first
+ * comparison failure.
+ */
+#define SILI_REG_BIST_STATUS   0x0058
+#define SILI_REG_BIST_STATUS_CNT  0x000007FF   /* pattern counter */
+#define SILI_REG_BIST_STATUS_GOOD 0x80000000   /* set to 0 on compare fail */
+
+#define SILI_REG_I2C_CTL       0x0060
+#define SILI_REG_I2C_CTL_START 0x00000001
+#define SILI_REG_I2C_CTL_STOP  0x00000002
+#define SILI_REG_I2C_CTL_NACK  0x00000004      /* send nack on data byte rx */
+#define SILI_REG_I2C_CTL_TFDATA        0x00000008      /* set to initiate txfer     */
+                                               /* to/from data buffer       */
+#define SILI_REG_I2C_CTL_MABORT        0x00000010      /* set w/STOP to send stop   */
+                                               /* without first xfering a   */
+                                               /* byte                      */
+#define SILI_REG_I2C_CTL_SCLEN 0x00000020      /* clock-en for master mode  */
+#define SILI_REG_I2C_CTL_UNITEN        0x00000040      /* enable controller         */
+#define SILI_REG_I2C_CTL_GCALLD        0x00000080      /* Disable detect of a       */
+                                               /* general call address      */
+#define SILI_REG_I2C_CTL_TXBEI 0x00000100      /* xmit buffer empty int en  */
+#define SILI_REG_I2C_CTL_RXBFI 0x00000200      /* rx buffer full int en     */
+#define SILI_REG_I2C_CTL_BERRI 0x00000400      /* bus error int en          */
+#define SILI_REG_I2C_CTL_STOPI 0x00000800      /* stop detect int en        */
+#define SILI_REG_I2C_CTL_ARBLI 0x00001000      /* arb loss int en           */
+#define SILI_REG_I2C_CTL_ARBDI 0x00002000      /* arb detect int en         */
+#define SILI_REG_I2C_CTL_UNITRS        0x00004000      /* reset I2C controller      */
+#define SILI_REG_I2C_CTL_FASTM 0x00008000      /* 400kbit/s (else 100kbit/s)*/
+
+#define SILI_REG_I2C_STS       0x0064
+#define SILI_REG_I2C_STS_RW    0x00000001
+#define SILI_REG_I2C_STS_ACKSTS        0x00000002      /* ack/nack status(R) last   */
+                                               /* ack or nack sent or rcvd  */
+#define SILI_REG_I2C_STS_UNTBSY        0x00000004      /* unit busy (R)             */
+#define SILI_REG_I2C_STS_BUSBSY        0x00000008      /* bus busy with activity(R) */
+                                               /* other then from controller*/
+#define SILI_REG_I2C_STS_STOPDT        0x00000010      /* stop detect (R/W1C)       */
+#define SILI_REG_I2C_STS_ARBLD 0x00000020      /* arb loss detect   (R/W1C) */
+#define SILI_REG_I2C_STS_TXBED 0x00000040      /* tx buffer empty   (R)     */
+#define SILI_REG_I2C_STS_RXBFD 0x00000080      /* rx buffer full    (R/W1C) */
+#define SILI_REG_I2C_STS_GCALLD        0x00000100      /* Gen Call detect   (R/W1C) */
+#define SILI_REG_I2C_STS_SADDRD        0x00000200      /* Slave addr detect (R/W1C) */
+#define SILI_REG_I2C_STS_BERRD 0x00000400      /* Bus error detect  (R/W1C) */
+
+#define SILI_REG_I2C_SADDR     0x0068          /* our slave address         */
+#define SILI_REG_I2C_SADDR_MASK        0x0000003F      /* 6 bits                    */
+
+#define SILI_REG_I2C_DATA      0x006C          /* data buffer (8 bits)      */
+
+#define SILI_REG_FLASH_ADDR    0x0070          /* Flash control & addr reg  */
+#define SILI_REG_FLASH_ADDR_MEMPRS 0x04000000  /* Flash memory present      */
+#define SILI_REG_FLASH_ADDR_GPIOEN 0x80000000  /* use flash data pins for   */
+                                               /* GPIO */
+#define SILI_REG_FLASH_ADDR_MEMST  0x02000000  /* Mem Access Start (R/W)    */
+                                               /* (clears on op complete)   */
+#define SILI_REG_FLASH_ADDR_MEMRD  0x01000000  /* 0=write, 1=read           */
+#define SILI_REG_FLASH_ADDR_MASK   0x0003FFFF  /* 18 bit memory address     */
+
+/*
+ * NOTE: In order to set a GPIO pin to read the DATA bit must be written to
+ *       1and the DCTL (drain control) bit must be written to 1 as well
+ *       to make it open-drain only (drive on low only).
+ */
+#define SILI_REG_GPIO          0x0074
+#define SILI_REG_GPIO_DATA_SHIFT       0       /* 8 bits Flash or GPIO data */
+#define SILI_REG_GPIO_TDET_SHIFT       8       /* 8 bits transition detect  */
+#define SILI_REG_GPIO_DCTL_SHIFT       16      /* 8 bits drain control      */
+
+/*
+ * Per-port registers
+ *
+ */
+
+#define SILI_PORT_REGION(port) (8192 * (port))
+#define SILI_PORT_SIZE         8192
+#define SILI_PREG_LRAM         0x0000          /* 0x0000-0x0F7F             */
+#define SILI_PREG_LRAM_SLOT(n) (0x0000 + (128 * (n)))
+
+#define SILI_PREG_LRAM_SLOT_FIS        0x0000          /* Current FIS and Control   */
+#define SILI_PREG_LRAM_DMA0    0x0020          /* DMA Entry 0 or ATAPI cmd  */
+#define SILI_PREG_LRAM_DMA1    0x0030          /* DMA Entry 0 or ATAPI cmd  */
+#define SILI_PREG_LRAM_CMDACT  0x0040          /* Cmd Act Reg (actual) 64b  */
+#define SILI_PREG_LRAM_DMATAB  0x0040          /* Scatter Gather Table      */
+
+/*
+ * Each port has a port status and qactive register for each target behind
+ * the port multiplier, if there is a port multiplier.
+ *
+ * SERVICE - Service received from device, service command from controller
+ *          not yet acknowledged.
+ *
+ * LEGACY  - One or more legacy queued commands is outstanding.
+ *
+ * NATIVE  - One or more NCQ queued commands is outstanding.
+ *
+ * VBSY    - A virtual device busy indicating that a command has been issued
+ *          to the device and the device has not yet sent the final D2H
+ *          register FIS, or that a data transfer is in progress.
+ *
+ * The PM_QACTIVE register contains a demultiplexed bitmap of slots queued
+ * to each target behind the port multiplier.
+ *
+ */
+#define SILI_PREG_PM_STATUS(n) (0x0F80 + (8 * (n)))
+#define SILI_PREG_PM_QACTIVE(n)        (0x0F84 + (8 * (n)))
+
+#define SILI_PREG_PM_STATUS_SERVICE    0x00010000      /* Service pending */
+#define SILI_PREG_PM_STATUS_LEGACY     0x00008000      /* Legacy outstanding*/
+#define SILI_PREG_PM_STATUS_NATIVE     0x00004000      /* NCQ outstanding */
+#define SILI_PREG_PM_STATUS_VBSY       0x00002000      /* virtual dev busy */
+#define SILI_PREG_PM_STATUS_EXEC_SHIFT 8               /* last active slot */
+#define SILI_PREG_PM_STATUS_EXEC_MASK  0x1F
+#define SILI_PREG_PM_STATUS_PIO_MASK   0xFF            /* last PIO setup   */
+
+/*
+ * NOTE: SILI_PREG_STATUS uses the same address as SILI_PREG_CTL_SET,
+ * but for read.
+ */
+#define SILI_PREG_STATUS       0x1000          /* Port Control Status (R)   */
+#define SILI_PREG_STATUS_READY 0x80000000      /* Port Ready          (R)   */
+#define SILI_PREG_STATUS_SLOT  0x001F0000      /* Active Slot         (R)   */
+#define SILI_PREG_STATUS_SLOT_SHIFT    16      /* Shift value               */
+#define SILI_PREG_STATUS_MASK  0x0200FFFF      /* see PREG_CTL_xxx          */
+
+/*
+ * NOTE: Reset sequence.  Set CTL_RESET, Clear CTL_RESET, then Wait for
+ *       the port to become ready.
+ *
+ * NOTE: NOAUTOCC.  If set to 1 a 1 must be written to the command completion
+ *      bit in the port interrupt status register to clear it.  When set to
+ *      0 then reading the port slot status register will automatically clear
+ *       the command completion interrupt.
+ *
+ * NOTE: ATA16 controls whether a PACKET mode command is 12 or 16 bytes.
+ *
+ * NOTE: RESUME if set to 1 processing is enabled for outstanding commands
+ *       to additional targets connected to a port multiplier after a command
+ *      error has occured.  When set the internal BUSY status will be set
+ *      for the target that errored, preventing additional commands from
+ *       being sent until a Port Initialize operation is performed.
+ *
+ * NOTE: 32BITDMA if 1 causes a write to the low 32 bits of a Command
+ *      Activation register to  copy PREG_32BIT_ACTUA to the upper 32
+ *       bits and start command execution.  If 0 you must write to the
+ *       low 32 bits and then the high 32 bits and your write to the
+ *       high 32 bits will start command execution.
+ *
+ * NOTE: OOB_BYP is set on global reset and not changed by a port reset.
+ */
+#define SILI_PREG_CTL_SET      0x1000          /* Port Control Set    (W1S) */
+#define SILI_PREG_CTL_CLR      0x1004          /* Port Control Clear  (W1C) */
+#define SILI_PREG_CTL_RESET    0x00000001      /* Hold port in reset        */
+#define SILI_PREG_CTL_DEVRESET 0x00000002      /* Device reset              */
+                                               /* (Self clearing)           */
+#define SILI_PREG_CTL_INIT     0x00000004      /* Port initialize           */
+                                               /* (Self clearing)           */
+#define SILI_PREG_CTL_NOAUTOCC 0x00000008
+#define SILI_PREG_CTL_NOLED    0x00000010      /* Disable the LED port      */
+                                               /* activity indicator.       */
+#define SILI_PREG_CTL_ATA16    0x00000020      /* 0=12 byte 1=16 byte       */
+#define SILI_PREG_CTL_RESUME   0x00000040      /* PM special error handling */
+#define SILI_PREG_CTL_TXBIST   0x00000080      /* transmit a BIST FIS       */
+#define SILI_PREG_CTL_CONT_DIS 0x00000100      /* no CONT on repeat primitves*/
+#define SILI_PREG_CTL_NOSCRAM  0x00000200      /* Disable link scrambler    */
+#define SILI_PREG_CTL_32BITDMA 0x00000400      /* see above                 */
+#define SILI_PREG_CTL_ACC_ILCK 0x00000800      /* accept interlocked FIS rx */
+                                               /* (Self clearing)           */
+#define SILI_PREG_CTL_REJ_ILCK 0x00001000      /* reject interlocked FIS rx */
+                                               /* (Self clearing)           */
+#define SILI_PREG_CTL_PMA      0x00002000      /* Enable PM support         */
+#define SILI_PREG_CTL_AUTO_ILCK        0x00004000      /* Auto interlock accept     */
+#define SILI_PREG_CTL_LEDON    0x00008000      /* LED on                    */
+#define SILI_PREG_CTL_OOB_BYP  0x02000000      /* Bypass OOB initialization */
+
+/*
+ * Status bits in the upper half of the register report the actual condition
+ * while the status bits in the lower half of the register are masked by
+ * the interrupt enable bits or threshold registers.  Writing a 1 to either
+ * version will clear it.
+ *
+ * NOTE: The steering bits written to INT_ENABLE will not show up in the
+ *       status register.  The INT_ENABLE/INT_DISABLE registers are R+W1S
+ *      or R+W1C and thus can be read.
+ *
+ * NOTE: PHYRDYCHG, COMWAKE, UNRECFIS, DEVEXCHG: Can be cleared by writing
+ *      W1C either here or via the DIAG.xxx bit bit in SError.
+ */
+#define SILI_PREG_INT_STATUS   0x1008          /* Control clear             */
+#define SILI_PREG_INT_ENABLE   0x1010          /* Interrupt Enable Set      */
+#define SILI_PREG_INT_DISABLE  0x1014          /* Interrupt Enable Clear    */
+
+#define SILI_PREG_INT_STEER(n) ((n) << 30)     /* Port Int -> INTA...INTD   */
+#define SILI_PREG_INT_CCOMPLETE        0x00000001      /* one or more cmds completed*/
+#define SILI_PREG_INT_CERROR   0x00000002      /* read port error register  */
+                                               /* to get error              */
+#define SILI_PREG_INT_READY    0x00000004      /* Port is ready for cmds    */
+#define SILI_PREG_INT_PMCHANGE 0x00000008      /* Change in power mng state */
+#define SILI_PREG_INT_PHYRDYCHG        0x00000010      /* Mirrors DIAG.N in SError  */
+#define SILI_PREG_INT_COMWAKE  0x00000020      /* Mirrors DIAG.W in SError  */
+#define SILI_PREG_INT_UNRECFIS 0x00000040      /* Mirrors DIAG.F in SError  */
+#define SILI_PREG_INT_DEVEXCHG 0x00000080      /* Mirrors DIAG.X in SError  */
+#define SILI_PREG_INT_DECODE   0x00000100      /* 8b/10b dec err  cnt > thr */
+#define SILI_PREG_INT_CRC      0x00000200      /* CRC err       count > thr */
+#define SILI_PREG_INT_HANDSHK  0x00000400      /* Handshake err count > thr */
+#define SILI_PREG_INT_SDB      0x00000800      /* Set Device Bits (SNotify) */
+#define SILI_PREG_INT_SHIFT    16              /* shift upper bits of status*/
+
+#define SILI_PREG_IST_CCOMPLETE        0x00010000      /* one or more cmds completed*/
+#define SILI_PREG_IST_CERROR   0x00020000      /* read port error register  */
+                                               /* to get error              */
+#define SILI_PREG_IST_READY    0x00040000      /* Port is ready for cmds    */
+#define SILI_PREG_IST_PMCHANGE 0x00080000      /* Change in power mng state */
+#define SILI_PREG_IST_PHYRDYCHG        0x00100000      /* Mirrors DIAG.N in SError  */
+#define SILI_PREG_IST_COMWAKE  0x00200000      /* Mirrors DIAG.W in SError  */
+#define SILI_PREG_IST_UNRECFIS 0x00400000      /* Mirrors DIAG.F in SError  */
+#define SILI_PREG_IST_DEVEXCHG 0x00800000      /* Mirrors DIAG.X in SError  */
+#define SILI_PREG_IST_DECODE   0x01000000      /* 8b/10b dec err  cnt > thr */
+#define SILI_PREG_IST_CRC      0x02000000      /* CRC err       count > thr */
+#define SILI_PREG_IST_HANDSHK  0x04000000      /* Handshake err count > thr */
+#define SILI_PREG_IST_SDB      0x08000000      /* Set Device Bits (SNotify) */
+
+#define SILI_PREG_INT_MASK     (SILI_PREG_INT_CCOMPLETE |              \
+                                SILI_PREG_INT_CERROR |                 \
+                                SILI_PREG_INT_READY |                  \
+                                SILI_PREG_INT_PMCHANGE |               \
+                                SILI_PREG_INT_PHYRDYCHG |              \
+                                SILI_PREG_INT_COMWAKE |                \
+                                SILI_PREG_INT_UNRECFIS |               \
+                                SILI_PREG_INT_DEVEXCHG |               \
+                                SILI_PREG_INT_DECODE |                 \
+                                SILI_PREG_INT_CRC |                    \
+                                SILI_PREG_INT_HANDSHK |                \
+                                SILI_PREG_INT_SDB)
+#define SILI_PREG_IST_MASK     (SILI_PREG_INT_MASK << 16)
+
+#define SILI_PFMT_INT_STATUS   "\020"                  \
+                               "\034SDB"               \
+                               "\033HANDSHK"           \
+                               "\032CRC"               \
+                               "\031DECODE"            \
+                               "\030DEVEXCHG"          \
+                               "\027UNRECFIS"          \
+                               "\026COMWAKE"           \
+                               "\025PHYRDYCHG"         \
+                               "\024PMCHANGE"          \
+                               "\023READY"             \
+                               "\022ERROR"             \
+                               "\021COMPLETE"          \
+                                                       \
+                               "\014SDBm"              \
+                               "\013HANDSHKm"          \
+                               "\012CRCm"              \
+                               "\011DECODEm"           \
+                               "\010DEVEXCHGm"         \
+                               "\007UNRECFISm"         \
+                               "\006COMWAKEm"          \
+                               "\005PHYRDYCHGm"        \
+                               "\004PMCHANGEm"         \
+                               "\003READYm"            \
+                               "\002ERRORm"            \
+                               "\001COMPLETEm"
+
+/*
+ * 32BIT_ACTUA is only used when DMA is 32 bits.  It is typically set to 0.
+ */
+#define SILI_PREG_32BIT_ACTUA  0x101C          /* 32b activation upper addr */
+
+/*
+ * Writing a slot number 0-30 to CMD_FIFO starts the command from LRAM.
+ */
+#define SILI_PREG_CMD_FIFO     0x1020          /* Command execution FIFO    */
+
+/*
+ * If the port is interrupted via INT_CERROR this register contains
+ * the error code.
+ *
+ * Most errors write the task file register back to the PRB slot for host
+ * scrutiny.
+ */
+#define SILI_PREG_CERROR       0x1024          /* Command error             */
+#define SILI_PREG_CERROR_DEVICE                1       /* ERR set in D2H FIS        */
+#define SILI_PREG_CERROR_SDBERROR      2       /* ERR set in SDB from device*/
+#define SILI_PREG_CERROR_DATAFISERR    3       /* Sil3132 error on send     */
+#define SILI_PREG_CERROR_SENDFISERR    4       /* Sil3132 error on send     */
+#define SILI_PREG_CERROR_BADSTATE      5       /* Sil3132 inconsistency     */
+#define SILI_PREG_CERROR_DIRECTION     6       /* DMA direction mismatch    */
+#define SILI_PREG_CERROR_UNDERRUN      7       /* DMA SG H2D list too small */
+#define SILI_PREG_CERROR_OVERRUN       8       /* DMA SG D2H list too small */
+#define SILI_PREG_CERROR_LLOVERUN      9       /* Too much data from device */
+#define SILI_PREG_CERROR_PKTPROTO      11      /* Packet protocol error     */
+#define SILI_PREG_CERROR_BADALIGN      16      /* Bad SG list, not 8-byte   */
+                                               /* aligned                   */
+#define SILI_PREG_CERROR_PCITGTABRT    17      /* PCI target abort received */
+#define SILI_PREG_CERROR_PCIMASABRT    18      /* PCI master abort received */
+#define SILI_PREG_CERROR_PCIPARABRT    19      /* PCI parity abort received */
+#define SILI_PREG_CERROR_PRBALIGN      24      /* PRB addr not 8-byte algned*/
+#define SILI_PREG_CERROR_PCITGTABRT2   25      /* During fetch of PRB       */
+#define SILI_PREG_CERROR_PCIMASABRT2   26      /* During fetch of PRB       */
+#define SILI_PREG_CERROR_PCIPARABRT3   33      /* During data transfer      */
+#define SILI_PREG_CERROR_PCITGTABRT3   34      /* During data transfer      */
+#define SILI_PREG_CERROR_PCIMASABRT3   35      /* During data transfer      */
+#define SILI_PREG_CERROR_SERVICE       36      /* FIS received during tx    */
+                                               /* phase                     */
+
+/*
+ * Port FIS Configuration.  Fir each possible FIS type, a 2-bit code
+ * defines the desired reception behavior as follows.  Bits [1:0] define
+ * the code for all other FIS types not defined by [29:2].
+ *
+ *     00 Accept FIS without interlock
+ *     01 Reject FIS without interlock
+ *     10 Interlock FIS
+ *     11 (reserved)
+ *
+ * FIS Code    Name                    Start   Default
+ * --------    ------                  ------  -------
+ *    ----     (reserved)              30
+ *    0x27     Register (H2D)          28      01
+ *    0x34     Register (D2H)          26      00
+ *    0x39     DMA Activate            24      00
+ *    0x41     DMA Setup               22      00
+ *    0x46     Data                    20      00
+ *    0x58     BIST Activate           18      00
+ *    0x5F     PIO Setup               16      00
+ *    0xA1     Set Device Bits         14      00
+ *    0xA6     (reserved)              12      01
+ *    0xB8     (reserved)              10      01
+ *    0xBF     (reserved)              8       01
+ *    0xC7     (reserved)              6       01
+ *    0xD4     (reserved)              4       01
+ *    0xD9     (reserved)              2       01
+ * ALL OTHERS  (reserved)              0       01
+ */
+#define SILI_PREG_FIS_CONFIG   0x1028          /* FIS configuration         */
+
+/*
+ * The data FIFO is 2KBytes in each direction.
+ *
+ * When DMAing from the device the Write Request Threshold is used.
+ * When DMAing to the device the Read Request Threshold is used.
+ *
+ * The threshold can be set from 1-2040 bytes (write 0-2039), in multiples
+ * of 8 bits.  The low 3 bits are hardwired to 0.  A value of 0 causes a
+ * request whenever possible.
+ */
+#define SILI_PREG_FIFO_CTL     0x102C          /* PCIex request FIFO thresh */
+#define SILI_PREG_FIFO_CTL_READ_SHIFT  0
+#define SILI_PREG_FIFO_CTL_WRITE_SHIFT 16
+#define SILI_PREG_FIFO_CTL_MASK        0xFF
+
+/*
+ * Error counters and thresholds.  The counter occupies the low 16 bits
+ * and the threshold occupies the high 16 bits.  The appropriate interrupt
+ * occurs when the counter exceeds the threshold.  Clearing the interrupt
+ * clears the counter as well.  A threshold of 0 disables the interrupt
+ * assertion and masks the interrupt status bit in the port interrupt status
+ * register.
+ */
+#define SILI_PREG_CTR_DECODE   0x1040          /* 8B/10B Decode Error Ctr   */
+#define SILI_PREG_CTR_CRC      0x1044          /* CRC Error Counter         */
+#define SILI_PREG_CTR_HANDSHK  0x1048          /* Handshake Error Counter   */
+
+/*
+ * NOTE: This register is reset only by the global reset and will not be
+ *      reset by a port reset.
+ *
+ * NOTE: Bits 15:5 configure the PHY and should not be changed unless you
+ *      want to blow up the part.
+ *
+ *      Bits 4:0 define the nominal output swing for the transmitter
+ *      and are set to 0x0C by default.  Generally speaking, don't mess
+ *      with them.
+ */
+#define SILI_PREG_PHY_CONFIG   0x1050          /* Handshake Error Counter   */
+#define SILI_PREG_PHY_CONFIG_AMP_MASK  0x1F
+
+#define SILI_PREG_SLOTST       0x1800          /* Slot Status               */
+#define SILI_PREG_SLOTST_ATTN  0x80000000      /* 0-30 bit for each slot */
+
+/*
+ * Shadow command activation register, shadows low or high 32 bits
+ * of actual command activation register.
+ */
+#define SILI_PREG_CMDACT(n)    (0x1C00 + (4 * (n)))
+
+/*
+ * Port Context Register.  Contains the port multipler target (0-15) and
+ * the command slot (0-31) for the PM port state machine.
+ *
+ * Upon a processing halt due to a device specific error, the port multipler
+ * target is the one that returned the error status.
+ *
+ * The command slot is NOT deterministic in this case and should not be
+ * assumed valid.  Use READ LOG EXTENDED to determine the tag number.
+ * However, the documentation does appear to indicate that for non-NCQ
+ * errors the command slot does contain the tag that errored (since there
+ * will be only one truely active).
+ */
+#define SILI_PREG_CONTEXT              0x1E04
+#define SILI_PREG_CONTEXT_SLOT_MASK    0x1F
+#define SILI_PREG_CONTEXT_PMPORT_MASK  0x0F
+#define SILI_PREG_CONTEXT_SLOT_SHIFT   0
+#define SILI_PREG_CONTEXT_PMPORT_SHIFT 5
+
+/*
+ * SControl register - power management, speed negotiation, and COMRESET
+ *                    operation.
+ */
+#define SILI_PREG_SCTL                 0x1F00
+
+/*
+ * PMP: Identify the PM port for accessing the SActive register and some
+ *     bit fields of the Diagnostic registers.
+ */
+#define SILI_PREG_SCTL_PMP             0x000F0000
+#define SILI_PREG_SCTL_PMP_SHIFT       16
+
+/*
+ * SPM: It is unclear from mode 4 is.  "Transition from a power management
+ *     state initiate (ComWake asserted)".  When setting a state, the field
+ *     will self-reset to 0 as soon as the action is initiated.
+ */
+#define SILI_PREG_SCTL_SPM             0x0000F000
+#define  SILI_PREG_SCTL_SPM_NONE       0x00000000
+#define  SILI_PREG_SCTL_SPM_PARTIAL    0x00010000
+#define  SILI_PREG_SCTL_SPM_SLUMBER    0x00020000
+#define  SILI_PREG_SCTL_SPM_FROM       0x00040000
+
+/*
+ * IPM: Identify interface power management states not supported (bits).
+ */
+#define SILI_PREG_SCTL_IPM             0x00000F00
+#define  SILI_PREG_SCTL_IPM_NONE       0x00000000
+#define  SILI_PREG_SCTL_IPM_NPARTIAL   0x00000100
+#define  SILI_PREG_SCTL_IPM_NSLUMBER   0x00000200
+
+/*
+ * SPD: Limit speed negotiation (0000 for no restrictions)
+ */
+#define        SILI_PREG_SCTL_SPD              0x000000F0
+#define         SILI_PREG_SCTL_SPD_NONE        0x00000000
+#define         SILI_PREG_SCTL_SPD_GEN1        0x00000010
+#define         SILI_PREG_SCTL_SPD_GEN2        0x00000020
+
+/*
+ * DET: Control host adapter device detection and interface initialization
+ */
+#define        SILI_PREG_SCTL_DET              0x0000000F
+#define         SILI_PREG_SCTL_DET_NONE        0x00000000      /* nop/complete     */
+#define         SILI_PREG_SCTL_DET_INIT        0x00000001      /* hold in COMRESET */
+
+/*
+ * SStatus register - Probe status
+ */
+#define SILI_PREG_SSTS                 0x1F04
+#define  SILI_PREG_SSTS_IPM            0x00000F00
+#define  SILI_PREG_SSTS_IPM_NOCOMM     0x00000000
+#define  SILI_PREG_SSTS_IPM_ACTIVE     0x00000100
+#define  SILI_PREG_SSTS_IPM_PARTIAL    0x00000200
+#define  SILI_PREG_SSTS_IPM_SLUMBER    0x00000600
+
+#define SILI_PREG_SSTS_SPD             0x000000F0
+#define  SILI_PREG_SSTS_SPD_NONE       0x00000000
+#define  SILI_PREG_SSTS_SPD_GEN1       0x00000010
+#define  SILI_PREG_SSTS_SPD_GEN2       0x00000020
+
+#define SILI_PREG_SSTS_DET             0x0000000F
+#define  SILI_PREG_SSTS_DET_NOPHY      0x00000000      /* no dev, no phy */
+#define  SILI_PREG_SSTS_DET_DEV_NE     0x00000001      /* dev, no phy  */
+#define  SILI_PREG_SSTS_DET_DEV                0x00000003      /* dev and phy  */
+#define  SILI_PREG_SSTS_DET_OFFLINE    0x00000004      /* BIST/LOOPBACK */
+
+/*
+ * These are mostly R/W1C bits.  "B", "C", and "H" operate independantly
+ * and depend on the corresponding error counter register.
+ */
+#define SILI_PREG_SERR                 0x1F08
+#define  SILI_PREG_SERR_ERR_I          (1<<0) /* Recovered Data Integrity */
+#define  SILI_PREG_SERR_ERR_M          (1<<1) /* Recovered Communications */
+#define  SILI_PREG_SERR_ERR_T          (1<<8) /* Transient Data Integrity */
+#define  SILI_PREG_SERR_ERR_C          (1<<9) /* Persistent Comm/Data */
+#define  SILI_PREG_SERR_ERR_P          (1<<10) /* Protocol */
+#define  SILI_PREG_SERR_ERR_E          (1<<11) /* Internal */
+#define  SILI_PREG_SERR_DIAG_N         (1<<16) /* PhyRdy Change */
+#define  SILI_PREG_SERR_DIAG_I         (1<<17) /* Phy Internal Error */
+#define  SILI_PREG_SERR_DIAG_W         (1<<18) /* Comm Wake */
+#define  SILI_PREG_SERR_DIAG_B         (1<<19) /* 10B to 8B Decode Error */
+#define  SILI_PREG_SERR_DIAG_D         (1<<20) /* Disparity Error */
+#define  SILI_PREG_SERR_DIAG_C         (1<<21) /* CRC Error */
+#define  SILI_PREG_SERR_DIAG_H         (1<<22) /* Handshake Error */
+#define  SILI_PREG_SERR_DIAG_S         (1<<23) /* Link Sequence Error */
+#define  SILI_PREG_SERR_DIAG_T         (1<<24) /* Transport State Trans Err */
+#define  SILI_PREG_SERR_DIAG_F         (1<<25) /* Unknown FIS Type */
+#define  SILI_PREG_SERR_DIAG_X         (1<<26) /* Exchanged */
+
+#define  SILI_PFMT_SERR        "\020"  \
+                       "\033DIAG.X" "\032DIAG.F" "\031DIAG.T" "\030DIAG.S" \
+                       "\027DIAG.H" "\026DIAG.C" "\025DIAG.D" "\024DIAG.B" \
+                       "\023DIAG.W" "\022DIAG.I" "\021DIAG.N"              \
+                       "\014ERR.E" "\013ERR.P" "\012ERR.C" "\011ERR.T"     \
+                       "\002ERR.M" "\001ERR.I"
+
+/*
+ * SACT provides indirect access to the Port Device QActive registers.
+ * We have direct access and do not have to use this.
+ */
+#define SILI_PREG_SACT                 0x1F0C
+
+/*
+ * Indicate which devices have sent a Set Device Bits FIS with Notifcation
+ * set.  R/W1C
+ */
+#define SILI_PREG_SNTF                 0x1F10
+
+/*
+ * Internal register space indirect register access via the PCI I/O space.
+ * (This is for BIOSes running in 16-bit mode, we use the direct map).
+ *
+ * All offsets must be 4-byte aligned
+ */
+#define SILI_BAR2_GRO                  0x0000  /* Global Register Offset */
+#define SILI_BAR2_GRD                  0x0004  /* Global Register Data */
+#define SILI_BAR2_PRO                  0x0008  /* Port Register Offset */
+#define SILI_BAR2_PRD                  0x000C  /* Port Register Data */
+
+/*
+ * SILI mapped structures
+ */
+struct sili_sge {
+       u_int64_t               sge_paddr;
+       u_int32_t               sge_count;
+       u_int32_t               sge_flags;
+} __packed;
+
+#define SILI_SGE_FLAGS_TRM     0x80000000      /* last SGE associated w/cmd */
+#define SILI_SGE_FLAGS_LNK     0x40000000      /* link to SGE array    */
+#define SILI_SGE_FLAGS_DRD     0x20000000      /* discard (ign sge_paddr) */
+#define SILI_SGE_FLAGS_XCF     0x10000000      /* external cmd fetch   */
+
+/*
+ * The PRB
+ *
+ * NOTE: ATAPI PACKETS.  The packet is stored in prb_sge[0] and sge[1]
+ *                      is the first SGE.
+ */
+struct sili_prb {
+       u_int16_t               prb_control;
+       u_int16_t               prb_override;
+       u_int32_t               prb_xfer_count;
+       union {
+               struct ata_fis_h2d      h2d;
+               struct ata_fis_d2h      d2h;
+       } prb_fis;
+       u_int32_t               prb_reserved1c;
+       struct sili_sge         prb_sge[2];
+       /* note: command activation register is prb_ext[0].sge_paddr */
+       struct sili_sge         prb_ext[4];
+} __packed;
+
+#define prb_h2d                prb_fis.h2d
+#define prb_d2h                prb_fis.d2h
+#define prb_activation prb_ext[0].sge_paddr
+#define prb_packet(prb)        ((u_int8_t *)&(prb)->prb_sge[0])
+#define prb_sge_normal prb_sge[0]
+#define prb_sge_packet prb_sge[1]
+
+/*
+ * NOTE: override may be left 0 and the SIL3132 will decode the
+ *      8-bit ATA command and use the correct protocol.
+ */
+#define SILI_PRB_CTRL_OVERRIDE 0x0001  /* use protocol field override  */
+#define SILI_PRB_CTRL_REXMIT   0x0002  /* ok to rexmit ext command     */
+#define SILI_PRB_CTRL_EXTCMD   0x0004  /* FIS fetched from host memory */
+                                       /* (else from LRAM)             */
+#define SILI_PRB_CTRL_RECEIVE  0x0008  /* Reserve cmd slot to receive  */
+                                       /* an interlocked FIS           */
+#define SILI_PRB_CTRL_READ     0x0010  /* device to host data          */
+#define SILI_PRB_CTRL_WRITE    0x0020  /* host to device data          */
+#define SILI_PRB_CTRL_NOINT    0x0040  /* do not post int on completion*/
+#define SILI_PRB_CTRL_SOFTRESET        0x0080  /* issue soft reset (special)   */
+
+#define SILI_PRB_OVER_ATAPI    0x0001
+#define SILI_PRB_OVER_ATA      0x0002
+#define SILI_PRB_OVER_NCQ      0x0004
+#define SILI_PRB_OVER_READ     0x0008  /* device to host data */
+#define SILI_PRB_OVER_WRITE    0x0010  /* host to device data */
+#define SILI_PRB_OVER_RAW      0x0020  /* no protocol special case */
+
+/*
+ * Each sge is 16 bytes.  We need to accomodate MAXPHYS (128K) which is
+ * at least 32 entries, plus one for page slop, plus one more for every
+ * 3 entries for the link entry.  The command table for the Sili is
+ * built into its LRAM but it can have a separate SGE table for each
+ * command, and we use this.
+ */
+#define SILI_MAX_SGET          64
+#define SILI_MAX_PMPORTS       16
+
+#if MAXPHYS / PAGE_SIZE + 1 > SILI_MAX_SGET
+#error "SILI_MAX_SGET is not big enough"
+#endif
+
+#define SILI_MAX_PORTS         16
+#define SILI_MAX_CMDS          31      /* not 32 */
+
+struct sili_dmamem {
+       bus_dma_tag_t           adm_tag;
+       bus_dmamap_t            adm_map;
+       bus_dma_segment_t       adm_seg;
+       bus_addr_t              adm_busaddr;
+       caddr_t                 adm_kva;
+};
+#define SILI_DMA_MAP(_adm)     ((_adm)->adm_map)
+#define SILI_DMA_DVA(_adm)     ((_adm)->adm_busaddr)
+#define SILI_DMA_KVA(_adm)     ((void *)(_adm)->adm_kva)
+
+struct sili_softc;
+struct sili_port;
+struct sili_device;
+
+struct sili_ccb {
+       /* ATA xfer associated with this CCB.  Must be 1st struct member. */
+       struct ata_xfer         ccb_xa;
+       struct callout          ccb_timeout;
+
+       int                     ccb_slot;
+       struct sili_port        *ccb_port;
+
+       bus_dmamap_t            ccb_dmamap;
+       struct sili_prb         *ccb_prb;
+       struct sili_sge         *ccb_sge;       /* linked SGE table */
+       u_int64_t               ccb_sge_paddr;  /* phys addr of sge table */
+
+       void                    (*ccb_done)(struct sili_ccb *);
+
+       TAILQ_ENTRY(sili_ccb)   ccb_entry;
+};
+
+struct sili_port {
+       struct sili_softc       *ap_sc;
+       bus_space_handle_t      ap_ioh;
+
+       int                     ap_num;
+       int                     ap_pmcount;
+       int                     ap_flags;
+#define AP_F_BUS_REGISTERED    0x0001
+#define AP_F_CAM_ATTACHED      0x0002
+#define AP_F_IN_RESET          0x0004
+#define AP_F_SCAN_RUNNING      0x0008
+#define AP_F_SCAN_REQUESTED    0x0010
+#define AP_F_SCAN_COMPLETED    0x0020
+#define AP_F_IGNORE_IFS                0x0040
+#define AP_F_EXCLUSIVE_ACCESS  0x0200
+#define AP_F_ERR_CCB_RESERVED  0x0400
+       int                     ap_signal;      /* os per-port thread sig */
+       thread_t                ap_thread;      /* os per-port thread */
+       struct lock             ap_lock;        /* os per-port lock */
+#define AP_SIGF_INIT           0x0001
+#define AP_SIGF_TIMEOUT                0x0002
+#define AP_SIGF_PORTINT                0x0004
+#define AP_SIGF_STOP           0x8000
+       struct cam_sim          *ap_sim;
+
+       struct sili_sge         *ap_sget;
+
+       struct sili_dmamem      *ap_dmamem_sget;/* separate sge tables  */
+
+       u_int32_t               ap_active;      /* active bmask         */
+       u_int32_t               ap_active_cnt;  /* active count         */
+       u_int32_t               ap_expired;     /* deferred expired bmask */
+       struct sili_ccb         *ap_ccbs;
+       struct sili_ccb         *ap_err_ccb;    /* used to read LOG page  */
+
+       TAILQ_HEAD(, sili_ccb)  ap_ccb_free;
+       TAILQ_HEAD(, sili_ccb)  ap_ccb_pending;
+       struct lock             ap_ccb_lock;
+
+       int                     ap_type;        /* ATA_PORT_T_xxx */
+       int                     ap_probe;       /* ATA_PROBE_xxx */
+       struct ata_port         *ap_ata;
+
+       u_int32_t               ap_state;
+#define AP_S_NORMAL                    0
+#define AP_S_FATAL_ERROR               1
+
+       /* For error recovery. */
+#ifdef DIAGNOSTIC
+       int                     ap_err_busy;
+#endif
+       u_int8_t                ap_err_scratch[512];
+
+       char                    ap_name[16];
+};
+
+#define PORTNAME(_ap)          ((_ap)->ap_name)
+#define ATANAME(_ap, _at)      ((_at) ? (_at)->at_name : (_ap)->ap_name)
+
+struct sili_softc {
+       device_t                sc_dev;
+       const struct sili_device *sc_ad;        /* special casing */
+
+       struct resource         *sc_irq;        /* bus resources */
+       struct resource         *sc_regs;       /* bus resources */
+       struct resource         *sc_pregs;      /* bus resources */
+       bus_space_tag_t         sc_iot;         /* split from sc_regs */
+       bus_space_handle_t      sc_ioh;         /* split from sc_regs */
+       bus_space_tag_t         sc_piot;        /* split from sc_pregs */
+       bus_space_handle_t      sc_pioh;        /* split from sc_pregs */
+
+       int                     sc_rid_irq;     /* saved bus RIDs */
+       int                     sc_rid_regs;
+       int                     sc_rid_pregs;
+
+       void                    *sc_irq_handle; /* installed irq vector */
+
+/*     bus_dma_tag_t           sc_tag_prb;*/
+       bus_dma_tag_t           sc_tag_sget;
+       bus_dma_tag_t           sc_tag_data;
+
+       int                     sc_flags;
+#define SILI_F_NO_NCQ                  0x0001
+#define SILI_F_IGN_FR                  0x0002
+#define SILI_F_INT_GOOD                        0x0004
+#define SILI_F_64BIT                   0x0008
+#define SILI_F_300                     0x0010
+#define SILI_F_NCQ                     0x0020
+#define SILI_F_SSNTF                   0x0040
+#define SILI_F_SPM                     0x0080
+
+       u_int                   sc_ncmds;       /* max 31, NOT 32 */
+
+       struct sili_port        *sc_ports[SILI_MAX_PORTS];
+};
+#define DEVNAME(_s)            ((_s)->sc_dev.dv_xname)
+
+struct sili_device {
+       pci_vendor_id_t         ad_vendor;
+       pci_product_id_t        ad_product;
+       int                     ad_nports;
+       int                     (*ad_attach)(device_t dev);
+       int                     (*ad_detach)(device_t dev);
+       char                    *name;
+};
+
+const struct sili_device *sili_lookup_device(device_t dev);
+int    sili_init(struct sili_softc *);
+int    sili_port_init(struct sili_port *ap, struct ata_port *at);
+int    sili_port_alloc(struct sili_softc *, u_int);
+void   sili_port_state_machine(struct sili_port *ap, int initial);
+void   sili_port_free(struct sili_softc *, u_int);
+int    sili_port_reset(struct sili_port *, struct ata_port *at, int);
+
+u_int32_t sili_read(struct sili_softc *, bus_size_t);
+void   sili_write(struct sili_softc *, bus_size_t, u_int32_t);
+int    sili_wait_ne(struct sili_softc *, bus_size_t, u_int32_t, u_int32_t);
+u_int32_t sili_pread(struct sili_port *, bus_size_t);
+void   sili_pwrite(struct sili_port *, bus_size_t, u_int32_t);
+int    sili_pwait_eq(struct sili_port *, int, bus_size_t,
+                       u_int32_t, u_int32_t);
+void   sili_intr(void *);
+void   sili_port_intr(struct sili_port *ap, int blockable);
+
+int    sili_cam_attach(struct sili_port *ap);
+void   sili_cam_changed(struct sili_port *ap, struct ata_port *at, int found);
+void   sili_cam_detach(struct sili_port *ap);
+int    sili_cam_probe(struct sili_port *ap, struct ata_port *at);
+
+struct ata_xfer *sili_ata_get_xfer(struct sili_port *ap, struct ata_port *at);
+void   sili_ata_put_xfer(struct ata_xfer *xa);
+int    sili_ata_cmd(struct ata_xfer *xa);
+
+int    sili_pm_identify(struct sili_port *ap);
+int    sili_pm_set_feature(struct sili_port *ap, int feature, int enable);
+int    sili_pm_hardreset(struct sili_port *ap, int target, int hard);
+int    sili_pm_softreset(struct sili_port *ap, int target);
+int    sili_pm_phy_status(struct sili_port *ap, int target, u_int32_t *datap);
+int    sili_pm_read(struct sili_port *ap, int target,
+                       int which, u_int32_t *res);
+int    sili_pm_write(struct sili_port *ap, int target,
+                       int which, u_int32_t data);
+void   sili_pm_check_good(struct sili_port *ap, int target);
+void   sili_ata_cmd_timeout(struct sili_ccb *ccb);
+struct sili_ccb *sili_get_ccb(struct sili_port *ap);
+void   sili_put_ccb(struct sili_ccb *ccb);
+struct sili_ccb *sili_get_err_ccb(struct sili_port *);
+void   sili_put_err_ccb(struct sili_ccb *);
+int    sili_poll(struct sili_ccb *ccb, int timeout,
+                       void (*timeout_fn)(struct sili_ccb *));
+
+int     sili_port_signature(struct sili_port *ap, struct ata_port *at,
+                       u_int32_t sig);
+void   sili_port_thread_core(struct sili_port *ap, int mask);
+
+void   sili_os_sleep(int ms);
+void   sili_os_hardsleep(int us);
+int    sili_os_softsleep(void);
+void   sili_os_start_port(struct sili_port *ap);
+void   sili_os_stop_port(struct sili_port *ap);
+void   sili_os_signal_port_thread(struct sili_port *ap, int mask);
+void   sili_os_lock_port(struct sili_port *ap);
+int    sili_os_lock_port_nb(struct sili_port *ap);
+void   sili_os_unlock_port(struct sili_port *ap);
+
+extern u_int32_t SiliForceGen1;
+extern u_int32_t SiliNoFeatures;
diff --git a/sys/dev/disk/sili/sili_attach.c b/sys/dev/disk/sili/sili_attach.c
new file mode 100644 (file)
index 0000000..213653f
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2006 David Gwynne <dlg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ *
+ * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com>
+ *
+ * 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.
+ * 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. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``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
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+ *
+ * $OpenBSD: sili.c,v 1.147 2009/02/16 21:19:07 miod Exp $
+ */
+
+#include "sili.h"
+
+static int     sili_pci_attach(device_t);
+static int     sili_pci_detach(device_t);
+
+static const struct sili_device sili_devices[] = {
+       {
+               .ad_vendor = PCI_VENDOR_SII,
+               .ad_product = PCI_PRODUCT_SII_3132,
+               .ad_nports = 2,
+               .ad_attach = sili_pci_attach,
+               .ad_detach = sili_pci_detach,
+               .name = "SiliconImage-3132-SATA"
+       },
+       { 0, 0, 0, NULL, NULL, NULL }
+};
+
+/*
+ * Match during probe and attach.  The device does not yet have a softc.
+ */
+const struct sili_device *
+sili_lookup_device(device_t dev)
+{
+       const struct sili_device *ad;
+       u_int16_t vendor = pci_get_vendor(dev);
+       u_int16_t product = pci_get_device(dev);
+#if 0
+       u_int8_t class = pci_get_class(dev);
+       u_int8_t subclass = pci_get_subclass(dev);
+       u_int8_t progif = pci_read_config(dev, PCIR_PROGIF, 1);
+#endif
+
+       for (ad = &sili_devices[0]; ad->ad_vendor; ++ad) {
+               if (ad->ad_vendor == vendor && ad->ad_product == product)
+                       return (ad);
+       }
+       return (NULL);
+#if 0
+       /*
+        * Last ad is the default match if the PCI device matches SATA.
+        */
+       if (class == PCIC_STORAGE && subclass == PCIS_STORAGE_SATA &&
+           progif == PCIP_STORAGE_SATA_SILI_1_0) {
+               return (ad);
+       }
+       return (NULL);
+#endif
+}
+
+static int
+sili_pci_attach(device_t dev)
+{
+       struct sili_softc *sc = device_get_softc(dev);
+       struct sili_port *ap;
+       const char *gen;
+       u_int32_t nports, reg;
+       bus_addr_t addr;
+       int i;
+       int error;
+
+       /*
+        * Map the SILI controller's IRQ, BAR(0) (global regs),
+        * and BAR(1) (port regs and lram).
+        */
+       sc->sc_dev = dev;
+       sc->sc_rid_irq = SILI_IRQ_RID;
+       sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_rid_irq,
+                                           RF_SHAREABLE | RF_ACTIVE);
+       if (sc->sc_irq == NULL) {
+               device_printf(dev, "unable to map interrupt\n");
+               sili_pci_detach(dev);
+               return (ENXIO);
+       }
+
+       /*
+        * When mapping the register window store the tag and handle
+        * separately so we can use the tag with per-port bus handle
+        * sub-spaces.
+        */
+       sc->sc_rid_regs = PCIR_BAR(0);
+       sc->sc_regs = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+                                            &sc->sc_rid_regs, RF_ACTIVE);
+       if (sc->sc_regs == NULL) {
+               device_printf(dev, "unable to map registers\n");
+               sili_pci_detach(dev);
+               return (ENXIO);
+       }
+       sc->sc_iot = rman_get_bustag(sc->sc_regs);
+       sc->sc_ioh = rman_get_bushandle(sc->sc_regs);
+
+       sc->sc_rid_pregs = PCIR_BAR(2);
+       sc->sc_pregs = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+                                             &sc->sc_rid_pregs, RF_ACTIVE);
+       if (sc->sc_pregs == NULL) {
+               device_printf(dev, "unable to map port registers\n");
+               sili_pci_detach(dev);
+               return (ENXIO);
+       }
+       sc->sc_piot = rman_get_bustag(sc->sc_pregs);
+       sc->sc_pioh = rman_get_bushandle(sc->sc_pregs);
+
+       /*
+        * Initialize the chipset and then set the interrupt vector up
+        */
+       error = sili_init(sc);
+       if (error) {
+               sili_pci_detach(dev);
+               return (ENXIO);
+       }
+
+       /*
+        * We assume at least 4 commands.
+        */
+       sc->sc_ncmds = SILI_MAX_CMDS;
+       sc->sc_flags |= SILI_F_64BIT;
+       sc->sc_flags |= SILI_F_NCQ;
+       sc->sc_flags |= SILI_F_SSNTF;
+       sc->sc_flags |= SILI_F_SPM;
+
+       addr = (sc->sc_flags & SILI_F_64BIT) ?
+               BUS_SPACE_MAXADDR : BUS_SPACE_MAXADDR_32BIT;
+
+       /*
+        * DMA tags for allocation of DMA memory buffers, lists, and so
+        * forth.  These are typically per-port.
+        *
+        * The stuff is mostly built into the BAR mappings.  We only need
+        * tags for our external SGE list and data.
+        */
+       error = 0;
+       error += bus_dma_tag_create(
+                       NULL,                           /* parent tag */
+                       256,                            /* alignment */
+                       65536,                          /* boundary */
+                       addr,                           /* loaddr? */
+                       BUS_SPACE_MAXADDR,              /* hiaddr */
+                       NULL,                           /* filter */
+                       NULL,                           /* filterarg */
+                       sizeof(struct sili_sge) * SILI_MAX_SGET * SILI_MAX_CMDS,
+                                                       /* [max]size */
+                       1,                              /* maxsegs */
+                       sizeof(struct sili_sge) * SILI_MAX_SGET * SILI_MAX_CMDS,
+                                                       /* maxsegsz */
+                       0,                              /* flags */
+                       &sc->sc_tag_sget);              /* return tag */
+
+       /*
+        * The data tag is used for later dmamaps and not immediately
+        * allocated.
+        */
+       error += bus_dma_tag_create(
+                       NULL,                           /* parent tag */
+                       4,                              /* alignment */
+                       0,                              /* boundary */
+                       addr,                           /* loaddr? */
+                       BUS_SPACE_MAXADDR,              /* hiaddr */
+                       NULL,                           /* filter */
+                       NULL,                           /* filterarg */
+                       4096 * 1024,                    /* maxiosize */
+                       SILI_MAX_SGET,                  /* maxsegs */
+                       65536,                          /* maxsegsz */
+                       0,                              /* flags */
+                       &sc->sc_tag_data);              /* return tag */
+
+       if (error) {
+               device_printf(dev, "unable to create dma tags\n");
+               sili_pci_detach(dev);
+               return (ENXIO);
+       }
+
+       if (sili_read(sc, SILI_REG_GCTL) & SILI_REG_GCTL_300CAP) {
+               gen = "1 (1.5Gbps) and 2 (3Gbps)";
+               sc->sc_flags |= SILI_F_300;
+       } else {
+               gen = "1 (1.5Gbps)";
+       }
+
+       nports = sc->sc_ad->ad_nports;
+       KKASSERT(nports <= SILI_MAX_PORTS);
+
+       kprintf("SILI %s ports=%d\n", sc->sc_ad->name, nports);
+
+       /*
+        * Allocate per-port resources
+        *
+        * All ports are attached in parallel but the CAM scan-bus
+        * is held up until all ports are attached so we get a deterministic
+        * order.
+        */
+       for (i = 0; error == 0 && i < nports; i++) {
+               error = sili_port_alloc(sc, i);
+       }
+
+       /*
+        * Setup the interrupt vector and enable interrupts.  Note that
+        * since the irq may be shared we do not set it up until we are
+        * ready to go.
+        */
+       if (error == 0) {
+               error = bus_setup_intr(dev, sc->sc_irq, 0, sili_intr, sc,
+                                      &sc->sc_irq_handle, NULL);
+       }
+
+       if (error) {
+               device_printf(dev, "unable to install interrupt\n");
+               sili_pci_detach(dev);
+               return (ENXIO);
+       }
+
+       /*
+        * Interrupt subsystem is good to go now, enable all port interrupts
+        */
+       crit_enter();
+       reg = sili_read(sc, SILI_REG_GCTL);
+       for (i = 0; i < nports; ++i)
+               reg |= SILI_REG_GCTL_PORTEN(i);
+       sili_write(sc, SILI_REG_GCTL, reg);
+       sc->sc_flags |= SILI_F_INT_GOOD;
+       crit_exit();
+       sili_intr(sc);
+
+       /*
+        * All ports are probing in parallel.  Wait for them to finish
+        * and then issue the cam attachment and bus scan serially so
+        * the 'da' assignments are deterministic.
+        */
+       for (i = 0; i < nports; i++) {
+               if ((ap = sc->sc_ports[i]) != NULL) {
+                       while (ap->ap_signal & AP_SIGF_INIT)
+                               tsleep(&ap->ap_signal, 0, "ahprb1", hz);
+                       sili_os_lock_port(ap);
+                       if (sili_cam_attach(ap) == 0) {
+                               sili_cam_changed(ap, NULL, -1);
+                               sili_os_unlock_port(ap);
+                               while ((ap->ap_flags & AP_F_SCAN_COMPLETED) == 0) {
+                                       tsleep(&ap->ap_flags, 0, "ahprb2", hz);
+                               }
+                       } else {
+                               sili_os_unlock_port(ap);
+                       }
+               }
+       }
+
+       return(0);
+}
+
+/*
+ * Device unload / detachment
+ */
+static int
+sili_pci_detach(device_t dev)
+{
+       struct sili_softc *sc = device_get_softc(dev);
+       struct sili_port *ap;
+       int     i;
+
+       /*
+        * Disable the controller and de-register the interrupt, if any.
+        *
+        * XXX interlock last interrupt?
+        */
+       sc->sc_flags &= ~SILI_F_INT_GOOD;
+       if (sc->sc_regs)
+               sili_write(sc, SILI_REG_GCTL, SILI_REG_GCTL_GRESET);
+
+       if (sc->sc_irq_handle) {
+               bus_teardown_intr(dev, sc->sc_irq, sc->sc_irq_handle);
+               sc->sc_irq_handle = NULL;
+       }
+
+       /*
+        * Free port structures and DMA memory
+        */
+       for (i = 0; i < SILI_MAX_PORTS; i++) {
+               ap = sc->sc_ports[i];
+               if (ap) {
+                       sili_cam_detach(ap);
+                       sili_port_free(sc, i);
+               }
+       }
+
+       /*
+        * Clean up the bus space
+        */
+       if (sc->sc_irq) {
+               bus_release_resource(dev, SYS_RES_IRQ,
+                                    sc->sc_rid_irq, sc->sc_irq);
+               sc->sc_irq = NULL;
+       }
+       if (sc->sc_regs) {
+               bus_release_resource(dev, SYS_RES_MEMORY,
+                                    sc->sc_rid_regs, sc->sc_regs);
+               sc->sc_regs = NULL;
+       }
+       if (sc->sc_pregs) {
+               bus_release_resource(dev, SYS_RES_MEMORY,
+                                    sc->sc_rid_pregs, sc->sc_pregs);
+               sc->sc_regs = NULL;
+       }
+
+       if (sc->sc_tag_sget) {
+               bus_dma_tag_destroy(sc->sc_tag_sget);
+               sc->sc_tag_sget = NULL;
+       }
+       if (sc->sc_tag_data) {
+               bus_dma_tag_destroy(sc->sc_tag_data);
+               sc->sc_tag_data = NULL;
+       }
+
+       return (0);
+}
diff --git a/sys/dev/disk/sili/sili_cam.c b/sys/dev/disk/sili/sili_cam.c
new file mode 100644 (file)
index 0000000..180e562
--- /dev/null
@@ -0,0 +1,1539 @@
+/*
+ * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com>
+ *
+ * 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.
+ * 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. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``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
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+ *
+ *
+ * Copyright (c) 2007 David Gwynne <dlg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $OpenBSD: atascsi.c,v 1.64 2009/02/16 21:19:06 miod Exp $
+ * $DragonFly$
+ */
+/*
+ * Implement each SATA port as its own SCSI bus on CAM.  This way we can
+ * implement future port multiplier features as individual devices on the
+ * bus.
+ *
+ * Much of the cdb<->xa conversion code was taken from OpenBSD, the rest
+ * was written natively for DragonFly.
+ */
+
+#include "sili.h"
+
+const char *ScsiTypeArray[32] = {
+       "DIRECT",
+       "SEQUENTIAL",
+       "PRINTER",
+       "PROCESSOR",
+       "WORM",
+       "CDROM",
+       "SCANNER",
+       "OPTICAL",
+       "CHANGER",
+       "COMM",
+       "ASC0",
+       "ASC1",
+       "STORARRAY",
+       "ENCLOSURE",
+       "RBC",
+       "OCRW",
+       "0x10",
+       "OSD",
+       "ADC",
+       "0x13",
+       "0x14",
+       "0x15",
+       "0x16",
+       "0x17",
+       "0x18",
+       "0x19",
+       "0x1A",
+       "0x1B",
+       "0x1C",
+       "0x1D",
+       "0x1E",
+       "NODEVICE"
+};
+
+static void sili_xpt_action(struct cam_sim *sim, union ccb *ccb);
+static void sili_xpt_poll(struct cam_sim *sim);
+static void sili_xpt_scsi_disk_io(struct sili_port *ap,
+                       struct ata_port *at, union ccb *ccb);
+static void sili_xpt_scsi_atapi_io(struct sili_port *ap,
+                       struct ata_port *at, union ccb *ccb);
+
+static void sili_ata_complete_disk_rw(struct ata_xfer *xa);
+static void sili_ata_complete_disk_synchronize_cache(struct ata_xfer *xa);
+static void sili_atapi_complete_cmd(struct ata_xfer *xa);
+static void sili_ata_dummy_sense(struct scsi_sense_data *sense_data);
+static void sili_ata_atapi_sense(struct ata_fis_d2h *rfis,
+                    struct scsi_sense_data *sense_data);
+
+static int sili_cam_probe_disk(struct sili_port *ap, struct ata_port *at);
+static int sili_cam_probe_atapi(struct sili_port *ap, struct ata_port *at);
+static void sili_ata_dummy_done(struct ata_xfer *xa);
+static void ata_fix_identify(struct ata_identify *id);
+static void sili_cam_rescan(struct sili_port *ap);
+
+int
+sili_cam_attach(struct sili_port *ap)
+{
+       struct cam_devq *devq;
+       struct cam_sim *sim;
+       int error;
+       int unit;
+
+       kprintf("%s: CAM ATTACH\n", PORTNAME(ap));
+       /*
+        * We want at least one ccb to be available for error processing
+        * so don't let CAM use more then ncmds - 1.
+        */
+       unit = device_get_unit(ap->ap_sc->sc_dev);
+       if (ap->ap_sc->sc_ncmds > 1)
+               devq = cam_simq_alloc(ap->ap_sc->sc_ncmds - 1);
+       else
+               devq = cam_simq_alloc(ap->ap_sc->sc_ncmds);
+       if (devq == NULL) {
+               return (ENOMEM);
+       }
+       sim = cam_sim_alloc(sili_xpt_action, sili_xpt_poll, "sili",
+                          (void *)ap, unit, &sim_mplock, 1, 1, devq);
+       cam_simq_release(devq);
+       if (sim == NULL) {
+               return (ENOMEM);
+       }
+       ap->ap_sim = sim;
+       sili_os_unlock_port(ap);
+       error = xpt_bus_register(ap->ap_sim, ap->ap_num);
+       sili_os_lock_port(ap);
+       if (error != CAM_SUCCESS) {
+               sili_cam_detach(ap);
+               return (EINVAL);
+       }
+       kprintf("%s: CAM ATTACH2\n", PORTNAME(ap));
+       ap->ap_flags |= AP_F_BUS_REGISTERED;
+       kprintf("%s: CAM ATTACH3 %d\n", PORTNAME(ap), ap->ap_probe);
+
+       if (ap->ap_probe == ATA_PROBE_NEED_IDENT)
+               error = sili_cam_probe(ap, NULL);
+       else
+               error = 0;
+       if (error) {
+               sili_cam_detach(ap);
+               return (EIO);
+       }
+       ap->ap_flags |= AP_F_CAM_ATTACHED;
+
+       return(0);
+}
+
+/*
+ * The state of the port has changed.
+ *
+ * If at is NULL the physical port has changed state.
+ * If at is non-NULL a particular target behind a PM has changed state.
+ *
+ * If found is -1 the target state must be queued to a non-interrupt context.
+ * (only works with at == NULL).
+ *
+ * If found is 0 the target was removed.
+ * If found is 1 the target was inserted.
+ */
+void
+sili_cam_changed(struct sili_port *ap, struct ata_port *atx, int found)
+{
+       struct cam_path *tmppath;
+       int status;
+       int target;
+
+       target = atx ? atx->at_target : CAM_TARGET_WILDCARD;
+
+       if (ap->ap_sim == NULL)
+               return;
+       if (found == CAM_TARGET_WILDCARD) {
+               status = xpt_create_path(&tmppath, NULL,
+                                        cam_sim_path(ap->ap_sim),
+                                        target, CAM_LUN_WILDCARD);
+               if (status != CAM_REQ_CMP)
+                       return;
+               sili_cam_rescan(ap);
+       } else {
+               status = xpt_create_path(&tmppath, NULL,
+                                        cam_sim_path(ap->ap_sim),
+                                        target,
+                                        CAM_LUN_WILDCARD);
+               if (status != CAM_REQ_CMP)
+                       return;
+#if 0
+               /*
+                * This confuses CAM
+                */
+               if (found)
+                       xpt_async(AC_FOUND_DEVICE, tmppath, NULL);
+               else
+                       xpt_async(AC_LOST_DEVICE, tmppath, NULL);
+#endif
+       }
+       xpt_free_path(tmppath);
+}
+
+void
+sili_cam_detach(struct sili_port *ap)
+{
+       int error;
+
+       if ((ap->ap_flags & AP_F_CAM_ATTACHED) == 0)
+               return;
+       get_mplock();
+       if (ap->ap_sim) {
+               xpt_freeze_simq(ap->ap_sim, 1);
+       }
+       if (ap->ap_flags & AP_F_BUS_REGISTERED) {
+               error = xpt_bus_deregister(cam_sim_path(ap->ap_sim));
+               KKASSERT(error == CAM_REQ_CMP);
+               ap->ap_flags &= ~AP_F_BUS_REGISTERED;
+       }
+       if (ap->ap_sim) {
+               cam_sim_free(ap->ap_sim);
+               ap->ap_sim = NULL;
+       }
+       rel_mplock();
+       ap->ap_flags &= ~AP_F_CAM_ATTACHED;
+}
+
+/*
+ * Once the SILI port has been attached we need to probe for a device or
+ * devices on the port and setup various options.
+ *
+ * If at is NULL we are probing the direct-attached device on the port,
+ * which may or may not be a port multiplier.
+ */
+int
+sili_cam_probe(struct sili_port *ap, struct ata_port *atx)
+{
+       struct ata_port *at;
+       struct ata_xfer *xa;
+       u_int64_t       capacity;
+       u_int64_t       capacity_bytes;
+       int             model_len;
+       int             error;
+       int             devncqdepth;
+       int             i;
+       const char      *wcstr;
+       const char      *rastr;
+       const char      *scstr;
+       const char      *type;
+
+       error = EIO;
+
+       /*
+        * Delayed CAM attachment for initial probe, sim may be NULL
+        */
+       if (ap->ap_sim == NULL)
+               return(0);
+
+       /*
+        * A NULL atx indicates a probe of the directly connected device.
+        * A non-NULL atx indicates a device connected via a port multiplier.
+        * We need to preserve atx for calls to sili_ata_get_xfer().
+        *
+        * at is always non-NULL.  For directly connected devices we supply
+        * an (at) pointing to target 0.
+        */
+       if (atx == NULL) {
+               at = ap->ap_ata;        /* direct attached - device 0 */
+               if (ap->ap_type == ATA_PORT_T_PM) {
+                       kprintf("%s: Found Port Multiplier\n",
+                               ATANAME(ap, atx));
+                       return (0);
+               }
+               at->at_type = ap->ap_type;
+       } else {
+               at = atx;
+               if (atx->at_type == ATA_PORT_T_PM) {
+                       kprintf("%s: Bogus device, reducing port count to %d\n",
+                               ATANAME(ap, atx), atx->at_target);
+                       if (ap->ap_pmcount > atx->at_target)
+                               ap->ap_pmcount = atx->at_target;
+                       goto err;
+               }
+       }
+       if (ap->ap_type == ATA_PORT_T_NONE)
+               goto err;
+       if (at->at_type == ATA_PORT_T_NONE)
+               goto err;
+
+       /*
+        * Issue identify, saving the result
+        */
+       xa = sili_ata_get_xfer(ap, atx);
+       xa->complete = sili_ata_dummy_done;
+       xa->data = &at->at_identify;
+       xa->datalen = sizeof(at->at_identify);
+       xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
+
+       switch(at->at_type) {
+       case ATA_PORT_T_DISK:
+               xa->fis->command = ATA_C_IDENTIFY;
+               type = "DISK";
+               break;
+       case ATA_PORT_T_ATAPI:
+               xa->fis->command = ATA_C_ATAPI_IDENTIFY;
+               type = "ATAPI";
+               break;
+       default:
+               xa->fis->command = ATA_C_ATAPI_IDENTIFY;
+               type = "UNKNOWN(ATAPI?)";
+               break;
+       }
+       xa->fis->features = 0;
+       xa->fis->device = 0;
+       xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
+       xa->timeout = 1000;
+
+       if (sili_ata_cmd(xa) != ATA_S_COMPLETE) {
+               kprintf("%s: Detected %s device but unable to IDENTIFY\n",
+                       ATANAME(ap, atx), type);
+               sili_ata_put_xfer(xa);
+               goto err;
+       }
+       sili_ata_put_xfer(xa);
+
+       ata_fix_identify(&at->at_identify);
+
+       /*
+        * Read capacity using SATA probe info.
+        */
+       if (le16toh(at->at_identify.cmdset83) & 0x0400) {
+               /* LBA48 feature set supported */
+               capacity = 0;
+               for (i = 3; i >= 0; --i) {
+                       capacity <<= 16;
+                       capacity +=
+                           le16toh(at->at_identify.addrsecxt[i]);
+               }
+       } else {
+               capacity = le16toh(at->at_identify.addrsec[1]);
+               capacity <<= 16;
+               capacity += le16toh(at->at_identify.addrsec[0]);
+       }
+       at->at_capacity = capacity;
+       if (atx == NULL)
+               ap->ap_probe = ATA_PROBE_GOOD;
+
+       capacity_bytes = capacity * 512;
+
+       /*
+        * Negotiate NCQ, throw away any ata_xfer's beyond the negotiated
+        * number of slots and limit the number of CAM ccb's to one less
+        * so we always have a slot available for recovery.
+        *
+        * NCQ is not used if ap_ncqdepth is 1 or the host controller does
+        * not support it, and in that case the driver can handle extra
+        * ccb's.
+        *
+        * NCQ is currently used only with direct-attached disks.  It is
+        * not used with port multipliers or direct-attached ATAPI devices.
+        *
+        * Remember at least one extra CCB needs to be reserved for the
+        * error ccb.
+        */
+       if ((ap->ap_sc->sc_flags & SILI_F_NCQ) &&
+           at->at_type == ATA_PORT_T_DISK &&
+           (le16toh(at->at_identify.satacap) & (1 << 8))) {
+               at->at_ncqdepth = (le16toh(at->at_identify.qdepth) & 0x1F) + 1;
+               devncqdepth = at->at_ncqdepth;
+               if (at->at_ncqdepth > ap->ap_sc->sc_ncmds)
+                       at->at_ncqdepth = ap->ap_sc->sc_ncmds;
+               if (at->at_ncqdepth > 1) {
+                       for (i = 0; i < ap->ap_sc->sc_ncmds; ++i) {
+                               xa = sili_ata_get_xfer(ap, atx);
+                               if (xa->tag < at->at_ncqdepth) {
+                                       xa->state = ATA_S_COMPLETE;
+                                       sili_ata_put_xfer(xa);
+                               }
+                       }
+                       if (at->at_ncqdepth >= ap->ap_sc->sc_ncmds) {
+                               cam_devq_resize(ap->ap_sim->devq,
+                                               at->at_ncqdepth - 1);
+                       }
+               }
+       } else {
+               devncqdepth = 0;
+       }
+
+       /*
+        * Make the model string a bit more presentable
+        */
+       for (model_len = 40; model_len; --model_len) {
+               if (at->at_identify.model[model_len-1] == ' ')
+                       continue;
+               if (at->at_identify.model[model_len-1] == 0)
+                       continue;
+               break;
+       }
+
+       /*
+        * Generate informatiive strings.
+        *
+        * NOTE: We do not automatically set write caching, lookahead,
+        *       or the security state for ATAPI devices.
+        */
+       if (at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) {
+               if (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE)
+                       wcstr = "enabled";
+               else if (at->at_type == ATA_PORT_T_ATAPI)
+                       wcstr = "disabled";
+               else
+                       wcstr = "enabling";
+       } else {
+                   wcstr = "notsupp";
+       }
+
+       if (at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) {
+               if (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD)
+                       rastr = "enabled";
+               else if (at->at_type == ATA_PORT_T_ATAPI)
+                       rastr = "disabled";
+               else
+                       rastr = "enabling";
+       } else {
+                   rastr = "notsupp";
+       }
+
+       if (at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) {
+               if (at->at_identify.securestatus & ATA_SECURE_FROZEN)
+                       scstr = "frozen";
+               else if (at->at_type == ATA_PORT_T_ATAPI)
+                       scstr = "unfrozen";
+               else if (SiliNoFeatures & (1 << ap->ap_num))
+                       scstr = "<disabled>";
+               else
+                       scstr = "freezing";
+       } else {
+                   scstr = "notsupp";
+       }
+
+       kprintf("%s: Found %s \"%*.*s %8.8s\" serial=\"%20.20s\"\n"
+               "%s: tags=%d/%d satacap=%04x satafea=%04x NCQ=%s "
+               "capacity=%lld.%02dMB\n",
+
+               ATANAME(ap, atx),
+               type,
+               model_len, model_len,
+               at->at_identify.model,
+               at->at_identify.firmware,
+               at->at_identify.serial,
+
+               ATANAME(ap, atx),
+               devncqdepth, ap->ap_sc->sc_ncmds,
+               at->at_identify.satacap,
+               at->at_identify.satafsup,
+               (at->at_ncqdepth > 1 ? "YES" : "NO"),
+               (long long)capacity_bytes / (1024 * 1024),
+               (int)(capacity_bytes % (1024 * 1024)) * 100 / (1024 * 1024)
+       );
+       kprintf("%s: f85=%04x f86=%04x f87=%04x WC=%s RA=%s SEC=%s\n",
+               ATANAME(ap, atx),
+               at->at_identify.features85,
+               at->at_identify.features86,
+               at->at_identify.features87,
+               wcstr,
+               rastr,
+               scstr
+       );
+
+       /*
+        * Additional type-specific probing
+        */
+       switch(at->at_type) {
+       case ATA_PORT_T_DISK:
+               error = sili_cam_probe_disk(ap, atx);
+               break;
+       case ATA_PORT_T_ATAPI:
+               error = sili_cam_probe_atapi(ap, atx);
+               break;
+       default:
+               error = EIO;
+               break;
+       }
+err:
+       kprintf("%s: CAM PROBE %d\n", ATANAME(ap, atx), error);
+       if (error) {
+               at->at_probe = ATA_PROBE_FAILED;
+               if (atx == NULL)
+                       ap->ap_probe = at->at_probe;
+       } else {
+               at->at_probe = ATA_PROBE_GOOD;
+               if (atx == NULL)
+                       ap->ap_probe = at->at_probe;
+       }
+       return (error);
+}
+
+/*
+ * DISK-specific probe after initial ident
+ */
+static int
+sili_cam_probe_disk(struct sili_port *ap, struct ata_port *atx)
+{
+       struct ata_port *at;
+       struct ata_xfer *xa;
+
+       at = atx ? atx : ap->ap_ata;
+
+       /*
+        * Enable write cache if supported
+        *
+        * NOTE: "WD My Book" external disk devices have a very poor
+        *       daughter board between the the ESATA and the HD.  Sending
+        *       any ATA_C_SET_FEATURES commands will break the hardware port
+        *       with a fatal protocol error.  However, this device also
+        *       indicates that WRITECACHE is already on and READAHEAD is
+        *       not supported so we avoid the issue.
+        */
+       if ((at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) &&
+           (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE) == 0) {
+               xa = sili_ata_get_xfer(ap, atx);
+               xa->complete = sili_ata_dummy_done;
+               xa->fis->command = ATA_C_SET_FEATURES;
+               /*xa->fis->features = ATA_SF_WRITECACHE_EN;*/
+               xa->fis->features = ATA_SF_LOOKAHEAD_EN;
+               xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
+               xa->fis->device = 0;
+               xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
+               xa->timeout = 1000;
+               xa->datalen = 0;
+               if (sili_ata_cmd(xa) == ATA_S_COMPLETE)
+                       at->at_features |= ATA_PORT_F_WCACHE;
+               else
+                       kprintf("%s: Unable to enable write-caching\n",
+                               ATANAME(ap, atx));
+               sili_ata_put_xfer(xa);
+       }
+
+       /*
+        * Enable readahead if supported
+        */
+       if ((at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) &&
+           (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD) == 0) {
+               xa = sili_ata_get_xfer(ap, atx);
+               xa->complete = sili_ata_dummy_done;
+               xa->fis->command = ATA_C_SET_FEATURES;
+               xa->fis->features = ATA_SF_LOOKAHEAD_EN;
+               xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
+               xa->fis->device = 0;
+               xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
+               xa->timeout = 1000;
+               xa->datalen = 0;
+               if (sili_ata_cmd(xa) == ATA_S_COMPLETE)
+                       at->at_features |= ATA_PORT_F_RAHEAD;
+               else
+                       kprintf("%s: Unable to enable read-ahead\n",
+                               ATANAME(ap, atx));
+               sili_ata_put_xfer(xa);
+       }
+
+       /*
+        * FREEZE LOCK the device so malicious users can't lock it on us.
+        * As there is no harm in issuing this to devices that don't
+        * support the security feature set we just send it, and don't bother
+        * checking if the device sends a command abort to tell us it doesn't
+        * support it
+        */
+       if ((at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) &&
+           (at->at_identify.securestatus & ATA_SECURE_FROZEN) == 0 &&
+           (SiliNoFeatures & (1 << ap->ap_num)) == 0) {
+               xa = sili_ata_get_xfer(ap, atx);
+               xa->complete = sili_ata_dummy_done;
+               xa->fis->command = ATA_C_SEC_FREEZE_LOCK;
+               xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
+               xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
+               xa->timeout = 1000;
+               xa->datalen = 0;
+               if (sili_ata_cmd(xa) == ATA_S_COMPLETE)
+                       at->at_features |= ATA_PORT_F_FRZLCK;
+               else
+                       kprintf("%s: Unable to set security freeze\n",
+                               ATANAME(ap, atx));
+               sili_ata_put_xfer(xa);
+       }
+
+       return (0);
+}
+
+/*
+ * ATAPI-specific probe after initial ident
+ */
+static int
+sili_cam_probe_atapi(struct sili_port *ap, struct ata_port *atx)
+{
+       return(0);
+}
+
+/*
+ * Fix byte ordering so buffers can be accessed as
+ * strings.
+ */
+static void
+ata_fix_identify(struct ata_identify *id)
+{
+       u_int16_t       *swap;
+       int             i;
+
+       swap = (u_int16_t *)id->serial;
+       for (i = 0; i < sizeof(id->serial) / sizeof(u_int16_t); i++)
+               swap[i] = bswap16(swap[i]);
+
+       swap = (u_int16_t *)id->firmware;
+       for (i = 0; i < sizeof(id->firmware) / sizeof(u_int16_t); i++)
+               swap[i] = bswap16(swap[i]);
+
+       swap = (u_int16_t *)id->model;
+       for (i = 0; i < sizeof(id->model) / sizeof(u_int16_t); i++)
+               swap[i] = bswap16(swap[i]);
+}
+
+/*
+ * Dummy done callback for xa.
+ */
+static void
+sili_ata_dummy_done(struct ata_xfer *xa)
+{
+}
+
+/*
+ * Use an engineering request to initiate a target scan for devices
+ * behind a port multiplier.
+ *
+ * An asynchronous bus scan is used to avoid reentrancy issues.
+ */
+static void
+sili_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb)
+{
+       struct sili_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
+
+       if (ccb->ccb_h.func_code == XPT_SCAN_BUS) {
+               kprintf("%s: CAM RESCAN CALLBACK\n", PORTNAME(ap));
+               ap->ap_flags &= ~AP_F_SCAN_RUNNING;
+               if (ap->ap_flags & AP_F_SCAN_REQUESTED) {
+                       ap->ap_flags &= ~AP_F_SCAN_REQUESTED;
+                       sili_cam_rescan(ap);
+               }
+               ap->ap_flags |= AP_F_SCAN_COMPLETED;
+               wakeup(&ap->ap_flags);
+       }
+       xpt_free_ccb(ccb);
+}
+
+static void
+sili_cam_rescan(struct sili_port *ap)
+{
+       struct cam_path *path;
+       union ccb *ccb;
+       int status;
+       int i;
+
+       kprintf("%s: CAM RESCAN-A\n", PORTNAME(ap));
+       if (ap->ap_flags & AP_F_SCAN_RUNNING) {
+               ap->ap_flags |= AP_F_SCAN_REQUESTED;
+               return;
+       }
+       ap->ap_flags |= AP_F_SCAN_RUNNING;
+       for (i = 0; i < SILI_MAX_PMPORTS; ++i) {
+               ap->ap_ata[i].at_features |= ATA_PORT_F_RESCAN;
+       }
+
+       status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim),
+                                CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
+       if (status != CAM_REQ_CMP)
+               return;
+
+       ccb = xpt_alloc_ccb();
+       xpt_setup_ccb(&ccb->ccb_h, path, 5);    /* 5 = low priority */
+       ccb->ccb_h.func_code = XPT_ENG_EXEC;
+       ccb->ccb_h.cbfcnp = sili_cam_rescan_callback;
+       ccb->ccb_h.sim_priv.entries[0].ptr = ap;
+       ccb->crcn.flags = CAM_FLAG_NONE;
+       xpt_action_async(ccb);
+}
+
+static void
+sili_xpt_rescan(struct sili_port *ap)
+{
+       struct cam_path *path;
+       union ccb *ccb;
+       int status;
+
+       kprintf("%s: CAM RESCAN-B\n", PORTNAME(ap));
+       status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim),
+                                CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
+       if (status != CAM_REQ_CMP)
+               return;
+
+       ccb = xpt_alloc_ccb();
+       xpt_setup_ccb(&ccb->ccb_h, path, 5);    /* 5 = low priority */
+       ccb->ccb_h.func_code = XPT_SCAN_BUS;
+       ccb->ccb_h.cbfcnp = sili_cam_rescan_callback;
+       ccb->ccb_h.sim_priv.entries[0].ptr = ap;
+       ccb->crcn.flags = CAM_FLAG_NONE;
+       xpt_action_async(ccb);
+}
+
+/*
+ * Action function - dispatch command
+ */
+static
+void
+sili_xpt_action(struct cam_sim *sim, union ccb *ccb)
+{
+       struct sili_port *ap;
+       struct ata_port  *at, *atx;
+       struct ccb_hdr *ccbh;
+       int unit;
+
+       /* XXX lock */
+       ap = cam_sim_softc(sim);
+       at = ap->ap_ata;
+       atx = NULL;
+       KKASSERT(ap != NULL);
+       ccbh = &ccb->ccb_h;
+       unit = cam_sim_unit(sim);
+
+       /*
+        * Early failure checks.  These checks do not apply to XPT_PATH_INQ,
+        * otherwise the bus rescan will not remove the dead devices when
+        * unplugging a PM.
+        *
+        * For non-wildcards we have one target (0) and one lun (0),
+        * unless we have a port multiplier.
+        *
+        * A wildcard target indicates only the general bus is being
+        * probed.
+        *
+        * Calculate at and atx.  at is always non-NULL.  atx is only
+        * non-NULL for direct-attached devices.  It will be NULL for
+        * devices behind a port multiplier.
+        *
+        * XXX What do we do with a LUN wildcard?
+        */
+       if (ccbh->target_id != CAM_TARGET_WILDCARD &&
+           ccbh->func_code != XPT_PATH_INQ) {
+               if (ap->ap_type == ATA_PORT_T_NONE) {
+                       ccbh->status = CAM_DEV_NOT_THERE;
+                       xpt_done(ccb);
+                       return;
+               }
+               if (ccbh->target_id < 0 || ccbh->target_id >= ap->ap_pmcount) {
+                       ccbh->status = CAM_DEV_NOT_THERE;
+                       xpt_done(ccb);
+                       return;
+               }
+               at += ccbh->target_id;
+               if (ap->ap_type == ATA_PORT_T_PM)
+                       atx = at;
+
+               if (ccbh->target_lun != CAM_LUN_WILDCARD && ccbh->target_lun) {
+                       ccbh->status = CAM_DEV_NOT_THERE;
+                       xpt_done(ccb);
+                       return;
+               }
+       }
+
+       /*
+        * Switch on the meta XPT command
+        */
+       switch(ccbh->func_code) {
+       case XPT_ENG_EXEC:
+               /*
+                * This routine is called after a port multiplier has been
+                * probed.
+                */
+               ccbh->status = CAM_REQ_CMP;
+               sili_os_lock_port(ap);
+               sili_port_state_machine(ap, 0);
+               sili_os_unlock_port(ap);
+               xpt_done(ccb);
+               sili_xpt_rescan(ap);
+               break;
+       case XPT_PATH_INQ:
+               /*
+                * This command always succeeds, otherwise the bus scan
+                * will not detach dead devices.
+                */
+               ccb->cpi.version_num = 1;
+               ccb->cpi.hba_inquiry = 0;
+               ccb->cpi.target_sprt = 0;
+               ccb->cpi.hba_misc = PIM_SEQSCAN;
+               ccb->cpi.hba_eng_cnt = 0;
+               bzero(ccb->cpi.vuhba_flags, sizeof(ccb->cpi.vuhba_flags));
+               ccb->cpi.max_target = SILI_MAX_PMPORTS - 1;
+               ccb->cpi.max_lun = 0;
+               ccb->cpi.async_flags = 0;
+               ccb->cpi.hpath_id = 0;
+               ccb->cpi.initiator_id = SILI_MAX_PMPORTS - 1;
+               ccb->cpi.unit_number = cam_sim_unit(sim);
+               ccb->cpi.bus_id = cam_sim_bus(sim);
+               ccb->cpi.base_transfer_speed = 150000;
+               ccb->cpi.transport = XPORT_SATA;
+               ccb->cpi.transport_version = 1;
+               ccb->cpi.protocol = PROTO_SCSI;
+               ccb->cpi.protocol_version = SCSI_REV_2;
+
+               ccbh->status = CAM_REQ_CMP;
+               if (ccbh->target_id == CAM_TARGET_WILDCARD) {
+                       sili_os_lock_port(ap);
+                       sili_port_state_machine(ap, 0);
+                       sili_os_unlock_port(ap);
+               } else {
+                       switch(sili_pread(ap, SILI_PREG_SSTS) &
+                              SILI_PREG_SSTS_SPD) {
+                       case SILI_PREG_SSTS_SPD_GEN1:
+                               ccb->cpi.base_transfer_speed = 150000;
+                               break;
+                       case SILI_PREG_SSTS_SPD_GEN2:
+                               ccb->cpi.base_transfer_speed = 300000;
+                               break;
+                       default:
+                               /* unknown */
+                               ccb->cpi.base_transfer_speed = 1000;
+                               break;
+                       }
+#if 0
+                       if (ap->ap_type == ATA_PORT_T_NONE)
+                               ccbh->status = CAM_DEV_NOT_THERE;
+#endif
+               }
+               xpt_done(ccb);
+               break;
+       case XPT_RESET_DEV:
+               sili_os_lock_port(ap);
+               if (ap->ap_type == ATA_PORT_T_NONE) {
+                       ccbh->status = CAM_DEV_NOT_THERE;
+               } else {
+                       sili_port_reset(ap, atx, 0);
+                       ccbh->status = CAM_REQ_CMP;
+               }
+               sili_os_unlock_port(ap);
+               xpt_done(ccb);
+               break;
+       case XPT_RESET_BUS:
+               sili_os_lock_port(ap);
+               sili_port_reset(ap, NULL, 1);
+               sili_os_unlock_port(ap);
+               ccbh->status = CAM_REQ_CMP;
+               xpt_done(ccb);
+               break;
+       case XPT_SET_TRAN_SETTINGS:
+               ccbh->status = CAM_FUNC_NOTAVAIL;
+               xpt_done(ccb);
+               break;
+       case XPT_GET_TRAN_SETTINGS:
+               ccb->cts.protocol = PROTO_SCSI;
+               ccb->cts.protocol_version = SCSI_REV_2;
+               ccb->cts.transport = XPORT_SATA;
+               ccb->cts.transport_version = XPORT_VERSION_UNSPECIFIED;
+               ccb->cts.proto_specific.valid = 0;
+               ccb->cts.xport_specific.valid = 0;
+               ccbh->status = CAM_REQ_CMP;
+               xpt_done(ccb);
+               break;
+       case XPT_CALC_GEOMETRY:
+               cam_calc_geometry(&ccb->ccg, 1);
+               xpt_done(ccb);
+               break;
+       case XPT_SCSI_IO:
+               /*
+                * Our parallel startup code might have only probed through
+                * to the IDENT, so do the last step if necessary.
+                */
+               if (at->at_probe == ATA_PROBE_NEED_IDENT)
+                       sili_cam_probe(ap, atx);
+               if (at->at_probe != ATA_PROBE_GOOD) {
+                       ccbh->status = CAM_DEV_NOT_THERE;
+                       xpt_done(ccb);
+                       break;
+               }
+               switch(at->at_type) {
+               case ATA_PORT_T_DISK:
+                       sili_xpt_scsi_disk_io(ap, atx, ccb);
+                       break;
+               case ATA_PORT_T_ATAPI:
+                       sili_xpt_scsi_atapi_io(ap, atx, ccb);
+                       break;
+               default:
+                       ccbh->status = CAM_REQ_INVALID;
+                       xpt_done(ccb);
+                       break;
+               }
+               break;
+       default:
+               ccbh->status = CAM_REQ_INVALID;
+               xpt_done(ccb);
+               break;
+       }
+}
+
+/*
+ * Poll function.
+ *
+ * Generally this function gets called heavily when interrupts might be
+ * non-operational, during a halt/reboot or panic.
+ */
+static
+void
+sili_xpt_poll(struct cam_sim *sim)
+{
+       struct sili_port *ap;
+
+       ap = cam_sim_softc(sim);
+       crit_enter();
+       sili_os_lock_port(ap);
+       sili_port_intr(ap, 1);
+       sili_os_unlock_port(ap);
+       crit_exit();
+}
+
+/*
+ * Convert the SCSI command in ccb to an ata_xfer command in xa
+ * for ATA_PORT_T_DISK operations.  Set the completion function
+ * to convert the response back, then dispatch to the OpenBSD SILI
+ * layer.
+ *
+ * SILI DISK commands only support a limited command set, and we
+ * fake additional commands to make it play nice with the CAM subsystem.
+ */
+static
+void
+sili_xpt_scsi_disk_io(struct sili_port *ap, struct ata_port *atx,
+                     union ccb *ccb)
+{
+       struct ccb_hdr *ccbh;
+       struct ccb_scsiio *csio;
+       struct ata_xfer *xa;
+       struct ata_port *at;
+       struct ata_fis_h2d *fis;
+       scsi_cdb_t cdb;
+       union scsi_data *rdata;
+       int rdata_len;
+       u_int64_t capacity;
+       u_int64_t lba;
+       u_int32_t count;
+
+       ccbh = &ccb->csio.ccb_h;
+       csio = &ccb->csio;
+       at = atx ? atx : &ap->ap_ata[0];
+
+       /*
+        * XXX not passing NULL at for direct attach!
+        */
+       xa = sili_ata_get_xfer(ap, atx);
+       rdata = (void *)csio->data_ptr;
+       rdata_len = csio->dxfer_len;
+
+       /*
+        * Build the FIS or process the csio to completion.
+        */
+       cdb = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
+                       csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);
+
+       switch(cdb->generic.opcode) {
+       case REQUEST_SENSE:
+               /*
+                * Auto-sense everything, so explicit sense requests
+                * return no-sense.
+                */
+               ccbh->status = CAM_SCSI_STATUS_ERROR;
+               break;
+       case INQUIRY:
+               /*
+                * Inquiry supported features
+                *
+                * [opcode, byte2, page_code, length, control]
+                */
+               if (cdb->inquiry.byte2 & SI_EVPD) {
+                       switch(cdb->inquiry.page_code) {
+                       case SVPD_SUPPORTED_PAGE_LIST:
+                               /* XXX atascsi_disk_vpd_supported */
+                       case SVPD_UNIT_SERIAL_NUMBER:
+                               /* XXX atascsi_disk_vpd_serial */
+                       case SVPD_UNIT_DEVID:
+                               /* XXX atascsi_disk_vpd_ident */
+                       default:
+                               ccbh->status = CAM_FUNC_NOTAVAIL;
+                               break;
+                       }
+               } else {
+                       bzero(rdata, rdata_len);
+                       if (rdata_len < SHORT_INQUIRY_LENGTH) {
+                               ccbh->status = CAM_CCB_LEN_ERR;
+                               break;
+                       }
+                       if (rdata_len > sizeof(rdata->inquiry_data))
+                               rdata_len = sizeof(rdata->inquiry_data);
+                       rdata->inquiry_data.device = T_DIRECT;
+                       rdata->inquiry_data.version = SCSI_REV_SPC2;
+                       rdata->inquiry_data.response_format = 2;
+                       rdata->inquiry_data.additional_length = 32;
+                       bcopy("SATA    ", rdata->inquiry_data.vendor, 8);
+                       bcopy(at->at_identify.model,
+                             rdata->inquiry_data.product,
+                             sizeof(rdata->inquiry_data.product));
+                       bcopy(at->at_identify.firmware,
+                             rdata->inquiry_data.revision,
+                             sizeof(rdata->inquiry_data.revision));
+                       ccbh->status = CAM_REQ_CMP;
+               }
+               break;
+       case READ_CAPACITY_16:
+               if (cdb->read_capacity_16.service_action != SRC16_SERVICE_ACTION) {
+                       ccbh->status = CAM_REQ_INVALID;
+                       break;
+               }
+               if (rdata_len < sizeof(rdata->read_capacity_data_16)) {
+                       ccbh->status = CAM_CCB_LEN_ERR;
+                       break;
+               }
+               /* fall through */
+       case READ_CAPACITY:
+               if (rdata_len < sizeof(rdata->read_capacity_data)) {
+                       ccbh->status = CAM_CCB_LEN_ERR;
+                       break;
+               }
+
+               capacity = at->at_capacity;
+
+               bzero(rdata, rdata_len);
+               if (cdb->generic.opcode == READ_CAPACITY) {
+                       rdata_len = sizeof(rdata->read_capacity_data);
+                       if (capacity > 0xFFFFFFFFU)
+                               capacity = 0xFFFFFFFFU;
+                       bzero(&rdata->read_capacity_data, rdata_len);
+                       scsi_ulto4b((u_int32_t)capacity - 1,
+                                   rdata->read_capacity_data.addr);
+                       scsi_ulto4b(512, rdata->read_capacity_data.length);
+               } else {
+                       rdata_len = sizeof(rdata->read_capacity_data_16);
+                       bzero(&rdata->read_capacity_data_16, rdata_len);
+                       scsi_u64to8b(capacity - 1,
+                                    rdata->read_capacity_data_16.addr);
+                       scsi_ulto4b(512, rdata->read_capacity_data_16.length);
+               }
+               ccbh->status = CAM_REQ_CMP;
+               break;
+       case SYNCHRONIZE_CACHE:
+               /*
+                * Synchronize cache.  Specification says this can take
+                * greater then 30 seconds so give it at least 45.
+                */
+               fis = xa->fis;
+               fis->flags = ATA_H2D_FLAGS_CMD;
+               fis->command = ATA_C_FLUSH_CACHE;
+               fis->device = 0;
+               if (xa->timeout < 45000)
+                       xa->timeout = 45000;
+               xa->datalen = 0;
+               xa->flags = ATA_F_READ;
+               xa->complete = sili_ata_complete_disk_synchronize_cache;
+               break;
+       case TEST_UNIT_READY:
+       case START_STOP_UNIT:
+       case PREVENT_ALLOW:
+               /*
+                * Just silently return success
+                */
+               ccbh->status = CAM_REQ_CMP;
+               rdata_len = 0;
+               break;
+       case ATA_PASS_12:
+       case ATA_PASS_16:
+               /*
+                * XXX implement pass-through
+                */
+               ccbh->status = CAM_FUNC_NOTAVAIL;
+               break;
+       default:
+               switch(cdb->generic.opcode) {
+               case READ_6:
+                       lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF;
+                       count = cdb->rw_6.length ? cdb->rw_6.length : 0x100;
+                       xa->flags = ATA_F_READ;
+                       break;
+               case READ_10:
+                       lba = scsi_4btoul(cdb->rw_10.addr);
+                       count = scsi_2btoul(cdb->rw_10.length);
+                       xa->flags = ATA_F_READ;
+                       break;
+               case READ_12:
+                       lba = scsi_4btoul(cdb->rw_12.addr);
+                       count = scsi_4btoul(cdb->rw_12.length);
+                       xa->flags = ATA_F_READ;
+                       break;
+               case READ_16:
+                       lba = scsi_8btou64(cdb->rw_16.addr);
+                       count = scsi_4btoul(cdb->rw_16.length);
+                       xa->flags = ATA_F_READ;
+                       break;
+               case WRITE_6:
+                       lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF;
+                       count = cdb->rw_6.length ? cdb->rw_6.length : 0x100;
+                       xa->flags = ATA_F_WRITE;
+                       break;
+               case WRITE_10:
+                       lba = scsi_4btoul(cdb->rw_10.addr);
+                       count = scsi_2btoul(cdb->rw_10.length);
+                       xa->flags = ATA_F_WRITE;
+                       break;
+               case WRITE_12:
+                       lba = scsi_4btoul(cdb->rw_12.addr);
+                       count = scsi_4btoul(cdb->rw_12.length);
+                       xa->flags = ATA_F_WRITE;
+                       break;
+               case WRITE_16:
+                       lba = scsi_8btou64(cdb->rw_16.addr);
+                       count = scsi_4btoul(cdb->rw_16.length);
+                       xa->flags = ATA_F_WRITE;
+                       break;
+               default:
+                       ccbh->status = CAM_REQ_INVALID;
+                       break;
+               }
+               if (ccbh->status != CAM_REQ_INPROG)
+                       break;
+
+               fis = xa->fis;
+               fis->flags = ATA_H2D_FLAGS_CMD;
+               fis->lba_low = (u_int8_t)lba;
+               fis->lba_mid = (u_int8_t)(lba >> 8);
+               fis->lba_high = (u_int8_t)(lba >> 16);
+               fis->device = ATA_H2D_DEVICE_LBA;
+
+               /*
+                * NCQ only for direct-attached disks, do not currently
+                * try to use NCQ with port multipliers.
+                *
+                * XXX fixme SII chip can do NCQ w/ port multipliers.
+                */
+               if (at->at_ncqdepth > 1 &&
+                   at->at_type == ATA_PORT_T_DISK &&
+                   (ap->ap_sc->sc_flags & SILI_F_NCQ) &&
+                   (ccbh->flags & CAM_POLLED) == 0) {
+                       /*
+                        * Use NCQ - always uses 48 bit addressing
+                        */
+                       xa->flags |= ATA_F_NCQ;
+                       fis->command = (xa->flags & ATA_F_WRITE) ?
+                                       ATA_C_WRITE_FPDMA : ATA_C_READ_FPDMA;
+                       fis->lba_low_exp = (u_int8_t)(lba >> 24);
+                       fis->lba_mid_exp = (u_int8_t)(lba >> 32);
+                       fis->lba_high_exp = (u_int8_t)(lba >> 40);
+                       fis->sector_count = xa->tag << 3;
+                       fis->features = (u_int8_t)count;
+                       fis->features_exp = (u_int8_t)(count >> 8);
+               } else if (count > 0x100 || lba > 0xFFFFFFFFU) {
+                       /*
+                        * Use LBA48
+                        */
+                       fis->command = (xa->flags & ATA_F_WRITE) ?
+                                       ATA_C_WRITEDMA_EXT : ATA_C_READDMA_EXT;
+                       fis->lba_low_exp = (u_int8_t)(lba >> 24);
+                       fis->lba_mid_exp = (u_int8_t)(lba >> 32);
+                       fis->lba_high_exp = (u_int8_t)(lba >> 40);
+                       fis->sector_count = (u_int8_t)count;
+                       fis->sector_count_exp = (u_int8_t)(count >> 8);
+               } else {
+                       /*
+                        * Use LBA
+                        *
+                        * NOTE: 256 sectors is supported, stored as 0.
+                        */
+                       fis->command = (xa->flags & ATA_F_WRITE) ?
+                                       ATA_C_WRITEDMA : ATA_C_READDMA;
+                       fis->device |= (u_int8_t)(lba >> 24) & 0x0F;
+                       fis->sector_count = (u_int8_t)count;
+               }
+
+               xa->data = csio->data_ptr;
+               xa->datalen = csio->dxfer_len;
+               xa->complete = sili_ata_complete_disk_rw;
+               xa->timeout = ccbh->timeout;    /* milliseconds */
+               if (ccbh->flags & CAM_POLLED)
+                       xa->flags |= ATA_F_POLL;
+               break;
+       }
+
+       /*
+        * If the request is still in progress the xa and FIS have
+        * been set up and must be dispatched.  Otherwise the request
+        * is complete.
+        */
+       if (ccbh->status == CAM_REQ_INPROG) {
+               KKASSERT(xa->complete != NULL);
+               xa->atascsi_private = ccb;
+               ccb->ccb_h.sim_priv.entries[0].ptr = ap;
+               sili_os_lock_port(ap);
+               fis->flags |= at->at_target;
+               sili_ata_cmd(xa);
+               sili_os_unlock_port(ap);
+       } else {
+               sili_ata_put_xfer(xa);
+               xpt_done(ccb);
+       }
+}
+
+/*
+ * Convert the SCSI command in ccb to an ata_xfer command in xa
+ * for ATA_PORT_T_ATAPI operations.  Set the completion function
+ * to convert the response back, then dispatch to the OpenBSD SILI
+ * layer.
+ */
+static
+void
+sili_xpt_scsi_atapi_io(struct sili_port *ap, struct ata_port *atx,
+                       union ccb *ccb)
+{
+       struct ccb_hdr *ccbh;
+       struct ccb_scsiio *csio;
+       struct ata_xfer *xa;
+       struct ata_fis_h2d *fis;
+       scsi_cdb_t cdbs;
+       scsi_cdb_t cdbd;
+       int flags;
+       struct ata_port *at;
+
+       ccbh = &ccb->csio.ccb_h;
+       csio = &ccb->csio;
+       at = atx ? atx : &ap->ap_ata[0];
+
+       switch (ccbh->flags & CAM_DIR_MASK) {
+       case CAM_DIR_IN:
+               flags = ATA_F_PACKET | ATA_F_READ;
+               break;
+       case CAM_DIR_OUT:
+               flags = ATA_F_PACKET | ATA_F_WRITE;
+               break;
+       case CAM_DIR_NONE:
+               flags = ATA_F_PACKET;
+               break;
+       default:
+               ccbh->status = CAM_REQ_INVALID;
+               xpt_done(ccb);
+               return;
+               /* NOT REACHED */
+       }
+
+       /*
+        * The command has to fit in the packet command buffer.
+        */
+       if (csio->cdb_len < 6 || csio->cdb_len > 16) {
+               ccbh->status = CAM_CCB_LEN_ERR;
+               xpt_done(ccb);
+               return;
+       }
+
+       /*
+        * Initialize the XA and FIS.
+        *
+        * XXX not passing NULL at for direct attach!
+        */
+       xa = sili_ata_get_xfer(ap, atx);
+       fis = xa->fis;
+
+       fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
+       fis->command = ATA_C_PACKET;
+       fis->device = 0;
+       fis->sector_count = xa->tag << 3;
+       fis->features = ATA_H2D_FEATURES_DMA |
+                   ((flags & ATA_F_WRITE) ?
+                   ATA_H2D_FEATURES_DIR_WRITE : ATA_H2D_FEATURES_DIR_READ);
+       fis->lba_mid = 0x00;
+       fis->lba_high = 0x20;
+
+       xa->flags = flags;
+       xa->data = csio->data_ptr;
+       xa->datalen = csio->dxfer_len;
+       xa->timeout = ccbh->timeout;    /* milliseconds */
+
+       if (ccbh->flags & CAM_POLLED)
+               xa->flags |= ATA_F_POLL;
+
+       /*
+        * Copy the cdb to the packetcmd buffer in the FIS using a
+        * convenient pointer in the xa.
+        */
+       cdbs = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
+                       csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);
+       bcopy(cdbs, xa->packetcmd, csio->cdb_len);
+
+#if 0
+       kprintf("opcode %d cdb_len %d dxfer_len %d\n",
+               cdbs->generic.opcode,
+               csio->cdb_len, csio->dxfer_len);
+#endif
+
+       /*
+        * Some ATAPI commands do not actually follow the SCSI standard.
+        */
+       cdbd = (void *)xa->packetcmd;
+
+       switch(cdbd->generic.opcode) {
+       case INQUIRY:
+               /*
+                * Some ATAPI devices can't handle SI_EVPD being set
+                * for a basic inquiry (page_code == 0).
+                *
+                * Some ATAPI devices can't handle long inquiry lengths,
+                * don't ask me why.  Truncate the inquiry length.
+                */
+               if ((cdbd->inquiry.byte2 & SI_EVPD) &&
+                   cdbd->inquiry.page_code == 0) {
+                       cdbd->inquiry.byte2 &= ~SI_EVPD;
+               }
+               if (cdbd->inquiry.page_code == 0 &&
+                   cdbd->inquiry.length > SHORT_INQUIRY_LENGTH) {
+                       cdbd->inquiry.length = SHORT_INQUIRY_LENGTH;
+               }
+               break;
+       case READ_6:
+       case WRITE_6:
+               /*
+                * Convert *_6 to *_10 commands.  Most ATAPI devices
+                * cannot handle the SCSI READ_6 and WRITE_6 commands.
+                */
+               cdbd->rw_10.opcode |= 0x20;
+               cdbd->rw_10.byte2 = 0;
+               cdbd->rw_10.addr[0] = cdbs->rw_6.addr[0] & 0x1F;
+               cdbd->rw_10.addr[1] = cdbs->rw_6.addr[1];
+               cdbd->rw_10.addr[2] = cdbs->rw_6.addr[2];
+               cdbd->rw_10.addr[3] = 0;
+               cdbd->rw_10.reserved = 0;
+               cdbd->rw_10.length[0] = 0;
+               cdbd->rw_10.length[1] = cdbs->rw_6.length;
+               cdbd->rw_10.control = cdbs->rw_6.control;
+               break;
+       default:
+               break;
+       }
+
+       /*
+        * And dispatch
+        */
+       xa->complete = sili_atapi_complete_cmd;
+       xa->atascsi_private = ccb;
+       ccb->ccb_h.sim_priv.entries[0].ptr = ap;
+       sili_os_lock_port(ap);
+       sili_ata_cmd(xa);
+       sili_os_unlock_port(ap);
+}
+
+/*
+ * Completion function for ATA_PORT_T_DISK cache synchronization.
+ */
+static
+void
+sili_ata_complete_disk_synchronize_cache(struct ata_xfer *xa)
+{
+       union ccb *ccb = xa->atascsi_private;
+       struct ccb_hdr *ccbh = &ccb->ccb_h;
+       struct sili_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
+
+       switch(xa->state) {
+       case ATA_S_COMPLETE:
+               ccbh->status = CAM_REQ_CMP;
+               ccb->csio.scsi_status = SCSI_STATUS_OK;
+               break;
+       case ATA_S_ERROR:
+               kprintf("%s: synchronize_cache: error\n",
+                       ATANAME(ap, xa->at));
+               ccbh->status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
+               ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
+               sili_ata_dummy_sense(&ccb->csio.sense_data);
+               break;
+       case ATA_S_TIMEOUT:
+               kprintf("%s: synchronize_cache: timeout\n",
+                       ATANAME(ap, xa->at));
+               ccbh->status = CAM_CMD_TIMEOUT;
+               break;
+       default:
+               kprintf("%s: synchronize_cache: unknown state %d\n",
+                       ATANAME(ap, xa->at), xa->state);
+               ccbh->status = CAM_REQ_CMP_ERR;
+               break;
+       }
+       sili_ata_put_xfer(xa);
+       sili_os_unlock_port(ap);
+       xpt_done(ccb);
+       sili_os_lock_port(ap);
+}
+
+/*
+ * Completion function for ATA_PORT_T_DISK I/O
+ */
+static
+void
+sili_ata_complete_disk_rw(struct ata_xfer *xa)
+{
+       union ccb *ccb = xa->atascsi_private;
+       struct ccb_hdr *ccbh = &ccb->ccb_h;
+       struct sili_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
+
+       switch(xa->state) {
+       case ATA_S_COMPLETE:
+               ccbh->status = CAM_REQ_CMP;
+               ccb->csio.scsi_status = SCSI_STATUS_OK;
+               break;
+       case ATA_S_ERROR:
+               kprintf("%s: disk_rw: error\n", ATANAME(ap, xa->at));
+               ccbh->status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
+               ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
+               sili_ata_dummy_sense(&ccb->csio.sense_data);
+               break;
+       case ATA_S_TIMEOUT:
+               kprintf("%s: disk_rw: timeout\n", ATANAME(ap, xa->at));
+               ccbh->status = CAM_CMD_TIMEOUT;
+               ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
+               sili_ata_dummy_sense(&ccb->csio.sense_data);
+               break;
+       default:
+               kprintf("%s: disk_rw: unknown state %d\n",
+                       ATANAME(ap, xa->at), xa->state);
+               ccbh->status = CAM_REQ_CMP_ERR;
+               break;
+       }
+       ccb->csio.resid = xa->resid;
+       sili_ata_put_xfer(xa);
+       sili_os_unlock_port(ap);
+       xpt_done(ccb);
+       sili_os_lock_port(ap);
+}
+
+/*
+ * Completion function for ATA_PORT_T_ATAPI I/O
+ *
+ * Sense data is returned in the rfis.
+ */
+static
+void
+sili_atapi_complete_cmd(struct ata_xfer *xa)
+{
+       union ccb *ccb = xa->atascsi_private;
+       struct ccb_hdr *ccbh = &ccb->ccb_h;
+       struct sili_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
+       scsi_cdb_t cdb;
+
+       cdb = (void *)((ccb->ccb_h.flags & CAM_CDB_POINTER) ?
+                       ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes);
+
+       switch(xa->state) {
+       case ATA_S_COMPLETE:
+               ccbh->status = CAM_REQ_CMP;
+               ccb->csio.scsi_status = SCSI_STATUS_OK;
+               break;
+       case ATA_S_ERROR:
+               ccbh->status = CAM_SCSI_STATUS_ERROR;
+               ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
+               sili_ata_atapi_sense(xa->rfis, &ccb->csio.sense_data);
+               break;
+       case ATA_S_TIMEOUT:
+               kprintf("%s: cmd %d: timeout\n",
+                       PORTNAME(ap), cdb->generic.opcode);
+               ccbh->status = CAM_CMD_TIMEOUT;
+               ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
+               sili_ata_dummy_sense(&ccb->csio.sense_data);
+               break;
+       default:
+               kprintf("%s: cmd %d: unknown state %d\n",
+                       PORTNAME(ap), cdb->generic.opcode, xa->state);
+               ccbh->status = CAM_REQ_CMP_ERR;
+               break;
+       }
+       ccb->csio.resid = xa->resid;
+       sili_ata_put_xfer(xa);
+       sili_os_unlock_port(ap);
+       xpt_done(ccb);
+       sili_os_lock_port(ap);
+}
+
+/*
+ * Construct dummy sense data for errors on DISKs
+ */
+static
+void
+sili_ata_dummy_sense(struct scsi_sense_data *sense_data)
+{
+       sense_data->error_code = SSD_ERRCODE_VALID | SSD_CURRENT_ERROR;
+       sense_data->segment = 0;
+       sense_data->flags = SSD_KEY_MEDIUM_ERROR;
+       sense_data->info[0] = 0;
+       sense_data->info[1] = 0;
+       sense_data->info[2] = 0;
+       sense_data->info[3] = 0;
+       sense_data->extra_len = 0;
+}
+
+/*
+ * Construct atapi sense data for errors on ATAPI
+ *
+ * The ATAPI sense data is stored in the passed rfis and must be converted
+ * to SCSI sense data.
+ */
+static
+void
+sili_ata_atapi_sense(struct ata_fis_d2h *rfis,
+                    struct scsi_sense_data *sense_data)
+{
+       sense_data->error_code = SSD_ERRCODE_VALID | SSD_CURRENT_ERROR;
+       sense_data->segment = 0;
+       sense_data->flags = (rfis->error & 0xF0) >> 4;
+       if (rfis->error & 0x04)
+               sense_data->flags |= SSD_KEY_ILLEGAL_REQUEST;
+       if (rfis->error & 0x02)
+               sense_data->flags |= SSD_EOM;
+       if (rfis->error & 0x01)
+               sense_data->flags |= SSD_ILI;
+       sense_data->info[0] = 0;
+       sense_data->info[1] = 0;
+       sense_data->info[2] = 0;
+       sense_data->info[3] = 0;
+       sense_data->extra_len = 0;
+}
diff --git a/sys/dev/disk/sili/sili_dragonfly.c b/sys/dev/disk/sili/sili_dragonfly.c
new file mode 100644 (file)
index 0000000..6b3464d
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com>
+ *
+ * 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.
+ * 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. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``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
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+ */
+/*
+ * Primary device and CAM interface to OpenBSD SILI driver, for DragonFly
+ */
+
+#include "sili.h"
+
+u_int32_t SiliForceGen1 = 0;
+u_int32_t SiliNoFeatures = 0;
+
+/*
+ * Device bus methods
+ */
+
+static int     sili_probe (device_t dev);
+static int     sili_attach (device_t dev);
+static int     sili_detach (device_t dev);
+#if 0
+static int     sili_shutdown (device_t dev);
+static int     sili_suspend (device_t dev);
+static int     sili_resume (device_t dev);
+#endif
+
+static void    sili_port_thread(void *arg);
+
+static device_method_t sili_methods[] = {
+       DEVMETHOD(device_probe,         sili_probe),
+       DEVMETHOD(device_attach,        sili_attach),
+       DEVMETHOD(device_detach,        sili_detach),
+#if 0
+       DEVMETHOD(device_shutdown,      sili_shutdown),
+       DEVMETHOD(device_suspend,       sili_suspend),
+       DEVMETHOD(device_resume,        sili_resume),
+#endif
+
+       DEVMETHOD(bus_print_child,      bus_generic_print_child),
+       DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
+       {0, 0}
+};
+
+static devclass_t      sili_devclass;
+
+static driver_t sili_driver = {
+       "sili",
+       sili_methods,
+       sizeof(struct sili_softc)
+};
+
+MODULE_DEPEND(sili, cam, 1, 1, 1);
+DRIVER_MODULE(sili, pci, sili_driver, sili_devclass, 0, 0);
+
+/*
+ * Device bus method procedures
+ */
+static int
+sili_probe (device_t dev)
+{
+       const struct sili_device *ad;
+
+       if (kgetenv("hint.sili.disabled"))
+               return(ENXIO);
+       if (kgetenv("hint.sili.force150"))
+               SiliForceGen1 = -1;
+       if (kgetenv("hint.sili.nofeatures"))
+               SiliNoFeatures = -1;
+
+       ad = sili_lookup_device(dev);
+       if (ad) {
+               device_set_desc(dev, ad->name);
+               return(-5);     /* higher priority the NATA */
+       }
+       return(ENXIO);
+}
+
+static int
+sili_attach (device_t dev)
+{
+       struct sili_softc *sc = device_get_softc(dev);
+       int error;
+
+       sc->sc_ad = sili_lookup_device(dev);
+       if (sc->sc_ad == NULL)
+               return(ENXIO);
+       error = sc->sc_ad->ad_attach(dev);
+       return (error);
+}
+
+static int
+sili_detach (device_t dev)
+{
+       struct sili_softc *sc = device_get_softc(dev);
+       int error = 0;
+
+       if (sc->sc_ad) {
+               error = sc->sc_ad->ad_detach(dev);
+               sc->sc_ad = NULL;
+       }
+       return(error);
+}
+
+#if 0
+
+static int
+sili_shutdown (device_t dev)
+{
+       return (0);
+}
+
+static int
+sili_suspend (device_t dev)
+{
+       return (0);
+}
+
+static int
+sili_resume (device_t dev)
+{
+       return (0);
+}
+
+#endif
+
+/*
+ * Sleep (ms) milliseconds, error on the side of caution.
+ */
+void
+sili_os_sleep(int ms)
+{
+       int ticks;
+
+       ticks = hz * ms / 1000 + 1;
+       tsleep(&ticks, 0, "ahslp", ticks);
+}
+
+/*
+ * Sleep for a minimum interval and return the number of milliseconds
+ * that was.  The minimum value returned is 1
+ */
+int
+sili_os_softsleep(void)
+{
+       if (hz >= 1000) {
+               tsleep(&ticks, 0, "ahslp", hz / 1000);
+               return(1);
+       } else {
+               tsleep(&ticks, 0, "ahslp", 1);
+               return(1000 / hz);
+       }
+}
+
+void
+sili_os_hardsleep(int us)
+{
+       DELAY(us);
+}
+
+/*
+ * Create the OS-specific port helper thread and per-port lock.
+ */
+void
+sili_os_start_port(struct sili_port *ap)
+{
+       atomic_set_int(&ap->ap_signal, AP_SIGF_INIT);
+       lockinit(&ap->ap_lock, "silipo", 0, 0);
+       kthread_create(sili_port_thread, ap, &ap->ap_thread,
+                      "%s", PORTNAME(ap));
+}
+
+/*
+ * Stop the OS-specific port helper thread and kill the per-port lock.
+ */
+void
+sili_os_stop_port(struct sili_port *ap)
+{
+       if (ap->ap_thread) {
+               sili_os_signal_port_thread(ap, AP_SIGF_STOP);
+               sili_os_sleep(10);
+               if (ap->ap_thread) {
+                       kprintf("%s: Waiting for thread to terminate\n",
+                               PORTNAME(ap));
+                       while (ap->ap_thread)
+                               sili_os_sleep(100);
+                       kprintf("%s: thread terminated\n",
+                               PORTNAME(ap));
+               }
+       }
+       lockuninit(&ap->ap_lock);
+}
+
+/*
+ * Add (mask) to the set of bits being sent to the per-port thread helper
+ * and wake the helper up if necessary.
+ */
+void
+sili_os_signal_port_thread(struct sili_port *ap, int mask)
+{
+       atomic_set_int(&ap->ap_signal, mask);
+       wakeup(&ap->ap_thread);
+}
+
+/*
+ * Unconditionally lock the port structure for access.
+ */
+void
+sili_os_lock_port(struct sili_port *ap)
+{
+       lockmgr(&ap->ap_lock, LK_EXCLUSIVE);
+}
+
+/*
+ * Conditionally lock the port structure for access.
+ *
+ * Returns 0 on success, non-zero on failure.
+ */
+int
+sili_os_lock_port_nb(struct sili_port *ap)
+{
+       return (lockmgr(&ap->ap_lock, LK_EXCLUSIVE | LK_NOWAIT));
+}
+
+/*
+ * Unlock a previously locked port.
+ */
+void
+sili_os_unlock_port(struct sili_port *ap)
+{
+       lockmgr(&ap->ap_lock, LK_RELEASE);
+}
+
+/*
+ * Per-port thread helper.  This helper thread is responsible for
+ * atomically retrieving and clearing the signal mask and calling
+ * the machine-independant driver core.
+ */
+static
+void
+sili_port_thread(void *arg)
+{
+       struct sili_port *ap = arg;
+       int mask;
+
+       /*
+        * The helper thread is responsible for the initial port init,
+        * so all the ports can be inited in parallel.
+        *
+        * We also run the state machine which should do all probes.
+        * Since CAM is not attached yet we will not get out-of-order
+        * SCSI attachments.
+        */
+       sili_os_lock_port(ap);
+       sili_port_init(ap, NULL);
+       sili_port_state_machine(ap, 1);
+       sili_os_unlock_port(ap);
+       atomic_clear_int(&ap->ap_signal, AP_SIGF_INIT);
+       wakeup(&ap->ap_signal);
+
+       /*
+        * Then loop on the helper core.
+        */
+       mask = ap->ap_signal;
+       while ((mask & AP_SIGF_STOP) == 0) {
+               atomic_clear_int(&ap->ap_signal, mask);
+               sili_port_thread_core(ap, mask);
+               crit_enter();
+               tsleep_interlock(&ap->ap_thread);
+               if (ap->ap_signal == 0)
+                       tsleep(&ap->ap_thread, 0, "ahport", 0);
+               crit_exit();
+               mask = ap->ap_signal;
+       }
+       ap->ap_thread = NULL;
+}
diff --git a/sys/dev/disk/sili/sili_dragonfly.h b/sys/dev/disk/sili/sili_dragonfly.h
new file mode 100644 (file)
index 0000000..d29eb4c
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com>
+ *
+ * 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.
+ * 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. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``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
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/disk.h>
+#include <sys/stat.h>
+#include <sys/devicestat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/rman.h>
+#include <sys/endian.h>
+#include <sys/sysctl.h>
+#include <sys/kthread.h>
+
+#include <bus/cam/cam.h>
+#include <bus/cam/cam_ccb.h>
+#include <bus/cam/cam_periph.h>
+#include <bus/cam/cam_sim.h>
+#include <bus/cam/cam_xpt_sim.h>
+#include <bus/cam/cam_xpt_periph.h>
+#include <bus/cam/cam_debug.h>
+#include <bus/cam/scsi/scsi_all.h>
+
+#include <machine/clock.h>
+
+#include <vm/vm.h>
+
+#include <bus/pci/pcireg.h>
+#include <bus/pci/pcivar.h>
+#include <bus/pci/pcidevs.h>
+
+#include <sys/thread2.h>
+
+#define SILI_CDEV_MAJOR                188
+
+#define SILI_IRQ_RID           0
diff --git a/sys/dev/disk/sili/sili_pm.c b/sys/dev/disk/sili/sili_pm.c
new file mode 100644 (file)
index 0000000..da3868a
--- /dev/null
@@ -0,0 +1,552 @@
+/*
+ * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com>
+ *
+ * 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.
+ * 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. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``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
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+ */
+
+#include "sili.h"
+
+static void sili_pm_dummy_done(struct ata_xfer *xa);
+static void sili_pm_empty_done(struct sili_ccb *ccb);
+
+/*
+ * Identify the port multiplier
+ */
+int
+sili_pm_identify(struct sili_port *ap)
+{
+       u_int32_t chipid;
+       u_int32_t rev;
+       u_int32_t nports;
+       u_int32_t data1;
+       u_int32_t data2;
+
+       ap->ap_pmcount = 0;
+       ap->ap_probe = ATA_PROBE_FAILED;
+       if (sili_pm_read(ap, 15, 0, &chipid))
+               goto err;
+       if (sili_pm_read(ap, 15, 1, &rev))
+               goto err;
+       if (sili_pm_read(ap, 15, 2, &nports))
+               goto err;
+       nports &= 0x0000000F;   /* only the low 4 bits */
+       --nports;
+       ap->ap_probe = ATA_PROBE_GOOD;
+       kprintf("%s: Port multiplier: chip=%08x rev=0x%b nports=%d\n",
+               PORTNAME(ap),
+               chipid,
+               rev, SATA_PFMT_PM_REV,
+               nports);
+       ap->ap_pmcount = nports;
+
+       if (sili_pm_read(ap, 15, SATA_PMREG_FEA, &data1)) {
+               kprintf("%s: Port multiplier: Warning, "
+                       "cannot read feature register\n", PORTNAME(ap));
+       } else {
+               kprintf("%s: Port multiplier features: 0x%b\n",
+                       PORTNAME(ap),
+                       data1,
+                       SATA_PFMT_PM_FEA);
+       }
+       if (sili_pm_read(ap, 15, SATA_PMREG_FEAEN, &data2) == 0) {
+               kprintf("%s: Port multiplier defaults: 0x%b\n",
+                       PORTNAME(ap),
+                       data2,
+                       SATA_PFMT_PM_FEA);
+       }
+
+       /*
+        * Turn on async notification if we support and the PM supports it.
+        * This allows the PM to forward async notification events to us and
+        * it will also generate an event for target 15 for hot-plug events
+        * (or is supposed to anyway).
+        */
+       if ((ap->ap_sc->sc_flags & SILI_F_SSNTF) &&
+           (data1 & SATA_PMFEA_ASYNCNOTIFY)) {
+               u_int32_t serr_bits = SATA_PM_SERR_DIAG_N |
+                                     SATA_PM_SERR_DIAG_X;
+               data2 |= SATA_PMFEA_ASYNCNOTIFY;
+               if (sili_pm_write(ap, 15, SATA_PMREG_FEAEN, data2)) {
+                       kprintf("%s: Port multiplier: AsyncNotify cannot be "
+                               "enabled\n", PORTNAME(ap));
+               } else if (sili_pm_write(ap, 15, SATA_PMREG_EEENA, serr_bits)) {
+                       kprintf("%s: Port mulltiplier: AsyncNotify unable "
+                               "to enable error info bits\n", PORTNAME(ap));
+               } else {
+                       kprintf("%s: Port multiplier: AsyncNotify enabled\n",
+                               PORTNAME(ap));
+               }
+       }
+
+       return (0);
+err:
+       kprintf("%s: Port multiplier cannot be identified\n", PORTNAME(ap));
+       return (EIO);
+}
+
+/*
+ * Do a COMRESET sequence on the target behind a port multiplier.
+ *
+ * If hard is 2 we also cycle the phy on the target.
+ *
+ * This must be done prior to any softreset or probe attempts on
+ * targets behind the port multiplier.
+ *
+ * Returns 0 on success or an error.
+ */
+int
+sili_pm_hardreset(struct sili_port *ap, int target, int hard)
+{
+       struct ata_port *at;
+       u_int32_t data;
+       int loop;
+       int error = EIO;
+
+       at = &ap->ap_ata[target];
+
+       /*
+        * Turn off power management and kill the phy on the target
+        * if requested.  Hold state for 10ms.
+        */
+       data = SATA_PM_SCTL_IPM_DISABLED;
+       if (hard == 2)
+               data |= SATA_PM_SCTL_DET_DISABLE;
+       if (sili_pm_write(ap, target, SATA_PMREG_SERR, -1))
+               goto err;
+       if (sili_pm_write(ap, target, SATA_PMREG_SCTL, data))
+               goto err;
+       sili_os_sleep(10);
+
+       /*
+        * Start transmitting COMRESET.  COMRESET must be sent for at
+        * least 1ms.
+        */
+       at->at_probe = ATA_PROBE_FAILED;
+       at->at_type = ATA_PORT_T_NONE;
+       data = SATA_PM_SCTL_IPM_DISABLED | SATA_PM_SCTL_DET_INIT;
+       if (SiliForceGen1 & (1 << ap->ap_num)) {
+               kprintf("%s.%d: Force 1.5GBits\n", PORTNAME(ap), target);
+               data |= SATA_PM_SCTL_SPD_GEN1;
+       } else {
+               data |= SATA_PM_SCTL_SPD_ANY;
+       }
+       if (sili_pm_write(ap, target, SATA_PMREG_SCTL, data))
+               goto err;
+
+       /*
+        * It takes about 100ms for the DET logic to settle down,
+        * from trial and error testing.  If this is too short
+        * the softreset code will fail.
+        */
+       sili_os_sleep(100);
+
+       if (sili_pm_phy_status(ap, target, &data)) {
+               kprintf("%s: (A)Cannot clear phy status\n",
+                       ATANAME(ap ,at));
+       }
+
+       /*
+        * Flush any status, then clear DET to initiate negotiation.
+        */
+       sili_pm_write(ap, target, SATA_PMREG_SERR, -1);
+       data = SATA_PM_SCTL_IPM_DISABLED | SATA_PM_SCTL_DET_NONE;
+       if (sili_pm_write(ap, target, SATA_PMREG_SCTL, data))
+               goto err;
+
+       /*
+        * Try to determine if there is a device on the port.
+        *
+        * Give the device 3/10 second to at least be detected.
+        * If we fail clear any pending status since we may have
+        * cycled the phy and probably caused another PRCS interrupt.
+        */
+       for (loop = 3; loop; --loop) {
+               if (sili_pm_read(ap, target, SATA_PMREG_SSTS, &data))
+                       goto err;
+               if (data & SATA_PM_SSTS_DET)
+                       break;
+               sili_os_sleep(100);
+       }
+       if (loop == 0) {
+               kprintf("%s.%d: Port appears to be unplugged\n",
+                       PORTNAME(ap), target);
+               error = ENODEV;
+               goto err;
+       }
+
+       /*
+        * There is something on the port.  Give the device 3 seconds
+        * to fully negotiate.
+        */
+       for (loop = 30; loop; --loop) {
+               if (sili_pm_read(ap, target, SATA_PMREG_SSTS, &data))
+                       goto err;
+               if ((data & SATA_PM_SSTS_DET) == SATA_PM_SSTS_DET_DEV)
+                       break;
+               sili_os_sleep(100);
+       }
+
+       /*
+        * Device not detected
+        */
+       if (loop == 0) {
+               kprintf("%s: Device may be powered down\n",
+                       PORTNAME(ap));
+               error = ENODEV;
+               goto err;
+       }
+
+       /*
+        * Device detected
+        */
+       kprintf("%s.%d: Device detected data=%08x\n",
+               PORTNAME(ap), target, data);
+       /*
+        * Clear SERR on the target so we get a new NOTIFY event if a hot-plug
+        * or hot-unplug occurs.
+        */
+       sili_os_sleep(100);
+
+       error = 0;
+err:
+       at->at_probe = error ? ATA_PROBE_FAILED : ATA_PROBE_NEED_SOFT_RESET;
+       return (error);
+}
+
+/*
+ * SILI soft reset through port multiplier.
+ *
+ * This function keeps port communications intact and attempts to generate
+ * a reset to the connected device using device commands.  Unlike
+ * hard-port operations we can't do fancy stop/starts or stuff like
+ * that without messing up other commands that might be running or
+ * queued.
+ *
+ * The SII chip will do the whole mess for us.
+ */
+int
+sili_pm_softreset(struct sili_port *ap, int target)
+{
+       struct ata_port         *at;
+       struct sili_ccb         *ccb;
+       struct sili_prb         *prb;
+       int                     error;
+       u_int32_t               data;
+       u_int32_t               sig;
+
+       error = EIO;
+       at = &ap->ap_ata[target];
+
+       DPRINTF(SILI_D_VERBOSE, "%s: soft reset\n", PORTNAME(ap));
+
+       /*
+        * Prep the special soft-reset SII command.
+        */
+       ccb = sili_get_err_ccb(ap);
+       ccb->ccb_done = sili_pm_empty_done;
+       ccb->ccb_xa.flags = ATA_F_POLL;
+       ccb->ccb_xa.complete = sili_pm_dummy_done;
+       ccb->ccb_xa.at = at;
+
+       prb = ccb->ccb_prb;
+       bzero(&prb->prb_h2d, sizeof(prb->prb_h2d));
+       prb->prb_h2d.flags = at->at_target;
+       prb->prb_control = SILI_PRB_CTRL_SOFTRESET;
+       prb->prb_override = 0;
+       prb->prb_xfer_count = 0;
+
+       ccb->ccb_xa.state = ATA_S_PENDING;
+       ccb->ccb_xa.flags = 0;
+
+       if (sili_poll(ccb, 8000, sili_ata_cmd_timeout) != ATA_S_COMPLETE) {
+               kprintf("%s: (PM) Softreset FIS failed\n", ATANAME(ap, at));
+               sili_put_err_ccb(ccb);
+               goto err;
+       }
+
+       sig = (prb->prb_d2h.lba_high << 24) |
+             (prb->prb_d2h.lba_mid << 16) |
+             (prb->prb_d2h.lba_low << 8) |
+             (prb->prb_d2h.sector_count);
+       kprintf("%s: PM SOFTRESET SIGNATURE %08x\n", ATANAME(ap, at), sig);
+
+       sili_put_err_ccb(ccb);
+
+       /*
+        * Clear the phy status of the target so we can get a new event.
+        *
+        * Target 15 is the PM itself and these registers have
+        * different meanings.
+        */
+       if (target != 15) {
+               if (sili_pm_phy_status(ap, target, &data)) {
+                       kprintf("%s: (C)Cannot clear phy status\n",
+                               ATANAME(ap ,at));
+               }
+               sili_pm_write(ap, target, SATA_PMREG_SERR, -1);
+       }
+
+       /*
+        * If the softreset is trying to clear a BSY condition after a
+        * normal portreset we assign the port type.
+        *
+        * If the softreset is being run first as part of the ccb error
+        * processing code then report if the device signature changed
+        * unexpectedly.
+        */
+       if (at->at_type == ATA_PORT_T_NONE) {
+               at->at_type = sili_port_signature(ap, at, sig);
+       } else {
+               if (sili_port_signature(ap, at, sig) != at->at_type) {
+                       kprintf("%s: device signature unexpectedly "
+                               "changed\n", ATANAME(ap, at));
+                       error = EBUSY; /* XXX */
+               }
+       }
+       error = 0;
+err:
+       /*
+        * Clear error status so we can detect removal.
+        *
+        * Target 15 is the PM itself and these registers have
+        * different meanings.
+        */
+       if (error == 0 && target != 15) {
+               if (sili_pm_write(ap, target, SATA_PMREG_SERR, -1)) {
+                       kprintf("%s: sili_pm_softreset unable to clear SERR\n",
+                               ATANAME(ap, at));
+                       ap->ap_flags &= ~AP_F_IGNORE_IFS;
+               }
+       }
+
+       at->at_probe = error ? ATA_PROBE_FAILED : ATA_PROBE_NEED_IDENT;
+       return (error);
+}
+
+
+/*
+ * Return the phy status for a target behind a port multiplier and
+ * reset SATA_PMREG_SERR.
+ *
+ * Returned bits follow SILI_PREG_SSTS bits.  The SILI_PREG_SSTS_SPD
+ * bits can be used to determine the link speed and will be 0 if there
+ * is no link.
+ *
+ * 0 is returned if any communications error occurs.
+ */
+int
+sili_pm_phy_status(struct sili_port *ap, int target, u_int32_t *datap)
+{
+       int error;
+
+       error = sili_pm_read(ap, target, SATA_PMREG_SSTS, datap);
+       if (error == 0)
+               error = sili_pm_write(ap, target, SATA_PMREG_SERR, -1);
+       if (error)
+               *datap = 0;
+       return(error);
+}
+
+int
+sili_pm_set_feature(struct sili_port *ap, int feature, int enable)
+{
+       struct ata_xfer *xa;
+       int error;
+
+       xa = sili_ata_get_xfer(ap, &ap->ap_ata[15]);
+
+       xa->fis->type = ATA_FIS_TYPE_H2D;
+       xa->fis->flags = ATA_H2D_FLAGS_CMD | 15;
+       xa->fis->command = enable ? ATA_C_SATA_FEATURE_ENA :
+                                   ATA_C_SATA_FEATURE_DIS;
+       xa->fis->sector_count = feature;
+       xa->fis->control = ATA_FIS_CONTROL_4BIT;
+
+       xa->complete = sili_pm_dummy_done;
+       xa->datalen = 0;
+       xa->flags = ATA_F_READ | ATA_F_POLL;
+       xa->timeout = 1000;
+
+       if (sili_ata_cmd(xa) == ATA_S_COMPLETE)
+               error = 0;
+       else
+               error = EIO;
+       sili_ata_put_xfer(xa);
+       return(error);
+}
+
+/*
+ * Check that a target is still good.
+ */
+void
+sili_pm_check_good(struct sili_port *ap, int target)
+{
+       struct ata_port *at;
+       u_int32_t data;
+
+       /*
+        * It looks like we might have to read the EINFO register
+        * to allow the PM to generate a new event.
+        */
+       if (sili_pm_read(ap, 15, SATA_PMREG_EINFO, &data)) {
+               kprintf("%s: Port multiplier EINFO could not be read\n",
+                       PORTNAME(ap));
+       }
+
+       if (sili_pm_write(ap, target, SATA_PMREG_SERR, -1)) {
+               kprintf("%s: Port multiplier: SERR could not be cleared\n",
+                       PORTNAME(ap));
+       }
+
+       if (target == CAM_TARGET_WILDCARD || target >= ap->ap_pmcount)
+               return;
+       at = &ap->ap_ata[target];
+
+       /*
+        * If the device needs an init or hard reset also make sure the
+        * PHY is turned on.
+        */
+       if (at->at_probe <= ATA_PROBE_NEED_HARD_RESET) {
+               /*kprintf("%s DOHARD\n", ATANAME(ap, at));*/
+               sili_pm_hardreset(ap, target, 1);
+       }
+
+       /*
+        * Read the detect status
+        */
+       if (sili_pm_read(ap, target, SATA_PMREG_SSTS, &data)) {
+               kprintf("%s: Unable to access PM SSTS register target %d\n",
+                       PORTNAME(ap), target);
+               return;
+       }
+       if ((data & SATA_PM_SSTS_DET) != SATA_PM_SSTS_DET_DEV) {
+               /*kprintf("%s: DETECT %08x\n", ATANAME(ap, at), data);*/
+               if (at->at_probe != ATA_PROBE_FAILED) {
+                       at->at_probe = ATA_PROBE_FAILED;
+                       at->at_type = ATA_PORT_T_NONE;
+                       at->at_features |= ATA_PORT_F_RESCAN;
+                       kprintf("%s: HOTPLUG (PM) - Device removed\n",
+                               ATANAME(ap, at));
+               }
+       } else {
+               if (at->at_probe == ATA_PROBE_FAILED) {
+                       at->at_probe = ATA_PROBE_NEED_HARD_RESET;
+                       at->at_features |= ATA_PORT_F_RESCAN;
+                       kprintf("%s: HOTPLUG (PM) - Device inserted\n",
+                               ATANAME(ap, at));
+               }
+       }
+}
+
+/*
+ * Read a PM register
+ */
+int
+sili_pm_read(struct sili_port *ap, int target, int which, u_int32_t *datap)
+{
+       struct ata_xfer *xa;
+       int error;
+
+       xa = sili_ata_get_xfer(ap, &ap->ap_ata[15]);
+
+       xa->fis->type = ATA_FIS_TYPE_H2D;
+       xa->fis->flags = ATA_H2D_FLAGS_CMD | 15;
+       xa->fis->command = ATA_C_READ_PM;
+       xa->fis->features = which;
+       xa->fis->device = target | ATA_H2D_DEVICE_LBA;
+       xa->fis->control = ATA_FIS_CONTROL_4BIT;
+
+       xa->complete = sili_pm_dummy_done;
+       xa->datalen = 0;
+       xa->flags = ATA_F_READ | ATA_F_POLL;
+       xa->timeout = 1000;
+
+       if (sili_ata_cmd(xa) == ATA_S_COMPLETE) {
+               *datap = xa->rfis->sector_count | (xa->rfis->lba_low << 8) |
+                      (xa->rfis->lba_mid << 16) | (xa->rfis->lba_high << 24);
+               error = 0;
+       } else {
+               kprintf("%s.%d pm_read SCA[%d] failed\n",
+                       PORTNAME(ap), target, which);
+               *datap = 0;
+               error = EIO;
+       }
+       sili_ata_put_xfer(xa);
+       return (error);
+}
+
+/*
+ * Write a PM register
+ */
+int
+sili_pm_write(struct sili_port *ap, int target, int which, u_int32_t data)
+{
+       struct ata_xfer *xa;
+       int error;
+
+       xa = sili_ata_get_xfer(ap, &ap->ap_ata[15]);
+
+       xa->fis->type = ATA_FIS_TYPE_H2D;
+       xa->fis->flags = ATA_H2D_FLAGS_CMD | 15;
+       xa->fis->command = ATA_C_WRITE_PM;
+       xa->fis->features = which;
+       xa->fis->device = target | ATA_H2D_DEVICE_LBA;
+       xa->fis->sector_count = (u_int8_t)data;
+       xa->fis->lba_low = (u_int8_t)(data >> 8);
+       xa->fis->lba_mid = (u_int8_t)(data >> 16);
+       xa->fis->lba_high = (u_int8_t)(data >> 24);
+       xa->fis->control = ATA_FIS_CONTROL_4BIT;
+
+       xa->complete = sili_pm_dummy_done;
+       xa->datalen = 0;
+       xa->flags = ATA_F_READ | ATA_F_POLL;
+       xa->timeout = 1000;
+
+       if (sili_ata_cmd(xa) == ATA_S_COMPLETE)
+               error = 0;
+       else
+               error = EIO;
+       sili_ata_put_xfer(xa);
+       return(error);
+}
+
+/*
+ * Dummy done callback for xa.
+ */
+static void
+sili_pm_dummy_done(struct ata_xfer *xa)
+{
+}
+
+static void
+sili_pm_empty_done(struct sili_ccb *ccb)
+{
+}