11ac8d5baSMatthew Dillon /*
2fb00c6edSMatthew Dillon * (MPSAFE)
3fb00c6edSMatthew Dillon *
41ac8d5baSMatthew Dillon * Copyright (c) 2009 The DragonFly Project. All rights reserved.
51ac8d5baSMatthew Dillon *
61ac8d5baSMatthew Dillon * This code is derived from software contributed to The DragonFly Project
71ac8d5baSMatthew Dillon * by Matthew Dillon <dillon@backplane.com>
81ac8d5baSMatthew Dillon *
91ac8d5baSMatthew Dillon * Redistribution and use in source and binary forms, with or without
101ac8d5baSMatthew Dillon * modification, are permitted provided that the following conditions
111ac8d5baSMatthew Dillon * are met:
121ac8d5baSMatthew Dillon *
131ac8d5baSMatthew Dillon * 1. Redistributions of source code must retain the above copyright
141ac8d5baSMatthew Dillon * notice, this list of conditions and the following disclaimer.
151ac8d5baSMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright
161ac8d5baSMatthew Dillon * notice, this list of conditions and the following disclaimer in
171ac8d5baSMatthew Dillon * the documentation and/or other materials provided with the
181ac8d5baSMatthew Dillon * distribution.
191ac8d5baSMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its
201ac8d5baSMatthew Dillon * contributors may be used to endorse or promote products derived
211ac8d5baSMatthew Dillon * from this software without specific, prior written permission.
221ac8d5baSMatthew Dillon *
231ac8d5baSMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
241ac8d5baSMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
251ac8d5baSMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
261ac8d5baSMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
271ac8d5baSMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
281ac8d5baSMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
291ac8d5baSMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
301ac8d5baSMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
311ac8d5baSMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
321ac8d5baSMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
331ac8d5baSMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341ac8d5baSMatthew Dillon * SUCH DAMAGE.
351ac8d5baSMatthew Dillon *
361ac8d5baSMatthew Dillon *
371ac8d5baSMatthew Dillon * Copyright (c) 2006 David Gwynne <dlg@openbsd.org>
381ac8d5baSMatthew Dillon *
391ac8d5baSMatthew Dillon * Permission to use, copy, modify, and distribute this software for any
401ac8d5baSMatthew Dillon * purpose with or without fee is hereby granted, provided that the above
411ac8d5baSMatthew Dillon * copyright notice and this permission notice appear in all copies.
421ac8d5baSMatthew Dillon *
431ac8d5baSMatthew Dillon * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
441ac8d5baSMatthew Dillon * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
451ac8d5baSMatthew Dillon * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
461ac8d5baSMatthew Dillon * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
471ac8d5baSMatthew Dillon * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
481ac8d5baSMatthew Dillon * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
491ac8d5baSMatthew Dillon * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
501ac8d5baSMatthew Dillon *
511ac8d5baSMatthew Dillon *
521ac8d5baSMatthew Dillon *
531ac8d5baSMatthew Dillon * $OpenBSD: sili.c,v 1.147 2009/02/16 21:19:07 miod Exp $
541ac8d5baSMatthew Dillon */
551ac8d5baSMatthew Dillon
561ac8d5baSMatthew Dillon #include "sili.h"
571ac8d5baSMatthew Dillon
581ac8d5baSMatthew Dillon void sili_port_interrupt_enable(struct sili_port *ap);
591ac8d5baSMatthew Dillon void sili_port_interrupt_redisable(struct sili_port *ap);
601ac8d5baSMatthew Dillon void sili_port_interrupt_reenable(struct sili_port *ap);
611ac8d5baSMatthew Dillon
621ac8d5baSMatthew Dillon int sili_load_prb(struct sili_ccb *);
631ac8d5baSMatthew Dillon void sili_unload_prb(struct sili_ccb *);
641ac8d5baSMatthew Dillon static void sili_load_prb_callback(void *info, bus_dma_segment_t *segs,
651ac8d5baSMatthew Dillon int nsegs, int error);
661ac8d5baSMatthew Dillon void sili_start(struct sili_ccb *);
67187cac04SMatthew Dillon static void sili_port_reinit(struct sili_port *ap);
681ac8d5baSMatthew Dillon int sili_port_softreset(struct sili_port *ap);
69a35ddbb4SMatthew Dillon int sili_port_hardreset(struct sili_port *ap);
701ac8d5baSMatthew Dillon void sili_port_hardstop(struct sili_port *ap);
711ac8d5baSMatthew Dillon void sili_port_listen(struct sili_port *ap);
721ac8d5baSMatthew Dillon
731ac8d5baSMatthew Dillon static void sili_ata_cmd_timeout_unserialized(void *);
74132408ffSMatthew Dillon static int sili_core_timeout(struct sili_ccb *ccb, int really_error);
751ac8d5baSMatthew Dillon void sili_check_active_timeouts(struct sili_port *ap);
761ac8d5baSMatthew Dillon
771ac8d5baSMatthew Dillon void sili_issue_pending_commands(struct sili_port *ap, struct sili_ccb *ccb);
781ac8d5baSMatthew Dillon
79132408ffSMatthew Dillon void sili_port_read_ncq_error(struct sili_port *, int);
801ac8d5baSMatthew Dillon
811ac8d5baSMatthew Dillon struct sili_dmamem *sili_dmamem_alloc(struct sili_softc *, bus_dma_tag_t tag);
821ac8d5baSMatthew Dillon void sili_dmamem_free(struct sili_softc *, struct sili_dmamem *);
831ac8d5baSMatthew Dillon static void sili_dmamem_saveseg(void *info, bus_dma_segment_t *segs, int nsegs, int error);
841ac8d5baSMatthew Dillon
851ac8d5baSMatthew Dillon static void sili_dummy_done(struct ata_xfer *xa);
861ac8d5baSMatthew Dillon static void sili_empty_done(struct sili_ccb *ccb);
871ac8d5baSMatthew Dillon static void sili_ata_cmd_done(struct sili_ccb *ccb);
881ac8d5baSMatthew Dillon
891ac8d5baSMatthew Dillon /*
901ac8d5baSMatthew Dillon * Initialize the global SILI hardware. This code does not set up any of
911ac8d5baSMatthew Dillon * its ports.
921ac8d5baSMatthew Dillon */
931ac8d5baSMatthew Dillon int
sili_init(struct sili_softc * sc)941ac8d5baSMatthew Dillon sili_init(struct sili_softc *sc)
951ac8d5baSMatthew Dillon {
96c3783d8fSzrj DPRINTF(SILI_D_VERBOSE, " GHC 0x%pb%i",
97c3783d8fSzrj SILI_FMT_GHC, sili_read(sc, SILI_REG_GHC));
981ac8d5baSMatthew Dillon
991ac8d5baSMatthew Dillon /*
1001ac8d5baSMatthew Dillon * Reset the entire chip. This also resets all ports.
1011ac8d5baSMatthew Dillon *
1021ac8d5baSMatthew Dillon * The spec doesn't say anything about how long we have to
1031ac8d5baSMatthew Dillon * wait, so wait 10ms.
1041ac8d5baSMatthew Dillon */
1051ac8d5baSMatthew Dillon sili_write(sc, SILI_REG_GCTL, SILI_REG_GCTL_GRESET);
1061ac8d5baSMatthew Dillon sili_os_sleep(10);
1071ac8d5baSMatthew Dillon sili_write(sc, SILI_REG_GCTL, 0);
1081ac8d5baSMatthew Dillon sili_os_sleep(10);
1091ac8d5baSMatthew Dillon
1101ac8d5baSMatthew Dillon return (0);
1111ac8d5baSMatthew Dillon }
1121ac8d5baSMatthew Dillon
1131ac8d5baSMatthew Dillon /*
1141ac8d5baSMatthew Dillon * Allocate and initialize an SILI port.
1151ac8d5baSMatthew Dillon */
1161ac8d5baSMatthew Dillon int
sili_port_alloc(struct sili_softc * sc,u_int port)1171ac8d5baSMatthew Dillon sili_port_alloc(struct sili_softc *sc, u_int port)
1181ac8d5baSMatthew Dillon {
1191ac8d5baSMatthew Dillon struct sili_port *ap;
1201ac8d5baSMatthew Dillon struct ata_port *at;
1212102f407SMatthew Dillon struct sili_prb *prb;
1221ac8d5baSMatthew Dillon struct sili_ccb *ccb;
1231ac8d5baSMatthew Dillon int rc = ENOMEM;
1241ac8d5baSMatthew Dillon int error;
1251ac8d5baSMatthew Dillon int i;
1261ac8d5baSMatthew Dillon
1271ac8d5baSMatthew Dillon ap = kmalloc(sizeof(*ap), M_DEVBUF, M_WAITOK | M_ZERO);
1286f3b9849SMatthew Dillon ap->ap_err_scratch = kmalloc(512, M_DEVBUF, M_WAITOK | M_ZERO);
1291ac8d5baSMatthew Dillon
1301ac8d5baSMatthew Dillon ksnprintf(ap->ap_name, sizeof(ap->ap_name), "%s%d.%d",
1311ac8d5baSMatthew Dillon device_get_name(sc->sc_dev),
1321ac8d5baSMatthew Dillon device_get_unit(sc->sc_dev),
1331ac8d5baSMatthew Dillon port);
1341ac8d5baSMatthew Dillon sc->sc_ports[port] = ap;
1351ac8d5baSMatthew Dillon
1361ac8d5baSMatthew Dillon /*
1371ac8d5baSMatthew Dillon * Allocate enough so we never have to reallocate, it makes
1381ac8d5baSMatthew Dillon * it easier.
1391ac8d5baSMatthew Dillon *
1401ac8d5baSMatthew Dillon * ap_pmcount will be reduced by the scan if we encounter the
1411ac8d5baSMatthew Dillon * port multiplier port prior to target 15.
1421ac8d5baSMatthew Dillon */
1431ac8d5baSMatthew Dillon if (ap->ap_ata == NULL) {
1441ac8d5baSMatthew Dillon ap->ap_ata = kmalloc(sizeof(*ap->ap_ata) * SILI_MAX_PMPORTS,
1451ac8d5baSMatthew Dillon M_DEVBUF, M_INTWAIT | M_ZERO);
1461ac8d5baSMatthew Dillon for (i = 0; i < SILI_MAX_PMPORTS; ++i) {
1471ac8d5baSMatthew Dillon at = &ap->ap_ata[i];
1481ac8d5baSMatthew Dillon at->at_sili_port = ap;
1491ac8d5baSMatthew Dillon at->at_target = i;
1501ac8d5baSMatthew Dillon at->at_probe = ATA_PROBE_NEED_INIT;
1511ac8d5baSMatthew Dillon at->at_features |= ATA_PORT_F_RESCAN;
1521ac8d5baSMatthew Dillon ksnprintf(at->at_name, sizeof(at->at_name),
1531ac8d5baSMatthew Dillon "%s.%d", ap->ap_name, i);
1541ac8d5baSMatthew Dillon }
1551ac8d5baSMatthew Dillon }
1561ac8d5baSMatthew Dillon if (bus_space_subregion(sc->sc_piot, sc->sc_pioh,
1571ac8d5baSMatthew Dillon SILI_PORT_REGION(port), SILI_PORT_SIZE,
1581ac8d5baSMatthew Dillon &ap->ap_ioh) != 0) {
1591ac8d5baSMatthew Dillon device_printf(sc->sc_dev,
1601ac8d5baSMatthew Dillon "unable to create register window for port %d\n",
1611ac8d5baSMatthew Dillon port);
1621ac8d5baSMatthew Dillon goto freeport;
1631ac8d5baSMatthew Dillon }
1641ac8d5baSMatthew Dillon
1651ac8d5baSMatthew Dillon ap->ap_sc = sc;
1661ac8d5baSMatthew Dillon ap->ap_num = port;
1671ac8d5baSMatthew Dillon ap->ap_probe = ATA_PROBE_NEED_INIT;
1681ac8d5baSMatthew Dillon TAILQ_INIT(&ap->ap_ccb_free);
1691ac8d5baSMatthew Dillon TAILQ_INIT(&ap->ap_ccb_pending);
1701ac8d5baSMatthew Dillon lockinit(&ap->ap_ccb_lock, "silipo", 0, 0);
1711ac8d5baSMatthew Dillon
1721ac8d5baSMatthew Dillon /* Disable port interrupts */
1731ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_INT_DISABLE, SILI_PREG_INT_MASK);
1741ac8d5baSMatthew Dillon
1751ac8d5baSMatthew Dillon /*
1761ac8d5baSMatthew Dillon * Reset the port. This is similar to a Device Reset but far
1771ac8d5baSMatthew Dillon * more invasive. We use Device Reset in our hardreset function.
1781ac8d5baSMatthew Dillon * This function also does the same OOB initialization sequence
1791ac8d5baSMatthew Dillon * that Device Reset does.
1801ac8d5baSMatthew Dillon *
1811ac8d5baSMatthew Dillon * NOTE: SILI_PREG_STATUS_READY will not be asserted unless and until
1821ac8d5baSMatthew Dillon * a device is connected to the port, so we can't use it to
1831ac8d5baSMatthew Dillon * verify that the port exists.
1841ac8d5baSMatthew Dillon */
1851ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_RESET);
1861ac8d5baSMatthew Dillon if (sili_pread(ap, SILI_PREG_STATUS) & SILI_PREG_STATUS_READY) {
1871ac8d5baSMatthew Dillon device_printf(sc->sc_dev,
1881ac8d5baSMatthew Dillon "Port %d will not go into reset\n", port);
1891ac8d5baSMatthew Dillon goto freeport;
1901ac8d5baSMatthew Dillon }
1911ac8d5baSMatthew Dillon sili_os_sleep(10);
1921ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_RESET);
1931ac8d5baSMatthew Dillon
1941ac8d5baSMatthew Dillon /*
1951ac8d5baSMatthew Dillon * Allocate the SGE Table
1961ac8d5baSMatthew Dillon */
1972102f407SMatthew Dillon ap->ap_dmamem_prbs = sili_dmamem_alloc(sc, sc->sc_tag_prbs);
1982102f407SMatthew Dillon if (ap->ap_dmamem_prbs == NULL) {
1991ac8d5baSMatthew Dillon kprintf("%s: NOSGET\n", PORTNAME(ap));
2001ac8d5baSMatthew Dillon goto freeport;
2011ac8d5baSMatthew Dillon }
2021ac8d5baSMatthew Dillon
2031ac8d5baSMatthew Dillon /*
2041ac8d5baSMatthew Dillon * Set up the SGE table base address
2051ac8d5baSMatthew Dillon */
2062102f407SMatthew Dillon ap->ap_prbs = (struct sili_prb *)SILI_DMA_KVA(ap->ap_dmamem_prbs);
2071ac8d5baSMatthew Dillon
2081ac8d5baSMatthew Dillon /*
2091ac8d5baSMatthew Dillon * Allocate a CCB for each command slot
2101ac8d5baSMatthew Dillon */
2111ac8d5baSMatthew Dillon ap->ap_ccbs = kmalloc(sizeof(struct sili_ccb) * sc->sc_ncmds, M_DEVBUF,
2121ac8d5baSMatthew Dillon M_WAITOK | M_ZERO);
2131ac8d5baSMatthew Dillon
2141ac8d5baSMatthew Dillon /*
2151ac8d5baSMatthew Dillon * Most structures are in the port BAR. Assign convenient
2161ac8d5baSMatthew Dillon * pointers in the CCBs
2171ac8d5baSMatthew Dillon */
2181ac8d5baSMatthew Dillon for (i = 0; i < sc->sc_ncmds; i++) {
2191ac8d5baSMatthew Dillon ccb = &ap->ap_ccbs[i];
2201ac8d5baSMatthew Dillon
2211ac8d5baSMatthew Dillon error = bus_dmamap_create(sc->sc_tag_data, BUS_DMA_ALLOCNOW,
2221ac8d5baSMatthew Dillon &ccb->ccb_dmamap);
2231ac8d5baSMatthew Dillon if (error) {
2241ac8d5baSMatthew Dillon device_printf(sc->sc_dev,
2251ac8d5baSMatthew Dillon "unable to create dmamap for port %d "
2261ac8d5baSMatthew Dillon "ccb %d\n", port, i);
2271ac8d5baSMatthew Dillon goto freeport;
2281ac8d5baSMatthew Dillon }
2291ac8d5baSMatthew Dillon
2301ac8d5baSMatthew Dillon /*
2312102f407SMatthew Dillon * WARNING!!! Access to the rfis is only allowed under very
2322102f407SMatthew Dillon * carefully controlled circumstances because it
2332102f407SMatthew Dillon * is located in the LRAM and reading from the
2342102f407SMatthew Dillon * LRAM has hardware issues which can blow the
2352102f407SMatthew Dillon * port up. I kid you not (from Linux, and
2362102f407SMatthew Dillon * verified by testing here).
2371ac8d5baSMatthew Dillon */
2381ac8d5baSMatthew Dillon callout_init(&ccb->ccb_timeout);
2391ac8d5baSMatthew Dillon ccb->ccb_slot = i;
2401ac8d5baSMatthew Dillon ccb->ccb_port = ap;
2412102f407SMatthew Dillon ccb->ccb_prb = &ap->ap_prbs[i];
2422102f407SMatthew Dillon ccb->ccb_prb_paddr = SILI_DMA_DVA(ap->ap_dmamem_prbs) +
2432102f407SMatthew Dillon sizeof(*ccb->ccb_prb) * i;
2441ac8d5baSMatthew Dillon ccb->ccb_xa.fis = &ccb->ccb_prb->prb_h2d;
2452102f407SMatthew Dillon prb = bus_space_kva(ap->ap_sc->sc_iot, ap->ap_ioh,
2462102f407SMatthew Dillon SILI_PREG_LRAM_SLOT(i));
2474383d440SMatthew Dillon ccb->ccb_prb_lram = prb;
2484383d440SMatthew Dillon /*
2494383d440SMatthew Dillon * Point our rfis to host-memory instead of the LRAM PRB.
2504383d440SMatthew Dillon * It will be copied back if ATA_F_AUTOSENSE is set. The
2514383d440SMatthew Dillon * LRAM PRB is buggy.
2524383d440SMatthew Dillon */
2534383d440SMatthew Dillon /*ccb->ccb_xa.rfis = &prb->prb_d2h;*/
2544383d440SMatthew Dillon ccb->ccb_xa.rfis = (void *)ccb->ccb_xa.fis;
2554383d440SMatthew Dillon
2561ac8d5baSMatthew Dillon ccb->ccb_xa.packetcmd = prb_packet(ccb->ccb_prb);
2571ac8d5baSMatthew Dillon ccb->ccb_xa.tag = i;
2581ac8d5baSMatthew Dillon
2591ac8d5baSMatthew Dillon ccb->ccb_xa.state = ATA_S_COMPLETE;
2601ac8d5baSMatthew Dillon
2611ac8d5baSMatthew Dillon /*
2621ac8d5baSMatthew Dillon * Reserve CCB[1] as the error CCB. It doesn't matter
2631ac8d5baSMatthew Dillon * which one we use for the Sili controllers.
2641ac8d5baSMatthew Dillon */
2651ac8d5baSMatthew Dillon if (i == 1)
2661ac8d5baSMatthew Dillon ap->ap_err_ccb = ccb;
2671ac8d5baSMatthew Dillon else
2681ac8d5baSMatthew Dillon sili_put_ccb(ccb);
2691ac8d5baSMatthew Dillon }
270a35ddbb4SMatthew Dillon /*
271a35ddbb4SMatthew Dillon * Do not call sili_port_init() here, the helper thread will
272a35ddbb4SMatthew Dillon * call it for the parallel probe
273a35ddbb4SMatthew Dillon */
2741ac8d5baSMatthew Dillon sili_os_start_port(ap);
2751ac8d5baSMatthew Dillon return(0);
2761ac8d5baSMatthew Dillon freeport:
2771ac8d5baSMatthew Dillon sili_port_free(sc, port);
2781ac8d5baSMatthew Dillon return (rc);
2791ac8d5baSMatthew Dillon }
2801ac8d5baSMatthew Dillon
2811ac8d5baSMatthew Dillon /*
282a35ddbb4SMatthew Dillon * This is called once by the low level attach (from the helper thread)
283a35ddbb4SMatthew Dillon * to get the port state machine rolling, and typically only called again
284a35ddbb4SMatthew Dillon * on a hot-plug insertion event.
2851ac8d5baSMatthew Dillon *
286a35ddbb4SMatthew Dillon * This is called for PM attachments and hot-plug insertion events, and
287a35ddbb4SMatthew Dillon * typically not called again until after an unplug/replug sequence.
2881ac8d5baSMatthew Dillon *
2891ac8d5baSMatthew Dillon * Returns 0 if a device is successfully detected.
2901ac8d5baSMatthew Dillon */
2911ac8d5baSMatthew Dillon int
sili_port_init(struct sili_port * ap)292a35ddbb4SMatthew Dillon sili_port_init(struct sili_port *ap)
2931ac8d5baSMatthew Dillon {
294a35ddbb4SMatthew Dillon /*
295a35ddbb4SMatthew Dillon * Do a very hard reset of the port
296a35ddbb4SMatthew Dillon */
297a35ddbb4SMatthew Dillon sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_RESET);
298a35ddbb4SMatthew Dillon sili_os_sleep(10);
299a35ddbb4SMatthew Dillon sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_RESET);
3001ac8d5baSMatthew Dillon
3011ac8d5baSMatthew Dillon /*
302a35ddbb4SMatthew Dillon * Register initialization
3031ac8d5baSMatthew Dillon */
304a35ddbb4SMatthew Dillon sili_pwrite(ap, SILI_PREG_FIFO_CTL,
305a35ddbb4SMatthew Dillon SILI_PREG_FIFO_CTL_ENCODE(1024, 1024));
306a35ddbb4SMatthew Dillon sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_32BITDMA |
307a35ddbb4SMatthew Dillon SILI_PREG_CTL_PMA);
308a35ddbb4SMatthew Dillon sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_NOAUTOCC);
309a35ddbb4SMatthew Dillon if (ap->ap_sc->sc_flags & SILI_F_SSNTF)
3101ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_SNTF, -1);
3111ac8d5baSMatthew Dillon ap->ap_probe = ATA_PROBE_NEED_HARD_RESET;
3121ac8d5baSMatthew Dillon ap->ap_pmcount = 0;
3131ac8d5baSMatthew Dillon sili_port_interrupt_enable(ap);
314a35ddbb4SMatthew Dillon return (0);
3151ac8d5baSMatthew Dillon }
3161ac8d5baSMatthew Dillon
3171ac8d5baSMatthew Dillon /*
3181ac8d5baSMatthew Dillon * Handle an errored port. This routine is called when the only
3191ac8d5baSMatthew Dillon * commands left on the queue are expired, meaning we can safely
3201ac8d5baSMatthew Dillon * go through a port init to clear its state.
3211ac8d5baSMatthew Dillon *
3221ac8d5baSMatthew Dillon * We complete the expired CCBs and then restart the queue.
3231ac8d5baSMatthew Dillon */
3241ac8d5baSMatthew Dillon static
3251ac8d5baSMatthew Dillon void
sili_port_reinit(struct sili_port * ap)3261ac8d5baSMatthew Dillon sili_port_reinit(struct sili_port *ap)
3271ac8d5baSMatthew Dillon {
3281ac8d5baSMatthew Dillon struct sili_ccb *ccb;
3291ac8d5baSMatthew Dillon struct ata_port *at;
3301ac8d5baSMatthew Dillon int slot;
3311ac8d5baSMatthew Dillon int target;
3321ac8d5baSMatthew Dillon u_int32_t data;
3331ac8d5baSMatthew Dillon
334a35ddbb4SMatthew Dillon if (bootverbose || 1) {
335132408ffSMatthew Dillon kprintf("%s: reiniting port after error reent=%d "
336132408ffSMatthew Dillon "expired=%08x\n",
337187cac04SMatthew Dillon PORTNAME(ap),
338187cac04SMatthew Dillon (ap->ap_flags & AP_F_REINIT_ACTIVE),
339187cac04SMatthew Dillon ap->ap_expired);
340132408ffSMatthew Dillon }
3411ac8d5baSMatthew Dillon
3421ac8d5baSMatthew Dillon /*
3431ac8d5baSMatthew Dillon * Clear port resume, clear bits 16:13 in the port device status
3441ac8d5baSMatthew Dillon * register. This is from the data sheet.
3451ac8d5baSMatthew Dillon *
3461ac8d5baSMatthew Dillon * Data sheet does not specify a delay but it seems prudent.
3471ac8d5baSMatthew Dillon */
3481ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_RESUME);
3491ac8d5baSMatthew Dillon sili_os_sleep(10);
3501ac8d5baSMatthew Dillon for (target = 0; target < SILI_MAX_PMPORTS; ++target) {
3511ac8d5baSMatthew Dillon data = sili_pread(ap, SILI_PREG_PM_STATUS(target));
3521ac8d5baSMatthew Dillon data &= ~(SILI_PREG_PM_STATUS_SERVICE |
3531ac8d5baSMatthew Dillon SILI_PREG_PM_STATUS_LEGACY |
3541ac8d5baSMatthew Dillon SILI_PREG_PM_STATUS_NATIVE |
3551ac8d5baSMatthew Dillon SILI_PREG_PM_STATUS_VBSY);
3561ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_PM_STATUS(target), data);
3571ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_PM_QACTIVE(target), 0);
3581ac8d5baSMatthew Dillon }
3591ac8d5baSMatthew Dillon
3601ac8d5baSMatthew Dillon /*
3611ac8d5baSMatthew Dillon * Issue a Port Initialize and wait for it to clear. This flushes
3621ac8d5baSMatthew Dillon * commands but does not reset the port. Then wait for port ready.
3631ac8d5baSMatthew Dillon */
3641ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_INIT);
365a35ddbb4SMatthew Dillon if (sili_pwait_clr_to(ap, 5000, SILI_PREG_STATUS, SILI_PREG_CTL_INIT)) {
3661ac8d5baSMatthew Dillon kprintf("%s: Unable to reinit, port failed\n",
3671ac8d5baSMatthew Dillon PORTNAME(ap));
3681ac8d5baSMatthew Dillon }
3691ac8d5baSMatthew Dillon if (sili_pwait_set(ap, SILI_PREG_STATUS, SILI_PREG_STATUS_READY)) {
3701ac8d5baSMatthew Dillon kprintf("%s: Unable to reinit, port will not come ready\n",
3711ac8d5baSMatthew Dillon PORTNAME(ap));
3721ac8d5baSMatthew Dillon }
3731ac8d5baSMatthew Dillon
3741ac8d5baSMatthew Dillon /*
375132408ffSMatthew Dillon * If reentrant, stop here. Otherwise the state for the original
376132408ffSMatthew Dillon * ahci_port_reinit() will get ripped out from under it.
377132408ffSMatthew Dillon */
378187cac04SMatthew Dillon if (ap->ap_flags & AP_F_REINIT_ACTIVE)
379132408ffSMatthew Dillon return;
380187cac04SMatthew Dillon ap->ap_flags |= AP_F_REINIT_ACTIVE;
381132408ffSMatthew Dillon
382132408ffSMatthew Dillon /*
3831ac8d5baSMatthew Dillon * Read the LOG ERROR page for targets that returned a specific
3841ac8d5baSMatthew Dillon * D2H FIS with ERR set.
385187cac04SMatthew Dillon *
386187cac04SMatthew Dillon * Don't bother if we are already using the error CCB.
3871ac8d5baSMatthew Dillon */
388187cac04SMatthew Dillon if ((ap->ap_flags & AP_F_ERR_CCB_RESERVED) == 0) {
3891ac8d5baSMatthew Dillon for (target = 0; target < SILI_MAX_PMPORTS; ++target) {
3901ac8d5baSMatthew Dillon at = &ap->ap_ata[target];
3911ac8d5baSMatthew Dillon if (at->at_features & ATA_PORT_F_READLOG) {
3921ac8d5baSMatthew Dillon at->at_features &= ~ATA_PORT_F_READLOG;
3931ac8d5baSMatthew Dillon sili_port_read_ncq_error(ap, target);
3941ac8d5baSMatthew Dillon }
3951ac8d5baSMatthew Dillon }
396187cac04SMatthew Dillon }
3971ac8d5baSMatthew Dillon
3981ac8d5baSMatthew Dillon /*
3991ac8d5baSMatthew Dillon * Finally clean out the expired commands, we've probed the error
4001ac8d5baSMatthew Dillon * status (or hopefully probed the error status). Well, ok,
4011ac8d5baSMatthew Dillon * we probably didn't XXX.
4021ac8d5baSMatthew Dillon */
4031ac8d5baSMatthew Dillon while (ap->ap_expired) {
4041ac8d5baSMatthew Dillon slot = ffs(ap->ap_expired) - 1;
4051ac8d5baSMatthew Dillon ap->ap_expired &= ~(1 << slot);
4061ac8d5baSMatthew Dillon KKASSERT(ap->ap_active & (1 << slot));
4071ac8d5baSMatthew Dillon ap->ap_active &= ~(1 << slot);
4081ac8d5baSMatthew Dillon --ap->ap_active_cnt;
4091ac8d5baSMatthew Dillon ccb = &ap->ap_ccbs[slot];
4101ac8d5baSMatthew Dillon ccb->ccb_xa.state = ATA_S_TIMEOUT;
4111ac8d5baSMatthew Dillon ccb->ccb_done(ccb);
4121ac8d5baSMatthew Dillon ccb->ccb_xa.complete(&ccb->ccb_xa);
4131ac8d5baSMatthew Dillon }
414187cac04SMatthew Dillon ap->ap_flags &= ~AP_F_REINIT_ACTIVE;
4151ac8d5baSMatthew Dillon
4161ac8d5baSMatthew Dillon /*
4171ac8d5baSMatthew Dillon * Wow. All done. We can get the port moving again.
4181ac8d5baSMatthew Dillon */
419132408ffSMatthew Dillon if (ap->ap_probe == ATA_PROBE_FAILED) {
4201ac8d5baSMatthew Dillon kprintf("%s: reinit failed, port is dead\n", PORTNAME(ap));
4211ac8d5baSMatthew Dillon while ((ccb = TAILQ_FIRST(&ap->ap_ccb_pending)) != NULL) {
4221ac8d5baSMatthew Dillon TAILQ_REMOVE(&ap->ap_ccb_pending, ccb, ccb_entry);
4231ac8d5baSMatthew Dillon ccb->ccb_xa.flags &= ~ATA_F_TIMEOUT_DESIRED;
4241ac8d5baSMatthew Dillon ccb->ccb_xa.state = ATA_S_TIMEOUT;
4251ac8d5baSMatthew Dillon ccb->ccb_done(ccb);
4261ac8d5baSMatthew Dillon ccb->ccb_xa.complete(&ccb->ccb_xa);
4271ac8d5baSMatthew Dillon }
4281ac8d5baSMatthew Dillon } else {
4291ac8d5baSMatthew Dillon sili_issue_pending_commands(ap, NULL);
4301ac8d5baSMatthew Dillon }
4311ac8d5baSMatthew Dillon }
4321ac8d5baSMatthew Dillon
4331ac8d5baSMatthew Dillon /*
4341ac8d5baSMatthew Dillon * Enable or re-enable interrupts on a port.
4351ac8d5baSMatthew Dillon *
4361ac8d5baSMatthew Dillon * This routine is called from the port initialization code or from the
4371ac8d5baSMatthew Dillon * helper thread as the real interrupt may be forced to turn off certain
4381ac8d5baSMatthew Dillon * interrupt sources.
4391ac8d5baSMatthew Dillon */
4401ac8d5baSMatthew Dillon void
sili_port_interrupt_enable(struct sili_port * ap)4411ac8d5baSMatthew Dillon sili_port_interrupt_enable(struct sili_port *ap)
4421ac8d5baSMatthew Dillon {
4431ac8d5baSMatthew Dillon u_int32_t data;
4441ac8d5baSMatthew Dillon
4451ac8d5baSMatthew Dillon data = SILI_PREG_INT_CCOMPLETE | SILI_PREG_INT_CERROR |
4461ac8d5baSMatthew Dillon SILI_PREG_INT_PHYRDYCHG | SILI_PREG_INT_DEVEXCHG |
4471ac8d5baSMatthew Dillon SILI_PREG_INT_DECODE | SILI_PREG_INT_CRC |
4481ac8d5baSMatthew Dillon SILI_PREG_INT_HANDSHK | SILI_PREG_INT_PMCHANGE;
4491ac8d5baSMatthew Dillon if (ap->ap_sc->sc_flags & SILI_F_SSNTF)
4501ac8d5baSMatthew Dillon data |= SILI_PREG_INT_SDB;
4511ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_INT_ENABLE, data);
4521ac8d5baSMatthew Dillon }
4531ac8d5baSMatthew Dillon
4541ac8d5baSMatthew Dillon void
sili_port_interrupt_redisable(struct sili_port * ap)4551ac8d5baSMatthew Dillon sili_port_interrupt_redisable(struct sili_port *ap)
4561ac8d5baSMatthew Dillon {
4571ac8d5baSMatthew Dillon u_int32_t data;
4581ac8d5baSMatthew Dillon
4591ac8d5baSMatthew Dillon data = sili_read(ap->ap_sc, SILI_REG_GCTL);
4601ac8d5baSMatthew Dillon data &= SILI_REG_GINT_PORTMASK;
4611ac8d5baSMatthew Dillon data &= ~(1 << ap->ap_num);
4621ac8d5baSMatthew Dillon sili_write(ap->ap_sc, SILI_REG_GCTL, data);
4631ac8d5baSMatthew Dillon }
4641ac8d5baSMatthew Dillon
4651ac8d5baSMatthew Dillon void
sili_port_interrupt_reenable(struct sili_port * ap)4661ac8d5baSMatthew Dillon sili_port_interrupt_reenable(struct sili_port *ap)
4671ac8d5baSMatthew Dillon {
4681ac8d5baSMatthew Dillon u_int32_t data;
4691ac8d5baSMatthew Dillon
4701ac8d5baSMatthew Dillon data = sili_read(ap->ap_sc, SILI_REG_GCTL);
4711ac8d5baSMatthew Dillon data &= SILI_REG_GINT_PORTMASK;
4721ac8d5baSMatthew Dillon data |= (1 << ap->ap_num);
4731ac8d5baSMatthew Dillon sili_write(ap->ap_sc, SILI_REG_GCTL, data);
4741ac8d5baSMatthew Dillon }
4751ac8d5baSMatthew Dillon
4761ac8d5baSMatthew Dillon /*
4771ac8d5baSMatthew Dillon * Run the port / target state machine from a main context.
4781ac8d5baSMatthew Dillon *
4791ac8d5baSMatthew Dillon * The state machine for the port is always run.
4801ac8d5baSMatthew Dillon *
4811ac8d5baSMatthew Dillon * If atx is non-NULL run the state machine for a particular target.
4821ac8d5baSMatthew Dillon * If atx is NULL run the state machine for all targets.
4831ac8d5baSMatthew Dillon */
4841ac8d5baSMatthew Dillon void
sili_port_state_machine(struct sili_port * ap,int initial)4851ac8d5baSMatthew Dillon sili_port_state_machine(struct sili_port *ap, int initial)
4861ac8d5baSMatthew Dillon {
4871ac8d5baSMatthew Dillon struct ata_port *at;
4881ac8d5baSMatthew Dillon u_int32_t data;
4891ac8d5baSMatthew Dillon int target;
4901ac8d5baSMatthew Dillon int didsleep;
4911ac8d5baSMatthew Dillon int loop;
4921ac8d5baSMatthew Dillon
4931ac8d5baSMatthew Dillon /*
4941ac8d5baSMatthew Dillon * State machine for port. Note that CAM is not yet associated
4951ac8d5baSMatthew Dillon * during the initial parallel probe and the port's probe state
4961ac8d5baSMatthew Dillon * will not get past ATA_PROBE_NEED_IDENT.
4971ac8d5baSMatthew Dillon */
4981ac8d5baSMatthew Dillon {
4991ac8d5baSMatthew Dillon if (initial == 0 && ap->ap_probe <= ATA_PROBE_NEED_HARD_RESET) {
5003c6bae9dSMatthew Dillon kprintf("%s: Waiting 7 seconds on insertion\n",
5011ac8d5baSMatthew Dillon PORTNAME(ap));
5023c6bae9dSMatthew Dillon sili_os_sleep(7000);
5031ac8d5baSMatthew Dillon initial = 1;
5041ac8d5baSMatthew Dillon }
5051ac8d5baSMatthew Dillon if (ap->ap_probe == ATA_PROBE_NEED_INIT)
506a35ddbb4SMatthew Dillon sili_port_init(ap);
5071ac8d5baSMatthew Dillon if (ap->ap_probe == ATA_PROBE_NEED_HARD_RESET)
5081ac8d5baSMatthew Dillon sili_port_reset(ap, NULL, 1);
5091ac8d5baSMatthew Dillon if (ap->ap_probe == ATA_PROBE_NEED_SOFT_RESET)
5101ac8d5baSMatthew Dillon sili_port_reset(ap, NULL, 0);
5111ac8d5baSMatthew Dillon if (ap->ap_probe == ATA_PROBE_NEED_IDENT)
5121ac8d5baSMatthew Dillon sili_cam_probe(ap, NULL);
5131ac8d5baSMatthew Dillon }
5141ac8d5baSMatthew Dillon if (ap->ap_type != ATA_PORT_T_PM) {
5151ac8d5baSMatthew Dillon if (ap->ap_probe == ATA_PROBE_FAILED) {
5161ac8d5baSMatthew Dillon sili_cam_changed(ap, NULL, 0);
5171ac8d5baSMatthew Dillon } else if (ap->ap_probe >= ATA_PROBE_NEED_IDENT) {
5181ac8d5baSMatthew Dillon sili_cam_changed(ap, NULL, 1);
5191ac8d5baSMatthew Dillon }
5201ac8d5baSMatthew Dillon return;
5211ac8d5baSMatthew Dillon }
5221ac8d5baSMatthew Dillon
5231ac8d5baSMatthew Dillon /*
5241ac8d5baSMatthew Dillon * Port Multiplier state machine.
5251ac8d5baSMatthew Dillon *
5261ac8d5baSMatthew Dillon * Get a mask of changed targets and combine with any runnable
5271ac8d5baSMatthew Dillon * states already present.
5281ac8d5baSMatthew Dillon */
5291ac8d5baSMatthew Dillon for (loop = 0; ;++loop) {
5301ac8d5baSMatthew Dillon if (sili_pm_read(ap, 15, SATA_PMREG_EINFO, &data)) {
5311ac8d5baSMatthew Dillon kprintf("%s: PM unable to read hot-plug bitmap\n",
5321ac8d5baSMatthew Dillon PORTNAME(ap));
5331ac8d5baSMatthew Dillon break;
5341ac8d5baSMatthew Dillon }
5351ac8d5baSMatthew Dillon
5361ac8d5baSMatthew Dillon /*
5371ac8d5baSMatthew Dillon * Do at least one loop, then stop if no more state changes
5381ac8d5baSMatthew Dillon * have occured. The PM might not generate a new
5391ac8d5baSMatthew Dillon * notification until we clear the entire bitmap.
5401ac8d5baSMatthew Dillon */
5411ac8d5baSMatthew Dillon if (loop && data == 0)
5421ac8d5baSMatthew Dillon break;
5431ac8d5baSMatthew Dillon
5441ac8d5baSMatthew Dillon /*
5451ac8d5baSMatthew Dillon * New devices showing up in the bitmap require some spin-up
5461ac8d5baSMatthew Dillon * time before we start probing them. Reset didsleep. The
5471ac8d5baSMatthew Dillon * first new device we detect will sleep before probing.
5481ac8d5baSMatthew Dillon *
5491ac8d5baSMatthew Dillon * This only applies to devices whos change bit is set in
5501ac8d5baSMatthew Dillon * the data, and does not apply to the initial boot-time
5511ac8d5baSMatthew Dillon * probe.
5521ac8d5baSMatthew Dillon */
5531ac8d5baSMatthew Dillon didsleep = 0;
5541ac8d5baSMatthew Dillon
5551ac8d5baSMatthew Dillon for (target = 0; target < ap->ap_pmcount; ++target) {
5561ac8d5baSMatthew Dillon at = &ap->ap_ata[target];
5571ac8d5baSMatthew Dillon
5581ac8d5baSMatthew Dillon /*
5591ac8d5baSMatthew Dillon * Check the target state for targets behind the PM
5601ac8d5baSMatthew Dillon * which have changed state. This will adjust
5611ac8d5baSMatthew Dillon * at_probe and set ATA_PORT_F_RESCAN
5621ac8d5baSMatthew Dillon *
563*2fa886e9SMatthew Dillon * We want to wait at least 5 seconds before probing
5641ac8d5baSMatthew Dillon * a newly inserted device. If the check status
5651ac8d5baSMatthew Dillon * indicates a device is present and in need of a
5661ac8d5baSMatthew Dillon * hard reset, we make sure we have slept before
5671ac8d5baSMatthew Dillon * continuing.
5681ac8d5baSMatthew Dillon *
5691ac8d5baSMatthew Dillon * We also need to wait at least 1 second for the
5701ac8d5baSMatthew Dillon * PHY state to change after insertion, if we
571*2fa886e9SMatthew Dillon * haven't already waited the 5 seconds.
5721ac8d5baSMatthew Dillon *
5731ac8d5baSMatthew Dillon * NOTE: When pm_check_good finds a good port it
5741ac8d5baSMatthew Dillon * typically starts us in probe state
5751ac8d5baSMatthew Dillon * NEED_HARD_RESET rather than INIT.
5761ac8d5baSMatthew Dillon */
5771ac8d5baSMatthew Dillon if (data & (1 << target)) {
5781ac8d5baSMatthew Dillon if (initial == 0 && didsleep == 0)
5791ac8d5baSMatthew Dillon sili_os_sleep(1000);
5801ac8d5baSMatthew Dillon sili_pm_check_good(ap, target);
5811ac8d5baSMatthew Dillon if (initial == 0 && didsleep == 0 &&
5821ac8d5baSMatthew Dillon at->at_probe <= ATA_PROBE_NEED_HARD_RESET
5831ac8d5baSMatthew Dillon ) {
5841ac8d5baSMatthew Dillon didsleep = 1;
585*2fa886e9SMatthew Dillon kprintf("%s: Waiting 5 seconds on insertion\n", PORTNAME(ap));
586*2fa886e9SMatthew Dillon sili_os_sleep(5000);
5871ac8d5baSMatthew Dillon }
5881ac8d5baSMatthew Dillon }
5891ac8d5baSMatthew Dillon
5901ac8d5baSMatthew Dillon /*
5911ac8d5baSMatthew Dillon * Report hot-plug events before the probe state
5921ac8d5baSMatthew Dillon * really gets hot. Only actual events are reported
5931ac8d5baSMatthew Dillon * here to reduce spew.
5941ac8d5baSMatthew Dillon */
5951ac8d5baSMatthew Dillon if (data & (1 << target)) {
5961ac8d5baSMatthew Dillon kprintf("%s: HOTPLUG (PM) - ", ATANAME(ap, at));
5971ac8d5baSMatthew Dillon switch(at->at_probe) {
5981ac8d5baSMatthew Dillon case ATA_PROBE_NEED_INIT:
5991ac8d5baSMatthew Dillon case ATA_PROBE_NEED_HARD_RESET:
6001ac8d5baSMatthew Dillon kprintf("Device inserted\n");
6011ac8d5baSMatthew Dillon break;
6021ac8d5baSMatthew Dillon case ATA_PROBE_FAILED:
6031ac8d5baSMatthew Dillon kprintf("Device removed\n");
6041ac8d5baSMatthew Dillon break;
6051ac8d5baSMatthew Dillon default:
6061ac8d5baSMatthew Dillon kprintf("Device probe in progress\n");
6071ac8d5baSMatthew Dillon break;
6081ac8d5baSMatthew Dillon }
6091ac8d5baSMatthew Dillon }
6101ac8d5baSMatthew Dillon
6111ac8d5baSMatthew Dillon /*
6121ac8d5baSMatthew Dillon * Run through the state machine as necessary if
6131ac8d5baSMatthew Dillon * the port is not marked failed.
6141ac8d5baSMatthew Dillon *
6151ac8d5baSMatthew Dillon * The state machine may stop at NEED_IDENT if
6161ac8d5baSMatthew Dillon * CAM is not yet attached.
6171ac8d5baSMatthew Dillon *
6181ac8d5baSMatthew Dillon * Acquire exclusive access to the port while we
6191ac8d5baSMatthew Dillon * are doing this. This prevents command-completion
6201ac8d5baSMatthew Dillon * from queueing commands for non-polled targets
6211ac8d5baSMatthew Dillon * inbetween our probe steps. We need to do this
6221ac8d5baSMatthew Dillon * because the reset probes can generate severe PHY
6231ac8d5baSMatthew Dillon * and protocol errors and soft-brick the port.
6241ac8d5baSMatthew Dillon */
6251ac8d5baSMatthew Dillon if (at->at_probe != ATA_PROBE_FAILED &&
6261ac8d5baSMatthew Dillon at->at_probe != ATA_PROBE_GOOD) {
6271ac8d5baSMatthew Dillon if (at->at_probe == ATA_PROBE_NEED_INIT)
628a35ddbb4SMatthew Dillon sili_pm_port_init(ap, at);
6291ac8d5baSMatthew Dillon if (at->at_probe == ATA_PROBE_NEED_HARD_RESET)
6301ac8d5baSMatthew Dillon sili_port_reset(ap, at, 1);
6311ac8d5baSMatthew Dillon if (at->at_probe == ATA_PROBE_NEED_SOFT_RESET)
6321ac8d5baSMatthew Dillon sili_port_reset(ap, at, 0);
6331ac8d5baSMatthew Dillon if (at->at_probe == ATA_PROBE_NEED_IDENT)
6341ac8d5baSMatthew Dillon sili_cam_probe(ap, at);
6351ac8d5baSMatthew Dillon }
6361ac8d5baSMatthew Dillon
6371ac8d5baSMatthew Dillon /*
6381ac8d5baSMatthew Dillon * Add or remove from CAM
6391ac8d5baSMatthew Dillon */
6401ac8d5baSMatthew Dillon if (at->at_features & ATA_PORT_F_RESCAN) {
6411ac8d5baSMatthew Dillon at->at_features &= ~ATA_PORT_F_RESCAN;
6421ac8d5baSMatthew Dillon if (at->at_probe == ATA_PROBE_FAILED) {
6431ac8d5baSMatthew Dillon sili_cam_changed(ap, at, 0);
6441ac8d5baSMatthew Dillon } else if (at->at_probe >= ATA_PROBE_NEED_IDENT) {
6451ac8d5baSMatthew Dillon sili_cam_changed(ap, at, 1);
6461ac8d5baSMatthew Dillon }
6471ac8d5baSMatthew Dillon }
6481ac8d5baSMatthew Dillon data &= ~(1 << target);
6491ac8d5baSMatthew Dillon }
6501ac8d5baSMatthew Dillon if (data) {
6511ac8d5baSMatthew Dillon kprintf("%s: WARNING (PM): extra bits set in "
6521ac8d5baSMatthew Dillon "EINFO: %08x\n", PORTNAME(ap), data);
6531ac8d5baSMatthew Dillon while (target < SILI_MAX_PMPORTS) {
6541ac8d5baSMatthew Dillon sili_pm_check_good(ap, target);
6551ac8d5baSMatthew Dillon ++target;
6561ac8d5baSMatthew Dillon }
6571ac8d5baSMatthew Dillon }
6581ac8d5baSMatthew Dillon }
6591ac8d5baSMatthew Dillon }
6601ac8d5baSMatthew Dillon
6611ac8d5baSMatthew Dillon /*
6621ac8d5baSMatthew Dillon * De-initialize and detach a port.
6631ac8d5baSMatthew Dillon */
6641ac8d5baSMatthew Dillon void
sili_port_free(struct sili_softc * sc,u_int port)6651ac8d5baSMatthew Dillon sili_port_free(struct sili_softc *sc, u_int port)
6661ac8d5baSMatthew Dillon {
6671ac8d5baSMatthew Dillon struct sili_port *ap = sc->sc_ports[port];
6681ac8d5baSMatthew Dillon struct sili_ccb *ccb;
6691ac8d5baSMatthew Dillon
6701ac8d5baSMatthew Dillon /*
6711ac8d5baSMatthew Dillon * Ensure port is disabled and its interrupts are all flushed.
6721ac8d5baSMatthew Dillon */
6731ac8d5baSMatthew Dillon if (ap->ap_sc) {
6741ac8d5baSMatthew Dillon sili_os_stop_port(ap);
6751ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_INT_DISABLE, SILI_PREG_INT_MASK);
6761ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_RESET);
6771ac8d5baSMatthew Dillon sili_write(ap->ap_sc, SILI_REG_GCTL,
6781ac8d5baSMatthew Dillon sili_read(ap->ap_sc, SILI_REG_GCTL) &
6791ac8d5baSMatthew Dillon ~SILI_REG_GINT_PORTST(ap->ap_num));
6801ac8d5baSMatthew Dillon }
6811ac8d5baSMatthew Dillon
6821ac8d5baSMatthew Dillon if (ap->ap_ccbs) {
6831ac8d5baSMatthew Dillon while ((ccb = sili_get_ccb(ap)) != NULL) {
6841ac8d5baSMatthew Dillon if (ccb->ccb_dmamap) {
6851ac8d5baSMatthew Dillon bus_dmamap_destroy(sc->sc_tag_data,
6861ac8d5baSMatthew Dillon ccb->ccb_dmamap);
6871ac8d5baSMatthew Dillon ccb->ccb_dmamap = NULL;
6881ac8d5baSMatthew Dillon }
6891ac8d5baSMatthew Dillon }
6901ac8d5baSMatthew Dillon if ((ccb = ap->ap_err_ccb) != NULL) {
6911ac8d5baSMatthew Dillon if (ccb->ccb_dmamap) {
6921ac8d5baSMatthew Dillon bus_dmamap_destroy(sc->sc_tag_data,
6931ac8d5baSMatthew Dillon ccb->ccb_dmamap);
6941ac8d5baSMatthew Dillon ccb->ccb_dmamap = NULL;
6951ac8d5baSMatthew Dillon }
6961ac8d5baSMatthew Dillon ap->ap_err_ccb = NULL;
6971ac8d5baSMatthew Dillon }
6981ac8d5baSMatthew Dillon kfree(ap->ap_ccbs, M_DEVBUF);
6991ac8d5baSMatthew Dillon ap->ap_ccbs = NULL;
7001ac8d5baSMatthew Dillon }
7011ac8d5baSMatthew Dillon
7022102f407SMatthew Dillon if (ap->ap_dmamem_prbs) {
7032102f407SMatthew Dillon sili_dmamem_free(sc, ap->ap_dmamem_prbs);
7042102f407SMatthew Dillon ap->ap_dmamem_prbs = NULL;
7051ac8d5baSMatthew Dillon }
7061ac8d5baSMatthew Dillon if (ap->ap_ata) {
7071ac8d5baSMatthew Dillon kfree(ap->ap_ata, M_DEVBUF);
7081ac8d5baSMatthew Dillon ap->ap_ata = NULL;
7091ac8d5baSMatthew Dillon }
7106f3b9849SMatthew Dillon if (ap->ap_err_scratch) {
7116f3b9849SMatthew Dillon kfree(ap->ap_err_scratch, M_DEVBUF);
7126f3b9849SMatthew Dillon ap->ap_err_scratch = NULL;
7136f3b9849SMatthew Dillon }
7141ac8d5baSMatthew Dillon
7151ac8d5baSMatthew Dillon /* bus_space(9) says we dont free the subregions handle */
7161ac8d5baSMatthew Dillon
7171ac8d5baSMatthew Dillon kfree(ap, M_DEVBUF);
7181ac8d5baSMatthew Dillon sc->sc_ports[port] = NULL;
7191ac8d5baSMatthew Dillon }
7201ac8d5baSMatthew Dillon
7211ac8d5baSMatthew Dillon /*
7221ac8d5baSMatthew Dillon * Reset a port.
7231ac8d5baSMatthew Dillon *
7241ac8d5baSMatthew Dillon * If hard is 0 perform a softreset of the port.
7251ac8d5baSMatthew Dillon * If hard is 1 perform a hard reset of the port.
7261ac8d5baSMatthew Dillon * If hard is 2 perform a hard reset of the port and cycle the phy.
7271ac8d5baSMatthew Dillon *
7281ac8d5baSMatthew Dillon * If at is non-NULL an indirect port via a port-multiplier is being
7291ac8d5baSMatthew Dillon * reset, otherwise a direct port is being reset.
7301ac8d5baSMatthew Dillon *
7311ac8d5baSMatthew Dillon * NOTE: Indirect ports can only be soft-reset.
7321ac8d5baSMatthew Dillon */
7331ac8d5baSMatthew Dillon int
sili_port_reset(struct sili_port * ap,struct ata_port * at,int hard)7341ac8d5baSMatthew Dillon sili_port_reset(struct sili_port *ap, struct ata_port *at, int hard)
7351ac8d5baSMatthew Dillon {
7361ac8d5baSMatthew Dillon int rc;
7371ac8d5baSMatthew Dillon
7381ac8d5baSMatthew Dillon if (hard) {
7391ac8d5baSMatthew Dillon if (at)
7401ac8d5baSMatthew Dillon rc = sili_pm_hardreset(ap, at->at_target, hard);
7411ac8d5baSMatthew Dillon else
742a35ddbb4SMatthew Dillon rc = sili_port_hardreset(ap);
7431ac8d5baSMatthew Dillon } else {
7441ac8d5baSMatthew Dillon if (at)
7451ac8d5baSMatthew Dillon rc = sili_pm_softreset(ap, at->at_target);
7461ac8d5baSMatthew Dillon else
7471ac8d5baSMatthew Dillon rc = sili_port_softreset(ap);
7481ac8d5baSMatthew Dillon }
7491ac8d5baSMatthew Dillon return(rc);
7501ac8d5baSMatthew Dillon }
7511ac8d5baSMatthew Dillon
7521ac8d5baSMatthew Dillon /*
7531ac8d5baSMatthew Dillon * SILI soft reset, Section 10.4.1
7541ac8d5baSMatthew Dillon *
7551ac8d5baSMatthew Dillon * (at) will be NULL when soft-resetting a directly-attached device, and
7561ac8d5baSMatthew Dillon * non-NULL when soft-resetting a device through a port multiplier.
7571ac8d5baSMatthew Dillon *
7581ac8d5baSMatthew Dillon * This function keeps port communications intact and attempts to generate
7591ac8d5baSMatthew Dillon * a reset to the connected device using device commands.
7601ac8d5baSMatthew Dillon */
7611ac8d5baSMatthew Dillon int
sili_port_softreset(struct sili_port * ap)7621ac8d5baSMatthew Dillon sili_port_softreset(struct sili_port *ap)
7631ac8d5baSMatthew Dillon {
7641ac8d5baSMatthew Dillon struct sili_ccb *ccb = NULL;
7651ac8d5baSMatthew Dillon struct sili_prb *prb;
7661ac8d5baSMatthew Dillon int error;
7671ac8d5baSMatthew Dillon u_int32_t sig;
7681ac8d5baSMatthew Dillon
7691ac8d5baSMatthew Dillon error = EIO;
7701ac8d5baSMatthew Dillon
771a35ddbb4SMatthew Dillon if (bootverbose)
7721ac8d5baSMatthew Dillon kprintf("%s: START SOFTRESET\n", PORTNAME(ap));
7731ac8d5baSMatthew Dillon
7741ac8d5baSMatthew Dillon crit_enter();
7751ac8d5baSMatthew Dillon ap->ap_state = AP_S_NORMAL;
7761ac8d5baSMatthew Dillon
7771ac8d5baSMatthew Dillon /*
7781ac8d5baSMatthew Dillon * Prep the special soft-reset SII command.
7791ac8d5baSMatthew Dillon */
7801ac8d5baSMatthew Dillon ccb = sili_get_err_ccb(ap);
7811ac8d5baSMatthew Dillon ccb->ccb_done = sili_empty_done;
7824383d440SMatthew Dillon ccb->ccb_xa.flags = ATA_F_POLL | ATA_F_AUTOSENSE | ATA_F_EXCLUSIVE;
7831ac8d5baSMatthew Dillon ccb->ccb_xa.complete = sili_dummy_done;
7841ac8d5baSMatthew Dillon ccb->ccb_xa.at = NULL;
7851ac8d5baSMatthew Dillon
7861ac8d5baSMatthew Dillon prb = ccb->ccb_prb;
7871ac8d5baSMatthew Dillon bzero(&prb->prb_h2d, sizeof(prb->prb_h2d));
7881ac8d5baSMatthew Dillon prb->prb_h2d.flags = 0;
7891ac8d5baSMatthew Dillon prb->prb_control = SILI_PRB_CTRL_SOFTRESET;
7901ac8d5baSMatthew Dillon prb->prb_override = 0;
791a35ddbb4SMatthew Dillon prb->prb_xfer_count = 0;
7921ac8d5baSMatthew Dillon
7931ac8d5baSMatthew Dillon ccb->ccb_xa.state = ATA_S_PENDING;
7941ac8d5baSMatthew Dillon
795a35ddbb4SMatthew Dillon /*
796a35ddbb4SMatthew Dillon * NOTE: Must use sili_quick_timeout() because we hold the err_ccb
797a35ddbb4SMatthew Dillon */
7981ac8d5baSMatthew Dillon if (sili_poll(ccb, 8000, sili_quick_timeout) != ATA_S_COMPLETE) {
7991ac8d5baSMatthew Dillon kprintf("%s: First FIS failed\n", PORTNAME(ap));
8001ac8d5baSMatthew Dillon goto err;
8011ac8d5baSMatthew Dillon }
8021ac8d5baSMatthew Dillon
8031ac8d5baSMatthew Dillon sig = (prb->prb_d2h.lba_high << 24) |
8041ac8d5baSMatthew Dillon (prb->prb_d2h.lba_mid << 16) |
8051ac8d5baSMatthew Dillon (prb->prb_d2h.lba_low << 8) |
8061ac8d5baSMatthew Dillon (prb->prb_d2h.sector_count);
807a35ddbb4SMatthew Dillon if (bootverbose)
8081ac8d5baSMatthew Dillon kprintf("%s: SOFTRESET SIGNATURE %08x\n", PORTNAME(ap), sig);
8091ac8d5baSMatthew Dillon
8101ac8d5baSMatthew Dillon /*
8111ac8d5baSMatthew Dillon * If the softreset is trying to clear a BSY condition after a
8121ac8d5baSMatthew Dillon * normal portreset we assign the port type.
8131ac8d5baSMatthew Dillon *
8141ac8d5baSMatthew Dillon * If the softreset is being run first as part of the ccb error
8151ac8d5baSMatthew Dillon * processing code then report if the device signature changed
8161ac8d5baSMatthew Dillon * unexpectedly.
8171ac8d5baSMatthew Dillon */
8181ac8d5baSMatthew Dillon if (ap->ap_type == ATA_PORT_T_NONE) {
8191ac8d5baSMatthew Dillon ap->ap_type = sili_port_signature(ap, NULL, sig);
8201ac8d5baSMatthew Dillon } else {
8211ac8d5baSMatthew Dillon if (sili_port_signature(ap, NULL, sig) != ap->ap_type) {
8221ac8d5baSMatthew Dillon kprintf("%s: device signature unexpectedly "
8231ac8d5baSMatthew Dillon "changed\n", PORTNAME(ap));
8241ac8d5baSMatthew Dillon error = EBUSY; /* XXX */
8251ac8d5baSMatthew Dillon }
8261ac8d5baSMatthew Dillon }
8271ac8d5baSMatthew Dillon error = 0;
8281ac8d5baSMatthew Dillon err:
8291ac8d5baSMatthew Dillon if (ccb != NULL) {
8301ac8d5baSMatthew Dillon sili_put_err_ccb(ccb);
8311ac8d5baSMatthew Dillon }
8321ac8d5baSMatthew Dillon
8331ac8d5baSMatthew Dillon /*
8341ac8d5baSMatthew Dillon * If we failed to softreset make the port quiescent, otherwise
8351ac8d5baSMatthew Dillon * make sure the port's start/stop state matches what it was on
8361ac8d5baSMatthew Dillon * entry.
8371ac8d5baSMatthew Dillon *
8381ac8d5baSMatthew Dillon * Don't kill the port if the softreset is on a port multiplier
8391ac8d5baSMatthew Dillon * target, that would kill all the targets!
8401ac8d5baSMatthew Dillon */
841a35ddbb4SMatthew Dillon if (bootverbose) {
842a35ddbb4SMatthew Dillon kprintf("%s: END SOFTRESET %d prob=%d state=%d\n",
843a35ddbb4SMatthew Dillon PORTNAME(ap), error, ap->ap_probe, ap->ap_state);
844a35ddbb4SMatthew Dillon }
8451ac8d5baSMatthew Dillon if (error) {
8461ac8d5baSMatthew Dillon sili_port_hardstop(ap);
8471ac8d5baSMatthew Dillon /* ap_probe set to failed */
8481ac8d5baSMatthew Dillon } else {
8491ac8d5baSMatthew Dillon ap->ap_probe = ATA_PROBE_NEED_IDENT;
850a35ddbb4SMatthew Dillon ap->ap_pmcount = 1;
8511ac8d5baSMatthew Dillon }
8521ac8d5baSMatthew Dillon crit_exit();
8531ac8d5baSMatthew Dillon
854a35ddbb4SMatthew Dillon sili_pwrite(ap, SILI_PREG_SERR, -1);
8551ac8d5baSMatthew Dillon if (bootverbose)
8561ac8d5baSMatthew Dillon kprintf("%s: END SOFTRESET\n", PORTNAME(ap));
8571ac8d5baSMatthew Dillon
8581ac8d5baSMatthew Dillon return (error);
8591ac8d5baSMatthew Dillon }
8601ac8d5baSMatthew Dillon
8611ac8d5baSMatthew Dillon /*
8621ac8d5baSMatthew Dillon * This function does a hard reset of the port. Note that the device
863a35ddbb4SMatthew Dillon * connected to the port could still end-up hung. Phy detection is
864a35ddbb4SMatthew Dillon * used to short-cut longer operations.
8651ac8d5baSMatthew Dillon */
8661ac8d5baSMatthew Dillon int
sili_port_hardreset(struct sili_port * ap)867a35ddbb4SMatthew Dillon sili_port_hardreset(struct sili_port *ap)
8681ac8d5baSMatthew Dillon {
869a35ddbb4SMatthew Dillon u_int32_t data;
8701ac8d5baSMatthew Dillon int error;
8711ac8d5baSMatthew Dillon int loop;
8721ac8d5baSMatthew Dillon
873a35ddbb4SMatthew Dillon if (bootverbose)
874a35ddbb4SMatthew Dillon kprintf("%s: START HARDRESET\n", PORTNAME(ap));
8751ac8d5baSMatthew Dillon
8761ac8d5baSMatthew Dillon ap->ap_state = AP_S_NORMAL;
8771ac8d5baSMatthew Dillon
8781ac8d5baSMatthew Dillon /*
879a35ddbb4SMatthew Dillon * Set SCTL up for any speed restrictions before issuing the
8803c6bae9dSMatthew Dillon * device reset. This may also take us out of an INIT state
8813c6bae9dSMatthew Dillon * (if we were previously in a continuous reset state from
8823c6bae9dSMatthew Dillon * sili_port_listen()).
883a35ddbb4SMatthew Dillon */
884a35ddbb4SMatthew Dillon data = SILI_PREG_SCTL_SPM_NONE |
885a35ddbb4SMatthew Dillon SILI_PREG_SCTL_IPM_NONE |
886a35ddbb4SMatthew Dillon SILI_PREG_SCTL_SPD_NONE |
887a35ddbb4SMatthew Dillon SILI_PREG_SCTL_DET_NONE;
888a35ddbb4SMatthew Dillon if (SiliForceGen1 & (1 << ap->ap_num)) {
889a35ddbb4SMatthew Dillon data &= ~SILI_PREG_SCTL_SPD_NONE;
890a35ddbb4SMatthew Dillon data |= SILI_PREG_SCTL_SPD_GEN1;
891a35ddbb4SMatthew Dillon }
892a35ddbb4SMatthew Dillon sili_pwrite(ap, SILI_PREG_SCTL, data);
893a35ddbb4SMatthew Dillon
894a35ddbb4SMatthew Dillon /*
8953c6bae9dSMatthew Dillon * The transition from a continuous COMRESET state from
8963c6bae9dSMatthew Dillon * sili_port_listen() back to device detect can take a
8973c6bae9dSMatthew Dillon * few seconds. It's quite non-deterministic. Most of
8983c6bae9dSMatthew Dillon * the time it takes far less. Use a polling loop to
8993c6bae9dSMatthew Dillon * wait.
9003c6bae9dSMatthew Dillon */
9013c6bae9dSMatthew Dillon loop = 4000;
9023c6bae9dSMatthew Dillon while (loop > 0) {
9033c6bae9dSMatthew Dillon data = sili_pread(ap, SILI_PREG_SSTS);
9043c6bae9dSMatthew Dillon if (data & SILI_PREG_SSTS_DET)
9053c6bae9dSMatthew Dillon break;
9063c6bae9dSMatthew Dillon loop -= sili_os_softsleep();
9073c6bae9dSMatthew Dillon }
9083c6bae9dSMatthew Dillon sili_os_sleep(100);
9093c6bae9dSMatthew Dillon
9103c6bae9dSMatthew Dillon /*
911a35ddbb4SMatthew Dillon * Issue Device Reset, give the phy a little time to settle down.
9121ac8d5baSMatthew Dillon *
9131ac8d5baSMatthew Dillon * NOTE: Unlike Port Reset, the port ready signal will not
9141ac8d5baSMatthew Dillon * go active unless a device is established to be on
9151ac8d5baSMatthew Dillon * the port.
9161ac8d5baSMatthew Dillon */
9171ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_PMA);
9181ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_RESUME);
9191ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_DEVRESET);
9201ac8d5baSMatthew Dillon if (sili_pwait_clr(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_DEVRESET)) {
9211ac8d5baSMatthew Dillon kprintf("%s: hardreset failed to clear\n", PORTNAME(ap));
9221ac8d5baSMatthew Dillon }
923a35ddbb4SMatthew Dillon sili_os_sleep(20);
9241ac8d5baSMatthew Dillon
9251ac8d5baSMatthew Dillon /*
9261ac8d5baSMatthew Dillon * Try to determine if there is a device on the port.
9271ac8d5baSMatthew Dillon *
9281ac8d5baSMatthew Dillon * Give the device 3/10 second to at least be detected.
9291ac8d5baSMatthew Dillon */
9301ac8d5baSMatthew Dillon loop = 300;
9311ac8d5baSMatthew Dillon while (loop > 0) {
932a35ddbb4SMatthew Dillon data = sili_pread(ap, SILI_PREG_SSTS);
933a35ddbb4SMatthew Dillon if (data & SILI_PREG_SSTS_DET)
9341ac8d5baSMatthew Dillon break;
9351ac8d5baSMatthew Dillon loop -= sili_os_softsleep();
9361ac8d5baSMatthew Dillon }
9371ac8d5baSMatthew Dillon if (loop <= 0) {
9381ac8d5baSMatthew Dillon if (bootverbose) {
9391ac8d5baSMatthew Dillon kprintf("%s: Port appears to be unplugged\n",
9401ac8d5baSMatthew Dillon PORTNAME(ap));
9411ac8d5baSMatthew Dillon }
9421ac8d5baSMatthew Dillon error = ENODEV;
943a35ddbb4SMatthew Dillon goto done;
9441ac8d5baSMatthew Dillon }
9451ac8d5baSMatthew Dillon
9461ac8d5baSMatthew Dillon /*
9471ac8d5baSMatthew Dillon * There is something on the port. Give the device 3 seconds
948a35ddbb4SMatthew Dillon * to detect.
9491ac8d5baSMatthew Dillon */
950a35ddbb4SMatthew Dillon if (sili_pwait_eq(ap, 3000, SILI_PREG_SSTS,
9511ac8d5baSMatthew Dillon SILI_PREG_SSTS_DET, SILI_PREG_SSTS_DET_DEV)) {
9521ac8d5baSMatthew Dillon if (bootverbose) {
9531ac8d5baSMatthew Dillon kprintf("%s: Device may be powered down\n",
9541ac8d5baSMatthew Dillon PORTNAME(ap));
9551ac8d5baSMatthew Dillon }
9561ac8d5baSMatthew Dillon error = ENODEV;
957a35ddbb4SMatthew Dillon goto pmdetect;
9581ac8d5baSMatthew Dillon }
9591ac8d5baSMatthew Dillon
9601ac8d5baSMatthew Dillon /*
961a35ddbb4SMatthew Dillon * We got something that definitely looks like a device. Give
962a35ddbb4SMatthew Dillon * the device time to send us its first D2H FIS.
9631ac8d5baSMatthew Dillon *
964a35ddbb4SMatthew Dillon * This effectively waits for BSY to clear.
9651ac8d5baSMatthew Dillon */
966a35ddbb4SMatthew Dillon if (sili_pwait_set_to(ap, 3000, SILI_PREG_STATUS,
9671ac8d5baSMatthew Dillon SILI_PREG_STATUS_READY)) {
968a35ddbb4SMatthew Dillon error = EBUSY;
9691ac8d5baSMatthew Dillon } else {
9701ac8d5baSMatthew Dillon error = 0;
9711ac8d5baSMatthew Dillon }
9721ac8d5baSMatthew Dillon
973a35ddbb4SMatthew Dillon pmdetect:
9741ac8d5baSMatthew Dillon /*
975a35ddbb4SMatthew Dillon * Do the PM port probe regardless of how things turned out above.
976a35ddbb4SMatthew Dillon *
977a35ddbb4SMatthew Dillon * If the PM port probe fails it will return the original error
978a35ddbb4SMatthew Dillon * from above.
9791ac8d5baSMatthew Dillon */
980a35ddbb4SMatthew Dillon if (ap->ap_sc->sc_flags & SILI_F_SPM) {
981a35ddbb4SMatthew Dillon error = sili_pm_port_probe(ap, error);
982a35ddbb4SMatthew Dillon }
983a35ddbb4SMatthew Dillon
984a35ddbb4SMatthew Dillon done:
985a35ddbb4SMatthew Dillon /*
986a35ddbb4SMatthew Dillon * Finish up
987a35ddbb4SMatthew Dillon */
988a35ddbb4SMatthew Dillon switch(error) {
989a35ddbb4SMatthew Dillon case 0:
9901ac8d5baSMatthew Dillon if (ap->ap_type == ATA_PORT_T_PM)
9911ac8d5baSMatthew Dillon ap->ap_probe = ATA_PROBE_GOOD;
9921ac8d5baSMatthew Dillon else
9931ac8d5baSMatthew Dillon ap->ap_probe = ATA_PROBE_NEED_SOFT_RESET;
994a35ddbb4SMatthew Dillon break;
995a35ddbb4SMatthew Dillon case ENODEV:
9961ac8d5baSMatthew Dillon /*
997a35ddbb4SMatthew Dillon * No device detected.
9981ac8d5baSMatthew Dillon */
999a35ddbb4SMatthew Dillon data = sili_pread(ap, SILI_PREG_SSTS);
10001ac8d5baSMatthew Dillon
1001a35ddbb4SMatthew Dillon switch(data & SATA_PM_SSTS_DET) {
1002a35ddbb4SMatthew Dillon case SILI_PREG_SSTS_DET_DEV_NE:
1003a35ddbb4SMatthew Dillon kprintf("%s: Device not communicating\n",
10041ac8d5baSMatthew Dillon PORTNAME(ap));
1005a35ddbb4SMatthew Dillon break;
1006a35ddbb4SMatthew Dillon case SILI_PREG_SSTS_DET_OFFLINE:
1007a35ddbb4SMatthew Dillon kprintf("%s: PHY offline\n",
1008a35ddbb4SMatthew Dillon PORTNAME(ap));
1009a35ddbb4SMatthew Dillon break;
1010a35ddbb4SMatthew Dillon default:
1011a35ddbb4SMatthew Dillon kprintf("%s: No device detected\n",
1012a35ddbb4SMatthew Dillon PORTNAME(ap));
1013a35ddbb4SMatthew Dillon break;
10141ac8d5baSMatthew Dillon }
1015a35ddbb4SMatthew Dillon sili_port_hardstop(ap);
1016a35ddbb4SMatthew Dillon break;
1017a35ddbb4SMatthew Dillon default:
10181ac8d5baSMatthew Dillon /*
1019a35ddbb4SMatthew Dillon * (EBUSY)
10201ac8d5baSMatthew Dillon */
1021a35ddbb4SMatthew Dillon kprintf("%s: Device on port is bricked\n",
1022a35ddbb4SMatthew Dillon PORTNAME(ap));
1023a35ddbb4SMatthew Dillon sili_port_hardstop(ap);
1024a35ddbb4SMatthew Dillon break;
10251ac8d5baSMatthew Dillon }
1026a35ddbb4SMatthew Dillon sili_pwrite(ap, SILI_PREG_SERR, -1);
10271ac8d5baSMatthew Dillon
1028a35ddbb4SMatthew Dillon if (bootverbose)
1029a35ddbb4SMatthew Dillon kprintf("%s: END HARDRESET %d\n", PORTNAME(ap), error);
10301ac8d5baSMatthew Dillon return (error);
10311ac8d5baSMatthew Dillon }
10321ac8d5baSMatthew Dillon
10331ac8d5baSMatthew Dillon /*
10341ac8d5baSMatthew Dillon * Hard-stop on hot-swap device removal. See 10.10.1
10351ac8d5baSMatthew Dillon *
10361ac8d5baSMatthew Dillon * Place the port in a mode that will allow it to detect hot-swap insertions.
10371ac8d5baSMatthew Dillon * This is a bit imprecise because just setting-up SCTL to DET_INIT doesn't
10381ac8d5baSMatthew Dillon * seem to do the job.
10391ac8d5baSMatthew Dillon */
10401ac8d5baSMatthew Dillon void
sili_port_hardstop(struct sili_port * ap)10411ac8d5baSMatthew Dillon sili_port_hardstop(struct sili_port *ap)
10421ac8d5baSMatthew Dillon {
10431ac8d5baSMatthew Dillon struct sili_ccb *ccb;
10441ac8d5baSMatthew Dillon struct ata_port *at;
10451ac8d5baSMatthew Dillon int i;
10461ac8d5baSMatthew Dillon int slot;
1047e27434d7SMatthew Dillon int serial;
10481ac8d5baSMatthew Dillon
10491ac8d5baSMatthew Dillon ap->ap_state = AP_S_FATAL_ERROR;
10501ac8d5baSMatthew Dillon ap->ap_probe = ATA_PROBE_FAILED;
10511ac8d5baSMatthew Dillon ap->ap_type = ATA_PORT_T_NONE;
10521ac8d5baSMatthew Dillon
10531ac8d5baSMatthew Dillon /*
10541ac8d5baSMatthew Dillon * Clean up AT sub-ports on SATA port.
10551ac8d5baSMatthew Dillon */
10561ac8d5baSMatthew Dillon for (i = 0; ap->ap_ata && i < SILI_MAX_PMPORTS; ++i) {
10571ac8d5baSMatthew Dillon at = &ap->ap_ata[i];
10581ac8d5baSMatthew Dillon at->at_type = ATA_PORT_T_NONE;
10591ac8d5baSMatthew Dillon at->at_probe = ATA_PROBE_FAILED;
10601ac8d5baSMatthew Dillon at->at_features &= ~ATA_PORT_F_READLOG;
10611ac8d5baSMatthew Dillon }
10621ac8d5baSMatthew Dillon
10631ac8d5baSMatthew Dillon /*
10641ac8d5baSMatthew Dillon * Kill the port. Don't bother waiting for it to transition
10651ac8d5baSMatthew Dillon * back up.
10661ac8d5baSMatthew Dillon */
10671ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_RESET);
10681ac8d5baSMatthew Dillon if (sili_pread(ap, SILI_PREG_STATUS) & SILI_PREG_STATUS_READY) {
10691ac8d5baSMatthew Dillon kprintf("%s: Port will not go into reset\n",
10701ac8d5baSMatthew Dillon PORTNAME(ap));
10711ac8d5baSMatthew Dillon }
10721ac8d5baSMatthew Dillon sili_os_sleep(10);
10731ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_RESET);
10741ac8d5baSMatthew Dillon
10751ac8d5baSMatthew Dillon /*
10761ac8d5baSMatthew Dillon * Turn off port-multiplier control bit
10771ac8d5baSMatthew Dillon */
10781ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_PMA);
10791ac8d5baSMatthew Dillon
10801ac8d5baSMatthew Dillon /*
10811ac8d5baSMatthew Dillon * Clean up the command list.
10821ac8d5baSMatthew Dillon */
1083e27434d7SMatthew Dillon restart:
10841ac8d5baSMatthew Dillon while (ap->ap_active) {
10851ac8d5baSMatthew Dillon slot = ffs(ap->ap_active) - 1;
10861ac8d5baSMatthew Dillon ap->ap_active &= ~(1 << slot);
10871ac8d5baSMatthew Dillon ap->ap_expired &= ~(1 << slot);
10881ac8d5baSMatthew Dillon --ap->ap_active_cnt;
10891ac8d5baSMatthew Dillon ccb = &ap->ap_ccbs[slot];
10901ac8d5baSMatthew Dillon if (ccb->ccb_xa.flags & ATA_F_TIMEOUT_RUNNING) {
1091e27434d7SMatthew Dillon serial = ccb->ccb_xa.serial;
1092eb67213aSMatthew Dillon callout_cancel(&ccb->ccb_timeout);
1093e27434d7SMatthew Dillon if (serial != ccb->ccb_xa.serial) {
1094e27434d7SMatthew Dillon kprintf("%s: Warning: timeout race ccb %p\n",
1095e27434d7SMatthew Dillon PORTNAME(ap), ccb);
1096e27434d7SMatthew Dillon goto restart;
1097e27434d7SMatthew Dillon }
10981ac8d5baSMatthew Dillon ccb->ccb_xa.flags &= ~ATA_F_TIMEOUT_RUNNING;
10991ac8d5baSMatthew Dillon }
11001ac8d5baSMatthew Dillon ccb->ccb_xa.flags &= ~(ATA_F_TIMEOUT_DESIRED |
11011ac8d5baSMatthew Dillon ATA_F_TIMEOUT_EXPIRED);
11021ac8d5baSMatthew Dillon ccb->ccb_xa.state = ATA_S_TIMEOUT;
11031ac8d5baSMatthew Dillon ccb->ccb_done(ccb);
11041ac8d5baSMatthew Dillon ccb->ccb_xa.complete(&ccb->ccb_xa);
11051ac8d5baSMatthew Dillon }
11061ac8d5baSMatthew Dillon while ((ccb = TAILQ_FIRST(&ap->ap_ccb_pending)) != NULL) {
11071ac8d5baSMatthew Dillon TAILQ_REMOVE(&ap->ap_ccb_pending, ccb, ccb_entry);
11081ac8d5baSMatthew Dillon ccb->ccb_xa.state = ATA_S_TIMEOUT;
11091ac8d5baSMatthew Dillon ccb->ccb_xa.flags &= ~ATA_F_TIMEOUT_DESIRED;
11101ac8d5baSMatthew Dillon ccb->ccb_done(ccb);
11111ac8d5baSMatthew Dillon ccb->ccb_xa.complete(&ccb->ccb_xa);
11121ac8d5baSMatthew Dillon }
11131ac8d5baSMatthew Dillon KKASSERT(ap->ap_active_cnt == 0);
11141ac8d5baSMatthew Dillon
11151ac8d5baSMatthew Dillon /*
11161ac8d5baSMatthew Dillon * Put the port into a listen mode, we want to get insertion/removal
11171ac8d5baSMatthew Dillon * events.
11181ac8d5baSMatthew Dillon */
11191ac8d5baSMatthew Dillon sili_port_listen(ap);
11201ac8d5baSMatthew Dillon }
11211ac8d5baSMatthew Dillon
11221ac8d5baSMatthew Dillon /*
11231ac8d5baSMatthew Dillon * Place port into a listen mode for hotplug events only. The port has
11241ac8d5baSMatthew Dillon * already been reset and the command processor may not be ready due
11251ac8d5baSMatthew Dillon * to the lack of a device.
11261ac8d5baSMatthew Dillon */
11271ac8d5baSMatthew Dillon void
sili_port_listen(struct sili_port * ap)11281ac8d5baSMatthew Dillon sili_port_listen(struct sili_port *ap)
11291ac8d5baSMatthew Dillon {
11301ac8d5baSMatthew Dillon u_int32_t data;
11311ac8d5baSMatthew Dillon
11321ac8d5baSMatthew Dillon #if 1
11331ac8d5baSMatthew Dillon data = SILI_PREG_SCTL_SPM_NONE |
11341ac8d5baSMatthew Dillon SILI_PREG_SCTL_IPM_NONE |
11351ac8d5baSMatthew Dillon SILI_PREG_SCTL_SPD_NONE |
11361ac8d5baSMatthew Dillon SILI_PREG_SCTL_DET_INIT;
11371ac8d5baSMatthew Dillon if (SiliForceGen1 & (1 << ap->ap_num)) {
11381ac8d5baSMatthew Dillon data &= ~SILI_PREG_SCTL_SPD_NONE;
11391ac8d5baSMatthew Dillon data |= SILI_PREG_SCTL_SPD_GEN1;
11401ac8d5baSMatthew Dillon }
11411ac8d5baSMatthew Dillon #endif
1142a35ddbb4SMatthew Dillon sili_os_sleep(20);
11431ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_SERR, -1);
11441ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_INT_ENABLE, SILI_PREG_INT_PHYRDYCHG |
11451ac8d5baSMatthew Dillon SILI_PREG_INT_DEVEXCHG);
11461ac8d5baSMatthew Dillon }
11471ac8d5baSMatthew Dillon
11481ac8d5baSMatthew Dillon /*
11491ac8d5baSMatthew Dillon * Figure out what type of device is connected to the port, ATAPI or
11501ac8d5baSMatthew Dillon * DISK.
11511ac8d5baSMatthew Dillon */
11521ac8d5baSMatthew Dillon int
sili_port_signature(struct sili_port * ap,struct ata_port * at,u_int32_t sig)11531ac8d5baSMatthew Dillon sili_port_signature(struct sili_port *ap, struct ata_port *at, u_int32_t sig)
11541ac8d5baSMatthew Dillon {
11551ac8d5baSMatthew Dillon if (bootverbose)
11561ac8d5baSMatthew Dillon kprintf("%s: sig %08x\n", ATANAME(ap, at), sig);
11571ac8d5baSMatthew Dillon if ((sig & 0xffff0000) == (SATA_SIGNATURE_ATAPI & 0xffff0000)) {
11581ac8d5baSMatthew Dillon return(ATA_PORT_T_ATAPI);
11591ac8d5baSMatthew Dillon } else if ((sig & 0xffff0000) ==
11601ac8d5baSMatthew Dillon (SATA_SIGNATURE_PORT_MULTIPLIER & 0xffff0000)) {
11611ac8d5baSMatthew Dillon return(ATA_PORT_T_PM);
11621ac8d5baSMatthew Dillon } else {
11631ac8d5baSMatthew Dillon return(ATA_PORT_T_DISK);
11641ac8d5baSMatthew Dillon }
11651ac8d5baSMatthew Dillon }
11661ac8d5baSMatthew Dillon
11671ac8d5baSMatthew Dillon /*
11681ac8d5baSMatthew Dillon * Load the DMA descriptor table for a CCB's buffer.
11691ac8d5baSMatthew Dillon *
11701ac8d5baSMatthew Dillon * NOTE: ATA_F_PIO is auto-selected by sili part.
11711ac8d5baSMatthew Dillon */
11721ac8d5baSMatthew Dillon int
sili_load_prb(struct sili_ccb * ccb)11731ac8d5baSMatthew Dillon sili_load_prb(struct sili_ccb *ccb)
11741ac8d5baSMatthew Dillon {
11751ac8d5baSMatthew Dillon struct sili_port *ap = ccb->ccb_port;
11761ac8d5baSMatthew Dillon struct sili_softc *sc = ap->ap_sc;
11771ac8d5baSMatthew Dillon struct ata_xfer *xa = &ccb->ccb_xa;
11781ac8d5baSMatthew Dillon struct sili_prb *prb = ccb->ccb_prb;
11791ac8d5baSMatthew Dillon struct sili_sge *sge;
11801ac8d5baSMatthew Dillon bus_dmamap_t dmap = ccb->ccb_dmamap;
11811ac8d5baSMatthew Dillon int error;
11821ac8d5baSMatthew Dillon
11831ac8d5baSMatthew Dillon /*
11842102f407SMatthew Dillon * Set up the PRB. The PRB contains 2 SGE's (1 if it is an ATAPI
11852102f407SMatthew Dillon * command). The SGE must be set up to link to the rest of our
11862102f407SMatthew Dillon * SGE array, in blocks of four SGEs (a SGE table) starting at
11871ac8d5baSMatthew Dillon */
11881ac8d5baSMatthew Dillon prb->prb_xfer_count = 0;
11891ac8d5baSMatthew Dillon prb->prb_control = 0;
11901ac8d5baSMatthew Dillon prb->prb_override = 0;
11911ac8d5baSMatthew Dillon sge = (ccb->ccb_xa.flags & ATA_F_PACKET) ?
11921ac8d5baSMatthew Dillon &prb->prb_sge_packet : &prb->prb_sge_normal;
11931ac8d5baSMatthew Dillon if (xa->datalen == 0) {
11941ac8d5baSMatthew Dillon sge->sge_flags = SILI_SGE_FLAGS_TRM | SILI_SGE_FLAGS_DRD;
11951ac8d5baSMatthew Dillon sge->sge_count = 0;
11961ac8d5baSMatthew Dillon return (0);
11971ac8d5baSMatthew Dillon }
11981ac8d5baSMatthew Dillon
11991ac8d5baSMatthew Dillon if (ccb->ccb_xa.flags & ATA_F_READ)
12001ac8d5baSMatthew Dillon prb->prb_control |= SILI_PRB_CTRL_READ;
12011ac8d5baSMatthew Dillon if (ccb->ccb_xa.flags & ATA_F_WRITE)
12021ac8d5baSMatthew Dillon prb->prb_control |= SILI_PRB_CTRL_WRITE;
12031ac8d5baSMatthew Dillon sge->sge_flags = SILI_SGE_FLAGS_LNK;
12041ac8d5baSMatthew Dillon sge->sge_count = 0;
12052102f407SMatthew Dillon sge->sge_paddr = ccb->ccb_prb_paddr +
12062102f407SMatthew Dillon offsetof(struct sili_prb, prb_sge[0]);
12071ac8d5baSMatthew Dillon
12081ac8d5baSMatthew Dillon /*
12092102f407SMatthew Dillon * Load our sge array.
12101ac8d5baSMatthew Dillon */
12111ac8d5baSMatthew Dillon error = bus_dmamap_load(sc->sc_tag_data, dmap,
12121ac8d5baSMatthew Dillon xa->data, xa->datalen,
12131ac8d5baSMatthew Dillon sili_load_prb_callback,
12141ac8d5baSMatthew Dillon ccb,
12151ac8d5baSMatthew Dillon ((xa->flags & ATA_F_NOWAIT) ?
12161ac8d5baSMatthew Dillon BUS_DMA_NOWAIT : BUS_DMA_WAITOK));
12171ac8d5baSMatthew Dillon if (error != 0) {
12181ac8d5baSMatthew Dillon kprintf("%s: error %d loading dmamap\n", PORTNAME(ap), error);
12191ac8d5baSMatthew Dillon return (1);
12201ac8d5baSMatthew Dillon }
12211ac8d5baSMatthew Dillon
12221ac8d5baSMatthew Dillon bus_dmamap_sync(sc->sc_tag_data, dmap,
12231ac8d5baSMatthew Dillon (xa->flags & ATA_F_READ) ?
12241ac8d5baSMatthew Dillon BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
12251ac8d5baSMatthew Dillon
12261ac8d5baSMatthew Dillon return (0);
12271ac8d5baSMatthew Dillon }
12281ac8d5baSMatthew Dillon
12291ac8d5baSMatthew Dillon /*
12301ac8d5baSMatthew Dillon * Callback from BUSDMA system to load the segment list.
12311ac8d5baSMatthew Dillon *
12321ac8d5baSMatthew Dillon * The scatter/gather table is loaded by the sili chip in blocks of
12331ac8d5baSMatthew Dillon * four SGE's. If a continuance is required the last entry in each
12341ac8d5baSMatthew Dillon * block must point to the next block.
12351ac8d5baSMatthew Dillon */
12361ac8d5baSMatthew Dillon static
12371ac8d5baSMatthew Dillon void
sili_load_prb_callback(void * info,bus_dma_segment_t * segs,int nsegs,int error)12381ac8d5baSMatthew Dillon sili_load_prb_callback(void *info, bus_dma_segment_t *segs, int nsegs,
12391ac8d5baSMatthew Dillon int error)
12401ac8d5baSMatthew Dillon {
12411ac8d5baSMatthew Dillon struct sili_ccb *ccb = info;
12421ac8d5baSMatthew Dillon struct sili_sge *sge;
12431ac8d5baSMatthew Dillon int sgi;
12441ac8d5baSMatthew Dillon
12451ac8d5baSMatthew Dillon KKASSERT(nsegs <= SILI_MAX_SGET);
12461ac8d5baSMatthew Dillon
12471ac8d5baSMatthew Dillon sgi = 0;
12482102f407SMatthew Dillon sge = &ccb->ccb_prb->prb_sge[0];
12491ac8d5baSMatthew Dillon while (nsegs) {
12501ac8d5baSMatthew Dillon if ((sgi & 3) == 3) {
12512102f407SMatthew Dillon sge->sge_paddr = htole64(ccb->ccb_prb_paddr +
12522102f407SMatthew Dillon offsetof(struct sili_prb,
12532102f407SMatthew Dillon prb_sge[sgi + 1]));
12541ac8d5baSMatthew Dillon sge->sge_count = 0;
12551ac8d5baSMatthew Dillon sge->sge_flags = SILI_SGE_FLAGS_LNK;
12561ac8d5baSMatthew Dillon } else {
12571ac8d5baSMatthew Dillon sge->sge_paddr = htole64(segs->ds_addr);
12581ac8d5baSMatthew Dillon sge->sge_count = htole32(segs->ds_len);
12591ac8d5baSMatthew Dillon sge->sge_flags = 0;
12601ac8d5baSMatthew Dillon --nsegs;
12611ac8d5baSMatthew Dillon ++segs;
12621ac8d5baSMatthew Dillon }
12631ac8d5baSMatthew Dillon ++sge;
12641ac8d5baSMatthew Dillon ++sgi;
12651ac8d5baSMatthew Dillon }
12661ac8d5baSMatthew Dillon --sge;
12671ac8d5baSMatthew Dillon sge->sge_flags |= SILI_SGE_FLAGS_TRM;
12681ac8d5baSMatthew Dillon }
12691ac8d5baSMatthew Dillon
12701ac8d5baSMatthew Dillon void
sili_unload_prb(struct sili_ccb * ccb)12711ac8d5baSMatthew Dillon sili_unload_prb(struct sili_ccb *ccb)
12721ac8d5baSMatthew Dillon {
12731ac8d5baSMatthew Dillon struct sili_port *ap = ccb->ccb_port;
12741ac8d5baSMatthew Dillon struct sili_softc *sc = ap->ap_sc;
12751ac8d5baSMatthew Dillon struct ata_xfer *xa = &ccb->ccb_xa;
12761ac8d5baSMatthew Dillon bus_dmamap_t dmap = ccb->ccb_dmamap;
12771ac8d5baSMatthew Dillon
12781ac8d5baSMatthew Dillon if (xa->datalen != 0) {
12791ac8d5baSMatthew Dillon bus_dmamap_sync(sc->sc_tag_data, dmap,
12801ac8d5baSMatthew Dillon (xa->flags & ATA_F_READ) ?
12811ac8d5baSMatthew Dillon BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
12821ac8d5baSMatthew Dillon
12831ac8d5baSMatthew Dillon bus_dmamap_unload(sc->sc_tag_data, dmap);
12841ac8d5baSMatthew Dillon
12851ac8d5baSMatthew Dillon if (ccb->ccb_xa.flags & ATA_F_NCQ)
12861ac8d5baSMatthew Dillon xa->resid = 0;
12871ac8d5baSMatthew Dillon else
12881ac8d5baSMatthew Dillon xa->resid = xa->datalen -
12891ac8d5baSMatthew Dillon le32toh(ccb->ccb_prb->prb_xfer_count);
12901ac8d5baSMatthew Dillon }
12911ac8d5baSMatthew Dillon }
12921ac8d5baSMatthew Dillon
12931ac8d5baSMatthew Dillon /*
12941ac8d5baSMatthew Dillon * Start a command and poll for completion.
12951ac8d5baSMatthew Dillon *
12961ac8d5baSMatthew Dillon * timeout is in ms and only counts once the command gets on-chip.
12971ac8d5baSMatthew Dillon *
12981ac8d5baSMatthew Dillon * Returns ATA_S_* state, compare against ATA_S_COMPLETE to determine
12991ac8d5baSMatthew Dillon * that no error occured.
13001ac8d5baSMatthew Dillon *
13011ac8d5baSMatthew Dillon * NOTE: If the caller specifies a NULL timeout function the caller is
13021ac8d5baSMatthew Dillon * responsible for clearing hardware state on failure, but we will
13031ac8d5baSMatthew Dillon * deal with removing the ccb from any pending queue.
13041ac8d5baSMatthew Dillon *
13051ac8d5baSMatthew Dillon * NOTE: NCQ should never be used with this function.
13061ac8d5baSMatthew Dillon *
13071ac8d5baSMatthew Dillon * NOTE: If the port is in a failed state and stopped we do not try
13081ac8d5baSMatthew Dillon * to activate the ccb.
13091ac8d5baSMatthew Dillon */
13101ac8d5baSMatthew Dillon int
sili_poll(struct sili_ccb * ccb,int timeout,void (* timeout_fn)(struct sili_ccb *))13111ac8d5baSMatthew Dillon sili_poll(struct sili_ccb *ccb, int timeout,
13121ac8d5baSMatthew Dillon void (*timeout_fn)(struct sili_ccb *))
13131ac8d5baSMatthew Dillon {
13141ac8d5baSMatthew Dillon struct sili_port *ap = ccb->ccb_port;
13151ac8d5baSMatthew Dillon
13161ac8d5baSMatthew Dillon if (ccb->ccb_port->ap_state == AP_S_FATAL_ERROR) {
13171ac8d5baSMatthew Dillon ccb->ccb_xa.state = ATA_S_ERROR;
13181ac8d5baSMatthew Dillon return(ccb->ccb_xa.state);
13191ac8d5baSMatthew Dillon }
13201ac8d5baSMatthew Dillon
1321187cac04SMatthew Dillon KKASSERT((ap->ap_expired & (1 << ccb->ccb_slot)) == 0);
13221ac8d5baSMatthew Dillon sili_start(ccb);
13231ac8d5baSMatthew Dillon
13241ac8d5baSMatthew Dillon do {
13251ac8d5baSMatthew Dillon sili_port_intr(ap, 1);
13261ac8d5baSMatthew Dillon switch(ccb->ccb_xa.state) {
13271ac8d5baSMatthew Dillon case ATA_S_ONCHIP:
13281ac8d5baSMatthew Dillon timeout -= sili_os_softsleep();
13291ac8d5baSMatthew Dillon break;
13301ac8d5baSMatthew Dillon case ATA_S_PENDING:
13311ac8d5baSMatthew Dillon /*
13321ac8d5baSMatthew Dillon * The packet can get stuck on the pending queue
13334383d440SMatthew Dillon * if the port refuses to come ready. XXX
13341ac8d5baSMatthew Dillon */
13354383d440SMatthew Dillon #if 0
13364383d440SMatthew Dillon if (xxx AP_F_EXCLUSIVE_ACCESS)
13371ac8d5baSMatthew Dillon timeout -= sili_os_softsleep();
13381ac8d5baSMatthew Dillon else
13394383d440SMatthew Dillon #endif
13401ac8d5baSMatthew Dillon sili_os_softsleep();
13411ac8d5baSMatthew Dillon sili_check_active_timeouts(ap);
13421ac8d5baSMatthew Dillon break;
13431ac8d5baSMatthew Dillon default:
13441ac8d5baSMatthew Dillon return (ccb->ccb_xa.state);
13451ac8d5baSMatthew Dillon }
13461ac8d5baSMatthew Dillon } while (timeout > 0);
13471ac8d5baSMatthew Dillon
1348a35ddbb4SMatthew Dillon /*
1349a35ddbb4SMatthew Dillon * Don't spew if this is a probe during hard reset
1350a35ddbb4SMatthew Dillon */
1351a35ddbb4SMatthew Dillon if (ap->ap_probe != ATA_PROBE_NEED_HARD_RESET) {
13521ac8d5baSMatthew Dillon kprintf("%s: Poll timeout slot %d\n",
13531ac8d5baSMatthew Dillon ATANAME(ap, ccb->ccb_xa.at),
13541ac8d5baSMatthew Dillon ccb->ccb_slot);
1355a35ddbb4SMatthew Dillon }
13561ac8d5baSMatthew Dillon
13571ac8d5baSMatthew Dillon timeout_fn(ccb);
13581ac8d5baSMatthew Dillon
13591ac8d5baSMatthew Dillon return(ccb->ccb_xa.state);
13601ac8d5baSMatthew Dillon }
13611ac8d5baSMatthew Dillon
13621ac8d5baSMatthew Dillon /*
13631ac8d5baSMatthew Dillon * When polling we have to check if the currently active CCB(s)
13641ac8d5baSMatthew Dillon * have timed out as the callout will be deadlocked while we
13651ac8d5baSMatthew Dillon * hold the port lock.
13661ac8d5baSMatthew Dillon */
13671ac8d5baSMatthew Dillon void
sili_check_active_timeouts(struct sili_port * ap)13681ac8d5baSMatthew Dillon sili_check_active_timeouts(struct sili_port *ap)
13691ac8d5baSMatthew Dillon {
13701ac8d5baSMatthew Dillon struct sili_ccb *ccb;
13711ac8d5baSMatthew Dillon u_int32_t mask;
13721ac8d5baSMatthew Dillon int tag;
13731ac8d5baSMatthew Dillon
13741ac8d5baSMatthew Dillon mask = ap->ap_active;
13751ac8d5baSMatthew Dillon while (mask) {
13761ac8d5baSMatthew Dillon tag = ffs(mask) - 1;
13771ac8d5baSMatthew Dillon mask &= ~(1 << tag);
13781ac8d5baSMatthew Dillon ccb = &ap->ap_ccbs[tag];
13791ac8d5baSMatthew Dillon if (ccb->ccb_xa.flags & ATA_F_TIMEOUT_EXPIRED) {
1380132408ffSMatthew Dillon sili_core_timeout(ccb, 0);
13811ac8d5baSMatthew Dillon }
13821ac8d5baSMatthew Dillon }
13831ac8d5baSMatthew Dillon }
13841ac8d5baSMatthew Dillon
13851ac8d5baSMatthew Dillon static
13861ac8d5baSMatthew Dillon __inline
13871ac8d5baSMatthew Dillon void
sili_start_timeout(struct sili_ccb * ccb)13881ac8d5baSMatthew Dillon sili_start_timeout(struct sili_ccb *ccb)
13891ac8d5baSMatthew Dillon {
13901ac8d5baSMatthew Dillon if (ccb->ccb_xa.flags & ATA_F_TIMEOUT_DESIRED) {
13911ac8d5baSMatthew Dillon ccb->ccb_xa.flags |= ATA_F_TIMEOUT_RUNNING;
13921ac8d5baSMatthew Dillon callout_reset(&ccb->ccb_timeout,
13931ac8d5baSMatthew Dillon (ccb->ccb_xa.timeout * hz + 999) / 1000,
13941ac8d5baSMatthew Dillon sili_ata_cmd_timeout_unserialized, ccb);
13951ac8d5baSMatthew Dillon }
13961ac8d5baSMatthew Dillon }
13971ac8d5baSMatthew Dillon
13981ac8d5baSMatthew Dillon void
sili_start(struct sili_ccb * ccb)13991ac8d5baSMatthew Dillon sili_start(struct sili_ccb *ccb)
14001ac8d5baSMatthew Dillon {
14011ac8d5baSMatthew Dillon struct sili_port *ap = ccb->ccb_port;
14021ac8d5baSMatthew Dillon #if 0
14031ac8d5baSMatthew Dillon struct sili_softc *sc = ap->ap_sc;
14041ac8d5baSMatthew Dillon #endif
14051ac8d5baSMatthew Dillon
14061ac8d5baSMatthew Dillon KKASSERT(ccb->ccb_xa.state == ATA_S_PENDING);
14071ac8d5baSMatthew Dillon
14081ac8d5baSMatthew Dillon /*
14091ac8d5baSMatthew Dillon * Sync our SGE table and PRB
14101ac8d5baSMatthew Dillon */
14112102f407SMatthew Dillon bus_dmamap_sync(ap->ap_dmamem_prbs->adm_tag,
14122102f407SMatthew Dillon ap->ap_dmamem_prbs->adm_map,
14131ac8d5baSMatthew Dillon BUS_DMASYNC_PREWRITE);
14141ac8d5baSMatthew Dillon
14151ac8d5baSMatthew Dillon /*
14161ac8d5baSMatthew Dillon * XXX dmamap for PRB XXX BUS_DMASYNC_PREWRITE
14171ac8d5baSMatthew Dillon */
14181ac8d5baSMatthew Dillon
14191ac8d5baSMatthew Dillon /*
14201ac8d5baSMatthew Dillon * Controller will update shared memory!
14211ac8d5baSMatthew Dillon * XXX bus_dmamap_sync ... BUS_DMASYNC_PREREAD ...
14221ac8d5baSMatthew Dillon */
14231ac8d5baSMatthew Dillon /* Prepare RFIS area for write by controller */
14241ac8d5baSMatthew Dillon
14251ac8d5baSMatthew Dillon /*
14261ac8d5baSMatthew Dillon * There's no point trying to optimize this, it only shaves a few
14271ac8d5baSMatthew Dillon * nanoseconds so just queue the command and call our generic issue.
14281ac8d5baSMatthew Dillon */
14291ac8d5baSMatthew Dillon sili_issue_pending_commands(ap, ccb);
14301ac8d5baSMatthew Dillon }
14311ac8d5baSMatthew Dillon
14321ac8d5baSMatthew Dillon /*
1433187cac04SMatthew Dillon * Wait for all commands to complete processing. We hold the lock so no
1434187cac04SMatthew Dillon * new commands will be queued.
14351ac8d5baSMatthew Dillon */
14361ac8d5baSMatthew Dillon void
sili_exclusive_access(struct sili_port * ap)1437187cac04SMatthew Dillon sili_exclusive_access(struct sili_port *ap)
14381ac8d5baSMatthew Dillon {
14391ac8d5baSMatthew Dillon while (ap->ap_active) {
14401ac8d5baSMatthew Dillon sili_port_intr(ap, 1);
14411ac8d5baSMatthew Dillon sili_os_softsleep();
14421ac8d5baSMatthew Dillon }
14431ac8d5baSMatthew Dillon }
14441ac8d5baSMatthew Dillon
14451ac8d5baSMatthew Dillon /*
14461ac8d5baSMatthew Dillon * If ccb is not NULL enqueue and/or issue it.
14471ac8d5baSMatthew Dillon *
14481ac8d5baSMatthew Dillon * If ccb is NULL issue whatever we can from the queue. However, nothing
14491ac8d5baSMatthew Dillon * new is issued if the exclusive access flag is set or expired ccb's are
14501ac8d5baSMatthew Dillon * present.
14511ac8d5baSMatthew Dillon *
14521ac8d5baSMatthew Dillon * If existing commands are still active (ap_active) we can only
14531ac8d5baSMatthew Dillon * issue matching new commands.
14541ac8d5baSMatthew Dillon */
14551ac8d5baSMatthew Dillon void
sili_issue_pending_commands(struct sili_port * ap,struct sili_ccb * ccb)14561ac8d5baSMatthew Dillon sili_issue_pending_commands(struct sili_port *ap, struct sili_ccb *ccb)
14571ac8d5baSMatthew Dillon {
14581ac8d5baSMatthew Dillon /*
14591ac8d5baSMatthew Dillon * Enqueue the ccb.
14601ac8d5baSMatthew Dillon *
14611ac8d5baSMatthew Dillon * If just running the queue and in exclusive access mode we
14621ac8d5baSMatthew Dillon * just return. Also in this case if there are any expired ccb's
14631ac8d5baSMatthew Dillon * we want to clear the queue so the port can be safely stopped.
14641ac8d5baSMatthew Dillon *
14651ac8d5baSMatthew Dillon * XXX sili chip - expiration needs to be per-target if PM supports
14661ac8d5baSMatthew Dillon * FBSS?
14671ac8d5baSMatthew Dillon */
14681ac8d5baSMatthew Dillon if (ccb) {
14691ac8d5baSMatthew Dillon TAILQ_INSERT_TAIL(&ap->ap_ccb_pending, ccb, ccb_entry);
14704383d440SMatthew Dillon } else if (ap->ap_expired) {
14711ac8d5baSMatthew Dillon return;
14721ac8d5baSMatthew Dillon }
14731ac8d5baSMatthew Dillon
14741ac8d5baSMatthew Dillon /*
14751ac8d5baSMatthew Dillon * Pull the next ccb off the queue and run it if possible.
14761ac8d5baSMatthew Dillon * If the port is not ready to accept commands enable the
14771ac8d5baSMatthew Dillon * ready interrupt instead of starting a new command.
14781ac8d5baSMatthew Dillon *
14791ac8d5baSMatthew Dillon * XXX limit ncqdepth for attached devices behind PM
14801ac8d5baSMatthew Dillon */
14811ac8d5baSMatthew Dillon while ((ccb = TAILQ_FIRST(&ap->ap_ccb_pending)) != NULL) {
14824383d440SMatthew Dillon /*
14834383d440SMatthew Dillon * Port may be wedged.
14844383d440SMatthew Dillon */
14851ac8d5baSMatthew Dillon if ((sili_pread(ap, SILI_PREG_STATUS) &
14861ac8d5baSMatthew Dillon SILI_PREG_STATUS_READY) == 0) {
14871ac8d5baSMatthew Dillon kprintf("%s: slot %d NOT READY\n",
14881ac8d5baSMatthew Dillon ATANAME(ap, ccb->ccb_xa.at), ccb->ccb_slot);
14891ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_INT_ENABLE,
14901ac8d5baSMatthew Dillon SILI_PREG_INT_READY);
14911ac8d5baSMatthew Dillon break;
14921ac8d5baSMatthew Dillon }
14934383d440SMatthew Dillon
14944383d440SMatthew Dillon /*
14954383d440SMatthew Dillon * Handle exclusivity requirements. ATA_F_EXCLUSIVE is used
14964383d440SMatthew Dillon * when we may have to access the rfis which is stored in
14974383d440SMatthew Dillon * the LRAM PRB. Unfortunately reading the LRAM PRB is
14984383d440SMatthew Dillon * highly problematic, so requests (like PM requests) which
14994383d440SMatthew Dillon * need to access the rfis use exclusive mode and then
15004383d440SMatthew Dillon * access the copy made by the port interrupt code back in
15014383d440SMatthew Dillon * host memory.
15024383d440SMatthew Dillon */
15034383d440SMatthew Dillon if (ap->ap_active & ~ap->ap_expired) {
15044383d440SMatthew Dillon /*
15054383d440SMatthew Dillon * There may be multiple ccb's already running,
15066f3b9849SMatthew Dillon * if any are running and ap_run_flags sets
15076f3b9849SMatthew Dillon * one of these flags then we know only one is
15086f3b9849SMatthew Dillon * running.
15094383d440SMatthew Dillon *
15104383d440SMatthew Dillon * XXX Current AUTOSENSE code forces exclusivity
15114383d440SMatthew Dillon * to simplify the code.
15124383d440SMatthew Dillon */
15136f3b9849SMatthew Dillon if (ap->ap_run_flags &
15144383d440SMatthew Dillon (ATA_F_EXCLUSIVE | ATA_F_AUTOSENSE)) {
15154383d440SMatthew Dillon break;
15164383d440SMatthew Dillon }
15174383d440SMatthew Dillon
15184383d440SMatthew Dillon /*
15194383d440SMatthew Dillon * If the ccb we want to run is exclusive and ccb's
15204383d440SMatthew Dillon * are still active on the port, we can't queue it
15214383d440SMatthew Dillon * yet.
15224383d440SMatthew Dillon *
15234383d440SMatthew Dillon * XXX Current AUTOSENSE code forces exclusivity
15244383d440SMatthew Dillon * to simplify the code.
15254383d440SMatthew Dillon */
15264383d440SMatthew Dillon if (ccb->ccb_xa.flags &
15274383d440SMatthew Dillon (ATA_F_EXCLUSIVE | ATA_F_AUTOSENSE)) {
15284383d440SMatthew Dillon break;
15294383d440SMatthew Dillon }
15304383d440SMatthew Dillon }
15314383d440SMatthew Dillon
15321ac8d5baSMatthew Dillon TAILQ_REMOVE(&ap->ap_ccb_pending, ccb, ccb_entry);
15331ac8d5baSMatthew Dillon ccb->ccb_xa.state = ATA_S_ONCHIP;
15341ac8d5baSMatthew Dillon ap->ap_active |= 1 << ccb->ccb_slot;
15351ac8d5baSMatthew Dillon ap->ap_active_cnt++;
15366f3b9849SMatthew Dillon ap->ap_run_flags = ccb->ccb_xa.flags;
15372102f407SMatthew Dillon
15382102f407SMatthew Dillon /*
15392102f407SMatthew Dillon * We can't use the CMD_FIFO method because it requires us
15402102f407SMatthew Dillon * building the PRB in the LRAM, and the LRAM is buggy. So
15412102f407SMatthew Dillon * we use host memory for the PRB.
15422102f407SMatthew Dillon */
15432102f407SMatthew Dillon sili_pwrite(ap, SILI_PREG_CMDACT(ccb->ccb_slot),
15442102f407SMatthew Dillon (u_int32_t)ccb->ccb_prb_paddr);
15452102f407SMatthew Dillon sili_pwrite(ap, SILI_PREG_CMDACT(ccb->ccb_slot) + 4,
15462102f407SMatthew Dillon (u_int32_t)(ccb->ccb_prb_paddr >> 32));
15472102f407SMatthew Dillon /* sili_pwrite(ap, SILI_PREG_CMD_FIFO, ccb->ccb_slot); */
15481ac8d5baSMatthew Dillon sili_start_timeout(ccb);
15491ac8d5baSMatthew Dillon }
15501ac8d5baSMatthew Dillon }
15511ac8d5baSMatthew Dillon
15521ac8d5baSMatthew Dillon void
sili_intr(void * arg)15531ac8d5baSMatthew Dillon sili_intr(void *arg)
15541ac8d5baSMatthew Dillon {
15551ac8d5baSMatthew Dillon struct sili_softc *sc = arg;
15561ac8d5baSMatthew Dillon struct sili_port *ap;
15571ac8d5baSMatthew Dillon u_int32_t gint;
15581ac8d5baSMatthew Dillon int port;
15591ac8d5baSMatthew Dillon
15601ac8d5baSMatthew Dillon /*
15611ac8d5baSMatthew Dillon * Check if the master enable is up, and whether any interrupts are
15621ac8d5baSMatthew Dillon * pending.
15631ac8d5baSMatthew Dillon *
15641ac8d5baSMatthew Dillon * Clear the ints we got.
15651ac8d5baSMatthew Dillon */
15661ac8d5baSMatthew Dillon if ((sc->sc_flags & SILI_F_INT_GOOD) == 0)
15671ac8d5baSMatthew Dillon return;
15681ac8d5baSMatthew Dillon gint = sili_read(sc, SILI_REG_GINT);
15691ac8d5baSMatthew Dillon if (gint == 0 || gint == 0xffffffff)
15701ac8d5baSMatthew Dillon return;
15711ac8d5baSMatthew Dillon sili_write(sc, SILI_REG_GINT, gint);
15721ac8d5baSMatthew Dillon
15731ac8d5baSMatthew Dillon /*
15741ac8d5baSMatthew Dillon * Process interrupts for each port in a non-blocking fashion.
15751ac8d5baSMatthew Dillon */
15761ac8d5baSMatthew Dillon while (gint & SILI_REG_GINT_PORTMASK) {
15771ac8d5baSMatthew Dillon port = ffs(gint) - 1;
15781ac8d5baSMatthew Dillon ap = sc->sc_ports[port];
15791ac8d5baSMatthew Dillon if (ap) {
15801ac8d5baSMatthew Dillon if (sili_os_lock_port_nb(ap) == 0) {
15811ac8d5baSMatthew Dillon sili_port_intr(ap, 0);
15821ac8d5baSMatthew Dillon sili_os_unlock_port(ap);
15831ac8d5baSMatthew Dillon } else {
15841ac8d5baSMatthew Dillon sili_port_interrupt_redisable(ap);
15851ac8d5baSMatthew Dillon sili_os_signal_port_thread(ap, AP_SIGF_PORTINT);
15861ac8d5baSMatthew Dillon }
15871ac8d5baSMatthew Dillon }
15881ac8d5baSMatthew Dillon gint &= ~(1 << port);
15891ac8d5baSMatthew Dillon }
15901ac8d5baSMatthew Dillon }
15911ac8d5baSMatthew Dillon
15921ac8d5baSMatthew Dillon /*
15931ac8d5baSMatthew Dillon * Core called from helper thread.
15941ac8d5baSMatthew Dillon */
15951ac8d5baSMatthew Dillon void
sili_port_thread_core(struct sili_port * ap,int mask)15961ac8d5baSMatthew Dillon sili_port_thread_core(struct sili_port *ap, int mask)
15971ac8d5baSMatthew Dillon {
15981ac8d5baSMatthew Dillon /*
15991ac8d5baSMatthew Dillon * Process any expired timedouts.
16001ac8d5baSMatthew Dillon */
16011ac8d5baSMatthew Dillon sili_os_lock_port(ap);
16021ac8d5baSMatthew Dillon if (mask & AP_SIGF_TIMEOUT) {
16031ac8d5baSMatthew Dillon sili_check_active_timeouts(ap);
16041ac8d5baSMatthew Dillon }
16051ac8d5baSMatthew Dillon
16061ac8d5baSMatthew Dillon /*
16071ac8d5baSMatthew Dillon * Process port interrupts which require a higher level of
16081ac8d5baSMatthew Dillon * intervention.
16091ac8d5baSMatthew Dillon */
16101ac8d5baSMatthew Dillon if (mask & AP_SIGF_PORTINT) {
16111ac8d5baSMatthew Dillon sili_port_intr(ap, 1);
16121ac8d5baSMatthew Dillon sili_port_interrupt_reenable(ap);
16131ac8d5baSMatthew Dillon }
16146bac9ae4SMatthew Dillon sili_os_unlock_port(ap);
16151ac8d5baSMatthew Dillon }
16161ac8d5baSMatthew Dillon
16171ac8d5baSMatthew Dillon /*
16181ac8d5baSMatthew Dillon * Core per-port interrupt handler.
16191ac8d5baSMatthew Dillon *
16201ac8d5baSMatthew Dillon * If blockable is 0 we cannot call sili_os_sleep() at all and we can only
16211ac8d5baSMatthew Dillon * deal with normal command completions which do not require blocking.
16221ac8d5baSMatthew Dillon */
16231ac8d5baSMatthew Dillon void
sili_port_intr(struct sili_port * ap,int blockable)16241ac8d5baSMatthew Dillon sili_port_intr(struct sili_port *ap, int blockable)
16251ac8d5baSMatthew Dillon {
16261ac8d5baSMatthew Dillon struct sili_softc *sc = ap->ap_sc;
16271ac8d5baSMatthew Dillon u_int32_t is;
16281ac8d5baSMatthew Dillon int slot;
16291ac8d5baSMatthew Dillon struct sili_ccb *ccb = NULL;
16301ac8d5baSMatthew Dillon struct ata_port *ccb_at = NULL;
16311ac8d5baSMatthew Dillon u_int32_t active;
1632a35ddbb4SMatthew Dillon u_int32_t finished;
16331ac8d5baSMatthew Dillon const u_int32_t blockable_mask = SILI_PREG_IST_PHYRDYCHG |
16341ac8d5baSMatthew Dillon SILI_PREG_IST_DEVEXCHG |
16351ac8d5baSMatthew Dillon SILI_PREG_IST_CERROR |
16361ac8d5baSMatthew Dillon SILI_PREG_IST_DECODE |
16371ac8d5baSMatthew Dillon SILI_PREG_IST_CRC |
16381ac8d5baSMatthew Dillon SILI_PREG_IST_HANDSHK;
16391ac8d5baSMatthew Dillon const u_int32_t fatal_mask = SILI_PREG_IST_PHYRDYCHG |
16401ac8d5baSMatthew Dillon SILI_PREG_IST_DEVEXCHG |
16411ac8d5baSMatthew Dillon SILI_PREG_IST_DECODE |
16421ac8d5baSMatthew Dillon SILI_PREG_IST_CRC |
16431ac8d5baSMatthew Dillon SILI_PREG_IST_HANDSHK;
16441ac8d5baSMatthew Dillon
16451ac8d5baSMatthew Dillon enum { NEED_NOTHING, NEED_HOTPLUG_INSERT,
16461ac8d5baSMatthew Dillon NEED_HOTPLUG_REMOVE } need = NEED_NOTHING;
16471ac8d5baSMatthew Dillon
16481ac8d5baSMatthew Dillon /*
16491ac8d5baSMatthew Dillon * NOTE: CCOMPLETE was automatically cleared when we read INT_STATUS.
16501ac8d5baSMatthew Dillon */
16511ac8d5baSMatthew Dillon is = sili_pread(ap, SILI_PREG_INT_STATUS);
16521ac8d5baSMatthew Dillon is &= SILI_PREG_IST_MASK;
16531ac8d5baSMatthew Dillon if (is & SILI_PREG_IST_CCOMPLETE)
16541ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_INT_STATUS, SILI_PREG_IST_CCOMPLETE);
16551ac8d5baSMatthew Dillon
16561ac8d5baSMatthew Dillon /*
16571ac8d5baSMatthew Dillon * If we can't block then we can't handle these here. Disable
16581ac8d5baSMatthew Dillon * the interrupts in question so we don't live-lock, the helper
16591ac8d5baSMatthew Dillon * thread will re-enable them.
16601ac8d5baSMatthew Dillon *
16611ac8d5baSMatthew Dillon * If the port is in a completely failed state we do not want
16621ac8d5baSMatthew Dillon * to drop through to failed-command-processing if blockable is 0,
16631ac8d5baSMatthew Dillon * just let the thread deal with it all.
16641ac8d5baSMatthew Dillon *
16651ac8d5baSMatthew Dillon * Otherwise we fall through and still handle DHRS and any commands
16661ac8d5baSMatthew Dillon * which completed normally. Even if we are errored we haven't
16671ac8d5baSMatthew Dillon * stopped the port yet so CI/SACT are still good.
16681ac8d5baSMatthew Dillon */
16691ac8d5baSMatthew Dillon if (blockable == 0) {
16701ac8d5baSMatthew Dillon if (ap->ap_state == AP_S_FATAL_ERROR) {
16711ac8d5baSMatthew Dillon sili_port_interrupt_redisable(ap);
16721ac8d5baSMatthew Dillon sili_os_signal_port_thread(ap, AP_SIGF_PORTINT);
16731ac8d5baSMatthew Dillon /*is &= ~blockable_mask;*/
16741ac8d5baSMatthew Dillon return;
16751ac8d5baSMatthew Dillon }
16761ac8d5baSMatthew Dillon if (is & blockable_mask) {
16771ac8d5baSMatthew Dillon sili_port_interrupt_redisable(ap);
16781ac8d5baSMatthew Dillon sili_os_signal_port_thread(ap, AP_SIGF_PORTINT);
16791ac8d5baSMatthew Dillon /*is &= ~blockable_mask;*/
16801ac8d5baSMatthew Dillon return;
16811ac8d5baSMatthew Dillon }
16821ac8d5baSMatthew Dillon }
16831ac8d5baSMatthew Dillon
16841ac8d5baSMatthew Dillon if (is & SILI_PREG_IST_CERROR) {
16851ac8d5baSMatthew Dillon /*
16861ac8d5baSMatthew Dillon * Command failed (blockable).
16871ac8d5baSMatthew Dillon *
16881ac8d5baSMatthew Dillon * This stops command processing. We can extract the PM
16891ac8d5baSMatthew Dillon * target from the PMP field in SILI_PREG_CONTEXT. The
16901ac8d5baSMatthew Dillon * tag is not necessarily valid so don't use that.
16911ac8d5baSMatthew Dillon *
16921ac8d5baSMatthew Dillon * We must then expire all CCB's for that target and resume
16931ac8d5baSMatthew Dillon * processing if any other targets have active commands.
16941ac8d5baSMatthew Dillon * Particular error codes can be recovered by reading the LOG
16951ac8d5baSMatthew Dillon * page.
16961ac8d5baSMatthew Dillon *
16971ac8d5baSMatthew Dillon * The expire handling code will do the rest, which is
16981ac8d5baSMatthew Dillon * basically to reset the port once the only active
16991ac8d5baSMatthew Dillon * commands remaining are all expired.
17001ac8d5baSMatthew Dillon */
17011ac8d5baSMatthew Dillon u_int32_t error;
17021ac8d5baSMatthew Dillon int target;
17031ac8d5baSMatthew Dillon int resume = 1;
17041ac8d5baSMatthew Dillon
17051ac8d5baSMatthew Dillon target = (sili_pread(ap, SILI_PREG_CONTEXT) >>
17061ac8d5baSMatthew Dillon SILI_PREG_CONTEXT_PMPORT_SHIFT) &
17071ac8d5baSMatthew Dillon SILI_PREG_CONTEXT_PMPORT_MASK;
17081ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_INT_STATUS, SILI_PREG_IST_CERROR);
17091ac8d5baSMatthew Dillon active = ap->ap_active & ~ap->ap_expired;
17101ac8d5baSMatthew Dillon error = sili_pread(ap, SILI_PREG_CERROR);
17111ac8d5baSMatthew Dillon kprintf("%s.%d target error %d active=%08x hactive=%08x "
1712c3783d8fSzrj "SERR=%pb%i\n",
17131ac8d5baSMatthew Dillon PORTNAME(ap), target, error,
17141ac8d5baSMatthew Dillon active, sili_pread(ap, SILI_PREG_SLOTST),
1715c3783d8fSzrj SILI_PFMT_SERR, sili_pread(ap, SILI_PREG_SERR));
17161ac8d5baSMatthew Dillon
17171ac8d5baSMatthew Dillon while (active) {
17181ac8d5baSMatthew Dillon slot = ffs(active) - 1;
17191ac8d5baSMatthew Dillon ccb = &ap->ap_ccbs[slot];
17201ac8d5baSMatthew Dillon if ((ccb_at = ccb->ccb_xa.at) == NULL)
17211ac8d5baSMatthew Dillon ccb_at = &ap->ap_ata[0];
17221ac8d5baSMatthew Dillon if (target == ccb_at->at_target) {
1723187cac04SMatthew Dillon if ((ccb->ccb_xa.flags & ATA_F_NCQ) &&
17241ac8d5baSMatthew Dillon (error == SILI_PREG_CERROR_DEVICE ||
17251ac8d5baSMatthew Dillon error == SILI_PREG_CERROR_SDBERROR)) {
17261ac8d5baSMatthew Dillon ccb_at->at_features |= ATA_PORT_F_READLOG;
17271ac8d5baSMatthew Dillon }
1728132408ffSMatthew Dillon if (sili_core_timeout(ccb, 1) == 0)
17291ac8d5baSMatthew Dillon resume = 0;
17301ac8d5baSMatthew Dillon }
17311ac8d5baSMatthew Dillon active &= ~(1 << slot);
17321ac8d5baSMatthew Dillon }
17331ac8d5baSMatthew Dillon
17341ac8d5baSMatthew Dillon /*
17351ac8d5baSMatthew Dillon * Resume will be 0 if the timeout reinited and restarted
17361ac8d5baSMatthew Dillon * the port. Otherwise we resume the port to allow other
17371ac8d5baSMatthew Dillon * commands to complete.
17381ac8d5baSMatthew Dillon */
17391ac8d5baSMatthew Dillon if (resume)
17401ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_RESUME);
17411ac8d5baSMatthew Dillon }
17421ac8d5baSMatthew Dillon
17431ac8d5baSMatthew Dillon /*
17441ac8d5baSMatthew Dillon * Device notification to us (non-blocking)
17451ac8d5baSMatthew Dillon *
17461ac8d5baSMatthew Dillon * This is interrupt status SILIPREG_IST_SDB
17471ac8d5baSMatthew Dillon *
17481ac8d5baSMatthew Dillon * NOTE! On some parts notification bits can get set without
17491ac8d5baSMatthew Dillon * generating an interrupt. It is unclear whether this is
17501ac8d5baSMatthew Dillon * a bug in the PM (sending a DTOH device setbits with 'N' set
17511ac8d5baSMatthew Dillon * and 'I' not set), or a bug in the host controller.
17521ac8d5baSMatthew Dillon *
17531ac8d5baSMatthew Dillon * It only seems to occur under load.
17541ac8d5baSMatthew Dillon */
17551ac8d5baSMatthew Dillon if (sc->sc_flags & SILI_F_SSNTF) {
17561ac8d5baSMatthew Dillon u_int32_t data;
17571ac8d5baSMatthew Dillon const char *xstr;
17581ac8d5baSMatthew Dillon
17591ac8d5baSMatthew Dillon data = sili_pread(ap, SILI_PREG_SNTF);
17601ac8d5baSMatthew Dillon if (is & SILI_PREG_IST_SDB) {
17611ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_INT_STATUS,
17621ac8d5baSMatthew Dillon SILI_PREG_IST_SDB);
17631ac8d5baSMatthew Dillon is &= ~SILI_PREG_IST_SDB;
17641ac8d5baSMatthew Dillon xstr = " (no SDBS!)";
17651ac8d5baSMatthew Dillon } else {
17661ac8d5baSMatthew Dillon xstr = "";
17671ac8d5baSMatthew Dillon }
17681ac8d5baSMatthew Dillon if (data) {
17691ac8d5baSMatthew Dillon kprintf("%s: NOTIFY %08x%s\n",
17701ac8d5baSMatthew Dillon PORTNAME(ap), data, xstr);
17711ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_SNTF, data);
17721ac8d5baSMatthew Dillon sili_cam_changed(ap, NULL, -1);
17731ac8d5baSMatthew Dillon }
17741ac8d5baSMatthew Dillon }
17751ac8d5baSMatthew Dillon
17761ac8d5baSMatthew Dillon /*
17771ac8d5baSMatthew Dillon * Port change (hot-plug) (blockable).
17781ac8d5baSMatthew Dillon *
17791ac8d5baSMatthew Dillon * A PCS interrupt will occur on hot-plug once communication is
17801ac8d5baSMatthew Dillon * established.
17811ac8d5baSMatthew Dillon *
17821ac8d5baSMatthew Dillon * A PRCS interrupt will occur on hot-unplug (and possibly also
17831ac8d5baSMatthew Dillon * on hot-plug).
17841ac8d5baSMatthew Dillon *
17851ac8d5baSMatthew Dillon * XXX We can then check the CPS (Cold Presence State) bit, if
17861ac8d5baSMatthew Dillon * supported, to determine if a device is plugged in or not and do
17871ac8d5baSMatthew Dillon * the right thing.
17881ac8d5baSMatthew Dillon *
17891ac8d5baSMatthew Dillon * WARNING: A PCS interrupt is cleared by clearing DIAG_X, and
17901ac8d5baSMatthew Dillon * can also occur if an unsolicited COMINIT is received.
17911ac8d5baSMatthew Dillon * If this occurs command processing is automatically
17921ac8d5baSMatthew Dillon * stopped (CR goes inactive) and the port must be stopped
17931ac8d5baSMatthew Dillon * and restarted.
17941ac8d5baSMatthew Dillon */
17951ac8d5baSMatthew Dillon if (is & (SILI_PREG_IST_PHYRDYCHG | SILI_PREG_IST_DEVEXCHG)) {
17961ac8d5baSMatthew Dillon /* XXX */
17971ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_SERR,
17981ac8d5baSMatthew Dillon (SILI_PREG_SERR_DIAG_N | SILI_PREG_SERR_DIAG_X));
17991ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_INT_STATUS,
18001ac8d5baSMatthew Dillon is & (SILI_PREG_IST_PHYRDYCHG | SILI_PREG_IST_DEVEXCHG));
18011ac8d5baSMatthew Dillon
18021ac8d5baSMatthew Dillon is &= ~(SILI_PREG_IST_PHYRDYCHG | SILI_PREG_IST_DEVEXCHG);
18031ac8d5baSMatthew Dillon kprintf("%s: Port change\n", PORTNAME(ap));
18041ac8d5baSMatthew Dillon
18051ac8d5baSMatthew Dillon switch (sili_pread(ap, SILI_PREG_SSTS) & SILI_PREG_SSTS_DET) {
18061ac8d5baSMatthew Dillon case SILI_PREG_SSTS_DET_DEV:
18071ac8d5baSMatthew Dillon if (ap->ap_type == ATA_PORT_T_NONE &&
18081ac8d5baSMatthew Dillon ap->ap_probe == ATA_PROBE_FAILED) {
18091ac8d5baSMatthew Dillon need = NEED_HOTPLUG_INSERT;
18101ac8d5baSMatthew Dillon goto fatal;
18111ac8d5baSMatthew Dillon }
18121ac8d5baSMatthew Dillon break;
18131ac8d5baSMatthew Dillon default:
18141ac8d5baSMatthew Dillon kprintf("%s: Device lost\n", PORTNAME(ap));
18151ac8d5baSMatthew Dillon if (ap->ap_type != ATA_PORT_T_NONE) {
18161ac8d5baSMatthew Dillon need = NEED_HOTPLUG_REMOVE;
18171ac8d5baSMatthew Dillon goto fatal;
18181ac8d5baSMatthew Dillon }
18191ac8d5baSMatthew Dillon break;
18201ac8d5baSMatthew Dillon }
18211ac8d5baSMatthew Dillon }
18221ac8d5baSMatthew Dillon
18231ac8d5baSMatthew Dillon /*
18241ac8d5baSMatthew Dillon * Check for remaining errors - they are fatal. (blockable)
18251ac8d5baSMatthew Dillon */
18261ac8d5baSMatthew Dillon if (is & fatal_mask) {
18271ac8d5baSMatthew Dillon u_int32_t serr;
18281ac8d5baSMatthew Dillon
18291ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_INT_STATUS, is & fatal_mask);
18301ac8d5baSMatthew Dillon
18311ac8d5baSMatthew Dillon serr = sili_pread(ap, SILI_PREG_SERR);
1832c3783d8fSzrj kprintf("%s: Unrecoverable errors (IS: %pb%i, SERR: %pb%i), "
18331ac8d5baSMatthew Dillon "disabling port.\n",
18341ac8d5baSMatthew Dillon PORTNAME(ap),
1835c3783d8fSzrj SILI_PFMT_INT_STATUS, is,
1836c3783d8fSzrj SILI_PFMT_SERR, serr
18371ac8d5baSMatthew Dillon );
18381ac8d5baSMatthew Dillon is &= ~fatal_mask;
18391ac8d5baSMatthew Dillon /* XXX try recovery first */
18401ac8d5baSMatthew Dillon goto fatal;
18411ac8d5baSMatthew Dillon }
18421ac8d5baSMatthew Dillon
18431ac8d5baSMatthew Dillon /*
18441ac8d5baSMatthew Dillon * Fail all outstanding commands if we know the port won't recover.
18451ac8d5baSMatthew Dillon *
18461ac8d5baSMatthew Dillon * We may have a ccb_at if the failed command is known and was
18471ac8d5baSMatthew Dillon * being sent to a device over a port multiplier (PM). In this
18481ac8d5baSMatthew Dillon * case if the port itself has not completely failed we fail just
18491ac8d5baSMatthew Dillon * the commands related to that target.
18501ac8d5baSMatthew Dillon */
1851a35ddbb4SMatthew Dillon if (ap->ap_state == AP_S_FATAL_ERROR &&
1852a35ddbb4SMatthew Dillon (ap->ap_active & ~ap->ap_expired)) {
1853a35ddbb4SMatthew Dillon kprintf("%s: Fatal port error, expiring %08x\n",
1854a35ddbb4SMatthew Dillon PORTNAME(ap), ap->ap_active & ~ap->ap_expired);
18551ac8d5baSMatthew Dillon fatal:
18561ac8d5baSMatthew Dillon ap->ap_state = AP_S_FATAL_ERROR;
1857a35ddbb4SMatthew Dillon
18581ac8d5baSMatthew Dillon /*
18591ac8d5baSMatthew Dillon * Error all the active slots. If running across a PM
18601ac8d5baSMatthew Dillon * try to error out just the slots related to the target.
18611ac8d5baSMatthew Dillon */
18621ac8d5baSMatthew Dillon active = ap->ap_active & ~ap->ap_expired;
18631ac8d5baSMatthew Dillon
18641ac8d5baSMatthew Dillon while (active) {
18651ac8d5baSMatthew Dillon slot = ffs(active) - 1;
18661ac8d5baSMatthew Dillon active &= ~(1 << slot);
18671ac8d5baSMatthew Dillon ccb = &ap->ap_ccbs[slot];
1868132408ffSMatthew Dillon sili_core_timeout(ccb, 1);
18691ac8d5baSMatthew Dillon }
18701ac8d5baSMatthew Dillon }
18711ac8d5baSMatthew Dillon
18721ac8d5baSMatthew Dillon /*
18731ac8d5baSMatthew Dillon * CCB completion (non blocking).
18741ac8d5baSMatthew Dillon *
18751ac8d5baSMatthew Dillon * CCB completion is detected by noticing the slot bit in
18761ac8d5baSMatthew Dillon * the port slot status register has cleared while the bit
18771ac8d5baSMatthew Dillon * is still set in our ap_active variable.
18781ac8d5baSMatthew Dillon *
18791ac8d5baSMatthew Dillon * When completing expired events we must remember to reinit
18801ac8d5baSMatthew Dillon * the port once everything is clear.
1881132408ffSMatthew Dillon *
1882132408ffSMatthew Dillon * Due to a single-level recursion when reading the log page,
1883132408ffSMatthew Dillon * it is possible for the slot to already have been cleared
1884132408ffSMatthew Dillon * for some expired tags, do not include expired tags in
1885132408ffSMatthew Dillon * the list.
18861ac8d5baSMatthew Dillon */
18871ac8d5baSMatthew Dillon active = ap->ap_active & ~sili_pread(ap, SILI_PREG_SLOTST);
1888132408ffSMatthew Dillon active &= ~ap->ap_expired;
18891ac8d5baSMatthew Dillon
1890a35ddbb4SMatthew Dillon finished = active;
18911ac8d5baSMatthew Dillon while (active) {
18921ac8d5baSMatthew Dillon slot = ffs(active) - 1;
18931ac8d5baSMatthew Dillon ccb = &ap->ap_ccbs[slot];
18941ac8d5baSMatthew Dillon
18951ac8d5baSMatthew Dillon DPRINTF(SILI_D_INTR, "%s: slot %d is complete%s\n",
18961ac8d5baSMatthew Dillon PORTNAME(ap), slot, ccb->ccb_xa.state == ATA_S_ERROR ?
18971ac8d5baSMatthew Dillon " (error)" : "");
18981ac8d5baSMatthew Dillon
18991ac8d5baSMatthew Dillon active &= ~(1 << slot);
19001ac8d5baSMatthew Dillon
19011ac8d5baSMatthew Dillon /*
19021ac8d5baSMatthew Dillon * XXX sync POSTREAD for return data?
19031ac8d5baSMatthew Dillon */
19041ac8d5baSMatthew Dillon ap->ap_active &= ~(1 << ccb->ccb_slot);
19051ac8d5baSMatthew Dillon --ap->ap_active_cnt;
19061ac8d5baSMatthew Dillon
19071ac8d5baSMatthew Dillon /*
19081ac8d5baSMatthew Dillon * Complete the ccb. If the ccb was marked expired it
19091ac8d5baSMatthew Dillon * may or may not have been cleared from the port,
19101ac8d5baSMatthew Dillon * make sure we mark it as having timed out.
19114383d440SMatthew Dillon *
19124383d440SMatthew Dillon * In a normal completion if AUTOSENSE is set we copy
19134383d440SMatthew Dillon * the PRB LRAM rfis back to the rfis in host-memory.
19144383d440SMatthew Dillon *
19154383d440SMatthew Dillon * XXX Currently AUTOSENSE also forces exclusivity so we
19164383d440SMatthew Dillon * can safely work around a hardware bug when reading
19174383d440SMatthew Dillon * the LRAM.
19181ac8d5baSMatthew Dillon */
19191ac8d5baSMatthew Dillon if (ap->ap_expired & (1 << ccb->ccb_slot)) {
19201ac8d5baSMatthew Dillon ap->ap_expired &= ~(1 << ccb->ccb_slot);
19211ac8d5baSMatthew Dillon ccb->ccb_xa.state = ATA_S_TIMEOUT;
19221ac8d5baSMatthew Dillon ccb->ccb_done(ccb);
19231ac8d5baSMatthew Dillon ccb->ccb_xa.complete(&ccb->ccb_xa);
19241ac8d5baSMatthew Dillon } else {
19256f3b9849SMatthew Dillon if (ccb->ccb_xa.state == ATA_S_ONCHIP) {
19266f3b9849SMatthew Dillon ccb->ccb_xa.state = ATA_S_COMPLETE;
19274383d440SMatthew Dillon if (ccb->ccb_xa.flags & ATA_F_AUTOSENSE) {
19284383d440SMatthew Dillon memcpy(ccb->ccb_xa.rfis,
19294383d440SMatthew Dillon &ccb->ccb_prb_lram->prb_d2h,
19304383d440SMatthew Dillon sizeof(ccb->ccb_prb_lram->prb_d2h));
1931132408ffSMatthew Dillon if (ccb->ccb_xa.state == ATA_S_TIMEOUT)
1932132408ffSMatthew Dillon ccb->ccb_xa.state = ATA_S_ERROR;
19334383d440SMatthew Dillon }
19346f3b9849SMatthew Dillon }
19351ac8d5baSMatthew Dillon ccb->ccb_done(ccb);
19361ac8d5baSMatthew Dillon }
19371ac8d5baSMatthew Dillon }
19381ac8d5baSMatthew Dillon if (is & SILI_PREG_IST_READY) {
19391ac8d5baSMatthew Dillon is &= ~SILI_PREG_IST_READY;
19401ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_INT_DISABLE, SILI_PREG_INT_READY);
19411ac8d5baSMatthew Dillon sili_pwrite(ap, SILI_PREG_INT_STATUS, SILI_PREG_IST_READY);
19421ac8d5baSMatthew Dillon }
19431ac8d5baSMatthew Dillon
19441ac8d5baSMatthew Dillon /*
19451ac8d5baSMatthew Dillon * If we had expired commands and were waiting for
19461ac8d5baSMatthew Dillon * remaining commands to complete, and they have now
19471ac8d5baSMatthew Dillon * completed, we can reinit the port.
19481ac8d5baSMatthew Dillon *
19491ac8d5baSMatthew Dillon * This will also clean out the expired commands.
19501ac8d5baSMatthew Dillon * The timeout code also calls sili_port_reinit() if
19511ac8d5baSMatthew Dillon * the only commands remaining after a timeout are all
19521ac8d5baSMatthew Dillon * now expired commands.
19531ac8d5baSMatthew Dillon *
19541ac8d5baSMatthew Dillon * Otherwise just reissue.
19551ac8d5baSMatthew Dillon */
1956a35ddbb4SMatthew Dillon if (ap->ap_expired && ap->ap_active == ap->ap_expired) {
1957a35ddbb4SMatthew Dillon if (finished)
19581ac8d5baSMatthew Dillon sili_port_reinit(ap);
1959a35ddbb4SMatthew Dillon } else {
19601ac8d5baSMatthew Dillon sili_issue_pending_commands(ap, NULL);
1961a35ddbb4SMatthew Dillon }
19621ac8d5baSMatthew Dillon
19631ac8d5baSMatthew Dillon /*
19641ac8d5baSMatthew Dillon * Cleanup. Will not be set if non-blocking.
19651ac8d5baSMatthew Dillon */
19661ac8d5baSMatthew Dillon switch(need) {
19671ac8d5baSMatthew Dillon case NEED_HOTPLUG_INSERT:
19681ac8d5baSMatthew Dillon /*
19691ac8d5baSMatthew Dillon * A hot-plug insertion event has occured and all
19701ac8d5baSMatthew Dillon * outstanding commands have already been revoked.
19711ac8d5baSMatthew Dillon *
19721ac8d5baSMatthew Dillon * Don't recurse if this occurs while we are
19731ac8d5baSMatthew Dillon * resetting the port.
19741ac8d5baSMatthew Dillon *
19751ac8d5baSMatthew Dillon * Place the port in a continuous COMRESET state
19761ac8d5baSMatthew Dillon * until the INIT code gets to it.
19771ac8d5baSMatthew Dillon */
19781ac8d5baSMatthew Dillon kprintf("%s: HOTPLUG - Device inserted\n",
19791ac8d5baSMatthew Dillon PORTNAME(ap));
19801ac8d5baSMatthew Dillon ap->ap_probe = ATA_PROBE_NEED_INIT;
19811ac8d5baSMatthew Dillon sili_cam_changed(ap, NULL, -1);
19821ac8d5baSMatthew Dillon break;
19831ac8d5baSMatthew Dillon case NEED_HOTPLUG_REMOVE:
19841ac8d5baSMatthew Dillon /*
19851ac8d5baSMatthew Dillon * A hot-plug removal event has occured and all
19861ac8d5baSMatthew Dillon * outstanding commands have already been revoked.
19871ac8d5baSMatthew Dillon *
19881ac8d5baSMatthew Dillon * Don't recurse if this occurs while we are
19891ac8d5baSMatthew Dillon * resetting the port.
19901ac8d5baSMatthew Dillon */
19911ac8d5baSMatthew Dillon kprintf("%s: HOTPLUG - Device removed\n",
19921ac8d5baSMatthew Dillon PORTNAME(ap));
19931ac8d5baSMatthew Dillon sili_port_hardstop(ap);
19941ac8d5baSMatthew Dillon /* ap_probe set to failed */
19951ac8d5baSMatthew Dillon sili_cam_changed(ap, NULL, -1);
19961ac8d5baSMatthew Dillon break;
19971ac8d5baSMatthew Dillon default:
19981ac8d5baSMatthew Dillon break;
19991ac8d5baSMatthew Dillon }
20001ac8d5baSMatthew Dillon }
20011ac8d5baSMatthew Dillon
20021ac8d5baSMatthew Dillon struct sili_ccb *
sili_get_ccb(struct sili_port * ap)20031ac8d5baSMatthew Dillon sili_get_ccb(struct sili_port *ap)
20041ac8d5baSMatthew Dillon {
20051ac8d5baSMatthew Dillon struct sili_ccb *ccb;
20061ac8d5baSMatthew Dillon
20071ac8d5baSMatthew Dillon lockmgr(&ap->ap_ccb_lock, LK_EXCLUSIVE);
20081ac8d5baSMatthew Dillon ccb = TAILQ_FIRST(&ap->ap_ccb_free);
20091ac8d5baSMatthew Dillon if (ccb != NULL) {
20101ac8d5baSMatthew Dillon KKASSERT(ccb->ccb_xa.state == ATA_S_PUT);
20111ac8d5baSMatthew Dillon TAILQ_REMOVE(&ap->ap_ccb_free, ccb, ccb_entry);
20121ac8d5baSMatthew Dillon ccb->ccb_xa.state = ATA_S_SETUP;
20131ac8d5baSMatthew Dillon ccb->ccb_xa.at = NULL;
20141ac8d5baSMatthew Dillon }
20151ac8d5baSMatthew Dillon lockmgr(&ap->ap_ccb_lock, LK_RELEASE);
20161ac8d5baSMatthew Dillon
20171ac8d5baSMatthew Dillon return (ccb);
20181ac8d5baSMatthew Dillon }
20191ac8d5baSMatthew Dillon
20201ac8d5baSMatthew Dillon void
sili_put_ccb(struct sili_ccb * ccb)20211ac8d5baSMatthew Dillon sili_put_ccb(struct sili_ccb *ccb)
20221ac8d5baSMatthew Dillon {
20231ac8d5baSMatthew Dillon struct sili_port *ap = ccb->ccb_port;
20241ac8d5baSMatthew Dillon
20251ac8d5baSMatthew Dillon lockmgr(&ap->ap_ccb_lock, LK_EXCLUSIVE);
2026dcdc0770SMatthew Dillon ccb->ccb_xa.state = ATA_S_PUT;
2027e27434d7SMatthew Dillon ++ccb->ccb_xa.serial;
20281ac8d5baSMatthew Dillon TAILQ_INSERT_TAIL(&ap->ap_ccb_free, ccb, ccb_entry);
20291ac8d5baSMatthew Dillon lockmgr(&ap->ap_ccb_lock, LK_RELEASE);
20301ac8d5baSMatthew Dillon }
20311ac8d5baSMatthew Dillon
20321ac8d5baSMatthew Dillon struct sili_ccb *
sili_get_err_ccb(struct sili_port * ap)20331ac8d5baSMatthew Dillon sili_get_err_ccb(struct sili_port *ap)
20341ac8d5baSMatthew Dillon {
20351ac8d5baSMatthew Dillon struct sili_ccb *err_ccb;
20361ac8d5baSMatthew Dillon
20371ac8d5baSMatthew Dillon KKASSERT((ap->ap_flags & AP_F_ERR_CCB_RESERVED) == 0);
20381ac8d5baSMatthew Dillon ap->ap_flags |= AP_F_ERR_CCB_RESERVED;
20391ac8d5baSMatthew Dillon
20401ac8d5baSMatthew Dillon /*
20411ac8d5baSMatthew Dillon * Grab a CCB to use for error recovery. This should never fail, as
20421ac8d5baSMatthew Dillon * we ask atascsi to reserve one for us at init time.
20431ac8d5baSMatthew Dillon */
20441ac8d5baSMatthew Dillon err_ccb = ap->ap_err_ccb;
20451ac8d5baSMatthew Dillon KKASSERT(err_ccb != NULL);
20461ac8d5baSMatthew Dillon err_ccb->ccb_xa.flags = 0;
20471ac8d5baSMatthew Dillon err_ccb->ccb_done = sili_empty_done;
20481ac8d5baSMatthew Dillon
20491ac8d5baSMatthew Dillon return err_ccb;
20501ac8d5baSMatthew Dillon }
20511ac8d5baSMatthew Dillon
20521ac8d5baSMatthew Dillon void
sili_put_err_ccb(struct sili_ccb * ccb)20531ac8d5baSMatthew Dillon sili_put_err_ccb(struct sili_ccb *ccb)
20541ac8d5baSMatthew Dillon {
20551ac8d5baSMatthew Dillon struct sili_port *ap = ccb->ccb_port;
20561ac8d5baSMatthew Dillon
20571ac8d5baSMatthew Dillon KKASSERT((ap->ap_flags & AP_F_ERR_CCB_RESERVED) != 0);
20581ac8d5baSMatthew Dillon
20591ac8d5baSMatthew Dillon KKASSERT(ccb == ap->ap_err_ccb);
20601ac8d5baSMatthew Dillon
20611ac8d5baSMatthew Dillon ap->ap_flags &= ~AP_F_ERR_CCB_RESERVED;
20621ac8d5baSMatthew Dillon }
20631ac8d5baSMatthew Dillon
20641ac8d5baSMatthew Dillon /*
20651ac8d5baSMatthew Dillon * Read log page to get NCQ error.
2066132408ffSMatthew Dillon *
2067132408ffSMatthew Dillon * Return 0 on success
20681ac8d5baSMatthew Dillon */
2069132408ffSMatthew Dillon void
sili_port_read_ncq_error(struct sili_port * ap,int target)20701ac8d5baSMatthew Dillon sili_port_read_ncq_error(struct sili_port *ap, int target)
20711ac8d5baSMatthew Dillon {
20721ac8d5baSMatthew Dillon struct sili_ccb *ccb;
20731ac8d5baSMatthew Dillon struct ata_fis_h2d *fis;
2074132408ffSMatthew Dillon int status;
20751ac8d5baSMatthew Dillon
20761ac8d5baSMatthew Dillon DPRINTF(SILI_D_VERBOSE, "%s: read log page\n", PORTNAME(ap));
20771ac8d5baSMatthew Dillon
20781ac8d5baSMatthew Dillon /* Prep error CCB for READ LOG EXT, page 10h, 1 sector. */
20791ac8d5baSMatthew Dillon ccb = sili_get_err_ccb(ap);
20801ac8d5baSMatthew Dillon ccb->ccb_done = sili_empty_done;
20811ac8d5baSMatthew Dillon ccb->ccb_xa.flags = ATA_F_NOWAIT | ATA_F_READ | ATA_F_POLL;
20821ac8d5baSMatthew Dillon ccb->ccb_xa.data = ap->ap_err_scratch;
20831ac8d5baSMatthew Dillon ccb->ccb_xa.datalen = 512;
20841ac8d5baSMatthew Dillon ccb->ccb_xa.complete = sili_dummy_done;
20851ac8d5baSMatthew Dillon ccb->ccb_xa.at = &ap->ap_ata[target];
20861ac8d5baSMatthew Dillon fis = &ccb->ccb_prb->prb_h2d;
20871ac8d5baSMatthew Dillon bzero(fis, sizeof(*fis));
20881ac8d5baSMatthew Dillon
20891ac8d5baSMatthew Dillon fis->type = ATA_FIS_TYPE_H2D;
20901ac8d5baSMatthew Dillon fis->flags = ATA_H2D_FLAGS_CMD | target;
20911ac8d5baSMatthew Dillon fis->command = ATA_C_READ_LOG_EXT;
20921ac8d5baSMatthew Dillon fis->lba_low = 0x10; /* queued error log page (10h) */
20931ac8d5baSMatthew Dillon fis->sector_count = 1; /* number of sectors (1) */
20941ac8d5baSMatthew Dillon fis->sector_count_exp = 0;
20951ac8d5baSMatthew Dillon fis->lba_mid = 0; /* starting offset */
20961ac8d5baSMatthew Dillon fis->lba_mid_exp = 0;
20971ac8d5baSMatthew Dillon fis->device = 0;
20981ac8d5baSMatthew Dillon
2099a35ddbb4SMatthew Dillon /*
2100a35ddbb4SMatthew Dillon * NOTE: Must use sili_quick_timeout() because we hold the err_ccb
2101a35ddbb4SMatthew Dillon */
21021ac8d5baSMatthew Dillon if (sili_load_prb(ccb) != 0) {
2103132408ffSMatthew Dillon status = ATA_S_ERROR;
21041ac8d5baSMatthew Dillon } else {
21051ac8d5baSMatthew Dillon ccb->ccb_xa.state = ATA_S_PENDING;
2106132408ffSMatthew Dillon status = sili_poll(ccb, 1000, sili_quick_timeout);
21071ac8d5baSMatthew Dillon }
21081ac8d5baSMatthew Dillon
2109132408ffSMatthew Dillon /*
2110132408ffSMatthew Dillon * Just spew if it fails, there isn't much we can do at this point.
2111132408ffSMatthew Dillon */
2112132408ffSMatthew Dillon if (status != ATA_S_COMPLETE) {
21131ac8d5baSMatthew Dillon kprintf("%s: log page read failed, slot %d was still active.\n",
21141ac8d5baSMatthew Dillon ATANAME(ap, ccb->ccb_xa.at), ccb->ccb_slot);
21151ac8d5baSMatthew Dillon }
21161ac8d5baSMatthew Dillon
21171ac8d5baSMatthew Dillon /* Done with the error CCB now. */
21181ac8d5baSMatthew Dillon sili_unload_prb(ccb);
21191ac8d5baSMatthew Dillon sili_put_err_ccb(ccb);
21201ac8d5baSMatthew Dillon
21211ac8d5baSMatthew Dillon /* Extract failed register set and tags from the scratch space. */
2122132408ffSMatthew Dillon if (status == ATA_S_COMPLETE) {
21231ac8d5baSMatthew Dillon struct ata_log_page_10h *log;
21241ac8d5baSMatthew Dillon int err_slot;
21251ac8d5baSMatthew Dillon
21261ac8d5baSMatthew Dillon log = (struct ata_log_page_10h *)ap->ap_err_scratch;
21271ac8d5baSMatthew Dillon if (log->err_regs.type & ATA_LOG_10H_TYPE_NOTQUEUED) {
2128132408ffSMatthew Dillon /*
2129132408ffSMatthew Dillon * Not queued bit was set - wasn't an NCQ error?
2130132408ffSMatthew Dillon *
2131132408ffSMatthew Dillon * XXX This bit seems to be set a lot even for NCQ
2132132408ffSMatthew Dillon * errors?
2133132408ffSMatthew Dillon */
21341ac8d5baSMatthew Dillon } else {
2135132408ffSMatthew Dillon /*
2136132408ffSMatthew Dillon * Copy back the log record as a D2H register FIS.
2137132408ffSMatthew Dillon */
21381ac8d5baSMatthew Dillon err_slot = log->err_regs.type &
21391ac8d5baSMatthew Dillon ATA_LOG_10H_TYPE_TAG_MASK;
21401ac8d5baSMatthew Dillon ccb = &ap->ap_ccbs[err_slot];
21411ac8d5baSMatthew Dillon if (ap->ap_expired & (1 << ccb->ccb_slot)) {
2142132408ffSMatthew Dillon kprintf("%s: read NCQ error page slot=%d\n",
2143132408ffSMatthew Dillon ATANAME(ap, ccb->ccb_xa.at), err_slot
2144132408ffSMatthew Dillon );
21451ac8d5baSMatthew Dillon memcpy(&ccb->ccb_prb->prb_d2h, &log->err_regs,
21461ac8d5baSMatthew Dillon sizeof(struct ata_fis_d2h));
21471ac8d5baSMatthew Dillon ccb->ccb_prb->prb_d2h.type = ATA_FIS_TYPE_D2H;
21481ac8d5baSMatthew Dillon ccb->ccb_prb->prb_d2h.flags = 0;
2149132408ffSMatthew Dillon if (ccb->ccb_xa.state == ATA_S_TIMEOUT)
2150132408ffSMatthew Dillon ccb->ccb_xa.state = ATA_S_ERROR;
21511ac8d5baSMatthew Dillon } else {
2152132408ffSMatthew Dillon kprintf("%s: read NCQ error page slot=%d, "
2153132408ffSMatthew Dillon "slot does not match any cmds\n",
2154132408ffSMatthew Dillon ATANAME(ccb->ccb_port, ccb->ccb_xa.at),
2155132408ffSMatthew Dillon err_slot
2156132408ffSMatthew Dillon );
2157132408ffSMatthew Dillon }
2158132408ffSMatthew Dillon }
2159132408ffSMatthew Dillon }
21601ac8d5baSMatthew Dillon }
21611ac8d5baSMatthew Dillon
21621ac8d5baSMatthew Dillon /*
21631ac8d5baSMatthew Dillon * Allocate memory for various structures DMAd by hardware. The maximum
21641ac8d5baSMatthew Dillon * number of segments for these tags is 1 so the DMA memory will have a
21651ac8d5baSMatthew Dillon * single physical base address.
21661ac8d5baSMatthew Dillon */
21671ac8d5baSMatthew Dillon struct sili_dmamem *
sili_dmamem_alloc(struct sili_softc * sc,bus_dma_tag_t tag)21681ac8d5baSMatthew Dillon sili_dmamem_alloc(struct sili_softc *sc, bus_dma_tag_t tag)
21691ac8d5baSMatthew Dillon {
21701ac8d5baSMatthew Dillon struct sili_dmamem *adm;
21711ac8d5baSMatthew Dillon int error;
21721ac8d5baSMatthew Dillon
21731ac8d5baSMatthew Dillon adm = kmalloc(sizeof(*adm), M_DEVBUF, M_INTWAIT | M_ZERO);
21741ac8d5baSMatthew Dillon
21751ac8d5baSMatthew Dillon error = bus_dmamem_alloc(tag, (void **)&adm->adm_kva,
21761ac8d5baSMatthew Dillon BUS_DMA_ZERO, &adm->adm_map);
21771ac8d5baSMatthew Dillon if (error == 0) {
21781ac8d5baSMatthew Dillon adm->adm_tag = tag;
21791ac8d5baSMatthew Dillon error = bus_dmamap_load(tag, adm->adm_map,
21801ac8d5baSMatthew Dillon adm->adm_kva,
21811ac8d5baSMatthew Dillon bus_dma_tag_getmaxsize(tag),
21821ac8d5baSMatthew Dillon sili_dmamem_saveseg, &adm->adm_busaddr,
21831ac8d5baSMatthew Dillon 0);
21841ac8d5baSMatthew Dillon }
21851ac8d5baSMatthew Dillon if (error) {
21861ac8d5baSMatthew Dillon if (adm->adm_map) {
21871ac8d5baSMatthew Dillon bus_dmamap_destroy(tag, adm->adm_map);
21881ac8d5baSMatthew Dillon adm->adm_map = NULL;
21891ac8d5baSMatthew Dillon adm->adm_tag = NULL;
21901ac8d5baSMatthew Dillon adm->adm_kva = NULL;
21911ac8d5baSMatthew Dillon }
21921ac8d5baSMatthew Dillon kfree(adm, M_DEVBUF);
21931ac8d5baSMatthew Dillon adm = NULL;
21941ac8d5baSMatthew Dillon }
21951ac8d5baSMatthew Dillon return (adm);
21961ac8d5baSMatthew Dillon }
21971ac8d5baSMatthew Dillon
21981ac8d5baSMatthew Dillon static
21991ac8d5baSMatthew Dillon void
sili_dmamem_saveseg(void * info,bus_dma_segment_t * segs,int nsegs,int error)22001ac8d5baSMatthew Dillon sili_dmamem_saveseg(void *info, bus_dma_segment_t *segs, int nsegs, int error)
22011ac8d5baSMatthew Dillon {
22021ac8d5baSMatthew Dillon KKASSERT(error == 0);
22031ac8d5baSMatthew Dillon KKASSERT(nsegs == 1);
22041ac8d5baSMatthew Dillon *(bus_addr_t *)info = segs->ds_addr;
22051ac8d5baSMatthew Dillon }
22061ac8d5baSMatthew Dillon
22071ac8d5baSMatthew Dillon
22081ac8d5baSMatthew Dillon void
sili_dmamem_free(struct sili_softc * sc,struct sili_dmamem * adm)22091ac8d5baSMatthew Dillon sili_dmamem_free(struct sili_softc *sc, struct sili_dmamem *adm)
22101ac8d5baSMatthew Dillon {
22111ac8d5baSMatthew Dillon if (adm->adm_map) {
22121ac8d5baSMatthew Dillon bus_dmamap_unload(adm->adm_tag, adm->adm_map);
22131ac8d5baSMatthew Dillon bus_dmamap_destroy(adm->adm_tag, adm->adm_map);
22141ac8d5baSMatthew Dillon adm->adm_map = NULL;
22151ac8d5baSMatthew Dillon adm->adm_tag = NULL;
22161ac8d5baSMatthew Dillon adm->adm_kva = NULL;
22171ac8d5baSMatthew Dillon }
22181ac8d5baSMatthew Dillon kfree(adm, M_DEVBUF);
22191ac8d5baSMatthew Dillon }
22201ac8d5baSMatthew Dillon
22211ac8d5baSMatthew Dillon u_int32_t
sili_read(struct sili_softc * sc,bus_size_t r)22221ac8d5baSMatthew Dillon sili_read(struct sili_softc *sc, bus_size_t r)
22231ac8d5baSMatthew Dillon {
22241ac8d5baSMatthew Dillon bus_space_barrier(sc->sc_iot, sc->sc_ioh, r, 4,
22251ac8d5baSMatthew Dillon BUS_SPACE_BARRIER_READ);
22261ac8d5baSMatthew Dillon return (bus_space_read_4(sc->sc_iot, sc->sc_ioh, r));
22271ac8d5baSMatthew Dillon }
22281ac8d5baSMatthew Dillon
22291ac8d5baSMatthew Dillon void
sili_write(struct sili_softc * sc,bus_size_t r,u_int32_t v)22301ac8d5baSMatthew Dillon sili_write(struct sili_softc *sc, bus_size_t r, u_int32_t v)
22311ac8d5baSMatthew Dillon {
22321ac8d5baSMatthew Dillon bus_space_write_4(sc->sc_iot, sc->sc_ioh, r, v);
22331ac8d5baSMatthew Dillon bus_space_barrier(sc->sc_iot, sc->sc_ioh, r, 4,
22341ac8d5baSMatthew Dillon BUS_SPACE_BARRIER_WRITE);
22351ac8d5baSMatthew Dillon }
22361ac8d5baSMatthew Dillon
22371ac8d5baSMatthew Dillon u_int32_t
sili_pread(struct sili_port * ap,bus_size_t r)22381ac8d5baSMatthew Dillon sili_pread(struct sili_port *ap, bus_size_t r)
22391ac8d5baSMatthew Dillon {
22401ac8d5baSMatthew Dillon bus_space_barrier(ap->ap_sc->sc_iot, ap->ap_ioh, r, 4,
22411ac8d5baSMatthew Dillon BUS_SPACE_BARRIER_READ);
22421ac8d5baSMatthew Dillon return (bus_space_read_4(ap->ap_sc->sc_iot, ap->ap_ioh, r));
22431ac8d5baSMatthew Dillon }
22441ac8d5baSMatthew Dillon
22451ac8d5baSMatthew Dillon void
sili_pwrite(struct sili_port * ap,bus_size_t r,u_int32_t v)22461ac8d5baSMatthew Dillon sili_pwrite(struct sili_port *ap, bus_size_t r, u_int32_t v)
22471ac8d5baSMatthew Dillon {
22481ac8d5baSMatthew Dillon bus_space_write_4(ap->ap_sc->sc_iot, ap->ap_ioh, r, v);
22491ac8d5baSMatthew Dillon bus_space_barrier(ap->ap_sc->sc_iot, ap->ap_ioh, r, 4,
22501ac8d5baSMatthew Dillon BUS_SPACE_BARRIER_WRITE);
22511ac8d5baSMatthew Dillon }
22521ac8d5baSMatthew Dillon
22531ac8d5baSMatthew Dillon /*
22541ac8d5baSMatthew Dillon * Wait up to (timeout) milliseconds for the masked port register to
22551ac8d5baSMatthew Dillon * match the target.
22561ac8d5baSMatthew Dillon *
22571ac8d5baSMatthew Dillon * Timeout is in milliseconds.
22581ac8d5baSMatthew Dillon */
22591ac8d5baSMatthew Dillon int
sili_pwait_eq(struct sili_port * ap,int timeout,bus_size_t r,u_int32_t mask,u_int32_t target)22601ac8d5baSMatthew Dillon sili_pwait_eq(struct sili_port *ap, int timeout,
22611ac8d5baSMatthew Dillon bus_size_t r, u_int32_t mask, u_int32_t target)
22621ac8d5baSMatthew Dillon {
22631ac8d5baSMatthew Dillon int t;
22641ac8d5baSMatthew Dillon
22651ac8d5baSMatthew Dillon /*
22661ac8d5baSMatthew Dillon * Loop hard up to 100uS
22671ac8d5baSMatthew Dillon */
22681ac8d5baSMatthew Dillon for (t = 0; t < 100; ++t) {
22691ac8d5baSMatthew Dillon if ((sili_pread(ap, r) & mask) == target)
22701ac8d5baSMatthew Dillon return (0);
22711ac8d5baSMatthew Dillon sili_os_hardsleep(1); /* us */
22721ac8d5baSMatthew Dillon }
22731ac8d5baSMatthew Dillon
22741ac8d5baSMatthew Dillon do {
22751ac8d5baSMatthew Dillon timeout -= sili_os_softsleep();
22761ac8d5baSMatthew Dillon if ((sili_pread(ap, r) & mask) == target)
22771ac8d5baSMatthew Dillon return (0);
22781ac8d5baSMatthew Dillon } while (timeout > 0);
22791ac8d5baSMatthew Dillon return (1);
22801ac8d5baSMatthew Dillon }
22811ac8d5baSMatthew Dillon
22821ac8d5baSMatthew Dillon int
sili_wait_ne(struct sili_softc * sc,bus_size_t r,u_int32_t mask,u_int32_t target)22831ac8d5baSMatthew Dillon sili_wait_ne(struct sili_softc *sc, bus_size_t r, u_int32_t mask,
22841ac8d5baSMatthew Dillon u_int32_t target)
22851ac8d5baSMatthew Dillon {
22861ac8d5baSMatthew Dillon int t;
22871ac8d5baSMatthew Dillon
22881ac8d5baSMatthew Dillon /*
22891ac8d5baSMatthew Dillon * Loop hard up to 100uS
22901ac8d5baSMatthew Dillon */
22911ac8d5baSMatthew Dillon for (t = 0; t < 100; ++t) {
22921ac8d5baSMatthew Dillon if ((sili_read(sc, r) & mask) != target)
22931ac8d5baSMatthew Dillon return (0);
22941ac8d5baSMatthew Dillon sili_os_hardsleep(1); /* us */
22951ac8d5baSMatthew Dillon }
22961ac8d5baSMatthew Dillon
22971ac8d5baSMatthew Dillon /*
22981ac8d5baSMatthew Dillon * And one millisecond the slow way
22991ac8d5baSMatthew Dillon */
23001ac8d5baSMatthew Dillon t = 1000;
23011ac8d5baSMatthew Dillon do {
23021ac8d5baSMatthew Dillon t -= sili_os_softsleep();
23031ac8d5baSMatthew Dillon if ((sili_read(sc, r) & mask) != target)
23041ac8d5baSMatthew Dillon return (0);
23051ac8d5baSMatthew Dillon } while (t > 0);
23061ac8d5baSMatthew Dillon
23071ac8d5baSMatthew Dillon return (1);
23081ac8d5baSMatthew Dillon }
23091ac8d5baSMatthew Dillon
23101ac8d5baSMatthew Dillon
23111ac8d5baSMatthew Dillon /*
23121ac8d5baSMatthew Dillon * Acquire an ata transfer.
23131ac8d5baSMatthew Dillon *
23141ac8d5baSMatthew Dillon * Pass a NULL at for direct-attached transfers, and a non-NULL at for
23151ac8d5baSMatthew Dillon * targets that go through the port multiplier.
23161ac8d5baSMatthew Dillon */
23171ac8d5baSMatthew Dillon struct ata_xfer *
sili_ata_get_xfer(struct sili_port * ap,struct ata_port * at)23181ac8d5baSMatthew Dillon sili_ata_get_xfer(struct sili_port *ap, struct ata_port *at)
23191ac8d5baSMatthew Dillon {
23201ac8d5baSMatthew Dillon struct sili_ccb *ccb;
23211ac8d5baSMatthew Dillon
23221ac8d5baSMatthew Dillon ccb = sili_get_ccb(ap);
23231ac8d5baSMatthew Dillon if (ccb == NULL) {
23241ac8d5baSMatthew Dillon DPRINTF(SILI_D_XFER, "%s: sili_ata_get_xfer: NULL ccb\n",
23251ac8d5baSMatthew Dillon PORTNAME(ap));
23261ac8d5baSMatthew Dillon return (NULL);
23271ac8d5baSMatthew Dillon }
23281ac8d5baSMatthew Dillon
23291ac8d5baSMatthew Dillon DPRINTF(SILI_D_XFER, "%s: sili_ata_get_xfer got slot %d\n",
23301ac8d5baSMatthew Dillon PORTNAME(ap), ccb->ccb_slot);
23311ac8d5baSMatthew Dillon
23321ac8d5baSMatthew Dillon bzero(ccb->ccb_xa.fis, sizeof(*ccb->ccb_xa.fis));
23331ac8d5baSMatthew Dillon ccb->ccb_xa.at = at;
23341ac8d5baSMatthew Dillon ccb->ccb_xa.fis->type = ATA_FIS_TYPE_H2D;
23351ac8d5baSMatthew Dillon
23361ac8d5baSMatthew Dillon return (&ccb->ccb_xa);
23371ac8d5baSMatthew Dillon }
23381ac8d5baSMatthew Dillon
23391ac8d5baSMatthew Dillon void
sili_ata_put_xfer(struct ata_xfer * xa)23401ac8d5baSMatthew Dillon sili_ata_put_xfer(struct ata_xfer *xa)
23411ac8d5baSMatthew Dillon {
23421ac8d5baSMatthew Dillon struct sili_ccb *ccb = (struct sili_ccb *)xa;
23431ac8d5baSMatthew Dillon
23441ac8d5baSMatthew Dillon DPRINTF(SILI_D_XFER, "sili_ata_put_xfer slot %d\n", ccb->ccb_slot);
23451ac8d5baSMatthew Dillon
23461ac8d5baSMatthew Dillon sili_put_ccb(ccb);
23471ac8d5baSMatthew Dillon }
23481ac8d5baSMatthew Dillon
23491ac8d5baSMatthew Dillon int
sili_ata_cmd(struct ata_xfer * xa)23501ac8d5baSMatthew Dillon sili_ata_cmd(struct ata_xfer *xa)
23511ac8d5baSMatthew Dillon {
23521ac8d5baSMatthew Dillon struct sili_ccb *ccb = (struct sili_ccb *)xa;
23531ac8d5baSMatthew Dillon
23541ac8d5baSMatthew Dillon KKASSERT(xa->state == ATA_S_SETUP);
23551ac8d5baSMatthew Dillon
23561ac8d5baSMatthew Dillon if (ccb->ccb_port->ap_state == AP_S_FATAL_ERROR)
23571ac8d5baSMatthew Dillon goto failcmd;
23581ac8d5baSMatthew Dillon #if 0
2359c3783d8fSzrj kprintf("%s: started std command %pb%i ccb %d ccb_at %p %d\n",
23601ac8d5baSMatthew Dillon ATANAME(ccb->ccb_port, ccb->ccb_xa.at),
2361c3783d8fSzrj SILI_PFMT_CMD, sili_pread(ccb->ccb_port, SILI_PREG_CMD),
23621ac8d5baSMatthew Dillon ccb->ccb_slot,
23631ac8d5baSMatthew Dillon ccb->ccb_xa.at,
23641ac8d5baSMatthew Dillon ccb->ccb_xa.at ? ccb->ccb_xa.at->at_target : -1);
23651ac8d5baSMatthew Dillon #endif
23661ac8d5baSMatthew Dillon
23671ac8d5baSMatthew Dillon ccb->ccb_done = sili_ata_cmd_done;
23681ac8d5baSMatthew Dillon
23691ac8d5baSMatthew Dillon if (sili_load_prb(ccb) != 0)
23701ac8d5baSMatthew Dillon goto failcmd;
23711ac8d5baSMatthew Dillon
23721ac8d5baSMatthew Dillon xa->state = ATA_S_PENDING;
23731ac8d5baSMatthew Dillon
23741ac8d5baSMatthew Dillon if (xa->flags & ATA_F_POLL)
23751ac8d5baSMatthew Dillon return (sili_poll(ccb, xa->timeout, sili_ata_cmd_timeout));
23761ac8d5baSMatthew Dillon
23771ac8d5baSMatthew Dillon crit_enter();
23781ac8d5baSMatthew Dillon KKASSERT((xa->flags & ATA_F_TIMEOUT_EXPIRED) == 0);
23791ac8d5baSMatthew Dillon xa->flags |= ATA_F_TIMEOUT_DESIRED;
23801ac8d5baSMatthew Dillon sili_start(ccb);
23811ac8d5baSMatthew Dillon crit_exit();
23821ac8d5baSMatthew Dillon return (xa->state);
23831ac8d5baSMatthew Dillon
23841ac8d5baSMatthew Dillon failcmd:
23851ac8d5baSMatthew Dillon crit_enter();
23861ac8d5baSMatthew Dillon xa->state = ATA_S_ERROR;
23871ac8d5baSMatthew Dillon xa->complete(xa);
23881ac8d5baSMatthew Dillon crit_exit();
23891ac8d5baSMatthew Dillon return (ATA_S_ERROR);
23901ac8d5baSMatthew Dillon }
23911ac8d5baSMatthew Dillon
23921ac8d5baSMatthew Dillon static void
sili_ata_cmd_done(struct sili_ccb * ccb)23931ac8d5baSMatthew Dillon sili_ata_cmd_done(struct sili_ccb *ccb)
23941ac8d5baSMatthew Dillon {
23951ac8d5baSMatthew Dillon struct ata_xfer *xa = &ccb->ccb_xa;
2396e27434d7SMatthew Dillon int serial;
23971ac8d5baSMatthew Dillon
23981ac8d5baSMatthew Dillon /*
23991ac8d5baSMatthew Dillon * NOTE: callout does not lock port and may race us modifying
24001ac8d5baSMatthew Dillon * the flags, so make sure its stopped.
24011ac8d5baSMatthew Dillon */
24021ac8d5baSMatthew Dillon if (xa->flags & ATA_F_TIMEOUT_RUNNING) {
2403e27434d7SMatthew Dillon serial = ccb->ccb_xa.serial;
2404eb67213aSMatthew Dillon callout_cancel(&ccb->ccb_timeout);
2405e27434d7SMatthew Dillon if (serial != ccb->ccb_xa.serial) {
2406e27434d7SMatthew Dillon kprintf("%s: Warning: timeout race ccb %p\n",
2407e27434d7SMatthew Dillon PORTNAME(ccb->ccb_port), ccb);
2408e27434d7SMatthew Dillon return;
2409e27434d7SMatthew Dillon }
24101ac8d5baSMatthew Dillon xa->flags &= ~ATA_F_TIMEOUT_RUNNING;
24111ac8d5baSMatthew Dillon }
24121ac8d5baSMatthew Dillon xa->flags &= ~(ATA_F_TIMEOUT_DESIRED | ATA_F_TIMEOUT_EXPIRED);
24131ac8d5baSMatthew Dillon
24141ac8d5baSMatthew Dillon KKASSERT(xa->state != ATA_S_ONCHIP);
24151ac8d5baSMatthew Dillon sili_unload_prb(ccb);
24161ac8d5baSMatthew Dillon
24171ac8d5baSMatthew Dillon if (xa->state != ATA_S_TIMEOUT)
24181ac8d5baSMatthew Dillon xa->complete(xa);
24191ac8d5baSMatthew Dillon }
24201ac8d5baSMatthew Dillon
24211ac8d5baSMatthew Dillon /*
24221ac8d5baSMatthew Dillon * Timeout from callout, MPSAFE - nothing can mess with the CCB's flags
24231ac8d5baSMatthew Dillon * while the callout is runing.
24241ac8d5baSMatthew Dillon *
24251ac8d5baSMatthew Dillon * We can't safely get the port lock here or delay, we could block
24261ac8d5baSMatthew Dillon * the callout thread.
24271ac8d5baSMatthew Dillon */
24281ac8d5baSMatthew Dillon static void
sili_ata_cmd_timeout_unserialized(void * arg)24291ac8d5baSMatthew Dillon sili_ata_cmd_timeout_unserialized(void *arg)
24301ac8d5baSMatthew Dillon {
24311ac8d5baSMatthew Dillon struct sili_ccb *ccb = arg;
24321ac8d5baSMatthew Dillon struct sili_port *ap = ccb->ccb_port;
24331ac8d5baSMatthew Dillon
24341ac8d5baSMatthew Dillon ccb->ccb_xa.flags &= ~ATA_F_TIMEOUT_RUNNING;
24351ac8d5baSMatthew Dillon ccb->ccb_xa.flags |= ATA_F_TIMEOUT_EXPIRED;
24361ac8d5baSMatthew Dillon sili_os_signal_port_thread(ap, AP_SIGF_TIMEOUT);
24371ac8d5baSMatthew Dillon }
24381ac8d5baSMatthew Dillon
24391ac8d5baSMatthew Dillon void
sili_ata_cmd_timeout(struct sili_ccb * ccb)24401ac8d5baSMatthew Dillon sili_ata_cmd_timeout(struct sili_ccb *ccb)
24411ac8d5baSMatthew Dillon {
2442132408ffSMatthew Dillon sili_core_timeout(ccb, 0);
24431ac8d5baSMatthew Dillon }
24441ac8d5baSMatthew Dillon
24451ac8d5baSMatthew Dillon /*
24461ac8d5baSMatthew Dillon * Timeout code, typically called when the port command processor is running.
24471ac8d5baSMatthew Dillon *
24481ac8d5baSMatthew Dillon * Returns 0 if all timeout processing completed, non-zero if it is still
24491ac8d5baSMatthew Dillon * in progress.
24501ac8d5baSMatthew Dillon */
24511ac8d5baSMatthew Dillon static
24521ac8d5baSMatthew Dillon int
sili_core_timeout(struct sili_ccb * ccb,int really_error)2453132408ffSMatthew Dillon sili_core_timeout(struct sili_ccb *ccb, int really_error)
24541ac8d5baSMatthew Dillon {
24551ac8d5baSMatthew Dillon struct ata_xfer *xa = &ccb->ccb_xa;
24561ac8d5baSMatthew Dillon struct sili_port *ap = ccb->ccb_port;
24571ac8d5baSMatthew Dillon struct ata_port *at;
24581ac8d5baSMatthew Dillon
24591ac8d5baSMatthew Dillon at = ccb->ccb_xa.at;
24601ac8d5baSMatthew Dillon
2461132408ffSMatthew Dillon kprintf("%s: CMD %s state=%d slot=%d\n"
24621ac8d5baSMatthew Dillon "\t active=%08x\n"
24631ac8d5baSMatthew Dillon "\texpired=%08x\n"
24641ac8d5baSMatthew Dillon "\thactive=%08x\n",
24651ac8d5baSMatthew Dillon ATANAME(ap, at),
2466132408ffSMatthew Dillon (really_error ? "ERROR" : "TIMEOUT"),
24671ac8d5baSMatthew Dillon ccb->ccb_xa.state, ccb->ccb_slot,
24681ac8d5baSMatthew Dillon ap->ap_active,
24691ac8d5baSMatthew Dillon ap->ap_expired,
24701ac8d5baSMatthew Dillon sili_pread(ap, SILI_PREG_SLOTST)
24711ac8d5baSMatthew Dillon );
24721ac8d5baSMatthew Dillon
24731ac8d5baSMatthew Dillon /*
24741ac8d5baSMatthew Dillon * NOTE: Timeout will not be running if the command was polled.
24751ac8d5baSMatthew Dillon * If we got here at least one of these flags should be set.
24761ac8d5baSMatthew Dillon *
24771ac8d5baSMatthew Dillon * However, it might be running if we are called from the
24781ac8d5baSMatthew Dillon * interrupt error handling code.
24791ac8d5baSMatthew Dillon */
24801ac8d5baSMatthew Dillon KKASSERT(xa->flags & (ATA_F_POLL | ATA_F_TIMEOUT_DESIRED |
24811ac8d5baSMatthew Dillon ATA_F_TIMEOUT_RUNNING));
24821ac8d5baSMatthew Dillon if (xa->flags & ATA_F_TIMEOUT_RUNNING) {
24831ac8d5baSMatthew Dillon callout_stop(&ccb->ccb_timeout);
24841ac8d5baSMatthew Dillon xa->flags &= ~ATA_F_TIMEOUT_RUNNING;
24851ac8d5baSMatthew Dillon }
24861ac8d5baSMatthew Dillon xa->flags &= ~ATA_F_TIMEOUT_EXPIRED;
24871ac8d5baSMatthew Dillon
24881ac8d5baSMatthew Dillon if (ccb->ccb_xa.state == ATA_S_PENDING) {
24891ac8d5baSMatthew Dillon TAILQ_REMOVE(&ap->ap_ccb_pending, ccb, ccb_entry);
24901ac8d5baSMatthew Dillon ccb->ccb_xa.state = ATA_S_TIMEOUT;
24911ac8d5baSMatthew Dillon ccb->ccb_done(ccb);
24921ac8d5baSMatthew Dillon xa->complete(xa);
24931ac8d5baSMatthew Dillon sili_issue_pending_commands(ap, NULL);
24941ac8d5baSMatthew Dillon return(1);
24951ac8d5baSMatthew Dillon }
24961ac8d5baSMatthew Dillon if (ccb->ccb_xa.state != ATA_S_ONCHIP) {
24971ac8d5baSMatthew Dillon kprintf("%s: Unexpected state during timeout: %d\n",
24981ac8d5baSMatthew Dillon ATANAME(ap, at), ccb->ccb_xa.state);
24991ac8d5baSMatthew Dillon return(1);
25001ac8d5baSMatthew Dillon }
25011ac8d5baSMatthew Dillon
25021ac8d5baSMatthew Dillon /*
25031ac8d5baSMatthew Dillon * We can't process timeouts while other commands are running.
25041ac8d5baSMatthew Dillon */
25051ac8d5baSMatthew Dillon ap->ap_expired |= 1 << ccb->ccb_slot;
25061ac8d5baSMatthew Dillon
25071ac8d5baSMatthew Dillon if (ap->ap_active != ap->ap_expired) {
25081ac8d5baSMatthew Dillon kprintf("%s: Deferred timeout until its safe, slot %d\n",
25091ac8d5baSMatthew Dillon ATANAME(ap, at), ccb->ccb_slot);
25101ac8d5baSMatthew Dillon return(1);
25111ac8d5baSMatthew Dillon }
25121ac8d5baSMatthew Dillon
25131ac8d5baSMatthew Dillon /*
25141ac8d5baSMatthew Dillon * We have to issue a Port reinit. We don't read an error log
25151ac8d5baSMatthew Dillon * page for timeouts. Reiniting the port will clear all pending
25161ac8d5baSMatthew Dillon * commands.
25171ac8d5baSMatthew Dillon */
25181ac8d5baSMatthew Dillon sili_port_reinit(ap);
25191ac8d5baSMatthew Dillon return(0);
25201ac8d5baSMatthew Dillon }
25211ac8d5baSMatthew Dillon
25221ac8d5baSMatthew Dillon /*
2523a35ddbb4SMatthew Dillon * Used by the softreset, pm_port_probe, and read_ncq_error only, in very
25241ac8d5baSMatthew Dillon * specialized, controlled circumstances.
25251ac8d5baSMatthew Dillon */
25261ac8d5baSMatthew Dillon void
sili_quick_timeout(struct sili_ccb * ccb)25271ac8d5baSMatthew Dillon sili_quick_timeout(struct sili_ccb *ccb)
25281ac8d5baSMatthew Dillon {
25291ac8d5baSMatthew Dillon struct sili_port *ap = ccb->ccb_port;
25301ac8d5baSMatthew Dillon
25311ac8d5baSMatthew Dillon switch (ccb->ccb_xa.state) {
25321ac8d5baSMatthew Dillon case ATA_S_PENDING:
25331ac8d5baSMatthew Dillon TAILQ_REMOVE(&ap->ap_ccb_pending, ccb, ccb_entry);
25341ac8d5baSMatthew Dillon ccb->ccb_xa.state = ATA_S_TIMEOUT;
25351ac8d5baSMatthew Dillon break;
25361ac8d5baSMatthew Dillon case ATA_S_ONCHIP:
25371ac8d5baSMatthew Dillon KKASSERT((ap->ap_active & ~ap->ap_expired) ==
25381ac8d5baSMatthew Dillon (1 << ccb->ccb_slot));
25391ac8d5baSMatthew Dillon ccb->ccb_xa.state = ATA_S_TIMEOUT;
25401ac8d5baSMatthew Dillon ap->ap_active &= ~(1 << ccb->ccb_slot);
25411ac8d5baSMatthew Dillon KKASSERT(ap->ap_active_cnt > 0);
25421ac8d5baSMatthew Dillon --ap->ap_active_cnt;
25431ac8d5baSMatthew Dillon sili_port_reinit(ap);
25441ac8d5baSMatthew Dillon break;
25451ac8d5baSMatthew Dillon default:
25461ac8d5baSMatthew Dillon panic("%s: sili_quick_timeout: ccb in bad state %d",
25471ac8d5baSMatthew Dillon ATANAME(ap, ccb->ccb_xa.at), ccb->ccb_xa.state);
25481ac8d5baSMatthew Dillon }
25491ac8d5baSMatthew Dillon }
25501ac8d5baSMatthew Dillon
25511ac8d5baSMatthew Dillon static void
sili_dummy_done(struct ata_xfer * xa)25521ac8d5baSMatthew Dillon sili_dummy_done(struct ata_xfer *xa)
25531ac8d5baSMatthew Dillon {
25541ac8d5baSMatthew Dillon }
25551ac8d5baSMatthew Dillon
25561ac8d5baSMatthew Dillon static void
sili_empty_done(struct sili_ccb * ccb)25571ac8d5baSMatthew Dillon sili_empty_done(struct sili_ccb *ccb)
25581ac8d5baSMatthew Dillon {
25591ac8d5baSMatthew Dillon }
2560