svn commit: r207536 - in head: share/man/man4 sys/arm/mv sys/conf sys/dev/mvs sys/modules sys/modules/mvs

Alexander Motin mav at FreeBSD.org
Sun May 2 19:28:31 UTC 2010


Author: mav
Date: Sun May  2 19:28:30 2010
New Revision: 207536
URL: http://svn.freebsd.org/changeset/base/207536

Log:
  Import mvs(4) - Marvell 88SX50XX/88SX60XX/88SX70XX/SoC SATA controllers
  driver for CAM ATA subsystem. This driver supports same hardware as
  atamarvell, ataadaptec and atamvsata drivers from ata(4), but provides
  many additional features, such as NCQ, PMP, etc.

Added:
  head/share/man/man4/mvs.4   (contents, props changed)
  head/sys/dev/mvs/
  head/sys/dev/mvs/mvs.c   (contents, props changed)
  head/sys/dev/mvs/mvs.h   (contents, props changed)
  head/sys/dev/mvs/mvs_if.m   (contents, props changed)
  head/sys/dev/mvs/mvs_pci.c   (contents, props changed)
  head/sys/dev/mvs/mvs_soc.c   (contents, props changed)
  head/sys/modules/mvs/
  head/sys/modules/mvs/Makefile   (contents, props changed)
Modified:
  head/share/man/man4/Makefile
  head/sys/arm/mv/files.mv
  head/sys/conf/NOTES
  head/sys/conf/files
  head/sys/conf/kmod.mk
  head/sys/modules/Makefile

Modified: head/share/man/man4/Makefile
==============================================================================
--- head/share/man/man4/Makefile	Sun May  2 19:25:22 2010	(r207535)
+++ head/share/man/man4/Makefile	Sun May  2 19:28:30 2010	(r207536)
@@ -220,6 +220,7 @@ MAN=	aac.4 \
 	msk.4 \
 	mtio.4 \
 	multicast.4 \
+	mvs.4 \
 	mwl.4 \
 	mwlfw.4 \
 	mxge.4 \

Added: head/share/man/man4/mvs.4
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/share/man/man4/mvs.4	Sun May  2 19:28:30 2010	(r207536)
@@ -0,0 +1,176 @@
+.\" Copyright (c) 2009 Alexander Motin <mav at FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\"    derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 27, 2010
+.Dt MVS 4
+.Os
+.Sh NAME
+.Nm mvs
+.Nd Marvell Serial ATA Host Controller driver
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following lines in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device pci"
+.Cd "device scbus"
+.Cd "device mvs"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+mvs_load="YES"
+.Ed
+.Pp
+The following tunables are settable from the
+.Xr loader 8 :
+.Bl -ohang
+.It Va hint.mvs. Ns Ar X Ns Va .msi
+controls Message Signaled Interrupts (MSI) usage by the specified controller.
+.It Va hint.mvs. Ns Ar X Ns Va .ccc
+controls Command Completion Coalescing (CCC) usage by the specified controller.
+Non-zero value enables CCC and defines maximum time (in us), request can wait
+for interrupt.
+CCC reduces number of context switches on systems with many parallel requests,
+but it can decrease disk performance on some workloads due to additional
+command latency.
+.It Va hint.mvs. Ns Ar X Ns Va .cccc
+defines number of completed commands for CCC, which trigger interrupt without
+waiting for specified coalescing timeout.
+.It Va hint.mvs. Ns Ar X Ns Va .pm_level
+controls SATA interface Power Management for the specified channel,
+allowing some power to be saved at the cost of additional command
+latency.
+Possible values:
+.Bl -tag -compact
+.It 0
+interface Power Management is disabled (default);
+.It 1
+device is allowed to initiate PM state change, host is passive;
+.It 4
+driver initiates PARTIAL PM state transition 1ms after port becomes idle;
+.It 5
+driver initiates SLUMBER PM state transition 125ms after port becomes idle.
+.El
+.Pp
+Note that interface Power Management is not compatible with
+device presence detection.
+A manual bus reset is needed on device hot-plug.
+.It Va hint.mvs. Ns Ar X Ns Va .sata_rev
+setting to nonzero value limits maximum SATA revision (speed).
+Values 1, 2 and 3 are respectively 1.5, 3 and 6Gbps.
+.El
+.Sh DESCRIPTION
+This driver provides the
+.Xr CAM 4
+subsystem with native access to the
+.Tn SATA
+ports of several generations (Gen-I/II/IIe) of Marvell SATA controllers.
+Each SATA port found is represented to CAM as a separate bus with one
+target, or, if HBA supports Port Multipliers (Gen-II/IIe), 16 targets.
+Most of the bus-management details are handled by the SATA-specific
+transport of CAM.
+Connected ATA disks are handled by the ATA protocol disk peripheral driver
+.Xr ada 4 .
+ATAPI devices are handled by the SCSI protocol peripheral drivers
+.Xr cd 4 ,
+.Xr da 4 ,
+.Xr sa 4 ,
+etc.
+.Pp
+Driver features include support for Serial ATA and ATAPI devices,
+Port Multipliers (including FIS-based switching, when supported),
+hardware command queues (up to 31 command per port),
+Native Command Queuing, SATA interface Power Management, device hot-plug
+and Message Signaled Interrupts.
+.Pp
+Same hardware is also supported by atamarvell and ataadaptec drivers from
+.Xr ata 4
+subsystem.
+If both drivers are loaded at the same time, this one will be
+given precedence as the more functional of the two.
+.Sh HARDWARE
+The
+.Nm
+driver supports the following controllers:
+.Bl -tag -compact
+.It Gen-I (SATA 1.5Gbps):
+.Bl -bullet -compact
+.It
+88SX5040
+.It
+88SX5041
+.It
+88SX5080
+.It
+88SX5081
+.El
+.It Gen-II (SATA 3Gbps, NCQ, PMP):
+.Bl -bullet -compact
+.It
+88SX6040
+.It
+88SX6041 (including Adaptec 1420SA)
+.It
+88SX6080
+.It
+88SX6081
+.El
+.It Gen-IIe (SATA 3Gbps, NCQ, PMP with FBS):
+.Bl -bullet -compact
+.It
+88SX6042
+.It
+88SX7042 (including Adaptec 1430SA)
+.It
+88F5182 SoC
+.It
+88F6281 SoC
+.It
+MV78100 SoC
+.El
+.El
+Note, that this hardware supports command queueing and FIS-based switching
+only for ATA DMA commands. ATAPI and non-DMA ATA commands executed one by one
+for each port.
+.Pp
+.Sh SEE ALSO
+.Xr ada 4 ,
+.Xr ata 4 ,
+.Xr cam 4 ,
+.Xr cd 4 ,
+.Xr da 4 ,
+.Xr sa 4
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Fx 9.0 .
+.Sh AUTHORS
+.An Alexander Motin Aq mav at FreeBSD.org .

