2007-10-26 04:58:22 +04:00
/*
mvsas . c - Marvell 88 SE6440 SAS / SATA support
Copyright 2007 Red Hat , Inc .
2008-02-23 16:15:27 +03:00
Copyright 2008 Marvell . < kewei @ marvell . com >
2007-10-26 04:58:22 +04:00
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation ; either version 2 ,
or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
See the GNU General Public License for more details .
You should have received a copy of the GNU General Public
License along with this program ; see the file COPYING . If not ,
write to the Free Software Foundation , 675 Mass Ave , Cambridge ,
MA 0213 9 , USA .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Random notes :
* hardware supports controlling the endian - ness of data
structures . this permits elimination of all the le32_to_cpu ( )
and cpu_to_le32 ( ) conversions .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/interrupt.h>
# include <linux/spinlock.h>
# include <linux/delay.h>
# include <linux/dma-mapping.h>
2008-02-24 00:53:44 +03:00
# include <linux/ctype.h>
2007-10-26 04:58:22 +04:00
# include <scsi/libsas.h>
2008-03-27 09:53:24 +03:00
# include <scsi/scsi_tcq.h>
# include <scsi/sas_ata.h>
2007-10-26 04:58:22 +04:00
# include <asm/io.h>
2008-02-23 16:15:27 +03:00
# define DRV_NAME "mvsas"
2008-03-27 09:53:24 +03:00
# define DRV_VERSION "0.5.2"
# define _MV_DUMP 0
2008-02-23 16:15:27 +03:00
# define MVS_DISABLE_NVRAM
# define MVS_DISABLE_MSI
2007-10-26 04:58:22 +04:00
# define mr32(reg) readl(regs + MVS_##reg)
# define mw32(reg,val) writel((val), regs + MVS_##reg)
2008-02-23 16:15:27 +03:00
# define mw32_f(reg,val) do { \
2007-10-26 04:58:22 +04:00
writel ( ( val ) , regs + MVS_ # # reg ) ; \
readl ( regs + MVS_ # # reg ) ; \
} while ( 0 )
2008-03-27 09:53:24 +03:00
# define MVS_ID_NOT_MAPPED 0x7f
2008-02-23 16:15:27 +03:00
# define MVS_CHIP_SLOT_SZ (1U << mvi->chip->slot_width)
/* offset for D2H FIS in the Received FIS List Structure */
# define SATA_RECEIVED_D2H_FIS(reg_set) \
( ( void * ) mvi - > rx_fis + 0x400 + 0x100 * reg_set + 0x40 )
# define SATA_RECEIVED_PIO_FIS(reg_set) \
( ( void * ) mvi - > rx_fis + 0x400 + 0x100 * reg_set + 0x20 )
# define UNASSOC_D2H_FIS(id) \
( ( void * ) mvi - > rx_fis + 0x100 * id )
# define for_each_phy(__lseq_mask, __mc, __lseq, __rest) \
for ( ( __mc ) = ( __lseq_mask ) , ( __lseq ) = 0 ; \
( __mc ) ! = 0 & & __rest ; \
( + + __lseq ) , ( __mc ) > > = 1 )
2007-10-26 04:58:22 +04:00
/* driver compile-time configuration */
enum driver_configuration {
MVS_TX_RING_SZ = 1024 , /* TX ring size (12-bit) */
MVS_RX_RING_SZ = 1024 , /* RX ring size (12-bit) */
/* software requires power-of-2
ring size */
MVS_SLOTS = 512 , /* command slots */
MVS_SLOT_BUF_SZ = 8192 , /* cmd tbl + IU + status + PRD */
MVS_SSP_CMD_SZ = 64 , /* SSP command table buffer size */
2008-02-23 16:15:27 +03:00
MVS_ATA_CMD_SZ = 96 , /* SATA command table buffer size */
2007-10-26 04:58:22 +04:00
MVS_OAF_SZ = 64 , /* Open address frame buffer size */
MVS_RX_FIS_COUNT = 17 , /* Optional rx'd FISs (max 17) */
2008-02-23 16:15:27 +03:00
MVS_QUEUE_SIZE = 30 , /* Support Queue depth */
2008-03-27 09:53:24 +03:00
MVS_CAN_QUEUE = MVS_SLOTS - 1 , /* SCSI Queue depth */
2007-10-26 04:58:22 +04:00
} ;
/* unchangeable hardware details */
enum hardware_details {
MVS_MAX_PHYS = 8 , /* max. possible phys */
MVS_MAX_PORTS = 8 , /* max. possible ports */
MVS_RX_FISL_SZ = 0x400 + ( MVS_RX_FIS_COUNT * 0x100 ) ,
} ;
/* peripheral registers (BAR2) */
enum peripheral_registers {
SPI_CTL = 0x10 , /* EEPROM control */
SPI_CMD = 0x14 , /* EEPROM command */
SPI_DATA = 0x18 , /* EEPROM data */
} ;
enum peripheral_register_bits {
TWSI_RDY = ( 1U < < 7 ) , /* EEPROM interface ready */
TWSI_RD = ( 1U < < 4 ) , /* EEPROM read access */
SPI_ADDR_MASK = 0x3ffff , /* bits 17:0 */
} ;
/* enhanced mode registers (BAR4) */
enum hw_registers {
MVS_GBL_CTL = 0x04 , /* global control */
MVS_GBL_INT_STAT = 0x08 , /* global irq status */
MVS_GBL_PI = 0x0C , /* ports implemented bitmask */
2008-02-23 16:15:27 +03:00
MVS_GBL_PORT_TYPE = 0xa0 , /* port type */
2007-10-26 04:58:22 +04:00
MVS_CTL = 0x100 , /* SAS/SATA port configuration */
MVS_PCS = 0x104 , /* SAS/SATA port control/status */
MVS_CMD_LIST_LO = 0x108 , /* cmd list addr */
MVS_CMD_LIST_HI = 0x10C ,
MVS_RX_FIS_LO = 0x110 , /* RX FIS list addr */
MVS_RX_FIS_HI = 0x114 ,
MVS_TX_CFG = 0x120 , /* TX configuration */
MVS_TX_LO = 0x124 , /* TX (delivery) ring addr */
MVS_TX_HI = 0x128 ,
2008-02-23 16:15:27 +03:00
MVS_TX_PROD_IDX = 0x12C , /* TX producer pointer */
MVS_TX_CONS_IDX = 0x130 , /* TX consumer pointer (RO) */
2007-10-26 04:58:22 +04:00
MVS_RX_CFG = 0x134 , /* RX configuration */
MVS_RX_LO = 0x138 , /* RX (completion) ring addr */
MVS_RX_HI = 0x13C ,
2008-02-23 16:15:27 +03:00
MVS_RX_CONS_IDX = 0x140 , /* RX consumer pointer (RO) */
2007-10-26 04:58:22 +04:00
MVS_INT_COAL = 0x148 , /* Int coalescing config */
MVS_INT_COAL_TMOUT = 0x14C , /* Int coalescing timeout */
MVS_INT_STAT = 0x150 , /* Central int status */
MVS_INT_MASK = 0x154 , /* Central int enable */
MVS_INT_STAT_SRS = 0x158 , /* SATA register set status */
2008-02-23 16:15:27 +03:00
MVS_INT_MASK_SRS = 0x15C ,
2007-10-26 04:58:22 +04:00
/* ports 1-3 follow after this */
MVS_P0_INT_STAT = 0x160 , /* port0 interrupt status */
MVS_P0_INT_MASK = 0x164 , /* port0 interrupt mask */
2008-02-23 16:15:27 +03:00
MVS_P4_INT_STAT = 0x200 , /* Port 4 interrupt status */
MVS_P4_INT_MASK = 0x204 , /* Port 4 interrupt enable mask */
2007-10-26 04:58:22 +04:00
/* ports 1-3 follow after this */
MVS_P0_SER_CTLSTAT = 0x180 , /* port0 serial control/status */
2008-02-23 16:15:27 +03:00
MVS_P4_SER_CTLSTAT = 0x220 , /* port4 serial control/status */
2007-10-26 04:58:22 +04:00
MVS_CMD_ADDR = 0x1B8 , /* Command register port (addr) */
MVS_CMD_DATA = 0x1BC , /* Command register port (data) */
/* ports 1-3 follow after this */
MVS_P0_CFG_ADDR = 0x1C0 , /* port0 phy register address */
MVS_P0_CFG_DATA = 0x1C4 , /* port0 phy register data */
2008-02-23 16:15:27 +03:00
MVS_P4_CFG_ADDR = 0x230 , /* Port 4 config address */
MVS_P4_CFG_DATA = 0x234 , /* Port 4 config data */
/* ports 1-3 follow after this */
MVS_P0_VSR_ADDR = 0x1E0 , /* port0 VSR address */
MVS_P0_VSR_DATA = 0x1E4 , /* port0 VSR data */
MVS_P4_VSR_ADDR = 0x250 , /* port 4 VSR addr */
MVS_P4_VSR_DATA = 0x254 , /* port 4 VSR data */
2007-10-26 04:58:22 +04:00
} ;
enum hw_register_bits {
/* MVS_GBL_CTL */
INT_EN = ( 1U < < 1 ) , /* Global int enable */
HBA_RST = ( 1U < < 0 ) , /* HBA reset */
/* MVS_GBL_INT_STAT */
INT_XOR = ( 1U < < 4 ) , /* XOR engine event */
INT_SAS_SATA = ( 1U < < 0 ) , /* SAS/SATA event */
/* MVS_GBL_PORT_TYPE */ /* shl for ports 1-3 */
SATA_TARGET = ( 1U < < 16 ) , /* port0 SATA target enable */
2008-02-23 16:15:27 +03:00
MODE_AUTO_DET_PORT7 = ( 1U < < 15 ) , /* port0 SAS/SATA autodetect */
MODE_AUTO_DET_PORT6 = ( 1U < < 14 ) ,
MODE_AUTO_DET_PORT5 = ( 1U < < 13 ) ,
MODE_AUTO_DET_PORT4 = ( 1U < < 12 ) ,
MODE_AUTO_DET_PORT3 = ( 1U < < 11 ) ,
MODE_AUTO_DET_PORT2 = ( 1U < < 10 ) ,
MODE_AUTO_DET_PORT1 = ( 1U < < 9 ) ,
MODE_AUTO_DET_PORT0 = ( 1U < < 8 ) ,
MODE_AUTO_DET_EN = MODE_AUTO_DET_PORT0 | MODE_AUTO_DET_PORT1 |
MODE_AUTO_DET_PORT2 | MODE_AUTO_DET_PORT3 |
MODE_AUTO_DET_PORT4 | MODE_AUTO_DET_PORT5 |
MODE_AUTO_DET_PORT6 | MODE_AUTO_DET_PORT7 ,
MODE_SAS_PORT7_MASK = ( 1U < < 7 ) , /* port0 SAS(1), SATA(0) mode */
MODE_SAS_PORT6_MASK = ( 1U < < 6 ) ,
MODE_SAS_PORT5_MASK = ( 1U < < 5 ) ,
MODE_SAS_PORT4_MASK = ( 1U < < 4 ) ,
MODE_SAS_PORT3_MASK = ( 1U < < 3 ) ,
MODE_SAS_PORT2_MASK = ( 1U < < 2 ) ,
MODE_SAS_PORT1_MASK = ( 1U < < 1 ) ,
MODE_SAS_PORT0_MASK = ( 1U < < 0 ) ,
MODE_SAS_SATA = MODE_SAS_PORT0_MASK | MODE_SAS_PORT1_MASK |
MODE_SAS_PORT2_MASK | MODE_SAS_PORT3_MASK |
MODE_SAS_PORT4_MASK | MODE_SAS_PORT5_MASK |
MODE_SAS_PORT6_MASK | MODE_SAS_PORT7_MASK ,
/* SAS_MODE value may be
* dictated ( in hw ) by values
* of SATA_TARGET & AUTO_DET
*/
2007-10-26 04:58:22 +04:00
/* MVS_TX_CFG */
TX_EN = ( 1U < < 16 ) , /* Enable TX */
TX_RING_SZ_MASK = 0xfff , /* TX ring size, bits 11:0 */
/* MVS_RX_CFG */
RX_EN = ( 1U < < 16 ) , /* Enable RX */
RX_RING_SZ_MASK = 0xfff , /* RX ring size, bits 11:0 */
/* MVS_INT_COAL */
COAL_EN = ( 1U < < 16 ) , /* Enable int coalescing */
/* MVS_INT_STAT, MVS_INT_MASK */
CINT_I2C = ( 1U < < 31 ) , /* I2C event */
CINT_SW0 = ( 1U < < 30 ) , /* software event 0 */
CINT_SW1 = ( 1U < < 29 ) , /* software event 1 */
CINT_PRD_BC = ( 1U < < 28 ) , /* PRD BC err for read cmd */
CINT_DMA_PCIE = ( 1U < < 27 ) , /* DMA to PCIE timeout */
CINT_MEM = ( 1U < < 26 ) , /* int mem parity err */
CINT_I2C_SLAVE = ( 1U < < 25 ) , /* slave I2C event */
CINT_SRS = ( 1U < < 3 ) , /* SRS event */
2008-02-23 16:15:27 +03:00
CINT_CI_STOP = ( 1U < < 1 ) , /* cmd issue stopped */
2007-10-26 04:58:22 +04:00
CINT_DONE = ( 1U < < 0 ) , /* cmd completion */
/* shl for ports 1-3 */
CINT_PORT_STOPPED = ( 1U < < 16 ) , /* port0 stopped */
CINT_PORT = ( 1U < < 8 ) , /* port0 event */
2008-02-23 16:15:27 +03:00
CINT_PORT_MASK_OFFSET = 8 ,
CINT_PORT_MASK = ( 0xFF < < CINT_PORT_MASK_OFFSET ) ,
2007-10-26 04:58:22 +04:00
/* TX (delivery) ring bits */
TXQ_CMD_SHIFT = 29 ,
TXQ_CMD_SSP = 1 , /* SSP protocol */
TXQ_CMD_SMP = 2 , /* SMP protocol */
TXQ_CMD_STP = 3 , /* STP/SATA protocol */
TXQ_CMD_SSP_FREE_LIST = 4 , /* add to SSP targ free list */
TXQ_CMD_SLOT_RESET = 7 , /* reset command slot */
TXQ_MODE_I = ( 1U < < 28 ) , /* mode: 0=target,1=initiator */
TXQ_PRIO_HI = ( 1U < < 27 ) , /* priority: 0=normal, 1=high */
TXQ_SRS_SHIFT = 20 , /* SATA register set */
TXQ_SRS_MASK = 0x7f ,
TXQ_PHY_SHIFT = 12 , /* PHY bitmap */
TXQ_PHY_MASK = 0xff ,
TXQ_SLOT_MASK = 0xfff , /* slot number */
/* RX (completion) ring bits */
RXQ_GOOD = ( 1U < < 23 ) , /* Response good */
RXQ_SLOT_RESET = ( 1U < < 21 ) , /* Slot reset complete */
RXQ_CMD_RX = ( 1U < < 20 ) , /* target cmd received */
RXQ_ATTN = ( 1U < < 19 ) , /* attention */
RXQ_RSP = ( 1U < < 18 ) , /* response frame xfer'd */
RXQ_ERR = ( 1U < < 17 ) , /* err info rec xfer'd */
RXQ_DONE = ( 1U < < 16 ) , /* cmd complete */
RXQ_SLOT_MASK = 0xfff , /* slot number */
/* mvs_cmd_hdr bits */
MCH_PRD_LEN_SHIFT = 16 , /* 16-bit PRD table len */
MCH_SSP_FR_TYPE_SHIFT = 13 , /* SSP frame type */
/* SSP initiator only */
MCH_SSP_FR_CMD = 0x0 , /* COMMAND frame */
/* SSP initiator or target */
MCH_SSP_FR_TASK = 0x1 , /* TASK frame */
/* SSP target only */
MCH_SSP_FR_XFER_RDY = 0x4 , /* XFER_RDY frame */
MCH_SSP_FR_RESP = 0x5 , /* RESPONSE frame */
MCH_SSP_FR_READ = 0x6 , /* Read DATA frame(s) */
MCH_SSP_FR_READ_RESP = 0x7 , /* ditto, plus RESPONSE */
MCH_PASSTHRU = ( 1U < < 12 ) , /* pass-through (SSP) */
MCH_FBURST = ( 1U < < 11 ) , /* first burst (SSP) */
MCH_CHK_LEN = ( 1U < < 10 ) , /* chk xfer len (SSP) */
MCH_RETRY = ( 1U < < 9 ) , /* tport layer retry (SSP) */
MCH_PROTECTION = ( 1U < < 8 ) , /* protection info rec (SSP) */
MCH_RESET = ( 1U < < 7 ) , /* Reset (STP/SATA) */
MCH_FPDMA = ( 1U < < 6 ) , /* First party DMA (STP/SATA) */
MCH_ATAPI = ( 1U < < 5 ) , /* ATAPI (STP/SATA) */
MCH_BIST = ( 1U < < 4 ) , /* BIST activate (STP/SATA) */
MCH_PMP_MASK = 0xf , /* PMP from cmd FIS (STP/SATA)*/
CCTL_RST = ( 1U < < 5 ) , /* port logic reset */
/* 0(LSB first), 1(MSB first) */
CCTL_ENDIAN_DATA = ( 1U < < 3 ) , /* PRD data */
CCTL_ENDIAN_RSP = ( 1U < < 2 ) , /* response frame */
CCTL_ENDIAN_OPEN = ( 1U < < 1 ) , /* open address frame */
CCTL_ENDIAN_CMD = ( 1U < < 0 ) , /* command table */
/* MVS_Px_SER_CTLSTAT (per-phy control) */
PHY_SSP_RST = ( 1U < < 3 ) , /* reset SSP link layer */
PHY_BCAST_CHG = ( 1U < < 2 ) , /* broadcast(change) notif */
PHY_RST_HARD = ( 1U < < 1 ) , /* hard reset + phy reset */
PHY_RST = ( 1U < < 0 ) , /* phy reset */
2008-02-23 16:15:27 +03:00
PHY_MIN_SPP_PHYS_LINK_RATE_MASK = ( 0xF < < 8 ) ,
PHY_MAX_SPP_PHYS_LINK_RATE_MASK = ( 0xF < < 12 ) ,
PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET = ( 16 ) ,
PHY_NEG_SPP_PHYS_LINK_RATE_MASK =
( 0xF < < PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET ) ,
PHY_READY_MASK = ( 1U < < 20 ) ,
2007-10-26 04:58:22 +04:00
/* MVS_Px_INT_STAT, MVS_Px_INT_MASK (per-phy events) */
2008-02-23 16:15:27 +03:00
PHYEV_DEC_ERR = ( 1U < < 24 ) , /* Phy Decoding Error */
2007-10-26 04:58:22 +04:00
PHYEV_UNASSOC_FIS = ( 1U < < 19 ) , /* unassociated FIS rx'd */
PHYEV_AN = ( 1U < < 18 ) , /* SATA async notification */
PHYEV_BIST_ACT = ( 1U < < 17 ) , /* BIST activate FIS */
PHYEV_SIG_FIS = ( 1U < < 16 ) , /* signature FIS */
PHYEV_POOF = ( 1U < < 12 ) , /* phy ready from 1 -> 0 */
PHYEV_IU_BIG = ( 1U < < 11 ) , /* IU too long err */
PHYEV_IU_SMALL = ( 1U < < 10 ) , /* IU too short err */
PHYEV_UNK_TAG = ( 1U < < 9 ) , /* unknown tag */
PHYEV_BROAD_CH = ( 1U < < 8 ) , /* broadcast(CHANGE) */
PHYEV_COMWAKE = ( 1U < < 7 ) , /* COMWAKE rx'd */
PHYEV_PORT_SEL = ( 1U < < 6 ) , /* port selector present */
PHYEV_HARD_RST = ( 1U < < 5 ) , /* hard reset rx'd */
PHYEV_ID_TMOUT = ( 1U < < 4 ) , /* identify timeout */
PHYEV_ID_FAIL = ( 1U < < 3 ) , /* identify failed */
PHYEV_ID_DONE = ( 1U < < 2 ) , /* identify done */
PHYEV_HARD_RST_DONE = ( 1U < < 1 ) , /* hard reset done */
PHYEV_RDY_CH = ( 1U < < 0 ) , /* phy ready changed state */
/* MVS_PCS */
2008-02-23 16:15:27 +03:00
PCS_EN_SATA_REG_SHIFT = ( 16 ) , /* Enable SATA Register Set */
PCS_EN_PORT_XMT_SHIFT = ( 12 ) , /* Enable Port Transmit */
PCS_EN_PORT_XMT_SHIFT2 = ( 8 ) , /* For 6480 */
2007-10-26 04:58:22 +04:00
PCS_SATA_RETRY = ( 1U < < 8 ) , /* retry ctl FIS on R_ERR */
PCS_RSP_RX_EN = ( 1U < < 7 ) , /* raw response rx */
PCS_SELF_CLEAR = ( 1U < < 5 ) , /* self-clearing int mode */
PCS_FIS_RX_EN = ( 1U < < 4 ) , /* FIS rx enable */
PCS_CMD_STOP_ERR = ( 1U < < 3 ) , /* cmd stop-on-err enable */
2008-02-23 16:15:27 +03:00
PCS_CMD_RST = ( 1U < < 1 ) , /* reset cmd issue */
2007-10-26 04:58:22 +04:00
PCS_CMD_EN = ( 1U < < 0 ) , /* enable cmd issue */
2008-02-23 16:15:27 +03:00
/* Port n Attached Device Info */
PORT_DEV_SSP_TRGT = ( 1U < < 19 ) ,
PORT_DEV_SMP_TRGT = ( 1U < < 18 ) ,
PORT_DEV_STP_TRGT = ( 1U < < 17 ) ,
PORT_DEV_SSP_INIT = ( 1U < < 11 ) ,
PORT_DEV_SMP_INIT = ( 1U < < 10 ) ,
PORT_DEV_STP_INIT = ( 1U < < 9 ) ,
PORT_PHY_ID_MASK = ( 0xFFU < < 24 ) ,
PORT_DEV_TRGT_MASK = ( 0x7U < < 17 ) ,
PORT_DEV_INIT_MASK = ( 0x7U < < 9 ) ,
PORT_DEV_TYPE_MASK = ( 0x7U < < 0 ) ,
/* Port n PHY Status */
PHY_RDY = ( 1U < < 2 ) ,
PHY_DW_SYNC = ( 1U < < 1 ) ,
PHY_OOB_DTCTD = ( 1U < < 0 ) ,
/* VSR */
/* PHYMODE 6 (CDB) */
2008-03-27 09:53:24 +03:00
PHY_MODE6_LATECLK = ( 1U < < 29 ) , /* Lock Clock */
PHY_MODE6_DTL_SPEED = ( 1U < < 27 ) , /* Digital Loop Speed */
PHY_MODE6_FC_ORDER = ( 1U < < 26 ) , /* Fibre Channel Mode Order*/
PHY_MODE6_MUCNT_EN = ( 1U < < 24 ) , /* u Count Enable */
PHY_MODE6_SEL_MUCNT_LEN = ( 1U < < 22 ) , /* Training Length Select */
PHY_MODE6_SELMUPI = ( 1U < < 20 ) , /* Phase Multi Select (init) */
PHY_MODE6_SELMUPF = ( 1U < < 18 ) , /* Phase Multi Select (final) */
PHY_MODE6_SELMUFF = ( 1U < < 16 ) , /* Freq Loop Multi Sel(final) */
PHY_MODE6_SELMUFI = ( 1U < < 14 ) , /* Freq Loop Multi Sel(init) */
PHY_MODE6_FREEZE_LOOP = ( 1U < < 12 ) , /* Freeze Rx CDR Loop */
PHY_MODE6_INT_RXFOFFS = ( 1U < < 3 ) , /* Rx CDR Freq Loop Enable */
PHY_MODE6_FRC_RXFOFFS = ( 1U < < 2 ) , /* Initial Rx CDR Offset */
PHY_MODE6_STAU_0D8 = ( 1U < < 1 ) , /* Rx CDR Freq Loop Saturate */
PHY_MODE6_RXSAT_DIS = ( 1U < < 0 ) , /* Saturate Ctl */
2007-10-26 04:58:22 +04:00
} ;
enum mvs_info_flags {
MVF_MSI = ( 1U < < 0 ) , /* MSI is enabled */
MVF_PHY_PWR_FIX = ( 1U < < 1 ) , /* bug workaround */
} ;
enum sas_cmd_port_registers {
CMD_CMRST_OOB_DET = 0x100 , /* COMRESET OOB detect register */
CMD_CMWK_OOB_DET = 0x104 , /* COMWAKE OOB detect register */
CMD_CMSAS_OOB_DET = 0x108 , /* COMSAS OOB detect register */
CMD_BRST_OOB_DET = 0x10c , /* burst OOB detect register */
CMD_OOB_SPACE = 0x110 , /* OOB space control register */
CMD_OOB_BURST = 0x114 , /* OOB burst control register */
CMD_PHY_TIMER = 0x118 , /* PHY timer control register */
CMD_PHY_CONFIG0 = 0x11c , /* PHY config register 0 */
CMD_PHY_CONFIG1 = 0x120 , /* PHY config register 1 */
CMD_SAS_CTL0 = 0x124 , /* SAS control register 0 */
CMD_SAS_CTL1 = 0x128 , /* SAS control register 1 */
CMD_SAS_CTL2 = 0x12c , /* SAS control register 2 */
CMD_SAS_CTL3 = 0x130 , /* SAS control register 3 */
CMD_ID_TEST = 0x134 , /* ID test register */
CMD_PL_TIMER = 0x138 , /* PL timer register */
CMD_WD_TIMER = 0x13c , /* WD timer register */
CMD_PORT_SEL_COUNT = 0x140 , /* port selector count register */
CMD_APP_MEM_CTL = 0x144 , /* Application Memory Control */
CMD_XOR_MEM_CTL = 0x148 , /* XOR Block Memory Control */
CMD_DMA_MEM_CTL = 0x14c , /* DMA Block Memory Control */
CMD_PORT_MEM_CTL0 = 0x150 , /* Port Memory Control 0 */
CMD_PORT_MEM_CTL1 = 0x154 , /* Port Memory Control 1 */
CMD_SATA_PORT_MEM_CTL0 = 0x158 , /* SATA Port Memory Control 0 */
CMD_SATA_PORT_MEM_CTL1 = 0x15c , /* SATA Port Memory Control 1 */
CMD_XOR_MEM_BIST_CTL = 0x160 , /* XOR Memory BIST Control */
CMD_XOR_MEM_BIST_STAT = 0x164 , /* XOR Memroy BIST Status */
CMD_DMA_MEM_BIST_CTL = 0x168 , /* DMA Memory BIST Control */
CMD_DMA_MEM_BIST_STAT = 0x16c , /* DMA Memory BIST Status */
CMD_PORT_MEM_BIST_CTL = 0x170 , /* Port Memory BIST Control */
CMD_PORT_MEM_BIST_STAT0 = 0x174 , /* Port Memory BIST Status 0 */
CMD_PORT_MEM_BIST_STAT1 = 0x178 , /* Port Memory BIST Status 1 */
CMD_STP_MEM_BIST_CTL = 0x17c , /* STP Memory BIST Control */
CMD_STP_MEM_BIST_STAT0 = 0x180 , /* STP Memory BIST Status 0 */
CMD_STP_MEM_BIST_STAT1 = 0x184 , /* STP Memory BIST Status 1 */
CMD_RESET_COUNT = 0x188 , /* Reset Count */
CMD_MONTR_DATA_SEL = 0x18C , /* Monitor Data/Select */
CMD_PLL_PHY_CONFIG = 0x190 , /* PLL/PHY Configuration */
CMD_PHY_CTL = 0x194 , /* PHY Control and Status */
CMD_PHY_TEST_COUNT0 = 0x198 , /* Phy Test Count 0 */
CMD_PHY_TEST_COUNT1 = 0x19C , /* Phy Test Count 1 */
CMD_PHY_TEST_COUNT2 = 0x1A0 , /* Phy Test Count 2 */
CMD_APP_ERR_CONFIG = 0x1A4 , /* Application Error Configuration */
CMD_PND_FIFO_CTL0 = 0x1A8 , /* Pending FIFO Control 0 */
CMD_HOST_CTL = 0x1AC , /* Host Control Status */
CMD_HOST_WR_DATA = 0x1B0 , /* Host Write Data */
CMD_HOST_RD_DATA = 0x1B4 , /* Host Read Data */
CMD_PHY_MODE_21 = 0x1B8 , /* Phy Mode 21 */
CMD_SL_MODE0 = 0x1BC , /* SL Mode 0 */
CMD_SL_MODE1 = 0x1C0 , /* SL Mode 1 */
CMD_PND_FIFO_CTL1 = 0x1C4 , /* Pending FIFO Control 1 */
} ;
/* SAS/SATA configuration port registers, aka phy registers */
enum sas_sata_config_port_regs {
2008-02-23 16:15:27 +03:00
PHYR_IDENTIFY = 0x00 , /* info for IDENTIFY frame */
PHYR_ADDR_LO = 0x04 , /* my SAS address (low) */
PHYR_ADDR_HI = 0x08 , /* my SAS address (high) */
PHYR_ATT_DEV_INFO = 0x0C , /* attached device info */
2007-10-26 04:58:22 +04:00
PHYR_ATT_ADDR_LO = 0x10 , /* attached dev SAS addr (low) */
PHYR_ATT_ADDR_HI = 0x14 , /* attached dev SAS addr (high) */
PHYR_SATA_CTL = 0x18 , /* SATA control */
PHYR_PHY_STAT = 0x1C , /* PHY status */
2008-02-23 16:15:27 +03:00
PHYR_SATA_SIG0 = 0x20 , /*port SATA signature FIS(Byte 0-3) */
PHYR_SATA_SIG1 = 0x24 , /*port SATA signature FIS(Byte 4-7) */
PHYR_SATA_SIG2 = 0x28 , /*port SATA signature FIS(Byte 8-11) */
PHYR_SATA_SIG3 = 0x2c , /*port SATA signature FIS(Byte 12-15) */
PHYR_R_ERR_COUNT = 0x30 , /* port R_ERR count register */
PHYR_CRC_ERR_COUNT = 0x34 , /* port CRC error count register */
2007-10-26 04:58:22 +04:00
PHYR_WIDE_PORT = 0x38 , /* wide port participating */
PHYR_CURRENT0 = 0x80 , /* current connection info 0 */
PHYR_CURRENT1 = 0x84 , /* current connection info 1 */
PHYR_CURRENT2 = 0x88 , /* current connection info 2 */
} ;
2008-02-23 16:15:27 +03:00
/* SAS/SATA Vendor Specific Port Registers */
enum sas_sata_vsp_regs {
VSR_PHY_STAT = 0x00 , /* Phy Status */
VSR_PHY_MODE1 = 0x01 , /* phy tx */
VSR_PHY_MODE2 = 0x02 , /* tx scc */
VSR_PHY_MODE3 = 0x03 , /* pll */
VSR_PHY_MODE4 = 0x04 , /* VCO */
VSR_PHY_MODE5 = 0x05 , /* Rx */
VSR_PHY_MODE6 = 0x06 , /* CDR */
VSR_PHY_MODE7 = 0x07 , /* Impedance */
VSR_PHY_MODE8 = 0x08 , /* Voltage */
VSR_PHY_MODE9 = 0x09 , /* Test */
VSR_PHY_MODE10 = 0x0A , /* Power */
VSR_PHY_MODE11 = 0x0B , /* Phy Mode */
VSR_PHY_VS0 = 0x0C , /* Vednor Specific 0 */
VSR_PHY_VS1 = 0x0D , /* Vednor Specific 1 */
} ;
2007-10-26 04:58:22 +04:00
enum pci_cfg_registers {
2008-02-23 16:15:27 +03:00
PCR_PHY_CTL = 0x40 ,
PCR_PHY_CTL2 = 0x90 ,
PCR_DEV_CTRL = 0xE8 ,
2007-10-26 04:58:22 +04:00
} ;
enum pci_cfg_register_bits {
2008-02-23 16:15:27 +03:00
PCTL_PWR_ON = ( 0xFU < < 24 ) ,
PCTL_OFF = ( 0xFU < < 12 ) ,
PRD_REQ_SIZE = ( 0x4000 ) ,
PRD_REQ_MASK = ( 0x00007000 ) ,
2007-10-26 04:58:22 +04:00
} ;
enum nvram_layout_offsets {
2008-02-23 16:15:27 +03:00
NVR_SIG = 0x00 , /* 0xAA, 0x55 */
NVR_SAS_ADDR = 0x02 , /* 8-byte SAS address */
2007-10-26 04:58:22 +04:00
} ;
enum chip_flavors {
chip_6320 ,
chip_6440 ,
chip_6480 ,
} ;
2008-02-23 16:15:27 +03:00
enum port_type {
PORT_TYPE_SAS = ( 1L < < 1 ) ,
PORT_TYPE_SATA = ( 1L < < 0 ) ,
} ;
/* Command Table Format */
enum ct_format {
/* SSP */
SSP_F_H = 0x00 ,
SSP_F_IU = 0x18 ,
SSP_F_MAX = 0x4D ,
/* STP */
STP_CMD_FIS = 0x00 ,
STP_ATAPI_CMD = 0x40 ,
STP_F_MAX = 0x10 ,
/* SMP */
SMP_F_T = 0x00 ,
SMP_F_DEP = 0x01 ,
SMP_F_MAX = 0x101 ,
} ;
enum status_buffer {
SB_EIR_OFF = 0x00 , /* Error Information Record */
SB_RFB_OFF = 0x08 , /* Response Frame Buffer */
SB_RFB_MAX = 0x400 , /* RFB size*/
} ;
enum error_info_rec {
2008-03-27 09:53:24 +03:00
CMD_ISS_STPD = ( 1U < < 31 ) , /* Cmd Issue Stopped */
CMD_PI_ERR = ( 1U < < 30 ) , /* Protection info error. see flags2 */
RSP_OVER = ( 1U < < 29 ) , /* rsp buffer overflow */
RETRY_LIM = ( 1U < < 28 ) , /* FIS/frame retry limit exceeded */
UNK_FIS = ( 1U < < 27 ) , /* unknown FIS */
DMA_TERM = ( 1U < < 26 ) , /* DMA terminate primitive rx'd */
SYNC_ERR = ( 1U < < 25 ) , /* SYNC rx'd during frame xmit */
TFILE_ERR = ( 1U < < 24 ) , /* SATA taskfile Error bit set */
R_ERR = ( 1U < < 23 ) , /* SATA returned R_ERR prim */
RD_OFS = ( 1U < < 20 ) , /* Read DATA frame invalid offset */
XFER_RDY_OFS = ( 1U < < 19 ) , /* XFER_RDY offset error */
UNEXP_XFER_RDY = ( 1U < < 18 ) , /* unexpected XFER_RDY error */
DATA_OVER_UNDER = ( 1U < < 16 ) , /* data overflow/underflow */
INTERLOCK = ( 1U < < 15 ) , /* interlock error */
NAK = ( 1U < < 14 ) , /* NAK rx'd */
ACK_NAK_TO = ( 1U < < 13 ) , /* ACK/NAK timeout */
CXN_CLOSED = ( 1U < < 12 ) , /* cxn closed w/out ack/nak */
OPEN_TO = ( 1U < < 11 ) , /* I_T nexus lost, open cxn timeout */
PATH_BLOCKED = ( 1U < < 10 ) , /* I_T nexus lost, pathway blocked */
NO_DEST = ( 1U < < 9 ) , /* I_T nexus lost, no destination */
STP_RES_BSY = ( 1U < < 8 ) , /* STP resources busy */
BREAK = ( 1U < < 7 ) , /* break received */
BAD_DEST = ( 1U < < 6 ) , /* bad destination */
BAD_PROTO = ( 1U < < 5 ) , /* protocol not supported */
BAD_RATE = ( 1U < < 4 ) , /* cxn rate not supported */
WRONG_DEST = ( 1U < < 3 ) , /* wrong destination error */
CREDIT_TO = ( 1U < < 2 ) , /* credit timeout */
WDOG_TO = ( 1U < < 1 ) , /* watchdog timeout */
BUF_PAR = ( 1U < < 0 ) , /* buffer parity error */
} ;
enum error_info_rec_2 {
SLOT_BSY_ERR = ( 1U < < 31 ) , /* Slot Busy Error */
GRD_CHK_ERR = ( 1U < < 14 ) , /* Guard Check Error */
APP_CHK_ERR = ( 1U < < 13 ) , /* Application Check error */
REF_CHK_ERR = ( 1U < < 12 ) , /* Reference Check Error */
USR_BLK_NM = ( 1U < < 0 ) , /* User Block Number */
2008-02-23 16:15:27 +03:00
} ;
2007-10-26 04:58:22 +04:00
struct mvs_chip_info {
2008-02-23 16:15:27 +03:00
u32 n_phy ;
u32 srs_sz ;
u32 slot_width ;
2007-10-26 04:58:22 +04:00
} ;
struct mvs_err_info {
__le32 flags ;
__le32 flags2 ;
} ;
struct mvs_prd {
__le64 addr ; /* 64-bit buffer address */
__le32 reserved ;
__le32 len ; /* 16-bit length */
} ;
struct mvs_cmd_hdr {
__le32 flags ; /* PRD tbl len; SAS, SATA ctl */
__le32 lens ; /* cmd, max resp frame len */
__le32 tags ; /* targ port xfer tag; tag */
__le32 data_len ; /* data xfer len */
__le64 cmd_tbl ; /* command table address */
__le64 open_frame ; /* open addr frame address */
__le64 status_buf ; /* status buffer address */
__le64 prd_tbl ; /* PRD tbl address */
__le32 reserved [ 4 ] ;
} ;
struct mvs_port {
struct asd_sas_port sas_port ;
2008-02-23 16:15:27 +03:00
u8 port_attached ;
u8 taskfileset ;
u8 wide_port_phymap ;
2008-03-27 09:53:24 +03:00
struct list_head list ;
2007-10-26 04:58:22 +04:00
} ;
struct mvs_phy {
struct mvs_port * port ;
struct asd_sas_phy sas_phy ;
2008-02-23 16:15:27 +03:00
struct sas_identify identify ;
struct scsi_device * sdev ;
u64 dev_sas_addr ;
u64 att_dev_sas_addr ;
u32 att_dev_info ;
u32 dev_info ;
u32 phy_type ;
u32 phy_status ;
u32 irq_status ;
u32 frame_rcvd_size ;
u8 frame_rcvd [ 32 ] ;
u8 phy_attached ;
2008-03-27 09:53:24 +03:00
enum sas_linkrate minimum_linkrate ;
enum sas_linkrate maximum_linkrate ;
} ;
struct mvs_slot_info {
struct list_head list ;
struct sas_task * task ;
u32 n_elem ;
u32 tx ;
/* DMA buffer for storing cmd tbl, open addr frame, status buffer,
* and PRD table
*/
void * buf ;
dma_addr_t buf_dma ;
# if _MV_DUMP
u32 cmd_size ;
# endif
void * response ;
struct mvs_port * port ;
2007-10-26 04:58:22 +04:00
} ;
struct mvs_info {
unsigned long flags ;
spinlock_t lock ; /* host-wide lock */
struct pci_dev * pdev ; /* our device */
void __iomem * regs ; /* enhanced mode registers */
void __iomem * peri_regs ; /* peripheral registers */
u8 sas_addr [ SAS_ADDR_SIZE ] ;
struct sas_ha_struct sas ; /* SCSI/SAS glue */
struct Scsi_Host * shost ;
__le32 * tx ; /* TX (delivery) DMA ring */
dma_addr_t tx_dma ;
u32 tx_prod ; /* cached next-producer idx */
__le32 * rx ; /* RX (completion) DMA ring */
dma_addr_t rx_dma ;
u32 rx_cons ; /* RX consumer idx */
__le32 * rx_fis ; /* RX'd FIS area */
dma_addr_t rx_fis_dma ;
2008-02-23 16:15:27 +03:00
struct mvs_cmd_hdr * slot ; /* DMA command header slots */
2007-10-26 04:58:22 +04:00
dma_addr_t slot_dma ;
const struct mvs_chip_info * chip ;
2008-03-27 09:53:24 +03:00
u8 tags [ MVS_SLOTS ] ;
2007-10-26 04:58:22 +04:00
struct mvs_slot_info slot_info [ MVS_SLOTS ] ;
2008-02-23 16:15:27 +03:00
/* further per-slot information */
2007-10-26 04:58:22 +04:00
struct mvs_phy phy [ MVS_MAX_PHYS ] ;
struct mvs_port port [ MVS_MAX_PHYS ] ;
2008-03-27 09:53:24 +03:00
# ifdef MVS_USE_TASKLET
struct tasklet_struct tasklet ;
# endif
2008-02-23 16:15:27 +03:00
} ;
static int mvs_phy_control ( struct asd_sas_phy * sas_phy , enum phy_func func ,
void * funcdata ) ;
static u32 mvs_read_phy_ctl ( struct mvs_info * mvi , u32 port ) ;
static void mvs_write_phy_ctl ( struct mvs_info * mvi , u32 port , u32 val ) ;
static u32 mvs_read_port_irq_stat ( struct mvs_info * mvi , u32 port ) ;
static void mvs_write_port_irq_stat ( struct mvs_info * mvi , u32 port , u32 val ) ;
static void mvs_write_port_irq_mask ( struct mvs_info * mvi , u32 port , u32 val ) ;
static u32 mvs_read_port_irq_mask ( struct mvs_info * mvi , u32 port ) ;
static u32 mvs_is_phy_ready ( struct mvs_info * mvi , int i ) ;
static void mvs_detect_porttype ( struct mvs_info * mvi , int i ) ;
static void mvs_update_phyinfo ( struct mvs_info * mvi , int i , int get_st ) ;
2008-03-27 09:53:24 +03:00
static void mvs_release_task ( struct mvs_info * mvi , int phy_no ) ;
2008-02-23 16:15:27 +03:00
static int mvs_scan_finished ( struct Scsi_Host * , unsigned long ) ;
static void mvs_scan_start ( struct Scsi_Host * ) ;
2008-03-27 09:53:24 +03:00
static int mvs_slave_configure ( struct scsi_device * sdev ) ;
2008-02-23 16:15:27 +03:00
2007-10-26 04:58:22 +04:00
static struct scsi_transport_template * mvs_stt ;
static const struct mvs_chip_info mvs_chips [ ] = {
2008-02-23 16:15:27 +03:00
[ chip_6320 ] = { 2 , 16 , 9 } ,
[ chip_6440 ] = { 4 , 16 , 9 } ,
2007-10-26 04:58:22 +04:00
[ chip_6480 ] = { 8 , 32 , 10 } ,
} ;
static struct scsi_host_template mvs_sht = {
. module = THIS_MODULE ,
. name = DRV_NAME ,
. queuecommand = sas_queuecommand ,
. target_alloc = sas_target_alloc ,
2008-03-27 09:53:24 +03:00
. slave_configure = mvs_slave_configure ,
2007-10-26 04:58:22 +04:00
. slave_destroy = sas_slave_destroy ,
2008-02-23 16:15:27 +03:00
. scan_finished = mvs_scan_finished ,
. scan_start = mvs_scan_start ,
2007-10-26 04:58:22 +04:00
. change_queue_depth = sas_change_queue_depth ,
. change_queue_type = sas_change_queue_type ,
. bios_param = sas_bios_param ,
. can_queue = 1 ,
. cmd_per_lun = 1 ,
. this_id = - 1 ,
. sg_tablesize = SG_ALL ,
. max_sectors = SCSI_DEFAULT_MAX_SECTORS ,
. use_clustering = ENABLE_CLUSTERING ,
2008-02-23 16:15:27 +03:00
. eh_device_reset_handler = sas_eh_device_reset_handler ,
2007-10-26 04:58:22 +04:00
. eh_bus_reset_handler = sas_eh_bus_reset_handler ,
2008-03-27 09:53:24 +03:00
. slave_alloc = sas_slave_alloc ,
2007-10-26 04:58:22 +04:00
. target_destroy = sas_target_destroy ,
. ioctl = sas_ioctl ,
} ;
2008-02-23 16:15:27 +03:00
static void mvs_hexdump ( u32 size , u8 * data , u32 baseaddr )
{
u32 i ;
u32 run ;
u32 offset ;
offset = 0 ;
while ( size ) {
printk ( " %08X : " , baseaddr + offset ) ;
if ( size > = 16 )
run = 16 ;
else
run = size ;
size - = run ;
for ( i = 0 ; i < 16 ; i + + ) {
if ( i < run )
printk ( " %02X " , ( u32 ) data [ i ] ) ;
else
printk ( " " ) ;
}
printk ( " : " ) ;
for ( i = 0 ; i < run ; i + + )
printk ( " %c " , isalnum ( data [ i ] ) ? data [ i ] : ' . ' ) ;
printk ( " \n " ) ;
data = & data [ 16 ] ;
offset + = run ;
}
printk ( " \n " ) ;
}
2008-03-27 09:53:47 +03:00
# if _MV_DUMP
2008-02-23 16:15:27 +03:00
static void mvs_hba_sb_dump ( struct mvs_info * mvi , u32 tag ,
enum sas_protocol proto )
{
u32 offset ;
struct pci_dev * pdev = mvi - > pdev ;
struct mvs_slot_info * slot = & mvi - > slot_info [ tag ] ;
offset = slot - > cmd_size + MVS_OAF_SZ +
sizeof ( struct mvs_prd ) * slot - > n_elem ;
dev_printk ( KERN_DEBUG , & pdev - > dev , " +---->Status buffer[%d] : \n " ,
tag ) ;
mvs_hexdump ( 32 , ( u8 * ) slot - > response ,
( u32 ) slot - > buf_dma + offset ) ;
}
2008-03-27 09:53:47 +03:00
# endif
2008-02-23 16:15:27 +03:00
static void mvs_hba_memory_dump ( struct mvs_info * mvi , u32 tag ,
enum sas_protocol proto )
{
# if _MV_DUMP
2008-03-27 09:53:47 +03:00
u32 sz , w_ptr ;
2008-02-23 16:15:27 +03:00
u64 addr ;
void __iomem * regs = mvi - > regs ;
struct pci_dev * pdev = mvi - > pdev ;
struct mvs_slot_info * slot = & mvi - > slot_info [ tag ] ;
/*Delivery Queue */
sz = mr32 ( TX_CFG ) & TX_RING_SZ_MASK ;
2008-03-27 09:53:47 +03:00
w_ptr = slot - > tx ;
2008-02-23 16:15:27 +03:00
addr = mr32 ( TX_HI ) < < 16 < < 16 | mr32 ( TX_LO ) ;
dev_printk ( KERN_DEBUG , & pdev - > dev ,
2008-03-27 09:53:47 +03:00
" Delivery Queue Size=%04d , WRT_PTR=%04X \n " , sz , w_ptr ) ;
2008-02-23 16:15:27 +03:00
dev_printk ( KERN_DEBUG , & pdev - > dev ,
" Delivery Queue Base Address=0x%llX (PA) "
" (tx_dma=0x%llX), Entry=%04d \n " ,
addr , mvi - > tx_dma , w_ptr ) ;
mvs_hexdump ( sizeof ( u32 ) , ( u8 * ) ( & mvi - > tx [ mvi - > tx_prod ] ) ,
( u32 ) mvi - > tx_dma + sizeof ( u32 ) * w_ptr ) ;
/*Command List */
2008-03-27 09:53:47 +03:00
addr = mvi - > slot_dma ;
2008-02-23 16:15:27 +03:00
dev_printk ( KERN_DEBUG , & pdev - > dev ,
" Command List Base Address=0x%llX (PA) "
" (slot_dma=0x%llX), Header=%03d \n " ,
2008-03-27 09:53:47 +03:00
addr , slot - > buf_dma , tag ) ;
2008-02-23 16:15:27 +03:00
dev_printk ( KERN_DEBUG , & pdev - > dev , " Command Header[%03d]: \n " , tag ) ;
/*mvs_cmd_hdr */
mvs_hexdump ( sizeof ( struct mvs_cmd_hdr ) , ( u8 * ) ( & mvi - > slot [ tag ] ) ,
( u32 ) mvi - > slot_dma + tag * sizeof ( struct mvs_cmd_hdr ) ) ;
/*1.command table area */
dev_printk ( KERN_DEBUG , & pdev - > dev , " +---->Command Table : \n " ) ;
mvs_hexdump ( slot - > cmd_size , ( u8 * ) slot - > buf , ( u32 ) slot - > buf_dma ) ;
/*2.open address frame area */
dev_printk ( KERN_DEBUG , & pdev - > dev , " +---->Open Address Frame : \n " ) ;
mvs_hexdump ( MVS_OAF_SZ , ( u8 * ) slot - > buf + slot - > cmd_size ,
( u32 ) slot - > buf_dma + slot - > cmd_size ) ;
/*3.status buffer */
mvs_hba_sb_dump ( mvi , tag , proto ) ;
/*4.PRD table */
dev_printk ( KERN_DEBUG , & pdev - > dev , " +---->PRD table : \n " ) ;
mvs_hexdump ( sizeof ( struct mvs_prd ) * slot - > n_elem ,
( u8 * ) slot - > buf + slot - > cmd_size + MVS_OAF_SZ ,
( u32 ) slot - > buf_dma + slot - > cmd_size + MVS_OAF_SZ ) ;
# endif
}
static void mvs_hba_cq_dump ( struct mvs_info * mvi )
{
2008-03-27 09:53:47 +03:00
# if (_MV_DUMP > 2)
2008-02-23 16:15:27 +03:00
u64 addr ;
void __iomem * regs = mvi - > regs ;
struct pci_dev * pdev = mvi - > pdev ;
u32 entry = mvi - > rx_cons + 1 ;
u32 rx_desc = le32_to_cpu ( mvi - > rx [ entry ] ) ;
/*Completion Queue */
addr = mr32 ( RX_HI ) < < 16 < < 16 | mr32 ( RX_LO ) ;
2008-03-27 09:53:47 +03:00
dev_printk ( KERN_DEBUG , & pdev - > dev , " Completion Task = 0x%p \n " ,
mvi - > slot_info [ rx_desc & RXQ_SLOT_MASK ] . task ) ;
2008-02-23 16:15:27 +03:00
dev_printk ( KERN_DEBUG , & pdev - > dev ,
" Completion List Base Address=0x%llX (PA), "
" CQ_Entry=%04d, CQ_WP=0x%08X \n " ,
addr , entry - 1 , mvi - > rx [ 0 ] ) ;
mvs_hexdump ( sizeof ( u32 ) , ( u8 * ) ( & rx_desc ) ,
mvi - > rx_dma + sizeof ( u32 ) * entry ) ;
# endif
}
static void mvs_hba_interrupt_enable ( struct mvs_info * mvi )
{
void __iomem * regs = mvi - > regs ;
u32 tmp ;
tmp = mr32 ( GBL_CTL ) ;
mw32 ( GBL_CTL , tmp | INT_EN ) ;
}
static void mvs_hba_interrupt_disable ( struct mvs_info * mvi )
{
void __iomem * regs = mvi - > regs ;
u32 tmp ;
tmp = mr32 ( GBL_CTL ) ;
mw32 ( GBL_CTL , tmp & ~ INT_EN ) ;
}
static int mvs_int_rx ( struct mvs_info * mvi , bool self_clear ) ;
2007-10-26 04:58:22 +04:00
/* move to PCI layer or libata core? */
static int pci_go_64 ( struct pci_dev * pdev )
{
int rc ;
if ( ! pci_set_dma_mask ( pdev , DMA_64BIT_MASK ) ) {
rc = pci_set_consistent_dma_mask ( pdev , DMA_64BIT_MASK ) ;
if ( rc ) {
rc = pci_set_consistent_dma_mask ( pdev , DMA_32BIT_MASK ) ;
if ( rc ) {
dev_printk ( KERN_ERR , & pdev - > dev ,
" 64-bit DMA enable failed \n " ) ;
return rc ;
}
}
} else {
rc = pci_set_dma_mask ( pdev , DMA_32BIT_MASK ) ;
if ( rc ) {
dev_printk ( KERN_ERR , & pdev - > dev ,
" 32-bit DMA enable failed \n " ) ;
return rc ;
}
rc = pci_set_consistent_dma_mask ( pdev , DMA_32BIT_MASK ) ;
if ( rc ) {
dev_printk ( KERN_ERR , & pdev - > dev ,
" 32-bit consistent DMA enable failed \n " ) ;
return rc ;
}
}
return rc ;
}
2008-03-27 09:53:47 +03:00
static int mvs_find_tag ( struct mvs_info * mvi , struct sas_task * task , u32 * tag )
{
if ( task - > lldd_task ) {
struct mvs_slot_info * slot ;
slot = ( struct mvs_slot_info * ) task - > lldd_task ;
* tag = slot - mvi - > slot_info ;
return 1 ;
}
return 0 ;
}
2008-02-23 16:15:27 +03:00
static void mvs_tag_clear ( struct mvs_info * mvi , u32 tag )
2007-10-26 04:58:22 +04:00
{
2008-03-27 09:53:47 +03:00
void * bitmap = ( void * ) & mvi - > tags ;
clear_bit ( tag , bitmap ) ;
2007-10-26 04:58:22 +04:00
}
2008-02-23 16:15:27 +03:00
static void mvs_tag_free ( struct mvs_info * mvi , u32 tag )
2007-10-26 04:58:22 +04:00
{
2008-03-27 09:53:47 +03:00
mvs_tag_clear ( mvi , tag ) ;
}
static void mvs_tag_set ( struct mvs_info * mvi , unsigned int tag )
{
void * bitmap = ( void * ) & mvi - > tags ;
set_bit ( tag , bitmap ) ;
2007-10-26 04:58:22 +04:00
}
2008-02-23 16:15:27 +03:00
static int mvs_tag_alloc ( struct mvs_info * mvi , u32 * tag_out )
2007-10-26 04:58:22 +04:00
{
2008-03-27 09:53:47 +03:00
unsigned int index , tag ;
void * bitmap = ( void * ) & mvi - > tags ;
index = find_first_zero_bit ( bitmap , MVS_SLOTS ) ;
tag = index ;
if ( tag > = MVS_SLOTS )
return - SAS_QUEUE_FULL ;
mvs_tag_set ( mvi , tag ) ;
* tag_out = tag ;
return 0 ;
2007-10-26 04:58:22 +04:00
}
2008-02-23 16:15:27 +03:00
static void mvs_tag_init ( struct mvs_info * mvi )
2007-10-26 04:58:22 +04:00
{
2008-02-23 16:15:27 +03:00
int i ;
for ( i = 0 ; i < MVS_SLOTS ; + + i )
2008-03-27 09:53:47 +03:00
mvs_tag_clear ( mvi , i ) ;
2007-10-26 04:58:22 +04:00
}
2008-02-23 16:15:27 +03:00
# ifndef MVS_DISABLE_NVRAM
static int mvs_eep_read ( void __iomem * regs , u32 addr , u32 * data )
2007-10-26 04:58:22 +04:00
{
int timeout = 1000 ;
if ( addr & ~ SPI_ADDR_MASK )
return - EINVAL ;
writel ( addr , regs + SPI_CMD ) ;
writel ( TWSI_RD , regs + SPI_CTL ) ;
while ( timeout - - > 0 ) {
if ( readl ( regs + SPI_CTL ) & TWSI_RDY ) {
* data = readl ( regs + SPI_DATA ) ;
return 0 ;
}
udelay ( 10 ) ;
}
return - EBUSY ;
}
2008-02-23 16:15:27 +03:00
static int mvs_eep_read_buf ( void __iomem * regs , u32 addr ,
void * buf , u32 buflen )
2007-10-26 04:58:22 +04:00
{
2008-02-23 16:15:27 +03:00
u32 addr_end , tmp_addr , i , j ;
2007-10-26 04:58:22 +04:00
u32 tmp = 0 ;
int rc ;
u8 * tmp8 , * buf8 = buf ;
addr_end = addr + buflen ;
tmp_addr = ALIGN ( addr , 4 ) ;
if ( addr > 0xff )
return - EINVAL ;
j = addr & 0x3 ;
if ( j ) {
rc = mvs_eep_read ( regs , tmp_addr , & tmp ) ;
if ( rc )
return rc ;
2008-02-23 16:15:27 +03:00
tmp8 = ( u8 * ) & tmp ;
2007-10-26 04:58:22 +04:00
for ( i = j ; i < 4 ; i + + )
* buf8 + + = tmp8 [ i ] ;
tmp_addr + = 4 ;
}
for ( j = ALIGN ( addr_end , 4 ) ; tmp_addr < j ; tmp_addr + = 4 ) {
rc = mvs_eep_read ( regs , tmp_addr , & tmp ) ;
if ( rc )
return rc ;
memcpy ( buf8 , & tmp , 4 ) ;
buf8 + = 4 ;
}
if ( tmp_addr < addr_end ) {
rc = mvs_eep_read ( regs , tmp_addr , & tmp ) ;
if ( rc )
return rc ;
2008-02-23 16:15:27 +03:00
tmp8 = ( u8 * ) & tmp ;
2007-10-26 04:58:22 +04:00
j = addr_end - tmp_addr ;
for ( i = 0 ; i < j ; i + + )
* buf8 + + = tmp8 [ i ] ;
tmp_addr + = 4 ;
}
return 0 ;
}
2008-02-23 16:15:27 +03:00
# endif
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
static int mvs_nvram_read ( struct mvs_info * mvi , u32 addr ,
void * buf , u32 buflen )
2007-10-26 04:58:22 +04:00
{
2008-02-23 16:15:27 +03:00
# ifndef MVS_DISABLE_NVRAM
2007-10-26 04:58:22 +04:00
void __iomem * regs = mvi - > regs ;
int rc , i ;
2008-02-23 16:15:27 +03:00
u32 sum ;
2007-10-26 04:58:22 +04:00
u8 hdr [ 2 ] , * tmp ;
const char * msg ;
rc = mvs_eep_read_buf ( regs , addr , & hdr , 2 ) ;
if ( rc ) {
msg = " nvram hdr read failed " ;
goto err_out ;
}
rc = mvs_eep_read_buf ( regs , addr + 2 , buf , buflen ) ;
if ( rc ) {
msg = " nvram read failed " ;
goto err_out ;
}
2008-02-23 16:15:27 +03:00
if ( hdr [ 0 ] ! = 0x5A ) {
/* entry id */
2007-10-26 04:58:22 +04:00
msg = " invalid nvram entry id " ;
rc = - ENOENT ;
goto err_out ;
}
tmp = buf ;
2008-02-23 16:15:27 +03:00
sum = ( ( u32 ) hdr [ 0 ] ) + ( ( u32 ) hdr [ 1 ] ) ;
2007-10-26 04:58:22 +04:00
for ( i = 0 ; i < buflen ; i + + )
2008-02-23 16:15:27 +03:00
sum + = ( ( u32 ) tmp [ i ] ) ;
2007-10-26 04:58:22 +04:00
if ( sum ) {
msg = " nvram checksum failure " ;
rc = - EILSEQ ;
goto err_out ;
}
return 0 ;
err_out :
dev_printk ( KERN_ERR , & mvi - > pdev - > dev , " %s " , msg ) ;
2008-02-23 16:15:27 +03:00
return rc ;
# else
/* FIXME , For SAS target mode */
2008-02-27 15:50:25 +03:00
memcpy ( buf , " \x50 \x05 \x04 \x30 \x11 \xab \x00 \x00 " , 8 ) ;
2008-02-23 16:15:27 +03:00
return 0 ;
# endif
}
static void mvs_bytes_dmaed ( struct mvs_info * mvi , int i )
{
struct mvs_phy * phy = & mvi - > phy [ i ] ;
2008-03-27 09:53:47 +03:00
struct asd_sas_phy * sas_phy = mvi - > sas . sas_phy [ i ] ;
2008-02-23 16:15:27 +03:00
if ( ! phy - > phy_attached )
return ;
2008-03-27 09:53:47 +03:00
if ( sas_phy - > phy ) {
struct sas_phy * sphy = sas_phy - > phy ;
sphy - > negotiated_linkrate = sas_phy - > linkrate ;
sphy - > minimum_linkrate = phy - > minimum_linkrate ;
sphy - > minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS ;
sphy - > maximum_linkrate = phy - > maximum_linkrate ;
sphy - > maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS ;
}
2008-02-23 16:15:27 +03:00
if ( phy - > phy_type & PORT_TYPE_SAS ) {
struct sas_identify_frame * id ;
id = ( struct sas_identify_frame * ) phy - > frame_rcvd ;
id - > dev_type = phy - > identify . device_type ;
id - > initiator_bits = SAS_PROTOCOL_ALL ;
id - > target_bits = phy - > identify . target_port_protocols ;
} else if ( phy - > phy_type & PORT_TYPE_SATA ) {
/* TODO */
}
mvi - > sas . sas_phy [ i ] - > frame_rcvd_size = phy - > frame_rcvd_size ;
mvi - > sas . notify_port_event ( mvi - > sas . sas_phy [ i ] ,
PORTE_BYTES_DMAED ) ;
}
static int mvs_scan_finished ( struct Scsi_Host * shost , unsigned long time )
{
/* give the phy enabling interrupt event time to come in (1s
* is empirically about all it takes ) */
if ( time < HZ )
return 0 ;
/* Wait for discovery to finish */
scsi_flush_work ( shost ) ;
return 1 ;
}
static void mvs_scan_start ( struct Scsi_Host * shost )
{
int i ;
struct mvs_info * mvi = SHOST_TO_SAS_HA ( shost ) - > lldd_ha ;
for ( i = 0 ; i < mvi - > chip - > n_phy ; + + i ) {
mvs_bytes_dmaed ( mvi , i ) ;
}
}
2008-03-27 09:53:47 +03:00
static int mvs_slave_configure ( struct scsi_device * sdev )
2008-02-23 16:15:27 +03:00
{
2008-03-27 09:53:47 +03:00
struct domain_device * dev = sdev_to_domain_dev ( sdev ) ;
int ret = sas_slave_configure ( sdev ) ;
2008-02-23 16:15:27 +03:00
2008-03-27 09:53:47 +03:00
if ( ret )
return ret ;
2008-02-23 16:15:27 +03:00
2008-03-27 09:53:47 +03:00
if ( dev_is_sata ( dev ) ) {
/* struct ata_port *ap = dev->sata_dev.ap; */
/* struct ata_device *adev = ap->link.device; */
/* clamp at no NCQ for the time being */
/* adev->flags |= ATA_DFLAG_NCQ_OFF; */
scsi_adjust_queue_depth ( sdev , MSG_SIMPLE_TAG , 1 ) ;
}
return 0 ;
2007-10-26 04:58:22 +04:00
}
2008-03-27 09:53:47 +03:00
static void mvs_int_port ( struct mvs_info * mvi , int phy_no , u32 events )
2007-10-26 04:58:22 +04:00
{
2008-02-23 16:15:27 +03:00
struct pci_dev * pdev = mvi - > pdev ;
struct sas_ha_struct * sas_ha = & mvi - > sas ;
2008-03-27 09:53:47 +03:00
struct mvs_phy * phy = & mvi - > phy [ phy_no ] ;
2008-02-23 16:15:27 +03:00
struct asd_sas_phy * sas_phy = & phy - > sas_phy ;
2008-03-27 09:53:47 +03:00
phy - > irq_status = mvs_read_port_irq_stat ( mvi , phy_no ) ;
2008-02-23 16:15:27 +03:00
/*
* events is port event now ,
* we need check the interrupt status which belongs to per port .
*/
dev_printk ( KERN_DEBUG , & pdev - > dev ,
" Port %d Event = %X \n " ,
2008-03-27 09:53:47 +03:00
phy_no , phy - > irq_status ) ;
2008-02-23 16:15:27 +03:00
if ( phy - > irq_status & ( PHYEV_POOF | PHYEV_DEC_ERR ) ) {
2008-03-27 09:53:47 +03:00
mvs_release_task ( mvi , phy_no ) ;
if ( ! mvs_is_phy_ready ( mvi , phy_no ) ) {
2008-02-23 16:15:27 +03:00
sas_phy_disconnected ( sas_phy ) ;
sas_ha - > notify_phy_event ( sas_phy , PHYE_LOSS_OF_SIGNAL ) ;
2008-03-27 09:53:47 +03:00
dev_printk ( KERN_INFO , & pdev - > dev ,
" Port %d Unplug Notice \n " , phy_no ) ;
2008-02-23 16:15:27 +03:00
} else
mvs_phy_control ( sas_phy , PHY_FUNC_LINK_RESET , NULL ) ;
}
if ( ! ( phy - > irq_status & PHYEV_DEC_ERR ) ) {
if ( phy - > irq_status & PHYEV_COMWAKE ) {
2008-03-27 09:53:47 +03:00
u32 tmp = mvs_read_port_irq_mask ( mvi , phy_no ) ;
mvs_write_port_irq_mask ( mvi , phy_no ,
2008-02-23 16:15:27 +03:00
tmp | PHYEV_SIG_FIS ) ;
}
if ( phy - > irq_status & ( PHYEV_SIG_FIS | PHYEV_ID_DONE ) ) {
2008-03-27 09:53:47 +03:00
phy - > phy_status = mvs_is_phy_ready ( mvi , phy_no ) ;
2008-02-23 16:15:27 +03:00
if ( phy - > phy_status ) {
2008-03-27 09:53:47 +03:00
mvs_detect_porttype ( mvi , phy_no ) ;
2008-02-23 16:15:27 +03:00
if ( phy - > phy_type & PORT_TYPE_SATA ) {
u32 tmp = mvs_read_port_irq_mask ( mvi ,
2008-03-27 09:53:47 +03:00
phy_no ) ;
2008-02-23 16:15:27 +03:00
tmp & = ~ PHYEV_SIG_FIS ;
mvs_write_port_irq_mask ( mvi ,
2008-03-27 09:53:47 +03:00
phy_no , tmp ) ;
2008-02-23 16:15:27 +03:00
}
2008-03-27 09:53:47 +03:00
mvs_update_phyinfo ( mvi , phy_no , 0 ) ;
2008-02-23 16:15:27 +03:00
sas_ha - > notify_phy_event ( sas_phy ,
PHYE_OOB_DONE ) ;
2008-03-27 09:53:47 +03:00
mvs_bytes_dmaed ( mvi , phy_no ) ;
2008-02-23 16:15:27 +03:00
} else {
dev_printk ( KERN_DEBUG , & pdev - > dev ,
" plugin interrupt but phy is gone \n " ) ;
mvs_phy_control ( sas_phy , PHY_FUNC_LINK_RESET ,
NULL ) ;
}
2008-03-27 09:53:47 +03:00
} else if ( phy - > irq_status & PHYEV_BROAD_CH ) {
mvs_release_task ( mvi , phy_no ) ;
2008-02-23 16:15:27 +03:00
sas_ha - > notify_port_event ( sas_phy ,
PORTE_BROADCAST_RCVD ) ;
2008-03-27 09:53:47 +03:00
}
2008-02-23 16:15:27 +03:00
}
2008-03-27 09:53:47 +03:00
mvs_write_port_irq_stat ( mvi , phy_no , phy - > irq_status ) ;
2007-10-26 04:58:22 +04:00
}
static void mvs_int_sata ( struct mvs_info * mvi )
{
2008-03-27 09:54:23 +03:00
u32 tmp ;
void __iomem * regs = mvi - > regs ;
tmp = mr32 ( INT_STAT_SRS ) ;
mw32 ( INT_STAT_SRS , tmp & 0xFFFF ) ;
}
static void mvs_slot_reset ( struct mvs_info * mvi , struct sas_task * task ,
u32 slot_idx )
{
void __iomem * regs = mvi - > regs ;
struct domain_device * dev = task - > dev ;
struct asd_sas_port * sas_port = dev - > port ;
struct mvs_port * port = mvi - > slot_info [ slot_idx ] . port ;
u32 reg_set , phy_mask ;
if ( ! sas_protocol_ata ( task - > task_proto ) ) {
reg_set = 0 ;
phy_mask = ( port - > wide_port_phymap ) ? port - > wide_port_phymap :
sas_port - > phy_mask ;
} else {
reg_set = port - > taskfileset ;
phy_mask = sas_port - > phy_mask ;
}
mvi - > tx [ mvi - > tx_prod ] = cpu_to_le32 ( TXQ_MODE_I | slot_idx |
( TXQ_CMD_SLOT_RESET < < TXQ_CMD_SHIFT ) |
( phy_mask < < TXQ_PHY_SHIFT ) |
( reg_set < < TXQ_SRS_SHIFT ) ) ;
mw32 ( TX_PROD_IDX , mvi - > tx_prod ) ;
mvi - > tx_prod = ( mvi - > tx_prod + 1 ) & ( MVS_CHIP_SLOT_SZ - 1 ) ;
}
static int mvs_sata_done ( struct mvs_info * mvi , struct sas_task * task ,
u32 slot_idx , int err )
{
struct mvs_port * port = mvi - > slot_info [ slot_idx ] . port ;
struct task_status_struct * tstat = & task - > task_status ;
struct ata_task_resp * resp = ( struct ata_task_resp * ) tstat - > buf ;
int stat = SAM_GOOD ;
resp - > frame_len = sizeof ( struct dev_to_host_fis ) ;
memcpy ( & resp - > ending_fis [ 0 ] ,
SATA_RECEIVED_D2H_FIS ( port - > taskfileset ) ,
sizeof ( struct dev_to_host_fis ) ) ;
tstat - > buf_valid_size = sizeof ( * resp ) ;
if ( unlikely ( err ) )
stat = SAS_PROTO_RESPONSE ;
return stat ;
2007-10-26 04:58:22 +04:00
}
2008-03-27 09:54:23 +03:00
static void mvs_slot_free ( struct mvs_info * mvi , u32 rx_desc )
{
u32 slot_idx = rx_desc & RXQ_SLOT_MASK ;
mvs_tag_clear ( mvi , slot_idx ) ;
}
static void mvs_slot_task_free ( struct mvs_info * mvi , struct sas_task * task ,
2008-02-23 16:15:27 +03:00
struct mvs_slot_info * slot , u32 slot_idx )
2007-10-26 04:58:22 +04:00
{
2008-02-23 16:15:27 +03:00
if ( ! sas_protocol_ata ( task - > task_proto ) )
if ( slot - > n_elem )
pci_unmap_sg ( mvi - > pdev , task - > scatter ,
slot - > n_elem , task - > data_dir ) ;
2007-10-26 04:58:22 +04:00
switch ( task - > task_proto ) {
case SAS_PROTOCOL_SMP :
pci_unmap_sg ( mvi - > pdev , & task - > smp_task . smp_resp , 1 ,
PCI_DMA_FROMDEVICE ) ;
pci_unmap_sg ( mvi - > pdev , & task - > smp_task . smp_req , 1 ,
PCI_DMA_TODEVICE ) ;
break ;
case SAS_PROTOCOL_SATA :
case SAS_PROTOCOL_STP :
case SAS_PROTOCOL_SSP :
default :
/* do nothing */
break ;
}
2008-03-27 09:54:23 +03:00
list_del ( & slot - > list ) ;
task - > lldd_task = NULL ;
2008-02-23 16:15:27 +03:00
slot - > task = NULL ;
2008-03-27 09:54:23 +03:00
slot - > port = NULL ;
2007-10-26 04:58:22 +04:00
}
2008-03-27 09:54:23 +03:00
static int mvs_slot_err ( struct mvs_info * mvi , struct sas_task * task ,
2008-02-23 16:15:27 +03:00
u32 slot_idx )
2007-10-26 04:58:22 +04:00
{
2008-02-23 16:15:27 +03:00
struct mvs_slot_info * slot = & mvi - > slot_info [ slot_idx ] ;
2008-03-27 09:54:23 +03:00
u32 err_dw0 = le32_to_cpu ( * ( u32 * ) ( slot - > response ) ) ;
u32 err_dw1 = le32_to_cpu ( * ( u32 * ) ( slot - > response + 4 ) ) ;
int stat = SAM_CHECK_COND ;
2008-02-23 16:15:27 +03:00
2008-03-27 09:54:23 +03:00
if ( err_dw1 & SLOT_BSY_ERR ) {
stat = SAS_QUEUE_FULL ;
mvs_slot_reset ( mvi , task , slot_idx ) ;
}
switch ( task - > task_proto ) {
case SAS_PROTOCOL_SSP :
break ;
case SAS_PROTOCOL_SMP :
break ;
case SAS_PROTOCOL_SATA :
case SAS_PROTOCOL_STP :
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP :
if ( err_dw0 & TFILE_ERR )
stat = mvs_sata_done ( mvi , task , slot_idx , 1 ) ;
break ;
default :
break ;
}
2008-02-23 16:15:27 +03:00
2008-03-27 09:54:23 +03:00
mvs_hexdump ( 16 , ( u8 * ) slot - > response , 0 ) ;
return stat ;
2007-10-26 04:58:22 +04:00
}
2008-03-27 09:54:23 +03:00
static int mvs_slot_complete ( struct mvs_info * mvi , u32 rx_desc , u32 flags )
2007-10-26 04:58:22 +04:00
{
2008-02-23 16:15:27 +03:00
u32 slot_idx = rx_desc & RXQ_SLOT_MASK ;
2007-10-26 04:58:22 +04:00
struct mvs_slot_info * slot = & mvi - > slot_info [ slot_idx ] ;
struct sas_task * task = slot - > task ;
2008-03-27 09:54:23 +03:00
struct task_status_struct * tstat ;
struct mvs_port * port ;
2007-10-26 04:58:22 +04:00
bool aborted ;
2008-02-23 16:15:27 +03:00
void * to ;
2007-10-26 04:58:22 +04:00
2008-03-27 09:54:23 +03:00
if ( unlikely ( ! task | | ! task - > lldd_task ) )
return - 1 ;
mvs_hba_cq_dump ( mvi ) ;
2007-10-26 04:58:22 +04:00
spin_lock ( & task - > task_state_lock ) ;
aborted = task - > task_state_flags & SAS_TASK_STATE_ABORTED ;
if ( ! aborted ) {
task - > task_state_flags & =
2008-02-23 16:15:27 +03:00
~ ( SAS_TASK_STATE_PENDING | SAS_TASK_AT_INITIATOR ) ;
2007-10-26 04:58:22 +04:00
task - > task_state_flags | = SAS_TASK_STATE_DONE ;
}
spin_unlock ( & task - > task_state_lock ) ;
2008-03-27 09:54:23 +03:00
if ( aborted ) {
mvs_slot_task_free ( mvi , task , slot , slot_idx ) ;
mvs_slot_free ( mvi , rx_desc ) ;
2008-02-23 16:15:27 +03:00
return - 1 ;
2008-03-27 09:54:23 +03:00
}
2007-10-26 04:58:22 +04:00
2008-03-27 09:54:23 +03:00
port = slot - > port ;
tstat = & task - > task_status ;
2007-10-26 04:58:22 +04:00
memset ( tstat , 0 , sizeof ( * tstat ) ) ;
tstat - > resp = SAS_TASK_COMPLETE ;
2008-03-27 09:54:23 +03:00
if ( unlikely ( ! port - > port_attached | | flags ) ) {
mvs_slot_err ( mvi , task , slot_idx ) ;
if ( ! sas_protocol_ata ( task - > task_proto ) )
tstat - > stat = SAS_PHY_DOWN ;
2008-02-23 16:15:27 +03:00
goto out ;
}
2007-10-26 04:58:22 +04:00
/* error info record present */
2008-03-27 09:54:23 +03:00
if ( unlikely ( ( rx_desc & RXQ_ERR ) & & ( * ( u64 * ) slot - > response ) ) ) {
tstat - > stat = mvs_slot_err ( mvi , task , slot_idx ) ;
2007-10-26 04:58:22 +04:00
goto out ;
}
switch ( task - > task_proto ) {
case SAS_PROTOCOL_SSP :
/* hw says status == 0, datapres == 0 */
2008-02-23 16:15:27 +03:00
if ( rx_desc & RXQ_GOOD ) {
2007-10-26 04:58:22 +04:00
tstat - > stat = SAM_GOOD ;
2008-02-23 16:15:27 +03:00
tstat - > resp = SAS_TASK_COMPLETE ;
}
2007-10-26 04:58:22 +04:00
/* response frame present */
else if ( rx_desc & RXQ_RSP ) {
struct ssp_response_iu * iu =
2008-02-23 16:15:27 +03:00
slot - > response + sizeof ( struct mvs_err_info ) ;
2007-10-26 04:58:22 +04:00
sas_ssp_task_response ( & mvi - > pdev - > dev , task , iu ) ;
}
/* should never happen? */
else
tstat - > stat = SAM_CHECK_COND ;
break ;
2008-02-23 16:15:27 +03:00
case SAS_PROTOCOL_SMP : {
struct scatterlist * sg_resp = & task - > smp_task . smp_resp ;
tstat - > stat = SAM_GOOD ;
to = kmap_atomic ( sg_page ( sg_resp ) , KM_IRQ0 ) ;
memcpy ( to + sg_resp - > offset ,
slot - > response + sizeof ( struct mvs_err_info ) ,
sg_dma_len ( sg_resp ) ) ;
kunmap_atomic ( to , KM_IRQ0 ) ;
break ;
}
2007-10-26 04:58:22 +04:00
case SAS_PROTOCOL_SATA :
case SAS_PROTOCOL_STP :
2008-02-23 16:15:27 +03:00
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP : {
2008-03-27 09:54:23 +03:00
tstat - > stat = mvs_sata_done ( mvi , task , slot_idx , 0 ) ;
2008-02-23 16:15:27 +03:00
break ;
}
2007-10-26 04:58:22 +04:00
default :
tstat - > stat = SAM_CHECK_COND ;
break ;
}
out :
2008-03-27 09:54:23 +03:00
mvs_slot_task_free ( mvi , task , slot , slot_idx ) ;
if ( unlikely ( tstat - > stat ! = SAS_QUEUE_FULL ) )
mvs_slot_free ( mvi , rx_desc ) ;
spin_unlock ( & mvi - > lock ) ;
2007-10-26 04:58:22 +04:00
task - > task_done ( task ) ;
2008-03-27 09:54:23 +03:00
spin_lock ( & mvi - > lock ) ;
2008-02-23 16:15:27 +03:00
return tstat - > stat ;
2007-10-26 04:58:22 +04:00
}
2008-03-27 09:54:23 +03:00
static void mvs_release_task ( struct mvs_info * mvi , int phy_no )
{
struct list_head * pos , * n ;
struct mvs_slot_info * slot ;
struct mvs_phy * phy = & mvi - > phy [ phy_no ] ;
struct mvs_port * port = phy - > port ;
u32 rx_desc ;
if ( ! port )
return ;
list_for_each_safe ( pos , n , & port - > list ) {
slot = container_of ( pos , struct mvs_slot_info , list ) ;
rx_desc = ( u32 ) ( slot - mvi - > slot_info ) ;
mvs_slot_complete ( mvi , rx_desc , 1 ) ;
}
}
2007-10-26 04:58:22 +04:00
static void mvs_int_full ( struct mvs_info * mvi )
{
void __iomem * regs = mvi - > regs ;
u32 tmp , stat ;
int i ;
stat = mr32 ( INT_STAT ) ;
2008-02-23 16:15:27 +03:00
mvs_int_rx ( mvi , false ) ;
2007-10-26 04:58:22 +04:00
for ( i = 0 ; i < MVS_MAX_PORTS ; i + + ) {
tmp = ( stat > > i ) & ( CINT_PORT | CINT_PORT_STOPPED ) ;
if ( tmp )
mvs_int_port ( mvi , i , tmp ) ;
}
if ( stat & CINT_SRS )
mvs_int_sata ( mvi ) ;
mw32 ( INT_STAT , stat ) ;
}
2008-02-23 16:15:27 +03:00
static int mvs_int_rx ( struct mvs_info * mvi , bool self_clear )
2007-10-26 04:58:22 +04:00
{
2008-02-23 16:15:27 +03:00
void __iomem * regs = mvi - > regs ;
2007-10-26 04:58:22 +04:00
u32 rx_prod_idx , rx_desc ;
bool attn = false ;
2008-02-23 16:15:27 +03:00
struct pci_dev * pdev = mvi - > pdev ;
2007-10-26 04:58:22 +04:00
/* the first dword in the RX ring is special: it contains
* a mirror of the hardware ' s RX producer index , so that
* we don ' t have to stall the CPU reading that register .
* The actual RX ring is offset by one dword , due to this .
*/
2008-03-27 09:54:23 +03:00
rx_prod_idx = mvi - > rx_cons ;
mvi - > rx_cons = le32_to_cpu ( mvi - > rx [ 0 ] ) ;
if ( mvi - > rx_cons = = 0xfff ) /* h/w hasn't touched RX ring yet */
2008-02-23 16:15:27 +03:00
return 0 ;
/* The CMPL_Q may come late, read from register and try again
* note : if coalescing is enabled ,
* it will need to read from register every time for sure
*/
if ( mvi - > rx_cons = = rx_prod_idx )
2008-03-27 09:54:23 +03:00
mvi - > rx_cons = mr32 ( RX_CONS_IDX ) & RX_RING_SZ_MASK ;
2008-02-23 16:15:27 +03:00
2008-03-27 09:54:23 +03:00
if ( mvi - > rx_cons = = rx_prod_idx )
return 0 ;
2007-10-26 04:58:22 +04:00
while ( mvi - > rx_cons ! = rx_prod_idx ) {
2008-02-23 16:15:27 +03:00
2007-10-26 04:58:22 +04:00
/* increment our internal RX consumer pointer */
2008-03-27 09:54:23 +03:00
rx_prod_idx = ( rx_prod_idx + 1 ) & ( MVS_RX_RING_SZ - 1 ) ;
2007-10-26 04:58:22 +04:00
2008-03-27 09:54:23 +03:00
rx_desc = le32_to_cpu ( mvi - > rx [ rx_prod_idx + 1 ] ) ;
2007-10-26 04:58:22 +04:00
2008-02-27 15:50:25 +03:00
if ( likely ( rx_desc & RXQ_DONE ) )
2008-03-27 09:54:23 +03:00
mvs_slot_complete ( mvi , rx_desc , 0 ) ;
2008-02-23 16:15:27 +03:00
if ( rx_desc & RXQ_ATTN ) {
2007-10-26 04:58:22 +04:00
attn = true ;
2008-02-23 16:15:27 +03:00
dev_printk ( KERN_DEBUG , & pdev - > dev , " ATTN %X \n " ,
rx_desc ) ;
} else if ( rx_desc & RXQ_ERR ) {
2008-03-27 09:54:23 +03:00
if ( ! ( rx_desc & RXQ_DONE ) )
mvs_slot_complete ( mvi , rx_desc , 0 ) ;
2008-02-23 16:15:27 +03:00
dev_printk ( KERN_DEBUG , & pdev - > dev , " RXQ_ERR %X \n " ,
rx_desc ) ;
2008-03-27 09:54:23 +03:00
} else if ( rx_desc & RXQ_SLOT_RESET ) {
dev_printk ( KERN_DEBUG , & pdev - > dev , " Slot reset[%X] \n " ,
rx_desc ) ;
mvs_slot_free ( mvi , rx_desc ) ;
2008-02-23 16:15:27 +03:00
}
2007-10-26 04:58:22 +04:00
}
if ( attn & & self_clear )
mvs_int_full ( mvi ) ;
2008-02-23 16:15:27 +03:00
return 0 ;
2007-10-26 04:58:22 +04:00
}
2008-03-27 09:54:23 +03:00
# ifdef MVS_USE_TASKLET
static void mvs_tasklet ( unsigned long data )
{
struct mvs_info * mvi = ( struct mvs_info * ) data ;
unsigned long flags ;
spin_lock_irqsave ( & mvi - > lock , flags ) ;
# ifdef MVS_DISABLE_MSI
mvs_int_full ( mvi ) ;
# else
mvs_int_rx ( mvi , true ) ;
# endif
spin_unlock_irqrestore ( & mvi - > lock , flags ) ;
}
# endif
2007-10-26 04:58:22 +04:00
static irqreturn_t mvs_interrupt ( int irq , void * opaque )
{
struct mvs_info * mvi = opaque ;
void __iomem * regs = mvi - > regs ;
u32 stat ;
stat = mr32 ( GBL_INT_STAT ) ;
2008-02-23 16:15:27 +03:00
2007-10-26 04:58:22 +04:00
if ( stat = = 0 | | stat = = 0xffffffff )
return IRQ_NONE ;
2008-03-27 09:54:23 +03:00
/* clear CMD_CMPLT ASAP */
mw32_f ( INT_STAT , CINT_DONE ) ;
# ifndef MVS_USE_TASKLET
2007-10-26 04:58:22 +04:00
spin_lock ( & mvi - > lock ) ;
mvs_int_full ( mvi ) ;
spin_unlock ( & mvi - > lock ) ;
2008-03-27 09:54:23 +03:00
# else
tasklet_schedule ( & mvi - > tasklet ) ;
# endif
2007-10-26 04:58:22 +04:00
return IRQ_HANDLED ;
}
2008-02-23 16:15:27 +03:00
# ifndef MVS_DISABLE_MSI
2007-10-26 04:58:22 +04:00
static irqreturn_t mvs_msi_interrupt ( int irq , void * opaque )
{
struct mvs_info * mvi = opaque ;
2008-03-27 09:54:23 +03:00
# ifndef MVS_USE_TASKLET
2007-10-26 04:58:22 +04:00
spin_lock ( & mvi - > lock ) ;
mvs_int_rx ( mvi , true ) ;
spin_unlock ( & mvi - > lock ) ;
2008-03-27 09:54:23 +03:00
# else
tasklet_schedule ( & mvi - > tasklet ) ;
# endif
2007-10-26 04:58:22 +04:00
return IRQ_HANDLED ;
}
2008-02-23 16:15:27 +03:00
# endif
2007-10-26 04:58:22 +04:00
struct mvs_task_exec_info {
2008-02-23 16:15:27 +03:00
struct sas_task * task ;
struct mvs_cmd_hdr * hdr ;
struct mvs_port * port ;
u32 tag ;
int n_elem ;
2007-10-26 04:58:22 +04:00
} ;
2008-02-23 16:15:27 +03:00
static int mvs_task_prep_smp ( struct mvs_info * mvi ,
struct mvs_task_exec_info * tei )
2007-10-26 04:58:22 +04:00
{
2008-02-23 16:15:27 +03:00
int elem , rc , i ;
struct sas_task * task = tei - > task ;
2007-10-26 04:58:22 +04:00
struct mvs_cmd_hdr * hdr = tei - > hdr ;
struct scatterlist * sg_req , * sg_resp ;
2008-02-23 16:15:27 +03:00
u32 req_len , resp_len , tag = tei - > tag ;
void * buf_tmp ;
u8 * buf_oaf ;
dma_addr_t buf_tmp_dma ;
struct mvs_prd * buf_prd ;
struct scatterlist * sg ;
struct mvs_slot_info * slot = & mvi - > slot_info [ tag ] ;
struct asd_sas_port * sas_port = task - > dev - > port ;
u32 flags = ( tei - > n_elem < < MCH_PRD_LEN_SHIFT ) ;
# if _MV_DUMP
u8 * buf_cmd ;
void * from ;
# endif
2007-10-26 04:58:22 +04:00
/*
* DMA - map SMP request , response buffers
*/
2008-02-23 16:15:27 +03:00
sg_req = & task - > smp_task . smp_req ;
2007-10-26 04:58:22 +04:00
elem = pci_map_sg ( mvi - > pdev , sg_req , 1 , PCI_DMA_TODEVICE ) ;
if ( ! elem )
return - ENOMEM ;
req_len = sg_dma_len ( sg_req ) ;
2008-02-23 16:15:27 +03:00
sg_resp = & task - > smp_task . smp_resp ;
2007-10-26 04:58:22 +04:00
elem = pci_map_sg ( mvi - > pdev , sg_resp , 1 , PCI_DMA_FROMDEVICE ) ;
if ( ! elem ) {
rc = - ENOMEM ;
goto err_out ;
}
resp_len = sg_dma_len ( sg_resp ) ;
/* must be in dwords */
if ( ( req_len & 0x3 ) | | ( resp_len & 0x3 ) ) {
rc = - EINVAL ;
goto err_out_2 ;
}
/*
2008-02-23 16:15:27 +03:00
* arrange MVS_SLOT_BUF_SZ - sized DMA buffer according to our needs
2007-10-26 04:58:22 +04:00
*/
2008-02-23 16:15:27 +03:00
/* region 1: command table area (MVS_SSP_CMD_SZ bytes) ************** */
buf_tmp = slot - > buf ;
buf_tmp_dma = slot - > buf_dma ;
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
# if _MV_DUMP
buf_cmd = buf_tmp ;
hdr - > cmd_tbl = cpu_to_le64 ( buf_tmp_dma ) ;
buf_tmp + = req_len ;
buf_tmp_dma + = req_len ;
slot - > cmd_size = req_len ;
# else
hdr - > cmd_tbl = cpu_to_le64 ( sg_dma_address ( sg_req ) ) ;
# endif
/* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
buf_oaf = buf_tmp ;
hdr - > open_frame = cpu_to_le64 ( buf_tmp_dma ) ;
buf_tmp + = MVS_OAF_SZ ;
buf_tmp_dma + = MVS_OAF_SZ ;
/* region 3: PRD table ********************************************* */
buf_prd = buf_tmp ;
if ( tei - > n_elem )
hdr - > prd_tbl = cpu_to_le64 ( buf_tmp_dma ) ;
else
hdr - > prd_tbl = 0 ;
i = sizeof ( struct mvs_prd ) * tei - > n_elem ;
buf_tmp + = i ;
buf_tmp_dma + = i ;
/* region 4: status buffer (larger the PRD, smaller this buf) ****** */
slot - > response = buf_tmp ;
hdr - > status_buf = cpu_to_le64 ( buf_tmp_dma ) ;
/*
* Fill in TX ring and command slot header
*/
slot - > tx = mvi - > tx_prod ;
mvi - > tx [ mvi - > tx_prod ] = cpu_to_le32 ( ( TXQ_CMD_SMP < < TXQ_CMD_SHIFT ) |
TXQ_MODE_I | tag |
( sas_port - > phy_mask < < TXQ_PHY_SHIFT ) ) ;
hdr - > flags | = flags ;
hdr - > lens = cpu_to_le32 ( ( ( resp_len / 4 ) < < 16 ) | ( ( req_len - 4 ) / 4 ) ) ;
2007-10-26 04:58:22 +04:00
hdr - > tags = cpu_to_le32 ( tag ) ;
hdr - > data_len = 0 ;
2008-02-23 16:15:27 +03:00
/* generate open address frame hdr (first 12 bytes) */
buf_oaf [ 0 ] = ( 1 < < 7 ) | ( 0 < < 4 ) | 0x01 ; /* initiator, SMP, ftype 1h */
buf_oaf [ 1 ] = task - > dev - > linkrate & 0xf ;
* ( u16 * ) ( buf_oaf + 2 ) = 0xFFFF ; /* SAS SPEC */
memcpy ( buf_oaf + 4 , task - > dev - > sas_addr , SAS_ADDR_SIZE ) ;
/* fill in PRD (scatter/gather) table, if any */
for_each_sg ( task - > scatter , sg , tei - > n_elem , i ) {
buf_prd - > addr = cpu_to_le64 ( sg_dma_address ( sg ) ) ;
buf_prd - > len = cpu_to_le32 ( sg_dma_len ( sg ) ) ;
buf_prd + + ;
}
# if _MV_DUMP
/* copy cmd table */
from = kmap_atomic ( sg_page ( sg_req ) , KM_IRQ0 ) ;
memcpy ( buf_cmd , from + sg_req - > offset , req_len ) ;
kunmap_atomic ( from , KM_IRQ0 ) ;
# endif
2007-10-26 04:58:22 +04:00
return 0 ;
err_out_2 :
pci_unmap_sg ( mvi - > pdev , & tei - > task - > smp_task . smp_resp , 1 ,
PCI_DMA_FROMDEVICE ) ;
err_out :
pci_unmap_sg ( mvi - > pdev , & tei - > task - > smp_task . smp_req , 1 ,
PCI_DMA_TODEVICE ) ;
return rc ;
}
2008-02-23 16:15:27 +03:00
static void mvs_free_reg_set ( struct mvs_info * mvi , struct mvs_port * port )
{
void __iomem * regs = mvi - > regs ;
u32 tmp , offs ;
u8 * tfs = & port - > taskfileset ;
if ( * tfs = = MVS_ID_NOT_MAPPED )
return ;
offs = 1U < < ( ( * tfs & 0x0f ) + PCS_EN_SATA_REG_SHIFT ) ;
if ( * tfs < 16 ) {
tmp = mr32 ( PCS ) ;
mw32 ( PCS , tmp & ~ offs ) ;
} else {
tmp = mr32 ( CTL ) ;
mw32 ( CTL , tmp & ~ offs ) ;
}
tmp = mr32 ( INT_STAT_SRS ) & ( 1U < < * tfs ) ;
if ( tmp )
mw32 ( INT_STAT_SRS , tmp ) ;
* tfs = MVS_ID_NOT_MAPPED ;
}
static u8 mvs_assign_reg_set ( struct mvs_info * mvi , struct mvs_port * port )
{
int i ;
u32 tmp , offs ;
void __iomem * regs = mvi - > regs ;
if ( port - > taskfileset ! = MVS_ID_NOT_MAPPED )
return 0 ;
tmp = mr32 ( PCS ) ;
for ( i = 0 ; i < mvi - > chip - > srs_sz ; i + + ) {
if ( i = = 16 )
tmp = mr32 ( CTL ) ;
offs = 1U < < ( ( i & 0x0f ) + PCS_EN_SATA_REG_SHIFT ) ;
if ( ! ( tmp & offs ) ) {
port - > taskfileset = i ;
if ( i < 16 )
mw32 ( PCS , tmp | offs ) ;
else
mw32 ( CTL , tmp | offs ) ;
tmp = mr32 ( INT_STAT_SRS ) & ( 1U < < i ) ;
if ( tmp )
mw32 ( INT_STAT_SRS , tmp ) ;
return 0 ;
}
}
return MVS_ID_NOT_MAPPED ;
}
2008-03-27 09:54:50 +03:00
static u32 mvs_get_ncq_tag ( struct sas_task * task , u32 * tag )
2008-02-23 16:15:27 +03:00
{
struct ata_queued_cmd * qc = task - > uldd_task ;
2008-03-27 09:54:50 +03:00
if ( qc ) {
if ( qc - > tf . command = = ATA_CMD_FPDMA_WRITE | |
qc - > tf . command = = ATA_CMD_FPDMA_READ ) {
* tag = qc - > tag ;
return 1 ;
}
}
2008-02-23 16:15:27 +03:00
2008-03-27 09:54:50 +03:00
return 0 ;
2008-02-23 16:15:27 +03:00
}
2007-10-26 04:58:22 +04:00
static int mvs_task_prep_ata ( struct mvs_info * mvi ,
struct mvs_task_exec_info * tei )
{
struct sas_task * task = tei - > task ;
struct domain_device * dev = task - > dev ;
struct mvs_cmd_hdr * hdr = tei - > hdr ;
struct asd_sas_port * sas_port = dev - > port ;
2008-02-23 16:15:27 +03:00
struct mvs_slot_info * slot ;
2007-10-26 04:58:22 +04:00
struct scatterlist * sg ;
struct mvs_prd * buf_prd ;
2008-02-23 16:15:27 +03:00
struct mvs_port * port = tei - > port ;
u32 tag = tei - > tag ;
u32 flags = ( tei - > n_elem < < MCH_PRD_LEN_SHIFT ) ;
2007-10-26 04:58:22 +04:00
void * buf_tmp ;
u8 * buf_cmd , * buf_oaf ;
dma_addr_t buf_tmp_dma ;
2008-02-23 16:15:27 +03:00
u32 i , req_len , resp_len ;
const u32 max_resp_len = SB_RFB_MAX ;
if ( mvs_assign_reg_set ( mvi , port ) = = MVS_ID_NOT_MAPPED )
return - EBUSY ;
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
slot = & mvi - > slot_info [ tag ] ;
slot - > tx = mvi - > tx_prod ;
mvi - > tx [ mvi - > tx_prod ] = cpu_to_le32 ( TXQ_MODE_I | tag |
( TXQ_CMD_STP < < TXQ_CMD_SHIFT ) |
( sas_port - > phy_mask < < TXQ_PHY_SHIFT ) |
( port - > taskfileset < < TXQ_SRS_SHIFT ) ) ;
2007-10-26 04:58:22 +04:00
if ( task - > ata_task . use_ncq )
flags | = MCH_FPDMA ;
2008-02-23 16:15:27 +03:00
if ( dev - > sata_dev . command_set = = ATAPI_COMMAND_SET ) {
if ( task - > ata_task . fis . command ! = ATA_CMD_ID_ATAPI )
flags | = MCH_ATAPI ;
}
2007-10-26 04:58:22 +04:00
/* FIXME: fill in port multiplier number */
hdr - > flags = cpu_to_le32 ( flags ) ;
2008-02-23 16:15:27 +03:00
/* FIXME: the low order order 5 bits for the TAG if enable NCQ */
2008-03-27 09:54:50 +03:00
if ( task - > ata_task . use_ncq & & mvs_get_ncq_tag ( task , & hdr - > tags ) )
task - > ata_task . fis . sector_count | = hdr - > tags < < 3 ;
else
2008-02-23 16:15:27 +03:00
hdr - > tags = cpu_to_le32 ( tag ) ;
2007-10-26 04:58:22 +04:00
hdr - > data_len = cpu_to_le32 ( task - > total_xfer_len ) ;
/*
* arrange MVS_SLOT_BUF_SZ - sized DMA buffer according to our needs
*/
2008-02-23 16:15:27 +03:00
/* region 1: command table area (MVS_ATA_CMD_SZ bytes) ************** */
buf_cmd = buf_tmp = slot - > buf ;
2007-10-26 04:58:22 +04:00
buf_tmp_dma = slot - > buf_dma ;
hdr - > cmd_tbl = cpu_to_le64 ( buf_tmp_dma ) ;
buf_tmp + = MVS_ATA_CMD_SZ ;
buf_tmp_dma + = MVS_ATA_CMD_SZ ;
2008-02-23 16:15:27 +03:00
# if _MV_DUMP
slot - > cmd_size = MVS_ATA_CMD_SZ ;
# endif
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
/* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
2007-10-26 04:58:22 +04:00
/* used for STP. unused for SATA? */
buf_oaf = buf_tmp ;
hdr - > open_frame = cpu_to_le64 ( buf_tmp_dma ) ;
buf_tmp + = MVS_OAF_SZ ;
buf_tmp_dma + = MVS_OAF_SZ ;
2008-02-23 16:15:27 +03:00
/* region 3: PRD table ********************************************* */
2007-10-26 04:58:22 +04:00
buf_prd = buf_tmp ;
2008-02-23 16:15:27 +03:00
if ( tei - > n_elem )
hdr - > prd_tbl = cpu_to_le64 ( buf_tmp_dma ) ;
else
hdr - > prd_tbl = 0 ;
2007-10-26 04:58:22 +04:00
i = sizeof ( struct mvs_prd ) * tei - > n_elem ;
buf_tmp + = i ;
buf_tmp_dma + = i ;
2008-02-23 16:15:27 +03:00
/* region 4: status buffer (larger the PRD, smaller this buf) ****** */
2007-10-26 04:58:22 +04:00
/* FIXME: probably unused, for SATA. kept here just in case
* we get a STP / SATA error information record
*/
slot - > response = buf_tmp ;
hdr - > status_buf = cpu_to_le64 ( buf_tmp_dma ) ;
2008-02-23 16:15:27 +03:00
req_len = sizeof ( struct host_to_dev_fis ) ;
2007-10-26 04:58:22 +04:00
resp_len = MVS_SLOT_BUF_SZ - MVS_ATA_CMD_SZ -
2008-02-23 16:15:27 +03:00
sizeof ( struct mvs_err_info ) - i ;
2007-10-26 04:58:22 +04:00
/* request, response lengths */
2008-02-23 16:15:27 +03:00
resp_len = min ( resp_len , max_resp_len ) ;
2007-10-26 04:58:22 +04:00
hdr - > lens = cpu_to_le32 ( ( ( resp_len / 4 ) < < 16 ) | ( req_len / 4 ) ) ;
2008-02-23 16:15:27 +03:00
task - > ata_task . fis . flags | = 0x80 ; /* C=1: update ATA cmd reg */
2007-10-26 04:58:22 +04:00
/* fill in command FIS and ATAPI CDB */
2008-02-23 16:15:27 +03:00
memcpy ( buf_cmd , & task - > ata_task . fis , sizeof ( struct host_to_dev_fis ) ) ;
if ( dev - > sata_dev . command_set = = ATAPI_COMMAND_SET )
memcpy ( buf_cmd + STP_ATAPI_CMD ,
task - > ata_task . atapi_packet , 16 ) ;
/* generate open address frame hdr (first 12 bytes) */
buf_oaf [ 0 ] = ( 1 < < 7 ) | ( 2 < < 4 ) | 0x1 ; /* initiator, STP, ftype 1h */
buf_oaf [ 1 ] = task - > dev - > linkrate & 0xf ;
* ( u16 * ) ( buf_oaf + 2 ) = cpu_to_be16 ( tag ) ;
memcpy ( buf_oaf + 4 , task - > dev - > sas_addr , SAS_ADDR_SIZE ) ;
2007-10-26 04:58:22 +04:00
/* fill in PRD (scatter/gather) table, if any */
2008-02-23 16:15:27 +03:00
for_each_sg ( task - > scatter , sg , tei - > n_elem , i ) {
2007-10-26 04:58:22 +04:00
buf_prd - > addr = cpu_to_le64 ( sg_dma_address ( sg ) ) ;
buf_prd - > len = cpu_to_le32 ( sg_dma_len ( sg ) ) ;
buf_prd + + ;
}
return 0 ;
}
static int mvs_task_prep_ssp ( struct mvs_info * mvi ,
struct mvs_task_exec_info * tei )
{
struct sas_task * task = tei - > task ;
struct mvs_cmd_hdr * hdr = tei - > hdr ;
2008-02-23 16:15:27 +03:00
struct mvs_port * port = tei - > port ;
2007-10-26 04:58:22 +04:00
struct mvs_slot_info * slot ;
struct scatterlist * sg ;
struct mvs_prd * buf_prd ;
struct ssp_frame_hdr * ssp_hdr ;
void * buf_tmp ;
u8 * buf_cmd , * buf_oaf , fburst = 0 ;
dma_addr_t buf_tmp_dma ;
u32 flags ;
2008-02-23 16:15:27 +03:00
u32 resp_len , req_len , i , tag = tei - > tag ;
const u32 max_resp_len = SB_RFB_MAX ;
2008-03-27 09:54:50 +03:00
u8 phy_mask ;
2007-10-26 04:58:22 +04:00
slot = & mvi - > slot_info [ tag ] ;
2008-03-27 09:54:50 +03:00
phy_mask = ( port - > wide_port_phymap ) ? port - > wide_port_phymap :
task - > dev - > port - > phy_mask ;
2008-02-23 16:15:27 +03:00
slot - > tx = mvi - > tx_prod ;
mvi - > tx [ mvi - > tx_prod ] = cpu_to_le32 ( TXQ_MODE_I | tag |
( TXQ_CMD_SSP < < TXQ_CMD_SHIFT ) |
2008-03-27 09:54:50 +03:00
( phy_mask < < TXQ_PHY_SHIFT ) ) ;
2007-10-26 04:58:22 +04:00
flags = MCH_RETRY ;
if ( task - > ssp_task . enable_first_burst ) {
flags | = MCH_FBURST ;
fburst = ( 1 < < 7 ) ;
}
hdr - > flags = cpu_to_le32 ( flags |
2008-02-23 16:15:27 +03:00
( tei - > n_elem < < MCH_PRD_LEN_SHIFT ) |
( MCH_SSP_FR_CMD < < MCH_SSP_FR_TYPE_SHIFT ) ) ;
2007-10-26 04:58:22 +04:00
hdr - > tags = cpu_to_le32 ( tag ) ;
hdr - > data_len = cpu_to_le32 ( task - > total_xfer_len ) ;
/*
* arrange MVS_SLOT_BUF_SZ - sized DMA buffer according to our needs
*/
2008-02-23 16:15:27 +03:00
/* region 1: command table area (MVS_SSP_CMD_SZ bytes) ************** */
buf_cmd = buf_tmp = slot - > buf ;
2007-10-26 04:58:22 +04:00
buf_tmp_dma = slot - > buf_dma ;
hdr - > cmd_tbl = cpu_to_le64 ( buf_tmp_dma ) ;
buf_tmp + = MVS_SSP_CMD_SZ ;
buf_tmp_dma + = MVS_SSP_CMD_SZ ;
2008-02-23 16:15:27 +03:00
# if _MV_DUMP
slot - > cmd_size = MVS_SSP_CMD_SZ ;
# endif
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
/* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
2007-10-26 04:58:22 +04:00
buf_oaf = buf_tmp ;
hdr - > open_frame = cpu_to_le64 ( buf_tmp_dma ) ;
buf_tmp + = MVS_OAF_SZ ;
buf_tmp_dma + = MVS_OAF_SZ ;
2008-02-23 16:15:27 +03:00
/* region 3: PRD table ********************************************* */
2007-10-26 04:58:22 +04:00
buf_prd = buf_tmp ;
2008-02-23 16:15:27 +03:00
if ( tei - > n_elem )
hdr - > prd_tbl = cpu_to_le64 ( buf_tmp_dma ) ;
else
hdr - > prd_tbl = 0 ;
2007-10-26 04:58:22 +04:00
i = sizeof ( struct mvs_prd ) * tei - > n_elem ;
buf_tmp + = i ;
buf_tmp_dma + = i ;
2008-02-23 16:15:27 +03:00
/* region 4: status buffer (larger the PRD, smaller this buf) ****** */
2007-10-26 04:58:22 +04:00
slot - > response = buf_tmp ;
hdr - > status_buf = cpu_to_le64 ( buf_tmp_dma ) ;
resp_len = MVS_SLOT_BUF_SZ - MVS_SSP_CMD_SZ - MVS_OAF_SZ -
2008-02-23 16:15:27 +03:00
sizeof ( struct mvs_err_info ) - i ;
resp_len = min ( resp_len , max_resp_len ) ;
req_len = sizeof ( struct ssp_frame_hdr ) + 28 ;
2007-10-26 04:58:22 +04:00
/* request, response lengths */
hdr - > lens = cpu_to_le32 ( ( ( resp_len / 4 ) < < 16 ) | ( req_len / 4 ) ) ;
/* generate open address frame hdr (first 12 bytes) */
buf_oaf [ 0 ] = ( 1 < < 7 ) | ( 1 < < 4 ) | 0x1 ; /* initiator, SSP, ftype 1h */
buf_oaf [ 1 ] = task - > dev - > linkrate & 0xf ;
2008-02-23 16:15:27 +03:00
* ( u16 * ) ( buf_oaf + 2 ) = cpu_to_be16 ( tag ) ;
2007-10-26 04:58:22 +04:00
memcpy ( buf_oaf + 4 , task - > dev - > sas_addr , SAS_ADDR_SIZE ) ;
2008-02-23 16:15:27 +03:00
/* fill in SSP frame header (Command Table.SSP frame header) */
ssp_hdr = ( struct ssp_frame_hdr * ) buf_cmd ;
2007-10-26 04:58:22 +04:00
ssp_hdr - > frame_type = SSP_COMMAND ;
memcpy ( ssp_hdr - > hashed_dest_addr , task - > dev - > hashed_sas_addr ,
HASHED_SAS_ADDR_SIZE ) ;
memcpy ( ssp_hdr - > hashed_src_addr ,
task - > dev - > port - > ha - > hashed_sas_addr , HASHED_SAS_ADDR_SIZE ) ;
ssp_hdr - > tag = cpu_to_be16 ( tag ) ;
/* fill in command frame IU */
buf_cmd + = sizeof ( * ssp_hdr ) ;
memcpy ( buf_cmd , & task - > ssp_task . LUN , 8 ) ;
2008-02-23 16:15:27 +03:00
buf_cmd [ 9 ] = fburst | task - > ssp_task . task_attr |
( task - > ssp_task . task_prio < < 3 ) ;
2007-10-26 04:58:22 +04:00
memcpy ( buf_cmd + 12 , & task - > ssp_task . cdb , 16 ) ;
/* fill in PRD (scatter/gather) table, if any */
2008-02-23 16:15:27 +03:00
for_each_sg ( task - > scatter , sg , tei - > n_elem , i ) {
2007-10-26 04:58:22 +04:00
buf_prd - > addr = cpu_to_le64 ( sg_dma_address ( sg ) ) ;
buf_prd - > len = cpu_to_le32 ( sg_dma_len ( sg ) ) ;
buf_prd + + ;
}
return 0 ;
}
static int mvs_task_exec ( struct sas_task * task , const int num , gfp_t gfp_flags )
{
2008-02-23 16:15:27 +03:00
struct domain_device * dev = task - > dev ;
struct mvs_info * mvi = dev - > port - > ha - > lldd_ha ;
struct pci_dev * pdev = mvi - > pdev ;
2007-10-26 04:58:22 +04:00
void __iomem * regs = mvi - > regs ;
struct mvs_task_exec_info tei ;
2008-02-23 16:15:27 +03:00
struct sas_task * t = task ;
2008-03-27 09:54:50 +03:00
struct mvs_slot_info * slot ;
2008-02-23 16:15:27 +03:00
u32 tag = 0xdeadbeef , rc , n_elem = 0 ;
unsigned long flags ;
u32 n = num , pass = 0 ;
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
spin_lock_irqsave ( & mvi - > lock , flags ) ;
do {
2008-03-27 09:54:50 +03:00
dev = t - > dev ;
2008-02-23 16:15:27 +03:00
tei . port = & mvi - > port [ dev - > port - > id ] ;
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
if ( ! tei . port - > port_attached ) {
2008-03-27 09:54:50 +03:00
if ( sas_protocol_ata ( t - > task_proto ) ) {
rc = SAS_PHY_DOWN ;
goto out_done ;
} else {
struct task_status_struct * ts = & t - > task_status ;
ts - > resp = SAS_TASK_UNDELIVERED ;
ts - > stat = SAS_PHY_DOWN ;
t - > task_done ( t ) ;
if ( n > 1 )
t = list_entry ( t - > list . next ,
struct sas_task , list ) ;
continue ;
}
2008-02-23 16:15:27 +03:00
}
2008-03-27 09:54:50 +03:00
2008-02-23 16:15:27 +03:00
if ( ! sas_protocol_ata ( t - > task_proto ) ) {
if ( t - > num_scatter ) {
n_elem = pci_map_sg ( mvi - > pdev , t - > scatter ,
t - > num_scatter ,
t - > data_dir ) ;
if ( ! n_elem ) {
rc = - ENOMEM ;
goto err_out ;
}
}
} else {
n_elem = t - > num_scatter ;
}
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
rc = mvs_tag_alloc ( mvi , & tag ) ;
if ( rc )
goto err_out ;
2007-10-26 04:58:22 +04:00
2008-03-27 09:54:50 +03:00
slot = & mvi - > slot_info [ tag ] ;
t - > lldd_task = NULL ;
slot - > n_elem = n_elem ;
memset ( slot - > buf , 0 , MVS_SLOT_BUF_SZ ) ;
2008-02-23 16:15:27 +03:00
tei . task = t ;
tei . hdr = & mvi - > slot [ tag ] ;
tei . tag = tag ;
tei . n_elem = n_elem ;
switch ( t - > task_proto ) {
case SAS_PROTOCOL_SMP :
rc = mvs_task_prep_smp ( mvi , & tei ) ;
break ;
case SAS_PROTOCOL_SSP :
rc = mvs_task_prep_ssp ( mvi , & tei ) ;
break ;
case SAS_PROTOCOL_SATA :
case SAS_PROTOCOL_STP :
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP :
rc = mvs_task_prep_ata ( mvi , & tei ) ;
break ;
default :
dev_printk ( KERN_ERR , & pdev - > dev ,
" unknown sas_task proto: 0x%x \n " ,
t - > task_proto ) ;
rc = - EINVAL ;
break ;
}
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
if ( rc )
goto err_out_tag ;
2007-10-26 04:58:22 +04:00
2008-03-27 09:54:50 +03:00
slot - > task = t ;
slot - > port = tei . port ;
t - > lldd_task = ( void * ) slot ;
list_add_tail ( & slot - > list , & slot - > port - > list ) ;
2008-02-23 16:15:27 +03:00
/* TODO: select normal or high priority */
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
spin_lock ( & t - > task_state_lock ) ;
t - > task_state_flags | = SAS_TASK_AT_INITIATOR ;
spin_unlock ( & t - > task_state_lock ) ;
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
mvs_hba_memory_dump ( mvi , tag , t - > task_proto ) ;
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
+ + pass ;
mvi - > tx_prod = ( mvi - > tx_prod + 1 ) & ( MVS_CHIP_SLOT_SZ - 1 ) ;
2008-03-27 09:54:50 +03:00
if ( n > 1 )
t = list_entry ( t - > list . next , struct sas_task , list ) ;
2008-02-23 16:15:27 +03:00
} while ( - - n ) ;
2007-10-26 04:58:22 +04:00
2008-03-27 09:54:50 +03:00
rc = 0 ;
goto out_done ;
2007-10-26 04:58:22 +04:00
err_out_tag :
2008-02-23 16:15:27 +03:00
mvs_tag_free ( mvi , tag ) ;
2007-10-26 04:58:22 +04:00
err_out :
2008-02-23 16:15:27 +03:00
dev_printk ( KERN_ERR , & pdev - > dev , " mvsas exec failed[%d]! \n " , rc ) ;
if ( ! sas_protocol_ata ( t - > task_proto ) )
if ( n_elem )
pci_unmap_sg ( mvi - > pdev , t - > scatter , n_elem ,
t - > data_dir ) ;
2008-03-27 09:54:50 +03:00
out_done :
2008-02-23 16:15:27 +03:00
if ( pass )
mw32 ( TX_PROD_IDX , ( mvi - > tx_prod - 1 ) & ( MVS_CHIP_SLOT_SZ - 1 ) ) ;
2007-10-26 04:58:22 +04:00
spin_unlock_irqrestore ( & mvi - > lock , flags ) ;
return rc ;
}
2008-02-23 16:15:27 +03:00
static int mvs_task_abort ( struct sas_task * task )
{
2008-03-27 09:55:04 +03:00
int rc ;
2008-02-23 16:15:27 +03:00
unsigned long flags ;
struct mvs_info * mvi = task - > dev - > port - > ha - > lldd_ha ;
struct pci_dev * pdev = mvi - > pdev ;
2008-03-27 09:55:04 +03:00
int tag ;
2008-02-23 16:15:27 +03:00
spin_lock_irqsave ( & task - > task_state_lock , flags ) ;
if ( task - > task_state_flags & SAS_TASK_STATE_DONE ) {
rc = TMF_RESP_FUNC_COMPLETE ;
2008-03-27 09:55:04 +03:00
spin_unlock_irqrestore ( & task - > task_state_lock , flags ) ;
2008-02-23 16:15:27 +03:00
goto out_done ;
}
spin_unlock_irqrestore ( & task - > task_state_lock , flags ) ;
switch ( task - > task_proto ) {
case SAS_PROTOCOL_SMP :
2008-03-27 09:55:04 +03:00
dev_printk ( KERN_DEBUG , & pdev - > dev , " SMP Abort! \n " ) ;
2008-02-23 16:15:27 +03:00
break ;
case SAS_PROTOCOL_SSP :
2008-03-27 09:55:04 +03:00
dev_printk ( KERN_DEBUG , & pdev - > dev , " SSP Abort! \n " ) ;
2008-02-23 16:15:27 +03:00
break ;
case SAS_PROTOCOL_SATA :
case SAS_PROTOCOL_STP :
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP : {
2008-03-27 09:55:04 +03:00
dev_printk ( KERN_DEBUG , & pdev - > dev , " STP Abort! \n " ) ;
# if _MV_DUMP
dev_printk ( KERN_DEBUG , & pdev - > dev , " Dump D2H FIS: \n " ) ;
2008-02-23 16:15:27 +03:00
mvs_hexdump ( sizeof ( struct host_to_dev_fis ) ,
( void * ) & task - > ata_task . fis , 0 ) ;
dev_printk ( KERN_DEBUG , & pdev - > dev , " Dump ATAPI Cmd : \n " ) ;
mvs_hexdump ( 16 , task - > ata_task . atapi_packet , 0 ) ;
2008-03-27 09:55:04 +03:00
# endif
spin_lock_irqsave ( & task - > task_state_lock , flags ) ;
if ( task - > task_state_flags & SAS_TASK_NEED_DEV_RESET ) {
/* TODO */
;
}
spin_unlock_irqrestore ( & task - > task_state_lock , flags ) ;
2008-02-23 16:15:27 +03:00
break ;
}
default :
break ;
}
2008-03-27 09:55:04 +03:00
if ( mvs_find_tag ( mvi , task , & tag ) ) {
spin_lock_irqsave ( & mvi - > lock , flags ) ;
mvs_slot_task_free ( mvi , task , & mvi - > slot_info [ tag ] , tag ) ;
spin_unlock_irqrestore ( & mvi - > lock , flags ) ;
}
if ( ! mvs_task_exec ( task , 1 , GFP_ATOMIC ) )
rc = TMF_RESP_FUNC_COMPLETE ;
else
rc = TMF_RESP_FUNC_FAILED ;
2008-02-23 16:15:27 +03:00
out_done :
return rc ;
}
2007-10-26 04:58:22 +04:00
static void mvs_free ( struct mvs_info * mvi )
{
int i ;
if ( ! mvi )
return ;
for ( i = 0 ; i < MVS_SLOTS ; i + + ) {
struct mvs_slot_info * slot = & mvi - > slot_info [ i ] ;
if ( slot - > buf )
dma_free_coherent ( & mvi - > pdev - > dev , MVS_SLOT_BUF_SZ ,
slot - > buf , slot - > buf_dma ) ;
}
if ( mvi - > tx )
dma_free_coherent ( & mvi - > pdev - > dev ,
2008-02-23 16:15:27 +03:00
sizeof ( * mvi - > tx ) * MVS_CHIP_SLOT_SZ ,
2007-10-26 04:58:22 +04:00
mvi - > tx , mvi - > tx_dma ) ;
if ( mvi - > rx_fis )
dma_free_coherent ( & mvi - > pdev - > dev , MVS_RX_FISL_SZ ,
mvi - > rx_fis , mvi - > rx_fis_dma ) ;
if ( mvi - > rx )
dma_free_coherent ( & mvi - > pdev - > dev ,
2008-03-27 09:55:23 +03:00
sizeof ( * mvi - > rx ) * ( MVS_RX_RING_SZ + 1 ) ,
2007-10-26 04:58:22 +04:00
mvi - > rx , mvi - > rx_dma ) ;
if ( mvi - > slot )
dma_free_coherent ( & mvi - > pdev - > dev ,
2008-02-23 16:15:27 +03:00
sizeof ( * mvi - > slot ) * MVS_SLOTS ,
2007-10-26 04:58:22 +04:00
mvi - > slot , mvi - > slot_dma ) ;
2008-02-23 16:15:27 +03:00
# ifdef MVS_ENABLE_PERI
2007-10-26 04:58:22 +04:00
if ( mvi - > peri_regs )
iounmap ( mvi - > peri_regs ) ;
2008-02-23 16:15:27 +03:00
# endif
2007-10-26 04:58:22 +04:00
if ( mvi - > regs )
iounmap ( mvi - > regs ) ;
if ( mvi - > shost )
scsi_host_put ( mvi - > shost ) ;
kfree ( mvi - > sas . sas_port ) ;
kfree ( mvi - > sas . sas_phy ) ;
kfree ( mvi ) ;
}
/* FIXME: locking? */
static int mvs_phy_control ( struct asd_sas_phy * sas_phy , enum phy_func func ,
void * funcdata )
{
struct mvs_info * mvi = sas_phy - > ha - > lldd_ha ;
int rc = 0 , phy_id = sas_phy - > id ;
u32 tmp ;
2008-02-23 16:15:27 +03:00
tmp = mvs_read_phy_ctl ( mvi , phy_id ) ;
2007-10-26 04:58:22 +04:00
switch ( func ) {
2008-02-23 16:15:27 +03:00
case PHY_FUNC_SET_LINK_RATE : {
struct sas_phy_linkrates * rates = funcdata ;
u32 lrmin = 0 , lrmax = 0 ;
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
lrmin = ( rates - > minimum_linkrate < < 8 ) ;
lrmax = ( rates - > maximum_linkrate < < 12 ) ;
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
if ( lrmin ) {
tmp & = ~ ( 0xf < < 8 ) ;
tmp | = lrmin ;
}
if ( lrmax ) {
tmp & = ~ ( 0xf < < 12 ) ;
tmp | = lrmax ;
}
mvs_write_phy_ctl ( mvi , phy_id , tmp ) ;
break ;
2007-10-26 04:58:22 +04:00
}
case PHY_FUNC_HARD_RESET :
if ( tmp & PHY_RST_HARD )
break ;
2008-02-23 16:15:27 +03:00
mvs_write_phy_ctl ( mvi , phy_id , tmp | PHY_RST_HARD ) ;
2007-10-26 04:58:22 +04:00
break ;
case PHY_FUNC_LINK_RESET :
2008-02-23 16:15:27 +03:00
mvs_write_phy_ctl ( mvi , phy_id , tmp | PHY_RST ) ;
2007-10-26 04:58:22 +04:00
break ;
case PHY_FUNC_DISABLE :
case PHY_FUNC_RELEASE_SPINUP_HOLD :
default :
rc = - EOPNOTSUPP ;
}
return rc ;
}
static void __devinit mvs_phy_init ( struct mvs_info * mvi , int phy_id )
{
struct mvs_phy * phy = & mvi - > phy [ phy_id ] ;
struct asd_sas_phy * sas_phy = & phy - > sas_phy ;
sas_phy - > enabled = ( phy_id < mvi - > chip - > n_phy ) ? 1 : 0 ;
sas_phy - > class = SAS ;
sas_phy - > iproto = SAS_PROTOCOL_ALL ;
sas_phy - > tproto = 0 ;
sas_phy - > type = PHY_TYPE_PHYSICAL ;
sas_phy - > role = PHY_ROLE_INITIATOR ;
sas_phy - > oob_mode = OOB_NOT_CONNECTED ;
sas_phy - > linkrate = SAS_LINK_RATE_UNKNOWN ;
sas_phy - > id = phy_id ;
sas_phy - > sas_addr = & mvi - > sas_addr [ 0 ] ;
sas_phy - > frame_rcvd = & phy - > frame_rcvd [ 0 ] ;
sas_phy - > ha = & mvi - > sas ;
sas_phy - > lldd_phy = phy ;
}
2008-02-23 16:15:27 +03:00
static struct mvs_info * __devinit mvs_alloc ( struct pci_dev * pdev ,
const struct pci_device_id * ent )
2007-10-26 04:58:22 +04:00
{
struct mvs_info * mvi ;
2008-02-23 16:15:27 +03:00
unsigned long res_start , res_len , res_flag ;
2007-10-26 04:58:22 +04:00
struct asd_sas_phy * * arr_phy ;
struct asd_sas_port * * arr_port ;
const struct mvs_chip_info * chip = & mvs_chips [ ent - > driver_data ] ;
int i ;
/*
* alloc and init our per - HBA mvs_info struct
*/
mvi = kzalloc ( sizeof ( * mvi ) , GFP_KERNEL ) ;
if ( ! mvi )
return NULL ;
spin_lock_init ( & mvi - > lock ) ;
2008-03-27 09:55:23 +03:00
# ifdef MVS_USE_TASKLET
tasklet_init ( & mvi - > tasklet , mvs_tasklet , ( unsigned long ) mvi ) ;
# endif
2007-10-26 04:58:22 +04:00
mvi - > pdev = pdev ;
mvi - > chip = chip ;
if ( pdev - > device = = 0x6440 & & pdev - > revision = = 0 )
mvi - > flags | = MVF_PHY_PWR_FIX ;
/*
* alloc and init SCSI , SAS glue
*/
mvi - > shost = scsi_host_alloc ( & mvs_sht , sizeof ( void * ) ) ;
if ( ! mvi - > shost )
goto err_out ;
arr_phy = kcalloc ( MVS_MAX_PHYS , sizeof ( void * ) , GFP_KERNEL ) ;
arr_port = kcalloc ( MVS_MAX_PHYS , sizeof ( void * ) , GFP_KERNEL ) ;
if ( ! arr_phy | | ! arr_port )
goto err_out ;
for ( i = 0 ; i < MVS_MAX_PHYS ; i + + ) {
mvs_phy_init ( mvi , i ) ;
arr_phy [ i ] = & mvi - > phy [ i ] . sas_phy ;
arr_port [ i ] = & mvi - > port [ i ] . sas_port ;
2008-03-27 09:55:23 +03:00
mvi - > port [ i ] . taskfileset = MVS_ID_NOT_MAPPED ;
mvi - > port [ i ] . wide_port_phymap = 0 ;
mvi - > port [ i ] . port_attached = 0 ;
INIT_LIST_HEAD ( & mvi - > port [ i ] . list ) ;
2007-10-26 04:58:22 +04:00
}
SHOST_TO_SAS_HA ( mvi - > shost ) = & mvi - > sas ;
mvi - > shost - > transportt = mvs_stt ;
2008-02-23 16:15:27 +03:00
mvi - > shost - > max_id = 21 ;
2007-10-26 04:58:22 +04:00
mvi - > shost - > max_lun = ~ 0 ;
2008-02-23 16:15:27 +03:00
mvi - > shost - > max_channel = 0 ;
mvi - > shost - > max_cmd_len = 16 ;
2007-10-26 04:58:22 +04:00
mvi - > sas . sas_ha_name = DRV_NAME ;
mvi - > sas . dev = & pdev - > dev ;
mvi - > sas . lldd_module = THIS_MODULE ;
mvi - > sas . sas_addr = & mvi - > sas_addr [ 0 ] ;
mvi - > sas . sas_phy = arr_phy ;
mvi - > sas . sas_port = arr_port ;
mvi - > sas . num_phys = chip - > n_phy ;
2008-03-27 09:55:23 +03:00
mvi - > sas . lldd_max_execute_num = 1 ;
2008-02-23 16:15:27 +03:00
mvi - > sas . lldd_queue_size = MVS_QUEUE_SIZE ;
2008-03-27 09:55:23 +03:00
mvi - > shost - > can_queue = MVS_CAN_QUEUE ;
mvi - > shost - > cmd_per_lun = MVS_SLOTS / mvi - > sas . num_phys ;
2007-10-26 04:58:22 +04:00
mvi - > sas . lldd_ha = mvi ;
mvi - > sas . core . shost = mvi - > shost ;
2008-02-23 16:15:27 +03:00
mvs_tag_init ( mvi ) ;
2007-10-26 04:58:22 +04:00
/*
* ioremap main and peripheral registers
*/
2008-02-23 16:15:27 +03:00
# ifdef MVS_ENABLE_PERI
2007-10-26 04:58:22 +04:00
res_start = pci_resource_start ( pdev , 2 ) ;
res_len = pci_resource_len ( pdev , 2 ) ;
if ( ! res_start | | ! res_len )
goto err_out ;
mvi - > peri_regs = ioremap_nocache ( res_start , res_len ) ;
2008-02-23 16:15:27 +03:00
if ( ! mvi - > peri_regs )
2007-10-26 04:58:22 +04:00
goto err_out ;
2008-02-23 16:15:27 +03:00
# endif
2007-10-26 04:58:22 +04:00
res_start = pci_resource_start ( pdev , 4 ) ;
res_len = pci_resource_len ( pdev , 4 ) ;
if ( ! res_start | | ! res_len )
goto err_out ;
2008-02-23 16:15:27 +03:00
res_flag = pci_resource_flags ( pdev , 4 ) ;
if ( res_flag & IORESOURCE_CACHEABLE )
mvi - > regs = ioremap ( res_start , res_len ) ;
else
mvi - > regs = ioremap_nocache ( res_start , res_len ) ;
2007-10-26 04:58:22 +04:00
if ( ! mvi - > regs )
goto err_out ;
/*
* alloc and init our DMA areas
*/
mvi - > tx = dma_alloc_coherent ( & pdev - > dev ,
2008-02-23 16:15:27 +03:00
sizeof ( * mvi - > tx ) * MVS_CHIP_SLOT_SZ ,
2007-10-26 04:58:22 +04:00
& mvi - > tx_dma , GFP_KERNEL ) ;
if ( ! mvi - > tx )
goto err_out ;
2008-02-23 16:15:27 +03:00
memset ( mvi - > tx , 0 , sizeof ( * mvi - > tx ) * MVS_CHIP_SLOT_SZ ) ;
2007-10-26 04:58:22 +04:00
mvi - > rx_fis = dma_alloc_coherent ( & pdev - > dev , MVS_RX_FISL_SZ ,
2008-02-23 16:15:27 +03:00
& mvi - > rx_fis_dma , GFP_KERNEL ) ;
2007-10-26 04:58:22 +04:00
if ( ! mvi - > rx_fis )
goto err_out ;
memset ( mvi - > rx_fis , 0 , MVS_RX_FISL_SZ ) ;
mvi - > rx = dma_alloc_coherent ( & pdev - > dev ,
2008-03-27 09:55:23 +03:00
sizeof ( * mvi - > rx ) * ( MVS_RX_RING_SZ + 1 ) ,
2007-10-26 04:58:22 +04:00
& mvi - > rx_dma , GFP_KERNEL ) ;
if ( ! mvi - > rx )
goto err_out ;
2008-03-27 09:55:23 +03:00
memset ( mvi - > rx , 0 , sizeof ( * mvi - > rx ) * ( MVS_RX_RING_SZ + 1 ) ) ;
2007-10-26 04:58:22 +04:00
mvi - > rx [ 0 ] = cpu_to_le32 ( 0xfff ) ;
mvi - > rx_cons = 0xfff ;
mvi - > slot = dma_alloc_coherent ( & pdev - > dev ,
sizeof ( * mvi - > slot ) * MVS_SLOTS ,
& mvi - > slot_dma , GFP_KERNEL ) ;
if ( ! mvi - > slot )
goto err_out ;
memset ( mvi - > slot , 0 , sizeof ( * mvi - > slot ) * MVS_SLOTS ) ;
for ( i = 0 ; i < MVS_SLOTS ; i + + ) {
struct mvs_slot_info * slot = & mvi - > slot_info [ i ] ;
slot - > buf = dma_alloc_coherent ( & pdev - > dev , MVS_SLOT_BUF_SZ ,
2008-02-23 16:15:27 +03:00
& slot - > buf_dma , GFP_KERNEL ) ;
2007-10-26 04:58:22 +04:00
if ( ! slot - > buf )
goto err_out ;
memset ( slot - > buf , 0 , MVS_SLOT_BUF_SZ ) ;
}
/* finally, read NVRAM to get our SAS address */
if ( mvs_nvram_read ( mvi , NVR_SAS_ADDR , & mvi - > sas_addr , 8 ) )
goto err_out ;
return mvi ;
err_out :
mvs_free ( mvi ) ;
return NULL ;
}
static u32 mvs_cr32 ( void __iomem * regs , u32 addr )
{
mw32 ( CMD_ADDR , addr ) ;
return mr32 ( CMD_DATA ) ;
}
static void mvs_cw32 ( void __iomem * regs , u32 addr , u32 val )
{
mw32 ( CMD_ADDR , addr ) ;
mw32 ( CMD_DATA , val ) ;
}
2008-02-23 16:15:27 +03:00
static u32 mvs_read_phy_ctl ( struct mvs_info * mvi , u32 port )
2007-10-26 04:58:22 +04:00
{
void __iomem * regs = mvi - > regs ;
2008-02-23 16:15:27 +03:00
return ( port < 4 ) ? mr32 ( P0_SER_CTLSTAT + port * 4 ) :
mr32 ( P4_SER_CTLSTAT + ( port - 4 ) * 4 ) ;
2007-10-26 04:58:22 +04:00
}
2008-02-23 16:15:27 +03:00
static void mvs_write_phy_ctl ( struct mvs_info * mvi , u32 port , u32 val )
2007-10-26 04:58:22 +04:00
{
void __iomem * regs = mvi - > regs ;
2008-02-23 16:15:27 +03:00
if ( port < 4 )
mw32 ( P0_SER_CTLSTAT + port * 4 , val ) ;
else
mw32 ( P4_SER_CTLSTAT + ( port - 4 ) * 4 , val ) ;
}
static u32 mvs_read_port ( struct mvs_info * mvi , u32 off , u32 off2 , u32 port )
{
void __iomem * regs = mvi - > regs + off ;
void __iomem * regs2 = mvi - > regs + off2 ;
return ( port < 4 ) ? readl ( regs + port * 8 ) :
readl ( regs2 + ( port - 4 ) * 8 ) ;
}
static void mvs_write_port ( struct mvs_info * mvi , u32 off , u32 off2 ,
u32 port , u32 val )
{
void __iomem * regs = mvi - > regs + off ;
void __iomem * regs2 = mvi - > regs + off2 ;
if ( port < 4 )
writel ( val , regs + port * 8 ) ;
else
writel ( val , regs2 + ( port - 4 ) * 8 ) ;
}
static u32 mvs_read_port_cfg_data ( struct mvs_info * mvi , u32 port )
{
return mvs_read_port ( mvi , MVS_P0_CFG_DATA , MVS_P4_CFG_DATA , port ) ;
}
static void mvs_write_port_cfg_data ( struct mvs_info * mvi , u32 port , u32 val )
{
mvs_write_port ( mvi , MVS_P0_CFG_DATA , MVS_P4_CFG_DATA , port , val ) ;
}
static void mvs_write_port_cfg_addr ( struct mvs_info * mvi , u32 port , u32 addr )
{
mvs_write_port ( mvi , MVS_P0_CFG_ADDR , MVS_P4_CFG_ADDR , port , addr ) ;
}
static u32 mvs_read_port_vsr_data ( struct mvs_info * mvi , u32 port )
{
return mvs_read_port ( mvi , MVS_P0_VSR_DATA , MVS_P4_VSR_DATA , port ) ;
}
static void mvs_write_port_vsr_data ( struct mvs_info * mvi , u32 port , u32 val )
{
mvs_write_port ( mvi , MVS_P0_VSR_DATA , MVS_P4_VSR_DATA , port , val ) ;
}
static void mvs_write_port_vsr_addr ( struct mvs_info * mvi , u32 port , u32 addr )
{
mvs_write_port ( mvi , MVS_P0_VSR_ADDR , MVS_P4_VSR_ADDR , port , addr ) ;
}
static u32 mvs_read_port_irq_stat ( struct mvs_info * mvi , u32 port )
{
return mvs_read_port ( mvi , MVS_P0_INT_STAT , MVS_P4_INT_STAT , port ) ;
}
static void mvs_write_port_irq_stat ( struct mvs_info * mvi , u32 port , u32 val )
{
mvs_write_port ( mvi , MVS_P0_INT_STAT , MVS_P4_INT_STAT , port , val ) ;
}
static u32 mvs_read_port_irq_mask ( struct mvs_info * mvi , u32 port )
{
return mvs_read_port ( mvi , MVS_P0_INT_MASK , MVS_P4_INT_MASK , port ) ;
}
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
static void mvs_write_port_irq_mask ( struct mvs_info * mvi , u32 port , u32 val )
{
mvs_write_port ( mvi , MVS_P0_INT_MASK , MVS_P4_INT_MASK , port , val ) ;
2007-10-26 04:58:22 +04:00
}
static void __devinit mvs_phy_hacks ( struct mvs_info * mvi )
{
void __iomem * regs = mvi - > regs ;
u32 tmp ;
/* workaround for SATA R-ERR, to ignore phy glitch */
tmp = mvs_cr32 ( regs , CMD_PHY_TIMER ) ;
tmp & = ~ ( 1 < < 9 ) ;
tmp | = ( 1 < < 10 ) ;
mvs_cw32 ( regs , CMD_PHY_TIMER , tmp ) ;
/* enable retry 127 times */
mvs_cw32 ( regs , CMD_SAS_CTL1 , 0x7f7f ) ;
/* extend open frame timeout to max */
tmp = mvs_cr32 ( regs , CMD_SAS_CTL0 ) ;
tmp & = ~ 0xffff ;
tmp | = 0x3fff ;
mvs_cw32 ( regs , CMD_SAS_CTL0 , tmp ) ;
/* workaround for WDTIMEOUT , set to 550 ms */
2008-03-27 09:55:23 +03:00
mvs_cw32 ( regs , CMD_WD_TIMER , 0x86470 ) ;
2007-10-26 04:58:22 +04:00
/* not to halt for different port op during wideport link change */
mvs_cw32 ( regs , CMD_APP_ERR_CONFIG , 0xffefbf7d ) ;
/* workaround for Seagate disk not-found OOB sequence, recv
* COMINIT before sending out COMWAKE */
tmp = mvs_cr32 ( regs , CMD_PHY_MODE_21 ) ;
tmp & = 0x0000ffff ;
tmp | = 0x00fa0000 ;
mvs_cw32 ( regs , CMD_PHY_MODE_21 , tmp ) ;
tmp = mvs_cr32 ( regs , CMD_PHY_TIMER ) ;
tmp & = 0x1fffffff ;
tmp | = ( 2U < < 29 ) ; /* 8 ms retry */
mvs_cw32 ( regs , CMD_PHY_TIMER , tmp ) ;
2008-02-23 16:15:27 +03:00
/* TEST - for phy decoding error, adjust voltage levels */
mw32 ( P0_VSR_ADDR + 0 , 0x8 ) ;
mw32 ( P0_VSR_DATA + 0 , 0x2F0 ) ;
mw32 ( P0_VSR_ADDR + 8 , 0x8 ) ;
mw32 ( P0_VSR_DATA + 8 , 0x2F0 ) ;
mw32 ( P0_VSR_ADDR + 16 , 0x8 ) ;
mw32 ( P0_VSR_DATA + 16 , 0x2F0 ) ;
mw32 ( P0_VSR_ADDR + 24 , 0x8 ) ;
mw32 ( P0_VSR_DATA + 24 , 0x2F0 ) ;
}
static void mvs_enable_xmt ( struct mvs_info * mvi , int PhyId )
{
void __iomem * regs = mvi - > regs ;
u32 tmp ;
tmp = mr32 ( PCS ) ;
if ( mvi - > chip - > n_phy < = 4 )
tmp | = 1 < < ( PhyId + PCS_EN_PORT_XMT_SHIFT ) ;
else
tmp | = 1 < < ( PhyId + PCS_EN_PORT_XMT_SHIFT2 ) ;
mw32 ( PCS , tmp ) ;
}
static void mvs_detect_porttype ( struct mvs_info * mvi , int i )
{
void __iomem * regs = mvi - > regs ;
u32 reg ;
struct mvs_phy * phy = & mvi - > phy [ i ] ;
/* TODO check & save device type */
reg = mr32 ( GBL_PORT_TYPE ) ;
if ( reg & MODE_SAS_SATA & ( 1 < < i ) )
phy - > phy_type | = PORT_TYPE_SAS ;
else
phy - > phy_type | = PORT_TYPE_SATA ;
}
static void * mvs_get_d2h_reg ( struct mvs_info * mvi , int i , void * buf )
{
u32 * s = ( u32 * ) buf ;
if ( ! s )
return NULL ;
mvs_write_port_cfg_addr ( mvi , i , PHYR_SATA_SIG3 ) ;
s [ 3 ] = mvs_read_port_cfg_data ( mvi , i ) ;
mvs_write_port_cfg_addr ( mvi , i , PHYR_SATA_SIG2 ) ;
s [ 2 ] = mvs_read_port_cfg_data ( mvi , i ) ;
mvs_write_port_cfg_addr ( mvi , i , PHYR_SATA_SIG1 ) ;
s [ 1 ] = mvs_read_port_cfg_data ( mvi , i ) ;
mvs_write_port_cfg_addr ( mvi , i , PHYR_SATA_SIG0 ) ;
s [ 0 ] = mvs_read_port_cfg_data ( mvi , i ) ;
return ( void * ) s ;
}
static u32 mvs_is_sig_fis_received ( u32 irq_status )
{
return irq_status & PHYEV_SIG_FIS ;
}
static void mvs_update_wideport ( struct mvs_info * mvi , int i )
{
struct mvs_phy * phy = & mvi - > phy [ i ] ;
struct mvs_port * port = phy - > port ;
int j , no ;
for_each_phy ( port - > wide_port_phymap , no , j , mvi - > chip - > n_phy )
if ( no & 1 ) {
mvs_write_port_cfg_addr ( mvi , no , PHYR_WIDE_PORT ) ;
mvs_write_port_cfg_data ( mvi , no ,
port - > wide_port_phymap ) ;
} else {
mvs_write_port_cfg_addr ( mvi , no , PHYR_WIDE_PORT ) ;
mvs_write_port_cfg_data ( mvi , no , 0 ) ;
}
}
static u32 mvs_is_phy_ready ( struct mvs_info * mvi , int i )
{
u32 tmp ;
struct mvs_phy * phy = & mvi - > phy [ i ] ;
2008-03-27 09:55:23 +03:00
struct mvs_port * port = phy - > port ; ;
2008-02-23 16:15:27 +03:00
tmp = mvs_read_phy_ctl ( mvi , i ) ;
if ( ( tmp & PHY_READY_MASK ) & & ! ( phy - > irq_status & PHYEV_POOF ) ) {
2008-03-27 09:55:23 +03:00
if ( ! port )
2008-02-23 16:15:27 +03:00
phy - > phy_attached = 1 ;
return tmp ;
}
if ( port ) {
if ( phy - > phy_type & PORT_TYPE_SAS ) {
port - > wide_port_phymap & = ~ ( 1U < < i ) ;
if ( ! port - > wide_port_phymap )
port - > port_attached = 0 ;
mvs_update_wideport ( mvi , i ) ;
} else if ( phy - > phy_type & PORT_TYPE_SATA )
port - > port_attached = 0 ;
mvs_free_reg_set ( mvi , phy - > port ) ;
phy - > port = NULL ;
phy - > phy_attached = 0 ;
phy - > phy_type & = ~ ( PORT_TYPE_SAS | PORT_TYPE_SATA ) ;
}
return 0 ;
}
static void mvs_update_phyinfo ( struct mvs_info * mvi , int i ,
int get_st )
{
struct mvs_phy * phy = & mvi - > phy [ i ] ;
struct pci_dev * pdev = mvi - > pdev ;
2008-03-27 09:55:33 +03:00
u32 tmp ;
2008-02-23 16:15:27 +03:00
u64 tmp64 ;
mvs_write_port_cfg_addr ( mvi , i , PHYR_IDENTIFY ) ;
phy - > dev_info = mvs_read_port_cfg_data ( mvi , i ) ;
mvs_write_port_cfg_addr ( mvi , i , PHYR_ADDR_HI ) ;
phy - > dev_sas_addr = ( u64 ) mvs_read_port_cfg_data ( mvi , i ) < < 32 ;
mvs_write_port_cfg_addr ( mvi , i , PHYR_ADDR_LO ) ;
phy - > dev_sas_addr | = mvs_read_port_cfg_data ( mvi , i ) ;
if ( get_st ) {
phy - > irq_status = mvs_read_port_irq_stat ( mvi , i ) ;
phy - > phy_status = mvs_is_phy_ready ( mvi , i ) ;
}
if ( phy - > phy_status ) {
u32 phy_st ;
struct asd_sas_phy * sas_phy = mvi - > sas . sas_phy [ i ] ;
mvs_write_port_cfg_addr ( mvi , i , PHYR_PHY_STAT ) ;
phy_st = mvs_read_port_cfg_data ( mvi , i ) ;
sas_phy - > linkrate =
( phy - > phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK ) > >
PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET ;
2008-03-27 09:55:33 +03:00
phy - > minimum_linkrate =
( phy - > phy_status &
PHY_MIN_SPP_PHYS_LINK_RATE_MASK ) > > 8 ;
phy - > maximum_linkrate =
( phy - > phy_status &
PHY_MAX_SPP_PHYS_LINK_RATE_MASK ) > > 12 ;
2008-02-23 16:15:27 +03:00
if ( phy - > phy_type & PORT_TYPE_SAS ) {
2008-03-27 09:55:33 +03:00
/* Updated attached_sas_addr */
mvs_write_port_cfg_addr ( mvi , i , PHYR_ATT_ADDR_HI ) ;
phy - > att_dev_sas_addr =
( u64 ) mvs_read_port_cfg_data ( mvi , i ) < < 32 ;
mvs_write_port_cfg_addr ( mvi , i , PHYR_ATT_ADDR_LO ) ;
phy - > att_dev_sas_addr | = mvs_read_port_cfg_data ( mvi , i ) ;
2008-02-23 16:15:27 +03:00
mvs_write_port_cfg_addr ( mvi , i , PHYR_ATT_DEV_INFO ) ;
phy - > att_dev_info = mvs_read_port_cfg_data ( mvi , i ) ;
phy - > identify . device_type =
phy - > att_dev_info & PORT_DEV_TYPE_MASK ;
if ( phy - > identify . device_type = = SAS_END_DEV )
phy - > identify . target_port_protocols =
SAS_PROTOCOL_SSP ;
else if ( phy - > identify . device_type ! = NO_DEVICE )
phy - > identify . target_port_protocols =
SAS_PROTOCOL_SMP ;
if ( phy_st & PHY_OOB_DTCTD )
sas_phy - > oob_mode = SAS_OOB_MODE ;
phy - > frame_rcvd_size =
sizeof ( struct sas_identify_frame ) ;
} else if ( phy - > phy_type & PORT_TYPE_SATA ) {
phy - > identify . target_port_protocols = SAS_PROTOCOL_STP ;
if ( mvs_is_sig_fis_received ( phy - > irq_status ) ) {
2008-03-27 09:55:33 +03:00
phy - > att_dev_sas_addr = i ; /* temp */
2008-02-23 16:15:27 +03:00
if ( phy_st & PHY_OOB_DTCTD )
sas_phy - > oob_mode = SATA_OOB_MODE ;
phy - > frame_rcvd_size =
sizeof ( struct dev_to_host_fis ) ;
mvs_get_d2h_reg ( mvi , i ,
( void * ) sas_phy - > frame_rcvd ) ;
} else {
dev_printk ( KERN_DEBUG , & pdev - > dev ,
" No sig fis \n " ) ;
2008-03-27 09:55:33 +03:00
phy - > phy_type & = ~ ( PORT_TYPE_SATA ) ;
goto out_done ;
2008-02-23 16:15:27 +03:00
}
}
2008-03-27 09:55:33 +03:00
tmp64 = cpu_to_be64 ( phy - > att_dev_sas_addr ) ;
memcpy ( sas_phy - > attached_sas_addr , & tmp64 , SAS_ADDR_SIZE ) ;
dev_printk ( KERN_DEBUG , & pdev - > dev ,
" phy[%d] Get Attached Address 0x%llX , "
" SAS Address 0x%llX \n " ,
i , phy - > att_dev_sas_addr , phy - > dev_sas_addr ) ;
dev_printk ( KERN_DEBUG , & pdev - > dev ,
" Rate = %x , type = %d \n " ,
sas_phy - > linkrate , phy - > phy_type ) ;
2008-02-23 16:15:27 +03:00
/* workaround for HW phy decoding error on 1.5g disk drive */
mvs_write_port_vsr_addr ( mvi , i , VSR_PHY_MODE6 ) ;
tmp = mvs_read_port_vsr_data ( mvi , i ) ;
if ( ( ( phy - > phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK ) > >
PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET ) = =
SAS_LINK_RATE_1_5_GBPS )
2008-03-27 09:55:33 +03:00
tmp & = ~ PHY_MODE6_LATECLK ;
2008-02-23 16:15:27 +03:00
else
2008-03-27 09:55:33 +03:00
tmp | = PHY_MODE6_LATECLK ;
2008-02-23 16:15:27 +03:00
mvs_write_port_vsr_data ( mvi , i , tmp ) ;
}
2008-03-27 09:55:33 +03:00
out_done :
2008-02-23 16:15:27 +03:00
if ( get_st )
mvs_write_port_irq_stat ( mvi , i , phy - > irq_status ) ;
}
static void mvs_port_formed ( struct asd_sas_phy * sas_phy )
{
struct sas_ha_struct * sas_ha = sas_phy - > ha ;
struct mvs_info * mvi = sas_ha - > lldd_ha ;
struct asd_sas_port * sas_port = sas_phy - > port ;
struct mvs_phy * phy = sas_phy - > lldd_phy ;
struct mvs_port * port = & mvi - > port [ sas_port - > id ] ;
unsigned long flags ;
spin_lock_irqsave ( & mvi - > lock , flags ) ;
port - > port_attached = 1 ;
phy - > port = port ;
port - > taskfileset = MVS_ID_NOT_MAPPED ;
if ( phy - > phy_type & PORT_TYPE_SAS ) {
port - > wide_port_phymap = sas_port - > phy_mask ;
mvs_update_wideport ( mvi , sas_phy - > id ) ;
}
spin_unlock_irqrestore ( & mvi - > lock , flags ) ;
2007-10-26 04:58:22 +04:00
}
2008-03-27 09:55:33 +03:00
static int mvs_I_T_nexus_reset ( struct domain_device * dev )
{
return TMF_RESP_FUNC_FAILED ;
}
2007-10-26 04:58:22 +04:00
static int __devinit mvs_hw_init ( struct mvs_info * mvi )
{
void __iomem * regs = mvi - > regs ;
int i ;
u32 tmp , cctl ;
/* make sure interrupts are masked immediately (paranoia) */
mw32 ( GBL_CTL , 0 ) ;
tmp = mr32 ( GBL_CTL ) ;
2008-02-23 16:15:27 +03:00
/* Reset Controller */
2007-10-26 04:58:22 +04:00
if ( ! ( tmp & HBA_RST ) ) {
if ( mvi - > flags & MVF_PHY_PWR_FIX ) {
pci_read_config_dword ( mvi - > pdev , PCR_PHY_CTL , & tmp ) ;
tmp & = ~ PCTL_PWR_ON ;
tmp | = PCTL_OFF ;
pci_write_config_dword ( mvi - > pdev , PCR_PHY_CTL , tmp ) ;
pci_read_config_dword ( mvi - > pdev , PCR_PHY_CTL2 , & tmp ) ;
tmp & = ~ PCTL_PWR_ON ;
tmp | = PCTL_OFF ;
pci_write_config_dword ( mvi - > pdev , PCR_PHY_CTL2 , tmp ) ;
}
/* global reset, incl. COMRESET/H_RESET_N (self-clearing) */
mw32_f ( GBL_CTL , HBA_RST ) ;
}
/* wait for reset to finish; timeout is just a guess */
i = 1000 ;
while ( i - - > 0 ) {
msleep ( 10 ) ;
if ( ! ( mr32 ( GBL_CTL ) & HBA_RST ) )
break ;
}
if ( mr32 ( GBL_CTL ) & HBA_RST ) {
dev_printk ( KERN_ERR , & mvi - > pdev - > dev , " HBA reset failed \n " ) ;
return - EBUSY ;
}
2008-02-23 16:15:27 +03:00
/* Init Chip */
2007-10-26 04:58:22 +04:00
/* make sure RST is set; HBA_RST /should/ have done that for us */
cctl = mr32 ( CTL ) ;
if ( cctl & CCTL_RST )
cctl & = ~ CCTL_RST ;
else
mw32_f ( CTL , cctl | CCTL_RST ) ;
2008-02-23 16:15:27 +03:00
/* write to device control _AND_ device status register? - A.C. */
pci_read_config_dword ( mvi - > pdev , PCR_DEV_CTRL , & tmp ) ;
tmp & = ~ PRD_REQ_MASK ;
tmp | = PRD_REQ_SIZE ;
pci_write_config_dword ( mvi - > pdev , PCR_DEV_CTRL , tmp ) ;
2007-10-26 04:58:22 +04:00
pci_read_config_dword ( mvi - > pdev , PCR_PHY_CTL , & tmp ) ;
tmp | = PCTL_PWR_ON ;
tmp & = ~ PCTL_OFF ;
pci_write_config_dword ( mvi - > pdev , PCR_PHY_CTL , tmp ) ;
pci_read_config_dword ( mvi - > pdev , PCR_PHY_CTL2 , & tmp ) ;
tmp | = PCTL_PWR_ON ;
tmp & = ~ PCTL_OFF ;
pci_write_config_dword ( mvi - > pdev , PCR_PHY_CTL2 , tmp ) ;
mw32_f ( CTL , cctl ) ;
2008-02-23 16:15:27 +03:00
/* reset control */
mw32 ( PCS , 0 ) ; /*MVS_PCS */
2007-10-26 04:58:22 +04:00
mvs_phy_hacks ( mvi ) ;
mw32 ( CMD_LIST_LO , mvi - > slot_dma ) ;
mw32 ( CMD_LIST_HI , ( mvi - > slot_dma > > 16 ) > > 16 ) ;
mw32 ( RX_FIS_LO , mvi - > rx_fis_dma ) ;
mw32 ( RX_FIS_HI , ( mvi - > rx_fis_dma > > 16 ) > > 16 ) ;
2008-02-23 16:15:27 +03:00
mw32 ( TX_CFG , MVS_CHIP_SLOT_SZ ) ;
2007-10-26 04:58:22 +04:00
mw32 ( TX_LO , mvi - > tx_dma ) ;
mw32 ( TX_HI , ( mvi - > tx_dma > > 16 ) > > 16 ) ;
mw32 ( RX_CFG , MVS_RX_RING_SZ ) ;
mw32 ( RX_LO , mvi - > rx_dma ) ;
mw32 ( RX_HI , ( mvi - > rx_dma > > 16 ) > > 16 ) ;
2008-02-23 16:15:27 +03:00
/* enable auto port detection */
mw32 ( GBL_PORT_TYPE , MODE_AUTO_DET_EN ) ;
msleep ( 100 ) ;
2007-10-26 04:58:22 +04:00
/* init and reset phys */
for ( i = 0 ; i < mvi - > chip - > n_phy ; i + + ) {
2008-02-27 15:50:25 +03:00
u32 lo = be32_to_cpu ( * ( u32 * ) & mvi - > sas_addr [ 4 ] ) ;
u32 hi = be32_to_cpu ( * ( u32 * ) & mvi - > sas_addr [ 0 ] ) ;
2008-02-23 16:15:27 +03:00
mvs_detect_porttype ( mvi , i ) ;
2007-10-26 04:58:22 +04:00
/* set phy local SAS address */
2008-02-23 16:15:27 +03:00
mvs_write_port_cfg_addr ( mvi , i , PHYR_ADDR_LO ) ;
mvs_write_port_cfg_data ( mvi , i , lo ) ;
mvs_write_port_cfg_addr ( mvi , i , PHYR_ADDR_HI ) ;
mvs_write_port_cfg_data ( mvi , i , hi ) ;
2007-10-26 04:58:22 +04:00
/* reset phy */
2008-02-23 16:15:27 +03:00
tmp = mvs_read_phy_ctl ( mvi , i ) ;
2007-10-26 04:58:22 +04:00
tmp | = PHY_RST ;
2008-02-23 16:15:27 +03:00
mvs_write_phy_ctl ( mvi , i , tmp ) ;
2007-10-26 04:58:22 +04:00
}
msleep ( 100 ) ;
for ( i = 0 ; i < mvi - > chip - > n_phy ; i + + ) {
2008-02-23 16:15:27 +03:00
/* clear phy int status */
tmp = mvs_read_port_irq_stat ( mvi , i ) ;
tmp & = ~ PHYEV_SIG_FIS ;
mvs_write_port_irq_stat ( mvi , i , tmp ) ;
2007-10-26 04:58:22 +04:00
/* set phy int mask */
2008-02-23 16:15:27 +03:00
tmp = PHYEV_RDY_CH | PHYEV_BROAD_CH | PHYEV_UNASSOC_FIS |
PHYEV_ID_DONE | PHYEV_DEC_ERR ;
mvs_write_port_irq_mask ( mvi , i , tmp ) ;
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
msleep ( 100 ) ;
mvs_update_phyinfo ( mvi , i , 1 ) ;
mvs_enable_xmt ( mvi , i ) ;
2007-10-26 04:58:22 +04:00
}
/* FIXME: update wide port bitmaps */
2008-02-23 16:15:27 +03:00
/* little endian for open address and command table, etc. */
/* A.C.
* it seems that ( from the spec ) turning on big - endian won ' t
* do us any good on big - endian machines , need further confirmation
*/
cctl = mr32 ( CTL ) ;
cctl | = CCTL_ENDIAN_CMD ;
cctl | = CCTL_ENDIAN_DATA ;
cctl & = ~ CCTL_ENDIAN_OPEN ;
cctl | = CCTL_ENDIAN_RSP ;
mw32_f ( CTL , cctl ) ;
/* reset CMD queue */
tmp = mr32 ( PCS ) ;
tmp | = PCS_CMD_RST ;
mw32 ( PCS , tmp ) ;
/* interrupt coalescing may cause missing HW interrput in some case,
* and the max count is 0x1ff , while our max slot is 0x200 ,
* it will make count 0.
*/
tmp = 0 ;
mw32 ( INT_COAL , tmp ) ;
tmp = 0x100 ;
mw32 ( INT_COAL_TMOUT , tmp ) ;
2007-10-26 04:58:22 +04:00
/* ladies and gentlemen, start your engines */
2008-02-23 16:15:27 +03:00
mw32 ( TX_CFG , 0 ) ;
mw32 ( TX_CFG , MVS_CHIP_SLOT_SZ | TX_EN ) ;
2007-10-26 04:58:22 +04:00
mw32 ( RX_CFG , MVS_RX_RING_SZ | RX_EN ) ;
2008-02-23 16:15:27 +03:00
/* enable CMD/CMPL_Q/RESP mode */
mw32 ( PCS , PCS_SATA_RETRY | PCS_FIS_RX_EN | PCS_CMD_EN ) ;
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
/* enable completion queue interrupt */
2008-03-27 09:55:33 +03:00
tmp = ( CINT_PORT_MASK | CINT_DONE | CINT_MEM | CINT_SRS ) ;
2008-02-23 16:15:27 +03:00
mw32 ( INT_MASK , tmp ) ;
2007-10-26 04:58:22 +04:00
2008-03-27 09:55:33 +03:00
/* Enable SRS interrupt */
mw32 ( INT_MASK_SRS , 0xFF ) ;
2007-10-26 04:58:22 +04:00
return 0 ;
}
static void __devinit mvs_print_info ( struct mvs_info * mvi )
{
struct pci_dev * pdev = mvi - > pdev ;
static int printed_version ;
if ( ! printed_version + + )
dev_printk ( KERN_INFO , & pdev - > dev , " version " DRV_VERSION " \n " ) ;
dev_printk ( KERN_INFO , & pdev - > dev , " %u phys, addr %llx \n " ,
mvi - > chip - > n_phy , SAS_ADDR ( mvi - > sas_addr ) ) ;
}
static int __devinit mvs_pci_init ( struct pci_dev * pdev ,
2008-02-23 16:15:27 +03:00
const struct pci_device_id * ent )
2007-10-26 04:58:22 +04:00
{
int rc ;
struct mvs_info * mvi ;
irq_handler_t irq_handler = mvs_interrupt ;
rc = pci_enable_device ( pdev ) ;
if ( rc )
return rc ;
pci_set_master ( pdev ) ;
rc = pci_request_regions ( pdev , DRV_NAME ) ;
if ( rc )
goto err_out_disable ;
rc = pci_go_64 ( pdev ) ;
if ( rc )
goto err_out_regions ;
mvi = mvs_alloc ( pdev , ent ) ;
if ( ! mvi ) {
rc = - ENOMEM ;
goto err_out_regions ;
}
rc = mvs_hw_init ( mvi ) ;
if ( rc )
goto err_out_mvi ;
2008-02-23 16:15:27 +03:00
# ifndef MVS_DISABLE_MSI
2007-10-26 04:58:22 +04:00
if ( ! pci_enable_msi ( pdev ) ) {
2008-02-23 16:15:27 +03:00
u32 tmp ;
void __iomem * regs = mvi - > regs ;
2007-10-26 04:58:22 +04:00
mvi - > flags | = MVF_MSI ;
irq_handler = mvs_msi_interrupt ;
2008-02-23 16:15:27 +03:00
tmp = mr32 ( PCS ) ;
mw32 ( PCS , tmp | PCS_SELF_CLEAR ) ;
2007-10-26 04:58:22 +04:00
}
2008-02-23 16:15:27 +03:00
# endif
2007-10-26 04:58:22 +04:00
rc = request_irq ( pdev - > irq , irq_handler , IRQF_SHARED , DRV_NAME , mvi ) ;
if ( rc )
goto err_out_msi ;
rc = scsi_add_host ( mvi - > shost , & pdev - > dev ) ;
if ( rc )
goto err_out_irq ;
rc = sas_register_ha ( & mvi - > sas ) ;
if ( rc )
goto err_out_shost ;
pci_set_drvdata ( pdev , mvi ) ;
mvs_print_info ( mvi ) ;
2008-03-27 09:55:33 +03:00
mvs_hba_interrupt_enable ( mvi ) ;
2007-10-26 04:58:22 +04:00
scsi_scan_host ( mvi - > shost ) ;
2008-02-23 16:15:27 +03:00
2007-10-26 04:58:22 +04:00
return 0 ;
err_out_shost :
scsi_remove_host ( mvi - > shost ) ;
err_out_irq :
free_irq ( pdev - > irq , mvi ) ;
err_out_msi :
if ( mvi - > flags | = MVF_MSI )
pci_disable_msi ( pdev ) ;
err_out_mvi :
mvs_free ( mvi ) ;
err_out_regions :
pci_release_regions ( pdev ) ;
err_out_disable :
pci_disable_device ( pdev ) ;
return rc ;
}
static void __devexit mvs_pci_remove ( struct pci_dev * pdev )
{
struct mvs_info * mvi = pci_get_drvdata ( pdev ) ;
pci_set_drvdata ( pdev , NULL ) ;
2008-02-23 16:15:27 +03:00
if ( mvi ) {
sas_unregister_ha ( & mvi - > sas ) ;
mvs_hba_interrupt_disable ( mvi ) ;
sas_remove_host ( mvi - > shost ) ;
scsi_remove_host ( mvi - > shost ) ;
free_irq ( pdev - > irq , mvi ) ;
if ( mvi - > flags & MVF_MSI )
pci_disable_msi ( pdev ) ;
mvs_free ( mvi ) ;
pci_release_regions ( pdev ) ;
}
2007-10-26 04:58:22 +04:00
pci_disable_device ( pdev ) ;
}
static struct sas_domain_function_template mvs_transport_ops = {
. lldd_execute_task = mvs_task_exec ,
. lldd_control_phy = mvs_phy_control ,
2008-02-23 16:15:27 +03:00
. lldd_abort_task = mvs_task_abort ,
2008-03-27 09:55:33 +03:00
. lldd_port_formed = mvs_port_formed ,
. lldd_I_T_nexus_reset = mvs_I_T_nexus_reset ,
2007-10-26 04:58:22 +04:00
} ;
static struct pci_device_id __devinitdata mvs_pci_table [ ] = {
{ PCI_VDEVICE ( MARVELL , 0x6320 ) , chip_6320 } ,
{ PCI_VDEVICE ( MARVELL , 0x6340 ) , chip_6440 } ,
2008-03-27 09:55:41 +03:00
{
. vendor = PCI_VENDOR_ID_MARVELL ,
. device = 0x6440 ,
. subvendor = PCI_ANY_ID ,
. subdevice = 0x6480 ,
. class = 0 ,
. class_mask = 0 ,
. driver_data = chip_6480 ,
} ,
2007-10-26 04:58:22 +04:00
{ PCI_VDEVICE ( MARVELL , 0x6440 ) , chip_6440 } ,
{ PCI_VDEVICE ( MARVELL , 0x6480 ) , chip_6480 } ,
{ } /* terminate list */
} ;
static struct pci_driver mvs_pci_driver = {
. name = DRV_NAME ,
. id_table = mvs_pci_table ,
. probe = mvs_pci_init ,
. remove = __devexit_p ( mvs_pci_remove ) ,
} ;
static int __init mvs_init ( void )
{
int rc ;
mvs_stt = sas_domain_attach_transport ( & mvs_transport_ops ) ;
if ( ! mvs_stt )
return - ENOMEM ;
rc = pci_register_driver ( & mvs_pci_driver ) ;
if ( rc )
goto err_out ;
return 0 ;
err_out :
sas_release_transport ( mvs_stt ) ;
return rc ;
}
static void __exit mvs_exit ( void )
{
pci_unregister_driver ( & mvs_pci_driver ) ;
sas_release_transport ( mvs_stt ) ;
}
module_init ( mvs_init ) ;
module_exit ( mvs_exit ) ;
MODULE_AUTHOR ( " Jeff Garzik <jgarzik@pobox.com> " ) ;
MODULE_DESCRIPTION ( " Marvell 88SE6440 SAS/SATA controller driver " ) ;
MODULE_VERSION ( DRV_VERSION ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DEVICE_TABLE ( pci , mvs_pci_table ) ;