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.
This commit is contained in:
Alexander Motin 2010-05-02 19:28:30 +00:00
parent a0b8e597e5
commit dd48af360f
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=207536
13 changed files with 3996 additions and 1 deletions

View File

@ -220,6 +220,7 @@ MAN= aac.4 \
msk.4 \
mtio.4 \
multicast.4 \
mvs.4 \
mwl.4 \
mwlfw.4 \
mxge.4 \

176
share/man/man4/mvs.4 Normal file
View File

@ -0,0 +1,176 @@
.\" Copyright (c) 2009 Alexander Motin <mav@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@FreeBSD.org .

View File

@ -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

View File

@ -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
#

View File

@ -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

View File

@ -342,7 +342,7 @@ MFILES?= dev/acpica/acpi_if.m dev/acpi_support/acpi_wmi_if.m \
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 \

2173
sys/dev/mvs/mvs.c Normal file

File diff suppressed because it is too large Load Diff

650
sys/dev/mvs/mvs.h Normal file
View File

@ -0,0 +1,650 @@
/*-
* Copyright (c) 2010 Alexander Motin <mav@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.
*
* $FreeBSD$
*/
#include "mvs_if.h"
/* Chip registers */
#define CHIP_PCIEIC 0x1900 /* PCIe Interrupt Cause */
#define CHIP_PCIEIM 0x1910 /* PCIe Interrupt Mask */
#define CHIP_PCIIC 0x1d58 /* PCI Interrupt Cause */
#define CHIP_PCIIM 0x1d5c /* PCI Interrupt Mask */
#define CHIP_MIC 0x1d60 /* Main Interrupt Cause */
#define CHIP_MIM 0x1d64 /* Main Interrupt Mask */
#define CHIP_SOC_MIC 0x20 /* SoC Main Interrupt Cause */
#define CHIP_SOC_MIM 0x24 /* SoC Main Interrupt Mask */
#define IC_ERR_IRQ (1 << 0) /* shift by (2 * port #) */
#define IC_DONE_IRQ (1 << 1) /* shift by (2 * port #) */
#define IC_HC0 0x000001ff /* bits 0-8 = HC0 */
#define IC_HC_SHIFT 9 /* HC1 shift */
#define IC_HC1 (IC_HC0 << IC_HC_SHIFT) /* 9-17 = HC1 */
#define IC_ERR_HC0 0x00000055 /* HC0 ERR_IRQ */
#define IC_DONE_HC0 0x000000aa /* HC0 DONE_IRQ */
#define IC_ERR_HC1 (IC_ERR_HC0 << IC_HC_SHIFT) /* HC1 ERR_IRQ */
#define IC_DONE_HC1 (IC_DONE_HC0 << IC_HC_SHIFT) /* HC1 DONE_IRQ */
#define IC_HC0_COAL_DONE (1 << 8) /* HC0 IRQ coalescing */
#define IC_HC1_COAL_DONE (1 << 17) /* HC1 IRQ coalescing */
#define IC_PCI_ERR (1 << 18)
#define IC_TRAN_COAL_LO_DONE (1 << 19) /* transaction coalescing */
#define IC_TRAN_COAL_HI_DONE (1 << 20) /* transaction coalescing */
#define IC_ALL_PORTS_COAL_DONE (1 << 21) /* GEN_II(E) IRQ coalescing */
#define IC_GPIO_INT (1 << 22)
#define IC_SELF_INT (1 << 23)
#define IC_TWSI_INT (1 << 24)
#define IC_MAIN_RSVD (0xfe000000) /* bits 31-25 */
#define IC_MAIN_RSVD_5 (0xfff10000) /* bits 31-19 */
#define IC_MAIN_RSVD_SOC (0xfffffec0) /* bits 31-9, 7-6 */
#define CHIP_SOC_LED 0x2C /* SoC LED Configuration */
/* Chip CCC registers */
#define CHIP_ICC 0x18008
#define CHIP_ICC_ALL_PORTS (1 << 4) /* all ports irq event */
#define CHIP_ICT 0x180cc
#define CHIP_ITT 0x180d0
#define CHIP_TRAN_COAL_CAUSE_LO 0x18088
#define CHIP_TRAN_COAL_CAUSE_HI 0x1808c
/* Host Controller registers */
#define HC_SIZE 0x10000
#define HC_OFFSET 0x20000
#define HC_BASE(hc) ((hc) * HC_SIZE + HC_OFFSET)
#define HC_CFG 0x0 /* Configuration */
#define HC_CFG_TIMEOUT_MASK (0xff << 0)
#define HC_CFG_NODMABS (1 << 8)
#define HC_CFG_NOEDMABS (1 << 9)
#define HC_CFG_NOPRDBS (1 << 10)
#define HC_CFG_TIMEOUTEN (1 << 16) /* Timer Enable */
#define HC_CFG_COALDIS(p) (1 << ((p) + 24))/* Coalescing Disable*/
#define HC_RQOP 0x4 /* Request Queue Out-Pointer */
#define HC_RQIP 0x8 /* Response Queue In-Pointer */
#define HC_ICT 0xc /* Interrupt Coalescing Threshold */
#define HC_ICT_SAICOALT_MASK 0x000000ff
#define HC_ITT 0x10 /* Interrupt Time Threshold */
#define HC_ITT_SAITMTH_MASK 0x00ffffff
#define HC_IC 0x14 /* Interrupt Cause */
#define HC_IC_DONE(p) (1 << (p)) /* SaCrpb/DMA Done */
#define HC_IC_COAL (1 << 4) /* Intr Coalescing */
#define HC_IC_DEV(p) (1 << ((p) + 8)) /* Device Intr */
/* Port registers */
#define PORT_SIZE 0x2000
#define PORT_OFFSET 0x2000
#define PORT_BASE(hc) ((hc) * PORT_SIZE + PORT_OFFSET)
#define EDMA_CFG 0x0 /* Configuration */
#define EDMA_CFG_RESERVED (0x1f << 0) /* Queue len ? */
#define EDMA_CFG_ESATANATVCMDQUE (1 << 5)
#define EDMA_CFG_ERDBSZ (1 << 8)
#define EDMA_CFG_EQUE (1 << 9)
#define EDMA_CFG_ERDBSZEXT (1 << 11)
#define EDMA_CFG_RESERVED2 (1 << 12)
#define EDMA_CFG_EWRBUFFERLEN (1 << 13)
#define EDMA_CFG_EDEVERR (1 << 14)
#define EDMA_CFG_EEDMAFBS (1 << 16)
#define EDMA_CFG_ECUTTHROUGHEN (1 << 17)
#define EDMA_CFG_EEARLYCOMPLETIONEN (1 << 18)
#define EDMA_CFG_EEDMAQUELEN (1 << 19)
#define EDMA_CFG_EHOSTQUEUECACHEEN (1 << 22)
#define EDMA_CFG_EMASKRXPM (1 << 23)
#define EDMA_CFG_RESUMEDIS (1 << 24)
#define EDMA_CFG_EDMAFBS (1 << 26)
#define EDMA_T 0x4 /* Timer */
#define EDMA_IEC 0x8 /* Interrupt Error Cause */
#define EDMA_IEM 0xc /* Interrupt Error Mask */
#define EDMA_IE_EDEVERR (1 << 2) /* EDMA Device Error */
#define EDMA_IE_EDEVDIS (1 << 3) /* EDMA Dev Disconn */
#define EDMA_IE_EDEVCON (1 << 4) /* EDMA Dev Conn */
#define EDMA_IE_SERRINT (1 << 5)
#define EDMA_IE_ESELFDIS (1 << 7) /* EDMA Self Disable */
#define EDMA_IE_ETRANSINT (1 << 8) /* Transport Layer */
#define EDMA_IE_EIORDYERR (1 << 12) /* EDMA IORdy Error */
#define EDMA_IE_LINKXERR_SATACRC (1 << 0) /* SATA CRC error */
#define EDMA_IE_LINKXERR_INTERNALFIFO (1 << 1) /* internal FIFO err */
#define EDMA_IE_LINKXERR_LINKLAYERRESET (1 << 2)
/* Link Layer is reset by the reception of SYNC primitive from device */
#define EDMA_IE_LINKXERR_OTHERERRORS (1 << 3)
/*
* Link state errors, coding errors, or running disparity errors occur
* during FIS reception.
*/
#define EDMA_IE_LINKTXERR_FISTXABORTED (1 << 4) /* FIS Tx is aborted */
#define EDMA_IE_LINKCTLRXERR(x) ((x) << 13) /* Link Ctrl Recv Err */
#define EDMA_IE_LINKDATARXERR(x) ((x) << 17) /* Link Data Recv Err */
#define EDMA_IE_LINKCTLTXERR(x) ((x) << 21) /* Link Ctrl Tx Error */
#define EDMA_IE_LINKDATATXERR(x) ((x) << 26) /* Link Data Tx Error */
#define EDMA_IE_TRANSPROTERR (1 << 31) /* Transport Proto E */
#define EDMA_IE_TRANSIENT (EDMA_IE_LINKCTLRXERR(0x0b) | \
EDMA_IE_LINKCTLTXERR(0x1f))
/* Non-fatal Errors */
#define EDMA_REQQBAH 0x10 /* Request Queue Base Address High */
#define EDMA_REQQIP 0x14 /* Request Queue In-Pointer */
#define EDMA_REQQOP 0x18 /* Request Queue Out-Pointer */
#define EDMA_REQQP_ERQQP_SHIFT 5
#define EDMA_REQQP_ERQQP_MASK 0x000003e0
#define EDMA_REQQP_ERQQBAP_MASK 0x00000c00
#define EDMA_REQQP_ERQQBA_MASK 0xfffff000
#define EDMA_RESQBAH 0x1c /* Response Queue Base Address High */
#define EDMA_RESQIP 0x20 /* Response Queue In-Pointer */
#define EDMA_RESQOP 0x24 /* Response Queue Out-Pointer */
#define EDMA_RESQP_ERPQP_SHIFT 3
#define EDMA_RESQP_ERPQP_MASK 0x000000f8
#define EDMA_RESQP_ERPQBAP_MASK 0x00000300
#define EDMA_RESQP_ERPQBA_MASK 0xfffffc00
#define EDMA_CMD 0x28 /* Command */
#define EDMA_CMD_EENEDMA (1 << 0) /* Enable EDMA */
#define EDMA_CMD_EDSEDMA (1 << 1) /* Disable EDMA */
#define EDMA_CMD_EATARST (1 << 2) /* ATA Device Reset */
#define EDMA_CMD_EEDMAFRZ (1 << 4) /* EDMA Freeze */
#define EDMA_TC 0x2c /* Test Control */
#define EDMA_S 0x30 /* Status */
#define EDMA_S_EDEVQUETAG(s) ((s) & 0x0000001f)
#define EDMA_S_EDEVDIR_WRITE (0 << 5)
#define EDMA_S_EDEVDIR_READ (1 << 5)
#define EDMA_S_ECACHEEMPTY (1 << 6)
#define EDMA_S_EDMAIDLE (1 << 7)
#define EDMA_S_ESTATE(s) (((s) & 0x0000ff00) >> 8)
#define EDMA_S_EIOID(s) (((s) & 0x003f0000) >> 16)
#define EDMA_IORT 0x34 /* IORdy Timeout */
#define EDMA_CDT 0x40 /* Command Delay Threshold */
#define EDMA_HC 0x60 /* Halt Condition */
#define EDMA_UNKN_RESD 0x6C /* Unknown register */
#define EDMA_CQDCQOS(x) (0x90 + ((x) << 2)
/* NCQ Done/TCQ Outstanding Status */
/* ATA register defines */
#define ATA_DATA 0x100 /* (RW) data */
#define ATA_FEATURE 0x104 /* (W) feature */
#define ATA_F_DMA 0x01 /* enable DMA */
#define ATA_F_OVL 0x02 /* enable overlap */
#define ATA_ERROR 0x104 /* (R) error */
#define ATA_E_ILI 0x01 /* illegal length */
#define ATA_E_NM 0x02 /* no media */
#define ATA_E_ABORT 0x04 /* command aborted */
#define ATA_E_MCR 0x08 /* media change request */
#define ATA_E_IDNF 0x10 /* ID not found */
#define ATA_E_MC 0x20 /* media changed */
#define ATA_E_UNC 0x40 /* uncorrectable data */
#define ATA_E_ICRC 0x80 /* UDMA crc error */
#define ATA_E_ATAPI_SENSE_MASK 0xf0 /* ATAPI sense key mask */
#define ATA_COUNT 0x108 /* (W) sector count */
#define ATA_IREASON 0x108 /* (R) interrupt reason */
#define ATA_I_CMD 0x01 /* cmd (1) | data (0) */
#define ATA_I_IN 0x02 /* read (1) | write (0) */
#define ATA_I_RELEASE 0x04 /* released bus (1) */
#define ATA_I_TAGMASK 0xf8 /* tag mask */
#define ATA_SECTOR 0x10c /* (RW) sector # */
#define ATA_CYL_LSB 0x110 /* (RW) cylinder# LSB */
#define ATA_CYL_MSB 0x114 /* (RW) cylinder# MSB */
#define ATA_DRIVE 0x118 /* (W) Sector/Drive/Head */
#define ATA_D_LBA 0x40 /* use LBA addressing */
#define ATA_D_IBM 0xa0 /* 512 byte sectors, ECC */
#define ATA_COMMAND 0x11c /* (W) command */
#define ATA_STATUS 0x11c /* (R) status */
#define ATA_S_ERROR 0x01 /* error */
#define ATA_S_INDEX 0x02 /* index */
#define ATA_S_CORR 0x04 /* data corrected */
#define ATA_S_DRQ 0x08 /* data request */
#define ATA_S_DSC 0x10 /* drive seek completed */
#define ATA_S_SERVICE 0x10 /* drive needs service */
#define ATA_S_DWF 0x20 /* drive write fault */
#define ATA_S_DMA 0x20 /* DMA ready */
#define ATA_S_READY 0x40 /* drive ready */
#define ATA_S_BUSY 0x80 /* busy */
#define ATA_CONTROL 0x120 /* (W) control */
#define ATA_A_IDS 0x02 /* disable interrupts */
#define ATA_A_RESET 0x04 /* RESET controller */
#define ATA_A_4BIT 0x08 /* 4 head bits */
#define ATA_A_HOB 0x80 /* High Order Byte enable */
#define ATA_ALTSTAT 0x120 /* (R) alternate status */
#define ATAPI_P_READ (ATA_S_DRQ | ATA_I_IN)
#define ATAPI_P_WRITE (ATA_S_DRQ)
#define ATAPI_P_CMDOUT (ATA_S_DRQ | ATA_I_CMD)
#define ATAPI_P_DONEDRQ (ATA_S_DRQ | ATA_I_CMD | ATA_I_IN)
#define ATAPI_P_DONE (ATA_I_CMD | ATA_I_IN)
#define ATAPI_P_ABORT 0
/* Basic DMA Registers */
#define DMA_C 0x224 /* Basic DMA Command */
#define DMA_C_START (1 << 0)
#define DMA_C_READ (1 << 3)
#define DMA_C_DREGIONVALID (1 << 8)
#define DMA_C_DREGIONLAST (1 << 9)
#define DMA_C_CONTFROMPREV (1 << 10)
#define DMA_C_DRBC(n) (((n) & 0xffff) << 16)
#define DMA_S 0x228 /* Basic DMA Status */
#define DMA_S_ACT (1 << 0) /* Active */
#define DMA_S_ERR (1 << 1) /* Error */
#define DMA_S_PAUSED (1 << 2) /* Paused */
#define DMA_S_LAST (1 << 3) /* Last */
#define DMA_DTLBA 0x22c /* Descriptor Table Low Base Address */
#define DMA_DTLBA_MASK 0xfffffff0
#define DMA_DTHBA 0x230 /* Descriptor Table High Base Address */
#define DMA_DRLA 0x234 /* Data Region Low Address */
#define DMA_DRHA 0x238 /* Data Region High Address */
/* Serial-ATA Registers */
#define SATA_SS 0x300 /* SStatus */
#define SATA_SS_DET_MASK 0x0000000f
#define SATA_SS_DET_NO_DEVICE 0x00000000
#define SATA_SS_DET_DEV_PRESENT 0x00000001
#define SATA_SS_DET_PHY_ONLINE 0x00000003
#define SATA_SS_DET_PHY_OFFLINE 0x00000004
#define SATA_SS_SPD_MASK 0x000000f0
#define SATA_SS_SPD_NO_SPEED 0x00000000
#define SATA_SS_SPD_GEN1 0x00000010
#define SATA_SS_SPD_GEN2 0x00000020
#define SATA_SS_SPD_GEN3 0x00000040
#define SATA_SS_IPM_MASK 0x00000f00
#define SATA_SS_IPM_NO_DEVICE 0x00000000
#define SATA_SS_IPM_ACTIVE 0x00000100
#define SATA_SS_IPM_PARTIAL 0x00000200
#define SATA_SS_IPM_SLUMBER 0x00000600
#define SATA_SE 0x304 /* SError */
#define SATA_SEIM 0x340 /* SError Interrupt Mask */
#define SATA_SE_DATA_CORRECTED 0x00000001
#define SATA_SE_COMM_CORRECTED 0x00000002
#define SATA_SE_DATA_ERR 0x00000100
#define SATA_SE_COMM_ERR 0x00000200
#define SATA_SE_PROT_ERR 0x00000400
#define SATA_SE_HOST_ERR 0x00000800
#define SATA_SE_PHY_CHANGED 0x00010000
#define SATA_SE_PHY_IERROR 0x00020000
#define SATA_SE_COMM_WAKE 0x00040000
#define SATA_SE_DECODE_ERR 0x00080000
#define SATA_SE_PARITY_ERR 0x00100000
#define SATA_SE_CRC_ERR 0x00200000
#define SATA_SE_HANDSHAKE_ERR 0x00400000
#define SATA_SE_LINKSEQ_ERR 0x00800000
#define SATA_SE_TRANSPORT_ERR 0x01000000
#define SATA_SE_UNKNOWN_FIS 0x02000000
#define SATA_SC 0x308 /* SControl */
#define SATA_SC_DET_MASK 0x0000000f
#define SATA_SC_DET_IDLE 0x00000000
#define SATA_SC_DET_RESET 0x00000001
#define SATA_SC_DET_DISABLE 0x00000004
#define SATA_SC_SPD_MASK 0x000000f0
#define SATA_SC_SPD_NO_SPEED 0x00000000
#define SATA_SC_SPD_SPEED_GEN1 0x00000010
#define SATA_SC_SPD_SPEED_GEN2 0x00000020
#define SATA_SC_SPD_SPEED_GEN3 0x00000040
#define SATA_SC_IPM_MASK 0x00000f00
#define SATA_SC_IPM_NONE 0x00000000
#define SATA_SC_IPM_DIS_PARTIAL 0x00000100
#define SATA_SC_IPM_DIS_SLUMBER 0x00000200
#define SATA_SC_SPM_MASK 0x0000f000
#define SATA_SC_SPM_NONE 0x00000000
#define SATA_SC_SPM_PARTIAL 0x00001000
#define SATA_SC_SPM_SLUMBER 0x00002000
#define SATA_SC_SPM_ACTIVE 0x00004000
#define SATA_LTM 0x30c /* LTMode */
#define SATA_PHYM3 0x310 /* PHY Mode 3 */
#define SATA_PHYM4 0x314 /* PHY Mode 4 */
#define SATA_PHYM1 0x32c /* PHY Mode 1 */
#define SATA_PHYM2 0x330 /* PHY Mode 2 */
#define SATA_BISTC 0x334 /* BIST Control */
#define SATA_BISTDW1 0x338 /* BIST DW1 */
#define SATA_BISTDW2 0x33c /* BIST DW2 */
#define SATA_SATAICFG 0x050 /* Serial-ATA Interface Configuration */
#define SATA_SATAICFG_REFCLKCNF_20MHZ (0 << 0)
#define SATA_SATAICFG_REFCLKCNF_25MHZ (1 << 0)
#define SATA_SATAICFG_REFCLKCNF_30MHZ (2 << 0)
#define SATA_SATAICFG_REFCLKCNF_40MHZ (3 << 0)
#define SATA_SATAICFG_REFCLKCNF_MASK (3 << 0)
#define SATA_SATAICFG_REFCLKDIV_1 (0 << 2)
#define SATA_SATAICFG_REFCLKDIV_2 (1 << 2) /* Used 20 or 25MHz */
#define SATA_SATAICFG_REFCLKDIV_4 (2 << 2) /* Used 40MHz */
#define SATA_SATAICFG_REFCLKDIV_3 (3 << 2) /* Used 30MHz */
#define SATA_SATAICFG_REFCLKDIV_MASK (3 << 2)
#define SATA_SATAICFG_REFCLKFEEDDIV_50 (0 << 4) /* or 100, when Gen2En is 1 */
#define SATA_SATAICFG_REFCLKFEEDDIV_60 (1 << 4) /* or 120. Used 25MHz */
#define SATA_SATAICFG_REFCLKFEEDDIV_75 (2 << 4) /* or 150. Used 20MHz */
#define SATA_SATAICFG_REFCLKFEEDDIV_90 (3 << 4) /* or 180 */
#define SATA_SATAICFG_REFCLKFEEDDIV_MASK (3 << 4)
#define SATA_SATAICFG_PHYSSCEN (1 << 6)
#define SATA_SATAICFG_GEN2EN (1 << 7)
#define SATA_SATAICFG_COMMEN (1 << 8)
#define SATA_SATAICFG_PHYSHUTDOWN (1 << 9)
#define SATA_SATAICFG_TARGETMODE (1 << 10) /* 1 = Initiator */
#define SATA_SATAICFG_COMCHANNEL (1 << 11)
#define SATA_SATAICFG_IGNOREBSY (1 << 24)
#define SATA_SATAICFG_LINKRSTEN (1 << 25)
#define SATA_SATAICFG_CMDRETXDS (1 << 26)
#define SATA_SATAICTL 0x344 /* Serial-ATA Interface Control */
#define SATA_SATAICTL_PMPTX_MASK 0x0000000f
#define SATA_SATAICTL_PMPTX_SHIFT 0
#define SATA_SATAICTL_VUM (1 << 8)
#define SATA_SATAICTL_VUS (1 << 9)
#define SATA_SATAICTL_EDMAACT (1 << 16)
#define SATA_SATAICTL_CLEARSTAT (1 << 24)
#define SATA_SATAICTL_SRST (1 << 25)
#define SATA_SATAITC 0x348 /* Serial-ATA Interface Test Control */
#define SATA_SATAIS 0x34c /* Serial-ATA Interface Status */
#define SATA_VU 0x35c /* Vendor Unique */
#define SATA_FISC 0x360 /* FIS Configuration */
#define SATA_FISC_FISWAIT4RDYEN_B0 (1 << 0) /* Device to Host FIS */
#define SATA_FISC_FISWAIT4RDYEN_B1 (1 << 1) /* SDB FIS rcv with <N>bit 0 */
#define SATA_FISC_FISWAIT4RDYEN_B2 (1 << 2) /* DMA Activate FIS */
#define SATA_FISC_FISWAIT4RDYEN_B3 (1 << 3) /* DMA Setup FIS */
#define SATA_FISC_FISWAIT4RDYEN_B4 (1 << 4) /* Data FIS first DW */
#define SATA_FISC_FISWAIT4RDYEN_B5 (1 << 5) /* Data FIS entire FIS */
#define SATA_FISC_FISWAIT4HOSTRDYEN_B0 (1 << 8)
/* Device to Host FIS with <ERR> or <DF> */
#define SATA_FISC_FISWAIT4HOSTRDYEN_B1 (1 << 9) /* SDB FIS rcv with <N>bit */
#define SATA_FISC_FISWAIT4HOSTRDYEN_B2 (1 << 10) /* SDB FIS rcv with <ERR> */
#define SATA_FISC_FISWAIT4HOSTRDYEN_B3 (1 << 11) /* BIST Acivate FIS */
#define SATA_FISC_FISWAIT4HOSTRDYEN_B4 (1 << 12) /* PIO Setup FIS */
#define SATA_FISC_FISWAIT4HOSTRDYEN_B5 (1 << 13) /* Data FIS with Link error */
#define SATA_FISC_FISWAIT4HOSTRDYEN_B6 (1 << 14) /* Unrecognized FIS type */
#define SATA_FISC_FISWAIT4HOSTRDYEN_B7 (1 << 15) /* Any FIS */
#define SATA_FISC_FISDMAACTIVATESYNCRESP (1 << 16)
#define SATA_FISC_FISUNRECTYPECONT (1 << 17)
#define SATA_FISIC 0x364 /* FIS Interrupt Cause */
#define SATA_FISIM 0x368 /* FIS Interrupt Mask */
#define SATA_FISDW0 0x370 /* FIS DW0 */
#define SATA_FISDW1 0x374 /* FIS DW1 */
#define SATA_FISDW2 0x378 /* FIS DW2 */
#define SATA_FISDW3 0x37c /* FIS DW3 */
#define SATA_FISDW4 0x380 /* FIS DW4 */
#define SATA_FISDW5 0x384 /* FIS DW5 */
#define SATA_FISDW6 0x388 /* FIS DW6 */
#define MVS_MAX_PORTS 8
#define MVS_MAX_SLOTS 32
/* Pessimistic prognosis on number of required S/G entries */
#define MVS_SG_ENTRIES (btoc(MAXPHYS) + 1)
/* EDMA Command Request Block (CRQB) Data */
struct mvs_crqb {
uint32_t cprdbl; /* cPRD Desriptor Table Base Low Address */
uint32_t cprdbh; /* cPRD Desriptor Table Base High Address */
uint16_t ctrlflg; /* Control Flags */
#define MVS_CRQB_READ 0x0001
#define MVS_CRQB_TAG_MASK 0x003e
#define MVS_CRQB_TAG_SHIFT 1
#define MVS_CRQB_PMP_MASK 0xf000
#define MVS_CRQB_PMP_SHIFT 12
uint8_t cmd[22];
} __packed;
struct mvs_crqb_gen2e {
uint32_t cprdbl; /* cPRD Desriptor Table Base Low Address */
uint32_t cprdbh; /* cPRD Desriptor Table Base High Address */
uint32_t ctrlflg; /* Control Flags */
#define MVS_CRQB2E_READ 0x00000001
#define MVS_CRQB2E_DTAG_MASK 0x0000003e
#define MVS_CRQB2E_DTAG_SHIFT 1
#define MVS_CRQB2E_PMP_MASK 0x0000f000
#define MVS_CRQB2E_PMP_SHIFT 12
#define MVS_CRQB2E_CPRD 0x00010000
#define MVS_CRQB2E_HTAG_MASK 0x003e0000
#define MVS_CRQB2E_HTAG_SHIFT 17
uint32_t drbc; /* Data Region Byte Count */
uint8_t cmd[16];
} __packed;
/* EDMA Phisical Region Descriptors (ePRD) Table Data Structure */
struct mvs_eprd {
uint32_t prdbal; /* Address bits[31:1] */
uint32_t bytecount; /* Byte Count */
#define MVS_EPRD_MASK 0x0000ffff /* max 64KB */
#define MVS_EPRD_MAX (MVS_EPRD_MASK + 1)
#define MVS_EPRD_EOF 0x80000000
uint32_t prdbah; /* Address bits[63:32] */
uint32_t resv;
} __packed;
/* Command request blocks. 32 commands. First 1Kbyte aligned. */
#define MVS_CRQB_OFFSET 0
#define MVS_CRQB_SIZE 32 /* sizeof(struct mvs_crqb) */
#define MVS_CRQB_MASK 0x000003e0
#define MVS_CRQB_SHIFT 5
#define MVS_CRQB_TO_ADDR(slot) ((slot) << MVS_CRQB_SHIFT)
#define MVS_ADDR_TO_CRQB(addr) (((addr) & MVS_CRQB_MASK) >> MVS_CRQB_SHIFT)
/* ePRD blocks. Up to 32 commands, Each 16byte aligned. */
#define MVS_EPRD_OFFSET (MVS_CRQB_OFFSET + MVS_CRQB_SIZE * MVS_MAX_SLOTS)
#define MVS_EPRD_SIZE (MVS_SG_ENTRIES * 16) /* sizeof(struct mvs_eprd) */
/* Request work area. */
#define MVS_WORKRQ_SIZE (MVS_EPRD_OFFSET + MVS_EPRD_SIZE * MVS_MAX_SLOTS)
/* EDMA Command Response Block (CRPB) Data */
struct mvs_crpb {
uint16_t id; /* CRPB ID */
#define MVS_CRPB_TAG_MASK 0x001F
#define MVS_CRPB_TAG_SHIFT 0
uint16_t rspflg; /* CPRB Response Flags */
#define MVS_CRPB_EDMASTS_MASK 0x007F
#define MVS_CRPB_EDMASTS_SHIFT 0
#define MVS_CRPB_ATASTS_MASK 0xFF00
#define MVS_CRPB_ATASTS_SHIFT 8
uint32_t ts; /* CPRB Time Stamp */
} __packed;
/* Command response blocks. 32 commands. First 256byte aligned. */
#define MVS_CRPB_OFFSET 0
#define MVS_CRPB_SIZE sizeof(struct mvs_crpb)
#define MVS_CRPB_MASK 0x000000f8
#define MVS_CRPB_SHIFT 3
#define MVS_CRPB_TO_ADDR(slot) ((slot) << MVS_CRPB_SHIFT)
#define MVS_ADDR_TO_CRPB(addr) (((addr) & MVS_CRPB_MASK) >> MVS_CRPB_SHIFT)
/* Request work area. */
#define MVS_WORKRP_SIZE (MVS_CRPB_OFFSET + MVS_CRPB_SIZE * MVS_MAX_SLOTS)
/* misc defines */
#define ATA_IRQ_RID 0
#define ATA_INTR_FLAGS (INTR_MPSAFE|INTR_TYPE_BIO|INTR_ENTROPY)
struct ata_dmaslot {
bus_dmamap_t data_map; /* Data DMA map */
bus_addr_t addr; /* Data address */
uint16_t len; /* Data size */
};
/* structure holding DMA related information */
struct mvs_dma {
bus_dma_tag_t workrq_tag; /* Request workspace DMA tag */
bus_dmamap_t workrq_map; /* Request workspace DMA map */
uint8_t *workrq; /* Request workspace */
bus_addr_t workrq_bus; /* Request bus address */
bus_dma_tag_t workrp_tag; /* Reply workspace DMA tag */
bus_dmamap_t workrp_map; /* Reply workspace DMA map */
uint8_t *workrp; /* Reply workspace */
bus_addr_t workrp_bus; /* Reply bus address */
bus_dma_tag_t data_tag; /* Data DMA tag */
};
enum mvs_slot_states {
MVS_SLOT_EMPTY,
MVS_SLOT_LOADING,
MVS_SLOT_RUNNING,
MVS_SLOT_EXECUTING
};
struct mvs_slot {
device_t dev; /* Device handle */
int slot; /* Number of this slot */
int tag; /* Used command tag */
enum mvs_slot_states state; /* Slot state */
union ccb *ccb; /* CCB occupying slot */
struct ata_dmaslot dma; /* DMA data of this slot */
struct callout timeout; /* Execution timeout */
};
struct mvs_device {
int revision;
int mode;
u_int bytecount;
u_int atapi;
u_int tags;
u_int caps;
};
enum mvs_edma_mode {
MVS_EDMA_UNKNOWN,
MVS_EDMA_OFF,
MVS_EDMA_ON,
MVS_EDMA_QUEUED,
MVS_EDMA_NCQ,
};
/* structure describing an ATA channel */
struct mvs_channel {
device_t dev; /* Device handle */
int unit; /* Physical channel */
struct resource *r_mem; /* Memory of this channel */
struct resource *r_irq; /* Interrupt of this channel */
void *ih; /* Interrupt handle */
struct mvs_dma dma; /* DMA data */
struct cam_sim *sim;
struct cam_path *path;
int quirks;
#define MVS_Q_GENI 1
#define MVS_Q_GENII 2
#define MVS_Q_GENIIE 4
#define MVS_Q_SOC 8
#define MVS_Q_CT 16
int pm_level; /* power management level */
struct mvs_slot slot[MVS_MAX_SLOTS];
union ccb *hold[MVS_MAX_SLOTS];
int holdtag[MVS_MAX_SLOTS]; /* Tags used for holden commands. */
struct mtx mtx; /* state lock */
int devices; /* What is present */
int pm_present; /* PM presence reported */
enum mvs_edma_mode curr_mode; /* Current EDMA mode */
int fbs_enabled; /* FIS-based switching enabled */
uint32_t oslots; /* Occupied slots */
uint32_t otagspd[16]; /* Occupied device tags */
uint32_t rslots; /* Running slots */
uint32_t aslots; /* Slots with atomic commands */
uint32_t eslots; /* Slots in error */
uint32_t toslots; /* Slots in timeout */
int numrslots; /* Number of running slots */
int numrslotspd[16];/* Number of running slots per dev */
int numpslots; /* Number of PIO slots */
int numdslots; /* Number of DMA slots */
int numtslots; /* Number of NCQ slots */
int numtslotspd[16];/* Number of NCQ slots per dev */
int numhslots; /* Number of holden slots */
int readlog; /* Our READ LOG active */
int fatalerr; /* Fatal error happend */
int lastslot; /* Last used slot */
int taggedtarget; /* Last tagged target */
int out_idx; /* Next written CRQB */
int in_idx; /* Next read CRPB */
u_int transfersize; /* PIO transfer size */
u_int donecount; /* PIO bytes sent/received */
u_int basic_dma; /* Basic DMA used for ATAPI */
u_int fake_busy; /* Fake busy bit after command submission */
union ccb *frozen; /* Frozen command */
struct callout pm_timer; /* Power management events */
struct mvs_device user[16]; /* User-specified settings */
struct mvs_device curr[16]; /* Current settings */
};
/* structure describing a MVS controller */
struct mvs_controller {
device_t dev;
int r_rid;
struct resource *r_mem;
struct rman sc_iomem;
struct mvs_controller_irq {
struct resource *r_irq;
void *handle;
int r_irq_rid;
} irq;
int quirks;
int channels;
int ccc; /* CCC timeout */
int cccc; /* CCC commands */
struct mtx mtx; /* MIM access lock */
int gmim; /* Globally wanted MIM bits */
int pmim; /* Port wanted MIM bits */
int mim; /* Current MIM bits */
int msi; /* MSI enabled */
int msia; /* MSI active */
struct {
void (*function)(void *);
void *argument;
} interrupt[MVS_MAX_PORTS];
};
enum mvs_err_type {
MVS_ERR_NONE, /* No error */
MVS_ERR_INVALID, /* Error detected by us before submitting. */
MVS_ERR_INNOCENT, /* Innocent victim. */
MVS_ERR_TFE, /* Task File Error. */
MVS_ERR_SATA, /* SATA error. */
MVS_ERR_TIMEOUT, /* Command execution timeout. */
MVS_ERR_NCQ, /* NCQ command error. CCB should be put on hold
* until READ LOG executed to reveal error. */
};
struct mvs_intr_arg {
void *arg;
u_int cause;
};
extern devclass_t mvs_devclass;
/* macros to hide busspace uglyness */
#define ATA_INB(res, offset) \
bus_read_1((res), (offset))
#define ATA_INW(res, offset) \
bus_read_2((res), (offset))
#define ATA_INL(res, offset) \
bus_read_4((res), (offset))
#define ATA_INSW(res, offset, addr, count) \
bus_read_multi_2((res), (offset), (addr), (count))
#define ATA_INSW_STRM(res, offset, addr, count) \
bus_read_multi_stream_2((res), (offset), (addr), (count))
#define ATA_INSL(res, offset, addr, count) \
bus_read_multi_4((res), (offset), (addr), (count))
#define ATA_INSL_STRM(res, offset, addr, count) \
bus_read_multi_stream_4((res), (offset), (addr), (count))
#define ATA_OUTB(res, offset, value) \
bus_write_1((res), (offset), (value))
#define ATA_OUTW(res, offset, value) \
bus_write_2((res), (offset), (value))
#define ATA_OUTL(res, offset, value) \
bus_write_4((res), (offset), (value));
#define ATA_OUTSW(res, offset, addr, count) \
bus_write_multi_2((res), (offset), (addr), (count))
#define ATA_OUTSW_STRM(res, offset, addr, count) \
bus_write_multi_stream_2((res), (offset), (addr), (count))
#define ATA_OUTSL(res, offset, addr, count) \
bus_write_multi_4((res), (offset), (addr), (count))
#define ATA_OUTSL_STRM(res, offset, addr, count) \
bus_write_multi_stream_4((res), (offset), (addr), (count))

