Index: sys/dev/cardbus/files.cardbus *************** *** 131,133 **** --- 131,137 ---- # attach njata at cardbus with njata_cardbus file dev/cardbus/njata_cardbus.c njata_cardbus + + # Silicon Image SATALink controllers + attach satalink at cardbus with satalink_cardbus + file dev/cardbus/satalink_cardbus.c satalink_cardbus Index: sys/dev/cardbus/satalink_cardbus.c *************** *** 0 **** --- 1,580 ---- + /* $Id: satalink_cardbus.c,v 1.1 2009/06/23 11:42:43 tsubai Exp $ */ + + /*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + #include + #include + + #include + #include + #include + #include + #include + #include + + #include + #include + #include + + struct satalink_cardbus_softc { + struct pciide_softc sc_pciide; + struct wdc_regs sc_wdc_regs[2]; + + cardbus_devfunc_t sc_ct; + cardbustag_t sc_tag; + void *sc_ih; + }; + + /* + * Register map for BA5 register space, indexed by channel. + */ + static const struct { + u_int ba5_IDE_DTM; + u_int ba5_SControl; + u_int ba5_SStatus; + u_int ba5_SError; + } satalink_ba5_regmap[] = { + { /* Channel 0 */ + .ba5_IDE_DTM = 0x0b4, + .ba5_SControl = 0x100, + .ba5_SStatus = 0x104, + .ba5_SError = 0x108, + }, + { /* Channel 1 */ + .ba5_IDE_DTM = 0x0f4, + .ba5_SControl = 0x180, + .ba5_SStatus = 0x184, + .ba5_SError = 0x188, + }, + }; + + int satalink_cardbus_match(struct device *, struct cfdata *, void *); + void satalink_cardbus_attach(struct device *, struct device *, void *); + int satalink_cardbus_detach(struct device *, int); + + static u_int ba5_read_4_ind(struct pciide_softc *, u_int); + static u_int ba5_read_4(struct pciide_softc *, u_int); + static void ba5_write_4_ind(struct pciide_softc *, u_int, u_int); + static void ba5_write_4(struct pciide_softc *, u_int, u_int); + static void sii3112_drv_probe(struct ata_channel *); + static void sii3112_setup_channel(struct ata_channel *); + static void sii_fixup_cacheline(struct pciide_softc *, int); + + CFATTACH_DECL_NEW(satalink_cardbus, sizeof(struct satalink_cardbus_softc), + satalink_cardbus_match, satalink_cardbus_attach, satalink_cardbus_detach, + NULL); + + int + satalink_cardbus_match(parent, match, aux) + struct device *parent; + struct cfdata *match; + void *aux; + { + struct cardbus_attach_args *ca = aux; + + switch (ca->ca_id) { + case PCI_ID_CODE(PCI_VENDOR_CMDTECH, PCI_PRODUCT_CMDTECH_3512): + return 1; + } + + return 0; + } + + void + satalink_cardbus_attach(parent, self, aux) + struct device *parent, *self; + void *aux; + { + struct cardbus_attach_args *ca = aux; + struct satalink_cardbus_softc *csc = device_private(self); + struct pciide_softc *sc = &csc->sc_pciide; + cardbus_devfunc_t ct = ca->ca_ct; + cardbus_chipset_tag_t cc = ct->ct_cc; + cardbus_function_tag_t cf = ct->ct_cf; + struct ata_channel *chp; + struct wdc_regs *wdr; + u_int csr, interface, scs_cmd, cfgctl; + int chan, i; + + sc->sc_dma_maxsegsz = IDEDMA_BYTE_COUNT_MAX; + sc->sc_dma_boundary = IDEDMA_BYTE_COUNT_ALIGN; + + sc->sc_dmat = ca->ca_dmat; + csc->sc_ct = ct; + csc->sc_tag = ca->ca_tag; + + #define SII3112_RESET_BITS \ + (SCS_CMD_PBM_RESET | SCS_CMD_ARB_RESET | \ + SCS_CMD_FF1_RESET | SCS_CMD_FF0_RESET | \ + SCS_CMD_IDE1_RESET | SCS_CMD_IDE0_RESET) + + /* + * Reset everything and then unblock all of the interrupts. + */ + scs_cmd = Cardbus_conf_read(ct, ca->ca_tag, SII3112_SCS_CMD); + Cardbus_conf_write(ct, ca->ca_tag, SII3112_SCS_CMD, + scs_cmd | SII3112_RESET_BITS); + delay(50 * 1000); + Cardbus_conf_write(ct, ca->ca_tag, SII3112_SCS_CMD, + scs_cmd & SCS_CMD_BA5_EN); + delay(50 * 1000); + + if (scs_cmd & SCS_CMD_BA5_EN) { + if (Cardbus_mapreg_map(ct, 0x24, + PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT, 0, + &sc->sc_ba5_st, &sc->sc_ba5_sh, NULL, NULL)) { + printf(": unable to map SATALink BA5 register space\n"); + return; + } + sc->sc_ba5_en = 1; + } else { + cfgctl = Cardbus_conf_read(ct, ca->ca_tag, SII3112_PCI_CFGCTL); + Cardbus_conf_write(ct, ca->ca_tag, SII3112_PCI_CFGCTL, + cfgctl | CFGCTL_BA5INDEN); + } + + if (Cardbus_mapreg_map(ct, 0x20, PCI_MAPREG_TYPE_IO, 0, + &sc->sc_dma_iot, &sc->sc_dma_ioh, NULL, NULL)) { + printf(": failed to map dma registers\n"); + return; + } + + /* + * Rev. <= 0x01 of the 3112 have a bug that can cause data + * corruption if DMA transfers cross an 8K boundary. This is + * apparently hard to tickle, but we'll go ahead and play it + * safe. + */ + if (PCI_REVISION(ca->ca_class) <= 0x01) { + sc->sc_dma_maxsegsz = 8192; + sc->sc_dma_boundary = 8192; + } + + sii_fixup_cacheline(sc, 2); + + csc->sc_ih = cardbus_intr_establish(cc, cf, ca->ca_intrline, IPL_BIO, + pciide_pci_intr, sc); + if (csc->sc_ih == NULL) { + printf(": failed to establish interrupt\n"); + return; + } + + /* Make sure the right access type is on the CardBus bridge. */ + (*cf->cardbus_ctrl)(cc, CARDBUS_IO_ENABLE); + (*cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE); + if (sc->sc_ba5_en) + (*cf->cardbus_ctrl)(cc, CARDBUS_MEM_ENABLE); + + /* Enable the appropriate bits in the PCI CSR. */ + csr = Cardbus_conf_read(ct, ca->ca_tag, PCI_COMMAND_STATUS_REG); + csr |= PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_IO_ENABLE; + if (sc->sc_ba5_en) + csr |= PCI_COMMAND_MEM_ENABLE; + Cardbus_conf_write(ct, ca->ca_tag, PCI_COMMAND_STATUS_REG, csr); + + sc->sc_wdcdev.regs = csc->sc_wdc_regs; + sc->sc_wdcdev.sc_atac.atac_dev = self; + sc->sc_wdcdev.sc_atac.atac_nchannels = 2; + sc->sc_wdcdev.sc_atac.atac_pio_cap = 4; + sc->sc_wdcdev.sc_atac.atac_dma_cap = 2; + sc->sc_wdcdev.sc_atac.atac_udma_cap = 6; + sc->sc_wdcdev.sc_atac.atac_cap |= ATAC_CAP_DATA16 | ATAC_CAP_DATA32; + sc->sc_wdcdev.sc_atac.atac_cap |= ATAC_CAP_UDMA | ATAC_CAP_DMA; + sc->sc_wdcdev.sc_atac.atac_channels = sc->wdc_chanarray; + sc->sc_wdcdev.sc_atac.atac_probe = sii3112_drv_probe; + sc->sc_wdcdev.sc_atac.atac_set_modes = sii3112_setup_channel; + sc->sc_wdcdev.irqack = pciide_irqack; + + sc->sc_dma_ok = 1; + sc->sc_wdcdev.dma_arg = sc; + sc->sc_wdcdev.dma_init = pciide_dma_init; + sc->sc_wdcdev.dma_start = pciide_dma_start; + sc->sc_wdcdev.dma_finish = pciide_dma_finish; + + interface = PCIIDE_INTERFACE_PCI(0) | PCIIDE_INTERFACE_PCI(1); + interface |= PCIIDE_INTERFACE_BUS_MASTER_DMA; + + for (chan = 0; chan < sc->sc_wdcdev.sc_atac.atac_nchannels; chan++) { + pciide_chansetup(sc, chan, interface); + + chp = &sc->pciide_channels[chan].ata_channel; + chp->ch_ndrive = 1; + wdr = CHAN_TO_WDC_REGS(chp); + + if (Cardbus_mapreg_map(ct, 0x10 + chan * 8, PCI_MAPREG_TYPE_IO, + 0, &wdr->cmd_iot, &wdr->cmd_baseioh, NULL, NULL)) { + printf(": failed to map device registers\n"); + return; + } + if (Cardbus_mapreg_map(ct, 0x14 + chan * 8, PCI_MAPREG_TYPE_IO, + 0, &wdr->ctl_iot, &wdr->ctl_ioh, NULL, NULL)) { + printf(": failed to map device registers\n"); + return; + } + } + for (chan = 0; chan < sc->sc_wdcdev.sc_atac.atac_nchannels; chan++) { + struct pciide_channel *pc = &sc->pciide_channels[chan]; + + for (i = 0; i < IDEDMA_NREGS; i++) { + bus_space_subregion(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_SCH_OFFSET * chan + i, 4, + &pc->dma_iohs[i]); + } + } + + if (sc->sc_ba5_en) + printf(": SATALink BA5 register space enabled\n"); + else + printf(": SATALink BA5 register space disabled\n"); + + for (chan = 0; chan < sc->sc_wdcdev.sc_atac.atac_nchannels; chan++) { + chp = &sc->pciide_channels[chan].ata_channel; + wdr = CHAN_TO_WDC_REGS(chp); + + for (i = 0; i < WDC_NREG; i++) + bus_space_subregion(wdr->cmd_iot, wdr->cmd_baseioh, + i, 1, &wdr->cmd_iohs[i]); + + wdc_init_shadow_regs(chp); + wdr->data32iot = wdr->cmd_iot; + wdr->data32ioh = wdr->cmd_iohs[0]; + + wdcattach(chp); + } + } + + int + satalink_cardbus_detach(self, flags) + struct device *self; + int flags; + { + struct satalink_cardbus_softc *csc = device_private(self); + struct pciide_softc *sc = &csc->sc_pciide; + struct ata_channel *chp; + struct wdc_regs *wdr; + int chan, error; + + error = wdcdetach(sc->sc_wdcdev.sc_atac.atac_dev, flags); + if (error) + return error; + + for (chan = 0; chan < sc->sc_wdcdev.sc_atac.atac_nchannels; chan++) { + chp = &sc->pciide_channels[chan].ata_channel; + wdr = CHAN_TO_WDC_REGS(chp); + if (wdr->cmd_baseioh) + Cardbus_mapreg_unmap(csc->sc_ct, 0x10 + chan * 8, + wdr->cmd_iot, wdr->cmd_baseioh, 8); + if (wdr->ctl_ioh) + Cardbus_mapreg_unmap(csc->sc_ct, 0x14 + chan * 8, + wdr->ctl_iot, wdr->ctl_ioh, 4); + } + + if (sc->sc_dma_ioh) + Cardbus_mapreg_unmap(csc->sc_ct, 0x20, sc->sc_dma_iot, + sc->sc_dma_ioh, 16); + + if (sc->sc_ba5_sh) + Cardbus_mapreg_unmap(csc->sc_ct, 0x24, sc->sc_ba5_st, + sc->sc_ba5_sh, 256); + + /* XXX bus_dmamap */ + + if (csc->sc_ih) + cardbus_intr_disestablish(csc->sc_ct->ct_cc, + csc->sc_ct->ct_cf, csc->sc_ih); + + return 0; + } + + static inline u_int + ba5_read_4_ind(sc, reg) + struct pciide_softc *sc; + u_int reg; + { + struct satalink_cardbus_softc *csc = (void *)sc; + u_int rv; + int s; + + s = splbio(); + Cardbus_conf_write(csc->sc_ct, csc->sc_tag, SII3112_BA5_IND_ADDR, reg); + rv = Cardbus_conf_read(csc->sc_ct, csc->sc_tag, SII3112_BA5_IND_DATA); + splx(s); + + return rv; + } + + static inline u_int + ba5_read_4(sc, reg) + struct pciide_softc *sc; + u_int reg; + { + + if (__predict_true(sc->sc_ba5_en != 0)) + return (bus_space_read_4(sc->sc_ba5_st, sc->sc_ba5_sh, reg)); + + return ba5_read_4_ind(sc, reg); + } + + #define BA5_READ_4(sc, chan, reg) \ + ba5_read_4((sc), satalink_ba5_regmap[(chan)].reg) + + static inline void + ba5_write_4_ind(sc, reg, val) + struct pciide_softc *sc; + u_int reg, val; + { + struct satalink_cardbus_softc *csc = (void *)sc; + int s; + + s = splbio(); + Cardbus_conf_write(csc->sc_ct, csc->sc_tag, SII3112_BA5_IND_ADDR, reg); + Cardbus_conf_write(csc->sc_ct, csc->sc_tag, SII3112_BA5_IND_DATA, val); + splx(s); + } + + static inline void + ba5_write_4(sc, reg, val) + struct pciide_softc *sc; + u_int reg, val; + { + + if (__predict_true(sc->sc_ba5_en != 0)) + bus_space_write_4(sc->sc_ba5_st, sc->sc_ba5_sh, reg, val); + else + ba5_write_4_ind(sc, reg, val); + } + + #define BA5_WRITE_4(sc, chan, reg, val) \ + ba5_write_4((sc), satalink_ba5_regmap[(chan)].reg, (val)) + + /* + * When the Silicon Image 3112 retries a PCI memory read command, + * it may retry it as a memory read multiple command under some + * circumstances. This can totally confuse some PCI controllers, + * so ensure that it will never do this by making sure that the + * Read Threshold (FIFO Read Request Control) field of the FIFO + * Valid Byte Count and Control registers for both channels (BA5 + * offset 0x40 and 0x44) are set to be at least as large as the + * cacheline size register. + * This may also happen on the 3114 (ragge 050527) + */ + static void + sii_fixup_cacheline(sc, n) + struct pciide_softc *sc; + int n; + { + struct satalink_cardbus_softc *csc = (void *)sc; + pcireg_t cls, reg; + int i; + static u_int addr[] = { 0x40, 0x44, 0x240, 0x244 }; + + cls = Cardbus_conf_read(csc->sc_ct, csc->sc_tag, PCI_BHLC_REG); + cls = (cls >> PCI_CACHELINE_SHIFT) & PCI_CACHELINE_MASK; + cls *= 4; + if (cls > 224) { + cls = Cardbus_conf_read(csc->sc_ct, csc->sc_tag, PCI_BHLC_REG); + cls &= ~(PCI_CACHELINE_MASK << PCI_CACHELINE_SHIFT); + cls |= ((224/4) << PCI_CACHELINE_SHIFT); + Cardbus_conf_write(csc->sc_ct, csc->sc_tag, PCI_BHLC_REG, cls); + cls = 224; + } + if (cls < 32) + cls = 32; + cls = (cls + 31) / 32; + for (i = 0; i < n; i++) { + reg = ba5_read_4(sc, addr[i]); + if ((reg & 0x7) < cls) + ba5_write_4(sc, addr[i], (reg & 0x07) | cls); + } + } + + /* Probe the drives using SATA registers. + * Note we can't use wdc_sataprobe as we may not be able to map ba5 + */ + static void + sii3112_drv_probe(chp) + struct ata_channel *chp; + { + struct pciide_softc *sc = CHAN_TO_PCIIDE(chp); + struct wdc_regs *wdr = CHAN_TO_WDC_REGS(chp); + u_int scontrol, sstatus; + u_int scnt, sn, cl, ch; + int i, s; + + /* XXX This should be done by other code. */ + for (i = 0; i < 2; i++) { + chp->ch_drive[i].chnl_softc = chp; + chp->ch_drive[i].drive = i; + } + + /* + * The 3112 is a 2-port part, and only has one drive per channel + * (each port emulates a master drive). + * + * The 3114 is similar, but has 4 channels. + */ + + /* + * Request communication initialization sequence, any speed. + * Performing this is the equivalent of an ATA Reset. + */ + scontrol = SControl_DET_INIT | SControl_SPD_ANY; + + /* + * XXX We don't yet support SATA power management; disable all + * power management state transitions. + */ + scontrol |= SControl_IPM_NONE; + + BA5_WRITE_4(sc, chp->ch_channel, ba5_SControl, scontrol); + delay(50 * 1000); + scontrol &= ~SControl_DET_INIT; + BA5_WRITE_4(sc, chp->ch_channel, ba5_SControl, scontrol); + delay(50 * 1000); + + sstatus = BA5_READ_4(sc, chp->ch_channel, ba5_SStatus); + #if 0 + aprint_normal_dev(&sc->sc_wdcdev.sc_atac.atac_dev, + "port %d: SStatus=0x%08x, SControl=0x%08x\n", + chp->ch_channel, sstatus, + BA5_READ_4(sc, chp->ch_channel, ba5_SControl)); + #endif + switch (sstatus & SStatus_DET_mask) { + case SStatus_DET_NODEV: + /* No device; be silent. */ + break; + + case SStatus_DET_DEV_NE: + aprint_error_dev(sc->sc_wdcdev.sc_atac.atac_dev, + "port %d: device connected, but " + "communication not established\n", chp->ch_channel); + break; + + case SStatus_DET_OFFLINE: + aprint_error_dev(sc->sc_wdcdev.sc_atac.atac_dev, + "port %d: PHY offline\n", chp->ch_channel); + break; + + case SStatus_DET_DEV: + /* + * XXX ATAPI detection doesn't currently work. Don't + * XXX know why. But, it's not like the standard method + * XXX can detect an ATAPI device connected via a SATA/PATA + * XXX bridge, so at least this is no worse. --thorpej + */ + bus_space_write_1(wdr->cmd_iot, wdr->cmd_iohs[wd_sdh], 0, + WDSD_IBM | (0 << 4)); + delay(10); /* 400ns delay */ + /* Save register contents. */ + scnt = bus_space_read_1(wdr->cmd_iot, + wdr->cmd_iohs[wd_seccnt], 0); + sn = bus_space_read_1(wdr->cmd_iot, + wdr->cmd_iohs[wd_sector], 0); + cl = bus_space_read_1(wdr->cmd_iot, + wdr->cmd_iohs[wd_cyl_lo], 0); + ch = bus_space_read_1(wdr->cmd_iot, + wdr->cmd_iohs[wd_cyl_hi], 0); + #if 0 + printf("%s: port %d: scnt=0x%x sn=0x%x cl=0x%x ch=0x%x\n", + device_xname(&sc->sc_wdcdev.sc_atac.atac_dev), chp->ch_channel, + scnt, sn, cl, ch); + #endif + /* + * scnt and sn are supposed to be 0x1 for ATAPI, but in some + * cases we get wrong values here, so ignore it. + */ + s = splbio(); + if (cl == 0x14 && ch == 0xeb) + chp->ch_drive[0].drive_flags |= DRIVE_ATAPI; + else + chp->ch_drive[0].drive_flags |= DRIVE_ATA; + splx(s); + + aprint_normal_dev(sc->sc_wdcdev.sc_atac.atac_dev, + "port %d: device present, speed: %s\n", + chp->ch_channel, + sata_speed(sstatus)); + break; + + default: + aprint_error_dev(sc->sc_wdcdev.sc_atac.atac_dev, + "port %d: unknown SStatus: 0x%08x\n", + chp->ch_channel, sstatus); + } + } + + static void + sii3112_setup_channel(chp) + struct ata_channel *chp; + { + struct ata_drive_datas *drvp; + int drive, s; + u_int32_t idedma_ctl, dtm; + struct pciide_channel *cp = CHAN_TO_PCHAN(chp); + struct pciide_softc *sc = CHAN_TO_PCIIDE(chp); + + /* setup DMA if needed */ + pciide_channel_dma_setup(cp); + + idedma_ctl = 0; + dtm = 0; + + for (drive = 0; drive < 2; drive++) { + drvp = &chp->ch_drive[drive]; + /* If no drive, skip */ + if ((drvp->drive_flags & DRIVE) == 0) + continue; + if (drvp->drive_flags & DRIVE_UDMA) { + /* use Ultra/DMA */ + s = splbio(); + drvp->drive_flags &= ~DRIVE_DMA; + splx(s); + idedma_ctl |= IDEDMA_CTL_DRV_DMA(drive); + dtm |= DTM_IDEx_DMA; + } else if (drvp->drive_flags & DRIVE_DMA) { + idedma_ctl |= IDEDMA_CTL_DRV_DMA(drive); + dtm |= DTM_IDEx_DMA; + } else { + dtm |= DTM_IDEx_PIO; + } + } + + /* + * Nothing to do to setup modes; it is meaningless in S-ATA + * (but many S-ATA drives still want to get the SET_FEATURE + * command). + */ + if (idedma_ctl != 0) { + /* Add software bits in status register */ + bus_space_write_1(sc->sc_dma_iot, cp->dma_iohs[IDEDMA_CTL], 0, + idedma_ctl); + } + BA5_WRITE_4(sc, chp->ch_channel, ba5_IDE_DTM, dtm); + }