NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

kern/38177: first try of support for SATAlink 3132 chip



>Number:         38177
>Category:       kern
>Synopsis:       first try of support for SATAlink 3132 chip
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Wed Mar 05 18:10:00 +0000 2008
>Originator:     Wolfgang Stukenbrock
>Release:        NetBSD 4.0
>Organization:
Dr. Nagler & Company GmbH
        
>Environment:
        
        
System: NetBSD s012 2.1 NetBSD 2.1 (NSW-S012) #10: Mon Dec 12 12:03:54 CET 2005 
wgstuken@s011:/export/netbsd-2.1/usr/src/sys/arch/i386/compile/NSW-S012 i386
Architecture: i386
Machine: i386
>Description:
        In NetBSD there is still no support for the 3132 chip family of SATA 
controler chips.
        There is a driver in OpenBSD.
        I've needed the support in an amd64 kernel, therefore I've spend some 
time in porting a driver
        for the 3132 chip to NetBSD. The extionsions I've done can be found 
below.
        It works fine under heavy load without any problems - but currently 
only with one disc connect.

        Accedently I've not the possibility or time to check the driver on 
multiple platforms.
        Also I've not the time to complete it to reach the state of a 
productive driver, but perhaps someone
        else will have the time and can start from the work I've done.

        I've added the driver to the satalink driver, because I've recognized 
too late, that this chip is
        completely different to the chips in the satalink driver before.
        It is something like the ahci-core ide stuff.
        My current implementation is a mix of pciide-common stuff and 
ahci_core.c parts.
        It should be separated again and a new driver should be created for 
that.

        Some aspects of this implementation:
        - only some basic ideas are taken from the OpenBSD driver. It is mainly 
based on the other NetBSD-drivers.
          If the copyritgh message from OpenBSD should be added anyway to the 
code, this must be done!
        - only the 3132 is support - the OpenBSD driver supports more chips
        - all command slots of the chip per channel are supported.
          (This is differend to the ahci_core stuff where only slot 0 is used 
...)
          In order to do this, the driver will empty chp->ch_queue->active_xfer 
after assigning the xfer to
          an availabel slot again. Otherwise the ata framework would not be 
able to pass an additional xfer to
          the lower level.
          This is not the very best sollution - some better support for this 
kind of HW-drivers should be added
          to the ata framework.
        - Error handling is poor up to now in this driver. I've no change to 
test this, because I've seen no errors
          after some fatal-implementation-dependent one during development.
          The error handling in the OpenBSD version is wrong (and incomplete). 
The Chip does not return a number as error,
          it returns a bitfield. Therefore I haven't take anything of the 
handling from there.
        - ATAPI is not supported - as in the ahci-core - due to the fact, that 
the ATAPI-stuff will directly call
          routines like "wdc_exec_command" and does not use the function switch 
via atabus->ata_exec_command
          function pointers. I've stopped here. It looks like that there is 
still some rework to do ...
          This driver will detect the ATAPI device and ignore it. (the ahci 
driver does the same without message)
        - This driver for the 3132 seems to be about 10% faster than the 
piixide.c driver to the ICH9 with the
          same disk connected to it. I'm using a card from Dawicontrol.
        - due to a very bad restriction in the WD disk driver (ata/wd.c), only 
2 slots of the 31 available will be used.
          (Perhaps the performace will be even better it that is fixed ...)
          The problem is, that "openings" is set to one from the ata side in 
the WD-structures ...
          - there is no "normal" way to change this
          - some data is stored in the struct wd_softc for the current 
transfer, so it is impossible to have more
            than one transfer active at the time.
            e.g. the pointer to the buffer in work is stored in sc_bp
                 the ata_bio structure is static there - so only one transfer 
desription can be there
            The ATA-framework itself is able to handle many requests at a time 
...
          It would be a good idea to change something in the WD-driver in order 
to increase the performance!
          This restrictions seems to be absent in the SD-driver implementation. 
Perhaps this flexibility
          should be ported from there to the wd-disk driver, because there 
seems to come more and more
          SATA chips, that will be able to handle more than one transfer at a 
time.

          If someone will take up this work, please feel free to contact me, if 
there are come up any question.

          W. Stukenbrock
>How-To-Repeat:
        not relevant
>Fix:

changes done to src/sys/dev/pci/pciide_sii3112_reg.h
(some defines for the cip registers added)

--- pciide_sii3112_reg.h        2008/03/05 16:47:01     1.1
+++ pciide_sii3112_reg.h        2008/03/05 16:47:41
@@ -108,4 +108,105 @@
 
 #define        SII3112_BA5_IND_DATA    0xc4    /* BA5 indirect data */
 
+
+/***********************************************************/
+/***********************************************************/
+
+#define SII3132_PCI_GLOBAL      0x10
+#define SII3132_PCI_PORT        0x18
+#define SII3132_PORT_SIZE             0x2000
+#define SII3132_PCI_INDIRECT    0x20
+
+#define SII3132_GR_P0_STATUS    0x00
+#define SII3132_GR_P1_STATUS    0x04
+#define SII3132_GR_GC           0x40
+#define SII3132_GR_GC_GR               (1<<31)
+#define SII3132_GR_GC_MSIACK           (1<<30)
+#define SII3132_GR_GC_I2CINT           (1<<29)
+#define SII3132_GR_GC_P1_ENA           (1<<1)
+#define SII3132_GR_GC_P0_ENA           (1<<0)
+
+#define SII3132_GR_GIS          0x44
+#define SII3132_GR_GIS_I2C              (1<<29)
+#define SII3132_GR_GIS_P1               (1<<1)
+#define SII3132_GR_GIS_P0               (1<<0)
+
+#define SII3132_PR_LRAM        0x0000
+#define SII3132_PR_LRAM_SLOT(_S_)       (SII3132_PR_LRAM + (_S_) * 0x80)
+#define SII3132_PR_LRAM_RX_CNT(_S_)     (SII3132_PR_LRAM_SLOT(_S_) + 0x04)
+#define SII3132_PR_LRAM_SIG_HI(_S_)     (SII3132_PR_LRAM_SLOT(_S_) + 0x0c)
+#define SII3132_PR_LRAM_SIG_LO(_S_)     (SII3132_PR_LRAM_SLOT(_S_) + 0x14)
+#define SII3132_NUM_SLOTS               31
+#define SII3132_PR_PCS         0x1000
+#define SII3132_PR_PCS_RDY              (1<<31)
+#define SII3132_PR_PCS_OOBB             (1<<25)
+#define   PCS_ACTIVE_SHIFT              16
+#define SII3132_PR_PCS_ACTIVE           (0x1f<<PCS_ACTIVE_SHIFT)
+#define SII3132_PR_PCS_LED_ON           (1<<15)
+#define SII3132_PR_PCS_AIA              (1<<14)
+#define SII3132_PR_PCS_PMEN             (1<<13)
+#define SII3132_PR_PCS_IA               (1<<12)
+#define SII3132_PR_PCS_IR               (1<<11)
+#define SII3132_PR_PCS_A32B             (1<<10)
+#define SII3132_PR_PCS_SD               (1<<9)
+#define SII3132_PR_PCS_CD               (1<<8)
+#define SII3132_PR_PCS_TB               (1<<7)
+#define SII3132_PR_PCS_RESUME           (1<<6)
+#define SII3132_PR_PCS_PLEN             (1<<5)
+#define SII3132_PR_PCS_LEDDISABLE       (1<<4)
+#define SII3132_PR_PCS_NOINTCLR         (1<<3)
+#define SII3132_PR_PCS_PORTINIT         (1<<2)
+#define SII3132_PR_PCS_DEVRESET         (1<<1)
+#define SII3132_PR_PCS_PORTRESET        (1<<0)
+#define SII3132_PR_PCC         0x1004
+#define SII3132_PR_PCC_OOBB             (1<<25)
+#define SII3132_PR_PCC_LED_ON           (1<<15)
+#define SII3132_PR_PCC_AIA              (1<<14)
+#define SII3132_PR_PCC_PMEN             (1<<13)
+#define SII3132_PR_PCC_IA               (1<<12)
+#define SII3132_PR_PCC_IR               (1<<11)
+#define SII3132_PR_PCC_A32B             (1<<10)
+#define SII3132_PR_PCC_SD               (1<<9)
+#define SII3132_PR_PCC_CD               (1<<8)
+#define SII3132_PR_PCC_TB               (1<<7)
+#define SII3132_PR_PCC_RESUME           (1<<6)
+#define SII3132_PR_PCC_PLEN             (1<<5)
+#define SII3132_PR_PCC_LEDDISABLE       (1<<4)
+#define SII3132_PR_PCC_NOINTCLR         (1<<3)
+#define SII3132_PR_PCC_PORTINIT         (1<<2)
+#define SII3132_PR_PCC_DEVRESET         (1<<1)
+#define SII3132_PR_PCC_PORTRESET        (1<<0)
+#define SII3132_PR_IS          0x1008
+#define SII3132_PR_IS_SDBnote           (1<<11)
+#define SII3132_PR_IS_DevExchr          (1<<7)
+#define SII3132_PR_IS_UnrecFIS          (1<<6)
+#define SII3132_PR_IS_ComWake           (1<<5)
+#define SII3132_PR_IS_PhyRdyChg         (1<<4)
+#define SII3132_PR_IS_PMChange          (1<<3)
+#define SII3132_PR_IS_PortRdy           (1<<2)
+#define SII3132_PR_IS_ComErr            (1<<1)
+#define SII3132_PR_IS_ComComplete       (1<<0)
+#define SII3132_PR_IS_STATUS(_X_)       ((_X_) << 16)
+#define SII3132_PR_IES         0x1010
+#define SII3132_PR_IEC         0x1014
+#define SII3132_PR_AUA         0x101c
+#define SII3132_PR_FIFO        0x1020
+#define SII3132_PR_CE          0x1022
+#define SII3132_PR_FC          0x1028
+#define SII3132_PR_FTC         0x102c
+#define SII3132_PR_DEC         0x1040
+#define SII3132_PR_CEC         0x1044
+#define SII3132_PR_HEC         0x1048
+#define SII3132_PR_PHYCONF     0x1050
+#define SII3132_PR_PSS         0x1800
+#define SII3132_PR_PSS_MASK             (~(1<<31))
+#define SII3132_PR_CAR_LO(_S_) (0x1c00 + ((_S_) * 0x8))
+#define SII3132_PR_CAR_HI(_S_) (0x1c00 + ((_S_) * 0x8) + 0x4)
+#define SII3132_PR_CONTEXT     0x1e0f
+#define SII3132_PR_SCTL        0x1f00
+#define SII3132_PR_SSTS        0x1f04
+#define SII3132_PR_SERR        0x1f08
+#define SII3132_PR_SACT        0x1f0c
+
+
 #endif /* _DEV_PCI_PCIIDE_SII3112_REG_H_ */