34
sys/dev/mvs/mvs_if.m Normal file
View File

@ -0,0 +1,34 @@
# Copyright (c) 2010 Alexander Motin <mav@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.
#
# $FreeBSD$
INTERFACE mvs;
METHOD void edma {
device_t dev;
device_t child;
int mode;
};

507
sys/dev/mvs/mvs_pci.c Normal file
View File

@ -0,0 +1,507 @@
/*-
* Copyright (c) 2010 Alexander Motin <mav@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/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 <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include "mvs.h"
/* local prototypes */
static int mvs_setup_interrupt(device_t dev);
static void mvs_intr(void *data);
static int mvs_suspend(device_t dev);
static int mvs_resume(device_t dev);
static int mvs_ctlr_setup(device_t dev);
static struct {
uint32_t id;
uint8_t rev;
const char *name;
int ports;
int quirks;
} mvs_ids[] = {
{0x504011ab, 0x00, "Marvell 88SX5040", 4, MVS_Q_GENI},
{0x504111ab, 0x00, "Marvell 88SX5041", 4, MVS_Q_GENI},
{0x508011ab, 0x00, "Marvell 88SX5080", 8, MVS_Q_GENI},
{0x508111ab, 0x00, "Marvell 88SX5081", 8, MVS_Q_GENI},
{0x604011ab, 0x00, "Marvell 88SX6040", 4, MVS_Q_GENII},
{0x604111ab, 0x00, "Marvell 88SX6041", 4, MVS_Q_GENII},
{0x604211ab, 0x00, "Marvell 88SX6042", 4, MVS_Q_GENIIE},
{0x608011ab, 0x00, "Marvell 88SX6080", 8, MVS_Q_GENII},
{0x608111ab, 0x00, "Marvell 88SX6081", 8, MVS_Q_GENII},
{0x704211ab, 0x00, "Marvell 88SX7042", 4, MVS_Q_GENIIE|MVS_Q_CT},
{0x02419005, 0x00, "Adaptec 1420SA", 4, MVS_Q_GENII},
{0x02439005, 0x00, "Adaptec 1430SA", 4, MVS_Q_GENIIE|MVS_Q_CT},
{0x00000000, 0x00, NULL, 0, 0}
};
static int
mvs_probe(device_t dev)
{
char buf[64];
int i;
uint32_t devid = pci_get_devid(dev);
uint8_t revid = pci_get_revid(dev);
for (i = 0; mvs_ids[i].id != 0; i++) {
if (mvs_ids[i].id == devid &&
mvs_ids[i].rev <= revid) {
snprintf(buf, sizeof(buf), "%s SATA controller",
mvs_ids[i].name);
device_set_desc_copy(dev, buf);
return (BUS_PROBE_VENDOR);
}
}
return (ENXIO);
}
static int
mvs_attach(device_t dev)
{
struct mvs_controller *ctlr = device_get_softc(dev);
device_t child;
int error, unit, i;
uint32_t devid = pci_get_devid(dev);
uint8_t revid = pci_get_revid(dev);
ctlr->dev = dev;
i = 0;
while (mvs_ids[i].id != 0 &&
(mvs_ids[i].id != devid ||
mvs_ids[i].rev > revid))
i++;
ctlr->channels = mvs_ids[i].ports;
ctlr->quirks = mvs_ids[i].quirks;
resource_int_value(device_get_name(dev),
device_get_unit(dev), "ccc", &ctlr->ccc);
ctlr->cccc = 8;
resource_int_value(device_get_name(dev),
device_get_unit(dev), "cccc", &ctlr->cccc);
if (ctlr->ccc == 0 || ctlr->cccc == 0) {
ctlr->ccc = 0;
ctlr->cccc = 0;
}
if (ctlr->ccc > 100000)
ctlr->ccc = 100000;
device_printf(dev,
"Gen-%s, %d %sGbps ports, Port Multiplier %s%s\n",
((ctlr->quirks & MVS_Q_GENI) ? "I" :
((ctlr->quirks & MVS_Q_GENII) ? "II" : "IIe")),
ctlr->channels,
((ctlr->quirks & MVS_Q_GENI) ? "1.5" : "3"),
((ctlr->quirks & MVS_Q_GENI) ?
"not supported" : "supported"),
((ctlr->quirks & MVS_Q_GENIIE) ?
" with FBS" : ""));
mtx_init(&ctlr->mtx, "MVS controller lock", NULL, MTX_DEF);
/* We should have a memory BAR(0). */
ctlr->r_rid = PCIR_BAR(0);
if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
&ctlr->r_rid, RF_ACTIVE)))
return ENXIO;
/* Setup our own memory management for channels. */
ctlr->sc_iomem.rm_type = RMAN_ARRAY;
ctlr->sc_iomem.rm_descr = "I/O memory addresses";
if ((error = rman_init(&ctlr->sc_iomem)) != 0) {
bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem);
return (error);
}
if ((error = rman_manage_region(&ctlr->sc_iomem,
rman_get_start(ctlr->r_mem), rman_get_end(ctlr->r_mem))) != 0) {
bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem);
rman_fini(&ctlr->sc_iomem);
return (error);
}
pci_enable_busmaster(dev);
mvs_ctlr_setup(dev);
/* Setup interrupts. */
if (mvs_setup_interrupt(dev)) {
bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem);
rman_fini(&ctlr->sc_iomem);
return ENXIO;
}
/* Attach all channels on this controller */
for (unit = 0; unit < ctlr->channels; unit++) {
child = device_add_child(dev, "mvsch", -1);
if (child == NULL)
device_printf(dev, "failed to add channel device\n");
else
device_set_ivars(child, (void *)(intptr_t)unit);
}
bus_generic_attach(dev);
return 0;
}
static int
mvs_detach(device_t dev)
{
struct mvs_controller *ctlr = device_get_softc(dev);
device_t *children;
int nchildren, i;
/* Detach & delete all children */
if (!device_get_children(dev, &children, &nchildren)) {
for (i = 0; i < nchildren; i++)
device_delete_child(dev, children[i]);
free(children, M_TEMP);
}
/* Free interrupt. */
if (ctlr->irq.r_irq) {
bus_teardown_intr(dev, ctlr->irq.r_irq,
ctlr->irq.handle);
bus_release_resource(dev, SYS_RES_IRQ,
ctlr->irq.r_irq_rid, ctlr->irq.r_irq);
}
pci_release_msi(dev);
/* Free memory. */
rman_fini(&ctlr->sc_iomem);
if (ctlr->r_mem)
bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem);
mtx_destroy(&ctlr->mtx);
return (0);
}
static int
mvs_ctlr_setup(device_t dev)
{
struct mvs_controller *ctlr = device_get_softc(dev);
int i, ccc = ctlr->ccc, cccc = ctlr->cccc, ccim = 0;
/* Mask chip interrupts */
ATA_OUTL(ctlr->r_mem, CHIP_MIM, 0x00000000);
/* Mask PCI interrupts */
ATA_OUTL(ctlr->r_mem, CHIP_PCIIM, 0x00000000);
/* Clear PCI interrupts */
ATA_OUTL(ctlr->r_mem, CHIP_PCIIC, 0x00000000);
if (ccc && bootverbose) {
device_printf(dev,
"CCC with %dus/%dcmd enabled\n",
ctlr->ccc, ctlr->cccc);
}
ccc *= 150;
/* Configure chip-global CCC */
if (ctlr->channels > 4 && (ctlr->quirks & MVS_Q_GENI) == 0) {
ATA_OUTL(ctlr->r_mem, CHIP_ICT, cccc);
ATA_OUTL(ctlr->r_mem, CHIP_ITT, ccc);
ATA_OUTL(ctlr->r_mem, CHIP_ICC, ~CHIP_ICC_ALL_PORTS);
if (ccc)
ccim |= IC_ALL_PORTS_COAL_DONE;
ccc = 0;
cccc = 0;
}
for (i = 0; i < ctlr->channels / 4; i++) {
/* Configure per-HC CCC */
ATA_OUTL(ctlr->r_mem, HC_BASE(i) + HC_ICT, cccc);
ATA_OUTL(ctlr->r_mem, HC_BASE(i) + HC_ITT, ccc);
if (ccc)
ccim |= (IC_HC0_COAL_DONE << (i * IC_HC_SHIFT));
/* Clear HC interrupts */
ATA_OUTL(ctlr->r_mem, HC_BASE(i) + HC_IC, 0x00000000);
}
/* Enable chip interrupts */
ctlr->gmim = (ccim ? ccim : (IC_DONE_HC0 | IC_DONE_HC1)) |
IC_ERR_HC0 | IC_ERR_HC1;
ctlr->mim = ctlr->gmim | ctlr->pmim;
ATA_OUTL(ctlr->r_mem, CHIP_MIM, ctlr->mim);
/* Enable PCI interrupts */
ATA_OUTL(ctlr->r_mem, CHIP_PCIIM, 0x007fffff);
return (0);
}
static void
mvs_edma(device_t dev, device_t child, int mode)
{
struct mvs_controller *ctlr = device_get_softc(dev);
int unit = ((struct mvs_channel *)device_get_softc(child))->unit;
int bit = IC_DONE_IRQ << (unit * 2 + unit / 4) ;
if (ctlr->ccc == 0)
return;
/* CCC is not working for non-EDMA mode. Unmask device interrupts. */
mtx_lock(&ctlr->mtx);
if (mode == MVS_EDMA_OFF)
ctlr->pmim |= bit;
else
ctlr->pmim &= ~bit;
ctlr->mim = ctlr->gmim | ctlr->pmim;
if (!ctlr->msia)
ATA_OUTL(ctlr->r_mem, CHIP_MIM, ctlr->mim);
mtx_unlock(&ctlr->mtx);
}
static int
mvs_suspend(device_t dev)
{
struct mvs_controller *ctlr = device_get_softc(dev);
bus_generic_suspend(dev);
/* Mask chip interrupts */
ATA_OUTL(ctlr->r_mem, CHIP_MIM, 0x00000000);
/* Mask PCI interrupts */
ATA_OUTL(ctlr->r_mem, CHIP_PCIIM, 0x00000000);
return 0;
}
static int
mvs_resume(device_t dev)
{
mvs_ctlr_setup(dev);
return (bus_generic_resume(dev));
}
static int
mvs_setup_interrupt(device_t dev)
{
struct mvs_controller *ctlr = device_get_softc(dev);
int msi = 0;
/* Process hints. */
resource_int_value(device_get_name(dev),
device_get_unit(dev), "msi", &msi);
if (msi < 0)
msi = 0;
else if (msi > 0)
msi = min(1, pci_msi_count(dev));
/* Allocate MSI if needed/present. */
if (msi && pci_alloc_msi(dev, &msi) != 0)
msi = 0;
ctlr->msi = msi;
/* Allocate all IRQs. */
ctlr->irq.r_irq_rid = msi ? 1 : 0;
if (!(ctlr->irq.r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
&ctlr->irq.r_irq_rid, RF_SHAREABLE | RF_ACTIVE))) {
device_printf(dev, "unable to map interrupt\n");
return (ENXIO);
}
if ((bus_setup_intr(dev, ctlr->irq.r_irq, ATA_INTR_FLAGS, NULL,
mvs_intr, ctlr, &ctlr->irq.handle))) {
device_printf(dev, "unable to setup interrupt\n");
bus_release_resource(dev, SYS_RES_IRQ,
ctlr->irq.r_irq_rid, ctlr->irq.r_irq);
ctlr->irq.r_irq = 0;
return (ENXIO);
}
return (0);
}
/*
* Common case interrupt handler.
*/
static void
mvs_intr(void *data)
{
struct mvs_controller *ctlr = data;
struct mvs_intr_arg arg;
void (*function)(void *);
int p;
u_int32_t ic, aic;
ic = ATA_INL(ctlr->r_mem, CHIP_MIC);
//device_printf(ctlr->dev, "irq MIC:%08x\n", ic);
if (ctlr->msi) {
/* We have to to mask MSI during processing. */
mtx_lock(&ctlr->mtx);
ATA_OUTL(ctlr->r_mem, CHIP_MIM, 0);
ctlr->msia = 1; /* Deny MIM update during processing. */
mtx_unlock(&ctlr->mtx);
} else if (ic == 0)
return;
/* Acknowledge all-ports CCC interrupt. */
if (ic & IC_ALL_PORTS_COAL_DONE)
ATA_OUTL(ctlr->r_mem, CHIP_ICC, ~CHIP_ICC_ALL_PORTS);
for (p = 0; p < ctlr->channels; p++) {
if ((p & 3) == 0) {
if (p != 0)
ic >>= 1;
if ((ic & IC_HC0) == 0) {
p += 3;
ic >>= 8;
continue;
}
/* Acknowledge interrupts of this HC. */
aic = 0;
if (ic & (IC_DONE_IRQ << 0))
aic |= HC_IC_DONE(0) | HC_IC_DEV(0);
if (ic & (IC_DONE_IRQ << 2))
aic |= HC_IC_DONE(1) | HC_IC_DEV(1);
if (ic & (IC_DONE_IRQ << 4))
aic |= HC_IC_DONE(2) | HC_IC_DEV(2);
if (ic & (IC_DONE_IRQ << 6))
aic |= HC_IC_DONE(3) | HC_IC_DEV(3);
if (ic & IC_HC0_COAL_DONE)
aic |= HC_IC_COAL;
ATA_OUTL(ctlr->r_mem, HC_BASE(p == 4) + HC_IC, ~aic);
}
/* Call per-port interrupt handler. */
arg.cause = ic & (IC_ERR_IRQ|IC_DONE_IRQ);
if ((arg.cause != 0) &&
(function = ctlr->interrupt[p].function)) {
arg.arg = ctlr->interrupt[p].argument;
function(&arg);
}
ic >>= 2;
}
if (ctlr->msi) {
/* Unmasking MSI triggers next interrupt, if needed. */
mtx_lock(&ctlr->mtx);
ctlr->msia = 0; /* Allow MIM update. */
ATA_OUTL(ctlr->r_mem, CHIP_MIM, ctlr->mim);
mtx_unlock(&ctlr->mtx);
}
}
static struct resource *
mvs_alloc_resource(device_t dev, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags)
{
struct mvs_controller *ctlr = device_get_softc(dev);
int unit = ((struct mvs_channel *)device_get_softc(child))->unit;
struct resource *res = NULL;
int offset = HC_BASE(unit >> 2) + PORT_BASE(unit & 0x03);
long st;
switch (type) {
case SYS_RES_MEMORY:
st = rman_get_start(ctlr->r_mem);
res = rman_reserve_resource(&ctlr->sc_iomem, st + offset,
st + offset + PORT_SIZE - 1, PORT_SIZE, RF_ACTIVE, child);
if (res) {
bus_space_handle_t bsh;
bus_space_tag_t bst;
bsh = rman_get_bushandle(ctlr->r_mem);
bst = rman_get_bustag(ctlr->r_mem);
bus_space_subregion(bst, bsh, offset, PORT_SIZE, &bsh);
rman_set_bushandle(res, bsh);
rman_set_bustag(res, bst);
}
break;
case SYS_RES_IRQ:
if (*rid == ATA_IRQ_RID)
res = ctlr->irq.r_irq;
break;
}
return (res);
}
static int
mvs_release_resource(device_t dev, device_t child, int type, int rid,
struct resource *r)
{
switch (type) {
case SYS_RES_MEMORY:
rman_release_resource(r);
return (0);
case SYS_RES_IRQ:
if (rid != ATA_IRQ_RID)
return ENOENT;
return (0);
}
return (EINVAL);
}
static int
mvs_setup_intr(device_t dev, device_t child, struct resource *irq,
int flags, driver_filter_t *filter, driver_intr_t *function,
void *argument, void **cookiep)
{
struct mvs_controller *ctlr = device_get_softc(dev);
int unit = (intptr_t)device_get_ivars(child);
if (filter != NULL) {
printf("mvs.c: we cannot use a filter here\n");
return (EINVAL);
}
ctlr->interrupt[unit].function = function;
ctlr->interrupt[unit].argument = argument;
return (0);
}
static int
mvs_teardown_intr(device_t dev, device_t child, struct resource *irq,
void *cookie)
{
struct mvs_controller *ctlr = device_get_softc(dev);
int unit = (intptr_t)device_get_ivars(child);
ctlr->interrupt[unit].function = NULL;
ctlr->interrupt[unit].argument = NULL;
return (0);
}
static int
mvs_print_child(device_t dev, device_t child)
{
int retval;
retval = bus_print_child_header(dev, child);
retval += printf(" at channel %d",
(int)(intptr_t)device_get_ivars(child));
retval += bus_print_child_footer(dev, child);
return (retval);
}
static device_method_t mvs_methods[] = {
DEVMETHOD(device_probe, mvs_probe),
DEVMETHOD(device_attach, mvs_attach),
DEVMETHOD(device_detach, mvs_detach),
DEVMETHOD(device_suspend, mvs_suspend),
DEVMETHOD(device_resume, mvs_resume),
DEVMETHOD(bus_print_child, mvs_print_child),
DEVMETHOD(bus_alloc_resource, mvs_alloc_resource),
DEVMETHOD(bus_release_resource, mvs_release_resource),
DEVMETHOD(bus_setup_intr, mvs_setup_intr),
DEVMETHOD(bus_teardown_intr,mvs_teardown_intr),
DEVMETHOD(mvs_edma, mvs_edma),
{ 0, 0 }
};
static driver_t mvs_driver = {
"mvs",
mvs_methods,
sizeof(struct mvs_controller)
};
DRIVER_MODULE(mvs, pci, mvs_driver, mvs_devclass, 0, 0);
MODULE_VERSION(mvs, 1);
MODULE_DEPEND(mvs, cam, 1, 1, 1);