Modified: head/sys/arm/mv/files.mv
==============================================================================
--- head/sys/arm/mv/files.mv	Sun May  2 19:25:22 2010	(r207535)
+++ head/sys/arm/mv/files.mv	Sun May  2 19:28:30 2010	(r207536)
@@ -30,6 +30,7 @@ arm/mv/timer.c			standard
 arm/mv/twsi.c			optional	iicbus
 
 dev/mge/if_mge.c		optional	mge
+dev/mvs/mvs_soc.c		optional	mvs
 dev/uart/uart_bus_mbus.c	optional	uart
 dev/uart/uart_cpu_mv.c		optional	uart
 dev/uart/uart_dev_ns8250.c	optional	uart

Modified: head/sys/conf/NOTES
==============================================================================
--- head/sys/conf/NOTES	Sun May  2 19:25:22 2010	(r207535)
+++ head/sys/conf/NOTES	Sun May  2 19:28:30 2010	(r207536)
@@ -1660,12 +1660,14 @@ device		twe		# 3ware ATA RAID
 # Serial ATA host controllers:
 #
 # ahci: Advanced Host Controller Interface (AHCI) compatible
+# mvs:  Marvell 88SX50XX/88SX60XX/88SX70XX/SoC controllers
 # siis: SiliconImage SiI3124/SiI3132/SiI3531 controllers
 #
 # These drivers are part of cam(4) subsystem. They supersede less featured
 # ata(4) subsystem drivers, supporting same hardware.
 
 device		ahci
+device		mvs
 device		siis
 
 #

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Sun May  2 19:25:22 2010	(r207535)
+++ head/sys/conf/files	Sun May  2 19:28:30 2010	(r207536)
@@ -1293,6 +1293,9 @@ dev/mpt/mpt_pci.c		optional mpt pci
 dev/mpt/mpt_raid.c		optional mpt
 dev/mpt/mpt_user.c		optional mpt
 dev/msk/if_msk.c		optional msk inet
+dev/mvs/mvs.c			optional mvs
+dev/mvs/mvs_if.m		standard
+dev/mvs/mvs_pci.c		optional mvs pci
 dev/mwl/if_mwl.c		optional mwl
 dev/mwl/if_mwl_pci.c		optional mwl pci
 dev/mwl/mwlhal.c		optional mwl

Modified: head/sys/conf/kmod.mk
==============================================================================
--- head/sys/conf/kmod.mk	Sun May  2 19:25:22 2010	(r207535)
+++ head/sys/conf/kmod.mk	Sun May  2 19:28:30 2010	(r207536)
@@ -342,7 +342,7 @@ MFILES?= dev/acpica/acpi_if.m dev/acpi_s
 	dev/agp/agp_if.m dev/ata/ata_if.m dev/eisa/eisa_if.m \
 	dev/iicbus/iicbb_if.m dev/iicbus/iicbus_if.m \
 	dev/mmc/mmcbr_if.m dev/mmc/mmcbus_if.m \
-	dev/mii/miibus_if.m dev/ofw/ofw_bus_if.m \
+	dev/mii/miibus_if.m dev/mvs/mvs_if.m dev/ofw/ofw_bus_if.m \
 	dev/pccard/card_if.m dev/pccard/power_if.m dev/pci/pci_if.m \
 	dev/pci/pcib_if.m dev/ppbus/ppbus_if.m dev/smbus/smbus_if.m \
 	dev/sound/pcm/ac97_if.m dev/sound/pcm/channel_if.m \

