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