437
sys/dev/mvs/mvs_soc.c Normal file
View File

@ -0,0 +1,437 @@
/*-
* Copyright (c) 2010 Alexander Motin <mav@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/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 <arm/mv/mvreg.h>
#include <arm/mv/mvvar.h>
#include "mvs.h"
/* local prototypes */
static int mvs_setup_interrupt(device_t dev);
static void mvs_intr(void *data);
static int mvs_suspend(device_t dev);
static int mvs_resume(device_t dev);
static int mvs_ctlr_setup(device_t dev);
static struct {
uint32_t id;
uint8_t rev;
const char *name;
int ports;
int quirks;
} mvs_ids[] = {
{MV_DEV_88F5182, 0x00, "Marvell 88F5182", 2, MVS_Q_GENIIE|MVS_Q_SOC},
{MV_DEV_88F6281, 0x00, "Marvell 88F6281", 2, MVS_Q_GENIIE|MVS_Q_SOC},
{MV_DEV_MV78100, 0x00, "Marvell MV78100", 2, MVS_Q_GENIIE|MVS_Q_SOC},
{MV_DEV_MV78100_Z0, 0x00,"Marvell MV78100", 2, MVS_Q_GENIIE|MVS_Q_SOC},
{0, 0x00, NULL, 0, 0}
};
static int
mvs_probe(device_t dev)
{
char buf[64];
int i;
uint32_t devid, revid;
soc_id(&devid, &revid);
for (i = 0; mvs_ids[i].id != 0; i++) {
if (mvs_ids[i].id == devid &&
mvs_ids[i].rev <= revid) {
snprintf(buf, sizeof(buf), "%s SATA controller",
mvs_ids[i].name);
device_set_desc_copy(dev, buf);
return (BUS_PROBE_VENDOR);
}
}
return (ENXIO);
}
static int
mvs_attach(device_t dev)
{
struct mvs_controller *ctlr = device_get_softc(dev);
device_t child;
int error, unit, i;
uint32_t devid, revid;
soc_id(&devid, &revid);
ctlr->dev = dev;
i = 0;
while (mvs_ids[i].id != 0 &&
(mvs_ids[i].id != devid ||
mvs_ids[i].rev > revid))
i++;
ctlr->channels = mvs_ids[i].ports;
ctlr->quirks = mvs_ids[i].quirks;
resource_int_value(device_get_name(dev),
device_get_unit(dev), "ccc", &ctlr->ccc);
ctlr->cccc = 8;
resource_int_value(device_get_name(dev),
device_get_unit(dev), "cccc", &ctlr->cccc);
if (ctlr->ccc == 0 || ctlr->cccc == 0) {
ctlr->ccc = 0;
ctlr->cccc = 0;
}
if (ctlr->ccc > 100000)
ctlr->ccc = 100000;
device_printf(dev,
"Gen-%s, %d %sGbps ports, Port Multiplier %s%s\n",
((ctlr->quirks & MVS_Q_GENI) ? "I" :
((ctlr->quirks & MVS_Q_GENII) ? "II" : "IIe")),
ctlr->channels,
((ctlr->quirks & MVS_Q_GENI) ? "1.5" : "3"),
((ctlr->quirks & MVS_Q_GENI) ?
"not supported" : "supported"),
((ctlr->quirks & MVS_Q_GENIIE) ?
" with FBS" : ""));
mtx_init(&ctlr->mtx, "MVS controller lock", NULL, MTX_DEF);
/* We should have a memory BAR(0). */
ctlr->r_rid = 0;
if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
&ctlr->r_rid, RF_ACTIVE)))
return ENXIO;
/* Setup our own memory management for channels. */
ctlr->sc_iomem.rm_type = RMAN_ARRAY;
ctlr->sc_iomem.rm_descr = "I/O memory addresses";
if ((error = rman_init(&ctlr->sc_iomem)) != 0) {
bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem);
return (error);
}
if ((error = rman_manage_region(&ctlr->sc_iomem,
rman_get_start(ctlr->r_mem), rman_get_end(ctlr->r_mem))) != 0) {
bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem);
rman_fini(&ctlr->sc_iomem);
return (error);
}
mvs_ctlr_setup(dev);
/* Setup interrupts. */
if (mvs_setup_interrupt(dev)) {
bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem);
rman_fini(&ctlr->sc_iomem);
return ENXIO;
}
/* Attach all channels on this controller */
for (unit = 0; unit < ctlr->channels; unit++) {
child = device_add_child(dev, "mvsch", -1);
if (child == NULL)
device_printf(dev, "failed to add channel device\n");
else
device_set_ivars(child, (void *)(intptr_t)unit);
}
bus_generic_attach(dev);
return 0;
}
static int
mvs_detach(device_t dev)
{
struct mvs_controller *ctlr = device_get_softc(dev);
device_t *children;
int nchildren, i;
/* Detach & delete all children */
if (!device_get_children(dev, &children, &nchildren)) {
for (i = 0; i < nchildren; i++)
device_delete_child(dev, children[i]);
free(children, M_TEMP);
}
/* Free interrupt. */
if (ctlr->irq.r_irq) {
bus_teardown_intr(dev, ctlr->irq.r_irq,
ctlr->irq.handle);
bus_release_resource(dev, SYS_RES_IRQ,
ctlr->irq.r_irq_rid, ctlr->irq.r_irq);
}
/* Free memory. */
rman_fini(&ctlr->sc_iomem);
if (ctlr->r_mem)
bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem);
mtx_destroy(&ctlr->mtx);
return (0);
}
static int
mvs_ctlr_setup(device_t dev)
{
struct mvs_controller *ctlr = device_get_softc(dev);
int ccc = ctlr->ccc, cccc = ctlr->cccc, ccim = 0;
/* Mask chip interrupts */
ATA_OUTL(ctlr->r_mem, CHIP_SOC_MIM, 0x00000000);
/* Clear HC interrupts */
ATA_OUTL(ctlr->r_mem, HC_IC, 0x00000000);
/* Clear chip interrupts */
ATA_OUTL(ctlr->r_mem, CHIP_SOC_MIC, 0);
/* Configure per-HC CCC */
if (ccc && bootverbose) {
device_printf(dev,
"CCC with %dus/%dcmd enabled\n",
ctlr->ccc, ctlr->cccc);
}
ccc *= 150;
ATA_OUTL(ctlr->r_mem, HC_ICT, cccc);
ATA_OUTL(ctlr->r_mem, HC_ITT, ccc);
if (ccc)
ccim |= IC_HC0_COAL_DONE;
/* Enable chip interrupts */
ctlr->gmim = (ccc ? IC_HC0_COAL_DONE : IC_DONE_HC0) | IC_ERR_HC0;
ATA_OUTL(ctlr->r_mem, CHIP_SOC_MIM, ctlr->gmim | ctlr->pmim);
return (0);
}
static void
mvs_edma(device_t dev, device_t child, int mode)
{
struct mvs_controller *ctlr = device_get_softc(dev);
int unit = ((struct mvs_channel *)device_get_softc(child))->unit;
int bit = IC_DONE_IRQ << (unit * 2);
if (ctlr->ccc == 0)
return;
/* CCC is not working for non-EDMA mode. Unmask device interrupts. */
mtx_lock(&ctlr->mtx);
if (mode == MVS_EDMA_OFF)
ctlr->pmim |= bit;
else
ctlr->pmim &= ~bit;
ATA_OUTL(ctlr->r_mem, CHIP_SOC_MIM, ctlr->gmim | ctlr->pmim);
mtx_unlock(&ctlr->mtx);
}
static int
mvs_suspend(device_t dev)
{
struct mvs_controller *ctlr = device_get_softc(dev);
bus_generic_suspend(dev);
/* Mask chip interrupts */
ATA_OUTL(ctlr->r_mem, CHIP_SOC_MIM, 0x00000000);
return 0;
}
static int
mvs_resume(device_t dev)
{
mvs_ctlr_setup(dev);
return (bus_generic_resume(dev));
}
static int
mvs_setup_interrupt(device_t dev)
{
struct mvs_controller *ctlr = device_get_softc(dev);
/* Allocate all IRQs. */
ctlr->irq.r_irq_rid = 0;
if (!(ctlr->irq.r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
&ctlr->irq.r_irq_rid, RF_SHAREABLE | RF_ACTIVE))) {
device_printf(dev, "unable to map interrupt\n");
return (ENXIO);
}
if ((bus_setup_intr(dev, ctlr->irq.r_irq, ATA_INTR_FLAGS, NULL,
mvs_intr, ctlr, &ctlr->irq.handle))) {
device_printf(dev, "unable to setup interrupt\n");
bus_release_resource(dev, SYS_RES_IRQ,
ctlr->irq.r_irq_rid, ctlr->irq.r_irq);
ctlr->irq.r_irq = 0;
return (ENXIO);
}
return (0);
}
/*
* Common case interrupt handler.
*/
static void
mvs_intr(void *data)
{
struct mvs_controller *ctlr = data;
struct mvs_intr_arg arg;
void (*function)(void *);
int p;
u_int32_t ic, aic;
ic = ATA_INL(ctlr->r_mem, CHIP_SOC_MIC);
//device_printf(ctlr->dev, "irq MIC:%08x\n", ic);
if ((ic & IC_HC0) == 0)
return;
/* Acknowledge interrupts of this HC. */
aic = 0;
if (ic & (IC_DONE_IRQ << 0))
aic |= HC_IC_DONE(0) | HC_IC_DEV(0);
if (ic & (IC_DONE_IRQ << 2))
aic |= HC_IC_DONE(1) | HC_IC_DEV(1);
if (ic & (IC_DONE_IRQ << 4))
aic |= HC_IC_DONE(2) | HC_IC_DEV(2);
if (ic & (IC_DONE_IRQ << 6))
aic |= HC_IC_DONE(3) | HC_IC_DEV(3);
if (ic & IC_HC0_COAL_DONE)
aic |= HC_IC_COAL;
ATA_OUTL(ctlr->r_mem, HC_IC, ~aic);
/* Call per-port interrupt handler. */
for (p = 0; p < ctlr->channels; p++) {
arg.cause = ic & (IC_ERR_IRQ|IC_DONE_IRQ);
if ((arg.cause != 0) &&
(function = ctlr->interrupt[p].function)) {
arg.arg = ctlr->interrupt[p].argument;
function(&arg);
}
ic >>= 2;
}
}
static struct resource *
mvs_alloc_resource(device_t dev, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags)
{
struct mvs_controller *ctlr = device_get_softc(dev);
int unit = ((struct mvs_channel *)device_get_softc(child))->unit;
struct resource *res = NULL;
int offset = PORT_BASE(unit & 0x03);
long st;
switch (type) {
case SYS_RES_MEMORY:
st = rman_get_start(ctlr->r_mem);
res = rman_reserve_resource(&ctlr->sc_iomem, st + offset,
st + offset + PORT_SIZE - 1, PORT_SIZE, RF_ACTIVE, child);
if (res) {
bus_space_handle_t bsh;
bus_space_tag_t bst;
bsh = rman_get_bushandle(ctlr->r_mem);
bst = rman_get_bustag(ctlr->r_mem);
bus_space_subregion(bst, bsh, offset, PORT_SIZE, &bsh);
rman_set_bushandle(res, bsh);
rman_set_bustag(res, bst);
}
break;
case SYS_RES_IRQ:
if (*rid == ATA_IRQ_RID)
res = ctlr->irq.r_irq;
break;
}
return (res);
}
static int
mvs_release_resource(device_t dev, device_t child, int type, int rid,
struct resource *r)
{
switch (type) {
case SYS_RES_MEMORY:
rman_release_resource(r);
return (0);
case SYS_RES_IRQ:
if (rid != ATA_IRQ_RID)
return ENOENT;
return (0);
}
return (EINVAL);
}
static int
mvs_setup_intr(device_t dev, device_t child, struct resource *irq,
int flags, driver_filter_t *filter, driver_intr_t *function,
void *argument, void **cookiep)
{
struct mvs_controller *ctlr = device_get_softc(dev);
int unit = (intptr_t)device_get_ivars(child);
if (filter != NULL) {
printf("mvs.c: we cannot use a filter here\n");
return (EINVAL);
}
ctlr->interrupt[unit].function = function;
ctlr->interrupt[unit].argument = argument;
return (0);
}
static int
mvs_teardown_intr(device_t dev, device_t child, struct resource *irq,
void *cookie)
{
struct mvs_controller *ctlr = device_get_softc(dev);
int unit = (intptr_t)device_get_ivars(child);
ctlr->interrupt[unit].function = NULL;
ctlr->interrupt[unit].argument = NULL;
return (0);
}
static int
mvs_print_child(device_t dev, device_t child)
{
int retval;
retval = bus_print_child_header(dev, child);
retval += printf(" at channel %d",
(int)(intptr_t)device_get_ivars(child));
retval += bus_print_child_footer(dev, child);
return (retval);
}
static device_method_t mvs_methods[] = {
DEVMETHOD(device_probe, mvs_probe),
DEVMETHOD(device_attach, mvs_attach),
DEVMETHOD(device_detach, mvs_detach),
DEVMETHOD(device_suspend, mvs_suspend),
DEVMETHOD(device_resume, mvs_resume),
DEVMETHOD(bus_print_child, mvs_print_child),
DEVMETHOD(bus_alloc_resource, mvs_alloc_resource),
DEVMETHOD(bus_release_resource, mvs_release_resource),
DEVMETHOD(bus_setup_intr, mvs_setup_intr),
DEVMETHOD(bus_teardown_intr,mvs_teardown_intr),
DEVMETHOD(mvs_edma, mvs_edma),
{ 0, 0 }
};
static driver_t mvs_driver = {
"sata",
mvs_methods,
sizeof(struct mvs_controller)
};
DRIVER_MODULE(sata, mbus, mvs_driver, mvs_devclass, 0, 0);
MODULE_VERSION(sata, 1);

View File

@ -188,6 +188,7 @@ SUBDIR= ${_3dfx} \
msdosfs_iconv \
${_mse} \
msk \
mvs \
mwl \
mxge \
my \

10
sys/modules/mvs/Makefile Normal file
View File

@ -0,0 +1,10 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../dev/mvs
KMOD= mvs
SRCS= mvs.c mvs_pci.c mvs.h mvs_if.h device_if.h bus_if.h pci_if.h opt_cam.h
MFILES= kern/bus_if.m kern/device_if.m dev/pci/pci_if.m dev/mvs/mvs_if.m
.include <bsd.kmod.mk>