changes done to src/sys/dev/pci/pciidevar.h
(some addtional space to store things needed -> if done with own driver, an own 
structure wourd be better
 perhaps shared with ahci-core ...)


--- pciidevar.h 2008/03/05 16:47:01     1.1
+++ pciidevar.h 2008/03/05 16:47:41
@@ -116,6 +116,11 @@
        bus_space_tag_t sc_ba5_st;
        bus_space_handle_t sc_ba5_sh;
        int sc_ba5_en;
+#define sc_glob_iot sc_ba5_st
+#define sc_glob_ioh sc_ba5_sh
+        bus_space_tag_t sc_port_iot;
+       bus_space_handle_t sc_port_ioh;
+       void *priv_data[PCIIDE_MAX_CHANNELS]; /* some pointers for driver 
specific allocations */
 #endif /* NATA_DMA */
 
        /* Vendor info (for interpreting Chip description) */


changes done to src/sys/dev/pci/satalink.c
(chip added to the list of supported chips. driver added at end of file )


--- satalink.c  2008/03/05 16:47:01     1.1
+++ satalink.c  2008/03/05 16:50:06
@@ -274,6 +274,8 @@
 static void sii3112_drv_probe(struct ata_channel*);
 static void sii3112_setup_channel(struct ata_channel*);
 
+static void sii3132_chip_map(struct pciide_softc*, struct pci_attach_args*);
+
 static const struct pciide_product_desc pciide_satalink_products[] =  {
        { PCI_PRODUCT_CMDTECH_3112,
          0,
@@ -295,6 +297,11 @@
          "Silicon Image SATALink 3114",
          sii3114_chip_map,
        },
+       { PCI_PRODUCT_CMDTECH_3132,
+         0,
+         "Silicon Image SATALink 3132",
+         sii3132_chip_map,
+       },
        { 0,
          0,
          NULL,
@@ -573,15 +580,11 @@
        sc->sc_dma_ok = 1;
 }
 
+static const char *channel_names[] = { "port 0", "port 1", "port 2", "port 3", 
};
+
 static int
 sii3114_chansetup(struct pciide_softc *sc, int channel)
 {
-       static const char *channel_names[] = {
-               "port 0",
-               "port 1",
-               "port 2",
-               "port 3",
-       };
        struct pciide_channel *cp = &sc->pciide_channels[channel];
 
        sc->wdc_chanarray[channel] = &cp->ata_channel;
@@ -935,3 +936,1022 @@
        }
        BA5_WRITE_4(sc, chp->ch_channel, ba5_IDE_DTM, dtm);
 }