Added: head/sys/dev/mvs/mvs.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/mvs/mvs.c	Sun May  2 19:28:30 2010	(r207536)
@@ -0,0 +1,2173 @@
+/*-
+ * Copyright (c) 2010 Alexander Motin <mav at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/ata.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <vm/uma.h>
+#include <machine/stdarg.h>
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include "mvs.h"
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_debug.h>
+
+/* local prototypes */
+static int mvs_ch_suspend(device_t dev);
+static int mvs_ch_resume(device_t dev);
+static void mvs_dmainit(device_t dev);
+static void mvs_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error);
+static void mvs_dmafini(device_t dev);
+static void mvs_slotsalloc(device_t dev);
+static void mvs_slotsfree(device_t dev);
+static void mvs_setup_edma_queues(device_t dev);
+static void mvs_set_edma_mode(device_t dev, enum mvs_edma_mode mode);
+static void mvs_ch_pm(void *arg);
+static void mvs_ch_intr_locked(void *data);
+static void mvs_ch_intr(void *data);
+static void mvs_reset(device_t dev);
+static void mvs_softreset(device_t dev, union ccb *ccb);
+
+static int mvs_sata_connect(struct mvs_channel *ch);
+static int mvs_sata_phy_reset(device_t dev);
+static int mvs_wait(device_t dev, u_int s, u_int c, int t);
+static void mvs_tfd_read(device_t dev, union ccb *ccb);
+static void mvs_tfd_write(device_t dev, union ccb *ccb);
+static void mvs_legacy_intr(device_t dev);
+static void mvs_crbq_intr(device_t dev);
+static void mvs_begin_transaction(device_t dev, union ccb *ccb);
+static void mvs_legacy_execute_transaction(struct mvs_slot *slot);
+static void mvs_timeout(struct mvs_slot *slot);
+static void mvs_dmasetprd(void *arg, bus_dma_segment_t *segs, int nsegs, int error);
+static void mvs_requeue_frozen(device_t dev);
+static void mvs_execute_transaction(struct mvs_slot *slot);
+static void mvs_end_transaction(struct mvs_slot *slot, enum mvs_err_type et);
+
+static void mvs_issue_read_log(device_t dev);
+static void mvs_process_read_log(device_t dev, union ccb *ccb);
+
+static void mvsaction(struct cam_sim *sim, union ccb *ccb);
+static void mvspoll(struct cam_sim *sim);
+
+MALLOC_DEFINE(M_MVS, "MVS driver", "MVS driver data buffers");
+
+static int
+mvs_ch_probe(device_t dev)
+{
+
+	device_set_desc_copy(dev, "Marvell SATA channel");
+	return (0);
+}
+
+static int
+mvs_ch_attach(device_t dev)
+{
+	struct mvs_controller *ctlr = device_get_softc(device_get_parent(dev));
+	struct mvs_channel *ch = device_get_softc(dev);
+	struct cam_devq *devq;
+	int rid, error, i, sata_rev = 0;
+
+	ch->dev = dev;
+	ch->unit = (intptr_t)device_get_ivars(dev);
+	ch->quirks = ctlr->quirks;
+	mtx_init(&ch->mtx, "MVS channel lock", NULL, MTX_DEF);
+	resource_int_value(device_get_name(dev),
+	    device_get_unit(dev), "pm_level", &ch->pm_level);
+	if (ch->pm_level > 3)
+		callout_init_mtx(&ch->pm_timer, &ch->mtx, 0);
+	resource_int_value(device_get_name(dev),
+	    device_get_unit(dev), "sata_rev", &sata_rev);
+	for (i = 0; i < 16; i++) {
+		ch->user[i].revision = sata_rev;
+		ch->user[i].mode = 0;
+		ch->user[i].bytecount = (ch->quirks & MVS_Q_GENIIE) ? 8192 : 2048;
+		ch->user[i].tags = MVS_MAX_SLOTS;
+		ch->curr[i] = ch->user[i];
+		if (ch->pm_level) {
+			ch->user[i].caps = CTS_SATA_CAPS_H_PMREQ |
+			    CTS_SATA_CAPS_H_APST |
+			    CTS_SATA_CAPS_D_PMREQ | CTS_SATA_CAPS_D_APST;
+		}
+	}
+	rid = ch->unit;
+	if (!(ch->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+	    &rid, RF_ACTIVE)))
+		return (ENXIO);
+	mvs_dmainit(dev);
+	mvs_slotsalloc(dev);
+	mvs_ch_resume(dev);
+	mtx_lock(&ch->mtx);
+	rid = ATA_IRQ_RID;
+	if (!(ch->r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+	    &rid, RF_SHAREABLE | RF_ACTIVE))) {
+		device_printf(dev, "Unable to map interrupt\n");
+		error = ENXIO;
+		goto err0;
+	}
+	if ((bus_setup_intr(dev, ch->r_irq, ATA_INTR_FLAGS, NULL,
+	    mvs_ch_intr_locked, dev, &ch->ih))) {
+		device_printf(dev, "Unable to setup interrupt\n");
+		error = ENXIO;
+		goto err1;
+	}
+	/* Create the device queue for our SIM. */
+	devq = cam_simq_alloc(MVS_MAX_SLOTS - 1);
+	if (devq == NULL) {
+		device_printf(dev, "Unable to allocate simq\n");
+		error = ENOMEM;
+		goto err1;
+	}
+	/* Construct SIM entry */
+	ch->sim = cam_sim_alloc(mvsaction, mvspoll, "mvsch", ch,
+	    device_get_unit(dev), &ch->mtx,
+	    2, (ch->quirks & MVS_Q_GENI) ? 0 : MVS_MAX_SLOTS - 1,
+	    devq);
+	if (ch->sim == NULL) {
+		cam_simq_free(devq);
+		device_printf(dev, "unable to allocate sim\n");
+		error = ENOMEM;
+		goto err1;
+	}
+	if (xpt_bus_register(ch->sim, dev, 0) != CAM_SUCCESS) {
+		device_printf(dev, "unable to register xpt bus\n");
+		error = ENXIO;
+		goto err2;
+	}
+	if (xpt_create_path(&ch->path, /*periph*/NULL, cam_sim_path(ch->sim),
+	    CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+		device_printf(dev, "unable to create path\n");
+		error = ENXIO;
+		goto err3;
+	}
+	if (ch->pm_level > 3) {
+		callout_reset(&ch->pm_timer,
+		    (ch->pm_level == 4) ? hz / 1000 : hz / 8,
+		    mvs_ch_pm, dev);
+	}
+	mtx_unlock(&ch->mtx);
+	return (0);
+
+err3:
+	xpt_bus_deregister(cam_sim_path(ch->sim));
+err2:
+	cam_sim_free(ch->sim, /*free_devq*/TRUE);
+err1:
+	bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq);
+err0:
+	bus_release_resource(dev, SYS_RES_MEMORY, ch->unit, ch->r_mem);
+	mtx_unlock(&ch->mtx);
+	mtx_destroy(&ch->mtx);
+	return (error);
+}
+
+static int
+mvs_ch_detach(device_t dev)
+{
+	struct mvs_channel *ch = device_get_softc(dev);
+
+	mtx_lock(&ch->mtx);
+	xpt_async(AC_LOST_DEVICE, ch->path, NULL);
+	xpt_free_path(ch->path);
+	xpt_bus_deregister(cam_sim_path(ch->sim));
+	cam_sim_free(ch->sim, /*free_devq*/TRUE);
+	mtx_unlock(&ch->mtx);
+
+	if (ch->pm_level > 3)
+		callout_drain(&ch->pm_timer);
+	bus_teardown_intr(dev, ch->r_irq, ch->ih);
+	bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq);
+
+	mvs_ch_suspend(dev);
+	mvs_slotsfree(dev);
+	mvs_dmafini(dev);
+
+	bus_release_resource(dev, SYS_RES_MEMORY, ch->unit, ch->r_mem);
+	mtx_destroy(&ch->mtx);
+	return (0);
+}
+
+static int
+mvs_ch_suspend(device_t dev)
+{
+	struct mvs_channel *ch = device_get_softc(dev);
+
+	/* Stop EDMA */
+	mvs_set_edma_mode(dev, MVS_EDMA_OFF);
+	/* Disable port interrupts. */
+	ATA_OUTL(ch->r_mem, EDMA_IEM, 0);
+	return (0);
+}
+
+static int
+mvs_ch_resume(device_t dev)
+{
+	struct mvs_channel *ch = device_get_softc(dev);
+	uint32_t reg;
+
+	/* Disable port interrupts */
+	ATA_OUTL(ch->r_mem, EDMA_IEM, 0);
+	/* Stop EDMA */
+	ch->curr_mode = MVS_EDMA_UNKNOWN;
+	mvs_set_edma_mode(dev, MVS_EDMA_OFF);
+	/* Clear and configure FIS interrupts. */
+	ATA_OUTL(ch->r_mem, SATA_FISIC, 0);
+	reg = ATA_INL(ch->r_mem, SATA_FISC);
+	reg |= SATA_FISC_FISWAIT4HOSTRDYEN_B1;
+	ATA_OUTL(ch->r_mem, SATA_FISC, reg);
+	reg = ATA_INL(ch->r_mem, SATA_FISIM);
+	reg |= SATA_FISC_FISWAIT4HOSTRDYEN_B1;
+	ATA_OUTL(ch->r_mem, SATA_FISC, reg);
+	/* Clear SATA error register. */
+	ATA_OUTL(ch->r_mem, SATA_SE, 0xffffffff);
+	/* Clear any outstanding error interrupts. */
+	ATA_OUTL(ch->r_mem, EDMA_IEC, 0);
+	/* Unmask all error interrupts */
+	ATA_OUTL(ch->r_mem, EDMA_IEM, ~EDMA_IE_TRANSIENT);
+	return (0);
+}
+
+struct mvs_dc_cb_args {
+	bus_addr_t maddr;
+	int error;
+};
+
+static void
+mvs_dmainit(device_t dev)
+{
+	struct mvs_channel *ch = device_get_softc(dev);
+	struct mvs_dc_cb_args dcba;
+
+	/* EDMA command request area. */
+	if (bus_dma_tag_create(bus_get_dma_tag(dev), 1024, 0,
+	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR,
+	    NULL, NULL, MVS_WORKRQ_SIZE, 1, MVS_WORKRQ_SIZE,
+	    0, NULL, NULL, &ch->dma.workrq_tag))
+		goto error;
+	if (bus_dmamem_alloc(ch->dma.workrq_tag, (void **)&ch->dma.workrq, 0,
+	    &ch->dma.workrq_map))
+		goto error;
+	if (bus_dmamap_load(ch->dma.workrq_tag, ch->dma.workrq_map, ch->dma.workrq,
+	    MVS_WORKRQ_SIZE, mvs_dmasetupc_cb, &dcba, 0) || dcba.error) {
+		bus_dmamem_free(ch->dma.workrq_tag, ch->dma.workrq, ch->dma.workrq_map);
+		goto error;
+	}
+	ch->dma.workrq_bus = dcba.maddr;
+	/* EDMA command response area. */
+	if (bus_dma_tag_create(bus_get_dma_tag(dev), 256, 0,
+	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR,
+	    NULL, NULL, MVS_WORKRP_SIZE, 1, MVS_WORKRP_SIZE,
+	    0, NULL, NULL, &ch->dma.workrp_tag))
+		goto error;
+	if (bus_dmamem_alloc(ch->dma.workrp_tag, (void **)&ch->dma.workrp, 0,
+	    &ch->dma.workrp_map))
+		goto error;
+	if (bus_dmamap_load(ch->dma.workrp_tag, ch->dma.workrp_map, ch->dma.workrp,
+	    MVS_WORKRP_SIZE, mvs_dmasetupc_cb, &dcba, 0) || dcba.error) {
+		bus_dmamem_free(ch->dma.workrp_tag, ch->dma.workrp, ch->dma.workrp_map);
+		goto error;
+	}
+	ch->dma.workrp_bus = dcba.maddr;
+	/* Data area. */
+	if (bus_dma_tag_create(bus_get_dma_tag(dev), 2, MVS_EPRD_MAX,
+	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR,
+	    NULL, NULL,
+	    MVS_SG_ENTRIES * PAGE_SIZE * MVS_MAX_SLOTS,
+	    MVS_SG_ENTRIES, MVS_EPRD_MAX,
+	    0, busdma_lock_mutex, &ch->mtx, &ch->dma.data_tag)) {
+		goto error;
+	}
+	return;
+
+error:
+	device_printf(dev, "WARNING - DMA initialization failed\n");
+	mvs_dmafini(dev);
+}
+
+static void
+mvs_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
+{
+	struct mvs_dc_cb_args *dcba = (struct mvs_dc_cb_args *)xsc;
+
+	if (!(dcba->error = error))
+		dcba->maddr = segs[0].ds_addr;
+}
+
+static void
+mvs_dmafini(device_t dev)
+{
+	struct mvs_channel *ch = device_get_softc(dev);
+
+	if (ch->dma.data_tag) {
+		bus_dma_tag_destroy(ch->dma.data_tag);
+		ch->dma.data_tag = NULL;
+	}
+	if (ch->dma.workrp_bus) {
+		bus_dmamap_unload(ch->dma.workrp_tag, ch->dma.workrp_map);
+		bus_dmamem_free(ch->dma.workrp_tag, ch->dma.workrp, ch->dma.workrp_map);
+		ch->dma.workrp_bus = 0;
+		ch->dma.workrp_map = NULL;
+		ch->dma.workrp = NULL;
+	}
+	if (ch->dma.workrp_tag) {
+		bus_dma_tag_destroy(ch->dma.workrp_tag);
+		ch->dma.workrp_tag = NULL;
+	}
+	if (ch->dma.workrq_bus) {
+		bus_dmamap_unload(ch->dma.workrq_tag, ch->dma.workrq_map);
+		bus_dmamem_free(ch->dma.workrq_tag, ch->dma.workrq, ch->dma.workrq_map);
+		ch->dma.workrq_bus = 0;
+		ch->dma.workrq_map = NULL;
+		ch->dma.workrq = NULL;
+	}
+	if (ch->dma.workrq_tag) {
+		bus_dma_tag_destroy(ch->dma.workrq_tag);
+		ch->dma.workrq_tag = NULL;
+	}
+}
+
+static void
+mvs_slotsalloc(device_t dev)
+{
+	struct mvs_channel *ch = device_get_softc(dev);
+	int i;
+
+	/* Alloc and setup command/dma slots */
+	bzero(ch->slot, sizeof(ch->slot));
+	for (i = 0; i < MVS_MAX_SLOTS; i++) {
+		struct mvs_slot *slot = &ch->slot[i];
+
+		slot->dev = dev;
+		slot->slot = i;
+		slot->state = MVS_SLOT_EMPTY;
+		slot->ccb = NULL;
+		callout_init_mtx(&slot->timeout, &ch->mtx, 0);
+
+		if (bus_dmamap_create(ch->dma.data_tag, 0, &slot->dma.data_map))
+			device_printf(ch->dev, "FAILURE - create data_map\n");
+	}
+}
+
+static void
+mvs_slotsfree(device_t dev)
+{
+	struct mvs_channel *ch = device_get_softc(dev);
+	int i;
+
+	/* Free all dma slots */
+	for (i = 0; i < MVS_MAX_SLOTS; i++) {
+		struct mvs_slot *slot = &ch->slot[i];
+
+		callout_drain(&slot->timeout);
+		if (slot->dma.data_map) {
+			bus_dmamap_destroy(ch->dma.data_tag, slot->dma.data_map);
+			slot->dma.data_map = NULL;
+		}
+	}
+}
+
+static void
+mvs_setup_edma_queues(device_t dev)
+{
+	struct mvs_channel *ch = device_get_softc(dev);
+	uint64_t work;
+
+	/* Requests queue. */
+	work = ch->dma.workrq_bus;
+	ATA_OUTL(ch->r_mem, EDMA_REQQBAH, work >> 32);
+	ATA_OUTL(ch->r_mem, EDMA_REQQIP, work & 0xffffffff);
+	ATA_OUTL(ch->r_mem, EDMA_REQQOP, work & 0xffffffff);
+	bus_dmamap_sync(ch->dma.workrq_tag, ch->dma.workrq_map, BUS_DMASYNC_PREWRITE);
+	/* Reponses queue. */
+	bzero(ch->dma.workrp, 256);
+	work = ch->dma.workrp_bus;
+	ATA_OUTL(ch->r_mem, EDMA_RESQBAH, work >> 32);
+	ATA_OUTL(ch->r_mem, EDMA_RESQIP, work & 0xffffffff);
+	ATA_OUTL(ch->r_mem, EDMA_RESQOP, work & 0xffffffff);
+	bus_dmamap_sync(ch->dma.workrp_tag, ch->dma.workrp_map, BUS_DMASYNC_PREREAD);
+	ch->out_idx = 0;
+	ch->in_idx = 0;
+}
+
+static void
+mvs_set_edma_mode(device_t dev, enum mvs_edma_mode mode)
+{
+	struct mvs_channel *ch = device_get_softc(dev);
+	int timeout;
+	uint32_t ecfg, fcfg, hc, ltm, unkn;
+
+	if (mode == ch->curr_mode)
+		return;
+	/* If we are running, we should stop first. */
+	if (ch->curr_mode != MVS_EDMA_OFF) {
+		ATA_OUTL(ch->r_mem, EDMA_CMD, EDMA_CMD_EDSEDMA);
+		timeout = 0;
+		while (ATA_INL(ch->r_mem, EDMA_CMD) & EDMA_CMD_EENEDMA) {
+			DELAY(1000);
+			if (timeout++ > 1000) {
+				device_printf(dev, "stopping EDMA engine failed\n");
+				break;
+			}
+		};
+	}
+	ch->curr_mode = mode;
+	ch->fbs_enabled = 0;
+	ch->fake_busy = 0;
+	/* Report mode to controller. Needed for correct CCC operation. */
+	MVS_EDMA(device_get_parent(dev), dev, mode);
+	/* Configure new mode. */
+	ecfg = EDMA_CFG_RESERVED | EDMA_CFG_RESERVED2 | EDMA_CFG_EHOSTQUEUECACHEEN;
+	if (ch->pm_present) {
+		ecfg |= EDMA_CFG_EMASKRXPM;
+		if (ch->quirks & MVS_Q_GENIIE) {
+			ecfg |= EDMA_CFG_EEDMAFBS;
+			ch->fbs_enabled = 1;
+		}
+	}
+	if (ch->quirks & MVS_Q_GENI)
+		ecfg |= EDMA_CFG_ERDBSZ;
+	else if (ch->quirks & MVS_Q_GENII)
+		ecfg |= EDMA_CFG_ERDBSZEXT | EDMA_CFG_EWRBUFFERLEN;
+	if (ch->quirks & MVS_Q_CT)
+		ecfg |= EDMA_CFG_ECUTTHROUGHEN;
+	if (mode != MVS_EDMA_OFF)
+		ecfg |= EDMA_CFG_EEARLYCOMPLETIONEN;
+	if (mode == MVS_EDMA_QUEUED)
+		ecfg |= EDMA_CFG_EQUE;
+	else if (mode == MVS_EDMA_NCQ)
+		ecfg |= EDMA_CFG_ESATANATVCMDQUE;
+	ATA_OUTL(ch->r_mem, EDMA_CFG, ecfg);
+	mvs_setup_edma_queues(dev);
+	if (ch->quirks & MVS_Q_GENIIE) {
+		/* Configure FBS-related registers */
+		fcfg = ATA_INL(ch->r_mem, SATA_FISC);
+		ltm = ATA_INL(ch->r_mem, SATA_LTM);
+		hc = ATA_INL(ch->r_mem, EDMA_HC);
+		if (ch->fbs_enabled) {
+			fcfg |= SATA_FISC_FISDMAACTIVATESYNCRESP;
+			if (mode == MVS_EDMA_NCQ) {
+				fcfg &= ~SATA_FISC_FISWAIT4HOSTRDYEN_B0;
+				hc &= ~EDMA_IE_EDEVERR;
+			} else {
+				fcfg |= SATA_FISC_FISWAIT4HOSTRDYEN_B0;
+				hc |= EDMA_IE_EDEVERR;
+			}
+			ltm |= (1 << 8);
+		} else {
+			fcfg &= ~SATA_FISC_FISDMAACTIVATESYNCRESP;
+			fcfg &= ~SATA_FISC_FISWAIT4HOSTRDYEN_B0;
+			hc |= EDMA_IE_EDEVERR;
+			ltm &= ~(1 << 8);
+		}
+		ATA_OUTL(ch->r_mem, SATA_FISC, fcfg);
+		ATA_OUTL(ch->r_mem, SATA_LTM, ltm);
+		ATA_OUTL(ch->r_mem, EDMA_HC, hc);
+		/* This is some magic, required to handle several DRQs
+		 * with basic DMA. */
+		unkn = ATA_INL(ch->r_mem, EDMA_UNKN_RESD);
+		if (mode == MVS_EDMA_OFF)
+			unkn |= 1;
+		else
+			unkn &= ~1;
+		ATA_OUTL(ch->r_mem, EDMA_UNKN_RESD, unkn);
+	}
+	/* Run EDMA. */
+	if (mode != MVS_EDMA_OFF)
+		ATA_OUTL(ch->r_mem, EDMA_CMD, EDMA_CMD_EENEDMA);
+}
+
+devclass_t mvs_devclass;
+devclass_t mvsch_devclass;
+static device_method_t mvsch_methods[] = {
+	DEVMETHOD(device_probe,     mvs_ch_probe),
+	DEVMETHOD(device_attach,    mvs_ch_attach),
+	DEVMETHOD(device_detach,    mvs_ch_detach),
+	DEVMETHOD(device_suspend,   mvs_ch_suspend),
+	DEVMETHOD(device_resume,    mvs_ch_resume),
+	{ 0, 0 }
+};
+static driver_t mvsch_driver = {
+        "mvsch",
+        mvsch_methods,
+        sizeof(struct mvs_channel)
+};
+DRIVER_MODULE(mvsch, mvs, mvsch_driver, mvsch_devclass, 0, 0);
+DRIVER_MODULE(mvsch, sata, mvsch_driver, mvsch_devclass, 0, 0);
+
+static void
+mvs_phy_check_events(device_t dev, u_int32_t serr)
+{
+	struct mvs_channel *ch = device_get_softc(dev);
+
+	if (ch->pm_level == 0) {
+		u_int32_t status = ATA_INL(ch->r_mem, SATA_SS);
+		union ccb *ccb;
+
+		if (bootverbose) {
+			if (((status & SATA_SS_DET_MASK) == SATA_SS_DET_PHY_ONLINE) &&
+			    ((status & SATA_SS_SPD_MASK) != SATA_SS_SPD_NO_SPEED) &&
+			    ((status & SATA_SS_IPM_MASK) == SATA_SS_IPM_ACTIVE)) {
+				device_printf(dev, "CONNECT requested\n");
+			} else
+				device_printf(dev, "DISCONNECT requested\n");
+		}
+		mvs_reset(dev);
+		if ((ccb = xpt_alloc_ccb_nowait()) == NULL)
+			return;
+		if (xpt_create_path(&ccb->ccb_h.path, NULL,
+		    cam_sim_path(ch->sim),
+		    CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+			xpt_free_ccb(ccb);
+			return;
+		}
+		xpt_rescan(ccb);
+	}
+}
+
+static void
+mvs_notify_events(device_t dev)
+{
+	struct mvs_channel *ch = device_get_softc(dev);
+	struct cam_path *dpath;
+	uint32_t fis;
+	int d;
+
+	/* Try to read PMP field from SDB FIS. Present only for Gen-IIe. */
+	fis = ATA_INL(ch->r_mem, SATA_FISDW0);
+	if ((fis & 0x80ff) == 0x80a1)
+		d = (fis & 0x0f00) >> 8;
+	else
+		d = ch->pm_present ? 15 : 0;
+	if (bootverbose)
+		device_printf(dev, "SNTF %d\n", d);
+	if (xpt_create_path(&dpath, NULL,
+	    xpt_path_path_id(ch->path), d, 0) == CAM_REQ_CMP) {
+		xpt_async(AC_SCSI_AEN, dpath, NULL);
+		xpt_free_path(dpath);
+	}
+}
+
+static void
+mvs_ch_intr_locked(void *data)
+{
+	struct mvs_intr_arg *arg = (struct mvs_intr_arg *)data;
+	device_t dev = (device_t)arg->arg;
+	struct mvs_channel *ch = device_get_softc(dev);
+
+	mtx_lock(&ch->mtx);
+	mvs_ch_intr(data);
+	mtx_unlock(&ch->mtx);
+}
+
+static void
+mvs_ch_pm(void *arg)
+{
+	device_t dev = (device_t)arg;
+	struct mvs_channel *ch = device_get_softc(dev);
+	uint32_t work;
+
+	if (ch->numrslots != 0)
+		return;
+	/* If we are idle - request power state transition. */
+	work = ATA_INL(ch->r_mem, SATA_SC);
+	work &= ~SATA_SC_SPM_MASK;
+	if (ch->pm_level == 4)
+		work |= SATA_SC_SPM_PARTIAL;
+	else
+		work |= SATA_SC_SPM_SLUMBER;
+	ATA_OUTL(ch->r_mem, SATA_SC, work);
+}
+
+static void
+mvs_ch_pm_wake(device_t dev)
+{
+	struct mvs_channel *ch = device_get_softc(dev);
+	uint32_t work;
+	int timeout = 0;
+
+	work = ATA_INL(ch->r_mem, SATA_SS);
+	if (work & SATA_SS_IPM_ACTIVE)
+		return;
+	/* If we are not in active state - request power state transition. */
+	work = ATA_INL(ch->r_mem, SATA_SC);
+	work &= ~SATA_SC_SPM_MASK;
+	work |= SATA_SC_SPM_ACTIVE;
+	ATA_OUTL(ch->r_mem, SATA_SC, work);
+	/* Wait for transition to happen. */
+	while ((ATA_INL(ch->r_mem, SATA_SS) & SATA_SS_IPM_ACTIVE) == 0 &&
+	    timeout++ < 100) {
+		DELAY(100);
+	}
+}
+
+static void
+mvs_ch_intr(void *data)
+{
+	struct mvs_intr_arg *arg = (struct mvs_intr_arg *)data;
+	device_t dev = (device_t)arg->arg;
+	struct mvs_channel *ch = device_get_softc(dev);
+	uint32_t iec, serr = 0, fisic = 0;
+	enum mvs_err_type et;
+	int i, ccs, port = -1, selfdis = 0;
+	int edma = (ch->numtslots != 0 || ch->numdslots != 0);
+
+//device_printf(dev, "irq cause %02x EDMA %d IEC %08x\n",
+//    arg->cause, edma, ATA_INL(ch->r_mem, EDMA_IEC));
+	/* New item in response queue. */
+	if ((arg->cause & 2) && edma)
+		mvs_crbq_intr(dev);
+	/* Some error or special event. */
+	if (arg->cause & 1) {
+		iec = ATA_INL(ch->r_mem, EDMA_IEC);
+//device_printf(dev, "irq cause %02x EDMA %d IEC %08x\n",
+//    arg->cause, edma, iec);
+		if (iec & EDMA_IE_SERRINT) {
+			serr = ATA_INL(ch->r_mem, SATA_SE);
+			ATA_OUTL(ch->r_mem, SATA_SE, serr);
+//device_printf(dev, "SERR %08x\n", serr);
+		}
+		/* EDMA self-disabled due to error. */
+		if (iec & EDMA_IE_ESELFDIS)
+			selfdis = 1;
+		/* Transport interrupt. */
+		if (iec & EDMA_IE_ETRANSINT) {
+			/* For Gen-I this bit means self-disable. */
+			if (ch->quirks & MVS_Q_GENI)
+				selfdis = 1;
+			/* For Gen-II this bit means SDB-N. */
+			else if (ch->quirks & MVS_Q_GENII)
+				fisic = SATA_FISC_FISWAIT4HOSTRDYEN_B1;
+			else	/* For Gen-IIe - read FIS interrupt cause. */
+				fisic = ATA_INL(ch->r_mem, SATA_FISIC);
+//device_printf(dev, "FISIC %08x\n", fisic);
+		}
+		if (selfdis)
+			ch->curr_mode = MVS_EDMA_UNKNOWN;
+		ATA_OUTL(ch->r_mem, EDMA_IEC, ~iec);
+		/* Interface errors or Device error. */
+		if (iec & (0xfc1e9000 | EDMA_IE_EDEVERR)) {
+			port = -1;
+			if (ch->numpslots != 0) {
+				ccs = 0;
+			} else {
+				if (ch->quirks & MVS_Q_GENIIE)
+					ccs = EDMA_S_EIOID(ATA_INL(ch->r_mem, EDMA_S));
+				else
+					ccs = EDMA_S_EDEVQUETAG(ATA_INL(ch->r_mem, EDMA_S));
+				/* Check if error is one-PMP-port-specific, */
+				if (ch->fbs_enabled) {
+					/* Which ports were active. */
+					for (i = 0; i < 16; i++) {
+						if (ch->numrslotspd[i] == 0)
+							continue;
+						if (port == -1)
+							port = i;
+						else if (port != i) {
+							port = -2;
+							break;
+						}
+					}
+					/* If several ports were active and EDMA still enabled - 
+					 * other ports are probably unaffected and may continue.
+					 */
+					if (port == -2 && !selfdis) {
+						uint16_t p = ATA_INL(ch->r_mem, SATA_SATAITC) >> 16;
+						port = ffs(p) - 1;
+						if (port != (fls(p) - 1))
+							port = -2;
+					}
+				}
+			}
+//device_printf(dev, "err slot %d port %d\n", ccs, port);
+			mvs_requeue_frozen(dev);
+			for (i = 0; i < MVS_MAX_SLOTS; i++) {
+				/* XXX: reqests in loading state. */
+				if (((ch->rslots >> i) & 1) == 0)
+					continue;
+				if (port >= 0 &&
+				    ch->slot[i].ccb->ccb_h.target_id != port)
+					continue;
+				if (iec & EDMA_IE_EDEVERR) { /* Device error. */
+				    if (port != -2) {
+					if (ch->numtslots == 0) {
+						/* Untagged operation. */
+						if (i == ccs)
+							et = MVS_ERR_TFE;
+						else
+							et = MVS_ERR_INNOCENT;
+					} else {
+						/* Tagged operation. */
+						et = MVS_ERR_NCQ;
+					}
+				    } else {
+					et = MVS_ERR_TFE;
+					ch->fatalerr = 1;
+				    }
+				} else if (iec & 0xfc1e9000) {
+					if (ch->numtslots == 0 && i != ccs && port != -2)
+						et = MVS_ERR_INNOCENT;
+					else
+						et = MVS_ERR_SATA;
+				} else
+					et = MVS_ERR_INVALID;
+				mvs_end_transaction(&ch->slot[i], et);
+			}
+		}
+		/* Process SDB-N. */
+		if (fisic & SATA_FISC_FISWAIT4HOSTRDYEN_B1)
+			mvs_notify_events(dev);
+		if (fisic)
+			ATA_OUTL(ch->r_mem, SATA_FISIC, ~fisic);
+		/* Process hot-plug. */

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-all mailing list