+
+/***********************************************************************************/
+/***********************************************************************************/
+/***********************************************************************************/
+/***********************************************************************************/
+
+#include <sys/queue.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/disklabel.h>
+#include <machine/vmparam.h>
+#include <dev/ic/wdcreg.h> /* for WDCTL_4BIT declaration */
+#if 0
+#include <sys/disk.h>
+#include <dev/ata/wdvar.h>
+#endif
+
+/* NC-support for SATA 300 versions */
+
+#define sii3132_write(_SC_, _R_, _V_) bus_space_write_4((_SC_)->sc_glob_iot, 
(_SC_)->sc_glob_ioh, _R_, _V_);
+#define sii3132_read(_SC_, _R_) bus_space_read_4((_SC_)->sc_glob_iot, 
(_SC_)->sc_glob_ioh, _R_)
+#define sii3132_p_write(_SC_, _P_, _R_, _V_) 
bus_space_write_4((_SC_)->sc_glob_iot, CHAN_TO_PCHAN(_P_)->ctl_baseioh, _R_, 
_V_);
+#define sii3132_p_read(_SC_, _P_, _R_) bus_space_read_4((_SC_)->sc_glob_iot, 
CHAN_TO_PCHAN(_P_)->ctl_baseioh, _R_)
+
+static int sii3132_p_wait_eq(struct pciide_softc *sc, struct ata_channel *chp,
+  bus_size_t r, uint32_t m, uint32_t v, int t)
+{
+  while ((sii3132_p_read(sc, chp, r) & m) != v) { if (t == 0) return 0; 
delay(1000); t--; }
+  return 1;
+}
+
+#if 0
+static int sii3132_p_wait_ne(struct pciide_softc *sc, struct ata_channel *chp,
+  bus_size_t r, uint32_t m, uint32_t v, int t)
+{
+  while ((sii3132_p_read(sc, chp, r) & m) == v) { if (t == 0) return 0; 
delay(1000); t--; }
+  return 1;
+}
+#endif
+
+static void sii3132_slot_write_64(struct pciide_softc *sc, struct ata_channel 
*chp, int slot, void *data)
+{
+  bus_space_write_region_4(sc->sc_glob_iot, CHAN_TO_PCHAN(chp)->ctl_baseioh, 
slot * 0x80, data, 64 / 4);
+  sii3132_p_write(sc, chp, SII3132_PR_FIFO, slot);
+}
+
+/* some type definitions for the 3132 driver ... */
+
+struct sii3132_sge
+{
+  uint32_t  addr_lo;
+  uint32_t  addr_hi;
+  uint32_t  count;
+  uint32_t  flags;
+} __attribute__ ((packed));
+#define SII3132_SGE_TRM  (1 << 31)
+#define SII3132_SGE_LNK  (1 << 30)
+#define SII3132_SGE_DRD  (1 << 29)
+#define SII3132_SGE_XCF  (1 << 28)
+
+/* XXXX not shure if this is BYTE_ORDER independent .... */
+struct sii3132_prb
+{
+  uint16_t  control;
+  uint16_t  p_over;
+  uint32_t  count;
+  uint8_t   fis[20];
+  uint32_t  spare;
+  union
+    {
+      struct { struct sii3132_sge sge[2];
+       } ata;
+      struct { uint8_t   cdg[16];
+              struct sii3132_sge sge[1];
+       } packet;
+    } u;
+}  __attribute__ ((packed));
+
+#define SII3132_CMD_NUM_SGT 7
+#define SII3132_CMD_NUM_SGE ((SII3132_CMD_NUM_SGT - 1) * 3 + 4)
+struct sii3132_command
+{
+  struct sii3132_prb prb;
+  struct sii3132_sge sge[4 * SII3132_CMD_NUM_SGT]; /* SII3132_CMD_NUM_SGT sgt 
entries with each 4 sge elements */
+} __attribute__ ((packed));
+
+#define SII3132_PRB_PROTOCOL_OVERRIDE      (1<<0)
+#define SII3132_PRB_RETRANSMIT             (1<<1)
+#define SII3132_PRB_EXTERNAL_COMMAND       (1<<2) 
+#define SII3132_PRB_RECEIVE                (1<<3)
+#define SII3132_PRB_PACKET_READ            (1<<4)
+#define SII3132_PRB_PACKET_WRITE           (1<<5)
+#define SII3132_PRB_INTERRUPT_MASK         (1<<6)
+#define SII3132_PRB_SOFT_RESET             (1<<7)
+
+struct sii3132_port_data
+{
+  struct sii3132_slot_data
+    {
+      struct ata_xfer          *xfer;
+      struct sii3132_slot_data *next;
+      int                       slot_num;
+      int                       mode;
+#define SII3132_SLOT_MODE_NONE   0
+#define SII3132_SLOT_MODE_ATA    1
+#define SII3132_SLOT_MODE_ATABIO 2
+#define SII3132_SLOT_MODE_ATAPI  3
+      caddr_t                   dma_kva;
+      paddr_t                   dma_kpa;
+      struct callout            to;
+      bus_dmamap_t              data_map;
+    }                       slot[SII3132_NUM_SLOTS];
+  struct sii3132_slot_data *free;
+  struct sii3132_slot_data *busy;
+  struct sii3132_slot_data *busy_last;
+  uint32_t                  slot_status;
+  bus_dmamap_t              dma_map;
+  bus_dma_segment_t         dma_seg;
+  caddr_t                   scratch_kva;
+};
+
+
+static void __sii3132command_done_end(struct ata_channel*, struct 
sii3132_slot_data*, struct sii3132_port_data*);
+static void __sii3132command_done(struct ata_channel*, struct 
sii3132_slot_data*, struct sii3132_port_data*);
+
+static void     
+sii3132_drv_probe(struct ata_channel *chp)
+{
+  struct pciide_softc *sc = CHAN_TO_PCIIDE(chp);
+  int i, s;
+  uint32_t sstatus;
+  struct sii3132_prb pbr_cmd;
+  const char *msg, *speed;
+  struct sii3132_port_data *p_priv;
+
+  for (i = 0; i < chp->ch_ndrive; i++)
+    { /* XXX This should be done by other code. */
+      chp->ch_drive[i].chnl_softc = chp;
+      chp->ch_drive[i].drive = i;
+    }
+  /* reset the channel now ... */
+  sii3132_p_write(sc, chp, SII3132_PR_PCC, SII3132_PR_PCC_PORTRESET);
+  delay(1000); /* not shure if required - but this is more secure ... */
+  sii3132_p_write(sc, chp, SII3132_PR_PCC, SII3132_PR_PCC_A32B); /* in case it 
was set before ... */
+  sii3132_p_write(sc, chp, SII3132_PR_PCS, SII3132_PR_PCS_NOINTCLR);
+  sii3132_p_write(sc, chp, SII3132_PR_FTC, 0x00400020); /* set recieve to 32 
and write threshhold to 64 qwords - FIFO length 256 qwords*/
+  if (sii3132_p_wait_eq(sc, chp, SII3132_PR_PCS, SII3132_PR_PCS_RDY, 
SII3132_PR_PCS_RDY, 1000) == 0)
+    {
+#if 0 /* this messages comes in the middle of other output and is not realy 
interessting - no DEBUG stuff inside of this module ... */
+      aprint_verbose("%s: channel %d - port not ready - ignored\n", 
sc->sc_wdcdev.sc_atac.atac_dev.dv_xname, chp->ch_channel);
+#endif
+    err_exit:
+      sii3132_p_write(sc, chp, SII3132_PR_PCS, SII3132_PR_PCC_PORTRESET); /* 
reset port again - no support for auto-connect like usb ... */
+      return;
+    }
+  sstatus = sii3132_p_read(sc, chp, SII3132_PR_SSTS);
+  switch (sstatus & SStatus_DET_mask) {
+  case SStatus_DET_NODEV:
+         /* No device; be silent. - will no be reached, because the port wil 
not get ready above ... */
+         goto err_exit;
+
+  case SStatus_DET_DEV_NE:
+         aprint_error("%s: channel %d: device connected, but communication not 
established\n",
+             sc->sc_wdcdev.sc_atac.atac_dev.dv_xname, chp->ch_channel);
+         goto err_exit;
+
+  case SStatus_DET_OFFLINE:
+         aprint_error("%s: channel %d: PHY offline\n",
+             sc->sc_wdcdev.sc_atac.atac_dev.dv_xname, chp->ch_channel);
+         goto err_exit;
+
+  case SStatus_DET_DEV:
+          speed = sata_speed(sstatus);
+         memset(&pbr_cmd, 0, sizeof(pbr_cmd));
+         pbr_cmd.control = htole16(SII3132_PRB_SOFT_RESET | 
SII3132_PRB_INTERRUPT_MASK);
+         sii3132_slot_write_64(sc, chp, 0, &pbr_cmd);
+         if (sii3132_p_wait_eq(sc, chp, SII3132_PR_PSS, (1 << 0), 0, 1000) == 
0)
+           {
+             aprint_error("%s: port %d: device reset failed\n",
+               sc->sc_wdcdev.sc_atac.atac_dev.dv_xname, chp->ch_channel);
+             goto err_exit;
+           }
+         sstatus = sii3132_p_read(sc, chp, SII3132_PR_LRAM_SIG_HI(0)) << 8;
+         sstatus |= sii3132_p_read(sc, chp, SII3132_PR_LRAM_SIG_LO(0)) && 0xff;
+         s=splbio();
+         switch (sstatus)
+           {
+             case 0x00000101:   chp->ch_drive[0].drive_flags |= DRIVE_ATA;   
msg = "ATA"; break;
+             case 0xeb140101:   chp->ch_drive[0].drive_flags |= DRIVE_ATAPI; 
msg = "ATAPI"; break;
+             default: msg = NULL; break;
+           }
+         splx(s);
+         if (msg != NULL)
+           {
+             aprint_normal("%s: channel %d: %s-device present, speed: %s\n",
+                 sc->sc_wdcdev.sc_atac.atac_dev.dv_xname, chp->ch_channel, 
msg, speed);
+             if (chp->ch_drive[0].drive_flags & DRIVE_ATAPI)
+               { 
+/* XXXXXXX support missing XXXXXX */
+                 aprint_error("%s: channel %d: ATAPI still not supported by 
this driver - sorry\n",
+                   sc->sc_wdcdev.sc_atac.atac_dev.dv_xname, chp->ch_channel);
+                 chp->ch_drive[0].drive_flags &= ~(DRIVE_ATA | DRIVE_ATAPI);
+                 goto err_exit;
+               }
+             p_priv = sc->priv_data[chp->ch_channel] = malloc(sizeof(struct 
sii3132_port_data), M_DEVBUF, M_WAITOK);
+             if (p_priv == NULL) goto no_priv;
+             memset(sc->priv_data[chp->ch_channel], 0, sizeof(struct 
sii3132_port_data));
+/* see original sili driver from ObenBSD for explanation ...
+   for each slot one prb plus 7 sgts - whatever that is -> somethink a little 
bit smaller than 512
+   scratch buffer for error recovery at least one disk block -> 512 bytes
+   the command-blocks need 8 byte alignment, the scratch will be PAGE_SIZE 
aligned
+
+   remark: we alloc all in one peace not in two parts as done in the original 
driver
+*/
+#define SII3132_DMA_SLOTSIZE sizeof(struct sii3132_command)
+#define SII3132_DMA_SCRATCHSIZE 512
+#define SII3132_DMA_MEMSIZE (SII3132_DMA_SLOTSIZE * SII3132_NUM_SLOTS + 
SII3132_DMA_SCRATCHSIZE)
+#define SII3132_DMA_MEMOFF(_SL_) (SII3132_DMA_SCRATCHSIZE + (_SL_) * 
SII3132_DMA_SLOTSIZE)
+             if (bus_dmamap_create(sc->sc_dmat, SII3132_DMA_MEMSIZE, 1, 
SII3132_DMA_MEMSIZE, 0,
+                   BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &p_priv->dma_map) != 0) 
goto no_dma_map;
+             if (bus_dmamem_alloc(sc->sc_dmat, SII3132_DMA_MEMSIZE, PAGE_SIZE, 
0,
+                &p_priv->dma_seg, 1, &s, BUS_DMA_NOWAIT) != 0) goto 
no_dma_alloc;
+             if (bus_dmamem_map(sc->sc_dmat, &p_priv->dma_seg, s, 
SII3132_DMA_MEMSIZE,
+                 &p_priv->scratch_kva, BUS_DMA_NOWAIT) != 0) goto 
no_dma_mapping;
+             if (bus_dmamap_load(sc->sc_dmat, p_priv->dma_map, 
p_priv->scratch_kva,
+                 SII3132_DMA_MEMSIZE, NULL, BUS_DMA_NOWAIT) != 0)
+               {
+               no_data_map:
+                 bus_dmamem_unmap(sc->sc_dmat, p_priv->scratch_kva, 
SII3132_DMA_MEMSIZE);
+               no_dma_mapping:
+                 bus_dmamem_free(sc->sc_dmat, &p_priv->dma_seg, 1);
+               no_dma_alloc:
+                 bus_dmamap_destroy(sc->sc_dmat, p_priv->dma_map);
+               no_dma_map:
+                 free(p_priv, M_DEVBUF);
+                 sc->priv_data[chp->ch_channel] = NULL;
+               no_priv:
+                 aprint_error("%s: channel %d: out of memory for port data\n",
+                   sc->sc_wdcdev.sc_atac.atac_dev.dv_xname, chp->ch_channel);
+                 chp->ch_drive[0].drive_flags &= ~(DRIVE_ATA | DRIVE_ATAPI);
+                 goto err_exit;
+               }
+             KASSERT(p_priv->dma_map->dm_nsegs == 1);
+             memset(p_priv->scratch_kva, 0, SII3132_DMA_MEMSIZE);
+             for (s = 0; s < SII3132_NUM_SLOTS; s++)
+               { /* build slot free list */
+                 p_priv->slot[s].slot_num = s;
+                 p_priv->slot[s].next = p_priv->free;
+                 p_priv->free = &p_priv->slot[s];
+                 p_priv->slot[s].dma_kva = p_priv->scratch_kva + 
SII3132_DMA_SCRATCHSIZE + s * SII3132_DMA_SLOTSIZE;
+                 p_priv->slot[s].dma_kpa = p_priv->dma_map->dm_segs[0].ds_addr 
+ SII3132_DMA_SCRATCHSIZE + s * SII3132_DMA_SLOTSIZE;
+                 callout_init(&p_priv->slot[s].to);
+                 if (bus_dmamap_create(sc->sc_dmat, MAXPHYS, 
SII3132_CMD_NUM_SGE, MAXPHYS, 0,
+                     BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, 
&p_priv->slot[s].data_map) != 0)
+                   {
+                     while (s > 0) { bus_dmamap_destroy(sc->sc_dmat, 
p_priv->slot[--s].data_map); }
+                     goto no_data_map;
+                   }
+               }
+             /* enable interrupts for this port ... */
+              sii3132_write(sc, SII3132_GR_GC, sii3132_read(sc, SII3132_GR_GC) 
| (1 << chp->ch_channel));
+              sii3132_p_write(sc, chp, SII3132_PR_IS, 0x0000ffff); /* reset 
all pending interrupts ... */
+              sii3132_p_write(sc, chp, SII3132_PR_IES, (SII3132_PR_IS_ComErr | 
SII3132_PR_IS_ComComplete));
+           }
+         else
+           {
+             aprint_error("%s: channel %d: unknown device present (signature 
0x%08x), speed: %s\n",
+               sc->sc_wdcdev.sc_atac.atac_dev.dv_xname, chp->ch_channel, 
sstatus, speed);
+             goto err_exit;
+           }
+         break;
+
+  default:
+         aprint_error("%s: channel %d: unknown SStatus: 0x%08x\n",
+             sc->sc_wdcdev.sc_atac.atac_dev.dv_xname, chp->ch_channel, 
sstatus);
+    }
+}
+
+static int
+sii3132_chansetup(struct pciide_softc *sc, int channel)
+{
+  struct pciide_channel *cp = &sc->pciide_channels[channel];
+
+  sc->wdc_chanarray[channel] = &cp->ata_channel;
+  cp->name = channel_names[channel];
+  cp->ata_channel.ch_channel = channel;
+  cp->ata_channel.ch_ndrive = 1; /* we have only one drive per channel ... */
+  cp->ata_channel.ch_atac = &sc->sc_wdcdev.sc_atac;
+  cp->ata_channel.ch_queue = malloc(sizeof(struct ata_queue), M_DEVBUF, 
M_NOWAIT);
+  if (cp->ata_channel.ch_queue == NULL)
+    {
+      aprint_error("%s %s channel: can't allocate memory for command queue", 
sc->sc_wdcdev.sc_atac.atac_dev.dv_xname, cp->name);
+      return 0;
+    }
+
+  cp->compat = 0;
+  cp->ih = sc->sc_pci_ih;
+
+  if (bus_space_subregion(sc->sc_port_iot, sc->sc_port_ioh, channel * 
SII3132_PORT_SIZE, SII3132_PORT_SIZE, &cp->ctl_baseioh) != 0)
+    {
+      aprint_error("%s: couldn't subregion %s port base\n", 
sc->sc_wdcdev.sc_atac.atac_dev.dv_xname, cp->name);
+      free(cp->ata_channel.ch_queue, M_DEVBUF);
+      cp->ata_channel.ch_queue = NULL;
+      return 0;
+    }
+  wdcattach(&cp->ata_channel);
+  return 1;
+}
+
+static void
+sii3132_port_intr(struct pciide_softc *sc, int port, int to_slot)
+{
+  struct ata_channel *chp = &sc->pciide_channels[port].ata_channel;
+  struct sii3132_port_data *p_priv = sc->priv_data[port];
+  struct sii3132_slot_data *s1, *s2, *s3;
+  uint32_t ps, is, pss, ce, mask, restart_mode = 0;
+  int slot;
+
+  ps = sii3132_p_read(sc, chp, SII3132_PR_PCS);
+  is = sii3132_p_read(sc, chp, SII3132_PR_IS);
+  sii3132_p_write(sc, chp, SII3132_PR_IS, is & 0x0000ffff);
+  pss = sii3132_p_read(sc, chp, SII3132_PR_PSS) & SII3132_PR_PSS_MASK;
+  slot = (ps & SII3132_PR_PCS_ACTIVE) >> PCS_ACTIVE_SHIFT;
+  if (is & SII3132_PR_IS_ComErr)
+    { /* remark we do not catch slot == 31 here - will force a port-init but 
doesn harm anything else */
+      ce = sii3132_p_read(sc, chp, SII3132_PR_CE);
+aprint_error("command error channel %d slot %d 0x%08x ....\n", 
chp->ch_channel, slot, ce);
+      p_priv->slot[slot].xfer->c_flags |= C_TIMEOU; /* no better idea for now 
.... */
+      pss &= ~(1 << slot); /* remove this one from active slot set */
+      restart_mode = SII3132_PR_PCS_PORTINIT;
+    }
+  if (to_slot >= 0 && (pss & (1 << to_slot)))
+    { /* command timeout */
+aprint_error("command timeout channel %d slot %d ....\n", chp->ch_channel, 
slot);
+      p_priv->slot[slot].xfer->c_flags |= C_TIMEOU;
+      pss &= ~(1 << to_slot);
+      restart_mode = SII3132_PR_PCS_DEVRESET;
+    }
+  mask = ~pss & p_priv->slot_status; /* get set of terminated commands */
+  if (restart_mode != 0)
+    { /* perform reset operation an restart all non-terminated commands in the 
correct order */
+      sii3132_p_write(sc, chp, SII3132_PR_PCS, restart_mode);
+      if (sii3132_p_wait_eq(sc, chp, SII3132_PR_PCS, SII3132_PR_PCS_RDY, 
SII3132_PR_PCS_RDY, 1000) == 0)
+       { /* port will not get ready again ... */
+         aprint_error("%s: channel %d: restart failed after error - trying to 
continue anyway ...\n",
+           sc->sc_wdcdev.sc_atac.atac_dev.dv_xname, port);
+       }
+      for (s1 = p_priv->busy; s1 != NULL; s1 = s1->next)
+       {
+         if (!(mask & (1 << s1->slot_num)))
+           { /* this one is still not finished - restart it ... */
+             uint64_t c_addr = (uint64_t)s1->dma_kpa; /* need to copy to 64 
bit variable to avoid size problem */
+             sii3132_p_write(sc, chp, SII3132_PR_CAR_LO(s1->slot_num), 
(uint32_t)c_addr);
+             sii3132_p_write(sc, chp, SII3132_PR_CAR_HI(s1->slot_num), 
(uint32_t)(c_addr >> 32));
+           }
+       }
+    }
+  for (s1 = p_priv->busy, s2 = NULL; mask != 0 && s1 != NULL;)
+    {
+      if (mask & (1 << s1->slot_num))
+       { /* done - remove from list and call cleanup stuff ... */
+         mask &= ~(1 << s1->slot_num);
+         s3 = s1;
+         if (s2 != NULL) s2->next = s1->next; else p_priv->busy = s1->next;
+         if ((s1 = s1->next) == NULL) p_priv->busy_last = s2;
+         __sii3132command_done(chp, s3, p_priv);
+       }
+      else
+       { s2 = s1, s1 = s1->next; }
+    }
+  KASSERT(mask == 0);
+}
+
+static int
+sii3132_pci_intr(void *arg)
+{
+  struct pciide_softc *sc = arg;
+  uint32_t is;
+  int port;
+
+  if ((is = sii3132_read(sc, SII3132_GR_GIS)) == 0)
+    return 0; /* no interrupt pending */
+  sii3132_write(sc, SII3132_GR_GIS, is);
+  if (is & SII3132_GR_GIS_I2C)
+    {
+      aprint_error("%s: I2C interrupt ignored\n", 
sc->sc_wdcdev.sc_atac.atac_dev.dv_xname);
+      is &= ~SII3132_GR_GIS_I2C;
+    }
+  for (port = 0; is != 0; is >>= 1, port++)
+    { if (is & 0x0001) sii3132_port_intr(sc, port, -1); }
+  return 1;
+}
+
+static void
+sii3132_datain_pio(struct ata_channel *chp, int flags, void *bf, size_t len)
+{
+aprint_error("sii3132 - datain_pio ...\n");
+panic("sii3132 - unimpleneted datain_pio");
+}
+
+static void
+sii3132_dataout_pio(struct ata_channel *chp, int flags, void *bf, size_t len)
+{
+aprint_error("sii3132 - dataout_pio ...\n");
+panic("sii3132 - unimpleneted dataout_pio");
+}
+
+#if NATA_DMA || NATA_PIOBM
+static void
+sii3132_do_reset(struct ata_channel *chp, int poll)
+{
+aprint_error("sii3132 - do_reset ...\n");
+panic("sii3132 - unimpleneted do_reset");
+}
+#endif /* NATA_DMA || NATA_PIOBM */
+
+static int
+__sii3132command_start_sub(struct pciide_softc *sc, struct ata_channel *chp, 
void *datap, size_t count,
+  struct sii3132_slot_data *slot, int dma_mode, struct sii3132_sge *sge)
+{
+  struct sii3132_port_data *p_priv = sc->priv_data[chp->ch_channel];
+  uint64_t c_addr;
+
+  if (datap != NULL)
+    { /* setup data transfer list ... */
+      int i;
+
+      if (bus_dmamap_load(sc->sc_dmat, slot->data_map, datap, count, NULL,
+         BUS_DMA_NOWAIT | BUS_DMA_STREAMING | dma_mode) != 0)
+       {
+         aprint_error("%s: channel %d: problems load dma-map for %td bytes\n",
+           sc->sc_wdcdev.sc_atac.atac_dev.dv_xname, chp->ch_channel, count);
+         return 1;
+       }
+#if 0
+aprint_verbose("sii - dma-s size %td r %d data %p - slot %p - num %d\n",
+  slot->data_map->dm_mapsize, (dma_mode == BUS_DMA_READ), datap, slot, 
slot->slot_num);
+#endif
+      bus_dmamap_sync(sc->sc_dmat, slot->data_map, 0, 
slot->data_map->dm_mapsize,
+       ((dma_mode == BUS_DMA_READ) ? BUS_DMASYNC_PREREAD : 
BUS_DMASYNC_PREWRITE));
+      for (i = 0; i < slot->data_map->dm_nsegs; i++, sge++)
+       {
+         c_addr = slot->data_map->dm_segs[i].ds_addr;
+         sge->addr_lo = htole32((uint32_t)c_addr);
+         sge->addr_hi = htole32((uint32_t)(c_addr >> 32));
+         sge->count = htole32(slot->data_map->dm_segs[i].ds_len);
+       }
+      sge[-1].flags = htole32(SII3132_SGE_TRM);
+    }
+  bus_dmamap_sync(sc->sc_dmat, p_priv->dma_map, 
SII3132_DMA_MEMOFF(slot->slot_num), SII3132_DMA_SLOTSIZE, BUS_DMASYNC_PREWRITE);
+  c_addr = (uint64_t)slot->dma_kpa; /* need to copy to 64 bit variable to 
avoid size problem */
+  sii3132_p_write(sc, chp, SII3132_PR_CAR_LO(slot->slot_num), 
(uint32_t)c_addr);
+  sii3132_p_write(sc, chp, SII3132_PR_CAR_HI(slot->slot_num), 
(uint32_t)(c_addr >> 32));
+  return 0;
+}
+
+static void
+sii3132_to(void *arg)
+{
+  struct sii3132_slot_data *slot = arg;
+  struct ata_channel *chp;
+  int s;
+
+  s = splbio();
+  KASSERT(slot->xfer != NULL);
+  KASSERT(slot->xfer->c_chp != NULL);
+  chp = slot->xfer->c_chp;
+  sii3132_port_intr(CHAN_TO_PCIIDE(chp), chp->ch_channel, slot->slot_num);
+  splx(s);
+}
+
+static void
+__sii3132command_b_start(struct ata_channel *chp, struct ata_xfer *xfer)
+{ /* check if there is an unused slot avaliable, if yes move the entry from 
active_xfer to the chip */
+  struct pciide_softc *sc = CHAN_TO_PCIIDE(chp);
+  struct sii3132_port_data *p_priv = sc->priv_data[chp->ch_channel];
+  struct sii3132_slot_data *slot;
+  struct ata_bio *ata_bio = xfer->c_cmd;
+  struct sii3132_command *cmd;
+  int nblks;
+
+/* remark: we do not support polling - WAIT is used instead */
+  if (p_priv->free == NULL)
+    {
+aprint_error("sii3132 (BIO) - no slot left (slot status 0x%08x) ...\n", 
p_priv->slot_status);
+      if (!(ata_bio->flags & ATA_POLL))
+       return; /* will be done later ... */
+      while (p_priv->free == NULL) /* simulate interrupts until a slot gets 
free ... */
+       {
+         delay(1000);
+         sii3132_port_intr(sc, chp->ch_channel, -1);
+       }
+    }
+  slot = p_priv->free; p_priv->free = slot->next; slot->next = NULL;
+  slot->xfer = xfer;
+  slot->mode = SII3132_SLOT_MODE_ATABIO;
+  p_priv->slot_status |= (1 << slot->slot_num);
+  chp->ch_queue->active_xfer = NULL; /* free active_xfer entry so that the 
next command can be placed in the next slot */
+
+/* build command for the chip ... */
+  nblks = xfer->c_bcount / ata_bio->lp->d_secsize;
+  cmd = (struct sii3132_command*)slot->dma_kva;
+  memset(cmd, 0, sizeof(struct sii3132_command));  /* clear all - zero 
assignments are skipped below */
+  cmd->prb.fis[0] = 0x27;                               /* type - host to 
device */
+  cmd->prb.fis[1] = 0x80;                               /* command-flag, 
port-mux port */
+  if (ata_bio->flags & ATA_LBA48)                       /* command */
+    cmd->prb.fis[2] = (ata_bio->flags & ATA_READ) ? WDCC_READDMA_EXT : 
WDCC_WRITEDMA_EXT;
+  else
+    cmd->prb.fis[2] = (ata_bio->flags & ATA_READ) ? WDCC_READDMA : 
WDCC_WRITEDMA;
+  cmd->prb.fis[3] = 0;                                  /* feature */
+  cmd->prb.fis[4] = ata_bio->blkno & 0xff;              /* sector */
+  cmd->prb.fis[5] = (ata_bio->blkno >> 8) & 0xff;       /* cyl lo */
+  cmd->prb.fis[6] = (ata_bio->blkno >> 16) & 0xff;      /* cyl hi */
+  if (ata_bio->flags & ATA_LBA48)
+    {
+      cmd->prb.fis[7] = WDSD_LBA;                       /* head */
+      cmd->prb.fis[8] = (ata_bio->blkno >> 24) & 0xff;  /* sector (exp) */
+      cmd->prb.fis[9] = (ata_bio->blkno >> 32) & 0xff;  /* cyl lo (exp) */
+      cmd->prb.fis[10] = (ata_bio->blkno >> 40) & 0xff; /* cyl hi (exp)  */
+    }
+  else
+    {
+      cmd->prb.fis[7] = ((ata_bio->blkno >> 24) & 0x0f) | WDSD_LBA;
+#if 0
+      cmd->prb.fis[8] = 0;
+      cmd->prb.fis[9] = 0;
+      cmd->prb.fis[10] = 0;
+#endif
+    }
+#if 0
+  cmd->prb.fis[11] = 0;                                 /* feature (exp) */
+#endif
+  cmd->prb.fis[12] = nblks & 0xff;                      /* count */
+  cmd->prb.fis[13] = (ata_bio->flags & ATA_LBA48) ? ((nblks >> 8) & 0xff) : 0; 
/* count (exp) */
+  cmd->prb.fis[15] = WDCTL_4BIT;                        /* control */
+
+  if (__sii3132command_start_sub(sc, chp, ata_bio->databuf, ata_bio->bcount,
+      slot, ((ata_bio->flags & ATA_READ) ? BUS_DMA_READ : BUS_DMA_WRITE), 
cmd->prb.u.ata.sge) != 0)
+    {
+      ata_bio->error |= ERR_DMA;
+      ata_bio->r_error = 0;
+      __sii3132command_done_end(chp, slot, p_priv);
+      return;
+    }
+  if (p_priv->busy_last == NULL) /* command now on chip - put it to busy list 
... */
+    p_priv->busy = slot;
+  else
+    p_priv->busy_last->next = slot;
+  p_priv->busy_last = slot;
+
+  if (xfer->c_flags & C_POLL)
+    {
+      int to = 10000;
+
+      do /* we are at splbio() here - so our interrupt will not come ... */
+       {
+         if (ata_bio->flags & ATA_NOSLEEP) delay(1000); else tsleep(&xfer, 
PRIBIO, "sii_pl4", mstohz(10));
+         sii3132_port_intr(sc, chp->ch_channel, -1);
+       }
+      while (--to > 0 && !(ata_bio->flags & ATA_ITSDONE));
+      if (!(ata_bio->flags & ATA_ITSDONE))
+       sii3132_port_intr(sc, chp->ch_channel, slot->slot_num);
+    }
+  else
+    callout_reset(&slot->to, mstohz(10000), sii3132_to, slot);
+
+  atastart(chp); /* XXXX this may lead to a recursive call of the driver 
functions !!! */
+}
+
+static void
+__sii3132command_b_kill_xfer (struct ata_channel *chp, struct ata_xfer *xfer, 
int reason)
+{
+  struct ata_bio *ata_bio = xfer->c_cmd;
+  struct pciide_softc *sc = CHAN_TO_PCIIDE(chp);
+  struct sii3132_port_data *p_priv = sc->priv_data[chp->ch_channel];
+  int i;
+
+  switch (reason)
+    {
+    case KILL_GONE:  ata_bio->error |= ERR_NODEV;  break;
+    case KILL_RESET: ata_bio->error |= ERR_RESET; break;
+    default:
+      aprint_error("__sii3132command_b_kill_xfer: unknown reason %d\n", 
reason);
+    do_panic:
+      panic("__sii3132command_b_kill_xfer");
+    }
+  ata_bio->r_error = WDCE_ABRT;
+  for (i = 0; i < SII3132_NUM_SLOTS; i++)
+    {
+      if (p_priv->slot[i].xfer == xfer)
+       {
+aprint_error("__sii3132command_b_kill_xfer channel %d slot %d ...\n", 
chp->ch_channel, i);
+         __sii3132command_done(chp, &p_priv->slot[i], p_priv);
+         return;
+       }
+    }
+  aprint_error("__sii3132command_b_kill_xfer: xfer not found in slot info\n");
+  goto do_panic;
+}
+
+static int
+sii3132_ata_bio(struct ata_drive_datas *drvp, struct ata_bio *ata_bio)
+{
+  struct ata_xfer *xfer;
+  struct ata_channel *chp = drvp->chnl_softc;
+
+  xfer = ata_get_xfer(ATAXF_NOSLEEP);
+  if (xfer == NULL) return ATACMD_TRY_AGAIN;
+  if (ata_bio->flags & ATA_POLL)       xfer->c_flags |= C_POLL;
+  xfer->c_drive = drvp->drive;
+  xfer->c_cmd = ata_bio;
+  xfer->c_databuf = ata_bio->databuf;
+  xfer->c_bcount = ata_bio->bcount;
+  xfer->c_start =     __sii3132command_b_start;
+  xfer->c_intr =      NULL; /* cannot be used - parameter missing ... */
+  xfer->c_kill_xfer = __sii3132command_b_kill_xfer;
+  ata_exec_xfer(chp, xfer);
+  return (ata_bio->flags & ATA_ITSDONE) ? ATACMD_COMPLETE : ATACMD_QUEUED;
+}
+
+static int sii3132_ata_addref(struct ata_drive_datas *drvp) { return 
ata_addref(drvp->chnl_softc); }
+static void sii3132_ata_delref(struct ata_drive_datas *drvp) { 
ata_delref(drvp->chnl_softc); }
+
+static void
+__sii3132command_start(struct ata_channel *chp, struct ata_xfer *xfer)
+{ /* check if there is an unused slot avaliable, if yes move the entry from 
active_xfer to the chip */
+  struct pciide_softc *sc = CHAN_TO_PCIIDE(chp);
+  struct sii3132_port_data *p_priv = sc->priv_data[chp->ch_channel];
+  struct sii3132_slot_data *slot;
+  struct ata_command *ata_c = xfer->c_cmd;
+  struct sii3132_command *cmd;
+
+/* remark: we do not support polling - WAIT is used instead */
+  if (p_priv->free == NULL)
+    {
+aprint_error("sii3132 (ATA) - no slot left (slot status 0x%08x) ...\n", 
p_priv->slot_status);
+      if (!(ata_c->flags & AT_POLL))
+       return; /* will be done later ... */
+      while (p_priv->free == NULL) /* simulate interrupts until a slot gets 
free ... */
+       {
+         if (ata_c->flags & AT_WAIT) tsleep(&xfer, PRIBIO, "sii_pl1", 
mstohz(10)); else delay(1000);
+         sii3132_port_intr(sc, chp->ch_channel, -1);
+       }
+    }
+  slot = p_priv->free; p_priv->free = slot->next; slot->next = NULL;
+  slot->xfer = xfer;
+  slot->mode = SII3132_SLOT_MODE_ATA;
+  p_priv->slot_status |= (1 << slot->slot_num);
+  chp->ch_queue->active_xfer = NULL; /* free active_xfer entry so that the 
next command can be placed in the next slot */
+
+/* build command for the chip ... */
+  cmd = (struct sii3132_command*)slot->dma_kva;
+  memset(cmd, 0, sizeof(struct sii3132_command)); /* clear all - zero 
assignments are skipped below */
+  if (ata_c->flags & AT_POLL)
+    cmd->prb.control = (1 << 6); /* no interrupt on command completion ... */
+  cmd->prb.fis[0] = 0x27;                        /* type - host to device */
+  cmd->prb.fis[1] = 0x80;                        /* command-flag, port-mux 
port */
+  cmd->prb.fis[2] = ata_c->r_command;            /* command */
+  cmd->prb.fis[3] = ata_c->r_features;           /* feature */
+  cmd->prb.fis[4] = ata_c->r_sector;             /* sector */
+  cmd->prb.fis[5] = ata_c->r_cyl & 0xff;         /* cyl lo */
+  cmd->prb.fis[6] = (ata_c->r_cyl >> 8) & 0xff;  /* cyl hi */
+  cmd->prb.fis[7] = ata_c->r_head & 0x0f;        /* head */
+#if 0
+  cmd->prb.fis[8] = 0;                           /* sector (exp) */
+  cmd->prb.fis[9] = 0;                           /* cyl lo (exp) */
+  cmd->prb.fis[10] = 0;                          /* cyl hi (exp)  */
+  cmd->prb.fis[11] = 0;                          /* feature (exp) */
+#endif
+  cmd->prb.fis[12] = ata_c->r_count;             /* count */
+#if 0
+  cmd->prb.fis[13] = 0;                           /* count (exp) */
+#endif
+  cmd->prb.fis[15] = WDCTL_4BIT;                /* control */
+
+  if (__sii3132command_start_sub(sc, chp, ((ata_c->flags & (AT_READ | 
AT_WRITE)) ? ata_c->data : NULL),
+      ata_c->bcount, slot, ((ata_c->flags & AT_READ) ? BUS_DMA_READ : 
BUS_DMA_WRITE), cmd->prb.u.ata.sge) != 0)
+    {
+      ata_c->flags |= AT_TIMEOU;
+      __sii3132command_done_end(chp, slot, p_priv);
+      return;
+    }
+  if (p_priv->busy_last == NULL) /* command now on chip - put it to busy list 
... */
+    p_priv->busy = slot;
+  else
+    p_priv->busy_last->next = slot;
+  p_priv->busy_last = slot;
+
+  if (ata_c->flags & AT_POLL)
+    {
+      int to = ata_c->timeout;
+
+      do /* we are at splbio() here - so our interrupt will not come ... */
+       {
+         if (ata_c->flags & AT_WAIT) tsleep(&xfer, PRIBIO, "sii_pl2", 
mstohz(10)); else delay(1000);
+         sii3132_port_intr(sc, chp->ch_channel, -1);
+       }
+      while ((ata_c->timeout == 0 || --to > 0) && !(ata_c->flags & AT_DONE));
+      if (!(ata_c->flags & AT_DONE))
+       sii3132_port_intr(sc, chp->ch_channel, slot->slot_num);
+    }
+  else
+    callout_reset(&slot->to, mstohz(ata_c->timeout), sii3132_to, slot);
+
+  atastart(chp); /* XXXX this may lead to a recursive call of the driver 
functions !!! */
+}
+
+static void     
+__sii3132command_done_end(struct ata_channel *chp, struct sii3132_slot_data 
*slot, struct sii3132_port_data *p_priv)
+{
+  struct ata_command *ata_c = NULL;
+  struct ata_bio * ata_bio = NULL;
+  int drv = 0;
+
+  if (slot->mode == SII3132_SLOT_MODE_ATABIO)
+    {
+      ata_bio = slot->xfer->c_cmd;
+      ata_bio->flags |= ATA_ITSDONE;
+      drv = slot->xfer->c_drive;
+    }
+  else if (slot->mode == SII3132_SLOT_MODE_ATA)
+    {
+      ata_c = slot->xfer->c_cmd;
+      ata_c->flags |= AT_DONE;
+    }
+  else
+    panic("sii3132 done_end - slot-mode");
+  ata_free_xfer(chp, slot->xfer);
+  slot->xfer = NULL;
+  p_priv->slot_status &= ~(1 << slot->slot_num);
+  if (slot->mode == SII3132_SLOT_MODE_ATABIO)
+    {
+      (*chp->ch_drive[drv].drv_done)(chp->ch_drive[drv].drv_softc);
+    }
+  else
+    {
+      if (ata_c->flags & AT_WAIT) wakeup(ata_c);
+      else if (ata_c->callback)   ata_c->callback(ata_c->callback_arg);
+    }
+  slot->mode = SII3132_SLOT_MODE_NONE;
+  slot->next = p_priv->free; p_priv->free = slot;
+  if (chp->ch_queue->active_xfer != NULL)
+    { /* start waiting command - ata-level don't know anything about this 
feature ... */
+      __sii3132command_start(chp, chp->ch_queue->active_xfer);
+    }
+  atastart(chp); /* XXXX this may lead to a recursive call of the driver 
functions !!! */
+}
+
+static void
+__sii3132command_done(struct ata_channel *chp, struct sii3132_slot_data *slot, 
struct sii3132_port_data *p_priv)
+{
+  struct pciide_softc *sc = CHAN_TO_PCIIDE(chp);
+  struct ata_command *ata_c = NULL;
+  struct ata_bio *ata_bio = NULL;
+  int im = 0;
+
+  callout_stop(&slot->to); /* cancel any pending timeout */
+  bus_dmamap_sync(sc->sc_dmat, p_priv->dma_map, 
SII3132_DMA_MEMOFF(slot->slot_num), SII3132_DMA_SLOTSIZE, 
BUS_DMASYNC_POSTWRITE);
+  if (slot->mode == SII3132_SLOT_MODE_ATABIO)
+    {
+      ata_bio = slot->xfer->c_cmd;
+      if (ata_bio->flags & ATA_READ)
+       {
+         im = 1;
+         ata_bio->bcount -= sii3132_p_read(sc, chp, 
SII3132_PR_LRAM_RX_CNT(slot->slot_num));
+       }
+      else
+       {
+         ata_bio->bcount = 0; /* we have no transmit counter - assume all done 
- else we would have seen an error ... */
+         im = 2;
+       }
+      if (slot->xfer->c_flags & C_TIMEOU)
+       ata_bio->error = TIMEOUT;
+      else
+       ata_bio->error = 0;
+    }
+  else if (slot->mode == SII3132_SLOT_MODE_ATA)
+    {
+      ata_c = slot->xfer->c_cmd;
+      if (ata_c->flags & (AT_READ | AT_WRITE))
+       im = (ata_c->flags & AT_READ) ? 1 : 2;
+      if (sii3132_p_read(sc, chp, SII3132_PR_LRAM_RX_CNT(slot->slot_num)) != 0)
+       ata_c->flags |= AT_XFDONE;
+      if (slot->xfer->c_flags & C_TIMEOU)
+       ata_c->flags |= AT_TIMEOU;
+    }
+  else
+    panic("sii3132 done - slot-mode");
+  if (im != 0)
+    {
+      bus_dmamap_sync(sc->sc_dmat, slot->data_map, 0, 
slot->data_map->dm_mapsize,
+       (im == 1 ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE));
+      bus_dmamap_unload(sc->sc_dmat, slot->data_map);
+    }
+  __sii3132command_done_end(chp, slot, p_priv);
+}
+
+static void     
+__sii3132command_kill_xfer(struct ata_channel *chp, struct ata_xfer *xfer, int 
reason) 
+{
+  struct ata_command *ata_c = xfer->c_cmd;
+  struct pciide_softc *sc = CHAN_TO_PCIIDE(chp);
+  struct sii3132_port_data *p_priv = sc->priv_data[chp->ch_channel];
+  int i;
+
+  switch (reason)
+    {
+    case KILL_GONE:  ata_c->flags |= AT_GONE;  break;
+    case KILL_RESET: ata_c->flags |= AT_RESET; break;
+    default:
+      aprint_error("__sii3132command_kill_xfer: unknown reason %d\n", reason);
+    do_panic:
+      panic("__sii3132command_kill_xfer");
+    }
+  for (i = 0; i < SII3132_NUM_SLOTS; i++)
+    {
+      if (p_priv->slot[i].xfer == xfer)
+       {
+aprint_error("__sii3132command_kill_xfer channel %d slot %d ...\n", 
chp->ch_channel, i);
+         __sii3132command_done(chp, &p_priv->slot[i], p_priv);
+         return;
+       }
+    }
+  aprint_error("__sii3132command_kill_xfer: xfer not found in slot info\n");
+  goto do_panic;
+}
+
+static int
+sii3132_exec_command(struct ata_drive_datas *drvp, struct ata_command *ata_c)  
+{ /* own function because we have different HW here ... derived from 
wdc_exec_command() */
+  struct ata_channel *chp = drvp->chnl_softc;
+  struct ata_xfer *xfer;
+  int s, ret;
+
+  xfer = ata_get_xfer(ata_c->flags & AT_WAIT ? ATAXF_CANSLEEP : ATAXF_NOSLEEP);
+  if (xfer == NULL) return ATACMD_TRY_AGAIN;
+  if (ata_c->flags & AT_POLL)             xfer->c_flags |= C_POLL;
+  if (ata_c->flags & AT_WAIT)             xfer->c_flags |= C_WAIT;
+  xfer->c_drive = drvp->drive;
+  xfer->c_databuf = ata_c->data;
+  xfer->c_bcount = ata_c->bcount;
+  xfer->c_cmd = ata_c;
+  xfer->c_start =     __sii3132command_start;
+  xfer->c_intr =      NULL; /* cannot be used by this driver - paramter 
missing ... */
+  xfer->c_kill_xfer = __sii3132command_kill_xfer;
+
+  s = splbio();
+  ata_exec_xfer(chp, xfer);
+  if (ata_c->flags & AT_DONE)
+    ret = ATACMD_COMPLETE;
+  else
+    {
+      if (ata_c->flags & AT_WAIT)
+       {
+         while ((ata_c->flags & AT_DONE) == 0)
+           tsleep(ata_c, PRIBIO, "sii_cmd", 0);
+         ret = ATACMD_COMPLETE;
+       }
+      else
+       ret = ATACMD_QUEUED;
+    }
+  splx(s);
+  return ret;
+}
+
+static void
+sii3132_reset_drive(struct ata_drive_datas *drvp, int flags)
+{
+  struct ata_channel *chp = drvp->chnl_softc;
+  struct atac_softc *atac = chp->ch_atac;
+/* never called up to now ... */
+aprint_error("%s: drive reset requested for channel %d\n", 
atac->atac_dev.dv_xname, chp->ch_channel);
+panic("sii3132 - unimpleneted reset_drive");
+}
+
+static void
+sii3132_reset_channel(struct ata_channel *chp, int flags)
+{
+  struct pciide_softc *sc = CHAN_TO_PCIIDE(chp);
+
+aprint_error("%s: channel %d reset requested\n", 
sc->sc_wdcdev.sc_atac.atac_dev.dv_xname, chp->ch_channel);
+panic("sii3132 - unimpleneted reset_channel");
+}
+
+static const struct ata_bustype sii3132_ata_bustype = {
+        SCSIPI_BUSTYPE_ATA,
+        sii3132_ata_bio,
+        sii3132_reset_drive,
+        sii3132_reset_channel,
+        sii3132_exec_command,
+        ata_get_params,
+        sii3132_ata_addref,
+        sii3132_ata_delref,
+        ata_kill_pending,
+};
+
+static void
+sii3132_setup_channel(struct ata_channel *chp)
+{
+#if NATA_UDMA 
+  struct ata_drive_datas *drvp;
+  int drive;
+#endif
+  int s;
+  struct pciide_channel *cp = CHAN_TO_PCHAN(chp);
+
+  for (drive = 0; drive < cp->ata_channel.ch_ndrive; drive++)
+    {
+      drvp = &chp->ch_drive[drive];
+      if ((drvp->drive_flags & DRIVE) == 0) continue;
+#if NATA_UDMA 
+      if (drvp->drive_flags & DRIVE_UDMA)
+       { /* use UDMA - see pciide_common.c ... */
+         s = splbio();
+         drvp->drive_flags &= ~DRIVE_DMA;
+         splx(s);
+       }
+#endif
+#if 0
+      aprint_error("XXXX - openings %d\n", ((struct 
wd_softc*)chp->ata_drives[drive])->openings);
+#endif
+    }
+}
+
+static void
+sii3132_chip_map(struct pciide_softc *sc, struct pci_attach_args *pa)
+{
+       pcireg_t memtype;
+       bus_size_t glob_size, port_size;
+       pci_intr_handle_t intrhandle;
+       const char *intrstr;
+       int channel;
+
+       if (pciide_chipen(sc, pa) == 0)
+               return;
+       /* get some values from the attach args ... */
+       sc->sc_dmat = pa->pa_dmat;
+
+       memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, SII3132_PCI_GLOBAL);
+       if (pci_mapreg_map(pa, SII3132_PCI_GLOBAL, memtype, 0, 
&sc->sc_glob_iot, &sc->sc_glob_ioh, NULL, &glob_size) != 0)
+         {
+           aprint_error("%s: unable to map GLBOAL register space\n", 
sc->sc_wdcdev.sc_atac.atac_dev.dv_xname);
+           return;
+         }
+       memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, SII3132_PCI_PORT);
+       if (pci_mapreg_map(pa, SII3132_PCI_PORT, memtype, 0, &sc->sc_port_iot, 
&sc->sc_port_ioh, NULL, &port_size) != 0)
+         {
+           aprint_error("%s: unable to map PORT register space\n", 
sc->sc_wdcdev.sc_atac.atac_dev.dv_xname);
+           bus_space_unmap(sc->sc_glob_iot, sc->sc_glob_ioh, glob_size);
+           return;
+         }
+
+       /* reset the chip now ... */
+       sii3132_write(sc, SII3132_GR_GC, SII3132_GR_GC_GR);
+       delay(50 * 1000); /* not shure if required - copied from other chip 
setup ... */
+       sii3132_write(sc, SII3132_GR_GC, 0);
+       delay(50 * 1000); /* not shure if required - copied from other chip 
setup ... */
+
+       sc->sc_wdcdev.sc_atac.atac_set_modes = sii3132_setup_channel;
+       sc->sc_wdcdev.sc_atac.atac_probe = sii3132_drv_probe;
+       sc->sc_wdcdev.sc_atac.atac_bustype_ata = &sii3132_ata_bustype;
+#if NATAPIBUS > 0
+       sc->sc_wdcdev.sc_atac.atac_atapibus_attach = NULL; /* XXX  - from ahci 
driver ????
+                                                           * wdcattach will 
set it's own routine XXXXXXX
+                                                           */
+#endif
+       sc->sc_wdcdev.sc_atac.atac_cap = ATAC_CAP_DATA16 | ATAC_CAP_DATA32 | 
ATAC_CAP_DMA | ATAC_CAP_UDMA;
+       sc->sc_wdcdev.sc_atac.atac_pio_cap = 4;
+#if NATA_DMA
+       sc->sc_wdcdev.sc_atac.atac_dma_cap = 2;
+#if NATA_UDMA
+       sc->sc_wdcdev.sc_atac.atac_udma_cap = 6;
+#endif
+#endif
+
+#if NATA_DMA || NATA_PIOBM
+        sc->sc_wdcdev.dma_init = NULL;
+        sc->sc_wdcdev.dma_start = NULL;
+        sc->sc_wdcdev.dma_finish = NULL;
+       sc->sc_wdcdev.irqack = NULL;
+       sc->sc_wdcdev.reset = sii3132_do_reset;          /* needed - 
wdcattach() will set it otherwise */
+#endif  /* NATA_DMA || NATA_PIOBM */
+       sc->sc_wdcdev.datain_pio = sii3132_datain_pio;   /* needed - 
wdcattach() will set it otherwise */
+       sc->sc_wdcdev.dataout_pio = sii3132_dataout_pio; /* needed - 
wdcattach() will set it otherwise */
+
+       sc->sc_wdcdev.sc_atac.atac_channels = sc->wdc_chanarray;
+       sc->sc_wdcdev.sc_atac.atac_nchannels = 2;
+
+       if (pci_intr_map(pa, &intrhandle) != 0)
+         {
+           aprint_error("%s: couldn't map native-PCI interrupt\n", 
sc->sc_wdcdev.sc_atac.atac_dev.dv_xname);
+           goto map_error;
+         }
+       intrstr = pci_intr_string(pa->pa_pc, intrhandle);
+       sc->sc_pci_ih = pci_intr_establish(pa->pa_pc, intrhandle, IPL_BIO, 
sii3132_pci_intr, sc);
+       if (sc->sc_pci_ih != NULL)
+         {
+           aprint_normal("%s: using %s for native-PCI interrupt\n", 
sc->sc_wdcdev.sc_atac.atac_dev.dv_xname,
+             intrstr ? intrstr : "unknown interrupt");
+         }
+       else
+         {
+           aprint_error("%s: couldn't establish native-PCI interrupt", 
sc->sc_wdcdev.sc_atac.atac_dev.dv_xname);
+           if (intrstr != NULL) aprint_normal(" at %s", intrstr);
+           aprint_normal("\n");
+           goto map_error;
+         }
+       for (channel = 0; channel < sc->sc_wdcdev.sc_atac.atac_nchannels; 
channel++)
+         {
+           if (sii3132_chansetup(sc, channel) == 0) goto map_error_intr;
+         }
+        return;
+
+map_error_intr:
+       pci_intr_disestablish(pa->pa_pc, sc->sc_pci_ih);
+map_error:
+       bus_space_unmap(sc->sc_glob_iot, sc->sc_glob_ioh, glob_size);
+       bus_space_unmap(sc->sc_port_iot, sc->sc_port_ioh, port_size);
+}

>Unformatted:
        
        


Home | Main Index | Thread Index | Old Index