From dd3bec63f80e663cdb655b8bdc9c1a0ea938f1c5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 1 Mar 2006 15:13:50 +0900 Subject: [PATCH] sata_sil: remove unaffected drives from m15w blacklist m15w blacklist overgrew by attributing unrelated problems to m15w including R_ERR on DMA activate FIS errata. This patch shrinks sata_sil m15w blacklist such that it's as reported by Silicon Image. Signed-off-by: Tejun Heo Cc: Carlos Pardo Signed-off-by: Jeff Garzik --- drivers/scsi/sata_sil.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c index d0a85073ebf..f4e262b06cf 100644 --- a/drivers/scsi/sata_sil.c +++ b/drivers/scsi/sata_sil.c @@ -141,12 +141,8 @@ static const struct sil_drivelist { { "ST330013AS", SIL_QUIRK_MOD15WRITE }, { "ST340017AS", SIL_QUIRK_MOD15WRITE }, { "ST360015AS", SIL_QUIRK_MOD15WRITE }, - { "ST380013AS", SIL_QUIRK_MOD15WRITE }, { "ST380023AS", SIL_QUIRK_MOD15WRITE }, { "ST3120023AS", SIL_QUIRK_MOD15WRITE }, - { "ST3160023AS", SIL_QUIRK_MOD15WRITE }, - { "ST3120026AS", SIL_QUIRK_MOD15WRITE }, - { "ST3200822AS", SIL_QUIRK_MOD15WRITE }, { "ST340014ASL", SIL_QUIRK_MOD15WRITE }, { "ST360014ASL", SIL_QUIRK_MOD15WRITE }, { "ST380011ASL", SIL_QUIRK_MOD15WRITE }, -- cgit v1.2.3 From 5457f2194ad198a0aba4190ec99a6a81846fdca5 Mon Sep 17 00:00:00 2001 From: "zhao, forrest" Date: Thu, 13 Jul 2006 13:38:32 +0800 Subject: [PATCH] The redefinition of ahci_start_engine() and ahci_stop_engine() - Make ahci_start_engine() and ahci_stop_engine() more consistent with AHCI spec 1.1 - Change their input parameter from ap to port_mmio - Update the existing users of ahci_start_engine() and ahci_stop_engine() Signed-off-by: Forrest Zhao Signed-off-by: Hannes Reinecke Signed-off-by: Jens Axboe Signed-off-by: Jeff Garzik --- drivers/scsi/ahci.c | 82 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 25 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index 77e7202a0eb..f1516ca2c52 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -205,6 +205,8 @@ static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs * static void ahci_irq_clear(struct ata_port *ap); static int ahci_port_start(struct ata_port *ap); static void ahci_port_stop(struct ata_port *ap); +static int ahci_start_engine(void __iomem *port_mmio); +static int ahci_stop_engine(void __iomem *port_mmio); static void ahci_tf_read(struct ata_port *ap, struct ata_taskfile *tf); static void ahci_qc_prep(struct ata_queued_cmd *qc); static u8 ahci_check_status(struct ata_port *ap); @@ -508,41 +510,64 @@ static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in, writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4)); } -static int ahci_stop_engine(struct ata_port *ap) +static int ahci_stop_engine(void __iomem *port_mmio) { - void __iomem *mmio = ap->host_set->mmio_base; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - int work; u32 tmp; tmp = readl(port_mmio + PORT_CMD); + + /* Check if the HBA is idle */ + if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0) + return 0; + + /* Setting HBA to idle */ tmp &= ~PORT_CMD_START; writel(tmp, port_mmio + PORT_CMD); - /* wait for engine to stop. TODO: this could be + /* wait for engine to stop. This could be * as long as 500 msec */ - work = 1000; - while (work-- > 0) { - tmp = readl(port_mmio + PORT_CMD); - if ((tmp & PORT_CMD_LIST_ON) == 0) - return 0; - udelay(10); - } + tmp = ata_wait_register(port_mmio + PORT_CMD, + PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500); + if(tmp & PORT_CMD_LIST_ON) + return -EIO; - return -EIO; + return 0; } -static void ahci_start_engine(struct ata_port *ap) +static int ahci_start_engine(void __iomem *port_mmio) { - void __iomem *mmio = ap->host_set->mmio_base; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); u32 tmp; + /* + * Get current status + */ tmp = readl(port_mmio + PORT_CMD); + + /* + * AHCI rev 1.1 section 10.3.1: + * Software shall not set PxCMD.ST to '1' until it verifies + * that PxCMD.CR is '0' and has set PxCMD.FRE to '1' + */ + if ((tmp & PORT_CMD_FIS_RX) == 0) + return -EPERM; + + /* + * wait for engine to become idle. + */ + tmp = ata_wait_register(port_mmio + PORT_CMD, + PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1,500); + if(tmp & PORT_CMD_LIST_ON) + return -EBUSY; + + /* + * Start DMA + */ tmp |= PORT_CMD_START; writel(tmp, port_mmio + PORT_CMD); readl(port_mmio + PORT_CMD); /* flush */ + + return 0; } static unsigned int ahci_dev_classify(struct ata_port *ap) @@ -626,7 +651,7 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class) } /* prepare for SRST (AHCI-1.1 10.4.1) */ - rc = ahci_stop_engine(ap); + rc = ahci_stop_engine(port_mmio); if (rc) { reason = "failed to stop engine"; goto fail_restart; @@ -647,7 +672,7 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class) } /* restart engine */ - ahci_start_engine(ap); + ahci_start_engine(port_mmio); ata_tf_init(ap->device, &tf); fis = pp->cmd_tbl; @@ -706,7 +731,7 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class) return 0; fail_restart: - ahci_start_engine(ap); + ahci_start_engine(port_mmio); fail: ata_port_printk(ap, KERN_ERR, "softreset failed (%s)\n", reason); return rc; @@ -717,11 +742,13 @@ static int ahci_hardreset(struct ata_port *ap, unsigned int *class) struct ahci_port_priv *pp = ap->private_data; u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; struct ata_taskfile tf; + void __iomem *mmio = ap->host_set->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); int rc; DPRINTK("ENTER\n"); - ahci_stop_engine(ap); + ahci_stop_engine(port_mmio); /* clear D2H reception area to properly wait for D2H FIS */ ata_tf_init(ap->device, &tf); @@ -730,7 +757,7 @@ static int ahci_hardreset(struct ata_port *ap, unsigned int *class) rc = sata_std_hardreset(ap, class); - ahci_start_engine(ap); + ahci_start_engine(port_mmio); if (rc == 0 && ata_port_online(ap)) *class = ahci_dev_classify(ap); @@ -1052,10 +1079,13 @@ static void ahci_thaw(struct ata_port *ap) static void ahci_error_handler(struct ata_port *ap) { + void __iomem *mmio = ap->host_set->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + if (!(ap->pflags & ATA_PFLAG_FROZEN)) { /* restart engine */ - ahci_stop_engine(ap); - ahci_start_engine(ap); + ahci_stop_engine(port_mmio); + ahci_start_engine(port_mmio); } /* perform recovery */ @@ -1066,14 +1096,16 @@ static void ahci_error_handler(struct ata_port *ap) static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; + void __iomem *mmio = ap->host_set->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); if (qc->flags & ATA_QCFLAG_FAILED) qc->err_mask |= AC_ERR_OTHER; if (qc->err_mask) { /* make DMA engine forget about the failed command */ - ahci_stop_engine(ap); - ahci_start_engine(ap); + ahci_stop_engine(port_mmio); + ahci_start_engine(port_mmio); } } -- cgit v1.2.3 From f4c8aa1107969c26b1984eb2996a58f816dea71f Mon Sep 17 00:00:00 2001 From: "brking@charter.net" Date: Wed, 5 Jul 2006 17:00:01 -0500 Subject: [SCSI] megaraid: Add support for change_queue_depth Adds support for change_queue_depth so that device queue depth can be changed at runtime through sysfs. Signed-off-by: Acked-by: Seokmann Ju Signed-off-by: James Bottomley --- drivers/scsi/megaraid/megaraid_mbox.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c index 92715130ac0..7ae580f17e6 100644 --- a/drivers/scsi/megaraid/megaraid_mbox.c +++ b/drivers/scsi/megaraid/megaraid_mbox.c @@ -330,6 +330,21 @@ static struct device_attribute *megaraid_sdev_attrs[] = { NULL, }; +/** + * megaraid_change_queue_depth - Change the device's queue depth + * @sdev: scsi device struct + * @qdepth: depth to set + * + * Return value: + * actual depth set + **/ +static int megaraid_change_queue_depth(struct scsi_device *sdev, int qdepth) +{ + if (qdepth > MBOX_MAX_SCSI_CMDS) + qdepth = MBOX_MAX_SCSI_CMDS; + scsi_adjust_queue_depth(sdev, 0, qdepth); + return sdev->queue_depth; +} /* * Scsi host template for megaraid unified driver @@ -343,6 +358,7 @@ static struct scsi_host_template megaraid_template_g = { .eh_device_reset_handler = megaraid_reset_handler, .eh_bus_reset_handler = megaraid_reset_handler, .eh_host_reset_handler = megaraid_reset_handler, + .change_queue_depth = megaraid_change_queue_depth, .use_clustering = ENABLE_CLUSTERING, .sdev_attrs = megaraid_sdev_attrs, .shost_attrs = megaraid_shost_attrs, -- cgit v1.2.3 From 1c57e86d75cf162bdadb3a5fe0cd3f65aa1a9ca3 Mon Sep 17 00:00:00 2001 From: Erich Chen Date: Wed, 12 Jul 2006 08:59:32 -0700 Subject: [SCSI] arcmsr: initial driver, version 1.20.00.13 arcmsr is a driver for the Areca Raid controller, a host based RAID subsystem that speaks SCSI at the firmware level. This patch is quite a clean up over the initial submission with contributions from: Randy Dunlap Christoph Hellwig Matthew Wilcox Adrian Bunk Signed-off-by: Erich Chen Signed-off-by: Andrew Morton Signed-off-by: James Bottomley --- drivers/scsi/Kconfig | 14 + drivers/scsi/Makefile | 1 + drivers/scsi/arcmsr/Makefile | 6 + drivers/scsi/arcmsr/arcmsr.h | 472 ++++++++++++ drivers/scsi/arcmsr/arcmsr_attr.c | 392 ++++++++++ drivers/scsi/arcmsr/arcmsr_hba.c | 1496 +++++++++++++++++++++++++++++++++++++ 6 files changed, 2381 insertions(+) create mode 100644 drivers/scsi/arcmsr/Makefile create mode 100644 drivers/scsi/arcmsr/arcmsr.h create mode 100644 drivers/scsi/arcmsr/arcmsr_attr.c create mode 100644 drivers/scsi/arcmsr/arcmsr_hba.c (limited to 'drivers/scsi') diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 96a81cd1761..d61662c1a0e 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -469,6 +469,20 @@ config SCSI_IN2000 To compile this driver as a module, choose M here: the module will be called in2000. +config SCSI_ARCMSR + tristate "ARECA ARC11X0[PCI-X]/ARC12X0[PCI-EXPRESS] SATA-RAID support" + depends on PCI && SCSI + help + This driver supports all of ARECA's SATA RAID controller cards. + This is an ARECA-maintained driver by Erich Chen. + If you have any problems, please mail to: < erich@areca.com.tw > + Areca supports Linux RAID config tools. + + < http://www.areca.com.tw > + + To compile this driver as a module, choose M here: the + module will be called arcmsr (modprobe arcmsr). + source "drivers/scsi/megaraid/Kconfig.megaraid" config SCSI_SATA diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index ebd0cf00bf3..b2de9bfdfdc 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_SCSI_PSI240I) += psi240i.o obj-$(CONFIG_SCSI_BUSLOGIC) += BusLogic.o obj-$(CONFIG_SCSI_DPT_I2O) += dpt_i2o.o obj-$(CONFIG_SCSI_U14_34F) += u14-34f.o +obj-$(CONFIG_SCSI_ARCMSR) += arcmsr/ obj-$(CONFIG_SCSI_ULTRASTOR) += ultrastor.o obj-$(CONFIG_SCSI_AHA152X) += aha152x.o obj-$(CONFIG_SCSI_AHA1542) += aha1542.o diff --git a/drivers/scsi/arcmsr/Makefile b/drivers/scsi/arcmsr/Makefile new file mode 100644 index 00000000000..721aced3916 --- /dev/null +++ b/drivers/scsi/arcmsr/Makefile @@ -0,0 +1,6 @@ +# File: drivers/arcmsr/Makefile +# Makefile for the ARECA PCI-X PCI-EXPRESS SATA RAID controllers SCSI driver. + +arcmsr-objs := arcmsr_attr.o arcmsr_hba.o + +obj-$(CONFIG_SCSI_ARCMSR) := arcmsr.o diff --git a/drivers/scsi/arcmsr/arcmsr.h b/drivers/scsi/arcmsr/arcmsr.h new file mode 100644 index 00000000000..aff96db9ccf --- /dev/null +++ b/drivers/scsi/arcmsr/arcmsr.h @@ -0,0 +1,472 @@ +/* +******************************************************************************* +** O.S : Linux +** FILE NAME : arcmsr.h +** BY : Erich Chen +** Description: SCSI RAID Device Driver for +** ARECA RAID Host adapter +******************************************************************************* +** Copyright (C) 2002 - 2005, Areca Technology Corporation All rights reserved. +** +** Web site: www.areca.com.tw +** E-mail: erich@areca.com.tw +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License version 2 as +** published by the Free Software Foundation. +** 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. +******************************************************************************* +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +**(INCLUDING NEGLIGENCE OR OTHERWISE)ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************* +*/ +#include + +struct class_device_attribute; + +#define ARCMSR_MAX_OUTSTANDING_CMD 256 +#define ARCMSR_MAX_FREECCB_NUM 288 +#define ARCMSR_DRIVER_VERSION "Driver Version 1.20.00.13" +#define ARCMSR_SCSI_INITIATOR_ID 255 +#define ARCMSR_MAX_XFER_SECTORS 512 +#define ARCMSR_MAX_TARGETID 17 +#define ARCMSR_MAX_TARGETLUN 8 +#define ARCMSR_MAX_CMD_PERLUN ARCMSR_MAX_OUTSTANDING_CMD +#define ARCMSR_MAX_QBUFFER 4096 +#define ARCMSR_MAX_SG_ENTRIES 38 + +/* +******************************************************************************* +** split 64bits dma addressing +******************************************************************************* +*/ +#define dma_addr_hi32(addr) (uint32_t) ((addr>>16)>>16) +#define dma_addr_lo32(addr) (uint32_t) (addr & 0xffffffff) +/* +******************************************************************************* +** MESSAGE CONTROL CODE +******************************************************************************* +*/ +struct CMD_MESSAGE +{ + uint32_t HeaderLength; + uint8_t Signature[8]; + uint32_t Timeout; + uint32_t ControlCode; + uint32_t ReturnCode; + uint32_t Length; +}; +/* +******************************************************************************* +** IOP Message Transfer Data for user space +******************************************************************************* +*/ +struct CMD_MESSAGE_FIELD +{ + struct CMD_MESSAGE cmdmessage; + uint8_t messagedatabuffer[1032]; +}; +/* IOP message transfer */ +#define ARCMSR_MESSAGE_FAIL 0x0001 +/* DeviceType */ +#define ARECA_SATA_RAID 0x90000000 +/* FunctionCode */ +#define FUNCTION_READ_RQBUFFER 0x0801 +#define FUNCTION_WRITE_WQBUFFER 0x0802 +#define FUNCTION_CLEAR_RQBUFFER 0x0803 +#define FUNCTION_CLEAR_WQBUFFER 0x0804 +#define FUNCTION_CLEAR_ALLQBUFFER 0x0805 +#define FUNCTION_RETURN_CODE_3F 0x0806 +#define FUNCTION_SAY_HELLO 0x0807 +#define FUNCTION_SAY_GOODBYE 0x0808 +#define FUNCTION_FLUSH_ADAPTER_CACHE 0x0809 +/* ARECA IO CONTROL CODE*/ +#define ARCMSR_MESSAGE_READ_RQBUFFER \ + ARECA_SATA_RAID | FUNCTION_READ_RQBUFFER +#define ARCMSR_MESSAGE_WRITE_WQBUFFER \ + ARECA_SATA_RAID | FUNCTION_WRITE_WQBUFFER +#define ARCMSR_MESSAGE_CLEAR_RQBUFFER \ + ARECA_SATA_RAID | FUNCTION_CLEAR_RQBUFFER +#define ARCMSR_MESSAGE_CLEAR_WQBUFFER \ + ARECA_SATA_RAID | FUNCTION_CLEAR_WQBUFFER +#define ARCMSR_MESSAGE_CLEAR_ALLQBUFFER \ + ARECA_SATA_RAID | FUNCTION_CLEAR_ALLQBUFFER +#define ARCMSR_MESSAGE_RETURN_CODE_3F \ + ARECA_SATA_RAID | FUNCTION_RETURN_CODE_3F +#define ARCMSR_MESSAGE_SAY_HELLO \ + ARECA_SATA_RAID | FUNCTION_SAY_HELLO +#define ARCMSR_MESSAGE_SAY_GOODBYE \ + ARECA_SATA_RAID | FUNCTION_SAY_GOODBYE +#define ARCMSR_MESSAGE_FLUSH_ADAPTER_CACHE \ + ARECA_SATA_RAID | FUNCTION_FLUSH_ADAPTER_CACHE +/* ARECA IOCTL ReturnCode */ +#define ARCMSR_MESSAGE_RETURNCODE_OK 0x00000001 +#define ARCMSR_MESSAGE_RETURNCODE_ERROR 0x00000006 +#define ARCMSR_MESSAGE_RETURNCODE_3F 0x0000003F +/* +************************************************************* +** structure for holding DMA address data +************************************************************* +*/ +#define IS_SG64_ADDR 0x01000000 /* bit24 */ +struct SG32ENTRY +{ + uint32_t length; + uint32_t address; +}; +struct SG64ENTRY +{ + uint32_t length; + uint32_t address; + uint32_t addresshigh; +}; +struct SGENTRY_UNION +{ + union + { + struct SG32ENTRY sg32entry; + struct SG64ENTRY sg64entry; + }u; +}; +/* +******************************************************************** +** Q Buffer of IOP Message Transfer +******************************************************************** +*/ +struct QBUFFER +{ + uint32_t data_len; + uint8_t data[124]; +}; +/* +******************************************************************************* +** FIRMWARE INFO +******************************************************************************* +*/ +struct FIRMWARE_INFO +{ + uint32_t signature; /*0, 00-03*/ + uint32_t request_len; /*1, 04-07*/ + uint32_t numbers_queue; /*2, 08-11*/ + uint32_t sdram_size; /*3, 12-15*/ + uint32_t ide_channels; /*4, 16-19*/ + char vendor[40]; /*5, 20-59*/ + char model[8]; /*15, 60-67*/ + char firmware_ver[16]; /*17, 68-83*/ + char device_map[16]; /*21, 84-99*/ +}; +/* signature of set and get firmware config */ +#define ARCMSR_SIGNATURE_GET_CONFIG 0x87974060 +#define ARCMSR_SIGNATURE_SET_CONFIG 0x87974063 +/* message code of inbound message register */ +#define ARCMSR_INBOUND_MESG0_NOP 0x00000000 +#define ARCMSR_INBOUND_MESG0_GET_CONFIG 0x00000001 +#define ARCMSR_INBOUND_MESG0_SET_CONFIG 0x00000002 +#define ARCMSR_INBOUND_MESG0_ABORT_CMD 0x00000003 +#define ARCMSR_INBOUND_MESG0_STOP_BGRB 0x00000004 +#define ARCMSR_INBOUND_MESG0_FLUSH_CACHE 0x00000005 +#define ARCMSR_INBOUND_MESG0_START_BGRB 0x00000006 +#define ARCMSR_INBOUND_MESG0_CHK331PENDING 0x00000007 +#define ARCMSR_INBOUND_MESG0_SYNC_TIMER 0x00000008 +/* doorbell interrupt generator */ +#define ARCMSR_INBOUND_DRIVER_DATA_WRITE_OK 0x00000001 +#define ARCMSR_INBOUND_DRIVER_DATA_READ_OK 0x00000002 +#define ARCMSR_OUTBOUND_IOP331_DATA_WRITE_OK 0x00000001 +#define ARCMSR_OUTBOUND_IOP331_DATA_READ_OK 0x00000002 +/* ccb areca cdb flag */ +#define ARCMSR_CCBPOST_FLAG_SGL_BSIZE 0x80000000 +#define ARCMSR_CCBPOST_FLAG_IAM_BIOS 0x40000000 +#define ARCMSR_CCBREPLY_FLAG_IAM_BIOS 0x40000000 +#define ARCMSR_CCBREPLY_FLAG_ERROR 0x10000000 +/* outbound firmware ok */ +#define ARCMSR_OUTBOUND_MESG1_FIRMWARE_OK 0x80000000 +/* +******************************************************************************* +** ARECA SCSI COMMAND DESCRIPTOR BLOCK size 0x1F8 (504) +******************************************************************************* +*/ +struct ARCMSR_CDB +{ + uint8_t Bus; + uint8_t TargetID; + uint8_t LUN; + uint8_t Function; + + uint8_t CdbLength; + uint8_t sgcount; + uint8_t Flags; +#define ARCMSR_CDB_FLAG_SGL_BSIZE 0x01 +#define ARCMSR_CDB_FLAG_BIOS 0x02 +#define ARCMSR_CDB_FLAG_WRITE 0x04 +#define ARCMSR_CDB_FLAG_SIMPLEQ 0x00 +#define ARCMSR_CDB_FLAG_HEADQ 0x08 +#define ARCMSR_CDB_FLAG_ORDEREDQ 0x10 + uint8_t Reserved1; + + uint32_t Context; + uint32_t DataLength; + + uint8_t Cdb[16]; + + uint8_t DeviceStatus; +#define ARCMSR_DEV_CHECK_CONDITION 0x02 +#define ARCMSR_DEV_SELECT_TIMEOUT 0xF0 +#define ARCMSR_DEV_ABORTED 0xF1 +#define ARCMSR_DEV_INIT_FAIL 0xF2 + uint8_t SenseData[15]; + + union + { + struct SG32ENTRY sg32entry[ARCMSR_MAX_SG_ENTRIES]; + struct SG64ENTRY sg64entry[ARCMSR_MAX_SG_ENTRIES]; + } u; +}; +/* +******************************************************************************* +** Messaging Unit (MU) of the Intel R 80331 I/O processor (80331) +******************************************************************************* +*/ +struct MessageUnit +{ + uint32_t resrved0[4]; /*0000 000F*/ + uint32_t inbound_msgaddr0; /*0010 0013*/ + uint32_t inbound_msgaddr1; /*0014 0017*/ + uint32_t outbound_msgaddr0; /*0018 001B*/ + uint32_t outbound_msgaddr1; /*001C 001F*/ + uint32_t inbound_doorbell; /*0020 0023*/ + uint32_t inbound_intstatus; /*0024 0027*/ + uint32_t inbound_intmask; /*0028 002B*/ + uint32_t outbound_doorbell; /*002C 002F*/ + uint32_t outbound_intstatus; /*0030 0033*/ + uint32_t outbound_intmask; /*0034 0037*/ + uint32_t reserved1[2]; /*0038 003F*/ + uint32_t inbound_queueport; /*0040 0043*/ + uint32_t outbound_queueport; /*0044 0047*/ + uint32_t reserved2[2]; /*0048 004F*/ + uint32_t reserved3[492]; /*0050 07FF 492*/ + uint32_t reserved4[128]; /*0800 09FF 128*/ + uint32_t message_rwbuffer[256]; /*0a00 0DFF 256*/ + uint32_t message_wbuffer[32]; /*0E00 0E7F 32*/ + uint32_t reserved5[32]; /*0E80 0EFF 32*/ + uint32_t message_rbuffer[32]; /*0F00 0F7F 32*/ + uint32_t reserved6[32]; /*0F80 0FFF 32*/ +}; +/* +******************************************************************************* +** Adapter Control Block +******************************************************************************* +*/ +struct AdapterControlBlock +{ + struct pci_dev * pdev; + struct Scsi_Host * host; + unsigned long vir2phy_offset; + /* Offset is used in making arc cdb physical to virtual calculations */ + uint32_t outbound_int_enable; + + struct MessageUnit __iomem * pmu; + /* message unit ATU inbound base address0 */ + + uint32_t acb_flags; +#define ACB_F_SCSISTOPADAPTER 0x0001 +#define ACB_F_MSG_STOP_BGRB 0x0002 + /* stop RAID background rebuild */ +#define ACB_F_MSG_START_BGRB 0x0004 + /* stop RAID background rebuild */ +#define ACB_F_IOPDATA_OVERFLOW 0x0008 + /* iop message data rqbuffer overflow */ +#define ACB_F_MESSAGE_WQBUFFER_CLEARED 0x0010 + /* message clear wqbuffer */ +#define ACB_F_MESSAGE_RQBUFFER_CLEARED 0x0020 + /* message clear rqbuffer */ +#define ACB_F_MESSAGE_WQBUFFER_READED 0x0040 +#define ACB_F_BUS_RESET 0x0080 +#define ACB_F_IOP_INITED 0x0100 + /* iop init */ + + struct CommandControlBlock * pccb_pool[ARCMSR_MAX_FREECCB_NUM]; + /* used for memory free */ + struct list_head ccb_free_list; + /* head of free ccb list */ + atomic_t ccboutstandingcount; + + void * dma_coherent; + /* dma_coherent used for memory free */ + dma_addr_t dma_coherent_handle; + /* dma_coherent_handle used for memory free */ + + uint8_t rqbuffer[ARCMSR_MAX_QBUFFER]; + /* data collection buffer for read from 80331 */ + int32_t rqbuf_firstindex; + /* first of read buffer */ + int32_t rqbuf_lastindex; + /* last of read buffer */ + uint8_t wqbuffer[ARCMSR_MAX_QBUFFER]; + /* data collection buffer for write to 80331 */ + int32_t wqbuf_firstindex; + /* first of write buffer */ + int32_t wqbuf_lastindex; + /* last of write buffer */ + uint8_t devstate[ARCMSR_MAX_TARGETID][ARCMSR_MAX_TARGETLUN]; + /* id0 ..... id15, lun0...lun7 */ +#define ARECA_RAID_GONE 0x55 +#define ARECA_RAID_GOOD 0xaa + uint32_t num_resets; + uint32_t num_aborts; + uint32_t firm_request_len; + uint32_t firm_numbers_queue; + uint32_t firm_sdram_size; + uint32_t firm_hd_channels; + char firm_model[12]; + char firm_version[20]; +};/* HW_DEVICE_EXTENSION */ +/* +******************************************************************************* +** Command Control Block +** this CCB length must be 32 bytes boundary +******************************************************************************* +*/ +struct CommandControlBlock +{ + struct ARCMSR_CDB arcmsr_cdb; + /* + ** 0-503 (size of CDB=504): + ** arcmsr messenger scsi command descriptor size 504 bytes + */ + uint32_t cdb_shifted_phyaddr; + /* 504-507 */ + uint32_t reserved1; + /* 508-511 */ +#if BITS_PER_LONG == 64 + /* ======================512+64 bytes======================== */ + struct list_head list; + /* 512-527 16 bytes next/prev ptrs for ccb lists */ + struct scsi_cmnd * pcmd; + /* 528-535 8 bytes pointer of linux scsi command */ + struct AdapterControlBlock * acb; + /* 536-543 8 bytes pointer of acb */ + + uint16_t ccb_flags; + /* 544-545 */ + #define CCB_FLAG_READ 0x0000 + #define CCB_FLAG_WRITE 0x0001 + #define CCB_FLAG_ERROR 0x0002 + #define CCB_FLAG_FLUSHCACHE 0x0004 + #define CCB_FLAG_MASTER_ABORTED 0x0008 + uint16_t startdone; + /* 546-547 */ + #define ARCMSR_CCB_DONE 0x0000 + #define ARCMSR_CCB_START 0x55AA + #define ARCMSR_CCB_ABORTED 0xAA55 + #define ARCMSR_CCB_ILLEGAL 0xFFFF + uint32_t reserved2[7]; + /* 548-551 552-555 556-559 560-563 564-567 568-571 572-575 */ +#else + /* ======================512+32 bytes======================== */ + struct list_head list; + /* 512-519 8 bytes next/prev ptrs for ccb lists */ + struct scsi_cmnd * pcmd; + /* 520-523 4 bytes pointer of linux scsi command */ + struct AdapterControlBlock * acb; + /* 524-527 4 bytes pointer of acb */ + + uint16_t ccb_flags; + /* 528-529 */ + #define CCB_FLAG_READ 0x0000 + #define CCB_FLAG_WRITE 0x0001 + #define CCB_FLAG_ERROR 0x0002 + #define CCB_FLAG_FLUSHCACHE 0x0004 + #define CCB_FLAG_MASTER_ABORTED 0x0008 + uint16_t startdone; + /* 530-531 */ + #define ARCMSR_CCB_DONE 0x0000 + #define ARCMSR_CCB_START 0x55AA + #define ARCMSR_CCB_ABORTED 0xAA55 + #define ARCMSR_CCB_ILLEGAL 0xFFFF + uint32_t reserved2[3]; + /* 532-535 536-539 540-543 */ +#endif + /* ========================================================== */ +}; +/* +******************************************************************************* +** ARECA SCSI sense data +******************************************************************************* +*/ +struct SENSE_DATA +{ + uint8_t ErrorCode:7; +#define SCSI_SENSE_CURRENT_ERRORS 0x70 +#define SCSI_SENSE_DEFERRED_ERRORS 0x71 + uint8_t Valid:1; + uint8_t SegmentNumber; + uint8_t SenseKey:4; + uint8_t Reserved:1; + uint8_t IncorrectLength:1; + uint8_t EndOfMedia:1; + uint8_t FileMark:1; + uint8_t Information[4]; + uint8_t AdditionalSenseLength; + uint8_t CommandSpecificInformation[4]; + uint8_t AdditionalSenseCode; + uint8_t AdditionalSenseCodeQualifier; + uint8_t FieldReplaceableUnitCode; + uint8_t SenseKeySpecific[3]; +}; +/* +******************************************************************************* +** Outbound Interrupt Status Register - OISR +******************************************************************************* +*/ +#define ARCMSR_MU_OUTBOUND_INTERRUPT_STATUS_REG 0x30 +#define ARCMSR_MU_OUTBOUND_PCI_INT 0x10 +#define ARCMSR_MU_OUTBOUND_POSTQUEUE_INT 0x08 +#define ARCMSR_MU_OUTBOUND_DOORBELL_INT 0x04 +#define ARCMSR_MU_OUTBOUND_MESSAGE1_INT 0x02 +#define ARCMSR_MU_OUTBOUND_MESSAGE0_INT 0x01 +#define ARCMSR_MU_OUTBOUND_HANDLE_INT \ + (ARCMSR_MU_OUTBOUND_MESSAGE0_INT \ + |ARCMSR_MU_OUTBOUND_MESSAGE1_INT \ + |ARCMSR_MU_OUTBOUND_DOORBELL_INT \ + |ARCMSR_MU_OUTBOUND_POSTQUEUE_INT \ + |ARCMSR_MU_OUTBOUND_PCI_INT) +/* +******************************************************************************* +** Outbound Interrupt Mask Register - OIMR +******************************************************************************* +*/ +#define ARCMSR_MU_OUTBOUND_INTERRUPT_MASK_REG 0x34 +#define ARCMSR_MU_OUTBOUND_PCI_INTMASKENABLE 0x10 +#define ARCMSR_MU_OUTBOUND_POSTQUEUE_INTMASKENABLE 0x08 +#define ARCMSR_MU_OUTBOUND_DOORBELL_INTMASKENABLE 0x04 +#define ARCMSR_MU_OUTBOUND_MESSAGE1_INTMASKENABLE 0x02 +#define ARCMSR_MU_OUTBOUND_MESSAGE0_INTMASKENABLE 0x01 +#define ARCMSR_MU_OUTBOUND_ALL_INTMASKENABLE 0x1F + +extern void arcmsr_post_Qbuffer(struct AdapterControlBlock *acb); +extern struct class_device_attribute *arcmsr_host_attrs[]; +extern int arcmsr_alloc_sysfs_attr(struct AdapterControlBlock *acb); +void arcmsr_free_sysfs_attr(struct AdapterControlBlock *acb); + diff --git a/drivers/scsi/arcmsr/arcmsr_attr.c b/drivers/scsi/arcmsr/arcmsr_attr.c new file mode 100644 index 00000000000..0459f4194d7 --- /dev/null +++ b/drivers/scsi/arcmsr/arcmsr_attr.c @@ -0,0 +1,392 @@ +/* +******************************************************************************* +** O.S : Linux +** FILE NAME : arcmsr_attr.c +** BY : Erich Chen +** Description: attributes exported to sysfs and device host +******************************************************************************* +** Copyright (C) 2002 - 2005, Areca Technology Corporation All rights reserved +** +** Web site: www.areca.com.tw +** E-mail: erich@areca.com.tw +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License version 2 as +** published by the Free Software Foundation. +** 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. +******************************************************************************* +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING,BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE)ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************* +** For history of changes, see Documentation/scsi/ChangeLog.arcmsr +** Firmware Specification, see Documentation/scsi/arcmsr_spec.txt +******************************************************************************* +*/ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "arcmsr.h" + +struct class_device_attribute *arcmsr_host_attrs[]; + +static ssize_t +arcmsr_sysfs_iop_message_read(struct kobject *kobj, char *buf, loff_t off, + size_t count) +{ + struct class_device *cdev = container_of(kobj,struct class_device,kobj); + struct Scsi_Host *host = class_to_shost(cdev); + struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; + struct MessageUnit __iomem *reg = acb->pmu; + uint8_t *pQbuffer,*ptmpQbuffer; + int32_t allxfer_len = 0; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + /* do message unit read. */ + ptmpQbuffer = (uint8_t *)buf; + while ((acb->rqbuf_firstindex != acb->rqbuf_lastindex) + && (allxfer_len < 1031)) { + pQbuffer = &acb->rqbuffer[acb->rqbuf_firstindex]; + memcpy(ptmpQbuffer, pQbuffer, 1); + acb->rqbuf_firstindex++; + acb->rqbuf_firstindex %= ARCMSR_MAX_QBUFFER; + ptmpQbuffer++; + allxfer_len++; + } + if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) { + struct QBUFFER __iomem * prbuffer = (struct QBUFFER __iomem *) + ®->message_rbuffer; + uint8_t __iomem * iop_data = (uint8_t __iomem *)prbuffer->data; + int32_t iop_len; + + acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW; + iop_len = readl(&prbuffer->data_len); + while (iop_len > 0) { + acb->rqbuffer[acb->rqbuf_lastindex] = readb(iop_data); + acb->rqbuf_lastindex++; + acb->rqbuf_lastindex %= ARCMSR_MAX_QBUFFER; + iop_data++; + iop_len--; + } + writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK, + ®->inbound_doorbell); + } + return (allxfer_len); +} + +static ssize_t +arcmsr_sysfs_iop_message_write(struct kobject *kobj, char *buf, loff_t off, + size_t count) +{ + struct class_device *cdev = container_of(kobj,struct class_device,kobj); + struct Scsi_Host *host = class_to_shost(cdev); + struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; + int32_t my_empty_len, user_len, wqbuf_firstindex, wqbuf_lastindex; + uint8_t *pQbuffer, *ptmpuserbuffer; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (count > 1032) + return -EINVAL; + /* do message unit write. */ + ptmpuserbuffer = (uint8_t *)buf; + user_len = (int32_t)count; + wqbuf_lastindex = acb->wqbuf_lastindex; + wqbuf_firstindex = acb->wqbuf_firstindex; + if (wqbuf_lastindex != wqbuf_firstindex) { + arcmsr_post_Qbuffer(acb); + return 0; /*need retry*/ + } else { + my_empty_len = (wqbuf_firstindex-wqbuf_lastindex - 1) + &(ARCMSR_MAX_QBUFFER - 1); + if (my_empty_len >= user_len) { + while (user_len > 0) { + pQbuffer = + &acb->wqbuffer[acb->wqbuf_lastindex]; + memcpy(pQbuffer, ptmpuserbuffer, 1); + acb->wqbuf_lastindex++; + acb->wqbuf_lastindex %= ARCMSR_MAX_QBUFFER; + ptmpuserbuffer++; + user_len--; + } + if (acb->acb_flags & ACB_F_MESSAGE_WQBUFFER_CLEARED) { + acb->acb_flags &= + ~ACB_F_MESSAGE_WQBUFFER_CLEARED; + arcmsr_post_Qbuffer(acb); + } + return count; + } else { + return 0; /*need retry*/ + } + } +} + +static ssize_t +arcmsr_sysfs_iop_message_clear(struct kobject *kobj, char *buf, loff_t off, + size_t count) +{ + struct class_device *cdev = container_of(kobj,struct class_device,kobj); + struct Scsi_Host *host = class_to_shost(cdev); + struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; + struct MessageUnit __iomem *reg = acb->pmu; + uint8_t *pQbuffer; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) { + acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW; + writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK + , ®->inbound_doorbell); + } + acb->acb_flags |= + (ACB_F_MESSAGE_WQBUFFER_CLEARED + | ACB_F_MESSAGE_RQBUFFER_CLEARED + | ACB_F_MESSAGE_WQBUFFER_READED); + acb->rqbuf_firstindex = 0; + acb->rqbuf_lastindex = 0; + acb->wqbuf_firstindex = 0; + acb->wqbuf_lastindex = 0; + pQbuffer = acb->rqbuffer; + memset(pQbuffer, 0, sizeof (struct QBUFFER)); + pQbuffer = acb->wqbuffer; + memset(pQbuffer, 0, sizeof (struct QBUFFER)); + return 1; +} + +static struct bin_attribute arcmsr_sysfs_message_read_attr = { + .attr = { + .name = "mu_read", + .mode = S_IRUSR , + .owner = THIS_MODULE, + }, + .size = 1032, + .read = arcmsr_sysfs_iop_message_read, +}; + +static struct bin_attribute arcmsr_sysfs_message_write_attr = { + .attr = { + .name = "mu_write", + .mode = S_IWUSR, + .owner = THIS_MODULE, + }, + .size = 1032, + .write = arcmsr_sysfs_iop_message_write, +}; + +static struct bin_attribute arcmsr_sysfs_message_clear_attr = { + .attr = { + .name = "mu_clear", + .mode = S_IWUSR, + .owner = THIS_MODULE, + }, + .size = 1, + .write = arcmsr_sysfs_iop_message_clear, +}; + +int arcmsr_alloc_sysfs_attr(struct AdapterControlBlock *acb) +{ + struct Scsi_Host *host = acb->host; + int error; + + error = sysfs_create_bin_file(&host->shost_classdev.kobj, + &arcmsr_sysfs_message_read_attr); + if (error) { + printk(KERN_ERR "arcmsr: alloc sysfs mu_read failed\n"); + goto error_bin_file_message_read; + } + error = sysfs_create_bin_file(&host->shost_classdev.kobj, + &arcmsr_sysfs_message_write_attr); + if (error) { + printk(KERN_ERR "arcmsr: alloc sysfs mu_write failed\n"); + goto error_bin_file_message_write; + } + error = sysfs_create_bin_file(&host->shost_classdev.kobj, + &arcmsr_sysfs_message_clear_attr); + if (error) { + printk(KERN_ERR "arcmsr: alloc sysfs mu_clear failed\n"); + goto error_bin_file_message_clear; + } + return 0; +error_bin_file_message_clear: + error = sysfs_remove_bin_file(&host->shost_classdev.kobj, + &arcmsr_sysfs_message_write_attr); + if (error) + printk(KERN_ERR "arcmsr: sysfs_remove_bin_file mu_write failed\n"); +error_bin_file_message_write: + error = sysfs_remove_bin_file(&host->shost_classdev.kobj, + &arcmsr_sysfs_message_read_attr); + if (error) + printk(KERN_ERR "arcmsr: sysfs_remove_bin_file mu_read failed\n"); +error_bin_file_message_read: + return error; +} + +void +arcmsr_free_sysfs_attr(struct AdapterControlBlock *acb) { + struct Scsi_Host *host = acb->host; + int error; + + error = sysfs_remove_bin_file(&host->shost_classdev.kobj, + &arcmsr_sysfs_message_clear_attr); + if (error) + printk(KERN_ERR "arcmsr: free sysfs mu_clear failed\n"); + error = sysfs_remove_bin_file(&host->shost_classdev.kobj, + &arcmsr_sysfs_message_write_attr); + if (error) + printk(KERN_ERR "arcmsr: free sysfs mu_write failed\n"); + error = sysfs_remove_bin_file(&host->shost_classdev.kobj, + &arcmsr_sysfs_message_read_attr); + if (error) + printk(KERN_ERR "arcmsr: free sysfss mu_read failed\n"); +} + + +static ssize_t +arcmsr_attr_host_driver_version(struct class_device *cdev, char *buf) { + return snprintf(buf, PAGE_SIZE, + "ARCMSR: %s\n", + ARCMSR_DRIVER_VERSION); +} + +static ssize_t +arcmsr_attr_host_driver_posted_cmd(struct class_device *cdev, char *buf) { + struct Scsi_Host *host = class_to_shost(cdev); + struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; + return snprintf(buf, PAGE_SIZE, + "Current commands posted: %4d\n", + atomic_read(&acb->ccboutstandingcount)); +} + +static ssize_t +arcmsr_attr_host_driver_reset(struct class_device *cdev, char *buf) { + struct Scsi_Host *host = class_to_shost(cdev); + struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; + return snprintf(buf, PAGE_SIZE, + "SCSI Host Resets: %4d\n", + acb->num_resets); +} + +static ssize_t +arcmsr_attr_host_driver_abort(struct class_device *cdev, char *buf) { + struct Scsi_Host *host = class_to_shost(cdev); + struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; + return snprintf(buf, PAGE_SIZE, + "SCSI Aborts/Timeouts: %4d\n", + acb->num_aborts); +} + +static ssize_t +arcmsr_attr_host_fw_model(struct class_device *cdev, char *buf) { + struct Scsi_Host *host = class_to_shost(cdev); + struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; + return snprintf(buf, PAGE_SIZE, + "Adapter Model: %s\n", + acb->firm_model); +} + +static ssize_t +arcmsr_attr_host_fw_version(struct class_device *cdev, char *buf) { + struct Scsi_Host *host = class_to_shost(cdev); + struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; + + return snprintf(buf, PAGE_SIZE, + "Firmware Version: %s\n", + acb->firm_version); +} + +static ssize_t +arcmsr_attr_host_fw_request_len(struct class_device *cdev, char *buf) { + struct Scsi_Host *host = class_to_shost(cdev); + struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; + + return snprintf(buf, PAGE_SIZE, + "Reguest Lenth: %4d\n", + acb->firm_request_len); +} + +static ssize_t +arcmsr_attr_host_fw_numbers_queue(struct class_device *cdev, char *buf) { + struct Scsi_Host *host = class_to_shost(cdev); + struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; + + return snprintf(buf, PAGE_SIZE, + "Numbers of Queue: %4d\n", + acb->firm_numbers_queue); +} + +static ssize_t +arcmsr_attr_host_fw_sdram_size(struct class_device *cdev, char *buf) { + struct Scsi_Host *host = class_to_shost(cdev); + struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; + + return snprintf(buf, PAGE_SIZE, + "SDRAM Size: %4d\n", + acb->firm_sdram_size); +} + +static ssize_t +arcmsr_attr_host_fw_hd_channels(struct class_device *cdev, char *buf) { + struct Scsi_Host *host = class_to_shost(cdev); + struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; + + return snprintf(buf, PAGE_SIZE, + "Hard Disk Channels: %4d\n", + acb->firm_hd_channels); +} + +static CLASS_DEVICE_ATTR(host_driver_version, S_IRUGO, arcmsr_attr_host_driver_version, NULL); +static CLASS_DEVICE_ATTR(host_driver_posted_cmd, S_IRUGO, arcmsr_attr_host_driver_posted_cmd, NULL); +static CLASS_DEVICE_ATTR(host_driver_reset, S_IRUGO, arcmsr_attr_host_driver_reset, NULL); +static CLASS_DEVICE_ATTR(host_driver_abort, S_IRUGO, arcmsr_attr_host_driver_abort, NULL); +static CLASS_DEVICE_ATTR(host_fw_model, S_IRUGO, arcmsr_attr_host_fw_model, NULL); +static CLASS_DEVICE_ATTR(host_fw_version, S_IRUGO, arcmsr_attr_host_fw_version, NULL); +static CLASS_DEVICE_ATTR(host_fw_request_len, S_IRUGO, arcmsr_attr_host_fw_request_len, NULL); +static CLASS_DEVICE_ATTR(host_fw_numbers_queue, S_IRUGO, arcmsr_attr_host_fw_numbers_queue, NULL); +static CLASS_DEVICE_ATTR(host_fw_sdram_size, S_IRUGO, arcmsr_attr_host_fw_sdram_size, NULL); +static CLASS_DEVICE_ATTR(host_fw_hd_channels, S_IRUGO, arcmsr_attr_host_fw_hd_channels, NULL); + +struct class_device_attribute *arcmsr_host_attrs[] = { + &class_device_attr_host_driver_version, + &class_device_attr_host_driver_posted_cmd, + &class_device_attr_host_driver_reset, + &class_device_attr_host_driver_abort, + &class_device_attr_host_fw_model, + &class_device_attr_host_fw_version, + &class_device_attr_host_fw_request_len, + &class_device_attr_host_fw_numbers_queue, + &class_device_attr_host_fw_sdram_size, + &class_device_attr_host_fw_hd_channels, + NULL, +}; diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c new file mode 100644 index 00000000000..475f978ff8f --- /dev/null +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -0,0 +1,1496 @@ +/* +******************************************************************************* +** O.S : Linux +** FILE NAME : arcmsr_hba.c +** BY : Erich Chen +** Description: SCSI RAID Device Driver for +** ARECA RAID Host adapter +******************************************************************************* +** Copyright (C) 2002 - 2005, Areca Technology Corporation All rights reserved +** +** Web site: www.areca.com.tw +** E-mail: erich@areca.com.tw +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License version 2 as +** published by the Free Software Foundation. +** 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. +******************************************************************************* +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING,BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE)ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************* +** For history of changes, see Documentation/scsi/ChangeLog.arcmsr +** Firmware Specification, see Documentation/scsi/arcmsr_spec.txt +******************************************************************************* +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "arcmsr.h" + +MODULE_AUTHOR("Erich Chen "); +MODULE_DESCRIPTION("ARECA (ARC11xx/12xx) SATA RAID HOST Adapter"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(ARCMSR_DRIVER_VERSION); + +static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb, struct scsi_cmnd *cmd); +static int arcmsr_abort(struct scsi_cmnd *); +static int arcmsr_bus_reset(struct scsi_cmnd *); +static int arcmsr_bios_param(struct scsi_device *sdev, + struct block_device *bdev, sector_t capacity, int *info); +static int arcmsr_queue_command(struct scsi_cmnd * cmd, + void (*done) (struct scsi_cmnd *)); +static int arcmsr_probe(struct pci_dev *pdev, + const struct pci_device_id *id); +static void arcmsr_remove(struct pci_dev *pdev); +static void arcmsr_shutdown(struct pci_dev *pdev); +static void arcmsr_iop_init(struct AdapterControlBlock *acb); +static void arcmsr_free_ccb_pool(struct AdapterControlBlock *acb); +static void arcmsr_stop_adapter_bgrb(struct AdapterControlBlock *acb); +static void arcmsr_flush_adapter_cache(struct AdapterControlBlock *acb); +static uint8_t arcmsr_wait_msgint_ready(struct AdapterControlBlock *acb); +static const char *arcmsr_info(struct Scsi_Host *); +static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb); + +static int arcmsr_adjust_disk_queue_depth(struct scsi_device *sdev, int queue_depth) +{ + if (queue_depth > ARCMSR_MAX_CMD_PERLUN) + queue_depth = ARCMSR_MAX_CMD_PERLUN; + scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth); + return queue_depth; +} + +static struct scsi_host_template arcmsr_scsi_host_template = { + .module = THIS_MODULE, + .name = "ARCMSR ARECA SATA RAID HOST Adapter" ARCMSR_DRIVER_VERSION, + .info = arcmsr_info, + .queuecommand = arcmsr_queue_command, + .eh_abort_handler = arcmsr_abort, + .eh_bus_reset_handler = arcmsr_bus_reset, + .bios_param = arcmsr_bios_param, + .change_queue_depth = arcmsr_adjust_disk_queue_depth, + .can_queue = ARCMSR_MAX_OUTSTANDING_CMD, + .this_id = ARCMSR_SCSI_INITIATOR_ID, + .sg_tablesize = ARCMSR_MAX_SG_ENTRIES, + .max_sectors = ARCMSR_MAX_XFER_SECTORS, + .cmd_per_lun = ARCMSR_MAX_CMD_PERLUN, + .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = arcmsr_host_attrs, +}; + +static struct pci_device_id arcmsr_device_id_table[] = { + {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1110)}, + {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1120)}, + {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1130)}, + {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1160)}, + {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1170)}, + {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1210)}, + {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1220)}, + {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1230)}, + {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1260)}, + {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1270)}, + {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1280)}, + {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1380)}, + {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1381)}, + {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1680)}, + {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1681)}, + {0, 0}, /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, arcmsr_device_id_table); +static struct pci_driver arcmsr_pci_driver = { + .name = "arcmsr", + .id_table = arcmsr_device_id_table, + .probe = arcmsr_probe, + .remove = arcmsr_remove, + .shutdown = arcmsr_shutdown +}; + +static irqreturn_t arcmsr_do_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + irqreturn_t handle_state; + struct AdapterControlBlock *acb; + unsigned long flags; + + acb = (struct AdapterControlBlock *)dev_id; + + spin_lock_irqsave(acb->host->host_lock, flags); + handle_state = arcmsr_interrupt(acb); + spin_unlock_irqrestore(acb->host->host_lock, flags); + return handle_state; +} + +static int arcmsr_bios_param(struct scsi_device *sdev, + struct block_device *bdev, sector_t capacity, int *geom) +{ + int ret, heads, sectors, cylinders, total_capacity; + unsigned char *buffer;/* return copy of block device's partition table */ + + buffer = scsi_bios_ptable(bdev); + if (buffer) { + ret = scsi_partsize(buffer, capacity, &geom[2], &geom[0], &geom[1]); + kfree(buffer); + if (ret != -1) + return ret; + } + total_capacity = capacity; + heads = 64; + sectors = 32; + cylinders = total_capacity / (heads * sectors); + if (cylinders > 1024) { + heads = 255; + sectors = 63; + cylinders = total_capacity / (heads * sectors); + } + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + return 0; +} + +static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb) +{ + struct pci_dev *pdev = acb->pdev; + struct MessageUnit __iomem *reg = acb->pmu; + u32 ccb_phyaddr_hi32; + void *dma_coherent; + dma_addr_t dma_coherent_handle, dma_addr; + struct CommandControlBlock *ccb_tmp; + int i, j; + + dma_coherent = dma_alloc_coherent(&pdev->dev, + ARCMSR_MAX_FREECCB_NUM * + sizeof (struct CommandControlBlock) + 0x20, + &dma_coherent_handle, GFP_KERNEL); + if (!dma_coherent) + return -ENOMEM; + + acb->dma_coherent = dma_coherent; + acb->dma_coherent_handle = dma_coherent_handle; + + if (((unsigned long)dma_coherent & 0x1F)) { + dma_coherent = dma_coherent + + (0x20 - ((unsigned long)dma_coherent & 0x1F)); + dma_coherent_handle = dma_coherent_handle + + (0x20 - ((unsigned long)dma_coherent_handle & 0x1F)); + } + + dma_addr = dma_coherent_handle; + ccb_tmp = (struct CommandControlBlock *)dma_coherent; + for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) { + ccb_tmp->cdb_shifted_phyaddr = dma_addr >> 5; + ccb_tmp->acb = acb; + acb->pccb_pool[i] = ccb_tmp; + list_add_tail(&ccb_tmp->list, &acb->ccb_free_list); + dma_addr = dma_addr + sizeof (struct CommandControlBlock); + ccb_tmp++; + } + + acb->vir2phy_offset = (unsigned long)ccb_tmp - + (unsigned long)dma_addr; + for (i = 0; i < ARCMSR_MAX_TARGETID; i++) + for (j = 0; j < ARCMSR_MAX_TARGETLUN; j++) + acb->devstate[i][j] = ARECA_RAID_GOOD; + + /* + ** here we need to tell iop 331 our ccb_tmp.HighPart + ** if ccb_tmp.HighPart is not zero + */ + ccb_phyaddr_hi32 = (uint32_t) ((dma_coherent_handle >> 16) >> 16); + if (ccb_phyaddr_hi32 != 0) { + writel(ARCMSR_SIGNATURE_SET_CONFIG, ®->message_rwbuffer[0]); + writel(ccb_phyaddr_hi32, ®->message_rwbuffer[1]); + writel(ARCMSR_INBOUND_MESG0_SET_CONFIG, ®->inbound_msgaddr0); + if (arcmsr_wait_msgint_ready(acb)) + printk(KERN_NOTICE "arcmsr%d: " + "'set ccb high part physical address' timeout\n", + acb->host->host_no); + } + + writel(readl(®->outbound_intmask) | + ARCMSR_MU_OUTBOUND_ALL_INTMASKENABLE, + ®->outbound_intmask); + return 0; +} + +static int arcmsr_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct Scsi_Host *host; + struct AdapterControlBlock *acb; + uint8_t bus, dev_fun; + int error; + + error = pci_enable_device(pdev); + if (error) + goto out; + pci_set_master(pdev); + + host = scsi_host_alloc(&arcmsr_scsi_host_template, + sizeof(struct AdapterControlBlock)); + if (!host) { + error = -ENOMEM; + goto out_disable_device; + } + acb = (struct AdapterControlBlock *)host->hostdata; + memset(acb, 0, sizeof (struct AdapterControlBlock)); + + error = pci_set_dma_mask(pdev, DMA_64BIT_MASK); + if (error) { + error = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (error) { + printk(KERN_WARNING + "scsi%d: No suitable DMA mask available\n", + host->host_no); + goto out_host_put; + } + } + bus = pdev->bus->number; + dev_fun = pdev->devfn; + acb->host = host; + acb->pdev = pdev; + host->max_sectors = ARCMSR_MAX_XFER_SECTORS; + host->max_lun = ARCMSR_MAX_TARGETLUN; + host->max_id = ARCMSR_MAX_TARGETID;/*16:8*/ + host->max_cmd_len = 16; /*this is issue of 64bit LBA, over 2T byte*/ + host->sg_tablesize = ARCMSR_MAX_SG_ENTRIES; + host->can_queue = ARCMSR_MAX_FREECCB_NUM; /* max simultaneous cmds */ + host->cmd_per_lun = ARCMSR_MAX_CMD_PERLUN; + host->this_id = ARCMSR_SCSI_INITIATOR_ID; + host->unique_id = (bus << 8) | dev_fun; + host->irq = pdev->irq; + error = pci_request_regions(pdev, "arcmsr"); + if (error) + goto out_host_put; + + acb->pmu = ioremap(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if (!acb->pmu) { + printk(KERN_NOTICE "arcmsr%d: memory" + " mapping region fail \n", acb->host->host_no); + goto out_release_regions; + } + acb->acb_flags |= (ACB_F_MESSAGE_WQBUFFER_CLEARED | + ACB_F_MESSAGE_RQBUFFER_CLEARED | + ACB_F_MESSAGE_WQBUFFER_READED); + acb->acb_flags &= ~ACB_F_SCSISTOPADAPTER; + INIT_LIST_HEAD(&acb->ccb_free_list); + + error = arcmsr_alloc_ccb_pool(acb); + if (error) + goto out_iounmap; + + error = request_irq(pdev->irq, arcmsr_do_interrupt, + SA_INTERRUPT | SA_SHIRQ, "arcmsr", acb); + if (error) + goto out_free_ccb_pool; + + arcmsr_iop_init(acb); + pci_set_drvdata(pdev, host); + + error = scsi_add_host(host, &pdev->dev); + if (error) + goto out_free_irq; + + error = arcmsr_alloc_sysfs_attr(acb); + if (error) + goto out_free_sysfs; + + scsi_scan_host(host); + return 0; + out_free_sysfs: + out_free_irq: + free_irq(pdev->irq, acb); + out_free_ccb_pool: + arcmsr_free_ccb_pool(acb); + out_iounmap: + iounmap(acb->pmu); + out_release_regions: + pci_release_regions(pdev); + out_host_put: + scsi_host_put(host); + out_disable_device: + pci_disable_device(pdev); + out: + return error; +} + +static void arcmsr_abort_allcmd(struct AdapterControlBlock *acb) +{ + struct MessageUnit __iomem *reg = acb->pmu; + + writel(ARCMSR_INBOUND_MESG0_ABORT_CMD, ®->inbound_msgaddr0); + if (arcmsr_wait_msgint_ready(acb)) + printk(KERN_NOTICE + "arcmsr%d: wait 'abort all outstanding command' timeout \n" + , acb->host->host_no); +} + +static void arcmsr_pci_unmap_dma(struct CommandControlBlock *ccb) +{ + struct AdapterControlBlock *acb = ccb->acb; + struct scsi_cmnd *pcmd = ccb->pcmd; + + if (pcmd->use_sg != 0) { + struct scatterlist *sl; + + sl = (struct scatterlist *)pcmd->request_buffer; + pci_unmap_sg(acb->pdev, sl, pcmd->use_sg, pcmd->sc_data_direction); + } + else if (pcmd->request_bufflen != 0) + pci_unmap_single(acb->pdev, + pcmd->SCp.dma_handle, + pcmd->request_bufflen, pcmd->sc_data_direction); +} + +static void arcmsr_ccb_complete(struct CommandControlBlock *ccb, int stand_flag) +{ + struct AdapterControlBlock *acb = ccb->acb; + struct scsi_cmnd *pcmd = ccb->pcmd; + + arcmsr_pci_unmap_dma(ccb); + if (stand_flag == 1) + atomic_dec(&acb->ccboutstandingcount); + ccb->startdone = ARCMSR_CCB_DONE; + ccb->ccb_flags = 0; + list_add_tail(&ccb->list, &acb->ccb_free_list); + pcmd->scsi_done(pcmd); +} + +static void arcmsr_remove(struct pci_dev *pdev) +{ + struct Scsi_Host *host = pci_get_drvdata(pdev); + struct AdapterControlBlock *acb = + (struct AdapterControlBlock *) host->hostdata; + struct MessageUnit __iomem *reg = acb->pmu; + int poll_count = 0; + + arcmsr_free_sysfs_attr(acb); + scsi_remove_host(host); + arcmsr_stop_adapter_bgrb(acb); + arcmsr_flush_adapter_cache(acb); + writel(readl(®->outbound_intmask) | + ARCMSR_MU_OUTBOUND_ALL_INTMASKENABLE, + ®->outbound_intmask); + acb->acb_flags |= ACB_F_SCSISTOPADAPTER; + acb->acb_flags &= ~ACB_F_IOP_INITED; + + for (poll_count = 0; poll_count < 256; poll_count++) { + if (!atomic_read(&acb->ccboutstandingcount)) + break; + arcmsr_interrupt(acb); + msleep(25); + } + + if (atomic_read(&acb->ccboutstandingcount)) { + int i; + + arcmsr_abort_allcmd(acb); + for (i = 0; i < ARCMSR_MAX_OUTSTANDING_CMD; i++) + readl(®->outbound_queueport); + for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) { + struct CommandControlBlock *ccb = acb->pccb_pool[i]; + if (ccb->startdone == ARCMSR_CCB_START) { + ccb->startdone = ARCMSR_CCB_ABORTED; + ccb->pcmd->result = DID_ABORT << 16; + arcmsr_ccb_complete(ccb, 1); + } + } + } + + free_irq(pdev->irq, acb); + iounmap(acb->pmu); + arcmsr_free_ccb_pool(acb); + pci_release_regions(pdev); + + scsi_host_put(host); + + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +static void arcmsr_shutdown(struct pci_dev *pdev) +{ + struct Scsi_Host *host = pci_get_drvdata(pdev); + struct AdapterControlBlock *acb = + (struct AdapterControlBlock *)host->hostdata; + + arcmsr_stop_adapter_bgrb(acb); + arcmsr_flush_adapter_cache(acb); +} + +static int arcmsr_module_init(void) +{ + int error = 0; + + error = pci_register_driver(&arcmsr_pci_driver); + return error; +} + +static void arcmsr_module_exit(void) +{ + pci_unregister_driver(&arcmsr_pci_driver); +} +module_init(arcmsr_module_init); +module_exit(arcmsr_module_exit); + +static u32 arcmsr_disable_outbound_ints(struct AdapterControlBlock *acb) +{ + struct MessageUnit __iomem *reg = acb->pmu; + u32 orig_mask = readl(®->outbound_intmask); + + writel(orig_mask | ARCMSR_MU_OUTBOUND_ALL_INTMASKENABLE, + ®->outbound_intmask); + return orig_mask; +} + +static void arcmsr_enable_outbound_ints(struct AdapterControlBlock *acb, + u32 orig_mask) +{ + struct MessageUnit __iomem *reg = acb->pmu; + u32 mask; + + mask = orig_mask & ~(ARCMSR_MU_OUTBOUND_POSTQUEUE_INTMASKENABLE | + ARCMSR_MU_OUTBOUND_DOORBELL_INTMASKENABLE); + writel(mask, ®->outbound_intmask); +} + +static void arcmsr_flush_adapter_cache(struct AdapterControlBlock *acb) +{ + struct MessageUnit __iomem *reg=acb->pmu; + + writel(ARCMSR_INBOUND_MESG0_FLUSH_CACHE, ®->inbound_msgaddr0); + if (arcmsr_wait_msgint_ready(acb)) + printk(KERN_NOTICE + "arcmsr%d: wait 'flush adapter cache' timeout \n" + , acb->host->host_no); +} + +static void arcmsr_report_sense_info(struct CommandControlBlock *ccb) +{ + struct scsi_cmnd *pcmd = ccb->pcmd; + struct SENSE_DATA *sensebuffer = (struct SENSE_DATA *)pcmd->sense_buffer; + + pcmd->result = DID_OK << 16; + if (sensebuffer) { + int sense_data_length = + sizeof (struct SENSE_DATA) < sizeof (pcmd->sense_buffer) + ? sizeof (struct SENSE_DATA) : sizeof (pcmd->sense_buffer); + memset(sensebuffer, 0, sizeof (pcmd->sense_buffer)); + memcpy(sensebuffer, ccb->arcmsr_cdb.SenseData, sense_data_length); + sensebuffer->ErrorCode = SCSI_SENSE_CURRENT_ERRORS; + sensebuffer->Valid = 1; + } +} + +static uint8_t arcmsr_wait_msgint_ready(struct AdapterControlBlock *acb) +{ + struct MessageUnit __iomem *reg = acb->pmu; + uint32_t Index; + uint8_t Retries = 0x00; + + do { + for (Index = 0; Index < 100; Index++) { + if (readl(®->outbound_intstatus) + & ARCMSR_MU_OUTBOUND_MESSAGE0_INT) { + writel(ARCMSR_MU_OUTBOUND_MESSAGE0_INT + , ®->outbound_intstatus); + return 0x00; + } + msleep_interruptible(10); + }/*max 1 seconds*/ + } while (Retries++ < 20);/*max 20 sec*/ + return 0xff; +} + +static void arcmsr_build_ccb(struct AdapterControlBlock *acb, + struct CommandControlBlock *ccb, struct scsi_cmnd *pcmd) +{ + struct ARCMSR_CDB *arcmsr_cdb = (struct ARCMSR_CDB *)&ccb->arcmsr_cdb; + int8_t *psge = (int8_t *)&arcmsr_cdb->u; + uint32_t address_lo, address_hi; + int arccdbsize = 0x30; + + ccb->pcmd = pcmd; + memset(arcmsr_cdb, 0, sizeof (struct ARCMSR_CDB)); + arcmsr_cdb->Bus = 0; + arcmsr_cdb->TargetID = pcmd->device->id; + arcmsr_cdb->LUN = pcmd->device->lun; + arcmsr_cdb->Function = 1; + arcmsr_cdb->CdbLength = (uint8_t)pcmd->cmd_len; + arcmsr_cdb->Context = (unsigned long)arcmsr_cdb; + memcpy(arcmsr_cdb->Cdb, pcmd->cmnd, pcmd->cmd_len); + if (pcmd->use_sg) { + int length, sgcount, i, cdb_sgcount = 0; + struct scatterlist *sl; + + /* Get Scatter Gather List from scsiport. */ + sl = (struct scatterlist *) pcmd->request_buffer; + sgcount = pci_map_sg(acb->pdev, sl, pcmd->use_sg, + pcmd->sc_data_direction); + /* map stor port SG list to our iop SG List. */ + for (i = 0; i < sgcount; i++) { + /* Get the physical address of the current data pointer */ + length = cpu_to_le32(sg_dma_len(sl)); + address_lo = cpu_to_le32(dma_addr_lo32(sg_dma_address(sl))); + address_hi = cpu_to_le32(dma_addr_hi32(sg_dma_address(sl))); + if (address_hi == 0) { + struct SG32ENTRY *pdma_sg = (struct SG32ENTRY *)psge; + + pdma_sg->address = address_lo; + pdma_sg->length = length; + psge += sizeof (struct SG32ENTRY); + arccdbsize += sizeof (struct SG32ENTRY); + } else { + struct SG64ENTRY *pdma_sg = (struct SG64ENTRY *)psge; + + pdma_sg->addresshigh = address_hi; + pdma_sg->address = address_lo; + pdma_sg->length = length|IS_SG64_ADDR; + psge += sizeof (struct SG64ENTRY); + arccdbsize += sizeof (struct SG64ENTRY); + } + sl++; + cdb_sgcount++; + } + arcmsr_cdb->sgcount = (uint8_t)cdb_sgcount; + arcmsr_cdb->DataLength = pcmd->request_bufflen; + if ( arccdbsize > 256) + arcmsr_cdb->Flags |= ARCMSR_CDB_FLAG_SGL_BSIZE; + } else if (pcmd->request_bufflen) { + dma_addr_t dma_addr; + dma_addr = pci_map_single(acb->pdev, pcmd->request_buffer, + pcmd->request_bufflen, pcmd->sc_data_direction); + pcmd->SCp.dma_handle = dma_addr; + address_lo = cpu_to_le32(dma_addr_lo32(dma_addr)); + address_hi = cpu_to_le32(dma_addr_hi32(dma_addr)); + if (address_hi == 0) { + struct SG32ENTRY *pdma_sg = (struct SG32ENTRY *)psge; + pdma_sg->address = address_lo; + pdma_sg->length = pcmd->request_bufflen; + } else { + struct SG64ENTRY *pdma_sg = (struct SG64ENTRY *)psge; + pdma_sg->addresshigh = address_hi; + pdma_sg->address = address_lo; + pdma_sg->length = pcmd->request_bufflen|IS_SG64_ADDR; + } + arcmsr_cdb->sgcount = 1; + arcmsr_cdb->DataLength = pcmd->request_bufflen; + } + if (pcmd->sc_data_direction == DMA_TO_DEVICE ) { + arcmsr_cdb->Flags |= ARCMSR_CDB_FLAG_WRITE; + ccb->ccb_flags |= CCB_FLAG_WRITE; + } +} + +static void arcmsr_post_ccb(struct AdapterControlBlock *acb, struct CommandControlBlock *ccb) +{ + struct MessageUnit __iomem *reg = acb->pmu; + uint32_t cdb_shifted_phyaddr = ccb->cdb_shifted_phyaddr; + struct ARCMSR_CDB *arcmsr_cdb = (struct ARCMSR_CDB *)&ccb->arcmsr_cdb; + + atomic_inc(&acb->ccboutstandingcount); + ccb->startdone = ARCMSR_CCB_START; + if (arcmsr_cdb->Flags & ARCMSR_CDB_FLAG_SGL_BSIZE) + writel(cdb_shifted_phyaddr | ARCMSR_CCBPOST_FLAG_SGL_BSIZE, + ®->inbound_queueport); + else + writel(cdb_shifted_phyaddr, ®->inbound_queueport); +} + +void arcmsr_post_Qbuffer(struct AdapterControlBlock *acb) +{ + struct MessageUnit __iomem *reg = acb->pmu; + struct QBUFFER __iomem *pwbuffer = (struct QBUFFER __iomem *) ®->message_wbuffer; + uint8_t __iomem *iop_data = (uint8_t __iomem *) pwbuffer->data; + int32_t allxfer_len = 0; + + if (acb->acb_flags & ACB_F_MESSAGE_WQBUFFER_READED) { + acb->acb_flags &= (~ACB_F_MESSAGE_WQBUFFER_READED); + while ((acb->wqbuf_firstindex != acb->wqbuf_lastindex) + && (allxfer_len < 124)) { + writeb(acb->wqbuffer[acb->wqbuf_firstindex], iop_data); + acb->wqbuf_firstindex++; + acb->wqbuf_firstindex %= ARCMSR_MAX_QBUFFER; + iop_data++; + allxfer_len++; + } + writel(allxfer_len, &pwbuffer->data_len); + writel(ARCMSR_INBOUND_DRIVER_DATA_WRITE_OK + , ®->inbound_doorbell); + } +} + +static void arcmsr_stop_adapter_bgrb(struct AdapterControlBlock *acb) +{ + struct MessageUnit __iomem *reg = acb->pmu; + + acb->acb_flags &= ~ACB_F_MSG_START_BGRB; + writel(ARCMSR_INBOUND_MESG0_STOP_BGRB, ®->inbound_msgaddr0); + if (arcmsr_wait_msgint_ready(acb)) + printk(KERN_NOTICE + "arcmsr%d: wait 'stop adapter background rebulid' timeout \n" + , acb->host->host_no); +} + +static void arcmsr_free_ccb_pool(struct AdapterControlBlock *acb) +{ + dma_free_coherent(&acb->pdev->dev, + ARCMSR_MAX_FREECCB_NUM * sizeof (struct CommandControlBlock) + 0x20, + acb->dma_coherent, + acb->dma_coherent_handle); +} + +static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb) +{ + struct MessageUnit __iomem *reg = acb->pmu; + struct CommandControlBlock *ccb; + uint32_t flag_ccb, outbound_intstatus, outbound_doorbell; + + outbound_intstatus = readl(®->outbound_intstatus) + & acb->outbound_int_enable; + writel(outbound_intstatus, ®->outbound_intstatus); + if (outbound_intstatus & ARCMSR_MU_OUTBOUND_DOORBELL_INT) { + outbound_doorbell = readl(®->outbound_doorbell); + writel(outbound_doorbell, ®->outbound_doorbell); + if (outbound_doorbell & ARCMSR_OUTBOUND_IOP331_DATA_WRITE_OK) { + struct QBUFFER __iomem * prbuffer = + (struct QBUFFER __iomem *) ®->message_rbuffer; + uint8_t __iomem * iop_data = (uint8_t __iomem *)prbuffer->data; + int32_t my_empty_len, iop_len, rqbuf_firstindex, rqbuf_lastindex; + + rqbuf_lastindex = acb->rqbuf_lastindex; + rqbuf_firstindex = acb->rqbuf_firstindex; + iop_len = readl(&prbuffer->data_len); + my_empty_len = (rqbuf_firstindex - rqbuf_lastindex - 1) + &(ARCMSR_MAX_QBUFFER - 1); + if (my_empty_len >= iop_len) { + while (iop_len > 0) { + acb->rqbuffer[acb->rqbuf_lastindex] = readb(iop_data); + acb->rqbuf_lastindex++; + acb->rqbuf_lastindex %= ARCMSR_MAX_QBUFFER; + iop_data++; + iop_len--; + } + writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK, + ®->inbound_doorbell); + } else + acb->acb_flags |= ACB_F_IOPDATA_OVERFLOW; + } + if (outbound_doorbell & ARCMSR_OUTBOUND_IOP331_DATA_READ_OK) { + acb->acb_flags |= ACB_F_MESSAGE_WQBUFFER_READED; + if (acb->wqbuf_firstindex != acb->wqbuf_lastindex) { + struct QBUFFER __iomem * pwbuffer = + (struct QBUFFER __iomem *) ®->message_wbuffer; + uint8_t __iomem * iop_data = (uint8_t __iomem *) pwbuffer->data; + int32_t allxfer_len = 0; + + acb->acb_flags &= (~ACB_F_MESSAGE_WQBUFFER_READED); + while ((acb->wqbuf_firstindex != acb->wqbuf_lastindex) + && (allxfer_len < 124)) { + writeb(acb->wqbuffer[acb->wqbuf_firstindex], iop_data); + acb->wqbuf_firstindex++; + acb->wqbuf_firstindex %= ARCMSR_MAX_QBUFFER; + iop_data++; + allxfer_len++; + } + writel(allxfer_len, &pwbuffer->data_len); + writel(ARCMSR_INBOUND_DRIVER_DATA_WRITE_OK, + ®->inbound_doorbell); + } + if (acb->wqbuf_firstindex == acb->wqbuf_lastindex) + acb->acb_flags |= ACB_F_MESSAGE_WQBUFFER_CLEARED; + } + } + if (outbound_intstatus & ARCMSR_MU_OUTBOUND_POSTQUEUE_INT) { + int id, lun; + /* + **************************************************************** + ** areca cdb command done + **************************************************************** + */ + while (1) { + if ((flag_ccb = readl(®->outbound_queueport)) == 0xFFFFFFFF) + break;/*chip FIFO no ccb for completion already*/ + /* check if command done with no error*/ + ccb = (struct CommandControlBlock *)(acb->vir2phy_offset + + (flag_ccb << 5)); + if ((ccb->acb != acb) || (ccb->startdone != ARCMSR_CCB_START)) { + if (ccb->startdone == ARCMSR_CCB_ABORTED) { + struct scsi_cmnd *abortcmd=ccb->pcmd; + if (abortcmd) { + abortcmd->result |= DID_ABORT >> 16; + arcmsr_ccb_complete(ccb, 1); + printk(KERN_NOTICE + "arcmsr%d: ccb='0x%p' isr got aborted command \n" + , acb->host->host_no, ccb); + } + continue; + } + printk(KERN_NOTICE + "arcmsr%d: isr get an illegal ccb command done acb='0x%p'" + "ccb='0x%p' ccbacb='0x%p' startdone = 0x%x" + " ccboutstandingcount=%d \n" + , acb->host->host_no + , acb + , ccb + , ccb->acb + , ccb->startdone + , atomic_read(&acb->ccboutstandingcount)); + continue; + } + id = ccb->pcmd->device->id; + lun = ccb->pcmd->device->lun; + if (!(flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR)) { + if (acb->devstate[id][lun] == ARECA_RAID_GONE) + acb->devstate[id][lun] = ARECA_RAID_GOOD; + ccb->pcmd->result = DID_OK << 16; + arcmsr_ccb_complete(ccb, 1); + } else { + switch(ccb->arcmsr_cdb.DeviceStatus) { + case ARCMSR_DEV_SELECT_TIMEOUT: { + acb->devstate[id][lun] = ARECA_RAID_GONE; + ccb->pcmd->result = DID_TIME_OUT << 16; + arcmsr_ccb_complete(ccb, 1); + } + break; + case ARCMSR_DEV_ABORTED: + case ARCMSR_DEV_INIT_FAIL: { + acb->devstate[id][lun] = ARECA_RAID_GONE; + ccb->pcmd->result = DID_BAD_TARGET << 16; + arcmsr_ccb_complete(ccb, 1); + } + break; + case ARCMSR_DEV_CHECK_CONDITION: { + acb->devstate[id][lun] = ARECA_RAID_GOOD; + arcmsr_report_sense_info(ccb); + arcmsr_ccb_complete(ccb, 1); + } + break; + default: + printk(KERN_NOTICE + "arcmsr%d: scsi id=%d lun=%d" + " isr get command error done," + "but got unknown DeviceStatus = 0x%x \n" + , acb->host->host_no + , id + , lun + , ccb->arcmsr_cdb.DeviceStatus); + acb->devstate[id][lun] = ARECA_RAID_GONE; + ccb->pcmd->result = DID_NO_CONNECT << 16; + arcmsr_ccb_complete(ccb, 1); + break; + } + } + }/*drain reply FIFO*/ + } + if (!(outbound_intstatus & ARCMSR_MU_OUTBOUND_HANDLE_INT)) + return IRQ_NONE; + return IRQ_HANDLED; +} + +static void arcmsr_iop_parking(struct AdapterControlBlock *acb) +{ + if (acb) { + /* stop adapter background rebuild */ + if (acb->acb_flags & ACB_F_MSG_START_BGRB) { + acb->acb_flags &= ~ACB_F_MSG_START_BGRB; + arcmsr_stop_adapter_bgrb(acb); + arcmsr_flush_adapter_cache(acb); + } + } +} + +static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb, struct scsi_cmnd *cmd) +{ + struct MessageUnit __iomem *reg = acb->pmu; + struct CMD_MESSAGE_FIELD *pcmdmessagefld; + int retvalue = 0, transfer_len = 0; + char *buffer; + uint32_t controlcode = (uint32_t ) cmd->cmnd[5] << 24 | + (uint32_t ) cmd->cmnd[6] << 16 | + (uint32_t ) cmd->cmnd[7] << 8 | + (uint32_t ) cmd->cmnd[8]; + /* 4 bytes: Areca io control code */ + if (cmd->use_sg) { + struct scatterlist *sg = (struct scatterlist *)cmd->request_buffer; + + buffer = kmap_atomic(sg->page, KM_IRQ0) + sg->offset; + if (cmd->use_sg > 1) { + retvalue = ARCMSR_MESSAGE_FAIL; + goto message_out; + } + transfer_len += sg->length; + } else { + buffer = cmd->request_buffer; + transfer_len = cmd->request_bufflen; + } + if (transfer_len > sizeof(struct CMD_MESSAGE_FIELD)) { + retvalue = ARCMSR_MESSAGE_FAIL; + goto message_out; + } + pcmdmessagefld = (struct CMD_MESSAGE_FIELD *) buffer; + switch(controlcode) { + case ARCMSR_MESSAGE_READ_RQBUFFER: { + unsigned long *ver_addr; + dma_addr_t buf_handle; + uint8_t *pQbuffer, *ptmpQbuffer; + int32_t allxfer_len = 0; + + ver_addr = pci_alloc_consistent(acb->pdev, 1032, &buf_handle); + if (!ver_addr) { + retvalue = ARCMSR_MESSAGE_FAIL; + goto message_out; + } + ptmpQbuffer = (uint8_t *) ver_addr; + while ((acb->rqbuf_firstindex != acb->rqbuf_lastindex) + && (allxfer_len < 1031)) { + pQbuffer = &acb->rqbuffer[acb->rqbuf_firstindex]; + memcpy(ptmpQbuffer, pQbuffer, 1); + acb->rqbuf_firstindex++; + acb->rqbuf_firstindex %= ARCMSR_MAX_QBUFFER; + ptmpQbuffer++; + allxfer_len++; + } + if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) { + struct QBUFFER __iomem * prbuffer = (struct QBUFFER __iomem *) + ®->message_rbuffer; + uint8_t __iomem * iop_data = (uint8_t __iomem *)prbuffer->data; + int32_t iop_len; + + acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW; + iop_len = readl(&prbuffer->data_len); + while (iop_len > 0) { + acb->rqbuffer[acb->rqbuf_lastindex] = readb(iop_data); + acb->rqbuf_lastindex++; + acb->rqbuf_lastindex %= ARCMSR_MAX_QBUFFER; + iop_data++; + iop_len--; + } + writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK, + ®->inbound_doorbell); + } + memcpy(pcmdmessagefld->messagedatabuffer, + (uint8_t *)ver_addr, allxfer_len); + pcmdmessagefld->cmdmessage.Length = allxfer_len; + pcmdmessagefld->cmdmessage.ReturnCode = ARCMSR_MESSAGE_RETURNCODE_OK; + pci_free_consistent(acb->pdev, 1032, ver_addr, buf_handle); + } + break; + case ARCMSR_MESSAGE_WRITE_WQBUFFER: { + unsigned long *ver_addr; + dma_addr_t buf_handle; + int32_t my_empty_len, user_len, wqbuf_firstindex, wqbuf_lastindex; + uint8_t *pQbuffer, *ptmpuserbuffer; + + ver_addr = pci_alloc_consistent(acb->pdev, 1032, &buf_handle); + if (!ver_addr) { + retvalue = ARCMSR_MESSAGE_FAIL; + goto message_out; + } + ptmpuserbuffer = (uint8_t *)ver_addr; + user_len = pcmdmessagefld->cmdmessage.Length; + memcpy(ptmpuserbuffer, pcmdmessagefld->messagedatabuffer, user_len); + wqbuf_lastindex = acb->wqbuf_lastindex; + wqbuf_firstindex = acb->wqbuf_firstindex; + if (wqbuf_lastindex != wqbuf_firstindex) { + struct SENSE_DATA *sensebuffer = + (struct SENSE_DATA *)cmd->sense_buffer; + arcmsr_post_Qbuffer(acb); + /* has error report sensedata */ + sensebuffer->ErrorCode = 0x70; + sensebuffer->SenseKey = ILLEGAL_REQUEST; + sensebuffer->AdditionalSenseLength = 0x0A; + sensebuffer->AdditionalSenseCode = 0x20; + sensebuffer->Valid = 1; + retvalue = ARCMSR_MESSAGE_FAIL; + } else { + my_empty_len = (wqbuf_firstindex-wqbuf_lastindex - 1) + &(ARCMSR_MAX_QBUFFER - 1); + if (my_empty_len >= user_len) { + while (user_len > 0) { + pQbuffer = + &acb->wqbuffer[acb->wqbuf_lastindex]; + memcpy(pQbuffer, ptmpuserbuffer, 1); + acb->wqbuf_lastindex++; + acb->wqbuf_lastindex %= ARCMSR_MAX_QBUFFER; + ptmpuserbuffer++; + user_len--; + } + if (acb->acb_flags & ACB_F_MESSAGE_WQBUFFER_CLEARED) { + acb->acb_flags &= + ~ACB_F_MESSAGE_WQBUFFER_CLEARED; + arcmsr_post_Qbuffer(acb); + } + } else { + /* has error report sensedata */ + struct SENSE_DATA *sensebuffer = + (struct SENSE_DATA *)cmd->sense_buffer; + sensebuffer->ErrorCode = 0x70; + sensebuffer->SenseKey = ILLEGAL_REQUEST; + sensebuffer->AdditionalSenseLength = 0x0A; + sensebuffer->AdditionalSenseCode = 0x20; + sensebuffer->Valid = 1; + retvalue = ARCMSR_MESSAGE_FAIL; + } + } + pci_free_consistent(acb->pdev, 1032, ver_addr, buf_handle); + } + break; + case ARCMSR_MESSAGE_CLEAR_RQBUFFER: { + uint8_t *pQbuffer = acb->rqbuffer; + + if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) { + acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW; + writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK, + ®->inbound_doorbell); + } + acb->acb_flags |= ACB_F_MESSAGE_RQBUFFER_CLEARED; + acb->rqbuf_firstindex = 0; + acb->rqbuf_lastindex = 0; + memset(pQbuffer, 0, ARCMSR_MAX_QBUFFER); + pcmdmessagefld->cmdmessage.ReturnCode = + ARCMSR_MESSAGE_RETURNCODE_OK; + } + break; + case ARCMSR_MESSAGE_CLEAR_WQBUFFER: { + uint8_t *pQbuffer = acb->wqbuffer; + + if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) { + acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW; + writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK + , ®->inbound_doorbell); + } + acb->acb_flags |= + (ACB_F_MESSAGE_WQBUFFER_CLEARED | + ACB_F_MESSAGE_WQBUFFER_READED); + acb->wqbuf_firstindex = 0; + acb->wqbuf_lastindex = 0; + memset(pQbuffer, 0, ARCMSR_MAX_QBUFFER); + pcmdmessagefld->cmdmessage.ReturnCode = + ARCMSR_MESSAGE_RETURNCODE_OK; + } + break; + case ARCMSR_MESSAGE_CLEAR_ALLQBUFFER: { + uint8_t *pQbuffer; + + if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) { + acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW; + writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK + , ®->inbound_doorbell); + } + acb->acb_flags |= + (ACB_F_MESSAGE_WQBUFFER_CLEARED + | ACB_F_MESSAGE_RQBUFFER_CLEARED + | ACB_F_MESSAGE_WQBUFFER_READED); + acb->rqbuf_firstindex = 0; + acb->rqbuf_lastindex = 0; + acb->wqbuf_firstindex = 0; + acb->wqbuf_lastindex = 0; + pQbuffer = acb->rqbuffer; + memset(pQbuffer, 0, sizeof (struct QBUFFER)); + pQbuffer = acb->wqbuffer; + memset(pQbuffer, 0, sizeof (struct QBUFFER)); + pcmdmessagefld->cmdmessage.ReturnCode = ARCMSR_MESSAGE_RETURNCODE_OK; + } + break; + case ARCMSR_MESSAGE_RETURN_CODE_3F: { + pcmdmessagefld->cmdmessage.ReturnCode = ARCMSR_MESSAGE_RETURNCODE_3F; + } + break; + case ARCMSR_MESSAGE_SAY_HELLO: { + int8_t * hello_string = "Hello! I am ARCMSR"; + + memcpy(pcmdmessagefld->messagedatabuffer, hello_string + , (int16_t)strlen(hello_string)); + pcmdmessagefld->cmdmessage.ReturnCode = ARCMSR_MESSAGE_RETURNCODE_OK; + } + break; + case ARCMSR_MESSAGE_SAY_GOODBYE: + arcmsr_iop_parking(acb); + break; + case ARCMSR_MESSAGE_FLUSH_ADAPTER_CACHE: + arcmsr_flush_adapter_cache(acb); + break; + default: + retvalue = ARCMSR_MESSAGE_FAIL; + } + message_out: + if (cmd->use_sg) { + struct scatterlist *sg; + + sg = (struct scatterlist *) cmd->request_buffer; + kunmap_atomic(buffer - sg->offset, KM_IRQ0); + } + return retvalue; +} + +static struct CommandControlBlock *arcmsr_get_freeccb(struct AdapterControlBlock *acb) +{ + struct list_head *head = &acb->ccb_free_list; + struct CommandControlBlock *ccb = NULL; + + if (!list_empty(head)) { + ccb = list_entry(head->next, struct CommandControlBlock, list); + list_del(head->next); + } + return ccb; +} + +static void arcmsr_handle_virtual_command(struct AdapterControlBlock *acb, + struct scsi_cmnd *cmd) +{ + switch (cmd->cmnd[0]) { + case INQUIRY: { + unsigned char inqdata[36]; + char *buffer; + + if (cmd->device->lun) { + cmd->result = (DID_TIME_OUT << 16); + cmd->scsi_done(cmd); + return; + } + inqdata[0] = TYPE_PROCESSOR; + /* Periph Qualifier & Periph Dev Type */ + inqdata[1] = 0; + /* rem media bit & Dev Type Modifier */ + inqdata[2] = 0; + /* ISO,ECMA,& ANSI versions */ + inqdata[4] = 31; + /* length of additional data */ + strncpy(&inqdata[8], "Areca ", 8); + /* Vendor Identification */ + strncpy(&inqdata[16], "RAID controller ", 16); + /* Product Identification */ + strncpy(&inqdata[32], "R001", 4); /* Product Revision */ + if (cmd->use_sg) { + struct scatterlist *sg; + + sg = (struct scatterlist *) cmd->request_buffer; + buffer = kmap_atomic(sg->page, KM_IRQ0) + sg->offset; + } else { + buffer = cmd->request_buffer; + } + memcpy(buffer, inqdata, sizeof(inqdata)); + if (cmd->use_sg) { + struct scatterlist *sg; + + sg = (struct scatterlist *) cmd->request_buffer; + kunmap_atomic(buffer - sg->offset, KM_IRQ0); + } + cmd->scsi_done(cmd); + } + break; + case WRITE_BUFFER: + case READ_BUFFER: { + if (arcmsr_iop_message_xfer(acb, cmd)) + cmd->result = (DID_ERROR << 16); + cmd->scsi_done(cmd); + } + break; + default: + cmd->scsi_done(cmd); + } +} + +static int arcmsr_queue_command(struct scsi_cmnd *cmd, + void (* done)(struct scsi_cmnd *)) +{ + struct Scsi_Host *host = cmd->device->host; + struct AdapterControlBlock *acb = + (struct AdapterControlBlock *) host->hostdata; + struct CommandControlBlock *ccb; + int target = cmd->device->id; + int lun = cmd->device->lun; + + cmd->scsi_done = done; + cmd->host_scribble = NULL; + cmd->result = 0; + if (acb->acb_flags & ACB_F_BUS_RESET) { + printk(KERN_NOTICE "arcmsr%d: bus reset" + " and return busy \n" + , acb->host->host_no); + return SCSI_MLQUEUE_HOST_BUSY; + } + if(target == 16) { + /* virtual device for iop message transfer */ + arcmsr_handle_virtual_command(acb, cmd); + return 0; + } + if (acb->devstate[target][lun] == ARECA_RAID_GONE) { + uint8_t block_cmd; + + block_cmd = cmd->cmnd[0] & 0x0f; + if (block_cmd == 0x08 || block_cmd == 0x0a) { + printk(KERN_NOTICE + "arcmsr%d: block 'read/write'" + "command with gone raid volume" + " Cmd=%2x, TargetId=%d, Lun=%d \n" + , acb->host->host_no + , cmd->cmnd[0] + , target, lun); + cmd->result = (DID_NO_CONNECT << 16); + cmd->scsi_done(cmd); + return 0; + } + } + if (atomic_read(&acb->ccboutstandingcount) >= + ARCMSR_MAX_OUTSTANDING_CMD) + return SCSI_MLQUEUE_HOST_BUSY; + + ccb = arcmsr_get_freeccb(acb); + if (!ccb) + return SCSI_MLQUEUE_HOST_BUSY; + arcmsr_build_ccb(acb, ccb, cmd); + arcmsr_post_ccb(acb, ccb); + return 0; +} + +static void arcmsr_get_firmware_spec(struct AdapterControlBlock *acb) +{ + struct MessageUnit __iomem *reg = acb->pmu; + char *acb_firm_model = acb->firm_model; + char *acb_firm_version = acb->firm_version; + char __iomem *iop_firm_model = (char __iomem *) ®->message_rwbuffer[15]; + char __iomem *iop_firm_version = (char __iomem *) ®->message_rwbuffer[17]; + int count; + + writel(ARCMSR_INBOUND_MESG0_GET_CONFIG, ®->inbound_msgaddr0); + if (arcmsr_wait_msgint_ready(acb)) + printk(KERN_NOTICE + "arcmsr%d: wait " + "'get adapter firmware miscellaneous data' timeout \n" + , acb->host->host_no); + count = 8; + while (count) { + *acb_firm_model = readb(iop_firm_model); + acb_firm_model++; + iop_firm_model++; + count--; + } + count = 16; + while (count) { + *acb_firm_version = readb(iop_firm_version); + acb_firm_version++; + iop_firm_version++; + count--; + } + printk(KERN_INFO + "ARECA RAID ADAPTER%d: FIRMWARE VERSION %s \n" + , acb->host->host_no + , acb->firm_version); + acb->firm_request_len = readl(®->message_rwbuffer[1]); + acb->firm_numbers_queue = readl(®->message_rwbuffer[2]); + acb->firm_sdram_size = readl(®->message_rwbuffer[3]); + acb->firm_hd_channels = readl(®->message_rwbuffer[4]); +} + +static void arcmsr_polling_ccbdone(struct AdapterControlBlock *acb, + struct CommandControlBlock *poll_ccb) +{ + struct MessageUnit __iomem *reg = acb->pmu; + struct CommandControlBlock *ccb; + uint32_t flag_ccb, outbound_intstatus, poll_ccb_done = 0, poll_count = 0; + int id, lun; + + polling_ccb_retry: + poll_count++; + outbound_intstatus = readl(®->outbound_intstatus) + & acb->outbound_int_enable; + writel(outbound_intstatus, ®->outbound_intstatus);/*clear interrupt*/ + while (1) { + if ((flag_ccb = readl(®->outbound_queueport)) == 0xFFFFFFFF) { + if (poll_ccb_done) + break; + else { + msleep(25); + if (poll_count > 100) + break; + goto polling_ccb_retry; + } + } + ccb = (struct CommandControlBlock *) + (acb->vir2phy_offset + (flag_ccb << 5)); + if ((ccb->acb != acb) || + (ccb->startdone != ARCMSR_CCB_START)) { + if ((ccb->startdone == ARCMSR_CCB_ABORTED) || + (ccb == poll_ccb)) { + printk(KERN_NOTICE + "arcmsr%d: scsi id=%d lun=%d ccb='0x%p'" + " poll command abort successfully \n" + , acb->host->host_no + , ccb->pcmd->device->id + , ccb->pcmd->device->lun + , ccb); + ccb->pcmd->result = DID_ABORT << 16; + arcmsr_ccb_complete(ccb, 1); + poll_ccb_done = 1; + continue; + } + printk(KERN_NOTICE + "arcmsr%d: polling get an illegal ccb" + " command done ccb='0x%p'" + "ccboutstandingcount=%d \n" + , acb->host->host_no + , ccb + , atomic_read(&acb->ccboutstandingcount)); + continue; + } + id = ccb->pcmd->device->id; + lun = ccb->pcmd->device->lun; + if (!(flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR)) { + if (acb->devstate[id][lun] == ARECA_RAID_GONE) + acb->devstate[id][lun] = ARECA_RAID_GOOD; + ccb->pcmd->result = DID_OK << 16; + arcmsr_ccb_complete(ccb, 1); + } else { + switch(ccb->arcmsr_cdb.DeviceStatus) { + case ARCMSR_DEV_SELECT_TIMEOUT: { + acb->devstate[id][lun] = ARECA_RAID_GONE; + ccb->pcmd->result = DID_TIME_OUT << 16; + arcmsr_ccb_complete(ccb, 1); + } + break; + case ARCMSR_DEV_ABORTED: + case ARCMSR_DEV_INIT_FAIL: { + acb->devstate[id][lun] = ARECA_RAID_GONE; + ccb->pcmd->result = DID_BAD_TARGET << 16; + arcmsr_ccb_complete(ccb, 1); + } + break; + case ARCMSR_DEV_CHECK_CONDITION: { + acb->devstate[id][lun] = ARECA_RAID_GOOD; + arcmsr_report_sense_info(ccb); + arcmsr_ccb_complete(ccb, 1); + } + break; + default: + printk(KERN_NOTICE + "arcmsr%d: scsi id=%d lun=%d" + " polling and getting command error done" + "but got unknown DeviceStatus = 0x%x \n" + , acb->host->host_no + , id + , lun + , ccb->arcmsr_cdb.DeviceStatus); + acb->devstate[id][lun] = ARECA_RAID_GONE; + ccb->pcmd->result = DID_BAD_TARGET << 16; + arcmsr_ccb_complete(ccb, 1); + break; + } + } + } +} + +static void arcmsr_iop_init(struct AdapterControlBlock *acb) +{ + struct MessageUnit __iomem *reg = acb->pmu; + uint32_t intmask_org, mask, outbound_doorbell, firmware_state = 0; + + do { + firmware_state = readl(®->outbound_msgaddr1); + } while (!(firmware_state & ARCMSR_OUTBOUND_MESG1_FIRMWARE_OK)); + intmask_org = readl(®->outbound_intmask) + | ARCMSR_MU_OUTBOUND_MESSAGE0_INTMASKENABLE; + arcmsr_get_firmware_spec(acb); + + acb->acb_flags |= ACB_F_MSG_START_BGRB; + writel(ARCMSR_INBOUND_MESG0_START_BGRB, ®->inbound_msgaddr0); + if (arcmsr_wait_msgint_ready(acb)) { + printk(KERN_NOTICE "arcmsr%d: " + "wait 'start adapter background rebulid' timeout\n", + acb->host->host_no); + } + + outbound_doorbell = readl(®->outbound_doorbell); + writel(outbound_doorbell, ®->outbound_doorbell); + writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK, ®->inbound_doorbell); + mask = ~(ARCMSR_MU_OUTBOUND_POSTQUEUE_INTMASKENABLE + | ARCMSR_MU_OUTBOUND_DOORBELL_INTMASKENABLE); + writel(intmask_org & mask, ®->outbound_intmask); + acb->outbound_int_enable = ~(intmask_org & mask) & 0x000000ff; + acb->acb_flags |= ACB_F_IOP_INITED; +} + +static void arcmsr_iop_reset(struct AdapterControlBlock *acb) +{ + struct MessageUnit __iomem *reg = acb->pmu; + struct CommandControlBlock *ccb; + uint32_t intmask_org; + int i = 0; + + if (atomic_read(&acb->ccboutstandingcount) != 0) { + /* talk to iop 331 outstanding command aborted */ + arcmsr_abort_allcmd(acb); + /* wait for 3 sec for all command aborted*/ + msleep_interruptible(3000); + /* disable all outbound interrupt */ + intmask_org = arcmsr_disable_outbound_ints(acb); + /* clear all outbound posted Q */ + for (i = 0; i < ARCMSR_MAX_OUTSTANDING_CMD; i++) + readl(®->outbound_queueport); + for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) { + ccb = acb->pccb_pool[i]; + if ((ccb->startdone == ARCMSR_CCB_START) || + (ccb->startdone == ARCMSR_CCB_ABORTED)) { + ccb->startdone = ARCMSR_CCB_ABORTED; + ccb->pcmd->result = DID_ABORT << 16; + arcmsr_ccb_complete(ccb, 1); + } + } + /* enable all outbound interrupt */ + arcmsr_enable_outbound_ints(acb, intmask_org); + } + atomic_set(&acb->ccboutstandingcount, 0); +} + +static int arcmsr_bus_reset(struct scsi_cmnd *cmd) +{ + struct AdapterControlBlock *acb = + (struct AdapterControlBlock *)cmd->device->host->hostdata; + int i; + + acb->num_resets++; + acb->acb_flags |= ACB_F_BUS_RESET; + for (i = 0; i < 400; i++) { + if (!atomic_read(&acb->ccboutstandingcount)) + break; + arcmsr_interrupt(acb); + msleep(25); + } + arcmsr_iop_reset(acb); + acb->acb_flags &= ~ACB_F_BUS_RESET; + return SUCCESS; +} + +static void arcmsr_abort_one_cmd(struct AdapterControlBlock *acb, + struct CommandControlBlock *ccb) +{ + u32 intmask; + + ccb->startdone = ARCMSR_CCB_ABORTED; + + /* + ** Wait for 3 sec for all command done. + */ + msleep_interruptible(3000); + + intmask = arcmsr_disable_outbound_ints(acb); + arcmsr_polling_ccbdone(acb, ccb); + arcmsr_enable_outbound_ints(acb, intmask); +} + +static int arcmsr_abort(struct scsi_cmnd *cmd) +{ + struct AdapterControlBlock *acb = + (struct AdapterControlBlock *)cmd->device->host->hostdata; + int i = 0; + + printk(KERN_NOTICE + "arcmsr%d: abort device command of scsi id=%d lun=%d \n", + acb->host->host_no, cmd->device->id, cmd->device->lun); + acb->num_aborts++; + + /* + ************************************************ + ** the all interrupt service routine is locked + ** we need to handle it as soon as possible and exit + ************************************************ + */ + if (!atomic_read(&acb->ccboutstandingcount)) + return SUCCESS; + + for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) { + struct CommandControlBlock *ccb = acb->pccb_pool[i]; + if (ccb->startdone == ARCMSR_CCB_START && ccb->pcmd == cmd) { + arcmsr_abort_one_cmd(acb, ccb); + break; + } + } + + return SUCCESS; +} + +static const char *arcmsr_info(struct Scsi_Host *host) +{ + struct AdapterControlBlock *acb = + (struct AdapterControlBlock *) host->hostdata; + static char buf[256]; + char *type; + int raid6 = 1; + + switch (acb->pdev->device) { + case PCI_DEVICE_ID_ARECA_1110: + case PCI_DEVICE_ID_ARECA_1210: + raid6 = 0; + /*FALLTHRU*/ + case PCI_DEVICE_ID_ARECA_1120: + case PCI_DEVICE_ID_ARECA_1130: + case PCI_DEVICE_ID_ARECA_1160: + case PCI_DEVICE_ID_ARECA_1170: + case PCI_DEVICE_ID_ARECA_1220: + case PCI_DEVICE_ID_ARECA_1230: + case PCI_DEVICE_ID_ARECA_1260: + case PCI_DEVICE_ID_ARECA_1270: + case PCI_DEVICE_ID_ARECA_1280: + type = "SATA"; + break; + case PCI_DEVICE_ID_ARECA_1380: + case PCI_DEVICE_ID_ARECA_1381: + case PCI_DEVICE_ID_ARECA_1680: + case PCI_DEVICE_ID_ARECA_1681: + type = "SAS"; + break; + default: + type = "X-TYPE"; + break; + } + sprintf(buf, "Areca %s Host Adapter RAID Controller%s\n %s", + type, raid6 ? "( RAID6 capable)" : "", + ARCMSR_DRIVER_VERSION); + return buf; +} + + -- cgit v1.2.3 From 254950cd56fee220c9d548f3e57211b95976ba64 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 26 Jul 2006 15:59:25 +0900 Subject: [PATCH] ahci: relocate several internal functions * move ahci_port_start/stop() below EH functions. This makes ahci more consistent with other drivers and makes prototypes for ahci_start/stop_engine() unnecessary. * swap positions between ahci_start_engine() and ahci_stop_engine() for readability. Signed-off-by: Tejun Heo Signed-off-by: Zhao, Forrest Signed-off-by: Jeff Garzik --- drivers/scsi/ahci.c | 255 ++++++++++++++++++++++++++-------------------------- 1 file changed, 126 insertions(+), 129 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index f1516ca2c52..92e2b950eff 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -205,8 +205,6 @@ static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs * static void ahci_irq_clear(struct ata_port *ap); static int ahci_port_start(struct ata_port *ap); static void ahci_port_stop(struct ata_port *ap); -static int ahci_start_engine(void __iomem *port_mmio); -static int ahci_stop_engine(void __iomem *port_mmio); static void ahci_tf_read(struct ata_port *ap, struct ata_taskfile *tf); static void ahci_qc_prep(struct ata_queued_cmd *qc); static u8 ahci_check_status(struct ata_port *ap); @@ -374,108 +372,6 @@ static inline void __iomem *ahci_port_base (void __iomem *base, unsigned int por return (void __iomem *) ahci_port_base_ul((unsigned long)base, port); } -static int ahci_port_start(struct ata_port *ap) -{ - struct device *dev = ap->host_set->dev; - struct ahci_host_priv *hpriv = ap->host_set->private_data; - struct ahci_port_priv *pp; - void __iomem *mmio = ap->host_set->mmio_base; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - void *mem; - dma_addr_t mem_dma; - int rc; - - pp = kmalloc(sizeof(*pp), GFP_KERNEL); - if (!pp) - return -ENOMEM; - memset(pp, 0, sizeof(*pp)); - - rc = ata_pad_alloc(ap, dev); - if (rc) { - kfree(pp); - return rc; - } - - mem = dma_alloc_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, &mem_dma, GFP_KERNEL); - if (!mem) { - ata_pad_free(ap, dev); - kfree(pp); - return -ENOMEM; - } - memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ); - - /* - * First item in chunk of DMA memory: 32-slot command table, - * 32 bytes each in size - */ - pp->cmd_slot = mem; - pp->cmd_slot_dma = mem_dma; - - mem += AHCI_CMD_SLOT_SZ; - mem_dma += AHCI_CMD_SLOT_SZ; - - /* - * Second item: Received-FIS area - */ - pp->rx_fis = mem; - pp->rx_fis_dma = mem_dma; - - mem += AHCI_RX_FIS_SZ; - mem_dma += AHCI_RX_FIS_SZ; - - /* - * Third item: data area for storing a single command - * and its scatter-gather table - */ - pp->cmd_tbl = mem; - pp->cmd_tbl_dma = mem_dma; - - ap->private_data = pp; - - if (hpriv->cap & HOST_CAP_64) - writel((pp->cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI); - writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR); - readl(port_mmio + PORT_LST_ADDR); /* flush */ - - if (hpriv->cap & HOST_CAP_64) - writel((pp->rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI); - writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR); - readl(port_mmio + PORT_FIS_ADDR); /* flush */ - - writel(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX | - PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP | - PORT_CMD_START, port_mmio + PORT_CMD); - readl(port_mmio + PORT_CMD); /* flush */ - - return 0; -} - - -static void ahci_port_stop(struct ata_port *ap) -{ - struct device *dev = ap->host_set->dev; - struct ahci_port_priv *pp = ap->private_data; - void __iomem *mmio = ap->host_set->mmio_base; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - u32 tmp; - - tmp = readl(port_mmio + PORT_CMD); - tmp &= ~(PORT_CMD_START | PORT_CMD_FIS_RX); - writel(tmp, port_mmio + PORT_CMD); - readl(port_mmio + PORT_CMD); /* flush */ - - /* spec says 500 msecs for each PORT_CMD_{START,FIS_RX} bit, so - * this is slightly incorrect. - */ - msleep(500); - - ap->private_data = NULL; - dma_free_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, - pp->cmd_slot, pp->cmd_slot_dma); - ata_pad_free(ap, dev); - kfree(pp); -} - static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in) { unsigned int sc_reg; @@ -510,31 +406,6 @@ static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in, writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4)); } -static int ahci_stop_engine(void __iomem *port_mmio) -{ - u32 tmp; - - tmp = readl(port_mmio + PORT_CMD); - - /* Check if the HBA is idle */ - if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0) - return 0; - - /* Setting HBA to idle */ - tmp &= ~PORT_CMD_START; - writel(tmp, port_mmio + PORT_CMD); - - /* wait for engine to stop. This could be - * as long as 500 msec - */ - tmp = ata_wait_register(port_mmio + PORT_CMD, - PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500); - if(tmp & PORT_CMD_LIST_ON) - return -EIO; - - return 0; -} - static int ahci_start_engine(void __iomem *port_mmio) { u32 tmp; @@ -570,6 +441,31 @@ static int ahci_start_engine(void __iomem *port_mmio) return 0; } +static int ahci_stop_engine(void __iomem *port_mmio) +{ + u32 tmp; + + tmp = readl(port_mmio + PORT_CMD); + + /* Check if the HBA is idle */ + if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0) + return 0; + + /* Setting HBA to idle */ + tmp &= ~PORT_CMD_START; + writel(tmp, port_mmio + PORT_CMD); + + /* wait for engine to stop. This could be + * as long as 500 msec + */ + tmp = ata_wait_register(port_mmio + PORT_CMD, + PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500); + if(tmp & PORT_CMD_LIST_ON) + return -EIO; + + return 0; +} + static unsigned int ahci_dev_classify(struct ata_port *ap) { void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr; @@ -1109,6 +1005,107 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) } } +static int ahci_port_start(struct ata_port *ap) +{ + struct device *dev = ap->host_set->dev; + struct ahci_host_priv *hpriv = ap->host_set->private_data; + struct ahci_port_priv *pp; + void __iomem *mmio = ap->host_set->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + void *mem; + dma_addr_t mem_dma; + int rc; + + pp = kmalloc(sizeof(*pp), GFP_KERNEL); + if (!pp) + return -ENOMEM; + memset(pp, 0, sizeof(*pp)); + + rc = ata_pad_alloc(ap, dev); + if (rc) { + kfree(pp); + return rc; + } + + mem = dma_alloc_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, &mem_dma, GFP_KERNEL); + if (!mem) { + ata_pad_free(ap, dev); + kfree(pp); + return -ENOMEM; + } + memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ); + + /* + * First item in chunk of DMA memory: 32-slot command table, + * 32 bytes each in size + */ + pp->cmd_slot = mem; + pp->cmd_slot_dma = mem_dma; + + mem += AHCI_CMD_SLOT_SZ; + mem_dma += AHCI_CMD_SLOT_SZ; + + /* + * Second item: Received-FIS area + */ + pp->rx_fis = mem; + pp->rx_fis_dma = mem_dma; + + mem += AHCI_RX_FIS_SZ; + mem_dma += AHCI_RX_FIS_SZ; + + /* + * Third item: data area for storing a single command + * and its scatter-gather table + */ + pp->cmd_tbl = mem; + pp->cmd_tbl_dma = mem_dma; + + ap->private_data = pp; + + if (hpriv->cap & HOST_CAP_64) + writel((pp->cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI); + writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR); + readl(port_mmio + PORT_LST_ADDR); /* flush */ + + if (hpriv->cap & HOST_CAP_64) + writel((pp->rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI); + writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR); + readl(port_mmio + PORT_FIS_ADDR); /* flush */ + + writel(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX | + PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP | + PORT_CMD_START, port_mmio + PORT_CMD); + readl(port_mmio + PORT_CMD); /* flush */ + + return 0; +} + +static void ahci_port_stop(struct ata_port *ap) +{ + struct device *dev = ap->host_set->dev; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *mmio = ap->host_set->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + u32 tmp; + + tmp = readl(port_mmio + PORT_CMD); + tmp &= ~(PORT_CMD_START | PORT_CMD_FIS_RX); + writel(tmp, port_mmio + PORT_CMD); + readl(port_mmio + PORT_CMD); /* flush */ + + /* spec says 500 msecs for each PORT_CMD_{START,FIS_RX} bit, so + * this is slightly incorrect. + */ + msleep(500); + + ap->private_data = NULL; + dma_free_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, + pp->cmd_slot, pp->cmd_slot_dma); + ata_pad_free(ap, dev); + kfree(pp); +} + static void ahci_setup_port(struct ata_ioports *port, unsigned long base, unsigned int port_idx) { -- cgit v1.2.3 From d8fcd116d203dfe2f6c272d0cd67724b172f1bc2 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 26 Jul 2006 15:59:25 +0900 Subject: [PATCH] ahci: cosmetic changes to ahci_start/stop_engine() * fascist-format comments according to comment style used in libata core layer. * if() -> if () Signed-off-by: Tejun Heo Signed-off-by: Zhao, Forrest Signed-off-by: Jeff Garzik --- drivers/scsi/ahci.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index 92e2b950eff..ee00aed9bde 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -410,30 +410,23 @@ static int ahci_start_engine(void __iomem *port_mmio) { u32 tmp; - /* - * Get current status - */ + /* get current status */ tmp = readl(port_mmio + PORT_CMD); - /* - * AHCI rev 1.1 section 10.3.1: + /* AHCI rev 1.1 section 10.3.1: * Software shall not set PxCMD.ST to '1' until it verifies * that PxCMD.CR is '0' and has set PxCMD.FRE to '1' */ if ((tmp & PORT_CMD_FIS_RX) == 0) return -EPERM; - /* - * wait for engine to become idle. - */ + /* wait for engine to become idle */ tmp = ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1,500); - if(tmp & PORT_CMD_LIST_ON) + if (tmp & PORT_CMD_LIST_ON) return -EBUSY; - /* - * Start DMA - */ + /* start DMA */ tmp |= PORT_CMD_START; writel(tmp, port_mmio + PORT_CMD); readl(port_mmio + PORT_CMD); /* flush */ @@ -447,20 +440,18 @@ static int ahci_stop_engine(void __iomem *port_mmio) tmp = readl(port_mmio + PORT_CMD); - /* Check if the HBA is idle */ + /* check if the HBA is idle */ if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0) return 0; - /* Setting HBA to idle */ + /* setting HBA to idle */ tmp &= ~PORT_CMD_START; writel(tmp, port_mmio + PORT_CMD); - /* wait for engine to stop. This could be - * as long as 500 msec - */ + /* wait for engine to stop. This could be as long as 500 msec */ tmp = ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500); - if(tmp & PORT_CMD_LIST_ON) + if (tmp & PORT_CMD_LIST_ON) return -EIO; return 0; -- cgit v1.2.3 From 9f5920567bfabbd1be26112a31c44652b6587394 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 26 Jul 2006 15:59:26 +0900 Subject: [PATCH] ahci: simplify ahci_start_engine() Simplify ahci_start_engine() by killing prerequisite condition checks. Rationales are.. * No user checks error return from ahci_start_engine() * Code flow guarantees the prerequisite conditions unless the controller is malfunctioning. In such cases, the driver had chances to learn about the problem _before_ calling this function. * Closely related to the above two, driver calls into this function even when prerequisites fail hoping for the best. Basically, ahci_start_engine() should only do the operation itself. It isn't the right place to check for prerequisites. Signed-off-by: Tejun Heo Signed-off-by: Zhao, Forrest Signed-off-by: Jeff Garzik --- drivers/scsi/ahci.c | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index ee00aed9bde..e02b9c65287 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -406,32 +406,15 @@ static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in, writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4)); } -static int ahci_start_engine(void __iomem *port_mmio) +static void ahci_start_engine(void __iomem *port_mmio) { u32 tmp; - /* get current status */ - tmp = readl(port_mmio + PORT_CMD); - - /* AHCI rev 1.1 section 10.3.1: - * Software shall not set PxCMD.ST to '1' until it verifies - * that PxCMD.CR is '0' and has set PxCMD.FRE to '1' - */ - if ((tmp & PORT_CMD_FIS_RX) == 0) - return -EPERM; - - /* wait for engine to become idle */ - tmp = ata_wait_register(port_mmio + PORT_CMD, - PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1,500); - if (tmp & PORT_CMD_LIST_ON) - return -EBUSY; - /* start DMA */ + tmp = readl(port_mmio + PORT_CMD); tmp |= PORT_CMD_START; writel(tmp, port_mmio + PORT_CMD); readl(port_mmio + PORT_CMD); /* flush */ - - return 0; } static int ahci_stop_engine(void __iomem *port_mmio) -- cgit v1.2.3 From 0be0aa98985dfec42502c0d0af2a1baff9bdb19f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 26 Jul 2006 15:59:26 +0900 Subject: [PATCH] libata: improve driver initialization and deinitialization Implement ahci_[de]init_port() and use it during initialization and de-initialization. ahci_[de]init_port() are supersets of what used to be done during driver [de-]initialization. This patch makes the following behavior changes. * Per-port IRQ mask is cleared on driver load as done in other drivers. The mask will be configured properly during probe. * During init_one(), HOST_IRQ_STAT is cleared after masking port IRQs such that there is no race window. * CMD_SPIN_UP is cleared during init_one() instead of being set. It is set in port_start(). This is more consistent with overall structure of initialization. Note that CMD_SPIN_UP simply controls PHY activation. * Slumber and staggered spin-up are handled properly. * All init/deinit operations are done in step-by-step manner as described in the spec instead of issued as single merged command. Original implementation is from Zhao, Forrest Signed-off-by: Tejun Heo Signed-off-by: Zhao, Forrest Signed-off-by: Jeff Garzik --- drivers/scsi/ahci.c | 202 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 151 insertions(+), 51 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index e02b9c65287..fb71fa7bc5d 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -92,7 +92,9 @@ enum { HOST_AHCI_EN = (1 << 31), /* AHCI enabled */ /* HOST_CAP bits */ + HOST_CAP_SSC = (1 << 14), /* Slumber capable */ HOST_CAP_CLO = (1 << 24), /* Command List Override support */ + HOST_CAP_SSS = (1 << 27), /* Staggered Spin-up */ HOST_CAP_NCQ = (1 << 30), /* Native Command Queueing */ HOST_CAP_64 = (1 << 31), /* PCI DAC (64-bit DMA) support */ @@ -155,6 +157,7 @@ enum { PORT_CMD_SPIN_UP = (1 << 1), /* Spin up device */ PORT_CMD_START = (1 << 0), /* Enable port DMA engine */ + PORT_CMD_ICC_MASK = (0xf << 28), /* i/f ICC state mask */ PORT_CMD_ICC_ACTIVE = (0x1 << 28), /* Put i/f in active state */ PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */ PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */ @@ -440,6 +443,135 @@ static int ahci_stop_engine(void __iomem *port_mmio) return 0; } +static void ahci_start_fis_rx(void __iomem *port_mmio, u32 cap, + dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma) +{ + u32 tmp; + + /* set FIS registers */ + if (cap & HOST_CAP_64) + writel((cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI); + writel(cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR); + + if (cap & HOST_CAP_64) + writel((rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI); + writel(rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR); + + /* enable FIS reception */ + tmp = readl(port_mmio + PORT_CMD); + tmp |= PORT_CMD_FIS_RX; + writel(tmp, port_mmio + PORT_CMD); + + /* flush */ + readl(port_mmio + PORT_CMD); +} + +static int ahci_stop_fis_rx(void __iomem *port_mmio) +{ + u32 tmp; + + /* disable FIS reception */ + tmp = readl(port_mmio + PORT_CMD); + tmp &= ~PORT_CMD_FIS_RX; + writel(tmp, port_mmio + PORT_CMD); + + /* wait for completion, spec says 500ms, give it 1000 */ + tmp = ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_FIS_ON, + PORT_CMD_FIS_ON, 10, 1000); + if (tmp & PORT_CMD_FIS_ON) + return -EBUSY; + + return 0; +} + +static void ahci_power_up(void __iomem *port_mmio, u32 cap) +{ + u32 cmd; + + cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK; + + /* spin up device */ + if (cap & HOST_CAP_SSS) { + cmd |= PORT_CMD_SPIN_UP; + writel(cmd, port_mmio + PORT_CMD); + } + + /* wake up link */ + writel(cmd | PORT_CMD_ICC_ACTIVE, port_mmio + PORT_CMD); +} + +static void ahci_power_down(void __iomem *port_mmio, u32 cap) +{ + u32 cmd, scontrol; + + cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK; + + if (cap & HOST_CAP_SSC) { + /* enable transitions to slumber mode */ + scontrol = readl(port_mmio + PORT_SCR_CTL); + if ((scontrol & 0x0f00) > 0x100) { + scontrol &= ~0xf00; + writel(scontrol, port_mmio + PORT_SCR_CTL); + } + + /* put device into slumber mode */ + writel(cmd | PORT_CMD_ICC_SLUMBER, port_mmio + PORT_CMD); + + /* wait for the transition to complete */ + ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_ICC_SLUMBER, + PORT_CMD_ICC_SLUMBER, 1, 50); + } + + /* put device into listen mode */ + if (cap & HOST_CAP_SSS) { + /* first set PxSCTL.DET to 0 */ + scontrol = readl(port_mmio + PORT_SCR_CTL); + scontrol &= ~0xf; + writel(scontrol, port_mmio + PORT_SCR_CTL); + + /* then set PxCMD.SUD to 0 */ + cmd &= ~PORT_CMD_SPIN_UP; + writel(cmd, port_mmio + PORT_CMD); + } +} + +static void ahci_init_port(void __iomem *port_mmio, u32 cap, + dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma) +{ + /* power up */ + ahci_power_up(port_mmio, cap); + + /* enable FIS reception */ + ahci_start_fis_rx(port_mmio, cap, cmd_slot_dma, rx_fis_dma); + + /* enable DMA */ + ahci_start_engine(port_mmio); +} + +static int ahci_deinit_port(void __iomem *port_mmio, u32 cap, const char **emsg) +{ + int rc; + + /* disable DMA */ + rc = ahci_stop_engine(port_mmio); + if (rc) { + *emsg = "failed to stop engine"; + return rc; + } + + /* disable FIS reception */ + rc = ahci_stop_fis_rx(port_mmio); + if (rc) { + *emsg = "failed stop FIS RX"; + return rc; + } + + /* put device into slumber mode */ + ahci_power_down(port_mmio, cap); + + return 0; +} + static unsigned int ahci_dev_classify(struct ata_port *ap) { void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr; @@ -1037,20 +1169,8 @@ static int ahci_port_start(struct ata_port *ap) ap->private_data = pp; - if (hpriv->cap & HOST_CAP_64) - writel((pp->cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI); - writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR); - readl(port_mmio + PORT_LST_ADDR); /* flush */ - - if (hpriv->cap & HOST_CAP_64) - writel((pp->rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI); - writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR); - readl(port_mmio + PORT_FIS_ADDR); /* flush */ - - writel(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX | - PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP | - PORT_CMD_START, port_mmio + PORT_CMD); - readl(port_mmio + PORT_CMD); /* flush */ + /* initialize port */ + ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma); return 0; } @@ -1058,20 +1178,17 @@ static int ahci_port_start(struct ata_port *ap) static void ahci_port_stop(struct ata_port *ap) { struct device *dev = ap->host_set->dev; + struct ahci_host_priv *hpriv = ap->host_set->private_data; struct ahci_port_priv *pp = ap->private_data; void __iomem *mmio = ap->host_set->mmio_base; void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - u32 tmp; - - tmp = readl(port_mmio + PORT_CMD); - tmp &= ~(PORT_CMD_START | PORT_CMD_FIS_RX); - writel(tmp, port_mmio + PORT_CMD); - readl(port_mmio + PORT_CMD); /* flush */ + const char *emsg = NULL; + int rc; - /* spec says 500 msecs for each PORT_CMD_{START,FIS_RX} bit, so - * this is slightly incorrect. - */ - msleep(500); + /* de-initialize port */ + rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); + if (rc) + ata_port_printk(ap, KERN_WARNING, "%s (%d)\n", emsg, rc); ap->private_data = NULL; dma_free_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, @@ -1099,7 +1216,7 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) struct pci_dev *pdev = to_pci_dev(probe_ent->dev); void __iomem *mmio = probe_ent->mmio_base; u32 tmp, cap_save; - unsigned int i, j, using_dac; + unsigned int i, using_dac; int rc; void __iomem *port_mmio; @@ -1175,6 +1292,8 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) } for (i = 0; i < probe_ent->n_ports; i++) { + const char *emsg = NULL; + #if 0 /* BIOSen initialize this incorrectly */ if (!(hpriv->port_map & (1 << i))) continue; @@ -1187,43 +1306,24 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) (unsigned long) mmio, i); /* make sure port is not active */ - tmp = readl(port_mmio + PORT_CMD); - VPRINTK("PORT_CMD 0x%x\n", tmp); - if (tmp & (PORT_CMD_LIST_ON | PORT_CMD_FIS_ON | - PORT_CMD_FIS_RX | PORT_CMD_START)) { - tmp &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON | - PORT_CMD_FIS_RX | PORT_CMD_START); - writel(tmp, port_mmio + PORT_CMD); - readl(port_mmio + PORT_CMD); /* flush */ - - /* spec says 500 msecs for each bit, so - * this is slightly incorrect. - */ - msleep(500); - } - - writel(PORT_CMD_SPIN_UP, port_mmio + PORT_CMD); - - j = 0; - while (j < 100) { - msleep(10); - tmp = readl(port_mmio + PORT_SCR_STAT); - if ((tmp & 0xf) == 0x3) - break; - j++; - } + rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); + if (rc) + dev_printk(KERN_WARNING, &pdev->dev, + "%s (%d)\n", emsg, rc); + /* clear SError */ tmp = readl(port_mmio + PORT_SCR_ERR); VPRINTK("PORT_SCR_ERR 0x%x\n", tmp); writel(tmp, port_mmio + PORT_SCR_ERR); - /* ack any pending irq events for this port */ + /* clear & turn off port IRQ */ tmp = readl(port_mmio + PORT_IRQ_STAT); VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); if (tmp) writel(tmp, port_mmio + PORT_IRQ_STAT); writel(1 << i, mmio + HOST_IRQ_STAT); + writel(0, port_mmio + PORT_IRQ_MASK); } tmp = readl(mmio + HOST_CTL); -- cgit v1.2.3 From d91542c11f3981768367815cf087ad36e792ea4a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 26 Jul 2006 15:59:26 +0900 Subject: [PATCH] ahci: separate out ahci_reset_controller() and ahci_init_controller() Separate out ahci_reset_controller() and ahci_init_controller() from ata_host_init(). These will be used by PM callbacks. This patch doesn't introduce any behavior change. Signed-off-by: Tejun Heo Signed-off-by: Zhao, Forrest Signed-off-by: Jeff Garzik --- drivers/scsi/ahci.c | 171 +++++++++++++++++++++++++++++----------------------- 1 file changed, 94 insertions(+), 77 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index fb71fa7bc5d..a9e0c5f7909 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -572,6 +572,94 @@ static int ahci_deinit_port(void __iomem *port_mmio, u32 cap, const char **emsg) return 0; } +static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev) +{ + u32 cap_save, tmp; + + cap_save = readl(mmio + HOST_CAP); + cap_save &= ( (1<<28) | (1<<17) ); + cap_save |= (1 << 27); + + /* global controller reset */ + tmp = readl(mmio + HOST_CTL); + if ((tmp & HOST_RESET) == 0) { + writel(tmp | HOST_RESET, mmio + HOST_CTL); + readl(mmio + HOST_CTL); /* flush */ + } + + /* reset must complete within 1 second, or + * the hardware should be considered fried. + */ + ssleep(1); + + tmp = readl(mmio + HOST_CTL); + if (tmp & HOST_RESET) { + dev_printk(KERN_ERR, &pdev->dev, + "controller reset failed (0x%x)\n", tmp); + return -EIO; + } + + writel(HOST_AHCI_EN, mmio + HOST_CTL); + (void) readl(mmio + HOST_CTL); /* flush */ + writel(cap_save, mmio + HOST_CAP); + writel(0xf, mmio + HOST_PORTS_IMPL); + (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ + + if (pdev->vendor == PCI_VENDOR_ID_INTEL) { + u16 tmp16; + + /* configure PCS */ + pci_read_config_word(pdev, 0x92, &tmp16); + tmp16 |= 0xf; + pci_write_config_word(pdev, 0x92, tmp16); + } + + return 0; +} + +static void ahci_init_controller(void __iomem *mmio, struct pci_dev *pdev, + int n_ports, u32 cap) +{ + int i, rc; + u32 tmp; + + for (i = 0; i < n_ports; i++) { + void __iomem *port_mmio = ahci_port_base(mmio, i); + const char *emsg = NULL; + +#if 0 /* BIOSen initialize this incorrectly */ + if (!(hpriv->port_map & (1 << i))) + continue; +#endif + + /* make sure port is not active */ + rc = ahci_deinit_port(port_mmio, cap, &emsg); + if (rc) + dev_printk(KERN_WARNING, &pdev->dev, + "%s (%d)\n", emsg, rc); + + /* clear SError */ + tmp = readl(port_mmio + PORT_SCR_ERR); + VPRINTK("PORT_SCR_ERR 0x%x\n", tmp); + writel(tmp, port_mmio + PORT_SCR_ERR); + + /* clear & turn off port IRQ */ + tmp = readl(port_mmio + PORT_IRQ_STAT); + VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); + if (tmp) + writel(tmp, port_mmio + PORT_IRQ_STAT); + + writel(1 << i, mmio + HOST_IRQ_STAT); + writel(0, port_mmio + PORT_IRQ_MASK); + } + + tmp = readl(mmio + HOST_CTL); + VPRINTK("HOST_CTL 0x%x\n", tmp); + writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL); + tmp = readl(mmio + HOST_CTL); + VPRINTK("HOST_CTL 0x%x\n", tmp); +} + static unsigned int ahci_dev_classify(struct ata_port *ap) { void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr; @@ -1215,47 +1303,12 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) struct ahci_host_priv *hpriv = probe_ent->private_data; struct pci_dev *pdev = to_pci_dev(probe_ent->dev); void __iomem *mmio = probe_ent->mmio_base; - u32 tmp, cap_save; unsigned int i, using_dac; int rc; - void __iomem *port_mmio; - - cap_save = readl(mmio + HOST_CAP); - cap_save &= ( (1<<28) | (1<<17) ); - cap_save |= (1 << 27); - - /* global controller reset */ - tmp = readl(mmio + HOST_CTL); - if ((tmp & HOST_RESET) == 0) { - writel(tmp | HOST_RESET, mmio + HOST_CTL); - readl(mmio + HOST_CTL); /* flush */ - } - - /* reset must complete within 1 second, or - * the hardware should be considered fried. - */ - ssleep(1); - - tmp = readl(mmio + HOST_CTL); - if (tmp & HOST_RESET) { - dev_printk(KERN_ERR, &pdev->dev, - "controller reset failed (0x%x)\n", tmp); - return -EIO; - } - writel(HOST_AHCI_EN, mmio + HOST_CTL); - (void) readl(mmio + HOST_CTL); /* flush */ - writel(cap_save, mmio + HOST_CAP); - writel(0xf, mmio + HOST_PORTS_IMPL); - (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ - - if (pdev->vendor == PCI_VENDOR_ID_INTEL) { - u16 tmp16; - - pci_read_config_word(pdev, 0x92, &tmp16); - tmp16 |= 0xf; - pci_write_config_word(pdev, 0x92, tmp16); - } + rc = ahci_reset_controller(mmio, pdev); + if (rc) + return rc; hpriv->cap = readl(mmio + HOST_CAP); hpriv->port_map = readl(mmio + HOST_PORTS_IMPL); @@ -1291,46 +1344,10 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) } } - for (i = 0; i < probe_ent->n_ports; i++) { - const char *emsg = NULL; - -#if 0 /* BIOSen initialize this incorrectly */ - if (!(hpriv->port_map & (1 << i))) - continue; -#endif + for (i = 0; i < probe_ent->n_ports; i++) + ahci_setup_port(&probe_ent->port[i], (unsigned long) mmio, i); - port_mmio = ahci_port_base(mmio, i); - VPRINTK("mmio %p port_mmio %p\n", mmio, port_mmio); - - ahci_setup_port(&probe_ent->port[i], - (unsigned long) mmio, i); - - /* make sure port is not active */ - rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); - if (rc) - dev_printk(KERN_WARNING, &pdev->dev, - "%s (%d)\n", emsg, rc); - - /* clear SError */ - tmp = readl(port_mmio + PORT_SCR_ERR); - VPRINTK("PORT_SCR_ERR 0x%x\n", tmp); - writel(tmp, port_mmio + PORT_SCR_ERR); - - /* clear & turn off port IRQ */ - tmp = readl(port_mmio + PORT_IRQ_STAT); - VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); - if (tmp) - writel(tmp, port_mmio + PORT_IRQ_STAT); - - writel(1 << i, mmio + HOST_IRQ_STAT); - writel(0, port_mmio + PORT_IRQ_MASK); - } - - tmp = readl(mmio + HOST_CTL); - VPRINTK("HOST_CTL 0x%x\n", tmp); - writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL); - tmp = readl(mmio + HOST_CTL); - VPRINTK("HOST_CTL 0x%x\n", tmp); + ahci_init_controller(mmio, pdev, probe_ent->n_ports, hpriv->cap); pci_set_master(pdev); -- cgit v1.2.3 From c1332875cbe0c148c7f200d4f9b36b64e34d9872 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 26 Jul 2006 15:59:26 +0900 Subject: [PATCH] ahci: implement Power Management support Implement power management support. Original implementation is from Zhao, Forrest Signed-off-by: Tejun Heo Signed-off-by: Zhao, Forrest Signed-off-by: Jeff Garzik --- drivers/scsi/ahci.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index a9e0c5f7909..909a4dc79d0 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -215,6 +215,10 @@ static void ahci_freeze(struct ata_port *ap); static void ahci_thaw(struct ata_port *ap); static void ahci_error_handler(struct ata_port *ap); static void ahci_post_internal_cmd(struct ata_queued_cmd *qc); +static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg); +static int ahci_port_resume(struct ata_port *ap); +static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); +static int ahci_pci_device_resume(struct pci_dev *pdev); static void ahci_remove_one (struct pci_dev *pdev); static struct scsi_host_template ahci_sht = { @@ -234,6 +238,8 @@ static struct scsi_host_template ahci_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, + .suspend = ata_scsi_device_suspend, + .resume = ata_scsi_device_resume, }; static const struct ata_port_operations ahci_ops = { @@ -260,6 +266,9 @@ static const struct ata_port_operations ahci_ops = { .error_handler = ahci_error_handler, .post_internal_cmd = ahci_post_internal_cmd, + .port_suspend = ahci_port_suspend, + .port_resume = ahci_port_resume, + .port_start = ahci_port_start, .port_stop = ahci_port_stop, }; @@ -361,6 +370,8 @@ static struct pci_driver ahci_pci_driver = { .name = DRV_NAME, .id_table = ahci_pci_tbl, .probe = ahci_init_one, + .suspend = ahci_pci_device_suspend, + .resume = ahci_pci_device_resume, .remove = ahci_remove_one, }; @@ -1199,6 +1210,79 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) } } +static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) +{ + struct ahci_host_priv *hpriv = ap->host_set->private_data; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *mmio = ap->host_set->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + const char *emsg = NULL; + int rc; + + rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); + if (rc) { + ata_port_printk(ap, KERN_ERR, "%s (%d)\n", emsg, rc); + ahci_init_port(port_mmio, hpriv->cap, + pp->cmd_slot_dma, pp->rx_fis_dma); + } + + return rc; +} + +static int ahci_port_resume(struct ata_port *ap) +{ + struct ahci_port_priv *pp = ap->private_data; + struct ahci_host_priv *hpriv = ap->host_set->private_data; + void __iomem *mmio = ap->host_set->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + + ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma); + + return 0; +} + +static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) +{ + struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev); + void __iomem *mmio = host_set->mmio_base; + u32 ctl; + + if (mesg.event == PM_EVENT_SUSPEND) { + /* AHCI spec rev1.1 section 8.3.3: + * Software must disable interrupts prior to requesting a + * transition of the HBA to D3 state. + */ + ctl = readl(mmio + HOST_CTL); + ctl &= ~HOST_IRQ_EN; + writel(ctl, mmio + HOST_CTL); + readl(mmio + HOST_CTL); /* flush */ + } + + return ata_pci_device_suspend(pdev, mesg); +} + +static int ahci_pci_device_resume(struct pci_dev *pdev) +{ + struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev); + struct ahci_host_priv *hpriv = host_set->private_data; + void __iomem *mmio = host_set->mmio_base; + int rc; + + ata_pci_device_do_resume(pdev); + + if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { + rc = ahci_reset_controller(mmio, pdev); + if (rc) + return rc; + + ahci_init_controller(mmio, pdev, host_set->n_ports, hpriv->cap); + } + + ata_host_set_resume(host_set); + + return 0; +} + static int ahci_port_start(struct ata_port *ap) { struct device *dev = ap->host_set->dev; -- cgit v1.2.3 From 3c5100c1c40cc5e27b4da4a736994c76d93392a0 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 26 Jul 2006 16:58:33 +0900 Subject: [PATCH] libata: cosmetic changes to PM functions Unify pm_message_t argument to the new-style @mesg. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/scsi/libata-core.c | 12 ++++++------ drivers/scsi/libata-scsi.c | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 386e5f21e19..66feebdb01c 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -5767,11 +5767,11 @@ int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits) return (tmp == bits->val) ? 1 : 0; } -void ata_pci_device_do_suspend(struct pci_dev *pdev, pm_message_t state) +void ata_pci_device_do_suspend(struct pci_dev *pdev, pm_message_t mesg) { pci_save_state(pdev); - if (state.event == PM_EVENT_SUSPEND) { + if (mesg.event == PM_EVENT_SUSPEND) { pci_disable_device(pdev); pci_set_power_state(pdev, PCI_D3hot); } @@ -5785,24 +5785,24 @@ void ata_pci_device_do_resume(struct pci_dev *pdev) pci_set_master(pdev); } -int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state) +int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) { struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev); int rc = 0; - rc = ata_host_set_suspend(host_set, state); + rc = ata_host_set_suspend(host_set, mesg); if (rc) return rc; if (host_set->next) { - rc = ata_host_set_suspend(host_set->next, state); + rc = ata_host_set_suspend(host_set->next, mesg); if (rc) { ata_host_set_resume(host_set); return rc; } } - ata_pci_device_do_suspend(pdev, state); + ata_pci_device_do_suspend(pdev, mesg); return 0; } diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c index 7ced41ecde8..1638e57028c 100644 --- a/drivers/scsi/libata-scsi.c +++ b/drivers/scsi/libata-scsi.c @@ -400,7 +400,7 @@ void ata_dump_status(unsigned id, struct ata_taskfile *tf) /** * ata_scsi_device_suspend - suspend ATA device associated with sdev * @sdev: the SCSI device to suspend - * @state: target power management state + * @mesg: target power management message * * Request suspend EH action on the ATA device associated with * @sdev and wait for the operation to complete. @@ -411,7 +411,7 @@ void ata_dump_status(unsigned id, struct ata_taskfile *tf) * RETURNS: * 0 on success, -errno otherwise. */ -int ata_scsi_device_suspend(struct scsi_device *sdev, pm_message_t state) +int ata_scsi_device_suspend(struct scsi_device *sdev, pm_message_t mesg) { struct ata_port *ap = ata_shost_to_port(sdev->host); struct ata_device *dev = ata_scsi_find_dev(ap, sdev); @@ -438,7 +438,7 @@ int ata_scsi_device_suspend(struct scsi_device *sdev, pm_message_t state) /* request suspend */ action = ATA_EH_SUSPEND; - if (state.event != PM_EVENT_SUSPEND) + if (mesg.event != PM_EVENT_SUSPEND) action |= ATA_EH_PM_FREEZE; ap->eh_info.dev_action[dev->devno] |= action; ap->eh_info.flags |= ATA_EHI_QUIET; @@ -463,7 +463,7 @@ int ata_scsi_device_suspend(struct scsi_device *sdev, pm_message_t state) spin_unlock_irqrestore(ap->lock, flags); out: if (rc == 0) - sdev->sdev_gendev.power.power_state = state; + sdev->sdev_gendev.power.power_state = mesg; return rc; } -- cgit v1.2.3 From 95916edd02e3a7752315422f386c55723d4a3637 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Sat, 29 Jul 2006 04:10:14 -0400 Subject: [libata] ahci: add SiS PCI IDs Signed-off-by: David Wang Signed-off-by: Jeff Garzik --- drivers/scsi/ahci.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index 909a4dc79d0..35058f9296d 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -362,6 +362,14 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VENDOR_ID_NVIDIA, 0x044f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, board_ahci }, /* MCP65 */ + /* SiS */ + { PCI_VENDOR_ID_SI, 0x1184, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* SiS 966 */ + { PCI_VENDOR_ID_SI, 0x1185, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* SiS 966 */ + { PCI_VENDOR_ID_SI, 0x0186, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* SiS 968 */ + { } /* terminate list */ }; -- cgit v1.2.3 From 294ef16a2ee34d0d94aa63616f7552d3bc66c982 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Wed, 12 Jul 2006 15:40:51 +1000 Subject: [POWERPC] scsi: Constify & voidify get_property() Now that get_property() returns a void *, there's no need to cast its return value. Also, treat the return value as const, so we can constify get_property later. powerpc-specific scsi driver changes. Signed-off-by: Jeremy Kerr Signed-off-by: Paul Mackerras --- drivers/scsi/ibmvscsi/rpa_vscsi.c | 11 ++++------- drivers/scsi/mac53c94.c | 2 +- drivers/scsi/mesh.c | 5 +++-- 3 files changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ibmvscsi/rpa_vscsi.c b/drivers/scsi/ibmvscsi/rpa_vscsi.c index 242b8873b33..cafef9cbbe2 100644 --- a/drivers/scsi/ibmvscsi/rpa_vscsi.c +++ b/drivers/scsi/ibmvscsi/rpa_vscsi.c @@ -156,8 +156,8 @@ static void gather_partition_info(void) { struct device_node *rootdn; - char *ppartition_name; - unsigned int *p_number_ptr; + const char *ppartition_name; + const unsigned int *p_number_ptr; /* Retrieve information about this partition */ rootdn = find_path_device("/"); @@ -165,14 +165,11 @@ static void gather_partition_info(void) return; } - ppartition_name = - get_property(rootdn, "ibm,partition-name", NULL); + ppartition_name = get_property(rootdn, "ibm,partition-name", NULL); if (ppartition_name) strncpy(partition_name, ppartition_name, sizeof(partition_name)); - p_number_ptr = - (unsigned int *)get_property(rootdn, "ibm,partition-no", - NULL); + p_number_ptr = get_property(rootdn, "ibm,partition-no", NULL); if (p_number_ptr) partition_number = *p_number_ptr; } diff --git a/drivers/scsi/mac53c94.c b/drivers/scsi/mac53c94.c index 93edaa8696c..c77f6f2581f 100644 --- a/drivers/scsi/mac53c94.c +++ b/drivers/scsi/mac53c94.c @@ -431,7 +431,7 @@ static int mac53c94_probe(struct macio_dev *mdev, const struct of_device_id *mat struct fsc_state *state; struct Scsi_Host *host; void *dma_cmd_space; - unsigned char *clkprop; + const unsigned char *clkprop; int proplen, rc = -ENODEV; if (macio_resource_count(mdev) != 2 || macio_irq_count(mdev) != 2) { diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c index c88717727be..cee9758b927 100644 --- a/drivers/scsi/mesh.c +++ b/drivers/scsi/mesh.c @@ -1850,7 +1850,8 @@ static int mesh_probe(struct macio_dev *mdev, const struct of_device_id *match) { struct device_node *mesh = macio_get_of_node(mdev); struct pci_dev* pdev = macio_get_pci_dev(mdev); - int tgt, *cfp, minper; + int tgt, minper; + const int *cfp; struct mesh_state *ms; struct Scsi_Host *mesh_host; void *dma_cmd_space; @@ -1939,7 +1940,7 @@ static int mesh_probe(struct macio_dev *mdev, const struct of_device_id *match) ms->tgts[tgt].current_req = NULL; } - if ((cfp = (int *) get_property(mesh, "clock-frequency", NULL))) + if ((cfp = get_property(mesh, "clock-frequency", NULL))) ms->clk_freq = *cfp; else { printk(KERN_INFO "mesh: assuming 50MHz clock frequency\n"); -- cgit v1.2.3 From af5f92d881d783b47d1f993ddffa2bce8b2993fe Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Wed, 12 Jul 2006 15:41:41 +1000 Subject: [POWERPC] sata_svw: Constify & voidify get_property() Now that get_property() returns a void *, there's no need to cast its return value. Also, treat the return value as const, so we can constify get_property later. sata_svw changes Signed-off-by: Jeremy Kerr Signed-off-by: Paul Mackerras --- drivers/scsi/sata_svw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sata_svw.c b/drivers/scsi/sata_svw.c index 7d0858095e1..6b70c3c76df 100644 --- a/drivers/scsi/sata_svw.c +++ b/drivers/scsi/sata_svw.c @@ -268,7 +268,7 @@ static int k2_sata_proc_info(struct Scsi_Host *shost, char *page, char **start, /* Match it to a port node */ index = (ap == ap->host_set->ports[0]) ? 0 : 1; for (np = np->child; np != NULL; np = np->sibling) { - u32 *reg = (u32 *)get_property(np, "reg", NULL); + const u32 *reg = get_property(np, "reg", NULL); if (!reg) continue; if (index == *reg) -- cgit v1.2.3 From 43d6b68dc38867e489995e21649bb82f6ee7b5d3 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 29 Jul 2006 11:14:08 -0700 Subject: [SCSI] areca sysfs fix Remove sysfs_remove_bin_file() return-value checking from the areca driver. There's nothing a driver can do if sysfs file removal fails, so we'll soon be changing sysfs_remove_bin_file() to internally print a diagnostic and to return void. Cc: Erich Chen Signed-off-by: Andrew Morton Signed-off-by: James Bottomley --- drivers/scsi/arcmsr/arcmsr_attr.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/arcmsr/arcmsr_attr.c b/drivers/scsi/arcmsr/arcmsr_attr.c index 0459f4194d7..c96f7140cb6 100644 --- a/drivers/scsi/arcmsr/arcmsr_attr.c +++ b/drivers/scsi/arcmsr/arcmsr_attr.c @@ -240,15 +240,11 @@ int arcmsr_alloc_sysfs_attr(struct AdapterControlBlock *acb) } return 0; error_bin_file_message_clear: - error = sysfs_remove_bin_file(&host->shost_classdev.kobj, + sysfs_remove_bin_file(&host->shost_classdev.kobj, &arcmsr_sysfs_message_write_attr); - if (error) - printk(KERN_ERR "arcmsr: sysfs_remove_bin_file mu_write failed\n"); error_bin_file_message_write: - error = sysfs_remove_bin_file(&host->shost_classdev.kobj, + sysfs_remove_bin_file(&host->shost_classdev.kobj, &arcmsr_sysfs_message_read_attr); - if (error) - printk(KERN_ERR "arcmsr: sysfs_remove_bin_file mu_read failed\n"); error_bin_file_message_read: return error; } @@ -256,20 +252,13 @@ error_bin_file_message_read: void arcmsr_free_sysfs_attr(struct AdapterControlBlock *acb) { struct Scsi_Host *host = acb->host; - int error; - error = sysfs_remove_bin_file(&host->shost_classdev.kobj, + sysfs_remove_bin_file(&host->shost_classdev.kobj, &arcmsr_sysfs_message_clear_attr); - if (error) - printk(KERN_ERR "arcmsr: free sysfs mu_clear failed\n"); - error = sysfs_remove_bin_file(&host->shost_classdev.kobj, + sysfs_remove_bin_file(&host->shost_classdev.kobj, &arcmsr_sysfs_message_write_attr); - if (error) - printk(KERN_ERR "arcmsr: free sysfs mu_write failed\n"); - error = sysfs_remove_bin_file(&host->shost_classdev.kobj, + sysfs_remove_bin_file(&host->shost_classdev.kobj, &arcmsr_sysfs_message_read_attr); - if (error) - printk(KERN_ERR "arcmsr: free sysfss mu_read failed\n"); } -- cgit v1.2.3 From d67a70aca200f67be42428e74eb3353f20ad1130 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 28 Jul 2006 17:36:46 -0500 Subject: [SCSI] arcmsr: fix up sysfs values The sysfs files in arcmsr are non-standard in that they aren't simple filename value pairs, the values actually contain preceeding text which would have to be parsed. The idea of sysfs files is that the file name is the description and the contents is a simple value. Fix up arcmsr to conform to this standard. Acked-By: Erich Chen Signed-off-by: James Bottomley --- drivers/scsi/arcmsr/arcmsr_attr.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/arcmsr/arcmsr_attr.c b/drivers/scsi/arcmsr/arcmsr_attr.c index c96f7140cb6..12497da5529 100644 --- a/drivers/scsi/arcmsr/arcmsr_attr.c +++ b/drivers/scsi/arcmsr/arcmsr_attr.c @@ -265,7 +265,7 @@ arcmsr_free_sysfs_attr(struct AdapterControlBlock *acb) { static ssize_t arcmsr_attr_host_driver_version(struct class_device *cdev, char *buf) { return snprintf(buf, PAGE_SIZE, - "ARCMSR: %s\n", + "%s\n", ARCMSR_DRIVER_VERSION); } @@ -274,7 +274,7 @@ arcmsr_attr_host_driver_posted_cmd(struct class_device *cdev, char *buf) { struct Scsi_Host *host = class_to_shost(cdev); struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; return snprintf(buf, PAGE_SIZE, - "Current commands posted: %4d\n", + "%4d\n", atomic_read(&acb->ccboutstandingcount)); } @@ -283,7 +283,7 @@ arcmsr_attr_host_driver_reset(struct class_device *cdev, char *buf) { struct Scsi_Host *host = class_to_shost(cdev); struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; return snprintf(buf, PAGE_SIZE, - "SCSI Host Resets: %4d\n", + "%4d\n", acb->num_resets); } @@ -292,7 +292,7 @@ arcmsr_attr_host_driver_abort(struct class_device *cdev, char *buf) { struct Scsi_Host *host = class_to_shost(cdev); struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; return snprintf(buf, PAGE_SIZE, - "SCSI Aborts/Timeouts: %4d\n", + "%4d\n", acb->num_aborts); } @@ -301,7 +301,7 @@ arcmsr_attr_host_fw_model(struct class_device *cdev, char *buf) { struct Scsi_Host *host = class_to_shost(cdev); struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; return snprintf(buf, PAGE_SIZE, - "Adapter Model: %s\n", + "%s\n", acb->firm_model); } @@ -311,7 +311,7 @@ arcmsr_attr_host_fw_version(struct class_device *cdev, char *buf) { struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; return snprintf(buf, PAGE_SIZE, - "Firmware Version: %s\n", + "%s\n", acb->firm_version); } @@ -321,7 +321,7 @@ arcmsr_attr_host_fw_request_len(struct class_device *cdev, char *buf) { struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; return snprintf(buf, PAGE_SIZE, - "Reguest Lenth: %4d\n", + "%4d\n", acb->firm_request_len); } @@ -331,7 +331,7 @@ arcmsr_attr_host_fw_numbers_queue(struct class_device *cdev, char *buf) { struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; return snprintf(buf, PAGE_SIZE, - "Numbers of Queue: %4d\n", + "%4d\n", acb->firm_numbers_queue); } @@ -341,7 +341,7 @@ arcmsr_attr_host_fw_sdram_size(struct class_device *cdev, char *buf) { struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; return snprintf(buf, PAGE_SIZE, - "SDRAM Size: %4d\n", + "%4d\n", acb->firm_sdram_size); } @@ -351,7 +351,7 @@ arcmsr_attr_host_fw_hd_channels(struct class_device *cdev, char *buf) { struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; return snprintf(buf, PAGE_SIZE, - "Hard Disk Channels: %4d\n", + "%4d\n", acb->firm_hd_channels); } -- cgit v1.2.3 From 2672ea86be26353108a72a28910df4dc61cdb5e2 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Wed, 2 Aug 2006 17:11:49 -0400 Subject: [SCSI] advansys pci tweaks. Remove a lot of duplicate #defines from the advansys driver, and make them look like PCI IDs as defined elsewhere in the kernel. Also add a module table so that it automatically gets picked up by tools relying on modinfo output (like say, distro installers). Signed-off-by: Dave Jones Signed-off-by: James Bottomley --- drivers/scsi/advansys.c | 90 +++++++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 41 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c index e32b4ab2f8f..773f02e3b10 100644 --- a/drivers/scsi/advansys.c +++ b/drivers/scsi/advansys.c @@ -888,10 +888,6 @@ typedef unsigned char uchar; #define ASC_PCI_ID2DEV(id) (((id) >> 11) & 0x1F) #define ASC_PCI_ID2FUNC(id) (((id) >> 8) & 0x7) #define ASC_PCI_MKID(bus, dev, func) ((((dev) & 0x1F) << 11) | (((func) & 0x7) << 8) | ((bus) & 0xFF)) -#define ASC_PCI_VENDORID 0x10CD -#define ASC_PCI_DEVICEID_1200A 0x1100 -#define ASC_PCI_DEVICEID_1200B 0x1200 -#define ASC_PCI_DEVICEID_ULTRA 0x1300 #define ASC_PCI_REVISION_3150 0x02 #define ASC_PCI_REVISION_3050 0x03 @@ -899,6 +895,14 @@ typedef unsigned char uchar; #define ASC_DVCLIB_CALL_FAILED (0) #define ASC_DVCLIB_CALL_ERROR (-1) +#define PCI_VENDOR_ID_ASP 0x10cd +#define PCI_DEVICE_ID_ASP_1200A 0x1100 +#define PCI_DEVICE_ID_ASP_ABP940 0x1200 +#define PCI_DEVICE_ID_ASP_ABP940U 0x1300 +#define PCI_DEVICE_ID_ASP_ABP940UW 0x2300 +#define PCI_DEVICE_ID_38C0800_REV1 0x2500 +#define PCI_DEVICE_ID_38C1600_REV1 0x2700 + /* * Enable CC_VERY_LONG_SG_LIST to support up to 64K element SG lists. * The SRB structure will have to be changed and the ASC_SRB2SCSIQ() @@ -1492,8 +1496,6 @@ typedef struct asc_dvc_cfg { #define ASC_INIT_STATE_END_INQUIRY 0x0080 #define ASC_INIT_RESET_SCSI_DONE 0x0100 #define ASC_INIT_STATE_WITHOUT_EEP 0x8000 -#define ASC_PCI_DEVICE_ID_REV_A 0x1100 -#define ASC_PCI_DEVICE_ID_REV_B 0x1200 #define ASC_BUG_FIX_IF_NOT_DWB 0x0001 #define ASC_BUG_FIX_ASYN_USE_SYN 0x0002 #define ASYN_SDTR_DATA_FIX_PCI_REV_AB 0x41 @@ -2100,12 +2102,6 @@ STATIC ASC_DCNT AscGetMaxDmaCount(ushort); #define ADV_NUM_PAGE_CROSSING \ ((ADV_SG_TOTAL_MEM_SIZE + (ADV_PAGE_SIZE - 1))/ADV_PAGE_SIZE) -/* a_condor.h */ -#define ADV_PCI_VENDOR_ID 0x10CD -#define ADV_PCI_DEVICE_ID_REV_A 0x2300 -#define ADV_PCI_DEVID_38C0800_REV1 0x2500 -#define ADV_PCI_DEVID_38C1600_REV1 0x2700 - #define ADV_EEP_DVC_CFG_BEGIN (0x00) #define ADV_EEP_DVC_CFG_END (0x15) #define ADV_EEP_DVC_CTL_BEGIN (0x16) /* location of OEM name */ @@ -3569,14 +3565,7 @@ typedef struct scsi_cmnd REQ, *REQP; #define PCI_MAX_SLOT 0x1F #define PCI_MAX_BUS 0xFF #define PCI_IOADDRESS_MASK 0xFFFE -#define ASC_PCI_VENDORID 0x10CD #define ASC_PCI_DEVICE_ID_CNT 6 /* PCI Device ID count. */ -#define ASC_PCI_DEVICE_ID_1100 0x1100 -#define ASC_PCI_DEVICE_ID_1200 0x1200 -#define ASC_PCI_DEVICE_ID_1300 0x1300 -#define ASC_PCI_DEVICE_ID_2300 0x2300 /* ASC-3550 */ -#define ASC_PCI_DEVICE_ID_2500 0x2500 /* ASC-38C0800 */ -#define ASC_PCI_DEVICE_ID_2700 0x2700 /* ASC-38C1600 */ #ifndef ADVANSYS_STATS #define ASC_STATS(shp, counter) @@ -4330,12 +4319,12 @@ advansys_detect(struct scsi_host_template *tpnt) struct pci_dev *pci_devp = NULL; int pci_device_id_cnt = 0; unsigned int pci_device_id[ASC_PCI_DEVICE_ID_CNT] = { - ASC_PCI_DEVICE_ID_1100, - ASC_PCI_DEVICE_ID_1200, - ASC_PCI_DEVICE_ID_1300, - ASC_PCI_DEVICE_ID_2300, - ASC_PCI_DEVICE_ID_2500, - ASC_PCI_DEVICE_ID_2700 + PCI_DEVICE_ID_ASP_1200A, + PCI_DEVICE_ID_ASP_ABP940, + PCI_DEVICE_ID_ASP_ABP940U, + PCI_DEVICE_ID_ASP_ABP940UW, + PCI_DEVICE_ID_38C0800_REV1, + PCI_DEVICE_ID_38C1600_REV1 }; ADV_PADDR pci_memory_address; #endif /* CONFIG_PCI */ @@ -4471,7 +4460,7 @@ advansys_detect(struct scsi_host_template *tpnt) /* Find all PCI cards. */ while (pci_device_id_cnt < ASC_PCI_DEVICE_ID_CNT) { - if ((pci_devp = pci_find_device(ASC_PCI_VENDORID, + if ((pci_devp = pci_find_device(PCI_VENDOR_ID_ASP, pci_device_id[pci_device_id_cnt], pci_devp)) == NULL) { pci_device_id_cnt++; @@ -4575,9 +4564,9 @@ advansys_detect(struct scsi_host_template *tpnt) */ #ifdef CONFIG_PCI if (asc_bus[bus] == ASC_IS_PCI && - (pci_devp->device == ASC_PCI_DEVICE_ID_2300 || - pci_devp->device == ASC_PCI_DEVICE_ID_2500 || - pci_devp->device == ASC_PCI_DEVICE_ID_2700)) + (pci_devp->device == PCI_DEVICE_ID_ASP_ABP940UW || + pci_devp->device == PCI_DEVICE_ID_38C0800_REV1 || + pci_devp->device == PCI_DEVICE_ID_38C1600_REV1)) { boardp->flags |= ASC_IS_WIDE_BOARD; } @@ -4600,11 +4589,11 @@ advansys_detect(struct scsi_host_template *tpnt) adv_dvc_varp->isr_callback = adv_isr_callback; adv_dvc_varp->async_callback = adv_async_callback; #ifdef CONFIG_PCI - if (pci_devp->device == ASC_PCI_DEVICE_ID_2300) + if (pci_devp->device == PCI_DEVICE_ID_ASP_ABP940UW) { ASC_DBG(1, "advansys_detect: ASC-3550\n"); adv_dvc_varp->chip_type = ADV_CHIP_ASC3550; - } else if (pci_devp->device == ASC_PCI_DEVICE_ID_2500) + } else if (pci_devp->device == PCI_DEVICE_ID_38C0800_REV1) { ASC_DBG(1, "advansys_detect: ASC-38C0800\n"); adv_dvc_varp->chip_type = ADV_CHIP_ASC38C0800; @@ -11922,7 +11911,7 @@ AscInitGetConfig( PCIRevisionID = DvcReadPCIConfigByte(asc_dvc, AscPCIConfigRevisionIDRegister); - if (PCIVendorID != ASC_PCI_VENDORID) { + if (PCIVendorID != PCI_VENDOR_ID_ASP) { warn_code |= ASC_WARN_SET_PCI_CONFIG_SPACE; } prevCmdRegBits = DvcReadPCIConfigByte(asc_dvc, @@ -11942,15 +11931,15 @@ AscInitGetConfig( warn_code |= ASC_WARN_SET_PCI_CONFIG_SPACE; } } - if ((PCIDeviceID == ASC_PCI_DEVICEID_1200A) || - (PCIDeviceID == ASC_PCI_DEVICEID_1200B)) { + if ((PCIDeviceID == PCI_DEVICE_ID_ASP_1200A) || + (PCIDeviceID == PCI_DEVICE_ID_ASP_ABP940)) { DvcWritePCIConfigByte(asc_dvc, AscPCIConfigLatencyTimer, 0x00); if (DvcReadPCIConfigByte(asc_dvc, AscPCIConfigLatencyTimer) != 0x00) { warn_code |= ASC_WARN_SET_PCI_CONFIG_SPACE; } - } else if (PCIDeviceID == ASC_PCI_DEVICEID_ULTRA) { + } else if (PCIDeviceID == PCI_DEVICE_ID_ASP_ABP940U) { if (DvcReadPCIConfigByte(asc_dvc, AscPCIConfigLatencyTimer) < 0x20) { DvcWritePCIConfigByte(asc_dvc, @@ -12037,8 +12026,8 @@ AscInitFromAscDvcVar( AscSetChipCfgMsw(iop_base, cfg_msw); if ((asc_dvc->bus_type & ASC_IS_PCI_ULTRA) == ASC_IS_PCI_ULTRA) { } else { - if ((pci_device_id == ASC_PCI_DEVICE_ID_REV_A) || - (pci_device_id == ASC_PCI_DEVICE_ID_REV_B)) { + if ((pci_device_id == PCI_DEVICE_ID_ASP_1200A) || + (pci_device_id == PCI_DEVICE_ID_ASP_ABP940)) { asc_dvc->bug_fix_cntl |= ASC_BUG_FIX_IF_NOT_DWB; asc_dvc->bug_fix_cntl |= ASC_BUG_FIX_ASYN_USE_SYN; } @@ -14275,8 +14264,8 @@ Default_38C0800_EEPROM_Config __initdata = { 0, /* 55 reserved */ 0, /* 56 cisptr_lsw */ 0, /* 57 cisprt_msw */ - ADV_PCI_VENDOR_ID, /* 58 subsysvid */ - ADV_PCI_DEVID_38C0800_REV1, /* 59 subsysid */ + PCI_VENDOR_ID_ASP, /* 58 subsysvid */ + PCI_DEVICE_ID_38C0800_REV1, /* 59 subsysid */ 0, /* 60 reserved */ 0, /* 61 reserved */ 0, /* 62 reserved */ @@ -14405,8 +14394,8 @@ Default_38C1600_EEPROM_Config __initdata = { 0, /* 55 reserved */ 0, /* 56 cisptr_lsw */ 0, /* 57 cisprt_msw */ - ADV_PCI_VENDOR_ID, /* 58 subsysvid */ - ADV_PCI_DEVID_38C1600_REV1, /* 59 subsysid */ + PCI_VENDOR_ID_ASP, /* 58 subsysvid */ + PCI_DEVICE_ID_38C1600_REV1, /* 59 subsysid */ 0, /* 60 reserved */ 0, /* 61 reserved */ 0, /* 62 reserved */ @@ -18225,3 +18214,22 @@ AdvInquiryHandling( } } MODULE_LICENSE("Dual BSD/GPL"); + +/* PCI Devices supported by this driver */ +static struct pci_device_id advansys_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_ASP, PCI_DEVICE_ID_ASP_1200A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_ASP, PCI_DEVICE_ID_ASP_ABP940, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_ASP, PCI_DEVICE_ID_ASP_ABP940U, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_ASP, PCI_DEVICE_ID_ASP_ABP940UW, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_ASP, PCI_DEVICE_ID_38C0800_REV1, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_ASP, PCI_DEVICE_ID_38C1600_REV1, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { } +}; +MODULE_DEVICE_TABLE(pci, advansys_pci_tbl); + -- cgit v1.2.3 From dd7e2f2266acf66ec882baa6fbd79f853b5fe966 Mon Sep 17 00:00:00 2001 From: Michael Reed Date: Fri, 4 Aug 2006 12:09:24 -0500 Subject: [SCSI] scsi_queue_work() documented return value is incorrect If you examine the queue_work() routine you'll see that it returns 1 on success, 0 if the work is already queued. This patch corrects the source code documentation for the scsi_queue_work function. Signed-off-by: Michael Reed Signed-off-by: James Bottomley --- drivers/scsi/hosts.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index dfcb96f3e60..f244d4f6597 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -487,7 +487,9 @@ EXPORT_SYMBOL(scsi_is_host_device); * @work: Work to queue for execution. * * Return value: - * 0 on success / != 0 for error + * 1 - work queued for execution + * 0 - work is already queued + * -EINVAL - work queue doesn't exist **/ int scsi_queue_work(struct Scsi_Host *shost, struct work_struct *work) { -- cgit v1.2.3 From b5145d25f0d8eae21ad7969822f2d4ce7f22e72a Mon Sep 17 00:00:00 2001 From: Brian King Date: Wed, 2 Aug 2006 14:57:36 -0500 Subject: [SCSI] ipr: Add some hardware defined types for SATA Add some hardware defined types for SATA. This is required by future patches to add SATA support to ipr. Signed-off-by: Brian King Signed-off-by: James Bottomley --- drivers/scsi/ipr.h | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 75 insertions(+), 3 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index 1ad24df69d7..1e9c1227fda 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -45,6 +45,7 @@ * This can be adjusted at runtime through sysfs device attributes. */ #define IPR_MAX_CMD_PER_LUN 6 +#define IPR_MAX_CMD_PER_ATA_LUN 1 /* * IPR_NUM_BASE_CMD_BLKS: This defines the maximum number of @@ -106,7 +107,7 @@ #define IPR_IOA_BUS 0xff #define IPR_IOA_TARGET 0xff #define IPR_IOA_LUN 0xff -#define IPR_MAX_NUM_BUSES 8 +#define IPR_MAX_NUM_BUSES 16 #define IPR_MAX_BUS_TO_SCAN IPR_MAX_NUM_BUSES #define IPR_NUM_RESET_RELOAD_RETRIES 3 @@ -145,6 +146,7 @@ #define IPR_LUN_RESET 0x40 #define IPR_TARGET_RESET 0x20 #define IPR_BUS_RESET 0x10 +#define IPR_ATA_PHY_RESET 0x80 #define IPR_ID_HOST_RR_Q 0xC4 #define IPR_QUERY_IOA_CONFIG 0xC5 #define IPR_CANCEL_ALL_REQUESTS 0xCE @@ -295,7 +297,11 @@ struct ipr_std_inq_data { }__attribute__ ((packed)); struct ipr_config_table_entry { - u8 service_level; + u8 proto; +#define IPR_PROTO_SATA 0x02 +#define IPR_PROTO_SATA_ATAPI 0x03 +#define IPR_PROTO_SAS_STP 0x06 +#define IPR_PROTO_SAS_STP_ATAPI 0x07 u8 array_id; u8 flags; #define IPR_IS_IOA_RESOURCE 0x80 @@ -307,6 +313,7 @@ struct ipr_config_table_entry { #define IPR_SUBTYPE_AF_DASD 0 #define IPR_SUBTYPE_GENERIC_SCSI 1 #define IPR_SUBTYPE_VOLUME_SET 2 +#define IPR_SUBTYPE_GENERIC_ATA 4 #define IPR_QUEUEING_MODEL(res) ((((res)->cfgte.flags) & 0x70) >> 4) #define IPR_QUEUE_FROZEN_MODEL 0 @@ -350,6 +357,7 @@ struct ipr_cmd_pkt { #define IPR_RQTYPE_SCSICDB 0x00 #define IPR_RQTYPE_IOACMD 0x01 #define IPR_RQTYPE_HCAM 0x02 +#define IPR_RQTYPE_ATA_PASSTHRU 0x04 u8 luntar_luntrn; @@ -373,6 +381,37 @@ struct ipr_cmd_pkt { __be16 timeout; }__attribute__ ((packed, aligned(4))); +struct ipr_ioarcb_ata_regs { + u8 flags; +#define IPR_ATA_FLAG_PACKET_CMD 0x80 +#define IPR_ATA_FLAG_XFER_TYPE_DMA 0x40 +#define IPR_ATA_FLAG_STATUS_ON_GOOD_COMPLETION 0x20 + u8 reserved[3]; + + __be16 data; + u8 feature; + u8 nsect; + u8 lbal; + u8 lbam; + u8 lbah; + u8 device; + u8 command; + u8 reserved2[3]; + u8 hob_feature; + u8 hob_nsect; + u8 hob_lbal; + u8 hob_lbam; + u8 hob_lbah; + u8 ctl; +}__attribute__ ((packed, aligned(4))); + +struct ipr_ioarcb_add_data { + union { + struct ipr_ioarcb_ata_regs regs; + __be32 add_cmd_parms[10]; + }u; +}__attribute__ ((packed, aligned(4))); + /* IOA Request Control Block 128 bytes */ struct ipr_ioarcb { __be32 ioarcb_host_pci_addr; @@ -397,7 +436,7 @@ struct ipr_ioarcb { struct ipr_cmd_pkt cmd_pkt; __be32 add_cmd_parms_len; - __be32 add_cmd_parms[10]; + struct ipr_ioarcb_add_data add_data; }__attribute__((packed, aligned (4))); struct ipr_ioadl_desc { @@ -433,6 +472,21 @@ struct ipr_ioasa_gpdd { __be32 ioa_data[2]; }__attribute__((packed, aligned (4))); +struct ipr_ioasa_gata { + u8 error; + u8 nsect; /* Interrupt reason */ + u8 lbal; + u8 lbam; + u8 lbah; + u8 device; + u8 status; + u8 alt_status; /* ATA CTL */ + u8 hob_nsect; + u8 hob_lbal; + u8 hob_lbam; + u8 hob_lbah; +}__attribute__((packed, aligned (4))); + struct ipr_auto_sense { __be16 auto_sense_len; __be16 ioa_data_len; @@ -466,6 +520,7 @@ struct ipr_ioasa { __be32 ioasc_specific; /* status code specific field */ #define IPR_ADDITIONAL_STATUS_FMT 0x80000000 #define IPR_AUTOSENSE_VALID 0x40000000 +#define IPR_ATA_DEVICE_WAS_RESET 0x20000000 #define IPR_IOASC_SPECIFIC_MASK 0x00ffffff #define IPR_FIELD_POINTER_VALID (0x80000000 >> 8) #define IPR_FIELD_POINTER_MASK 0x0000ffff @@ -474,6 +529,7 @@ struct ipr_ioasa { struct ipr_ioasa_vset vset; struct ipr_ioasa_af_dasd dasd; struct ipr_ioasa_gpdd gpdd; + struct ipr_ioasa_gata gata; } u; struct ipr_auto_sense auto_sense; @@ -1307,6 +1363,22 @@ static inline int ipr_is_scsi_disk(struct ipr_resource_entry *res) return 0; } +/** + * ipr_is_gata - Determine if a resource is a generic ATA resource + * @res: resource entry struct + * + * Return value: + * 1 if GATA / 0 if not GATA + **/ +static inline int ipr_is_gata(struct ipr_resource_entry *res) +{ + if (!ipr_is_ioa_resource(res) && + IPR_RES_SUBTYPE(res) == IPR_SUBTYPE_GENERIC_ATA) + return 1; + else + return 0; +} + /** * ipr_is_naca_model - Determine if a resource is using NACA queueing model * @res: resource entry struct -- cgit v1.2.3 From 896bbd21408ddbfb9a57819404dbb04f4f0afb35 Mon Sep 17 00:00:00 2001 From: Brian King Date: Wed, 2 Aug 2006 14:57:44 -0500 Subject: [SCSI] ipr: Handle new SAS error codes Add definitions for some SAS error codes that can be logged by ipr SAS adapters. Signed-off-by: Brian King Signed-off-by: James Bottomley --- drivers/scsi/ipr.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 01080b3acf5..7f2c5cfc57b 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -175,6 +175,8 @@ struct ipr_error_table_t ipr_error_table[] = { "Qualified success"}, {0x01080000, 1, 1, "FFFE: Soft device bus error recovered by the IOA"}, + {0x01088100, 0, 1, + "4101: Soft device bus fabric error"}, {0x01170600, 0, 1, "FFF9: Device sector reassign successful"}, {0x01170900, 0, 1, @@ -225,6 +227,8 @@ struct ipr_error_table_t ipr_error_table[] = { "3109: IOA timed out a device command"}, {0x04088000, 0, 0, "3120: SCSI bus is not operational"}, + {0x04088100, 0, 1, + "4100: Hard device bus fabric error"}, {0x04118000, 0, 1, "9000: IOA reserved area data check"}, {0x04118100, 0, 1, @@ -273,6 +277,14 @@ struct ipr_error_table_t ipr_error_table[] = { "9091: Incorrect hardware configuration change has been detected"}, {0x04678000, 0, 1, "9073: Invalid multi-adapter configuration"}, + {0x04678100, 0, 1, + "4010: Incorrect connection between cascaded expanders"}, + {0x04678200, 0, 1, + "4020: Connections exceed IOA design limits"}, + {0x04678300, 0, 1, + "4030: Incorrect multipath connection"}, + {0x04679000, 0, 1, + "4110: Unsupported enclosure function"}, {0x046E0000, 0, 1, "FFF4: Command to logical unit failed"}, {0x05240000, 1, 0, @@ -297,6 +309,8 @@ struct ipr_error_table_t ipr_error_table[] = { "9031: Array protection temporarily suspended, protection resuming"}, {0x06040600, 0, 1, "9040: Array protection temporarily suspended, protection resuming"}, + {0x06288000, 0, 1, + "3140: Device bus not ready to ready transition"}, {0x06290000, 0, 1, "FFFB: SCSI bus was reset"}, {0x06290500, 0, 0, @@ -319,6 +333,16 @@ struct ipr_error_table_t ipr_error_table[] = { "3150: SCSI bus configuration error"}, {0x06678100, 0, 1, "9074: Asymmetric advanced function disk configuration"}, + {0x06678300, 0, 1, + "4040: Incomplete multipath connection between IOA and enclosure"}, + {0x06678400, 0, 1, + "4041: Incomplete multipath connection between enclosure and device"}, + {0x06678500, 0, 1, + "9075: Incomplete multipath connection between IOA and remote IOA"}, + {0x06678600, 0, 1, + "9076: Configuration error, missing remote IOA"}, + {0x06679100, 0, 1, + "4050: Enclosure does not support a required multipath function"}, {0x06690200, 0, 1, "9041: Array protection temporarily suspended"}, {0x06698200, 0, 1, @@ -331,6 +355,10 @@ struct ipr_error_table_t ipr_error_table[] = { "9072: Link not operational transition"}, {0x066B8200, 0, 1, "9032: Array exposed but still protected"}, + {0x066B9100, 0, 1, + "4061: Multipath redundancy level got better"}, + {0x066B9200, 0, 1, + "4060: Multipath redundancy level got worse"}, {0x07270000, 0, 0, "Failure due to other device"}, {0x07278000, 0, 1, -- cgit v1.2.3 From 5b7304fbfb74bfca6f7d5a88b28197e3f7f2743b Mon Sep 17 00:00:00 2001 From: Brian King Date: Wed, 2 Aug 2006 14:57:51 -0500 Subject: [SCSI] ipr: Properly handle IOA recovered errors The ipr driver currently translates adapter recovered errors to DID_ERROR. This patch fixes this to translate these errors to success instead. Signed-off-by: Brian King Signed-off-by: James Bottomley --- drivers/scsi/ipr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 7f2c5cfc57b..55c0156e36b 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -4218,7 +4218,8 @@ static void ipr_erp_start(struct ipr_ioa_cfg *ioa_cfg, case IPR_IOASC_NR_INIT_CMD_REQUIRED: break; default: - scsi_cmd->result |= (DID_ERROR << 16); + if (IPR_IOASC_SENSE_KEY(ioasc) > RECOVERED_ERROR) + scsi_cmd->result |= (DID_ERROR << 16); if (!ipr_is_vset_device(res) && !ipr_is_naca_model(res)) res->needs_sync_complete = 1; break; -- cgit v1.2.3 From 117d2ce1cea25fc94302ff418ccef644cd3e59af Mon Sep 17 00:00:00 2001 From: Brian King Date: Wed, 2 Aug 2006 14:57:58 -0500 Subject: [SCSI] ipr: Auto sense handling fix Fix up a logic error in the checking for valid sense data. Signed-off-by: Brian King Signed-off-by: James Bottomley --- drivers/scsi/ipr.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 55c0156e36b..7ed4eef8347 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -4127,8 +4127,7 @@ static int ipr_get_autosense(struct ipr_cmnd *ipr_cmd) { struct ipr_ioasa *ioasa = &ipr_cmd->ioasa; - if ((be32_to_cpu(ioasa->ioasc_specific) & - (IPR_ADDITIONAL_STATUS_FMT | IPR_AUTOSENSE_VALID)) == 0) + if ((be32_to_cpu(ioasa->ioasc_specific) & IPR_AUTOSENSE_VALID) == 0) return 0; memcpy(ipr_cmd->scsi_cmd->sense_buffer, ioasa->auto_sense.data, -- cgit v1.2.3 From 008cd5bbfb4763322837cd1f7c621f02ebe22fef Mon Sep 17 00:00:00 2001 From: Brian King Date: Wed, 2 Aug 2006 14:58:04 -0500 Subject: [SCSI] ipr: Bump driver version to 2.1.4 Bump the ipr driver version to 2.1.4. Signed-off-by: Brian King Signed-off-by: James Bottomley --- drivers/scsi/ipr.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index 1e9c1227fda..11eaff52432 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -36,8 +36,8 @@ /* * Literals */ -#define IPR_DRIVER_VERSION "2.1.3" -#define IPR_DRIVER_DATE "(March 29, 2006)" +#define IPR_DRIVER_VERSION "2.1.4" +#define IPR_DRIVER_DATE "(August 2, 2006)" /* * IPR_MAX_CMD_PER_LUN: This defines the maximum number of outstanding -- cgit v1.2.3 From 4ff36718ede26ee2da73f2dae94d71e2b06845fc Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 4 Jul 2006 12:15:20 -0600 Subject: [SCSI] Improve inquiry printing - Replace scsi_device_types array API with scsi_device_type function API. Gets rid of a lot of common code, as well as being easier to use. - Add the new device types in SPC4 r05a, and rename some of the older ones. - Reformat the printing of inquiry data; now fits on one line and includes PQ. I think I've addressed all the feedback from the previous versions. My current test box prints: scsi 2:0:1:0: Direct access HP 18.2G ATLAS10K3_18_SCA HP05 PQ: 0 ANSI: 2 Signed-off-by: Matthew Wilcox Signed-off-by: James Bottomley --- drivers/scsi/fcal.c | 3 +-- drivers/scsi/g_NCR5380.c | 3 +-- drivers/scsi/megaraid.c | 4 +-- drivers/scsi/scsi.c | 36 ++++++++++++++++++-------- drivers/scsi/scsi_proc.c | 4 +-- drivers/scsi/scsi_scan.c | 66 ++++++------------------------------------------ 6 files changed, 38 insertions(+), 78 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/fcal.c b/drivers/scsi/fcal.c index 7f891023aa1..c4e16c0775d 100644 --- a/drivers/scsi/fcal.c +++ b/drivers/scsi/fcal.c @@ -248,8 +248,7 @@ int fcal_proc_info (struct Scsi_Host *host, char *buffer, char **start, off_t of if (scd->id == target) { SPRINTF (" [AL-PA: %02x, Id: %02d, Port WWN: %08x%08x, Node WWN: %08x%08x] ", alpa, target, u1[0], u1[1], u2[0], u2[1]); - SPRINTF ("%s ", (scd->type < MAX_SCSI_DEVICE_CODE) ? - scsi_device_types[(short) scd->type] : "Unknown device"); + SPRINTF ("%s ", scsi_device_type(scd->type)); for (j = 0; (j < 8) && (scd->vendor[j] >= 0x20); j++) SPRINTF ("%c", scd->vendor[j]); diff --git a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c index 67f1100f310..cdd893bb4e2 100644 --- a/drivers/scsi/g_NCR5380.c +++ b/drivers/scsi/g_NCR5380.c @@ -811,7 +811,6 @@ static int generic_NCR5380_proc_info(struct Scsi_Host *scsi_ptr, char *buffer, c struct NCR5380_hostdata *hostdata; #ifdef NCR5380_STATS struct scsi_device *dev; - extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE]; #endif NCR5380_setup(scsi_ptr); @@ -851,7 +850,7 @@ static int generic_NCR5380_proc_info(struct Scsi_Host *scsi_ptr, char *buffer, c long tr = hostdata->time_read[dev->id] / HZ; long tw = hostdata->time_write[dev->id] / HZ; - PRINTP(" T:%d %s " ANDP dev->id ANDP(dev->type < MAX_SCSI_DEVICE_CODE) ? scsi_device_types[(int) dev->type] : "Unknown"); + PRINTP(" T:%d %s " ANDP dev->id ANDP scsi_device_type(dev->type)); for (i = 0; i < 8; i++) if (dev->vendor[i] >= 0x20) *(buffer + (len++)) = dev->vendor[i]; diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index 76edbb639d3..ccb0055ac73 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -2822,9 +2822,7 @@ mega_print_inquiry(char *page, char *scsi_inq) i = scsi_inq[0] & 0x1f; - len += sprintf(page+len, " Type: %s ", - i < MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] : - "Unknown "); + len += sprintf(page+len, " Type: %s ", scsi_device_type(i)); len += sprintf(page+len, " ANSI SCSI revision: %02x", scsi_inq[2] & 0x07); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index b332caddd5b..94df671d776 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -96,24 +96,40 @@ unsigned int scsi_logging_level; EXPORT_SYMBOL(scsi_logging_level); #endif -const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] = { - "Direct-Access ", - "Sequential-Access", +static const char *const scsi_device_types[] = { + "Direct access ", + "Sequential access", "Printer ", "Processor ", "WORM ", - "CD-ROM ", + "CD/DVD ", "Scanner ", - "Optical Device ", - "Medium Changer ", + "Optical memory ", + "Media changer ", "Communications ", - "Unknown ", - "Unknown ", + "ASC IT8 ", + "ASC IT8 ", "RAID ", "Enclosure ", - "Direct-Access-RBC", + "Direct access RBC", + "Optical card ", + "Bridge controller", + "Object storage ", + "Automation/Drive ", }; -EXPORT_SYMBOL(scsi_device_types); + +const char * scsi_device_type(unsigned type) +{ + if (type == 0x1e) + return "Well-known LUN "; + if (type == 0x1f) + return "No Device "; + if (type > ARRAY_SIZE(scsi_device_types)) + return "Unknown "; + return scsi_device_types[type]; +} + +EXPORT_SYMBOL(scsi_device_type); struct scsi_host_cmd_pool { kmem_cache_t *slab; diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c index 55200e4fdf1..524a5f7a519 100644 --- a/drivers/scsi/scsi_proc.c +++ b/drivers/scsi/scsi_proc.c @@ -178,9 +178,7 @@ static int proc_print_scsidevice(struct device *dev, void *data) seq_printf(s, "\n"); - seq_printf(s, " Type: %s ", - sdev->type < MAX_SCSI_DEVICE_CODE ? - scsi_device_types[(int) sdev->type] : "Unknown "); + seq_printf(s, " Type: %s ", scsi_device_type(sdev->type)); seq_printf(s, " ANSI" " SCSI revision: %02x", (sdev->scsi_level - 1) ? sdev->scsi_level - 1 : 1); diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 1bd92b9b46d..18039940651 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -133,59 +133,6 @@ static void scsi_unlock_floptical(struct scsi_device *sdev, SCSI_TIMEOUT, 3); } -/** - * print_inquiry - printk the inquiry information - * @inq_result: printk this SCSI INQUIRY - * - * Description: - * printk the vendor, model, and other information found in the - * INQUIRY data in @inq_result. - * - * Notes: - * Remove this, and replace with a hotplug event that logs any - * relevant information. - **/ -static void print_inquiry(unsigned char *inq_result) -{ - int i; - - printk(KERN_NOTICE " Vendor: "); - for (i = 8; i < 16; i++) - if (inq_result[i] >= 0x20 && i < inq_result[4] + 5) - printk("%c", inq_result[i]); - else - printk(" "); - - printk(" Model: "); - for (i = 16; i < 32; i++) - if (inq_result[i] >= 0x20 && i < inq_result[4] + 5) - printk("%c", inq_result[i]); - else - printk(" "); - - printk(" Rev: "); - for (i = 32; i < 36; i++) - if (inq_result[i] >= 0x20 && i < inq_result[4] + 5) - printk("%c", inq_result[i]); - else - printk(" "); - - printk("\n"); - - i = inq_result[0] & 0x1f; - - printk(KERN_NOTICE " Type: %s ", - i < - MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] : - "Unknown "); - printk(" ANSI SCSI revision: %02x", - inq_result[2] & 0x07); - if ((inq_result[2] & 0x07) == 1 && (inq_result[3] & 0x0f) == 1) - printk(" CCS\n"); - else - printk("\n"); -} - /** * scsi_alloc_sdev - allocate and setup a scsi_Device * @@ -653,9 +600,8 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) if (*bflags & BLIST_ISROM) { /* * It would be better to modify sdev->type, and set - * sdev->removable, but then the print_inquiry() output - * would not show TYPE_ROM; if print_inquiry() is removed - * the issue goes away. + * sdev->removable; this can now be done since + * print_inquiry has gone away. */ inq_result[0] = TYPE_ROM; inq_result[1] |= 0x80; /* removable */ @@ -684,8 +630,6 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) printk(KERN_INFO "scsi: unknown device type %d\n", sdev->type); } - print_inquiry(inq_result); - /* * For a peripheral qualifier (PQ) value of 1 (001b), the SCSI * spec says: The device server is capable of supporting the @@ -715,6 +659,12 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) if (inq_result[7] & 0x10) sdev->sdtr = 1; + sdev_printk(KERN_NOTICE "scsi", sdev, "%s %.8s %.16s %.4s PQ: %d " + "ANSI: %d%s\n", scsi_device_type(sdev->type), + sdev->vendor, sdev->model, sdev->rev, + sdev->inq_periph_qual, inq_result[2] & 0x07, + (inq_result[3] & 0x0f) == 1 ? " CCS" : ""); + /* * End sysfs code. */ -- cgit v1.2.3 From 19ac0db3e22de3b00cc4aadc7efbad0420c7aa08 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sun, 6 Aug 2006 18:15:22 -0500 Subject: [SCSI] fix up short inquiry printing A recent drivers base commit: 3e95637a48820ff8bedb33e6439def96ccff1de5 Caused the bus to be added to dev_printk, so now our SCSI inquiry short messages print like this: scsiscsi 2:0:0:0: Direct access IBM-ESXS ST973401SS B519 PQ: 0 ANSI: 5 Just remove the "scsi" from the sdev_printk to compensate. Signed-off-by: James Bottomley --- drivers/scsi/scsi_scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 18039940651..114e2067dce 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -659,7 +659,7 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) if (inq_result[7] & 0x10) sdev->sdtr = 1; - sdev_printk(KERN_NOTICE "scsi", sdev, "%s %.8s %.16s %.4s PQ: %d " + sdev_printk(KERN_NOTICE, sdev, "%s %.8s %.16s %.4s PQ: %d " "ANSI: %d%s\n", scsi_device_type(sdev->type), sdev->vendor, sdev->model, sdev->rev, sdev->inq_periph_qual, inq_result[2] & 0x07, -- cgit v1.2.3 From b03732f006bd1ecee32587ec8235c41af5ad905f Mon Sep 17 00:00:00 2001 From: Brian King Date: Mon, 7 Aug 2006 14:27:10 -0500 Subject: [PATCH] libata: Add ata_host_set_init Add ata_host_set_init in preparation for SAS attached SATA. Signed-off-by: Brian King Signed-off-by: Jeff Garzik --- drivers/scsi/libata-core.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 351544d3653..886440b128a 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -5350,6 +5350,28 @@ err_out: return NULL; } +/** + * ata_sas_host_init - Initialize a host_set struct + * @host_set: host_set to initialize + * @dev: device host_set is attached to + * @flags: host_set flags + * @ops: port_ops + * + * LOCKING: + * PCI/etc. bus probe sem. + * + */ + +void ata_host_set_init(struct ata_host_set *host_set, + struct device *dev, unsigned long flags, + const struct ata_port_operations *ops) +{ + spin_lock_init(&host_set->lock); + host_set->dev = dev; + host_set->flags = flags; + host_set->ops = ops; +} + /** * ata_device_add - Register hardware device with ATA and SCSI layers * @ent: Probe information describing hardware device to be registered @@ -5381,15 +5403,12 @@ int ata_device_add(const struct ata_probe_ent *ent) (ent->n_ports * sizeof(void *)), GFP_KERNEL); if (!host_set) return 0; - spin_lock_init(&host_set->lock); - host_set->dev = dev; + ata_host_set_init(host_set, dev, ent->host_set_flags, ent->port_ops); host_set->n_ports = ent->n_ports; host_set->irq = ent->irq; host_set->mmio_base = ent->mmio_base; host_set->private_data = ent->private_data; - host_set->ops = ent->port_ops; - host_set->flags = ent->host_set_flags; /* register each port bound to this device */ for (i = 0; i < ent->n_ports; i++) { @@ -5908,6 +5927,7 @@ EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug); EXPORT_SYMBOL_GPL(sata_deb_timing_long); EXPORT_SYMBOL_GPL(ata_std_bios_param); EXPORT_SYMBOL_GPL(ata_std_ports); +EXPORT_SYMBOL_GPL(ata_host_set_init); EXPORT_SYMBOL_GPL(ata_device_add); EXPORT_SYMBOL_GPL(ata_port_detach); EXPORT_SYMBOL_GPL(ata_host_set_remove); -- cgit v1.2.3 From 155a8a9c8f4084016d9e27bf03ba1f19201438f4 Mon Sep 17 00:00:00 2001 From: Brian King Date: Mon, 7 Aug 2006 14:27:17 -0500 Subject: [PATCH] libata: Add ata_port_init Separate out the ata_port initialization from ata_host_init so that it can be used in future SAS patches. Signed-off-by: Brian King Signed-off-by: Jeff Garzik --- drivers/scsi/libata-core.c | 49 ++++++++++++++++++++++++++++++++-------------- drivers/scsi/libata.h | 2 ++ 2 files changed, 36 insertions(+), 15 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 886440b128a..f553c9dd962 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -5218,35 +5218,25 @@ void ata_dev_init(struct ata_device *dev) } /** - * ata_host_init - Initialize an ata_port structure + * ata_port_init - Initialize an ata_port structure * @ap: Structure to initialize - * @host: associated SCSI mid-layer structure * @host_set: Collection of hosts to which @ap belongs * @ent: Probe information provided by low-level driver * @port_no: Port number associated with this ata_port * - * Initialize a new ata_port structure, and its associated - * scsi_host. + * Initialize a new ata_port structure. * * LOCKING: * Inherited from caller. */ -static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host, - struct ata_host_set *host_set, - const struct ata_probe_ent *ent, unsigned int port_no) +void ata_port_init(struct ata_port *ap, struct ata_host_set *host_set, + const struct ata_probe_ent *ent, unsigned int port_no) { unsigned int i; - host->max_id = 16; - host->max_lun = 1; - host->max_channel = 1; - host->unique_id = ata_unique_id++; - host->max_cmd_len = 12; - ap->lock = &host_set->lock; ap->flags = ATA_FLAG_DISABLED; - ap->id = host->unique_id; - ap->host = host; + ap->id = ata_unique_id++; ap->ctl = ATA_DEVCTL_OBS; ap->host_set = host_set; ap->dev = ent->dev; @@ -5297,6 +5287,35 @@ static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host, memcpy(&ap->ioaddr, &ent->port[port_no], sizeof(struct ata_ioports)); } +/** + * ata_host_init - Initialize an ata_port structure + * @ap: Structure to initialize + * @host: associated SCSI mid-layer structure + * @host_set: Collection of hosts to which @ap belongs + * @ent: Probe information provided by low-level driver + * @port_no: Port number associated with this ata_port + * + * Initialize a new ata_port structure, and its associated + * scsi_host. + * + * LOCKING: + * Inherited from caller. + */ + +static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host, + struct ata_host_set *host_set, + const struct ata_probe_ent *ent, unsigned int port_no) +{ + ata_port_init(ap, host_set, ent, port_no); + ap->host = host; + + host->unique_id = ap->id; + host->max_id = 16; + host->max_lun = 1; + host->max_channel = 1; + host->max_cmd_len = 12; +} + /** * ata_host_add - Attach low-level ATA driver to system * @ent: Information provided by low-level driver diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h index c325679d9b5..b38aa4a286b 100644 --- a/drivers/scsi/libata.h +++ b/drivers/scsi/libata.h @@ -69,6 +69,8 @@ extern int ata_flush_cache(struct ata_device *dev); extern void ata_dev_init(struct ata_device *dev); extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg); extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); +extern void ata_port_init(struct ata_port *ap, struct ata_host_set *host_set, + const struct ata_probe_ent *ent, unsigned int port_no); /* libata-scsi.c */ -- cgit v1.2.3 From f6d950e2a5209bd7e3fb1a238f43f24f3697f5b0 Mon Sep 17 00:00:00 2001 From: Brian King Date: Mon, 7 Aug 2006 14:27:24 -0500 Subject: [PATCH] libata: Move ata_probe_ent_alloc to libata_core Move ata_probe_ent_alloc to libata-core. It will also be used by future SAS/SATA integration patches. Signed-off-by: Brian King Signed-off-by: Jeff Garzik --- drivers/scsi/libata-bmdma.c | 26 -------------------------- drivers/scsi/libata-core.c | 25 +++++++++++++++++++++++++ drivers/scsi/libata.h | 2 ++ 3 files changed, 27 insertions(+), 26 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libata-bmdma.c b/drivers/scsi/libata-bmdma.c index 9ce221f2595..3268cf5375e 100644 --- a/drivers/scsi/libata-bmdma.c +++ b/drivers/scsi/libata-bmdma.c @@ -797,32 +797,6 @@ void ata_bmdma_post_internal_cmd(struct ata_queued_cmd *qc) } #ifdef CONFIG_PCI -static struct ata_probe_ent * -ata_probe_ent_alloc(struct device *dev, const struct ata_port_info *port) -{ - struct ata_probe_ent *probe_ent; - - probe_ent = kzalloc(sizeof(*probe_ent), GFP_KERNEL); - if (!probe_ent) { - printk(KERN_ERR DRV_NAME "(%s): out of memory\n", - kobject_name(&(dev->kobj))); - return NULL; - } - - INIT_LIST_HEAD(&probe_ent->node); - probe_ent->dev = dev; - - probe_ent->sht = port->sht; - probe_ent->host_flags = port->host_flags; - probe_ent->pio_mask = port->pio_mask; - probe_ent->mwdma_mask = port->mwdma_mask; - probe_ent->udma_mask = port->udma_mask; - probe_ent->port_ops = port->port_ops; - - return probe_ent; -} - - /** * ata_pci_init_native_mode - Initialize native-mode driver * @pdev: pci device to be initialized diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index f553c9dd962..dc35cccd289 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -5688,6 +5688,31 @@ int ata_scsi_release(struct Scsi_Host *host) return 1; } +struct ata_probe_ent * +ata_probe_ent_alloc(struct device *dev, const struct ata_port_info *port) +{ + struct ata_probe_ent *probe_ent; + + probe_ent = kzalloc(sizeof(*probe_ent), GFP_KERNEL); + if (!probe_ent) { + printk(KERN_ERR DRV_NAME "(%s): out of memory\n", + kobject_name(&(dev->kobj))); + return NULL; + } + + INIT_LIST_HEAD(&probe_ent->node); + probe_ent->dev = dev; + + probe_ent->sht = port->sht; + probe_ent->host_flags = port->host_flags; + probe_ent->pio_mask = port->pio_mask; + probe_ent->mwdma_mask = port->mwdma_mask; + probe_ent->udma_mask = port->udma_mask; + probe_ent->port_ops = port->port_ops; + + return probe_ent; +} + /** * ata_std_ports - initialize ioaddr with standard port offsets. * @ioaddr: IO address structure to be initialized diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h index b38aa4a286b..0b7a37c2785 100644 --- a/drivers/scsi/libata.h +++ b/drivers/scsi/libata.h @@ -71,6 +71,8 @@ extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg); extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); extern void ata_port_init(struct ata_port *ap, struct ata_host_set *host_set, const struct ata_probe_ent *ent, unsigned int port_no); +extern struct ata_probe_ent *ata_probe_ent_alloc(struct device *dev, + const struct ata_port_info *port); /* libata-scsi.c */ -- cgit v1.2.3 From 80289167fd3ebaeb7b2641e69cbec44b61165fe7 Mon Sep 17 00:00:00 2001 From: Brian King Date: Mon, 7 Aug 2006 14:27:31 -0500 Subject: [PATCH] libata: Add support for SATA attachment to SAS adapters The following patch enhances libata to allow SAS device drivers to utilize libata to talk to SATA devices. It introduces some new APIs which allow libata to be used without allocating a virtual scsi host. New APIs: ata_sas_port_alloc - Allocate an ata_port ata_sas_port_init - Initialize an ata_port (probe device, etc) ata_sas_port_destroy - Free an ata_port allocated by ata_sas_port_alloc ata_sas_slave_configure - configure scsi device ata_sas_queuecmd - queue a scsi command, similar to ata_scsi_queuecomand These new APIs can be used either directly by a SAS LLDD or could be used by the SAS transport class. Possible usage for a SAS LLDD would be: scsi_scan_host target_alloc ata_sas_port_alloc slave_alloc ata_sas_port_init slave_configure ata_sas_slave_configure Commands received by the LLDD for SATA devices would call ata_sas_queuecmd. Device teardown would occur with: slave_destroy port_disable target_destroy ata_sas_port_destroy Signed-off-by: Brian King Signed-off-by: Jeff Garzik --- drivers/scsi/libata-core.c | 2 +- drivers/scsi/libata-scsi.c | 149 +++++++++++++++++++++++++++++++++++++++++++++ drivers/scsi/libata.h | 1 + 3 files changed, 151 insertions(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index dc35cccd289..83d93fc0751 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -1528,7 +1528,7 @@ err_out_nosup: * Zero on success, negative errno otherwise. */ -static int ata_bus_probe(struct ata_port *ap) +int ata_bus_probe(struct ata_port *ap) { unsigned int classes[ATA_MAX_DEVICES]; int tries[ATA_MAX_DEVICES]; diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c index 1638e57028c..37ec0266143 100644 --- a/drivers/scsi/libata-scsi.c +++ b/drivers/scsi/libata-scsi.c @@ -3158,3 +3158,152 @@ void ata_scsi_dev_rescan(void *data) scsi_rescan_device(&(dev->sdev->sdev_gendev)); } } + +/** + * ata_sas_port_alloc - Allocate port for a SAS attached SATA device + * @pdev: PCI device that the scsi device is attached to + * @port_info: Information from low-level host driver + * @host: SCSI host that the scsi device is attached to + * + * LOCKING: + * PCI/etc. bus probe sem. + * + * RETURNS: + * ata_port pointer on success / NULL on failure. + */ + +struct ata_port *ata_sas_port_alloc(struct ata_host_set *host_set, + struct ata_port_info *port_info, + struct Scsi_Host *host) +{ + struct ata_port *ap = kzalloc(sizeof(*ap), GFP_KERNEL); + struct ata_probe_ent *ent; + + if (!ap) + return NULL; + + ent = ata_probe_ent_alloc(host_set->dev, port_info); + if (!ent) { + kfree(ap); + return NULL; + } + + ata_port_init(ap, host_set, ent, 0); + ap->lock = host->host_lock; + kfree(ent); + return ap; +} +EXPORT_SYMBOL_GPL(ata_sas_port_alloc); + +/** + * ata_sas_port_start - Set port up for dma. + * @ap: Port to initialize + * + * Called just after data structures for each port are + * initialized. Allocates DMA pad. + * + * May be used as the port_start() entry in ata_port_operations. + * + * LOCKING: + * Inherited from caller. + */ +int ata_sas_port_start(struct ata_port *ap) +{ + return ata_pad_alloc(ap, ap->dev); +} +EXPORT_SYMBOL_GPL(ata_sas_port_start); + +/** + * ata_port_stop - Undo ata_sas_port_start() + * @ap: Port to shut down + * + * Frees the DMA pad. + * + * May be used as the port_stop() entry in ata_port_operations. + * + * LOCKING: + * Inherited from caller. + */ + +void ata_sas_port_stop(struct ata_port *ap) +{ + ata_pad_free(ap, ap->dev); +} +EXPORT_SYMBOL_GPL(ata_sas_port_stop); + +/** + * ata_sas_port_init - Initialize a SATA device + * @ap: SATA port to initialize + * + * LOCKING: + * PCI/etc. bus probe sem. + * + * RETURNS: + * Zero on success, non-zero on error. + */ + +int ata_sas_port_init(struct ata_port *ap) +{ + int rc = ap->ops->port_start(ap); + + if (!rc) + rc = ata_bus_probe(ap); + + return rc; +} +EXPORT_SYMBOL_GPL(ata_sas_port_init); + +/** + * ata_sas_port_destroy - Destroy a SATA port allocated by ata_sas_port_alloc + * @ap: SATA port to destroy + * + */ + +void ata_sas_port_destroy(struct ata_port *ap) +{ + ap->ops->port_stop(ap); + kfree(ap); +} +EXPORT_SYMBOL_GPL(ata_sas_port_destroy); + +/** + * ata_sas_slave_configure - Default slave_config routine for libata devices + * @sdev: SCSI device to configure + * @ap: ATA port to which SCSI device is attached + * + * RETURNS: + * Zero. + */ + +int ata_sas_slave_configure(struct scsi_device *sdev, struct ata_port *ap) +{ + ata_scsi_sdev_config(sdev); + ata_scsi_dev_config(sdev, ap->device); + return 0; +} +EXPORT_SYMBOL_GPL(ata_sas_slave_configure); + +/** + * ata_sas_queuecmd - Issue SCSI cdb to libata-managed device + * @cmd: SCSI command to be sent + * @done: Completion function, called when command is complete + * @ap: ATA port to which the command is being sent + * + * RETURNS: + * Zero. + */ + +int ata_sas_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *), + struct ata_port *ap) +{ + ata_scsi_dump_cdb(ap, cmd); + + if (likely(ata_scsi_dev_enabled(ap->device))) + __ata_scsi_queuecmd(cmd, done, ap->device); + else { + cmd->result = (DID_BAD_TARGET << 16); + done(cmd); + } + return 0; +} +EXPORT_SYMBOL_GPL(ata_sas_queuecmd); diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h index 0b7a37c2785..d4a4f82360e 100644 --- a/drivers/scsi/libata.h +++ b/drivers/scsi/libata.h @@ -111,6 +111,7 @@ extern void ata_scsi_rbuf_fill(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen)); extern void ata_schedule_scsi_eh(struct Scsi_Host *shost); extern void ata_scsi_dev_rescan(void *data); +extern int ata_bus_probe(struct ata_port *ap); /* libata-eh.c */ extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); -- cgit v1.2.3 From f4b5cc874158a139c091b3decd468929e10645e6 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 7 Aug 2006 11:39:04 +0900 Subject: [PATCH] ahci: remove IRQ mask clearing from init_controller() Initial IRQ mask clearing is done by libata-core by freezing all ports prior to requesting IRQ. Remove redundant IRQ clearing from init_controller(). Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/scsi/ahci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index f5734a97580..68fd7667a08 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -662,14 +662,13 @@ static void ahci_init_controller(void __iomem *mmio, struct pci_dev *pdev, VPRINTK("PORT_SCR_ERR 0x%x\n", tmp); writel(tmp, port_mmio + PORT_SCR_ERR); - /* clear & turn off port IRQ */ + /* clear port IRQ */ tmp = readl(port_mmio + PORT_IRQ_STAT); VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); if (tmp) writel(tmp, port_mmio + PORT_IRQ_STAT); writel(1 << i, mmio + HOST_IRQ_STAT); - writel(0, port_mmio + PORT_IRQ_MASK); } tmp = readl(mmio + HOST_CTL); -- cgit v1.2.3 From 4608c1608501cf2b0dc4478c9b6c3902db58408a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 10 Aug 2006 16:59:01 +0900 Subject: [PATCH] libata: update ata_host_init() and rename it to ata_port_init_shost() Update ata_host_init() such that it only initializes SCSI host related stuff and doesn't call into ata_port_init(), and rename it to ata_port_init_shost(). Signed-off-by: Tejun Heo --- drivers/scsi/libata-core.c | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 83d93fc0751..eb9de073639 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -5288,32 +5288,24 @@ void ata_port_init(struct ata_port *ap, struct ata_host_set *host_set, } /** - * ata_host_init - Initialize an ata_port structure - * @ap: Structure to initialize - * @host: associated SCSI mid-layer structure - * @host_set: Collection of hosts to which @ap belongs - * @ent: Probe information provided by low-level driver - * @port_no: Port number associated with this ata_port + * ata_port_init_shost - Initialize SCSI host associated with ATA port + * @ap: ATA port to initialize SCSI host for + * @shost: SCSI host associated with @ap * - * Initialize a new ata_port structure, and its associated - * scsi_host. + * Initialize SCSI host @shost associated with ATA port @ap. * * LOCKING: * Inherited from caller. */ - -static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host, - struct ata_host_set *host_set, - const struct ata_probe_ent *ent, unsigned int port_no) +static void ata_port_init_shost(struct ata_port *ap, struct Scsi_Host *shost) { - ata_port_init(ap, host_set, ent, port_no); - ap->host = host; + ap->host = shost; - host->unique_id = ap->id; - host->max_id = 16; - host->max_lun = 1; - host->max_channel = 1; - host->max_cmd_len = 12; + shost->unique_id = ap->id; + shost->max_id = 16; + shost->max_lun = 1; + shost->max_channel = 1; + shost->max_cmd_len = 12; } /** @@ -5356,7 +5348,8 @@ static struct ata_port * ata_host_add(const struct ata_probe_ent *ent, ap = ata_shost_to_port(host); - ata_host_init(ap, host, host_set, ent, port_no); + ata_port_init(ap, host_set, ent, port_no); + ata_port_init_shost(ap, host); rc = ap->ops->port_start(ap); if (rc) -- cgit v1.2.3 From 996139f1ce50c56c5e66f86b6aba17ff5ea00b86 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Thu, 10 Aug 2006 16:59:03 +0900 Subject: [PATCH] [libata] some function renaming s/ata_host_add/ata_port_add/ s/ata_host_init/ata_port_init/ libata naming got stuck in the middle of a Great Renaming: ata_host -> ata_port ata_host_set -> ata_host To eliminate confusion, let's just give up for now, and simply ensure that things are internally consistent. Signed-off-by: Jeff Garzik Signed-off-by: Tejun Heo --- drivers/scsi/libata-core.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index eb9de073639..3aa477a3302 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -5309,7 +5309,7 @@ static void ata_port_init_shost(struct ata_port *ap, struct Scsi_Host *shost) } /** - * ata_host_add - Attach low-level ATA driver to system + * ata_port_add - Attach low-level ATA driver to system * @ent: Information provided by low-level driver * @host_set: Collections of ports to which we add * @port_no: Port number associated with this host @@ -5322,12 +5322,11 @@ static void ata_port_init_shost(struct ata_port *ap, struct Scsi_Host *shost) * RETURNS: * New ata_port on success, for NULL on error. */ - -static struct ata_port * ata_host_add(const struct ata_probe_ent *ent, +static struct ata_port * ata_port_add(const struct ata_probe_ent *ent, struct ata_host_set *host_set, unsigned int port_no) { - struct Scsi_Host *host; + struct Scsi_Host *shost; struct ata_port *ap; int rc; @@ -5340,16 +5339,16 @@ static struct ata_port * ata_host_add(const struct ata_probe_ent *ent, return NULL; } - host = scsi_host_alloc(ent->sht, sizeof(struct ata_port)); - if (!host) + shost = scsi_host_alloc(ent->sht, sizeof(struct ata_port)); + if (!shost) return NULL; - host->transportt = &ata_scsi_transport_template; + shost->transportt = &ata_scsi_transport_template; - ap = ata_shost_to_port(host); + ap = ata_shost_to_port(shost); ata_port_init(ap, host_set, ent, port_no); - ata_port_init_shost(ap, host); + ata_port_init_shost(ap, shost); rc = ap->ops->port_start(ap); if (rc) @@ -5358,7 +5357,7 @@ static struct ata_port * ata_host_add(const struct ata_probe_ent *ent, return ap; err_out: - scsi_host_put(host); + scsi_host_put(shost); return NULL; } @@ -5427,7 +5426,7 @@ int ata_device_add(const struct ata_probe_ent *ent) struct ata_port *ap; unsigned long xfer_mode_mask; - ap = ata_host_add(ent, host_set, i); + ap = ata_port_add(ent, host_set, i); if (!ap) goto err_out; -- cgit v1.2.3 From 6d0500df5b37c94b779ac2c3f522ff917e039f99 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Thu, 10 Aug 2006 16:59:05 +0900 Subject: [PATCH] [libata] Kill 'count' var in ata_device_add() Eliminate redundant loop variable 'count' Signed-off-by: Jeff Garzik Signed-off-by: Tejun Heo --- drivers/scsi/libata-core.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 3aa477a3302..38fc75d6b39 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -5403,7 +5403,7 @@ void ata_host_set_init(struct ata_host_set *host_set, */ int ata_device_add(const struct ata_probe_ent *ent) { - unsigned int count = 0, i; + unsigned int i; struct device *dev = ent->dev; struct ata_host_set *host_set; int rc; @@ -5422,7 +5422,7 @@ int ata_device_add(const struct ata_probe_ent *ent) host_set->private_data = ent->private_data; /* register each port bound to this device */ - for (i = 0; i < ent->n_ports; i++) { + for (i = 0; i < host_set->n_ports; i++) { struct ata_port *ap; unsigned long xfer_mode_mask; @@ -5448,12 +5448,8 @@ int ata_device_add(const struct ata_probe_ent *ent) ata_chk_status(ap); host_set->ops->irq_clear(ap); ata_eh_freeze_port(ap); /* freeze port before requesting IRQ */ - count++; } - if (!count) - goto err_free_ret; - /* obtain irq, that is shared between channels */ rc = request_irq(ent->irq, ent->port_ops->irq_handler, ent->irq_flags, DRV_NAME, host_set); @@ -5465,13 +5461,11 @@ int ata_device_add(const struct ata_probe_ent *ent) /* perform each probe synchronously */ DPRINTK("probe begin\n"); - for (i = 0; i < count; i++) { - struct ata_port *ap; + for (i = 0; i < host_set->n_ports; i++) { + struct ata_port *ap = host_set->ports[i]; u32 scontrol; int rc; - ap = host_set->ports[i]; - /* init sata_spd_limit to the current value */ if (sata_scr_read(ap, SCR_CONTROL, &scontrol) == 0) { int spd = (scontrol >> 4) & 0xf; @@ -5527,7 +5521,7 @@ int ata_device_add(const struct ata_probe_ent *ent) /* probes are done, now scan each port's disk(s) */ DPRINTK("host probe begin\n"); - for (i = 0; i < count; i++) { + for (i = 0; i < host_set->n_ports; i++) { struct ata_port *ap = host_set->ports[i]; ata_scsi_scan_host(ap); @@ -5539,14 +5533,14 @@ int ata_device_add(const struct ata_probe_ent *ent) return ent->n_ports; /* success */ err_out: - for (i = 0; i < count; i++) { + for (i = 0; i < host_set->n_ports; i++) { struct ata_port *ap = host_set->ports[i]; if (ap) { ap->ops->port_stop(ap); scsi_host_put(ap->host); } } -err_free_ret: + kfree(host_set); VPRINTK("EXIT, returning 0\n"); return 0; -- cgit v1.2.3 From 37deecb5139ee431233781a9a093d9fcaab54c5b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 10 Aug 2006 16:59:07 +0900 Subject: [PATCH] libata: implement per-dev xfermask Implement per-dev xfermask. libata used to determine xfermask per-port - the fastest mode of the slowest device on the port. This patch enables per-dev xfermask. Original patch is from Alan Cox . The following changes are made by me. * simplex warning message is added * remove disabled device handling code which is never invoked (originally for choosing port-wide lowest PIO mode) Cc: Alan Cox Signed-off-by: Tejun Heo --- drivers/scsi/libata-core.c | 40 +++++++++++----------------------------- 1 file changed, 11 insertions(+), 29 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 38fc75d6b39..4e6c2e8ac0f 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -3040,10 +3040,6 @@ static int ata_dma_blacklisted(const struct ata_device *dev) * known limits including host controller limits, device * blacklist, etc... * - * FIXME: The current implementation limits all transfer modes to - * the fastest of the lowested device on the port. This is not - * required on most controllers. - * * LOCKING: * None. */ @@ -3052,8 +3048,8 @@ static void ata_dev_xfermask(struct ata_device *dev) struct ata_port *ap = dev->ap; struct ata_host_set *hs = ap->host_set; unsigned long xfer_mask; - int i; + /* controller modes available */ xfer_mask = ata_pack_xfermask(ap->pio_mask, ap->mwdma_mask, ap->udma_mask); @@ -3063,34 +3059,20 @@ static void ata_dev_xfermask(struct ata_device *dev) if (ap->cbl == ATA_CBL_PATA40) xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA); - /* FIXME: Use port-wide xfermask for now */ - for (i = 0; i < ATA_MAX_DEVICES; i++) { - struct ata_device *d = &ap->device[i]; - - if (ata_dev_absent(d)) - continue; + xfer_mask &= ata_pack_xfermask(dev->pio_mask, + dev->mwdma_mask, dev->udma_mask); + xfer_mask &= ata_id_xfermask(dev->id); - if (ata_dev_disabled(d)) { - /* to avoid violating device selection timing */ - xfer_mask &= ata_pack_xfermask(d->pio_mask, - UINT_MAX, UINT_MAX); - continue; - } - - xfer_mask &= ata_pack_xfermask(d->pio_mask, - d->mwdma_mask, d->udma_mask); - xfer_mask &= ata_id_xfermask(d->id); - if (ata_dma_blacklisted(d)) - xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); - } - - if (ata_dma_blacklisted(dev)) + if (ata_dma_blacklisted(dev)) { + xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); ata_dev_printk(dev, KERN_WARNING, "device is on DMA blacklist, disabling DMA\n"); + } - if (hs->flags & ATA_HOST_SIMPLEX) { - if (hs->simplex_claimed) - xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); + if ((hs->flags & ATA_HOST_SIMPLEX) && hs->simplex_claimed) { + xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); + ata_dev_printk(dev, KERN_WARNING, "simplex DMA is claimed by " + "other device, disabling DMA\n"); } if (ap->ops->mode_filter) -- cgit v1.2.3 From 2ec7df0457b710d9201f211dbccdbecf0ad38b7e Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 10 Aug 2006 16:59:10 +0900 Subject: [PATCH] libata: rework legacy handling to remove much of the cruft Kill host_set->next Fix simplex support Allow per platform setting of IDE legacy bases Some of this can be tidied further later on, in particular all the legacy port gunge belongs as a PCI quirk/PCI header decode to understand the special legacy IDE rules in the PCI spec. Longer term Jeff also wants to move the request_irq/free_irq out of core which will make this even cleaner. tj: folded in three followup patches - ata_piix-fix, broken-arch-fix and fix-new-legacy-handling, and separated per-dev xfermask into separate patch preceding this one. Folded in fixes are... * ata_piix-fix: fix build failure due to host_set->next removal * broken-arch-fix: add missing include/asm-*/libata-portmap.h * fix-new-legacy-handling: * In ata_pci_init_legacy_port(), probe_num was incorrectly incremented during initialization of the secondary port and probe_ent->n_ports was incorrectly fixed to 1. * Both legacy ports ended up having the same hard_port_no. * When printing port information, both legacy ports printed the first irq. Signed-off-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Tejun Heo --- drivers/scsi/ata_piix.c | 2 - drivers/scsi/libata-bmdma.c | 135 +++++++++++++++++++++----------------------- drivers/scsi/libata-core.c | 59 +++++++++++-------- 3 files changed, 100 insertions(+), 96 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ata_piix.c b/drivers/scsi/ata_piix.c index 5e8afc87698..40ecab5793e 100644 --- a/drivers/scsi/ata_piix.c +++ b/drivers/scsi/ata_piix.c @@ -932,8 +932,6 @@ static int piix_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) static void piix_host_stop(struct ata_host_set *host_set) { - if (host_set->next == NULL) - kfree(host_set->private_data); ata_host_stop(host_set); } diff --git a/drivers/scsi/libata-bmdma.c b/drivers/scsi/libata-bmdma.c index 3268cf5375e..e694f6075c3 100644 --- a/drivers/scsi/libata-bmdma.c +++ b/drivers/scsi/libata-bmdma.c @@ -854,7 +854,7 @@ ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int if (bmdma) { bmdma += 8; if(inb(bmdma + 2) & 0x80) - probe_ent->host_set_flags |= ATA_HOST_SIMPLEX; + probe_ent->host_set_flags |= ATA_HOST_SIMPLEX; probe_ent->port[p].bmdma_addr = bmdma; } ata_std_ports(&probe_ent->port[p]); @@ -867,44 +867,55 @@ ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev, - struct ata_port_info *port, int port_num) + struct ata_port_info **port, int port_mask) { struct ata_probe_ent *probe_ent; - unsigned long bmdma; + unsigned long bmdma = pci_resource_start(pdev, 4); - probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port); + int port_num = 0; + + probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]); if (!probe_ent) return NULL; probe_ent->legacy_mode = 1; - probe_ent->n_ports = 1; - probe_ent->hard_port_no = port_num; - probe_ent->private_data = port->private_data; - - switch(port_num) - { - case 0: - probe_ent->irq = 14; - probe_ent->port[0].cmd_addr = 0x1f0; - probe_ent->port[0].altstatus_addr = - probe_ent->port[0].ctl_addr = 0x3f6; - break; - case 1: + probe_ent->hard_port_no = 0; + probe_ent->private_data = port[0]->private_data; + + if (port_mask & ATA_PORT_PRIMARY) { + probe_ent->irq = 14; + probe_ent->port[port_num].cmd_addr = ATA_PRIMARY_CMD; + probe_ent->port[port_num].altstatus_addr = + probe_ent->port[port_num].ctl_addr = ATA_PRIMARY_CTL; + if (bmdma) { + probe_ent->port[0].bmdma_addr = bmdma; + if (inb(bmdma + 2) & 0x80) + probe_ent->host_set_flags |= ATA_HOST_SIMPLEX; + } + ata_std_ports(&probe_ent->port[port_num]); + port_num ++; + } + if (port_mask & ATA_PORT_SECONDARY) { + if (port_num == 1) + probe_ent->irq2 = 15; + else { + /* Secondary only. IRQ 15 only and "first" port is port 1 */ probe_ent->irq = 15; - probe_ent->port[0].cmd_addr = 0x170; - probe_ent->port[0].altstatus_addr = - probe_ent->port[0].ctl_addr = 0x376; - break; + probe_ent->hard_port_no = 1; + } + probe_ent->port[port_num].cmd_addr = ATA_SECONDARY_CMD; + probe_ent->port[port_num].altstatus_addr = + probe_ent->port[port_num].ctl_addr = ATA_SECONDARY_CTL; + if (bmdma) { + probe_ent->port[port_num].bmdma_addr = bmdma + 8; + if (inb(bmdma + 10) & 0x80) + probe_ent->host_set_flags |= ATA_HOST_SIMPLEX; + } + ata_std_ports(&probe_ent->port[port_num]); + port_num ++; } - bmdma = pci_resource_start(pdev, 4); - if (bmdma != 0) { - bmdma += 8 * port_num; - probe_ent->port[0].bmdma_addr = bmdma; - if (inb(bmdma + 2) & 0x80) - probe_ent->host_set_flags |= ATA_HOST_SIMPLEX; - } - ata_std_ports(&probe_ent->port[0]); + probe_ent->n_ports = port_num; return probe_ent; } @@ -924,6 +935,10 @@ static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev, * regions, sets the dma mask, enables bus master mode, and calls * ata_device_add() * + * ASSUMPTION: + * Nobody makes a single channel controller that appears solely as + * the secondary legacy port on PCI. + * * LOCKING: * Inherited from PCI layer (may sleep). * @@ -934,7 +949,7 @@ static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev, int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, unsigned int n_ports) { - struct ata_probe_ent *probe_ent = NULL, *probe_ent2 = NULL; + struct ata_probe_ent *probe_ent = NULL; struct ata_port_info *port[2]; u8 tmp8, mask; unsigned int legacy_mode = 0; @@ -983,35 +998,34 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, goto err_out; } - /* FIXME: Should use platform specific mappers for legacy port ranges */ if (legacy_mode) { - if (!request_region(0x1f0, 8, "libata")) { + if (!request_region(ATA_PRIMARY_CMD, 8, "libata")) { struct resource *conflict, res; - res.start = 0x1f0; - res.end = 0x1f0 + 8 - 1; + res.start = ATA_PRIMARY_CMD; + res.end = ATA_PRIMARY_CMD + 8 - 1; conflict = ____request_resource(&ioport_resource, &res); if (!strcmp(conflict->name, "libata")) - legacy_mode |= (1 << 0); + legacy_mode |= ATA_PORT_PRIMARY; else { disable_dev_on_err = 0; - printk(KERN_WARNING "ata: 0x1f0 IDE port busy\n"); + printk(KERN_WARNING "ata: 0x%0X IDE port busy\n", ATA_PRIMARY_CMD); } } else - legacy_mode |= (1 << 0); + legacy_mode |= ATA_PORT_PRIMARY; - if (!request_region(0x170, 8, "libata")) { + if (!request_region(ATA_SECONDARY_CMD, 8, "libata")) { struct resource *conflict, res; - res.start = 0x170; - res.end = 0x170 + 8 - 1; + res.start = ATA_SECONDARY_CMD; + res.end = ATA_SECONDARY_CMD + 8 - 1; conflict = ____request_resource(&ioport_resource, &res); if (!strcmp(conflict->name, "libata")) - legacy_mode |= (1 << 1); + legacy_mode |= ATA_PORT_SECONDARY; else { disable_dev_on_err = 0; - printk(KERN_WARNING "ata: 0x170 IDE port busy\n"); + printk(KERN_WARNING "ata: 0x%X IDE port busy\n", ATA_SECONDARY_CMD); } } else - legacy_mode |= (1 << 1); + legacy_mode |= ATA_PORT_SECONDARY; } /* we have legacy mode, but all ports are unavailable */ @@ -1029,17 +1043,14 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, goto err_out_regions; if (legacy_mode) { - if (legacy_mode & (1 << 0)) - probe_ent = ata_pci_init_legacy_port(pdev, port[0], 0); - if (legacy_mode & (1 << 1)) - probe_ent2 = ata_pci_init_legacy_port(pdev, port[1], 1); + probe_ent = ata_pci_init_legacy_port(pdev, port, legacy_mode); } else { if (n_ports == 2) probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); else probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY); } - if (!probe_ent && !probe_ent2) { + if (!probe_ent) { rc = -ENOMEM; goto err_out_regions; } @@ -1047,35 +1058,17 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, pci_set_master(pdev); /* FIXME: check ata_device_add return */ - if (legacy_mode) { - struct device *dev = &pdev->dev; - struct ata_host_set *host_set = NULL; - - if (legacy_mode & (1 << 0)) { - ata_device_add(probe_ent); - host_set = dev_get_drvdata(dev); - } - - if (legacy_mode & (1 << 1)) { - ata_device_add(probe_ent2); - if (host_set) { - host_set->next = dev_get_drvdata(dev); - dev_set_drvdata(dev, host_set); - } - } - } else - ata_device_add(probe_ent); + ata_device_add(probe_ent); kfree(probe_ent); - kfree(probe_ent2); return 0; err_out_regions: - if (legacy_mode & (1 << 0)) - release_region(0x1f0, 8); - if (legacy_mode & (1 << 1)) - release_region(0x170, 8); + if (legacy_mode & ATA_PORT_PRIMARY) + release_region(ATA_PRIMARY_CMD, 8); + if (legacy_mode & ATA_PORT_SECONDARY) + release_region(ATA_SECONDARY_CMD, 8); pci_release_regions(pdev); err_out: if (disable_dev_on_err) diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 4e6c2e8ac0f..3634279d896 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -5223,8 +5223,9 @@ void ata_port_init(struct ata_port *ap, struct ata_host_set *host_set, ap->host_set = host_set; ap->dev = ent->dev; ap->port_no = port_no; - ap->hard_port_no = - ent->legacy_mode ? ent->hard_port_no : port_no; + ap->hard_port_no = port_no; + if (ent->legacy_mode) + ap->hard_port_no += ent->hard_port_no; ap->pio_mask = ent->pio_mask; ap->mwdma_mask = ent->mwdma_mask; ap->udma_mask = ent->udma_mask; @@ -5400,6 +5401,7 @@ int ata_device_add(const struct ata_probe_ent *ent) ata_host_set_init(host_set, dev, ent->host_set_flags, ent->port_ops); host_set->n_ports = ent->n_ports; host_set->irq = ent->irq; + host_set->irq2 = ent->irq2; host_set->mmio_base = ent->mmio_base; host_set->private_data = ent->private_data; @@ -5407,11 +5409,16 @@ int ata_device_add(const struct ata_probe_ent *ent) for (i = 0; i < host_set->n_ports; i++) { struct ata_port *ap; unsigned long xfer_mode_mask; + int irq_line = ent->irq; ap = ata_port_add(ent, host_set, i); if (!ap) goto err_out; + /* Report the secondary IRQ for second channel legacy */ + if (i == 1 && ent->irq2) + irq_line = ent->irq2; + host_set->ports[i] = ap; xfer_mode_mask =(ap->udma_mask << ATA_SHIFT_UDMA) | (ap->mwdma_mask << ATA_SHIFT_MWDMA) | @@ -5419,20 +5426,20 @@ int ata_device_add(const struct ata_probe_ent *ent) /* print per-port info to dmesg */ ata_port_printk(ap, KERN_INFO, "%cATA max %s cmd 0x%lX " - "ctl 0x%lX bmdma 0x%lX irq %lu\n", + "ctl 0x%lX bmdma 0x%lX irq %d\n", ap->flags & ATA_FLAG_SATA ? 'S' : 'P', ata_mode_string(xfer_mode_mask), ap->ioaddr.cmd_addr, ap->ioaddr.ctl_addr, ap->ioaddr.bmdma_addr, - ent->irq); + irq_line); ata_chk_status(ap); host_set->ops->irq_clear(ap); ata_eh_freeze_port(ap); /* freeze port before requesting IRQ */ } - /* obtain irq, that is shared between channels */ + /* obtain irq, that may be shared between channels */ rc = request_irq(ent->irq, ent->port_ops->irq_handler, ent->irq_flags, DRV_NAME, host_set); if (rc) { @@ -5441,6 +5448,21 @@ int ata_device_add(const struct ata_probe_ent *ent) goto err_out; } + /* do we have a second IRQ for the other channel, eg legacy mode */ + if (ent->irq2) { + /* We will get weird core code crashes later if this is true + so trap it now */ + BUG_ON(ent->irq == ent->irq2); + + rc = request_irq(ent->irq2, ent->port_ops->irq_handler, ent->irq_flags, + DRV_NAME, host_set); + if (rc) { + dev_printk(KERN_ERR, dev, "irq %lu request failed: %d\n", + ent->irq2, rc); + goto err_out_free_irq; + } + } + /* perform each probe synchronously */ DPRINTK("probe begin\n"); for (i = 0; i < host_set->n_ports; i++) { @@ -5514,6 +5536,8 @@ int ata_device_add(const struct ata_probe_ent *ent) VPRINTK("EXIT, returning %u\n", ent->n_ports); return ent->n_ports; /* success */ +err_out_free_irq: + free_irq(ent->irq, host_set); err_out: for (i = 0; i < host_set->n_ports; i++) { struct ata_port *ap = host_set->ports[i]; @@ -5605,6 +5629,8 @@ void ata_host_set_remove(struct ata_host_set *host_set) ata_port_detach(host_set->ports[i]); free_irq(host_set->irq, host_set); + if (host_set->irq2) + free_irq(host_set->irq2, host_set); for (i = 0; i < host_set->n_ports; i++) { struct ata_port *ap = host_set->ports[i]; @@ -5614,10 +5640,11 @@ void ata_host_set_remove(struct ata_host_set *host_set) if ((ap->flags & ATA_FLAG_NO_LEGACY) == 0) { struct ata_ioports *ioaddr = &ap->ioaddr; - if (ioaddr->cmd_addr == 0x1f0) - release_region(0x1f0, 8); - else if (ioaddr->cmd_addr == 0x170) - release_region(0x170, 8); + /* FIXME: Add -ac IDE pci mods to remove these special cases */ + if (ioaddr->cmd_addr == ATA_PRIMARY_CMD) + release_region(ATA_PRIMARY_CMD, 8); + else if (ioaddr->cmd_addr == ATA_SECONDARY_CMD) + release_region(ATA_SECONDARY_CMD, 8); } scsi_host_put(ap->host); @@ -5735,11 +5762,8 @@ void ata_pci_remove_one (struct pci_dev *pdev) { struct device *dev = pci_dev_to_dev(pdev); struct ata_host_set *host_set = dev_get_drvdata(dev); - struct ata_host_set *host_set2 = host_set->next; ata_host_set_remove(host_set); - if (host_set2) - ata_host_set_remove(host_set2); pci_release_regions(pdev); pci_disable_device(pdev); @@ -5807,14 +5831,6 @@ int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) if (rc) return rc; - if (host_set->next) { - rc = ata_host_set_suspend(host_set->next, mesg); - if (rc) { - ata_host_set_resume(host_set); - return rc; - } - } - ata_pci_device_do_suspend(pdev, mesg); return 0; @@ -5826,9 +5842,6 @@ int ata_pci_device_resume(struct pci_dev *pdev) ata_pci_device_do_resume(pdev); ata_host_set_resume(host_set); - if (host_set->next) - ata_host_set_resume(host_set->next); - return 0; } #endif /* CONFIG_PCI */ -- cgit v1.2.3 From dd5b06c490de72440ec39f814de99a714a45a1a9 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 10 Aug 2006 16:59:12 +0900 Subject: [PATCH] libata: implement dummy port Implement dummy port which can be requested by setting appropriate bit in probe_ent->dummy_port_mask. The dummy port is used as placeholder for stolen legacy port. This allows libata to guarantee that index_of(ap) == ap->port_no == actual_device_port_no, and thus to remove error-prone ap->hard_port_no. As it's used only when one port of a legacy controller is reserved by some other entity (e.g. IDE), the focus is on keeping the added *code* complexity at minimum, so dummy port allocates all libata core resources and acts as a normal port. It just has all dummy port_ops. This patch only implements dummy port. The following patch will make libata use it for stolen legacy ports. Signed-off-by: Tejun Heo --- drivers/scsi/libata-core.c | 61 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 10 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 3634279d896..f2e7e2f13db 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -5311,7 +5311,6 @@ static struct ata_port * ata_port_add(const struct ata_probe_ent *ent, { struct Scsi_Host *shost; struct ata_port *ap; - int rc; DPRINTK("ENTER\n"); @@ -5333,15 +5332,7 @@ static struct ata_port * ata_port_add(const struct ata_probe_ent *ent, ata_port_init(ap, host_set, ent, port_no); ata_port_init_shost(ap, shost); - rc = ap->ops->port_start(ap); - if (rc) - goto err_out; - return ap; - -err_out: - scsi_host_put(shost); - return NULL; } /** @@ -5415,11 +5406,27 @@ int ata_device_add(const struct ata_probe_ent *ent) if (!ap) goto err_out; + host_set->ports[i] = ap; + + /* dummy? */ + if (ent->dummy_port_mask & (1 << i)) { + ata_port_printk(ap, KERN_INFO, "DUMMY\n"); + ap->ops = &ata_dummy_port_ops; + continue; + } + + /* start port */ + rc = ap->ops->port_start(ap); + if (rc) { + host_set->ports[i] = NULL; + scsi_host_put(ap->host); + goto err_out; + } + /* Report the secondary IRQ for second channel legacy */ if (i == 1 && ent->irq2) irq_line = ent->irq2; - host_set->ports[i] = ap; xfer_mode_mask =(ap->udma_mask << ATA_SHIFT_UDMA) | (ap->mwdma_mask << ATA_SHIFT_MWDMA) | (ap->pio_mask << ATA_SHIFT_PIO); @@ -5940,6 +5947,39 @@ u32 ata_wait_register(void __iomem *reg, u32 mask, u32 val, return tmp; } +/* + * Dummy port_ops + */ +static void ata_dummy_noret(struct ata_port *ap) { } +static int ata_dummy_ret0(struct ata_port *ap) { return 0; } +static void ata_dummy_qc_noret(struct ata_queued_cmd *qc) { } + +static u8 ata_dummy_check_status(struct ata_port *ap) +{ + return ATA_DRDY; +} + +static unsigned int ata_dummy_qc_issue(struct ata_queued_cmd *qc) +{ + return AC_ERR_SYSTEM; +} + +const struct ata_port_operations ata_dummy_port_ops = { + .port_disable = ata_port_disable, + .check_status = ata_dummy_check_status, + .check_altstatus = ata_dummy_check_status, + .dev_select = ata_noop_dev_select, + .qc_prep = ata_noop_qc_prep, + .qc_issue = ata_dummy_qc_issue, + .freeze = ata_dummy_noret, + .thaw = ata_dummy_noret, + .error_handler = ata_dummy_noret, + .post_internal_cmd = ata_dummy_qc_noret, + .irq_clear = ata_dummy_noret, + .port_start = ata_dummy_ret0, + .port_stop = ata_dummy_noret, +}; + /* * libata is essentially a library of internal helper functions for * low-level ATA host controller drivers. As such, the API/ABI is @@ -5950,6 +5990,7 @@ u32 ata_wait_register(void __iomem *reg, u32 mask, u32 val, EXPORT_SYMBOL_GPL(sata_deb_timing_normal); EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug); EXPORT_SYMBOL_GPL(sata_deb_timing_long); +EXPORT_SYMBOL_GPL(ata_dummy_port_ops); EXPORT_SYMBOL_GPL(ata_std_bios_param); EXPORT_SYMBOL_GPL(ata_std_ports); EXPORT_SYMBOL_GPL(ata_host_set_init); -- cgit v1.2.3 From c4b01f1de254820175fe2d02b4cf7c0fab335846 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 10 Aug 2006 16:59:14 +0900 Subject: [PATCH] libata: use dummy port for stolen legacy ports Use dummy port for stolen legacy ports. This makes ap->port_no always equal ap->hard_port_no. Signed-off-by: Tejun Heo --- drivers/scsi/libata-bmdma.c | 41 +++++++++++++++++------------------------ drivers/scsi/libata-core.c | 2 -- 2 files changed, 17 insertions(+), 26 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libata-bmdma.c b/drivers/scsi/libata-bmdma.c index e694f6075c3..158f62dbf21 100644 --- a/drivers/scsi/libata-bmdma.c +++ b/drivers/scsi/libata-bmdma.c @@ -872,50 +872,43 @@ static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev, struct ata_probe_ent *probe_ent; unsigned long bmdma = pci_resource_start(pdev, 4); - int port_num = 0; - probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]); if (!probe_ent) return NULL; - probe_ent->legacy_mode = 1; - probe_ent->hard_port_no = 0; + probe_ent->n_ports = 2; probe_ent->private_data = port[0]->private_data; if (port_mask & ATA_PORT_PRIMARY) { probe_ent->irq = 14; - probe_ent->port[port_num].cmd_addr = ATA_PRIMARY_CMD; - probe_ent->port[port_num].altstatus_addr = - probe_ent->port[port_num].ctl_addr = ATA_PRIMARY_CTL; + probe_ent->port[0].cmd_addr = ATA_PRIMARY_CMD; + probe_ent->port[0].altstatus_addr = + probe_ent->port[0].ctl_addr = ATA_PRIMARY_CTL; if (bmdma) { probe_ent->port[0].bmdma_addr = bmdma; if (inb(bmdma + 2) & 0x80) probe_ent->host_set_flags |= ATA_HOST_SIMPLEX; } - ata_std_ports(&probe_ent->port[port_num]); - port_num ++; - } + ata_std_ports(&probe_ent->port[0]); + } else + probe_ent->dummy_port_mask |= ATA_PORT_PRIMARY; + if (port_mask & ATA_PORT_SECONDARY) { - if (port_num == 1) + if (probe_ent->irq) probe_ent->irq2 = 15; - else { - /* Secondary only. IRQ 15 only and "first" port is port 1 */ + else probe_ent->irq = 15; - probe_ent->hard_port_no = 1; - } - probe_ent->port[port_num].cmd_addr = ATA_SECONDARY_CMD; - probe_ent->port[port_num].altstatus_addr = - probe_ent->port[port_num].ctl_addr = ATA_SECONDARY_CTL; + probe_ent->port[1].cmd_addr = ATA_SECONDARY_CMD; + probe_ent->port[1].altstatus_addr = + probe_ent->port[1].ctl_addr = ATA_SECONDARY_CTL; if (bmdma) { - probe_ent->port[port_num].bmdma_addr = bmdma + 8; + probe_ent->port[1].bmdma_addr = bmdma + 8; if (inb(bmdma + 10) & 0x80) probe_ent->host_set_flags |= ATA_HOST_SIMPLEX; } - ata_std_ports(&probe_ent->port[port_num]); - port_num ++; - } - - probe_ent->n_ports = port_num; + ata_std_ports(&probe_ent->port[1]); + } else + probe_ent->dummy_port_mask |= ATA_PORT_SECONDARY; return probe_ent; } diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index f2e7e2f13db..2ef86b4e7f6 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -5224,8 +5224,6 @@ void ata_port_init(struct ata_port *ap, struct ata_host_set *host_set, ap->dev = ent->dev; ap->port_no = port_no; ap->hard_port_no = port_no; - if (ent->legacy_mode) - ap->hard_port_no += ent->hard_port_no; ap->pio_mask = ent->pio_mask; ap->mwdma_mask = ent->mwdma_mask; ap->udma_mask = ent->udma_mask; -- cgit v1.2.3 From 2a88d1ac8dca898a7fb602ddd581bf4d2977509d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 10 Aug 2006 16:59:16 +0900 Subject: [PATCH] libata: replace ap->hard_port_no with ap->port_no Replace ap->hard_port_no with ap->port_no. Signed-off-by: Tejun Heo --- drivers/scsi/ata_piix.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ata_piix.c b/drivers/scsi/ata_piix.c index 40ecab5793e..501755a606e 100644 --- a/drivers/scsi/ata_piix.c +++ b/drivers/scsi/ata_piix.c @@ -487,7 +487,7 @@ static void piix_pata_cbl_detect(struct ata_port *ap) goto cbl40; /* check BIOS cable detect results */ - mask = ap->hard_port_no == 0 ? PIIX_80C_PRI : PIIX_80C_SEC; + mask = ap->port_no == 0 ? PIIX_80C_PRI : PIIX_80C_SEC; pci_read_config_byte(pdev, PIIX_IOCFG, &tmp); if ((tmp & mask) == 0) goto cbl40; @@ -513,7 +513,7 @@ static int piix_pata_prereset(struct ata_port *ap) { struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); - if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->hard_port_no])) { + if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->port_no])) { ata_port_printk(ap, KERN_INFO, "port disabled. ignoring.\n"); ap->eh_context.i.action &= ~ATA_EH_RESET_MASK; return 0; @@ -550,7 +550,7 @@ static int piix_sata_prereset(struct ata_port *ap) struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); struct piix_host_priv *hpriv = ap->host_set->private_data; const unsigned int *map = hpriv->map; - int base = 2 * ap->hard_port_no; + int base = 2 * ap->port_no; unsigned int present = 0; int port, i; u16 pcs; @@ -601,7 +601,7 @@ static void piix_set_piomode (struct ata_port *ap, struct ata_device *adev) unsigned int pio = adev->pio_mode - XFER_PIO_0; struct pci_dev *dev = to_pci_dev(ap->host_set->dev); unsigned int is_slave = (adev->devno != 0); - unsigned int master_port= ap->hard_port_no ? 0x42 : 0x40; + unsigned int master_port= ap->port_no ? 0x42 : 0x40; unsigned int slave_port = 0x44; u16 master_data; u8 slave_data; @@ -619,10 +619,10 @@ static void piix_set_piomode (struct ata_port *ap, struct ata_device *adev) /* enable PPE, IE and TIME */ master_data |= 0x0070; pci_read_config_byte(dev, slave_port, &slave_data); - slave_data &= (ap->hard_port_no ? 0x0f : 0xf0); + slave_data &= (ap->port_no ? 0x0f : 0xf0); slave_data |= (timings[pio][0] << 2) | - (timings[pio][1] << (ap->hard_port_no ? 4 : 0)); + (timings[pio][1] << (ap->port_no ? 4 : 0)); } else { master_data &= 0xccf8; /* enable PPE, IE and TIME */ @@ -652,9 +652,9 @@ static void piix_set_dmamode (struct ata_port *ap, struct ata_device *adev) { unsigned int udma = adev->dma_mode; /* FIXME: MWDMA too */ struct pci_dev *dev = to_pci_dev(ap->host_set->dev); - u8 maslave = ap->hard_port_no ? 0x42 : 0x40; + u8 maslave = ap->port_no ? 0x42 : 0x40; u8 speed = udma; - unsigned int drive_dn = (ap->hard_port_no ? 2 : 0) + adev->devno; + unsigned int drive_dn = (ap->port_no ? 2 : 0) + adev->devno; int a_speed = 3 << (drive_dn * 4); int u_flag = 1 << drive_dn; int v_flag = 0x01 << drive_dn; -- cgit v1.2.3 From 4852ba24f647199be797545226c6d325db231937 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 10 Aug 2006 16:59:18 +0900 Subject: [PATCH] libata: kill unused hard_port_no and legacy_mode Kill unused probe_ent/ap->hard_port_no and probe_ent->legacy_mode. Signed-off-by: Tejun Heo --- drivers/scsi/libata-core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 2ef86b4e7f6..3f963f206d4 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -5223,7 +5223,6 @@ void ata_port_init(struct ata_port *ap, struct ata_host_set *host_set, ap->host_set = host_set; ap->dev = ent->dev; ap->port_no = port_no; - ap->hard_port_no = port_no; ap->pio_mask = ent->pio_mask; ap->mwdma_mask = ent->mwdma_mask; ap->udma_mask = ent->udma_mask; -- cgit v1.2.3 From b7887196e38da54ff893897b80875d632d1a1114 Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Thu, 10 Aug 2006 18:13:18 +0900 Subject: [PATCH] libata: replace pci_module_init() with pci_register_driver() Replace pci_module_init() with pci_register_driver(). Signed-off-by: Pavel Roskin Signed-off-by: Tejun Heo --- drivers/scsi/ahci.c | 2 +- drivers/scsi/ata_piix.c | 4 ++-- drivers/scsi/pdc_adma.c | 2 +- drivers/scsi/sata_mv.c | 2 +- drivers/scsi/sata_nv.c | 2 +- drivers/scsi/sata_promise.c | 2 +- drivers/scsi/sata_qstor.c | 2 +- drivers/scsi/sata_sil.c | 2 +- drivers/scsi/sata_sil24.c | 2 +- drivers/scsi/sata_sis.c | 2 +- drivers/scsi/sata_svw.c | 2 +- drivers/scsi/sata_sx4.c | 2 +- drivers/scsi/sata_uli.c | 2 +- drivers/scsi/sata_via.c | 2 +- drivers/scsi/sata_vsc.c | 2 +- 15 files changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index 68fd7667a08..813031c01fb 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -1665,7 +1665,7 @@ static void ahci_remove_one (struct pci_dev *pdev) static int __init ahci_init(void) { - return pci_module_init(&ahci_pci_driver); + return pci_register_driver(&ahci_pci_driver); } static void __exit ahci_exit(void) diff --git a/drivers/scsi/ata_piix.c b/drivers/scsi/ata_piix.c index 501755a606e..46c34fd5af8 100644 --- a/drivers/scsi/ata_piix.c +++ b/drivers/scsi/ata_piix.c @@ -939,8 +939,8 @@ static int __init piix_init(void) { int rc; - DPRINTK("pci_module_init\n"); - rc = pci_module_init(&piix_pci_driver); + DPRINTK("pci_register_driver\n"); + rc = pci_register_driver(&piix_pci_driver); if (rc) return rc; diff --git a/drivers/scsi/pdc_adma.c b/drivers/scsi/pdc_adma.c index d1f38c32aa1..6b7b62408e4 100644 --- a/drivers/scsi/pdc_adma.c +++ b/drivers/scsi/pdc_adma.c @@ -721,7 +721,7 @@ err_out: static int __init adma_ata_init(void) { - return pci_module_init(&adma_ata_pci_driver); + return pci_register_driver(&adma_ata_pci_driver); } static void __exit adma_ata_exit(void) diff --git a/drivers/scsi/sata_mv.c b/drivers/scsi/sata_mv.c index 1053c7c76b7..a2915a56acc 100644 --- a/drivers/scsi/sata_mv.c +++ b/drivers/scsi/sata_mv.c @@ -2447,7 +2447,7 @@ err_out: static int __init mv_init(void) { - return pci_module_init(&mv_pci_driver); + return pci_register_driver(&mv_pci_driver); } static void __exit mv_exit(void) diff --git a/drivers/scsi/sata_nv.c b/drivers/scsi/sata_nv.c index 56da25581f3..be46df75ab5 100644 --- a/drivers/scsi/sata_nv.c +++ b/drivers/scsi/sata_nv.c @@ -583,7 +583,7 @@ static void nv_ck804_host_stop(struct ata_host_set *host_set) static int __init nv_init(void) { - return pci_module_init(&nv_pci_driver); + return pci_register_driver(&nv_pci_driver); } static void __exit nv_exit(void) diff --git a/drivers/scsi/sata_promise.c b/drivers/scsi/sata_promise.c index 4776f4e5583..a5b3a7db7a9 100644 --- a/drivers/scsi/sata_promise.c +++ b/drivers/scsi/sata_promise.c @@ -824,7 +824,7 @@ err_out: static int __init pdc_ata_init(void) { - return pci_module_init(&pdc_ata_pci_driver); + return pci_register_driver(&pdc_ata_pci_driver); } diff --git a/drivers/scsi/sata_qstor.c b/drivers/scsi/sata_qstor.c index d374c1db0cf..71bd6712b37 100644 --- a/drivers/scsi/sata_qstor.c +++ b/drivers/scsi/sata_qstor.c @@ -712,7 +712,7 @@ err_out: static int __init qs_ata_init(void) { - return pci_module_init(&qs_ata_pci_driver); + return pci_register_driver(&qs_ata_pci_driver); } static void __exit qs_ata_exit(void) diff --git a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c index d0a85073ebf..2393379b239 100644 --- a/drivers/scsi/sata_sil.c +++ b/drivers/scsi/sata_sil.c @@ -714,7 +714,7 @@ static int sil_pci_device_resume(struct pci_dev *pdev) static int __init sil_init(void) { - return pci_module_init(&sil_pci_driver); + return pci_register_driver(&sil_pci_driver); } static void __exit sil_exit(void) diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c index 3f368c7d3ef..3a0161ddc33 100644 --- a/drivers/scsi/sata_sil24.c +++ b/drivers/scsi/sata_sil24.c @@ -1205,7 +1205,7 @@ static int sil24_pci_device_resume(struct pci_dev *pdev) static int __init sil24_init(void) { - return pci_module_init(&sil24_pci_driver); + return pci_register_driver(&sil24_pci_driver); } static void __exit sil24_exit(void) diff --git a/drivers/scsi/sata_sis.c b/drivers/scsi/sata_sis.c index ee6b5df41d3..ac24f66897f 100644 --- a/drivers/scsi/sata_sis.c +++ b/drivers/scsi/sata_sis.c @@ -334,7 +334,7 @@ err_out: static int __init sis_init(void) { - return pci_module_init(&sis_pci_driver); + return pci_register_driver(&sis_pci_driver); } static void __exit sis_exit(void) diff --git a/drivers/scsi/sata_svw.c b/drivers/scsi/sata_svw.c index 7d0858095e1..baf259a966d 100644 --- a/drivers/scsi/sata_svw.c +++ b/drivers/scsi/sata_svw.c @@ -488,7 +488,7 @@ static struct pci_driver k2_sata_pci_driver = { static int __init k2_sata_init(void) { - return pci_module_init(&k2_sata_pci_driver); + return pci_register_driver(&k2_sata_pci_driver); } diff --git a/drivers/scsi/sata_sx4.c b/drivers/scsi/sata_sx4.c index ccc8cad24f7..0da83cba5c1 100644 --- a/drivers/scsi/sata_sx4.c +++ b/drivers/scsi/sata_sx4.c @@ -1482,7 +1482,7 @@ err_out: static int __init pdc_sata_init(void) { - return pci_module_init(&pdc_sata_pci_driver); + return pci_register_driver(&pdc_sata_pci_driver); } diff --git a/drivers/scsi/sata_uli.c b/drivers/scsi/sata_uli.c index 33cdb4867ef..654aae2b25c 100644 --- a/drivers/scsi/sata_uli.c +++ b/drivers/scsi/sata_uli.c @@ -287,7 +287,7 @@ err_out: static int __init uli_init(void) { - return pci_module_init(&uli_pci_driver); + return pci_register_driver(&uli_pci_driver); } static void __exit uli_exit(void) diff --git a/drivers/scsi/sata_via.c b/drivers/scsi/sata_via.c index 03baec2191b..0bf1dbea640 100644 --- a/drivers/scsi/sata_via.c +++ b/drivers/scsi/sata_via.c @@ -381,7 +381,7 @@ err_out: static int __init svia_init(void) { - return pci_module_init(&svia_pci_driver); + return pci_register_driver(&svia_pci_driver); } static void __exit svia_exit(void) diff --git a/drivers/scsi/sata_vsc.c b/drivers/scsi/sata_vsc.c index ad37871594f..4c69a705a48 100644 --- a/drivers/scsi/sata_vsc.c +++ b/drivers/scsi/sata_vsc.c @@ -462,7 +462,7 @@ static struct pci_driver vsc_sata_pci_driver = { static int __init vsc_sata_init(void) { - return pci_module_init(&vsc_sata_pci_driver); + return pci_register_driver(&vsc_sata_pci_driver); } -- cgit v1.2.3 From c6fd280766a050b13360d7c2d59a3d6bd3a27d9a Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Thu, 10 Aug 2006 07:31:37 -0400 Subject: Move libata to drivers/ata. --- drivers/scsi/Kconfig | 138 - drivers/scsi/Makefile | 16 - drivers/scsi/ahci.c | 1684 ------------ drivers/scsi/ata_piix.c | 960 ------- drivers/scsi/libata-bmdma.c | 1109 -------- drivers/scsi/libata-core.c | 6098 ------------------------------------------- drivers/scsi/libata-eh.c | 2246 ---------------- drivers/scsi/libata-scsi.c | 3322 ----------------------- drivers/scsi/libata.h | 122 - drivers/scsi/pdc_adma.c | 740 ------ drivers/scsi/sata_mv.c | 2468 ----------------- drivers/scsi/sata_nv.c | 595 ----- drivers/scsi/sata_promise.c | 844 ------ drivers/scsi/sata_promise.h | 157 -- drivers/scsi/sata_qstor.c | 730 ------ drivers/scsi/sata_sil.c | 723 ----- drivers/scsi/sata_sil24.c | 1222 --------- drivers/scsi/sata_sis.c | 347 --- drivers/scsi/sata_svw.c | 508 ---- drivers/scsi/sata_sx4.c | 1502 ----------- drivers/scsi/sata_uli.c | 300 --- drivers/scsi/sata_via.c | 394 --- drivers/scsi/sata_vsc.c | 482 ---- 23 files changed, 26707 deletions(-) delete mode 100644 drivers/scsi/ahci.c delete mode 100644 drivers/scsi/ata_piix.c delete mode 100644 drivers/scsi/libata-bmdma.c delete mode 100644 drivers/scsi/libata-core.c delete mode 100644 drivers/scsi/libata-eh.c delete mode 100644 drivers/scsi/libata-scsi.c delete mode 100644 drivers/scsi/libata.h delete mode 100644 drivers/scsi/pdc_adma.c delete mode 100644 drivers/scsi/sata_mv.c delete mode 100644 drivers/scsi/sata_nv.c delete mode 100644 drivers/scsi/sata_promise.c delete mode 100644 drivers/scsi/sata_promise.h delete mode 100644 drivers/scsi/sata_qstor.c delete mode 100644 drivers/scsi/sata_sil.c delete mode 100644 drivers/scsi/sata_sil24.c delete mode 100644 drivers/scsi/sata_sis.c delete mode 100644 drivers/scsi/sata_svw.c delete mode 100644 drivers/scsi/sata_sx4.c delete mode 100644 drivers/scsi/sata_uli.c delete mode 100644 drivers/scsi/sata_via.c delete mode 100644 drivers/scsi/sata_vsc.c (limited to 'drivers/scsi') diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 96a81cd1761..2df4d15c963 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -471,67 +471,6 @@ config SCSI_IN2000 source "drivers/scsi/megaraid/Kconfig.megaraid" -config SCSI_SATA - tristate "Serial ATA (SATA) support" - depends on SCSI - help - This driver family supports Serial ATA host controllers - and devices. - - If unsure, say N. - -config SCSI_SATA_AHCI - tristate "AHCI SATA support" - depends on SCSI_SATA && PCI - help - This option enables support for AHCI Serial ATA. - - If unsure, say N. - -config SCSI_SATA_SVW - tristate "ServerWorks Frodo / Apple K2 SATA support" - depends on SCSI_SATA && PCI - help - This option enables support for Broadcom/Serverworks/Apple K2 - SATA support. - - If unsure, say N. - -config SCSI_ATA_PIIX - tristate "Intel PIIX/ICH SATA support" - depends on SCSI_SATA && PCI - help - This option enables support for ICH5/6/7/8 Serial ATA. - If PATA support was enabled previously, this enables - support for select Intel PIIX/ICH PATA host controllers. - - If unsure, say N. - -config SCSI_SATA_MV - tristate "Marvell SATA support (HIGHLY EXPERIMENTAL)" - depends on SCSI_SATA && PCI && EXPERIMENTAL - help - This option enables support for the Marvell Serial ATA family. - Currently supports 88SX[56]0[48][01] chips. - - If unsure, say N. - -config SCSI_SATA_NV - tristate "NVIDIA SATA support" - depends on SCSI_SATA && PCI && EXPERIMENTAL - help - This option enables support for NVIDIA Serial ATA. - - If unsure, say N. - -config SCSI_PDC_ADMA - tristate "Pacific Digital ADMA support" - depends on SCSI_SATA && PCI - help - This option enables support for Pacific Digital ADMA controllers - - If unsure, say N. - config SCSI_HPTIOP tristate "HighPoint RocketRAID 3xxx Controller support" depends on SCSI && PCI @@ -542,83 +481,6 @@ config SCSI_HPTIOP To compile this driver as a module, choose M here; the module will be called hptiop. If unsure, say N. -config SCSI_SATA_QSTOR - tristate "Pacific Digital SATA QStor support" - depends on SCSI_SATA && PCI - help - This option enables support for Pacific Digital Serial ATA QStor. - - If unsure, say N. - -config SCSI_SATA_PROMISE - tristate "Promise SATA TX2/TX4 support" - depends on SCSI_SATA && PCI - help - This option enables support for Promise Serial ATA TX2/TX4. - - If unsure, say N. - -config SCSI_SATA_SX4 - tristate "Promise SATA SX4 support" - depends on SCSI_SATA && PCI && EXPERIMENTAL - help - This option enables support for Promise Serial ATA SX4. - - If unsure, say N. - -config SCSI_SATA_SIL - tristate "Silicon Image SATA support" - depends on SCSI_SATA && PCI && EXPERIMENTAL - help - This option enables support for Silicon Image Serial ATA. - - If unsure, say N. - -config SCSI_SATA_SIL24 - tristate "Silicon Image 3124/3132 SATA support" - depends on SCSI_SATA && PCI && EXPERIMENTAL - help - This option enables support for Silicon Image 3124/3132 Serial ATA. - - If unsure, say N. - -config SCSI_SATA_SIS - tristate "SiS 964/180 SATA support" - depends on SCSI_SATA && PCI && EXPERIMENTAL - help - This option enables support for SiS Serial ATA 964/180. - - If unsure, say N. - -config SCSI_SATA_ULI - tristate "ULi Electronics SATA support" - depends on SCSI_SATA && PCI && EXPERIMENTAL - help - This option enables support for ULi Electronics SATA. - - If unsure, say N. - -config SCSI_SATA_VIA - tristate "VIA SATA support" - depends on SCSI_SATA && PCI - help - This option enables support for VIA Serial ATA. - - If unsure, say N. - -config SCSI_SATA_VITESSE - tristate "VITESSE VSC-7174 / INTEL 31244 SATA support" - depends on SCSI_SATA && PCI - help - This option enables support for Vitesse VSC7174 and Intel 31244 Serial ATA. - - If unsure, say N. - -config SCSI_SATA_INTEL_COMBINED - bool - depends on IDE=y && !BLK_DEV_IDE_SATA && (SCSI_SATA_AHCI || SCSI_ATA_PIIX) - default y - config SCSI_BUSLOGIC tristate "BusLogic SCSI support" depends on (PCI || ISA || MCA) && SCSI && ISA_DMA_API diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index ebd0cf00bf3..b678f957cfe 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -122,21 +122,6 @@ obj-$(CONFIG_SCSI_LASI700) += 53c700.o lasi700.o obj-$(CONFIG_SCSI_NSP32) += nsp32.o obj-$(CONFIG_SCSI_IPR) += ipr.o obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi/ -obj-$(CONFIG_SCSI_SATA_AHCI) += libata.o ahci.o -obj-$(CONFIG_SCSI_SATA_SVW) += libata.o sata_svw.o -obj-$(CONFIG_SCSI_ATA_PIIX) += libata.o ata_piix.o -obj-$(CONFIG_SCSI_SATA_PROMISE) += libata.o sata_promise.o -obj-$(CONFIG_SCSI_SATA_QSTOR) += libata.o sata_qstor.o -obj-$(CONFIG_SCSI_SATA_SIL) += libata.o sata_sil.o -obj-$(CONFIG_SCSI_SATA_SIL24) += libata.o sata_sil24.o -obj-$(CONFIG_SCSI_SATA_VIA) += libata.o sata_via.o -obj-$(CONFIG_SCSI_SATA_VITESSE) += libata.o sata_vsc.o -obj-$(CONFIG_SCSI_SATA_SIS) += libata.o sata_sis.o -obj-$(CONFIG_SCSI_SATA_SX4) += libata.o sata_sx4.o -obj-$(CONFIG_SCSI_SATA_NV) += libata.o sata_nv.o -obj-$(CONFIG_SCSI_SATA_ULI) += libata.o sata_uli.o -obj-$(CONFIG_SCSI_SATA_MV) += libata.o sata_mv.o -obj-$(CONFIG_SCSI_PDC_ADMA) += libata.o pdc_adma.o obj-$(CONFIG_SCSI_HPTIOP) += hptiop.o obj-$(CONFIG_ARM) += arm/ @@ -166,7 +151,6 @@ ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \ CFLAGS_ncr53c8xx.o := $(ncr53c8xx-flags-y) $(ncr53c8xx-flags-m) zalon7xx-objs := zalon.o ncr53c8xx.o NCR_Q720_mod-objs := NCR_Q720.o ncr53c8xx.o -libata-objs := libata-core.o libata-scsi.o libata-bmdma.o libata-eh.o oktagon_esp_mod-objs := oktagon_esp.o oktagon_io.o # Files generated that shall be removed upon make clean diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c deleted file mode 100644 index 813031c01fb..00000000000 --- a/drivers/scsi/ahci.c +++ /dev/null @@ -1,1684 +0,0 @@ -/* - * ahci.c - AHCI SATA support - * - * Maintained by: Jeff Garzik - * Please ALWAYS copy linux-ide@vger.kernel.org - * on emails. - * - * Copyright 2004-2005 Red Hat, Inc. - * - * - * 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 02139, USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - * AHCI hardware documentation: - * http://www.intel.com/technology/serialata/pdf/rev1_0.pdf - * http://www.intel.com/technology/serialata/pdf/rev1_1.pdf - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "ahci" -#define DRV_VERSION "2.0" - - -enum { - AHCI_PCI_BAR = 5, - AHCI_MAX_SG = 168, /* hardware max is 64K */ - AHCI_DMA_BOUNDARY = 0xffffffff, - AHCI_USE_CLUSTERING = 0, - AHCI_MAX_CMDS = 32, - AHCI_CMD_SZ = 32, - AHCI_CMD_SLOT_SZ = AHCI_MAX_CMDS * AHCI_CMD_SZ, - AHCI_RX_FIS_SZ = 256, - AHCI_CMD_TBL_CDB = 0x40, - AHCI_CMD_TBL_HDR_SZ = 0x80, - AHCI_CMD_TBL_SZ = AHCI_CMD_TBL_HDR_SZ + (AHCI_MAX_SG * 16), - AHCI_CMD_TBL_AR_SZ = AHCI_CMD_TBL_SZ * AHCI_MAX_CMDS, - AHCI_PORT_PRIV_DMA_SZ = AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_AR_SZ + - AHCI_RX_FIS_SZ, - AHCI_IRQ_ON_SG = (1 << 31), - AHCI_CMD_ATAPI = (1 << 5), - AHCI_CMD_WRITE = (1 << 6), - AHCI_CMD_PREFETCH = (1 << 7), - AHCI_CMD_RESET = (1 << 8), - AHCI_CMD_CLR_BUSY = (1 << 10), - - RX_FIS_D2H_REG = 0x40, /* offset of D2H Register FIS data */ - RX_FIS_UNK = 0x60, /* offset of Unknown FIS data */ - - board_ahci = 0, - board_ahci_vt8251 = 1, - - /* global controller registers */ - HOST_CAP = 0x00, /* host capabilities */ - HOST_CTL = 0x04, /* global host control */ - HOST_IRQ_STAT = 0x08, /* interrupt status */ - HOST_PORTS_IMPL = 0x0c, /* bitmap of implemented ports */ - HOST_VERSION = 0x10, /* AHCI spec. version compliancy */ - - /* HOST_CTL bits */ - HOST_RESET = (1 << 0), /* reset controller; self-clear */ - HOST_IRQ_EN = (1 << 1), /* global IRQ enable */ - HOST_AHCI_EN = (1 << 31), /* AHCI enabled */ - - /* HOST_CAP bits */ - HOST_CAP_SSC = (1 << 14), /* Slumber capable */ - HOST_CAP_CLO = (1 << 24), /* Command List Override support */ - HOST_CAP_SSS = (1 << 27), /* Staggered Spin-up */ - HOST_CAP_NCQ = (1 << 30), /* Native Command Queueing */ - HOST_CAP_64 = (1 << 31), /* PCI DAC (64-bit DMA) support */ - - /* registers for each SATA port */ - PORT_LST_ADDR = 0x00, /* command list DMA addr */ - PORT_LST_ADDR_HI = 0x04, /* command list DMA addr hi */ - PORT_FIS_ADDR = 0x08, /* FIS rx buf addr */ - PORT_FIS_ADDR_HI = 0x0c, /* FIS rx buf addr hi */ - PORT_IRQ_STAT = 0x10, /* interrupt status */ - PORT_IRQ_MASK = 0x14, /* interrupt enable/disable mask */ - PORT_CMD = 0x18, /* port command */ - PORT_TFDATA = 0x20, /* taskfile data */ - PORT_SIG = 0x24, /* device TF signature */ - PORT_CMD_ISSUE = 0x38, /* command issue */ - PORT_SCR = 0x28, /* SATA phy register block */ - PORT_SCR_STAT = 0x28, /* SATA phy register: SStatus */ - PORT_SCR_CTL = 0x2c, /* SATA phy register: SControl */ - PORT_SCR_ERR = 0x30, /* SATA phy register: SError */ - PORT_SCR_ACT = 0x34, /* SATA phy register: SActive */ - - /* PORT_IRQ_{STAT,MASK} bits */ - PORT_IRQ_COLD_PRES = (1 << 31), /* cold presence detect */ - PORT_IRQ_TF_ERR = (1 << 30), /* task file error */ - PORT_IRQ_HBUS_ERR = (1 << 29), /* host bus fatal error */ - PORT_IRQ_HBUS_DATA_ERR = (1 << 28), /* host bus data error */ - PORT_IRQ_IF_ERR = (1 << 27), /* interface fatal error */ - PORT_IRQ_IF_NONFATAL = (1 << 26), /* interface non-fatal error */ - PORT_IRQ_OVERFLOW = (1 << 24), /* xfer exhausted available S/G */ - PORT_IRQ_BAD_PMP = (1 << 23), /* incorrect port multiplier */ - - PORT_IRQ_PHYRDY = (1 << 22), /* PhyRdy changed */ - PORT_IRQ_DEV_ILCK = (1 << 7), /* device interlock */ - PORT_IRQ_CONNECT = (1 << 6), /* port connect change status */ - PORT_IRQ_SG_DONE = (1 << 5), /* descriptor processed */ - PORT_IRQ_UNK_FIS = (1 << 4), /* unknown FIS rx'd */ - PORT_IRQ_SDB_FIS = (1 << 3), /* Set Device Bits FIS rx'd */ - PORT_IRQ_DMAS_FIS = (1 << 2), /* DMA Setup FIS rx'd */ - PORT_IRQ_PIOS_FIS = (1 << 1), /* PIO Setup FIS rx'd */ - PORT_IRQ_D2H_REG_FIS = (1 << 0), /* D2H Register FIS rx'd */ - - PORT_IRQ_FREEZE = PORT_IRQ_HBUS_ERR | - PORT_IRQ_IF_ERR | - PORT_IRQ_CONNECT | - PORT_IRQ_PHYRDY | - PORT_IRQ_UNK_FIS, - PORT_IRQ_ERROR = PORT_IRQ_FREEZE | - PORT_IRQ_TF_ERR | - PORT_IRQ_HBUS_DATA_ERR, - DEF_PORT_IRQ = PORT_IRQ_ERROR | PORT_IRQ_SG_DONE | - PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS | - PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS, - - /* PORT_CMD bits */ - PORT_CMD_ATAPI = (1 << 24), /* Device is ATAPI */ - PORT_CMD_LIST_ON = (1 << 15), /* cmd list DMA engine running */ - PORT_CMD_FIS_ON = (1 << 14), /* FIS DMA engine running */ - PORT_CMD_FIS_RX = (1 << 4), /* Enable FIS receive DMA engine */ - PORT_CMD_CLO = (1 << 3), /* Command list override */ - PORT_CMD_POWER_ON = (1 << 2), /* Power up device */ - PORT_CMD_SPIN_UP = (1 << 1), /* Spin up device */ - PORT_CMD_START = (1 << 0), /* Enable port DMA engine */ - - PORT_CMD_ICC_MASK = (0xf << 28), /* i/f ICC state mask */ - PORT_CMD_ICC_ACTIVE = (0x1 << 28), /* Put i/f in active state */ - PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */ - PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */ - - /* hpriv->flags bits */ - AHCI_FLAG_MSI = (1 << 0), - - /* ap->flags bits */ - AHCI_FLAG_RESET_NEEDS_CLO = (1 << 24), - AHCI_FLAG_NO_NCQ = (1 << 25), -}; - -struct ahci_cmd_hdr { - u32 opts; - u32 status; - u32 tbl_addr; - u32 tbl_addr_hi; - u32 reserved[4]; -}; - -struct ahci_sg { - u32 addr; - u32 addr_hi; - u32 reserved; - u32 flags_size; -}; - -struct ahci_host_priv { - unsigned long flags; - u32 cap; /* cache of HOST_CAP register */ - u32 port_map; /* cache of HOST_PORTS_IMPL reg */ -}; - -struct ahci_port_priv { - struct ahci_cmd_hdr *cmd_slot; - dma_addr_t cmd_slot_dma; - void *cmd_tbl; - dma_addr_t cmd_tbl_dma; - void *rx_fis; - dma_addr_t rx_fis_dma; -}; - -static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg); -static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); -static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc); -static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *regs); -static void ahci_irq_clear(struct ata_port *ap); -static int ahci_port_start(struct ata_port *ap); -static void ahci_port_stop(struct ata_port *ap); -static void ahci_tf_read(struct ata_port *ap, struct ata_taskfile *tf); -static void ahci_qc_prep(struct ata_queued_cmd *qc); -static u8 ahci_check_status(struct ata_port *ap); -static void ahci_freeze(struct ata_port *ap); -static void ahci_thaw(struct ata_port *ap); -static void ahci_error_handler(struct ata_port *ap); -static void ahci_post_internal_cmd(struct ata_queued_cmd *qc); -static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg); -static int ahci_port_resume(struct ata_port *ap); -static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); -static int ahci_pci_device_resume(struct pci_dev *pdev); -static void ahci_remove_one (struct pci_dev *pdev); - -static struct scsi_host_template ahci_sht = { - .module = THIS_MODULE, - .name = DRV_NAME, - .ioctl = ata_scsi_ioctl, - .queuecommand = ata_scsi_queuecmd, - .change_queue_depth = ata_scsi_change_queue_depth, - .can_queue = AHCI_MAX_CMDS - 1, - .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = AHCI_MAX_SG, - .cmd_per_lun = ATA_SHT_CMD_PER_LUN, - .emulated = ATA_SHT_EMULATED, - .use_clustering = AHCI_USE_CLUSTERING, - .proc_name = DRV_NAME, - .dma_boundary = AHCI_DMA_BOUNDARY, - .slave_configure = ata_scsi_slave_config, - .slave_destroy = ata_scsi_slave_destroy, - .bios_param = ata_std_bios_param, - .suspend = ata_scsi_device_suspend, - .resume = ata_scsi_device_resume, -}; - -static const struct ata_port_operations ahci_ops = { - .port_disable = ata_port_disable, - - .check_status = ahci_check_status, - .check_altstatus = ahci_check_status, - .dev_select = ata_noop_dev_select, - - .tf_read = ahci_tf_read, - - .qc_prep = ahci_qc_prep, - .qc_issue = ahci_qc_issue, - - .irq_handler = ahci_interrupt, - .irq_clear = ahci_irq_clear, - - .scr_read = ahci_scr_read, - .scr_write = ahci_scr_write, - - .freeze = ahci_freeze, - .thaw = ahci_thaw, - - .error_handler = ahci_error_handler, - .post_internal_cmd = ahci_post_internal_cmd, - - .port_suspend = ahci_port_suspend, - .port_resume = ahci_port_resume, - - .port_start = ahci_port_start, - .port_stop = ahci_port_stop, -}; - -static const struct ata_port_info ahci_port_info[] = { - /* board_ahci */ - { - .sht = &ahci_sht, - .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - ATA_FLAG_SKIP_D2H_BSY, - .pio_mask = 0x1f, /* pio0-4 */ - .udma_mask = 0x7f, /* udma0-6 ; FIXME */ - .port_ops = &ahci_ops, - }, - /* board_ahci_vt8251 */ - { - .sht = &ahci_sht, - .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - ATA_FLAG_SKIP_D2H_BSY | - AHCI_FLAG_RESET_NEEDS_CLO | AHCI_FLAG_NO_NCQ, - .pio_mask = 0x1f, /* pio0-4 */ - .udma_mask = 0x7f, /* udma0-6 ; FIXME */ - .port_ops = &ahci_ops, - }, -}; - -static const struct pci_device_id ahci_pci_tbl[] = { - /* Intel */ - { PCI_VENDOR_ID_INTEL, 0x2652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH6 */ - { PCI_VENDOR_ID_INTEL, 0x2653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH6M */ - { PCI_VENDOR_ID_INTEL, 0x27c1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH7 */ - { PCI_VENDOR_ID_INTEL, 0x27c5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH7M */ - { PCI_VENDOR_ID_INTEL, 0x27c3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH7R */ - { PCI_VENDOR_ID_AL, 0x5288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ULi M5288 */ - { PCI_VENDOR_ID_INTEL, 0x2681, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ESB2 */ - { PCI_VENDOR_ID_INTEL, 0x2682, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ESB2 */ - { PCI_VENDOR_ID_INTEL, 0x2683, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ESB2 */ - { PCI_VENDOR_ID_INTEL, 0x27c6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH7-M DH */ - { PCI_VENDOR_ID_INTEL, 0x2821, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH8 */ - { PCI_VENDOR_ID_INTEL, 0x2822, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH8 */ - { PCI_VENDOR_ID_INTEL, 0x2824, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH8 */ - { PCI_VENDOR_ID_INTEL, 0x2829, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH8M */ - { PCI_VENDOR_ID_INTEL, 0x282a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH8M */ - - /* JMicron */ - { 0x197b, 0x2360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* JMicron JMB360 */ - { 0x197b, 0x2361, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* JMicron JMB361 */ - { 0x197b, 0x2363, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* JMicron JMB363 */ - { 0x197b, 0x2365, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* JMicron JMB365 */ - { 0x197b, 0x2366, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* JMicron JMB366 */ - - /* ATI */ - { PCI_VENDOR_ID_ATI, 0x4380, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ATI SB600 non-raid */ - { PCI_VENDOR_ID_ATI, 0x4381, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ATI SB600 raid */ - - /* VIA */ - { PCI_VENDOR_ID_VIA, 0x3349, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci_vt8251 }, /* VIA VT8251 */ - - /* NVIDIA */ - { PCI_VENDOR_ID_NVIDIA, 0x044c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* MCP65 */ - { PCI_VENDOR_ID_NVIDIA, 0x044d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* MCP65 */ - { PCI_VENDOR_ID_NVIDIA, 0x044e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* MCP65 */ - { PCI_VENDOR_ID_NVIDIA, 0x044f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* MCP65 */ - - /* SiS */ - { PCI_VENDOR_ID_SI, 0x1184, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* SiS 966 */ - { PCI_VENDOR_ID_SI, 0x1185, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* SiS 966 */ - { PCI_VENDOR_ID_SI, 0x0186, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* SiS 968 */ - - { } /* terminate list */ -}; - - -static struct pci_driver ahci_pci_driver = { - .name = DRV_NAME, - .id_table = ahci_pci_tbl, - .probe = ahci_init_one, - .suspend = ahci_pci_device_suspend, - .resume = ahci_pci_device_resume, - .remove = ahci_remove_one, -}; - - -static inline unsigned long ahci_port_base_ul (unsigned long base, unsigned int port) -{ - return base + 0x100 + (port * 0x80); -} - -static inline void __iomem *ahci_port_base (void __iomem *base, unsigned int port) -{ - return (void __iomem *) ahci_port_base_ul((unsigned long)base, port); -} - -static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in) -{ - unsigned int sc_reg; - - switch (sc_reg_in) { - case SCR_STATUS: sc_reg = 0; break; - case SCR_CONTROL: sc_reg = 1; break; - case SCR_ERROR: sc_reg = 2; break; - case SCR_ACTIVE: sc_reg = 3; break; - default: - return 0xffffffffU; - } - - return readl((void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4)); -} - - -static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in, - u32 val) -{ - unsigned int sc_reg; - - switch (sc_reg_in) { - case SCR_STATUS: sc_reg = 0; break; - case SCR_CONTROL: sc_reg = 1; break; - case SCR_ERROR: sc_reg = 2; break; - case SCR_ACTIVE: sc_reg = 3; break; - default: - return; - } - - writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4)); -} - -static void ahci_start_engine(void __iomem *port_mmio) -{ - u32 tmp; - - /* start DMA */ - tmp = readl(port_mmio + PORT_CMD); - tmp |= PORT_CMD_START; - writel(tmp, port_mmio + PORT_CMD); - readl(port_mmio + PORT_CMD); /* flush */ -} - -static int ahci_stop_engine(void __iomem *port_mmio) -{ - u32 tmp; - - tmp = readl(port_mmio + PORT_CMD); - - /* check if the HBA is idle */ - if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0) - return 0; - - /* setting HBA to idle */ - tmp &= ~PORT_CMD_START; - writel(tmp, port_mmio + PORT_CMD); - - /* wait for engine to stop. This could be as long as 500 msec */ - tmp = ata_wait_register(port_mmio + PORT_CMD, - PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500); - if (tmp & PORT_CMD_LIST_ON) - return -EIO; - - return 0; -} - -static void ahci_start_fis_rx(void __iomem *port_mmio, u32 cap, - dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma) -{ - u32 tmp; - - /* set FIS registers */ - if (cap & HOST_CAP_64) - writel((cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI); - writel(cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR); - - if (cap & HOST_CAP_64) - writel((rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI); - writel(rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR); - - /* enable FIS reception */ - tmp = readl(port_mmio + PORT_CMD); - tmp |= PORT_CMD_FIS_RX; - writel(tmp, port_mmio + PORT_CMD); - - /* flush */ - readl(port_mmio + PORT_CMD); -} - -static int ahci_stop_fis_rx(void __iomem *port_mmio) -{ - u32 tmp; - - /* disable FIS reception */ - tmp = readl(port_mmio + PORT_CMD); - tmp &= ~PORT_CMD_FIS_RX; - writel(tmp, port_mmio + PORT_CMD); - - /* wait for completion, spec says 500ms, give it 1000 */ - tmp = ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_FIS_ON, - PORT_CMD_FIS_ON, 10, 1000); - if (tmp & PORT_CMD_FIS_ON) - return -EBUSY; - - return 0; -} - -static void ahci_power_up(void __iomem *port_mmio, u32 cap) -{ - u32 cmd; - - cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK; - - /* spin up device */ - if (cap & HOST_CAP_SSS) { - cmd |= PORT_CMD_SPIN_UP; - writel(cmd, port_mmio + PORT_CMD); - } - - /* wake up link */ - writel(cmd | PORT_CMD_ICC_ACTIVE, port_mmio + PORT_CMD); -} - -static void ahci_power_down(void __iomem *port_mmio, u32 cap) -{ - u32 cmd, scontrol; - - cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK; - - if (cap & HOST_CAP_SSC) { - /* enable transitions to slumber mode */ - scontrol = readl(port_mmio + PORT_SCR_CTL); - if ((scontrol & 0x0f00) > 0x100) { - scontrol &= ~0xf00; - writel(scontrol, port_mmio + PORT_SCR_CTL); - } - - /* put device into slumber mode */ - writel(cmd | PORT_CMD_ICC_SLUMBER, port_mmio + PORT_CMD); - - /* wait for the transition to complete */ - ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_ICC_SLUMBER, - PORT_CMD_ICC_SLUMBER, 1, 50); - } - - /* put device into listen mode */ - if (cap & HOST_CAP_SSS) { - /* first set PxSCTL.DET to 0 */ - scontrol = readl(port_mmio + PORT_SCR_CTL); - scontrol &= ~0xf; - writel(scontrol, port_mmio + PORT_SCR_CTL); - - /* then set PxCMD.SUD to 0 */ - cmd &= ~PORT_CMD_SPIN_UP; - writel(cmd, port_mmio + PORT_CMD); - } -} - -static void ahci_init_port(void __iomem *port_mmio, u32 cap, - dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma) -{ - /* power up */ - ahci_power_up(port_mmio, cap); - - /* enable FIS reception */ - ahci_start_fis_rx(port_mmio, cap, cmd_slot_dma, rx_fis_dma); - - /* enable DMA */ - ahci_start_engine(port_mmio); -} - -static int ahci_deinit_port(void __iomem *port_mmio, u32 cap, const char **emsg) -{ - int rc; - - /* disable DMA */ - rc = ahci_stop_engine(port_mmio); - if (rc) { - *emsg = "failed to stop engine"; - return rc; - } - - /* disable FIS reception */ - rc = ahci_stop_fis_rx(port_mmio); - if (rc) { - *emsg = "failed stop FIS RX"; - return rc; - } - - /* put device into slumber mode */ - ahci_power_down(port_mmio, cap); - - return 0; -} - -static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev) -{ - u32 cap_save, tmp; - - cap_save = readl(mmio + HOST_CAP); - cap_save &= ( (1<<28) | (1<<17) ); - cap_save |= (1 << 27); - - /* global controller reset */ - tmp = readl(mmio + HOST_CTL); - if ((tmp & HOST_RESET) == 0) { - writel(tmp | HOST_RESET, mmio + HOST_CTL); - readl(mmio + HOST_CTL); /* flush */ - } - - /* reset must complete within 1 second, or - * the hardware should be considered fried. - */ - ssleep(1); - - tmp = readl(mmio + HOST_CTL); - if (tmp & HOST_RESET) { - dev_printk(KERN_ERR, &pdev->dev, - "controller reset failed (0x%x)\n", tmp); - return -EIO; - } - - writel(HOST_AHCI_EN, mmio + HOST_CTL); - (void) readl(mmio + HOST_CTL); /* flush */ - writel(cap_save, mmio + HOST_CAP); - writel(0xf, mmio + HOST_PORTS_IMPL); - (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ - - if (pdev->vendor == PCI_VENDOR_ID_INTEL) { - u16 tmp16; - - /* configure PCS */ - pci_read_config_word(pdev, 0x92, &tmp16); - tmp16 |= 0xf; - pci_write_config_word(pdev, 0x92, tmp16); - } - - return 0; -} - -static void ahci_init_controller(void __iomem *mmio, struct pci_dev *pdev, - int n_ports, u32 cap) -{ - int i, rc; - u32 tmp; - - for (i = 0; i < n_ports; i++) { - void __iomem *port_mmio = ahci_port_base(mmio, i); - const char *emsg = NULL; - -#if 0 /* BIOSen initialize this incorrectly */ - if (!(hpriv->port_map & (1 << i))) - continue; -#endif - - /* make sure port is not active */ - rc = ahci_deinit_port(port_mmio, cap, &emsg); - if (rc) - dev_printk(KERN_WARNING, &pdev->dev, - "%s (%d)\n", emsg, rc); - - /* clear SError */ - tmp = readl(port_mmio + PORT_SCR_ERR); - VPRINTK("PORT_SCR_ERR 0x%x\n", tmp); - writel(tmp, port_mmio + PORT_SCR_ERR); - - /* clear port IRQ */ - tmp = readl(port_mmio + PORT_IRQ_STAT); - VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); - if (tmp) - writel(tmp, port_mmio + PORT_IRQ_STAT); - - writel(1 << i, mmio + HOST_IRQ_STAT); - } - - tmp = readl(mmio + HOST_CTL); - VPRINTK("HOST_CTL 0x%x\n", tmp); - writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL); - tmp = readl(mmio + HOST_CTL); - VPRINTK("HOST_CTL 0x%x\n", tmp); -} - -static unsigned int ahci_dev_classify(struct ata_port *ap) -{ - void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr; - struct ata_taskfile tf; - u32 tmp; - - tmp = readl(port_mmio + PORT_SIG); - tf.lbah = (tmp >> 24) & 0xff; - tf.lbam = (tmp >> 16) & 0xff; - tf.lbal = (tmp >> 8) & 0xff; - tf.nsect = (tmp) & 0xff; - - return ata_dev_classify(&tf); -} - -static void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag, - u32 opts) -{ - dma_addr_t cmd_tbl_dma; - - cmd_tbl_dma = pp->cmd_tbl_dma + tag * AHCI_CMD_TBL_SZ; - - pp->cmd_slot[tag].opts = cpu_to_le32(opts); - pp->cmd_slot[tag].status = 0; - pp->cmd_slot[tag].tbl_addr = cpu_to_le32(cmd_tbl_dma & 0xffffffff); - pp->cmd_slot[tag].tbl_addr_hi = cpu_to_le32((cmd_tbl_dma >> 16) >> 16); -} - -static int ahci_clo(struct ata_port *ap) -{ - void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr; - struct ahci_host_priv *hpriv = ap->host_set->private_data; - u32 tmp; - - if (!(hpriv->cap & HOST_CAP_CLO)) - return -EOPNOTSUPP; - - tmp = readl(port_mmio + PORT_CMD); - tmp |= PORT_CMD_CLO; - writel(tmp, port_mmio + PORT_CMD); - - tmp = ata_wait_register(port_mmio + PORT_CMD, - PORT_CMD_CLO, PORT_CMD_CLO, 1, 500); - if (tmp & PORT_CMD_CLO) - return -EIO; - - return 0; -} - -static int ahci_prereset(struct ata_port *ap) -{ - if ((ap->flags & AHCI_FLAG_RESET_NEEDS_CLO) && - (ata_busy_wait(ap, ATA_BUSY, 1000) & ATA_BUSY)) { - /* ATA_BUSY hasn't cleared, so send a CLO */ - ahci_clo(ap); - } - - return ata_std_prereset(ap); -} - -static int ahci_softreset(struct ata_port *ap, unsigned int *class) -{ - struct ahci_port_priv *pp = ap->private_data; - void __iomem *mmio = ap->host_set->mmio_base; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - const u32 cmd_fis_len = 5; /* five dwords */ - const char *reason = NULL; - struct ata_taskfile tf; - u32 tmp; - u8 *fis; - int rc; - - DPRINTK("ENTER\n"); - - if (ata_port_offline(ap)) { - DPRINTK("PHY reports no device\n"); - *class = ATA_DEV_NONE; - return 0; - } - - /* prepare for SRST (AHCI-1.1 10.4.1) */ - rc = ahci_stop_engine(port_mmio); - if (rc) { - reason = "failed to stop engine"; - goto fail_restart; - } - - /* check BUSY/DRQ, perform Command List Override if necessary */ - ahci_tf_read(ap, &tf); - if (tf.command & (ATA_BUSY | ATA_DRQ)) { - rc = ahci_clo(ap); - - if (rc == -EOPNOTSUPP) { - reason = "port busy but CLO unavailable"; - goto fail_restart; - } else if (rc) { - reason = "port busy but CLO failed"; - goto fail_restart; - } - } - - /* restart engine */ - ahci_start_engine(port_mmio); - - ata_tf_init(ap->device, &tf); - fis = pp->cmd_tbl; - - /* issue the first D2H Register FIS */ - ahci_fill_cmd_slot(pp, 0, - cmd_fis_len | AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY); - - tf.ctl |= ATA_SRST; - ata_tf_to_fis(&tf, fis, 0); - fis[1] &= ~(1 << 7); /* turn off Command FIS bit */ - - writel(1, port_mmio + PORT_CMD_ISSUE); - - tmp = ata_wait_register(port_mmio + PORT_CMD_ISSUE, 0x1, 0x1, 1, 500); - if (tmp & 0x1) { - rc = -EIO; - reason = "1st FIS failed"; - goto fail; - } - - /* spec says at least 5us, but be generous and sleep for 1ms */ - msleep(1); - - /* issue the second D2H Register FIS */ - ahci_fill_cmd_slot(pp, 0, cmd_fis_len); - - tf.ctl &= ~ATA_SRST; - ata_tf_to_fis(&tf, fis, 0); - fis[1] &= ~(1 << 7); /* turn off Command FIS bit */ - - writel(1, port_mmio + PORT_CMD_ISSUE); - readl(port_mmio + PORT_CMD_ISSUE); /* flush */ - - /* spec mandates ">= 2ms" before checking status. - * We wait 150ms, because that was the magic delay used for - * ATAPI devices in Hale Landis's ATADRVR, for the period of time - * between when the ATA command register is written, and then - * status is checked. Because waiting for "a while" before - * checking status is fine, post SRST, we perform this magic - * delay here as well. - */ - msleep(150); - - *class = ATA_DEV_NONE; - if (ata_port_online(ap)) { - if (ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT)) { - rc = -EIO; - reason = "device not ready"; - goto fail; - } - *class = ahci_dev_classify(ap); - } - - DPRINTK("EXIT, class=%u\n", *class); - return 0; - - fail_restart: - ahci_start_engine(port_mmio); - fail: - ata_port_printk(ap, KERN_ERR, "softreset failed (%s)\n", reason); - return rc; -} - -static int ahci_hardreset(struct ata_port *ap, unsigned int *class) -{ - struct ahci_port_priv *pp = ap->private_data; - u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; - struct ata_taskfile tf; - void __iomem *mmio = ap->host_set->mmio_base; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - int rc; - - DPRINTK("ENTER\n"); - - ahci_stop_engine(port_mmio); - - /* clear D2H reception area to properly wait for D2H FIS */ - ata_tf_init(ap->device, &tf); - tf.command = 0xff; - ata_tf_to_fis(&tf, d2h_fis, 0); - - rc = sata_std_hardreset(ap, class); - - ahci_start_engine(port_mmio); - - if (rc == 0 && ata_port_online(ap)) - *class = ahci_dev_classify(ap); - if (*class == ATA_DEV_UNKNOWN) - *class = ATA_DEV_NONE; - - DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); - return rc; -} - -static void ahci_postreset(struct ata_port *ap, unsigned int *class) -{ - void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr; - u32 new_tmp, tmp; - - ata_std_postreset(ap, class); - - /* Make sure port's ATAPI bit is set appropriately */ - new_tmp = tmp = readl(port_mmio + PORT_CMD); - if (*class == ATA_DEV_ATAPI) - new_tmp |= PORT_CMD_ATAPI; - else - new_tmp &= ~PORT_CMD_ATAPI; - if (new_tmp != tmp) { - writel(new_tmp, port_mmio + PORT_CMD); - readl(port_mmio + PORT_CMD); /* flush */ - } -} - -static u8 ahci_check_status(struct ata_port *ap) -{ - void __iomem *mmio = (void __iomem *) ap->ioaddr.cmd_addr; - - return readl(mmio + PORT_TFDATA) & 0xFF; -} - -static void ahci_tf_read(struct ata_port *ap, struct ata_taskfile *tf) -{ - struct ahci_port_priv *pp = ap->private_data; - u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; - - ata_tf_from_fis(d2h_fis, tf); -} - -static unsigned int ahci_fill_sg(struct ata_queued_cmd *qc, void *cmd_tbl) -{ - struct scatterlist *sg; - struct ahci_sg *ahci_sg; - unsigned int n_sg = 0; - - VPRINTK("ENTER\n"); - - /* - * Next, the S/G list. - */ - ahci_sg = cmd_tbl + AHCI_CMD_TBL_HDR_SZ; - ata_for_each_sg(sg, qc) { - dma_addr_t addr = sg_dma_address(sg); - u32 sg_len = sg_dma_len(sg); - - ahci_sg->addr = cpu_to_le32(addr & 0xffffffff); - ahci_sg->addr_hi = cpu_to_le32((addr >> 16) >> 16); - ahci_sg->flags_size = cpu_to_le32(sg_len - 1); - - ahci_sg++; - n_sg++; - } - - return n_sg; -} - -static void ahci_qc_prep(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct ahci_port_priv *pp = ap->private_data; - int is_atapi = is_atapi_taskfile(&qc->tf); - void *cmd_tbl; - u32 opts; - const u32 cmd_fis_len = 5; /* five dwords */ - unsigned int n_elem; - - /* - * Fill in command table information. First, the header, - * a SATA Register - Host to Device command FIS. - */ - cmd_tbl = pp->cmd_tbl + qc->tag * AHCI_CMD_TBL_SZ; - - ata_tf_to_fis(&qc->tf, cmd_tbl, 0); - if (is_atapi) { - memset(cmd_tbl + AHCI_CMD_TBL_CDB, 0, 32); - memcpy(cmd_tbl + AHCI_CMD_TBL_CDB, qc->cdb, qc->dev->cdb_len); - } - - n_elem = 0; - if (qc->flags & ATA_QCFLAG_DMAMAP) - n_elem = ahci_fill_sg(qc, cmd_tbl); - - /* - * Fill in command slot information. - */ - opts = cmd_fis_len | n_elem << 16; - if (qc->tf.flags & ATA_TFLAG_WRITE) - opts |= AHCI_CMD_WRITE; - if (is_atapi) - opts |= AHCI_CMD_ATAPI | AHCI_CMD_PREFETCH; - - ahci_fill_cmd_slot(pp, qc->tag, opts); -} - -static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) -{ - struct ahci_port_priv *pp = ap->private_data; - struct ata_eh_info *ehi = &ap->eh_info; - unsigned int err_mask = 0, action = 0; - struct ata_queued_cmd *qc; - u32 serror; - - ata_ehi_clear_desc(ehi); - - /* AHCI needs SError cleared; otherwise, it might lock up */ - serror = ahci_scr_read(ap, SCR_ERROR); - ahci_scr_write(ap, SCR_ERROR, serror); - - /* analyze @irq_stat */ - ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat); - - if (irq_stat & PORT_IRQ_TF_ERR) - err_mask |= AC_ERR_DEV; - - if (irq_stat & (PORT_IRQ_HBUS_ERR | PORT_IRQ_HBUS_DATA_ERR)) { - err_mask |= AC_ERR_HOST_BUS; - action |= ATA_EH_SOFTRESET; - } - - if (irq_stat & PORT_IRQ_IF_ERR) { - err_mask |= AC_ERR_ATA_BUS; - action |= ATA_EH_SOFTRESET; - ata_ehi_push_desc(ehi, ", interface fatal error"); - } - - if (irq_stat & (PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY)) { - ata_ehi_hotplugged(ehi); - ata_ehi_push_desc(ehi, ", %s", irq_stat & PORT_IRQ_CONNECT ? - "connection status changed" : "PHY RDY changed"); - } - - if (irq_stat & PORT_IRQ_UNK_FIS) { - u32 *unk = (u32 *)(pp->rx_fis + RX_FIS_UNK); - - err_mask |= AC_ERR_HSM; - action |= ATA_EH_SOFTRESET; - ata_ehi_push_desc(ehi, ", unknown FIS %08x %08x %08x %08x", - unk[0], unk[1], unk[2], unk[3]); - } - - /* okay, let's hand over to EH */ - ehi->serror |= serror; - ehi->action |= action; - - qc = ata_qc_from_tag(ap, ap->active_tag); - if (qc) - qc->err_mask |= err_mask; - else - ehi->err_mask |= err_mask; - - if (irq_stat & PORT_IRQ_FREEZE) - ata_port_freeze(ap); - else - ata_port_abort(ap); -} - -static void ahci_host_intr(struct ata_port *ap) -{ - void __iomem *mmio = ap->host_set->mmio_base; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - struct ata_eh_info *ehi = &ap->eh_info; - u32 status, qc_active; - int rc; - - status = readl(port_mmio + PORT_IRQ_STAT); - writel(status, port_mmio + PORT_IRQ_STAT); - - if (unlikely(status & PORT_IRQ_ERROR)) { - ahci_error_intr(ap, status); - return; - } - - if (ap->sactive) - qc_active = readl(port_mmio + PORT_SCR_ACT); - else - qc_active = readl(port_mmio + PORT_CMD_ISSUE); - - rc = ata_qc_complete_multiple(ap, qc_active, NULL); - if (rc > 0) - return; - if (rc < 0) { - ehi->err_mask |= AC_ERR_HSM; - ehi->action |= ATA_EH_SOFTRESET; - ata_port_freeze(ap); - return; - } - - /* hmmm... a spurious interupt */ - - /* some devices send D2H reg with I bit set during NCQ command phase */ - if (ap->sactive && status & PORT_IRQ_D2H_REG_FIS) - return; - - /* ignore interim PIO setup fis interrupts */ - if (ata_tag_valid(ap->active_tag) && (status & PORT_IRQ_PIOS_FIS)) - return; - - if (ata_ratelimit()) - ata_port_printk(ap, KERN_INFO, "spurious interrupt " - "(irq_stat 0x%x active_tag %d sactive 0x%x)\n", - status, ap->active_tag, ap->sactive); -} - -static void ahci_irq_clear(struct ata_port *ap) -{ - /* TODO */ -} - -static irqreturn_t ahci_interrupt(int irq, void *dev_instance, struct pt_regs *regs) -{ - struct ata_host_set *host_set = dev_instance; - struct ahci_host_priv *hpriv; - unsigned int i, handled = 0; - void __iomem *mmio; - u32 irq_stat, irq_ack = 0; - - VPRINTK("ENTER\n"); - - hpriv = host_set->private_data; - mmio = host_set->mmio_base; - - /* sigh. 0xffffffff is a valid return from h/w */ - irq_stat = readl(mmio + HOST_IRQ_STAT); - irq_stat &= hpriv->port_map; - if (!irq_stat) - return IRQ_NONE; - - spin_lock(&host_set->lock); - - for (i = 0; i < host_set->n_ports; i++) { - struct ata_port *ap; - - if (!(irq_stat & (1 << i))) - continue; - - ap = host_set->ports[i]; - if (ap) { - ahci_host_intr(ap); - VPRINTK("port %u\n", i); - } else { - VPRINTK("port %u (no irq)\n", i); - if (ata_ratelimit()) - dev_printk(KERN_WARNING, host_set->dev, - "interrupt on disabled port %u\n", i); - } - - irq_ack |= (1 << i); - } - - if (irq_ack) { - writel(irq_ack, mmio + HOST_IRQ_STAT); - handled = 1; - } - - spin_unlock(&host_set->lock); - - VPRINTK("EXIT\n"); - - return IRQ_RETVAL(handled); -} - -static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr; - - if (qc->tf.protocol == ATA_PROT_NCQ) - writel(1 << qc->tag, port_mmio + PORT_SCR_ACT); - writel(1 << qc->tag, port_mmio + PORT_CMD_ISSUE); - readl(port_mmio + PORT_CMD_ISSUE); /* flush */ - - return 0; -} - -static void ahci_freeze(struct ata_port *ap) -{ - void __iomem *mmio = ap->host_set->mmio_base; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - - /* turn IRQ off */ - writel(0, port_mmio + PORT_IRQ_MASK); -} - -static void ahci_thaw(struct ata_port *ap) -{ - void __iomem *mmio = ap->host_set->mmio_base; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - u32 tmp; - - /* clear IRQ */ - tmp = readl(port_mmio + PORT_IRQ_STAT); - writel(tmp, port_mmio + PORT_IRQ_STAT); - writel(1 << ap->id, mmio + HOST_IRQ_STAT); - - /* turn IRQ back on */ - writel(DEF_PORT_IRQ, port_mmio + PORT_IRQ_MASK); -} - -static void ahci_error_handler(struct ata_port *ap) -{ - void __iomem *mmio = ap->host_set->mmio_base; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - - if (!(ap->pflags & ATA_PFLAG_FROZEN)) { - /* restart engine */ - ahci_stop_engine(port_mmio); - ahci_start_engine(port_mmio); - } - - /* perform recovery */ - ata_do_eh(ap, ahci_prereset, ahci_softreset, ahci_hardreset, - ahci_postreset); -} - -static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - void __iomem *mmio = ap->host_set->mmio_base; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - - if (qc->flags & ATA_QCFLAG_FAILED) - qc->err_mask |= AC_ERR_OTHER; - - if (qc->err_mask) { - /* make DMA engine forget about the failed command */ - ahci_stop_engine(port_mmio); - ahci_start_engine(port_mmio); - } -} - -static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) -{ - struct ahci_host_priv *hpriv = ap->host_set->private_data; - struct ahci_port_priv *pp = ap->private_data; - void __iomem *mmio = ap->host_set->mmio_base; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - const char *emsg = NULL; - int rc; - - rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); - if (rc) { - ata_port_printk(ap, KERN_ERR, "%s (%d)\n", emsg, rc); - ahci_init_port(port_mmio, hpriv->cap, - pp->cmd_slot_dma, pp->rx_fis_dma); - } - - return rc; -} - -static int ahci_port_resume(struct ata_port *ap) -{ - struct ahci_port_priv *pp = ap->private_data; - struct ahci_host_priv *hpriv = ap->host_set->private_data; - void __iomem *mmio = ap->host_set->mmio_base; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - - ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma); - - return 0; -} - -static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) -{ - struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev); - void __iomem *mmio = host_set->mmio_base; - u32 ctl; - - if (mesg.event == PM_EVENT_SUSPEND) { - /* AHCI spec rev1.1 section 8.3.3: - * Software must disable interrupts prior to requesting a - * transition of the HBA to D3 state. - */ - ctl = readl(mmio + HOST_CTL); - ctl &= ~HOST_IRQ_EN; - writel(ctl, mmio + HOST_CTL); - readl(mmio + HOST_CTL); /* flush */ - } - - return ata_pci_device_suspend(pdev, mesg); -} - -static int ahci_pci_device_resume(struct pci_dev *pdev) -{ - struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev); - struct ahci_host_priv *hpriv = host_set->private_data; - void __iomem *mmio = host_set->mmio_base; - int rc; - - ata_pci_device_do_resume(pdev); - - if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { - rc = ahci_reset_controller(mmio, pdev); - if (rc) - return rc; - - ahci_init_controller(mmio, pdev, host_set->n_ports, hpriv->cap); - } - - ata_host_set_resume(host_set); - - return 0; -} - -static int ahci_port_start(struct ata_port *ap) -{ - struct device *dev = ap->host_set->dev; - struct ahci_host_priv *hpriv = ap->host_set->private_data; - struct ahci_port_priv *pp; - void __iomem *mmio = ap->host_set->mmio_base; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - void *mem; - dma_addr_t mem_dma; - int rc; - - pp = kmalloc(sizeof(*pp), GFP_KERNEL); - if (!pp) - return -ENOMEM; - memset(pp, 0, sizeof(*pp)); - - rc = ata_pad_alloc(ap, dev); - if (rc) { - kfree(pp); - return rc; - } - - mem = dma_alloc_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, &mem_dma, GFP_KERNEL); - if (!mem) { - ata_pad_free(ap, dev); - kfree(pp); - return -ENOMEM; - } - memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ); - - /* - * First item in chunk of DMA memory: 32-slot command table, - * 32 bytes each in size - */ - pp->cmd_slot = mem; - pp->cmd_slot_dma = mem_dma; - - mem += AHCI_CMD_SLOT_SZ; - mem_dma += AHCI_CMD_SLOT_SZ; - - /* - * Second item: Received-FIS area - */ - pp->rx_fis = mem; - pp->rx_fis_dma = mem_dma; - - mem += AHCI_RX_FIS_SZ; - mem_dma += AHCI_RX_FIS_SZ; - - /* - * Third item: data area for storing a single command - * and its scatter-gather table - */ - pp->cmd_tbl = mem; - pp->cmd_tbl_dma = mem_dma; - - ap->private_data = pp; - - /* initialize port */ - ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma); - - return 0; -} - -static void ahci_port_stop(struct ata_port *ap) -{ - struct device *dev = ap->host_set->dev; - struct ahci_host_priv *hpriv = ap->host_set->private_data; - struct ahci_port_priv *pp = ap->private_data; - void __iomem *mmio = ap->host_set->mmio_base; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - const char *emsg = NULL; - int rc; - - /* de-initialize port */ - rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); - if (rc) - ata_port_printk(ap, KERN_WARNING, "%s (%d)\n", emsg, rc); - - ap->private_data = NULL; - dma_free_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, - pp->cmd_slot, pp->cmd_slot_dma); - ata_pad_free(ap, dev); - kfree(pp); -} - -static void ahci_setup_port(struct ata_ioports *port, unsigned long base, - unsigned int port_idx) -{ - VPRINTK("ENTER, base==0x%lx, port_idx %u\n", base, port_idx); - base = ahci_port_base_ul(base, port_idx); - VPRINTK("base now==0x%lx\n", base); - - port->cmd_addr = base; - port->scr_addr = base + PORT_SCR; - - VPRINTK("EXIT\n"); -} - -static int ahci_host_init(struct ata_probe_ent *probe_ent) -{ - struct ahci_host_priv *hpriv = probe_ent->private_data; - struct pci_dev *pdev = to_pci_dev(probe_ent->dev); - void __iomem *mmio = probe_ent->mmio_base; - unsigned int i, using_dac; - int rc; - - rc = ahci_reset_controller(mmio, pdev); - if (rc) - return rc; - - hpriv->cap = readl(mmio + HOST_CAP); - hpriv->port_map = readl(mmio + HOST_PORTS_IMPL); - probe_ent->n_ports = (hpriv->cap & 0x1f) + 1; - - VPRINTK("cap 0x%x port_map 0x%x n_ports %d\n", - hpriv->cap, hpriv->port_map, probe_ent->n_ports); - - using_dac = hpriv->cap & HOST_CAP_64; - if (using_dac && - !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; - } - } - - for (i = 0; i < probe_ent->n_ports; i++) - ahci_setup_port(&probe_ent->port[i], (unsigned long) mmio, i); - - ahci_init_controller(mmio, pdev, probe_ent->n_ports, hpriv->cap); - - pci_set_master(pdev); - - return 0; -} - -static void ahci_print_info(struct ata_probe_ent *probe_ent) -{ - struct ahci_host_priv *hpriv = probe_ent->private_data; - struct pci_dev *pdev = to_pci_dev(probe_ent->dev); - void __iomem *mmio = probe_ent->mmio_base; - u32 vers, cap, impl, speed; - const char *speed_s; - u16 cc; - const char *scc_s; - - vers = readl(mmio + HOST_VERSION); - cap = hpriv->cap; - impl = hpriv->port_map; - - speed = (cap >> 20) & 0xf; - if (speed == 1) - speed_s = "1.5"; - else if (speed == 2) - speed_s = "3"; - else - speed_s = "?"; - - pci_read_config_word(pdev, 0x0a, &cc); - if (cc == 0x0101) - scc_s = "IDE"; - else if (cc == 0x0106) - scc_s = "SATA"; - else if (cc == 0x0104) - scc_s = "RAID"; - else - scc_s = "unknown"; - - dev_printk(KERN_INFO, &pdev->dev, - "AHCI %02x%02x.%02x%02x " - "%u slots %u ports %s Gbps 0x%x impl %s mode\n" - , - - (vers >> 24) & 0xff, - (vers >> 16) & 0xff, - (vers >> 8) & 0xff, - vers & 0xff, - - ((cap >> 8) & 0x1f) + 1, - (cap & 0x1f) + 1, - speed_s, - impl, - scc_s); - - dev_printk(KERN_INFO, &pdev->dev, - "flags: " - "%s%s%s%s%s%s" - "%s%s%s%s%s%s%s\n" - , - - cap & (1 << 31) ? "64bit " : "", - cap & (1 << 30) ? "ncq " : "", - cap & (1 << 28) ? "ilck " : "", - cap & (1 << 27) ? "stag " : "", - cap & (1 << 26) ? "pm " : "", - cap & (1 << 25) ? "led " : "", - - cap & (1 << 24) ? "clo " : "", - cap & (1 << 19) ? "nz " : "", - cap & (1 << 18) ? "only " : "", - cap & (1 << 17) ? "pmp " : "", - cap & (1 << 15) ? "pio " : "", - cap & (1 << 14) ? "slum " : "", - cap & (1 << 13) ? "part " : "" - ); -} - -static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) -{ - static int printed_version; - struct ata_probe_ent *probe_ent = NULL; - struct ahci_host_priv *hpriv; - unsigned long base; - void __iomem *mmio_base; - unsigned int board_idx = (unsigned int) ent->driver_data; - int have_msi, pci_dev_busy = 0; - int rc; - - VPRINTK("ENTER\n"); - - WARN_ON(ATA_MAX_QUEUE > AHCI_MAX_CMDS); - - if (!printed_version++) - dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); - - /* JMicron-specific fixup: make sure we're in AHCI mode */ - /* This is protected from races with ata_jmicron by the pci probe - locking */ - if (pdev->vendor == PCI_VENDOR_ID_JMICRON) { - /* AHCI enable, AHCI on function 0 */ - pci_write_config_byte(pdev, 0x41, 0xa1); - /* Function 1 is the PATA controller */ - if (PCI_FUNC(pdev->devfn)) - return -ENODEV; - } - - rc = pci_enable_device(pdev); - if (rc) - return rc; - - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pci_dev_busy = 1; - goto err_out; - } - - if (pci_enable_msi(pdev) == 0) - have_msi = 1; - else { - pci_intx(pdev, 1); - have_msi = 0; - } - - probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) { - rc = -ENOMEM; - goto err_out_msi; - } - - memset(probe_ent, 0, sizeof(*probe_ent)); - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - mmio_base = pci_iomap(pdev, AHCI_PCI_BAR, 0); - if (mmio_base == NULL) { - rc = -ENOMEM; - goto err_out_free_ent; - } - base = (unsigned long) mmio_base; - - hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL); - if (!hpriv) { - rc = -ENOMEM; - goto err_out_iounmap; - } - memset(hpriv, 0, sizeof(*hpriv)); - - probe_ent->sht = ahci_port_info[board_idx].sht; - probe_ent->host_flags = ahci_port_info[board_idx].host_flags; - probe_ent->pio_mask = ahci_port_info[board_idx].pio_mask; - probe_ent->udma_mask = ahci_port_info[board_idx].udma_mask; - probe_ent->port_ops = ahci_port_info[board_idx].port_ops; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->mmio_base = mmio_base; - probe_ent->private_data = hpriv; - - if (have_msi) - hpriv->flags |= AHCI_FLAG_MSI; - - /* initialize adapter */ - rc = ahci_host_init(probe_ent); - if (rc) - goto err_out_hpriv; - - if (!(probe_ent->host_flags & AHCI_FLAG_NO_NCQ) && - (hpriv->cap & HOST_CAP_NCQ)) - probe_ent->host_flags |= ATA_FLAG_NCQ; - - ahci_print_info(probe_ent); - - /* FIXME: check ata_device_add return value */ - ata_device_add(probe_ent); - kfree(probe_ent); - - return 0; - -err_out_hpriv: - kfree(hpriv); -err_out_iounmap: - pci_iounmap(pdev, mmio_base); -err_out_free_ent: - kfree(probe_ent); -err_out_msi: - if (have_msi) - pci_disable_msi(pdev); - else - pci_intx(pdev, 0); - pci_release_regions(pdev); -err_out: - if (!pci_dev_busy) - pci_disable_device(pdev); - return rc; -} - -static void ahci_remove_one (struct pci_dev *pdev) -{ - struct device *dev = pci_dev_to_dev(pdev); - struct ata_host_set *host_set = dev_get_drvdata(dev); - struct ahci_host_priv *hpriv = host_set->private_data; - unsigned int i; - int have_msi; - - for (i = 0; i < host_set->n_ports; i++) - ata_port_detach(host_set->ports[i]); - - have_msi = hpriv->flags & AHCI_FLAG_MSI; - free_irq(host_set->irq, host_set); - - for (i = 0; i < host_set->n_ports; i++) { - struct ata_port *ap = host_set->ports[i]; - - ata_scsi_release(ap->host); - scsi_host_put(ap->host); - } - - kfree(hpriv); - pci_iounmap(pdev, host_set->mmio_base); - kfree(host_set); - - if (have_msi) - pci_disable_msi(pdev); - else - pci_intx(pdev, 0); - pci_release_regions(pdev); - pci_disable_device(pdev); - dev_set_drvdata(dev, NULL); -} - -static int __init ahci_init(void) -{ - return pci_register_driver(&ahci_pci_driver); -} - -static void __exit ahci_exit(void) -{ - pci_unregister_driver(&ahci_pci_driver); -} - - -MODULE_AUTHOR("Jeff Garzik"); -MODULE_DESCRIPTION("AHCI SATA low-level driver"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, ahci_pci_tbl); -MODULE_VERSION(DRV_VERSION); - -module_init(ahci_init); -module_exit(ahci_exit); diff --git a/drivers/scsi/ata_piix.c b/drivers/scsi/ata_piix.c deleted file mode 100644 index 46c34fd5af8..00000000000 --- a/drivers/scsi/ata_piix.c +++ /dev/null @@ -1,960 +0,0 @@ -/* - * ata_piix.c - Intel PATA/SATA controllers - * - * Maintained by: Jeff Garzik - * Please ALWAYS copy linux-ide@vger.kernel.org - * on emails. - * - * - * Copyright 2003-2005 Red Hat Inc - * Copyright 2003-2005 Jeff Garzik - * - * - * Copyright header from piix.c: - * - * Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer - * Copyright (C) 1998-2000 Andre Hedrick - * Copyright (C) 2003 Red Hat Inc - * - * - * 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 02139, USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - * Hardware documentation available at http://developer.intel.com/ - * - * Documentation - * Publically available from Intel web site. Errata documentation - * is also publically available. As an aide to anyone hacking on this - * driver the list of errata that are relevant is below.going back to - * PIIX4. Older device documentation is now a bit tricky to find. - * - * The chipsets all follow very much the same design. The orginal Triton - * series chipsets do _not_ support independant device timings, but this - * is fixed in Triton II. With the odd mobile exception the chips then - * change little except in gaining more modes until SATA arrives. This - * driver supports only the chips with independant timing (that is those - * with SITRE and the 0x44 timing register). See pata_oldpiix and pata_mpiix - * for the early chip drivers. - * - * Errata of note: - * - * Unfixable - * PIIX4 errata #9 - Only on ultra obscure hw - * ICH3 errata #13 - Not observed to affect real hw - * by Intel - * - * Things we must deal with - * PIIX4 errata #10 - BM IDE hang with non UDMA - * (must stop/start dma to recover) - * 440MX errata #15 - As PIIX4 errata #10 - * PIIX4 errata #15 - Must not read control registers - * during a PIO transfer - * 440MX errata #13 - As PIIX4 errata #15 - * ICH2 errata #21 - DMA mode 0 doesn't work right - * ICH0/1 errata #55 - As ICH2 errata #21 - * ICH2 spec c #9 - Extra operations needed to handle - * drive hotswap [NOT YET SUPPORTED] - * ICH2 spec c #20 - IDE PRD must not cross a 64K boundary - * and must be dword aligned - * ICH2 spec c #24 - UDMA mode 4,5 t85/86 should be 6ns not 3.3 - * - * Should have been BIOS fixed: - * 450NX: errata #19 - DMA hangs on old 450NX - * 450NX: errata #20 - DMA hangs on old 450NX - * 450NX: errata #25 - Corruption with DMA on old 450NX - * ICH3 errata #15 - IDE deadlock under high load - * (BIOS must set dev 31 fn 0 bit 23) - * ICH3 errata #18 - Don't use native mode - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "ata_piix" -#define DRV_VERSION "2.00" - -enum { - PIIX_IOCFG = 0x54, /* IDE I/O configuration register */ - ICH5_PMR = 0x90, /* port mapping register */ - ICH5_PCS = 0x92, /* port control and status */ - PIIX_SCC = 0x0A, /* sub-class code register */ - - PIIX_FLAG_IGNORE_PCS = (1 << 25), /* ignore PCS present bits */ - PIIX_FLAG_SCR = (1 << 26), /* SCR available */ - PIIX_FLAG_AHCI = (1 << 27), /* AHCI possible */ - PIIX_FLAG_CHECKINTR = (1 << 28), /* make sure PCI INTx enabled */ - - /* combined mode. if set, PATA is channel 0. - * if clear, PATA is channel 1. - */ - PIIX_PORT_ENABLED = (1 << 0), - PIIX_PORT_PRESENT = (1 << 4), - - PIIX_80C_PRI = (1 << 5) | (1 << 4), - PIIX_80C_SEC = (1 << 7) | (1 << 6), - - /* controller IDs */ - piix4_pata = 0, - ich5_pata = 1, - ich5_sata = 2, - esb_sata = 3, - ich6_sata = 4, - ich6_sata_ahci = 5, - ich6m_sata_ahci = 6, - ich8_sata_ahci = 7, - - /* constants for mapping table */ - P0 = 0, /* port 0 */ - P1 = 1, /* port 1 */ - P2 = 2, /* port 2 */ - P3 = 3, /* port 3 */ - IDE = -1, /* IDE */ - NA = -2, /* not avaliable */ - RV = -3, /* reserved */ - - PIIX_AHCI_DEVICE = 6, -}; - -struct piix_map_db { - const u32 mask; - const u16 port_enable; - const int present_shift; - const int map[][4]; -}; - -struct piix_host_priv { - const int *map; - const struct piix_map_db *map_db; -}; - -static int piix_init_one (struct pci_dev *pdev, - const struct pci_device_id *ent); -static void piix_host_stop(struct ata_host_set *host_set); -static void piix_set_piomode (struct ata_port *ap, struct ata_device *adev); -static void piix_set_dmamode (struct ata_port *ap, struct ata_device *adev); -static void piix_pata_error_handler(struct ata_port *ap); -static void piix_sata_error_handler(struct ata_port *ap); - -static unsigned int in_module_init = 1; - -static const struct pci_device_id piix_pci_tbl[] = { -#ifdef ATA_ENABLE_PATA - { 0x8086, 0x7111, PCI_ANY_ID, PCI_ANY_ID, 0, 0, piix4_pata }, - { 0x8086, 0x24db, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_pata }, - { 0x8086, 0x25a2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_pata }, - { 0x8086, 0x27df, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_pata }, -#endif - - /* NOTE: The following PCI ids must be kept in sync with the - * list in drivers/pci/quirks.c. - */ - - /* 82801EB (ICH5) */ - { 0x8086, 0x24d1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_sata }, - /* 82801EB (ICH5) */ - { 0x8086, 0x24df, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_sata }, - /* 6300ESB (ICH5 variant with broken PCS present bits) */ - { 0x8086, 0x25a3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, esb_sata }, - /* 6300ESB pretending RAID */ - { 0x8086, 0x25b0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, esb_sata }, - /* 82801FB/FW (ICH6/ICH6W) */ - { 0x8086, 0x2651, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata }, - /* 82801FR/FRW (ICH6R/ICH6RW) */ - { 0x8086, 0x2652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata_ahci }, - /* 82801FBM ICH6M (ICH6R with only port 0 and 2 implemented) */ - { 0x8086, 0x2653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6m_sata_ahci }, - /* 82801GB/GR/GH (ICH7, identical to ICH6) */ - { 0x8086, 0x27c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata_ahci }, - /* 2801GBM/GHM (ICH7M, identical to ICH6M) */ - { 0x8086, 0x27c4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6m_sata_ahci }, - /* Enterprise Southbridge 2 (where's the datasheet?) */ - { 0x8086, 0x2680, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata_ahci }, - /* SATA Controller 1 IDE (ICH8, no datasheet yet) */ - { 0x8086, 0x2820, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci }, - /* SATA Controller 2 IDE (ICH8, ditto) */ - { 0x8086, 0x2825, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci }, - /* Mobile SATA Controller IDE (ICH8M, ditto) */ - { 0x8086, 0x2828, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci }, - - { } /* terminate list */ -}; - -static struct pci_driver piix_pci_driver = { - .name = DRV_NAME, - .id_table = piix_pci_tbl, - .probe = piix_init_one, - .remove = ata_pci_remove_one, - .suspend = ata_pci_device_suspend, - .resume = ata_pci_device_resume, -}; - -static struct scsi_host_template piix_sht = { - .module = THIS_MODULE, - .name = DRV_NAME, - .ioctl = ata_scsi_ioctl, - .queuecommand = ata_scsi_queuecmd, - .can_queue = ATA_DEF_QUEUE, - .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = LIBATA_MAX_PRD, - .cmd_per_lun = ATA_SHT_CMD_PER_LUN, - .emulated = ATA_SHT_EMULATED, - .use_clustering = ATA_SHT_USE_CLUSTERING, - .proc_name = DRV_NAME, - .dma_boundary = ATA_DMA_BOUNDARY, - .slave_configure = ata_scsi_slave_config, - .slave_destroy = ata_scsi_slave_destroy, - .bios_param = ata_std_bios_param, - .resume = ata_scsi_device_resume, - .suspend = ata_scsi_device_suspend, -}; - -static const struct ata_port_operations piix_pata_ops = { - .port_disable = ata_port_disable, - .set_piomode = piix_set_piomode, - .set_dmamode = piix_set_dmamode, - .mode_filter = ata_pci_default_filter, - - .tf_load = ata_tf_load, - .tf_read = ata_tf_read, - .check_status = ata_check_status, - .exec_command = ata_exec_command, - .dev_select = ata_std_dev_select, - - .bmdma_setup = ata_bmdma_setup, - .bmdma_start = ata_bmdma_start, - .bmdma_stop = ata_bmdma_stop, - .bmdma_status = ata_bmdma_status, - .qc_prep = ata_qc_prep, - .qc_issue = ata_qc_issue_prot, - .data_xfer = ata_pio_data_xfer, - - .freeze = ata_bmdma_freeze, - .thaw = ata_bmdma_thaw, - .error_handler = piix_pata_error_handler, - .post_internal_cmd = ata_bmdma_post_internal_cmd, - - .irq_handler = ata_interrupt, - .irq_clear = ata_bmdma_irq_clear, - - .port_start = ata_port_start, - .port_stop = ata_port_stop, - .host_stop = piix_host_stop, -}; - -static const struct ata_port_operations piix_sata_ops = { - .port_disable = ata_port_disable, - - .tf_load = ata_tf_load, - .tf_read = ata_tf_read, - .check_status = ata_check_status, - .exec_command = ata_exec_command, - .dev_select = ata_std_dev_select, - - .bmdma_setup = ata_bmdma_setup, - .bmdma_start = ata_bmdma_start, - .bmdma_stop = ata_bmdma_stop, - .bmdma_status = ata_bmdma_status, - .qc_prep = ata_qc_prep, - .qc_issue = ata_qc_issue_prot, - .data_xfer = ata_pio_data_xfer, - - .freeze = ata_bmdma_freeze, - .thaw = ata_bmdma_thaw, - .error_handler = piix_sata_error_handler, - .post_internal_cmd = ata_bmdma_post_internal_cmd, - - .irq_handler = ata_interrupt, - .irq_clear = ata_bmdma_irq_clear, - - .port_start = ata_port_start, - .port_stop = ata_port_stop, - .host_stop = piix_host_stop, -}; - -static const struct piix_map_db ich5_map_db = { - .mask = 0x7, - .port_enable = 0x3, - .present_shift = 4, - .map = { - /* PM PS SM SS MAP */ - { P0, NA, P1, NA }, /* 000b */ - { P1, NA, P0, NA }, /* 001b */ - { RV, RV, RV, RV }, - { RV, RV, RV, RV }, - { P0, P1, IDE, IDE }, /* 100b */ - { P1, P0, IDE, IDE }, /* 101b */ - { IDE, IDE, P0, P1 }, /* 110b */ - { IDE, IDE, P1, P0 }, /* 111b */ - }, -}; - -static const struct piix_map_db ich6_map_db = { - .mask = 0x3, - .port_enable = 0xf, - .present_shift = 4, - .map = { - /* PM PS SM SS MAP */ - { P0, P2, P1, P3 }, /* 00b */ - { IDE, IDE, P1, P3 }, /* 01b */ - { P0, P2, IDE, IDE }, /* 10b */ - { RV, RV, RV, RV }, - }, -}; - -static const struct piix_map_db ich6m_map_db = { - .mask = 0x3, - .port_enable = 0x5, - .present_shift = 4, - .map = { - /* PM PS SM SS MAP */ - { P0, P2, RV, RV }, /* 00b */ - { RV, RV, RV, RV }, - { P0, P2, IDE, IDE }, /* 10b */ - { RV, RV, RV, RV }, - }, -}; - -static const struct piix_map_db ich8_map_db = { - .mask = 0x3, - .port_enable = 0x3, - .present_shift = 8, - .map = { - /* PM PS SM SS MAP */ - { P0, NA, P1, NA }, /* 00b (hardwired) */ - { RV, RV, RV, RV }, - { RV, RV, RV, RV }, /* 10b (never) */ - { RV, RV, RV, RV }, - }, -}; - -static const struct piix_map_db *piix_map_db_table[] = { - [ich5_sata] = &ich5_map_db, - [esb_sata] = &ich5_map_db, - [ich6_sata] = &ich6_map_db, - [ich6_sata_ahci] = &ich6_map_db, - [ich6m_sata_ahci] = &ich6m_map_db, - [ich8_sata_ahci] = &ich8_map_db, -}; - -static struct ata_port_info piix_port_info[] = { - /* piix4_pata */ - { - .sht = &piix_sht, - .host_flags = ATA_FLAG_SLAVE_POSS, - .pio_mask = 0x1f, /* pio0-4 */ -#if 0 - .mwdma_mask = 0x06, /* mwdma1-2 */ -#else - .mwdma_mask = 0x00, /* mwdma broken */ -#endif - .udma_mask = ATA_UDMA_MASK_40C, - .port_ops = &piix_pata_ops, - }, - - /* ich5_pata */ - { - .sht = &piix_sht, - .host_flags = ATA_FLAG_SLAVE_POSS | PIIX_FLAG_CHECKINTR, - .pio_mask = 0x1f, /* pio0-4 */ -#if 0 - .mwdma_mask = 0x06, /* mwdma1-2 */ -#else - .mwdma_mask = 0x00, /* mwdma broken */ -#endif - .udma_mask = 0x3f, /* udma0-5 */ - .port_ops = &piix_pata_ops, - }, - - /* ich5_sata */ - { - .sht = &piix_sht, - .host_flags = ATA_FLAG_SATA | PIIX_FLAG_CHECKINTR, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x7f, /* udma0-6 */ - .port_ops = &piix_sata_ops, - }, - - /* i6300esb_sata */ - { - .sht = &piix_sht, - .host_flags = ATA_FLAG_SATA | - PIIX_FLAG_CHECKINTR | PIIX_FLAG_IGNORE_PCS, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x7f, /* udma0-6 */ - .port_ops = &piix_sata_ops, - }, - - /* ich6_sata */ - { - .sht = &piix_sht, - .host_flags = ATA_FLAG_SATA | - PIIX_FLAG_CHECKINTR | PIIX_FLAG_SCR, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x7f, /* udma0-6 */ - .port_ops = &piix_sata_ops, - }, - - /* ich6_sata_ahci */ - { - .sht = &piix_sht, - .host_flags = ATA_FLAG_SATA | - PIIX_FLAG_CHECKINTR | PIIX_FLAG_SCR | - PIIX_FLAG_AHCI, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x7f, /* udma0-6 */ - .port_ops = &piix_sata_ops, - }, - - /* ich6m_sata_ahci */ - { - .sht = &piix_sht, - .host_flags = ATA_FLAG_SATA | - PIIX_FLAG_CHECKINTR | PIIX_FLAG_SCR | - PIIX_FLAG_AHCI, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x7f, /* udma0-6 */ - .port_ops = &piix_sata_ops, - }, - - /* ich8_sata_ahci */ - { - .sht = &piix_sht, - .host_flags = ATA_FLAG_SATA | - PIIX_FLAG_CHECKINTR | PIIX_FLAG_SCR | - PIIX_FLAG_AHCI, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x7f, /* udma0-6 */ - .port_ops = &piix_sata_ops, - }, -}; - -static struct pci_bits piix_enable_bits[] = { - { 0x41U, 1U, 0x80UL, 0x80UL }, /* port 0 */ - { 0x43U, 1U, 0x80UL, 0x80UL }, /* port 1 */ -}; - -MODULE_AUTHOR("Andre Hedrick, Alan Cox, Andrzej Krzysztofowicz, Jeff Garzik"); -MODULE_DESCRIPTION("SCSI low-level driver for Intel PIIX/ICH ATA controllers"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, piix_pci_tbl); -MODULE_VERSION(DRV_VERSION); - -/** - * piix_pata_cbl_detect - Probe host controller cable detect info - * @ap: Port for which cable detect info is desired - * - * Read 80c cable indicator from ATA PCI device's PCI config - * register. This register is normally set by firmware (BIOS). - * - * LOCKING: - * None (inherited from caller). - */ -static void piix_pata_cbl_detect(struct ata_port *ap) -{ - struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); - u8 tmp, mask; - - /* no 80c support in host controller? */ - if ((ap->udma_mask & ~ATA_UDMA_MASK_40C) == 0) - goto cbl40; - - /* check BIOS cable detect results */ - mask = ap->port_no == 0 ? PIIX_80C_PRI : PIIX_80C_SEC; - pci_read_config_byte(pdev, PIIX_IOCFG, &tmp); - if ((tmp & mask) == 0) - goto cbl40; - - ap->cbl = ATA_CBL_PATA80; - return; - -cbl40: - ap->cbl = ATA_CBL_PATA40; - ap->udma_mask &= ATA_UDMA_MASK_40C; -} - -/** - * piix_pata_prereset - prereset for PATA host controller - * @ap: Target port - * - * Prereset including cable detection. - * - * LOCKING: - * None (inherited from caller). - */ -static int piix_pata_prereset(struct ata_port *ap) -{ - struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); - - if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->port_no])) { - ata_port_printk(ap, KERN_INFO, "port disabled. ignoring.\n"); - ap->eh_context.i.action &= ~ATA_EH_RESET_MASK; - return 0; - } - - piix_pata_cbl_detect(ap); - - return ata_std_prereset(ap); -} - -static void piix_pata_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, piix_pata_prereset, ata_std_softreset, NULL, - ata_std_postreset); -} - -/** - * piix_sata_prereset - prereset for SATA host controller - * @ap: Target port - * - * Reads and configures SATA PCI device's PCI config register - * Port Configuration and Status (PCS) to determine port and - * device availability. Return -ENODEV to skip reset if no - * device is present. - * - * LOCKING: - * None (inherited from caller). - * - * RETURNS: - * 0 if device is present, -ENODEV otherwise. - */ -static int piix_sata_prereset(struct ata_port *ap) -{ - struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); - struct piix_host_priv *hpriv = ap->host_set->private_data; - const unsigned int *map = hpriv->map; - int base = 2 * ap->port_no; - unsigned int present = 0; - int port, i; - u16 pcs; - - pci_read_config_word(pdev, ICH5_PCS, &pcs); - DPRINTK("ata%u: ENTER, pcs=0x%x base=%d\n", ap->id, pcs, base); - - for (i = 0; i < 2; i++) { - port = map[base + i]; - if (port < 0) - continue; - if ((ap->flags & PIIX_FLAG_IGNORE_PCS) || - (pcs & 1 << (hpriv->map_db->present_shift + port))) - present = 1; - } - - DPRINTK("ata%u: LEAVE, pcs=0x%x present=0x%x\n", - ap->id, pcs, present); - - if (!present) { - ata_port_printk(ap, KERN_INFO, "SATA port has no device.\n"); - ap->eh_context.i.action &= ~ATA_EH_RESET_MASK; - return 0; - } - - return ata_std_prereset(ap); -} - -static void piix_sata_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, piix_sata_prereset, ata_std_softreset, NULL, - ata_std_postreset); -} - -/** - * piix_set_piomode - Initialize host controller PATA PIO timings - * @ap: Port whose timings we are configuring - * @adev: um - * - * Set PIO mode for device, in host controller PCI config space. - * - * LOCKING: - * None (inherited from caller). - */ - -static void piix_set_piomode (struct ata_port *ap, struct ata_device *adev) -{ - unsigned int pio = adev->pio_mode - XFER_PIO_0; - struct pci_dev *dev = to_pci_dev(ap->host_set->dev); - unsigned int is_slave = (adev->devno != 0); - unsigned int master_port= ap->port_no ? 0x42 : 0x40; - unsigned int slave_port = 0x44; - u16 master_data; - u8 slave_data; - - static const /* ISP RTC */ - u8 timings[][2] = { { 0, 0 }, - { 0, 0 }, - { 1, 0 }, - { 2, 1 }, - { 2, 3 }, }; - - pci_read_config_word(dev, master_port, &master_data); - if (is_slave) { - master_data |= 0x4000; - /* enable PPE, IE and TIME */ - master_data |= 0x0070; - pci_read_config_byte(dev, slave_port, &slave_data); - slave_data &= (ap->port_no ? 0x0f : 0xf0); - slave_data |= - (timings[pio][0] << 2) | - (timings[pio][1] << (ap->port_no ? 4 : 0)); - } else { - master_data &= 0xccf8; - /* enable PPE, IE and TIME */ - master_data |= 0x0007; - master_data |= - (timings[pio][0] << 12) | - (timings[pio][1] << 8); - } - pci_write_config_word(dev, master_port, master_data); - if (is_slave) - pci_write_config_byte(dev, slave_port, slave_data); -} - -/** - * piix_set_dmamode - Initialize host controller PATA PIO timings - * @ap: Port whose timings we are configuring - * @adev: um - * @udma: udma mode, 0 - 6 - * - * Set UDMA mode for device, in host controller PCI config space. - * - * LOCKING: - * None (inherited from caller). - */ - -static void piix_set_dmamode (struct ata_port *ap, struct ata_device *adev) -{ - unsigned int udma = adev->dma_mode; /* FIXME: MWDMA too */ - struct pci_dev *dev = to_pci_dev(ap->host_set->dev); - u8 maslave = ap->port_no ? 0x42 : 0x40; - u8 speed = udma; - unsigned int drive_dn = (ap->port_no ? 2 : 0) + adev->devno; - int a_speed = 3 << (drive_dn * 4); - int u_flag = 1 << drive_dn; - int v_flag = 0x01 << drive_dn; - int w_flag = 0x10 << drive_dn; - int u_speed = 0; - int sitre; - u16 reg4042, reg4a; - u8 reg48, reg54, reg55; - - pci_read_config_word(dev, maslave, ®4042); - DPRINTK("reg4042 = 0x%04x\n", reg4042); - sitre = (reg4042 & 0x4000) ? 1 : 0; - pci_read_config_byte(dev, 0x48, ®48); - pci_read_config_word(dev, 0x4a, ®4a); - pci_read_config_byte(dev, 0x54, ®54); - pci_read_config_byte(dev, 0x55, ®55); - - switch(speed) { - case XFER_UDMA_4: - case XFER_UDMA_2: u_speed = 2 << (drive_dn * 4); break; - case XFER_UDMA_6: - case XFER_UDMA_5: - case XFER_UDMA_3: - case XFER_UDMA_1: u_speed = 1 << (drive_dn * 4); break; - case XFER_UDMA_0: u_speed = 0 << (drive_dn * 4); break; - case XFER_MW_DMA_2: - case XFER_MW_DMA_1: break; - default: - BUG(); - return; - } - - if (speed >= XFER_UDMA_0) { - if (!(reg48 & u_flag)) - pci_write_config_byte(dev, 0x48, reg48 | u_flag); - if (speed == XFER_UDMA_5) { - pci_write_config_byte(dev, 0x55, (u8) reg55|w_flag); - } else { - pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag); - } - if ((reg4a & a_speed) != u_speed) - pci_write_config_word(dev, 0x4a, (reg4a & ~a_speed) | u_speed); - if (speed > XFER_UDMA_2) { - if (!(reg54 & v_flag)) - pci_write_config_byte(dev, 0x54, reg54 | v_flag); - } else - pci_write_config_byte(dev, 0x54, reg54 & ~v_flag); - } else { - if (reg48 & u_flag) - pci_write_config_byte(dev, 0x48, reg48 & ~u_flag); - if (reg4a & a_speed) - pci_write_config_word(dev, 0x4a, reg4a & ~a_speed); - if (reg54 & v_flag) - pci_write_config_byte(dev, 0x54, reg54 & ~v_flag); - if (reg55 & w_flag) - pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag); - } -} - -#define AHCI_PCI_BAR 5 -#define AHCI_GLOBAL_CTL 0x04 -#define AHCI_ENABLE (1 << 31) -static int piix_disable_ahci(struct pci_dev *pdev) -{ - void __iomem *mmio; - u32 tmp; - int rc = 0; - - /* BUG: pci_enable_device has not yet been called. This - * works because this device is usually set up by BIOS. - */ - - if (!pci_resource_start(pdev, AHCI_PCI_BAR) || - !pci_resource_len(pdev, AHCI_PCI_BAR)) - return 0; - - mmio = pci_iomap(pdev, AHCI_PCI_BAR, 64); - if (!mmio) - return -ENOMEM; - - tmp = readl(mmio + AHCI_GLOBAL_CTL); - if (tmp & AHCI_ENABLE) { - tmp &= ~AHCI_ENABLE; - writel(tmp, mmio + AHCI_GLOBAL_CTL); - - tmp = readl(mmio + AHCI_GLOBAL_CTL); - if (tmp & AHCI_ENABLE) - rc = -EIO; - } - - pci_iounmap(pdev, mmio); - return rc; -} - -/** - * piix_check_450nx_errata - Check for problem 450NX setup - * @ata_dev: the PCI device to check - * - * Check for the present of 450NX errata #19 and errata #25. If - * they are found return an error code so we can turn off DMA - */ - -static int __devinit piix_check_450nx_errata(struct pci_dev *ata_dev) -{ - struct pci_dev *pdev = NULL; - u16 cfg; - u8 rev; - int no_piix_dma = 0; - - while((pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, pdev)) != NULL) - { - /* Look for 450NX PXB. Check for problem configurations - A PCI quirk checks bit 6 already */ - pci_read_config_byte(pdev, PCI_REVISION_ID, &rev); - pci_read_config_word(pdev, 0x41, &cfg); - /* Only on the original revision: IDE DMA can hang */ - if (rev == 0x00) - no_piix_dma = 1; - /* On all revisions below 5 PXB bus lock must be disabled for IDE */ - else if (cfg & (1<<14) && rev < 5) - no_piix_dma = 2; - } - if (no_piix_dma) - dev_printk(KERN_WARNING, &ata_dev->dev, "450NX errata present, disabling IDE DMA.\n"); - if (no_piix_dma == 2) - dev_printk(KERN_WARNING, &ata_dev->dev, "A BIOS update may resolve this.\n"); - return no_piix_dma; -} - -static void __devinit piix_init_pcs(struct pci_dev *pdev, - const struct piix_map_db *map_db) -{ - u16 pcs, new_pcs; - - pci_read_config_word(pdev, ICH5_PCS, &pcs); - - new_pcs = pcs | map_db->port_enable; - - if (new_pcs != pcs) { - DPRINTK("updating PCS from 0x%x to 0x%x\n", pcs, new_pcs); - pci_write_config_word(pdev, ICH5_PCS, new_pcs); - msleep(150); - } -} - -static void __devinit piix_init_sata_map(struct pci_dev *pdev, - struct ata_port_info *pinfo, - const struct piix_map_db *map_db) -{ - struct piix_host_priv *hpriv = pinfo[0].private_data; - const unsigned int *map; - int i, invalid_map = 0; - u8 map_value; - - pci_read_config_byte(pdev, ICH5_PMR, &map_value); - - map = map_db->map[map_value & map_db->mask]; - - dev_printk(KERN_INFO, &pdev->dev, "MAP ["); - for (i = 0; i < 4; i++) { - switch (map[i]) { - case RV: - invalid_map = 1; - printk(" XX"); - break; - - case NA: - printk(" --"); - break; - - case IDE: - WARN_ON((i & 1) || map[i + 1] != IDE); - pinfo[i / 2] = piix_port_info[ich5_pata]; - pinfo[i / 2].private_data = hpriv; - i++; - printk(" IDE IDE"); - break; - - default: - printk(" P%d", map[i]); - if (i & 1) - pinfo[i / 2].host_flags |= ATA_FLAG_SLAVE_POSS; - break; - } - } - printk(" ]\n"); - - if (invalid_map) - dev_printk(KERN_ERR, &pdev->dev, - "invalid MAP value %u\n", map_value); - - hpriv->map = map; - hpriv->map_db = map_db; -} - -/** - * piix_init_one - Register PIIX ATA PCI device with kernel services - * @pdev: PCI device to register - * @ent: Entry in piix_pci_tbl matching with @pdev - * - * Called from kernel PCI layer. We probe for combined mode (sigh), - * and then hand over control to libata, for it to do the rest. - * - * LOCKING: - * Inherited from PCI layer (may sleep). - * - * RETURNS: - * Zero on success, or -ERRNO value. - */ - -static int piix_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) -{ - static int printed_version; - struct ata_port_info port_info[2]; - struct ata_port_info *ppinfo[2] = { &port_info[0], &port_info[1] }; - struct piix_host_priv *hpriv; - unsigned long host_flags; - - if (!printed_version++) - dev_printk(KERN_DEBUG, &pdev->dev, - "version " DRV_VERSION "\n"); - - /* no hotplugging support (FIXME) */ - if (!in_module_init) - return -ENODEV; - - hpriv = kzalloc(sizeof(*hpriv), GFP_KERNEL); - if (!hpriv) - return -ENOMEM; - - port_info[0] = piix_port_info[ent->driver_data]; - port_info[1] = piix_port_info[ent->driver_data]; - port_info[0].private_data = hpriv; - port_info[1].private_data = hpriv; - - host_flags = port_info[0].host_flags; - - if (host_flags & PIIX_FLAG_AHCI) { - u8 tmp; - pci_read_config_byte(pdev, PIIX_SCC, &tmp); - if (tmp == PIIX_AHCI_DEVICE) { - int rc = piix_disable_ahci(pdev); - if (rc) - return rc; - } - } - - /* Initialize SATA map */ - if (host_flags & ATA_FLAG_SATA) { - piix_init_sata_map(pdev, port_info, - piix_map_db_table[ent->driver_data]); - piix_init_pcs(pdev, piix_map_db_table[ent->driver_data]); - } - - /* On ICH5, some BIOSen disable the interrupt using the - * PCI_COMMAND_INTX_DISABLE bit added in PCI 2.3. - * On ICH6, this bit has the same effect, but only when - * MSI is disabled (and it is disabled, as we don't use - * message-signalled interrupts currently). - */ - if (host_flags & PIIX_FLAG_CHECKINTR) - pci_intx(pdev, 1); - - if (piix_check_450nx_errata(pdev)) { - /* This writes into the master table but it does not - really matter for this errata as we will apply it to - all the PIIX devices on the board */ - port_info[0].mwdma_mask = 0; - port_info[0].udma_mask = 0; - port_info[1].mwdma_mask = 0; - port_info[1].udma_mask = 0; - } - return ata_pci_init_one(pdev, ppinfo, 2); -} - -static void piix_host_stop(struct ata_host_set *host_set) -{ - ata_host_stop(host_set); -} - -static int __init piix_init(void) -{ - int rc; - - DPRINTK("pci_register_driver\n"); - rc = pci_register_driver(&piix_pci_driver); - if (rc) - return rc; - - in_module_init = 0; - - DPRINTK("done\n"); - return 0; -} - -static void __exit piix_exit(void) -{ - pci_unregister_driver(&piix_pci_driver); -} - -module_init(piix_init); -module_exit(piix_exit); - diff --git a/drivers/scsi/libata-bmdma.c b/drivers/scsi/libata-bmdma.c deleted file mode 100644 index 158f62dbf21..00000000000 --- a/drivers/scsi/libata-bmdma.c +++ /dev/null @@ -1,1109 +0,0 @@ -/* - * libata-bmdma.c - helper library for PCI IDE BMDMA - * - * Maintained by: Jeff Garzik - * Please ALWAYS copy linux-ide@vger.kernel.org - * on emails. - * - * Copyright 2003-2006 Red Hat, Inc. All rights reserved. - * Copyright 2003-2006 Jeff Garzik - * - * - * 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 02139, USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - * Hardware documentation available from http://www.t13.org/ and - * http://www.sata-io.org/ - * - */ - -#include -#include -#include - -#include "libata.h" - -/** - * ata_tf_load_pio - send taskfile registers to host controller - * @ap: Port to which output is sent - * @tf: ATA taskfile register set - * - * Outputs ATA taskfile to standard ATA host controller. - * - * LOCKING: - * Inherited from caller. - */ - -static void ata_tf_load_pio(struct ata_port *ap, const struct ata_taskfile *tf) -{ - struct ata_ioports *ioaddr = &ap->ioaddr; - unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; - - if (tf->ctl != ap->last_ctl) { - outb(tf->ctl, ioaddr->ctl_addr); - ap->last_ctl = tf->ctl; - ata_wait_idle(ap); - } - - if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) { - outb(tf->hob_feature, ioaddr->feature_addr); - outb(tf->hob_nsect, ioaddr->nsect_addr); - outb(tf->hob_lbal, ioaddr->lbal_addr); - outb(tf->hob_lbam, ioaddr->lbam_addr); - outb(tf->hob_lbah, ioaddr->lbah_addr); - VPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n", - tf->hob_feature, - tf->hob_nsect, - tf->hob_lbal, - tf->hob_lbam, - tf->hob_lbah); - } - - if (is_addr) { - outb(tf->feature, ioaddr->feature_addr); - outb(tf->nsect, ioaddr->nsect_addr); - outb(tf->lbal, ioaddr->lbal_addr); - outb(tf->lbam, ioaddr->lbam_addr); - outb(tf->lbah, ioaddr->lbah_addr); - VPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n", - tf->feature, - tf->nsect, - tf->lbal, - tf->lbam, - tf->lbah); - } - - if (tf->flags & ATA_TFLAG_DEVICE) { - outb(tf->device, ioaddr->device_addr); - VPRINTK("device 0x%X\n", tf->device); - } - - ata_wait_idle(ap); -} - -/** - * ata_tf_load_mmio - send taskfile registers to host controller - * @ap: Port to which output is sent - * @tf: ATA taskfile register set - * - * Outputs ATA taskfile to standard ATA host controller using MMIO. - * - * LOCKING: - * Inherited from caller. - */ - -static void ata_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf) -{ - struct ata_ioports *ioaddr = &ap->ioaddr; - unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; - - if (tf->ctl != ap->last_ctl) { - writeb(tf->ctl, (void __iomem *) ap->ioaddr.ctl_addr); - ap->last_ctl = tf->ctl; - ata_wait_idle(ap); - } - - if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) { - writeb(tf->hob_feature, (void __iomem *) ioaddr->feature_addr); - writeb(tf->hob_nsect, (void __iomem *) ioaddr->nsect_addr); - writeb(tf->hob_lbal, (void __iomem *) ioaddr->lbal_addr); - writeb(tf->hob_lbam, (void __iomem *) ioaddr->lbam_addr); - writeb(tf->hob_lbah, (void __iomem *) ioaddr->lbah_addr); - VPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n", - tf->hob_feature, - tf->hob_nsect, - tf->hob_lbal, - tf->hob_lbam, - tf->hob_lbah); - } - - if (is_addr) { - writeb(tf->feature, (void __iomem *) ioaddr->feature_addr); - writeb(tf->nsect, (void __iomem *) ioaddr->nsect_addr); - writeb(tf->lbal, (void __iomem *) ioaddr->lbal_addr); - writeb(tf->lbam, (void __iomem *) ioaddr->lbam_addr); - writeb(tf->lbah, (void __iomem *) ioaddr->lbah_addr); - VPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n", - tf->feature, - tf->nsect, - tf->lbal, - tf->lbam, - tf->lbah); - } - - if (tf->flags & ATA_TFLAG_DEVICE) { - writeb(tf->device, (void __iomem *) ioaddr->device_addr); - VPRINTK("device 0x%X\n", tf->device); - } - - ata_wait_idle(ap); -} - - -/** - * ata_tf_load - send taskfile registers to host controller - * @ap: Port to which output is sent - * @tf: ATA taskfile register set - * - * Outputs ATA taskfile to standard ATA host controller using MMIO - * or PIO as indicated by the ATA_FLAG_MMIO flag. - * Writes the control, feature, nsect, lbal, lbam, and lbah registers. - * Optionally (ATA_TFLAG_LBA48) writes hob_feature, hob_nsect, - * hob_lbal, hob_lbam, and hob_lbah. - * - * This function waits for idle (!BUSY and !DRQ) after writing - * registers. If the control register has a new value, this - * function also waits for idle after writing control and before - * writing the remaining registers. - * - * May be used as the tf_load() entry in ata_port_operations. - * - * LOCKING: - * Inherited from caller. - */ -void ata_tf_load(struct ata_port *ap, const struct ata_taskfile *tf) -{ - if (ap->flags & ATA_FLAG_MMIO) - ata_tf_load_mmio(ap, tf); - else - ata_tf_load_pio(ap, tf); -} - -/** - * ata_exec_command_pio - issue ATA command to host controller - * @ap: port to which command is being issued - * @tf: ATA taskfile register set - * - * Issues PIO write to ATA command register, with proper - * synchronization with interrupt handler / other threads. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -static void ata_exec_command_pio(struct ata_port *ap, const struct ata_taskfile *tf) -{ - DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command); - - outb(tf->command, ap->ioaddr.command_addr); - ata_pause(ap); -} - - -/** - * ata_exec_command_mmio - issue ATA command to host controller - * @ap: port to which command is being issued - * @tf: ATA taskfile register set - * - * Issues MMIO write to ATA command register, with proper - * synchronization with interrupt handler / other threads. - * - * FIXME: missing write posting for 400nS delay enforcement - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -static void ata_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf) -{ - DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command); - - writeb(tf->command, (void __iomem *) ap->ioaddr.command_addr); - ata_pause(ap); -} - - -/** - * ata_exec_command - issue ATA command to host controller - * @ap: port to which command is being issued - * @tf: ATA taskfile register set - * - * Issues PIO/MMIO write to ATA command register, with proper - * synchronization with interrupt handler / other threads. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ -void ata_exec_command(struct ata_port *ap, const struct ata_taskfile *tf) -{ - if (ap->flags & ATA_FLAG_MMIO) - ata_exec_command_mmio(ap, tf); - else - ata_exec_command_pio(ap, tf); -} - -/** - * ata_tf_read_pio - input device's ATA taskfile shadow registers - * @ap: Port from which input is read - * @tf: ATA taskfile register set for storing input - * - * Reads ATA taskfile registers for currently-selected device - * into @tf. - * - * LOCKING: - * Inherited from caller. - */ - -static void ata_tf_read_pio(struct ata_port *ap, struct ata_taskfile *tf) -{ - struct ata_ioports *ioaddr = &ap->ioaddr; - - tf->command = ata_check_status(ap); - tf->feature = inb(ioaddr->error_addr); - tf->nsect = inb(ioaddr->nsect_addr); - tf->lbal = inb(ioaddr->lbal_addr); - tf->lbam = inb(ioaddr->lbam_addr); - tf->lbah = inb(ioaddr->lbah_addr); - tf->device = inb(ioaddr->device_addr); - - if (tf->flags & ATA_TFLAG_LBA48) { - outb(tf->ctl | ATA_HOB, ioaddr->ctl_addr); - tf->hob_feature = inb(ioaddr->error_addr); - tf->hob_nsect = inb(ioaddr->nsect_addr); - tf->hob_lbal = inb(ioaddr->lbal_addr); - tf->hob_lbam = inb(ioaddr->lbam_addr); - tf->hob_lbah = inb(ioaddr->lbah_addr); - } -} - -/** - * ata_tf_read_mmio - input device's ATA taskfile shadow registers - * @ap: Port from which input is read - * @tf: ATA taskfile register set for storing input - * - * Reads ATA taskfile registers for currently-selected device - * into @tf via MMIO. - * - * LOCKING: - * Inherited from caller. - */ - -static void ata_tf_read_mmio(struct ata_port *ap, struct ata_taskfile *tf) -{ - struct ata_ioports *ioaddr = &ap->ioaddr; - - tf->command = ata_check_status(ap); - tf->feature = readb((void __iomem *)ioaddr->error_addr); - tf->nsect = readb((void __iomem *)ioaddr->nsect_addr); - tf->lbal = readb((void __iomem *)ioaddr->lbal_addr); - tf->lbam = readb((void __iomem *)ioaddr->lbam_addr); - tf->lbah = readb((void __iomem *)ioaddr->lbah_addr); - tf->device = readb((void __iomem *)ioaddr->device_addr); - - if (tf->flags & ATA_TFLAG_LBA48) { - writeb(tf->ctl | ATA_HOB, (void __iomem *) ap->ioaddr.ctl_addr); - tf->hob_feature = readb((void __iomem *)ioaddr->error_addr); - tf->hob_nsect = readb((void __iomem *)ioaddr->nsect_addr); - tf->hob_lbal = readb((void __iomem *)ioaddr->lbal_addr); - tf->hob_lbam = readb((void __iomem *)ioaddr->lbam_addr); - tf->hob_lbah = readb((void __iomem *)ioaddr->lbah_addr); - } -} - - -/** - * ata_tf_read - input device's ATA taskfile shadow registers - * @ap: Port from which input is read - * @tf: ATA taskfile register set for storing input - * - * Reads ATA taskfile registers for currently-selected device - * into @tf. - * - * Reads nsect, lbal, lbam, lbah, and device. If ATA_TFLAG_LBA48 - * is set, also reads the hob registers. - * - * May be used as the tf_read() entry in ata_port_operations. - * - * LOCKING: - * Inherited from caller. - */ -void ata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) -{ - if (ap->flags & ATA_FLAG_MMIO) - ata_tf_read_mmio(ap, tf); - else - ata_tf_read_pio(ap, tf); -} - -/** - * ata_check_status_pio - Read device status reg & clear interrupt - * @ap: port where the device is - * - * Reads ATA taskfile status register for currently-selected device - * and return its value. This also clears pending interrupts - * from this device - * - * LOCKING: - * Inherited from caller. - */ -static u8 ata_check_status_pio(struct ata_port *ap) -{ - return inb(ap->ioaddr.status_addr); -} - -/** - * ata_check_status_mmio - Read device status reg & clear interrupt - * @ap: port where the device is - * - * Reads ATA taskfile status register for currently-selected device - * via MMIO and return its value. This also clears pending interrupts - * from this device - * - * LOCKING: - * Inherited from caller. - */ -static u8 ata_check_status_mmio(struct ata_port *ap) -{ - return readb((void __iomem *) ap->ioaddr.status_addr); -} - - -/** - * ata_check_status - Read device status reg & clear interrupt - * @ap: port where the device is - * - * Reads ATA taskfile status register for currently-selected device - * and return its value. This also clears pending interrupts - * from this device - * - * May be used as the check_status() entry in ata_port_operations. - * - * LOCKING: - * Inherited from caller. - */ -u8 ata_check_status(struct ata_port *ap) -{ - if (ap->flags & ATA_FLAG_MMIO) - return ata_check_status_mmio(ap); - return ata_check_status_pio(ap); -} - - -/** - * ata_altstatus - Read device alternate status reg - * @ap: port where the device is - * - * Reads ATA taskfile alternate status register for - * currently-selected device and return its value. - * - * Note: may NOT be used as the check_altstatus() entry in - * ata_port_operations. - * - * LOCKING: - * Inherited from caller. - */ -u8 ata_altstatus(struct ata_port *ap) -{ - if (ap->ops->check_altstatus) - return ap->ops->check_altstatus(ap); - - if (ap->flags & ATA_FLAG_MMIO) - return readb((void __iomem *)ap->ioaddr.altstatus_addr); - return inb(ap->ioaddr.altstatus_addr); -} - -/** - * ata_bmdma_setup_mmio - Set up PCI IDE BMDMA transaction - * @qc: Info associated with this ATA transaction. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -static void ata_bmdma_setup_mmio (struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE); - u8 dmactl; - void __iomem *mmio = (void __iomem *) ap->ioaddr.bmdma_addr; - - /* load PRD table addr. */ - mb(); /* make sure PRD table writes are visible to controller */ - writel(ap->prd_dma, mmio + ATA_DMA_TABLE_OFS); - - /* specify data direction, triple-check start bit is clear */ - dmactl = readb(mmio + ATA_DMA_CMD); - dmactl &= ~(ATA_DMA_WR | ATA_DMA_START); - if (!rw) - dmactl |= ATA_DMA_WR; - writeb(dmactl, mmio + ATA_DMA_CMD); - - /* issue r/w command */ - ap->ops->exec_command(ap, &qc->tf); -} - -/** - * ata_bmdma_start_mmio - Start a PCI IDE BMDMA transaction - * @qc: Info associated with this ATA transaction. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -static void ata_bmdma_start_mmio (struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - void __iomem *mmio = (void __iomem *) ap->ioaddr.bmdma_addr; - u8 dmactl; - - /* start host DMA transaction */ - dmactl = readb(mmio + ATA_DMA_CMD); - writeb(dmactl | ATA_DMA_START, mmio + ATA_DMA_CMD); - - /* Strictly, one may wish to issue a readb() here, to - * flush the mmio write. However, control also passes - * to the hardware at this point, and it will interrupt - * us when we are to resume control. So, in effect, - * we don't care when the mmio write flushes. - * Further, a read of the DMA status register _immediately_ - * following the write may not be what certain flaky hardware - * is expected, so I think it is best to not add a readb() - * without first all the MMIO ATA cards/mobos. - * Or maybe I'm just being paranoid. - */ -} - -/** - * ata_bmdma_setup_pio - Set up PCI IDE BMDMA transaction (PIO) - * @qc: Info associated with this ATA transaction. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -static void ata_bmdma_setup_pio (struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE); - u8 dmactl; - - /* load PRD table addr. */ - outl(ap->prd_dma, ap->ioaddr.bmdma_addr + ATA_DMA_TABLE_OFS); - - /* specify data direction, triple-check start bit is clear */ - dmactl = inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD); - dmactl &= ~(ATA_DMA_WR | ATA_DMA_START); - if (!rw) - dmactl |= ATA_DMA_WR; - outb(dmactl, ap->ioaddr.bmdma_addr + ATA_DMA_CMD); - - /* issue r/w command */ - ap->ops->exec_command(ap, &qc->tf); -} - -/** - * ata_bmdma_start_pio - Start a PCI IDE BMDMA transaction (PIO) - * @qc: Info associated with this ATA transaction. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -static void ata_bmdma_start_pio (struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - u8 dmactl; - - /* start host DMA transaction */ - dmactl = inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD); - outb(dmactl | ATA_DMA_START, - ap->ioaddr.bmdma_addr + ATA_DMA_CMD); -} - - -/** - * ata_bmdma_start - Start a PCI IDE BMDMA transaction - * @qc: Info associated with this ATA transaction. - * - * Writes the ATA_DMA_START flag to the DMA command register. - * - * May be used as the bmdma_start() entry in ata_port_operations. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ -void ata_bmdma_start(struct ata_queued_cmd *qc) -{ - if (qc->ap->flags & ATA_FLAG_MMIO) - ata_bmdma_start_mmio(qc); - else - ata_bmdma_start_pio(qc); -} - - -/** - * ata_bmdma_setup - Set up PCI IDE BMDMA transaction - * @qc: Info associated with this ATA transaction. - * - * Writes address of PRD table to device's PRD Table Address - * register, sets the DMA control register, and calls - * ops->exec_command() to start the transfer. - * - * May be used as the bmdma_setup() entry in ata_port_operations. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ -void ata_bmdma_setup(struct ata_queued_cmd *qc) -{ - if (qc->ap->flags & ATA_FLAG_MMIO) - ata_bmdma_setup_mmio(qc); - else - ata_bmdma_setup_pio(qc); -} - - -/** - * ata_bmdma_irq_clear - Clear PCI IDE BMDMA interrupt. - * @ap: Port associated with this ATA transaction. - * - * Clear interrupt and error flags in DMA status register. - * - * May be used as the irq_clear() entry in ata_port_operations. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -void ata_bmdma_irq_clear(struct ata_port *ap) -{ - if (!ap->ioaddr.bmdma_addr) - return; - - if (ap->flags & ATA_FLAG_MMIO) { - void __iomem *mmio = - ((void __iomem *) ap->ioaddr.bmdma_addr) + ATA_DMA_STATUS; - writeb(readb(mmio), mmio); - } else { - unsigned long addr = ap->ioaddr.bmdma_addr + ATA_DMA_STATUS; - outb(inb(addr), addr); - } -} - - -/** - * ata_bmdma_status - Read PCI IDE BMDMA status - * @ap: Port associated with this ATA transaction. - * - * Read and return BMDMA status register. - * - * May be used as the bmdma_status() entry in ata_port_operations. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -u8 ata_bmdma_status(struct ata_port *ap) -{ - u8 host_stat; - if (ap->flags & ATA_FLAG_MMIO) { - void __iomem *mmio = (void __iomem *) ap->ioaddr.bmdma_addr; - host_stat = readb(mmio + ATA_DMA_STATUS); - } else - host_stat = inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); - return host_stat; -} - - -/** - * ata_bmdma_stop - Stop PCI IDE BMDMA transfer - * @qc: Command we are ending DMA for - * - * Clears the ATA_DMA_START flag in the dma control register - * - * May be used as the bmdma_stop() entry in ata_port_operations. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -void ata_bmdma_stop(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - if (ap->flags & ATA_FLAG_MMIO) { - void __iomem *mmio = (void __iomem *) ap->ioaddr.bmdma_addr; - - /* clear start/stop bit */ - writeb(readb(mmio + ATA_DMA_CMD) & ~ATA_DMA_START, - mmio + ATA_DMA_CMD); - } else { - /* clear start/stop bit */ - outb(inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD) & ~ATA_DMA_START, - ap->ioaddr.bmdma_addr + ATA_DMA_CMD); - } - - /* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */ - ata_altstatus(ap); /* dummy read */ -} - -/** - * ata_bmdma_freeze - Freeze BMDMA controller port - * @ap: port to freeze - * - * Freeze BMDMA controller port. - * - * LOCKING: - * Inherited from caller. - */ -void ata_bmdma_freeze(struct ata_port *ap) -{ - struct ata_ioports *ioaddr = &ap->ioaddr; - - ap->ctl |= ATA_NIEN; - ap->last_ctl = ap->ctl; - - if (ap->flags & ATA_FLAG_MMIO) - writeb(ap->ctl, (void __iomem *)ioaddr->ctl_addr); - else - outb(ap->ctl, ioaddr->ctl_addr); -} - -/** - * ata_bmdma_thaw - Thaw BMDMA controller port - * @ap: port to thaw - * - * Thaw BMDMA controller port. - * - * LOCKING: - * Inherited from caller. - */ -void ata_bmdma_thaw(struct ata_port *ap) -{ - /* clear & re-enable interrupts */ - ata_chk_status(ap); - ap->ops->irq_clear(ap); - if (ap->ioaddr.ctl_addr) /* FIXME: hack. create a hook instead */ - ata_irq_on(ap); -} - -/** - * ata_bmdma_drive_eh - Perform EH with given methods for BMDMA controller - * @ap: port to handle error for - * @prereset: prereset method (can be NULL) - * @softreset: softreset method (can be NULL) - * @hardreset: hardreset method (can be NULL) - * @postreset: postreset method (can be NULL) - * - * Handle error for ATA BMDMA controller. It can handle both - * PATA and SATA controllers. Many controllers should be able to - * use this EH as-is or with some added handling before and - * after. - * - * This function is intended to be used for constructing - * ->error_handler callback by low level drivers. - * - * LOCKING: - * Kernel thread context (may sleep) - */ -void ata_bmdma_drive_eh(struct ata_port *ap, ata_prereset_fn_t prereset, - ata_reset_fn_t softreset, ata_reset_fn_t hardreset, - ata_postreset_fn_t postreset) -{ - struct ata_eh_context *ehc = &ap->eh_context; - struct ata_queued_cmd *qc; - unsigned long flags; - int thaw = 0; - - qc = __ata_qc_from_tag(ap, ap->active_tag); - if (qc && !(qc->flags & ATA_QCFLAG_FAILED)) - qc = NULL; - - /* reset PIO HSM and stop DMA engine */ - spin_lock_irqsave(ap->lock, flags); - - ap->hsm_task_state = HSM_ST_IDLE; - - if (qc && (qc->tf.protocol == ATA_PROT_DMA || - qc->tf.protocol == ATA_PROT_ATAPI_DMA)) { - u8 host_stat; - - host_stat = ata_bmdma_status(ap); - - ata_ehi_push_desc(&ehc->i, "BMDMA stat 0x%x", host_stat); - - /* BMDMA controllers indicate host bus error by - * setting DMA_ERR bit and timing out. As it wasn't - * really a timeout event, adjust error mask and - * cancel frozen state. - */ - if (qc->err_mask == AC_ERR_TIMEOUT && host_stat & ATA_DMA_ERR) { - qc->err_mask = AC_ERR_HOST_BUS; - thaw = 1; - } - - ap->ops->bmdma_stop(qc); - } - - ata_altstatus(ap); - ata_chk_status(ap); - ap->ops->irq_clear(ap); - - spin_unlock_irqrestore(ap->lock, flags); - - if (thaw) - ata_eh_thaw_port(ap); - - /* PIO and DMA engines have been stopped, perform recovery */ - ata_do_eh(ap, prereset, softreset, hardreset, postreset); -} - -/** - * ata_bmdma_error_handler - Stock error handler for BMDMA controller - * @ap: port to handle error for - * - * Stock error handler for BMDMA controller. - * - * LOCKING: - * Kernel thread context (may sleep) - */ -void ata_bmdma_error_handler(struct ata_port *ap) -{ - ata_reset_fn_t hardreset; - - hardreset = NULL; - if (sata_scr_valid(ap)) - hardreset = sata_std_hardreset; - - ata_bmdma_drive_eh(ap, ata_std_prereset, ata_std_softreset, hardreset, - ata_std_postreset); -} - -/** - * ata_bmdma_post_internal_cmd - Stock post_internal_cmd for - * BMDMA controller - * @qc: internal command to clean up - * - * LOCKING: - * Kernel thread context (may sleep) - */ -void ata_bmdma_post_internal_cmd(struct ata_queued_cmd *qc) -{ - ata_bmdma_stop(qc); -} - -#ifdef CONFIG_PCI -/** - * ata_pci_init_native_mode - Initialize native-mode driver - * @pdev: pci device to be initialized - * @port: array[2] of pointers to port info structures. - * @ports: bitmap of ports present - * - * Utility function which allocates and initializes an - * ata_probe_ent structure for a standard dual-port - * PIO-based IDE controller. The returned ata_probe_ent - * structure can be passed to ata_device_add(). The returned - * ata_probe_ent structure should then be freed with kfree(). - * - * The caller need only pass the address of the primary port, the - * secondary will be deduced automatically. If the device has non - * standard secondary port mappings this function can be called twice, - * once for each interface. - */ - -struct ata_probe_ent * -ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int ports) -{ - struct ata_probe_ent *probe_ent = - ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]); - int p = 0; - unsigned long bmdma; - - if (!probe_ent) - return NULL; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->private_data = port[0]->private_data; - - if (ports & ATA_PORT_PRIMARY) { - probe_ent->port[p].cmd_addr = pci_resource_start(pdev, 0); - probe_ent->port[p].altstatus_addr = - probe_ent->port[p].ctl_addr = - pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS; - bmdma = pci_resource_start(pdev, 4); - if (bmdma) { - if (inb(bmdma + 2) & 0x80) - probe_ent->host_set_flags |= ATA_HOST_SIMPLEX; - probe_ent->port[p].bmdma_addr = bmdma; - } - ata_std_ports(&probe_ent->port[p]); - p++; - } - - if (ports & ATA_PORT_SECONDARY) { - probe_ent->port[p].cmd_addr = pci_resource_start(pdev, 2); - probe_ent->port[p].altstatus_addr = - probe_ent->port[p].ctl_addr = - pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS; - bmdma = pci_resource_start(pdev, 4); - if (bmdma) { - bmdma += 8; - if(inb(bmdma + 2) & 0x80) - probe_ent->host_set_flags |= ATA_HOST_SIMPLEX; - probe_ent->port[p].bmdma_addr = bmdma; - } - ata_std_ports(&probe_ent->port[p]); - p++; - } - - probe_ent->n_ports = p; - return probe_ent; -} - - -static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev, - struct ata_port_info **port, int port_mask) -{ - struct ata_probe_ent *probe_ent; - unsigned long bmdma = pci_resource_start(pdev, 4); - - probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]); - if (!probe_ent) - return NULL; - - probe_ent->n_ports = 2; - probe_ent->private_data = port[0]->private_data; - - if (port_mask & ATA_PORT_PRIMARY) { - probe_ent->irq = 14; - probe_ent->port[0].cmd_addr = ATA_PRIMARY_CMD; - probe_ent->port[0].altstatus_addr = - probe_ent->port[0].ctl_addr = ATA_PRIMARY_CTL; - if (bmdma) { - probe_ent->port[0].bmdma_addr = bmdma; - if (inb(bmdma + 2) & 0x80) - probe_ent->host_set_flags |= ATA_HOST_SIMPLEX; - } - ata_std_ports(&probe_ent->port[0]); - } else - probe_ent->dummy_port_mask |= ATA_PORT_PRIMARY; - - if (port_mask & ATA_PORT_SECONDARY) { - if (probe_ent->irq) - probe_ent->irq2 = 15; - else - probe_ent->irq = 15; - probe_ent->port[1].cmd_addr = ATA_SECONDARY_CMD; - probe_ent->port[1].altstatus_addr = - probe_ent->port[1].ctl_addr = ATA_SECONDARY_CTL; - if (bmdma) { - probe_ent->port[1].bmdma_addr = bmdma + 8; - if (inb(bmdma + 10) & 0x80) - probe_ent->host_set_flags |= ATA_HOST_SIMPLEX; - } - ata_std_ports(&probe_ent->port[1]); - } else - probe_ent->dummy_port_mask |= ATA_PORT_SECONDARY; - - return probe_ent; -} - - -/** - * ata_pci_init_one - Initialize/register PCI IDE host controller - * @pdev: Controller to be initialized - * @port_info: Information from low-level host driver - * @n_ports: Number of ports attached to host controller - * - * This is a helper function which can be called from a driver's - * xxx_init_one() probe function if the hardware uses traditional - * IDE taskfile registers. - * - * This function calls pci_enable_device(), reserves its register - * regions, sets the dma mask, enables bus master mode, and calls - * ata_device_add() - * - * ASSUMPTION: - * Nobody makes a single channel controller that appears solely as - * the secondary legacy port on PCI. - * - * LOCKING: - * Inherited from PCI layer (may sleep). - * - * RETURNS: - * Zero on success, negative on errno-based value on error. - */ - -int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, - unsigned int n_ports) -{ - struct ata_probe_ent *probe_ent = NULL; - struct ata_port_info *port[2]; - u8 tmp8, mask; - unsigned int legacy_mode = 0; - int disable_dev_on_err = 1; - int rc; - - DPRINTK("ENTER\n"); - - port[0] = port_info[0]; - if (n_ports > 1) - port[1] = port_info[1]; - else - port[1] = port[0]; - - if ((port[0]->host_flags & ATA_FLAG_NO_LEGACY) == 0 - && (pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) { - /* TODO: What if one channel is in native mode ... */ - pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8); - mask = (1 << 2) | (1 << 0); - if ((tmp8 & mask) != mask) - legacy_mode = (1 << 3); - } - - /* FIXME... */ - if ((!legacy_mode) && (n_ports > 2)) { - printk(KERN_ERR "ata: BUG: native mode, n_ports > 2\n"); - n_ports = 2; - /* For now */ - } - - /* FIXME: Really for ATA it isn't safe because the device may be - multi-purpose and we want to leave it alone if it was already - enabled. Secondly for shared use as Arjan says we want refcounting - - Checking dev->is_enabled is insufficient as this is not set at - boot for the primary video which is BIOS enabled - */ - - rc = pci_enable_device(pdev); - if (rc) - return rc; - - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - disable_dev_on_err = 0; - goto err_out; - } - - if (legacy_mode) { - if (!request_region(ATA_PRIMARY_CMD, 8, "libata")) { - struct resource *conflict, res; - res.start = ATA_PRIMARY_CMD; - res.end = ATA_PRIMARY_CMD + 8 - 1; - conflict = ____request_resource(&ioport_resource, &res); - if (!strcmp(conflict->name, "libata")) - legacy_mode |= ATA_PORT_PRIMARY; - else { - disable_dev_on_err = 0; - printk(KERN_WARNING "ata: 0x%0X IDE port busy\n", ATA_PRIMARY_CMD); - } - } else - legacy_mode |= ATA_PORT_PRIMARY; - - if (!request_region(ATA_SECONDARY_CMD, 8, "libata")) { - struct resource *conflict, res; - res.start = ATA_SECONDARY_CMD; - res.end = ATA_SECONDARY_CMD + 8 - 1; - conflict = ____request_resource(&ioport_resource, &res); - if (!strcmp(conflict->name, "libata")) - legacy_mode |= ATA_PORT_SECONDARY; - else { - disable_dev_on_err = 0; - printk(KERN_WARNING "ata: 0x%X IDE port busy\n", ATA_SECONDARY_CMD); - } - } else - legacy_mode |= ATA_PORT_SECONDARY; - } - - /* we have legacy mode, but all ports are unavailable */ - if (legacy_mode == (1 << 3)) { - rc = -EBUSY; - goto err_out_regions; - } - - /* FIXME: If we get no DMA mask we should fall back to PIO */ - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out_regions; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out_regions; - - if (legacy_mode) { - probe_ent = ata_pci_init_legacy_port(pdev, port, legacy_mode); - } else { - if (n_ports == 2) - probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); - else - probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY); - } - if (!probe_ent) { - rc = -ENOMEM; - goto err_out_regions; - } - - pci_set_master(pdev); - - /* FIXME: check ata_device_add return */ - ata_device_add(probe_ent); - - kfree(probe_ent); - - return 0; - -err_out_regions: - if (legacy_mode & ATA_PORT_PRIMARY) - release_region(ATA_PRIMARY_CMD, 8); - if (legacy_mode & ATA_PORT_SECONDARY) - release_region(ATA_SECONDARY_CMD, 8); - pci_release_regions(pdev); -err_out: - if (disable_dev_on_err) - pci_disable_device(pdev); - return rc; -} - -/** - * ata_pci_clear_simplex - attempt to kick device out of simplex - * @pdev: PCI device - * - * Some PCI ATA devices report simplex mode but in fact can be told to - * enter non simplex mode. This implements the neccessary logic to - * perform the task on such devices. Calling it on other devices will - * have -undefined- behaviour. - */ - -int ata_pci_clear_simplex(struct pci_dev *pdev) -{ - unsigned long bmdma = pci_resource_start(pdev, 4); - u8 simplex; - - if (bmdma == 0) - return -ENOENT; - - simplex = inb(bmdma + 0x02); - outb(simplex & 0x60, bmdma + 0x02); - simplex = inb(bmdma + 0x02); - if (simplex & 0x80) - return -EOPNOTSUPP; - return 0; -} - -unsigned long ata_pci_default_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long xfer_mask) -{ - /* Filter out DMA modes if the device has been configured by - the BIOS as PIO only */ - - if (ap->ioaddr.bmdma_addr == 0) - xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); - return xfer_mask; -} - -#endif /* CONFIG_PCI */ - diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c deleted file mode 100644 index 3f963f206d4..00000000000 --- a/drivers/scsi/libata-core.c +++ /dev/null @@ -1,6098 +0,0 @@ -/* - * libata-core.c - helper library for ATA - * - * Maintained by: Jeff Garzik - * Please ALWAYS copy linux-ide@vger.kernel.org - * on emails. - * - * Copyright 2003-2004 Red Hat, Inc. All rights reserved. - * Copyright 2003-2004 Jeff Garzik - * - * - * 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 02139, USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - * Hardware documentation available from http://www.t13.org/ and - * http://www.sata-io.org/ - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "scsi_priv.h" -#include -#include -#include -#include -#include -#include - -#include "libata.h" - -/* debounce timing parameters in msecs { interval, duration, timeout } */ -const unsigned long sata_deb_timing_normal[] = { 5, 100, 2000 }; -const unsigned long sata_deb_timing_hotplug[] = { 25, 500, 2000 }; -const unsigned long sata_deb_timing_long[] = { 100, 2000, 5000 }; - -static unsigned int ata_dev_init_params(struct ata_device *dev, - u16 heads, u16 sectors); -static unsigned int ata_dev_set_xfermode(struct ata_device *dev); -static void ata_dev_xfermask(struct ata_device *dev); - -static unsigned int ata_unique_id = 1; -static struct workqueue_struct *ata_wq; - -struct workqueue_struct *ata_aux_wq; - -int atapi_enabled = 1; -module_param(atapi_enabled, int, 0444); -MODULE_PARM_DESC(atapi_enabled, "Enable discovery of ATAPI devices (0=off, 1=on)"); - -int atapi_dmadir = 0; -module_param(atapi_dmadir, int, 0444); -MODULE_PARM_DESC(atapi_dmadir, "Enable ATAPI DMADIR bridge support (0=off, 1=on)"); - -int libata_fua = 0; -module_param_named(fua, libata_fua, int, 0444); -MODULE_PARM_DESC(fua, "FUA support (0=off, 1=on)"); - -static int ata_probe_timeout = ATA_TMOUT_INTERNAL / HZ; -module_param(ata_probe_timeout, int, 0444); -MODULE_PARM_DESC(ata_probe_timeout, "Set ATA probing timeout (seconds)"); - -MODULE_AUTHOR("Jeff Garzik"); -MODULE_DESCRIPTION("Library module for ATA devices"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); - - -/** - * ata_tf_to_fis - Convert ATA taskfile to SATA FIS structure - * @tf: Taskfile to convert - * @fis: Buffer into which data will output - * @pmp: Port multiplier port - * - * Converts a standard ATA taskfile to a Serial ATA - * FIS structure (Register - Host to Device). - * - * LOCKING: - * Inherited from caller. - */ - -void ata_tf_to_fis(const struct ata_taskfile *tf, u8 *fis, u8 pmp) -{ - fis[0] = 0x27; /* Register - Host to Device FIS */ - fis[1] = (pmp & 0xf) | (1 << 7); /* Port multiplier number, - bit 7 indicates Command FIS */ - fis[2] = tf->command; - fis[3] = tf->feature; - - fis[4] = tf->lbal; - fis[5] = tf->lbam; - fis[6] = tf->lbah; - fis[7] = tf->device; - - fis[8] = tf->hob_lbal; - fis[9] = tf->hob_lbam; - fis[10] = tf->hob_lbah; - fis[11] = tf->hob_feature; - - fis[12] = tf->nsect; - fis[13] = tf->hob_nsect; - fis[14] = 0; - fis[15] = tf->ctl; - - fis[16] = 0; - fis[17] = 0; - fis[18] = 0; - fis[19] = 0; -} - -/** - * ata_tf_from_fis - Convert SATA FIS to ATA taskfile - * @fis: Buffer from which data will be input - * @tf: Taskfile to output - * - * Converts a serial ATA FIS structure to a standard ATA taskfile. - * - * LOCKING: - * Inherited from caller. - */ - -void ata_tf_from_fis(const u8 *fis, struct ata_taskfile *tf) -{ - tf->command = fis[2]; /* status */ - tf->feature = fis[3]; /* error */ - - tf->lbal = fis[4]; - tf->lbam = fis[5]; - tf->lbah = fis[6]; - tf->device = fis[7]; - - tf->hob_lbal = fis[8]; - tf->hob_lbam = fis[9]; - tf->hob_lbah = fis[10]; - - tf->nsect = fis[12]; - tf->hob_nsect = fis[13]; -} - -static const u8 ata_rw_cmds[] = { - /* pio multi */ - ATA_CMD_READ_MULTI, - ATA_CMD_WRITE_MULTI, - ATA_CMD_READ_MULTI_EXT, - ATA_CMD_WRITE_MULTI_EXT, - 0, - 0, - 0, - ATA_CMD_WRITE_MULTI_FUA_EXT, - /* pio */ - ATA_CMD_PIO_READ, - ATA_CMD_PIO_WRITE, - ATA_CMD_PIO_READ_EXT, - ATA_CMD_PIO_WRITE_EXT, - 0, - 0, - 0, - 0, - /* dma */ - ATA_CMD_READ, - ATA_CMD_WRITE, - ATA_CMD_READ_EXT, - ATA_CMD_WRITE_EXT, - 0, - 0, - 0, - ATA_CMD_WRITE_FUA_EXT -}; - -/** - * ata_rwcmd_protocol - set taskfile r/w commands and protocol - * @qc: command to examine and configure - * - * Examine the device configuration and tf->flags to calculate - * the proper read/write commands and protocol to use. - * - * LOCKING: - * caller. - */ -int ata_rwcmd_protocol(struct ata_queued_cmd *qc) -{ - struct ata_taskfile *tf = &qc->tf; - struct ata_device *dev = qc->dev; - u8 cmd; - - int index, fua, lba48, write; - - fua = (tf->flags & ATA_TFLAG_FUA) ? 4 : 0; - lba48 = (tf->flags & ATA_TFLAG_LBA48) ? 2 : 0; - write = (tf->flags & ATA_TFLAG_WRITE) ? 1 : 0; - - if (dev->flags & ATA_DFLAG_PIO) { - tf->protocol = ATA_PROT_PIO; - index = dev->multi_count ? 0 : 8; - } else if (lba48 && (qc->ap->flags & ATA_FLAG_PIO_LBA48)) { - /* Unable to use DMA due to host limitation */ - tf->protocol = ATA_PROT_PIO; - index = dev->multi_count ? 0 : 8; - } else { - tf->protocol = ATA_PROT_DMA; - index = 16; - } - - cmd = ata_rw_cmds[index + fua + lba48 + write]; - if (cmd) { - tf->command = cmd; - return 0; - } - return -1; -} - -/** - * ata_pack_xfermask - Pack pio, mwdma and udma masks into xfer_mask - * @pio_mask: pio_mask - * @mwdma_mask: mwdma_mask - * @udma_mask: udma_mask - * - * Pack @pio_mask, @mwdma_mask and @udma_mask into a single - * unsigned int xfer_mask. - * - * LOCKING: - * None. - * - * RETURNS: - * Packed xfer_mask. - */ -static unsigned int ata_pack_xfermask(unsigned int pio_mask, - unsigned int mwdma_mask, - unsigned int udma_mask) -{ - return ((pio_mask << ATA_SHIFT_PIO) & ATA_MASK_PIO) | - ((mwdma_mask << ATA_SHIFT_MWDMA) & ATA_MASK_MWDMA) | - ((udma_mask << ATA_SHIFT_UDMA) & ATA_MASK_UDMA); -} - -/** - * ata_unpack_xfermask - Unpack xfer_mask into pio, mwdma and udma masks - * @xfer_mask: xfer_mask to unpack - * @pio_mask: resulting pio_mask - * @mwdma_mask: resulting mwdma_mask - * @udma_mask: resulting udma_mask - * - * Unpack @xfer_mask into @pio_mask, @mwdma_mask and @udma_mask. - * Any NULL distination masks will be ignored. - */ -static void ata_unpack_xfermask(unsigned int xfer_mask, - unsigned int *pio_mask, - unsigned int *mwdma_mask, - unsigned int *udma_mask) -{ - if (pio_mask) - *pio_mask = (xfer_mask & ATA_MASK_PIO) >> ATA_SHIFT_PIO; - if (mwdma_mask) - *mwdma_mask = (xfer_mask & ATA_MASK_MWDMA) >> ATA_SHIFT_MWDMA; - if (udma_mask) - *udma_mask = (xfer_mask & ATA_MASK_UDMA) >> ATA_SHIFT_UDMA; -} - -static const struct ata_xfer_ent { - int shift, bits; - u8 base; -} ata_xfer_tbl[] = { - { ATA_SHIFT_PIO, ATA_BITS_PIO, XFER_PIO_0 }, - { ATA_SHIFT_MWDMA, ATA_BITS_MWDMA, XFER_MW_DMA_0 }, - { ATA_SHIFT_UDMA, ATA_BITS_UDMA, XFER_UDMA_0 }, - { -1, }, -}; - -/** - * ata_xfer_mask2mode - Find matching XFER_* for the given xfer_mask - * @xfer_mask: xfer_mask of interest - * - * Return matching XFER_* value for @xfer_mask. Only the highest - * bit of @xfer_mask is considered. - * - * LOCKING: - * None. - * - * RETURNS: - * Matching XFER_* value, 0 if no match found. - */ -static u8 ata_xfer_mask2mode(unsigned int xfer_mask) -{ - int highbit = fls(xfer_mask) - 1; - const struct ata_xfer_ent *ent; - - for (ent = ata_xfer_tbl; ent->shift >= 0; ent++) - if (highbit >= ent->shift && highbit < ent->shift + ent->bits) - return ent->base + highbit - ent->shift; - return 0; -} - -/** - * ata_xfer_mode2mask - Find matching xfer_mask for XFER_* - * @xfer_mode: XFER_* of interest - * - * Return matching xfer_mask for @xfer_mode. - * - * LOCKING: - * None. - * - * RETURNS: - * Matching xfer_mask, 0 if no match found. - */ -static unsigned int ata_xfer_mode2mask(u8 xfer_mode) -{ - const struct ata_xfer_ent *ent; - - for (ent = ata_xfer_tbl; ent->shift >= 0; ent++) - if (xfer_mode >= ent->base && xfer_mode < ent->base + ent->bits) - return 1 << (ent->shift + xfer_mode - ent->base); - return 0; -} - -/** - * ata_xfer_mode2shift - Find matching xfer_shift for XFER_* - * @xfer_mode: XFER_* of interest - * - * Return matching xfer_shift for @xfer_mode. - * - * LOCKING: - * None. - * - * RETURNS: - * Matching xfer_shift, -1 if no match found. - */ -static int ata_xfer_mode2shift(unsigned int xfer_mode) -{ - const struct ata_xfer_ent *ent; - - for (ent = ata_xfer_tbl; ent->shift >= 0; ent++) - if (xfer_mode >= ent->base && xfer_mode < ent->base + ent->bits) - return ent->shift; - return -1; -} - -/** - * ata_mode_string - convert xfer_mask to string - * @xfer_mask: mask of bits supported; only highest bit counts. - * - * Determine string which represents the highest speed - * (highest bit in @modemask). - * - * LOCKING: - * None. - * - * RETURNS: - * Constant C string representing highest speed listed in - * @mode_mask, or the constant C string "". - */ -static const char *ata_mode_string(unsigned int xfer_mask) -{ - static const char * const xfer_mode_str[] = { - "PIO0", - "PIO1", - "PIO2", - "PIO3", - "PIO4", - "MWDMA0", - "MWDMA1", - "MWDMA2", - "UDMA/16", - "UDMA/25", - "UDMA/33", - "UDMA/44", - "UDMA/66", - "UDMA/100", - "UDMA/133", - "UDMA7", - }; - int highbit; - - highbit = fls(xfer_mask) - 1; - if (highbit >= 0 && highbit < ARRAY_SIZE(xfer_mode_str)) - return xfer_mode_str[highbit]; - return ""; -} - -static const char *sata_spd_string(unsigned int spd) -{ - static const char * const spd_str[] = { - "1.5 Gbps", - "3.0 Gbps", - }; - - if (spd == 0 || (spd - 1) >= ARRAY_SIZE(spd_str)) - return ""; - return spd_str[spd - 1]; -} - -void ata_dev_disable(struct ata_device *dev) -{ - if (ata_dev_enabled(dev) && ata_msg_drv(dev->ap)) { - ata_dev_printk(dev, KERN_WARNING, "disabled\n"); - dev->class++; - } -} - -/** - * ata_pio_devchk - PATA device presence detection - * @ap: ATA channel to examine - * @device: Device to examine (starting at zero) - * - * This technique was originally described in - * Hale Landis's ATADRVR (www.ata-atapi.com), and - * later found its way into the ATA/ATAPI spec. - * - * Write a pattern to the ATA shadow registers, - * and if a device is present, it will respond by - * correctly storing and echoing back the - * ATA shadow register contents. - * - * LOCKING: - * caller. - */ - -static unsigned int ata_pio_devchk(struct ata_port *ap, - unsigned int device) -{ - struct ata_ioports *ioaddr = &ap->ioaddr; - u8 nsect, lbal; - - ap->ops->dev_select(ap, device); - - outb(0x55, ioaddr->nsect_addr); - outb(0xaa, ioaddr->lbal_addr); - - outb(0xaa, ioaddr->nsect_addr); - outb(0x55, ioaddr->lbal_addr); - - outb(0x55, ioaddr->nsect_addr); - outb(0xaa, ioaddr->lbal_addr); - - nsect = inb(ioaddr->nsect_addr); - lbal = inb(ioaddr->lbal_addr); - - if ((nsect == 0x55) && (lbal == 0xaa)) - return 1; /* we found a device */ - - return 0; /* nothing found */ -} - -/** - * ata_mmio_devchk - PATA device presence detection - * @ap: ATA channel to examine - * @device: Device to examine (starting at zero) - * - * This technique was originally described in - * Hale Landis's ATADRVR (www.ata-atapi.com), and - * later found its way into the ATA/ATAPI spec. - * - * Write a pattern to the ATA shadow registers, - * and if a device is present, it will respond by - * correctly storing and echoing back the - * ATA shadow register contents. - * - * LOCKING: - * caller. - */ - -static unsigned int ata_mmio_devchk(struct ata_port *ap, - unsigned int device) -{ - struct ata_ioports *ioaddr = &ap->ioaddr; - u8 nsect, lbal; - - ap->ops->dev_select(ap, device); - - writeb(0x55, (void __iomem *) ioaddr->nsect_addr); - writeb(0xaa, (void __iomem *) ioaddr->lbal_addr); - - writeb(0xaa, (void __iomem *) ioaddr->nsect_addr); - writeb(0x55, (void __iomem *) ioaddr->lbal_addr); - - writeb(0x55, (void __iomem *) ioaddr->nsect_addr); - writeb(0xaa, (void __iomem *) ioaddr->lbal_addr); - - nsect = readb((void __iomem *) ioaddr->nsect_addr); - lbal = readb((void __iomem *) ioaddr->lbal_addr); - - if ((nsect == 0x55) && (lbal == 0xaa)) - return 1; /* we found a device */ - - return 0; /* nothing found */ -} - -/** - * ata_devchk - PATA device presence detection - * @ap: ATA channel to examine - * @device: Device to examine (starting at zero) - * - * Dispatch ATA device presence detection, depending - * on whether we are using PIO or MMIO to talk to the - * ATA shadow registers. - * - * LOCKING: - * caller. - */ - -static unsigned int ata_devchk(struct ata_port *ap, - unsigned int device) -{ - if (ap->flags & ATA_FLAG_MMIO) - return ata_mmio_devchk(ap, device); - return ata_pio_devchk(ap, device); -} - -/** - * ata_dev_classify - determine device type based on ATA-spec signature - * @tf: ATA taskfile register set for device to be identified - * - * Determine from taskfile register contents whether a device is - * ATA or ATAPI, as per "Signature and persistence" section - * of ATA/PI spec (volume 1, sect 5.14). - * - * LOCKING: - * None. - * - * RETURNS: - * Device type, %ATA_DEV_ATA, %ATA_DEV_ATAPI, or %ATA_DEV_UNKNOWN - * the event of failure. - */ - -unsigned int ata_dev_classify(const struct ata_taskfile *tf) -{ - /* Apple's open source Darwin code hints that some devices only - * put a proper signature into the LBA mid/high registers, - * So, we only check those. It's sufficient for uniqueness. - */ - - if (((tf->lbam == 0) && (tf->lbah == 0)) || - ((tf->lbam == 0x3c) && (tf->lbah == 0xc3))) { - DPRINTK("found ATA device by sig\n"); - return ATA_DEV_ATA; - } - - if (((tf->lbam == 0x14) && (tf->lbah == 0xeb)) || - ((tf->lbam == 0x69) && (tf->lbah == 0x96))) { - DPRINTK("found ATAPI device by sig\n"); - return ATA_DEV_ATAPI; - } - - DPRINTK("unknown device\n"); - return ATA_DEV_UNKNOWN; -} - -/** - * ata_dev_try_classify - Parse returned ATA device signature - * @ap: ATA channel to examine - * @device: Device to examine (starting at zero) - * @r_err: Value of error register on completion - * - * After an event -- SRST, E.D.D., or SATA COMRESET -- occurs, - * an ATA/ATAPI-defined set of values is placed in the ATA - * shadow registers, indicating the results of device detection - * and diagnostics. - * - * Select the ATA device, and read the values from the ATA shadow - * registers. Then parse according to the Error register value, - * and the spec-defined values examined by ata_dev_classify(). - * - * LOCKING: - * caller. - * - * RETURNS: - * Device type - %ATA_DEV_ATA, %ATA_DEV_ATAPI or %ATA_DEV_NONE. - */ - -static unsigned int -ata_dev_try_classify(struct ata_port *ap, unsigned int device, u8 *r_err) -{ - struct ata_taskfile tf; - unsigned int class; - u8 err; - - ap->ops->dev_select(ap, device); - - memset(&tf, 0, sizeof(tf)); - - ap->ops->tf_read(ap, &tf); - err = tf.feature; - if (r_err) - *r_err = err; - - /* see if device passed diags */ - if (err == 1) - /* do nothing */ ; - else if ((device == 0) && (err == 0x81)) - /* do nothing */ ; - else - return ATA_DEV_NONE; - - /* determine if device is ATA or ATAPI */ - class = ata_dev_classify(&tf); - - if (class == ATA_DEV_UNKNOWN) - return ATA_DEV_NONE; - if ((class == ATA_DEV_ATA) && (ata_chk_status(ap) == 0)) - return ATA_DEV_NONE; - return class; -} - -/** - * ata_id_string - Convert IDENTIFY DEVICE page into string - * @id: IDENTIFY DEVICE results we will examine - * @s: string into which data is output - * @ofs: offset into identify device page - * @len: length of string to return. must be an even number. - * - * The strings in the IDENTIFY DEVICE page are broken up into - * 16-bit chunks. Run through the string, and output each - * 8-bit chunk linearly, regardless of platform. - * - * LOCKING: - * caller. - */ - -void ata_id_string(const u16 *id, unsigned char *s, - unsigned int ofs, unsigned int len) -{ - unsigned int c; - - while (len > 0) { - c = id[ofs] >> 8; - *s = c; - s++; - - c = id[ofs] & 0xff; - *s = c; - s++; - - ofs++; - len -= 2; - } -} - -/** - * ata_id_c_string - Convert IDENTIFY DEVICE page into C string - * @id: IDENTIFY DEVICE results we will examine - * @s: string into which data is output - * @ofs: offset into identify device page - * @len: length of string to return. must be an odd number. - * - * This function is identical to ata_id_string except that it - * trims trailing spaces and terminates the resulting string with - * null. @len must be actual maximum length (even number) + 1. - * - * LOCKING: - * caller. - */ -void ata_id_c_string(const u16 *id, unsigned char *s, - unsigned int ofs, unsigned int len) -{ - unsigned char *p; - - WARN_ON(!(len & 1)); - - ata_id_string(id, s, ofs, len - 1); - - p = s + strnlen(s, len - 1); - while (p > s && p[-1] == ' ') - p--; - *p = '\0'; -} - -static u64 ata_id_n_sectors(const u16 *id) -{ - if (ata_id_has_lba(id)) { - if (ata_id_has_lba48(id)) - return ata_id_u64(id, 100); - else - return ata_id_u32(id, 60); - } else { - if (ata_id_current_chs_valid(id)) - return ata_id_u32(id, 57); - else - return id[1] * id[3] * id[6]; - } -} - -/** - * ata_noop_dev_select - Select device 0/1 on ATA bus - * @ap: ATA channel to manipulate - * @device: ATA device (numbered from zero) to select - * - * This function performs no actual function. - * - * May be used as the dev_select() entry in ata_port_operations. - * - * LOCKING: - * caller. - */ -void ata_noop_dev_select (struct ata_port *ap, unsigned int device) -{ -} - - -/** - * ata_std_dev_select - Select device 0/1 on ATA bus - * @ap: ATA channel to manipulate - * @device: ATA device (numbered from zero) to select - * - * Use the method defined in the ATA specification to - * make either device 0, or device 1, active on the - * ATA channel. Works with both PIO and MMIO. - * - * May be used as the dev_select() entry in ata_port_operations. - * - * LOCKING: - * caller. - */ - -void ata_std_dev_select (struct ata_port *ap, unsigned int device) -{ - u8 tmp; - - if (device == 0) - tmp = ATA_DEVICE_OBS; - else - tmp = ATA_DEVICE_OBS | ATA_DEV1; - - if (ap->flags & ATA_FLAG_MMIO) { - writeb(tmp, (void __iomem *) ap->ioaddr.device_addr); - } else { - outb(tmp, ap->ioaddr.device_addr); - } - ata_pause(ap); /* needed; also flushes, for mmio */ -} - -/** - * ata_dev_select - Select device 0/1 on ATA bus - * @ap: ATA channel to manipulate - * @device: ATA device (numbered from zero) to select - * @wait: non-zero to wait for Status register BSY bit to clear - * @can_sleep: non-zero if context allows sleeping - * - * Use the method defined in the ATA specification to - * make either device 0, or device 1, active on the - * ATA channel. - * - * This is a high-level version of ata_std_dev_select(), - * which additionally provides the services of inserting - * the proper pauses and status polling, where needed. - * - * LOCKING: - * caller. - */ - -void ata_dev_select(struct ata_port *ap, unsigned int device, - unsigned int wait, unsigned int can_sleep) -{ - if (ata_msg_probe(ap)) - ata_port_printk(ap, KERN_INFO, "ata_dev_select: ENTER, ata%u: " - "device %u, wait %u\n", ap->id, device, wait); - - if (wait) - ata_wait_idle(ap); - - ap->ops->dev_select(ap, device); - - if (wait) { - if (can_sleep && ap->device[device].class == ATA_DEV_ATAPI) - msleep(150); - ata_wait_idle(ap); - } -} - -/** - * ata_dump_id - IDENTIFY DEVICE info debugging output - * @id: IDENTIFY DEVICE page to dump - * - * Dump selected 16-bit words from the given IDENTIFY DEVICE - * page. - * - * LOCKING: - * caller. - */ - -static inline void ata_dump_id(const u16 *id) -{ - DPRINTK("49==0x%04x " - "53==0x%04x " - "63==0x%04x " - "64==0x%04x " - "75==0x%04x \n", - id[49], - id[53], - id[63], - id[64], - id[75]); - DPRINTK("80==0x%04x " - "81==0x%04x " - "82==0x%04x " - "83==0x%04x " - "84==0x%04x \n", - id[80], - id[81], - id[82], - id[83], - id[84]); - DPRINTK("88==0x%04x " - "93==0x%04x\n", - id[88], - id[93]); -} - -/** - * ata_id_xfermask - Compute xfermask from the given IDENTIFY data - * @id: IDENTIFY data to compute xfer mask from - * - * Compute the xfermask for this device. This is not as trivial - * as it seems if we must consider early devices correctly. - * - * FIXME: pre IDE drive timing (do we care ?). - * - * LOCKING: - * None. - * - * RETURNS: - * Computed xfermask - */ -static unsigned int ata_id_xfermask(const u16 *id) -{ - unsigned int pio_mask, mwdma_mask, udma_mask; - - /* Usual case. Word 53 indicates word 64 is valid */ - if (id[ATA_ID_FIELD_VALID] & (1 << 1)) { - pio_mask = id[ATA_ID_PIO_MODES] & 0x03; - pio_mask <<= 3; - pio_mask |= 0x7; - } else { - /* If word 64 isn't valid then Word 51 high byte holds - * the PIO timing number for the maximum. Turn it into - * a mask. - */ - pio_mask = (2 << (id[ATA_ID_OLD_PIO_MODES] & 0xFF)) - 1 ; - - /* But wait.. there's more. Design your standards by - * committee and you too can get a free iordy field to - * process. However its the speeds not the modes that - * are supported... Note drivers using the timing API - * will get this right anyway - */ - } - - mwdma_mask = id[ATA_ID_MWDMA_MODES] & 0x07; - - udma_mask = 0; - if (id[ATA_ID_FIELD_VALID] & (1 << 2)) - udma_mask = id[ATA_ID_UDMA_MODES] & 0xff; - - return ata_pack_xfermask(pio_mask, mwdma_mask, udma_mask); -} - -/** - * ata_port_queue_task - Queue port_task - * @ap: The ata_port to queue port_task for - * @fn: workqueue function to be scheduled - * @data: data value to pass to workqueue function - * @delay: delay time for workqueue function - * - * Schedule @fn(@data) for execution after @delay jiffies using - * port_task. There is one port_task per port and it's the - * user(low level driver)'s responsibility to make sure that only - * one task is active at any given time. - * - * libata core layer takes care of synchronization between - * port_task and EH. ata_port_queue_task() may be ignored for EH - * synchronization. - * - * LOCKING: - * Inherited from caller. - */ -void ata_port_queue_task(struct ata_port *ap, void (*fn)(void *), void *data, - unsigned long delay) -{ - int rc; - - if (ap->pflags & ATA_PFLAG_FLUSH_PORT_TASK) - return; - - PREPARE_WORK(&ap->port_task, fn, data); - - if (!delay) - rc = queue_work(ata_wq, &ap->port_task); - else - rc = queue_delayed_work(ata_wq, &ap->port_task, delay); - - /* rc == 0 means that another user is using port task */ - WARN_ON(rc == 0); -} - -/** - * ata_port_flush_task - Flush port_task - * @ap: The ata_port to flush port_task for - * - * After this function completes, port_task is guranteed not to - * be running or scheduled. - * - * LOCKING: - * Kernel thread context (may sleep) - */ -void ata_port_flush_task(struct ata_port *ap) -{ - unsigned long flags; - - DPRINTK("ENTER\n"); - - spin_lock_irqsave(ap->lock, flags); - ap->pflags |= ATA_PFLAG_FLUSH_PORT_TASK; - spin_unlock_irqrestore(ap->lock, flags); - - DPRINTK("flush #1\n"); - flush_workqueue(ata_wq); - - /* - * At this point, if a task is running, it's guaranteed to see - * the FLUSH flag; thus, it will never queue pio tasks again. - * Cancel and flush. - */ - if (!cancel_delayed_work(&ap->port_task)) { - if (ata_msg_ctl(ap)) - ata_port_printk(ap, KERN_DEBUG, "%s: flush #2\n", - __FUNCTION__); - flush_workqueue(ata_wq); - } - - spin_lock_irqsave(ap->lock, flags); - ap->pflags &= ~ATA_PFLAG_FLUSH_PORT_TASK; - spin_unlock_irqrestore(ap->lock, flags); - - if (ata_msg_ctl(ap)) - ata_port_printk(ap, KERN_DEBUG, "%s: EXIT\n", __FUNCTION__); -} - -void ata_qc_complete_internal(struct ata_queued_cmd *qc) -{ - struct completion *waiting = qc->private_data; - - complete(waiting); -} - -/** - * ata_exec_internal - execute libata internal command - * @dev: Device to which the command is sent - * @tf: Taskfile registers for the command and the result - * @cdb: CDB for packet command - * @dma_dir: Data tranfer direction of the command - * @buf: Data buffer of the command - * @buflen: Length of data buffer - * - * Executes libata internal command with timeout. @tf contains - * command on entry and result on return. Timeout and error - * conditions are reported via return value. No recovery action - * is taken after a command times out. It's caller's duty to - * clean up after timeout. - * - * LOCKING: - * None. Should be called with kernel context, might sleep. - * - * RETURNS: - * Zero on success, AC_ERR_* mask on failure - */ -unsigned ata_exec_internal(struct ata_device *dev, - struct ata_taskfile *tf, const u8 *cdb, - int dma_dir, void *buf, unsigned int buflen) -{ - struct ata_port *ap = dev->ap; - u8 command = tf->command; - struct ata_queued_cmd *qc; - unsigned int tag, preempted_tag; - u32 preempted_sactive, preempted_qc_active; - DECLARE_COMPLETION_ONSTACK(wait); - unsigned long flags; - unsigned int err_mask; - int rc; - - spin_lock_irqsave(ap->lock, flags); - - /* no internal command while frozen */ - if (ap->pflags & ATA_PFLAG_FROZEN) { - spin_unlock_irqrestore(ap->lock, flags); - return AC_ERR_SYSTEM; - } - - /* initialize internal qc */ - - /* XXX: Tag 0 is used for drivers with legacy EH as some - * drivers choke if any other tag is given. This breaks - * ata_tag_internal() test for those drivers. Don't use new - * EH stuff without converting to it. - */ - if (ap->ops->error_handler) - tag = ATA_TAG_INTERNAL; - else - tag = 0; - - if (test_and_set_bit(tag, &ap->qc_allocated)) - BUG(); - qc = __ata_qc_from_tag(ap, tag); - - qc->tag = tag; - qc->scsicmd = NULL; - qc->ap = ap; - qc->dev = dev; - ata_qc_reinit(qc); - - preempted_tag = ap->active_tag; - preempted_sactive = ap->sactive; - preempted_qc_active = ap->qc_active; - ap->active_tag = ATA_TAG_POISON; - ap->sactive = 0; - ap->qc_active = 0; - - /* prepare & issue qc */ - qc->tf = *tf; - if (cdb) - memcpy(qc->cdb, cdb, ATAPI_CDB_LEN); - qc->flags |= ATA_QCFLAG_RESULT_TF; - qc->dma_dir = dma_dir; - if (dma_dir != DMA_NONE) { - ata_sg_init_one(qc, buf, buflen); - qc->nsect = buflen / ATA_SECT_SIZE; - } - - qc->private_data = &wait; - qc->complete_fn = ata_qc_complete_internal; - - ata_qc_issue(qc); - - spin_unlock_irqrestore(ap->lock, flags); - - rc = wait_for_completion_timeout(&wait, ata_probe_timeout); - - ata_port_flush_task(ap); - - if (!rc) { - spin_lock_irqsave(ap->lock, flags); - - /* We're racing with irq here. If we lose, the - * following test prevents us from completing the qc - * twice. If we win, the port is frozen and will be - * cleaned up by ->post_internal_cmd(). - */ - if (qc->flags & ATA_QCFLAG_ACTIVE) { - qc->err_mask |= AC_ERR_TIMEOUT; - - if (ap->ops->error_handler) - ata_port_freeze(ap); - else - ata_qc_complete(qc); - - if (ata_msg_warn(ap)) - ata_dev_printk(dev, KERN_WARNING, - "qc timeout (cmd 0x%x)\n", command); - } - - spin_unlock_irqrestore(ap->lock, flags); - } - - /* do post_internal_cmd */ - if (ap->ops->post_internal_cmd) - ap->ops->post_internal_cmd(qc); - - if (qc->flags & ATA_QCFLAG_FAILED && !qc->err_mask) { - if (ata_msg_warn(ap)) - ata_dev_printk(dev, KERN_WARNING, - "zero err_mask for failed " - "internal command, assuming AC_ERR_OTHER\n"); - qc->err_mask |= AC_ERR_OTHER; - } - - /* finish up */ - spin_lock_irqsave(ap->lock, flags); - - *tf = qc->result_tf; - err_mask = qc->err_mask; - - ata_qc_free(qc); - ap->active_tag = preempted_tag; - ap->sactive = preempted_sactive; - ap->qc_active = preempted_qc_active; - - /* XXX - Some LLDDs (sata_mv) disable port on command failure. - * Until those drivers are fixed, we detect the condition - * here, fail the command with AC_ERR_SYSTEM and reenable the - * port. - * - * Note that this doesn't change any behavior as internal - * command failure results in disabling the device in the - * higher layer for LLDDs without new reset/EH callbacks. - * - * Kill the following code as soon as those drivers are fixed. - */ - if (ap->flags & ATA_FLAG_DISABLED) { - err_mask |= AC_ERR_SYSTEM; - ata_port_probe(ap); - } - - spin_unlock_irqrestore(ap->lock, flags); - - return err_mask; -} - -/** - * ata_do_simple_cmd - execute simple internal command - * @dev: Device to which the command is sent - * @cmd: Opcode to execute - * - * Execute a 'simple' command, that only consists of the opcode - * 'cmd' itself, without filling any other registers - * - * LOCKING: - * Kernel thread context (may sleep). - * - * RETURNS: - * Zero on success, AC_ERR_* mask on failure - */ -unsigned int ata_do_simple_cmd(struct ata_device *dev, u8 cmd) -{ - struct ata_taskfile tf; - - ata_tf_init(dev, &tf); - - tf.command = cmd; - tf.flags |= ATA_TFLAG_DEVICE; - tf.protocol = ATA_PROT_NODATA; - - return ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0); -} - -/** - * ata_pio_need_iordy - check if iordy needed - * @adev: ATA device - * - * Check if the current speed of the device requires IORDY. Used - * by various controllers for chip configuration. - */ - -unsigned int ata_pio_need_iordy(const struct ata_device *adev) -{ - int pio; - int speed = adev->pio_mode - XFER_PIO_0; - - if (speed < 2) - return 0; - if (speed > 2) - return 1; - - /* If we have no drive specific rule, then PIO 2 is non IORDY */ - - if (adev->id[ATA_ID_FIELD_VALID] & 2) { /* EIDE */ - pio = adev->id[ATA_ID_EIDE_PIO]; - /* Is the speed faster than the drive allows non IORDY ? */ - if (pio) { - /* This is cycle times not frequency - watch the logic! */ - if (pio > 240) /* PIO2 is 240nS per cycle */ - return 1; - return 0; - } - } - return 0; -} - -/** - * ata_dev_read_id - Read ID data from the specified device - * @dev: target device - * @p_class: pointer to class of the target device (may be changed) - * @post_reset: is this read ID post-reset? - * @id: buffer to read IDENTIFY data into - * - * Read ID data from the specified device. ATA_CMD_ID_ATA is - * performed on ATA devices and ATA_CMD_ID_ATAPI on ATAPI - * devices. This function also issues ATA_CMD_INIT_DEV_PARAMS - * for pre-ATA4 drives. - * - * LOCKING: - * Kernel thread context (may sleep) - * - * RETURNS: - * 0 on success, -errno otherwise. - */ -int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class, - int post_reset, u16 *id) -{ - struct ata_port *ap = dev->ap; - unsigned int class = *p_class; - struct ata_taskfile tf; - unsigned int err_mask = 0; - const char *reason; - int rc; - - if (ata_msg_ctl(ap)) - ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER, host %u, dev %u\n", - __FUNCTION__, ap->id, dev->devno); - - ata_dev_select(ap, dev->devno, 1, 1); /* select device 0/1 */ - - retry: - ata_tf_init(dev, &tf); - - switch (class) { - case ATA_DEV_ATA: - tf.command = ATA_CMD_ID_ATA; - break; - case ATA_DEV_ATAPI: - tf.command = ATA_CMD_ID_ATAPI; - break; - default: - rc = -ENODEV; - reason = "unsupported class"; - goto err_out; - } - - tf.protocol = ATA_PROT_PIO; - - err_mask = ata_exec_internal(dev, &tf, NULL, DMA_FROM_DEVICE, - id, sizeof(id[0]) * ATA_ID_WORDS); - if (err_mask) { - rc = -EIO; - reason = "I/O error"; - goto err_out; - } - - swap_buf_le16(id, ATA_ID_WORDS); - - /* sanity check */ - if ((class == ATA_DEV_ATA) != (ata_id_is_ata(id) | ata_id_is_cfa(id))) { - rc = -EINVAL; - reason = "device reports illegal type"; - goto err_out; - } - - if (post_reset && class == ATA_DEV_ATA) { - /* - * The exact sequence expected by certain pre-ATA4 drives is: - * SRST RESET - * IDENTIFY - * INITIALIZE DEVICE PARAMETERS - * anything else.. - * Some drives were very specific about that exact sequence. - */ - if (ata_id_major_version(id) < 4 || !ata_id_has_lba(id)) { - err_mask = ata_dev_init_params(dev, id[3], id[6]); - if (err_mask) { - rc = -EIO; - reason = "INIT_DEV_PARAMS failed"; - goto err_out; - } - - /* current CHS translation info (id[53-58]) might be - * changed. reread the identify device info. - */ - post_reset = 0; - goto retry; - } - } - - *p_class = class; - - return 0; - - err_out: - if (ata_msg_warn(ap)) - ata_dev_printk(dev, KERN_WARNING, "failed to IDENTIFY " - "(%s, err_mask=0x%x)\n", reason, err_mask); - return rc; -} - -static inline u8 ata_dev_knobble(struct ata_device *dev) -{ - return ((dev->ap->cbl == ATA_CBL_SATA) && (!ata_id_is_sata(dev->id))); -} - -static void ata_dev_config_ncq(struct ata_device *dev, - char *desc, size_t desc_sz) -{ - struct ata_port *ap = dev->ap; - int hdepth = 0, ddepth = ata_id_queue_depth(dev->id); - - if (!ata_id_has_ncq(dev->id)) { - desc[0] = '\0'; - return; - } - - if (ap->flags & ATA_FLAG_NCQ) { - hdepth = min(ap->host->can_queue, ATA_MAX_QUEUE - 1); - dev->flags |= ATA_DFLAG_NCQ; - } - - if (hdepth >= ddepth) - snprintf(desc, desc_sz, "NCQ (depth %d)", ddepth); - else - snprintf(desc, desc_sz, "NCQ (depth %d/%d)", hdepth, ddepth); -} - -static void ata_set_port_max_cmd_len(struct ata_port *ap) -{ - int i; - - if (ap->host) { - ap->host->max_cmd_len = 0; - for (i = 0; i < ATA_MAX_DEVICES; i++) - ap->host->max_cmd_len = max_t(unsigned int, - ap->host->max_cmd_len, - ap->device[i].cdb_len); - } -} - -/** - * ata_dev_configure - Configure the specified ATA/ATAPI device - * @dev: Target device to configure - * @print_info: Enable device info printout - * - * Configure @dev according to @dev->id. Generic and low-level - * driver specific fixups are also applied. - * - * LOCKING: - * Kernel thread context (may sleep) - * - * RETURNS: - * 0 on success, -errno otherwise - */ -int ata_dev_configure(struct ata_device *dev, int print_info) -{ - struct ata_port *ap = dev->ap; - const u16 *id = dev->id; - unsigned int xfer_mask; - int rc; - - if (!ata_dev_enabled(dev) && ata_msg_info(ap)) { - ata_dev_printk(dev, KERN_INFO, - "%s: ENTER/EXIT (host %u, dev %u) -- nodev\n", - __FUNCTION__, ap->id, dev->devno); - return 0; - } - - if (ata_msg_probe(ap)) - ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER, host %u, dev %u\n", - __FUNCTION__, ap->id, dev->devno); - - /* print device capabilities */ - if (ata_msg_probe(ap)) - ata_dev_printk(dev, KERN_DEBUG, - "%s: cfg 49:%04x 82:%04x 83:%04x 84:%04x " - "85:%04x 86:%04x 87:%04x 88:%04x\n", - __FUNCTION__, - id[49], id[82], id[83], id[84], - id[85], id[86], id[87], id[88]); - - /* initialize to-be-configured parameters */ - dev->flags &= ~ATA_DFLAG_CFG_MASK; - dev->max_sectors = 0; - dev->cdb_len = 0; - dev->n_sectors = 0; - dev->cylinders = 0; - dev->heads = 0; - dev->sectors = 0; - - /* - * common ATA, ATAPI feature tests - */ - - /* find max transfer mode; for printk only */ - xfer_mask = ata_id_xfermask(id); - - if (ata_msg_probe(ap)) - ata_dump_id(id); - - /* ATA-specific feature tests */ - if (dev->class == ATA_DEV_ATA) { - dev->n_sectors = ata_id_n_sectors(id); - - if (ata_id_has_lba(id)) { - const char *lba_desc; - char ncq_desc[20]; - - lba_desc = "LBA"; - dev->flags |= ATA_DFLAG_LBA; - if (ata_id_has_lba48(id)) { - dev->flags |= ATA_DFLAG_LBA48; - lba_desc = "LBA48"; - } - - /* config NCQ */ - ata_dev_config_ncq(dev, ncq_desc, sizeof(ncq_desc)); - - /* print device info to dmesg */ - if (ata_msg_drv(ap) && print_info) - ata_dev_printk(dev, KERN_INFO, "ATA-%d, " - "max %s, %Lu sectors: %s %s\n", - ata_id_major_version(id), - ata_mode_string(xfer_mask), - (unsigned long long)dev->n_sectors, - lba_desc, ncq_desc); - } else { - /* CHS */ - - /* Default translation */ - dev->cylinders = id[1]; - dev->heads = id[3]; - dev->sectors = id[6]; - - if (ata_id_current_chs_valid(id)) { - /* Current CHS translation is valid. */ - dev->cylinders = id[54]; - dev->heads = id[55]; - dev->sectors = id[56]; - } - - /* print device info to dmesg */ - if (ata_msg_drv(ap) && print_info) - ata_dev_printk(dev, KERN_INFO, "ATA-%d, " - "max %s, %Lu sectors: CHS %u/%u/%u\n", - ata_id_major_version(id), - ata_mode_string(xfer_mask), - (unsigned long long)dev->n_sectors, - dev->cylinders, dev->heads, - dev->sectors); - } - - if (dev->id[59] & 0x100) { - dev->multi_count = dev->id[59] & 0xff; - if (ata_msg_drv(ap) && print_info) - ata_dev_printk(dev, KERN_INFO, - "ata%u: dev %u multi count %u\n", - ap->id, dev->devno, dev->multi_count); - } - - dev->cdb_len = 16; - } - - /* ATAPI-specific feature tests */ - else if (dev->class == ATA_DEV_ATAPI) { - char *cdb_intr_string = ""; - - rc = atapi_cdb_len(id); - if ((rc < 12) || (rc > ATAPI_CDB_LEN)) { - if (ata_msg_warn(ap)) - ata_dev_printk(dev, KERN_WARNING, - "unsupported CDB len\n"); - rc = -EINVAL; - goto err_out_nosup; - } - dev->cdb_len = (unsigned int) rc; - - if (ata_id_cdb_intr(dev->id)) { - dev->flags |= ATA_DFLAG_CDB_INTR; - cdb_intr_string = ", CDB intr"; - } - - /* print device info to dmesg */ - if (ata_msg_drv(ap) && print_info) - ata_dev_printk(dev, KERN_INFO, "ATAPI, max %s%s\n", - ata_mode_string(xfer_mask), - cdb_intr_string); - } - - ata_set_port_max_cmd_len(ap); - - /* limit bridge transfers to udma5, 200 sectors */ - if (ata_dev_knobble(dev)) { - if (ata_msg_drv(ap) && print_info) - ata_dev_printk(dev, KERN_INFO, - "applying bridge limits\n"); - dev->udma_mask &= ATA_UDMA5; - dev->max_sectors = ATA_MAX_SECTORS; - } - - if (ap->ops->dev_config) - ap->ops->dev_config(ap, dev); - - if (ata_msg_probe(ap)) - ata_dev_printk(dev, KERN_DEBUG, "%s: EXIT, drv_stat = 0x%x\n", - __FUNCTION__, ata_chk_status(ap)); - return 0; - -err_out_nosup: - if (ata_msg_probe(ap)) - ata_dev_printk(dev, KERN_DEBUG, - "%s: EXIT, err\n", __FUNCTION__); - return rc; -} - -/** - * ata_bus_probe - Reset and probe ATA bus - * @ap: Bus to probe - * - * Master ATA bus probing function. Initiates a hardware-dependent - * bus reset, then attempts to identify any devices found on - * the bus. - * - * LOCKING: - * PCI/etc. bus probe sem. - * - * RETURNS: - * Zero on success, negative errno otherwise. - */ - -int ata_bus_probe(struct ata_port *ap) -{ - unsigned int classes[ATA_MAX_DEVICES]; - int tries[ATA_MAX_DEVICES]; - int i, rc, down_xfermask; - struct ata_device *dev; - - ata_port_probe(ap); - - for (i = 0; i < ATA_MAX_DEVICES; i++) - tries[i] = ATA_PROBE_MAX_TRIES; - - retry: - down_xfermask = 0; - - /* reset and determine device classes */ - ap->ops->phy_reset(ap); - - for (i = 0; i < ATA_MAX_DEVICES; i++) { - dev = &ap->device[i]; - - if (!(ap->flags & ATA_FLAG_DISABLED) && - dev->class != ATA_DEV_UNKNOWN) - classes[dev->devno] = dev->class; - else - classes[dev->devno] = ATA_DEV_NONE; - - dev->class = ATA_DEV_UNKNOWN; - } - - ata_port_probe(ap); - - /* after the reset the device state is PIO 0 and the controller - state is undefined. Record the mode */ - - for (i = 0; i < ATA_MAX_DEVICES; i++) - ap->device[i].pio_mode = XFER_PIO_0; - - /* read IDENTIFY page and configure devices */ - for (i = 0; i < ATA_MAX_DEVICES; i++) { - dev = &ap->device[i]; - - if (tries[i]) - dev->class = classes[i]; - - if (!ata_dev_enabled(dev)) - continue; - - rc = ata_dev_read_id(dev, &dev->class, 1, dev->id); - if (rc) - goto fail; - - rc = ata_dev_configure(dev, 1); - if (rc) - goto fail; - } - - /* configure transfer mode */ - rc = ata_set_mode(ap, &dev); - if (rc) { - down_xfermask = 1; - goto fail; - } - - for (i = 0; i < ATA_MAX_DEVICES; i++) - if (ata_dev_enabled(&ap->device[i])) - return 0; - - /* no device present, disable port */ - ata_port_disable(ap); - ap->ops->port_disable(ap); - return -ENODEV; - - fail: - switch (rc) { - case -EINVAL: - case -ENODEV: - tries[dev->devno] = 0; - break; - case -EIO: - sata_down_spd_limit(ap); - /* fall through */ - default: - tries[dev->devno]--; - if (down_xfermask && - ata_down_xfermask_limit(dev, tries[dev->devno] == 1)) - tries[dev->devno] = 0; - } - - if (!tries[dev->devno]) { - ata_down_xfermask_limit(dev, 1); - ata_dev_disable(dev); - } - - goto retry; -} - -/** - * ata_port_probe - Mark port as enabled - * @ap: Port for which we indicate enablement - * - * Modify @ap data structure such that the system - * thinks that the entire port is enabled. - * - * LOCKING: host_set lock, or some other form of - * serialization. - */ - -void ata_port_probe(struct ata_port *ap) -{ - ap->flags &= ~ATA_FLAG_DISABLED; -} - -/** - * sata_print_link_status - Print SATA link status - * @ap: SATA port to printk link status about - * - * This function prints link speed and status of a SATA link. - * - * LOCKING: - * None. - */ -static void sata_print_link_status(struct ata_port *ap) -{ - u32 sstatus, scontrol, tmp; - - if (sata_scr_read(ap, SCR_STATUS, &sstatus)) - return; - sata_scr_read(ap, SCR_CONTROL, &scontrol); - - if (ata_port_online(ap)) { - tmp = (sstatus >> 4) & 0xf; - ata_port_printk(ap, KERN_INFO, - "SATA link up %s (SStatus %X SControl %X)\n", - sata_spd_string(tmp), sstatus, scontrol); - } else { - ata_port_printk(ap, KERN_INFO, - "SATA link down (SStatus %X SControl %X)\n", - sstatus, scontrol); - } -} - -/** - * __sata_phy_reset - Wake/reset a low-level SATA PHY - * @ap: SATA port associated with target SATA PHY. - * - * This function issues commands to standard SATA Sxxx - * PHY registers, to wake up the phy (and device), and - * clear any reset condition. - * - * LOCKING: - * PCI/etc. bus probe sem. - * - */ -void __sata_phy_reset(struct ata_port *ap) -{ - u32 sstatus; - unsigned long timeout = jiffies + (HZ * 5); - - if (ap->flags & ATA_FLAG_SATA_RESET) { - /* issue phy wake/reset */ - sata_scr_write_flush(ap, SCR_CONTROL, 0x301); - /* Couldn't find anything in SATA I/II specs, but - * AHCI-1.1 10.4.2 says at least 1 ms. */ - mdelay(1); - } - /* phy wake/clear reset */ - sata_scr_write_flush(ap, SCR_CONTROL, 0x300); - - /* wait for phy to become ready, if necessary */ - do { - msleep(200); - sata_scr_read(ap, SCR_STATUS, &sstatus); - if ((sstatus & 0xf) != 1) - break; - } while (time_before(jiffies, timeout)); - - /* print link status */ - sata_print_link_status(ap); - - /* TODO: phy layer with polling, timeouts, etc. */ - if (!ata_port_offline(ap)) - ata_port_probe(ap); - else - ata_port_disable(ap); - - if (ap->flags & ATA_FLAG_DISABLED) - return; - - if (ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT)) { - ata_port_disable(ap); - return; - } - - ap->cbl = ATA_CBL_SATA; -} - -/** - * sata_phy_reset - Reset SATA bus. - * @ap: SATA port associated with target SATA PHY. - * - * This function resets the SATA bus, and then probes - * the bus for devices. - * - * LOCKING: - * PCI/etc. bus probe sem. - * - */ -void sata_phy_reset(struct ata_port *ap) -{ - __sata_phy_reset(ap); - if (ap->flags & ATA_FLAG_DISABLED) - return; - ata_bus_reset(ap); -} - -/** - * ata_dev_pair - return other device on cable - * @adev: device - * - * Obtain the other device on the same cable, or if none is - * present NULL is returned - */ - -struct ata_device *ata_dev_pair(struct ata_device *adev) -{ - struct ata_port *ap = adev->ap; - struct ata_device *pair = &ap->device[1 - adev->devno]; - if (!ata_dev_enabled(pair)) - return NULL; - return pair; -} - -/** - * ata_port_disable - Disable port. - * @ap: Port to be disabled. - * - * Modify @ap data structure such that the system - * thinks that the entire port is disabled, and should - * never attempt to probe or communicate with devices - * on this port. - * - * LOCKING: host_set lock, or some other form of - * serialization. - */ - -void ata_port_disable(struct ata_port *ap) -{ - ap->device[0].class = ATA_DEV_NONE; - ap->device[1].class = ATA_DEV_NONE; - ap->flags |= ATA_FLAG_DISABLED; -} - -/** - * sata_down_spd_limit - adjust SATA spd limit downward - * @ap: Port to adjust SATA spd limit for - * - * Adjust SATA spd limit of @ap downward. Note that this - * function only adjusts the limit. The change must be applied - * using sata_set_spd(). - * - * LOCKING: - * Inherited from caller. - * - * RETURNS: - * 0 on success, negative errno on failure - */ -int sata_down_spd_limit(struct ata_port *ap) -{ - u32 sstatus, spd, mask; - int rc, highbit; - - rc = sata_scr_read(ap, SCR_STATUS, &sstatus); - if (rc) - return rc; - - mask = ap->sata_spd_limit; - if (mask <= 1) - return -EINVAL; - highbit = fls(mask) - 1; - mask &= ~(1 << highbit); - - spd = (sstatus >> 4) & 0xf; - if (spd <= 1) - return -EINVAL; - spd--; - mask &= (1 << spd) - 1; - if (!mask) - return -EINVAL; - - ap->sata_spd_limit = mask; - - ata_port_printk(ap, KERN_WARNING, "limiting SATA link speed to %s\n", - sata_spd_string(fls(mask))); - - return 0; -} - -static int __sata_set_spd_needed(struct ata_port *ap, u32 *scontrol) -{ - u32 spd, limit; - - if (ap->sata_spd_limit == UINT_MAX) - limit = 0; - else - limit = fls(ap->sata_spd_limit); - - spd = (*scontrol >> 4) & 0xf; - *scontrol = (*scontrol & ~0xf0) | ((limit & 0xf) << 4); - - return spd != limit; -} - -/** - * sata_set_spd_needed - is SATA spd configuration needed - * @ap: Port in question - * - * Test whether the spd limit in SControl matches - * @ap->sata_spd_limit. This function is used to determine - * whether hardreset is necessary to apply SATA spd - * configuration. - * - * LOCKING: - * Inherited from caller. - * - * RETURNS: - * 1 if SATA spd configuration is needed, 0 otherwise. - */ -int sata_set_spd_needed(struct ata_port *ap) -{ - u32 scontrol; - - if (sata_scr_read(ap, SCR_CONTROL, &scontrol)) - return 0; - - return __sata_set_spd_needed(ap, &scontrol); -} - -/** - * sata_set_spd - set SATA spd according to spd limit - * @ap: Port to set SATA spd for - * - * Set SATA spd of @ap according to sata_spd_limit. - * - * LOCKING: - * Inherited from caller. - * - * RETURNS: - * 0 if spd doesn't need to be changed, 1 if spd has been - * changed. Negative errno if SCR registers are inaccessible. - */ -int sata_set_spd(struct ata_port *ap) -{ - u32 scontrol; - int rc; - - if ((rc = sata_scr_read(ap, SCR_CONTROL, &scontrol))) - return rc; - - if (!__sata_set_spd_needed(ap, &scontrol)) - return 0; - - if ((rc = sata_scr_write(ap, SCR_CONTROL, scontrol))) - return rc; - - return 1; -} - -/* - * This mode timing computation functionality is ported over from - * drivers/ide/ide-timing.h and was originally written by Vojtech Pavlik - */ -/* - * PIO 0-5, MWDMA 0-2 and UDMA 0-6 timings (in nanoseconds). - * These were taken from ATA/ATAPI-6 standard, rev 0a, except - * for PIO 5, which is a nonstandard extension and UDMA6, which - * is currently supported only by Maxtor drives. - */ - -static const struct ata_timing ata_timing[] = { - - { XFER_UDMA_6, 0, 0, 0, 0, 0, 0, 0, 15 }, - { XFER_UDMA_5, 0, 0, 0, 0, 0, 0, 0, 20 }, - { XFER_UDMA_4, 0, 0, 0, 0, 0, 0, 0, 30 }, - { XFER_UDMA_3, 0, 0, 0, 0, 0, 0, 0, 45 }, - - { XFER_UDMA_2, 0, 0, 0, 0, 0, 0, 0, 60 }, - { XFER_UDMA_1, 0, 0, 0, 0, 0, 0, 0, 80 }, - { XFER_UDMA_0, 0, 0, 0, 0, 0, 0, 0, 120 }, - -/* { XFER_UDMA_SLOW, 0, 0, 0, 0, 0, 0, 0, 150 }, */ - - { XFER_MW_DMA_2, 25, 0, 0, 0, 70, 25, 120, 0 }, - { XFER_MW_DMA_1, 45, 0, 0, 0, 80, 50, 150, 0 }, - { XFER_MW_DMA_0, 60, 0, 0, 0, 215, 215, 480, 0 }, - - { XFER_SW_DMA_2, 60, 0, 0, 0, 120, 120, 240, 0 }, - { XFER_SW_DMA_1, 90, 0, 0, 0, 240, 240, 480, 0 }, - { XFER_SW_DMA_0, 120, 0, 0, 0, 480, 480, 960, 0 }, - -/* { XFER_PIO_5, 20, 50, 30, 100, 50, 30, 100, 0 }, */ - { XFER_PIO_4, 25, 70, 25, 120, 70, 25, 120, 0 }, - { XFER_PIO_3, 30, 80, 70, 180, 80, 70, 180, 0 }, - - { XFER_PIO_2, 30, 290, 40, 330, 100, 90, 240, 0 }, - { XFER_PIO_1, 50, 290, 93, 383, 125, 100, 383, 0 }, - { XFER_PIO_0, 70, 290, 240, 600, 165, 150, 600, 0 }, - -/* { XFER_PIO_SLOW, 120, 290, 240, 960, 290, 240, 960, 0 }, */ - - { 0xFF } -}; - -#define ENOUGH(v,unit) (((v)-1)/(unit)+1) -#define EZ(v,unit) ((v)?ENOUGH(v,unit):0) - -static void ata_timing_quantize(const struct ata_timing *t, struct ata_timing *q, int T, int UT) -{ - q->setup = EZ(t->setup * 1000, T); - q->act8b = EZ(t->act8b * 1000, T); - q->rec8b = EZ(t->rec8b * 1000, T); - q->cyc8b = EZ(t->cyc8b * 1000, T); - q->active = EZ(t->active * 1000, T); - q->recover = EZ(t->recover * 1000, T); - q->cycle = EZ(t->cycle * 1000, T); - q->udma = EZ(t->udma * 1000, UT); -} - -void ata_timing_merge(const struct ata_timing *a, const struct ata_timing *b, - struct ata_timing *m, unsigned int what) -{ - if (what & ATA_TIMING_SETUP ) m->setup = max(a->setup, b->setup); - if (what & ATA_TIMING_ACT8B ) m->act8b = max(a->act8b, b->act8b); - if (what & ATA_TIMING_REC8B ) m->rec8b = max(a->rec8b, b->rec8b); - if (what & ATA_TIMING_CYC8B ) m->cyc8b = max(a->cyc8b, b->cyc8b); - if (what & ATA_TIMING_ACTIVE ) m->active = max(a->active, b->active); - if (what & ATA_TIMING_RECOVER) m->recover = max(a->recover, b->recover); - if (what & ATA_TIMING_CYCLE ) m->cycle = max(a->cycle, b->cycle); - if (what & ATA_TIMING_UDMA ) m->udma = max(a->udma, b->udma); -} - -static const struct ata_timing* ata_timing_find_mode(unsigned short speed) -{ - const struct ata_timing *t; - - for (t = ata_timing; t->mode != speed; t++) - if (t->mode == 0xFF) - return NULL; - return t; -} - -int ata_timing_compute(struct ata_device *adev, unsigned short speed, - struct ata_timing *t, int T, int UT) -{ - const struct ata_timing *s; - struct ata_timing p; - - /* - * Find the mode. - */ - - if (!(s = ata_timing_find_mode(speed))) - return -EINVAL; - - memcpy(t, s, sizeof(*s)); - - /* - * If the drive is an EIDE drive, it can tell us it needs extended - * PIO/MW_DMA cycle timing. - */ - - if (adev->id[ATA_ID_FIELD_VALID] & 2) { /* EIDE drive */ - memset(&p, 0, sizeof(p)); - if(speed >= XFER_PIO_0 && speed <= XFER_SW_DMA_0) { - if (speed <= XFER_PIO_2) p.cycle = p.cyc8b = adev->id[ATA_ID_EIDE_PIO]; - else p.cycle = p.cyc8b = adev->id[ATA_ID_EIDE_PIO_IORDY]; - } else if(speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2) { - p.cycle = adev->id[ATA_ID_EIDE_DMA_MIN]; - } - ata_timing_merge(&p, t, t, ATA_TIMING_CYCLE | ATA_TIMING_CYC8B); - } - - /* - * Convert the timing to bus clock counts. - */ - - ata_timing_quantize(t, t, T, UT); - - /* - * Even in DMA/UDMA modes we still use PIO access for IDENTIFY, - * S.M.A.R.T * and some other commands. We have to ensure that the - * DMA cycle timing is slower/equal than the fastest PIO timing. - */ - - if (speed > XFER_PIO_4) { - ata_timing_compute(adev, adev->pio_mode, &p, T, UT); - ata_timing_merge(&p, t, t, ATA_TIMING_ALL); - } - - /* - * Lengthen active & recovery time so that cycle time is correct. - */ - - if (t->act8b + t->rec8b < t->cyc8b) { - t->act8b += (t->cyc8b - (t->act8b + t->rec8b)) / 2; - t->rec8b = t->cyc8b - t->act8b; - } - - if (t->active + t->recover < t->cycle) { - t->active += (t->cycle - (t->active + t->recover)) / 2; - t->recover = t->cycle - t->active; - } - - return 0; -} - -/** - * ata_down_xfermask_limit - adjust dev xfer masks downward - * @dev: Device to adjust xfer masks - * @force_pio0: Force PIO0 - * - * Adjust xfer masks of @dev downward. Note that this function - * does not apply the change. Invoking ata_set_mode() afterwards - * will apply the limit. - * - * LOCKING: - * Inherited from caller. - * - * RETURNS: - * 0 on success, negative errno on failure - */ -int ata_down_xfermask_limit(struct ata_device *dev, int force_pio0) -{ - unsigned long xfer_mask; - int highbit; - - xfer_mask = ata_pack_xfermask(dev->pio_mask, dev->mwdma_mask, - dev->udma_mask); - - if (!xfer_mask) - goto fail; - /* don't gear down to MWDMA from UDMA, go directly to PIO */ - if (xfer_mask & ATA_MASK_UDMA) - xfer_mask &= ~ATA_MASK_MWDMA; - - highbit = fls(xfer_mask) - 1; - xfer_mask &= ~(1 << highbit); - if (force_pio0) - xfer_mask &= 1 << ATA_SHIFT_PIO; - if (!xfer_mask) - goto fail; - - ata_unpack_xfermask(xfer_mask, &dev->pio_mask, &dev->mwdma_mask, - &dev->udma_mask); - - ata_dev_printk(dev, KERN_WARNING, "limiting speed to %s\n", - ata_mode_string(xfer_mask)); - - return 0; - - fail: - return -EINVAL; -} - -static int ata_dev_set_mode(struct ata_device *dev) -{ - unsigned int err_mask; - int rc; - - dev->flags &= ~ATA_DFLAG_PIO; - if (dev->xfer_shift == ATA_SHIFT_PIO) - dev->flags |= ATA_DFLAG_PIO; - - err_mask = ata_dev_set_xfermode(dev); - if (err_mask) { - ata_dev_printk(dev, KERN_ERR, "failed to set xfermode " - "(err_mask=0x%x)\n", err_mask); - return -EIO; - } - - rc = ata_dev_revalidate(dev, 0); - if (rc) - return rc; - - DPRINTK("xfer_shift=%u, xfer_mode=0x%x\n", - dev->xfer_shift, (int)dev->xfer_mode); - - ata_dev_printk(dev, KERN_INFO, "configured for %s\n", - ata_mode_string(ata_xfer_mode2mask(dev->xfer_mode))); - return 0; -} - -/** - * ata_set_mode - Program timings and issue SET FEATURES - XFER - * @ap: port on which timings will be programmed - * @r_failed_dev: out paramter for failed device - * - * Set ATA device disk transfer mode (PIO3, UDMA6, etc.). If - * ata_set_mode() fails, pointer to the failing device is - * returned in @r_failed_dev. - * - * LOCKING: - * PCI/etc. bus probe sem. - * - * RETURNS: - * 0 on success, negative errno otherwise - */ -int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev) -{ - struct ata_device *dev; - int i, rc = 0, used_dma = 0, found = 0; - - /* has private set_mode? */ - if (ap->ops->set_mode) { - /* FIXME: make ->set_mode handle no device case and - * return error code and failing device on failure. - */ - for (i = 0; i < ATA_MAX_DEVICES; i++) { - if (ata_dev_ready(&ap->device[i])) { - ap->ops->set_mode(ap); - break; - } - } - return 0; - } - - /* step 1: calculate xfer_mask */ - for (i = 0; i < ATA_MAX_DEVICES; i++) { - unsigned int pio_mask, dma_mask; - - dev = &ap->device[i]; - - if (!ata_dev_enabled(dev)) - continue; - - ata_dev_xfermask(dev); - - pio_mask = ata_pack_xfermask(dev->pio_mask, 0, 0); - dma_mask = ata_pack_xfermask(0, dev->mwdma_mask, dev->udma_mask); - dev->pio_mode = ata_xfer_mask2mode(pio_mask); - dev->dma_mode = ata_xfer_mask2mode(dma_mask); - - found = 1; - if (dev->dma_mode) - used_dma = 1; - } - if (!found) - goto out; - - /* step 2: always set host PIO timings */ - for (i = 0; i < ATA_MAX_DEVICES; i++) { - dev = &ap->device[i]; - if (!ata_dev_enabled(dev)) - continue; - - if (!dev->pio_mode) { - ata_dev_printk(dev, KERN_WARNING, "no PIO support\n"); - rc = -EINVAL; - goto out; - } - - dev->xfer_mode = dev->pio_mode; - dev->xfer_shift = ATA_SHIFT_PIO; - if (ap->ops->set_piomode) - ap->ops->set_piomode(ap, dev); - } - - /* step 3: set host DMA timings */ - for (i = 0; i < ATA_MAX_DEVICES; i++) { - dev = &ap->device[i]; - - if (!ata_dev_enabled(dev) || !dev->dma_mode) - continue; - - dev->xfer_mode = dev->dma_mode; - dev->xfer_shift = ata_xfer_mode2shift(dev->dma_mode); - if (ap->ops->set_dmamode) - ap->ops->set_dmamode(ap, dev); - } - - /* step 4: update devices' xfer mode */ - for (i = 0; i < ATA_MAX_DEVICES; i++) { - dev = &ap->device[i]; - - /* don't udpate suspended devices' xfer mode */ - if (!ata_dev_ready(dev)) - continue; - - rc = ata_dev_set_mode(dev); - if (rc) - goto out; - } - - /* Record simplex status. If we selected DMA then the other - * host channels are not permitted to do so. - */ - if (used_dma && (ap->host_set->flags & ATA_HOST_SIMPLEX)) - ap->host_set->simplex_claimed = 1; - - /* step5: chip specific finalisation */ - if (ap->ops->post_set_mode) - ap->ops->post_set_mode(ap); - - out: - if (rc) - *r_failed_dev = dev; - return rc; -} - -/** - * ata_tf_to_host - issue ATA taskfile to host controller - * @ap: port to which command is being issued - * @tf: ATA taskfile register set - * - * Issues ATA taskfile register set to ATA host controller, - * with proper synchronization with interrupt handler and - * other threads. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -static inline void ata_tf_to_host(struct ata_port *ap, - const struct ata_taskfile *tf) -{ - ap->ops->tf_load(ap, tf); - ap->ops->exec_command(ap, tf); -} - -/** - * ata_busy_sleep - sleep until BSY clears, or timeout - * @ap: port containing status register to be polled - * @tmout_pat: impatience timeout - * @tmout: overall timeout - * - * Sleep until ATA Status register bit BSY clears, - * or a timeout occurs. - * - * LOCKING: None. - */ - -unsigned int ata_busy_sleep (struct ata_port *ap, - unsigned long tmout_pat, unsigned long tmout) -{ - unsigned long timer_start, timeout; - u8 status; - - status = ata_busy_wait(ap, ATA_BUSY, 300); - timer_start = jiffies; - timeout = timer_start + tmout_pat; - while ((status & ATA_BUSY) && (time_before(jiffies, timeout))) { - msleep(50); - status = ata_busy_wait(ap, ATA_BUSY, 3); - } - - if (status & ATA_BUSY) - ata_port_printk(ap, KERN_WARNING, - "port is slow to respond, please be patient\n"); - - timeout = timer_start + tmout; - while ((status & ATA_BUSY) && (time_before(jiffies, timeout))) { - msleep(50); - status = ata_chk_status(ap); - } - - if (status & ATA_BUSY) { - ata_port_printk(ap, KERN_ERR, "port failed to respond " - "(%lu secs)\n", tmout / HZ); - return 1; - } - - return 0; -} - -static void ata_bus_post_reset(struct ata_port *ap, unsigned int devmask) -{ - struct ata_ioports *ioaddr = &ap->ioaddr; - unsigned int dev0 = devmask & (1 << 0); - unsigned int dev1 = devmask & (1 << 1); - unsigned long timeout; - - /* if device 0 was found in ata_devchk, wait for its - * BSY bit to clear - */ - if (dev0) - ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT); - - /* if device 1 was found in ata_devchk, wait for - * register access, then wait for BSY to clear - */ - timeout = jiffies + ATA_TMOUT_BOOT; - while (dev1) { - u8 nsect, lbal; - - ap->ops->dev_select(ap, 1); - if (ap->flags & ATA_FLAG_MMIO) { - nsect = readb((void __iomem *) ioaddr->nsect_addr); - lbal = readb((void __iomem *) ioaddr->lbal_addr); - } else { - nsect = inb(ioaddr->nsect_addr); - lbal = inb(ioaddr->lbal_addr); - } - if ((nsect == 1) && (lbal == 1)) - break; - if (time_after(jiffies, timeout)) { - dev1 = 0; - break; - } - msleep(50); /* give drive a breather */ - } - if (dev1) - ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT); - - /* is all this really necessary? */ - ap->ops->dev_select(ap, 0); - if (dev1) - ap->ops->dev_select(ap, 1); - if (dev0) - ap->ops->dev_select(ap, 0); -} - -static unsigned int ata_bus_softreset(struct ata_port *ap, - unsigned int devmask) -{ - struct ata_ioports *ioaddr = &ap->ioaddr; - - DPRINTK("ata%u: bus reset via SRST\n", ap->id); - - /* software reset. causes dev0 to be selected */ - if (ap->flags & ATA_FLAG_MMIO) { - writeb(ap->ctl, (void __iomem *) ioaddr->ctl_addr); - udelay(20); /* FIXME: flush */ - writeb(ap->ctl | ATA_SRST, (void __iomem *) ioaddr->ctl_addr); - udelay(20); /* FIXME: flush */ - writeb(ap->ctl, (void __iomem *) ioaddr->ctl_addr); - } else { - outb(ap->ctl, ioaddr->ctl_addr); - udelay(10); - outb(ap->ctl | ATA_SRST, ioaddr->ctl_addr); - udelay(10); - outb(ap->ctl, ioaddr->ctl_addr); - } - - /* spec mandates ">= 2ms" before checking status. - * We wait 150ms, because that was the magic delay used for - * ATAPI devices in Hale Landis's ATADRVR, for the period of time - * between when the ATA command register is written, and then - * status is checked. Because waiting for "a while" before - * checking status is fine, post SRST, we perform this magic - * delay here as well. - * - * Old drivers/ide uses the 2mS rule and then waits for ready - */ - msleep(150); - - /* Before we perform post reset processing we want to see if - * the bus shows 0xFF because the odd clown forgets the D7 - * pulldown resistor. - */ - if (ata_check_status(ap) == 0xFF) { - ata_port_printk(ap, KERN_ERR, "SRST failed (status 0xFF)\n"); - return AC_ERR_OTHER; - } - - ata_bus_post_reset(ap, devmask); - - return 0; -} - -/** - * ata_bus_reset - reset host port and associated ATA channel - * @ap: port to reset - * - * This is typically the first time we actually start issuing - * commands to the ATA channel. We wait for BSY to clear, then - * issue EXECUTE DEVICE DIAGNOSTIC command, polling for its - * result. Determine what devices, if any, are on the channel - * by looking at the device 0/1 error register. Look at the signature - * stored in each device's taskfile registers, to determine if - * the device is ATA or ATAPI. - * - * LOCKING: - * PCI/etc. bus probe sem. - * Obtains host_set lock. - * - * SIDE EFFECTS: - * Sets ATA_FLAG_DISABLED if bus reset fails. - */ - -void ata_bus_reset(struct ata_port *ap) -{ - struct ata_ioports *ioaddr = &ap->ioaddr; - unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS; - u8 err; - unsigned int dev0, dev1 = 0, devmask = 0; - - DPRINTK("ENTER, host %u, port %u\n", ap->id, ap->port_no); - - /* determine if device 0/1 are present */ - if (ap->flags & ATA_FLAG_SATA_RESET) - dev0 = 1; - else { - dev0 = ata_devchk(ap, 0); - if (slave_possible) - dev1 = ata_devchk(ap, 1); - } - - if (dev0) - devmask |= (1 << 0); - if (dev1) - devmask |= (1 << 1); - - /* select device 0 again */ - ap->ops->dev_select(ap, 0); - - /* issue bus reset */ - if (ap->flags & ATA_FLAG_SRST) - if (ata_bus_softreset(ap, devmask)) - goto err_out; - - /* - * determine by signature whether we have ATA or ATAPI devices - */ - ap->device[0].class = ata_dev_try_classify(ap, 0, &err); - if ((slave_possible) && (err != 0x81)) - ap->device[1].class = ata_dev_try_classify(ap, 1, &err); - - /* re-enable interrupts */ - if (ap->ioaddr.ctl_addr) /* FIXME: hack. create a hook instead */ - ata_irq_on(ap); - - /* is double-select really necessary? */ - if (ap->device[1].class != ATA_DEV_NONE) - ap->ops->dev_select(ap, 1); - if (ap->device[0].class != ATA_DEV_NONE) - ap->ops->dev_select(ap, 0); - - /* if no devices were detected, disable this port */ - if ((ap->device[0].class == ATA_DEV_NONE) && - (ap->device[1].class == ATA_DEV_NONE)) - goto err_out; - - if (ap->flags & (ATA_FLAG_SATA_RESET | ATA_FLAG_SRST)) { - /* set up device control for ATA_FLAG_SATA_RESET */ - if (ap->flags & ATA_FLAG_MMIO) - writeb(ap->ctl, (void __iomem *) ioaddr->ctl_addr); - else - outb(ap->ctl, ioaddr->ctl_addr); - } - - DPRINTK("EXIT\n"); - return; - -err_out: - ata_port_printk(ap, KERN_ERR, "disabling port\n"); - ap->ops->port_disable(ap); - - DPRINTK("EXIT\n"); -} - -/** - * sata_phy_debounce - debounce SATA phy status - * @ap: ATA port to debounce SATA phy status for - * @params: timing parameters { interval, duratinon, timeout } in msec - * - * Make sure SStatus of @ap reaches stable state, determined by - * holding the same value where DET is not 1 for @duration polled - * every @interval, before @timeout. Timeout constraints the - * beginning of the stable state. Because, after hot unplugging, - * DET gets stuck at 1 on some controllers, this functions waits - * until timeout then returns 0 if DET is stable at 1. - * - * LOCKING: - * Kernel thread context (may sleep) - * - * RETURNS: - * 0 on success, -errno on failure. - */ -int sata_phy_debounce(struct ata_port *ap, const unsigned long *params) -{ - unsigned long interval_msec = params[0]; - unsigned long duration = params[1] * HZ / 1000; - unsigned long timeout = jiffies + params[2] * HZ / 1000; - unsigned long last_jiffies; - u32 last, cur; - int rc; - - if ((rc = sata_scr_read(ap, SCR_STATUS, &cur))) - return rc; - cur &= 0xf; - - last = cur; - last_jiffies = jiffies; - - while (1) { - msleep(interval_msec); - if ((rc = sata_scr_read(ap, SCR_STATUS, &cur))) - return rc; - cur &= 0xf; - - /* DET stable? */ - if (cur == last) { - if (cur == 1 && time_before(jiffies, timeout)) - continue; - if (time_after(jiffies, last_jiffies + duration)) - return 0; - continue; - } - - /* unstable, start over */ - last = cur; - last_jiffies = jiffies; - - /* check timeout */ - if (time_after(jiffies, timeout)) - return -EBUSY; - } -} - -/** - * sata_phy_resume - resume SATA phy - * @ap: ATA port to resume SATA phy for - * @params: timing parameters { interval, duratinon, timeout } in msec - * - * Resume SATA phy of @ap and debounce it. - * - * LOCKING: - * Kernel thread context (may sleep) - * - * RETURNS: - * 0 on success, -errno on failure. - */ -int sata_phy_resume(struct ata_port *ap, const unsigned long *params) -{ - u32 scontrol; - int rc; - - if ((rc = sata_scr_read(ap, SCR_CONTROL, &scontrol))) - return rc; - - scontrol = (scontrol & 0x0f0) | 0x300; - - if ((rc = sata_scr_write(ap, SCR_CONTROL, scontrol))) - return rc; - - /* Some PHYs react badly if SStatus is pounded immediately - * after resuming. Delay 200ms before debouncing. - */ - msleep(200); - - return sata_phy_debounce(ap, params); -} - -static void ata_wait_spinup(struct ata_port *ap) -{ - struct ata_eh_context *ehc = &ap->eh_context; - unsigned long end, secs; - int rc; - - /* first, debounce phy if SATA */ - if (ap->cbl == ATA_CBL_SATA) { - rc = sata_phy_debounce(ap, sata_deb_timing_hotplug); - - /* if debounced successfully and offline, no need to wait */ - if ((rc == 0 || rc == -EOPNOTSUPP) && ata_port_offline(ap)) - return; - } - - /* okay, let's give the drive time to spin up */ - end = ehc->i.hotplug_timestamp + ATA_SPINUP_WAIT * HZ / 1000; - secs = ((end - jiffies) + HZ - 1) / HZ; - - if (time_after(jiffies, end)) - return; - - if (secs > 5) - ata_port_printk(ap, KERN_INFO, "waiting for device to spin up " - "(%lu secs)\n", secs); - - schedule_timeout_uninterruptible(end - jiffies); -} - -/** - * ata_std_prereset - prepare for reset - * @ap: ATA port to be reset - * - * @ap is about to be reset. Initialize it. - * - * LOCKING: - * Kernel thread context (may sleep) - * - * RETURNS: - * 0 on success, -errno otherwise. - */ -int ata_std_prereset(struct ata_port *ap) -{ - struct ata_eh_context *ehc = &ap->eh_context; - const unsigned long *timing = sata_ehc_deb_timing(ehc); - int rc; - - /* handle link resume & hotplug spinup */ - if ((ehc->i.flags & ATA_EHI_RESUME_LINK) && - (ap->flags & ATA_FLAG_HRST_TO_RESUME)) - ehc->i.action |= ATA_EH_HARDRESET; - - if ((ehc->i.flags & ATA_EHI_HOTPLUGGED) && - (ap->flags & ATA_FLAG_SKIP_D2H_BSY)) - ata_wait_spinup(ap); - - /* if we're about to do hardreset, nothing more to do */ - if (ehc->i.action & ATA_EH_HARDRESET) - return 0; - - /* if SATA, resume phy */ - if (ap->cbl == ATA_CBL_SATA) { - rc = sata_phy_resume(ap, timing); - if (rc && rc != -EOPNOTSUPP) { - /* phy resume failed */ - ata_port_printk(ap, KERN_WARNING, "failed to resume " - "link for reset (errno=%d)\n", rc); - return rc; - } - } - - /* Wait for !BSY if the controller can wait for the first D2H - * Reg FIS and we don't know that no device is attached. - */ - if (!(ap->flags & ATA_FLAG_SKIP_D2H_BSY) && !ata_port_offline(ap)) - ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT); - - return 0; -} - -/** - * ata_std_softreset - reset host port via ATA SRST - * @ap: port to reset - * @classes: resulting classes of attached devices - * - * Reset host port using ATA SRST. - * - * LOCKING: - * Kernel thread context (may sleep) - * - * RETURNS: - * 0 on success, -errno otherwise. - */ -int ata_std_softreset(struct ata_port *ap, unsigned int *classes) -{ - unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS; - unsigned int devmask = 0, err_mask; - u8 err; - - DPRINTK("ENTER\n"); - - if (ata_port_offline(ap)) { - classes[0] = ATA_DEV_NONE; - goto out; - } - - /* determine if device 0/1 are present */ - if (ata_devchk(ap, 0)) - devmask |= (1 << 0); - if (slave_possible && ata_devchk(ap, 1)) - devmask |= (1 << 1); - - /* select device 0 again */ - ap->ops->dev_select(ap, 0); - - /* issue bus reset */ - DPRINTK("about to softreset, devmask=%x\n", devmask); - err_mask = ata_bus_softreset(ap, devmask); - if (err_mask) { - ata_port_printk(ap, KERN_ERR, "SRST failed (err_mask=0x%x)\n", - err_mask); - return -EIO; - } - - /* determine by signature whether we have ATA or ATAPI devices */ - classes[0] = ata_dev_try_classify(ap, 0, &err); - if (slave_possible && err != 0x81) - classes[1] = ata_dev_try_classify(ap, 1, &err); - - out: - DPRINTK("EXIT, classes[0]=%u [1]=%u\n", classes[0], classes[1]); - return 0; -} - -/** - * sata_std_hardreset - reset host port via SATA phy reset - * @ap: port to reset - * @class: resulting class of attached device - * - * SATA phy-reset host port using DET bits of SControl register. - * - * LOCKING: - * Kernel thread context (may sleep) - * - * RETURNS: - * 0 on success, -errno otherwise. - */ -int sata_std_hardreset(struct ata_port *ap, unsigned int *class) -{ - struct ata_eh_context *ehc = &ap->eh_context; - const unsigned long *timing = sata_ehc_deb_timing(ehc); - u32 scontrol; - int rc; - - DPRINTK("ENTER\n"); - - if (sata_set_spd_needed(ap)) { - /* SATA spec says nothing about how to reconfigure - * spd. To be on the safe side, turn off phy during - * reconfiguration. This works for at least ICH7 AHCI - * and Sil3124. - */ - if ((rc = sata_scr_read(ap, SCR_CONTROL, &scontrol))) - return rc; - - scontrol = (scontrol & 0x0f0) | 0x302; - - if ((rc = sata_scr_write(ap, SCR_CONTROL, scontrol))) - return rc; - - sata_set_spd(ap); - } - - /* issue phy wake/reset */ - if ((rc = sata_scr_read(ap, SCR_CONTROL, &scontrol))) - return rc; - - scontrol = (scontrol & 0x0f0) | 0x301; - - if ((rc = sata_scr_write_flush(ap, SCR_CONTROL, scontrol))) - return rc; - - /* Couldn't find anything in SATA I/II specs, but AHCI-1.1 - * 10.4.2 says at least 1 ms. - */ - msleep(1); - - /* bring phy back */ - sata_phy_resume(ap, timing); - - /* TODO: phy layer with polling, timeouts, etc. */ - if (ata_port_offline(ap)) { - *class = ATA_DEV_NONE; - DPRINTK("EXIT, link offline\n"); - return 0; - } - - if (ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT)) { - ata_port_printk(ap, KERN_ERR, - "COMRESET failed (device not ready)\n"); - return -EIO; - } - - ap->ops->dev_select(ap, 0); /* probably unnecessary */ - - *class = ata_dev_try_classify(ap, 0, NULL); - - DPRINTK("EXIT, class=%u\n", *class); - return 0; -} - -/** - * ata_std_postreset - standard postreset callback - * @ap: the target ata_port - * @classes: classes of attached devices - * - * This function is invoked after a successful reset. Note that - * the device might have been reset more than once using - * different reset methods before postreset is invoked. - * - * LOCKING: - * Kernel thread context (may sleep) - */ -void ata_std_postreset(struct ata_port *ap, unsigned int *classes) -{ - u32 serror; - - DPRINTK("ENTER\n"); - - /* print link status */ - sata_print_link_status(ap); - - /* clear SError */ - if (sata_scr_read(ap, SCR_ERROR, &serror) == 0) - sata_scr_write(ap, SCR_ERROR, serror); - - /* re-enable interrupts */ - if (!ap->ops->error_handler) { - /* FIXME: hack. create a hook instead */ - if (ap->ioaddr.ctl_addr) - ata_irq_on(ap); - } - - /* is double-select really necessary? */ - if (classes[0] != ATA_DEV_NONE) - ap->ops->dev_select(ap, 1); - if (classes[1] != ATA_DEV_NONE) - ap->ops->dev_select(ap, 0); - - /* bail out if no device is present */ - if (classes[0] == ATA_DEV_NONE && classes[1] == ATA_DEV_NONE) { - DPRINTK("EXIT, no device\n"); - return; - } - - /* set up device control */ - if (ap->ioaddr.ctl_addr) { - if (ap->flags & ATA_FLAG_MMIO) - writeb(ap->ctl, (void __iomem *) ap->ioaddr.ctl_addr); - else - outb(ap->ctl, ap->ioaddr.ctl_addr); - } - - DPRINTK("EXIT\n"); -} - -/** - * ata_dev_same_device - Determine whether new ID matches configured device - * @dev: device to compare against - * @new_class: class of the new device - * @new_id: IDENTIFY page of the new device - * - * Compare @new_class and @new_id against @dev and determine - * whether @dev is the device indicated by @new_class and - * @new_id. - * - * LOCKING: - * None. - * - * RETURNS: - * 1 if @dev matches @new_class and @new_id, 0 otherwise. - */ -static int ata_dev_same_device(struct ata_device *dev, unsigned int new_class, - const u16 *new_id) -{ - const u16 *old_id = dev->id; - unsigned char model[2][41], serial[2][21]; - u64 new_n_sectors; - - if (dev->class != new_class) { - ata_dev_printk(dev, KERN_INFO, "class mismatch %d != %d\n", - dev->class, new_class); - return 0; - } - - ata_id_c_string(old_id, model[0], ATA_ID_PROD_OFS, sizeof(model[0])); - ata_id_c_string(new_id, model[1], ATA_ID_PROD_OFS, sizeof(model[1])); - ata_id_c_string(old_id, serial[0], ATA_ID_SERNO_OFS, sizeof(serial[0])); - ata_id_c_string(new_id, serial[1], ATA_ID_SERNO_OFS, sizeof(serial[1])); - new_n_sectors = ata_id_n_sectors(new_id); - - if (strcmp(model[0], model[1])) { - ata_dev_printk(dev, KERN_INFO, "model number mismatch " - "'%s' != '%s'\n", model[0], model[1]); - return 0; - } - - if (strcmp(serial[0], serial[1])) { - ata_dev_printk(dev, KERN_INFO, "serial number mismatch " - "'%s' != '%s'\n", serial[0], serial[1]); - return 0; - } - - if (dev->class == ATA_DEV_ATA && dev->n_sectors != new_n_sectors) { - ata_dev_printk(dev, KERN_INFO, "n_sectors mismatch " - "%llu != %llu\n", - (unsigned long long)dev->n_sectors, - (unsigned long long)new_n_sectors); - return 0; - } - - return 1; -} - -/** - * ata_dev_revalidate - Revalidate ATA device - * @dev: device to revalidate - * @post_reset: is this revalidation after reset? - * - * Re-read IDENTIFY page and make sure @dev is still attached to - * the port. - * - * LOCKING: - * Kernel thread context (may sleep) - * - * RETURNS: - * 0 on success, negative errno otherwise - */ -int ata_dev_revalidate(struct ata_device *dev, int post_reset) -{ - unsigned int class = dev->class; - u16 *id = (void *)dev->ap->sector_buf; - int rc; - - if (!ata_dev_enabled(dev)) { - rc = -ENODEV; - goto fail; - } - - /* read ID data */ - rc = ata_dev_read_id(dev, &class, post_reset, id); - if (rc) - goto fail; - - /* is the device still there? */ - if (!ata_dev_same_device(dev, class, id)) { - rc = -ENODEV; - goto fail; - } - - memcpy(dev->id, id, sizeof(id[0]) * ATA_ID_WORDS); - - /* configure device according to the new ID */ - rc = ata_dev_configure(dev, 0); - if (rc == 0) - return 0; - - fail: - ata_dev_printk(dev, KERN_ERR, "revalidation failed (errno=%d)\n", rc); - return rc; -} - -static const char * const ata_dma_blacklist [] = { - "WDC AC11000H", NULL, - "WDC AC22100H", NULL, - "WDC AC32500H", NULL, - "WDC AC33100H", NULL, - "WDC AC31600H", NULL, - "WDC AC32100H", "24.09P07", - "WDC AC23200L", "21.10N21", - "Compaq CRD-8241B", NULL, - "CRD-8400B", NULL, - "CRD-8480B", NULL, - "CRD-8482B", NULL, - "CRD-84", NULL, - "SanDisk SDP3B", NULL, - "SanDisk SDP3B-64", NULL, - "SANYO CD-ROM CRD", NULL, - "HITACHI CDR-8", NULL, - "HITACHI CDR-8335", NULL, - "HITACHI CDR-8435", NULL, - "Toshiba CD-ROM XM-6202B", NULL, - "TOSHIBA CD-ROM XM-1702BC", NULL, - "CD-532E-A", NULL, - "E-IDE CD-ROM CR-840", NULL, - "CD-ROM Drive/F5A", NULL, - "WPI CDD-820", NULL, - "SAMSUNG CD-ROM SC-148C", NULL, - "SAMSUNG CD-ROM SC", NULL, - "SanDisk SDP3B-64", NULL, - "ATAPI CD-ROM DRIVE 40X MAXIMUM",NULL, - "_NEC DV5800A", NULL, - "SAMSUNG CD-ROM SN-124", "N001" -}; - -static int ata_strim(char *s, size_t len) -{ - len = strnlen(s, len); - - /* ATAPI specifies that empty space is blank-filled; remove blanks */ - while ((len > 0) && (s[len - 1] == ' ')) { - len--; - s[len] = 0; - } - return len; -} - -static int ata_dma_blacklisted(const struct ata_device *dev) -{ - unsigned char model_num[40]; - unsigned char model_rev[16]; - unsigned int nlen, rlen; - int i; - - /* We don't support polling DMA. - * DMA blacklist those ATAPI devices with CDB-intr (and use PIO) - * if the LLDD handles only interrupts in the HSM_ST_LAST state. - */ - if ((dev->ap->flags & ATA_FLAG_PIO_POLLING) && - (dev->flags & ATA_DFLAG_CDB_INTR)) - return 1; - - ata_id_string(dev->id, model_num, ATA_ID_PROD_OFS, - sizeof(model_num)); - ata_id_string(dev->id, model_rev, ATA_ID_FW_REV_OFS, - sizeof(model_rev)); - nlen = ata_strim(model_num, sizeof(model_num)); - rlen = ata_strim(model_rev, sizeof(model_rev)); - - for (i = 0; i < ARRAY_SIZE(ata_dma_blacklist); i += 2) { - if (!strncmp(ata_dma_blacklist[i], model_num, nlen)) { - if (ata_dma_blacklist[i+1] == NULL) - return 1; - if (!strncmp(ata_dma_blacklist[i], model_rev, rlen)) - return 1; - } - } - return 0; -} - -/** - * ata_dev_xfermask - Compute supported xfermask of the given device - * @dev: Device to compute xfermask for - * - * Compute supported xfermask of @dev and store it in - * dev->*_mask. This function is responsible for applying all - * known limits including host controller limits, device - * blacklist, etc... - * - * LOCKING: - * None. - */ -static void ata_dev_xfermask(struct ata_device *dev) -{ - struct ata_port *ap = dev->ap; - struct ata_host_set *hs = ap->host_set; - unsigned long xfer_mask; - - /* controller modes available */ - xfer_mask = ata_pack_xfermask(ap->pio_mask, - ap->mwdma_mask, ap->udma_mask); - - /* Apply cable rule here. Don't apply it early because when - * we handle hot plug the cable type can itself change. - */ - if (ap->cbl == ATA_CBL_PATA40) - xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA); - - xfer_mask &= ata_pack_xfermask(dev->pio_mask, - dev->mwdma_mask, dev->udma_mask); - xfer_mask &= ata_id_xfermask(dev->id); - - if (ata_dma_blacklisted(dev)) { - xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); - ata_dev_printk(dev, KERN_WARNING, - "device is on DMA blacklist, disabling DMA\n"); - } - - if ((hs->flags & ATA_HOST_SIMPLEX) && hs->simplex_claimed) { - xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); - ata_dev_printk(dev, KERN_WARNING, "simplex DMA is claimed by " - "other device, disabling DMA\n"); - } - - if (ap->ops->mode_filter) - xfer_mask = ap->ops->mode_filter(ap, dev, xfer_mask); - - ata_unpack_xfermask(xfer_mask, &dev->pio_mask, - &dev->mwdma_mask, &dev->udma_mask); -} - -/** - * ata_dev_set_xfermode - Issue SET FEATURES - XFER MODE command - * @dev: Device to which command will be sent - * - * Issue SET FEATURES - XFER MODE command to device @dev - * on port @ap. - * - * LOCKING: - * PCI/etc. bus probe sem. - * - * RETURNS: - * 0 on success, AC_ERR_* mask otherwise. - */ - -static unsigned int ata_dev_set_xfermode(struct ata_device *dev) -{ - struct ata_taskfile tf; - unsigned int err_mask; - - /* set up set-features taskfile */ - DPRINTK("set features - xfer mode\n"); - - ata_tf_init(dev, &tf); - tf.command = ATA_CMD_SET_FEATURES; - tf.feature = SETFEATURES_XFER; - tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; - tf.protocol = ATA_PROT_NODATA; - tf.nsect = dev->xfer_mode; - - err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0); - - DPRINTK("EXIT, err_mask=%x\n", err_mask); - return err_mask; -} - -/** - * ata_dev_init_params - Issue INIT DEV PARAMS command - * @dev: Device to which command will be sent - * @heads: Number of heads (taskfile parameter) - * @sectors: Number of sectors (taskfile parameter) - * - * LOCKING: - * Kernel thread context (may sleep) - * - * RETURNS: - * 0 on success, AC_ERR_* mask otherwise. - */ -static unsigned int ata_dev_init_params(struct ata_device *dev, - u16 heads, u16 sectors) -{ - struct ata_taskfile tf; - unsigned int err_mask; - - /* Number of sectors per track 1-255. Number of heads 1-16 */ - if (sectors < 1 || sectors > 255 || heads < 1 || heads > 16) - return AC_ERR_INVALID; - - /* set up init dev params taskfile */ - DPRINTK("init dev params \n"); - - ata_tf_init(dev, &tf); - tf.command = ATA_CMD_INIT_DEV_PARAMS; - tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; - tf.protocol = ATA_PROT_NODATA; - tf.nsect = sectors; - tf.device |= (heads - 1) & 0x0f; /* max head = num. of heads - 1 */ - - err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0); - - DPRINTK("EXIT, err_mask=%x\n", err_mask); - return err_mask; -} - -/** - * ata_sg_clean - Unmap DMA memory associated with command - * @qc: Command containing DMA memory to be released - * - * Unmap all mapped DMA memory associated with this command. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -static void ata_sg_clean(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct scatterlist *sg = qc->__sg; - int dir = qc->dma_dir; - void *pad_buf = NULL; - - WARN_ON(!(qc->flags & ATA_QCFLAG_DMAMAP)); - WARN_ON(sg == NULL); - - if (qc->flags & ATA_QCFLAG_SINGLE) - WARN_ON(qc->n_elem > 1); - - VPRINTK("unmapping %u sg elements\n", qc->n_elem); - - /* if we padded the buffer out to 32-bit bound, and data - * xfer direction is from-device, we must copy from the - * pad buffer back into the supplied buffer - */ - if (qc->pad_len && !(qc->tf.flags & ATA_TFLAG_WRITE)) - pad_buf = ap->pad + (qc->tag * ATA_DMA_PAD_SZ); - - if (qc->flags & ATA_QCFLAG_SG) { - if (qc->n_elem) - dma_unmap_sg(ap->dev, sg, qc->n_elem, dir); - /* restore last sg */ - sg[qc->orig_n_elem - 1].length += qc->pad_len; - if (pad_buf) { - struct scatterlist *psg = &qc->pad_sgent; - void *addr = kmap_atomic(psg->page, KM_IRQ0); - memcpy(addr + psg->offset, pad_buf, qc->pad_len); - kunmap_atomic(addr, KM_IRQ0); - } - } else { - if (qc->n_elem) - dma_unmap_single(ap->dev, - sg_dma_address(&sg[0]), sg_dma_len(&sg[0]), - dir); - /* restore sg */ - sg->length += qc->pad_len; - if (pad_buf) - memcpy(qc->buf_virt + sg->length - qc->pad_len, - pad_buf, qc->pad_len); - } - - qc->flags &= ~ATA_QCFLAG_DMAMAP; - qc->__sg = NULL; -} - -/** - * ata_fill_sg - Fill PCI IDE PRD table - * @qc: Metadata associated with taskfile to be transferred - * - * Fill PCI IDE PRD (scatter-gather) table with segments - * associated with the current disk command. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - */ -static void ata_fill_sg(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct scatterlist *sg; - unsigned int idx; - - WARN_ON(qc->__sg == NULL); - WARN_ON(qc->n_elem == 0 && qc->pad_len == 0); - - idx = 0; - ata_for_each_sg(sg, qc) { - u32 addr, offset; - u32 sg_len, len; - - /* determine if physical DMA addr spans 64K boundary. - * Note h/w doesn't support 64-bit, so we unconditionally - * truncate dma_addr_t to u32. - */ - addr = (u32) sg_dma_address(sg); - sg_len = sg_dma_len(sg); - - while (sg_len) { - offset = addr & 0xffff; - len = sg_len; - if ((offset + sg_len) > 0x10000) - len = 0x10000 - offset; - - ap->prd[idx].addr = cpu_to_le32(addr); - ap->prd[idx].flags_len = cpu_to_le32(len & 0xffff); - VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len); - - idx++; - sg_len -= len; - addr += len; - } - } - - if (idx) - ap->prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT); -} -/** - * ata_check_atapi_dma - Check whether ATAPI DMA can be supported - * @qc: Metadata associated with taskfile to check - * - * Allow low-level driver to filter ATA PACKET commands, returning - * a status indicating whether or not it is OK to use DMA for the - * supplied PACKET command. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: 0 when ATAPI DMA can be used - * nonzero otherwise - */ -int ata_check_atapi_dma(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - int rc = 0; /* Assume ATAPI DMA is OK by default */ - - if (ap->ops->check_atapi_dma) - rc = ap->ops->check_atapi_dma(qc); - - return rc; -} -/** - * ata_qc_prep - Prepare taskfile for submission - * @qc: Metadata associated with taskfile to be prepared - * - * Prepare ATA taskfile for submission. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ -void ata_qc_prep(struct ata_queued_cmd *qc) -{ - if (!(qc->flags & ATA_QCFLAG_DMAMAP)) - return; - - ata_fill_sg(qc); -} - -void ata_noop_qc_prep(struct ata_queued_cmd *qc) { } - -/** - * ata_sg_init_one - Associate command with memory buffer - * @qc: Command to be associated - * @buf: Memory buffer - * @buflen: Length of memory buffer, in bytes. - * - * Initialize the data-related elements of queued_cmd @qc - * to point to a single memory buffer, @buf of byte length @buflen. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -void ata_sg_init_one(struct ata_queued_cmd *qc, void *buf, unsigned int buflen) -{ - struct scatterlist *sg; - - qc->flags |= ATA_QCFLAG_SINGLE; - - memset(&qc->sgent, 0, sizeof(qc->sgent)); - qc->__sg = &qc->sgent; - qc->n_elem = 1; - qc->orig_n_elem = 1; - qc->buf_virt = buf; - qc->nbytes = buflen; - - sg = qc->__sg; - sg_init_one(sg, buf, buflen); -} - -/** - * ata_sg_init - Associate command with scatter-gather table. - * @qc: Command to be associated - * @sg: Scatter-gather table. - * @n_elem: Number of elements in s/g table. - * - * Initialize the data-related elements of queued_cmd @qc - * to point to a scatter-gather table @sg, containing @n_elem - * elements. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -void ata_sg_init(struct ata_queued_cmd *qc, struct scatterlist *sg, - unsigned int n_elem) -{ - qc->flags |= ATA_QCFLAG_SG; - qc->__sg = sg; - qc->n_elem = n_elem; - qc->orig_n_elem = n_elem; -} - -/** - * ata_sg_setup_one - DMA-map the memory buffer associated with a command. - * @qc: Command with memory buffer to be mapped. - * - * DMA-map the memory buffer associated with queued_cmd @qc. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * Zero on success, negative on error. - */ - -static int ata_sg_setup_one(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - int dir = qc->dma_dir; - struct scatterlist *sg = qc->__sg; - dma_addr_t dma_address; - int trim_sg = 0; - - /* we must lengthen transfers to end on a 32-bit boundary */ - qc->pad_len = sg->length & 3; - if (qc->pad_len) { - void *pad_buf = ap->pad + (qc->tag * ATA_DMA_PAD_SZ); - struct scatterlist *psg = &qc->pad_sgent; - - WARN_ON(qc->dev->class != ATA_DEV_ATAPI); - - memset(pad_buf, 0, ATA_DMA_PAD_SZ); - - if (qc->tf.flags & ATA_TFLAG_WRITE) - memcpy(pad_buf, qc->buf_virt + sg->length - qc->pad_len, - qc->pad_len); - - sg_dma_address(psg) = ap->pad_dma + (qc->tag * ATA_DMA_PAD_SZ); - sg_dma_len(psg) = ATA_DMA_PAD_SZ; - /* trim sg */ - sg->length -= qc->pad_len; - if (sg->length == 0) - trim_sg = 1; - - DPRINTK("padding done, sg->length=%u pad_len=%u\n", - sg->length, qc->pad_len); - } - - if (trim_sg) { - qc->n_elem--; - goto skip_map; - } - - dma_address = dma_map_single(ap->dev, qc->buf_virt, - sg->length, dir); - if (dma_mapping_error(dma_address)) { - /* restore sg */ - sg->length += qc->pad_len; - return -1; - } - - sg_dma_address(sg) = dma_address; - sg_dma_len(sg) = sg->length; - -skip_map: - DPRINTK("mapped buffer of %d bytes for %s\n", sg_dma_len(sg), - qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read"); - - return 0; -} - -/** - * ata_sg_setup - DMA-map the scatter-gather table associated with a command. - * @qc: Command with scatter-gather table to be mapped. - * - * DMA-map the scatter-gather table associated with queued_cmd @qc. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * Zero on success, negative on error. - * - */ - -static int ata_sg_setup(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct scatterlist *sg = qc->__sg; - struct scatterlist *lsg = &sg[qc->n_elem - 1]; - int n_elem, pre_n_elem, dir, trim_sg = 0; - - VPRINTK("ENTER, ata%u\n", ap->id); - WARN_ON(!(qc->flags & ATA_QCFLAG_SG)); - - /* we must lengthen transfers to end on a 32-bit boundary */ - qc->pad_len = lsg->length & 3; - if (qc->pad_len) { - void *pad_buf = ap->pad + (qc->tag * ATA_DMA_PAD_SZ); - struct scatterlist *psg = &qc->pad_sgent; - unsigned int offset; - - WARN_ON(qc->dev->class != ATA_DEV_ATAPI); - - memset(pad_buf, 0, ATA_DMA_PAD_SZ); - - /* - * psg->page/offset are used to copy to-be-written - * data in this function or read data in ata_sg_clean. - */ - offset = lsg->offset + lsg->length - qc->pad_len; - psg->page = nth_page(lsg->page, offset >> PAGE_SHIFT); - psg->offset = offset_in_page(offset); - - if (qc->tf.flags & ATA_TFLAG_WRITE) { - void *addr = kmap_atomic(psg->page, KM_IRQ0); - memcpy(pad_buf, addr + psg->offset, qc->pad_len); - kunmap_atomic(addr, KM_IRQ0); - } - - sg_dma_address(psg) = ap->pad_dma + (qc->tag * ATA_DMA_PAD_SZ); - sg_dma_len(psg) = ATA_DMA_PAD_SZ; - /* trim last sg */ - lsg->length -= qc->pad_len; - if (lsg->length == 0) - trim_sg = 1; - - DPRINTK("padding done, sg[%d].length=%u pad_len=%u\n", - qc->n_elem - 1, lsg->length, qc->pad_len); - } - - pre_n_elem = qc->n_elem; - if (trim_sg && pre_n_elem) - pre_n_elem--; - - if (!pre_n_elem) { - n_elem = 0; - goto skip_map; - } - - dir = qc->dma_dir; - n_elem = dma_map_sg(ap->dev, sg, pre_n_elem, dir); - if (n_elem < 1) { - /* restore last sg */ - lsg->length += qc->pad_len; - return -1; - } - - DPRINTK("%d sg elements mapped\n", n_elem); - -skip_map: - qc->n_elem = n_elem; - - return 0; -} - -/** - * swap_buf_le16 - swap halves of 16-bit words in place - * @buf: Buffer to swap - * @buf_words: Number of 16-bit words in buffer. - * - * Swap halves of 16-bit words if needed to convert from - * little-endian byte order to native cpu byte order, or - * vice-versa. - * - * LOCKING: - * Inherited from caller. - */ -void swap_buf_le16(u16 *buf, unsigned int buf_words) -{ -#ifdef __BIG_ENDIAN - unsigned int i; - - for (i = 0; i < buf_words; i++) - buf[i] = le16_to_cpu(buf[i]); -#endif /* __BIG_ENDIAN */ -} - -/** - * ata_mmio_data_xfer - Transfer data by MMIO - * @adev: device for this I/O - * @buf: data buffer - * @buflen: buffer length - * @write_data: read/write - * - * Transfer data from/to the device data register by MMIO. - * - * LOCKING: - * Inherited from caller. - */ - -void ata_mmio_data_xfer(struct ata_device *adev, unsigned char *buf, - unsigned int buflen, int write_data) -{ - struct ata_port *ap = adev->ap; - unsigned int i; - unsigned int words = buflen >> 1; - u16 *buf16 = (u16 *) buf; - void __iomem *mmio = (void __iomem *)ap->ioaddr.data_addr; - - /* Transfer multiple of 2 bytes */ - if (write_data) { - for (i = 0; i < words; i++) - writew(le16_to_cpu(buf16[i]), mmio); - } else { - for (i = 0; i < words; i++) - buf16[i] = cpu_to_le16(readw(mmio)); - } - - /* Transfer trailing 1 byte, if any. */ - if (unlikely(buflen & 0x01)) { - u16 align_buf[1] = { 0 }; - unsigned char *trailing_buf = buf + buflen - 1; - - if (write_data) { - memcpy(align_buf, trailing_buf, 1); - writew(le16_to_cpu(align_buf[0]), mmio); - } else { - align_buf[0] = cpu_to_le16(readw(mmio)); - memcpy(trailing_buf, align_buf, 1); - } - } -} - -/** - * ata_pio_data_xfer - Transfer data by PIO - * @adev: device to target - * @buf: data buffer - * @buflen: buffer length - * @write_data: read/write - * - * Transfer data from/to the device data register by PIO. - * - * LOCKING: - * Inherited from caller. - */ - -void ata_pio_data_xfer(struct ata_device *adev, unsigned char *buf, - unsigned int buflen, int write_data) -{ - struct ata_port *ap = adev->ap; - unsigned int words = buflen >> 1; - - /* Transfer multiple of 2 bytes */ - if (write_data) - outsw(ap->ioaddr.data_addr, buf, words); - else - insw(ap->ioaddr.data_addr, buf, words); - - /* Transfer trailing 1 byte, if any. */ - if (unlikely(buflen & 0x01)) { - u16 align_buf[1] = { 0 }; - unsigned char *trailing_buf = buf + buflen - 1; - - if (write_data) { - memcpy(align_buf, trailing_buf, 1); - outw(le16_to_cpu(align_buf[0]), ap->ioaddr.data_addr); - } else { - align_buf[0] = cpu_to_le16(inw(ap->ioaddr.data_addr)); - memcpy(trailing_buf, align_buf, 1); - } - } -} - -/** - * ata_pio_data_xfer_noirq - Transfer data by PIO - * @adev: device to target - * @buf: data buffer - * @buflen: buffer length - * @write_data: read/write - * - * Transfer data from/to the device data register by PIO. Do the - * transfer with interrupts disabled. - * - * LOCKING: - * Inherited from caller. - */ - -void ata_pio_data_xfer_noirq(struct ata_device *adev, unsigned char *buf, - unsigned int buflen, int write_data) -{ - unsigned long flags; - local_irq_save(flags); - ata_pio_data_xfer(adev, buf, buflen, write_data); - local_irq_restore(flags); -} - - -/** - * ata_pio_sector - Transfer ATA_SECT_SIZE (512 bytes) of data. - * @qc: Command on going - * - * Transfer ATA_SECT_SIZE of data from/to the ATA device. - * - * LOCKING: - * Inherited from caller. - */ - -static void ata_pio_sector(struct ata_queued_cmd *qc) -{ - int do_write = (qc->tf.flags & ATA_TFLAG_WRITE); - struct scatterlist *sg = qc->__sg; - struct ata_port *ap = qc->ap; - struct page *page; - unsigned int offset; - unsigned char *buf; - - if (qc->cursect == (qc->nsect - 1)) - ap->hsm_task_state = HSM_ST_LAST; - - page = sg[qc->cursg].page; - offset = sg[qc->cursg].offset + qc->cursg_ofs * ATA_SECT_SIZE; - - /* get the current page and offset */ - page = nth_page(page, (offset >> PAGE_SHIFT)); - offset %= PAGE_SIZE; - - DPRINTK("data %s\n", qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read"); - - if (PageHighMem(page)) { - unsigned long flags; - - /* FIXME: use a bounce buffer */ - local_irq_save(flags); - buf = kmap_atomic(page, KM_IRQ0); - - /* do the actual data transfer */ - ap->ops->data_xfer(qc->dev, buf + offset, ATA_SECT_SIZE, do_write); - - kunmap_atomic(buf, KM_IRQ0); - local_irq_restore(flags); - } else { - buf = page_address(page); - ap->ops->data_xfer(qc->dev, buf + offset, ATA_SECT_SIZE, do_write); - } - - qc->cursect++; - qc->cursg_ofs++; - - if ((qc->cursg_ofs * ATA_SECT_SIZE) == (&sg[qc->cursg])->length) { - qc->cursg++; - qc->cursg_ofs = 0; - } -} - -/** - * ata_pio_sectors - Transfer one or many 512-byte sectors. - * @qc: Command on going - * - * Transfer one or many ATA_SECT_SIZE of data from/to the - * ATA device for the DRQ request. - * - * LOCKING: - * Inherited from caller. - */ - -static void ata_pio_sectors(struct ata_queued_cmd *qc) -{ - if (is_multi_taskfile(&qc->tf)) { - /* READ/WRITE MULTIPLE */ - unsigned int nsect; - - WARN_ON(qc->dev->multi_count == 0); - - nsect = min(qc->nsect - qc->cursect, qc->dev->multi_count); - while (nsect--) - ata_pio_sector(qc); - } else - ata_pio_sector(qc); -} - -/** - * atapi_send_cdb - Write CDB bytes to hardware - * @ap: Port to which ATAPI device is attached. - * @qc: Taskfile currently active - * - * When device has indicated its readiness to accept - * a CDB, this function is called. Send the CDB. - * - * LOCKING: - * caller. - */ - -static void atapi_send_cdb(struct ata_port *ap, struct ata_queued_cmd *qc) -{ - /* send SCSI cdb */ - DPRINTK("send cdb\n"); - WARN_ON(qc->dev->cdb_len < 12); - - ap->ops->data_xfer(qc->dev, qc->cdb, qc->dev->cdb_len, 1); - ata_altstatus(ap); /* flush */ - - switch (qc->tf.protocol) { - case ATA_PROT_ATAPI: - ap->hsm_task_state = HSM_ST; - break; - case ATA_PROT_ATAPI_NODATA: - ap->hsm_task_state = HSM_ST_LAST; - break; - case ATA_PROT_ATAPI_DMA: - ap->hsm_task_state = HSM_ST_LAST; - /* initiate bmdma */ - ap->ops->bmdma_start(qc); - break; - } -} - -/** - * __atapi_pio_bytes - Transfer data from/to the ATAPI device. - * @qc: Command on going - * @bytes: number of bytes - * - * Transfer Transfer data from/to the ATAPI device. - * - * LOCKING: - * Inherited from caller. - * - */ - -static void __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes) -{ - int do_write = (qc->tf.flags & ATA_TFLAG_WRITE); - struct scatterlist *sg = qc->__sg; - struct ata_port *ap = qc->ap; - struct page *page; - unsigned char *buf; - unsigned int offset, count; - - if (qc->curbytes + bytes >= qc->nbytes) - ap->hsm_task_state = HSM_ST_LAST; - -next_sg: - if (unlikely(qc->cursg >= qc->n_elem)) { - /* - * The end of qc->sg is reached and the device expects - * more data to transfer. In order not to overrun qc->sg - * and fulfill length specified in the byte count register, - * - for read case, discard trailing data from the device - * - for write case, padding zero data to the device - */ - u16 pad_buf[1] = { 0 }; - unsigned int words = bytes >> 1; - unsigned int i; - - if (words) /* warning if bytes > 1 */ - ata_dev_printk(qc->dev, KERN_WARNING, - "%u bytes trailing data\n", bytes); - - for (i = 0; i < words; i++) - ap->ops->data_xfer(qc->dev, (unsigned char*)pad_buf, 2, do_write); - - ap->hsm_task_state = HSM_ST_LAST; - return; - } - - sg = &qc->__sg[qc->cursg]; - - page = sg->page; - offset = sg->offset + qc->cursg_ofs; - - /* get the current page and offset */ - page = nth_page(page, (offset >> PAGE_SHIFT)); - offset %= PAGE_SIZE; - - /* don't overrun current sg */ - count = min(sg->length - qc->cursg_ofs, bytes); - - /* don't cross page boundaries */ - count = min(count, (unsigned int)PAGE_SIZE - offset); - - DPRINTK("data %s\n", qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read"); - - if (PageHighMem(page)) { - unsigned long flags; - - /* FIXME: use bounce buffer */ - local_irq_save(flags); - buf = kmap_atomic(page, KM_IRQ0); - - /* do the actual data transfer */ - ap->ops->data_xfer(qc->dev, buf + offset, count, do_write); - - kunmap_atomic(buf, KM_IRQ0); - local_irq_restore(flags); - } else { - buf = page_address(page); - ap->ops->data_xfer(qc->dev, buf + offset, count, do_write); - } - - bytes -= count; - qc->curbytes += count; - qc->cursg_ofs += count; - - if (qc->cursg_ofs == sg->length) { - qc->cursg++; - qc->cursg_ofs = 0; - } - - if (bytes) - goto next_sg; -} - -/** - * atapi_pio_bytes - Transfer data from/to the ATAPI device. - * @qc: Command on going - * - * Transfer Transfer data from/to the ATAPI device. - * - * LOCKING: - * Inherited from caller. - */ - -static void atapi_pio_bytes(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct ata_device *dev = qc->dev; - unsigned int ireason, bc_lo, bc_hi, bytes; - int i_write, do_write = (qc->tf.flags & ATA_TFLAG_WRITE) ? 1 : 0; - - /* Abuse qc->result_tf for temp storage of intermediate TF - * here to save some kernel stack usage. - * For normal completion, qc->result_tf is not relevant. For - * error, qc->result_tf is later overwritten by ata_qc_complete(). - * So, the correctness of qc->result_tf is not affected. - */ - ap->ops->tf_read(ap, &qc->result_tf); - ireason = qc->result_tf.nsect; - bc_lo = qc->result_tf.lbam; - bc_hi = qc->result_tf.lbah; - bytes = (bc_hi << 8) | bc_lo; - - /* shall be cleared to zero, indicating xfer of data */ - if (ireason & (1 << 0)) - goto err_out; - - /* make sure transfer direction matches expected */ - i_write = ((ireason & (1 << 1)) == 0) ? 1 : 0; - if (do_write != i_write) - goto err_out; - - VPRINTK("ata%u: xfering %d bytes\n", ap->id, bytes); - - __atapi_pio_bytes(qc, bytes); - - return; - -err_out: - ata_dev_printk(dev, KERN_INFO, "ATAPI check failed\n"); - qc->err_mask |= AC_ERR_HSM; - ap->hsm_task_state = HSM_ST_ERR; -} - -/** - * ata_hsm_ok_in_wq - Check if the qc can be handled in the workqueue. - * @ap: the target ata_port - * @qc: qc on going - * - * RETURNS: - * 1 if ok in workqueue, 0 otherwise. - */ - -static inline int ata_hsm_ok_in_wq(struct ata_port *ap, struct ata_queued_cmd *qc) -{ - if (qc->tf.flags & ATA_TFLAG_POLLING) - return 1; - - if (ap->hsm_task_state == HSM_ST_FIRST) { - if (qc->tf.protocol == ATA_PROT_PIO && - (qc->tf.flags & ATA_TFLAG_WRITE)) - return 1; - - if (is_atapi_taskfile(&qc->tf) && - !(qc->dev->flags & ATA_DFLAG_CDB_INTR)) - return 1; - } - - return 0; -} - -/** - * ata_hsm_qc_complete - finish a qc running on standard HSM - * @qc: Command to complete - * @in_wq: 1 if called from workqueue, 0 otherwise - * - * Finish @qc which is running on standard HSM. - * - * LOCKING: - * If @in_wq is zero, spin_lock_irqsave(host_set lock). - * Otherwise, none on entry and grabs host lock. - */ -static void ata_hsm_qc_complete(struct ata_queued_cmd *qc, int in_wq) -{ - struct ata_port *ap = qc->ap; - unsigned long flags; - - if (ap->ops->error_handler) { - if (in_wq) { - spin_lock_irqsave(ap->lock, flags); - - /* EH might have kicked in while host_set lock - * is released. - */ - qc = ata_qc_from_tag(ap, qc->tag); - if (qc) { - if (likely(!(qc->err_mask & AC_ERR_HSM))) { - ata_irq_on(ap); - ata_qc_complete(qc); - } else - ata_port_freeze(ap); - } - - spin_unlock_irqrestore(ap->lock, flags); - } else { - if (likely(!(qc->err_mask & AC_ERR_HSM))) - ata_qc_complete(qc); - else - ata_port_freeze(ap); - } - } else { - if (in_wq) { - spin_lock_irqsave(ap->lock, flags); - ata_irq_on(ap); - ata_qc_complete(qc); - spin_unlock_irqrestore(ap->lock, flags); - } else - ata_qc_complete(qc); - } - - ata_altstatus(ap); /* flush */ -} - -/** - * ata_hsm_move - move the HSM to the next state. - * @ap: the target ata_port - * @qc: qc on going - * @status: current device status - * @in_wq: 1 if called from workqueue, 0 otherwise - * - * RETURNS: - * 1 when poll next status needed, 0 otherwise. - */ -int ata_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc, - u8 status, int in_wq) -{ - unsigned long flags = 0; - int poll_next; - - WARN_ON((qc->flags & ATA_QCFLAG_ACTIVE) == 0); - - /* Make sure ata_qc_issue_prot() does not throw things - * like DMA polling into the workqueue. Notice that - * in_wq is not equivalent to (qc->tf.flags & ATA_TFLAG_POLLING). - */ - WARN_ON(in_wq != ata_hsm_ok_in_wq(ap, qc)); - -fsm_start: - DPRINTK("ata%u: protocol %d task_state %d (dev_stat 0x%X)\n", - ap->id, qc->tf.protocol, ap->hsm_task_state, status); - - switch (ap->hsm_task_state) { - case HSM_ST_FIRST: - /* Send first data block or PACKET CDB */ - - /* If polling, we will stay in the work queue after - * sending the data. Otherwise, interrupt handler - * takes over after sending the data. - */ - poll_next = (qc->tf.flags & ATA_TFLAG_POLLING); - - /* check device status */ - if (unlikely((status & ATA_DRQ) == 0)) { - /* handle BSY=0, DRQ=0 as error */ - if (likely(status & (ATA_ERR | ATA_DF))) - /* device stops HSM for abort/error */ - qc->err_mask |= AC_ERR_DEV; - else - /* HSM violation. Let EH handle this */ - qc->err_mask |= AC_ERR_HSM; - - ap->hsm_task_state = HSM_ST_ERR; - goto fsm_start; - } - - /* Device should not ask for data transfer (DRQ=1) - * when it finds something wrong. - * We ignore DRQ here and stop the HSM by - * changing hsm_task_state to HSM_ST_ERR and - * let the EH abort the command or reset the device. - */ - if (unlikely(status & (ATA_ERR | ATA_DF))) { - printk(KERN_WARNING "ata%d: DRQ=1 with device error, dev_stat 0x%X\n", - ap->id, status); - qc->err_mask |= AC_ERR_HSM; - ap->hsm_task_state = HSM_ST_ERR; - goto fsm_start; - } - - /* Send the CDB (atapi) or the first data block (ata pio out). - * During the state transition, interrupt handler shouldn't - * be invoked before the data transfer is complete and - * hsm_task_state is changed. Hence, the following locking. - */ - if (in_wq) - spin_lock_irqsave(ap->lock, flags); - - if (qc->tf.protocol == ATA_PROT_PIO) { - /* PIO data out protocol. - * send first data block. - */ - - /* ata_pio_sectors() might change the state - * to HSM_ST_LAST. so, the state is changed here - * before ata_pio_sectors(). - */ - ap->hsm_task_state = HSM_ST; - ata_pio_sectors(qc); - ata_altstatus(ap); /* flush */ - } else - /* send CDB */ - atapi_send_cdb(ap, qc); - - if (in_wq) - spin_unlock_irqrestore(ap->lock, flags); - - /* if polling, ata_pio_task() handles the rest. - * otherwise, interrupt handler takes over from here. - */ - break; - - case HSM_ST: - /* complete command or read/write the data register */ - if (qc->tf.protocol == ATA_PROT_ATAPI) { - /* ATAPI PIO protocol */ - if ((status & ATA_DRQ) == 0) { - /* No more data to transfer or device error. - * Device error will be tagged in HSM_ST_LAST. - */ - ap->hsm_task_state = HSM_ST_LAST; - goto fsm_start; - } - - /* Device should not ask for data transfer (DRQ=1) - * when it finds something wrong. - * We ignore DRQ here and stop the HSM by - * changing hsm_task_state to HSM_ST_ERR and - * let the EH abort the command or reset the device. - */ - if (unlikely(status & (ATA_ERR | ATA_DF))) { - printk(KERN_WARNING "ata%d: DRQ=1 with device error, dev_stat 0x%X\n", - ap->id, status); - qc->err_mask |= AC_ERR_HSM; - ap->hsm_task_state = HSM_ST_ERR; - goto fsm_start; - } - - atapi_pio_bytes(qc); - - if (unlikely(ap->hsm_task_state == HSM_ST_ERR)) - /* bad ireason reported by device */ - goto fsm_start; - - } else { - /* ATA PIO protocol */ - if (unlikely((status & ATA_DRQ) == 0)) { - /* handle BSY=0, DRQ=0 as error */ - if (likely(status & (ATA_ERR | ATA_DF))) - /* device stops HSM for abort/error */ - qc->err_mask |= AC_ERR_DEV; - else - /* HSM violation. Let EH handle this */ - qc->err_mask |= AC_ERR_HSM; - - ap->hsm_task_state = HSM_ST_ERR; - goto fsm_start; - } - - /* For PIO reads, some devices may ask for - * data transfer (DRQ=1) alone with ERR=1. - * We respect DRQ here and transfer one - * block of junk data before changing the - * hsm_task_state to HSM_ST_ERR. - * - * For PIO writes, ERR=1 DRQ=1 doesn't make - * sense since the data block has been - * transferred to the device. - */ - if (unlikely(status & (ATA_ERR | ATA_DF))) { - /* data might be corrputed */ - qc->err_mask |= AC_ERR_DEV; - - if (!(qc->tf.flags & ATA_TFLAG_WRITE)) { - ata_pio_sectors(qc); - ata_altstatus(ap); - status = ata_wait_idle(ap); - } - - if (status & (ATA_BUSY | ATA_DRQ)) - qc->err_mask |= AC_ERR_HSM; - - /* ata_pio_sectors() might change the - * state to HSM_ST_LAST. so, the state - * is changed after ata_pio_sectors(). - */ - ap->hsm_task_state = HSM_ST_ERR; - goto fsm_start; - } - - ata_pio_sectors(qc); - - if (ap->hsm_task_state == HSM_ST_LAST && - (!(qc->tf.flags & ATA_TFLAG_WRITE))) { - /* all data read */ - ata_altstatus(ap); - status = ata_wait_idle(ap); - goto fsm_start; - } - } - - ata_altstatus(ap); /* flush */ - poll_next = 1; - break; - - case HSM_ST_LAST: - if (unlikely(!ata_ok(status))) { - qc->err_mask |= __ac_err_mask(status); - ap->hsm_task_state = HSM_ST_ERR; - goto fsm_start; - } - - /* no more data to transfer */ - DPRINTK("ata%u: dev %u command complete, drv_stat 0x%x\n", - ap->id, qc->dev->devno, status); - - WARN_ON(qc->err_mask); - - ap->hsm_task_state = HSM_ST_IDLE; - - /* complete taskfile transaction */ - ata_hsm_qc_complete(qc, in_wq); - - poll_next = 0; - break; - - case HSM_ST_ERR: - /* make sure qc->err_mask is available to - * know what's wrong and recover - */ - WARN_ON(qc->err_mask == 0); - - ap->hsm_task_state = HSM_ST_IDLE; - - /* complete taskfile transaction */ - ata_hsm_qc_complete(qc, in_wq); - - poll_next = 0; - break; - default: - poll_next = 0; - BUG(); - } - - return poll_next; -} - -static void ata_pio_task(void *_data) -{ - struct ata_queued_cmd *qc = _data; - struct ata_port *ap = qc->ap; - u8 status; - int poll_next; - -fsm_start: - WARN_ON(ap->hsm_task_state == HSM_ST_IDLE); - - /* - * This is purely heuristic. This is a fast path. - * Sometimes when we enter, BSY will be cleared in - * a chk-status or two. If not, the drive is probably seeking - * or something. Snooze for a couple msecs, then - * chk-status again. If still busy, queue delayed work. - */ - status = ata_busy_wait(ap, ATA_BUSY, 5); - if (status & ATA_BUSY) { - msleep(2); - status = ata_busy_wait(ap, ATA_BUSY, 10); - if (status & ATA_BUSY) { - ata_port_queue_task(ap, ata_pio_task, qc, ATA_SHORT_PAUSE); - return; - } - } - - /* move the HSM */ - poll_next = ata_hsm_move(ap, qc, status, 1); - - /* another command or interrupt handler - * may be running at this point. - */ - if (poll_next) - goto fsm_start; -} - -/** - * ata_qc_new - Request an available ATA command, for queueing - * @ap: Port associated with device @dev - * @dev: Device from whom we request an available command structure - * - * LOCKING: - * None. - */ - -static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap) -{ - struct ata_queued_cmd *qc = NULL; - unsigned int i; - - /* no command while frozen */ - if (unlikely(ap->pflags & ATA_PFLAG_FROZEN)) - return NULL; - - /* the last tag is reserved for internal command. */ - for (i = 0; i < ATA_MAX_QUEUE - 1; i++) - if (!test_and_set_bit(i, &ap->qc_allocated)) { - qc = __ata_qc_from_tag(ap, i); - break; - } - - if (qc) - qc->tag = i; - - return qc; -} - -/** - * ata_qc_new_init - Request an available ATA command, and initialize it - * @dev: Device from whom we request an available command structure - * - * LOCKING: - * None. - */ - -struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev) -{ - struct ata_port *ap = dev->ap; - struct ata_queued_cmd *qc; - - qc = ata_qc_new(ap); - if (qc) { - qc->scsicmd = NULL; - qc->ap = ap; - qc->dev = dev; - - ata_qc_reinit(qc); - } - - return qc; -} - -/** - * ata_qc_free - free unused ata_queued_cmd - * @qc: Command to complete - * - * Designed to free unused ata_queued_cmd object - * in case something prevents using it. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ -void ata_qc_free(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - unsigned int tag; - - WARN_ON(qc == NULL); /* ata_qc_from_tag _might_ return NULL */ - - qc->flags = 0; - tag = qc->tag; - if (likely(ata_tag_valid(tag))) { - qc->tag = ATA_TAG_POISON; - clear_bit(tag, &ap->qc_allocated); - } -} - -void __ata_qc_complete(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - - WARN_ON(qc == NULL); /* ata_qc_from_tag _might_ return NULL */ - WARN_ON(!(qc->flags & ATA_QCFLAG_ACTIVE)); - - if (likely(qc->flags & ATA_QCFLAG_DMAMAP)) - ata_sg_clean(qc); - - /* command should be marked inactive atomically with qc completion */ - if (qc->tf.protocol == ATA_PROT_NCQ) - ap->sactive &= ~(1 << qc->tag); - else - ap->active_tag = ATA_TAG_POISON; - - /* atapi: mark qc as inactive to prevent the interrupt handler - * from completing the command twice later, before the error handler - * is called. (when rc != 0 and atapi request sense is needed) - */ - qc->flags &= ~ATA_QCFLAG_ACTIVE; - ap->qc_active &= ~(1 << qc->tag); - - /* call completion callback */ - qc->complete_fn(qc); -} - -/** - * ata_qc_complete - Complete an active ATA command - * @qc: Command to complete - * @err_mask: ATA Status register contents - * - * Indicate to the mid and upper layers that an ATA - * command has completed, with either an ok or not-ok status. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ -void ata_qc_complete(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - - /* XXX: New EH and old EH use different mechanisms to - * synchronize EH with regular execution path. - * - * In new EH, a failed qc is marked with ATA_QCFLAG_FAILED. - * Normal execution path is responsible for not accessing a - * failed qc. libata core enforces the rule by returning NULL - * from ata_qc_from_tag() for failed qcs. - * - * Old EH depends on ata_qc_complete() nullifying completion - * requests if ATA_QCFLAG_EH_SCHEDULED is set. Old EH does - * not synchronize with interrupt handler. Only PIO task is - * taken care of. - */ - if (ap->ops->error_handler) { - WARN_ON(ap->pflags & ATA_PFLAG_FROZEN); - - if (unlikely(qc->err_mask)) - qc->flags |= ATA_QCFLAG_FAILED; - - if (unlikely(qc->flags & ATA_QCFLAG_FAILED)) { - if (!ata_tag_internal(qc->tag)) { - /* always fill result TF for failed qc */ - ap->ops->tf_read(ap, &qc->result_tf); - ata_qc_schedule_eh(qc); - return; - } - } - - /* read result TF if requested */ - if (qc->flags & ATA_QCFLAG_RESULT_TF) - ap->ops->tf_read(ap, &qc->result_tf); - - __ata_qc_complete(qc); - } else { - if (qc->flags & ATA_QCFLAG_EH_SCHEDULED) - return; - - /* read result TF if failed or requested */ - if (qc->err_mask || qc->flags & ATA_QCFLAG_RESULT_TF) - ap->ops->tf_read(ap, &qc->result_tf); - - __ata_qc_complete(qc); - } -} - -/** - * ata_qc_complete_multiple - Complete multiple qcs successfully - * @ap: port in question - * @qc_active: new qc_active mask - * @finish_qc: LLDD callback invoked before completing a qc - * - * Complete in-flight commands. This functions is meant to be - * called from low-level driver's interrupt routine to complete - * requests normally. ap->qc_active and @qc_active is compared - * and commands are completed accordingly. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * Number of completed commands on success, -errno otherwise. - */ -int ata_qc_complete_multiple(struct ata_port *ap, u32 qc_active, - void (*finish_qc)(struct ata_queued_cmd *)) -{ - int nr_done = 0; - u32 done_mask; - int i; - - done_mask = ap->qc_active ^ qc_active; - - if (unlikely(done_mask & qc_active)) { - ata_port_printk(ap, KERN_ERR, "illegal qc_active transition " - "(%08x->%08x)\n", ap->qc_active, qc_active); - return -EINVAL; - } - - for (i = 0; i < ATA_MAX_QUEUE; i++) { - struct ata_queued_cmd *qc; - - if (!(done_mask & (1 << i))) - continue; - - if ((qc = ata_qc_from_tag(ap, i))) { - if (finish_qc) - finish_qc(qc); - ata_qc_complete(qc); - nr_done++; - } - } - - return nr_done; -} - -static inline int ata_should_dma_map(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - - switch (qc->tf.protocol) { - case ATA_PROT_NCQ: - case ATA_PROT_DMA: - case ATA_PROT_ATAPI_DMA: - return 1; - - case ATA_PROT_ATAPI: - case ATA_PROT_PIO: - if (ap->flags & ATA_FLAG_PIO_DMA) - return 1; - - /* fall through */ - - default: - return 0; - } - - /* never reached */ -} - -/** - * ata_qc_issue - issue taskfile to device - * @qc: command to issue to device - * - * Prepare an ATA command to submission to device. - * This includes mapping the data into a DMA-able - * area, filling in the S/G table, and finally - * writing the taskfile to hardware, starting the command. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ -void ata_qc_issue(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - - /* Make sure only one non-NCQ command is outstanding. The - * check is skipped for old EH because it reuses active qc to - * request ATAPI sense. - */ - WARN_ON(ap->ops->error_handler && ata_tag_valid(ap->active_tag)); - - if (qc->tf.protocol == ATA_PROT_NCQ) { - WARN_ON(ap->sactive & (1 << qc->tag)); - ap->sactive |= 1 << qc->tag; - } else { - WARN_ON(ap->sactive); - ap->active_tag = qc->tag; - } - - qc->flags |= ATA_QCFLAG_ACTIVE; - ap->qc_active |= 1 << qc->tag; - - if (ata_should_dma_map(qc)) { - if (qc->flags & ATA_QCFLAG_SG) { - if (ata_sg_setup(qc)) - goto sg_err; - } else if (qc->flags & ATA_QCFLAG_SINGLE) { - if (ata_sg_setup_one(qc)) - goto sg_err; - } - } else { - qc->flags &= ~ATA_QCFLAG_DMAMAP; - } - - ap->ops->qc_prep(qc); - - qc->err_mask |= ap->ops->qc_issue(qc); - if (unlikely(qc->err_mask)) - goto err; - return; - -sg_err: - qc->flags &= ~ATA_QCFLAG_DMAMAP; - qc->err_mask |= AC_ERR_SYSTEM; -err: - ata_qc_complete(qc); -} - -/** - * ata_qc_issue_prot - issue taskfile to device in proto-dependent manner - * @qc: command to issue to device - * - * Using various libata functions and hooks, this function - * starts an ATA command. ATA commands are grouped into - * classes called "protocols", and issuing each type of protocol - * is slightly different. - * - * May be used as the qc_issue() entry in ata_port_operations. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * Zero on success, AC_ERR_* mask on failure - */ - -unsigned int ata_qc_issue_prot(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - - /* Use polling pio if the LLD doesn't handle - * interrupt driven pio and atapi CDB interrupt. - */ - if (ap->flags & ATA_FLAG_PIO_POLLING) { - switch (qc->tf.protocol) { - case ATA_PROT_PIO: - case ATA_PROT_ATAPI: - case ATA_PROT_ATAPI_NODATA: - qc->tf.flags |= ATA_TFLAG_POLLING; - break; - case ATA_PROT_ATAPI_DMA: - if (qc->dev->flags & ATA_DFLAG_CDB_INTR) - /* see ata_dma_blacklisted() */ - BUG(); - break; - default: - break; - } - } - - /* select the device */ - ata_dev_select(ap, qc->dev->devno, 1, 0); - - /* start the command */ - switch (qc->tf.protocol) { - case ATA_PROT_NODATA: - if (qc->tf.flags & ATA_TFLAG_POLLING) - ata_qc_set_polling(qc); - - ata_tf_to_host(ap, &qc->tf); - ap->hsm_task_state = HSM_ST_LAST; - - if (qc->tf.flags & ATA_TFLAG_POLLING) - ata_port_queue_task(ap, ata_pio_task, qc, 0); - - break; - - case ATA_PROT_DMA: - WARN_ON(qc->tf.flags & ATA_TFLAG_POLLING); - - ap->ops->tf_load(ap, &qc->tf); /* load tf registers */ - ap->ops->bmdma_setup(qc); /* set up bmdma */ - ap->ops->bmdma_start(qc); /* initiate bmdma */ - ap->hsm_task_state = HSM_ST_LAST; - break; - - case ATA_PROT_PIO: - if (qc->tf.flags & ATA_TFLAG_POLLING) - ata_qc_set_polling(qc); - - ata_tf_to_host(ap, &qc->tf); - - if (qc->tf.flags & ATA_TFLAG_WRITE) { - /* PIO data out protocol */ - ap->hsm_task_state = HSM_ST_FIRST; - ata_port_queue_task(ap, ata_pio_task, qc, 0); - - /* always send first data block using - * the ata_pio_task() codepath. - */ - } else { - /* PIO data in protocol */ - ap->hsm_task_state = HSM_ST; - - if (qc->tf.flags & ATA_TFLAG_POLLING) - ata_port_queue_task(ap, ata_pio_task, qc, 0); - - /* if polling, ata_pio_task() handles the rest. - * otherwise, interrupt handler takes over from here. - */ - } - - break; - - case ATA_PROT_ATAPI: - case ATA_PROT_ATAPI_NODATA: - if (qc->tf.flags & ATA_TFLAG_POLLING) - ata_qc_set_polling(qc); - - ata_tf_to_host(ap, &qc->tf); - - ap->hsm_task_state = HSM_ST_FIRST; - - /* send cdb by polling if no cdb interrupt */ - if ((!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) || - (qc->tf.flags & ATA_TFLAG_POLLING)) - ata_port_queue_task(ap, ata_pio_task, qc, 0); - break; - - case ATA_PROT_ATAPI_DMA: - WARN_ON(qc->tf.flags & ATA_TFLAG_POLLING); - - ap->ops->tf_load(ap, &qc->tf); /* load tf registers */ - ap->ops->bmdma_setup(qc); /* set up bmdma */ - ap->hsm_task_state = HSM_ST_FIRST; - - /* send cdb by polling if no cdb interrupt */ - if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) - ata_port_queue_task(ap, ata_pio_task, qc, 0); - break; - - default: - WARN_ON(1); - return AC_ERR_SYSTEM; - } - - return 0; -} - -/** - * ata_host_intr - Handle host interrupt for given (port, task) - * @ap: Port on which interrupt arrived (possibly...) - * @qc: Taskfile currently active in engine - * - * Handle host interrupt for given queued command. Currently, - * only DMA interrupts are handled. All other commands are - * handled via polling with interrupts disabled (nIEN bit). - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * One if interrupt was handled, zero if not (shared irq). - */ - -inline unsigned int ata_host_intr (struct ata_port *ap, - struct ata_queued_cmd *qc) -{ - u8 status, host_stat = 0; - - VPRINTK("ata%u: protocol %d task_state %d\n", - ap->id, qc->tf.protocol, ap->hsm_task_state); - - /* Check whether we are expecting interrupt in this state */ - switch (ap->hsm_task_state) { - case HSM_ST_FIRST: - /* Some pre-ATAPI-4 devices assert INTRQ - * at this state when ready to receive CDB. - */ - - /* Check the ATA_DFLAG_CDB_INTR flag is enough here. - * The flag was turned on only for atapi devices. - * No need to check is_atapi_taskfile(&qc->tf) again. - */ - if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) - goto idle_irq; - break; - case HSM_ST_LAST: - if (qc->tf.protocol == ATA_PROT_DMA || - qc->tf.protocol == ATA_PROT_ATAPI_DMA) { - /* check status of DMA engine */ - host_stat = ap->ops->bmdma_status(ap); - VPRINTK("ata%u: host_stat 0x%X\n", ap->id, host_stat); - - /* if it's not our irq... */ - if (!(host_stat & ATA_DMA_INTR)) - goto idle_irq; - - /* before we do anything else, clear DMA-Start bit */ - ap->ops->bmdma_stop(qc); - - if (unlikely(host_stat & ATA_DMA_ERR)) { - /* error when transfering data to/from memory */ - qc->err_mask |= AC_ERR_HOST_BUS; - ap->hsm_task_state = HSM_ST_ERR; - } - } - break; - case HSM_ST: - break; - default: - goto idle_irq; - } - - /* check altstatus */ - status = ata_altstatus(ap); - if (status & ATA_BUSY) - goto idle_irq; - - /* check main status, clearing INTRQ */ - status = ata_chk_status(ap); - if (unlikely(status & ATA_BUSY)) - goto idle_irq; - - /* ack bmdma irq events */ - ap->ops->irq_clear(ap); - - ata_hsm_move(ap, qc, status, 0); - return 1; /* irq handled */ - -idle_irq: - ap->stats.idle_irq++; - -#ifdef ATA_IRQ_TRAP - if ((ap->stats.idle_irq % 1000) == 0) { - ata_irq_ack(ap, 0); /* debug trap */ - ata_port_printk(ap, KERN_WARNING, "irq trap\n"); - return 1; - } -#endif - return 0; /* irq not handled */ -} - -/** - * ata_interrupt - Default ATA host interrupt handler - * @irq: irq line (unused) - * @dev_instance: pointer to our ata_host_set information structure - * @regs: unused - * - * Default interrupt handler for PCI IDE devices. Calls - * ata_host_intr() for each port that is not disabled. - * - * LOCKING: - * Obtains host_set lock during operation. - * - * RETURNS: - * IRQ_NONE or IRQ_HANDLED. - */ - -irqreturn_t ata_interrupt (int irq, void *dev_instance, struct pt_regs *regs) -{ - struct ata_host_set *host_set = dev_instance; - unsigned int i; - unsigned int handled = 0; - unsigned long flags; - - /* TODO: make _irqsave conditional on x86 PCI IDE legacy mode */ - spin_lock_irqsave(&host_set->lock, flags); - - for (i = 0; i < host_set->n_ports; i++) { - struct ata_port *ap; - - ap = host_set->ports[i]; - if (ap && - !(ap->flags & ATA_FLAG_DISABLED)) { - struct ata_queued_cmd *qc; - - qc = ata_qc_from_tag(ap, ap->active_tag); - if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING)) && - (qc->flags & ATA_QCFLAG_ACTIVE)) - handled |= ata_host_intr(ap, qc); - } - } - - spin_unlock_irqrestore(&host_set->lock, flags); - - return IRQ_RETVAL(handled); -} - -/** - * sata_scr_valid - test whether SCRs are accessible - * @ap: ATA port to test SCR accessibility for - * - * Test whether SCRs are accessible for @ap. - * - * LOCKING: - * None. - * - * RETURNS: - * 1 if SCRs are accessible, 0 otherwise. - */ -int sata_scr_valid(struct ata_port *ap) -{ - return ap->cbl == ATA_CBL_SATA && ap->ops->scr_read; -} - -/** - * sata_scr_read - read SCR register of the specified port - * @ap: ATA port to read SCR for - * @reg: SCR to read - * @val: Place to store read value - * - * Read SCR register @reg of @ap into *@val. This function is - * guaranteed to succeed if the cable type of the port is SATA - * and the port implements ->scr_read. - * - * LOCKING: - * None. - * - * RETURNS: - * 0 on success, negative errno on failure. - */ -int sata_scr_read(struct ata_port *ap, int reg, u32 *val) -{ - if (sata_scr_valid(ap)) { - *val = ap->ops->scr_read(ap, reg); - return 0; - } - return -EOPNOTSUPP; -} - -/** - * sata_scr_write - write SCR register of the specified port - * @ap: ATA port to write SCR for - * @reg: SCR to write - * @val: value to write - * - * Write @val to SCR register @reg of @ap. This function is - * guaranteed to succeed if the cable type of the port is SATA - * and the port implements ->scr_read. - * - * LOCKING: - * None. - * - * RETURNS: - * 0 on success, negative errno on failure. - */ -int sata_scr_write(struct ata_port *ap, int reg, u32 val) -{ - if (sata_scr_valid(ap)) { - ap->ops->scr_write(ap, reg, val); - return 0; - } - return -EOPNOTSUPP; -} - -/** - * sata_scr_write_flush - write SCR register of the specified port and flush - * @ap: ATA port to write SCR for - * @reg: SCR to write - * @val: value to write - * - * This function is identical to sata_scr_write() except that this - * function performs flush after writing to the register. - * - * LOCKING: - * None. - * - * RETURNS: - * 0 on success, negative errno on failure. - */ -int sata_scr_write_flush(struct ata_port *ap, int reg, u32 val) -{ - if (sata_scr_valid(ap)) { - ap->ops->scr_write(ap, reg, val); - ap->ops->scr_read(ap, reg); - return 0; - } - return -EOPNOTSUPP; -} - -/** - * ata_port_online - test whether the given port is online - * @ap: ATA port to test - * - * Test whether @ap is online. Note that this function returns 0 - * if online status of @ap cannot be obtained, so - * ata_port_online(ap) != !ata_port_offline(ap). - * - * LOCKING: - * None. - * - * RETURNS: - * 1 if the port online status is available and online. - */ -int ata_port_online(struct ata_port *ap) -{ - u32 sstatus; - - if (!sata_scr_read(ap, SCR_STATUS, &sstatus) && (sstatus & 0xf) == 0x3) - return 1; - return 0; -} - -/** - * ata_port_offline - test whether the given port is offline - * @ap: ATA port to test - * - * Test whether @ap is offline. Note that this function returns - * 0 if offline status of @ap cannot be obtained, so - * ata_port_online(ap) != !ata_port_offline(ap). - * - * LOCKING: - * None. - * - * RETURNS: - * 1 if the port offline status is available and offline. - */ -int ata_port_offline(struct ata_port *ap) -{ - u32 sstatus; - - if (!sata_scr_read(ap, SCR_STATUS, &sstatus) && (sstatus & 0xf) != 0x3) - return 1; - return 0; -} - -int ata_flush_cache(struct ata_device *dev) -{ - unsigned int err_mask; - u8 cmd; - - if (!ata_try_flush_cache(dev)) - return 0; - - if (ata_id_has_flush_ext(dev->id)) - cmd = ATA_CMD_FLUSH_EXT; - else - cmd = ATA_CMD_FLUSH; - - err_mask = ata_do_simple_cmd(dev, cmd); - if (err_mask) { - ata_dev_printk(dev, KERN_ERR, "failed to flush cache\n"); - return -EIO; - } - - return 0; -} - -static int ata_host_set_request_pm(struct ata_host_set *host_set, - pm_message_t mesg, unsigned int action, - unsigned int ehi_flags, int wait) -{ - unsigned long flags; - int i, rc; - - for (i = 0; i < host_set->n_ports; i++) { - struct ata_port *ap = host_set->ports[i]; - - /* Previous resume operation might still be in - * progress. Wait for PM_PENDING to clear. - */ - if (ap->pflags & ATA_PFLAG_PM_PENDING) { - ata_port_wait_eh(ap); - WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); - } - - /* request PM ops to EH */ - spin_lock_irqsave(ap->lock, flags); - - ap->pm_mesg = mesg; - if (wait) { - rc = 0; - ap->pm_result = &rc; - } - - ap->pflags |= ATA_PFLAG_PM_PENDING; - ap->eh_info.action |= action; - ap->eh_info.flags |= ehi_flags; - - ata_port_schedule_eh(ap); - - spin_unlock_irqrestore(ap->lock, flags); - - /* wait and check result */ - if (wait) { - ata_port_wait_eh(ap); - WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); - if (rc) - return rc; - } - } - - return 0; -} - -/** - * ata_host_set_suspend - suspend host_set - * @host_set: host_set to suspend - * @mesg: PM message - * - * Suspend @host_set. Actual operation is performed by EH. This - * function requests EH to perform PM operations and waits for EH - * to finish. - * - * LOCKING: - * Kernel thread context (may sleep). - * - * RETURNS: - * 0 on success, -errno on failure. - */ -int ata_host_set_suspend(struct ata_host_set *host_set, pm_message_t mesg) -{ - int i, j, rc; - - rc = ata_host_set_request_pm(host_set, mesg, 0, ATA_EHI_QUIET, 1); - if (rc) - goto fail; - - /* EH is quiescent now. Fail if we have any ready device. - * This happens if hotplug occurs between completion of device - * suspension and here. - */ - for (i = 0; i < host_set->n_ports; i++) { - struct ata_port *ap = host_set->ports[i]; - - for (j = 0; j < ATA_MAX_DEVICES; j++) { - struct ata_device *dev = &ap->device[j]; - - if (ata_dev_ready(dev)) { - ata_port_printk(ap, KERN_WARNING, - "suspend failed, device %d " - "still active\n", dev->devno); - rc = -EBUSY; - goto fail; - } - } - } - - host_set->dev->power.power_state = mesg; - return 0; - - fail: - ata_host_set_resume(host_set); - return rc; -} - -/** - * ata_host_set_resume - resume host_set - * @host_set: host_set to resume - * - * Resume @host_set. Actual operation is performed by EH. This - * function requests EH to perform PM operations and returns. - * Note that all resume operations are performed parallely. - * - * LOCKING: - * Kernel thread context (may sleep). - */ -void ata_host_set_resume(struct ata_host_set *host_set) -{ - ata_host_set_request_pm(host_set, PMSG_ON, ATA_EH_SOFTRESET, - ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0); - host_set->dev->power.power_state = PMSG_ON; -} - -/** - * ata_port_start - Set port up for dma. - * @ap: Port to initialize - * - * Called just after data structures for each port are - * initialized. Allocates space for PRD table. - * - * May be used as the port_start() entry in ata_port_operations. - * - * LOCKING: - * Inherited from caller. - */ - -int ata_port_start (struct ata_port *ap) -{ - struct device *dev = ap->dev; - int rc; - - ap->prd = dma_alloc_coherent(dev, ATA_PRD_TBL_SZ, &ap->prd_dma, GFP_KERNEL); - if (!ap->prd) - return -ENOMEM; - - rc = ata_pad_alloc(ap, dev); - if (rc) { - dma_free_coherent(dev, ATA_PRD_TBL_SZ, ap->prd, ap->prd_dma); - return rc; - } - - DPRINTK("prd alloc, virt %p, dma %llx\n", ap->prd, (unsigned long long) ap->prd_dma); - - return 0; -} - - -/** - * ata_port_stop - Undo ata_port_start() - * @ap: Port to shut down - * - * Frees the PRD table. - * - * May be used as the port_stop() entry in ata_port_operations. - * - * LOCKING: - * Inherited from caller. - */ - -void ata_port_stop (struct ata_port *ap) -{ - struct device *dev = ap->dev; - - dma_free_coherent(dev, ATA_PRD_TBL_SZ, ap->prd, ap->prd_dma); - ata_pad_free(ap, dev); -} - -void ata_host_stop (struct ata_host_set *host_set) -{ - if (host_set->mmio_base) - iounmap(host_set->mmio_base); -} - -/** - * ata_dev_init - Initialize an ata_device structure - * @dev: Device structure to initialize - * - * Initialize @dev in preparation for probing. - * - * LOCKING: - * Inherited from caller. - */ -void ata_dev_init(struct ata_device *dev) -{ - struct ata_port *ap = dev->ap; - unsigned long flags; - - /* SATA spd limit is bound to the first device */ - ap->sata_spd_limit = ap->hw_sata_spd_limit; - - /* High bits of dev->flags are used to record warm plug - * requests which occur asynchronously. Synchronize using - * host_set lock. - */ - spin_lock_irqsave(ap->lock, flags); - dev->flags &= ~ATA_DFLAG_INIT_MASK; - spin_unlock_irqrestore(ap->lock, flags); - - memset((void *)dev + ATA_DEVICE_CLEAR_OFFSET, 0, - sizeof(*dev) - ATA_DEVICE_CLEAR_OFFSET); - dev->pio_mask = UINT_MAX; - dev->mwdma_mask = UINT_MAX; - dev->udma_mask = UINT_MAX; -} - -/** - * ata_port_init - Initialize an ata_port structure - * @ap: Structure to initialize - * @host_set: Collection of hosts to which @ap belongs - * @ent: Probe information provided by low-level driver - * @port_no: Port number associated with this ata_port - * - * Initialize a new ata_port structure. - * - * LOCKING: - * Inherited from caller. - */ -void ata_port_init(struct ata_port *ap, struct ata_host_set *host_set, - const struct ata_probe_ent *ent, unsigned int port_no) -{ - unsigned int i; - - ap->lock = &host_set->lock; - ap->flags = ATA_FLAG_DISABLED; - ap->id = ata_unique_id++; - ap->ctl = ATA_DEVCTL_OBS; - ap->host_set = host_set; - ap->dev = ent->dev; - ap->port_no = port_no; - ap->pio_mask = ent->pio_mask; - ap->mwdma_mask = ent->mwdma_mask; - ap->udma_mask = ent->udma_mask; - ap->flags |= ent->host_flags; - ap->ops = ent->port_ops; - ap->hw_sata_spd_limit = UINT_MAX; - ap->active_tag = ATA_TAG_POISON; - ap->last_ctl = 0xFF; - -#if defined(ATA_VERBOSE_DEBUG) - /* turn on all debugging levels */ - ap->msg_enable = 0x00FF; -#elif defined(ATA_DEBUG) - ap->msg_enable = ATA_MSG_DRV | ATA_MSG_INFO | ATA_MSG_CTL | ATA_MSG_WARN | ATA_MSG_ERR; -#else - ap->msg_enable = ATA_MSG_DRV | ATA_MSG_ERR | ATA_MSG_WARN; -#endif - - INIT_WORK(&ap->port_task, NULL, NULL); - INIT_WORK(&ap->hotplug_task, ata_scsi_hotplug, ap); - INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan, ap); - INIT_LIST_HEAD(&ap->eh_done_q); - init_waitqueue_head(&ap->eh_wait_q); - - /* set cable type */ - ap->cbl = ATA_CBL_NONE; - if (ap->flags & ATA_FLAG_SATA) - ap->cbl = ATA_CBL_SATA; - - for (i = 0; i < ATA_MAX_DEVICES; i++) { - struct ata_device *dev = &ap->device[i]; - dev->ap = ap; - dev->devno = i; - ata_dev_init(dev); - } - -#ifdef ATA_IRQ_TRAP - ap->stats.unhandled_irq = 1; - ap->stats.idle_irq = 1; -#endif - - memcpy(&ap->ioaddr, &ent->port[port_no], sizeof(struct ata_ioports)); -} - -/** - * ata_port_init_shost - Initialize SCSI host associated with ATA port - * @ap: ATA port to initialize SCSI host for - * @shost: SCSI host associated with @ap - * - * Initialize SCSI host @shost associated with ATA port @ap. - * - * LOCKING: - * Inherited from caller. - */ -static void ata_port_init_shost(struct ata_port *ap, struct Scsi_Host *shost) -{ - ap->host = shost; - - shost->unique_id = ap->id; - shost->max_id = 16; - shost->max_lun = 1; - shost->max_channel = 1; - shost->max_cmd_len = 12; -} - -/** - * ata_port_add - Attach low-level ATA driver to system - * @ent: Information provided by low-level driver - * @host_set: Collections of ports to which we add - * @port_no: Port number associated with this host - * - * Attach low-level ATA driver to system. - * - * LOCKING: - * PCI/etc. bus probe sem. - * - * RETURNS: - * New ata_port on success, for NULL on error. - */ -static struct ata_port * ata_port_add(const struct ata_probe_ent *ent, - struct ata_host_set *host_set, - unsigned int port_no) -{ - struct Scsi_Host *shost; - struct ata_port *ap; - - DPRINTK("ENTER\n"); - - if (!ent->port_ops->error_handler && - !(ent->host_flags & (ATA_FLAG_SATA_RESET | ATA_FLAG_SRST))) { - printk(KERN_ERR "ata%u: no reset mechanism available\n", - port_no); - return NULL; - } - - shost = scsi_host_alloc(ent->sht, sizeof(struct ata_port)); - if (!shost) - return NULL; - - shost->transportt = &ata_scsi_transport_template; - - ap = ata_shost_to_port(shost); - - ata_port_init(ap, host_set, ent, port_no); - ata_port_init_shost(ap, shost); - - return ap; -} - -/** - * ata_sas_host_init - Initialize a host_set struct - * @host_set: host_set to initialize - * @dev: device host_set is attached to - * @flags: host_set flags - * @ops: port_ops - * - * LOCKING: - * PCI/etc. bus probe sem. - * - */ - -void ata_host_set_init(struct ata_host_set *host_set, - struct device *dev, unsigned long flags, - const struct ata_port_operations *ops) -{ - spin_lock_init(&host_set->lock); - host_set->dev = dev; - host_set->flags = flags; - host_set->ops = ops; -} - -/** - * ata_device_add - Register hardware device with ATA and SCSI layers - * @ent: Probe information describing hardware device to be registered - * - * This function processes the information provided in the probe - * information struct @ent, allocates the necessary ATA and SCSI - * host information structures, initializes them, and registers - * everything with requisite kernel subsystems. - * - * This function requests irqs, probes the ATA bus, and probes - * the SCSI bus. - * - * LOCKING: - * PCI/etc. bus probe sem. - * - * RETURNS: - * Number of ports registered. Zero on error (no ports registered). - */ -int ata_device_add(const struct ata_probe_ent *ent) -{ - unsigned int i; - struct device *dev = ent->dev; - struct ata_host_set *host_set; - int rc; - - DPRINTK("ENTER\n"); - /* alloc a container for our list of ATA ports (buses) */ - host_set = kzalloc(sizeof(struct ata_host_set) + - (ent->n_ports * sizeof(void *)), GFP_KERNEL); - if (!host_set) - return 0; - - ata_host_set_init(host_set, dev, ent->host_set_flags, ent->port_ops); - host_set->n_ports = ent->n_ports; - host_set->irq = ent->irq; - host_set->irq2 = ent->irq2; - host_set->mmio_base = ent->mmio_base; - host_set->private_data = ent->private_data; - - /* register each port bound to this device */ - for (i = 0; i < host_set->n_ports; i++) { - struct ata_port *ap; - unsigned long xfer_mode_mask; - int irq_line = ent->irq; - - ap = ata_port_add(ent, host_set, i); - if (!ap) - goto err_out; - - host_set->ports[i] = ap; - - /* dummy? */ - if (ent->dummy_port_mask & (1 << i)) { - ata_port_printk(ap, KERN_INFO, "DUMMY\n"); - ap->ops = &ata_dummy_port_ops; - continue; - } - - /* start port */ - rc = ap->ops->port_start(ap); - if (rc) { - host_set->ports[i] = NULL; - scsi_host_put(ap->host); - goto err_out; - } - - /* Report the secondary IRQ for second channel legacy */ - if (i == 1 && ent->irq2) - irq_line = ent->irq2; - - xfer_mode_mask =(ap->udma_mask << ATA_SHIFT_UDMA) | - (ap->mwdma_mask << ATA_SHIFT_MWDMA) | - (ap->pio_mask << ATA_SHIFT_PIO); - - /* print per-port info to dmesg */ - ata_port_printk(ap, KERN_INFO, "%cATA max %s cmd 0x%lX " - "ctl 0x%lX bmdma 0x%lX irq %d\n", - ap->flags & ATA_FLAG_SATA ? 'S' : 'P', - ata_mode_string(xfer_mode_mask), - ap->ioaddr.cmd_addr, - ap->ioaddr.ctl_addr, - ap->ioaddr.bmdma_addr, - irq_line); - - ata_chk_status(ap); - host_set->ops->irq_clear(ap); - ata_eh_freeze_port(ap); /* freeze port before requesting IRQ */ - } - - /* obtain irq, that may be shared between channels */ - rc = request_irq(ent->irq, ent->port_ops->irq_handler, ent->irq_flags, - DRV_NAME, host_set); - if (rc) { - dev_printk(KERN_ERR, dev, "irq %lu request failed: %d\n", - ent->irq, rc); - goto err_out; - } - - /* do we have a second IRQ for the other channel, eg legacy mode */ - if (ent->irq2) { - /* We will get weird core code crashes later if this is true - so trap it now */ - BUG_ON(ent->irq == ent->irq2); - - rc = request_irq(ent->irq2, ent->port_ops->irq_handler, ent->irq_flags, - DRV_NAME, host_set); - if (rc) { - dev_printk(KERN_ERR, dev, "irq %lu request failed: %d\n", - ent->irq2, rc); - goto err_out_free_irq; - } - } - - /* perform each probe synchronously */ - DPRINTK("probe begin\n"); - for (i = 0; i < host_set->n_ports; i++) { - struct ata_port *ap = host_set->ports[i]; - u32 scontrol; - int rc; - - /* init sata_spd_limit to the current value */ - if (sata_scr_read(ap, SCR_CONTROL, &scontrol) == 0) { - int spd = (scontrol >> 4) & 0xf; - ap->hw_sata_spd_limit &= (1 << spd) - 1; - } - ap->sata_spd_limit = ap->hw_sata_spd_limit; - - rc = scsi_add_host(ap->host, dev); - if (rc) { - ata_port_printk(ap, KERN_ERR, "scsi_add_host failed\n"); - /* FIXME: do something useful here */ - /* FIXME: handle unconditional calls to - * scsi_scan_host and ata_host_remove, below, - * at the very least - */ - } - - if (ap->ops->error_handler) { - struct ata_eh_info *ehi = &ap->eh_info; - unsigned long flags; - - ata_port_probe(ap); - - /* kick EH for boot probing */ - spin_lock_irqsave(ap->lock, flags); - - ehi->probe_mask = (1 << ATA_MAX_DEVICES) - 1; - ehi->action |= ATA_EH_SOFTRESET; - ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET; - - ap->pflags |= ATA_PFLAG_LOADING; - ata_port_schedule_eh(ap); - - spin_unlock_irqrestore(ap->lock, flags); - - /* wait for EH to finish */ - ata_port_wait_eh(ap); - } else { - DPRINTK("ata%u: bus probe begin\n", ap->id); - rc = ata_bus_probe(ap); - DPRINTK("ata%u: bus probe end\n", ap->id); - - if (rc) { - /* FIXME: do something useful here? - * Current libata behavior will - * tear down everything when - * the module is removed - * or the h/w is unplugged. - */ - } - } - } - - /* probes are done, now scan each port's disk(s) */ - DPRINTK("host probe begin\n"); - for (i = 0; i < host_set->n_ports; i++) { - struct ata_port *ap = host_set->ports[i]; - - ata_scsi_scan_host(ap); - } - - dev_set_drvdata(dev, host_set); - - VPRINTK("EXIT, returning %u\n", ent->n_ports); - return ent->n_ports; /* success */ - -err_out_free_irq: - free_irq(ent->irq, host_set); -err_out: - for (i = 0; i < host_set->n_ports; i++) { - struct ata_port *ap = host_set->ports[i]; - if (ap) { - ap->ops->port_stop(ap); - scsi_host_put(ap->host); - } - } - - kfree(host_set); - VPRINTK("EXIT, returning 0\n"); - return 0; -} - -/** - * ata_port_detach - Detach ATA port in prepration of device removal - * @ap: ATA port to be detached - * - * Detach all ATA devices and the associated SCSI devices of @ap; - * then, remove the associated SCSI host. @ap is guaranteed to - * be quiescent on return from this function. - * - * LOCKING: - * Kernel thread context (may sleep). - */ -void ata_port_detach(struct ata_port *ap) -{ - unsigned long flags; - int i; - - if (!ap->ops->error_handler) - goto skip_eh; - - /* tell EH we're leaving & flush EH */ - spin_lock_irqsave(ap->lock, flags); - ap->pflags |= ATA_PFLAG_UNLOADING; - spin_unlock_irqrestore(ap->lock, flags); - - ata_port_wait_eh(ap); - - /* EH is now guaranteed to see UNLOADING, so no new device - * will be attached. Disable all existing devices. - */ - spin_lock_irqsave(ap->lock, flags); - - for (i = 0; i < ATA_MAX_DEVICES; i++) - ata_dev_disable(&ap->device[i]); - - spin_unlock_irqrestore(ap->lock, flags); - - /* Final freeze & EH. All in-flight commands are aborted. EH - * will be skipped and retrials will be terminated with bad - * target. - */ - spin_lock_irqsave(ap->lock, flags); - ata_port_freeze(ap); /* won't be thawed */ - spin_unlock_irqrestore(ap->lock, flags); - - ata_port_wait_eh(ap); - - /* Flush hotplug task. The sequence is similar to - * ata_port_flush_task(). - */ - flush_workqueue(ata_aux_wq); - cancel_delayed_work(&ap->hotplug_task); - flush_workqueue(ata_aux_wq); - - skip_eh: - /* remove the associated SCSI host */ - scsi_remove_host(ap->host); -} - -/** - * ata_host_set_remove - PCI layer callback for device removal - * @host_set: ATA host set that was removed - * - * Unregister all objects associated with this host set. Free those - * objects. - * - * LOCKING: - * Inherited from calling layer (may sleep). - */ - -void ata_host_set_remove(struct ata_host_set *host_set) -{ - unsigned int i; - - for (i = 0; i < host_set->n_ports; i++) - ata_port_detach(host_set->ports[i]); - - free_irq(host_set->irq, host_set); - if (host_set->irq2) - free_irq(host_set->irq2, host_set); - - for (i = 0; i < host_set->n_ports; i++) { - struct ata_port *ap = host_set->ports[i]; - - ata_scsi_release(ap->host); - - if ((ap->flags & ATA_FLAG_NO_LEGACY) == 0) { - struct ata_ioports *ioaddr = &ap->ioaddr; - - /* FIXME: Add -ac IDE pci mods to remove these special cases */ - if (ioaddr->cmd_addr == ATA_PRIMARY_CMD) - release_region(ATA_PRIMARY_CMD, 8); - else if (ioaddr->cmd_addr == ATA_SECONDARY_CMD) - release_region(ATA_SECONDARY_CMD, 8); - } - - scsi_host_put(ap->host); - } - - if (host_set->ops->host_stop) - host_set->ops->host_stop(host_set); - - kfree(host_set); -} - -/** - * ata_scsi_release - SCSI layer callback hook for host unload - * @host: libata host to be unloaded - * - * Performs all duties necessary to shut down a libata port... - * Kill port kthread, disable port, and release resources. - * - * LOCKING: - * Inherited from SCSI layer. - * - * RETURNS: - * One. - */ - -int ata_scsi_release(struct Scsi_Host *host) -{ - struct ata_port *ap = ata_shost_to_port(host); - - DPRINTK("ENTER\n"); - - ap->ops->port_disable(ap); - ap->ops->port_stop(ap); - - DPRINTK("EXIT\n"); - return 1; -} - -struct ata_probe_ent * -ata_probe_ent_alloc(struct device *dev, const struct ata_port_info *port) -{ - struct ata_probe_ent *probe_ent; - - probe_ent = kzalloc(sizeof(*probe_ent), GFP_KERNEL); - if (!probe_ent) { - printk(KERN_ERR DRV_NAME "(%s): out of memory\n", - kobject_name(&(dev->kobj))); - return NULL; - } - - INIT_LIST_HEAD(&probe_ent->node); - probe_ent->dev = dev; - - probe_ent->sht = port->sht; - probe_ent->host_flags = port->host_flags; - probe_ent->pio_mask = port->pio_mask; - probe_ent->mwdma_mask = port->mwdma_mask; - probe_ent->udma_mask = port->udma_mask; - probe_ent->port_ops = port->port_ops; - - return probe_ent; -} - -/** - * ata_std_ports - initialize ioaddr with standard port offsets. - * @ioaddr: IO address structure to be initialized - * - * Utility function which initializes data_addr, error_addr, - * feature_addr, nsect_addr, lbal_addr, lbam_addr, lbah_addr, - * device_addr, status_addr, and command_addr to standard offsets - * relative to cmd_addr. - * - * Does not set ctl_addr, altstatus_addr, bmdma_addr, or scr_addr. - */ - -void ata_std_ports(struct ata_ioports *ioaddr) -{ - ioaddr->data_addr = ioaddr->cmd_addr + ATA_REG_DATA; - ioaddr->error_addr = ioaddr->cmd_addr + ATA_REG_ERR; - ioaddr->feature_addr = ioaddr->cmd_addr + ATA_REG_FEATURE; - ioaddr->nsect_addr = ioaddr->cmd_addr + ATA_REG_NSECT; - ioaddr->lbal_addr = ioaddr->cmd_addr + ATA_REG_LBAL; - ioaddr->lbam_addr = ioaddr->cmd_addr + ATA_REG_LBAM; - ioaddr->lbah_addr = ioaddr->cmd_addr + ATA_REG_LBAH; - ioaddr->device_addr = ioaddr->cmd_addr + ATA_REG_DEVICE; - ioaddr->status_addr = ioaddr->cmd_addr + ATA_REG_STATUS; - ioaddr->command_addr = ioaddr->cmd_addr + ATA_REG_CMD; -} - - -#ifdef CONFIG_PCI - -void ata_pci_host_stop (struct ata_host_set *host_set) -{ - struct pci_dev *pdev = to_pci_dev(host_set->dev); - - pci_iounmap(pdev, host_set->mmio_base); -} - -/** - * ata_pci_remove_one - PCI layer callback for device removal - * @pdev: PCI device that was removed - * - * PCI layer indicates to libata via this hook that - * hot-unplug or module unload event has occurred. - * Handle this by unregistering all objects associated - * with this PCI device. Free those objects. Then finally - * release PCI resources and disable device. - * - * LOCKING: - * Inherited from PCI layer (may sleep). - */ - -void ata_pci_remove_one (struct pci_dev *pdev) -{ - struct device *dev = pci_dev_to_dev(pdev); - struct ata_host_set *host_set = dev_get_drvdata(dev); - - ata_host_set_remove(host_set); - - pci_release_regions(pdev); - pci_disable_device(pdev); - dev_set_drvdata(dev, NULL); -} - -/* move to PCI subsystem */ -int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits) -{ - unsigned long tmp = 0; - - switch (bits->width) { - case 1: { - u8 tmp8 = 0; - pci_read_config_byte(pdev, bits->reg, &tmp8); - tmp = tmp8; - break; - } - case 2: { - u16 tmp16 = 0; - pci_read_config_word(pdev, bits->reg, &tmp16); - tmp = tmp16; - break; - } - case 4: { - u32 tmp32 = 0; - pci_read_config_dword(pdev, bits->reg, &tmp32); - tmp = tmp32; - break; - } - - default: - return -EINVAL; - } - - tmp &= bits->mask; - - return (tmp == bits->val) ? 1 : 0; -} - -void ata_pci_device_do_suspend(struct pci_dev *pdev, pm_message_t mesg) -{ - pci_save_state(pdev); - - if (mesg.event == PM_EVENT_SUSPEND) { - pci_disable_device(pdev); - pci_set_power_state(pdev, PCI_D3hot); - } -} - -void ata_pci_device_do_resume(struct pci_dev *pdev) -{ - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - pci_enable_device(pdev); - pci_set_master(pdev); -} - -int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) -{ - struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev); - int rc = 0; - - rc = ata_host_set_suspend(host_set, mesg); - if (rc) - return rc; - - ata_pci_device_do_suspend(pdev, mesg); - - return 0; -} - -int ata_pci_device_resume(struct pci_dev *pdev) -{ - struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev); - - ata_pci_device_do_resume(pdev); - ata_host_set_resume(host_set); - return 0; -} -#endif /* CONFIG_PCI */ - - -static int __init ata_init(void) -{ - ata_probe_timeout *= HZ; - ata_wq = create_workqueue("ata"); - if (!ata_wq) - return -ENOMEM; - - ata_aux_wq = create_singlethread_workqueue("ata_aux"); - if (!ata_aux_wq) { - destroy_workqueue(ata_wq); - return -ENOMEM; - } - - printk(KERN_DEBUG "libata version " DRV_VERSION " loaded.\n"); - return 0; -} - -static void __exit ata_exit(void) -{ - destroy_workqueue(ata_wq); - destroy_workqueue(ata_aux_wq); -} - -module_init(ata_init); -module_exit(ata_exit); - -static unsigned long ratelimit_time; -static DEFINE_SPINLOCK(ata_ratelimit_lock); - -int ata_ratelimit(void) -{ - int rc; - unsigned long flags; - - spin_lock_irqsave(&ata_ratelimit_lock, flags); - - if (time_after(jiffies, ratelimit_time)) { - rc = 1; - ratelimit_time = jiffies + (HZ/5); - } else - rc = 0; - - spin_unlock_irqrestore(&ata_ratelimit_lock, flags); - - return rc; -} - -/** - * ata_wait_register - wait until register value changes - * @reg: IO-mapped register - * @mask: Mask to apply to read register value - * @val: Wait condition - * @interval_msec: polling interval in milliseconds - * @timeout_msec: timeout in milliseconds - * - * Waiting for some bits of register to change is a common - * operation for ATA controllers. This function reads 32bit LE - * IO-mapped register @reg and tests for the following condition. - * - * (*@reg & mask) != val - * - * If the condition is met, it returns; otherwise, the process is - * repeated after @interval_msec until timeout. - * - * LOCKING: - * Kernel thread context (may sleep) - * - * RETURNS: - * The final register value. - */ -u32 ata_wait_register(void __iomem *reg, u32 mask, u32 val, - unsigned long interval_msec, - unsigned long timeout_msec) -{ - unsigned long timeout; - u32 tmp; - - tmp = ioread32(reg); - - /* Calculate timeout _after_ the first read to make sure - * preceding writes reach the controller before starting to - * eat away the timeout. - */ - timeout = jiffies + (timeout_msec * HZ) / 1000; - - while ((tmp & mask) == val && time_before(jiffies, timeout)) { - msleep(interval_msec); - tmp = ioread32(reg); - } - - return tmp; -} - -/* - * Dummy port_ops - */ -static void ata_dummy_noret(struct ata_port *ap) { } -static int ata_dummy_ret0(struct ata_port *ap) { return 0; } -static void ata_dummy_qc_noret(struct ata_queued_cmd *qc) { } - -static u8 ata_dummy_check_status(struct ata_port *ap) -{ - return ATA_DRDY; -} - -static unsigned int ata_dummy_qc_issue(struct ata_queued_cmd *qc) -{ - return AC_ERR_SYSTEM; -} - -const struct ata_port_operations ata_dummy_port_ops = { - .port_disable = ata_port_disable, - .check_status = ata_dummy_check_status, - .check_altstatus = ata_dummy_check_status, - .dev_select = ata_noop_dev_select, - .qc_prep = ata_noop_qc_prep, - .qc_issue = ata_dummy_qc_issue, - .freeze = ata_dummy_noret, - .thaw = ata_dummy_noret, - .error_handler = ata_dummy_noret, - .post_internal_cmd = ata_dummy_qc_noret, - .irq_clear = ata_dummy_noret, - .port_start = ata_dummy_ret0, - .port_stop = ata_dummy_noret, -}; - -/* - * libata is essentially a library of internal helper functions for - * low-level ATA host controller drivers. As such, the API/ABI is - * likely to change as new drivers are added and updated. - * Do not depend on ABI/API stability. - */ - -EXPORT_SYMBOL_GPL(sata_deb_timing_normal); -EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug); -EXPORT_SYMBOL_GPL(sata_deb_timing_long); -EXPORT_SYMBOL_GPL(ata_dummy_port_ops); -EXPORT_SYMBOL_GPL(ata_std_bios_param); -EXPORT_SYMBOL_GPL(ata_std_ports); -EXPORT_SYMBOL_GPL(ata_host_set_init); -EXPORT_SYMBOL_GPL(ata_device_add); -EXPORT_SYMBOL_GPL(ata_port_detach); -EXPORT_SYMBOL_GPL(ata_host_set_remove); -EXPORT_SYMBOL_GPL(ata_sg_init); -EXPORT_SYMBOL_GPL(ata_sg_init_one); -EXPORT_SYMBOL_GPL(ata_hsm_move); -EXPORT_SYMBOL_GPL(ata_qc_complete); -EXPORT_SYMBOL_GPL(ata_qc_complete_multiple); -EXPORT_SYMBOL_GPL(ata_qc_issue_prot); -EXPORT_SYMBOL_GPL(ata_tf_load); -EXPORT_SYMBOL_GPL(ata_tf_read); -EXPORT_SYMBOL_GPL(ata_noop_dev_select); -EXPORT_SYMBOL_GPL(ata_std_dev_select); -EXPORT_SYMBOL_GPL(ata_tf_to_fis); -EXPORT_SYMBOL_GPL(ata_tf_from_fis); -EXPORT_SYMBOL_GPL(ata_check_status); -EXPORT_SYMBOL_GPL(ata_altstatus); -EXPORT_SYMBOL_GPL(ata_exec_command); -EXPORT_SYMBOL_GPL(ata_port_start); -EXPORT_SYMBOL_GPL(ata_port_stop); -EXPORT_SYMBOL_GPL(ata_host_stop); -EXPORT_SYMBOL_GPL(ata_interrupt); -EXPORT_SYMBOL_GPL(ata_mmio_data_xfer); -EXPORT_SYMBOL_GPL(ata_pio_data_xfer); -EXPORT_SYMBOL_GPL(ata_pio_data_xfer_noirq); -EXPORT_SYMBOL_GPL(ata_qc_prep); -EXPORT_SYMBOL_GPL(ata_noop_qc_prep); -EXPORT_SYMBOL_GPL(ata_bmdma_setup); -EXPORT_SYMBOL_GPL(ata_bmdma_start); -EXPORT_SYMBOL_GPL(ata_bmdma_irq_clear); -EXPORT_SYMBOL_GPL(ata_bmdma_status); -EXPORT_SYMBOL_GPL(ata_bmdma_stop); -EXPORT_SYMBOL_GPL(ata_bmdma_freeze); -EXPORT_SYMBOL_GPL(ata_bmdma_thaw); -EXPORT_SYMBOL_GPL(ata_bmdma_drive_eh); -EXPORT_SYMBOL_GPL(ata_bmdma_error_handler); -EXPORT_SYMBOL_GPL(ata_bmdma_post_internal_cmd); -EXPORT_SYMBOL_GPL(ata_port_probe); -EXPORT_SYMBOL_GPL(sata_set_spd); -EXPORT_SYMBOL_GPL(sata_phy_debounce); -EXPORT_SYMBOL_GPL(sata_phy_resume); -EXPORT_SYMBOL_GPL(sata_phy_reset); -EXPORT_SYMBOL_GPL(__sata_phy_reset); -EXPORT_SYMBOL_GPL(ata_bus_reset); -EXPORT_SYMBOL_GPL(ata_std_prereset); -EXPORT_SYMBOL_GPL(ata_std_softreset); -EXPORT_SYMBOL_GPL(sata_std_hardreset); -EXPORT_SYMBOL_GPL(ata_std_postreset); -EXPORT_SYMBOL_GPL(ata_dev_revalidate); -EXPORT_SYMBOL_GPL(ata_dev_classify); -EXPORT_SYMBOL_GPL(ata_dev_pair); -EXPORT_SYMBOL_GPL(ata_port_disable); -EXPORT_SYMBOL_GPL(ata_ratelimit); -EXPORT_SYMBOL_GPL(ata_wait_register); -EXPORT_SYMBOL_GPL(ata_busy_sleep); -EXPORT_SYMBOL_GPL(ata_port_queue_task); -EXPORT_SYMBOL_GPL(ata_scsi_ioctl); -EXPORT_SYMBOL_GPL(ata_scsi_queuecmd); -EXPORT_SYMBOL_GPL(ata_scsi_slave_config); -EXPORT_SYMBOL_GPL(ata_scsi_slave_destroy); -EXPORT_SYMBOL_GPL(ata_scsi_change_queue_depth); -EXPORT_SYMBOL_GPL(ata_scsi_release); -EXPORT_SYMBOL_GPL(ata_host_intr); -EXPORT_SYMBOL_GPL(sata_scr_valid); -EXPORT_SYMBOL_GPL(sata_scr_read); -EXPORT_SYMBOL_GPL(sata_scr_write); -EXPORT_SYMBOL_GPL(sata_scr_write_flush); -EXPORT_SYMBOL_GPL(ata_port_online); -EXPORT_SYMBOL_GPL(ata_port_offline); -EXPORT_SYMBOL_GPL(ata_host_set_suspend); -EXPORT_SYMBOL_GPL(ata_host_set_resume); -EXPORT_SYMBOL_GPL(ata_id_string); -EXPORT_SYMBOL_GPL(ata_id_c_string); -EXPORT_SYMBOL_GPL(ata_scsi_simulate); - -EXPORT_SYMBOL_GPL(ata_pio_need_iordy); -EXPORT_SYMBOL_GPL(ata_timing_compute); -EXPORT_SYMBOL_GPL(ata_timing_merge); - -#ifdef CONFIG_PCI -EXPORT_SYMBOL_GPL(pci_test_config_bits); -EXPORT_SYMBOL_GPL(ata_pci_host_stop); -EXPORT_SYMBOL_GPL(ata_pci_init_native_mode); -EXPORT_SYMBOL_GPL(ata_pci_init_one); -EXPORT_SYMBOL_GPL(ata_pci_remove_one); -EXPORT_SYMBOL_GPL(ata_pci_device_do_suspend); -EXPORT_SYMBOL_GPL(ata_pci_device_do_resume); -EXPORT_SYMBOL_GPL(ata_pci_device_suspend); -EXPORT_SYMBOL_GPL(ata_pci_device_resume); -EXPORT_SYMBOL_GPL(ata_pci_default_filter); -EXPORT_SYMBOL_GPL(ata_pci_clear_simplex); -#endif /* CONFIG_PCI */ - -EXPORT_SYMBOL_GPL(ata_scsi_device_suspend); -EXPORT_SYMBOL_GPL(ata_scsi_device_resume); - -EXPORT_SYMBOL_GPL(ata_eng_timeout); -EXPORT_SYMBOL_GPL(ata_port_schedule_eh); -EXPORT_SYMBOL_GPL(ata_port_abort); -EXPORT_SYMBOL_GPL(ata_port_freeze); -EXPORT_SYMBOL_GPL(ata_eh_freeze_port); -EXPORT_SYMBOL_GPL(ata_eh_thaw_port); -EXPORT_SYMBOL_GPL(ata_eh_qc_complete); -EXPORT_SYMBOL_GPL(ata_eh_qc_retry); -EXPORT_SYMBOL_GPL(ata_do_eh); diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c deleted file mode 100644 index 29f59345305..00000000000 --- a/drivers/scsi/libata-eh.c +++ /dev/null @@ -1,2246 +0,0 @@ -/* - * libata-eh.c - libata error handling - * - * Maintained by: Jeff Garzik - * Please ALWAYS copy linux-ide@vger.kernel.org - * on emails. - * - * Copyright 2006 Tejun Heo - * - * - * 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 02139, - * USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - * Hardware documentation available from http://www.t13.org/ and - * http://www.sata-io.org/ - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include "scsi_transport_api.h" - -#include - -#include "libata.h" - -static void __ata_port_freeze(struct ata_port *ap); -static void ata_eh_finish(struct ata_port *ap); -static void ata_eh_handle_port_suspend(struct ata_port *ap); -static void ata_eh_handle_port_resume(struct ata_port *ap); - -static void ata_ering_record(struct ata_ering *ering, int is_io, - unsigned int err_mask) -{ - struct ata_ering_entry *ent; - - WARN_ON(!err_mask); - - ering->cursor++; - ering->cursor %= ATA_ERING_SIZE; - - ent = &ering->ring[ering->cursor]; - ent->is_io = is_io; - ent->err_mask = err_mask; - ent->timestamp = get_jiffies_64(); -} - -static struct ata_ering_entry * ata_ering_top(struct ata_ering *ering) -{ - struct ata_ering_entry *ent = &ering->ring[ering->cursor]; - if (!ent->err_mask) - return NULL; - return ent; -} - -static int ata_ering_map(struct ata_ering *ering, - int (*map_fn)(struct ata_ering_entry *, void *), - void *arg) -{ - int idx, rc = 0; - struct ata_ering_entry *ent; - - idx = ering->cursor; - do { - ent = &ering->ring[idx]; - if (!ent->err_mask) - break; - rc = map_fn(ent, arg); - if (rc) - break; - idx = (idx - 1 + ATA_ERING_SIZE) % ATA_ERING_SIZE; - } while (idx != ering->cursor); - - return rc; -} - -static unsigned int ata_eh_dev_action(struct ata_device *dev) -{ - struct ata_eh_context *ehc = &dev->ap->eh_context; - - return ehc->i.action | ehc->i.dev_action[dev->devno]; -} - -static void ata_eh_clear_action(struct ata_device *dev, - struct ata_eh_info *ehi, unsigned int action) -{ - int i; - - if (!dev) { - ehi->action &= ~action; - for (i = 0; i < ATA_MAX_DEVICES; i++) - ehi->dev_action[i] &= ~action; - } else { - /* doesn't make sense for port-wide EH actions */ - WARN_ON(!(action & ATA_EH_PERDEV_MASK)); - - /* break ehi->action into ehi->dev_action */ - if (ehi->action & action) { - for (i = 0; i < ATA_MAX_DEVICES; i++) - ehi->dev_action[i] |= ehi->action & action; - ehi->action &= ~action; - } - - /* turn off the specified per-dev action */ - ehi->dev_action[dev->devno] &= ~action; - } -} - -/** - * ata_scsi_timed_out - SCSI layer time out callback - * @cmd: timed out SCSI command - * - * Handles SCSI layer timeout. We race with normal completion of - * the qc for @cmd. If the qc is already gone, we lose and let - * the scsi command finish (EH_HANDLED). Otherwise, the qc has - * timed out and EH should be invoked. Prevent ata_qc_complete() - * from finishing it by setting EH_SCHEDULED and return - * EH_NOT_HANDLED. - * - * TODO: kill this function once old EH is gone. - * - * LOCKING: - * Called from timer context - * - * RETURNS: - * EH_HANDLED or EH_NOT_HANDLED - */ -enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd) -{ - struct Scsi_Host *host = cmd->device->host; - struct ata_port *ap = ata_shost_to_port(host); - unsigned long flags; - struct ata_queued_cmd *qc; - enum scsi_eh_timer_return ret; - - DPRINTK("ENTER\n"); - - if (ap->ops->error_handler) { - ret = EH_NOT_HANDLED; - goto out; - } - - ret = EH_HANDLED; - spin_lock_irqsave(ap->lock, flags); - qc = ata_qc_from_tag(ap, ap->active_tag); - if (qc) { - WARN_ON(qc->scsicmd != cmd); - qc->flags |= ATA_QCFLAG_EH_SCHEDULED; - qc->err_mask |= AC_ERR_TIMEOUT; - ret = EH_NOT_HANDLED; - } - spin_unlock_irqrestore(ap->lock, flags); - - out: - DPRINTK("EXIT, ret=%d\n", ret); - return ret; -} - -/** - * ata_scsi_error - SCSI layer error handler callback - * @host: SCSI host on which error occurred - * - * Handles SCSI-layer-thrown error events. - * - * LOCKING: - * Inherited from SCSI layer (none, can sleep) - * - * RETURNS: - * Zero. - */ -void ata_scsi_error(struct Scsi_Host *host) -{ - struct ata_port *ap = ata_shost_to_port(host); - int i, repeat_cnt = ATA_EH_MAX_REPEAT; - unsigned long flags; - - DPRINTK("ENTER\n"); - - /* synchronize with port task */ - ata_port_flush_task(ap); - - /* synchronize with host_set lock and sort out timeouts */ - - /* For new EH, all qcs are finished in one of three ways - - * normal completion, error completion, and SCSI timeout. - * Both cmpletions can race against SCSI timeout. When normal - * completion wins, the qc never reaches EH. When error - * completion wins, the qc has ATA_QCFLAG_FAILED set. - * - * When SCSI timeout wins, things are a bit more complex. - * Normal or error completion can occur after the timeout but - * before this point. In such cases, both types of - * completions are honored. A scmd is determined to have - * timed out iff its associated qc is active and not failed. - */ - if (ap->ops->error_handler) { - struct scsi_cmnd *scmd, *tmp; - int nr_timedout = 0; - - spin_lock_irqsave(ap->lock, flags); - - list_for_each_entry_safe(scmd, tmp, &host->eh_cmd_q, eh_entry) { - struct ata_queued_cmd *qc; - - for (i = 0; i < ATA_MAX_QUEUE; i++) { - qc = __ata_qc_from_tag(ap, i); - if (qc->flags & ATA_QCFLAG_ACTIVE && - qc->scsicmd == scmd) - break; - } - - if (i < ATA_MAX_QUEUE) { - /* the scmd has an associated qc */ - if (!(qc->flags & ATA_QCFLAG_FAILED)) { - /* which hasn't failed yet, timeout */ - qc->err_mask |= AC_ERR_TIMEOUT; - qc->flags |= ATA_QCFLAG_FAILED; - nr_timedout++; - } - } else { - /* Normal completion occurred after - * SCSI timeout but before this point. - * Successfully complete it. - */ - scmd->retries = scmd->allowed; - scsi_eh_finish_cmd(scmd, &ap->eh_done_q); - } - } - - /* If we have timed out qcs. They belong to EH from - * this point but the state of the controller is - * unknown. Freeze the port to make sure the IRQ - * handler doesn't diddle with those qcs. This must - * be done atomically w.r.t. setting QCFLAG_FAILED. - */ - if (nr_timedout) - __ata_port_freeze(ap); - - spin_unlock_irqrestore(ap->lock, flags); - } else - spin_unlock_wait(ap->lock); - - repeat: - /* invoke error handler */ - if (ap->ops->error_handler) { - /* process port resume request */ - ata_eh_handle_port_resume(ap); - - /* fetch & clear EH info */ - spin_lock_irqsave(ap->lock, flags); - - memset(&ap->eh_context, 0, sizeof(ap->eh_context)); - ap->eh_context.i = ap->eh_info; - memset(&ap->eh_info, 0, sizeof(ap->eh_info)); - - ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS; - ap->pflags &= ~ATA_PFLAG_EH_PENDING; - - spin_unlock_irqrestore(ap->lock, flags); - - /* invoke EH, skip if unloading or suspended */ - if (!(ap->pflags & (ATA_PFLAG_UNLOADING | ATA_PFLAG_SUSPENDED))) - ap->ops->error_handler(ap); - else - ata_eh_finish(ap); - - /* process port suspend request */ - ata_eh_handle_port_suspend(ap); - - /* Exception might have happend after ->error_handler - * recovered the port but before this point. Repeat - * EH in such case. - */ - spin_lock_irqsave(ap->lock, flags); - - if (ap->pflags & ATA_PFLAG_EH_PENDING) { - if (--repeat_cnt) { - ata_port_printk(ap, KERN_INFO, - "EH pending after completion, " - "repeating EH (cnt=%d)\n", repeat_cnt); - spin_unlock_irqrestore(ap->lock, flags); - goto repeat; - } - ata_port_printk(ap, KERN_ERR, "EH pending after %d " - "tries, giving up\n", ATA_EH_MAX_REPEAT); - } - - /* this run is complete, make sure EH info is clear */ - memset(&ap->eh_info, 0, sizeof(ap->eh_info)); - - /* Clear host_eh_scheduled while holding ap->lock such - * that if exception occurs after this point but - * before EH completion, SCSI midlayer will - * re-initiate EH. - */ - host->host_eh_scheduled = 0; - - spin_unlock_irqrestore(ap->lock, flags); - } else { - WARN_ON(ata_qc_from_tag(ap, ap->active_tag) == NULL); - ap->ops->eng_timeout(ap); - } - - /* finish or retry handled scmd's and clean up */ - WARN_ON(host->host_failed || !list_empty(&host->eh_cmd_q)); - - scsi_eh_flush_done_q(&ap->eh_done_q); - - /* clean up */ - spin_lock_irqsave(ap->lock, flags); - - if (ap->pflags & ATA_PFLAG_LOADING) - ap->pflags &= ~ATA_PFLAG_LOADING; - else if (ap->pflags & ATA_PFLAG_SCSI_HOTPLUG) - queue_work(ata_aux_wq, &ap->hotplug_task); - - if (ap->pflags & ATA_PFLAG_RECOVERED) - ata_port_printk(ap, KERN_INFO, "EH complete\n"); - - ap->pflags &= ~(ATA_PFLAG_SCSI_HOTPLUG | ATA_PFLAG_RECOVERED); - - /* tell wait_eh that we're done */ - ap->pflags &= ~ATA_PFLAG_EH_IN_PROGRESS; - wake_up_all(&ap->eh_wait_q); - - spin_unlock_irqrestore(ap->lock, flags); - - DPRINTK("EXIT\n"); -} - -/** - * ata_port_wait_eh - Wait for the currently pending EH to complete - * @ap: Port to wait EH for - * - * Wait until the currently pending EH is complete. - * - * LOCKING: - * Kernel thread context (may sleep). - */ -void ata_port_wait_eh(struct ata_port *ap) -{ - unsigned long flags; - DEFINE_WAIT(wait); - - retry: - spin_lock_irqsave(ap->lock, flags); - - while (ap->pflags & (ATA_PFLAG_EH_PENDING | ATA_PFLAG_EH_IN_PROGRESS)) { - prepare_to_wait(&ap->eh_wait_q, &wait, TASK_UNINTERRUPTIBLE); - spin_unlock_irqrestore(ap->lock, flags); - schedule(); - spin_lock_irqsave(ap->lock, flags); - } - finish_wait(&ap->eh_wait_q, &wait); - - spin_unlock_irqrestore(ap->lock, flags); - - /* make sure SCSI EH is complete */ - if (scsi_host_in_recovery(ap->host)) { - msleep(10); - goto retry; - } -} - -/** - * ata_qc_timeout - Handle timeout of queued command - * @qc: Command that timed out - * - * Some part of the kernel (currently, only the SCSI layer) - * has noticed that the active command on port @ap has not - * completed after a specified length of time. Handle this - * condition by disabling DMA (if necessary) and completing - * transactions, with error if necessary. - * - * This also handles the case of the "lost interrupt", where - * for some reason (possibly hardware bug, possibly driver bug) - * an interrupt was not delivered to the driver, even though the - * transaction completed successfully. - * - * TODO: kill this function once old EH is gone. - * - * LOCKING: - * Inherited from SCSI layer (none, can sleep) - */ -static void ata_qc_timeout(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - u8 host_stat = 0, drv_stat; - unsigned long flags; - - DPRINTK("ENTER\n"); - - ap->hsm_task_state = HSM_ST_IDLE; - - spin_lock_irqsave(ap->lock, flags); - - switch (qc->tf.protocol) { - - case ATA_PROT_DMA: - case ATA_PROT_ATAPI_DMA: - host_stat = ap->ops->bmdma_status(ap); - - /* before we do anything else, clear DMA-Start bit */ - ap->ops->bmdma_stop(qc); - - /* fall through */ - - default: - ata_altstatus(ap); - drv_stat = ata_chk_status(ap); - - /* ack bmdma irq events */ - ap->ops->irq_clear(ap); - - ata_dev_printk(qc->dev, KERN_ERR, "command 0x%x timeout, " - "stat 0x%x host_stat 0x%x\n", - qc->tf.command, drv_stat, host_stat); - - /* complete taskfile transaction */ - qc->err_mask |= AC_ERR_TIMEOUT; - break; - } - - spin_unlock_irqrestore(ap->lock, flags); - - ata_eh_qc_complete(qc); - - DPRINTK("EXIT\n"); -} - -/** - * ata_eng_timeout - Handle timeout of queued command - * @ap: Port on which timed-out command is active - * - * Some part of the kernel (currently, only the SCSI layer) - * has noticed that the active command on port @ap has not - * completed after a specified length of time. Handle this - * condition by disabling DMA (if necessary) and completing - * transactions, with error if necessary. - * - * This also handles the case of the "lost interrupt", where - * for some reason (possibly hardware bug, possibly driver bug) - * an interrupt was not delivered to the driver, even though the - * transaction completed successfully. - * - * TODO: kill this function once old EH is gone. - * - * LOCKING: - * Inherited from SCSI layer (none, can sleep) - */ -void ata_eng_timeout(struct ata_port *ap) -{ - DPRINTK("ENTER\n"); - - ata_qc_timeout(ata_qc_from_tag(ap, ap->active_tag)); - - DPRINTK("EXIT\n"); -} - -/** - * ata_qc_schedule_eh - schedule qc for error handling - * @qc: command to schedule error handling for - * - * Schedule error handling for @qc. EH will kick in as soon as - * other commands are drained. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ -void ata_qc_schedule_eh(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - - WARN_ON(!ap->ops->error_handler); - - qc->flags |= ATA_QCFLAG_FAILED; - qc->ap->pflags |= ATA_PFLAG_EH_PENDING; - - /* The following will fail if timeout has already expired. - * ata_scsi_error() takes care of such scmds on EH entry. - * Note that ATA_QCFLAG_FAILED is unconditionally set after - * this function completes. - */ - scsi_req_abort_cmd(qc->scsicmd); -} - -/** - * ata_port_schedule_eh - schedule error handling without a qc - * @ap: ATA port to schedule EH for - * - * Schedule error handling for @ap. EH will kick in as soon as - * all commands are drained. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ -void ata_port_schedule_eh(struct ata_port *ap) -{ - WARN_ON(!ap->ops->error_handler); - - ap->pflags |= ATA_PFLAG_EH_PENDING; - scsi_schedule_eh(ap->host); - - DPRINTK("port EH scheduled\n"); -} - -/** - * ata_port_abort - abort all qc's on the port - * @ap: ATA port to abort qc's for - * - * Abort all active qc's of @ap and schedule EH. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * Number of aborted qc's. - */ -int ata_port_abort(struct ata_port *ap) -{ - int tag, nr_aborted = 0; - - WARN_ON(!ap->ops->error_handler); - - for (tag = 0; tag < ATA_MAX_QUEUE; tag++) { - struct ata_queued_cmd *qc = ata_qc_from_tag(ap, tag); - - if (qc) { - qc->flags |= ATA_QCFLAG_FAILED; - ata_qc_complete(qc); - nr_aborted++; - } - } - - if (!nr_aborted) - ata_port_schedule_eh(ap); - - return nr_aborted; -} - -/** - * __ata_port_freeze - freeze port - * @ap: ATA port to freeze - * - * This function is called when HSM violation or some other - * condition disrupts normal operation of the port. Frozen port - * is not allowed to perform any operation until the port is - * thawed, which usually follows a successful reset. - * - * ap->ops->freeze() callback can be used for freezing the port - * hardware-wise (e.g. mask interrupt and stop DMA engine). If a - * port cannot be frozen hardware-wise, the interrupt handler - * must ack and clear interrupts unconditionally while the port - * is frozen. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ -static void __ata_port_freeze(struct ata_port *ap) -{ - WARN_ON(!ap->ops->error_handler); - - if (ap->ops->freeze) - ap->ops->freeze(ap); - - ap->pflags |= ATA_PFLAG_FROZEN; - - DPRINTK("ata%u port frozen\n", ap->id); -} - -/** - * ata_port_freeze - abort & freeze port - * @ap: ATA port to freeze - * - * Abort and freeze @ap. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * Number of aborted commands. - */ -int ata_port_freeze(struct ata_port *ap) -{ - int nr_aborted; - - WARN_ON(!ap->ops->error_handler); - - nr_aborted = ata_port_abort(ap); - __ata_port_freeze(ap); - - return nr_aborted; -} - -/** - * ata_eh_freeze_port - EH helper to freeze port - * @ap: ATA port to freeze - * - * Freeze @ap. - * - * LOCKING: - * None. - */ -void ata_eh_freeze_port(struct ata_port *ap) -{ - unsigned long flags; - - if (!ap->ops->error_handler) - return; - - spin_lock_irqsave(ap->lock, flags); - __ata_port_freeze(ap); - spin_unlock_irqrestore(ap->lock, flags); -} - -/** - * ata_port_thaw_port - EH helper to thaw port - * @ap: ATA port to thaw - * - * Thaw frozen port @ap. - * - * LOCKING: - * None. - */ -void ata_eh_thaw_port(struct ata_port *ap) -{ - unsigned long flags; - - if (!ap->ops->error_handler) - return; - - spin_lock_irqsave(ap->lock, flags); - - ap->pflags &= ~ATA_PFLAG_FROZEN; - - if (ap->ops->thaw) - ap->ops->thaw(ap); - - spin_unlock_irqrestore(ap->lock, flags); - - DPRINTK("ata%u port thawed\n", ap->id); -} - -static void ata_eh_scsidone(struct scsi_cmnd *scmd) -{ - /* nada */ -} - -static void __ata_eh_qc_complete(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct scsi_cmnd *scmd = qc->scsicmd; - unsigned long flags; - - spin_lock_irqsave(ap->lock, flags); - qc->scsidone = ata_eh_scsidone; - __ata_qc_complete(qc); - WARN_ON(ata_tag_valid(qc->tag)); - spin_unlock_irqrestore(ap->lock, flags); - - scsi_eh_finish_cmd(scmd, &ap->eh_done_q); -} - -/** - * ata_eh_qc_complete - Complete an active ATA command from EH - * @qc: Command to complete - * - * Indicate to the mid and upper layers that an ATA command has - * completed. To be used from EH. - */ -void ata_eh_qc_complete(struct ata_queued_cmd *qc) -{ - struct scsi_cmnd *scmd = qc->scsicmd; - scmd->retries = scmd->allowed; - __ata_eh_qc_complete(qc); -} - -/** - * ata_eh_qc_retry - Tell midlayer to retry an ATA command after EH - * @qc: Command to retry - * - * Indicate to the mid and upper layers that an ATA command - * should be retried. To be used from EH. - * - * SCSI midlayer limits the number of retries to scmd->allowed. - * scmd->retries is decremented for commands which get retried - * due to unrelated failures (qc->err_mask is zero). - */ -void ata_eh_qc_retry(struct ata_queued_cmd *qc) -{ - struct scsi_cmnd *scmd = qc->scsicmd; - if (!qc->err_mask && scmd->retries) - scmd->retries--; - __ata_eh_qc_complete(qc); -} - -/** - * ata_eh_detach_dev - detach ATA device - * @dev: ATA device to detach - * - * Detach @dev. - * - * LOCKING: - * None. - */ -static void ata_eh_detach_dev(struct ata_device *dev) -{ - struct ata_port *ap = dev->ap; - unsigned long flags; - - ata_dev_disable(dev); - - spin_lock_irqsave(ap->lock, flags); - - dev->flags &= ~ATA_DFLAG_DETACH; - - if (ata_scsi_offline_dev(dev)) { - dev->flags |= ATA_DFLAG_DETACHED; - ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG; - } - - /* clear per-dev EH actions */ - ata_eh_clear_action(dev, &ap->eh_info, ATA_EH_PERDEV_MASK); - ata_eh_clear_action(dev, &ap->eh_context.i, ATA_EH_PERDEV_MASK); - - spin_unlock_irqrestore(ap->lock, flags); -} - -/** - * ata_eh_about_to_do - about to perform eh_action - * @ap: target ATA port - * @dev: target ATA dev for per-dev action (can be NULL) - * @action: action about to be performed - * - * Called just before performing EH actions to clear related bits - * in @ap->eh_info such that eh actions are not unnecessarily - * repeated. - * - * LOCKING: - * None. - */ -static void ata_eh_about_to_do(struct ata_port *ap, struct ata_device *dev, - unsigned int action) -{ - unsigned long flags; - struct ata_eh_info *ehi = &ap->eh_info; - struct ata_eh_context *ehc = &ap->eh_context; - - spin_lock_irqsave(ap->lock, flags); - - /* Reset is represented by combination of actions and EHI - * flags. Suck in all related bits before clearing eh_info to - * avoid losing requested action. - */ - if (action & ATA_EH_RESET_MASK) { - ehc->i.action |= ehi->action & ATA_EH_RESET_MASK; - ehc->i.flags |= ehi->flags & ATA_EHI_RESET_MODIFIER_MASK; - - /* make sure all reset actions are cleared & clear EHI flags */ - action |= ATA_EH_RESET_MASK; - ehi->flags &= ~ATA_EHI_RESET_MODIFIER_MASK; - } - - ata_eh_clear_action(dev, ehi, action); - - if (!(ehc->i.flags & ATA_EHI_QUIET)) - ap->pflags |= ATA_PFLAG_RECOVERED; - - spin_unlock_irqrestore(ap->lock, flags); -} - -/** - * ata_eh_done - EH action complete - * @ap: target ATA port - * @dev: target ATA dev for per-dev action (can be NULL) - * @action: action just completed - * - * Called right after performing EH actions to clear related bits - * in @ap->eh_context. - * - * LOCKING: - * None. - */ -static void ata_eh_done(struct ata_port *ap, struct ata_device *dev, - unsigned int action) -{ - /* if reset is complete, clear all reset actions & reset modifier */ - if (action & ATA_EH_RESET_MASK) { - action |= ATA_EH_RESET_MASK; - ap->eh_context.i.flags &= ~ATA_EHI_RESET_MODIFIER_MASK; - } - - ata_eh_clear_action(dev, &ap->eh_context.i, action); -} - -/** - * ata_err_string - convert err_mask to descriptive string - * @err_mask: error mask to convert to string - * - * Convert @err_mask to descriptive string. Errors are - * prioritized according to severity and only the most severe - * error is reported. - * - * LOCKING: - * None. - * - * RETURNS: - * Descriptive string for @err_mask - */ -static const char * ata_err_string(unsigned int err_mask) -{ - if (err_mask & AC_ERR_HOST_BUS) - return "host bus error"; - if (err_mask & AC_ERR_ATA_BUS) - return "ATA bus error"; - if (err_mask & AC_ERR_TIMEOUT) - return "timeout"; - if (err_mask & AC_ERR_HSM) - return "HSM violation"; - if (err_mask & AC_ERR_SYSTEM) - return "internal error"; - if (err_mask & AC_ERR_MEDIA) - return "media error"; - if (err_mask & AC_ERR_INVALID) - return "invalid argument"; - if (err_mask & AC_ERR_DEV) - return "device error"; - return "unknown error"; -} - -/** - * ata_read_log_page - read a specific log page - * @dev: target device - * @page: page to read - * @buf: buffer to store read page - * @sectors: number of sectors to read - * - * Read log page using READ_LOG_EXT command. - * - * LOCKING: - * Kernel thread context (may sleep). - * - * RETURNS: - * 0 on success, AC_ERR_* mask otherwise. - */ -static unsigned int ata_read_log_page(struct ata_device *dev, - u8 page, void *buf, unsigned int sectors) -{ - struct ata_taskfile tf; - unsigned int err_mask; - - DPRINTK("read log page - page %d\n", page); - - ata_tf_init(dev, &tf); - tf.command = ATA_CMD_READ_LOG_EXT; - tf.lbal = page; - tf.nsect = sectors; - tf.hob_nsect = sectors >> 8; - tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_LBA48 | ATA_TFLAG_DEVICE; - tf.protocol = ATA_PROT_PIO; - - err_mask = ata_exec_internal(dev, &tf, NULL, DMA_FROM_DEVICE, - buf, sectors * ATA_SECT_SIZE); - - DPRINTK("EXIT, err_mask=%x\n", err_mask); - return err_mask; -} - -/** - * ata_eh_read_log_10h - Read log page 10h for NCQ error details - * @dev: Device to read log page 10h from - * @tag: Resulting tag of the failed command - * @tf: Resulting taskfile registers of the failed command - * - * Read log page 10h to obtain NCQ error details and clear error - * condition. - * - * LOCKING: - * Kernel thread context (may sleep). - * - * RETURNS: - * 0 on success, -errno otherwise. - */ -static int ata_eh_read_log_10h(struct ata_device *dev, - int *tag, struct ata_taskfile *tf) -{ - u8 *buf = dev->ap->sector_buf; - unsigned int err_mask; - u8 csum; - int i; - - err_mask = ata_read_log_page(dev, ATA_LOG_SATA_NCQ, buf, 1); - if (err_mask) - return -EIO; - - csum = 0; - for (i = 0; i < ATA_SECT_SIZE; i++) - csum += buf[i]; - if (csum) - ata_dev_printk(dev, KERN_WARNING, - "invalid checksum 0x%x on log page 10h\n", csum); - - if (buf[0] & 0x80) - return -ENOENT; - - *tag = buf[0] & 0x1f; - - tf->command = buf[2]; - tf->feature = buf[3]; - tf->lbal = buf[4]; - tf->lbam = buf[5]; - tf->lbah = buf[6]; - tf->device = buf[7]; - tf->hob_lbal = buf[8]; - tf->hob_lbam = buf[9]; - tf->hob_lbah = buf[10]; - tf->nsect = buf[12]; - tf->hob_nsect = buf[13]; - - return 0; -} - -/** - * atapi_eh_request_sense - perform ATAPI REQUEST_SENSE - * @dev: device to perform REQUEST_SENSE to - * @sense_buf: result sense data buffer (SCSI_SENSE_BUFFERSIZE bytes long) - * - * Perform ATAPI REQUEST_SENSE after the device reported CHECK - * SENSE. This function is EH helper. - * - * LOCKING: - * Kernel thread context (may sleep). - * - * RETURNS: - * 0 on success, AC_ERR_* mask on failure - */ -static unsigned int atapi_eh_request_sense(struct ata_device *dev, - unsigned char *sense_buf) -{ - struct ata_port *ap = dev->ap; - struct ata_taskfile tf; - u8 cdb[ATAPI_CDB_LEN]; - - DPRINTK("ATAPI request sense\n"); - - ata_tf_init(dev, &tf); - - /* FIXME: is this needed? */ - memset(sense_buf, 0, SCSI_SENSE_BUFFERSIZE); - - /* XXX: why tf_read here? */ - ap->ops->tf_read(ap, &tf); - - /* fill these in, for the case where they are -not- overwritten */ - sense_buf[0] = 0x70; - sense_buf[2] = tf.feature >> 4; - - memset(cdb, 0, ATAPI_CDB_LEN); - cdb[0] = REQUEST_SENSE; - cdb[4] = SCSI_SENSE_BUFFERSIZE; - - tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; - tf.command = ATA_CMD_PACKET; - - /* is it pointless to prefer PIO for "safety reasons"? */ - if (ap->flags & ATA_FLAG_PIO_DMA) { - tf.protocol = ATA_PROT_ATAPI_DMA; - tf.feature |= ATAPI_PKT_DMA; - } else { - tf.protocol = ATA_PROT_ATAPI; - tf.lbam = (8 * 1024) & 0xff; - tf.lbah = (8 * 1024) >> 8; - } - - return ata_exec_internal(dev, &tf, cdb, DMA_FROM_DEVICE, - sense_buf, SCSI_SENSE_BUFFERSIZE); -} - -/** - * ata_eh_analyze_serror - analyze SError for a failed port - * @ap: ATA port to analyze SError for - * - * Analyze SError if available and further determine cause of - * failure. - * - * LOCKING: - * None. - */ -static void ata_eh_analyze_serror(struct ata_port *ap) -{ - struct ata_eh_context *ehc = &ap->eh_context; - u32 serror = ehc->i.serror; - unsigned int err_mask = 0, action = 0; - - if (serror & SERR_PERSISTENT) { - err_mask |= AC_ERR_ATA_BUS; - action |= ATA_EH_HARDRESET; - } - if (serror & - (SERR_DATA_RECOVERED | SERR_COMM_RECOVERED | SERR_DATA)) { - err_mask |= AC_ERR_ATA_BUS; - action |= ATA_EH_SOFTRESET; - } - if (serror & SERR_PROTOCOL) { - err_mask |= AC_ERR_HSM; - action |= ATA_EH_SOFTRESET; - } - if (serror & SERR_INTERNAL) { - err_mask |= AC_ERR_SYSTEM; - action |= ATA_EH_SOFTRESET; - } - if (serror & (SERR_PHYRDY_CHG | SERR_DEV_XCHG)) - ata_ehi_hotplugged(&ehc->i); - - ehc->i.err_mask |= err_mask; - ehc->i.action |= action; -} - -/** - * ata_eh_analyze_ncq_error - analyze NCQ error - * @ap: ATA port to analyze NCQ error for - * - * Read log page 10h, determine the offending qc and acquire - * error status TF. For NCQ device errors, all LLDDs have to do - * is setting AC_ERR_DEV in ehi->err_mask. This function takes - * care of the rest. - * - * LOCKING: - * Kernel thread context (may sleep). - */ -static void ata_eh_analyze_ncq_error(struct ata_port *ap) -{ - struct ata_eh_context *ehc = &ap->eh_context; - struct ata_device *dev = ap->device; - struct ata_queued_cmd *qc; - struct ata_taskfile tf; - int tag, rc; - - /* if frozen, we can't do much */ - if (ap->pflags & ATA_PFLAG_FROZEN) - return; - - /* is it NCQ device error? */ - if (!ap->sactive || !(ehc->i.err_mask & AC_ERR_DEV)) - return; - - /* has LLDD analyzed already? */ - for (tag = 0; tag < ATA_MAX_QUEUE; tag++) { - qc = __ata_qc_from_tag(ap, tag); - - if (!(qc->flags & ATA_QCFLAG_FAILED)) - continue; - - if (qc->err_mask) - return; - } - - /* okay, this error is ours */ - rc = ata_eh_read_log_10h(dev, &tag, &tf); - if (rc) { - ata_port_printk(ap, KERN_ERR, "failed to read log page 10h " - "(errno=%d)\n", rc); - return; - } - - if (!(ap->sactive & (1 << tag))) { - ata_port_printk(ap, KERN_ERR, "log page 10h reported " - "inactive tag %d\n", tag); - return; - } - - /* we've got the perpetrator, condemn it */ - qc = __ata_qc_from_tag(ap, tag); - memcpy(&qc->result_tf, &tf, sizeof(tf)); - qc->err_mask |= AC_ERR_DEV; - ehc->i.err_mask &= ~AC_ERR_DEV; -} - -/** - * ata_eh_analyze_tf - analyze taskfile of a failed qc - * @qc: qc to analyze - * @tf: Taskfile registers to analyze - * - * Analyze taskfile of @qc and further determine cause of - * failure. This function also requests ATAPI sense data if - * avaliable. - * - * LOCKING: - * Kernel thread context (may sleep). - * - * RETURNS: - * Determined recovery action - */ -static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc, - const struct ata_taskfile *tf) -{ - unsigned int tmp, action = 0; - u8 stat = tf->command, err = tf->feature; - - if ((stat & (ATA_BUSY | ATA_DRQ | ATA_DRDY)) != ATA_DRDY) { - qc->err_mask |= AC_ERR_HSM; - return ATA_EH_SOFTRESET; - } - - if (!(qc->err_mask & AC_ERR_DEV)) - return 0; - - switch (qc->dev->class) { - case ATA_DEV_ATA: - if (err & ATA_ICRC) - qc->err_mask |= AC_ERR_ATA_BUS; - if (err & ATA_UNC) - qc->err_mask |= AC_ERR_MEDIA; - if (err & ATA_IDNF) - qc->err_mask |= AC_ERR_INVALID; - break; - - case ATA_DEV_ATAPI: - tmp = atapi_eh_request_sense(qc->dev, - qc->scsicmd->sense_buffer); - if (!tmp) { - /* ATA_QCFLAG_SENSE_VALID is used to tell - * atapi_qc_complete() that sense data is - * already valid. - * - * TODO: interpret sense data and set - * appropriate err_mask. - */ - qc->flags |= ATA_QCFLAG_SENSE_VALID; - } else - qc->err_mask |= tmp; - } - - if (qc->err_mask & (AC_ERR_HSM | AC_ERR_TIMEOUT | AC_ERR_ATA_BUS)) - action |= ATA_EH_SOFTRESET; - - return action; -} - -static int ata_eh_categorize_ering_entry(struct ata_ering_entry *ent) -{ - if (ent->err_mask & (AC_ERR_ATA_BUS | AC_ERR_TIMEOUT)) - return 1; - - if (ent->is_io) { - if (ent->err_mask & AC_ERR_HSM) - return 1; - if ((ent->err_mask & - (AC_ERR_DEV|AC_ERR_MEDIA|AC_ERR_INVALID)) == AC_ERR_DEV) - return 2; - } - - return 0; -} - -struct speed_down_needed_arg { - u64 since; - int nr_errors[3]; -}; - -static int speed_down_needed_cb(struct ata_ering_entry *ent, void *void_arg) -{ - struct speed_down_needed_arg *arg = void_arg; - - if (ent->timestamp < arg->since) - return -1; - - arg->nr_errors[ata_eh_categorize_ering_entry(ent)]++; - return 0; -} - -/** - * ata_eh_speed_down_needed - Determine wheter speed down is necessary - * @dev: Device of interest - * - * This function examines error ring of @dev and determines - * whether speed down is necessary. Speed down is necessary if - * there have been more than 3 of Cat-1 errors or 10 of Cat-2 - * errors during last 15 minutes. - * - * Cat-1 errors are ATA_BUS, TIMEOUT for any command and HSM - * violation for known supported commands. - * - * Cat-2 errors are unclassified DEV error for known supported - * command. - * - * LOCKING: - * Inherited from caller. - * - * RETURNS: - * 1 if speed down is necessary, 0 otherwise - */ -static int ata_eh_speed_down_needed(struct ata_device *dev) -{ - const u64 interval = 15LLU * 60 * HZ; - static const int err_limits[3] = { -1, 3, 10 }; - struct speed_down_needed_arg arg; - struct ata_ering_entry *ent; - int err_cat; - u64 j64; - - ent = ata_ering_top(&dev->ering); - if (!ent) - return 0; - - err_cat = ata_eh_categorize_ering_entry(ent); - if (err_cat == 0) - return 0; - - memset(&arg, 0, sizeof(arg)); - - j64 = get_jiffies_64(); - if (j64 >= interval) - arg.since = j64 - interval; - else - arg.since = 0; - - ata_ering_map(&dev->ering, speed_down_needed_cb, &arg); - - return arg.nr_errors[err_cat] > err_limits[err_cat]; -} - -/** - * ata_eh_speed_down - record error and speed down if necessary - * @dev: Failed device - * @is_io: Did the device fail during normal IO? - * @err_mask: err_mask of the error - * - * Record error and examine error history to determine whether - * adjusting transmission speed is necessary. It also sets - * transmission limits appropriately if such adjustment is - * necessary. - * - * LOCKING: - * Kernel thread context (may sleep). - * - * RETURNS: - * 0 on success, -errno otherwise - */ -static int ata_eh_speed_down(struct ata_device *dev, int is_io, - unsigned int err_mask) -{ - if (!err_mask) - return 0; - - /* record error and determine whether speed down is necessary */ - ata_ering_record(&dev->ering, is_io, err_mask); - - if (!ata_eh_speed_down_needed(dev)) - return 0; - - /* speed down SATA link speed if possible */ - if (sata_down_spd_limit(dev->ap) == 0) - return ATA_EH_HARDRESET; - - /* lower transfer mode */ - if (ata_down_xfermask_limit(dev, 0) == 0) - return ATA_EH_SOFTRESET; - - ata_dev_printk(dev, KERN_ERR, - "speed down requested but no transfer mode left\n"); - return 0; -} - -/** - * ata_eh_autopsy - analyze error and determine recovery action - * @ap: ATA port to perform autopsy on - * - * Analyze why @ap failed and determine which recovery action is - * needed. This function also sets more detailed AC_ERR_* values - * and fills sense data for ATAPI CHECK SENSE. - * - * LOCKING: - * Kernel thread context (may sleep). - */ -static void ata_eh_autopsy(struct ata_port *ap) -{ - struct ata_eh_context *ehc = &ap->eh_context; - unsigned int all_err_mask = 0; - int tag, is_io = 0; - u32 serror; - int rc; - - DPRINTK("ENTER\n"); - - if (ehc->i.flags & ATA_EHI_NO_AUTOPSY) - return; - - /* obtain and analyze SError */ - rc = sata_scr_read(ap, SCR_ERROR, &serror); - if (rc == 0) { - ehc->i.serror |= serror; - ata_eh_analyze_serror(ap); - } else if (rc != -EOPNOTSUPP) - ehc->i.action |= ATA_EH_HARDRESET; - - /* analyze NCQ failure */ - ata_eh_analyze_ncq_error(ap); - - /* any real error trumps AC_ERR_OTHER */ - if (ehc->i.err_mask & ~AC_ERR_OTHER) - ehc->i.err_mask &= ~AC_ERR_OTHER; - - all_err_mask |= ehc->i.err_mask; - - for (tag = 0; tag < ATA_MAX_QUEUE; tag++) { - struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag); - - if (!(qc->flags & ATA_QCFLAG_FAILED)) - continue; - - /* inherit upper level err_mask */ - qc->err_mask |= ehc->i.err_mask; - - /* analyze TF */ - ehc->i.action |= ata_eh_analyze_tf(qc, &qc->result_tf); - - /* DEV errors are probably spurious in case of ATA_BUS error */ - if (qc->err_mask & AC_ERR_ATA_BUS) - qc->err_mask &= ~(AC_ERR_DEV | AC_ERR_MEDIA | - AC_ERR_INVALID); - - /* any real error trumps unknown error */ - if (qc->err_mask & ~AC_ERR_OTHER) - qc->err_mask &= ~AC_ERR_OTHER; - - /* SENSE_VALID trumps dev/unknown error and revalidation */ - if (qc->flags & ATA_QCFLAG_SENSE_VALID) { - qc->err_mask &= ~(AC_ERR_DEV | AC_ERR_OTHER); - ehc->i.action &= ~ATA_EH_REVALIDATE; - } - - /* accumulate error info */ - ehc->i.dev = qc->dev; - all_err_mask |= qc->err_mask; - if (qc->flags & ATA_QCFLAG_IO) - is_io = 1; - } - - /* enforce default EH actions */ - if (ap->pflags & ATA_PFLAG_FROZEN || - all_err_mask & (AC_ERR_HSM | AC_ERR_TIMEOUT)) - ehc->i.action |= ATA_EH_SOFTRESET; - else if (all_err_mask) - ehc->i.action |= ATA_EH_REVALIDATE; - - /* if we have offending qcs and the associated failed device */ - if (ehc->i.dev) { - /* speed down */ - ehc->i.action |= ata_eh_speed_down(ehc->i.dev, is_io, - all_err_mask); - - /* perform per-dev EH action only on the offending device */ - ehc->i.dev_action[ehc->i.dev->devno] |= - ehc->i.action & ATA_EH_PERDEV_MASK; - ehc->i.action &= ~ATA_EH_PERDEV_MASK; - } - - DPRINTK("EXIT\n"); -} - -/** - * ata_eh_report - report error handling to user - * @ap: ATA port EH is going on - * - * Report EH to user. - * - * LOCKING: - * None. - */ -static void ata_eh_report(struct ata_port *ap) -{ - struct ata_eh_context *ehc = &ap->eh_context; - const char *frozen, *desc; - int tag, nr_failed = 0; - - desc = NULL; - if (ehc->i.desc[0] != '\0') - desc = ehc->i.desc; - - for (tag = 0; tag < ATA_MAX_QUEUE; tag++) { - struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag); - - if (!(qc->flags & ATA_QCFLAG_FAILED)) - continue; - if (qc->flags & ATA_QCFLAG_SENSE_VALID && !qc->err_mask) - continue; - - nr_failed++; - } - - if (!nr_failed && !ehc->i.err_mask) - return; - - frozen = ""; - if (ap->pflags & ATA_PFLAG_FROZEN) - frozen = " frozen"; - - if (ehc->i.dev) { - ata_dev_printk(ehc->i.dev, KERN_ERR, "exception Emask 0x%x " - "SAct 0x%x SErr 0x%x action 0x%x%s\n", - ehc->i.err_mask, ap->sactive, ehc->i.serror, - ehc->i.action, frozen); - if (desc) - ata_dev_printk(ehc->i.dev, KERN_ERR, "(%s)\n", desc); - } else { - ata_port_printk(ap, KERN_ERR, "exception Emask 0x%x " - "SAct 0x%x SErr 0x%x action 0x%x%s\n", - ehc->i.err_mask, ap->sactive, ehc->i.serror, - ehc->i.action, frozen); - if (desc) - ata_port_printk(ap, KERN_ERR, "(%s)\n", desc); - } - - for (tag = 0; tag < ATA_MAX_QUEUE; tag++) { - struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag); - - if (!(qc->flags & ATA_QCFLAG_FAILED) || !qc->err_mask) - continue; - - ata_dev_printk(qc->dev, KERN_ERR, "tag %d cmd 0x%x " - "Emask 0x%x stat 0x%x err 0x%x (%s)\n", - qc->tag, qc->tf.command, qc->err_mask, - qc->result_tf.command, qc->result_tf.feature, - ata_err_string(qc->err_mask)); - } -} - -static int ata_do_reset(struct ata_port *ap, ata_reset_fn_t reset, - unsigned int *classes) -{ - int i, rc; - - for (i = 0; i < ATA_MAX_DEVICES; i++) - classes[i] = ATA_DEV_UNKNOWN; - - rc = reset(ap, classes); - if (rc) - return rc; - - /* If any class isn't ATA_DEV_UNKNOWN, consider classification - * is complete and convert all ATA_DEV_UNKNOWN to - * ATA_DEV_NONE. - */ - for (i = 0; i < ATA_MAX_DEVICES; i++) - if (classes[i] != ATA_DEV_UNKNOWN) - break; - - if (i < ATA_MAX_DEVICES) - for (i = 0; i < ATA_MAX_DEVICES; i++) - if (classes[i] == ATA_DEV_UNKNOWN) - classes[i] = ATA_DEV_NONE; - - return 0; -} - -static int ata_eh_followup_srst_needed(int rc, int classify, - const unsigned int *classes) -{ - if (rc == -EAGAIN) - return 1; - if (rc != 0) - return 0; - if (classify && classes[0] == ATA_DEV_UNKNOWN) - return 1; - return 0; -} - -static int ata_eh_reset(struct ata_port *ap, int classify, - ata_prereset_fn_t prereset, ata_reset_fn_t softreset, - ata_reset_fn_t hardreset, ata_postreset_fn_t postreset) -{ - struct ata_eh_context *ehc = &ap->eh_context; - unsigned int *classes = ehc->classes; - int tries = ATA_EH_RESET_TRIES; - int verbose = !(ehc->i.flags & ATA_EHI_QUIET); - unsigned int action; - ata_reset_fn_t reset; - int i, did_followup_srst, rc; - - /* about to reset */ - ata_eh_about_to_do(ap, NULL, ehc->i.action & ATA_EH_RESET_MASK); - - /* Determine which reset to use and record in ehc->i.action. - * prereset() may examine and modify it. - */ - action = ehc->i.action; - ehc->i.action &= ~ATA_EH_RESET_MASK; - if (softreset && (!hardreset || (!sata_set_spd_needed(ap) && - !(action & ATA_EH_HARDRESET)))) - ehc->i.action |= ATA_EH_SOFTRESET; - else - ehc->i.action |= ATA_EH_HARDRESET; - - if (prereset) { - rc = prereset(ap); - if (rc) { - ata_port_printk(ap, KERN_ERR, - "prereset failed (errno=%d)\n", rc); - return rc; - } - } - - /* prereset() might have modified ehc->i.action */ - if (ehc->i.action & ATA_EH_HARDRESET) - reset = hardreset; - else if (ehc->i.action & ATA_EH_SOFTRESET) - reset = softreset; - else { - /* prereset told us not to reset, bang classes and return */ - for (i = 0; i < ATA_MAX_DEVICES; i++) - classes[i] = ATA_DEV_NONE; - return 0; - } - - /* did prereset() screw up? if so, fix up to avoid oopsing */ - if (!reset) { - ata_port_printk(ap, KERN_ERR, "BUG: prereset() requested " - "invalid reset type\n"); - if (softreset) - reset = softreset; - else - reset = hardreset; - } - - retry: - /* shut up during boot probing */ - if (verbose) - ata_port_printk(ap, KERN_INFO, "%s resetting port\n", - reset == softreset ? "soft" : "hard"); - - /* mark that this EH session started with reset */ - ehc->i.flags |= ATA_EHI_DID_RESET; - - rc = ata_do_reset(ap, reset, classes); - - did_followup_srst = 0; - if (reset == hardreset && - ata_eh_followup_srst_needed(rc, classify, classes)) { - /* okay, let's do follow-up softreset */ - did_followup_srst = 1; - reset = softreset; - - if (!reset) { - ata_port_printk(ap, KERN_ERR, - "follow-up softreset required " - "but no softreset avaliable\n"); - return -EINVAL; - } - - ata_eh_about_to_do(ap, NULL, ATA_EH_RESET_MASK); - rc = ata_do_reset(ap, reset, classes); - - if (rc == 0 && classify && - classes[0] == ATA_DEV_UNKNOWN) { - ata_port_printk(ap, KERN_ERR, - "classification failed\n"); - return -EINVAL; - } - } - - if (rc && --tries) { - const char *type; - - if (reset == softreset) { - if (did_followup_srst) - type = "follow-up soft"; - else - type = "soft"; - } else - type = "hard"; - - ata_port_printk(ap, KERN_WARNING, - "%sreset failed, retrying in 5 secs\n", type); - ssleep(5); - - if (reset == hardreset) - sata_down_spd_limit(ap); - if (hardreset) - reset = hardreset; - goto retry; - } - - if (rc == 0) { - /* After the reset, the device state is PIO 0 and the - * controller state is undefined. Record the mode. - */ - for (i = 0; i < ATA_MAX_DEVICES; i++) - ap->device[i].pio_mode = XFER_PIO_0; - - if (postreset) - postreset(ap, classes); - - /* reset successful, schedule revalidation */ - ata_eh_done(ap, NULL, ehc->i.action & ATA_EH_RESET_MASK); - ehc->i.action |= ATA_EH_REVALIDATE; - } - - return rc; -} - -static int ata_eh_revalidate_and_attach(struct ata_port *ap, - struct ata_device **r_failed_dev) -{ - struct ata_eh_context *ehc = &ap->eh_context; - struct ata_device *dev; - unsigned long flags; - int i, rc = 0; - - DPRINTK("ENTER\n"); - - for (i = 0; i < ATA_MAX_DEVICES; i++) { - unsigned int action; - - dev = &ap->device[i]; - action = ata_eh_dev_action(dev); - - if (action & ATA_EH_REVALIDATE && ata_dev_ready(dev)) { - if (ata_port_offline(ap)) { - rc = -EIO; - break; - } - - ata_eh_about_to_do(ap, dev, ATA_EH_REVALIDATE); - rc = ata_dev_revalidate(dev, - ehc->i.flags & ATA_EHI_DID_RESET); - if (rc) - break; - - ata_eh_done(ap, dev, ATA_EH_REVALIDATE); - - /* schedule the scsi_rescan_device() here */ - queue_work(ata_aux_wq, &(ap->scsi_rescan_task)); - } else if (dev->class == ATA_DEV_UNKNOWN && - ehc->tries[dev->devno] && - ata_class_enabled(ehc->classes[dev->devno])) { - dev->class = ehc->classes[dev->devno]; - - rc = ata_dev_read_id(dev, &dev->class, 1, dev->id); - if (rc == 0) - rc = ata_dev_configure(dev, 1); - - if (rc) { - dev->class = ATA_DEV_UNKNOWN; - break; - } - - spin_lock_irqsave(ap->lock, flags); - ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG; - spin_unlock_irqrestore(ap->lock, flags); - } - } - - if (rc) - *r_failed_dev = dev; - - DPRINTK("EXIT\n"); - return rc; -} - -/** - * ata_eh_suspend - handle suspend EH action - * @ap: target host port - * @r_failed_dev: result parameter to indicate failing device - * - * Handle suspend EH action. Disk devices are spinned down and - * other types of devices are just marked suspended. Once - * suspended, no EH action to the device is allowed until it is - * resumed. - * - * LOCKING: - * Kernel thread context (may sleep). - * - * RETURNS: - * 0 on success, -errno otherwise - */ -static int ata_eh_suspend(struct ata_port *ap, struct ata_device **r_failed_dev) -{ - struct ata_device *dev; - int i, rc = 0; - - DPRINTK("ENTER\n"); - - for (i = 0; i < ATA_MAX_DEVICES; i++) { - unsigned long flags; - unsigned int action, err_mask; - - dev = &ap->device[i]; - action = ata_eh_dev_action(dev); - - if (!ata_dev_enabled(dev) || !(action & ATA_EH_SUSPEND)) - continue; - - WARN_ON(dev->flags & ATA_DFLAG_SUSPENDED); - - ata_eh_about_to_do(ap, dev, ATA_EH_SUSPEND); - - if (dev->class == ATA_DEV_ATA && !(action & ATA_EH_PM_FREEZE)) { - /* flush cache */ - rc = ata_flush_cache(dev); - if (rc) - break; - - /* spin down */ - err_mask = ata_do_simple_cmd(dev, ATA_CMD_STANDBYNOW1); - if (err_mask) { - ata_dev_printk(dev, KERN_ERR, "failed to " - "spin down (err_mask=0x%x)\n", - err_mask); - rc = -EIO; - break; - } - } - - spin_lock_irqsave(ap->lock, flags); - dev->flags |= ATA_DFLAG_SUSPENDED; - spin_unlock_irqrestore(ap->lock, flags); - - ata_eh_done(ap, dev, ATA_EH_SUSPEND); - } - - if (rc) - *r_failed_dev = dev; - - DPRINTK("EXIT\n"); - return 0; -} - -/** - * ata_eh_prep_resume - prep for resume EH action - * @ap: target host port - * - * Clear SUSPENDED in preparation for scheduled resume actions. - * This allows other parts of EH to access the devices being - * resumed. - * - * LOCKING: - * Kernel thread context (may sleep). - */ -static void ata_eh_prep_resume(struct ata_port *ap) -{ - struct ata_device *dev; - unsigned long flags; - int i; - - DPRINTK("ENTER\n"); - - for (i = 0; i < ATA_MAX_DEVICES; i++) { - unsigned int action; - - dev = &ap->device[i]; - action = ata_eh_dev_action(dev); - - if (!ata_dev_enabled(dev) || !(action & ATA_EH_RESUME)) - continue; - - spin_lock_irqsave(ap->lock, flags); - dev->flags &= ~ATA_DFLAG_SUSPENDED; - spin_unlock_irqrestore(ap->lock, flags); - } - - DPRINTK("EXIT\n"); -} - -/** - * ata_eh_resume - handle resume EH action - * @ap: target host port - * @r_failed_dev: result parameter to indicate failing device - * - * Handle resume EH action. Target devices are already reset and - * revalidated. Spinning up is the only operation left. - * - * LOCKING: - * Kernel thread context (may sleep). - * - * RETURNS: - * 0 on success, -errno otherwise - */ -static int ata_eh_resume(struct ata_port *ap, struct ata_device **r_failed_dev) -{ - struct ata_device *dev; - int i, rc = 0; - - DPRINTK("ENTER\n"); - - for (i = 0; i < ATA_MAX_DEVICES; i++) { - unsigned int action, err_mask; - - dev = &ap->device[i]; - action = ata_eh_dev_action(dev); - - if (!ata_dev_enabled(dev) || !(action & ATA_EH_RESUME)) - continue; - - ata_eh_about_to_do(ap, dev, ATA_EH_RESUME); - - if (dev->class == ATA_DEV_ATA && !(action & ATA_EH_PM_FREEZE)) { - err_mask = ata_do_simple_cmd(dev, - ATA_CMD_IDLEIMMEDIATE); - if (err_mask) { - ata_dev_printk(dev, KERN_ERR, "failed to " - "spin up (err_mask=0x%x)\n", - err_mask); - rc = -EIO; - break; - } - } - - ata_eh_done(ap, dev, ATA_EH_RESUME); - } - - if (rc) - *r_failed_dev = dev; - - DPRINTK("EXIT\n"); - return 0; -} - -static int ata_port_nr_enabled(struct ata_port *ap) -{ - int i, cnt = 0; - - for (i = 0; i < ATA_MAX_DEVICES; i++) - if (ata_dev_enabled(&ap->device[i])) - cnt++; - return cnt; -} - -static int ata_port_nr_vacant(struct ata_port *ap) -{ - int i, cnt = 0; - - for (i = 0; i < ATA_MAX_DEVICES; i++) - if (ap->device[i].class == ATA_DEV_UNKNOWN) - cnt++; - return cnt; -} - -static int ata_eh_skip_recovery(struct ata_port *ap) -{ - struct ata_eh_context *ehc = &ap->eh_context; - int i; - - /* skip if all possible devices are suspended */ - for (i = 0; i < ata_port_max_devices(ap); i++) { - struct ata_device *dev = &ap->device[i]; - - if (!(dev->flags & ATA_DFLAG_SUSPENDED)) - break; - } - - if (i == ata_port_max_devices(ap)) - return 1; - - /* thaw frozen port, resume link and recover failed devices */ - if ((ap->pflags & ATA_PFLAG_FROZEN) || - (ehc->i.flags & ATA_EHI_RESUME_LINK) || ata_port_nr_enabled(ap)) - return 0; - - /* skip if class codes for all vacant slots are ATA_DEV_NONE */ - for (i = 0; i < ATA_MAX_DEVICES; i++) { - struct ata_device *dev = &ap->device[i]; - - if (dev->class == ATA_DEV_UNKNOWN && - ehc->classes[dev->devno] != ATA_DEV_NONE) - return 0; - } - - return 1; -} - -/** - * ata_eh_recover - recover host port after error - * @ap: host port to recover - * @prereset: prereset method (can be NULL) - * @softreset: softreset method (can be NULL) - * @hardreset: hardreset method (can be NULL) - * @postreset: postreset method (can be NULL) - * - * This is the alpha and omega, eum and yang, heart and soul of - * libata exception handling. On entry, actions required to - * recover the port and hotplug requests are recorded in - * eh_context. This function executes all the operations with - * appropriate retrials and fallbacks to resurrect failed - * devices, detach goners and greet newcomers. - * - * LOCKING: - * Kernel thread context (may sleep). - * - * RETURNS: - * 0 on success, -errno on failure. - */ -static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, - ata_reset_fn_t softreset, ata_reset_fn_t hardreset, - ata_postreset_fn_t postreset) -{ - struct ata_eh_context *ehc = &ap->eh_context; - struct ata_device *dev; - int down_xfermask, i, rc; - - DPRINTK("ENTER\n"); - - /* prep for recovery */ - for (i = 0; i < ATA_MAX_DEVICES; i++) { - dev = &ap->device[i]; - - ehc->tries[dev->devno] = ATA_EH_DEV_TRIES; - - /* process hotplug request */ - if (dev->flags & ATA_DFLAG_DETACH) - ata_eh_detach_dev(dev); - - if (!ata_dev_enabled(dev) && - ((ehc->i.probe_mask & (1 << dev->devno)) && - !(ehc->did_probe_mask & (1 << dev->devno)))) { - ata_eh_detach_dev(dev); - ata_dev_init(dev); - ehc->did_probe_mask |= (1 << dev->devno); - ehc->i.action |= ATA_EH_SOFTRESET; - } - } - - retry: - down_xfermask = 0; - rc = 0; - - /* if UNLOADING, finish immediately */ - if (ap->pflags & ATA_PFLAG_UNLOADING) - goto out; - - /* prep for resume */ - ata_eh_prep_resume(ap); - - /* skip EH if possible. */ - if (ata_eh_skip_recovery(ap)) - ehc->i.action = 0; - - for (i = 0; i < ATA_MAX_DEVICES; i++) - ehc->classes[i] = ATA_DEV_UNKNOWN; - - /* reset */ - if (ehc->i.action & ATA_EH_RESET_MASK) { - ata_eh_freeze_port(ap); - - rc = ata_eh_reset(ap, ata_port_nr_vacant(ap), prereset, - softreset, hardreset, postreset); - if (rc) { - ata_port_printk(ap, KERN_ERR, - "reset failed, giving up\n"); - goto out; - } - - ata_eh_thaw_port(ap); - } - - /* revalidate existing devices and attach new ones */ - rc = ata_eh_revalidate_and_attach(ap, &dev); - if (rc) - goto dev_fail; - - /* resume devices */ - rc = ata_eh_resume(ap, &dev); - if (rc) - goto dev_fail; - - /* configure transfer mode if the port has been reset */ - if (ehc->i.flags & ATA_EHI_DID_RESET) { - rc = ata_set_mode(ap, &dev); - if (rc) { - down_xfermask = 1; - goto dev_fail; - } - } - - /* suspend devices */ - rc = ata_eh_suspend(ap, &dev); - if (rc) - goto dev_fail; - - goto out; - - dev_fail: - switch (rc) { - case -ENODEV: - /* device missing, schedule probing */ - ehc->i.probe_mask |= (1 << dev->devno); - case -EINVAL: - ehc->tries[dev->devno] = 0; - break; - case -EIO: - sata_down_spd_limit(ap); - default: - ehc->tries[dev->devno]--; - if (down_xfermask && - ata_down_xfermask_limit(dev, ehc->tries[dev->devno] == 1)) - ehc->tries[dev->devno] = 0; - } - - if (ata_dev_enabled(dev) && !ehc->tries[dev->devno]) { - /* disable device if it has used up all its chances */ - ata_dev_disable(dev); - - /* detach if offline */ - if (ata_port_offline(ap)) - ata_eh_detach_dev(dev); - - /* probe if requested */ - if ((ehc->i.probe_mask & (1 << dev->devno)) && - !(ehc->did_probe_mask & (1 << dev->devno))) { - ata_eh_detach_dev(dev); - ata_dev_init(dev); - - ehc->tries[dev->devno] = ATA_EH_DEV_TRIES; - ehc->did_probe_mask |= (1 << dev->devno); - ehc->i.action |= ATA_EH_SOFTRESET; - } - } else { - /* soft didn't work? be haaaaard */ - if (ehc->i.flags & ATA_EHI_DID_RESET) - ehc->i.action |= ATA_EH_HARDRESET; - else - ehc->i.action |= ATA_EH_SOFTRESET; - } - - if (ata_port_nr_enabled(ap)) { - ata_port_printk(ap, KERN_WARNING, "failed to recover some " - "devices, retrying in 5 secs\n"); - ssleep(5); - } else { - /* no device left, repeat fast */ - msleep(500); - } - - goto retry; - - out: - if (rc) { - for (i = 0; i < ATA_MAX_DEVICES; i++) - ata_dev_disable(&ap->device[i]); - } - - DPRINTK("EXIT, rc=%d\n", rc); - return rc; -} - -/** - * ata_eh_finish - finish up EH - * @ap: host port to finish EH for - * - * Recovery is complete. Clean up EH states and retry or finish - * failed qcs. - * - * LOCKING: - * None. - */ -static void ata_eh_finish(struct ata_port *ap) -{ - int tag; - - /* retry or finish qcs */ - for (tag = 0; tag < ATA_MAX_QUEUE; tag++) { - struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag); - - if (!(qc->flags & ATA_QCFLAG_FAILED)) - continue; - - if (qc->err_mask) { - /* FIXME: Once EH migration is complete, - * generate sense data in this function, - * considering both err_mask and tf. - */ - if (qc->err_mask & AC_ERR_INVALID) - ata_eh_qc_complete(qc); - else - ata_eh_qc_retry(qc); - } else { - if (qc->flags & ATA_QCFLAG_SENSE_VALID) { - ata_eh_qc_complete(qc); - } else { - /* feed zero TF to sense generation */ - memset(&qc->result_tf, 0, sizeof(qc->result_tf)); - ata_eh_qc_retry(qc); - } - } - } -} - -/** - * ata_do_eh - do standard error handling - * @ap: host port to handle error for - * @prereset: prereset method (can be NULL) - * @softreset: softreset method (can be NULL) - * @hardreset: hardreset method (can be NULL) - * @postreset: postreset method (can be NULL) - * - * Perform standard error handling sequence. - * - * LOCKING: - * Kernel thread context (may sleep). - */ -void ata_do_eh(struct ata_port *ap, ata_prereset_fn_t prereset, - ata_reset_fn_t softreset, ata_reset_fn_t hardreset, - ata_postreset_fn_t postreset) -{ - ata_eh_autopsy(ap); - ata_eh_report(ap); - ata_eh_recover(ap, prereset, softreset, hardreset, postreset); - ata_eh_finish(ap); -} - -/** - * ata_eh_handle_port_suspend - perform port suspend operation - * @ap: port to suspend - * - * Suspend @ap. - * - * LOCKING: - * Kernel thread context (may sleep). - */ -static void ata_eh_handle_port_suspend(struct ata_port *ap) -{ - unsigned long flags; - int rc = 0; - - /* are we suspending? */ - spin_lock_irqsave(ap->lock, flags); - if (!(ap->pflags & ATA_PFLAG_PM_PENDING) || - ap->pm_mesg.event == PM_EVENT_ON) { - spin_unlock_irqrestore(ap->lock, flags); - return; - } - spin_unlock_irqrestore(ap->lock, flags); - - WARN_ON(ap->pflags & ATA_PFLAG_SUSPENDED); - - /* suspend */ - ata_eh_freeze_port(ap); - - if (ap->ops->port_suspend) - rc = ap->ops->port_suspend(ap, ap->pm_mesg); - - /* report result */ - spin_lock_irqsave(ap->lock, flags); - - ap->pflags &= ~ATA_PFLAG_PM_PENDING; - if (rc == 0) - ap->pflags |= ATA_PFLAG_SUSPENDED; - else - ata_port_schedule_eh(ap); - - if (ap->pm_result) { - *ap->pm_result = rc; - ap->pm_result = NULL; - } - - spin_unlock_irqrestore(ap->lock, flags); - - return; -} - -/** - * ata_eh_handle_port_resume - perform port resume operation - * @ap: port to resume - * - * Resume @ap. - * - * This function also waits upto one second until all devices - * hanging off this port requests resume EH action. This is to - * prevent invoking EH and thus reset multiple times on resume. - * - * On DPM resume, where some of devices might not be resumed - * together, this may delay port resume upto one second, but such - * DPM resumes are rare and 1 sec delay isn't too bad. - * - * LOCKING: - * Kernel thread context (may sleep). - */ -static void ata_eh_handle_port_resume(struct ata_port *ap) -{ - unsigned long timeout; - unsigned long flags; - int i, rc = 0; - - /* are we resuming? */ - spin_lock_irqsave(ap->lock, flags); - if (!(ap->pflags & ATA_PFLAG_PM_PENDING) || - ap->pm_mesg.event != PM_EVENT_ON) { - spin_unlock_irqrestore(ap->lock, flags); - return; - } - spin_unlock_irqrestore(ap->lock, flags); - - /* spurious? */ - if (!(ap->pflags & ATA_PFLAG_SUSPENDED)) - goto done; - - if (ap->ops->port_resume) - rc = ap->ops->port_resume(ap); - - /* give devices time to request EH */ - timeout = jiffies + HZ; /* 1s max */ - while (1) { - for (i = 0; i < ATA_MAX_DEVICES; i++) { - struct ata_device *dev = &ap->device[i]; - unsigned int action = ata_eh_dev_action(dev); - - if ((dev->flags & ATA_DFLAG_SUSPENDED) && - !(action & ATA_EH_RESUME)) - break; - } - - if (i == ATA_MAX_DEVICES || time_after(jiffies, timeout)) - break; - msleep(10); - } - - done: - spin_lock_irqsave(ap->lock, flags); - ap->pflags &= ~(ATA_PFLAG_PM_PENDING | ATA_PFLAG_SUSPENDED); - if (ap->pm_result) { - *ap->pm_result = rc; - ap->pm_result = NULL; - } - spin_unlock_irqrestore(ap->lock, flags); -} diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c deleted file mode 100644 index d168e341366..00000000000 --- a/drivers/scsi/libata-scsi.c +++ /dev/null @@ -1,3322 +0,0 @@ -/* - * libata-scsi.c - helper library for ATA - * - * Maintained by: Jeff Garzik - * Please ALWAYS copy linux-ide@vger.kernel.org - * on emails. - * - * Copyright 2003-2004 Red Hat, Inc. All rights reserved. - * Copyright 2003-2004 Jeff Garzik - * - * - * 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 02139, USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - * Hardware documentation available from - * - http://www.t10.org/ - * - http://www.t13.org/ - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libata.h" - -#define SECTOR_SIZE 512 - -typedef unsigned int (*ata_xlat_func_t)(struct ata_queued_cmd *qc, const u8 *scsicmd); - -static struct ata_device * __ata_scsi_find_dev(struct ata_port *ap, - const struct scsi_device *scsidev); -static struct ata_device * ata_scsi_find_dev(struct ata_port *ap, - const struct scsi_device *scsidev); -static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, - unsigned int id, unsigned int lun); - - -#define RW_RECOVERY_MPAGE 0x1 -#define RW_RECOVERY_MPAGE_LEN 12 -#define CACHE_MPAGE 0x8 -#define CACHE_MPAGE_LEN 20 -#define CONTROL_MPAGE 0xa -#define CONTROL_MPAGE_LEN 12 -#define ALL_MPAGES 0x3f -#define ALL_SUB_MPAGES 0xff - - -static const u8 def_rw_recovery_mpage[] = { - RW_RECOVERY_MPAGE, - RW_RECOVERY_MPAGE_LEN - 2, - (1 << 7) | /* AWRE, sat-r06 say it shall be 0 */ - (1 << 6), /* ARRE (auto read reallocation) */ - 0, /* read retry count */ - 0, 0, 0, 0, - 0, /* write retry count */ - 0, 0, 0 -}; - -static const u8 def_cache_mpage[CACHE_MPAGE_LEN] = { - CACHE_MPAGE, - CACHE_MPAGE_LEN - 2, - 0, /* contains WCE, needs to be 0 for logic */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, /* contains DRA, needs to be 0 for logic */ - 0, 0, 0, 0, 0, 0, 0 -}; - -static const u8 def_control_mpage[CONTROL_MPAGE_LEN] = { - CONTROL_MPAGE, - CONTROL_MPAGE_LEN - 2, - 2, /* DSENSE=0, GLTSD=1 */ - 0, /* [QAM+QERR may be 1, see 05-359r1] */ - 0, 0, 0, 0, 0xff, 0xff, - 0, 30 /* extended self test time, see 05-359r1 */ -}; - -/* - * libata transport template. libata doesn't do real transport stuff. - * It just needs the eh_timed_out hook. - */ -struct scsi_transport_template ata_scsi_transport_template = { - .eh_strategy_handler = ata_scsi_error, - .eh_timed_out = ata_scsi_timed_out, - .user_scan = ata_scsi_user_scan, -}; - - -static void ata_scsi_invalid_field(struct scsi_cmnd *cmd, - void (*done)(struct scsi_cmnd *)) -{ - ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, 0x24, 0x0); - /* "Invalid field in cbd" */ - done(cmd); -} - -/** - * ata_std_bios_param - generic bios head/sector/cylinder calculator used by sd. - * @sdev: SCSI device for which BIOS geometry is to be determined - * @bdev: block device associated with @sdev - * @capacity: capacity of SCSI device - * @geom: location to which geometry will be output - * - * Generic bios head/sector/cylinder calculator - * used by sd. Most BIOSes nowadays expect a XXX/255/16 (CHS) - * mapping. Some situations may arise where the disk is not - * bootable if this is not used. - * - * LOCKING: - * Defined by the SCSI layer. We don't really care. - * - * RETURNS: - * Zero. - */ -int ata_std_bios_param(struct scsi_device *sdev, struct block_device *bdev, - sector_t capacity, int geom[]) -{ - geom[0] = 255; - geom[1] = 63; - sector_div(capacity, 255*63); - geom[2] = capacity; - - return 0; -} - -/** - * ata_cmd_ioctl - Handler for HDIO_DRIVE_CMD ioctl - * @scsidev: Device to which we are issuing command - * @arg: User provided data for issuing command - * - * LOCKING: - * Defined by the SCSI layer. We don't really care. - * - * RETURNS: - * Zero on success, negative errno on error. - */ - -int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg) -{ - int rc = 0; - u8 scsi_cmd[MAX_COMMAND_SIZE]; - u8 args[4], *argbuf = NULL; - int argsize = 0; - struct scsi_sense_hdr sshdr; - enum dma_data_direction data_dir; - - if (arg == NULL) - return -EINVAL; - - if (copy_from_user(args, arg, sizeof(args))) - return -EFAULT; - - memset(scsi_cmd, 0, sizeof(scsi_cmd)); - - if (args[3]) { - argsize = SECTOR_SIZE * args[3]; - argbuf = kmalloc(argsize, GFP_KERNEL); - if (argbuf == NULL) { - rc = -ENOMEM; - goto error; - } - - scsi_cmd[1] = (4 << 1); /* PIO Data-in */ - scsi_cmd[2] = 0x0e; /* no off.line or cc, read from dev, - block count in sector count field */ - data_dir = DMA_FROM_DEVICE; - } else { - scsi_cmd[1] = (3 << 1); /* Non-data */ - /* scsi_cmd[2] is already 0 -- no off.line, cc, or data xfer */ - data_dir = DMA_NONE; - } - - scsi_cmd[0] = ATA_16; - - scsi_cmd[4] = args[2]; - if (args[0] == WIN_SMART) { /* hack -- ide driver does this too... */ - scsi_cmd[6] = args[3]; - scsi_cmd[8] = args[1]; - scsi_cmd[10] = 0x4f; - scsi_cmd[12] = 0xc2; - } else { - scsi_cmd[6] = args[1]; - } - scsi_cmd[14] = args[0]; - - /* Good values for timeout and retries? Values below - from scsi_ioctl_send_command() for default case... */ - if (scsi_execute_req(scsidev, scsi_cmd, data_dir, argbuf, argsize, - &sshdr, (10*HZ), 5)) { - rc = -EIO; - goto error; - } - - /* Need code to retrieve data from check condition? */ - - if ((argbuf) - && copy_to_user(arg + sizeof(args), argbuf, argsize)) - rc = -EFAULT; -error: - kfree(argbuf); - return rc; -} - -/** - * ata_task_ioctl - Handler for HDIO_DRIVE_TASK ioctl - * @scsidev: Device to which we are issuing command - * @arg: User provided data for issuing command - * - * LOCKING: - * Defined by the SCSI layer. We don't really care. - * - * RETURNS: - * Zero on success, negative errno on error. - */ -int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg) -{ - int rc = 0; - u8 scsi_cmd[MAX_COMMAND_SIZE]; - u8 args[7]; - struct scsi_sense_hdr sshdr; - - if (arg == NULL) - return -EINVAL; - - if (copy_from_user(args, arg, sizeof(args))) - return -EFAULT; - - memset(scsi_cmd, 0, sizeof(scsi_cmd)); - scsi_cmd[0] = ATA_16; - scsi_cmd[1] = (3 << 1); /* Non-data */ - /* scsi_cmd[2] is already 0 -- no off.line, cc, or data xfer */ - scsi_cmd[4] = args[1]; - scsi_cmd[6] = args[2]; - scsi_cmd[8] = args[3]; - scsi_cmd[10] = args[4]; - scsi_cmd[12] = args[5]; - scsi_cmd[14] = args[0]; - - /* Good values for timeout and retries? Values below - from scsi_ioctl_send_command() for default case... */ - if (scsi_execute_req(scsidev, scsi_cmd, DMA_NONE, NULL, 0, &sshdr, - (10*HZ), 5)) - rc = -EIO; - - /* Need code to retrieve data from check condition? */ - return rc; -} - -int ata_scsi_ioctl(struct scsi_device *scsidev, int cmd, void __user *arg) -{ - int val = -EINVAL, rc = -EINVAL; - - switch (cmd) { - case ATA_IOC_GET_IO32: - val = 0; - if (copy_to_user(arg, &val, 1)) - return -EFAULT; - return 0; - - case ATA_IOC_SET_IO32: - val = (unsigned long) arg; - if (val != 0) - return -EINVAL; - return 0; - - case HDIO_DRIVE_CMD: - if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) - return -EACCES; - return ata_cmd_ioctl(scsidev, arg); - - case HDIO_DRIVE_TASK: - if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) - return -EACCES; - return ata_task_ioctl(scsidev, arg); - - default: - rc = -ENOTTY; - break; - } - - return rc; -} - -/** - * ata_scsi_qc_new - acquire new ata_queued_cmd reference - * @dev: ATA device to which the new command is attached - * @cmd: SCSI command that originated this ATA command - * @done: SCSI command completion function - * - * Obtain a reference to an unused ata_queued_cmd structure, - * which is the basic libata structure representing a single - * ATA command sent to the hardware. - * - * If a command was available, fill in the SCSI-specific - * portions of the structure with information on the - * current command. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * Command allocated, or %NULL if none available. - */ -struct ata_queued_cmd *ata_scsi_qc_new(struct ata_device *dev, - struct scsi_cmnd *cmd, - void (*done)(struct scsi_cmnd *)) -{ - struct ata_queued_cmd *qc; - - qc = ata_qc_new_init(dev); - if (qc) { - qc->scsicmd = cmd; - qc->scsidone = done; - - if (cmd->use_sg) { - qc->__sg = (struct scatterlist *) cmd->request_buffer; - qc->n_elem = cmd->use_sg; - } else { - qc->__sg = &qc->sgent; - qc->n_elem = 1; - } - } else { - cmd->result = (DID_OK << 16) | (QUEUE_FULL << 1); - done(cmd); - } - - return qc; -} - -/** - * ata_dump_status - user friendly display of error info - * @id: id of the port in question - * @tf: ptr to filled out taskfile - * - * Decode and dump the ATA error/status registers for the user so - * that they have some idea what really happened at the non - * make-believe layer. - * - * LOCKING: - * inherited from caller - */ -void ata_dump_status(unsigned id, struct ata_taskfile *tf) -{ - u8 stat = tf->command, err = tf->feature; - - printk(KERN_WARNING "ata%u: status=0x%02x { ", id, stat); - if (stat & ATA_BUSY) { - printk("Busy }\n"); /* Data is not valid in this case */ - } else { - if (stat & 0x40) printk("DriveReady "); - if (stat & 0x20) printk("DeviceFault "); - if (stat & 0x10) printk("SeekComplete "); - if (stat & 0x08) printk("DataRequest "); - if (stat & 0x04) printk("CorrectedError "); - if (stat & 0x02) printk("Index "); - if (stat & 0x01) printk("Error "); - printk("}\n"); - - if (err) { - printk(KERN_WARNING "ata%u: error=0x%02x { ", id, err); - if (err & 0x04) printk("DriveStatusError "); - if (err & 0x80) { - if (err & 0x04) printk("BadCRC "); - else printk("Sector "); - } - if (err & 0x40) printk("UncorrectableError "); - if (err & 0x10) printk("SectorIdNotFound "); - if (err & 0x02) printk("TrackZeroNotFound "); - if (err & 0x01) printk("AddrMarkNotFound "); - printk("}\n"); - } - } -} - -/** - * ata_scsi_device_suspend - suspend ATA device associated with sdev - * @sdev: the SCSI device to suspend - * @mesg: target power management message - * - * Request suspend EH action on the ATA device associated with - * @sdev and wait for the operation to complete. - * - * LOCKING: - * Kernel thread context (may sleep). - * - * RETURNS: - * 0 on success, -errno otherwise. - */ -int ata_scsi_device_suspend(struct scsi_device *sdev, pm_message_t mesg) -{ - struct ata_port *ap = ata_shost_to_port(sdev->host); - struct ata_device *dev = ata_scsi_find_dev(ap, sdev); - unsigned long flags; - unsigned int action; - int rc = 0; - - if (!dev) - goto out; - - spin_lock_irqsave(ap->lock, flags); - - /* wait for the previous resume to complete */ - while (dev->flags & ATA_DFLAG_SUSPENDED) { - spin_unlock_irqrestore(ap->lock, flags); - ata_port_wait_eh(ap); - spin_lock_irqsave(ap->lock, flags); - } - - /* if @sdev is already detached, nothing to do */ - if (sdev->sdev_state == SDEV_OFFLINE || - sdev->sdev_state == SDEV_CANCEL || sdev->sdev_state == SDEV_DEL) - goto out_unlock; - - /* request suspend */ - action = ATA_EH_SUSPEND; - if (mesg.event != PM_EVENT_SUSPEND) - action |= ATA_EH_PM_FREEZE; - ap->eh_info.dev_action[dev->devno] |= action; - ap->eh_info.flags |= ATA_EHI_QUIET; - ata_port_schedule_eh(ap); - - spin_unlock_irqrestore(ap->lock, flags); - - /* wait for EH to do the job */ - ata_port_wait_eh(ap); - - spin_lock_irqsave(ap->lock, flags); - - /* If @sdev is still attached but the associated ATA device - * isn't suspended, the operation failed. - */ - if (sdev->sdev_state != SDEV_OFFLINE && - sdev->sdev_state != SDEV_CANCEL && sdev->sdev_state != SDEV_DEL && - !(dev->flags & ATA_DFLAG_SUSPENDED)) - rc = -EIO; - - out_unlock: - spin_unlock_irqrestore(ap->lock, flags); - out: - if (rc == 0) - sdev->sdev_gendev.power.power_state = mesg; - return rc; -} - -/** - * ata_scsi_device_resume - resume ATA device associated with sdev - * @sdev: the SCSI device to resume - * - * Request resume EH action on the ATA device associated with - * @sdev and return immediately. This enables parallel - * wakeup/spinup of devices. - * - * LOCKING: - * Kernel thread context (may sleep). - * - * RETURNS: - * 0. - */ -int ata_scsi_device_resume(struct scsi_device *sdev) -{ - struct ata_port *ap = ata_shost_to_port(sdev->host); - struct ata_device *dev = ata_scsi_find_dev(ap, sdev); - struct ata_eh_info *ehi = &ap->eh_info; - unsigned long flags; - unsigned int action; - - if (!dev) - goto out; - - spin_lock_irqsave(ap->lock, flags); - - /* if @sdev is already detached, nothing to do */ - if (sdev->sdev_state == SDEV_OFFLINE || - sdev->sdev_state == SDEV_CANCEL || sdev->sdev_state == SDEV_DEL) - goto out_unlock; - - /* request resume */ - action = ATA_EH_RESUME; - if (sdev->sdev_gendev.power.power_state.event == PM_EVENT_SUSPEND) - __ata_ehi_hotplugged(ehi); - else - action |= ATA_EH_PM_FREEZE | ATA_EH_SOFTRESET; - ehi->dev_action[dev->devno] |= action; - - /* We don't want autopsy and verbose EH messages. Disable - * those if we're the only device on this link. - */ - if (ata_port_max_devices(ap) == 1) - ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET; - - ata_port_schedule_eh(ap); - - out_unlock: - spin_unlock_irqrestore(ap->lock, flags); - out: - sdev->sdev_gendev.power.power_state = PMSG_ON; - return 0; -} - -/** - * ata_to_sense_error - convert ATA error to SCSI error - * @id: ATA device number - * @drv_stat: value contained in ATA status register - * @drv_err: value contained in ATA error register - * @sk: the sense key we'll fill out - * @asc: the additional sense code we'll fill out - * @ascq: the additional sense code qualifier we'll fill out - * @verbose: be verbose - * - * Converts an ATA error into a SCSI error. Fill out pointers to - * SK, ASC, and ASCQ bytes for later use in fixed or descriptor - * format sense blocks. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ -void ata_to_sense_error(unsigned id, u8 drv_stat, u8 drv_err, u8 *sk, u8 *asc, - u8 *ascq, int verbose) -{ - int i; - - /* Based on the 3ware driver translation table */ - static const unsigned char sense_table[][4] = { - /* BBD|ECC|ID|MAR */ - {0xd1, ABORTED_COMMAND, 0x00, 0x00}, // Device busy Aborted command - /* BBD|ECC|ID */ - {0xd0, ABORTED_COMMAND, 0x00, 0x00}, // Device busy Aborted command - /* ECC|MC|MARK */ - {0x61, HARDWARE_ERROR, 0x00, 0x00}, // Device fault Hardware error - /* ICRC|ABRT */ /* NB: ICRC & !ABRT is BBD */ - {0x84, ABORTED_COMMAND, 0x47, 0x00}, // Data CRC error SCSI parity error - /* MC|ID|ABRT|TRK0|MARK */ - {0x37, NOT_READY, 0x04, 0x00}, // Unit offline Not ready - /* MCR|MARK */ - {0x09, NOT_READY, 0x04, 0x00}, // Unrecovered disk error Not ready - /* Bad address mark */ - {0x01, MEDIUM_ERROR, 0x13, 0x00}, // Address mark not found Address mark not found for data field - /* TRK0 */ - {0x02, HARDWARE_ERROR, 0x00, 0x00}, // Track 0 not found Hardware error - /* Abort & !ICRC */ - {0x04, ABORTED_COMMAND, 0x00, 0x00}, // Aborted command Aborted command - /* Media change request */ - {0x08, NOT_READY, 0x04, 0x00}, // Media change request FIXME: faking offline - /* SRV */ - {0x10, ABORTED_COMMAND, 0x14, 0x00}, // ID not found Recorded entity not found - /* Media change */ - {0x08, NOT_READY, 0x04, 0x00}, // Media change FIXME: faking offline - /* ECC */ - {0x40, MEDIUM_ERROR, 0x11, 0x04}, // Uncorrectable ECC error Unrecovered read error - /* BBD - block marked bad */ - {0x80, MEDIUM_ERROR, 0x11, 0x04}, // Block marked bad Medium error, unrecovered read error - {0xFF, 0xFF, 0xFF, 0xFF}, // END mark - }; - static const unsigned char stat_table[][4] = { - /* Must be first because BUSY means no other bits valid */ - {0x80, ABORTED_COMMAND, 0x47, 0x00}, // Busy, fake parity for now - {0x20, HARDWARE_ERROR, 0x00, 0x00}, // Device fault - {0x08, ABORTED_COMMAND, 0x47, 0x00}, // Timed out in xfer, fake parity for now - {0x04, RECOVERED_ERROR, 0x11, 0x00}, // Recovered ECC error Medium error, recovered - {0xFF, 0xFF, 0xFF, 0xFF}, // END mark - }; - - /* - * Is this an error we can process/parse - */ - if (drv_stat & ATA_BUSY) { - drv_err = 0; /* Ignore the err bits, they're invalid */ - } - - if (drv_err) { - /* Look for drv_err */ - for (i = 0; sense_table[i][0] != 0xFF; i++) { - /* Look for best matches first */ - if ((sense_table[i][0] & drv_err) == - sense_table[i][0]) { - *sk = sense_table[i][1]; - *asc = sense_table[i][2]; - *ascq = sense_table[i][3]; - goto translate_done; - } - } - /* No immediate match */ - if (verbose) - printk(KERN_WARNING "ata%u: no sense translation for " - "error 0x%02x\n", id, drv_err); - } - - /* Fall back to interpreting status bits */ - for (i = 0; stat_table[i][0] != 0xFF; i++) { - if (stat_table[i][0] & drv_stat) { - *sk = stat_table[i][1]; - *asc = stat_table[i][2]; - *ascq = stat_table[i][3]; - goto translate_done; - } - } - /* No error? Undecoded? */ - if (verbose) - printk(KERN_WARNING "ata%u: no sense translation for " - "status: 0x%02x\n", id, drv_stat); - - /* We need a sensible error return here, which is tricky, and one - that won't cause people to do things like return a disk wrongly */ - *sk = ABORTED_COMMAND; - *asc = 0x00; - *ascq = 0x00; - - translate_done: - if (verbose) - printk(KERN_ERR "ata%u: translated ATA stat/err 0x%02x/%02x " - "to SCSI SK/ASC/ASCQ 0x%x/%02x/%02x\n", - id, drv_stat, drv_err, *sk, *asc, *ascq); - return; -} - -/* - * ata_gen_ata_desc_sense - Generate check condition sense block. - * @qc: Command that completed. - * - * This function is specific to the ATA descriptor format sense - * block specified for the ATA pass through commands. Regardless - * of whether the command errored or not, return a sense - * block. Copy all controller registers into the sense - * block. Clear sense key, ASC & ASCQ if there is no error. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ -void ata_gen_ata_desc_sense(struct ata_queued_cmd *qc) -{ - struct scsi_cmnd *cmd = qc->scsicmd; - struct ata_taskfile *tf = &qc->result_tf; - unsigned char *sb = cmd->sense_buffer; - unsigned char *desc = sb + 8; - int verbose = qc->ap->ops->error_handler == NULL; - - memset(sb, 0, SCSI_SENSE_BUFFERSIZE); - - cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; - - /* - * Use ata_to_sense_error() to map status register bits - * onto sense key, asc & ascq. - */ - if (qc->err_mask || - tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) { - ata_to_sense_error(qc->ap->id, tf->command, tf->feature, - &sb[1], &sb[2], &sb[3], verbose); - sb[1] &= 0x0f; - } - - /* - * Sense data is current and format is descriptor. - */ - sb[0] = 0x72; - - desc[0] = 0x09; - - /* - * Set length of additional sense data. - * Since we only populate descriptor 0, the total - * length is the same (fixed) length as descriptor 0. - */ - desc[1] = sb[7] = 14; - - /* - * Copy registers into sense buffer. - */ - desc[2] = 0x00; - desc[3] = tf->feature; /* == error reg */ - desc[5] = tf->nsect; - desc[7] = tf->lbal; - desc[9] = tf->lbam; - desc[11] = tf->lbah; - desc[12] = tf->device; - desc[13] = tf->command; /* == status reg */ - - /* - * Fill in Extend bit, and the high order bytes - * if applicable. - */ - if (tf->flags & ATA_TFLAG_LBA48) { - desc[2] |= 0x01; - desc[4] = tf->hob_nsect; - desc[6] = tf->hob_lbal; - desc[8] = tf->hob_lbam; - desc[10] = tf->hob_lbah; - } -} - -/** - * ata_gen_fixed_sense - generate a SCSI fixed sense block - * @qc: Command that we are erroring out - * - * Leverage ata_to_sense_error() to give us the codes. Fit our - * LBA in here if there's room. - * - * LOCKING: - * inherited from caller - */ -void ata_gen_fixed_sense(struct ata_queued_cmd *qc) -{ - struct scsi_cmnd *cmd = qc->scsicmd; - struct ata_taskfile *tf = &qc->result_tf; - unsigned char *sb = cmd->sense_buffer; - int verbose = qc->ap->ops->error_handler == NULL; - - memset(sb, 0, SCSI_SENSE_BUFFERSIZE); - - cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; - - /* - * Use ata_to_sense_error() to map status register bits - * onto sense key, asc & ascq. - */ - if (qc->err_mask || - tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) { - ata_to_sense_error(qc->ap->id, tf->command, tf->feature, - &sb[2], &sb[12], &sb[13], verbose); - sb[2] &= 0x0f; - } - - sb[0] = 0x70; - sb[7] = 0x0a; - - if (tf->flags & ATA_TFLAG_LBA48) { - /* TODO: find solution for LBA48 descriptors */ - } - - else if (tf->flags & ATA_TFLAG_LBA) { - /* A small (28b) LBA will fit in the 32b info field */ - sb[0] |= 0x80; /* set valid bit */ - sb[3] = tf->device & 0x0f; - sb[4] = tf->lbah; - sb[5] = tf->lbam; - sb[6] = tf->lbal; - } - - else { - /* TODO: C/H/S */ - } -} - -static void ata_scsi_sdev_config(struct scsi_device *sdev) -{ - sdev->use_10_for_rw = 1; - sdev->use_10_for_ms = 1; -} - -static void ata_scsi_dev_config(struct scsi_device *sdev, - struct ata_device *dev) -{ - unsigned int max_sectors; - - /* TODO: 2048 is an arbitrary number, not the - * hardware maximum. This should be increased to - * 65534 when Jens Axboe's patch for dynamically - * determining max_sectors is merged. - */ - max_sectors = ATA_MAX_SECTORS; - if (dev->flags & ATA_DFLAG_LBA48) - max_sectors = ATA_MAX_SECTORS_LBA48; - if (dev->max_sectors) - max_sectors = dev->max_sectors; - - blk_queue_max_sectors(sdev->request_queue, max_sectors); - - /* - * SATA DMA transfers must be multiples of 4 byte, so - * we need to pad ATAPI transfers using an extra sg. - * Decrement max hw segments accordingly. - */ - if (dev->class == ATA_DEV_ATAPI) { - request_queue_t *q = sdev->request_queue; - blk_queue_max_hw_segments(q, q->max_hw_segments - 1); - } - - if (dev->flags & ATA_DFLAG_NCQ) { - int depth; - - depth = min(sdev->host->can_queue, ata_id_queue_depth(dev->id)); - depth = min(ATA_MAX_QUEUE - 1, depth); - scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, depth); - } -} - -/** - * ata_scsi_slave_config - Set SCSI device attributes - * @sdev: SCSI device to examine - * - * This is called before we actually start reading - * and writing to the device, to configure certain - * SCSI mid-layer behaviors. - * - * LOCKING: - * Defined by SCSI layer. We don't really care. - */ - -int ata_scsi_slave_config(struct scsi_device *sdev) -{ - struct ata_port *ap = ata_shost_to_port(sdev->host); - struct ata_device *dev = __ata_scsi_find_dev(ap, sdev); - - ata_scsi_sdev_config(sdev); - - blk_queue_max_phys_segments(sdev->request_queue, LIBATA_MAX_PRD); - - if (dev) - ata_scsi_dev_config(sdev, dev); - - return 0; /* scsi layer doesn't check return value, sigh */ -} - -/** - * ata_scsi_slave_destroy - SCSI device is about to be destroyed - * @sdev: SCSI device to be destroyed - * - * @sdev is about to be destroyed for hot/warm unplugging. If - * this unplugging was initiated by libata as indicated by NULL - * dev->sdev, this function doesn't have to do anything. - * Otherwise, SCSI layer initiated warm-unplug is in progress. - * Clear dev->sdev, schedule the device for ATA detach and invoke - * EH. - * - * LOCKING: - * Defined by SCSI layer. We don't really care. - */ -void ata_scsi_slave_destroy(struct scsi_device *sdev) -{ - struct ata_port *ap = ata_shost_to_port(sdev->host); - unsigned long flags; - struct ata_device *dev; - - if (!ap->ops->error_handler) - return; - - spin_lock_irqsave(ap->lock, flags); - dev = __ata_scsi_find_dev(ap, sdev); - if (dev && dev->sdev) { - /* SCSI device already in CANCEL state, no need to offline it */ - dev->sdev = NULL; - dev->flags |= ATA_DFLAG_DETACH; - ata_port_schedule_eh(ap); - } - spin_unlock_irqrestore(ap->lock, flags); -} - -/** - * ata_scsi_change_queue_depth - SCSI callback for queue depth config - * @sdev: SCSI device to configure queue depth for - * @queue_depth: new queue depth - * - * This is libata standard hostt->change_queue_depth callback. - * SCSI will call into this callback when user tries to set queue - * depth via sysfs. - * - * LOCKING: - * SCSI layer (we don't care) - * - * RETURNS: - * Newly configured queue depth. - */ -int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth) -{ - struct ata_port *ap = ata_shost_to_port(sdev->host); - struct ata_device *dev; - int max_depth; - - if (queue_depth < 1) - return sdev->queue_depth; - - dev = ata_scsi_find_dev(ap, sdev); - if (!dev || !ata_dev_enabled(dev)) - return sdev->queue_depth; - - max_depth = min(sdev->host->can_queue, ata_id_queue_depth(dev->id)); - max_depth = min(ATA_MAX_QUEUE - 1, max_depth); - if (queue_depth > max_depth) - queue_depth = max_depth; - - scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, queue_depth); - return queue_depth; -} - -/** - * ata_scsi_start_stop_xlat - Translate SCSI START STOP UNIT command - * @qc: Storage for translated ATA taskfile - * @scsicmd: SCSI command to translate - * - * Sets up an ATA taskfile to issue STANDBY (to stop) or READ VERIFY - * (to start). Perhaps these commands should be preceded by - * CHECK POWER MODE to see what power mode the device is already in. - * [See SAT revision 5 at www.t10.org] - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * Zero on success, non-zero on error. - */ - -static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc, - const u8 *scsicmd) -{ - struct ata_taskfile *tf = &qc->tf; - - tf->flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR; - tf->protocol = ATA_PROT_NODATA; - if (scsicmd[1] & 0x1) { - ; /* ignore IMMED bit, violates sat-r05 */ - } - if (scsicmd[4] & 0x2) - goto invalid_fld; /* LOEJ bit set not supported */ - if (((scsicmd[4] >> 4) & 0xf) != 0) - goto invalid_fld; /* power conditions not supported */ - if (scsicmd[4] & 0x1) { - tf->nsect = 1; /* 1 sector, lba=0 */ - - if (qc->dev->flags & ATA_DFLAG_LBA) { - tf->flags |= ATA_TFLAG_LBA; - - tf->lbah = 0x0; - tf->lbam = 0x0; - tf->lbal = 0x0; - tf->device |= ATA_LBA; - } else { - /* CHS */ - tf->lbal = 0x1; /* sect */ - tf->lbam = 0x0; /* cyl low */ - tf->lbah = 0x0; /* cyl high */ - } - - tf->command = ATA_CMD_VERIFY; /* READ VERIFY */ - } else { - tf->nsect = 0; /* time period value (0 implies now) */ - tf->command = ATA_CMD_STANDBY; - /* Consider: ATA STANDBY IMMEDIATE command */ - } - /* - * Standby and Idle condition timers could be implemented but that - * would require libata to implement the Power condition mode page - * and allow the user to change it. Changing mode pages requires - * MODE SELECT to be implemented. - */ - - return 0; - -invalid_fld: - ata_scsi_set_sense(qc->scsicmd, ILLEGAL_REQUEST, 0x24, 0x0); - /* "Invalid field in cbd" */ - return 1; -} - - -/** - * ata_scsi_flush_xlat - Translate SCSI SYNCHRONIZE CACHE command - * @qc: Storage for translated ATA taskfile - * @scsicmd: SCSI command to translate (ignored) - * - * Sets up an ATA taskfile to issue FLUSH CACHE or - * FLUSH CACHE EXT. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * Zero on success, non-zero on error. - */ - -static unsigned int ata_scsi_flush_xlat(struct ata_queued_cmd *qc, const u8 *scsicmd) -{ - struct ata_taskfile *tf = &qc->tf; - - tf->flags |= ATA_TFLAG_DEVICE; - tf->protocol = ATA_PROT_NODATA; - - if ((qc->dev->flags & ATA_DFLAG_LBA48) && - (ata_id_has_flush_ext(qc->dev->id))) - tf->command = ATA_CMD_FLUSH_EXT; - else - tf->command = ATA_CMD_FLUSH; - - return 0; -} - -/** - * scsi_6_lba_len - Get LBA and transfer length - * @scsicmd: SCSI command to translate - * - * Calculate LBA and transfer length for 6-byte commands. - * - * RETURNS: - * @plba: the LBA - * @plen: the transfer length - */ - -static void scsi_6_lba_len(const u8 *scsicmd, u64 *plba, u32 *plen) -{ - u64 lba = 0; - u32 len = 0; - - VPRINTK("six-byte command\n"); - - lba |= ((u64)scsicmd[2]) << 8; - lba |= ((u64)scsicmd[3]); - - len |= ((u32)scsicmd[4]); - - *plba = lba; - *plen = len; -} - -/** - * scsi_10_lba_len - Get LBA and transfer length - * @scsicmd: SCSI command to translate - * - * Calculate LBA and transfer length for 10-byte commands. - * - * RETURNS: - * @plba: the LBA - * @plen: the transfer length - */ - -static void scsi_10_lba_len(const u8 *scsicmd, u64 *plba, u32 *plen) -{ - u64 lba = 0; - u32 len = 0; - - VPRINTK("ten-byte command\n"); - - lba |= ((u64)scsicmd[2]) << 24; - lba |= ((u64)scsicmd[3]) << 16; - lba |= ((u64)scsicmd[4]) << 8; - lba |= ((u64)scsicmd[5]); - - len |= ((u32)scsicmd[7]) << 8; - len |= ((u32)scsicmd[8]); - - *plba = lba; - *plen = len; -} - -/** - * scsi_16_lba_len - Get LBA and transfer length - * @scsicmd: SCSI command to translate - * - * Calculate LBA and transfer length for 16-byte commands. - * - * RETURNS: - * @plba: the LBA - * @plen: the transfer length - */ - -static void scsi_16_lba_len(const u8 *scsicmd, u64 *plba, u32 *plen) -{ - u64 lba = 0; - u32 len = 0; - - VPRINTK("sixteen-byte command\n"); - - lba |= ((u64)scsicmd[2]) << 56; - lba |= ((u64)scsicmd[3]) << 48; - lba |= ((u64)scsicmd[4]) << 40; - lba |= ((u64)scsicmd[5]) << 32; - lba |= ((u64)scsicmd[6]) << 24; - lba |= ((u64)scsicmd[7]) << 16; - lba |= ((u64)scsicmd[8]) << 8; - lba |= ((u64)scsicmd[9]); - - len |= ((u32)scsicmd[10]) << 24; - len |= ((u32)scsicmd[11]) << 16; - len |= ((u32)scsicmd[12]) << 8; - len |= ((u32)scsicmd[13]); - - *plba = lba; - *plen = len; -} - -/** - * ata_scsi_verify_xlat - Translate SCSI VERIFY command into an ATA one - * @qc: Storage for translated ATA taskfile - * @scsicmd: SCSI command to translate - * - * Converts SCSI VERIFY command to an ATA READ VERIFY command. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * Zero on success, non-zero on error. - */ - -static unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc, const u8 *scsicmd) -{ - struct ata_taskfile *tf = &qc->tf; - struct ata_device *dev = qc->dev; - u64 dev_sectors = qc->dev->n_sectors; - u64 block; - u32 n_block; - - tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; - tf->protocol = ATA_PROT_NODATA; - - if (scsicmd[0] == VERIFY) - scsi_10_lba_len(scsicmd, &block, &n_block); - else if (scsicmd[0] == VERIFY_16) - scsi_16_lba_len(scsicmd, &block, &n_block); - else - goto invalid_fld; - - if (!n_block) - goto nothing_to_do; - if (block >= dev_sectors) - goto out_of_range; - if ((block + n_block) > dev_sectors) - goto out_of_range; - - if (dev->flags & ATA_DFLAG_LBA) { - tf->flags |= ATA_TFLAG_LBA; - - if (lba_28_ok(block, n_block)) { - /* use LBA28 */ - tf->command = ATA_CMD_VERIFY; - tf->device |= (block >> 24) & 0xf; - } else if (lba_48_ok(block, n_block)) { - if (!(dev->flags & ATA_DFLAG_LBA48)) - goto out_of_range; - - /* use LBA48 */ - tf->flags |= ATA_TFLAG_LBA48; - tf->command = ATA_CMD_VERIFY_EXT; - - tf->hob_nsect = (n_block >> 8) & 0xff; - - tf->hob_lbah = (block >> 40) & 0xff; - tf->hob_lbam = (block >> 32) & 0xff; - tf->hob_lbal = (block >> 24) & 0xff; - } else - /* request too large even for LBA48 */ - goto out_of_range; - - tf->nsect = n_block & 0xff; - - tf->lbah = (block >> 16) & 0xff; - tf->lbam = (block >> 8) & 0xff; - tf->lbal = block & 0xff; - - tf->device |= ATA_LBA; - } else { - /* CHS */ - u32 sect, head, cyl, track; - - if (!lba_28_ok(block, n_block)) - goto out_of_range; - - /* Convert LBA to CHS */ - track = (u32)block / dev->sectors; - cyl = track / dev->heads; - head = track % dev->heads; - sect = (u32)block % dev->sectors + 1; - - DPRINTK("block %u track %u cyl %u head %u sect %u\n", - (u32)block, track, cyl, head, sect); - - /* Check whether the converted CHS can fit. - Cylinder: 0-65535 - Head: 0-15 - Sector: 1-255*/ - if ((cyl >> 16) || (head >> 4) || (sect >> 8) || (!sect)) - goto out_of_range; - - tf->command = ATA_CMD_VERIFY; - tf->nsect = n_block & 0xff; /* Sector count 0 means 256 sectors */ - tf->lbal = sect; - tf->lbam = cyl; - tf->lbah = cyl >> 8; - tf->device |= head; - } - - return 0; - -invalid_fld: - ata_scsi_set_sense(qc->scsicmd, ILLEGAL_REQUEST, 0x24, 0x0); - /* "Invalid field in cbd" */ - return 1; - -out_of_range: - ata_scsi_set_sense(qc->scsicmd, ILLEGAL_REQUEST, 0x21, 0x0); - /* "Logical Block Address out of range" */ - return 1; - -nothing_to_do: - qc->scsicmd->result = SAM_STAT_GOOD; - return 1; -} - -/** - * ata_scsi_rw_xlat - Translate SCSI r/w command into an ATA one - * @qc: Storage for translated ATA taskfile - * @scsicmd: SCSI command to translate - * - * Converts any of six SCSI read/write commands into the - * ATA counterpart, including starting sector (LBA), - * sector count, and taking into account the device's LBA48 - * support. - * - * Commands %READ_6, %READ_10, %READ_16, %WRITE_6, %WRITE_10, and - * %WRITE_16 are currently supported. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * Zero on success, non-zero on error. - */ - -static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, const u8 *scsicmd) -{ - struct ata_taskfile *tf = &qc->tf; - struct ata_device *dev = qc->dev; - u64 block; - u32 n_block; - - qc->flags |= ATA_QCFLAG_IO; - tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; - - if (scsicmd[0] == WRITE_10 || scsicmd[0] == WRITE_6 || - scsicmd[0] == WRITE_16) - tf->flags |= ATA_TFLAG_WRITE; - - /* Calculate the SCSI LBA, transfer length and FUA. */ - switch (scsicmd[0]) { - case READ_10: - case WRITE_10: - scsi_10_lba_len(scsicmd, &block, &n_block); - if (unlikely(scsicmd[1] & (1 << 3))) - tf->flags |= ATA_TFLAG_FUA; - break; - case READ_6: - case WRITE_6: - scsi_6_lba_len(scsicmd, &block, &n_block); - - /* for 6-byte r/w commands, transfer length 0 - * means 256 blocks of data, not 0 block. - */ - if (!n_block) - n_block = 256; - break; - case READ_16: - case WRITE_16: - scsi_16_lba_len(scsicmd, &block, &n_block); - if (unlikely(scsicmd[1] & (1 << 3))) - tf->flags |= ATA_TFLAG_FUA; - break; - default: - DPRINTK("no-byte command\n"); - goto invalid_fld; - } - - /* Check and compose ATA command */ - if (!n_block) - /* For 10-byte and 16-byte SCSI R/W commands, transfer - * length 0 means transfer 0 block of data. - * However, for ATA R/W commands, sector count 0 means - * 256 or 65536 sectors, not 0 sectors as in SCSI. - * - * WARNING: one or two older ATA drives treat 0 as 0... - */ - goto nothing_to_do; - - if ((dev->flags & (ATA_DFLAG_PIO | ATA_DFLAG_NCQ)) == ATA_DFLAG_NCQ) { - /* yay, NCQ */ - if (!lba_48_ok(block, n_block)) - goto out_of_range; - - tf->protocol = ATA_PROT_NCQ; - tf->flags |= ATA_TFLAG_LBA | ATA_TFLAG_LBA48; - - if (tf->flags & ATA_TFLAG_WRITE) - tf->command = ATA_CMD_FPDMA_WRITE; - else - tf->command = ATA_CMD_FPDMA_READ; - - qc->nsect = n_block; - - tf->nsect = qc->tag << 3; - tf->hob_feature = (n_block >> 8) & 0xff; - tf->feature = n_block & 0xff; - - tf->hob_lbah = (block >> 40) & 0xff; - tf->hob_lbam = (block >> 32) & 0xff; - tf->hob_lbal = (block >> 24) & 0xff; - tf->lbah = (block >> 16) & 0xff; - tf->lbam = (block >> 8) & 0xff; - tf->lbal = block & 0xff; - - tf->device = 1 << 6; - if (tf->flags & ATA_TFLAG_FUA) - tf->device |= 1 << 7; - } else if (dev->flags & ATA_DFLAG_LBA) { - tf->flags |= ATA_TFLAG_LBA; - - if (lba_28_ok(block, n_block)) { - /* use LBA28 */ - tf->device |= (block >> 24) & 0xf; - } else if (lba_48_ok(block, n_block)) { - if (!(dev->flags & ATA_DFLAG_LBA48)) - goto out_of_range; - - /* use LBA48 */ - tf->flags |= ATA_TFLAG_LBA48; - - tf->hob_nsect = (n_block >> 8) & 0xff; - - tf->hob_lbah = (block >> 40) & 0xff; - tf->hob_lbam = (block >> 32) & 0xff; - tf->hob_lbal = (block >> 24) & 0xff; - } else - /* request too large even for LBA48 */ - goto out_of_range; - - if (unlikely(ata_rwcmd_protocol(qc) < 0)) - goto invalid_fld; - - qc->nsect = n_block; - tf->nsect = n_block & 0xff; - - tf->lbah = (block >> 16) & 0xff; - tf->lbam = (block >> 8) & 0xff; - tf->lbal = block & 0xff; - - tf->device |= ATA_LBA; - } else { - /* CHS */ - u32 sect, head, cyl, track; - - /* The request -may- be too large for CHS addressing. */ - if (!lba_28_ok(block, n_block)) - goto out_of_range; - - if (unlikely(ata_rwcmd_protocol(qc) < 0)) - goto invalid_fld; - - /* Convert LBA to CHS */ - track = (u32)block / dev->sectors; - cyl = track / dev->heads; - head = track % dev->heads; - sect = (u32)block % dev->sectors + 1; - - DPRINTK("block %u track %u cyl %u head %u sect %u\n", - (u32)block, track, cyl, head, sect); - - /* Check whether the converted CHS can fit. - Cylinder: 0-65535 - Head: 0-15 - Sector: 1-255*/ - if ((cyl >> 16) || (head >> 4) || (sect >> 8) || (!sect)) - goto out_of_range; - - qc->nsect = n_block; - tf->nsect = n_block & 0xff; /* Sector count 0 means 256 sectors */ - tf->lbal = sect; - tf->lbam = cyl; - tf->lbah = cyl >> 8; - tf->device |= head; - } - - return 0; - -invalid_fld: - ata_scsi_set_sense(qc->scsicmd, ILLEGAL_REQUEST, 0x24, 0x0); - /* "Invalid field in cbd" */ - return 1; - -out_of_range: - ata_scsi_set_sense(qc->scsicmd, ILLEGAL_REQUEST, 0x21, 0x0); - /* "Logical Block Address out of range" */ - return 1; - -nothing_to_do: - qc->scsicmd->result = SAM_STAT_GOOD; - return 1; -} - -static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) -{ - struct scsi_cmnd *cmd = qc->scsicmd; - u8 *cdb = cmd->cmnd; - int need_sense = (qc->err_mask != 0); - - /* We snoop the SET_FEATURES - Write Cache ON/OFF command, and - * schedule EH_REVALIDATE operation to update the IDENTIFY DEVICE - * cache - */ - if (!need_sense && (qc->tf.command == ATA_CMD_SET_FEATURES) && - ((qc->tf.feature == SETFEATURES_WC_ON) || - (qc->tf.feature == SETFEATURES_WC_OFF))) { - qc->ap->eh_info.action |= ATA_EH_REVALIDATE; - ata_port_schedule_eh(qc->ap); - } - - /* For ATA pass thru (SAT) commands, generate a sense block if - * user mandated it or if there's an error. Note that if we - * generate because the user forced us to, a check condition - * is generated and the ATA register values are returned - * whether the command completed successfully or not. If there - * was no error, SK, ASC and ASCQ will all be zero. - */ - if (((cdb[0] == ATA_16) || (cdb[0] == ATA_12)) && - ((cdb[2] & 0x20) || need_sense)) { - ata_gen_ata_desc_sense(qc); - } else { - if (!need_sense) { - cmd->result = SAM_STAT_GOOD; - } else { - /* TODO: decide which descriptor format to use - * for 48b LBA devices and call that here - * instead of the fixed desc, which is only - * good for smaller LBA (and maybe CHS?) - * devices. - */ - ata_gen_fixed_sense(qc); - } - } - - if (need_sense && !qc->ap->ops->error_handler) - ata_dump_status(qc->ap->id, &qc->result_tf); - - qc->scsidone(cmd); - - ata_qc_free(qc); -} - -/** - * ata_scmd_need_defer - Check whether we need to defer scmd - * @dev: ATA device to which the command is addressed - * @is_io: Is the command IO (and thus possibly NCQ)? - * - * NCQ and non-NCQ commands cannot run together. As upper layer - * only knows the queue depth, we are responsible for maintaining - * exclusion. This function checks whether a new command can be - * issued to @dev. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * 1 if deferring is needed, 0 otherwise. - */ -static int ata_scmd_need_defer(struct ata_device *dev, int is_io) -{ - struct ata_port *ap = dev->ap; - - if (!(dev->flags & ATA_DFLAG_NCQ)) - return 0; - - if (is_io) { - if (!ata_tag_valid(ap->active_tag)) - return 0; - } else { - if (!ata_tag_valid(ap->active_tag) && !ap->sactive) - return 0; - } - return 1; -} - -/** - * ata_scsi_translate - Translate then issue SCSI command to ATA device - * @dev: ATA device to which the command is addressed - * @cmd: SCSI command to execute - * @done: SCSI command completion function - * @xlat_func: Actor which translates @cmd to an ATA taskfile - * - * Our ->queuecommand() function has decided that the SCSI - * command issued can be directly translated into an ATA - * command, rather than handled internally. - * - * This function sets up an ata_queued_cmd structure for the - * SCSI command, and sends that ata_queued_cmd to the hardware. - * - * The xlat_func argument (actor) returns 0 if ready to execute - * ATA command, else 1 to finish translation. If 1 is returned - * then cmd->result (and possibly cmd->sense_buffer) are assumed - * to be set reflecting an error condition or clean (early) - * termination. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * 0 on success, SCSI_ML_QUEUE_DEVICE_BUSY if the command - * needs to be deferred. - */ -static int ata_scsi_translate(struct ata_device *dev, struct scsi_cmnd *cmd, - void (*done)(struct scsi_cmnd *), - ata_xlat_func_t xlat_func) -{ - struct ata_queued_cmd *qc; - u8 *scsicmd = cmd->cmnd; - int is_io = xlat_func == ata_scsi_rw_xlat; - - VPRINTK("ENTER\n"); - - if (unlikely(ata_scmd_need_defer(dev, is_io))) - goto defer; - - qc = ata_scsi_qc_new(dev, cmd, done); - if (!qc) - goto err_mem; - - /* data is present; dma-map it */ - if (cmd->sc_data_direction == DMA_FROM_DEVICE || - cmd->sc_data_direction == DMA_TO_DEVICE) { - if (unlikely(cmd->request_bufflen < 1)) { - ata_dev_printk(dev, KERN_WARNING, - "WARNING: zero len r/w req\n"); - goto err_did; - } - - if (cmd->use_sg) - ata_sg_init(qc, cmd->request_buffer, cmd->use_sg); - else - ata_sg_init_one(qc, cmd->request_buffer, - cmd->request_bufflen); - - qc->dma_dir = cmd->sc_data_direction; - } - - qc->complete_fn = ata_scsi_qc_complete; - - if (xlat_func(qc, scsicmd)) - goto early_finish; - - /* select device, send command to hardware */ - ata_qc_issue(qc); - - VPRINTK("EXIT\n"); - return 0; - -early_finish: - ata_qc_free(qc); - done(cmd); - DPRINTK("EXIT - early finish (good or error)\n"); - return 0; - -err_did: - ata_qc_free(qc); -err_mem: - cmd->result = (DID_ERROR << 16); - done(cmd); - DPRINTK("EXIT - internal\n"); - return 0; - -defer: - DPRINTK("EXIT - defer\n"); - return SCSI_MLQUEUE_DEVICE_BUSY; -} - -/** - * ata_scsi_rbuf_get - Map response buffer. - * @cmd: SCSI command containing buffer to be mapped. - * @buf_out: Pointer to mapped area. - * - * Maps buffer contained within SCSI command @cmd. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * Length of response buffer. - */ - -static unsigned int ata_scsi_rbuf_get(struct scsi_cmnd *cmd, u8 **buf_out) -{ - u8 *buf; - unsigned int buflen; - - if (cmd->use_sg) { - struct scatterlist *sg; - - sg = (struct scatterlist *) cmd->request_buffer; - buf = kmap_atomic(sg->page, KM_USER0) + sg->offset; - buflen = sg->length; - } else { - buf = cmd->request_buffer; - buflen = cmd->request_bufflen; - } - - *buf_out = buf; - return buflen; -} - -/** - * ata_scsi_rbuf_put - Unmap response buffer. - * @cmd: SCSI command containing buffer to be unmapped. - * @buf: buffer to unmap - * - * Unmaps response buffer contained within @cmd. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -static inline void ata_scsi_rbuf_put(struct scsi_cmnd *cmd, u8 *buf) -{ - if (cmd->use_sg) { - struct scatterlist *sg; - - sg = (struct scatterlist *) cmd->request_buffer; - kunmap_atomic(buf - sg->offset, KM_USER0); - } -} - -/** - * ata_scsi_rbuf_fill - wrapper for SCSI command simulators - * @args: device IDENTIFY data / SCSI command of interest. - * @actor: Callback hook for desired SCSI command simulator - * - * Takes care of the hard work of simulating a SCSI command... - * Mapping the response buffer, calling the command's handler, - * and handling the handler's return value. This return value - * indicates whether the handler wishes the SCSI command to be - * completed successfully (0), or not (in which case cmd->result - * and sense buffer are assumed to be set). - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -void ata_scsi_rbuf_fill(struct ata_scsi_args *args, - unsigned int (*actor) (struct ata_scsi_args *args, - u8 *rbuf, unsigned int buflen)) -{ - u8 *rbuf; - unsigned int buflen, rc; - struct scsi_cmnd *cmd = args->cmd; - - buflen = ata_scsi_rbuf_get(cmd, &rbuf); - memset(rbuf, 0, buflen); - rc = actor(args, rbuf, buflen); - ata_scsi_rbuf_put(cmd, rbuf); - - if (rc == 0) - cmd->result = SAM_STAT_GOOD; - args->done(cmd); -} - -/** - * ata_scsiop_inq_std - Simulate INQUIRY command - * @args: device IDENTIFY data / SCSI command of interest. - * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. - * @buflen: Response buffer length. - * - * Returns standard device identification data associated - * with non-VPD INQUIRY command output. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf, - unsigned int buflen) -{ - u8 hdr[] = { - TYPE_DISK, - 0, - 0x5, /* claim SPC-3 version compatibility */ - 2, - 95 - 4 - }; - - /* set scsi removeable (RMB) bit per ata bit */ - if (ata_id_removeable(args->id)) - hdr[1] |= (1 << 7); - - VPRINTK("ENTER\n"); - - memcpy(rbuf, hdr, sizeof(hdr)); - - if (buflen > 35) { - memcpy(&rbuf[8], "ATA ", 8); - ata_id_string(args->id, &rbuf[16], ATA_ID_PROD_OFS, 16); - ata_id_string(args->id, &rbuf[32], ATA_ID_FW_REV_OFS, 4); - if (rbuf[32] == 0 || rbuf[32] == ' ') - memcpy(&rbuf[32], "n/a ", 4); - } - - if (buflen > 63) { - const u8 versions[] = { - 0x60, /* SAM-3 (no version claimed) */ - - 0x03, - 0x20, /* SBC-2 (no version claimed) */ - - 0x02, - 0x60 /* SPC-3 (no version claimed) */ - }; - - memcpy(rbuf + 59, versions, sizeof(versions)); - } - - return 0; -} - -/** - * ata_scsiop_inq_00 - Simulate INQUIRY VPD page 0, list of pages - * @args: device IDENTIFY data / SCSI command of interest. - * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. - * @buflen: Response buffer length. - * - * Returns list of inquiry VPD pages available. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf, - unsigned int buflen) -{ - const u8 pages[] = { - 0x00, /* page 0x00, this page */ - 0x80, /* page 0x80, unit serial no page */ - 0x83 /* page 0x83, device ident page */ - }; - rbuf[3] = sizeof(pages); /* number of supported VPD pages */ - - if (buflen > 6) - memcpy(rbuf + 4, pages, sizeof(pages)); - - return 0; -} - -/** - * ata_scsiop_inq_80 - Simulate INQUIRY VPD page 80, device serial number - * @args: device IDENTIFY data / SCSI command of interest. - * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. - * @buflen: Response buffer length. - * - * Returns ATA device serial number. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf, - unsigned int buflen) -{ - const u8 hdr[] = { - 0, - 0x80, /* this page code */ - 0, - ATA_SERNO_LEN, /* page len */ - }; - memcpy(rbuf, hdr, sizeof(hdr)); - - if (buflen > (ATA_SERNO_LEN + 4 - 1)) - ata_id_string(args->id, (unsigned char *) &rbuf[4], - ATA_ID_SERNO_OFS, ATA_SERNO_LEN); - - return 0; -} - -/** - * ata_scsiop_inq_83 - Simulate INQUIRY VPD page 83, device identity - * @args: device IDENTIFY data / SCSI command of interest. - * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. - * @buflen: Response buffer length. - * - * Yields two logical unit device identification designators: - * - vendor specific ASCII containing the ATA serial number - * - SAT defined "t10 vendor id based" containing ASCII vendor - * name ("ATA "), model and serial numbers. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf, - unsigned int buflen) -{ - int num; - const int sat_model_serial_desc_len = 68; - const int ata_model_byte_len = 40; - - rbuf[1] = 0x83; /* this page code */ - num = 4; - - if (buflen > (ATA_SERNO_LEN + num + 3)) { - /* piv=0, assoc=lu, code_set=ACSII, designator=vendor */ - rbuf[num + 0] = 2; - rbuf[num + 3] = ATA_SERNO_LEN; - num += 4; - ata_id_string(args->id, (unsigned char *) rbuf + num, - ATA_ID_SERNO_OFS, ATA_SERNO_LEN); - num += ATA_SERNO_LEN; - } - if (buflen > (sat_model_serial_desc_len + num + 3)) { - /* SAT defined lu model and serial numbers descriptor */ - /* piv=0, assoc=lu, code_set=ACSII, designator=t10 vendor id */ - rbuf[num + 0] = 2; - rbuf[num + 1] = 1; - rbuf[num + 3] = sat_model_serial_desc_len; - num += 4; - memcpy(rbuf + num, "ATA ", 8); - num += 8; - ata_id_string(args->id, (unsigned char *) rbuf + num, - ATA_ID_PROD_OFS, ata_model_byte_len); - num += ata_model_byte_len; - ata_id_string(args->id, (unsigned char *) rbuf + num, - ATA_ID_SERNO_OFS, ATA_SERNO_LEN); - num += ATA_SERNO_LEN; - } - rbuf[3] = num - 4; /* page len (assume less than 256 bytes) */ - return 0; -} - -/** - * ata_scsiop_noop - Command handler that simply returns success. - * @args: device IDENTIFY data / SCSI command of interest. - * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. - * @buflen: Response buffer length. - * - * No operation. Simply returns success to caller, to indicate - * that the caller should successfully complete this SCSI command. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -unsigned int ata_scsiop_noop(struct ata_scsi_args *args, u8 *rbuf, - unsigned int buflen) -{ - VPRINTK("ENTER\n"); - return 0; -} - -/** - * ata_msense_push - Push data onto MODE SENSE data output buffer - * @ptr_io: (input/output) Location to store more output data - * @last: End of output data buffer - * @buf: Pointer to BLOB being added to output buffer - * @buflen: Length of BLOB - * - * Store MODE SENSE data on an output buffer. - * - * LOCKING: - * None. - */ - -static void ata_msense_push(u8 **ptr_io, const u8 *last, - const u8 *buf, unsigned int buflen) -{ - u8 *ptr = *ptr_io; - - if ((ptr + buflen - 1) > last) - return; - - memcpy(ptr, buf, buflen); - - ptr += buflen; - - *ptr_io = ptr; -} - -/** - * ata_msense_caching - Simulate MODE SENSE caching info page - * @id: device IDENTIFY data - * @ptr_io: (input/output) Location to store more output data - * @last: End of output data buffer - * - * Generate a caching info page, which conditionally indicates - * write caching to the SCSI layer, depending on device - * capabilities. - * - * LOCKING: - * None. - */ - -static unsigned int ata_msense_caching(u16 *id, u8 **ptr_io, - const u8 *last) -{ - u8 page[CACHE_MPAGE_LEN]; - - memcpy(page, def_cache_mpage, sizeof(page)); - if (ata_id_wcache_enabled(id)) - page[2] |= (1 << 2); /* write cache enable */ - if (!ata_id_rahead_enabled(id)) - page[12] |= (1 << 5); /* disable read ahead */ - - ata_msense_push(ptr_io, last, page, sizeof(page)); - return sizeof(page); -} - -/** - * ata_msense_ctl_mode - Simulate MODE SENSE control mode page - * @dev: Device associated with this MODE SENSE command - * @ptr_io: (input/output) Location to store more output data - * @last: End of output data buffer - * - * Generate a generic MODE SENSE control mode page. - * - * LOCKING: - * None. - */ - -static unsigned int ata_msense_ctl_mode(u8 **ptr_io, const u8 *last) -{ - ata_msense_push(ptr_io, last, def_control_mpage, - sizeof(def_control_mpage)); - return sizeof(def_control_mpage); -} - -/** - * ata_msense_rw_recovery - Simulate MODE SENSE r/w error recovery page - * @dev: Device associated with this MODE SENSE command - * @ptr_io: (input/output) Location to store more output data - * @last: End of output data buffer - * - * Generate a generic MODE SENSE r/w error recovery page. - * - * LOCKING: - * None. - */ - -static unsigned int ata_msense_rw_recovery(u8 **ptr_io, const u8 *last) -{ - - ata_msense_push(ptr_io, last, def_rw_recovery_mpage, - sizeof(def_rw_recovery_mpage)); - return sizeof(def_rw_recovery_mpage); -} - -/* - * We can turn this into a real blacklist if it's needed, for now just - * blacklist any Maxtor BANC1G10 revision firmware - */ -static int ata_dev_supports_fua(u16 *id) -{ - unsigned char model[41], fw[9]; - - if (!libata_fua) - return 0; - if (!ata_id_has_fua(id)) - return 0; - - ata_id_c_string(id, model, ATA_ID_PROD_OFS, sizeof(model)); - ata_id_c_string(id, fw, ATA_ID_FW_REV_OFS, sizeof(fw)); - - if (strcmp(model, "Maxtor")) - return 1; - if (strcmp(fw, "BANC1G10")) - return 1; - - return 0; /* blacklisted */ -} - -/** - * ata_scsiop_mode_sense - Simulate MODE SENSE 6, 10 commands - * @args: device IDENTIFY data / SCSI command of interest. - * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. - * @buflen: Response buffer length. - * - * Simulate MODE SENSE commands. Assume this is invoked for direct - * access devices (e.g. disks) only. There should be no block - * descriptor for other device types. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf, - unsigned int buflen) -{ - struct ata_device *dev = args->dev; - u8 *scsicmd = args->cmd->cmnd, *p, *last; - const u8 sat_blk_desc[] = { - 0, 0, 0, 0, /* number of blocks: sat unspecified */ - 0, - 0, 0x2, 0x0 /* block length: 512 bytes */ - }; - u8 pg, spg; - unsigned int ebd, page_control, six_byte, output_len, alloc_len, minlen; - u8 dpofua; - - VPRINTK("ENTER\n"); - - six_byte = (scsicmd[0] == MODE_SENSE); - ebd = !(scsicmd[1] & 0x8); /* dbd bit inverted == edb */ - /* - * LLBA bit in msense(10) ignored (compliant) - */ - - page_control = scsicmd[2] >> 6; - switch (page_control) { - case 0: /* current */ - break; /* supported */ - case 3: /* saved */ - goto saving_not_supp; - case 1: /* changeable */ - case 2: /* defaults */ - default: - goto invalid_fld; - } - - if (six_byte) { - output_len = 4 + (ebd ? 8 : 0); - alloc_len = scsicmd[4]; - } else { - output_len = 8 + (ebd ? 8 : 0); - alloc_len = (scsicmd[7] << 8) + scsicmd[8]; - } - minlen = (alloc_len < buflen) ? alloc_len : buflen; - - p = rbuf + output_len; - last = rbuf + minlen - 1; - - pg = scsicmd[2] & 0x3f; - spg = scsicmd[3]; - /* - * No mode subpages supported (yet) but asking for _all_ - * subpages may be valid - */ - if (spg && (spg != ALL_SUB_MPAGES)) - goto invalid_fld; - - switch(pg) { - case RW_RECOVERY_MPAGE: - output_len += ata_msense_rw_recovery(&p, last); - break; - - case CACHE_MPAGE: - output_len += ata_msense_caching(args->id, &p, last); - break; - - case CONTROL_MPAGE: { - output_len += ata_msense_ctl_mode(&p, last); - break; - } - - case ALL_MPAGES: - output_len += ata_msense_rw_recovery(&p, last); - output_len += ata_msense_caching(args->id, &p, last); - output_len += ata_msense_ctl_mode(&p, last); - break; - - default: /* invalid page code */ - goto invalid_fld; - } - - if (minlen < 1) - return 0; - - dpofua = 0; - if (ata_dev_supports_fua(args->id) && (dev->flags & ATA_DFLAG_LBA48) && - (!(dev->flags & ATA_DFLAG_PIO) || dev->multi_count)) - dpofua = 1 << 4; - - if (six_byte) { - output_len--; - rbuf[0] = output_len; - if (minlen > 2) - rbuf[2] |= dpofua; - if (ebd) { - if (minlen > 3) - rbuf[3] = sizeof(sat_blk_desc); - if (minlen > 11) - memcpy(rbuf + 4, sat_blk_desc, - sizeof(sat_blk_desc)); - } - } else { - output_len -= 2; - rbuf[0] = output_len >> 8; - if (minlen > 1) - rbuf[1] = output_len; - if (minlen > 3) - rbuf[3] |= dpofua; - if (ebd) { - if (minlen > 7) - rbuf[7] = sizeof(sat_blk_desc); - if (minlen > 15) - memcpy(rbuf + 8, sat_blk_desc, - sizeof(sat_blk_desc)); - } - } - return 0; - -invalid_fld: - ata_scsi_set_sense(args->cmd, ILLEGAL_REQUEST, 0x24, 0x0); - /* "Invalid field in cbd" */ - return 1; - -saving_not_supp: - ata_scsi_set_sense(args->cmd, ILLEGAL_REQUEST, 0x39, 0x0); - /* "Saving parameters not supported" */ - return 1; -} - -/** - * ata_scsiop_read_cap - Simulate READ CAPACITY[ 16] commands - * @args: device IDENTIFY data / SCSI command of interest. - * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. - * @buflen: Response buffer length. - * - * Simulate READ CAPACITY commands. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf, - unsigned int buflen) -{ - u64 n_sectors; - u32 tmp; - - VPRINTK("ENTER\n"); - - if (ata_id_has_lba(args->id)) { - if (ata_id_has_lba48(args->id)) - n_sectors = ata_id_u64(args->id, 100); - else - n_sectors = ata_id_u32(args->id, 60); - } else { - /* CHS default translation */ - n_sectors = args->id[1] * args->id[3] * args->id[6]; - - if (ata_id_current_chs_valid(args->id)) - /* CHS current translation */ - n_sectors = ata_id_u32(args->id, 57); - } - - n_sectors--; /* ATA TotalUserSectors - 1 */ - - if (args->cmd->cmnd[0] == READ_CAPACITY) { - if( n_sectors >= 0xffffffffULL ) - tmp = 0xffffffff ; /* Return max count on overflow */ - else - tmp = n_sectors ; - - /* sector count, 32-bit */ - rbuf[0] = tmp >> (8 * 3); - rbuf[1] = tmp >> (8 * 2); - rbuf[2] = tmp >> (8 * 1); - rbuf[3] = tmp; - - /* sector size */ - tmp = ATA_SECT_SIZE; - rbuf[6] = tmp >> 8; - rbuf[7] = tmp; - - } else { - /* sector count, 64-bit */ - tmp = n_sectors >> (8 * 4); - rbuf[2] = tmp >> (8 * 3); - rbuf[3] = tmp >> (8 * 2); - rbuf[4] = tmp >> (8 * 1); - rbuf[5] = tmp; - tmp = n_sectors; - rbuf[6] = tmp >> (8 * 3); - rbuf[7] = tmp >> (8 * 2); - rbuf[8] = tmp >> (8 * 1); - rbuf[9] = tmp; - - /* sector size */ - tmp = ATA_SECT_SIZE; - rbuf[12] = tmp >> 8; - rbuf[13] = tmp; - } - - return 0; -} - -/** - * ata_scsiop_report_luns - Simulate REPORT LUNS command - * @args: device IDENTIFY data / SCSI command of interest. - * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. - * @buflen: Response buffer length. - * - * Simulate REPORT LUNS command. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -unsigned int ata_scsiop_report_luns(struct ata_scsi_args *args, u8 *rbuf, - unsigned int buflen) -{ - VPRINTK("ENTER\n"); - rbuf[3] = 8; /* just one lun, LUN 0, size 8 bytes */ - - return 0; -} - -/** - * ata_scsi_set_sense - Set SCSI sense data and status - * @cmd: SCSI request to be handled - * @sk: SCSI-defined sense key - * @asc: SCSI-defined additional sense code - * @ascq: SCSI-defined additional sense code qualifier - * - * Helper function that builds a valid fixed format, current - * response code and the given sense key (sk), additional sense - * code (asc) and additional sense code qualifier (ascq) with - * a SCSI command status of %SAM_STAT_CHECK_CONDITION and - * DRIVER_SENSE set in the upper bits of scsi_cmnd::result . - * - * LOCKING: - * Not required - */ - -void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq) -{ - cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; - - cmd->sense_buffer[0] = 0x70; /* fixed format, current */ - cmd->sense_buffer[2] = sk; - cmd->sense_buffer[7] = 18 - 8; /* additional sense length */ - cmd->sense_buffer[12] = asc; - cmd->sense_buffer[13] = ascq; -} - -/** - * ata_scsi_badcmd - End a SCSI request with an error - * @cmd: SCSI request to be handled - * @done: SCSI command completion function - * @asc: SCSI-defined additional sense code - * @ascq: SCSI-defined additional sense code qualifier - * - * Helper function that completes a SCSI command with - * %SAM_STAT_CHECK_CONDITION, with a sense key %ILLEGAL_REQUEST - * and the specified additional sense codes. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -void ata_scsi_badcmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *), u8 asc, u8 ascq) -{ - DPRINTK("ENTER\n"); - ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, asc, ascq); - - done(cmd); -} - -static void atapi_sense_complete(struct ata_queued_cmd *qc) -{ - if (qc->err_mask && ((qc->err_mask & AC_ERR_DEV) == 0)) { - /* FIXME: not quite right; we don't want the - * translation of taskfile registers into - * a sense descriptors, since that's only - * correct for ATA, not ATAPI - */ - ata_gen_ata_desc_sense(qc); - } - - qc->scsidone(qc->scsicmd); - ata_qc_free(qc); -} - -/* is it pointless to prefer PIO for "safety reasons"? */ -static inline int ata_pio_use_silly(struct ata_port *ap) -{ - return (ap->flags & ATA_FLAG_PIO_DMA); -} - -static void atapi_request_sense(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct scsi_cmnd *cmd = qc->scsicmd; - - DPRINTK("ATAPI request sense\n"); - - /* FIXME: is this needed? */ - memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer)); - - ap->ops->tf_read(ap, &qc->tf); - - /* fill these in, for the case where they are -not- overwritten */ - cmd->sense_buffer[0] = 0x70; - cmd->sense_buffer[2] = qc->tf.feature >> 4; - - ata_qc_reinit(qc); - - ata_sg_init_one(qc, cmd->sense_buffer, sizeof(cmd->sense_buffer)); - qc->dma_dir = DMA_FROM_DEVICE; - - memset(&qc->cdb, 0, qc->dev->cdb_len); - qc->cdb[0] = REQUEST_SENSE; - qc->cdb[4] = SCSI_SENSE_BUFFERSIZE; - - qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; - qc->tf.command = ATA_CMD_PACKET; - - if (ata_pio_use_silly(ap)) { - qc->tf.protocol = ATA_PROT_ATAPI_DMA; - qc->tf.feature |= ATAPI_PKT_DMA; - } else { - qc->tf.protocol = ATA_PROT_ATAPI; - qc->tf.lbam = (8 * 1024) & 0xff; - qc->tf.lbah = (8 * 1024) >> 8; - } - qc->nbytes = SCSI_SENSE_BUFFERSIZE; - - qc->complete_fn = atapi_sense_complete; - - ata_qc_issue(qc); - - DPRINTK("EXIT\n"); -} - -static void atapi_qc_complete(struct ata_queued_cmd *qc) -{ - struct scsi_cmnd *cmd = qc->scsicmd; - unsigned int err_mask = qc->err_mask; - - VPRINTK("ENTER, err_mask 0x%X\n", err_mask); - - /* handle completion from new EH */ - if (unlikely(qc->ap->ops->error_handler && - (err_mask || qc->flags & ATA_QCFLAG_SENSE_VALID))) { - - if (!(qc->flags & ATA_QCFLAG_SENSE_VALID)) { - /* FIXME: not quite right; we don't want the - * translation of taskfile registers into a - * sense descriptors, since that's only - * correct for ATA, not ATAPI - */ - ata_gen_ata_desc_sense(qc); - } - - /* SCSI EH automatically locks door if sdev->locked is - * set. Sometimes door lock request continues to - * fail, for example, when no media is present. This - * creates a loop - SCSI EH issues door lock which - * fails and gets invoked again to acquire sense data - * for the failed command. - * - * If door lock fails, always clear sdev->locked to - * avoid this infinite loop. - */ - if (qc->cdb[0] == ALLOW_MEDIUM_REMOVAL) - qc->dev->sdev->locked = 0; - - qc->scsicmd->result = SAM_STAT_CHECK_CONDITION; - qc->scsidone(cmd); - ata_qc_free(qc); - return; - } - - /* successful completion or old EH failure path */ - if (unlikely(err_mask & AC_ERR_DEV)) { - cmd->result = SAM_STAT_CHECK_CONDITION; - atapi_request_sense(qc); - return; - } else if (unlikely(err_mask)) { - /* FIXME: not quite right; we don't want the - * translation of taskfile registers into - * a sense descriptors, since that's only - * correct for ATA, not ATAPI - */ - ata_gen_ata_desc_sense(qc); - } else { - u8 *scsicmd = cmd->cmnd; - - if ((scsicmd[0] == INQUIRY) && ((scsicmd[1] & 0x03) == 0)) { - u8 *buf = NULL; - unsigned int buflen; - - buflen = ata_scsi_rbuf_get(cmd, &buf); - - /* ATAPI devices typically report zero for their SCSI version, - * and sometimes deviate from the spec WRT response data - * format. If SCSI version is reported as zero like normal, - * then we make the following fixups: 1) Fake MMC-5 version, - * to indicate to the Linux scsi midlayer this is a modern - * device. 2) Ensure response data format / ATAPI information - * are always correct. - */ - if (buf[2] == 0) { - buf[2] = 0x5; - buf[3] = 0x32; - } - - ata_scsi_rbuf_put(cmd, buf); - } - - cmd->result = SAM_STAT_GOOD; - } - - qc->scsidone(cmd); - ata_qc_free(qc); -} -/** - * atapi_xlat - Initialize PACKET taskfile - * @qc: command structure to be initialized - * @scsicmd: SCSI CDB associated with this PACKET command - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * Zero on success, non-zero on failure. - */ - -static unsigned int atapi_xlat(struct ata_queued_cmd *qc, const u8 *scsicmd) -{ - struct scsi_cmnd *cmd = qc->scsicmd; - struct ata_device *dev = qc->dev; - int using_pio = (dev->flags & ATA_DFLAG_PIO); - int nodata = (cmd->sc_data_direction == DMA_NONE); - - if (!using_pio) - /* Check whether ATAPI DMA is safe */ - if (ata_check_atapi_dma(qc)) - using_pio = 1; - - memcpy(&qc->cdb, scsicmd, dev->cdb_len); - - qc->complete_fn = atapi_qc_complete; - - qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; - if (cmd->sc_data_direction == DMA_TO_DEVICE) { - qc->tf.flags |= ATA_TFLAG_WRITE; - DPRINTK("direction: write\n"); - } - - qc->tf.command = ATA_CMD_PACKET; - - /* no data, or PIO data xfer */ - if (using_pio || nodata) { - if (nodata) - qc->tf.protocol = ATA_PROT_ATAPI_NODATA; - else - qc->tf.protocol = ATA_PROT_ATAPI; - qc->tf.lbam = (8 * 1024) & 0xff; - qc->tf.lbah = (8 * 1024) >> 8; - } - - /* DMA data xfer */ - else { - qc->tf.protocol = ATA_PROT_ATAPI_DMA; - qc->tf.feature |= ATAPI_PKT_DMA; - - if (atapi_dmadir && (cmd->sc_data_direction != DMA_TO_DEVICE)) - /* some SATA bridges need us to indicate data xfer direction */ - qc->tf.feature |= ATAPI_DMADIR; - } - - qc->nbytes = cmd->request_bufflen; - - return 0; -} - -static struct ata_device * ata_find_dev(struct ata_port *ap, int id) -{ - if (likely(id < ATA_MAX_DEVICES)) - return &ap->device[id]; - return NULL; -} - -static struct ata_device * __ata_scsi_find_dev(struct ata_port *ap, - const struct scsi_device *scsidev) -{ - /* skip commands not addressed to targets we simulate */ - if (unlikely(scsidev->channel || scsidev->lun)) - return NULL; - - return ata_find_dev(ap, scsidev->id); -} - -/** - * ata_scsi_dev_enabled - determine if device is enabled - * @dev: ATA device - * - * Determine if commands should be sent to the specified device. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * 0 if commands are not allowed / 1 if commands are allowed - */ - -static int ata_scsi_dev_enabled(struct ata_device *dev) -{ - if (unlikely(!ata_dev_enabled(dev))) - return 0; - - if (!atapi_enabled || (dev->ap->flags & ATA_FLAG_NO_ATAPI)) { - if (unlikely(dev->class == ATA_DEV_ATAPI)) { - ata_dev_printk(dev, KERN_WARNING, - "WARNING: ATAPI is %s, device ignored.\n", - atapi_enabled ? "not supported with this driver" : "disabled"); - return 0; - } - } - - return 1; -} - -/** - * ata_scsi_find_dev - lookup ata_device from scsi_cmnd - * @ap: ATA port to which the device is attached - * @scsidev: SCSI device from which we derive the ATA device - * - * Given various information provided in struct scsi_cmnd, - * map that onto an ATA bus, and using that mapping - * determine which ata_device is associated with the - * SCSI command to be sent. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * Associated ATA device, or %NULL if not found. - */ -static struct ata_device * -ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev) -{ - struct ata_device *dev = __ata_scsi_find_dev(ap, scsidev); - - if (unlikely(!dev || !ata_scsi_dev_enabled(dev))) - return NULL; - - return dev; -} - -/* - * ata_scsi_map_proto - Map pass-thru protocol value to taskfile value. - * @byte1: Byte 1 from pass-thru CDB. - * - * RETURNS: - * ATA_PROT_UNKNOWN if mapping failed/unimplemented, protocol otherwise. - */ -static u8 -ata_scsi_map_proto(u8 byte1) -{ - switch((byte1 & 0x1e) >> 1) { - case 3: /* Non-data */ - return ATA_PROT_NODATA; - - case 6: /* DMA */ - return ATA_PROT_DMA; - - case 4: /* PIO Data-in */ - case 5: /* PIO Data-out */ - return ATA_PROT_PIO; - - case 10: /* Device Reset */ - case 0: /* Hard Reset */ - case 1: /* SRST */ - case 2: /* Bus Idle */ - case 7: /* Packet */ - case 8: /* DMA Queued */ - case 9: /* Device Diagnostic */ - case 11: /* UDMA Data-in */ - case 12: /* UDMA Data-Out */ - case 13: /* FPDMA */ - default: /* Reserved */ - break; - } - - return ATA_PROT_UNKNOWN; -} - -/** - * ata_scsi_pass_thru - convert ATA pass-thru CDB to taskfile - * @qc: command structure to be initialized - * @scsicmd: SCSI command to convert - * - * Handles either 12 or 16-byte versions of the CDB. - * - * RETURNS: - * Zero on success, non-zero on failure. - */ -static unsigned int -ata_scsi_pass_thru(struct ata_queued_cmd *qc, const u8 *scsicmd) -{ - struct ata_taskfile *tf = &(qc->tf); - struct scsi_cmnd *cmd = qc->scsicmd; - struct ata_device *dev = qc->dev; - - if ((tf->protocol = ata_scsi_map_proto(scsicmd[1])) == ATA_PROT_UNKNOWN) - goto invalid_fld; - - /* We may not issue DMA commands if no DMA mode is set */ - if (tf->protocol == ATA_PROT_DMA && dev->dma_mode == 0) - goto invalid_fld; - - if (scsicmd[1] & 0xe0) - /* PIO multi not supported yet */ - goto invalid_fld; - - /* - * 12 and 16 byte CDBs use different offsets to - * provide the various register values. - */ - if (scsicmd[0] == ATA_16) { - /* - * 16-byte CDB - may contain extended commands. - * - * If that is the case, copy the upper byte register values. - */ - if (scsicmd[1] & 0x01) { - tf->hob_feature = scsicmd[3]; - tf->hob_nsect = scsicmd[5]; - tf->hob_lbal = scsicmd[7]; - tf->hob_lbam = scsicmd[9]; - tf->hob_lbah = scsicmd[11]; - tf->flags |= ATA_TFLAG_LBA48; - } else - tf->flags &= ~ATA_TFLAG_LBA48; - - /* - * Always copy low byte, device and command registers. - */ - tf->feature = scsicmd[4]; - tf->nsect = scsicmd[6]; - tf->lbal = scsicmd[8]; - tf->lbam = scsicmd[10]; - tf->lbah = scsicmd[12]; - tf->device = scsicmd[13]; - tf->command = scsicmd[14]; - } else { - /* - * 12-byte CDB - incapable of extended commands. - */ - tf->flags &= ~ATA_TFLAG_LBA48; - - tf->feature = scsicmd[3]; - tf->nsect = scsicmd[4]; - tf->lbal = scsicmd[5]; - tf->lbam = scsicmd[6]; - tf->lbah = scsicmd[7]; - tf->device = scsicmd[8]; - tf->command = scsicmd[9]; - } - /* - * If slave is possible, enforce correct master/slave bit - */ - if (qc->ap->flags & ATA_FLAG_SLAVE_POSS) - tf->device = qc->dev->devno ? - tf->device | ATA_DEV1 : tf->device & ~ATA_DEV1; - - /* - * Filter SET_FEATURES - XFER MODE command -- otherwise, - * SET_FEATURES - XFER MODE must be preceded/succeeded - * by an update to hardware-specific registers for each - * controller (i.e. the reason for ->set_piomode(), - * ->set_dmamode(), and ->post_set_mode() hooks). - */ - if ((tf->command == ATA_CMD_SET_FEATURES) - && (tf->feature == SETFEATURES_XFER)) - goto invalid_fld; - - /* - * Set flags so that all registers will be written, - * and pass on write indication (used for PIO/DMA - * setup.) - */ - tf->flags |= (ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE); - - if (cmd->sc_data_direction == DMA_TO_DEVICE) - tf->flags |= ATA_TFLAG_WRITE; - - /* - * Set transfer length. - * - * TODO: find out if we need to do more here to - * cover scatter/gather case. - */ - qc->nsect = cmd->request_bufflen / ATA_SECT_SIZE; - - /* request result TF */ - qc->flags |= ATA_QCFLAG_RESULT_TF; - - return 0; - - invalid_fld: - ata_scsi_set_sense(qc->scsicmd, ILLEGAL_REQUEST, 0x24, 0x00); - /* "Invalid field in cdb" */ - return 1; -} - -/** - * ata_get_xlat_func - check if SCSI to ATA translation is possible - * @dev: ATA device - * @cmd: SCSI command opcode to consider - * - * Look up the SCSI command given, and determine whether the - * SCSI command is to be translated or simulated. - * - * RETURNS: - * Pointer to translation function if possible, %NULL if not. - */ - -static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd) -{ - switch (cmd) { - case READ_6: - case READ_10: - case READ_16: - - case WRITE_6: - case WRITE_10: - case WRITE_16: - return ata_scsi_rw_xlat; - - case SYNCHRONIZE_CACHE: - if (ata_try_flush_cache(dev)) - return ata_scsi_flush_xlat; - break; - - case VERIFY: - case VERIFY_16: - return ata_scsi_verify_xlat; - - case ATA_12: - case ATA_16: - return ata_scsi_pass_thru; - - case START_STOP: - return ata_scsi_start_stop_xlat; - } - - return NULL; -} - -/** - * ata_scsi_dump_cdb - dump SCSI command contents to dmesg - * @ap: ATA port to which the command was being sent - * @cmd: SCSI command to dump - * - * Prints the contents of a SCSI command via printk(). - */ - -static inline void ata_scsi_dump_cdb(struct ata_port *ap, - struct scsi_cmnd *cmd) -{ -#ifdef ATA_DEBUG - struct scsi_device *scsidev = cmd->device; - u8 *scsicmd = cmd->cmnd; - - DPRINTK("CDB (%u:%d,%d,%d) %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", - ap->id, - scsidev->channel, scsidev->id, scsidev->lun, - scsicmd[0], scsicmd[1], scsicmd[2], scsicmd[3], - scsicmd[4], scsicmd[5], scsicmd[6], scsicmd[7], - scsicmd[8]); -#endif -} - -static inline int __ata_scsi_queuecmd(struct scsi_cmnd *cmd, - void (*done)(struct scsi_cmnd *), - struct ata_device *dev) -{ - int rc = 0; - - if (dev->class == ATA_DEV_ATA) { - ata_xlat_func_t xlat_func = ata_get_xlat_func(dev, - cmd->cmnd[0]); - - if (xlat_func) - rc = ata_scsi_translate(dev, cmd, done, xlat_func); - else - ata_scsi_simulate(dev, cmd, done); - } else - rc = ata_scsi_translate(dev, cmd, done, atapi_xlat); - - return rc; -} - -/** - * ata_scsi_queuecmd - Issue SCSI cdb to libata-managed device - * @cmd: SCSI command to be sent - * @done: Completion function, called when command is complete - * - * In some cases, this function translates SCSI commands into - * ATA taskfiles, and queues the taskfiles to be sent to - * hardware. In other cases, this function simulates a - * SCSI device by evaluating and responding to certain - * SCSI commands. This creates the overall effect of - * ATA and ATAPI devices appearing as SCSI devices. - * - * LOCKING: - * Releases scsi-layer-held lock, and obtains host_set lock. - * - * RETURNS: - * Return value from __ata_scsi_queuecmd() if @cmd can be queued, - * 0 otherwise. - */ -int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) -{ - struct ata_port *ap; - struct ata_device *dev; - struct scsi_device *scsidev = cmd->device; - struct Scsi_Host *shost = scsidev->host; - int rc = 0; - - ap = ata_shost_to_port(shost); - - spin_unlock(shost->host_lock); - spin_lock(ap->lock); - - ata_scsi_dump_cdb(ap, cmd); - - dev = ata_scsi_find_dev(ap, scsidev); - if (likely(dev)) - rc = __ata_scsi_queuecmd(cmd, done, dev); - else { - cmd->result = (DID_BAD_TARGET << 16); - done(cmd); - } - - spin_unlock(ap->lock); - spin_lock(shost->host_lock); - return rc; -} - -/** - * ata_scsi_simulate - simulate SCSI command on ATA device - * @dev: the target device - * @cmd: SCSI command being sent to device. - * @done: SCSI command completion function. - * - * Interprets and directly executes a select list of SCSI commands - * that can be handled internally. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd, - void (*done)(struct scsi_cmnd *)) -{ - struct ata_scsi_args args; - const u8 *scsicmd = cmd->cmnd; - - args.dev = dev; - args.id = dev->id; - args.cmd = cmd; - args.done = done; - - switch(scsicmd[0]) { - /* no-op's, complete with success */ - case SYNCHRONIZE_CACHE: - case REZERO_UNIT: - case SEEK_6: - case SEEK_10: - case TEST_UNIT_READY: - case FORMAT_UNIT: /* FIXME: correct? */ - case SEND_DIAGNOSTIC: /* FIXME: correct? */ - ata_scsi_rbuf_fill(&args, ata_scsiop_noop); - break; - - case INQUIRY: - if (scsicmd[1] & 2) /* is CmdDt set? */ - ata_scsi_invalid_field(cmd, done); - else if ((scsicmd[1] & 1) == 0) /* is EVPD clear? */ - ata_scsi_rbuf_fill(&args, ata_scsiop_inq_std); - else if (scsicmd[2] == 0x00) - ata_scsi_rbuf_fill(&args, ata_scsiop_inq_00); - else if (scsicmd[2] == 0x80) - ata_scsi_rbuf_fill(&args, ata_scsiop_inq_80); - else if (scsicmd[2] == 0x83) - ata_scsi_rbuf_fill(&args, ata_scsiop_inq_83); - else - ata_scsi_invalid_field(cmd, done); - break; - - case MODE_SENSE: - case MODE_SENSE_10: - ata_scsi_rbuf_fill(&args, ata_scsiop_mode_sense); - break; - - case MODE_SELECT: /* unconditionally return */ - case MODE_SELECT_10: /* bad-field-in-cdb */ - ata_scsi_invalid_field(cmd, done); - break; - - case READ_CAPACITY: - ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap); - break; - - case SERVICE_ACTION_IN: - if ((scsicmd[1] & 0x1f) == SAI_READ_CAPACITY_16) - ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap); - else - ata_scsi_invalid_field(cmd, done); - break; - - case REPORT_LUNS: - ata_scsi_rbuf_fill(&args, ata_scsiop_report_luns); - break; - - /* mandatory commands we haven't implemented yet */ - case REQUEST_SENSE: - - /* all other commands */ - default: - ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, 0x20, 0x0); - /* "Invalid command operation code" */ - done(cmd); - break; - } -} - -void ata_scsi_scan_host(struct ata_port *ap) -{ - unsigned int i; - - if (ap->flags & ATA_FLAG_DISABLED) - return; - - for (i = 0; i < ATA_MAX_DEVICES; i++) { - struct ata_device *dev = &ap->device[i]; - struct scsi_device *sdev; - - if (!ata_dev_enabled(dev) || dev->sdev) - continue; - - sdev = __scsi_add_device(ap->host, 0, i, 0, NULL); - if (!IS_ERR(sdev)) { - dev->sdev = sdev; - scsi_device_put(sdev); - } - } -} - -/** - * ata_scsi_offline_dev - offline attached SCSI device - * @dev: ATA device to offline attached SCSI device for - * - * This function is called from ata_eh_hotplug() and responsible - * for taking the SCSI device attached to @dev offline. This - * function is called with host_set lock which protects dev->sdev - * against clearing. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * 1 if attached SCSI device exists, 0 otherwise. - */ -int ata_scsi_offline_dev(struct ata_device *dev) -{ - if (dev->sdev) { - scsi_device_set_state(dev->sdev, SDEV_OFFLINE); - return 1; - } - return 0; -} - -/** - * ata_scsi_remove_dev - remove attached SCSI device - * @dev: ATA device to remove attached SCSI device for - * - * This function is called from ata_eh_scsi_hotplug() and - * responsible for removing the SCSI device attached to @dev. - * - * LOCKING: - * Kernel thread context (may sleep). - */ -static void ata_scsi_remove_dev(struct ata_device *dev) -{ - struct ata_port *ap = dev->ap; - struct scsi_device *sdev; - unsigned long flags; - - /* Alas, we need to grab scan_mutex to ensure SCSI device - * state doesn't change underneath us and thus - * scsi_device_get() always succeeds. The mutex locking can - * be removed if there is __scsi_device_get() interface which - * increments reference counts regardless of device state. - */ - mutex_lock(&ap->host->scan_mutex); - spin_lock_irqsave(ap->lock, flags); - - /* clearing dev->sdev is protected by host_set lock */ - sdev = dev->sdev; - dev->sdev = NULL; - - if (sdev) { - /* If user initiated unplug races with us, sdev can go - * away underneath us after the host_set lock and - * scan_mutex are released. Hold onto it. - */ - if (scsi_device_get(sdev) == 0) { - /* The following ensures the attached sdev is - * offline on return from ata_scsi_offline_dev() - * regardless it wins or loses the race - * against this function. - */ - scsi_device_set_state(sdev, SDEV_OFFLINE); - } else { - WARN_ON(1); - sdev = NULL; - } - } - - spin_unlock_irqrestore(ap->lock, flags); - mutex_unlock(&ap->host->scan_mutex); - - if (sdev) { - ata_dev_printk(dev, KERN_INFO, "detaching (SCSI %s)\n", - sdev->sdev_gendev.bus_id); - - scsi_remove_device(sdev); - scsi_device_put(sdev); - } -} - -/** - * ata_scsi_hotplug - SCSI part of hotplug - * @data: Pointer to ATA port to perform SCSI hotplug on - * - * Perform SCSI part of hotplug. It's executed from a separate - * workqueue after EH completes. This is necessary because SCSI - * hot plugging requires working EH and hot unplugging is - * synchronized with hot plugging with a mutex. - * - * LOCKING: - * Kernel thread context (may sleep). - */ -void ata_scsi_hotplug(void *data) -{ - struct ata_port *ap = data; - int i; - - if (ap->pflags & ATA_PFLAG_UNLOADING) { - DPRINTK("ENTER/EXIT - unloading\n"); - return; - } - - DPRINTK("ENTER\n"); - - /* unplug detached devices */ - for (i = 0; i < ATA_MAX_DEVICES; i++) { - struct ata_device *dev = &ap->device[i]; - unsigned long flags; - - if (!(dev->flags & ATA_DFLAG_DETACHED)) - continue; - - spin_lock_irqsave(ap->lock, flags); - dev->flags &= ~ATA_DFLAG_DETACHED; - spin_unlock_irqrestore(ap->lock, flags); - - ata_scsi_remove_dev(dev); - } - - /* scan for new ones */ - ata_scsi_scan_host(ap); - - /* If we scanned while EH was in progress, scan would have - * failed silently. Requeue if there are enabled but - * unattached devices. - */ - for (i = 0; i < ATA_MAX_DEVICES; i++) { - struct ata_device *dev = &ap->device[i]; - if (ata_dev_enabled(dev) && !dev->sdev) { - queue_delayed_work(ata_aux_wq, &ap->hotplug_task, HZ); - break; - } - } - - DPRINTK("EXIT\n"); -} - -/** - * ata_scsi_user_scan - indication for user-initiated bus scan - * @shost: SCSI host to scan - * @channel: Channel to scan - * @id: ID to scan - * @lun: LUN to scan - * - * This function is called when user explicitly requests bus - * scan. Set probe pending flag and invoke EH. - * - * LOCKING: - * SCSI layer (we don't care) - * - * RETURNS: - * Zero. - */ -static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, - unsigned int id, unsigned int lun) -{ - struct ata_port *ap = ata_shost_to_port(shost); - unsigned long flags; - int rc = 0; - - if (!ap->ops->error_handler) - return -EOPNOTSUPP; - - if ((channel != SCAN_WILD_CARD && channel != 0) || - (lun != SCAN_WILD_CARD && lun != 0)) - return -EINVAL; - - spin_lock_irqsave(ap->lock, flags); - - if (id == SCAN_WILD_CARD) { - ap->eh_info.probe_mask |= (1 << ATA_MAX_DEVICES) - 1; - ap->eh_info.action |= ATA_EH_SOFTRESET; - } else { - struct ata_device *dev = ata_find_dev(ap, id); - - if (dev) { - ap->eh_info.probe_mask |= 1 << dev->devno; - ap->eh_info.action |= ATA_EH_SOFTRESET; - ap->eh_info.flags |= ATA_EHI_RESUME_LINK; - } else - rc = -EINVAL; - } - - if (rc == 0) - ata_port_schedule_eh(ap); - - spin_unlock_irqrestore(ap->lock, flags); - - return rc; -} - -/** - * ata_scsi_dev_rescan - initiate scsi_rescan_device() - * @data: Pointer to ATA port to perform scsi_rescan_device() - * - * After ATA pass thru (SAT) commands are executed successfully, - * libata need to propagate the changes to SCSI layer. This - * function must be executed from ata_aux_wq such that sdev - * attach/detach don't race with rescan. - * - * LOCKING: - * Kernel thread context (may sleep). - */ -void ata_scsi_dev_rescan(void *data) -{ - struct ata_port *ap = data; - struct ata_device *dev; - unsigned int i; - - for (i = 0; i < ATA_MAX_DEVICES; i++) { - dev = &ap->device[i]; - - if (ata_dev_enabled(dev) && dev->sdev) - scsi_rescan_device(&(dev->sdev->sdev_gendev)); - } -} - -/** - * ata_sas_port_alloc - Allocate port for a SAS attached SATA device - * @pdev: PCI device that the scsi device is attached to - * @port_info: Information from low-level host driver - * @host: SCSI host that the scsi device is attached to - * - * LOCKING: - * PCI/etc. bus probe sem. - * - * RETURNS: - * ata_port pointer on success / NULL on failure. - */ - -struct ata_port *ata_sas_port_alloc(struct ata_host_set *host_set, - struct ata_port_info *port_info, - struct Scsi_Host *host) -{ - struct ata_port *ap = kzalloc(sizeof(*ap), GFP_KERNEL); - struct ata_probe_ent *ent; - - if (!ap) - return NULL; - - ent = ata_probe_ent_alloc(host_set->dev, port_info); - if (!ent) { - kfree(ap); - return NULL; - } - - ata_port_init(ap, host_set, ent, 0); - ap->lock = host->host_lock; - kfree(ent); - return ap; -} -EXPORT_SYMBOL_GPL(ata_sas_port_alloc); - -/** - * ata_sas_port_start - Set port up for dma. - * @ap: Port to initialize - * - * Called just after data structures for each port are - * initialized. Allocates DMA pad. - * - * May be used as the port_start() entry in ata_port_operations. - * - * LOCKING: - * Inherited from caller. - */ -int ata_sas_port_start(struct ata_port *ap) -{ - return ata_pad_alloc(ap, ap->dev); -} -EXPORT_SYMBOL_GPL(ata_sas_port_start); - -/** - * ata_port_stop - Undo ata_sas_port_start() - * @ap: Port to shut down - * - * Frees the DMA pad. - * - * May be used as the port_stop() entry in ata_port_operations. - * - * LOCKING: - * Inherited from caller. - */ - -void ata_sas_port_stop(struct ata_port *ap) -{ - ata_pad_free(ap, ap->dev); -} -EXPORT_SYMBOL_GPL(ata_sas_port_stop); - -/** - * ata_sas_port_init - Initialize a SATA device - * @ap: SATA port to initialize - * - * LOCKING: - * PCI/etc. bus probe sem. - * - * RETURNS: - * Zero on success, non-zero on error. - */ - -int ata_sas_port_init(struct ata_port *ap) -{ - int rc = ap->ops->port_start(ap); - - if (!rc) - rc = ata_bus_probe(ap); - - return rc; -} -EXPORT_SYMBOL_GPL(ata_sas_port_init); - -/** - * ata_sas_port_destroy - Destroy a SATA port allocated by ata_sas_port_alloc - * @ap: SATA port to destroy - * - */ - -void ata_sas_port_destroy(struct ata_port *ap) -{ - ap->ops->port_stop(ap); - kfree(ap); -} -EXPORT_SYMBOL_GPL(ata_sas_port_destroy); - -/** - * ata_sas_slave_configure - Default slave_config routine for libata devices - * @sdev: SCSI device to configure - * @ap: ATA port to which SCSI device is attached - * - * RETURNS: - * Zero. - */ - -int ata_sas_slave_configure(struct scsi_device *sdev, struct ata_port *ap) -{ - ata_scsi_sdev_config(sdev); - ata_scsi_dev_config(sdev, ap->device); - return 0; -} -EXPORT_SYMBOL_GPL(ata_sas_slave_configure); - -/** - * ata_sas_queuecmd - Issue SCSI cdb to libata-managed device - * @cmd: SCSI command to be sent - * @done: Completion function, called when command is complete - * @ap: ATA port to which the command is being sent - * - * RETURNS: - * Zero. - */ - -int ata_sas_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *), - struct ata_port *ap) -{ - ata_scsi_dump_cdb(ap, cmd); - - if (likely(ata_scsi_dev_enabled(ap->device))) - __ata_scsi_queuecmd(cmd, done, ap->device); - else { - cmd->result = (DID_BAD_TARGET << 16); - done(cmd); - } - return 0; -} -EXPORT_SYMBOL_GPL(ata_sas_queuecmd); diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h deleted file mode 100644 index d4a4f82360e..00000000000 --- a/drivers/scsi/libata.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * libata.h - helper library for ATA - * - * Copyright 2003-2004 Red Hat, Inc. All rights reserved. - * Copyright 2003-2004 Jeff Garzik - * - * - * 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 02139, USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - */ - -#ifndef __LIBATA_H__ -#define __LIBATA_H__ - -#define DRV_NAME "libata" -#define DRV_VERSION "2.00" /* must be exactly four chars */ - -struct ata_scsi_args { - struct ata_device *dev; - u16 *id; - struct scsi_cmnd *cmd; - void (*done)(struct scsi_cmnd *); -}; - -/* libata-core.c */ -extern struct workqueue_struct *ata_aux_wq; -extern int atapi_enabled; -extern int atapi_dmadir; -extern int libata_fua; -extern struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev); -extern int ata_rwcmd_protocol(struct ata_queued_cmd *qc); -extern void ata_dev_disable(struct ata_device *dev); -extern void ata_port_flush_task(struct ata_port *ap); -extern unsigned ata_exec_internal(struct ata_device *dev, - struct ata_taskfile *tf, const u8 *cdb, - int dma_dir, void *buf, unsigned int buflen); -extern unsigned int ata_do_simple_cmd(struct ata_device *dev, u8 cmd); -extern int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class, - int post_reset, u16 *id); -extern int ata_dev_configure(struct ata_device *dev, int print_info); -extern int sata_down_spd_limit(struct ata_port *ap); -extern int sata_set_spd_needed(struct ata_port *ap); -extern int ata_down_xfermask_limit(struct ata_device *dev, int force_pio0); -extern int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev); -extern void ata_qc_free(struct ata_queued_cmd *qc); -extern void ata_qc_issue(struct ata_queued_cmd *qc); -extern void __ata_qc_complete(struct ata_queued_cmd *qc); -extern int ata_check_atapi_dma(struct ata_queued_cmd *qc); -extern void ata_dev_select(struct ata_port *ap, unsigned int device, - unsigned int wait, unsigned int can_sleep); -extern void swap_buf_le16(u16 *buf, unsigned int buf_words); -extern int ata_flush_cache(struct ata_device *dev); -extern void ata_dev_init(struct ata_device *dev); -extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg); -extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); -extern void ata_port_init(struct ata_port *ap, struct ata_host_set *host_set, - const struct ata_probe_ent *ent, unsigned int port_no); -extern struct ata_probe_ent *ata_probe_ent_alloc(struct device *dev, - const struct ata_port_info *port); - - -/* libata-scsi.c */ -extern struct scsi_transport_template ata_scsi_transport_template; - -extern void ata_scsi_scan_host(struct ata_port *ap); -extern int ata_scsi_offline_dev(struct ata_device *dev); -extern void ata_scsi_hotplug(void *data); -extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf, - unsigned int buflen); - -extern unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf, - unsigned int buflen); - -extern unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf, - unsigned int buflen); -extern unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf, - unsigned int buflen); -extern unsigned int ata_scsiop_noop(struct ata_scsi_args *args, u8 *rbuf, - unsigned int buflen); -extern unsigned int ata_scsiop_sync_cache(struct ata_scsi_args *args, u8 *rbuf, - unsigned int buflen); -extern unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf, - unsigned int buflen); -extern unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf, - unsigned int buflen); -extern unsigned int ata_scsiop_report_luns(struct ata_scsi_args *args, u8 *rbuf, - unsigned int buflen); -extern void ata_scsi_badcmd(struct scsi_cmnd *cmd, - void (*done)(struct scsi_cmnd *), - u8 asc, u8 ascq); -extern void ata_scsi_set_sense(struct scsi_cmnd *cmd, - u8 sk, u8 asc, u8 ascq); -extern void ata_scsi_rbuf_fill(struct ata_scsi_args *args, - unsigned int (*actor) (struct ata_scsi_args *args, - u8 *rbuf, unsigned int buflen)); -extern void ata_schedule_scsi_eh(struct Scsi_Host *shost); -extern void ata_scsi_dev_rescan(void *data); -extern int ata_bus_probe(struct ata_port *ap); - -/* libata-eh.c */ -extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); -extern void ata_scsi_error(struct Scsi_Host *host); -extern void ata_port_wait_eh(struct ata_port *ap); -extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc); - -#endif /* __LIBATA_H__ */ diff --git a/drivers/scsi/pdc_adma.c b/drivers/scsi/pdc_adma.c deleted file mode 100644 index 61d2aa697b4..00000000000 --- a/drivers/scsi/pdc_adma.c +++ /dev/null @@ -1,740 +0,0 @@ -/* - * pdc_adma.c - Pacific Digital Corporation ADMA - * - * Maintained by: Mark Lord - * - * Copyright 2005 Mark Lord - * - * 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 02139, USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - * - * Supports ATA disks in single-packet ADMA mode. - * Uses PIO for everything else. - * - * TODO: Use ADMA transfers for ATAPI devices, when possible. - * This requires careful attention to a number of quirks of the chip. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "pdc_adma" -#define DRV_VERSION "0.04" - -/* macro to calculate base address for ATA regs */ -#define ADMA_ATA_REGS(base,port_no) ((base) + ((port_no) * 0x40)) - -/* macro to calculate base address for ADMA regs */ -#define ADMA_REGS(base,port_no) ((base) + 0x80 + ((port_no) * 0x20)) - -enum { - ADMA_PORTS = 2, - ADMA_CPB_BYTES = 40, - ADMA_PRD_BYTES = LIBATA_MAX_PRD * 16, - ADMA_PKT_BYTES = ADMA_CPB_BYTES + ADMA_PRD_BYTES, - - ADMA_DMA_BOUNDARY = 0xffffffff, - - /* global register offsets */ - ADMA_MODE_LOCK = 0x00c7, - - /* per-channel register offsets */ - ADMA_CONTROL = 0x0000, /* ADMA control */ - ADMA_STATUS = 0x0002, /* ADMA status */ - ADMA_CPB_COUNT = 0x0004, /* CPB count */ - ADMA_CPB_CURRENT = 0x000c, /* current CPB address */ - ADMA_CPB_NEXT = 0x000c, /* next CPB address */ - ADMA_CPB_LOOKUP = 0x0010, /* CPB lookup table */ - ADMA_FIFO_IN = 0x0014, /* input FIFO threshold */ - ADMA_FIFO_OUT = 0x0016, /* output FIFO threshold */ - - /* ADMA_CONTROL register bits */ - aNIEN = (1 << 8), /* irq mask: 1==masked */ - aGO = (1 << 7), /* packet trigger ("Go!") */ - aRSTADM = (1 << 5), /* ADMA logic reset */ - aPIOMD4 = 0x0003, /* PIO mode 4 */ - - /* ADMA_STATUS register bits */ - aPSD = (1 << 6), - aUIRQ = (1 << 4), - aPERR = (1 << 0), - - /* CPB bits */ - cDONE = (1 << 0), - cVLD = (1 << 0), - cDAT = (1 << 2), - cIEN = (1 << 3), - - /* PRD bits */ - pORD = (1 << 4), - pDIRO = (1 << 5), - pEND = (1 << 7), - - /* ATA register flags */ - rIGN = (1 << 5), - rEND = (1 << 7), - - /* ATA register addresses */ - ADMA_REGS_CONTROL = 0x0e, - ADMA_REGS_SECTOR_COUNT = 0x12, - ADMA_REGS_LBA_LOW = 0x13, - ADMA_REGS_LBA_MID = 0x14, - ADMA_REGS_LBA_HIGH = 0x15, - ADMA_REGS_DEVICE = 0x16, - ADMA_REGS_COMMAND = 0x17, - - /* PCI device IDs */ - board_1841_idx = 0, /* ADMA 2-port controller */ -}; - -typedef enum { adma_state_idle, adma_state_pkt, adma_state_mmio } adma_state_t; - -struct adma_port_priv { - u8 *pkt; - dma_addr_t pkt_dma; - adma_state_t state; -}; - -static int adma_ata_init_one (struct pci_dev *pdev, - const struct pci_device_id *ent); -static irqreturn_t adma_intr (int irq, void *dev_instance, - struct pt_regs *regs); -static int adma_port_start(struct ata_port *ap); -static void adma_host_stop(struct ata_host_set *host_set); -static void adma_port_stop(struct ata_port *ap); -static void adma_phy_reset(struct ata_port *ap); -static void adma_qc_prep(struct ata_queued_cmd *qc); -static unsigned int adma_qc_issue(struct ata_queued_cmd *qc); -static int adma_check_atapi_dma(struct ata_queued_cmd *qc); -static void adma_bmdma_stop(struct ata_queued_cmd *qc); -static u8 adma_bmdma_status(struct ata_port *ap); -static void adma_irq_clear(struct ata_port *ap); -static void adma_eng_timeout(struct ata_port *ap); - -static struct scsi_host_template adma_ata_sht = { - .module = THIS_MODULE, - .name = DRV_NAME, - .ioctl = ata_scsi_ioctl, - .queuecommand = ata_scsi_queuecmd, - .can_queue = ATA_DEF_QUEUE, - .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = LIBATA_MAX_PRD, - .cmd_per_lun = ATA_SHT_CMD_PER_LUN, - .emulated = ATA_SHT_EMULATED, - .use_clustering = ENABLE_CLUSTERING, - .proc_name = DRV_NAME, - .dma_boundary = ADMA_DMA_BOUNDARY, - .slave_configure = ata_scsi_slave_config, - .slave_destroy = ata_scsi_slave_destroy, - .bios_param = ata_std_bios_param, -}; - -static const struct ata_port_operations adma_ata_ops = { - .port_disable = ata_port_disable, - .tf_load = ata_tf_load, - .tf_read = ata_tf_read, - .check_status = ata_check_status, - .check_atapi_dma = adma_check_atapi_dma, - .exec_command = ata_exec_command, - .dev_select = ata_std_dev_select, - .phy_reset = adma_phy_reset, - .qc_prep = adma_qc_prep, - .qc_issue = adma_qc_issue, - .eng_timeout = adma_eng_timeout, - .data_xfer = ata_mmio_data_xfer, - .irq_handler = adma_intr, - .irq_clear = adma_irq_clear, - .port_start = adma_port_start, - .port_stop = adma_port_stop, - .host_stop = adma_host_stop, - .bmdma_stop = adma_bmdma_stop, - .bmdma_status = adma_bmdma_status, -}; - -static struct ata_port_info adma_port_info[] = { - /* board_1841_idx */ - { - .sht = &adma_ata_sht, - .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST | - ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO | - ATA_FLAG_PIO_POLLING, - .pio_mask = 0x10, /* pio4 */ - .udma_mask = 0x1f, /* udma0-4 */ - .port_ops = &adma_ata_ops, - }, -}; - -static const struct pci_device_id adma_ata_pci_tbl[] = { - { PCI_VENDOR_ID_PDC, 0x1841, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_1841_idx }, - - { } /* terminate list */ -}; - -static struct pci_driver adma_ata_pci_driver = { - .name = DRV_NAME, - .id_table = adma_ata_pci_tbl, - .probe = adma_ata_init_one, - .remove = ata_pci_remove_one, -}; - -static int adma_check_atapi_dma(struct ata_queued_cmd *qc) -{ - return 1; /* ATAPI DMA not yet supported */ -} - -static void adma_bmdma_stop(struct ata_queued_cmd *qc) -{ - /* nothing */ -} - -static u8 adma_bmdma_status(struct ata_port *ap) -{ - return 0; -} - -static void adma_irq_clear(struct ata_port *ap) -{ - /* nothing */ -} - -static void adma_reset_engine(void __iomem *chan) -{ - /* reset ADMA to idle state */ - writew(aPIOMD4 | aNIEN | aRSTADM, chan + ADMA_CONTROL); - udelay(2); - writew(aPIOMD4, chan + ADMA_CONTROL); - udelay(2); -} - -static void adma_reinit_engine(struct ata_port *ap) -{ - struct adma_port_priv *pp = ap->private_data; - void __iomem *mmio_base = ap->host_set->mmio_base; - void __iomem *chan = ADMA_REGS(mmio_base, ap->port_no); - - /* mask/clear ATA interrupts */ - writeb(ATA_NIEN, (void __iomem *)ap->ioaddr.ctl_addr); - ata_check_status(ap); - - /* reset the ADMA engine */ - adma_reset_engine(chan); - - /* set in-FIFO threshold to 0x100 */ - writew(0x100, chan + ADMA_FIFO_IN); - - /* set CPB pointer */ - writel((u32)pp->pkt_dma, chan + ADMA_CPB_NEXT); - - /* set out-FIFO threshold to 0x100 */ - writew(0x100, chan + ADMA_FIFO_OUT); - - /* set CPB count */ - writew(1, chan + ADMA_CPB_COUNT); - - /* read/discard ADMA status */ - readb(chan + ADMA_STATUS); -} - -static inline void adma_enter_reg_mode(struct ata_port *ap) -{ - void __iomem *chan = ADMA_REGS(ap->host_set->mmio_base, ap->port_no); - - writew(aPIOMD4, chan + ADMA_CONTROL); - readb(chan + ADMA_STATUS); /* flush */ -} - -static void adma_phy_reset(struct ata_port *ap) -{ - struct adma_port_priv *pp = ap->private_data; - - pp->state = adma_state_idle; - adma_reinit_engine(ap); - ata_port_probe(ap); - ata_bus_reset(ap); -} - -static void adma_eng_timeout(struct ata_port *ap) -{ - struct adma_port_priv *pp = ap->private_data; - - if (pp->state != adma_state_idle) /* healthy paranoia */ - pp->state = adma_state_mmio; - adma_reinit_engine(ap); - ata_eng_timeout(ap); -} - -static int adma_fill_sg(struct ata_queued_cmd *qc) -{ - struct scatterlist *sg; - struct ata_port *ap = qc->ap; - struct adma_port_priv *pp = ap->private_data; - u8 *buf = pp->pkt; - int i = (2 + buf[3]) * 8; - u8 pFLAGS = pORD | ((qc->tf.flags & ATA_TFLAG_WRITE) ? pDIRO : 0); - - ata_for_each_sg(sg, qc) { - u32 addr; - u32 len; - - addr = (u32)sg_dma_address(sg); - *(__le32 *)(buf + i) = cpu_to_le32(addr); - i += 4; - - len = sg_dma_len(sg) >> 3; - *(__le32 *)(buf + i) = cpu_to_le32(len); - i += 4; - - if (ata_sg_is_last(sg, qc)) - pFLAGS |= pEND; - buf[i++] = pFLAGS; - buf[i++] = qc->dev->dma_mode & 0xf; - buf[i++] = 0; /* pPKLW */ - buf[i++] = 0; /* reserved */ - - *(__le32 *)(buf + i) - = (pFLAGS & pEND) ? 0 : cpu_to_le32(pp->pkt_dma + i + 4); - i += 4; - - VPRINTK("PRD[%u] = (0x%lX, 0x%X)\n", i/4, - (unsigned long)addr, len); - } - return i; -} - -static void adma_qc_prep(struct ata_queued_cmd *qc) -{ - struct adma_port_priv *pp = qc->ap->private_data; - u8 *buf = pp->pkt; - u32 pkt_dma = (u32)pp->pkt_dma; - int i = 0; - - VPRINTK("ENTER\n"); - - adma_enter_reg_mode(qc->ap); - if (qc->tf.protocol != ATA_PROT_DMA) { - ata_qc_prep(qc); - return; - } - - buf[i++] = 0; /* Response flags */ - buf[i++] = 0; /* reserved */ - buf[i++] = cVLD | cDAT | cIEN; - i++; /* cLEN, gets filled in below */ - - *(__le32 *)(buf+i) = cpu_to_le32(pkt_dma); /* cNCPB */ - i += 4; /* cNCPB */ - i += 4; /* cPRD, gets filled in below */ - - buf[i++] = 0; /* reserved */ - buf[i++] = 0; /* reserved */ - buf[i++] = 0; /* reserved */ - buf[i++] = 0; /* reserved */ - - /* ATA registers; must be a multiple of 4 */ - buf[i++] = qc->tf.device; - buf[i++] = ADMA_REGS_DEVICE; - if ((qc->tf.flags & ATA_TFLAG_LBA48)) { - buf[i++] = qc->tf.hob_nsect; - buf[i++] = ADMA_REGS_SECTOR_COUNT; - buf[i++] = qc->tf.hob_lbal; - buf[i++] = ADMA_REGS_LBA_LOW; - buf[i++] = qc->tf.hob_lbam; - buf[i++] = ADMA_REGS_LBA_MID; - buf[i++] = qc->tf.hob_lbah; - buf[i++] = ADMA_REGS_LBA_HIGH; - } - buf[i++] = qc->tf.nsect; - buf[i++] = ADMA_REGS_SECTOR_COUNT; - buf[i++] = qc->tf.lbal; - buf[i++] = ADMA_REGS_LBA_LOW; - buf[i++] = qc->tf.lbam; - buf[i++] = ADMA_REGS_LBA_MID; - buf[i++] = qc->tf.lbah; - buf[i++] = ADMA_REGS_LBA_HIGH; - buf[i++] = 0; - buf[i++] = ADMA_REGS_CONTROL; - buf[i++] = rIGN; - buf[i++] = 0; - buf[i++] = qc->tf.command; - buf[i++] = ADMA_REGS_COMMAND | rEND; - - buf[3] = (i >> 3) - 2; /* cLEN */ - *(__le32 *)(buf+8) = cpu_to_le32(pkt_dma + i); /* cPRD */ - - i = adma_fill_sg(qc); - wmb(); /* flush PRDs and pkt to memory */ -#if 0 - /* dump out CPB + PRDs for debug */ - { - int j, len = 0; - static char obuf[2048]; - for (j = 0; j < i; ++j) { - len += sprintf(obuf+len, "%02x ", buf[j]); - if ((j & 7) == 7) { - printk("%s\n", obuf); - len = 0; - } - } - if (len) - printk("%s\n", obuf); - } -#endif -} - -static inline void adma_packet_start(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - void __iomem *chan = ADMA_REGS(ap->host_set->mmio_base, ap->port_no); - - VPRINTK("ENTER, ap %p\n", ap); - - /* fire up the ADMA engine */ - writew(aPIOMD4 | aGO, chan + ADMA_CONTROL); -} - -static unsigned int adma_qc_issue(struct ata_queued_cmd *qc) -{ - struct adma_port_priv *pp = qc->ap->private_data; - - switch (qc->tf.protocol) { - case ATA_PROT_DMA: - pp->state = adma_state_pkt; - adma_packet_start(qc); - return 0; - - case ATA_PROT_ATAPI_DMA: - BUG(); - break; - - default: - break; - } - - pp->state = adma_state_mmio; - return ata_qc_issue_prot(qc); -} - -static inline unsigned int adma_intr_pkt(struct ata_host_set *host_set) -{ - unsigned int handled = 0, port_no; - u8 __iomem *mmio_base = host_set->mmio_base; - - for (port_no = 0; port_no < host_set->n_ports; ++port_no) { - struct ata_port *ap = host_set->ports[port_no]; - struct adma_port_priv *pp; - struct ata_queued_cmd *qc; - void __iomem *chan = ADMA_REGS(mmio_base, port_no); - u8 status = readb(chan + ADMA_STATUS); - - if (status == 0) - continue; - handled = 1; - adma_enter_reg_mode(ap); - if (ap->flags & ATA_FLAG_DISABLED) - continue; - pp = ap->private_data; - if (!pp || pp->state != adma_state_pkt) - continue; - qc = ata_qc_from_tag(ap, ap->active_tag); - if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) { - if ((status & (aPERR | aPSD | aUIRQ))) - qc->err_mask |= AC_ERR_OTHER; - else if (pp->pkt[0] != cDONE) - qc->err_mask |= AC_ERR_OTHER; - - ata_qc_complete(qc); - } - } - return handled; -} - -static inline unsigned int adma_intr_mmio(struct ata_host_set *host_set) -{ - unsigned int handled = 0, port_no; - - for (port_no = 0; port_no < host_set->n_ports; ++port_no) { - struct ata_port *ap; - ap = host_set->ports[port_no]; - if (ap && (!(ap->flags & ATA_FLAG_DISABLED))) { - struct ata_queued_cmd *qc; - struct adma_port_priv *pp = ap->private_data; - if (!pp || pp->state != adma_state_mmio) - continue; - qc = ata_qc_from_tag(ap, ap->active_tag); - if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) { - - /* check main status, clearing INTRQ */ - u8 status = ata_check_status(ap); - if ((status & ATA_BUSY)) - continue; - DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n", - ap->id, qc->tf.protocol, status); - - /* complete taskfile transaction */ - pp->state = adma_state_idle; - qc->err_mask |= ac_err_mask(status); - ata_qc_complete(qc); - handled = 1; - } - } - } - return handled; -} - -static irqreturn_t adma_intr(int irq, void *dev_instance, struct pt_regs *regs) -{ - struct ata_host_set *host_set = dev_instance; - unsigned int handled = 0; - - VPRINTK("ENTER\n"); - - spin_lock(&host_set->lock); - handled = adma_intr_pkt(host_set) | adma_intr_mmio(host_set); - spin_unlock(&host_set->lock); - - VPRINTK("EXIT\n"); - - return IRQ_RETVAL(handled); -} - -static void adma_ata_setup_port(struct ata_ioports *port, unsigned long base) -{ - port->cmd_addr = - port->data_addr = base + 0x000; - port->error_addr = - port->feature_addr = base + 0x004; - port->nsect_addr = base + 0x008; - port->lbal_addr = base + 0x00c; - port->lbam_addr = base + 0x010; - port->lbah_addr = base + 0x014; - port->device_addr = base + 0x018; - port->status_addr = - port->command_addr = base + 0x01c; - port->altstatus_addr = - port->ctl_addr = base + 0x038; -} - -static int adma_port_start(struct ata_port *ap) -{ - struct device *dev = ap->host_set->dev; - struct adma_port_priv *pp; - int rc; - - rc = ata_port_start(ap); - if (rc) - return rc; - adma_enter_reg_mode(ap); - rc = -ENOMEM; - pp = kcalloc(1, sizeof(*pp), GFP_KERNEL); - if (!pp) - goto err_out; - pp->pkt = dma_alloc_coherent(dev, ADMA_PKT_BYTES, &pp->pkt_dma, - GFP_KERNEL); - if (!pp->pkt) - goto err_out_kfree; - /* paranoia? */ - if ((pp->pkt_dma & 7) != 0) { - printk("bad alignment for pp->pkt_dma: %08x\n", - (u32)pp->pkt_dma); - dma_free_coherent(dev, ADMA_PKT_BYTES, - pp->pkt, pp->pkt_dma); - goto err_out_kfree; - } - memset(pp->pkt, 0, ADMA_PKT_BYTES); - ap->private_data = pp; - adma_reinit_engine(ap); - return 0; - -err_out_kfree: - kfree(pp); -err_out: - ata_port_stop(ap); - return rc; -} - -static void adma_port_stop(struct ata_port *ap) -{ - struct device *dev = ap->host_set->dev; - struct adma_port_priv *pp = ap->private_data; - - adma_reset_engine(ADMA_REGS(ap->host_set->mmio_base, ap->port_no)); - if (pp != NULL) { - ap->private_data = NULL; - if (pp->pkt != NULL) - dma_free_coherent(dev, ADMA_PKT_BYTES, - pp->pkt, pp->pkt_dma); - kfree(pp); - } - ata_port_stop(ap); -} - -static void adma_host_stop(struct ata_host_set *host_set) -{ - unsigned int port_no; - - for (port_no = 0; port_no < ADMA_PORTS; ++port_no) - adma_reset_engine(ADMA_REGS(host_set->mmio_base, port_no)); - - ata_pci_host_stop(host_set); -} - -static void adma_host_init(unsigned int chip_id, - struct ata_probe_ent *probe_ent) -{ - unsigned int port_no; - void __iomem *mmio_base = probe_ent->mmio_base; - - /* enable/lock aGO operation */ - writeb(7, mmio_base + ADMA_MODE_LOCK); - - /* reset the ADMA logic */ - for (port_no = 0; port_no < ADMA_PORTS; ++port_no) - adma_reset_engine(ADMA_REGS(mmio_base, port_no)); -} - -static int adma_set_dma_masks(struct pci_dev *pdev, void __iomem *mmio_base) -{ - int rc; - - 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 0; -} - -static int adma_ata_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - static int printed_version; - struct ata_probe_ent *probe_ent = NULL; - void __iomem *mmio_base; - unsigned int board_idx = (unsigned int) ent->driver_data; - int rc, port_no; - - if (!printed_version++) - dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); - - rc = pci_enable_device(pdev); - if (rc) - return rc; - - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) - goto err_out; - - if ((pci_resource_flags(pdev, 4) & IORESOURCE_MEM) == 0) { - rc = -ENODEV; - goto err_out_regions; - } - - mmio_base = pci_iomap(pdev, 4, 0); - if (mmio_base == NULL) { - rc = -ENOMEM; - goto err_out_regions; - } - - rc = adma_set_dma_masks(pdev, mmio_base); - if (rc) - goto err_out_iounmap; - - probe_ent = kcalloc(1, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) { - rc = -ENOMEM; - goto err_out_iounmap; - } - - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - probe_ent->sht = adma_port_info[board_idx].sht; - probe_ent->host_flags = adma_port_info[board_idx].host_flags; - probe_ent->pio_mask = adma_port_info[board_idx].pio_mask; - probe_ent->mwdma_mask = adma_port_info[board_idx].mwdma_mask; - probe_ent->udma_mask = adma_port_info[board_idx].udma_mask; - probe_ent->port_ops = adma_port_info[board_idx].port_ops; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->mmio_base = mmio_base; - probe_ent->n_ports = ADMA_PORTS; - - for (port_no = 0; port_no < probe_ent->n_ports; ++port_no) { - adma_ata_setup_port(&probe_ent->port[port_no], - ADMA_ATA_REGS((unsigned long)mmio_base, port_no)); - } - - pci_set_master(pdev); - - /* initialize adapter */ - adma_host_init(board_idx, probe_ent); - - rc = ata_device_add(probe_ent); - kfree(probe_ent); - if (rc != ADMA_PORTS) - goto err_out_iounmap; - return 0; - -err_out_iounmap: - pci_iounmap(pdev, mmio_base); -err_out_regions: - pci_release_regions(pdev); -err_out: - pci_disable_device(pdev); - return rc; -} - -static int __init adma_ata_init(void) -{ - return pci_register_driver(&adma_ata_pci_driver); -} - -static void __exit adma_ata_exit(void) -{ - pci_unregister_driver(&adma_ata_pci_driver); -} - -MODULE_AUTHOR("Mark Lord"); -MODULE_DESCRIPTION("Pacific Digital Corporation ADMA low-level driver"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, adma_ata_pci_tbl); -MODULE_VERSION(DRV_VERSION); - -module_init(adma_ata_init); -module_exit(adma_ata_exit); diff --git a/drivers/scsi/sata_mv.c b/drivers/scsi/sata_mv.c deleted file mode 100644 index a2915a56acc..00000000000 --- a/drivers/scsi/sata_mv.c +++ /dev/null @@ -1,2468 +0,0 @@ -/* - * sata_mv.c - Marvell SATA support - * - * Copyright 2005: EMC Corporation, all rights reserved. - * Copyright 2005 Red Hat, Inc. All rights reserved. - * - * Please ALWAYS copy linux-ide@vger.kernel.org on emails. - * - * 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; version 2 of the License. - * - * 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; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "sata_mv" -#define DRV_VERSION "0.7" - -enum { - /* BAR's are enumerated in terms of pci_resource_start() terms */ - MV_PRIMARY_BAR = 0, /* offset 0x10: memory space */ - MV_IO_BAR = 2, /* offset 0x18: IO space */ - MV_MISC_BAR = 3, /* offset 0x1c: FLASH, NVRAM, SRAM */ - - MV_MAJOR_REG_AREA_SZ = 0x10000, /* 64KB */ - MV_MINOR_REG_AREA_SZ = 0x2000, /* 8KB */ - - MV_PCI_REG_BASE = 0, - MV_IRQ_COAL_REG_BASE = 0x18000, /* 6xxx part only */ - MV_IRQ_COAL_CAUSE = (MV_IRQ_COAL_REG_BASE + 0x08), - MV_IRQ_COAL_CAUSE_LO = (MV_IRQ_COAL_REG_BASE + 0x88), - MV_IRQ_COAL_CAUSE_HI = (MV_IRQ_COAL_REG_BASE + 0x8c), - MV_IRQ_COAL_THRESHOLD = (MV_IRQ_COAL_REG_BASE + 0xcc), - MV_IRQ_COAL_TIME_THRESHOLD = (MV_IRQ_COAL_REG_BASE + 0xd0), - - MV_SATAHC0_REG_BASE = 0x20000, - MV_FLASH_CTL = 0x1046c, - MV_GPIO_PORT_CTL = 0x104f0, - MV_RESET_CFG = 0x180d8, - - MV_PCI_REG_SZ = MV_MAJOR_REG_AREA_SZ, - MV_SATAHC_REG_SZ = MV_MAJOR_REG_AREA_SZ, - MV_SATAHC_ARBTR_REG_SZ = MV_MINOR_REG_AREA_SZ, /* arbiter */ - MV_PORT_REG_SZ = MV_MINOR_REG_AREA_SZ, - - MV_USE_Q_DEPTH = ATA_DEF_QUEUE, - - MV_MAX_Q_DEPTH = 32, - MV_MAX_Q_DEPTH_MASK = MV_MAX_Q_DEPTH - 1, - - /* CRQB needs alignment on a 1KB boundary. Size == 1KB - * CRPB needs alignment on a 256B boundary. Size == 256B - * SG count of 176 leads to MV_PORT_PRIV_DMA_SZ == 4KB - * ePRD (SG) entries need alignment on a 16B boundary. Size == 16B - */ - MV_CRQB_Q_SZ = (32 * MV_MAX_Q_DEPTH), - MV_CRPB_Q_SZ = (8 * MV_MAX_Q_DEPTH), - MV_MAX_SG_CT = 176, - MV_SG_TBL_SZ = (16 * MV_MAX_SG_CT), - MV_PORT_PRIV_DMA_SZ = (MV_CRQB_Q_SZ + MV_CRPB_Q_SZ + MV_SG_TBL_SZ), - - MV_PORTS_PER_HC = 4, - /* == (port / MV_PORTS_PER_HC) to determine HC from 0-7 port */ - MV_PORT_HC_SHIFT = 2, - /* == (port % MV_PORTS_PER_HC) to determine hard port from 0-7 port */ - MV_PORT_MASK = 3, - - /* Host Flags */ - MV_FLAG_DUAL_HC = (1 << 30), /* two SATA Host Controllers */ - MV_FLAG_IRQ_COALESCE = (1 << 29), /* IRQ coalescing capability */ - MV_COMMON_FLAGS = (ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO | - ATA_FLAG_NO_ATAPI | ATA_FLAG_PIO_POLLING), - MV_6XXX_FLAGS = MV_FLAG_IRQ_COALESCE, - - CRQB_FLAG_READ = (1 << 0), - CRQB_TAG_SHIFT = 1, - CRQB_CMD_ADDR_SHIFT = 8, - CRQB_CMD_CS = (0x2 << 11), - CRQB_CMD_LAST = (1 << 15), - - CRPB_FLAG_STATUS_SHIFT = 8, - - EPRD_FLAG_END_OF_TBL = (1 << 31), - - /* PCI interface registers */ - - PCI_COMMAND_OFS = 0xc00, - - PCI_MAIN_CMD_STS_OFS = 0xd30, - STOP_PCI_MASTER = (1 << 2), - PCI_MASTER_EMPTY = (1 << 3), - GLOB_SFT_RST = (1 << 4), - - MV_PCI_MODE = 0xd00, - MV_PCI_EXP_ROM_BAR_CTL = 0xd2c, - MV_PCI_DISC_TIMER = 0xd04, - MV_PCI_MSI_TRIGGER = 0xc38, - MV_PCI_SERR_MASK = 0xc28, - MV_PCI_XBAR_TMOUT = 0x1d04, - MV_PCI_ERR_LOW_ADDRESS = 0x1d40, - MV_PCI_ERR_HIGH_ADDRESS = 0x1d44, - MV_PCI_ERR_ATTRIBUTE = 0x1d48, - MV_PCI_ERR_COMMAND = 0x1d50, - - PCI_IRQ_CAUSE_OFS = 0x1d58, - PCI_IRQ_MASK_OFS = 0x1d5c, - PCI_UNMASK_ALL_IRQS = 0x7fffff, /* bits 22-0 */ - - HC_MAIN_IRQ_CAUSE_OFS = 0x1d60, - HC_MAIN_IRQ_MASK_OFS = 0x1d64, - PORT0_ERR = (1 << 0), /* shift by port # */ - PORT0_DONE = (1 << 1), /* shift by port # */ - HC0_IRQ_PEND = 0x1ff, /* bits 0-8 = HC0's ports */ - HC_SHIFT = 9, /* bits 9-17 = HC1's ports */ - PCI_ERR = (1 << 18), - TRAN_LO_DONE = (1 << 19), /* 6xxx: IRQ coalescing */ - TRAN_HI_DONE = (1 << 20), /* 6xxx: IRQ coalescing */ - PORTS_0_7_COAL_DONE = (1 << 21), /* 6xxx: IRQ coalescing */ - GPIO_INT = (1 << 22), - SELF_INT = (1 << 23), - TWSI_INT = (1 << 24), - HC_MAIN_RSVD = (0x7f << 25), /* bits 31-25 */ - HC_MAIN_MASKED_IRQS = (TRAN_LO_DONE | TRAN_HI_DONE | - PORTS_0_7_COAL_DONE | GPIO_INT | TWSI_INT | - HC_MAIN_RSVD), - - /* SATAHC registers */ - HC_CFG_OFS = 0, - - HC_IRQ_CAUSE_OFS = 0x14, - CRPB_DMA_DONE = (1 << 0), /* shift by port # */ - HC_IRQ_COAL = (1 << 4), /* IRQ coalescing */ - DEV_IRQ = (1 << 8), /* shift by port # */ - - /* Shadow block registers */ - SHD_BLK_OFS = 0x100, - SHD_CTL_AST_OFS = 0x20, /* ofs from SHD_BLK_OFS */ - - /* SATA registers */ - SATA_STATUS_OFS = 0x300, /* ctrl, err regs follow status */ - SATA_ACTIVE_OFS = 0x350, - PHY_MODE3 = 0x310, - PHY_MODE4 = 0x314, - PHY_MODE2 = 0x330, - MV5_PHY_MODE = 0x74, - MV5_LT_MODE = 0x30, - MV5_PHY_CTL = 0x0C, - SATA_INTERFACE_CTL = 0x050, - - MV_M2_PREAMP_MASK = 0x7e0, - - /* Port registers */ - EDMA_CFG_OFS = 0, - EDMA_CFG_Q_DEPTH = 0, /* queueing disabled */ - EDMA_CFG_NCQ = (1 << 5), - EDMA_CFG_NCQ_GO_ON_ERR = (1 << 14), /* continue on error */ - EDMA_CFG_RD_BRST_EXT = (1 << 11), /* read burst 512B */ - EDMA_CFG_WR_BUFF_LEN = (1 << 13), /* write buffer 512B */ - - EDMA_ERR_IRQ_CAUSE_OFS = 0x8, - EDMA_ERR_IRQ_MASK_OFS = 0xc, - EDMA_ERR_D_PAR = (1 << 0), - EDMA_ERR_PRD_PAR = (1 << 1), - EDMA_ERR_DEV = (1 << 2), - EDMA_ERR_DEV_DCON = (1 << 3), - EDMA_ERR_DEV_CON = (1 << 4), - EDMA_ERR_SERR = (1 << 5), - EDMA_ERR_SELF_DIS = (1 << 7), - EDMA_ERR_BIST_ASYNC = (1 << 8), - EDMA_ERR_CRBQ_PAR = (1 << 9), - EDMA_ERR_CRPB_PAR = (1 << 10), - EDMA_ERR_INTRL_PAR = (1 << 11), - EDMA_ERR_IORDY = (1 << 12), - EDMA_ERR_LNK_CTRL_RX = (0xf << 13), - EDMA_ERR_LNK_CTRL_RX_2 = (1 << 15), - EDMA_ERR_LNK_DATA_RX = (0xf << 17), - EDMA_ERR_LNK_CTRL_TX = (0x1f << 21), - EDMA_ERR_LNK_DATA_TX = (0x1f << 26), - EDMA_ERR_TRANS_PROTO = (1 << 31), - EDMA_ERR_FATAL = (EDMA_ERR_D_PAR | EDMA_ERR_PRD_PAR | - EDMA_ERR_DEV_DCON | EDMA_ERR_CRBQ_PAR | - EDMA_ERR_CRPB_PAR | EDMA_ERR_INTRL_PAR | - EDMA_ERR_IORDY | EDMA_ERR_LNK_CTRL_RX_2 | - EDMA_ERR_LNK_DATA_RX | - EDMA_ERR_LNK_DATA_TX | - EDMA_ERR_TRANS_PROTO), - - EDMA_REQ_Q_BASE_HI_OFS = 0x10, - EDMA_REQ_Q_IN_PTR_OFS = 0x14, /* also contains BASE_LO */ - - EDMA_REQ_Q_OUT_PTR_OFS = 0x18, - EDMA_REQ_Q_PTR_SHIFT = 5, - - EDMA_RSP_Q_BASE_HI_OFS = 0x1c, - EDMA_RSP_Q_IN_PTR_OFS = 0x20, - EDMA_RSP_Q_OUT_PTR_OFS = 0x24, /* also contains BASE_LO */ - EDMA_RSP_Q_PTR_SHIFT = 3, - - EDMA_CMD_OFS = 0x28, - EDMA_EN = (1 << 0), - EDMA_DS = (1 << 1), - ATA_RST = (1 << 2), - - EDMA_IORDY_TMOUT = 0x34, - EDMA_ARB_CFG = 0x38, - - /* Host private flags (hp_flags) */ - MV_HP_FLAG_MSI = (1 << 0), - MV_HP_ERRATA_50XXB0 = (1 << 1), - MV_HP_ERRATA_50XXB2 = (1 << 2), - MV_HP_ERRATA_60X1B2 = (1 << 3), - MV_HP_ERRATA_60X1C0 = (1 << 4), - MV_HP_ERRATA_XX42A0 = (1 << 5), - MV_HP_50XX = (1 << 6), - MV_HP_GEN_IIE = (1 << 7), - - /* Port private flags (pp_flags) */ - MV_PP_FLAG_EDMA_EN = (1 << 0), - MV_PP_FLAG_EDMA_DS_ACT = (1 << 1), -}; - -#define IS_50XX(hpriv) ((hpriv)->hp_flags & MV_HP_50XX) -#define IS_60XX(hpriv) (((hpriv)->hp_flags & MV_HP_50XX) == 0) -#define IS_GEN_I(hpriv) IS_50XX(hpriv) -#define IS_GEN_II(hpriv) IS_60XX(hpriv) -#define IS_GEN_IIE(hpriv) ((hpriv)->hp_flags & MV_HP_GEN_IIE) - -enum { - /* Our DMA boundary is determined by an ePRD being unable to handle - * anything larger than 64KB - */ - MV_DMA_BOUNDARY = 0xffffU, - - EDMA_REQ_Q_BASE_LO_MASK = 0xfffffc00U, - - EDMA_RSP_Q_BASE_LO_MASK = 0xffffff00U, -}; - -enum chip_type { - chip_504x, - chip_508x, - chip_5080, - chip_604x, - chip_608x, - chip_6042, - chip_7042, -}; - -/* Command ReQuest Block: 32B */ -struct mv_crqb { - __le32 sg_addr; - __le32 sg_addr_hi; - __le16 ctrl_flags; - __le16 ata_cmd[11]; -}; - -struct mv_crqb_iie { - __le32 addr; - __le32 addr_hi; - __le32 flags; - __le32 len; - __le32 ata_cmd[4]; -}; - -/* Command ResPonse Block: 8B */ -struct mv_crpb { - __le16 id; - __le16 flags; - __le32 tmstmp; -}; - -/* EDMA Physical Region Descriptor (ePRD); A.K.A. SG */ -struct mv_sg { - __le32 addr; - __le32 flags_size; - __le32 addr_hi; - __le32 reserved; -}; - -struct mv_port_priv { - struct mv_crqb *crqb; - dma_addr_t crqb_dma; - struct mv_crpb *crpb; - dma_addr_t crpb_dma; - struct mv_sg *sg_tbl; - dma_addr_t sg_tbl_dma; - u32 pp_flags; -}; - -struct mv_port_signal { - u32 amps; - u32 pre; -}; - -struct mv_host_priv; -struct mv_hw_ops { - void (*phy_errata)(struct mv_host_priv *hpriv, void __iomem *mmio, - unsigned int port); - void (*enable_leds)(struct mv_host_priv *hpriv, void __iomem *mmio); - void (*read_preamp)(struct mv_host_priv *hpriv, int idx, - void __iomem *mmio); - int (*reset_hc)(struct mv_host_priv *hpriv, void __iomem *mmio, - unsigned int n_hc); - void (*reset_flash)(struct mv_host_priv *hpriv, void __iomem *mmio); - void (*reset_bus)(struct pci_dev *pdev, void __iomem *mmio); -}; - -struct mv_host_priv { - u32 hp_flags; - struct mv_port_signal signal[8]; - const struct mv_hw_ops *ops; -}; - -static void mv_irq_clear(struct ata_port *ap); -static u32 mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in); -static void mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val); -static u32 mv5_scr_read(struct ata_port *ap, unsigned int sc_reg_in); -static void mv5_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val); -static void mv_phy_reset(struct ata_port *ap); -static void __mv_phy_reset(struct ata_port *ap, int can_sleep); -static void mv_host_stop(struct ata_host_set *host_set); -static int mv_port_start(struct ata_port *ap); -static void mv_port_stop(struct ata_port *ap); -static void mv_qc_prep(struct ata_queued_cmd *qc); -static void mv_qc_prep_iie(struct ata_queued_cmd *qc); -static unsigned int mv_qc_issue(struct ata_queued_cmd *qc); -static irqreturn_t mv_interrupt(int irq, void *dev_instance, - struct pt_regs *regs); -static void mv_eng_timeout(struct ata_port *ap); -static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); - -static void mv5_phy_errata(struct mv_host_priv *hpriv, void __iomem *mmio, - unsigned int port); -static void mv5_enable_leds(struct mv_host_priv *hpriv, void __iomem *mmio); -static void mv5_read_preamp(struct mv_host_priv *hpriv, int idx, - void __iomem *mmio); -static int mv5_reset_hc(struct mv_host_priv *hpriv, void __iomem *mmio, - unsigned int n_hc); -static void mv5_reset_flash(struct mv_host_priv *hpriv, void __iomem *mmio); -static void mv5_reset_bus(struct pci_dev *pdev, void __iomem *mmio); - -static void mv6_phy_errata(struct mv_host_priv *hpriv, void __iomem *mmio, - unsigned int port); -static void mv6_enable_leds(struct mv_host_priv *hpriv, void __iomem *mmio); -static void mv6_read_preamp(struct mv_host_priv *hpriv, int idx, - void __iomem *mmio); -static int mv6_reset_hc(struct mv_host_priv *hpriv, void __iomem *mmio, - unsigned int n_hc); -static void mv6_reset_flash(struct mv_host_priv *hpriv, void __iomem *mmio); -static void mv_reset_pci_bus(struct pci_dev *pdev, void __iomem *mmio); -static void mv_channel_reset(struct mv_host_priv *hpriv, void __iomem *mmio, - unsigned int port_no); -static void mv_stop_and_reset(struct ata_port *ap); - -static struct scsi_host_template mv_sht = { - .module = THIS_MODULE, - .name = DRV_NAME, - .ioctl = ata_scsi_ioctl, - .queuecommand = ata_scsi_queuecmd, - .can_queue = MV_USE_Q_DEPTH, - .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = MV_MAX_SG_CT / 2, - .cmd_per_lun = ATA_SHT_CMD_PER_LUN, - .emulated = ATA_SHT_EMULATED, - .use_clustering = ATA_SHT_USE_CLUSTERING, - .proc_name = DRV_NAME, - .dma_boundary = MV_DMA_BOUNDARY, - .slave_configure = ata_scsi_slave_config, - .slave_destroy = ata_scsi_slave_destroy, - .bios_param = ata_std_bios_param, -}; - -static const struct ata_port_operations mv5_ops = { - .port_disable = ata_port_disable, - - .tf_load = ata_tf_load, - .tf_read = ata_tf_read, - .check_status = ata_check_status, - .exec_command = ata_exec_command, - .dev_select = ata_std_dev_select, - - .phy_reset = mv_phy_reset, - - .qc_prep = mv_qc_prep, - .qc_issue = mv_qc_issue, - .data_xfer = ata_mmio_data_xfer, - - .eng_timeout = mv_eng_timeout, - - .irq_handler = mv_interrupt, - .irq_clear = mv_irq_clear, - - .scr_read = mv5_scr_read, - .scr_write = mv5_scr_write, - - .port_start = mv_port_start, - .port_stop = mv_port_stop, - .host_stop = mv_host_stop, -}; - -static const struct ata_port_operations mv6_ops = { - .port_disable = ata_port_disable, - - .tf_load = ata_tf_load, - .tf_read = ata_tf_read, - .check_status = ata_check_status, - .exec_command = ata_exec_command, - .dev_select = ata_std_dev_select, - - .phy_reset = mv_phy_reset, - - .qc_prep = mv_qc_prep, - .qc_issue = mv_qc_issue, - .data_xfer = ata_mmio_data_xfer, - - .eng_timeout = mv_eng_timeout, - - .irq_handler = mv_interrupt, - .irq_clear = mv_irq_clear, - - .scr_read = mv_scr_read, - .scr_write = mv_scr_write, - - .port_start = mv_port_start, - .port_stop = mv_port_stop, - .host_stop = mv_host_stop, -}; - -static const struct ata_port_operations mv_iie_ops = { - .port_disable = ata_port_disable, - - .tf_load = ata_tf_load, - .tf_read = ata_tf_read, - .check_status = ata_check_status, - .exec_command = ata_exec_command, - .dev_select = ata_std_dev_select, - - .phy_reset = mv_phy_reset, - - .qc_prep = mv_qc_prep_iie, - .qc_issue = mv_qc_issue, - - .eng_timeout = mv_eng_timeout, - - .irq_handler = mv_interrupt, - .irq_clear = mv_irq_clear, - - .scr_read = mv_scr_read, - .scr_write = mv_scr_write, - - .port_start = mv_port_start, - .port_stop = mv_port_stop, - .host_stop = mv_host_stop, -}; - -static const struct ata_port_info mv_port_info[] = { - { /* chip_504x */ - .sht = &mv_sht, - .host_flags = MV_COMMON_FLAGS, - .pio_mask = 0x1f, /* pio0-4 */ - .udma_mask = 0x7f, /* udma0-6 */ - .port_ops = &mv5_ops, - }, - { /* chip_508x */ - .sht = &mv_sht, - .host_flags = (MV_COMMON_FLAGS | MV_FLAG_DUAL_HC), - .pio_mask = 0x1f, /* pio0-4 */ - .udma_mask = 0x7f, /* udma0-6 */ - .port_ops = &mv5_ops, - }, - { /* chip_5080 */ - .sht = &mv_sht, - .host_flags = (MV_COMMON_FLAGS | MV_FLAG_DUAL_HC), - .pio_mask = 0x1f, /* pio0-4 */ - .udma_mask = 0x7f, /* udma0-6 */ - .port_ops = &mv5_ops, - }, - { /* chip_604x */ - .sht = &mv_sht, - .host_flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS), - .pio_mask = 0x1f, /* pio0-4 */ - .udma_mask = 0x7f, /* udma0-6 */ - .port_ops = &mv6_ops, - }, - { /* chip_608x */ - .sht = &mv_sht, - .host_flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS | - MV_FLAG_DUAL_HC), - .pio_mask = 0x1f, /* pio0-4 */ - .udma_mask = 0x7f, /* udma0-6 */ - .port_ops = &mv6_ops, - }, - { /* chip_6042 */ - .sht = &mv_sht, - .host_flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS), - .pio_mask = 0x1f, /* pio0-4 */ - .udma_mask = 0x7f, /* udma0-6 */ - .port_ops = &mv_iie_ops, - }, - { /* chip_7042 */ - .sht = &mv_sht, - .host_flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS | - MV_FLAG_DUAL_HC), - .pio_mask = 0x1f, /* pio0-4 */ - .udma_mask = 0x7f, /* udma0-6 */ - .port_ops = &mv_iie_ops, - }, -}; - -static const struct pci_device_id mv_pci_tbl[] = { - {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5040), 0, 0, chip_504x}, - {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5041), 0, 0, chip_504x}, - {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5080), 0, 0, chip_5080}, - {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5081), 0, 0, chip_508x}, - - {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6040), 0, 0, chip_604x}, - {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6041), 0, 0, chip_604x}, - {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6042), 0, 0, chip_6042}, - {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6080), 0, 0, chip_608x}, - {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6081), 0, 0, chip_608x}, - - {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x0241), 0, 0, chip_604x}, - {} /* terminate list */ -}; - -static struct pci_driver mv_pci_driver = { - .name = DRV_NAME, - .id_table = mv_pci_tbl, - .probe = mv_init_one, - .remove = ata_pci_remove_one, -}; - -static const struct mv_hw_ops mv5xxx_ops = { - .phy_errata = mv5_phy_errata, - .enable_leds = mv5_enable_leds, - .read_preamp = mv5_read_preamp, - .reset_hc = mv5_reset_hc, - .reset_flash = mv5_reset_flash, - .reset_bus = mv5_reset_bus, -}; - -static const struct mv_hw_ops mv6xxx_ops = { - .phy_errata = mv6_phy_errata, - .enable_leds = mv6_enable_leds, - .read_preamp = mv6_read_preamp, - .reset_hc = mv6_reset_hc, - .reset_flash = mv6_reset_flash, - .reset_bus = mv_reset_pci_bus, -}; - -/* - * module options - */ -static int msi; /* Use PCI msi; either zero (off, default) or non-zero */ - - -/* - * Functions - */ - -static inline void writelfl(unsigned long data, void __iomem *addr) -{ - writel(data, addr); - (void) readl(addr); /* flush to avoid PCI posted write */ -} - -static inline void __iomem *mv_hc_base(void __iomem *base, unsigned int hc) -{ - return (base + MV_SATAHC0_REG_BASE + (hc * MV_SATAHC_REG_SZ)); -} - -static inline unsigned int mv_hc_from_port(unsigned int port) -{ - return port >> MV_PORT_HC_SHIFT; -} - -static inline unsigned int mv_hardport_from_port(unsigned int port) -{ - return port & MV_PORT_MASK; -} - -static inline void __iomem *mv_hc_base_from_port(void __iomem *base, - unsigned int port) -{ - return mv_hc_base(base, mv_hc_from_port(port)); -} - -static inline void __iomem *mv_port_base(void __iomem *base, unsigned int port) -{ - return mv_hc_base_from_port(base, port) + - MV_SATAHC_ARBTR_REG_SZ + - (mv_hardport_from_port(port) * MV_PORT_REG_SZ); -} - -static inline void __iomem *mv_ap_base(struct ata_port *ap) -{ - return mv_port_base(ap->host_set->mmio_base, ap->port_no); -} - -static inline int mv_get_hc_count(unsigned long host_flags) -{ - return ((host_flags & MV_FLAG_DUAL_HC) ? 2 : 1); -} - -static void mv_irq_clear(struct ata_port *ap) -{ -} - -/** - * mv_start_dma - Enable eDMA engine - * @base: port base address - * @pp: port private data - * - * Verify the local cache of the eDMA state is accurate with a - * WARN_ON. - * - * LOCKING: - * Inherited from caller. - */ -static void mv_start_dma(void __iomem *base, struct mv_port_priv *pp) -{ - if (!(MV_PP_FLAG_EDMA_EN & pp->pp_flags)) { - writelfl(EDMA_EN, base + EDMA_CMD_OFS); - pp->pp_flags |= MV_PP_FLAG_EDMA_EN; - } - WARN_ON(!(EDMA_EN & readl(base + EDMA_CMD_OFS))); -} - -/** - * mv_stop_dma - Disable eDMA engine - * @ap: ATA channel to manipulate - * - * Verify the local cache of the eDMA state is accurate with a - * WARN_ON. - * - * LOCKING: - * Inherited from caller. - */ -static void mv_stop_dma(struct ata_port *ap) -{ - void __iomem *port_mmio = mv_ap_base(ap); - struct mv_port_priv *pp = ap->private_data; - u32 reg; - int i; - - if (MV_PP_FLAG_EDMA_EN & pp->pp_flags) { - /* Disable EDMA if active. The disable bit auto clears. - */ - writelfl(EDMA_DS, port_mmio + EDMA_CMD_OFS); - pp->pp_flags &= ~MV_PP_FLAG_EDMA_EN; - } else { - WARN_ON(EDMA_EN & readl(port_mmio + EDMA_CMD_OFS)); - } - - /* now properly wait for the eDMA to stop */ - for (i = 1000; i > 0; i--) { - reg = readl(port_mmio + EDMA_CMD_OFS); - if (!(EDMA_EN & reg)) { - break; - } - udelay(100); - } - - if (EDMA_EN & reg) { - ata_port_printk(ap, KERN_ERR, "Unable to stop eDMA\n"); - /* FIXME: Consider doing a reset here to recover */ - } -} - -#ifdef ATA_DEBUG -static void mv_dump_mem(void __iomem *start, unsigned bytes) -{ - int b, w; - for (b = 0; b < bytes; ) { - DPRINTK("%p: ", start + b); - for (w = 0; b < bytes && w < 4; w++) { - printk("%08x ",readl(start + b)); - b += sizeof(u32); - } - printk("\n"); - } -} -#endif - -static void mv_dump_pci_cfg(struct pci_dev *pdev, unsigned bytes) -{ -#ifdef ATA_DEBUG - int b, w; - u32 dw; - for (b = 0; b < bytes; ) { - DPRINTK("%02x: ", b); - for (w = 0; b < bytes && w < 4; w++) { - (void) pci_read_config_dword(pdev,b,&dw); - printk("%08x ",dw); - b += sizeof(u32); - } - printk("\n"); - } -#endif -} -static void mv_dump_all_regs(void __iomem *mmio_base, int port, - struct pci_dev *pdev) -{ -#ifdef ATA_DEBUG - void __iomem *hc_base = mv_hc_base(mmio_base, - port >> MV_PORT_HC_SHIFT); - void __iomem *port_base; - int start_port, num_ports, p, start_hc, num_hcs, hc; - - if (0 > port) { - start_hc = start_port = 0; - num_ports = 8; /* shld be benign for 4 port devs */ - num_hcs = 2; - } else { - start_hc = port >> MV_PORT_HC_SHIFT; - start_port = port; - num_ports = num_hcs = 1; - } - DPRINTK("All registers for port(s) %u-%u:\n", start_port, - num_ports > 1 ? num_ports - 1 : start_port); - - if (NULL != pdev) { - DPRINTK("PCI config space regs:\n"); - mv_dump_pci_cfg(pdev, 0x68); - } - DPRINTK("PCI regs:\n"); - mv_dump_mem(mmio_base+0xc00, 0x3c); - mv_dump_mem(mmio_base+0xd00, 0x34); - mv_dump_mem(mmio_base+0xf00, 0x4); - mv_dump_mem(mmio_base+0x1d00, 0x6c); - for (hc = start_hc; hc < start_hc + num_hcs; hc++) { - hc_base = mv_hc_base(mmio_base, hc); - DPRINTK("HC regs (HC %i):\n", hc); - mv_dump_mem(hc_base, 0x1c); - } - for (p = start_port; p < start_port + num_ports; p++) { - port_base = mv_port_base(mmio_base, p); - DPRINTK("EDMA regs (port %i):\n",p); - mv_dump_mem(port_base, 0x54); - DPRINTK("SATA regs (port %i):\n",p); - mv_dump_mem(port_base+0x300, 0x60); - } -#endif -} - -static unsigned int mv_scr_offset(unsigned int sc_reg_in) -{ - unsigned int ofs; - - switch (sc_reg_in) { - case SCR_STATUS: - case SCR_CONTROL: - case SCR_ERROR: - ofs = SATA_STATUS_OFS + (sc_reg_in * sizeof(u32)); - break; - case SCR_ACTIVE: - ofs = SATA_ACTIVE_OFS; /* active is not with the others */ - break; - default: - ofs = 0xffffffffU; - break; - } - return ofs; -} - -static u32 mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in) -{ - unsigned int ofs = mv_scr_offset(sc_reg_in); - - if (0xffffffffU != ofs) { - return readl(mv_ap_base(ap) + ofs); - } else { - return (u32) ofs; - } -} - -static void mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) -{ - unsigned int ofs = mv_scr_offset(sc_reg_in); - - if (0xffffffffU != ofs) { - writelfl(val, mv_ap_base(ap) + ofs); - } -} - -/** - * mv_host_stop - Host specific cleanup/stop routine. - * @host_set: host data structure - * - * Disable ints, cleanup host memory, call general purpose - * host_stop. - * - * LOCKING: - * Inherited from caller. - */ -static void mv_host_stop(struct ata_host_set *host_set) -{ - struct mv_host_priv *hpriv = host_set->private_data; - struct pci_dev *pdev = to_pci_dev(host_set->dev); - - if (hpriv->hp_flags & MV_HP_FLAG_MSI) { - pci_disable_msi(pdev); - } else { - pci_intx(pdev, 0); - } - kfree(hpriv); - ata_host_stop(host_set); -} - -static inline void mv_priv_free(struct mv_port_priv *pp, struct device *dev) -{ - dma_free_coherent(dev, MV_PORT_PRIV_DMA_SZ, pp->crpb, pp->crpb_dma); -} - -static void mv_edma_cfg(struct mv_host_priv *hpriv, void __iomem *port_mmio) -{ - u32 cfg = readl(port_mmio + EDMA_CFG_OFS); - - /* set up non-NCQ EDMA configuration */ - cfg &= ~0x1f; /* clear queue depth */ - cfg &= ~EDMA_CFG_NCQ; /* clear NCQ mode */ - cfg &= ~(1 << 9); /* disable equeue */ - - if (IS_GEN_I(hpriv)) - cfg |= (1 << 8); /* enab config burst size mask */ - - else if (IS_GEN_II(hpriv)) - cfg |= EDMA_CFG_RD_BRST_EXT | EDMA_CFG_WR_BUFF_LEN; - - else if (IS_GEN_IIE(hpriv)) { - cfg |= (1 << 23); /* dis RX PM port mask */ - cfg &= ~(1 << 16); /* dis FIS-based switching (for now) */ - cfg &= ~(1 << 19); /* dis 128-entry queue (for now?) */ - cfg |= (1 << 18); /* enab early completion */ - cfg |= (1 << 17); /* enab host q cache */ - cfg |= (1 << 22); /* enab cutthrough */ - } - - writelfl(cfg, port_mmio + EDMA_CFG_OFS); -} - -/** - * mv_port_start - Port specific init/start routine. - * @ap: ATA channel to manipulate - * - * Allocate and point to DMA memory, init port private memory, - * zero indices. - * - * LOCKING: - * Inherited from caller. - */ -static int mv_port_start(struct ata_port *ap) -{ - struct device *dev = ap->host_set->dev; - struct mv_host_priv *hpriv = ap->host_set->private_data; - struct mv_port_priv *pp; - void __iomem *port_mmio = mv_ap_base(ap); - void *mem; - dma_addr_t mem_dma; - int rc = -ENOMEM; - - pp = kmalloc(sizeof(*pp), GFP_KERNEL); - if (!pp) - goto err_out; - memset(pp, 0, sizeof(*pp)); - - mem = dma_alloc_coherent(dev, MV_PORT_PRIV_DMA_SZ, &mem_dma, - GFP_KERNEL); - if (!mem) - goto err_out_pp; - memset(mem, 0, MV_PORT_PRIV_DMA_SZ); - - rc = ata_pad_alloc(ap, dev); - if (rc) - goto err_out_priv; - - /* First item in chunk of DMA memory: - * 32-slot command request table (CRQB), 32 bytes each in size - */ - pp->crqb = mem; - pp->crqb_dma = mem_dma; - mem += MV_CRQB_Q_SZ; - mem_dma += MV_CRQB_Q_SZ; - - /* Second item: - * 32-slot command response table (CRPB), 8 bytes each in size - */ - pp->crpb = mem; - pp->crpb_dma = mem_dma; - mem += MV_CRPB_Q_SZ; - mem_dma += MV_CRPB_Q_SZ; - - /* Third item: - * Table of scatter-gather descriptors (ePRD), 16 bytes each - */ - pp->sg_tbl = mem; - pp->sg_tbl_dma = mem_dma; - - mv_edma_cfg(hpriv, port_mmio); - - writel((pp->crqb_dma >> 16) >> 16, port_mmio + EDMA_REQ_Q_BASE_HI_OFS); - writelfl(pp->crqb_dma & EDMA_REQ_Q_BASE_LO_MASK, - port_mmio + EDMA_REQ_Q_IN_PTR_OFS); - - if (hpriv->hp_flags & MV_HP_ERRATA_XX42A0) - writelfl(pp->crqb_dma & 0xffffffff, - port_mmio + EDMA_REQ_Q_OUT_PTR_OFS); - else - writelfl(0, port_mmio + EDMA_REQ_Q_OUT_PTR_OFS); - - writel((pp->crpb_dma >> 16) >> 16, port_mmio + EDMA_RSP_Q_BASE_HI_OFS); - - if (hpriv->hp_flags & MV_HP_ERRATA_XX42A0) - writelfl(pp->crpb_dma & 0xffffffff, - port_mmio + EDMA_RSP_Q_IN_PTR_OFS); - else - writelfl(0, port_mmio + EDMA_RSP_Q_IN_PTR_OFS); - - writelfl(pp->crpb_dma & EDMA_RSP_Q_BASE_LO_MASK, - port_mmio + EDMA_RSP_Q_OUT_PTR_OFS); - - /* Don't turn on EDMA here...do it before DMA commands only. Else - * we'll be unable to send non-data, PIO, etc due to restricted access - * to shadow regs. - */ - ap->private_data = pp; - return 0; - -err_out_priv: - mv_priv_free(pp, dev); -err_out_pp: - kfree(pp); -err_out: - return rc; -} - -/** - * mv_port_stop - Port specific cleanup/stop routine. - * @ap: ATA channel to manipulate - * - * Stop DMA, cleanup port memory. - * - * LOCKING: - * This routine uses the host_set lock to protect the DMA stop. - */ -static void mv_port_stop(struct ata_port *ap) -{ - struct device *dev = ap->host_set->dev; - struct mv_port_priv *pp = ap->private_data; - unsigned long flags; - - spin_lock_irqsave(&ap->host_set->lock, flags); - mv_stop_dma(ap); - spin_unlock_irqrestore(&ap->host_set->lock, flags); - - ap->private_data = NULL; - ata_pad_free(ap, dev); - mv_priv_free(pp, dev); - kfree(pp); -} - -/** - * mv_fill_sg - Fill out the Marvell ePRD (scatter gather) entries - * @qc: queued command whose SG list to source from - * - * Populate the SG list and mark the last entry. - * - * LOCKING: - * Inherited from caller. - */ -static void mv_fill_sg(struct ata_queued_cmd *qc) -{ - struct mv_port_priv *pp = qc->ap->private_data; - unsigned int i = 0; - struct scatterlist *sg; - - ata_for_each_sg(sg, qc) { - dma_addr_t addr; - u32 sg_len, len, offset; - - addr = sg_dma_address(sg); - sg_len = sg_dma_len(sg); - - while (sg_len) { - offset = addr & MV_DMA_BOUNDARY; - len = sg_len; - if ((offset + sg_len) > 0x10000) - len = 0x10000 - offset; - - pp->sg_tbl[i].addr = cpu_to_le32(addr & 0xffffffff); - pp->sg_tbl[i].addr_hi = cpu_to_le32((addr >> 16) >> 16); - pp->sg_tbl[i].flags_size = cpu_to_le32(len & 0xffff); - - sg_len -= len; - addr += len; - - if (!sg_len && ata_sg_is_last(sg, qc)) - pp->sg_tbl[i].flags_size |= cpu_to_le32(EPRD_FLAG_END_OF_TBL); - - i++; - } - } -} - -static inline unsigned mv_inc_q_index(unsigned index) -{ - return (index + 1) & MV_MAX_Q_DEPTH_MASK; -} - -static inline void mv_crqb_pack_cmd(__le16 *cmdw, u8 data, u8 addr, unsigned last) -{ - u16 tmp = data | (addr << CRQB_CMD_ADDR_SHIFT) | CRQB_CMD_CS | - (last ? CRQB_CMD_LAST : 0); - *cmdw = cpu_to_le16(tmp); -} - -/** - * mv_qc_prep - Host specific command preparation. - * @qc: queued command to prepare - * - * This routine simply redirects to the general purpose routine - * if command is not DMA. Else, it handles prep of the CRQB - * (command request block), does some sanity checking, and calls - * the SG load routine. - * - * LOCKING: - * Inherited from caller. - */ -static void mv_qc_prep(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct mv_port_priv *pp = ap->private_data; - __le16 *cw; - struct ata_taskfile *tf; - u16 flags = 0; - unsigned in_index; - - if (ATA_PROT_DMA != qc->tf.protocol) - return; - - /* Fill in command request block - */ - if (!(qc->tf.flags & ATA_TFLAG_WRITE)) - flags |= CRQB_FLAG_READ; - WARN_ON(MV_MAX_Q_DEPTH <= qc->tag); - flags |= qc->tag << CRQB_TAG_SHIFT; - - /* get current queue index from hardware */ - in_index = (readl(mv_ap_base(ap) + EDMA_REQ_Q_IN_PTR_OFS) - >> EDMA_REQ_Q_PTR_SHIFT) & MV_MAX_Q_DEPTH_MASK; - - pp->crqb[in_index].sg_addr = - cpu_to_le32(pp->sg_tbl_dma & 0xffffffff); - pp->crqb[in_index].sg_addr_hi = - cpu_to_le32((pp->sg_tbl_dma >> 16) >> 16); - pp->crqb[in_index].ctrl_flags = cpu_to_le16(flags); - - cw = &pp->crqb[in_index].ata_cmd[0]; - tf = &qc->tf; - - /* Sadly, the CRQB cannot accomodate all registers--there are - * only 11 bytes...so we must pick and choose required - * registers based on the command. So, we drop feature and - * hob_feature for [RW] DMA commands, but they are needed for - * NCQ. NCQ will drop hob_nsect. - */ - switch (tf->command) { - case ATA_CMD_READ: - case ATA_CMD_READ_EXT: - case ATA_CMD_WRITE: - case ATA_CMD_WRITE_EXT: - case ATA_CMD_WRITE_FUA_EXT: - mv_crqb_pack_cmd(cw++, tf->hob_nsect, ATA_REG_NSECT, 0); - break; -#ifdef LIBATA_NCQ /* FIXME: remove this line when NCQ added */ - case ATA_CMD_FPDMA_READ: - case ATA_CMD_FPDMA_WRITE: - mv_crqb_pack_cmd(cw++, tf->hob_feature, ATA_REG_FEATURE, 0); - mv_crqb_pack_cmd(cw++, tf->feature, ATA_REG_FEATURE, 0); - break; -#endif /* FIXME: remove this line when NCQ added */ - default: - /* The only other commands EDMA supports in non-queued and - * non-NCQ mode are: [RW] STREAM DMA and W DMA FUA EXT, none - * of which are defined/used by Linux. If we get here, this - * driver needs work. - * - * FIXME: modify libata to give qc_prep a return value and - * return error here. - */ - BUG_ON(tf->command); - break; - } - mv_crqb_pack_cmd(cw++, tf->nsect, ATA_REG_NSECT, 0); - mv_crqb_pack_cmd(cw++, tf->hob_lbal, ATA_REG_LBAL, 0); - mv_crqb_pack_cmd(cw++, tf->lbal, ATA_REG_LBAL, 0); - mv_crqb_pack_cmd(cw++, tf->hob_lbam, ATA_REG_LBAM, 0); - mv_crqb_pack_cmd(cw++, tf->lbam, ATA_REG_LBAM, 0); - mv_crqb_pack_cmd(cw++, tf->hob_lbah, ATA_REG_LBAH, 0); - mv_crqb_pack_cmd(cw++, tf->lbah, ATA_REG_LBAH, 0); - mv_crqb_pack_cmd(cw++, tf->device, ATA_REG_DEVICE, 0); - mv_crqb_pack_cmd(cw++, tf->command, ATA_REG_CMD, 1); /* last */ - - if (!(qc->flags & ATA_QCFLAG_DMAMAP)) - return; - mv_fill_sg(qc); -} - -/** - * mv_qc_prep_iie - Host specific command preparation. - * @qc: queued command to prepare - * - * This routine simply redirects to the general purpose routine - * if command is not DMA. Else, it handles prep of the CRQB - * (command request block), does some sanity checking, and calls - * the SG load routine. - * - * LOCKING: - * Inherited from caller. - */ -static void mv_qc_prep_iie(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct mv_port_priv *pp = ap->private_data; - struct mv_crqb_iie *crqb; - struct ata_taskfile *tf; - unsigned in_index; - u32 flags = 0; - - if (ATA_PROT_DMA != qc->tf.protocol) - return; - - /* Fill in Gen IIE command request block - */ - if (!(qc->tf.flags & ATA_TFLAG_WRITE)) - flags |= CRQB_FLAG_READ; - - WARN_ON(MV_MAX_Q_DEPTH <= qc->tag); - flags |= qc->tag << CRQB_TAG_SHIFT; - - /* get current queue index from hardware */ - in_index = (readl(mv_ap_base(ap) + EDMA_REQ_Q_IN_PTR_OFS) - >> EDMA_REQ_Q_PTR_SHIFT) & MV_MAX_Q_DEPTH_MASK; - - crqb = (struct mv_crqb_iie *) &pp->crqb[in_index]; - crqb->addr = cpu_to_le32(pp->sg_tbl_dma & 0xffffffff); - crqb->addr_hi = cpu_to_le32((pp->sg_tbl_dma >> 16) >> 16); - crqb->flags = cpu_to_le32(flags); - - tf = &qc->tf; - crqb->ata_cmd[0] = cpu_to_le32( - (tf->command << 16) | - (tf->feature << 24) - ); - crqb->ata_cmd[1] = cpu_to_le32( - (tf->lbal << 0) | - (tf->lbam << 8) | - (tf->lbah << 16) | - (tf->device << 24) - ); - crqb->ata_cmd[2] = cpu_to_le32( - (tf->hob_lbal << 0) | - (tf->hob_lbam << 8) | - (tf->hob_lbah << 16) | - (tf->hob_feature << 24) - ); - crqb->ata_cmd[3] = cpu_to_le32( - (tf->nsect << 0) | - (tf->hob_nsect << 8) - ); - - if (!(qc->flags & ATA_QCFLAG_DMAMAP)) - return; - mv_fill_sg(qc); -} - -/** - * mv_qc_issue - Initiate a command to the host - * @qc: queued command to start - * - * This routine simply redirects to the general purpose routine - * if command is not DMA. Else, it sanity checks our local - * caches of the request producer/consumer indices then enables - * DMA and bumps the request producer index. - * - * LOCKING: - * Inherited from caller. - */ -static unsigned int mv_qc_issue(struct ata_queued_cmd *qc) -{ - void __iomem *port_mmio = mv_ap_base(qc->ap); - struct mv_port_priv *pp = qc->ap->private_data; - unsigned in_index; - u32 in_ptr; - - if (ATA_PROT_DMA != qc->tf.protocol) { - /* We're about to send a non-EDMA capable command to the - * port. Turn off EDMA so there won't be problems accessing - * shadow block, etc registers. - */ - mv_stop_dma(qc->ap); - return ata_qc_issue_prot(qc); - } - - in_ptr = readl(port_mmio + EDMA_REQ_Q_IN_PTR_OFS); - in_index = (in_ptr >> EDMA_REQ_Q_PTR_SHIFT) & MV_MAX_Q_DEPTH_MASK; - - /* until we do queuing, the queue should be empty at this point */ - WARN_ON(in_index != ((readl(port_mmio + EDMA_REQ_Q_OUT_PTR_OFS) - >> EDMA_REQ_Q_PTR_SHIFT) & MV_MAX_Q_DEPTH_MASK)); - - in_index = mv_inc_q_index(in_index); /* now incr producer index */ - - mv_start_dma(port_mmio, pp); - - /* and write the request in pointer to kick the EDMA to life */ - in_ptr &= EDMA_REQ_Q_BASE_LO_MASK; - in_ptr |= in_index << EDMA_REQ_Q_PTR_SHIFT; - writelfl(in_ptr, port_mmio + EDMA_REQ_Q_IN_PTR_OFS); - - return 0; -} - -/** - * mv_get_crpb_status - get status from most recently completed cmd - * @ap: ATA channel to manipulate - * - * This routine is for use when the port is in DMA mode, when it - * will be using the CRPB (command response block) method of - * returning command completion information. We check indices - * are good, grab status, and bump the response consumer index to - * prove that we're up to date. - * - * LOCKING: - * Inherited from caller. - */ -static u8 mv_get_crpb_status(struct ata_port *ap) -{ - void __iomem *port_mmio = mv_ap_base(ap); - struct mv_port_priv *pp = ap->private_data; - unsigned out_index; - u32 out_ptr; - u8 ata_status; - - out_ptr = readl(port_mmio + EDMA_RSP_Q_OUT_PTR_OFS); - out_index = (out_ptr >> EDMA_RSP_Q_PTR_SHIFT) & MV_MAX_Q_DEPTH_MASK; - - ata_status = le16_to_cpu(pp->crpb[out_index].flags) - >> CRPB_FLAG_STATUS_SHIFT; - - /* increment our consumer index... */ - out_index = mv_inc_q_index(out_index); - - /* and, until we do NCQ, there should only be 1 CRPB waiting */ - WARN_ON(out_index != ((readl(port_mmio + EDMA_RSP_Q_IN_PTR_OFS) - >> EDMA_RSP_Q_PTR_SHIFT) & MV_MAX_Q_DEPTH_MASK)); - - /* write out our inc'd consumer index so EDMA knows we're caught up */ - out_ptr &= EDMA_RSP_Q_BASE_LO_MASK; - out_ptr |= out_index << EDMA_RSP_Q_PTR_SHIFT; - writelfl(out_ptr, port_mmio + EDMA_RSP_Q_OUT_PTR_OFS); - - /* Return ATA status register for completed CRPB */ - return ata_status; -} - -/** - * mv_err_intr - Handle error interrupts on the port - * @ap: ATA channel to manipulate - * @reset_allowed: bool: 0 == don't trigger from reset here - * - * In most cases, just clear the interrupt and move on. However, - * some cases require an eDMA reset, which is done right before - * the COMRESET in mv_phy_reset(). The SERR case requires a - * clear of pending errors in the SATA SERROR register. Finally, - * if the port disabled DMA, update our cached copy to match. - * - * LOCKING: - * Inherited from caller. - */ -static void mv_err_intr(struct ata_port *ap, int reset_allowed) -{ - void __iomem *port_mmio = mv_ap_base(ap); - u32 edma_err_cause, serr = 0; - - edma_err_cause = readl(port_mmio + EDMA_ERR_IRQ_CAUSE_OFS); - - if (EDMA_ERR_SERR & edma_err_cause) { - sata_scr_read(ap, SCR_ERROR, &serr); - sata_scr_write_flush(ap, SCR_ERROR, serr); - } - if (EDMA_ERR_SELF_DIS & edma_err_cause) { - struct mv_port_priv *pp = ap->private_data; - pp->pp_flags &= ~MV_PP_FLAG_EDMA_EN; - } - DPRINTK(KERN_ERR "ata%u: port error; EDMA err cause: 0x%08x " - "SERR: 0x%08x\n", ap->id, edma_err_cause, serr); - - /* Clear EDMA now that SERR cleanup done */ - writelfl(0, port_mmio + EDMA_ERR_IRQ_CAUSE_OFS); - - /* check for fatal here and recover if needed */ - if (reset_allowed && (EDMA_ERR_FATAL & edma_err_cause)) - mv_stop_and_reset(ap); -} - -/** - * mv_host_intr - Handle all interrupts on the given host controller - * @host_set: host specific structure - * @relevant: port error bits relevant to this host controller - * @hc: which host controller we're to look at - * - * Read then write clear the HC interrupt status then walk each - * port connected to the HC and see if it needs servicing. Port - * success ints are reported in the HC interrupt status reg, the - * port error ints are reported in the higher level main - * interrupt status register and thus are passed in via the - * 'relevant' argument. - * - * LOCKING: - * Inherited from caller. - */ -static void mv_host_intr(struct ata_host_set *host_set, u32 relevant, - unsigned int hc) -{ - void __iomem *mmio = host_set->mmio_base; - void __iomem *hc_mmio = mv_hc_base(mmio, hc); - struct ata_queued_cmd *qc; - u32 hc_irq_cause; - int shift, port, port0, hard_port, handled; - unsigned int err_mask; - - if (hc == 0) { - port0 = 0; - } else { - port0 = MV_PORTS_PER_HC; - } - - /* we'll need the HC success int register in most cases */ - hc_irq_cause = readl(hc_mmio + HC_IRQ_CAUSE_OFS); - if (hc_irq_cause) { - writelfl(~hc_irq_cause, hc_mmio + HC_IRQ_CAUSE_OFS); - } - - VPRINTK("ENTER, hc%u relevant=0x%08x HC IRQ cause=0x%08x\n", - hc,relevant,hc_irq_cause); - - for (port = port0; port < port0 + MV_PORTS_PER_HC; port++) { - u8 ata_status = 0; - struct ata_port *ap = host_set->ports[port]; - struct mv_port_priv *pp = ap->private_data; - - hard_port = mv_hardport_from_port(port); /* range 0..3 */ - handled = 0; /* ensure ata_status is set if handled++ */ - - /* Note that DEV_IRQ might happen spuriously during EDMA, - * and should be ignored in such cases. - * The cause of this is still under investigation. - */ - if (pp->pp_flags & MV_PP_FLAG_EDMA_EN) { - /* EDMA: check for response queue interrupt */ - if ((CRPB_DMA_DONE << hard_port) & hc_irq_cause) { - ata_status = mv_get_crpb_status(ap); - handled = 1; - } - } else { - /* PIO: check for device (drive) interrupt */ - if ((DEV_IRQ << hard_port) & hc_irq_cause) { - ata_status = readb((void __iomem *) - ap->ioaddr.status_addr); - handled = 1; - /* ignore spurious intr if drive still BUSY */ - if (ata_status & ATA_BUSY) { - ata_status = 0; - handled = 0; - } - } - } - - if (ap && (ap->flags & ATA_FLAG_DISABLED)) - continue; - - err_mask = ac_err_mask(ata_status); - - shift = port << 1; /* (port * 2) */ - if (port >= MV_PORTS_PER_HC) { - shift++; /* skip bit 8 in the HC Main IRQ reg */ - } - if ((PORT0_ERR << shift) & relevant) { - mv_err_intr(ap, 1); - err_mask |= AC_ERR_OTHER; - handled = 1; - } - - if (handled) { - qc = ata_qc_from_tag(ap, ap->active_tag); - if (qc && (qc->flags & ATA_QCFLAG_ACTIVE)) { - VPRINTK("port %u IRQ found for qc, " - "ata_status 0x%x\n", port,ata_status); - /* mark qc status appropriately */ - if (!(qc->tf.flags & ATA_TFLAG_POLLING)) { - qc->err_mask |= err_mask; - ata_qc_complete(qc); - } - } - } - } - VPRINTK("EXIT\n"); -} - -/** - * mv_interrupt - - * @irq: unused - * @dev_instance: private data; in this case the host structure - * @regs: unused - * - * Read the read only register to determine if any host - * controllers have pending interrupts. If so, call lower level - * routine to handle. Also check for PCI errors which are only - * reported here. - * - * LOCKING: - * This routine holds the host_set lock while processing pending - * interrupts. - */ -static irqreturn_t mv_interrupt(int irq, void *dev_instance, - struct pt_regs *regs) -{ - struct ata_host_set *host_set = dev_instance; - unsigned int hc, handled = 0, n_hcs; - void __iomem *mmio = host_set->mmio_base; - struct mv_host_priv *hpriv; - u32 irq_stat; - - irq_stat = readl(mmio + HC_MAIN_IRQ_CAUSE_OFS); - - /* check the cases where we either have nothing pending or have read - * a bogus register value which can indicate HW removal or PCI fault - */ - if (!irq_stat || (0xffffffffU == irq_stat)) { - return IRQ_NONE; - } - - n_hcs = mv_get_hc_count(host_set->ports[0]->flags); - spin_lock(&host_set->lock); - - for (hc = 0; hc < n_hcs; hc++) { - u32 relevant = irq_stat & (HC0_IRQ_PEND << (hc * HC_SHIFT)); - if (relevant) { - mv_host_intr(host_set, relevant, hc); - handled++; - } - } - - hpriv = host_set->private_data; - if (IS_60XX(hpriv)) { - /* deal with the interrupt coalescing bits */ - if (irq_stat & (TRAN_LO_DONE | TRAN_HI_DONE | PORTS_0_7_COAL_DONE)) { - writelfl(0, mmio + MV_IRQ_COAL_CAUSE_LO); - writelfl(0, mmio + MV_IRQ_COAL_CAUSE_HI); - writelfl(0, mmio + MV_IRQ_COAL_CAUSE); - } - } - - if (PCI_ERR & irq_stat) { - printk(KERN_ERR DRV_NAME ": PCI ERROR; PCI IRQ cause=0x%08x\n", - readl(mmio + PCI_IRQ_CAUSE_OFS)); - - DPRINTK("All regs @ PCI error\n"); - mv_dump_all_regs(mmio, -1, to_pci_dev(host_set->dev)); - - writelfl(0, mmio + PCI_IRQ_CAUSE_OFS); - handled++; - } - spin_unlock(&host_set->lock); - - return IRQ_RETVAL(handled); -} - -static void __iomem *mv5_phy_base(void __iomem *mmio, unsigned int port) -{ - void __iomem *hc_mmio = mv_hc_base_from_port(mmio, port); - unsigned long ofs = (mv_hardport_from_port(port) + 1) * 0x100UL; - - return hc_mmio + ofs; -} - -static unsigned int mv5_scr_offset(unsigned int sc_reg_in) -{ - unsigned int ofs; - - switch (sc_reg_in) { - case SCR_STATUS: - case SCR_ERROR: - case SCR_CONTROL: - ofs = sc_reg_in * sizeof(u32); - break; - default: - ofs = 0xffffffffU; - break; - } - return ofs; -} - -static u32 mv5_scr_read(struct ata_port *ap, unsigned int sc_reg_in) -{ - void __iomem *mmio = mv5_phy_base(ap->host_set->mmio_base, ap->port_no); - unsigned int ofs = mv5_scr_offset(sc_reg_in); - - if (ofs != 0xffffffffU) - return readl(mmio + ofs); - else - return (u32) ofs; -} - -static void mv5_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) -{ - void __iomem *mmio = mv5_phy_base(ap->host_set->mmio_base, ap->port_no); - unsigned int ofs = mv5_scr_offset(sc_reg_in); - - if (ofs != 0xffffffffU) - writelfl(val, mmio + ofs); -} - -static void mv5_reset_bus(struct pci_dev *pdev, void __iomem *mmio) -{ - u8 rev_id; - int early_5080; - - pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id); - - early_5080 = (pdev->device == 0x5080) && (rev_id == 0); - - if (!early_5080) { - u32 tmp = readl(mmio + MV_PCI_EXP_ROM_BAR_CTL); - tmp |= (1 << 0); - writel(tmp, mmio + MV_PCI_EXP_ROM_BAR_CTL); - } - - mv_reset_pci_bus(pdev, mmio); -} - -static void mv5_reset_flash(struct mv_host_priv *hpriv, void __iomem *mmio) -{ - writel(0x0fcfffff, mmio + MV_FLASH_CTL); -} - -static void mv5_read_preamp(struct mv_host_priv *hpriv, int idx, - void __iomem *mmio) -{ - void __iomem *phy_mmio = mv5_phy_base(mmio, idx); - u32 tmp; - - tmp = readl(phy_mmio + MV5_PHY_MODE); - - hpriv->signal[idx].pre = tmp & 0x1800; /* bits 12:11 */ - hpriv->signal[idx].amps = tmp & 0xe0; /* bits 7:5 */ -} - -static void mv5_enable_leds(struct mv_host_priv *hpriv, void __iomem *mmio) -{ - u32 tmp; - - writel(0, mmio + MV_GPIO_PORT_CTL); - - /* FIXME: handle MV_HP_ERRATA_50XXB2 errata */ - - tmp = readl(mmio + MV_PCI_EXP_ROM_BAR_CTL); - tmp |= ~(1 << 0); - writel(tmp, mmio + MV_PCI_EXP_ROM_BAR_CTL); -} - -static void mv5_phy_errata(struct mv_host_priv *hpriv, void __iomem *mmio, - unsigned int port) -{ - void __iomem *phy_mmio = mv5_phy_base(mmio, port); - const u32 mask = (1<<12) | (1<<11) | (1<<7) | (1<<6) | (1<<5); - u32 tmp; - int fix_apm_sq = (hpriv->hp_flags & MV_HP_ERRATA_50XXB0); - - if (fix_apm_sq) { - tmp = readl(phy_mmio + MV5_LT_MODE); - tmp |= (1 << 19); - writel(tmp, phy_mmio + MV5_LT_MODE); - - tmp = readl(phy_mmio + MV5_PHY_CTL); - tmp &= ~0x3; - tmp |= 0x1; - writel(tmp, phy_mmio + MV5_PHY_CTL); - } - - tmp = readl(phy_mmio + MV5_PHY_MODE); - tmp &= ~mask; - tmp |= hpriv->signal[port].pre; - tmp |= hpriv->signal[port].amps; - writel(tmp, phy_mmio + MV5_PHY_MODE); -} - - -#undef ZERO -#define ZERO(reg) writel(0, port_mmio + (reg)) -static void mv5_reset_hc_port(struct mv_host_priv *hpriv, void __iomem *mmio, - unsigned int port) -{ - void __iomem *port_mmio = mv_port_base(mmio, port); - - writelfl(EDMA_DS, port_mmio + EDMA_CMD_OFS); - - mv_channel_reset(hpriv, mmio, port); - - ZERO(0x028); /* command */ - writel(0x11f, port_mmio + EDMA_CFG_OFS); - ZERO(0x004); /* timer */ - ZERO(0x008); /* irq err cause */ - ZERO(0x00c); /* irq err mask */ - ZERO(0x010); /* rq bah */ - ZERO(0x014); /* rq inp */ - ZERO(0x018); /* rq outp */ - ZERO(0x01c); /* respq bah */ - ZERO(0x024); /* respq outp */ - ZERO(0x020); /* respq inp */ - ZERO(0x02c); /* test control */ - writel(0xbc, port_mmio + EDMA_IORDY_TMOUT); -} -#undef ZERO - -#define ZERO(reg) writel(0, hc_mmio + (reg)) -static void mv5_reset_one_hc(struct mv_host_priv *hpriv, void __iomem *mmio, - unsigned int hc) -{ - void __iomem *hc_mmio = mv_hc_base(mmio, hc); - u32 tmp; - - ZERO(0x00c); - ZERO(0x010); - ZERO(0x014); - ZERO(0x018); - - tmp = readl(hc_mmio + 0x20); - tmp &= 0x1c1c1c1c; - tmp |= 0x03030303; - writel(tmp, hc_mmio + 0x20); -} -#undef ZERO - -static int mv5_reset_hc(struct mv_host_priv *hpriv, void __iomem *mmio, - unsigned int n_hc) -{ - unsigned int hc, port; - - for (hc = 0; hc < n_hc; hc++) { - for (port = 0; port < MV_PORTS_PER_HC; port++) - mv5_reset_hc_port(hpriv, mmio, - (hc * MV_PORTS_PER_HC) + port); - - mv5_reset_one_hc(hpriv, mmio, hc); - } - - return 0; -} - -#undef ZERO -#define ZERO(reg) writel(0, mmio + (reg)) -static void mv_reset_pci_bus(struct pci_dev *pdev, void __iomem *mmio) -{ - u32 tmp; - - tmp = readl(mmio + MV_PCI_MODE); - tmp &= 0xff00ffff; - writel(tmp, mmio + MV_PCI_MODE); - - ZERO(MV_PCI_DISC_TIMER); - ZERO(MV_PCI_MSI_TRIGGER); - writel(0x000100ff, mmio + MV_PCI_XBAR_TMOUT); - ZERO(HC_MAIN_IRQ_MASK_OFS); - ZERO(MV_PCI_SERR_MASK); - ZERO(PCI_IRQ_CAUSE_OFS); - ZERO(PCI_IRQ_MASK_OFS); - ZERO(MV_PCI_ERR_LOW_ADDRESS); - ZERO(MV_PCI_ERR_HIGH_ADDRESS); - ZERO(MV_PCI_ERR_ATTRIBUTE); - ZERO(MV_PCI_ERR_COMMAND); -} -#undef ZERO - -static void mv6_reset_flash(struct mv_host_priv *hpriv, void __iomem *mmio) -{ - u32 tmp; - - mv5_reset_flash(hpriv, mmio); - - tmp = readl(mmio + MV_GPIO_PORT_CTL); - tmp &= 0x3; - tmp |= (1 << 5) | (1 << 6); - writel(tmp, mmio + MV_GPIO_PORT_CTL); -} - -/** - * mv6_reset_hc - Perform the 6xxx global soft reset - * @mmio: base address of the HBA - * - * This routine only applies to 6xxx parts. - * - * LOCKING: - * Inherited from caller. - */ -static int mv6_reset_hc(struct mv_host_priv *hpriv, void __iomem *mmio, - unsigned int n_hc) -{ - void __iomem *reg = mmio + PCI_MAIN_CMD_STS_OFS; - int i, rc = 0; - u32 t; - - /* Following procedure defined in PCI "main command and status - * register" table. - */ - t = readl(reg); - writel(t | STOP_PCI_MASTER, reg); - - for (i = 0; i < 1000; i++) { - udelay(1); - t = readl(reg); - if (PCI_MASTER_EMPTY & t) { - break; - } - } - if (!(PCI_MASTER_EMPTY & t)) { - printk(KERN_ERR DRV_NAME ": PCI master won't flush\n"); - rc = 1; - goto done; - } - - /* set reset */ - i = 5; - do { - writel(t | GLOB_SFT_RST, reg); - t = readl(reg); - udelay(1); - } while (!(GLOB_SFT_RST & t) && (i-- > 0)); - - if (!(GLOB_SFT_RST & t)) { - printk(KERN_ERR DRV_NAME ": can't set global reset\n"); - rc = 1; - goto done; - } - - /* clear reset and *reenable the PCI master* (not mentioned in spec) */ - i = 5; - do { - writel(t & ~(GLOB_SFT_RST | STOP_PCI_MASTER), reg); - t = readl(reg); - udelay(1); - } while ((GLOB_SFT_RST & t) && (i-- > 0)); - - if (GLOB_SFT_RST & t) { - printk(KERN_ERR DRV_NAME ": can't clear global reset\n"); - rc = 1; - } -done: - return rc; -} - -static void mv6_read_preamp(struct mv_host_priv *hpriv, int idx, - void __iomem *mmio) -{ - void __iomem *port_mmio; - u32 tmp; - - tmp = readl(mmio + MV_RESET_CFG); - if ((tmp & (1 << 0)) == 0) { - hpriv->signal[idx].amps = 0x7 << 8; - hpriv->signal[idx].pre = 0x1 << 5; - return; - } - - port_mmio = mv_port_base(mmio, idx); - tmp = readl(port_mmio + PHY_MODE2); - - hpriv->signal[idx].amps = tmp & 0x700; /* bits 10:8 */ - hpriv->signal[idx].pre = tmp & 0xe0; /* bits 7:5 */ -} - -static void mv6_enable_leds(struct mv_host_priv *hpriv, void __iomem *mmio) -{ - writel(0x00000060, mmio + MV_GPIO_PORT_CTL); -} - -static void mv6_phy_errata(struct mv_host_priv *hpriv, void __iomem *mmio, - unsigned int port) -{ - void __iomem *port_mmio = mv_port_base(mmio, port); - - u32 hp_flags = hpriv->hp_flags; - int fix_phy_mode2 = - hp_flags & (MV_HP_ERRATA_60X1B2 | MV_HP_ERRATA_60X1C0); - int fix_phy_mode4 = - hp_flags & (MV_HP_ERRATA_60X1B2 | MV_HP_ERRATA_60X1C0); - u32 m2, tmp; - - if (fix_phy_mode2) { - m2 = readl(port_mmio + PHY_MODE2); - m2 &= ~(1 << 16); - m2 |= (1 << 31); - writel(m2, port_mmio + PHY_MODE2); - - udelay(200); - - m2 = readl(port_mmio + PHY_MODE2); - m2 &= ~((1 << 16) | (1 << 31)); - writel(m2, port_mmio + PHY_MODE2); - - udelay(200); - } - - /* who knows what this magic does */ - tmp = readl(port_mmio + PHY_MODE3); - tmp &= ~0x7F800000; - tmp |= 0x2A800000; - writel(tmp, port_mmio + PHY_MODE3); - - if (fix_phy_mode4) { - u32 m4; - - m4 = readl(port_mmio + PHY_MODE4); - - if (hp_flags & MV_HP_ERRATA_60X1B2) - tmp = readl(port_mmio + 0x310); - - m4 = (m4 & ~(1 << 1)) | (1 << 0); - - writel(m4, port_mmio + PHY_MODE4); - - if (hp_flags & MV_HP_ERRATA_60X1B2) - writel(tmp, port_mmio + 0x310); - } - - /* Revert values of pre-emphasis and signal amps to the saved ones */ - m2 = readl(port_mmio + PHY_MODE2); - - m2 &= ~MV_M2_PREAMP_MASK; - m2 |= hpriv->signal[port].amps; - m2 |= hpriv->signal[port].pre; - m2 &= ~(1 << 16); - - /* according to mvSata 3.6.1, some IIE values are fixed */ - if (IS_GEN_IIE(hpriv)) { - m2 &= ~0xC30FF01F; - m2 |= 0x0000900F; - } - - writel(m2, port_mmio + PHY_MODE2); -} - -static void mv_channel_reset(struct mv_host_priv *hpriv, void __iomem *mmio, - unsigned int port_no) -{ - void __iomem *port_mmio = mv_port_base(mmio, port_no); - - writelfl(ATA_RST, port_mmio + EDMA_CMD_OFS); - - if (IS_60XX(hpriv)) { - u32 ifctl = readl(port_mmio + SATA_INTERFACE_CTL); - ifctl |= (1 << 7); /* enable gen2i speed */ - ifctl = (ifctl & 0xfff) | 0x9b1000; /* from chip spec */ - writelfl(ifctl, port_mmio + SATA_INTERFACE_CTL); - } - - udelay(25); /* allow reset propagation */ - - /* Spec never mentions clearing the bit. Marvell's driver does - * clear the bit, however. - */ - writelfl(0, port_mmio + EDMA_CMD_OFS); - - hpriv->ops->phy_errata(hpriv, mmio, port_no); - - if (IS_50XX(hpriv)) - mdelay(1); -} - -static void mv_stop_and_reset(struct ata_port *ap) -{ - struct mv_host_priv *hpriv = ap->host_set->private_data; - void __iomem *mmio = ap->host_set->mmio_base; - - mv_stop_dma(ap); - - mv_channel_reset(hpriv, mmio, ap->port_no); - - __mv_phy_reset(ap, 0); -} - -static inline void __msleep(unsigned int msec, int can_sleep) -{ - if (can_sleep) - msleep(msec); - else - mdelay(msec); -} - -/** - * __mv_phy_reset - Perform eDMA reset followed by COMRESET - * @ap: ATA channel to manipulate - * - * Part of this is taken from __sata_phy_reset and modified to - * not sleep since this routine gets called from interrupt level. - * - * LOCKING: - * Inherited from caller. This is coded to safe to call at - * interrupt level, i.e. it does not sleep. - */ -static void __mv_phy_reset(struct ata_port *ap, int can_sleep) -{ - struct mv_port_priv *pp = ap->private_data; - struct mv_host_priv *hpriv = ap->host_set->private_data; - void __iomem *port_mmio = mv_ap_base(ap); - struct ata_taskfile tf; - struct ata_device *dev = &ap->device[0]; - unsigned long timeout; - int retry = 5; - u32 sstatus; - - VPRINTK("ENTER, port %u, mmio 0x%p\n", ap->port_no, port_mmio); - - DPRINTK("S-regs after ATA_RST: SStat 0x%08x SErr 0x%08x " - "SCtrl 0x%08x\n", mv_scr_read(ap, SCR_STATUS), - mv_scr_read(ap, SCR_ERROR), mv_scr_read(ap, SCR_CONTROL)); - - /* Issue COMRESET via SControl */ -comreset_retry: - sata_scr_write_flush(ap, SCR_CONTROL, 0x301); - __msleep(1, can_sleep); - - sata_scr_write_flush(ap, SCR_CONTROL, 0x300); - __msleep(20, can_sleep); - - timeout = jiffies + msecs_to_jiffies(200); - do { - sata_scr_read(ap, SCR_STATUS, &sstatus); - sstatus &= 0x3; - if ((sstatus == 3) || (sstatus == 0)) - break; - - __msleep(1, can_sleep); - } while (time_before(jiffies, timeout)); - - /* work around errata */ - if (IS_60XX(hpriv) && - (sstatus != 0x0) && (sstatus != 0x113) && (sstatus != 0x123) && - (retry-- > 0)) - goto comreset_retry; - - DPRINTK("S-regs after PHY wake: SStat 0x%08x SErr 0x%08x " - "SCtrl 0x%08x\n", mv_scr_read(ap, SCR_STATUS), - mv_scr_read(ap, SCR_ERROR), mv_scr_read(ap, SCR_CONTROL)); - - if (ata_port_online(ap)) { - ata_port_probe(ap); - } else { - sata_scr_read(ap, SCR_STATUS, &sstatus); - ata_port_printk(ap, KERN_INFO, - "no device found (phy stat %08x)\n", sstatus); - ata_port_disable(ap); - return; - } - ap->cbl = ATA_CBL_SATA; - - /* even after SStatus reflects that device is ready, - * it seems to take a while for link to be fully - * established (and thus Status no longer 0x80/0x7F), - * so we poll a bit for that, here. - */ - retry = 20; - while (1) { - u8 drv_stat = ata_check_status(ap); - if ((drv_stat != 0x80) && (drv_stat != 0x7f)) - break; - __msleep(500, can_sleep); - if (retry-- <= 0) - break; - } - - tf.lbah = readb((void __iomem *) ap->ioaddr.lbah_addr); - tf.lbam = readb((void __iomem *) ap->ioaddr.lbam_addr); - tf.lbal = readb((void __iomem *) ap->ioaddr.lbal_addr); - tf.nsect = readb((void __iomem *) ap->ioaddr.nsect_addr); - - dev->class = ata_dev_classify(&tf); - if (!ata_dev_enabled(dev)) { - VPRINTK("Port disabled post-sig: No device present.\n"); - ata_port_disable(ap); - } - - writelfl(0, port_mmio + EDMA_ERR_IRQ_CAUSE_OFS); - - pp->pp_flags &= ~MV_PP_FLAG_EDMA_EN; - - VPRINTK("EXIT\n"); -} - -static void mv_phy_reset(struct ata_port *ap) -{ - __mv_phy_reset(ap, 1); -} - -/** - * mv_eng_timeout - Routine called by libata when SCSI times out I/O - * @ap: ATA channel to manipulate - * - * Intent is to clear all pending error conditions, reset the - * chip/bus, fail the command, and move on. - * - * LOCKING: - * This routine holds the host_set lock while failing the command. - */ -static void mv_eng_timeout(struct ata_port *ap) -{ - struct ata_queued_cmd *qc; - unsigned long flags; - - ata_port_printk(ap, KERN_ERR, "Entering mv_eng_timeout\n"); - DPRINTK("All regs @ start of eng_timeout\n"); - mv_dump_all_regs(ap->host_set->mmio_base, ap->port_no, - to_pci_dev(ap->host_set->dev)); - - qc = ata_qc_from_tag(ap, ap->active_tag); - printk(KERN_ERR "mmio_base %p ap %p qc %p scsi_cmnd %p &cmnd %p\n", - ap->host_set->mmio_base, ap, qc, qc->scsicmd, - &qc->scsicmd->cmnd); - - spin_lock_irqsave(&ap->host_set->lock, flags); - mv_err_intr(ap, 0); - mv_stop_and_reset(ap); - spin_unlock_irqrestore(&ap->host_set->lock, flags); - - WARN_ON(!(qc->flags & ATA_QCFLAG_ACTIVE)); - if (qc->flags & ATA_QCFLAG_ACTIVE) { - qc->err_mask |= AC_ERR_TIMEOUT; - ata_eh_qc_complete(qc); - } -} - -/** - * mv_port_init - Perform some early initialization on a single port. - * @port: libata data structure storing shadow register addresses - * @port_mmio: base address of the port - * - * Initialize shadow register mmio addresses, clear outstanding - * interrupts on the port, and unmask interrupts for the future - * start of the port. - * - * LOCKING: - * Inherited from caller. - */ -static void mv_port_init(struct ata_ioports *port, void __iomem *port_mmio) -{ - unsigned long shd_base = (unsigned long) port_mmio + SHD_BLK_OFS; - unsigned serr_ofs; - - /* PIO related setup - */ - port->data_addr = shd_base + (sizeof(u32) * ATA_REG_DATA); - port->error_addr = - port->feature_addr = shd_base + (sizeof(u32) * ATA_REG_ERR); - port->nsect_addr = shd_base + (sizeof(u32) * ATA_REG_NSECT); - port->lbal_addr = shd_base + (sizeof(u32) * ATA_REG_LBAL); - port->lbam_addr = shd_base + (sizeof(u32) * ATA_REG_LBAM); - port->lbah_addr = shd_base + (sizeof(u32) * ATA_REG_LBAH); - port->device_addr = shd_base + (sizeof(u32) * ATA_REG_DEVICE); - port->status_addr = - port->command_addr = shd_base + (sizeof(u32) * ATA_REG_STATUS); - /* special case: control/altstatus doesn't have ATA_REG_ address */ - port->altstatus_addr = port->ctl_addr = shd_base + SHD_CTL_AST_OFS; - - /* unused: */ - port->cmd_addr = port->bmdma_addr = port->scr_addr = 0; - - /* Clear any currently outstanding port interrupt conditions */ - serr_ofs = mv_scr_offset(SCR_ERROR); - writelfl(readl(port_mmio + serr_ofs), port_mmio + serr_ofs); - writelfl(0, port_mmio + EDMA_ERR_IRQ_CAUSE_OFS); - - /* unmask all EDMA error interrupts */ - writelfl(~0, port_mmio + EDMA_ERR_IRQ_MASK_OFS); - - VPRINTK("EDMA cfg=0x%08x EDMA IRQ err cause/mask=0x%08x/0x%08x\n", - readl(port_mmio + EDMA_CFG_OFS), - readl(port_mmio + EDMA_ERR_IRQ_CAUSE_OFS), - readl(port_mmio + EDMA_ERR_IRQ_MASK_OFS)); -} - -static int mv_chip_id(struct pci_dev *pdev, struct mv_host_priv *hpriv, - unsigned int board_idx) -{ - u8 rev_id; - u32 hp_flags = hpriv->hp_flags; - - pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id); - - switch(board_idx) { - case chip_5080: - hpriv->ops = &mv5xxx_ops; - hp_flags |= MV_HP_50XX; - - switch (rev_id) { - case 0x1: - hp_flags |= MV_HP_ERRATA_50XXB0; - break; - case 0x3: - hp_flags |= MV_HP_ERRATA_50XXB2; - break; - default: - dev_printk(KERN_WARNING, &pdev->dev, - "Applying 50XXB2 workarounds to unknown rev\n"); - hp_flags |= MV_HP_ERRATA_50XXB2; - break; - } - break; - - case chip_504x: - case chip_508x: - hpriv->ops = &mv5xxx_ops; - hp_flags |= MV_HP_50XX; - - switch (rev_id) { - case 0x0: - hp_flags |= MV_HP_ERRATA_50XXB0; - break; - case 0x3: - hp_flags |= MV_HP_ERRATA_50XXB2; - break; - default: - dev_printk(KERN_WARNING, &pdev->dev, - "Applying B2 workarounds to unknown rev\n"); - hp_flags |= MV_HP_ERRATA_50XXB2; - break; - } - break; - - case chip_604x: - case chip_608x: - hpriv->ops = &mv6xxx_ops; - - switch (rev_id) { - case 0x7: - hp_flags |= MV_HP_ERRATA_60X1B2; - break; - case 0x9: - hp_flags |= MV_HP_ERRATA_60X1C0; - break; - default: - dev_printk(KERN_WARNING, &pdev->dev, - "Applying B2 workarounds to unknown rev\n"); - hp_flags |= MV_HP_ERRATA_60X1B2; - break; - } - break; - - case chip_7042: - case chip_6042: - hpriv->ops = &mv6xxx_ops; - - hp_flags |= MV_HP_GEN_IIE; - - switch (rev_id) { - case 0x0: - hp_flags |= MV_HP_ERRATA_XX42A0; - break; - case 0x1: - hp_flags |= MV_HP_ERRATA_60X1C0; - break; - default: - dev_printk(KERN_WARNING, &pdev->dev, - "Applying 60X1C0 workarounds to unknown rev\n"); - hp_flags |= MV_HP_ERRATA_60X1C0; - break; - } - break; - - default: - printk(KERN_ERR DRV_NAME ": BUG: invalid board index %u\n", board_idx); - return 1; - } - - hpriv->hp_flags = hp_flags; - - return 0; -} - -/** - * mv_init_host - Perform some early initialization of the host. - * @pdev: host PCI device - * @probe_ent: early data struct representing the host - * - * If possible, do an early global reset of the host. Then do - * our port init and clear/unmask all/relevant host interrupts. - * - * LOCKING: - * Inherited from caller. - */ -static int mv_init_host(struct pci_dev *pdev, struct ata_probe_ent *probe_ent, - unsigned int board_idx) -{ - int rc = 0, n_hc, port, hc; - void __iomem *mmio = probe_ent->mmio_base; - struct mv_host_priv *hpriv = probe_ent->private_data; - - /* global interrupt mask */ - writel(0, mmio + HC_MAIN_IRQ_MASK_OFS); - - rc = mv_chip_id(pdev, hpriv, board_idx); - if (rc) - goto done; - - n_hc = mv_get_hc_count(probe_ent->host_flags); - probe_ent->n_ports = MV_PORTS_PER_HC * n_hc; - - for (port = 0; port < probe_ent->n_ports; port++) - hpriv->ops->read_preamp(hpriv, port, mmio); - - rc = hpriv->ops->reset_hc(hpriv, mmio, n_hc); - if (rc) - goto done; - - hpriv->ops->reset_flash(hpriv, mmio); - hpriv->ops->reset_bus(pdev, mmio); - hpriv->ops->enable_leds(hpriv, mmio); - - for (port = 0; port < probe_ent->n_ports; port++) { - if (IS_60XX(hpriv)) { - void __iomem *port_mmio = mv_port_base(mmio, port); - - u32 ifctl = readl(port_mmio + SATA_INTERFACE_CTL); - ifctl |= (1 << 7); /* enable gen2i speed */ - ifctl = (ifctl & 0xfff) | 0x9b1000; /* from chip spec */ - writelfl(ifctl, port_mmio + SATA_INTERFACE_CTL); - } - - hpriv->ops->phy_errata(hpriv, mmio, port); - } - - for (port = 0; port < probe_ent->n_ports; port++) { - void __iomem *port_mmio = mv_port_base(mmio, port); - mv_port_init(&probe_ent->port[port], port_mmio); - } - - for (hc = 0; hc < n_hc; hc++) { - void __iomem *hc_mmio = mv_hc_base(mmio, hc); - - VPRINTK("HC%i: HC config=0x%08x HC IRQ cause " - "(before clear)=0x%08x\n", hc, - readl(hc_mmio + HC_CFG_OFS), - readl(hc_mmio + HC_IRQ_CAUSE_OFS)); - - /* Clear any currently outstanding hc interrupt conditions */ - writelfl(0, hc_mmio + HC_IRQ_CAUSE_OFS); - } - - /* Clear any currently outstanding host interrupt conditions */ - writelfl(0, mmio + PCI_IRQ_CAUSE_OFS); - - /* and unmask interrupt generation for host regs */ - writelfl(PCI_UNMASK_ALL_IRQS, mmio + PCI_IRQ_MASK_OFS); - writelfl(~HC_MAIN_MASKED_IRQS, mmio + HC_MAIN_IRQ_MASK_OFS); - - VPRINTK("HC MAIN IRQ cause/mask=0x%08x/0x%08x " - "PCI int cause/mask=0x%08x/0x%08x\n", - readl(mmio + HC_MAIN_IRQ_CAUSE_OFS), - readl(mmio + HC_MAIN_IRQ_MASK_OFS), - readl(mmio + PCI_IRQ_CAUSE_OFS), - readl(mmio + PCI_IRQ_MASK_OFS)); - -done: - return rc; -} - -/** - * mv_print_info - Dump key info to kernel log for perusal. - * @probe_ent: early data struct representing the host - * - * FIXME: complete this. - * - * LOCKING: - * Inherited from caller. - */ -static void mv_print_info(struct ata_probe_ent *probe_ent) -{ - struct pci_dev *pdev = to_pci_dev(probe_ent->dev); - struct mv_host_priv *hpriv = probe_ent->private_data; - u8 rev_id, scc; - const char *scc_s; - - /* Use this to determine the HW stepping of the chip so we know - * what errata to workaround - */ - pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id); - - pci_read_config_byte(pdev, PCI_CLASS_DEVICE, &scc); - if (scc == 0) - scc_s = "SCSI"; - else if (scc == 0x01) - scc_s = "RAID"; - else - scc_s = "unknown"; - - dev_printk(KERN_INFO, &pdev->dev, - "%u slots %u ports %s mode IRQ via %s\n", - (unsigned)MV_MAX_Q_DEPTH, probe_ent->n_ports, - scc_s, (MV_HP_FLAG_MSI & hpriv->hp_flags) ? "MSI" : "INTx"); -} - -/** - * mv_init_one - handle a positive probe of a Marvell host - * @pdev: PCI device found - * @ent: PCI device ID entry for the matched host - * - * LOCKING: - * Inherited from caller. - */ -static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - static int printed_version = 0; - struct ata_probe_ent *probe_ent = NULL; - struct mv_host_priv *hpriv; - unsigned int board_idx = (unsigned int)ent->driver_data; - void __iomem *mmio_base; - int pci_dev_busy = 0, rc; - - if (!printed_version++) - dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n"); - - rc = pci_enable_device(pdev); - if (rc) { - return rc; - } - pci_set_master(pdev); - - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pci_dev_busy = 1; - goto err_out; - } - - probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) { - rc = -ENOMEM; - goto err_out_regions; - } - - memset(probe_ent, 0, sizeof(*probe_ent)); - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - mmio_base = pci_iomap(pdev, MV_PRIMARY_BAR, 0); - if (mmio_base == NULL) { - rc = -ENOMEM; - goto err_out_free_ent; - } - - hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL); - if (!hpriv) { - rc = -ENOMEM; - goto err_out_iounmap; - } - memset(hpriv, 0, sizeof(*hpriv)); - - probe_ent->sht = mv_port_info[board_idx].sht; - probe_ent->host_flags = mv_port_info[board_idx].host_flags; - probe_ent->pio_mask = mv_port_info[board_idx].pio_mask; - probe_ent->udma_mask = mv_port_info[board_idx].udma_mask; - probe_ent->port_ops = mv_port_info[board_idx].port_ops; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->mmio_base = mmio_base; - probe_ent->private_data = hpriv; - - /* initialize adapter */ - rc = mv_init_host(pdev, probe_ent, board_idx); - if (rc) { - goto err_out_hpriv; - } - - /* Enable interrupts */ - if (msi && pci_enable_msi(pdev) == 0) { - hpriv->hp_flags |= MV_HP_FLAG_MSI; - } else { - pci_intx(pdev, 1); - } - - mv_dump_pci_cfg(pdev, 0x68); - mv_print_info(probe_ent); - - if (ata_device_add(probe_ent) == 0) { - rc = -ENODEV; /* No devices discovered */ - goto err_out_dev_add; - } - - kfree(probe_ent); - return 0; - -err_out_dev_add: - if (MV_HP_FLAG_MSI & hpriv->hp_flags) { - pci_disable_msi(pdev); - } else { - pci_intx(pdev, 0); - } -err_out_hpriv: - kfree(hpriv); -err_out_iounmap: - pci_iounmap(pdev, mmio_base); -err_out_free_ent: - kfree(probe_ent); -err_out_regions: - pci_release_regions(pdev); -err_out: - if (!pci_dev_busy) { - pci_disable_device(pdev); - } - - return rc; -} - -static int __init mv_init(void) -{ - return pci_register_driver(&mv_pci_driver); -} - -static void __exit mv_exit(void) -{ - pci_unregister_driver(&mv_pci_driver); -} - -MODULE_AUTHOR("Brett Russ"); -MODULE_DESCRIPTION("SCSI low-level driver for Marvell SATA controllers"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, mv_pci_tbl); -MODULE_VERSION(DRV_VERSION); - -module_param(msi, int, 0444); -MODULE_PARM_DESC(msi, "Enable use of PCI MSI (0=off, 1=on)"); - -module_init(mv_init); -module_exit(mv_exit); diff --git a/drivers/scsi/sata_nv.c b/drivers/scsi/sata_nv.c deleted file mode 100644 index be46df75ab5..00000000000 --- a/drivers/scsi/sata_nv.c +++ /dev/null @@ -1,595 +0,0 @@ -/* - * sata_nv.c - NVIDIA nForce SATA - * - * Copyright 2004 NVIDIA Corp. All rights reserved. - * Copyright 2004 Andrew Chew - * - * - * 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 02139, USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - * No hardware documentation available outside of NVIDIA. - * This driver programs the NVIDIA SATA controller in a similar - * fashion as with other PCI IDE BMDMA controllers, with a few - * NV-specific details such as register offsets, SATA phy location, - * hotplug info, etc. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "sata_nv" -#define DRV_VERSION "2.0" - -enum { - NV_PORTS = 2, - NV_PIO_MASK = 0x1f, - NV_MWDMA_MASK = 0x07, - NV_UDMA_MASK = 0x7f, - NV_PORT0_SCR_REG_OFFSET = 0x00, - NV_PORT1_SCR_REG_OFFSET = 0x40, - - /* INT_STATUS/ENABLE */ - NV_INT_STATUS = 0x10, - NV_INT_ENABLE = 0x11, - NV_INT_STATUS_CK804 = 0x440, - NV_INT_ENABLE_CK804 = 0x441, - - /* INT_STATUS/ENABLE bits */ - NV_INT_DEV = 0x01, - NV_INT_PM = 0x02, - NV_INT_ADDED = 0x04, - NV_INT_REMOVED = 0x08, - - NV_INT_PORT_SHIFT = 4, /* each port occupies 4 bits */ - - NV_INT_ALL = 0x0f, - NV_INT_MASK = NV_INT_DEV | - NV_INT_ADDED | NV_INT_REMOVED, - - /* INT_CONFIG */ - NV_INT_CONFIG = 0x12, - NV_INT_CONFIG_METHD = 0x01, // 0 = INT, 1 = SMI - - // For PCI config register 20 - NV_MCP_SATA_CFG_20 = 0x50, - NV_MCP_SATA_CFG_20_SATA_SPACE_EN = 0x04, -}; - -static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static void nv_ck804_host_stop(struct ata_host_set *host_set); -static irqreturn_t nv_generic_interrupt(int irq, void *dev_instance, - struct pt_regs *regs); -static irqreturn_t nv_nf2_interrupt(int irq, void *dev_instance, - struct pt_regs *regs); -static irqreturn_t nv_ck804_interrupt(int irq, void *dev_instance, - struct pt_regs *regs); -static u32 nv_scr_read (struct ata_port *ap, unsigned int sc_reg); -static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); - -static void nv_nf2_freeze(struct ata_port *ap); -static void nv_nf2_thaw(struct ata_port *ap); -static void nv_ck804_freeze(struct ata_port *ap); -static void nv_ck804_thaw(struct ata_port *ap); -static void nv_error_handler(struct ata_port *ap); - -enum nv_host_type -{ - GENERIC, - NFORCE2, - NFORCE3 = NFORCE2, /* NF2 == NF3 as far as sata_nv is concerned */ - CK804 -}; - -static const struct pci_device_id nv_pci_tbl[] = { - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE2 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA3, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, 0x045c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, 0x045d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, 0x045e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, 0x045f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_STORAGE_IDE<<8, 0xffff00, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_STORAGE_RAID<<8, 0xffff00, GENERIC }, - { 0, } /* terminate list */ -}; - -static struct pci_driver nv_pci_driver = { - .name = DRV_NAME, - .id_table = nv_pci_tbl, - .probe = nv_init_one, - .remove = ata_pci_remove_one, -}; - -static struct scsi_host_template nv_sht = { - .module = THIS_MODULE, - .name = DRV_NAME, - .ioctl = ata_scsi_ioctl, - .queuecommand = ata_scsi_queuecmd, - .can_queue = ATA_DEF_QUEUE, - .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = LIBATA_MAX_PRD, - .cmd_per_lun = ATA_SHT_CMD_PER_LUN, - .emulated = ATA_SHT_EMULATED, - .use_clustering = ATA_SHT_USE_CLUSTERING, - .proc_name = DRV_NAME, - .dma_boundary = ATA_DMA_BOUNDARY, - .slave_configure = ata_scsi_slave_config, - .slave_destroy = ata_scsi_slave_destroy, - .bios_param = ata_std_bios_param, -}; - -static const struct ata_port_operations nv_generic_ops = { - .port_disable = ata_port_disable, - .tf_load = ata_tf_load, - .tf_read = ata_tf_read, - .exec_command = ata_exec_command, - .check_status = ata_check_status, - .dev_select = ata_std_dev_select, - .bmdma_setup = ata_bmdma_setup, - .bmdma_start = ata_bmdma_start, - .bmdma_stop = ata_bmdma_stop, - .bmdma_status = ata_bmdma_status, - .qc_prep = ata_qc_prep, - .qc_issue = ata_qc_issue_prot, - .freeze = ata_bmdma_freeze, - .thaw = ata_bmdma_thaw, - .error_handler = nv_error_handler, - .post_internal_cmd = ata_bmdma_post_internal_cmd, - .data_xfer = ata_pio_data_xfer, - .irq_handler = nv_generic_interrupt, - .irq_clear = ata_bmdma_irq_clear, - .scr_read = nv_scr_read, - .scr_write = nv_scr_write, - .port_start = ata_port_start, - .port_stop = ata_port_stop, - .host_stop = ata_pci_host_stop, -}; - -static const struct ata_port_operations nv_nf2_ops = { - .port_disable = ata_port_disable, - .tf_load = ata_tf_load, - .tf_read = ata_tf_read, - .exec_command = ata_exec_command, - .check_status = ata_check_status, - .dev_select = ata_std_dev_select, - .bmdma_setup = ata_bmdma_setup, - .bmdma_start = ata_bmdma_start, - .bmdma_stop = ata_bmdma_stop, - .bmdma_status = ata_bmdma_status, - .qc_prep = ata_qc_prep, - .qc_issue = ata_qc_issue_prot, - .freeze = nv_nf2_freeze, - .thaw = nv_nf2_thaw, - .error_handler = nv_error_handler, - .post_internal_cmd = ata_bmdma_post_internal_cmd, - .data_xfer = ata_pio_data_xfer, - .irq_handler = nv_nf2_interrupt, - .irq_clear = ata_bmdma_irq_clear, - .scr_read = nv_scr_read, - .scr_write = nv_scr_write, - .port_start = ata_port_start, - .port_stop = ata_port_stop, - .host_stop = ata_pci_host_stop, -}; - -static const struct ata_port_operations nv_ck804_ops = { - .port_disable = ata_port_disable, - .tf_load = ata_tf_load, - .tf_read = ata_tf_read, - .exec_command = ata_exec_command, - .check_status = ata_check_status, - .dev_select = ata_std_dev_select, - .bmdma_setup = ata_bmdma_setup, - .bmdma_start = ata_bmdma_start, - .bmdma_stop = ata_bmdma_stop, - .bmdma_status = ata_bmdma_status, - .qc_prep = ata_qc_prep, - .qc_issue = ata_qc_issue_prot, - .freeze = nv_ck804_freeze, - .thaw = nv_ck804_thaw, - .error_handler = nv_error_handler, - .post_internal_cmd = ata_bmdma_post_internal_cmd, - .data_xfer = ata_pio_data_xfer, - .irq_handler = nv_ck804_interrupt, - .irq_clear = ata_bmdma_irq_clear, - .scr_read = nv_scr_read, - .scr_write = nv_scr_write, - .port_start = ata_port_start, - .port_stop = ata_port_stop, - .host_stop = nv_ck804_host_stop, -}; - -static struct ata_port_info nv_port_info[] = { - /* generic */ - { - .sht = &nv_sht, - .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, - .pio_mask = NV_PIO_MASK, - .mwdma_mask = NV_MWDMA_MASK, - .udma_mask = NV_UDMA_MASK, - .port_ops = &nv_generic_ops, - }, - /* nforce2/3 */ - { - .sht = &nv_sht, - .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, - .pio_mask = NV_PIO_MASK, - .mwdma_mask = NV_MWDMA_MASK, - .udma_mask = NV_UDMA_MASK, - .port_ops = &nv_nf2_ops, - }, - /* ck804 */ - { - .sht = &nv_sht, - .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, - .pio_mask = NV_PIO_MASK, - .mwdma_mask = NV_MWDMA_MASK, - .udma_mask = NV_UDMA_MASK, - .port_ops = &nv_ck804_ops, - }, -}; - -MODULE_AUTHOR("NVIDIA"); -MODULE_DESCRIPTION("low-level driver for NVIDIA nForce SATA controller"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, nv_pci_tbl); -MODULE_VERSION(DRV_VERSION); - -static irqreturn_t nv_generic_interrupt(int irq, void *dev_instance, - struct pt_regs *regs) -{ - struct ata_host_set *host_set = dev_instance; - unsigned int i; - unsigned int handled = 0; - unsigned long flags; - - spin_lock_irqsave(&host_set->lock, flags); - - for (i = 0; i < host_set->n_ports; i++) { - struct ata_port *ap; - - ap = host_set->ports[i]; - if (ap && - !(ap->flags & ATA_FLAG_DISABLED)) { - struct ata_queued_cmd *qc; - - qc = ata_qc_from_tag(ap, ap->active_tag); - if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) - handled += ata_host_intr(ap, qc); - else - // No request pending? Clear interrupt status - // anyway, in case there's one pending. - ap->ops->check_status(ap); - } - - } - - spin_unlock_irqrestore(&host_set->lock, flags); - - return IRQ_RETVAL(handled); -} - -static int nv_host_intr(struct ata_port *ap, u8 irq_stat) -{ - struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag); - int handled; - - /* freeze if hotplugged */ - if (unlikely(irq_stat & (NV_INT_ADDED | NV_INT_REMOVED))) { - ata_port_freeze(ap); - return 1; - } - - /* bail out if not our interrupt */ - if (!(irq_stat & NV_INT_DEV)) - return 0; - - /* DEV interrupt w/ no active qc? */ - if (unlikely(!qc || (qc->tf.flags & ATA_TFLAG_POLLING))) { - ata_check_status(ap); - return 1; - } - - /* handle interrupt */ - handled = ata_host_intr(ap, qc); - if (unlikely(!handled)) { - /* spurious, clear it */ - ata_check_status(ap); - } - - return 1; -} - -static irqreturn_t nv_do_interrupt(struct ata_host_set *host_set, u8 irq_stat) -{ - int i, handled = 0; - - for (i = 0; i < host_set->n_ports; i++) { - struct ata_port *ap = host_set->ports[i]; - - if (ap && !(ap->flags & ATA_FLAG_DISABLED)) - handled += nv_host_intr(ap, irq_stat); - - irq_stat >>= NV_INT_PORT_SHIFT; - } - - return IRQ_RETVAL(handled); -} - -static irqreturn_t nv_nf2_interrupt(int irq, void *dev_instance, - struct pt_regs *regs) -{ - struct ata_host_set *host_set = dev_instance; - u8 irq_stat; - irqreturn_t ret; - - spin_lock(&host_set->lock); - irq_stat = inb(host_set->ports[0]->ioaddr.scr_addr + NV_INT_STATUS); - ret = nv_do_interrupt(host_set, irq_stat); - spin_unlock(&host_set->lock); - - return ret; -} - -static irqreturn_t nv_ck804_interrupt(int irq, void *dev_instance, - struct pt_regs *regs) -{ - struct ata_host_set *host_set = dev_instance; - u8 irq_stat; - irqreturn_t ret; - - spin_lock(&host_set->lock); - irq_stat = readb(host_set->mmio_base + NV_INT_STATUS_CK804); - ret = nv_do_interrupt(host_set, irq_stat); - spin_unlock(&host_set->lock); - - return ret; -} - -static u32 nv_scr_read (struct ata_port *ap, unsigned int sc_reg) -{ - if (sc_reg > SCR_CONTROL) - return 0xffffffffU; - - return ioread32((void __iomem *)ap->ioaddr.scr_addr + (sc_reg * 4)); -} - -static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) -{ - if (sc_reg > SCR_CONTROL) - return; - - iowrite32(val, (void __iomem *)ap->ioaddr.scr_addr + (sc_reg * 4)); -} - -static void nv_nf2_freeze(struct ata_port *ap) -{ - unsigned long scr_addr = ap->host_set->ports[0]->ioaddr.scr_addr; - int shift = ap->port_no * NV_INT_PORT_SHIFT; - u8 mask; - - mask = inb(scr_addr + NV_INT_ENABLE); - mask &= ~(NV_INT_ALL << shift); - outb(mask, scr_addr + NV_INT_ENABLE); -} - -static void nv_nf2_thaw(struct ata_port *ap) -{ - unsigned long scr_addr = ap->host_set->ports[0]->ioaddr.scr_addr; - int shift = ap->port_no * NV_INT_PORT_SHIFT; - u8 mask; - - outb(NV_INT_ALL << shift, scr_addr + NV_INT_STATUS); - - mask = inb(scr_addr + NV_INT_ENABLE); - mask |= (NV_INT_MASK << shift); - outb(mask, scr_addr + NV_INT_ENABLE); -} - -static void nv_ck804_freeze(struct ata_port *ap) -{ - void __iomem *mmio_base = ap->host_set->mmio_base; - int shift = ap->port_no * NV_INT_PORT_SHIFT; - u8 mask; - - mask = readb(mmio_base + NV_INT_ENABLE_CK804); - mask &= ~(NV_INT_ALL << shift); - writeb(mask, mmio_base + NV_INT_ENABLE_CK804); -} - -static void nv_ck804_thaw(struct ata_port *ap) -{ - void __iomem *mmio_base = ap->host_set->mmio_base; - int shift = ap->port_no * NV_INT_PORT_SHIFT; - u8 mask; - - writeb(NV_INT_ALL << shift, mmio_base + NV_INT_STATUS_CK804); - - mask = readb(mmio_base + NV_INT_ENABLE_CK804); - mask |= (NV_INT_MASK << shift); - writeb(mask, mmio_base + NV_INT_ENABLE_CK804); -} - -static int nv_hardreset(struct ata_port *ap, unsigned int *class) -{ - unsigned int dummy; - - /* SATA hardreset fails to retrieve proper device signature on - * some controllers. Don't classify on hardreset. For more - * info, see http://bugme.osdl.org/show_bug.cgi?id=3352 - */ - return sata_std_hardreset(ap, &dummy); -} - -static void nv_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, ata_std_prereset, ata_std_softreset, - nv_hardreset, ata_std_postreset); -} - -static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) -{ - static int printed_version = 0; - struct ata_port_info *ppi; - struct ata_probe_ent *probe_ent; - int pci_dev_busy = 0; - int rc; - u32 bar; - unsigned long base; - - // Make sure this is a SATA controller by counting the number of bars - // (NVIDIA SATA controllers will always have six bars). Otherwise, - // it's an IDE controller and we ignore it. - for (bar=0; bar<6; bar++) - if (pci_resource_start(pdev, bar) == 0) - return -ENODEV; - - if (!printed_version++) - dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); - - rc = pci_enable_device(pdev); - if (rc) - goto err_out; - - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pci_dev_busy = 1; - goto err_out_disable; - } - - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out_regions; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out_regions; - - rc = -ENOMEM; - - ppi = &nv_port_info[ent->driver_data]; - probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); - if (!probe_ent) - goto err_out_regions; - - probe_ent->mmio_base = pci_iomap(pdev, 5, 0); - if (!probe_ent->mmio_base) { - rc = -EIO; - goto err_out_free_ent; - } - - base = (unsigned long)probe_ent->mmio_base; - - probe_ent->port[0].scr_addr = base + NV_PORT0_SCR_REG_OFFSET; - probe_ent->port[1].scr_addr = base + NV_PORT1_SCR_REG_OFFSET; - - /* enable SATA space for CK804 */ - if (ent->driver_data == CK804) { - u8 regval; - - pci_read_config_byte(pdev, NV_MCP_SATA_CFG_20, ®val); - regval |= NV_MCP_SATA_CFG_20_SATA_SPACE_EN; - pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval); - } - - pci_set_master(pdev); - - rc = ata_device_add(probe_ent); - if (rc != NV_PORTS) - goto err_out_iounmap; - - kfree(probe_ent); - - return 0; - -err_out_iounmap: - pci_iounmap(pdev, probe_ent->mmio_base); -err_out_free_ent: - kfree(probe_ent); -err_out_regions: - pci_release_regions(pdev); -err_out_disable: - if (!pci_dev_busy) - pci_disable_device(pdev); -err_out: - return rc; -} - -static void nv_ck804_host_stop(struct ata_host_set *host_set) -{ - struct pci_dev *pdev = to_pci_dev(host_set->dev); - u8 regval; - - /* disable SATA space for CK804 */ - pci_read_config_byte(pdev, NV_MCP_SATA_CFG_20, ®val); - regval &= ~NV_MCP_SATA_CFG_20_SATA_SPACE_EN; - pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval); - - ata_pci_host_stop(host_set); -} - -static int __init nv_init(void) -{ - return pci_register_driver(&nv_pci_driver); -} - -static void __exit nv_exit(void) -{ - pci_unregister_driver(&nv_pci_driver); -} - -module_init(nv_init); -module_exit(nv_exit); diff --git a/drivers/scsi/sata_promise.c b/drivers/scsi/sata_promise.c deleted file mode 100644 index a5b3a7db7a9..00000000000 --- a/drivers/scsi/sata_promise.c +++ /dev/null @@ -1,844 +0,0 @@ -/* - * sata_promise.c - Promise SATA - * - * Maintained by: Jeff Garzik - * Please ALWAYS copy linux-ide@vger.kernel.org - * on emails. - * - * Copyright 2003-2004 Red Hat, Inc. - * - * - * 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 02139, USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - * Hardware information only available under NDA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sata_promise.h" - -#define DRV_NAME "sata_promise" -#define DRV_VERSION "1.04" - - -enum { - PDC_PKT_SUBMIT = 0x40, /* Command packet pointer addr */ - PDC_INT_SEQMASK = 0x40, /* Mask of asserted SEQ INTs */ - PDC_TBG_MODE = 0x41, /* TBG mode */ - PDC_FLASH_CTL = 0x44, /* Flash control register */ - PDC_PCI_CTL = 0x48, /* PCI control and status register */ - PDC_GLOBAL_CTL = 0x48, /* Global control/status (per port) */ - PDC_CTLSTAT = 0x60, /* IDE control and status (per port) */ - PDC_SATA_PLUG_CSR = 0x6C, /* SATA Plug control/status reg */ - PDC2_SATA_PLUG_CSR = 0x60, /* SATAII Plug control/status reg */ - PDC_SLEW_CTL = 0x470, /* slew rate control reg */ - - PDC_ERR_MASK = (1<<19) | (1<<20) | (1<<21) | (1<<22) | - (1<<8) | (1<<9) | (1<<10), - - board_2037x = 0, /* FastTrak S150 TX2plus */ - board_20319 = 1, /* FastTrak S150 TX4 */ - board_20619 = 2, /* FastTrak TX4000 */ - board_20771 = 3, /* FastTrak TX2300 */ - board_2057x = 4, /* SATAII150 Tx2plus */ - board_40518 = 5, /* SATAII150 Tx4 */ - - PDC_HAS_PATA = (1 << 1), /* PDC20375/20575 has PATA */ - - PDC_RESET = (1 << 11), /* HDMA reset */ - - PDC_COMMON_FLAGS = ATA_FLAG_NO_LEGACY | ATA_FLAG_SRST | - ATA_FLAG_MMIO | ATA_FLAG_NO_ATAPI | - ATA_FLAG_PIO_POLLING, -}; - - -struct pdc_port_priv { - u8 *pkt; - dma_addr_t pkt_dma; -}; - -struct pdc_host_priv { - int hotplug_offset; -}; - -static u32 pdc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg); -static void pdc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); -static int pdc_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static irqreturn_t pdc_interrupt (int irq, void *dev_instance, struct pt_regs *regs); -static void pdc_eng_timeout(struct ata_port *ap); -static int pdc_port_start(struct ata_port *ap); -static void pdc_port_stop(struct ata_port *ap); -static void pdc_pata_phy_reset(struct ata_port *ap); -static void pdc_sata_phy_reset(struct ata_port *ap); -static void pdc_qc_prep(struct ata_queued_cmd *qc); -static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf); -static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf); -static void pdc_irq_clear(struct ata_port *ap); -static unsigned int pdc_qc_issue_prot(struct ata_queued_cmd *qc); -static void pdc_host_stop(struct ata_host_set *host_set); - - -static struct scsi_host_template pdc_ata_sht = { - .module = THIS_MODULE, - .name = DRV_NAME, - .ioctl = ata_scsi_ioctl, - .queuecommand = ata_scsi_queuecmd, - .can_queue = ATA_DEF_QUEUE, - .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = LIBATA_MAX_PRD, - .cmd_per_lun = ATA_SHT_CMD_PER_LUN, - .emulated = ATA_SHT_EMULATED, - .use_clustering = ATA_SHT_USE_CLUSTERING, - .proc_name = DRV_NAME, - .dma_boundary = ATA_DMA_BOUNDARY, - .slave_configure = ata_scsi_slave_config, - .slave_destroy = ata_scsi_slave_destroy, - .bios_param = ata_std_bios_param, -}; - -static const struct ata_port_operations pdc_sata_ops = { - .port_disable = ata_port_disable, - .tf_load = pdc_tf_load_mmio, - .tf_read = ata_tf_read, - .check_status = ata_check_status, - .exec_command = pdc_exec_command_mmio, - .dev_select = ata_std_dev_select, - - .phy_reset = pdc_sata_phy_reset, - - .qc_prep = pdc_qc_prep, - .qc_issue = pdc_qc_issue_prot, - .eng_timeout = pdc_eng_timeout, - .data_xfer = ata_mmio_data_xfer, - .irq_handler = pdc_interrupt, - .irq_clear = pdc_irq_clear, - - .scr_read = pdc_sata_scr_read, - .scr_write = pdc_sata_scr_write, - .port_start = pdc_port_start, - .port_stop = pdc_port_stop, - .host_stop = pdc_host_stop, -}; - -static const struct ata_port_operations pdc_pata_ops = { - .port_disable = ata_port_disable, - .tf_load = pdc_tf_load_mmio, - .tf_read = ata_tf_read, - .check_status = ata_check_status, - .exec_command = pdc_exec_command_mmio, - .dev_select = ata_std_dev_select, - - .phy_reset = pdc_pata_phy_reset, - - .qc_prep = pdc_qc_prep, - .qc_issue = pdc_qc_issue_prot, - .data_xfer = ata_mmio_data_xfer, - .eng_timeout = pdc_eng_timeout, - .irq_handler = pdc_interrupt, - .irq_clear = pdc_irq_clear, - - .port_start = pdc_port_start, - .port_stop = pdc_port_stop, - .host_stop = pdc_host_stop, -}; - -static const struct ata_port_info pdc_port_info[] = { - /* board_2037x */ - { - .sht = &pdc_ata_sht, - .host_flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x7f, /* udma0-6 ; FIXME */ - .port_ops = &pdc_sata_ops, - }, - - /* board_20319 */ - { - .sht = &pdc_ata_sht, - .host_flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x7f, /* udma0-6 ; FIXME */ - .port_ops = &pdc_sata_ops, - }, - - /* board_20619 */ - { - .sht = &pdc_ata_sht, - .host_flags = PDC_COMMON_FLAGS | ATA_FLAG_SLAVE_POSS, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x7f, /* udma0-6 ; FIXME */ - .port_ops = &pdc_pata_ops, - }, - - /* board_20771 */ - { - .sht = &pdc_ata_sht, - .host_flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x7f, /* udma0-6 ; FIXME */ - .port_ops = &pdc_sata_ops, - }, - - /* board_2057x */ - { - .sht = &pdc_ata_sht, - .host_flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x7f, /* udma0-6 ; FIXME */ - .port_ops = &pdc_sata_ops, - }, - - /* board_40518 */ - { - .sht = &pdc_ata_sht, - .host_flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x7f, /* udma0-6 ; FIXME */ - .port_ops = &pdc_sata_ops, - }, -}; - -static const struct pci_device_id pdc_ata_pci_tbl[] = { - { PCI_VENDOR_ID_PROMISE, 0x3371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2037x }, - { PCI_VENDOR_ID_PROMISE, 0x3570, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2037x }, - { PCI_VENDOR_ID_PROMISE, 0x3571, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2037x }, - { PCI_VENDOR_ID_PROMISE, 0x3373, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2037x }, - { PCI_VENDOR_ID_PROMISE, 0x3375, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2037x }, - { PCI_VENDOR_ID_PROMISE, 0x3376, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2037x }, - { PCI_VENDOR_ID_PROMISE, 0x3574, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2057x }, - { PCI_VENDOR_ID_PROMISE, 0x3d75, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2057x }, - { PCI_VENDOR_ID_PROMISE, 0x3d73, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2037x }, - - { PCI_VENDOR_ID_PROMISE, 0x3318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_20319 }, - { PCI_VENDOR_ID_PROMISE, 0x3319, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_20319 }, - { PCI_VENDOR_ID_PROMISE, 0x3515, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_20319 }, - { PCI_VENDOR_ID_PROMISE, 0x3519, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_20319 }, - { PCI_VENDOR_ID_PROMISE, 0x3d17, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_20319 }, - { PCI_VENDOR_ID_PROMISE, 0x3d18, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_40518 }, - - { PCI_VENDOR_ID_PROMISE, 0x6629, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_20619 }, - -/* TODO: remove all associated board_20771 code, as it completely - * duplicates board_2037x code, unless reason for separation can be - * divined. - */ -#if 0 - { PCI_VENDOR_ID_PROMISE, 0x3570, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_20771 }, -#endif - - { } /* terminate list */ -}; - - -static struct pci_driver pdc_ata_pci_driver = { - .name = DRV_NAME, - .id_table = pdc_ata_pci_tbl, - .probe = pdc_ata_init_one, - .remove = ata_pci_remove_one, -}; - - -static int pdc_port_start(struct ata_port *ap) -{ - struct device *dev = ap->host_set->dev; - struct pdc_port_priv *pp; - int rc; - - rc = ata_port_start(ap); - if (rc) - return rc; - - pp = kzalloc(sizeof(*pp), GFP_KERNEL); - if (!pp) { - rc = -ENOMEM; - goto err_out; - } - - pp->pkt = dma_alloc_coherent(dev, 128, &pp->pkt_dma, GFP_KERNEL); - if (!pp->pkt) { - rc = -ENOMEM; - goto err_out_kfree; - } - - ap->private_data = pp; - - return 0; - -err_out_kfree: - kfree(pp); -err_out: - ata_port_stop(ap); - return rc; -} - - -static void pdc_port_stop(struct ata_port *ap) -{ - struct device *dev = ap->host_set->dev; - struct pdc_port_priv *pp = ap->private_data; - - ap->private_data = NULL; - dma_free_coherent(dev, 128, pp->pkt, pp->pkt_dma); - kfree(pp); - ata_port_stop(ap); -} - - -static void pdc_host_stop(struct ata_host_set *host_set) -{ - struct pdc_host_priv *hp = host_set->private_data; - - ata_pci_host_stop(host_set); - - kfree(hp); -} - - -static void pdc_reset_port(struct ata_port *ap) -{ - void __iomem *mmio = (void __iomem *) ap->ioaddr.cmd_addr + PDC_CTLSTAT; - unsigned int i; - u32 tmp; - - for (i = 11; i > 0; i--) { - tmp = readl(mmio); - if (tmp & PDC_RESET) - break; - - udelay(100); - - tmp |= PDC_RESET; - writel(tmp, mmio); - } - - tmp &= ~PDC_RESET; - writel(tmp, mmio); - readl(mmio); /* flush */ -} - -static void pdc_sata_phy_reset(struct ata_port *ap) -{ - pdc_reset_port(ap); - sata_phy_reset(ap); -} - -static void pdc_pata_cbl_detect(struct ata_port *ap) -{ - u8 tmp; - void __iomem *mmio = (void *) ap->ioaddr.cmd_addr + PDC_CTLSTAT + 0x03; - - tmp = readb(mmio); - - if (tmp & 0x01) { - ap->cbl = ATA_CBL_PATA40; - ap->udma_mask &= ATA_UDMA_MASK_40C; - } else - ap->cbl = ATA_CBL_PATA80; -} - -static void pdc_pata_phy_reset(struct ata_port *ap) -{ - pdc_pata_cbl_detect(ap); - pdc_reset_port(ap); - ata_port_probe(ap); - ata_bus_reset(ap); -} - -static u32 pdc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg) -{ - if (sc_reg > SCR_CONTROL) - return 0xffffffffU; - return readl((void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4)); -} - - -static void pdc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, - u32 val) -{ - if (sc_reg > SCR_CONTROL) - return; - writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4)); -} - -static void pdc_qc_prep(struct ata_queued_cmd *qc) -{ - struct pdc_port_priv *pp = qc->ap->private_data; - unsigned int i; - - VPRINTK("ENTER\n"); - - switch (qc->tf.protocol) { - case ATA_PROT_DMA: - ata_qc_prep(qc); - /* fall through */ - - case ATA_PROT_NODATA: - i = pdc_pkt_header(&qc->tf, qc->ap->prd_dma, - qc->dev->devno, pp->pkt); - - if (qc->tf.flags & ATA_TFLAG_LBA48) - i = pdc_prep_lba48(&qc->tf, pp->pkt, i); - else - i = pdc_prep_lba28(&qc->tf, pp->pkt, i); - - pdc_pkt_footer(&qc->tf, pp->pkt, i); - break; - - default: - break; - } -} - -static void pdc_eng_timeout(struct ata_port *ap) -{ - struct ata_host_set *host_set = ap->host_set; - u8 drv_stat; - struct ata_queued_cmd *qc; - unsigned long flags; - - DPRINTK("ENTER\n"); - - spin_lock_irqsave(&host_set->lock, flags); - - qc = ata_qc_from_tag(ap, ap->active_tag); - - switch (qc->tf.protocol) { - case ATA_PROT_DMA: - case ATA_PROT_NODATA: - ata_port_printk(ap, KERN_ERR, "command timeout\n"); - drv_stat = ata_wait_idle(ap); - qc->err_mask |= __ac_err_mask(drv_stat); - break; - - default: - drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000); - - ata_port_printk(ap, KERN_ERR, - "unknown timeout, cmd 0x%x stat 0x%x\n", - qc->tf.command, drv_stat); - - qc->err_mask |= ac_err_mask(drv_stat); - break; - } - - spin_unlock_irqrestore(&host_set->lock, flags); - ata_eh_qc_complete(qc); - DPRINTK("EXIT\n"); -} - -static inline unsigned int pdc_host_intr( struct ata_port *ap, - struct ata_queued_cmd *qc) -{ - unsigned int handled = 0; - u32 tmp; - void __iomem *mmio = (void __iomem *) ap->ioaddr.cmd_addr + PDC_GLOBAL_CTL; - - tmp = readl(mmio); - if (tmp & PDC_ERR_MASK) { - qc->err_mask |= AC_ERR_DEV; - pdc_reset_port(ap); - } - - switch (qc->tf.protocol) { - case ATA_PROT_DMA: - case ATA_PROT_NODATA: - qc->err_mask |= ac_err_mask(ata_wait_idle(ap)); - ata_qc_complete(qc); - handled = 1; - break; - - default: - ap->stats.idle_irq++; - break; - } - - return handled; -} - -static void pdc_irq_clear(struct ata_port *ap) -{ - struct ata_host_set *host_set = ap->host_set; - void __iomem *mmio = host_set->mmio_base; - - readl(mmio + PDC_INT_SEQMASK); -} - -static irqreturn_t pdc_interrupt (int irq, void *dev_instance, struct pt_regs *regs) -{ - struct ata_host_set *host_set = dev_instance; - struct ata_port *ap; - u32 mask = 0; - unsigned int i, tmp; - unsigned int handled = 0; - void __iomem *mmio_base; - - VPRINTK("ENTER\n"); - - if (!host_set || !host_set->mmio_base) { - VPRINTK("QUICK EXIT\n"); - return IRQ_NONE; - } - - mmio_base = host_set->mmio_base; - - /* reading should also clear interrupts */ - mask = readl(mmio_base + PDC_INT_SEQMASK); - - if (mask == 0xffffffff) { - VPRINTK("QUICK EXIT 2\n"); - return IRQ_NONE; - } - - spin_lock(&host_set->lock); - - mask &= 0xffff; /* only 16 tags possible */ - if (!mask) { - VPRINTK("QUICK EXIT 3\n"); - goto done_irq; - } - - writel(mask, mmio_base + PDC_INT_SEQMASK); - - for (i = 0; i < host_set->n_ports; i++) { - VPRINTK("port %u\n", i); - ap = host_set->ports[i]; - tmp = mask & (1 << (i + 1)); - if (tmp && ap && - !(ap->flags & ATA_FLAG_DISABLED)) { - struct ata_queued_cmd *qc; - - qc = ata_qc_from_tag(ap, ap->active_tag); - if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) - handled += pdc_host_intr(ap, qc); - } - } - - VPRINTK("EXIT\n"); - -done_irq: - spin_unlock(&host_set->lock); - return IRQ_RETVAL(handled); -} - -static inline void pdc_packet_start(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct pdc_port_priv *pp = ap->private_data; - unsigned int port_no = ap->port_no; - u8 seq = (u8) (port_no + 1); - - VPRINTK("ENTER, ap %p\n", ap); - - writel(0x00000001, ap->host_set->mmio_base + (seq * 4)); - readl(ap->host_set->mmio_base + (seq * 4)); /* flush */ - - pp->pkt[2] = seq; - wmb(); /* flush PRD, pkt writes */ - writel(pp->pkt_dma, (void __iomem *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); - readl((void __iomem *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); /* flush */ -} - -static unsigned int pdc_qc_issue_prot(struct ata_queued_cmd *qc) -{ - switch (qc->tf.protocol) { - case ATA_PROT_DMA: - case ATA_PROT_NODATA: - pdc_packet_start(qc); - return 0; - - case ATA_PROT_ATAPI_DMA: - BUG(); - break; - - default: - break; - } - - return ata_qc_issue_prot(qc); -} - -static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf) -{ - WARN_ON (tf->protocol == ATA_PROT_DMA || - tf->protocol == ATA_PROT_NODATA); - ata_tf_load(ap, tf); -} - - -static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf) -{ - WARN_ON (tf->protocol == ATA_PROT_DMA || - tf->protocol == ATA_PROT_NODATA); - ata_exec_command(ap, tf); -} - - -static void pdc_ata_setup_port(struct ata_ioports *port, unsigned long base) -{ - port->cmd_addr = base; - port->data_addr = base; - port->feature_addr = - port->error_addr = base + 0x4; - port->nsect_addr = base + 0x8; - port->lbal_addr = base + 0xc; - port->lbam_addr = base + 0x10; - port->lbah_addr = base + 0x14; - port->device_addr = base + 0x18; - port->command_addr = - port->status_addr = base + 0x1c; - port->altstatus_addr = - port->ctl_addr = base + 0x38; -} - - -static void pdc_host_init(unsigned int chip_id, struct ata_probe_ent *pe) -{ - void __iomem *mmio = pe->mmio_base; - struct pdc_host_priv *hp = pe->private_data; - int hotplug_offset = hp->hotplug_offset; - u32 tmp; - - /* - * Except for the hotplug stuff, this is voodoo from the - * Promise driver. Label this entire section - * "TODO: figure out why we do this" - */ - - /* change FIFO_SHD to 8 dwords, enable BMR_BURST */ - tmp = readl(mmio + PDC_FLASH_CTL); - tmp |= 0x12000; /* bit 16 (fifo 8 dw) and 13 (bmr burst?) */ - writel(tmp, mmio + PDC_FLASH_CTL); - - /* clear plug/unplug flags for all ports */ - tmp = readl(mmio + hotplug_offset); - writel(tmp | 0xff, mmio + hotplug_offset); - - /* mask plug/unplug ints */ - tmp = readl(mmio + hotplug_offset); - writel(tmp | 0xff0000, mmio + hotplug_offset); - - /* reduce TBG clock to 133 Mhz. */ - tmp = readl(mmio + PDC_TBG_MODE); - tmp &= ~0x30000; /* clear bit 17, 16*/ - tmp |= 0x10000; /* set bit 17:16 = 0:1 */ - writel(tmp, mmio + PDC_TBG_MODE); - - readl(mmio + PDC_TBG_MODE); /* flush */ - msleep(10); - - /* adjust slew rate control register. */ - tmp = readl(mmio + PDC_SLEW_CTL); - tmp &= 0xFFFFF03F; /* clear bit 11 ~ 6 */ - tmp |= 0x00000900; /* set bit 11-9 = 100b , bit 8-6 = 100 */ - writel(tmp, mmio + PDC_SLEW_CTL); -} - -static int pdc_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) -{ - static int printed_version; - struct ata_probe_ent *probe_ent = NULL; - struct pdc_host_priv *hp; - unsigned long base; - void __iomem *mmio_base; - unsigned int board_idx = (unsigned int) ent->driver_data; - int pci_dev_busy = 0; - int rc; - - if (!printed_version++) - dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); - - rc = pci_enable_device(pdev); - if (rc) - return rc; - - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pci_dev_busy = 1; - goto err_out; - } - - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out_regions; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out_regions; - - probe_ent = kzalloc(sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) { - rc = -ENOMEM; - goto err_out_regions; - } - - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - mmio_base = pci_iomap(pdev, 3, 0); - if (mmio_base == NULL) { - rc = -ENOMEM; - goto err_out_free_ent; - } - base = (unsigned long) mmio_base; - - hp = kzalloc(sizeof(*hp), GFP_KERNEL); - if (hp == NULL) { - rc = -ENOMEM; - goto err_out_free_ent; - } - - /* Set default hotplug offset */ - hp->hotplug_offset = PDC_SATA_PLUG_CSR; - probe_ent->private_data = hp; - - probe_ent->sht = pdc_port_info[board_idx].sht; - probe_ent->host_flags = pdc_port_info[board_idx].host_flags; - probe_ent->pio_mask = pdc_port_info[board_idx].pio_mask; - probe_ent->mwdma_mask = pdc_port_info[board_idx].mwdma_mask; - probe_ent->udma_mask = pdc_port_info[board_idx].udma_mask; - probe_ent->port_ops = pdc_port_info[board_idx].port_ops; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->mmio_base = mmio_base; - - pdc_ata_setup_port(&probe_ent->port[0], base + 0x200); - pdc_ata_setup_port(&probe_ent->port[1], base + 0x280); - - probe_ent->port[0].scr_addr = base + 0x400; - probe_ent->port[1].scr_addr = base + 0x500; - - /* notice 4-port boards */ - switch (board_idx) { - case board_40518: - /* Override hotplug offset for SATAII150 */ - hp->hotplug_offset = PDC2_SATA_PLUG_CSR; - /* Fall through */ - case board_20319: - probe_ent->n_ports = 4; - - pdc_ata_setup_port(&probe_ent->port[2], base + 0x300); - pdc_ata_setup_port(&probe_ent->port[3], base + 0x380); - - probe_ent->port[2].scr_addr = base + 0x600; - probe_ent->port[3].scr_addr = base + 0x700; - break; - case board_2057x: - /* Override hotplug offset for SATAII150 */ - hp->hotplug_offset = PDC2_SATA_PLUG_CSR; - /* Fall through */ - case board_2037x: - probe_ent->n_ports = 2; - break; - case board_20771: - probe_ent->n_ports = 2; - break; - case board_20619: - probe_ent->n_ports = 4; - - pdc_ata_setup_port(&probe_ent->port[2], base + 0x300); - pdc_ata_setup_port(&probe_ent->port[3], base + 0x380); - - probe_ent->port[2].scr_addr = base + 0x600; - probe_ent->port[3].scr_addr = base + 0x700; - break; - default: - BUG(); - break; - } - - pci_set_master(pdev); - - /* initialize adapter */ - pdc_host_init(board_idx, probe_ent); - - /* FIXME: Need any other frees than hp? */ - if (!ata_device_add(probe_ent)) - kfree(hp); - - kfree(probe_ent); - - return 0; - -err_out_free_ent: - kfree(probe_ent); -err_out_regions: - pci_release_regions(pdev); -err_out: - if (!pci_dev_busy) - pci_disable_device(pdev); - return rc; -} - - -static int __init pdc_ata_init(void) -{ - return pci_register_driver(&pdc_ata_pci_driver); -} - - -static void __exit pdc_ata_exit(void) -{ - pci_unregister_driver(&pdc_ata_pci_driver); -} - - -MODULE_AUTHOR("Jeff Garzik"); -MODULE_DESCRIPTION("Promise ATA TX2/TX4/TX4000 low-level driver"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, pdc_ata_pci_tbl); -MODULE_VERSION(DRV_VERSION); - -module_init(pdc_ata_init); -module_exit(pdc_ata_exit); diff --git a/drivers/scsi/sata_promise.h b/drivers/scsi/sata_promise.h deleted file mode 100644 index 6ee5e190262..00000000000 --- a/drivers/scsi/sata_promise.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * sata_promise.h - Promise SATA common definitions and inline funcs - * - * Copyright 2003-2004 Red Hat, Inc. - * - * - * 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 02139, USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - */ - -#ifndef __SATA_PROMISE_H__ -#define __SATA_PROMISE_H__ - -#include - -enum pdc_packet_bits { - PDC_PKT_READ = (1 << 2), - PDC_PKT_NODATA = (1 << 3), - - PDC_PKT_SIZEMASK = (1 << 7) | (1 << 6) | (1 << 5), - PDC_PKT_CLEAR_BSY = (1 << 4), - PDC_PKT_WAIT_DRDY = (1 << 3) | (1 << 4), - PDC_LAST_REG = (1 << 3), - - PDC_REG_DEVCTL = (1 << 3) | (1 << 2) | (1 << 1), -}; - -static inline unsigned int pdc_pkt_header(struct ata_taskfile *tf, - dma_addr_t sg_table, - unsigned int devno, u8 *buf) -{ - u8 dev_reg; - u32 *buf32 = (u32 *) buf; - - /* set control bits (byte 0), zero delay seq id (byte 3), - * and seq id (byte 2) - */ - switch (tf->protocol) { - case ATA_PROT_DMA: - if (!(tf->flags & ATA_TFLAG_WRITE)) - buf32[0] = cpu_to_le32(PDC_PKT_READ); - else - buf32[0] = 0; - break; - - case ATA_PROT_NODATA: - buf32[0] = cpu_to_le32(PDC_PKT_NODATA); - break; - - default: - BUG(); - break; - } - - buf32[1] = cpu_to_le32(sg_table); /* S/G table addr */ - buf32[2] = 0; /* no next-packet */ - - if (devno == 0) - dev_reg = ATA_DEVICE_OBS; - else - dev_reg = ATA_DEVICE_OBS | ATA_DEV1; - - /* select device */ - buf[12] = (1 << 5) | PDC_PKT_CLEAR_BSY | ATA_REG_DEVICE; - buf[13] = dev_reg; - - /* device control register */ - buf[14] = (1 << 5) | PDC_REG_DEVCTL; - buf[15] = tf->ctl; - - return 16; /* offset of next byte */ -} - -static inline unsigned int pdc_pkt_footer(struct ata_taskfile *tf, u8 *buf, - unsigned int i) -{ - if (tf->flags & ATA_TFLAG_DEVICE) { - buf[i++] = (1 << 5) | ATA_REG_DEVICE; - buf[i++] = tf->device; - } - - /* and finally the command itself; also includes end-of-pkt marker */ - buf[i++] = (1 << 5) | PDC_LAST_REG | ATA_REG_CMD; - buf[i++] = tf->command; - - return i; -} - -static inline unsigned int pdc_prep_lba28(struct ata_taskfile *tf, u8 *buf, unsigned int i) -{ - /* the "(1 << 5)" should be read "(count << 5)" */ - - /* ATA command block registers */ - buf[i++] = (1 << 5) | ATA_REG_FEATURE; - buf[i++] = tf->feature; - - buf[i++] = (1 << 5) | ATA_REG_NSECT; - buf[i++] = tf->nsect; - - buf[i++] = (1 << 5) | ATA_REG_LBAL; - buf[i++] = tf->lbal; - - buf[i++] = (1 << 5) | ATA_REG_LBAM; - buf[i++] = tf->lbam; - - buf[i++] = (1 << 5) | ATA_REG_LBAH; - buf[i++] = tf->lbah; - - return i; -} - -static inline unsigned int pdc_prep_lba48(struct ata_taskfile *tf, u8 *buf, unsigned int i) -{ - /* the "(2 << 5)" should be read "(count << 5)" */ - - /* ATA command block registers */ - buf[i++] = (2 << 5) | ATA_REG_FEATURE; - buf[i++] = tf->hob_feature; - buf[i++] = tf->feature; - - buf[i++] = (2 << 5) | ATA_REG_NSECT; - buf[i++] = tf->hob_nsect; - buf[i++] = tf->nsect; - - buf[i++] = (2 << 5) | ATA_REG_LBAL; - buf[i++] = tf->hob_lbal; - buf[i++] = tf->lbal; - - buf[i++] = (2 << 5) | ATA_REG_LBAM; - buf[i++] = tf->hob_lbam; - buf[i++] = tf->lbam; - - buf[i++] = (2 << 5) | ATA_REG_LBAH; - buf[i++] = tf->hob_lbah; - buf[i++] = tf->lbah; - - return i; -} - - -#endif /* __SATA_PROMISE_H__ */ diff --git a/drivers/scsi/sata_qstor.c b/drivers/scsi/sata_qstor.c deleted file mode 100644 index 71bd6712b37..00000000000 --- a/drivers/scsi/sata_qstor.c +++ /dev/null @@ -1,730 +0,0 @@ -/* - * sata_qstor.c - Pacific Digital Corporation QStor SATA - * - * Maintained by: Mark Lord - * - * Copyright 2005 Pacific Digital Corporation. - * (OSL/GPL code release authorized by Jalil Fadavi). - * - * - * 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 02139, USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "sata_qstor" -#define DRV_VERSION "0.06" - -enum { - QS_PORTS = 4, - QS_MAX_PRD = LIBATA_MAX_PRD, - QS_CPB_ORDER = 6, - QS_CPB_BYTES = (1 << QS_CPB_ORDER), - QS_PRD_BYTES = QS_MAX_PRD * 16, - QS_PKT_BYTES = QS_CPB_BYTES + QS_PRD_BYTES, - - /* global register offsets */ - QS_HCF_CNFG3 = 0x0003, /* host configuration offset */ - QS_HID_HPHY = 0x0004, /* host physical interface info */ - QS_HCT_CTRL = 0x00e4, /* global interrupt mask offset */ - QS_HST_SFF = 0x0100, /* host status fifo offset */ - QS_HVS_SERD3 = 0x0393, /* PHY enable offset */ - - /* global control bits */ - QS_HPHY_64BIT = (1 << 1), /* 64-bit bus detected */ - QS_CNFG3_GSRST = 0x01, /* global chip reset */ - QS_SERD3_PHY_ENA = 0xf0, /* PHY detection ENAble*/ - - /* per-channel register offsets */ - QS_CCF_CPBA = 0x0710, /* chan CPB base address */ - QS_CCF_CSEP = 0x0718, /* chan CPB separation factor */ - QS_CFC_HUFT = 0x0800, /* host upstream fifo threshold */ - QS_CFC_HDFT = 0x0804, /* host downstream fifo threshold */ - QS_CFC_DUFT = 0x0808, /* dev upstream fifo threshold */ - QS_CFC_DDFT = 0x080c, /* dev downstream fifo threshold */ - QS_CCT_CTR0 = 0x0900, /* chan control-0 offset */ - QS_CCT_CTR1 = 0x0901, /* chan control-1 offset */ - QS_CCT_CFF = 0x0a00, /* chan command fifo offset */ - - /* channel control bits */ - QS_CTR0_REG = (1 << 1), /* register mode (vs. pkt mode) */ - QS_CTR0_CLER = (1 << 2), /* clear channel errors */ - QS_CTR1_RDEV = (1 << 1), /* sata phy/comms reset */ - QS_CTR1_RCHN = (1 << 4), /* reset channel logic */ - QS_CCF_RUN_PKT = 0x107, /* RUN a new dma PKT */ - - /* pkt sub-field headers */ - QS_HCB_HDR = 0x01, /* Host Control Block header */ - QS_DCB_HDR = 0x02, /* Device Control Block header */ - - /* pkt HCB flag bits */ - QS_HF_DIRO = (1 << 0), /* data DIRection Out */ - QS_HF_DAT = (1 << 3), /* DATa pkt */ - QS_HF_IEN = (1 << 4), /* Interrupt ENable */ - QS_HF_VLD = (1 << 5), /* VaLiD pkt */ - - /* pkt DCB flag bits */ - QS_DF_PORD = (1 << 2), /* Pio OR Dma */ - QS_DF_ELBA = (1 << 3), /* Extended LBA (lba48) */ - - /* PCI device IDs */ - board_2068_idx = 0, /* QStor 4-port SATA/RAID */ -}; - -enum { - QS_DMA_BOUNDARY = ~0UL -}; - -typedef enum { qs_state_idle, qs_state_pkt, qs_state_mmio } qs_state_t; - -struct qs_port_priv { - u8 *pkt; - dma_addr_t pkt_dma; - qs_state_t state; -}; - -static u32 qs_scr_read (struct ata_port *ap, unsigned int sc_reg); -static void qs_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); -static int qs_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static irqreturn_t qs_intr (int irq, void *dev_instance, struct pt_regs *regs); -static int qs_port_start(struct ata_port *ap); -static void qs_host_stop(struct ata_host_set *host_set); -static void qs_port_stop(struct ata_port *ap); -static void qs_phy_reset(struct ata_port *ap); -static void qs_qc_prep(struct ata_queued_cmd *qc); -static unsigned int qs_qc_issue(struct ata_queued_cmd *qc); -static int qs_check_atapi_dma(struct ata_queued_cmd *qc); -static void qs_bmdma_stop(struct ata_queued_cmd *qc); -static u8 qs_bmdma_status(struct ata_port *ap); -static void qs_irq_clear(struct ata_port *ap); -static void qs_eng_timeout(struct ata_port *ap); - -static struct scsi_host_template qs_ata_sht = { - .module = THIS_MODULE, - .name = DRV_NAME, - .ioctl = ata_scsi_ioctl, - .queuecommand = ata_scsi_queuecmd, - .can_queue = ATA_DEF_QUEUE, - .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = QS_MAX_PRD, - .cmd_per_lun = ATA_SHT_CMD_PER_LUN, - .emulated = ATA_SHT_EMULATED, - //FIXME .use_clustering = ATA_SHT_USE_CLUSTERING, - .use_clustering = ENABLE_CLUSTERING, - .proc_name = DRV_NAME, - .dma_boundary = QS_DMA_BOUNDARY, - .slave_configure = ata_scsi_slave_config, - .slave_destroy = ata_scsi_slave_destroy, - .bios_param = ata_std_bios_param, -}; - -static const struct ata_port_operations qs_ata_ops = { - .port_disable = ata_port_disable, - .tf_load = ata_tf_load, - .tf_read = ata_tf_read, - .check_status = ata_check_status, - .check_atapi_dma = qs_check_atapi_dma, - .exec_command = ata_exec_command, - .dev_select = ata_std_dev_select, - .phy_reset = qs_phy_reset, - .qc_prep = qs_qc_prep, - .qc_issue = qs_qc_issue, - .data_xfer = ata_mmio_data_xfer, - .eng_timeout = qs_eng_timeout, - .irq_handler = qs_intr, - .irq_clear = qs_irq_clear, - .scr_read = qs_scr_read, - .scr_write = qs_scr_write, - .port_start = qs_port_start, - .port_stop = qs_port_stop, - .host_stop = qs_host_stop, - .bmdma_stop = qs_bmdma_stop, - .bmdma_status = qs_bmdma_status, -}; - -static const struct ata_port_info qs_port_info[] = { - /* board_2068_idx */ - { - .sht = &qs_ata_sht, - .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_SATA_RESET | - //FIXME ATA_FLAG_SRST | - ATA_FLAG_MMIO | ATA_FLAG_PIO_POLLING, - .pio_mask = 0x10, /* pio4 */ - .udma_mask = 0x7f, /* udma0-6 */ - .port_ops = &qs_ata_ops, - }, -}; - -static const struct pci_device_id qs_ata_pci_tbl[] = { - { PCI_VENDOR_ID_PDC, 0x2068, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2068_idx }, - - { } /* terminate list */ -}; - -static struct pci_driver qs_ata_pci_driver = { - .name = DRV_NAME, - .id_table = qs_ata_pci_tbl, - .probe = qs_ata_init_one, - .remove = ata_pci_remove_one, -}; - -static int qs_check_atapi_dma(struct ata_queued_cmd *qc) -{ - return 1; /* ATAPI DMA not supported */ -} - -static void qs_bmdma_stop(struct ata_queued_cmd *qc) -{ - /* nothing */ -} - -static u8 qs_bmdma_status(struct ata_port *ap) -{ - return 0; -} - -static void qs_irq_clear(struct ata_port *ap) -{ - /* nothing */ -} - -static inline void qs_enter_reg_mode(struct ata_port *ap) -{ - u8 __iomem *chan = ap->host_set->mmio_base + (ap->port_no * 0x4000); - - writeb(QS_CTR0_REG, chan + QS_CCT_CTR0); - readb(chan + QS_CCT_CTR0); /* flush */ -} - -static inline void qs_reset_channel_logic(struct ata_port *ap) -{ - u8 __iomem *chan = ap->host_set->mmio_base + (ap->port_no * 0x4000); - - writeb(QS_CTR1_RCHN, chan + QS_CCT_CTR1); - readb(chan + QS_CCT_CTR0); /* flush */ - qs_enter_reg_mode(ap); -} - -static void qs_phy_reset(struct ata_port *ap) -{ - struct qs_port_priv *pp = ap->private_data; - - pp->state = qs_state_idle; - qs_reset_channel_logic(ap); - sata_phy_reset(ap); -} - -static void qs_eng_timeout(struct ata_port *ap) -{ - struct qs_port_priv *pp = ap->private_data; - - if (pp->state != qs_state_idle) /* healthy paranoia */ - pp->state = qs_state_mmio; - qs_reset_channel_logic(ap); - ata_eng_timeout(ap); -} - -static u32 qs_scr_read (struct ata_port *ap, unsigned int sc_reg) -{ - if (sc_reg > SCR_CONTROL) - return ~0U; - return readl((void __iomem *)(ap->ioaddr.scr_addr + (sc_reg * 8))); -} - -static void qs_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) -{ - if (sc_reg > SCR_CONTROL) - return; - writel(val, (void __iomem *)(ap->ioaddr.scr_addr + (sc_reg * 8))); -} - -static unsigned int qs_fill_sg(struct ata_queued_cmd *qc) -{ - struct scatterlist *sg; - struct ata_port *ap = qc->ap; - struct qs_port_priv *pp = ap->private_data; - unsigned int nelem; - u8 *prd = pp->pkt + QS_CPB_BYTES; - - WARN_ON(qc->__sg == NULL); - WARN_ON(qc->n_elem == 0 && qc->pad_len == 0); - - nelem = 0; - ata_for_each_sg(sg, qc) { - u64 addr; - u32 len; - - addr = sg_dma_address(sg); - *(__le64 *)prd = cpu_to_le64(addr); - prd += sizeof(u64); - - len = sg_dma_len(sg); - *(__le32 *)prd = cpu_to_le32(len); - prd += sizeof(u64); - - VPRINTK("PRD[%u] = (0x%llX, 0x%X)\n", nelem, - (unsigned long long)addr, len); - nelem++; - } - - return nelem; -} - -static void qs_qc_prep(struct ata_queued_cmd *qc) -{ - struct qs_port_priv *pp = qc->ap->private_data; - u8 dflags = QS_DF_PORD, *buf = pp->pkt; - u8 hflags = QS_HF_DAT | QS_HF_IEN | QS_HF_VLD; - u64 addr; - unsigned int nelem; - - VPRINTK("ENTER\n"); - - qs_enter_reg_mode(qc->ap); - if (qc->tf.protocol != ATA_PROT_DMA) { - ata_qc_prep(qc); - return; - } - - nelem = qs_fill_sg(qc); - - if ((qc->tf.flags & ATA_TFLAG_WRITE)) - hflags |= QS_HF_DIRO; - if ((qc->tf.flags & ATA_TFLAG_LBA48)) - dflags |= QS_DF_ELBA; - - /* host control block (HCB) */ - buf[ 0] = QS_HCB_HDR; - buf[ 1] = hflags; - *(__le32 *)(&buf[ 4]) = cpu_to_le32(qc->nsect * ATA_SECT_SIZE); - *(__le32 *)(&buf[ 8]) = cpu_to_le32(nelem); - addr = ((u64)pp->pkt_dma) + QS_CPB_BYTES; - *(__le64 *)(&buf[16]) = cpu_to_le64(addr); - - /* device control block (DCB) */ - buf[24] = QS_DCB_HDR; - buf[28] = dflags; - - /* frame information structure (FIS) */ - ata_tf_to_fis(&qc->tf, &buf[32], 0); -} - -static inline void qs_packet_start(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - u8 __iomem *chan = ap->host_set->mmio_base + (ap->port_no * 0x4000); - - VPRINTK("ENTER, ap %p\n", ap); - - writeb(QS_CTR0_CLER, chan + QS_CCT_CTR0); - wmb(); /* flush PRDs and pkt to memory */ - writel(QS_CCF_RUN_PKT, chan + QS_CCT_CFF); - readl(chan + QS_CCT_CFF); /* flush */ -} - -static unsigned int qs_qc_issue(struct ata_queued_cmd *qc) -{ - struct qs_port_priv *pp = qc->ap->private_data; - - switch (qc->tf.protocol) { - case ATA_PROT_DMA: - - pp->state = qs_state_pkt; - qs_packet_start(qc); - return 0; - - case ATA_PROT_ATAPI_DMA: - BUG(); - break; - - default: - break; - } - - pp->state = qs_state_mmio; - return ata_qc_issue_prot(qc); -} - -static inline unsigned int qs_intr_pkt(struct ata_host_set *host_set) -{ - unsigned int handled = 0; - u8 sFFE; - u8 __iomem *mmio_base = host_set->mmio_base; - - do { - u32 sff0 = readl(mmio_base + QS_HST_SFF); - u32 sff1 = readl(mmio_base + QS_HST_SFF + 4); - u8 sEVLD = (sff1 >> 30) & 0x01; /* valid flag */ - sFFE = sff1 >> 31; /* empty flag */ - - if (sEVLD) { - u8 sDST = sff0 >> 16; /* dev status */ - u8 sHST = sff1 & 0x3f; /* host status */ - unsigned int port_no = (sff1 >> 8) & 0x03; - struct ata_port *ap = host_set->ports[port_no]; - - DPRINTK("SFF=%08x%08x: sCHAN=%u sHST=%d sDST=%02x\n", - sff1, sff0, port_no, sHST, sDST); - handled = 1; - if (ap && !(ap->flags & ATA_FLAG_DISABLED)) { - struct ata_queued_cmd *qc; - struct qs_port_priv *pp = ap->private_data; - if (!pp || pp->state != qs_state_pkt) - continue; - qc = ata_qc_from_tag(ap, ap->active_tag); - if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) { - switch (sHST) { - case 0: /* successful CPB */ - case 3: /* device error */ - pp->state = qs_state_idle; - qs_enter_reg_mode(qc->ap); - qc->err_mask |= ac_err_mask(sDST); - ata_qc_complete(qc); - break; - default: - break; - } - } - } - } - } while (!sFFE); - return handled; -} - -static inline unsigned int qs_intr_mmio(struct ata_host_set *host_set) -{ - unsigned int handled = 0, port_no; - - for (port_no = 0; port_no < host_set->n_ports; ++port_no) { - struct ata_port *ap; - ap = host_set->ports[port_no]; - if (ap && - !(ap->flags & ATA_FLAG_DISABLED)) { - struct ata_queued_cmd *qc; - struct qs_port_priv *pp = ap->private_data; - if (!pp || pp->state != qs_state_mmio) - continue; - qc = ata_qc_from_tag(ap, ap->active_tag); - if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) { - - /* check main status, clearing INTRQ */ - u8 status = ata_check_status(ap); - if ((status & ATA_BUSY)) - continue; - DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n", - ap->id, qc->tf.protocol, status); - - /* complete taskfile transaction */ - pp->state = qs_state_idle; - qc->err_mask |= ac_err_mask(status); - ata_qc_complete(qc); - handled = 1; - } - } - } - return handled; -} - -static irqreturn_t qs_intr(int irq, void *dev_instance, struct pt_regs *regs) -{ - struct ata_host_set *host_set = dev_instance; - unsigned int handled = 0; - - VPRINTK("ENTER\n"); - - spin_lock(&host_set->lock); - handled = qs_intr_pkt(host_set) | qs_intr_mmio(host_set); - spin_unlock(&host_set->lock); - - VPRINTK("EXIT\n"); - - return IRQ_RETVAL(handled); -} - -static void qs_ata_setup_port(struct ata_ioports *port, unsigned long base) -{ - port->cmd_addr = - port->data_addr = base + 0x400; - port->error_addr = - port->feature_addr = base + 0x408; /* hob_feature = 0x409 */ - port->nsect_addr = base + 0x410; /* hob_nsect = 0x411 */ - port->lbal_addr = base + 0x418; /* hob_lbal = 0x419 */ - port->lbam_addr = base + 0x420; /* hob_lbam = 0x421 */ - port->lbah_addr = base + 0x428; /* hob_lbah = 0x429 */ - port->device_addr = base + 0x430; - port->status_addr = - port->command_addr = base + 0x438; - port->altstatus_addr = - port->ctl_addr = base + 0x440; - port->scr_addr = base + 0xc00; -} - -static int qs_port_start(struct ata_port *ap) -{ - struct device *dev = ap->host_set->dev; - struct qs_port_priv *pp; - void __iomem *mmio_base = ap->host_set->mmio_base; - void __iomem *chan = mmio_base + (ap->port_no * 0x4000); - u64 addr; - int rc; - - rc = ata_port_start(ap); - if (rc) - return rc; - qs_enter_reg_mode(ap); - pp = kzalloc(sizeof(*pp), GFP_KERNEL); - if (!pp) { - rc = -ENOMEM; - goto err_out; - } - pp->pkt = dma_alloc_coherent(dev, QS_PKT_BYTES, &pp->pkt_dma, - GFP_KERNEL); - if (!pp->pkt) { - rc = -ENOMEM; - goto err_out_kfree; - } - memset(pp->pkt, 0, QS_PKT_BYTES); - ap->private_data = pp; - - addr = (u64)pp->pkt_dma; - writel((u32) addr, chan + QS_CCF_CPBA); - writel((u32)(addr >> 32), chan + QS_CCF_CPBA + 4); - return 0; - -err_out_kfree: - kfree(pp); -err_out: - ata_port_stop(ap); - return rc; -} - -static void qs_port_stop(struct ata_port *ap) -{ - struct device *dev = ap->host_set->dev; - struct qs_port_priv *pp = ap->private_data; - - if (pp != NULL) { - ap->private_data = NULL; - if (pp->pkt != NULL) - dma_free_coherent(dev, QS_PKT_BYTES, pp->pkt, - pp->pkt_dma); - kfree(pp); - } - ata_port_stop(ap); -} - -static void qs_host_stop(struct ata_host_set *host_set) -{ - void __iomem *mmio_base = host_set->mmio_base; - struct pci_dev *pdev = to_pci_dev(host_set->dev); - - writeb(0, mmio_base + QS_HCT_CTRL); /* disable host interrupts */ - writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */ - - pci_iounmap(pdev, mmio_base); -} - -static void qs_host_init(unsigned int chip_id, struct ata_probe_ent *pe) -{ - void __iomem *mmio_base = pe->mmio_base; - unsigned int port_no; - - writeb(0, mmio_base + QS_HCT_CTRL); /* disable host interrupts */ - writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */ - - /* reset each channel in turn */ - for (port_no = 0; port_no < pe->n_ports; ++port_no) { - u8 __iomem *chan = mmio_base + (port_no * 0x4000); - writeb(QS_CTR1_RDEV|QS_CTR1_RCHN, chan + QS_CCT_CTR1); - writeb(QS_CTR0_REG, chan + QS_CCT_CTR0); - readb(chan + QS_CCT_CTR0); /* flush */ - } - writeb(QS_SERD3_PHY_ENA, mmio_base + QS_HVS_SERD3); /* enable phy */ - - for (port_no = 0; port_no < pe->n_ports; ++port_no) { - u8 __iomem *chan = mmio_base + (port_no * 0x4000); - /* set FIFO depths to same settings as Windows driver */ - writew(32, chan + QS_CFC_HUFT); - writew(32, chan + QS_CFC_HDFT); - writew(10, chan + QS_CFC_DUFT); - writew( 8, chan + QS_CFC_DDFT); - /* set CPB size in bytes, as a power of two */ - writeb(QS_CPB_ORDER, chan + QS_CCF_CSEP); - } - writeb(1, mmio_base + QS_HCT_CTRL); /* enable host interrupts */ -} - -/* - * The QStor understands 64-bit buses, and uses 64-bit fields - * for DMA pointers regardless of bus width. We just have to - * make sure our DMA masks are set appropriately for whatever - * bridge lies between us and the QStor, and then the DMA mapping - * code will ensure we only ever "see" appropriate buffer addresses. - * If we're 32-bit limited somewhere, then our 64-bit fields will - * just end up with zeros in the upper 32-bits, without any special - * logic required outside of this routine (below). - */ -static int qs_set_dma_masks(struct pci_dev *pdev, void __iomem *mmio_base) -{ - u32 bus_info = readl(mmio_base + QS_HID_HPHY); - int rc, have_64bit_bus = (bus_info & QS_HPHY_64BIT); - - if (have_64bit_bus && - !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 0; -} - -static int qs_ata_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - static int printed_version; - struct ata_probe_ent *probe_ent = NULL; - void __iomem *mmio_base; - unsigned int board_idx = (unsigned int) ent->driver_data; - int rc, port_no; - - if (!printed_version++) - dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); - - rc = pci_enable_device(pdev); - if (rc) - return rc; - - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) - goto err_out; - - if ((pci_resource_flags(pdev, 4) & IORESOURCE_MEM) == 0) { - rc = -ENODEV; - goto err_out_regions; - } - - mmio_base = pci_iomap(pdev, 4, 0); - if (mmio_base == NULL) { - rc = -ENOMEM; - goto err_out_regions; - } - - rc = qs_set_dma_masks(pdev, mmio_base); - if (rc) - goto err_out_iounmap; - - probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) { - rc = -ENOMEM; - goto err_out_iounmap; - } - - memset(probe_ent, 0, sizeof(*probe_ent)); - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - probe_ent->sht = qs_port_info[board_idx].sht; - probe_ent->host_flags = qs_port_info[board_idx].host_flags; - probe_ent->pio_mask = qs_port_info[board_idx].pio_mask; - probe_ent->mwdma_mask = qs_port_info[board_idx].mwdma_mask; - probe_ent->udma_mask = qs_port_info[board_idx].udma_mask; - probe_ent->port_ops = qs_port_info[board_idx].port_ops; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->mmio_base = mmio_base; - probe_ent->n_ports = QS_PORTS; - - for (port_no = 0; port_no < probe_ent->n_ports; ++port_no) { - unsigned long chan = (unsigned long)mmio_base + - (port_no * 0x4000); - qs_ata_setup_port(&probe_ent->port[port_no], chan); - } - - pci_set_master(pdev); - - /* initialize adapter */ - qs_host_init(board_idx, probe_ent); - - rc = ata_device_add(probe_ent); - kfree(probe_ent); - if (rc != QS_PORTS) - goto err_out_iounmap; - return 0; - -err_out_iounmap: - pci_iounmap(pdev, mmio_base); -err_out_regions: - pci_release_regions(pdev); -err_out: - pci_disable_device(pdev); - return rc; -} - -static int __init qs_ata_init(void) -{ - return pci_register_driver(&qs_ata_pci_driver); -} - -static void __exit qs_ata_exit(void) -{ - pci_unregister_driver(&qs_ata_pci_driver); -} - -MODULE_AUTHOR("Mark Lord"); -MODULE_DESCRIPTION("Pacific Digital Corporation QStor SATA low-level driver"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, qs_ata_pci_tbl); -MODULE_VERSION(DRV_VERSION); - -module_init(qs_ata_init); -module_exit(qs_ata_exit); diff --git a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c deleted file mode 100644 index 907faa87239..00000000000 --- a/drivers/scsi/sata_sil.c +++ /dev/null @@ -1,723 +0,0 @@ -/* - * sata_sil.c - Silicon Image SATA - * - * Maintained by: Jeff Garzik - * Please ALWAYS copy linux-ide@vger.kernel.org - * on emails. - * - * Copyright 2003-2005 Red Hat, Inc. - * Copyright 2003 Benjamin Herrenschmidt - * - * - * 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 02139, USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - * Documentation for SiI 3112: - * http://gkernel.sourceforge.net/specs/sii/3112A_SiI-DS-0095-B2.pdf.bz2 - * - * Other errata and documentation available under NDA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "sata_sil" -#define DRV_VERSION "2.0" - -enum { - /* - * host flags - */ - SIL_FLAG_NO_SATA_IRQ = (1 << 28), - SIL_FLAG_RERR_ON_DMA_ACT = (1 << 29), - SIL_FLAG_MOD15WRITE = (1 << 30), - - SIL_DFL_HOST_FLAGS = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_HRST_TO_RESUME, - - /* - * Controller IDs - */ - sil_3112 = 0, - sil_3112_no_sata_irq = 1, - sil_3512 = 2, - sil_3114 = 3, - - /* - * Register offsets - */ - SIL_SYSCFG = 0x48, - - /* - * Register bits - */ - /* SYSCFG */ - SIL_MASK_IDE0_INT = (1 << 22), - SIL_MASK_IDE1_INT = (1 << 23), - SIL_MASK_IDE2_INT = (1 << 24), - SIL_MASK_IDE3_INT = (1 << 25), - SIL_MASK_2PORT = SIL_MASK_IDE0_INT | SIL_MASK_IDE1_INT, - SIL_MASK_4PORT = SIL_MASK_2PORT | - SIL_MASK_IDE2_INT | SIL_MASK_IDE3_INT, - - /* BMDMA/BMDMA2 */ - SIL_INTR_STEERING = (1 << 1), - - SIL_DMA_ENABLE = (1 << 0), /* DMA run switch */ - SIL_DMA_RDWR = (1 << 3), /* DMA Rd-Wr */ - SIL_DMA_SATA_IRQ = (1 << 4), /* OR of all SATA IRQs */ - SIL_DMA_ACTIVE = (1 << 16), /* DMA running */ - SIL_DMA_ERROR = (1 << 17), /* PCI bus error */ - SIL_DMA_COMPLETE = (1 << 18), /* cmd complete / IRQ pending */ - SIL_DMA_N_SATA_IRQ = (1 << 6), /* SATA_IRQ for the next channel */ - SIL_DMA_N_ACTIVE = (1 << 24), /* ACTIVE for the next channel */ - SIL_DMA_N_ERROR = (1 << 25), /* ERROR for the next channel */ - SIL_DMA_N_COMPLETE = (1 << 26), /* COMPLETE for the next channel */ - - /* SIEN */ - SIL_SIEN_N = (1 << 16), /* triggered by SError.N */ - - /* - * Others - */ - SIL_QUIRK_MOD15WRITE = (1 << 0), - SIL_QUIRK_UDMA5MAX = (1 << 1), -}; - -static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static int sil_pci_device_resume(struct pci_dev *pdev); -static void sil_dev_config(struct ata_port *ap, struct ata_device *dev); -static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg); -static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); -static void sil_post_set_mode (struct ata_port *ap); -static irqreturn_t sil_interrupt(int irq, void *dev_instance, - struct pt_regs *regs); -static void sil_freeze(struct ata_port *ap); -static void sil_thaw(struct ata_port *ap); - - -static const struct pci_device_id sil_pci_tbl[] = { - { 0x1095, 0x3112, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 }, - { 0x1095, 0x0240, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 }, - { 0x1095, 0x3512, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3512 }, - { 0x1095, 0x3114, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3114 }, - { 0x1002, 0x436e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 }, - { 0x1002, 0x4379, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112_no_sata_irq }, - { 0x1002, 0x437a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112_no_sata_irq }, - { } /* terminate list */ -}; - - -/* TODO firmware versions should be added - eric */ -static const struct sil_drivelist { - const char * product; - unsigned int quirk; -} sil_blacklist [] = { - { "ST320012AS", SIL_QUIRK_MOD15WRITE }, - { "ST330013AS", SIL_QUIRK_MOD15WRITE }, - { "ST340017AS", SIL_QUIRK_MOD15WRITE }, - { "ST360015AS", SIL_QUIRK_MOD15WRITE }, - { "ST380023AS", SIL_QUIRK_MOD15WRITE }, - { "ST3120023AS", SIL_QUIRK_MOD15WRITE }, - { "ST340014ASL", SIL_QUIRK_MOD15WRITE }, - { "ST360014ASL", SIL_QUIRK_MOD15WRITE }, - { "ST380011ASL", SIL_QUIRK_MOD15WRITE }, - { "ST3120022ASL", SIL_QUIRK_MOD15WRITE }, - { "ST3160021ASL", SIL_QUIRK_MOD15WRITE }, - { "Maxtor 4D060H3", SIL_QUIRK_UDMA5MAX }, - { } -}; - -static struct pci_driver sil_pci_driver = { - .name = DRV_NAME, - .id_table = sil_pci_tbl, - .probe = sil_init_one, - .remove = ata_pci_remove_one, - .suspend = ata_pci_device_suspend, - .resume = sil_pci_device_resume, -}; - -static struct scsi_host_template sil_sht = { - .module = THIS_MODULE, - .name = DRV_NAME, - .ioctl = ata_scsi_ioctl, - .queuecommand = ata_scsi_queuecmd, - .can_queue = ATA_DEF_QUEUE, - .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = LIBATA_MAX_PRD, - .cmd_per_lun = ATA_SHT_CMD_PER_LUN, - .emulated = ATA_SHT_EMULATED, - .use_clustering = ATA_SHT_USE_CLUSTERING, - .proc_name = DRV_NAME, - .dma_boundary = ATA_DMA_BOUNDARY, - .slave_configure = ata_scsi_slave_config, - .slave_destroy = ata_scsi_slave_destroy, - .bios_param = ata_std_bios_param, - .suspend = ata_scsi_device_suspend, - .resume = ata_scsi_device_resume, -}; - -static const struct ata_port_operations sil_ops = { - .port_disable = ata_port_disable, - .dev_config = sil_dev_config, - .tf_load = ata_tf_load, - .tf_read = ata_tf_read, - .check_status = ata_check_status, - .exec_command = ata_exec_command, - .dev_select = ata_std_dev_select, - .post_set_mode = sil_post_set_mode, - .bmdma_setup = ata_bmdma_setup, - .bmdma_start = ata_bmdma_start, - .bmdma_stop = ata_bmdma_stop, - .bmdma_status = ata_bmdma_status, - .qc_prep = ata_qc_prep, - .qc_issue = ata_qc_issue_prot, - .data_xfer = ata_mmio_data_xfer, - .freeze = sil_freeze, - .thaw = sil_thaw, - .error_handler = ata_bmdma_error_handler, - .post_internal_cmd = ata_bmdma_post_internal_cmd, - .irq_handler = sil_interrupt, - .irq_clear = ata_bmdma_irq_clear, - .scr_read = sil_scr_read, - .scr_write = sil_scr_write, - .port_start = ata_port_start, - .port_stop = ata_port_stop, - .host_stop = ata_pci_host_stop, -}; - -static const struct ata_port_info sil_port_info[] = { - /* sil_3112 */ - { - .sht = &sil_sht, - .host_flags = SIL_DFL_HOST_FLAGS | SIL_FLAG_MOD15WRITE, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x3f, /* udma0-5 */ - .port_ops = &sil_ops, - }, - /* sil_3112_no_sata_irq */ - { - .sht = &sil_sht, - .host_flags = SIL_DFL_HOST_FLAGS | SIL_FLAG_MOD15WRITE | - SIL_FLAG_NO_SATA_IRQ, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x3f, /* udma0-5 */ - .port_ops = &sil_ops, - }, - /* sil_3512 */ - { - .sht = &sil_sht, - .host_flags = SIL_DFL_HOST_FLAGS | SIL_FLAG_RERR_ON_DMA_ACT, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x3f, /* udma0-5 */ - .port_ops = &sil_ops, - }, - /* sil_3114 */ - { - .sht = &sil_sht, - .host_flags = SIL_DFL_HOST_FLAGS | SIL_FLAG_RERR_ON_DMA_ACT, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x3f, /* udma0-5 */ - .port_ops = &sil_ops, - }, -}; - -/* per-port register offsets */ -/* TODO: we can probably calculate rather than use a table */ -static const struct { - unsigned long tf; /* ATA taskfile register block */ - unsigned long ctl; /* ATA control/altstatus register block */ - unsigned long bmdma; /* DMA register block */ - unsigned long bmdma2; /* DMA register block #2 */ - unsigned long fifo_cfg; /* FIFO Valid Byte Count and Control */ - unsigned long scr; /* SATA control register block */ - unsigned long sien; /* SATA Interrupt Enable register */ - unsigned long xfer_mode;/* data transfer mode register */ - unsigned long sfis_cfg; /* SATA FIS reception config register */ -} sil_port[] = { - /* port 0 ... */ - { 0x80, 0x8A, 0x00, 0x10, 0x40, 0x100, 0x148, 0xb4, 0x14c }, - { 0xC0, 0xCA, 0x08, 0x18, 0x44, 0x180, 0x1c8, 0xf4, 0x1cc }, - { 0x280, 0x28A, 0x200, 0x210, 0x240, 0x300, 0x348, 0x2b4, 0x34c }, - { 0x2C0, 0x2CA, 0x208, 0x218, 0x244, 0x380, 0x3c8, 0x2f4, 0x3cc }, - /* ... port 3 */ -}; - -MODULE_AUTHOR("Jeff Garzik"); -MODULE_DESCRIPTION("low-level driver for Silicon Image SATA controller"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, sil_pci_tbl); -MODULE_VERSION(DRV_VERSION); - -static int slow_down = 0; -module_param(slow_down, int, 0444); -MODULE_PARM_DESC(slow_down, "Sledgehammer used to work around random problems, by limiting commands to 15 sectors (0=off, 1=on)"); - - -static unsigned char sil_get_device_cache_line(struct pci_dev *pdev) -{ - u8 cache_line = 0; - pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line); - return cache_line; -} - -static void sil_post_set_mode (struct ata_port *ap) -{ - struct ata_host_set *host_set = ap->host_set; - struct ata_device *dev; - void __iomem *addr = - host_set->mmio_base + sil_port[ap->port_no].xfer_mode; - u32 tmp, dev_mode[2]; - unsigned int i; - - for (i = 0; i < 2; i++) { - dev = &ap->device[i]; - if (!ata_dev_enabled(dev)) - dev_mode[i] = 0; /* PIO0/1/2 */ - else if (dev->flags & ATA_DFLAG_PIO) - dev_mode[i] = 1; /* PIO3/4 */ - else - dev_mode[i] = 3; /* UDMA */ - /* value 2 indicates MDMA */ - } - - tmp = readl(addr); - tmp &= ~((1<<5) | (1<<4) | (1<<1) | (1<<0)); - tmp |= dev_mode[0]; - tmp |= (dev_mode[1] << 4); - writel(tmp, addr); - readl(addr); /* flush */ -} - -static inline unsigned long sil_scr_addr(struct ata_port *ap, unsigned int sc_reg) -{ - unsigned long offset = ap->ioaddr.scr_addr; - - switch (sc_reg) { - case SCR_STATUS: - return offset + 4; - case SCR_ERROR: - return offset + 8; - case SCR_CONTROL: - return offset; - default: - /* do nothing */ - break; - } - - return 0; -} - -static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg) -{ - void __iomem *mmio = (void __iomem *) sil_scr_addr(ap, sc_reg); - if (mmio) - return readl(mmio); - return 0xffffffffU; -} - -static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) -{ - void *mmio = (void __iomem *) sil_scr_addr(ap, sc_reg); - if (mmio) - writel(val, mmio); -} - -static void sil_host_intr(struct ata_port *ap, u32 bmdma2) -{ - struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag); - u8 status; - - if (unlikely(bmdma2 & SIL_DMA_SATA_IRQ)) { - u32 serror; - - /* SIEN doesn't mask SATA IRQs on some 3112s. Those - * controllers continue to assert IRQ as long as - * SError bits are pending. Clear SError immediately. - */ - serror = sil_scr_read(ap, SCR_ERROR); - sil_scr_write(ap, SCR_ERROR, serror); - - /* Trigger hotplug and accumulate SError only if the - * port isn't already frozen. Otherwise, PHY events - * during hardreset makes controllers with broken SIEN - * repeat probing needlessly. - */ - if (!(ap->pflags & ATA_PFLAG_FROZEN)) { - ata_ehi_hotplugged(&ap->eh_info); - ap->eh_info.serror |= serror; - } - - goto freeze; - } - - if (unlikely(!qc || qc->tf.ctl & ATA_NIEN)) - goto freeze; - - /* Check whether we are expecting interrupt in this state */ - switch (ap->hsm_task_state) { - case HSM_ST_FIRST: - /* Some pre-ATAPI-4 devices assert INTRQ - * at this state when ready to receive CDB. - */ - - /* Check the ATA_DFLAG_CDB_INTR flag is enough here. - * The flag was turned on only for atapi devices. - * No need to check is_atapi_taskfile(&qc->tf) again. - */ - if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) - goto err_hsm; - break; - case HSM_ST_LAST: - if (qc->tf.protocol == ATA_PROT_DMA || - qc->tf.protocol == ATA_PROT_ATAPI_DMA) { - /* clear DMA-Start bit */ - ap->ops->bmdma_stop(qc); - - if (bmdma2 & SIL_DMA_ERROR) { - qc->err_mask |= AC_ERR_HOST_BUS; - ap->hsm_task_state = HSM_ST_ERR; - } - } - break; - case HSM_ST: - break; - default: - goto err_hsm; - } - - /* check main status, clearing INTRQ */ - status = ata_chk_status(ap); - if (unlikely(status & ATA_BUSY)) - goto err_hsm; - - /* ack bmdma irq events */ - ata_bmdma_irq_clear(ap); - - /* kick HSM in the ass */ - ata_hsm_move(ap, qc, status, 0); - - return; - - err_hsm: - qc->err_mask |= AC_ERR_HSM; - freeze: - ata_port_freeze(ap); -} - -static irqreturn_t sil_interrupt(int irq, void *dev_instance, - struct pt_regs *regs) -{ - struct ata_host_set *host_set = dev_instance; - void __iomem *mmio_base = host_set->mmio_base; - int handled = 0; - int i; - - spin_lock(&host_set->lock); - - for (i = 0; i < host_set->n_ports; i++) { - struct ata_port *ap = host_set->ports[i]; - u32 bmdma2 = readl(mmio_base + sil_port[ap->port_no].bmdma2); - - if (unlikely(!ap || ap->flags & ATA_FLAG_DISABLED)) - continue; - - /* turn off SATA_IRQ if not supported */ - if (ap->flags & SIL_FLAG_NO_SATA_IRQ) - bmdma2 &= ~SIL_DMA_SATA_IRQ; - - if (bmdma2 == 0xffffffff || - !(bmdma2 & (SIL_DMA_COMPLETE | SIL_DMA_SATA_IRQ))) - continue; - - sil_host_intr(ap, bmdma2); - handled = 1; - } - - spin_unlock(&host_set->lock); - - return IRQ_RETVAL(handled); -} - -static void sil_freeze(struct ata_port *ap) -{ - void __iomem *mmio_base = ap->host_set->mmio_base; - u32 tmp; - - /* global IRQ mask doesn't block SATA IRQ, turn off explicitly */ - writel(0, mmio_base + sil_port[ap->port_no].sien); - - /* plug IRQ */ - tmp = readl(mmio_base + SIL_SYSCFG); - tmp |= SIL_MASK_IDE0_INT << ap->port_no; - writel(tmp, mmio_base + SIL_SYSCFG); - readl(mmio_base + SIL_SYSCFG); /* flush */ -} - -static void sil_thaw(struct ata_port *ap) -{ - void __iomem *mmio_base = ap->host_set->mmio_base; - u32 tmp; - - /* clear IRQ */ - ata_chk_status(ap); - ata_bmdma_irq_clear(ap); - - /* turn on SATA IRQ if supported */ - if (!(ap->flags & SIL_FLAG_NO_SATA_IRQ)) - writel(SIL_SIEN_N, mmio_base + sil_port[ap->port_no].sien); - - /* turn on IRQ */ - tmp = readl(mmio_base + SIL_SYSCFG); - tmp &= ~(SIL_MASK_IDE0_INT << ap->port_no); - writel(tmp, mmio_base + SIL_SYSCFG); -} - -/** - * sil_dev_config - Apply device/host-specific errata fixups - * @ap: Port containing device to be examined - * @dev: Device to be examined - * - * After the IDENTIFY [PACKET] DEVICE step is complete, and a - * device is known to be present, this function is called. - * We apply two errata fixups which are specific to Silicon Image, - * a Seagate and a Maxtor fixup. - * - * For certain Seagate devices, we must limit the maximum sectors - * to under 8K. - * - * For certain Maxtor devices, we must not program the drive - * beyond udma5. - * - * Both fixups are unfairly pessimistic. As soon as I get more - * information on these errata, I will create a more exhaustive - * list, and apply the fixups to only the specific - * devices/hosts/firmwares that need it. - * - * 20040111 - Seagate drives affected by the Mod15Write bug are blacklisted - * The Maxtor quirk is in the blacklist, but I'm keeping the original - * pessimistic fix for the following reasons... - * - There seems to be less info on it, only one device gleaned off the - * Windows driver, maybe only one is affected. More info would be greatly - * appreciated. - * - But then again UDMA5 is hardly anything to complain about - */ -static void sil_dev_config(struct ata_port *ap, struct ata_device *dev) -{ - unsigned int n, quirks = 0; - unsigned char model_num[41]; - - ata_id_c_string(dev->id, model_num, ATA_ID_PROD_OFS, sizeof(model_num)); - - for (n = 0; sil_blacklist[n].product; n++) - if (!strcmp(sil_blacklist[n].product, model_num)) { - quirks = sil_blacklist[n].quirk; - break; - } - - /* limit requests to 15 sectors */ - if (slow_down || - ((ap->flags & SIL_FLAG_MOD15WRITE) && - (quirks & SIL_QUIRK_MOD15WRITE))) { - ata_dev_printk(dev, KERN_INFO, "applying Seagate errata fix " - "(mod15write workaround)\n"); - dev->max_sectors = 15; - return; - } - - /* limit to udma5 */ - if (quirks & SIL_QUIRK_UDMA5MAX) { - ata_dev_printk(dev, KERN_INFO, - "applying Maxtor errata fix %s\n", model_num); - dev->udma_mask &= ATA_UDMA5; - return; - } -} - -static void sil_init_controller(struct pci_dev *pdev, - int n_ports, unsigned long host_flags, - void __iomem *mmio_base) -{ - u8 cls; - u32 tmp; - int i; - - /* Initialize FIFO PCI bus arbitration */ - cls = sil_get_device_cache_line(pdev); - if (cls) { - cls >>= 3; - cls++; /* cls = (line_size/8)+1 */ - for (i = 0; i < n_ports; i++) - writew(cls << 8 | cls, - mmio_base + sil_port[i].fifo_cfg); - } else - dev_printk(KERN_WARNING, &pdev->dev, - "cache line size not set. Driver may not function\n"); - - /* Apply R_ERR on DMA activate FIS errata workaround */ - if (host_flags & SIL_FLAG_RERR_ON_DMA_ACT) { - int cnt; - - for (i = 0, cnt = 0; i < n_ports; i++) { - tmp = readl(mmio_base + sil_port[i].sfis_cfg); - if ((tmp & 0x3) != 0x01) - continue; - if (!cnt) - dev_printk(KERN_INFO, &pdev->dev, - "Applying R_ERR on DMA activate " - "FIS errata fix\n"); - writel(tmp & ~0x3, mmio_base + sil_port[i].sfis_cfg); - cnt++; - } - } - - if (n_ports == 4) { - /* flip the magic "make 4 ports work" bit */ - tmp = readl(mmio_base + sil_port[2].bmdma); - if ((tmp & SIL_INTR_STEERING) == 0) - writel(tmp | SIL_INTR_STEERING, - mmio_base + sil_port[2].bmdma); - } -} - -static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) -{ - static int printed_version; - struct ata_probe_ent *probe_ent = NULL; - unsigned long base; - void __iomem *mmio_base; - int rc; - unsigned int i; - int pci_dev_busy = 0; - - if (!printed_version++) - dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); - - rc = pci_enable_device(pdev); - if (rc) - return rc; - - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pci_dev_busy = 1; - goto err_out; - } - - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out_regions; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out_regions; - - probe_ent = kzalloc(sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) { - rc = -ENOMEM; - goto err_out_regions; - } - - INIT_LIST_HEAD(&probe_ent->node); - probe_ent->dev = pci_dev_to_dev(pdev); - probe_ent->port_ops = sil_port_info[ent->driver_data].port_ops; - probe_ent->sht = sil_port_info[ent->driver_data].sht; - probe_ent->n_ports = (ent->driver_data == sil_3114) ? 4 : 2; - probe_ent->pio_mask = sil_port_info[ent->driver_data].pio_mask; - probe_ent->mwdma_mask = sil_port_info[ent->driver_data].mwdma_mask; - probe_ent->udma_mask = sil_port_info[ent->driver_data].udma_mask; - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->host_flags = sil_port_info[ent->driver_data].host_flags; - - mmio_base = pci_iomap(pdev, 5, 0); - if (mmio_base == NULL) { - rc = -ENOMEM; - goto err_out_free_ent; - } - - probe_ent->mmio_base = mmio_base; - - base = (unsigned long) mmio_base; - - for (i = 0; i < probe_ent->n_ports; i++) { - probe_ent->port[i].cmd_addr = base + sil_port[i].tf; - probe_ent->port[i].altstatus_addr = - probe_ent->port[i].ctl_addr = base + sil_port[i].ctl; - probe_ent->port[i].bmdma_addr = base + sil_port[i].bmdma; - probe_ent->port[i].scr_addr = base + sil_port[i].scr; - ata_std_ports(&probe_ent->port[i]); - } - - sil_init_controller(pdev, probe_ent->n_ports, probe_ent->host_flags, - mmio_base); - - pci_set_master(pdev); - - /* FIXME: check ata_device_add return value */ - ata_device_add(probe_ent); - kfree(probe_ent); - - return 0; - -err_out_free_ent: - kfree(probe_ent); -err_out_regions: - pci_release_regions(pdev); -err_out: - if (!pci_dev_busy) - pci_disable_device(pdev); - return rc; -} - -static int sil_pci_device_resume(struct pci_dev *pdev) -{ - struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev); - - ata_pci_device_do_resume(pdev); - sil_init_controller(pdev, host_set->n_ports, host_set->ports[0]->flags, - host_set->mmio_base); - ata_host_set_resume(host_set); - - return 0; -} - -static int __init sil_init(void) -{ - return pci_register_driver(&sil_pci_driver); -} - -static void __exit sil_exit(void) -{ - pci_unregister_driver(&sil_pci_driver); -} - - -module_init(sil_init); -module_exit(sil_exit); diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c deleted file mode 100644 index 3a0161ddc33..00000000000 --- a/drivers/scsi/sata_sil24.c +++ /dev/null @@ -1,1222 +0,0 @@ -/* - * sata_sil24.c - Driver for Silicon Image 3124/3132 SATA-2 controllers - * - * Copyright 2005 Tejun Heo - * - * Based on preview driver from Silicon Image. - * - * 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. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "sata_sil24" -#define DRV_VERSION "0.3" - -/* - * Port request block (PRB) 32 bytes - */ -struct sil24_prb { - __le16 ctrl; - __le16 prot; - __le32 rx_cnt; - u8 fis[6 * 4]; -}; - -/* - * Scatter gather entry (SGE) 16 bytes - */ -struct sil24_sge { - __le64 addr; - __le32 cnt; - __le32 flags; -}; - -/* - * Port multiplier - */ -struct sil24_port_multiplier { - __le32 diag; - __le32 sactive; -}; - -enum { - /* - * Global controller registers (128 bytes @ BAR0) - */ - /* 32 bit regs */ - HOST_SLOT_STAT = 0x00, /* 32 bit slot stat * 4 */ - HOST_CTRL = 0x40, - HOST_IRQ_STAT = 0x44, - HOST_PHY_CFG = 0x48, - HOST_BIST_CTRL = 0x50, - HOST_BIST_PTRN = 0x54, - HOST_BIST_STAT = 0x58, - HOST_MEM_BIST_STAT = 0x5c, - HOST_FLASH_CMD = 0x70, - /* 8 bit regs */ - HOST_FLASH_DATA = 0x74, - HOST_TRANSITION_DETECT = 0x75, - HOST_GPIO_CTRL = 0x76, - HOST_I2C_ADDR = 0x78, /* 32 bit */ - HOST_I2C_DATA = 0x7c, - HOST_I2C_XFER_CNT = 0x7e, - HOST_I2C_CTRL = 0x7f, - - /* HOST_SLOT_STAT bits */ - HOST_SSTAT_ATTN = (1 << 31), - - /* HOST_CTRL bits */ - HOST_CTRL_M66EN = (1 << 16), /* M66EN PCI bus signal */ - HOST_CTRL_TRDY = (1 << 17), /* latched PCI TRDY */ - HOST_CTRL_STOP = (1 << 18), /* latched PCI STOP */ - HOST_CTRL_DEVSEL = (1 << 19), /* latched PCI DEVSEL */ - HOST_CTRL_REQ64 = (1 << 20), /* latched PCI REQ64 */ - HOST_CTRL_GLOBAL_RST = (1 << 31), /* global reset */ - - /* - * Port registers - * (8192 bytes @ +0x0000, +0x2000, +0x4000 and +0x6000 @ BAR2) - */ - PORT_REGS_SIZE = 0x2000, - - PORT_LRAM = 0x0000, /* 31 LRAM slots and PM regs */ - PORT_LRAM_SLOT_SZ = 0x0080, /* 32 bytes PRB + 2 SGE, ACT... */ - - PORT_PM = 0x0f80, /* 8 bytes PM * 16 (128 bytes) */ - /* 32 bit regs */ - PORT_CTRL_STAT = 0x1000, /* write: ctrl-set, read: stat */ - PORT_CTRL_CLR = 0x1004, /* write: ctrl-clear */ - PORT_IRQ_STAT = 0x1008, /* high: status, low: interrupt */ - PORT_IRQ_ENABLE_SET = 0x1010, /* write: enable-set */ - PORT_IRQ_ENABLE_CLR = 0x1014, /* write: enable-clear */ - PORT_ACTIVATE_UPPER_ADDR= 0x101c, - PORT_EXEC_FIFO = 0x1020, /* command execution fifo */ - PORT_CMD_ERR = 0x1024, /* command error number */ - PORT_FIS_CFG = 0x1028, - PORT_FIFO_THRES = 0x102c, - /* 16 bit regs */ - PORT_DECODE_ERR_CNT = 0x1040, - PORT_DECODE_ERR_THRESH = 0x1042, - PORT_CRC_ERR_CNT = 0x1044, - PORT_CRC_ERR_THRESH = 0x1046, - PORT_HSHK_ERR_CNT = 0x1048, - PORT_HSHK_ERR_THRESH = 0x104a, - /* 32 bit regs */ - PORT_PHY_CFG = 0x1050, - PORT_SLOT_STAT = 0x1800, - PORT_CMD_ACTIVATE = 0x1c00, /* 64 bit cmd activate * 31 (248 bytes) */ - PORT_EXEC_DIAG = 0x1e00, /* 32bit exec diag * 16 (64 bytes, 0-10 used on 3124) */ - PORT_PSD_DIAG = 0x1e40, /* 32bit psd diag * 16 (64 bytes, 0-8 used on 3124) */ - PORT_SCONTROL = 0x1f00, - PORT_SSTATUS = 0x1f04, - PORT_SERROR = 0x1f08, - PORT_SACTIVE = 0x1f0c, - - /* PORT_CTRL_STAT bits */ - PORT_CS_PORT_RST = (1 << 0), /* port reset */ - PORT_CS_DEV_RST = (1 << 1), /* device reset */ - PORT_CS_INIT = (1 << 2), /* port initialize */ - PORT_CS_IRQ_WOC = (1 << 3), /* interrupt write one to clear */ - PORT_CS_CDB16 = (1 << 5), /* 0=12b cdb, 1=16b cdb */ - PORT_CS_RESUME = (1 << 6), /* port resume */ - PORT_CS_32BIT_ACTV = (1 << 10), /* 32-bit activation */ - PORT_CS_PM_EN = (1 << 13), /* port multiplier enable */ - PORT_CS_RDY = (1 << 31), /* port ready to accept commands */ - - /* PORT_IRQ_STAT/ENABLE_SET/CLR */ - /* bits[11:0] are masked */ - PORT_IRQ_COMPLETE = (1 << 0), /* command(s) completed */ - PORT_IRQ_ERROR = (1 << 1), /* command execution error */ - PORT_IRQ_PORTRDY_CHG = (1 << 2), /* port ready change */ - PORT_IRQ_PWR_CHG = (1 << 3), /* power management change */ - PORT_IRQ_PHYRDY_CHG = (1 << 4), /* PHY ready change */ - PORT_IRQ_COMWAKE = (1 << 5), /* COMWAKE received */ - PORT_IRQ_UNK_FIS = (1 << 6), /* unknown FIS received */ - PORT_IRQ_DEV_XCHG = (1 << 7), /* device exchanged */ - PORT_IRQ_8B10B = (1 << 8), /* 8b/10b decode error threshold */ - PORT_IRQ_CRC = (1 << 9), /* CRC error threshold */ - PORT_IRQ_HANDSHAKE = (1 << 10), /* handshake error threshold */ - PORT_IRQ_SDB_NOTIFY = (1 << 11), /* SDB notify received */ - - DEF_PORT_IRQ = PORT_IRQ_COMPLETE | PORT_IRQ_ERROR | - PORT_IRQ_PHYRDY_CHG | PORT_IRQ_DEV_XCHG | - PORT_IRQ_UNK_FIS, - - /* bits[27:16] are unmasked (raw) */ - PORT_IRQ_RAW_SHIFT = 16, - PORT_IRQ_MASKED_MASK = 0x7ff, - PORT_IRQ_RAW_MASK = (0x7ff << PORT_IRQ_RAW_SHIFT), - - /* ENABLE_SET/CLR specific, intr steering - 2 bit field */ - PORT_IRQ_STEER_SHIFT = 30, - PORT_IRQ_STEER_MASK = (3 << PORT_IRQ_STEER_SHIFT), - - /* PORT_CMD_ERR constants */ - PORT_CERR_DEV = 1, /* Error bit in D2H Register FIS */ - PORT_CERR_SDB = 2, /* Error bit in SDB FIS */ - PORT_CERR_DATA = 3, /* Error in data FIS not detected by dev */ - PORT_CERR_SEND = 4, /* Initial cmd FIS transmission failure */ - PORT_CERR_INCONSISTENT = 5, /* Protocol mismatch */ - PORT_CERR_DIRECTION = 6, /* Data direction mismatch */ - PORT_CERR_UNDERRUN = 7, /* Ran out of SGEs while writing */ - PORT_CERR_OVERRUN = 8, /* Ran out of SGEs while reading */ - PORT_CERR_PKT_PROT = 11, /* DIR invalid in 1st PIO setup of ATAPI */ - PORT_CERR_SGT_BOUNDARY = 16, /* PLD ecode 00 - SGT not on qword boundary */ - PORT_CERR_SGT_TGTABRT = 17, /* PLD ecode 01 - target abort */ - PORT_CERR_SGT_MSTABRT = 18, /* PLD ecode 10 - master abort */ - PORT_CERR_SGT_PCIPERR = 19, /* PLD ecode 11 - PCI parity err while fetching SGT */ - PORT_CERR_CMD_BOUNDARY = 24, /* ctrl[15:13] 001 - PRB not on qword boundary */ - PORT_CERR_CMD_TGTABRT = 25, /* ctrl[15:13] 010 - target abort */ - PORT_CERR_CMD_MSTABRT = 26, /* ctrl[15:13] 100 - master abort */ - PORT_CERR_CMD_PCIPERR = 27, /* ctrl[15:13] 110 - PCI parity err while fetching PRB */ - PORT_CERR_XFR_UNDEF = 32, /* PSD ecode 00 - undefined */ - PORT_CERR_XFR_TGTABRT = 33, /* PSD ecode 01 - target abort */ - PORT_CERR_XFR_MSTABRT = 34, /* PSD ecode 10 - master abort */ - PORT_CERR_XFR_PCIPERR = 35, /* PSD ecode 11 - PCI prity err during transfer */ - PORT_CERR_SENDSERVICE = 36, /* FIS received while sending service */ - - /* bits of PRB control field */ - PRB_CTRL_PROTOCOL = (1 << 0), /* override def. ATA protocol */ - PRB_CTRL_PACKET_READ = (1 << 4), /* PACKET cmd read */ - PRB_CTRL_PACKET_WRITE = (1 << 5), /* PACKET cmd write */ - PRB_CTRL_NIEN = (1 << 6), /* Mask completion irq */ - PRB_CTRL_SRST = (1 << 7), /* Soft reset request (ign BSY?) */ - - /* PRB protocol field */ - PRB_PROT_PACKET = (1 << 0), - PRB_PROT_TCQ = (1 << 1), - PRB_PROT_NCQ = (1 << 2), - PRB_PROT_READ = (1 << 3), - PRB_PROT_WRITE = (1 << 4), - PRB_PROT_TRANSPARENT = (1 << 5), - - /* - * Other constants - */ - SGE_TRM = (1 << 31), /* Last SGE in chain */ - SGE_LNK = (1 << 30), /* linked list - Points to SGT, not SGE */ - SGE_DRD = (1 << 29), /* discard data read (/dev/null) - data address ignored */ - - SIL24_MAX_CMDS = 31, - - /* board id */ - BID_SIL3124 = 0, - BID_SIL3132 = 1, - BID_SIL3131 = 2, - - /* host flags */ - SIL24_COMMON_FLAGS = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - ATA_FLAG_NCQ | ATA_FLAG_SKIP_D2H_BSY, - SIL24_FLAG_PCIX_IRQ_WOC = (1 << 24), /* IRQ loss errata on PCI-X */ - - IRQ_STAT_4PORTS = 0xf, -}; - -struct sil24_ata_block { - struct sil24_prb prb; - struct sil24_sge sge[LIBATA_MAX_PRD]; -}; - -struct sil24_atapi_block { - struct sil24_prb prb; - u8 cdb[16]; - struct sil24_sge sge[LIBATA_MAX_PRD - 1]; -}; - -union sil24_cmd_block { - struct sil24_ata_block ata; - struct sil24_atapi_block atapi; -}; - -static struct sil24_cerr_info { - unsigned int err_mask, action; - const char *desc; -} sil24_cerr_db[] = { - [0] = { AC_ERR_DEV, ATA_EH_REVALIDATE, - "device error" }, - [PORT_CERR_DEV] = { AC_ERR_DEV, ATA_EH_REVALIDATE, - "device error via D2H FIS" }, - [PORT_CERR_SDB] = { AC_ERR_DEV, ATA_EH_REVALIDATE, - "device error via SDB FIS" }, - [PORT_CERR_DATA] = { AC_ERR_ATA_BUS, ATA_EH_SOFTRESET, - "error in data FIS" }, - [PORT_CERR_SEND] = { AC_ERR_ATA_BUS, ATA_EH_SOFTRESET, - "failed to transmit command FIS" }, - [PORT_CERR_INCONSISTENT] = { AC_ERR_HSM, ATA_EH_SOFTRESET, - "protocol mismatch" }, - [PORT_CERR_DIRECTION] = { AC_ERR_HSM, ATA_EH_SOFTRESET, - "data directon mismatch" }, - [PORT_CERR_UNDERRUN] = { AC_ERR_HSM, ATA_EH_SOFTRESET, - "ran out of SGEs while writing" }, - [PORT_CERR_OVERRUN] = { AC_ERR_HSM, ATA_EH_SOFTRESET, - "ran out of SGEs while reading" }, - [PORT_CERR_PKT_PROT] = { AC_ERR_HSM, ATA_EH_SOFTRESET, - "invalid data directon for ATAPI CDB" }, - [PORT_CERR_SGT_BOUNDARY] = { AC_ERR_SYSTEM, ATA_EH_SOFTRESET, - "SGT no on qword boundary" }, - [PORT_CERR_SGT_TGTABRT] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, - "PCI target abort while fetching SGT" }, - [PORT_CERR_SGT_MSTABRT] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, - "PCI master abort while fetching SGT" }, - [PORT_CERR_SGT_PCIPERR] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, - "PCI parity error while fetching SGT" }, - [PORT_CERR_CMD_BOUNDARY] = { AC_ERR_SYSTEM, ATA_EH_SOFTRESET, - "PRB not on qword boundary" }, - [PORT_CERR_CMD_TGTABRT] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, - "PCI target abort while fetching PRB" }, - [PORT_CERR_CMD_MSTABRT] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, - "PCI master abort while fetching PRB" }, - [PORT_CERR_CMD_PCIPERR] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, - "PCI parity error while fetching PRB" }, - [PORT_CERR_XFR_UNDEF] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, - "undefined error while transferring data" }, - [PORT_CERR_XFR_TGTABRT] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, - "PCI target abort while transferring data" }, - [PORT_CERR_XFR_MSTABRT] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, - "PCI master abort while transferring data" }, - [PORT_CERR_XFR_PCIPERR] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, - "PCI parity error while transferring data" }, - [PORT_CERR_SENDSERVICE] = { AC_ERR_HSM, ATA_EH_SOFTRESET, - "FIS received while sending service FIS" }, -}; - -/* - * ap->private_data - * - * The preview driver always returned 0 for status. We emulate it - * here from the previous interrupt. - */ -struct sil24_port_priv { - union sil24_cmd_block *cmd_block; /* 32 cmd blocks */ - dma_addr_t cmd_block_dma; /* DMA base addr for them */ - struct ata_taskfile tf; /* Cached taskfile registers */ -}; - -/* ap->host_set->private_data */ -struct sil24_host_priv { - void __iomem *host_base; /* global controller control (128 bytes @BAR0) */ - void __iomem *port_base; /* port registers (4 * 8192 bytes @BAR2) */ -}; - -static void sil24_dev_config(struct ata_port *ap, struct ata_device *dev); -static u8 sil24_check_status(struct ata_port *ap); -static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg); -static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val); -static void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf); -static void sil24_qc_prep(struct ata_queued_cmd *qc); -static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc); -static void sil24_irq_clear(struct ata_port *ap); -static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *regs); -static void sil24_freeze(struct ata_port *ap); -static void sil24_thaw(struct ata_port *ap); -static void sil24_error_handler(struct ata_port *ap); -static void sil24_post_internal_cmd(struct ata_queued_cmd *qc); -static int sil24_port_start(struct ata_port *ap); -static void sil24_port_stop(struct ata_port *ap); -static void sil24_host_stop(struct ata_host_set *host_set); -static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); -static int sil24_pci_device_resume(struct pci_dev *pdev); - -static const struct pci_device_id sil24_pci_tbl[] = { - { 0x1095, 0x3124, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3124 }, - { 0x8086, 0x3124, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3124 }, - { 0x1095, 0x3132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3132 }, - { 0x1095, 0x3131, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3131 }, - { 0x1095, 0x3531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3131 }, - { } /* terminate list */ -}; - -static struct pci_driver sil24_pci_driver = { - .name = DRV_NAME, - .id_table = sil24_pci_tbl, - .probe = sil24_init_one, - .remove = ata_pci_remove_one, /* safe? */ - .suspend = ata_pci_device_suspend, - .resume = sil24_pci_device_resume, -}; - -static struct scsi_host_template sil24_sht = { - .module = THIS_MODULE, - .name = DRV_NAME, - .ioctl = ata_scsi_ioctl, - .queuecommand = ata_scsi_queuecmd, - .change_queue_depth = ata_scsi_change_queue_depth, - .can_queue = SIL24_MAX_CMDS, - .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = LIBATA_MAX_PRD, - .cmd_per_lun = ATA_SHT_CMD_PER_LUN, - .emulated = ATA_SHT_EMULATED, - .use_clustering = ATA_SHT_USE_CLUSTERING, - .proc_name = DRV_NAME, - .dma_boundary = ATA_DMA_BOUNDARY, - .slave_configure = ata_scsi_slave_config, - .slave_destroy = ata_scsi_slave_destroy, - .bios_param = ata_std_bios_param, - .suspend = ata_scsi_device_suspend, - .resume = ata_scsi_device_resume, -}; - -static const struct ata_port_operations sil24_ops = { - .port_disable = ata_port_disable, - - .dev_config = sil24_dev_config, - - .check_status = sil24_check_status, - .check_altstatus = sil24_check_status, - .dev_select = ata_noop_dev_select, - - .tf_read = sil24_tf_read, - - .qc_prep = sil24_qc_prep, - .qc_issue = sil24_qc_issue, - - .irq_handler = sil24_interrupt, - .irq_clear = sil24_irq_clear, - - .scr_read = sil24_scr_read, - .scr_write = sil24_scr_write, - - .freeze = sil24_freeze, - .thaw = sil24_thaw, - .error_handler = sil24_error_handler, - .post_internal_cmd = sil24_post_internal_cmd, - - .port_start = sil24_port_start, - .port_stop = sil24_port_stop, - .host_stop = sil24_host_stop, -}; - -/* - * Use bits 30-31 of host_flags to encode available port numbers. - * Current maxium is 4. - */ -#define SIL24_NPORTS2FLAG(nports) ((((unsigned)(nports) - 1) & 0x3) << 30) -#define SIL24_FLAG2NPORTS(flag) ((((flag) >> 30) & 0x3) + 1) - -static struct ata_port_info sil24_port_info[] = { - /* sil_3124 */ - { - .sht = &sil24_sht, - .host_flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(4) | - SIL24_FLAG_PCIX_IRQ_WOC, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x3f, /* udma0-5 */ - .port_ops = &sil24_ops, - }, - /* sil_3132 */ - { - .sht = &sil24_sht, - .host_flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(2), - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x3f, /* udma0-5 */ - .port_ops = &sil24_ops, - }, - /* sil_3131/sil_3531 */ - { - .sht = &sil24_sht, - .host_flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(1), - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x3f, /* udma0-5 */ - .port_ops = &sil24_ops, - }, -}; - -static int sil24_tag(int tag) -{ - if (unlikely(ata_tag_internal(tag))) - return 0; - return tag; -} - -static void sil24_dev_config(struct ata_port *ap, struct ata_device *dev) -{ - void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; - - if (dev->cdb_len == 16) - writel(PORT_CS_CDB16, port + PORT_CTRL_STAT); - else - writel(PORT_CS_CDB16, port + PORT_CTRL_CLR); -} - -static inline void sil24_update_tf(struct ata_port *ap) -{ - struct sil24_port_priv *pp = ap->private_data; - void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; - struct sil24_prb __iomem *prb = port; - u8 fis[6 * 4]; - - memcpy_fromio(fis, prb->fis, 6 * 4); - ata_tf_from_fis(fis, &pp->tf); -} - -static u8 sil24_check_status(struct ata_port *ap) -{ - struct sil24_port_priv *pp = ap->private_data; - return pp->tf.command; -} - -static int sil24_scr_map[] = { - [SCR_CONTROL] = 0, - [SCR_STATUS] = 1, - [SCR_ERROR] = 2, - [SCR_ACTIVE] = 3, -}; - -static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg) -{ - void __iomem *scr_addr = (void __iomem *)ap->ioaddr.scr_addr; - if (sc_reg < ARRAY_SIZE(sil24_scr_map)) { - void __iomem *addr; - addr = scr_addr + sil24_scr_map[sc_reg] * 4; - return readl(scr_addr + sil24_scr_map[sc_reg] * 4); - } - return 0xffffffffU; -} - -static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val) -{ - void __iomem *scr_addr = (void __iomem *)ap->ioaddr.scr_addr; - if (sc_reg < ARRAY_SIZE(sil24_scr_map)) { - void __iomem *addr; - addr = scr_addr + sil24_scr_map[sc_reg] * 4; - writel(val, scr_addr + sil24_scr_map[sc_reg] * 4); - } -} - -static void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf) -{ - struct sil24_port_priv *pp = ap->private_data; - *tf = pp->tf; -} - -static int sil24_init_port(struct ata_port *ap) -{ - void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; - u32 tmp; - - writel(PORT_CS_INIT, port + PORT_CTRL_STAT); - ata_wait_register(port + PORT_CTRL_STAT, - PORT_CS_INIT, PORT_CS_INIT, 10, 100); - tmp = ata_wait_register(port + PORT_CTRL_STAT, - PORT_CS_RDY, 0, 10, 100); - - if ((tmp & (PORT_CS_INIT | PORT_CS_RDY)) != PORT_CS_RDY) - return -EIO; - return 0; -} - -static int sil24_softreset(struct ata_port *ap, unsigned int *class) -{ - void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; - struct sil24_port_priv *pp = ap->private_data; - struct sil24_prb *prb = &pp->cmd_block[0].ata.prb; - dma_addr_t paddr = pp->cmd_block_dma; - u32 mask, irq_stat; - const char *reason; - - DPRINTK("ENTER\n"); - - if (ata_port_offline(ap)) { - DPRINTK("PHY reports no device\n"); - *class = ATA_DEV_NONE; - goto out; - } - - /* put the port into known state */ - if (sil24_init_port(ap)) { - reason ="port not ready"; - goto err; - } - - /* do SRST */ - prb->ctrl = cpu_to_le16(PRB_CTRL_SRST); - prb->fis[1] = 0; /* no PM yet */ - - writel((u32)paddr, port + PORT_CMD_ACTIVATE); - writel((u64)paddr >> 32, port + PORT_CMD_ACTIVATE + 4); - - mask = (PORT_IRQ_COMPLETE | PORT_IRQ_ERROR) << PORT_IRQ_RAW_SHIFT; - irq_stat = ata_wait_register(port + PORT_IRQ_STAT, mask, 0x0, - 100, ATA_TMOUT_BOOT / HZ * 1000); - - writel(irq_stat, port + PORT_IRQ_STAT); /* clear IRQs */ - irq_stat >>= PORT_IRQ_RAW_SHIFT; - - if (!(irq_stat & PORT_IRQ_COMPLETE)) { - if (irq_stat & PORT_IRQ_ERROR) - reason = "SRST command error"; - else - reason = "timeout"; - goto err; - } - - sil24_update_tf(ap); - *class = ata_dev_classify(&pp->tf); - - if (*class == ATA_DEV_UNKNOWN) - *class = ATA_DEV_NONE; - - out: - DPRINTK("EXIT, class=%u\n", *class); - return 0; - - err: - ata_port_printk(ap, KERN_ERR, "softreset failed (%s)\n", reason); - return -EIO; -} - -static int sil24_hardreset(struct ata_port *ap, unsigned int *class) -{ - void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; - const char *reason; - int tout_msec, rc; - u32 tmp; - - /* sil24 does the right thing(tm) without any protection */ - sata_set_spd(ap); - - tout_msec = 100; - if (ata_port_online(ap)) - tout_msec = 5000; - - writel(PORT_CS_DEV_RST, port + PORT_CTRL_STAT); - tmp = ata_wait_register(port + PORT_CTRL_STAT, - PORT_CS_DEV_RST, PORT_CS_DEV_RST, 10, tout_msec); - - /* SStatus oscillates between zero and valid status after - * DEV_RST, debounce it. - */ - rc = sata_phy_debounce(ap, sata_deb_timing_long); - if (rc) { - reason = "PHY debouncing failed"; - goto err; - } - - if (tmp & PORT_CS_DEV_RST) { - if (ata_port_offline(ap)) - return 0; - reason = "link not ready"; - goto err; - } - - /* Sil24 doesn't store signature FIS after hardreset, so we - * can't wait for BSY to clear. Some devices take a long time - * to get ready and those devices will choke if we don't wait - * for BSY clearance here. Tell libata to perform follow-up - * softreset. - */ - return -EAGAIN; - - err: - ata_port_printk(ap, KERN_ERR, "hardreset failed (%s)\n", reason); - return -EIO; -} - -static inline void sil24_fill_sg(struct ata_queued_cmd *qc, - struct sil24_sge *sge) -{ - struct scatterlist *sg; - unsigned int idx = 0; - - ata_for_each_sg(sg, qc) { - sge->addr = cpu_to_le64(sg_dma_address(sg)); - sge->cnt = cpu_to_le32(sg_dma_len(sg)); - if (ata_sg_is_last(sg, qc)) - sge->flags = cpu_to_le32(SGE_TRM); - else - sge->flags = 0; - - sge++; - idx++; - } -} - -static void sil24_qc_prep(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct sil24_port_priv *pp = ap->private_data; - union sil24_cmd_block *cb; - struct sil24_prb *prb; - struct sil24_sge *sge; - u16 ctrl = 0; - - cb = &pp->cmd_block[sil24_tag(qc->tag)]; - - switch (qc->tf.protocol) { - case ATA_PROT_PIO: - case ATA_PROT_DMA: - case ATA_PROT_NCQ: - case ATA_PROT_NODATA: - prb = &cb->ata.prb; - sge = cb->ata.sge; - break; - - case ATA_PROT_ATAPI: - case ATA_PROT_ATAPI_DMA: - case ATA_PROT_ATAPI_NODATA: - prb = &cb->atapi.prb; - sge = cb->atapi.sge; - memset(cb->atapi.cdb, 0, 32); - memcpy(cb->atapi.cdb, qc->cdb, qc->dev->cdb_len); - - if (qc->tf.protocol != ATA_PROT_ATAPI_NODATA) { - if (qc->tf.flags & ATA_TFLAG_WRITE) - ctrl = PRB_CTRL_PACKET_WRITE; - else - ctrl = PRB_CTRL_PACKET_READ; - } - break; - - default: - prb = NULL; /* shut up, gcc */ - sge = NULL; - BUG(); - } - - prb->ctrl = cpu_to_le16(ctrl); - ata_tf_to_fis(&qc->tf, prb->fis, 0); - - if (qc->flags & ATA_QCFLAG_DMAMAP) - sil24_fill_sg(qc, sge); -} - -static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct sil24_port_priv *pp = ap->private_data; - void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; - unsigned int tag = sil24_tag(qc->tag); - dma_addr_t paddr; - void __iomem *activate; - - paddr = pp->cmd_block_dma + tag * sizeof(*pp->cmd_block); - activate = port + PORT_CMD_ACTIVATE + tag * 8; - - writel((u32)paddr, activate); - writel((u64)paddr >> 32, activate + 4); - - return 0; -} - -static void sil24_irq_clear(struct ata_port *ap) -{ - /* unused */ -} - -static void sil24_freeze(struct ata_port *ap) -{ - void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; - - /* Port-wide IRQ mask in HOST_CTRL doesn't really work, clear - * PORT_IRQ_ENABLE instead. - */ - writel(0xffff, port + PORT_IRQ_ENABLE_CLR); -} - -static void sil24_thaw(struct ata_port *ap) -{ - void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; - u32 tmp; - - /* clear IRQ */ - tmp = readl(port + PORT_IRQ_STAT); - writel(tmp, port + PORT_IRQ_STAT); - - /* turn IRQ back on */ - writel(DEF_PORT_IRQ, port + PORT_IRQ_ENABLE_SET); -} - -static void sil24_error_intr(struct ata_port *ap) -{ - void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; - struct ata_eh_info *ehi = &ap->eh_info; - int freeze = 0; - u32 irq_stat; - - /* on error, we need to clear IRQ explicitly */ - irq_stat = readl(port + PORT_IRQ_STAT); - writel(irq_stat, port + PORT_IRQ_STAT); - - /* first, analyze and record host port events */ - ata_ehi_clear_desc(ehi); - - ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat); - - if (irq_stat & (PORT_IRQ_PHYRDY_CHG | PORT_IRQ_DEV_XCHG)) { - ata_ehi_hotplugged(ehi); - ata_ehi_push_desc(ehi, ", %s", - irq_stat & PORT_IRQ_PHYRDY_CHG ? - "PHY RDY changed" : "device exchanged"); - freeze = 1; - } - - if (irq_stat & PORT_IRQ_UNK_FIS) { - ehi->err_mask |= AC_ERR_HSM; - ehi->action |= ATA_EH_SOFTRESET; - ata_ehi_push_desc(ehi , ", unknown FIS"); - freeze = 1; - } - - /* deal with command error */ - if (irq_stat & PORT_IRQ_ERROR) { - struct sil24_cerr_info *ci = NULL; - unsigned int err_mask = 0, action = 0; - struct ata_queued_cmd *qc; - u32 cerr; - - /* analyze CMD_ERR */ - cerr = readl(port + PORT_CMD_ERR); - if (cerr < ARRAY_SIZE(sil24_cerr_db)) - ci = &sil24_cerr_db[cerr]; - - if (ci && ci->desc) { - err_mask |= ci->err_mask; - action |= ci->action; - ata_ehi_push_desc(ehi, ", %s", ci->desc); - } else { - err_mask |= AC_ERR_OTHER; - action |= ATA_EH_SOFTRESET; - ata_ehi_push_desc(ehi, ", unknown command error %d", - cerr); - } - - /* record error info */ - qc = ata_qc_from_tag(ap, ap->active_tag); - if (qc) { - sil24_update_tf(ap); - qc->err_mask |= err_mask; - } else - ehi->err_mask |= err_mask; - - ehi->action |= action; - } - - /* freeze or abort */ - if (freeze) - ata_port_freeze(ap); - else - ata_port_abort(ap); -} - -static void sil24_finish_qc(struct ata_queued_cmd *qc) -{ - if (qc->flags & ATA_QCFLAG_RESULT_TF) - sil24_update_tf(qc->ap); -} - -static inline void sil24_host_intr(struct ata_port *ap) -{ - void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; - u32 slot_stat, qc_active; - int rc; - - slot_stat = readl(port + PORT_SLOT_STAT); - - if (unlikely(slot_stat & HOST_SSTAT_ATTN)) { - sil24_error_intr(ap); - return; - } - - if (ap->flags & SIL24_FLAG_PCIX_IRQ_WOC) - writel(PORT_IRQ_COMPLETE, port + PORT_IRQ_STAT); - - qc_active = slot_stat & ~HOST_SSTAT_ATTN; - rc = ata_qc_complete_multiple(ap, qc_active, sil24_finish_qc); - if (rc > 0) - return; - if (rc < 0) { - struct ata_eh_info *ehi = &ap->eh_info; - ehi->err_mask |= AC_ERR_HSM; - ehi->action |= ATA_EH_SOFTRESET; - ata_port_freeze(ap); - return; - } - - if (ata_ratelimit()) - ata_port_printk(ap, KERN_INFO, "spurious interrupt " - "(slot_stat 0x%x active_tag %d sactive 0x%x)\n", - slot_stat, ap->active_tag, ap->sactive); -} - -static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *regs) -{ - struct ata_host_set *host_set = dev_instance; - struct sil24_host_priv *hpriv = host_set->private_data; - unsigned handled = 0; - u32 status; - int i; - - status = readl(hpriv->host_base + HOST_IRQ_STAT); - - if (status == 0xffffffff) { - printk(KERN_ERR DRV_NAME ": IRQ status == 0xffffffff, " - "PCI fault or device removal?\n"); - goto out; - } - - if (!(status & IRQ_STAT_4PORTS)) - goto out; - - spin_lock(&host_set->lock); - - for (i = 0; i < host_set->n_ports; i++) - if (status & (1 << i)) { - struct ata_port *ap = host_set->ports[i]; - if (ap && !(ap->flags & ATA_FLAG_DISABLED)) { - sil24_host_intr(host_set->ports[i]); - handled++; - } else - printk(KERN_ERR DRV_NAME - ": interrupt from disabled port %d\n", i); - } - - spin_unlock(&host_set->lock); - out: - return IRQ_RETVAL(handled); -} - -static void sil24_error_handler(struct ata_port *ap) -{ - struct ata_eh_context *ehc = &ap->eh_context; - - if (sil24_init_port(ap)) { - ata_eh_freeze_port(ap); - ehc->i.action |= ATA_EH_HARDRESET; - } - - /* perform recovery */ - ata_do_eh(ap, ata_std_prereset, sil24_softreset, sil24_hardreset, - ata_std_postreset); -} - -static void sil24_post_internal_cmd(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - - if (qc->flags & ATA_QCFLAG_FAILED) - qc->err_mask |= AC_ERR_OTHER; - - /* make DMA engine forget about the failed command */ - if (qc->err_mask) - sil24_init_port(ap); -} - -static inline void sil24_cblk_free(struct sil24_port_priv *pp, struct device *dev) -{ - const size_t cb_size = sizeof(*pp->cmd_block) * SIL24_MAX_CMDS; - - dma_free_coherent(dev, cb_size, pp->cmd_block, pp->cmd_block_dma); -} - -static int sil24_port_start(struct ata_port *ap) -{ - struct device *dev = ap->host_set->dev; - struct sil24_port_priv *pp; - union sil24_cmd_block *cb; - size_t cb_size = sizeof(*cb) * SIL24_MAX_CMDS; - dma_addr_t cb_dma; - int rc = -ENOMEM; - - pp = kzalloc(sizeof(*pp), GFP_KERNEL); - if (!pp) - goto err_out; - - pp->tf.command = ATA_DRDY; - - cb = dma_alloc_coherent(dev, cb_size, &cb_dma, GFP_KERNEL); - if (!cb) - goto err_out_pp; - memset(cb, 0, cb_size); - - rc = ata_pad_alloc(ap, dev); - if (rc) - goto err_out_pad; - - pp->cmd_block = cb; - pp->cmd_block_dma = cb_dma; - - ap->private_data = pp; - - return 0; - -err_out_pad: - sil24_cblk_free(pp, dev); -err_out_pp: - kfree(pp); -err_out: - return rc; -} - -static void sil24_port_stop(struct ata_port *ap) -{ - struct device *dev = ap->host_set->dev; - struct sil24_port_priv *pp = ap->private_data; - - sil24_cblk_free(pp, dev); - ata_pad_free(ap, dev); - kfree(pp); -} - -static void sil24_host_stop(struct ata_host_set *host_set) -{ - struct sil24_host_priv *hpriv = host_set->private_data; - struct pci_dev *pdev = to_pci_dev(host_set->dev); - - pci_iounmap(pdev, hpriv->host_base); - pci_iounmap(pdev, hpriv->port_base); - kfree(hpriv); -} - -static void sil24_init_controller(struct pci_dev *pdev, int n_ports, - unsigned long host_flags, - void __iomem *host_base, - void __iomem *port_base) -{ - u32 tmp; - int i; - - /* GPIO off */ - writel(0, host_base + HOST_FLASH_CMD); - - /* clear global reset & mask interrupts during initialization */ - writel(0, host_base + HOST_CTRL); - - /* init ports */ - for (i = 0; i < n_ports; i++) { - void __iomem *port = port_base + i * PORT_REGS_SIZE; - - /* Initial PHY setting */ - writel(0x20c, port + PORT_PHY_CFG); - - /* Clear port RST */ - tmp = readl(port + PORT_CTRL_STAT); - if (tmp & PORT_CS_PORT_RST) { - writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR); - tmp = ata_wait_register(port + PORT_CTRL_STAT, - PORT_CS_PORT_RST, - PORT_CS_PORT_RST, 10, 100); - if (tmp & PORT_CS_PORT_RST) - dev_printk(KERN_ERR, &pdev->dev, - "failed to clear port RST\n"); - } - - /* Configure IRQ WoC */ - if (host_flags & SIL24_FLAG_PCIX_IRQ_WOC) - writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT); - else - writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR); - - /* Zero error counters. */ - writel(0x8000, port + PORT_DECODE_ERR_THRESH); - writel(0x8000, port + PORT_CRC_ERR_THRESH); - writel(0x8000, port + PORT_HSHK_ERR_THRESH); - writel(0x0000, port + PORT_DECODE_ERR_CNT); - writel(0x0000, port + PORT_CRC_ERR_CNT); - writel(0x0000, port + PORT_HSHK_ERR_CNT); - - /* Always use 64bit activation */ - writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR); - - /* Clear port multiplier enable and resume bits */ - writel(PORT_CS_PM_EN | PORT_CS_RESUME, port + PORT_CTRL_CLR); - } - - /* Turn on interrupts */ - writel(IRQ_STAT_4PORTS, host_base + HOST_CTRL); -} - -static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - static int printed_version = 0; - unsigned int board_id = (unsigned int)ent->driver_data; - struct ata_port_info *pinfo = &sil24_port_info[board_id]; - struct ata_probe_ent *probe_ent = NULL; - struct sil24_host_priv *hpriv = NULL; - void __iomem *host_base = NULL; - void __iomem *port_base = NULL; - int i, rc; - u32 tmp; - - if (!printed_version++) - dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); - - rc = pci_enable_device(pdev); - if (rc) - return rc; - - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) - goto out_disable; - - rc = -ENOMEM; - /* map mmio registers */ - host_base = pci_iomap(pdev, 0, 0); - if (!host_base) - goto out_free; - port_base = pci_iomap(pdev, 2, 0); - if (!port_base) - goto out_free; - - /* allocate & init probe_ent and hpriv */ - probe_ent = kzalloc(sizeof(*probe_ent), GFP_KERNEL); - if (!probe_ent) - goto out_free; - - hpriv = kzalloc(sizeof(*hpriv), GFP_KERNEL); - if (!hpriv) - goto out_free; - - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - probe_ent->sht = pinfo->sht; - probe_ent->host_flags = pinfo->host_flags; - probe_ent->pio_mask = pinfo->pio_mask; - probe_ent->mwdma_mask = pinfo->mwdma_mask; - probe_ent->udma_mask = pinfo->udma_mask; - probe_ent->port_ops = pinfo->port_ops; - probe_ent->n_ports = SIL24_FLAG2NPORTS(pinfo->host_flags); - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->private_data = hpriv; - - hpriv->host_base = host_base; - hpriv->port_base = port_base; - - /* - * Configure the device - */ - 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"); - goto out_free; - } - } - } else { - rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); - if (rc) { - dev_printk(KERN_ERR, &pdev->dev, - "32-bit DMA enable failed\n"); - goto out_free; - } - 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"); - goto out_free; - } - } - - /* Apply workaround for completion IRQ loss on PCI-X errata */ - if (probe_ent->host_flags & SIL24_FLAG_PCIX_IRQ_WOC) { - tmp = readl(host_base + HOST_CTRL); - if (tmp & (HOST_CTRL_TRDY | HOST_CTRL_STOP | HOST_CTRL_DEVSEL)) - dev_printk(KERN_INFO, &pdev->dev, - "Applying completion IRQ loss on PCI-X " - "errata fix\n"); - else - probe_ent->host_flags &= ~SIL24_FLAG_PCIX_IRQ_WOC; - } - - for (i = 0; i < probe_ent->n_ports; i++) { - unsigned long portu = - (unsigned long)port_base + i * PORT_REGS_SIZE; - - probe_ent->port[i].cmd_addr = portu; - probe_ent->port[i].scr_addr = portu + PORT_SCONTROL; - - ata_std_ports(&probe_ent->port[i]); - } - - sil24_init_controller(pdev, probe_ent->n_ports, probe_ent->host_flags, - host_base, port_base); - - pci_set_master(pdev); - - /* FIXME: check ata_device_add return value */ - ata_device_add(probe_ent); - - kfree(probe_ent); - return 0; - - out_free: - if (host_base) - pci_iounmap(pdev, host_base); - if (port_base) - pci_iounmap(pdev, port_base); - kfree(probe_ent); - kfree(hpriv); - pci_release_regions(pdev); - out_disable: - pci_disable_device(pdev); - return rc; -} - -static int sil24_pci_device_resume(struct pci_dev *pdev) -{ - struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev); - struct sil24_host_priv *hpriv = host_set->private_data; - - ata_pci_device_do_resume(pdev); - - if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) - writel(HOST_CTRL_GLOBAL_RST, hpriv->host_base + HOST_CTRL); - - sil24_init_controller(pdev, host_set->n_ports, - host_set->ports[0]->flags, - hpriv->host_base, hpriv->port_base); - - ata_host_set_resume(host_set); - - return 0; -} - -static int __init sil24_init(void) -{ - return pci_register_driver(&sil24_pci_driver); -} - -static void __exit sil24_exit(void) -{ - pci_unregister_driver(&sil24_pci_driver); -} - -MODULE_AUTHOR("Tejun Heo"); -MODULE_DESCRIPTION("Silicon Image 3124/3132 SATA low-level driver"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, sil24_pci_tbl); - -module_init(sil24_init); -module_exit(sil24_exit); diff --git a/drivers/scsi/sata_sis.c b/drivers/scsi/sata_sis.c deleted file mode 100644 index ac24f66897f..00000000000 --- a/drivers/scsi/sata_sis.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - * sata_sis.c - Silicon Integrated Systems SATA - * - * Maintained by: Uwe Koziolek - * Please ALWAYS copy linux-ide@vger.kernel.org - * on emails. - * - * Copyright 2004 Uwe Koziolek - * - * - * 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 02139, USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - * Hardware documentation available under NDA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "sata_sis" -#define DRV_VERSION "0.6" - -enum { - sis_180 = 0, - SIS_SCR_PCI_BAR = 5, - - /* PCI configuration registers */ - SIS_GENCTL = 0x54, /* IDE General Control register */ - SIS_SCR_BASE = 0xc0, /* sata0 phy SCR registers */ - SIS180_SATA1_OFS = 0x10, /* offset from sata0->sata1 phy regs */ - SIS182_SATA1_OFS = 0x20, /* offset from sata0->sata1 phy regs */ - SIS_PMR = 0x90, /* port mapping register */ - SIS_PMR_COMBINED = 0x30, - - /* random bits */ - SIS_FLAG_CFGSCR = (1 << 30), /* host flag: SCRs via PCI cfg */ - - GENCTL_IOMAPPED_SCR = (1 << 26), /* if set, SCRs are in IO space */ -}; - -static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static u32 sis_scr_read (struct ata_port *ap, unsigned int sc_reg); -static void sis_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); - -static const struct pci_device_id sis_pci_tbl[] = { - { PCI_VENDOR_ID_SI, 0x180, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 }, - { PCI_VENDOR_ID_SI, 0x181, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 }, - { PCI_VENDOR_ID_SI, 0x182, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 }, - { } /* terminate list */ -}; - - -static struct pci_driver sis_pci_driver = { - .name = DRV_NAME, - .id_table = sis_pci_tbl, - .probe = sis_init_one, - .remove = ata_pci_remove_one, -}; - -static struct scsi_host_template sis_sht = { - .module = THIS_MODULE, - .name = DRV_NAME, - .ioctl = ata_scsi_ioctl, - .queuecommand = ata_scsi_queuecmd, - .can_queue = ATA_DEF_QUEUE, - .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = ATA_MAX_PRD, - .cmd_per_lun = ATA_SHT_CMD_PER_LUN, - .emulated = ATA_SHT_EMULATED, - .use_clustering = ATA_SHT_USE_CLUSTERING, - .proc_name = DRV_NAME, - .dma_boundary = ATA_DMA_BOUNDARY, - .slave_configure = ata_scsi_slave_config, - .slave_destroy = ata_scsi_slave_destroy, - .bios_param = ata_std_bios_param, -}; - -static const struct ata_port_operations sis_ops = { - .port_disable = ata_port_disable, - .tf_load = ata_tf_load, - .tf_read = ata_tf_read, - .check_status = ata_check_status, - .exec_command = ata_exec_command, - .dev_select = ata_std_dev_select, - .bmdma_setup = ata_bmdma_setup, - .bmdma_start = ata_bmdma_start, - .bmdma_stop = ata_bmdma_stop, - .bmdma_status = ata_bmdma_status, - .qc_prep = ata_qc_prep, - .qc_issue = ata_qc_issue_prot, - .data_xfer = ata_pio_data_xfer, - .freeze = ata_bmdma_freeze, - .thaw = ata_bmdma_thaw, - .error_handler = ata_bmdma_error_handler, - .post_internal_cmd = ata_bmdma_post_internal_cmd, - .irq_handler = ata_interrupt, - .irq_clear = ata_bmdma_irq_clear, - .scr_read = sis_scr_read, - .scr_write = sis_scr_write, - .port_start = ata_port_start, - .port_stop = ata_port_stop, - .host_stop = ata_host_stop, -}; - -static struct ata_port_info sis_port_info = { - .sht = &sis_sht, - .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, - .pio_mask = 0x1f, - .mwdma_mask = 0x7, - .udma_mask = 0x7f, - .port_ops = &sis_ops, -}; - - -MODULE_AUTHOR("Uwe Koziolek"); -MODULE_DESCRIPTION("low-level driver for Silicon Integratad Systems SATA controller"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, sis_pci_tbl); -MODULE_VERSION(DRV_VERSION); - -static unsigned int get_scr_cfg_addr(unsigned int port_no, unsigned int sc_reg, int device) -{ - unsigned int addr = SIS_SCR_BASE + (4 * sc_reg); - - if (port_no) { - if (device == 0x182) - addr += SIS182_SATA1_OFS; - else - addr += SIS180_SATA1_OFS; - } - - return addr; -} - -static u32 sis_scr_cfg_read (struct ata_port *ap, unsigned int sc_reg) -{ - struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); - unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, sc_reg, pdev->device); - u32 val, val2 = 0; - u8 pmr; - - if (sc_reg == SCR_ERROR) /* doesn't exist in PCI cfg space */ - return 0xffffffff; - - pci_read_config_byte(pdev, SIS_PMR, &pmr); - - pci_read_config_dword(pdev, cfg_addr, &val); - - if ((pdev->device == 0x182) || (pmr & SIS_PMR_COMBINED)) - pci_read_config_dword(pdev, cfg_addr+0x10, &val2); - - return val|val2; -} - -static void sis_scr_cfg_write (struct ata_port *ap, unsigned int scr, u32 val) -{ - struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); - unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, scr, pdev->device); - u8 pmr; - - if (scr == SCR_ERROR) /* doesn't exist in PCI cfg space */ - return; - - pci_read_config_byte(pdev, SIS_PMR, &pmr); - - pci_write_config_dword(pdev, cfg_addr, val); - - if ((pdev->device == 0x182) || (pmr & SIS_PMR_COMBINED)) - pci_write_config_dword(pdev, cfg_addr+0x10, val); -} - -static u32 sis_scr_read (struct ata_port *ap, unsigned int sc_reg) -{ - struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); - u32 val, val2 = 0; - u8 pmr; - - if (sc_reg > SCR_CONTROL) - return 0xffffffffU; - - if (ap->flags & SIS_FLAG_CFGSCR) - return sis_scr_cfg_read(ap, sc_reg); - - pci_read_config_byte(pdev, SIS_PMR, &pmr); - - val = inl(ap->ioaddr.scr_addr + (sc_reg * 4)); - - if ((pdev->device == 0x182) || (pmr & SIS_PMR_COMBINED)) - val2 = inl(ap->ioaddr.scr_addr + (sc_reg * 4) + 0x10); - - return val | val2; -} - -static void sis_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) -{ - struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); - u8 pmr; - - if (sc_reg > SCR_CONTROL) - return; - - pci_read_config_byte(pdev, SIS_PMR, &pmr); - - if (ap->flags & SIS_FLAG_CFGSCR) - sis_scr_cfg_write(ap, sc_reg, val); - else { - outl(val, ap->ioaddr.scr_addr + (sc_reg * 4)); - if ((pdev->device == 0x182) || (pmr & SIS_PMR_COMBINED)) - outl(val, ap->ioaddr.scr_addr + (sc_reg * 4)+0x10); - } -} - -static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) -{ - static int printed_version; - struct ata_probe_ent *probe_ent = NULL; - int rc; - u32 genctl; - struct ata_port_info *ppi; - int pci_dev_busy = 0; - u8 pmr; - u8 port2_start; - - if (!printed_version++) - dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n"); - - rc = pci_enable_device(pdev); - if (rc) - return rc; - - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pci_dev_busy = 1; - goto err_out; - } - - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out_regions; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out_regions; - - ppi = &sis_port_info; - probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); - if (!probe_ent) { - rc = -ENOMEM; - goto err_out_regions; - } - - /* check and see if the SCRs are in IO space or PCI cfg space */ - pci_read_config_dword(pdev, SIS_GENCTL, &genctl); - if ((genctl & GENCTL_IOMAPPED_SCR) == 0) - probe_ent->host_flags |= SIS_FLAG_CFGSCR; - - /* if hardware thinks SCRs are in IO space, but there are - * no IO resources assigned, change to PCI cfg space. - */ - if ((!(probe_ent->host_flags & SIS_FLAG_CFGSCR)) && - ((pci_resource_start(pdev, SIS_SCR_PCI_BAR) == 0) || - (pci_resource_len(pdev, SIS_SCR_PCI_BAR) < 128))) { - genctl &= ~GENCTL_IOMAPPED_SCR; - pci_write_config_dword(pdev, SIS_GENCTL, genctl); - probe_ent->host_flags |= SIS_FLAG_CFGSCR; - } - - pci_read_config_byte(pdev, SIS_PMR, &pmr); - if (ent->device != 0x182) { - if ((pmr & SIS_PMR_COMBINED) == 0) { - dev_printk(KERN_INFO, &pdev->dev, - "Detected SiS 180/181 chipset in SATA mode\n"); - port2_start = 64; - } - else { - dev_printk(KERN_INFO, &pdev->dev, - "Detected SiS 180/181 chipset in combined mode\n"); - port2_start=0; - } - } - else { - dev_printk(KERN_INFO, &pdev->dev, "Detected SiS 182 chipset\n"); - port2_start = 0x20; - } - - if (!(probe_ent->host_flags & SIS_FLAG_CFGSCR)) { - probe_ent->port[0].scr_addr = - pci_resource_start(pdev, SIS_SCR_PCI_BAR); - probe_ent->port[1].scr_addr = - pci_resource_start(pdev, SIS_SCR_PCI_BAR) + port2_start; - } - - pci_set_master(pdev); - pci_intx(pdev, 1); - - /* FIXME: check ata_device_add return value */ - ata_device_add(probe_ent); - kfree(probe_ent); - - return 0; - -err_out_regions: - pci_release_regions(pdev); - -err_out: - if (!pci_dev_busy) - pci_disable_device(pdev); - return rc; - -} - -static int __init sis_init(void) -{ - return pci_register_driver(&sis_pci_driver); -} - -static void __exit sis_exit(void) -{ - pci_unregister_driver(&sis_pci_driver); -} - -module_init(sis_init); -module_exit(sis_exit); - diff --git a/drivers/scsi/sata_svw.c b/drivers/scsi/sata_svw.c deleted file mode 100644 index baf259a966d..00000000000 --- a/drivers/scsi/sata_svw.c +++ /dev/null @@ -1,508 +0,0 @@ -/* - * sata_svw.c - ServerWorks / Apple K2 SATA - * - * Maintained by: Benjamin Herrenschmidt and - * Jeff Garzik - * Please ALWAYS copy linux-ide@vger.kernel.org - * on emails. - * - * Copyright 2003 Benjamin Herrenschmidt - * - * Bits from Jeff Garzik, Copyright RedHat, Inc. - * - * This driver probably works with non-Apple versions of the - * Broadcom chipset... - * - * - * 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 02139, USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - * Hardware documentation available under NDA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_PPC_OF -#include -#include -#endif /* CONFIG_PPC_OF */ - -#define DRV_NAME "sata_svw" -#define DRV_VERSION "2.0" - -enum { - /* Taskfile registers offsets */ - K2_SATA_TF_CMD_OFFSET = 0x00, - K2_SATA_TF_DATA_OFFSET = 0x00, - K2_SATA_TF_ERROR_OFFSET = 0x04, - K2_SATA_TF_NSECT_OFFSET = 0x08, - K2_SATA_TF_LBAL_OFFSET = 0x0c, - K2_SATA_TF_LBAM_OFFSET = 0x10, - K2_SATA_TF_LBAH_OFFSET = 0x14, - K2_SATA_TF_DEVICE_OFFSET = 0x18, - K2_SATA_TF_CMDSTAT_OFFSET = 0x1c, - K2_SATA_TF_CTL_OFFSET = 0x20, - - /* DMA base */ - K2_SATA_DMA_CMD_OFFSET = 0x30, - - /* SCRs base */ - K2_SATA_SCR_STATUS_OFFSET = 0x40, - K2_SATA_SCR_ERROR_OFFSET = 0x44, - K2_SATA_SCR_CONTROL_OFFSET = 0x48, - - /* Others */ - K2_SATA_SICR1_OFFSET = 0x80, - K2_SATA_SICR2_OFFSET = 0x84, - K2_SATA_SIM_OFFSET = 0x88, - - /* Port stride */ - K2_SATA_PORT_OFFSET = 0x100, -}; - -static u8 k2_stat_check_status(struct ata_port *ap); - - -static u32 k2_sata_scr_read (struct ata_port *ap, unsigned int sc_reg) -{ - if (sc_reg > SCR_CONTROL) - return 0xffffffffU; - return readl((void *) ap->ioaddr.scr_addr + (sc_reg * 4)); -} - - -static void k2_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, - u32 val) -{ - if (sc_reg > SCR_CONTROL) - return; - writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4)); -} - - -static void k2_sata_tf_load(struct ata_port *ap, const struct ata_taskfile *tf) -{ - struct ata_ioports *ioaddr = &ap->ioaddr; - unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; - - if (tf->ctl != ap->last_ctl) { - writeb(tf->ctl, ioaddr->ctl_addr); - ap->last_ctl = tf->ctl; - ata_wait_idle(ap); - } - if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) { - writew(tf->feature | (((u16)tf->hob_feature) << 8), ioaddr->feature_addr); - writew(tf->nsect | (((u16)tf->hob_nsect) << 8), ioaddr->nsect_addr); - writew(tf->lbal | (((u16)tf->hob_lbal) << 8), ioaddr->lbal_addr); - writew(tf->lbam | (((u16)tf->hob_lbam) << 8), ioaddr->lbam_addr); - writew(tf->lbah | (((u16)tf->hob_lbah) << 8), ioaddr->lbah_addr); - } else if (is_addr) { - writew(tf->feature, ioaddr->feature_addr); - writew(tf->nsect, ioaddr->nsect_addr); - writew(tf->lbal, ioaddr->lbal_addr); - writew(tf->lbam, ioaddr->lbam_addr); - writew(tf->lbah, ioaddr->lbah_addr); - } - - if (tf->flags & ATA_TFLAG_DEVICE) - writeb(tf->device, ioaddr->device_addr); - - ata_wait_idle(ap); -} - - -static void k2_sata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) -{ - struct ata_ioports *ioaddr = &ap->ioaddr; - u16 nsect, lbal, lbam, lbah, feature; - - tf->command = k2_stat_check_status(ap); - tf->device = readw(ioaddr->device_addr); - feature = readw(ioaddr->error_addr); - nsect = readw(ioaddr->nsect_addr); - lbal = readw(ioaddr->lbal_addr); - lbam = readw(ioaddr->lbam_addr); - lbah = readw(ioaddr->lbah_addr); - - tf->feature = feature; - tf->nsect = nsect; - tf->lbal = lbal; - tf->lbam = lbam; - tf->lbah = lbah; - - if (tf->flags & ATA_TFLAG_LBA48) { - tf->hob_feature = feature >> 8; - tf->hob_nsect = nsect >> 8; - tf->hob_lbal = lbal >> 8; - tf->hob_lbam = lbam >> 8; - tf->hob_lbah = lbah >> 8; - } -} - -/** - * k2_bmdma_setup_mmio - Set up PCI IDE BMDMA transaction (MMIO) - * @qc: Info associated with this ATA transaction. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -static void k2_bmdma_setup_mmio (struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE); - u8 dmactl; - void *mmio = (void *) ap->ioaddr.bmdma_addr; - /* load PRD table addr. */ - mb(); /* make sure PRD table writes are visible to controller */ - writel(ap->prd_dma, mmio + ATA_DMA_TABLE_OFS); - - /* specify data direction, triple-check start bit is clear */ - dmactl = readb(mmio + ATA_DMA_CMD); - dmactl &= ~(ATA_DMA_WR | ATA_DMA_START); - if (!rw) - dmactl |= ATA_DMA_WR; - writeb(dmactl, mmio + ATA_DMA_CMD); - - /* issue r/w command if this is not a ATA DMA command*/ - if (qc->tf.protocol != ATA_PROT_DMA) - ap->ops->exec_command(ap, &qc->tf); -} - -/** - * k2_bmdma_start_mmio - Start a PCI IDE BMDMA transaction (MMIO) - * @qc: Info associated with this ATA transaction. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ - -static void k2_bmdma_start_mmio (struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - void *mmio = (void *) ap->ioaddr.bmdma_addr; - u8 dmactl; - - /* start host DMA transaction */ - dmactl = readb(mmio + ATA_DMA_CMD); - writeb(dmactl | ATA_DMA_START, mmio + ATA_DMA_CMD); - /* There is a race condition in certain SATA controllers that can - be seen when the r/w command is given to the controller before the - host DMA is started. On a Read command, the controller would initiate - the command to the drive even before it sees the DMA start. When there - are very fast drives connected to the controller, or when the data request - hits in the drive cache, there is the possibility that the drive returns a part - or all of the requested data to the controller before the DMA start is issued. - In this case, the controller would become confused as to what to do with the data. - In the worst case when all the data is returned back to the controller, the - controller could hang. In other cases it could return partial data returning - in data corruption. This problem has been seen in PPC systems and can also appear - on an system with very fast disks, where the SATA controller is sitting behind a - number of bridges, and hence there is significant latency between the r/w command - and the start command. */ - /* issue r/w command if the access is to ATA*/ - if (qc->tf.protocol == ATA_PROT_DMA) - ap->ops->exec_command(ap, &qc->tf); -} - - -static u8 k2_stat_check_status(struct ata_port *ap) -{ - return readl((void *) ap->ioaddr.status_addr); -} - -#ifdef CONFIG_PPC_OF -/* - * k2_sata_proc_info - * inout : decides on the direction of the dataflow and the meaning of the - * variables - * buffer: If inout==FALSE data is being written to it else read from it - * *start: If inout==FALSE start of the valid data in the buffer - * offset: If inout==FALSE offset from the beginning of the imaginary file - * from which we start writing into the buffer - * length: If inout==FALSE max number of bytes to be written into the buffer - * else number of bytes in the buffer - */ -static int k2_sata_proc_info(struct Scsi_Host *shost, char *page, char **start, - off_t offset, int count, int inout) -{ - struct ata_port *ap; - struct device_node *np; - int len, index; - - /* Find the ata_port */ - ap = ata_shost_to_port(shost); - if (ap == NULL) - return 0; - - /* Find the OF node for the PCI device proper */ - np = pci_device_to_OF_node(to_pci_dev(ap->host_set->dev)); - if (np == NULL) - return 0; - - /* Match it to a port node */ - index = (ap == ap->host_set->ports[0]) ? 0 : 1; - for (np = np->child; np != NULL; np = np->sibling) { - u32 *reg = (u32 *)get_property(np, "reg", NULL); - if (!reg) - continue; - if (index == *reg) - break; - } - if (np == NULL) - return 0; - - len = sprintf(page, "devspec: %s\n", np->full_name); - - return len; -} -#endif /* CONFIG_PPC_OF */ - - -static struct scsi_host_template k2_sata_sht = { - .module = THIS_MODULE, - .name = DRV_NAME, - .ioctl = ata_scsi_ioctl, - .queuecommand = ata_scsi_queuecmd, - .can_queue = ATA_DEF_QUEUE, - .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = LIBATA_MAX_PRD, - .cmd_per_lun = ATA_SHT_CMD_PER_LUN, - .emulated = ATA_SHT_EMULATED, - .use_clustering = ATA_SHT_USE_CLUSTERING, - .proc_name = DRV_NAME, - .dma_boundary = ATA_DMA_BOUNDARY, - .slave_configure = ata_scsi_slave_config, - .slave_destroy = ata_scsi_slave_destroy, -#ifdef CONFIG_PPC_OF - .proc_info = k2_sata_proc_info, -#endif - .bios_param = ata_std_bios_param, -}; - - -static const struct ata_port_operations k2_sata_ops = { - .port_disable = ata_port_disable, - .tf_load = k2_sata_tf_load, - .tf_read = k2_sata_tf_read, - .check_status = k2_stat_check_status, - .exec_command = ata_exec_command, - .dev_select = ata_std_dev_select, - .bmdma_setup = k2_bmdma_setup_mmio, - .bmdma_start = k2_bmdma_start_mmio, - .bmdma_stop = ata_bmdma_stop, - .bmdma_status = ata_bmdma_status, - .qc_prep = ata_qc_prep, - .qc_issue = ata_qc_issue_prot, - .data_xfer = ata_mmio_data_xfer, - .freeze = ata_bmdma_freeze, - .thaw = ata_bmdma_thaw, - .error_handler = ata_bmdma_error_handler, - .post_internal_cmd = ata_bmdma_post_internal_cmd, - .irq_handler = ata_interrupt, - .irq_clear = ata_bmdma_irq_clear, - .scr_read = k2_sata_scr_read, - .scr_write = k2_sata_scr_write, - .port_start = ata_port_start, - .port_stop = ata_port_stop, - .host_stop = ata_pci_host_stop, -}; - -static void k2_sata_setup_port(struct ata_ioports *port, unsigned long base) -{ - port->cmd_addr = base + K2_SATA_TF_CMD_OFFSET; - port->data_addr = base + K2_SATA_TF_DATA_OFFSET; - port->feature_addr = - port->error_addr = base + K2_SATA_TF_ERROR_OFFSET; - port->nsect_addr = base + K2_SATA_TF_NSECT_OFFSET; - port->lbal_addr = base + K2_SATA_TF_LBAL_OFFSET; - port->lbam_addr = base + K2_SATA_TF_LBAM_OFFSET; - port->lbah_addr = base + K2_SATA_TF_LBAH_OFFSET; - port->device_addr = base + K2_SATA_TF_DEVICE_OFFSET; - port->command_addr = - port->status_addr = base + K2_SATA_TF_CMDSTAT_OFFSET; - port->altstatus_addr = - port->ctl_addr = base + K2_SATA_TF_CTL_OFFSET; - port->bmdma_addr = base + K2_SATA_DMA_CMD_OFFSET; - port->scr_addr = base + K2_SATA_SCR_STATUS_OFFSET; -} - - -static int k2_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) -{ - static int printed_version; - struct ata_probe_ent *probe_ent = NULL; - unsigned long base; - void __iomem *mmio_base; - int pci_dev_busy = 0; - int rc; - int i; - - if (!printed_version++) - dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); - - /* - * If this driver happens to only be useful on Apple's K2, then - * we should check that here as it has a normal Serverworks ID - */ - rc = pci_enable_device(pdev); - if (rc) - return rc; - /* - * Check if we have resources mapped at all (second function may - * have been disabled by firmware) - */ - if (pci_resource_len(pdev, 5) == 0) - return -ENODEV; - - /* Request PCI regions */ - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pci_dev_busy = 1; - goto err_out; - } - - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out_regions; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out_regions; - - probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) { - rc = -ENOMEM; - goto err_out_regions; - } - - memset(probe_ent, 0, sizeof(*probe_ent)); - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - mmio_base = pci_iomap(pdev, 5, 0); - if (mmio_base == NULL) { - rc = -ENOMEM; - goto err_out_free_ent; - } - base = (unsigned long) mmio_base; - - /* Clear a magic bit in SCR1 according to Darwin, those help - * some funky seagate drives (though so far, those were already - * set by the firmware on the machines I had access to) - */ - writel(readl(mmio_base + K2_SATA_SICR1_OFFSET) & ~0x00040000, - mmio_base + K2_SATA_SICR1_OFFSET); - - /* Clear SATA error & interrupts we don't use */ - writel(0xffffffff, mmio_base + K2_SATA_SCR_ERROR_OFFSET); - writel(0x0, mmio_base + K2_SATA_SIM_OFFSET); - - probe_ent->sht = &k2_sata_sht; - probe_ent->host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO; - probe_ent->port_ops = &k2_sata_ops; - probe_ent->n_ports = 4; - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->mmio_base = mmio_base; - - /* We don't care much about the PIO/UDMA masks, but the core won't like us - * if we don't fill these - */ - probe_ent->pio_mask = 0x1f; - probe_ent->mwdma_mask = 0x7; - probe_ent->udma_mask = 0x7f; - - /* different controllers have different number of ports - currently 4 or 8 */ - /* All ports are on the same function. Multi-function device is no - * longer available. This should not be seen in any system. */ - for (i = 0; i < ent->driver_data; i++) - k2_sata_setup_port(&probe_ent->port[i], base + i * K2_SATA_PORT_OFFSET); - - pci_set_master(pdev); - - /* FIXME: check ata_device_add return value */ - ata_device_add(probe_ent); - kfree(probe_ent); - - return 0; - -err_out_free_ent: - kfree(probe_ent); -err_out_regions: - pci_release_regions(pdev); -err_out: - if (!pci_dev_busy) - pci_disable_device(pdev); - return rc; -} - -/* 0x240 is device ID for Apple K2 device - * 0x241 is device ID for Serverworks Frodo4 - * 0x242 is device ID for Serverworks Frodo8 - * 0x24a is device ID for BCM5785 (aka HT1000) HT southbridge integrated SATA - * controller - * */ -static const struct pci_device_id k2_sata_pci_tbl[] = { - { 0x1166, 0x0240, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, - { 0x1166, 0x0241, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, - { 0x1166, 0x0242, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, - { 0x1166, 0x024a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, - { 0x1166, 0x024b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, - { } -}; - - -static struct pci_driver k2_sata_pci_driver = { - .name = DRV_NAME, - .id_table = k2_sata_pci_tbl, - .probe = k2_sata_init_one, - .remove = ata_pci_remove_one, -}; - - -static int __init k2_sata_init(void) -{ - return pci_register_driver(&k2_sata_pci_driver); -} - - -static void __exit k2_sata_exit(void) -{ - pci_unregister_driver(&k2_sata_pci_driver); -} - - -MODULE_AUTHOR("Benjamin Herrenschmidt"); -MODULE_DESCRIPTION("low-level driver for K2 SATA controller"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, k2_sata_pci_tbl); -MODULE_VERSION(DRV_VERSION); - -module_init(k2_sata_init); -module_exit(k2_sata_exit); diff --git a/drivers/scsi/sata_sx4.c b/drivers/scsi/sata_sx4.c deleted file mode 100644 index 0da83cba5c1..00000000000 --- a/drivers/scsi/sata_sx4.c +++ /dev/null @@ -1,1502 +0,0 @@ -/* - * sata_sx4.c - Promise SATA - * - * Maintained by: Jeff Garzik - * Please ALWAYS copy linux-ide@vger.kernel.org - * on emails. - * - * Copyright 2003-2004 Red Hat, Inc. - * - * - * 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 02139, USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - * Hardware documentation available under NDA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sata_promise.h" - -#define DRV_NAME "sata_sx4" -#define DRV_VERSION "0.9" - - -enum { - PDC_PRD_TBL = 0x44, /* Direct command DMA table addr */ - - PDC_PKT_SUBMIT = 0x40, /* Command packet pointer addr */ - PDC_HDMA_PKT_SUBMIT = 0x100, /* Host DMA packet pointer addr */ - PDC_INT_SEQMASK = 0x40, /* Mask of asserted SEQ INTs */ - PDC_HDMA_CTLSTAT = 0x12C, /* Host DMA control / status */ - - PDC_20621_SEQCTL = 0x400, - PDC_20621_SEQMASK = 0x480, - PDC_20621_GENERAL_CTL = 0x484, - PDC_20621_PAGE_SIZE = (32 * 1024), - - /* chosen, not constant, values; we design our own DIMM mem map */ - PDC_20621_DIMM_WINDOW = 0x0C, /* page# for 32K DIMM window */ - PDC_20621_DIMM_BASE = 0x00200000, - PDC_20621_DIMM_DATA = (64 * 1024), - PDC_DIMM_DATA_STEP = (256 * 1024), - PDC_DIMM_WINDOW_STEP = (8 * 1024), - PDC_DIMM_HOST_PRD = (6 * 1024), - PDC_DIMM_HOST_PKT = (128 * 0), - PDC_DIMM_HPKT_PRD = (128 * 1), - PDC_DIMM_ATA_PKT = (128 * 2), - PDC_DIMM_APKT_PRD = (128 * 3), - PDC_DIMM_HEADER_SZ = PDC_DIMM_APKT_PRD + 128, - PDC_PAGE_WINDOW = 0x40, - PDC_PAGE_DATA = PDC_PAGE_WINDOW + - (PDC_20621_DIMM_DATA / PDC_20621_PAGE_SIZE), - PDC_PAGE_SET = PDC_DIMM_DATA_STEP / PDC_20621_PAGE_SIZE, - - PDC_CHIP0_OFS = 0xC0000, /* offset of chip #0 */ - - PDC_20621_ERR_MASK = (1<<19) | (1<<20) | (1<<21) | (1<<22) | - (1<<23), - - board_20621 = 0, /* FastTrak S150 SX4 */ - - PDC_RESET = (1 << 11), /* HDMA reset */ - - PDC_MAX_HDMA = 32, - PDC_HDMA_Q_MASK = (PDC_MAX_HDMA - 1), - - PDC_DIMM0_SPD_DEV_ADDRESS = 0x50, - PDC_DIMM1_SPD_DEV_ADDRESS = 0x51, - PDC_MAX_DIMM_MODULE = 0x02, - PDC_I2C_CONTROL_OFFSET = 0x48, - PDC_I2C_ADDR_DATA_OFFSET = 0x4C, - PDC_DIMM0_CONTROL_OFFSET = 0x80, - PDC_DIMM1_CONTROL_OFFSET = 0x84, - PDC_SDRAM_CONTROL_OFFSET = 0x88, - PDC_I2C_WRITE = 0x00000000, - PDC_I2C_READ = 0x00000040, - PDC_I2C_START = 0x00000080, - PDC_I2C_MASK_INT = 0x00000020, - PDC_I2C_COMPLETE = 0x00010000, - PDC_I2C_NO_ACK = 0x00100000, - PDC_DIMM_SPD_SUBADDRESS_START = 0x00, - PDC_DIMM_SPD_SUBADDRESS_END = 0x7F, - PDC_DIMM_SPD_ROW_NUM = 3, - PDC_DIMM_SPD_COLUMN_NUM = 4, - PDC_DIMM_SPD_MODULE_ROW = 5, - PDC_DIMM_SPD_TYPE = 11, - PDC_DIMM_SPD_FRESH_RATE = 12, - PDC_DIMM_SPD_BANK_NUM = 17, - PDC_DIMM_SPD_CAS_LATENCY = 18, - PDC_DIMM_SPD_ATTRIBUTE = 21, - PDC_DIMM_SPD_ROW_PRE_CHARGE = 27, - PDC_DIMM_SPD_ROW_ACTIVE_DELAY = 28, - PDC_DIMM_SPD_RAS_CAS_DELAY = 29, - PDC_DIMM_SPD_ACTIVE_PRECHARGE = 30, - PDC_DIMM_SPD_SYSTEM_FREQ = 126, - PDC_CTL_STATUS = 0x08, - PDC_DIMM_WINDOW_CTLR = 0x0C, - PDC_TIME_CONTROL = 0x3C, - PDC_TIME_PERIOD = 0x40, - PDC_TIME_COUNTER = 0x44, - PDC_GENERAL_CTLR = 0x484, - PCI_PLL_INIT = 0x8A531824, - PCI_X_TCOUNT = 0xEE1E5CFF -}; - - -struct pdc_port_priv { - u8 dimm_buf[(ATA_PRD_SZ * ATA_MAX_PRD) + 512]; - u8 *pkt; - dma_addr_t pkt_dma; -}; - -struct pdc_host_priv { - void __iomem *dimm_mmio; - - unsigned int doing_hdma; - unsigned int hdma_prod; - unsigned int hdma_cons; - struct { - struct ata_queued_cmd *qc; - unsigned int seq; - unsigned long pkt_ofs; - } hdma[32]; -}; - - -static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static irqreturn_t pdc20621_interrupt (int irq, void *dev_instance, struct pt_regs *regs); -static void pdc_eng_timeout(struct ata_port *ap); -static void pdc_20621_phy_reset (struct ata_port *ap); -static int pdc_port_start(struct ata_port *ap); -static void pdc_port_stop(struct ata_port *ap); -static void pdc20621_qc_prep(struct ata_queued_cmd *qc); -static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf); -static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf); -static void pdc20621_host_stop(struct ata_host_set *host_set); -static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe); -static int pdc20621_detect_dimm(struct ata_probe_ent *pe); -static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, - u32 device, u32 subaddr, u32 *pdata); -static int pdc20621_prog_dimm0(struct ata_probe_ent *pe); -static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe); -#ifdef ATA_VERBOSE_DEBUG -static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, - void *psource, u32 offset, u32 size); -#endif -static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, - void *psource, u32 offset, u32 size); -static void pdc20621_irq_clear(struct ata_port *ap); -static unsigned int pdc20621_qc_issue_prot(struct ata_queued_cmd *qc); - - -static struct scsi_host_template pdc_sata_sht = { - .module = THIS_MODULE, - .name = DRV_NAME, - .ioctl = ata_scsi_ioctl, - .queuecommand = ata_scsi_queuecmd, - .can_queue = ATA_DEF_QUEUE, - .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = LIBATA_MAX_PRD, - .cmd_per_lun = ATA_SHT_CMD_PER_LUN, - .emulated = ATA_SHT_EMULATED, - .use_clustering = ATA_SHT_USE_CLUSTERING, - .proc_name = DRV_NAME, - .dma_boundary = ATA_DMA_BOUNDARY, - .slave_configure = ata_scsi_slave_config, - .slave_destroy = ata_scsi_slave_destroy, - .bios_param = ata_std_bios_param, -}; - -static const struct ata_port_operations pdc_20621_ops = { - .port_disable = ata_port_disable, - .tf_load = pdc_tf_load_mmio, - .tf_read = ata_tf_read, - .check_status = ata_check_status, - .exec_command = pdc_exec_command_mmio, - .dev_select = ata_std_dev_select, - .phy_reset = pdc_20621_phy_reset, - .qc_prep = pdc20621_qc_prep, - .qc_issue = pdc20621_qc_issue_prot, - .data_xfer = ata_mmio_data_xfer, - .eng_timeout = pdc_eng_timeout, - .irq_handler = pdc20621_interrupt, - .irq_clear = pdc20621_irq_clear, - .port_start = pdc_port_start, - .port_stop = pdc_port_stop, - .host_stop = pdc20621_host_stop, -}; - -static const struct ata_port_info pdc_port_info[] = { - /* board_20621 */ - { - .sht = &pdc_sata_sht, - .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_SRST | ATA_FLAG_MMIO | - ATA_FLAG_NO_ATAPI | ATA_FLAG_PIO_POLLING, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x7f, /* udma0-6 ; FIXME */ - .port_ops = &pdc_20621_ops, - }, - -}; - -static const struct pci_device_id pdc_sata_pci_tbl[] = { - { PCI_VENDOR_ID_PROMISE, 0x6622, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_20621 }, - { } /* terminate list */ -}; - - -static struct pci_driver pdc_sata_pci_driver = { - .name = DRV_NAME, - .id_table = pdc_sata_pci_tbl, - .probe = pdc_sata_init_one, - .remove = ata_pci_remove_one, -}; - - -static void pdc20621_host_stop(struct ata_host_set *host_set) -{ - struct pci_dev *pdev = to_pci_dev(host_set->dev); - struct pdc_host_priv *hpriv = host_set->private_data; - void __iomem *dimm_mmio = hpriv->dimm_mmio; - - pci_iounmap(pdev, dimm_mmio); - kfree(hpriv); - - pci_iounmap(pdev, host_set->mmio_base); -} - -static int pdc_port_start(struct ata_port *ap) -{ - struct device *dev = ap->host_set->dev; - struct pdc_port_priv *pp; - int rc; - - rc = ata_port_start(ap); - if (rc) - return rc; - - pp = kmalloc(sizeof(*pp), GFP_KERNEL); - if (!pp) { - rc = -ENOMEM; - goto err_out; - } - memset(pp, 0, sizeof(*pp)); - - pp->pkt = dma_alloc_coherent(dev, 128, &pp->pkt_dma, GFP_KERNEL); - if (!pp->pkt) { - rc = -ENOMEM; - goto err_out_kfree; - } - - ap->private_data = pp; - - return 0; - -err_out_kfree: - kfree(pp); -err_out: - ata_port_stop(ap); - return rc; -} - - -static void pdc_port_stop(struct ata_port *ap) -{ - struct device *dev = ap->host_set->dev; - struct pdc_port_priv *pp = ap->private_data; - - ap->private_data = NULL; - dma_free_coherent(dev, 128, pp->pkt, pp->pkt_dma); - kfree(pp); - ata_port_stop(ap); -} - - -static void pdc_20621_phy_reset (struct ata_port *ap) -{ - VPRINTK("ENTER\n"); - ap->cbl = ATA_CBL_SATA; - ata_port_probe(ap); - ata_bus_reset(ap); -} - -static inline void pdc20621_ata_sg(struct ata_taskfile *tf, u8 *buf, - unsigned int portno, - unsigned int total_len) -{ - u32 addr; - unsigned int dw = PDC_DIMM_APKT_PRD >> 2; - u32 *buf32 = (u32 *) buf; - - /* output ATA packet S/G table */ - addr = PDC_20621_DIMM_BASE + PDC_20621_DIMM_DATA + - (PDC_DIMM_DATA_STEP * portno); - VPRINTK("ATA sg addr 0x%x, %d\n", addr, addr); - buf32[dw] = cpu_to_le32(addr); - buf32[dw + 1] = cpu_to_le32(total_len | ATA_PRD_EOT); - - VPRINTK("ATA PSG @ %x == (0x%x, 0x%x)\n", - PDC_20621_DIMM_BASE + - (PDC_DIMM_WINDOW_STEP * portno) + - PDC_DIMM_APKT_PRD, - buf32[dw], buf32[dw + 1]); -} - -static inline void pdc20621_host_sg(struct ata_taskfile *tf, u8 *buf, - unsigned int portno, - unsigned int total_len) -{ - u32 addr; - unsigned int dw = PDC_DIMM_HPKT_PRD >> 2; - u32 *buf32 = (u32 *) buf; - - /* output Host DMA packet S/G table */ - addr = PDC_20621_DIMM_BASE + PDC_20621_DIMM_DATA + - (PDC_DIMM_DATA_STEP * portno); - - buf32[dw] = cpu_to_le32(addr); - buf32[dw + 1] = cpu_to_le32(total_len | ATA_PRD_EOT); - - VPRINTK("HOST PSG @ %x == (0x%x, 0x%x)\n", - PDC_20621_DIMM_BASE + - (PDC_DIMM_WINDOW_STEP * portno) + - PDC_DIMM_HPKT_PRD, - buf32[dw], buf32[dw + 1]); -} - -static inline unsigned int pdc20621_ata_pkt(struct ata_taskfile *tf, - unsigned int devno, u8 *buf, - unsigned int portno) -{ - unsigned int i, dw; - u32 *buf32 = (u32 *) buf; - u8 dev_reg; - - unsigned int dimm_sg = PDC_20621_DIMM_BASE + - (PDC_DIMM_WINDOW_STEP * portno) + - PDC_DIMM_APKT_PRD; - VPRINTK("ENTER, dimm_sg == 0x%x, %d\n", dimm_sg, dimm_sg); - - i = PDC_DIMM_ATA_PKT; - - /* - * Set up ATA packet - */ - if ((tf->protocol == ATA_PROT_DMA) && (!(tf->flags & ATA_TFLAG_WRITE))) - buf[i++] = PDC_PKT_READ; - else if (tf->protocol == ATA_PROT_NODATA) - buf[i++] = PDC_PKT_NODATA; - else - buf[i++] = 0; - buf[i++] = 0; /* reserved */ - buf[i++] = portno + 1; /* seq. id */ - buf[i++] = 0xff; /* delay seq. id */ - - /* dimm dma S/G, and next-pkt */ - dw = i >> 2; - if (tf->protocol == ATA_PROT_NODATA) - buf32[dw] = 0; - else - buf32[dw] = cpu_to_le32(dimm_sg); - buf32[dw + 1] = 0; - i += 8; - - if (devno == 0) - dev_reg = ATA_DEVICE_OBS; - else - dev_reg = ATA_DEVICE_OBS | ATA_DEV1; - - /* select device */ - buf[i++] = (1 << 5) | PDC_PKT_CLEAR_BSY | ATA_REG_DEVICE; - buf[i++] = dev_reg; - - /* device control register */ - buf[i++] = (1 << 5) | PDC_REG_DEVCTL; - buf[i++] = tf->ctl; - - return i; -} - -static inline void pdc20621_host_pkt(struct ata_taskfile *tf, u8 *buf, - unsigned int portno) -{ - unsigned int dw; - u32 tmp, *buf32 = (u32 *) buf; - - unsigned int host_sg = PDC_20621_DIMM_BASE + - (PDC_DIMM_WINDOW_STEP * portno) + - PDC_DIMM_HOST_PRD; - unsigned int dimm_sg = PDC_20621_DIMM_BASE + - (PDC_DIMM_WINDOW_STEP * portno) + - PDC_DIMM_HPKT_PRD; - VPRINTK("ENTER, dimm_sg == 0x%x, %d\n", dimm_sg, dimm_sg); - VPRINTK("host_sg == 0x%x, %d\n", host_sg, host_sg); - - dw = PDC_DIMM_HOST_PKT >> 2; - - /* - * Set up Host DMA packet - */ - if ((tf->protocol == ATA_PROT_DMA) && (!(tf->flags & ATA_TFLAG_WRITE))) - tmp = PDC_PKT_READ; - else - tmp = 0; - tmp |= ((portno + 1 + 4) << 16); /* seq. id */ - tmp |= (0xff << 24); /* delay seq. id */ - buf32[dw + 0] = cpu_to_le32(tmp); - buf32[dw + 1] = cpu_to_le32(host_sg); - buf32[dw + 2] = cpu_to_le32(dimm_sg); - buf32[dw + 3] = 0; - - VPRINTK("HOST PKT @ %x == (0x%x 0x%x 0x%x 0x%x)\n", - PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * portno) + - PDC_DIMM_HOST_PKT, - buf32[dw + 0], - buf32[dw + 1], - buf32[dw + 2], - buf32[dw + 3]); -} - -static void pdc20621_dma_prep(struct ata_queued_cmd *qc) -{ - struct scatterlist *sg; - struct ata_port *ap = qc->ap; - struct pdc_port_priv *pp = ap->private_data; - void __iomem *mmio = ap->host_set->mmio_base; - struct pdc_host_priv *hpriv = ap->host_set->private_data; - void __iomem *dimm_mmio = hpriv->dimm_mmio; - unsigned int portno = ap->port_no; - unsigned int i, idx, total_len = 0, sgt_len; - u32 *buf = (u32 *) &pp->dimm_buf[PDC_DIMM_HEADER_SZ]; - - WARN_ON(!(qc->flags & ATA_QCFLAG_DMAMAP)); - - VPRINTK("ata%u: ENTER\n", ap->id); - - /* hard-code chip #0 */ - mmio += PDC_CHIP0_OFS; - - /* - * Build S/G table - */ - idx = 0; - ata_for_each_sg(sg, qc) { - buf[idx++] = cpu_to_le32(sg_dma_address(sg)); - buf[idx++] = cpu_to_le32(sg_dma_len(sg)); - total_len += sg_dma_len(sg); - } - buf[idx - 1] |= cpu_to_le32(ATA_PRD_EOT); - sgt_len = idx * 4; - - /* - * Build ATA, host DMA packets - */ - pdc20621_host_sg(&qc->tf, &pp->dimm_buf[0], portno, total_len); - pdc20621_host_pkt(&qc->tf, &pp->dimm_buf[0], portno); - - pdc20621_ata_sg(&qc->tf, &pp->dimm_buf[0], portno, total_len); - i = pdc20621_ata_pkt(&qc->tf, qc->dev->devno, &pp->dimm_buf[0], portno); - - if (qc->tf.flags & ATA_TFLAG_LBA48) - i = pdc_prep_lba48(&qc->tf, &pp->dimm_buf[0], i); - else - i = pdc_prep_lba28(&qc->tf, &pp->dimm_buf[0], i); - - pdc_pkt_footer(&qc->tf, &pp->dimm_buf[0], i); - - /* copy three S/G tables and two packets to DIMM MMIO window */ - memcpy_toio(dimm_mmio + (portno * PDC_DIMM_WINDOW_STEP), - &pp->dimm_buf, PDC_DIMM_HEADER_SZ); - memcpy_toio(dimm_mmio + (portno * PDC_DIMM_WINDOW_STEP) + - PDC_DIMM_HOST_PRD, - &pp->dimm_buf[PDC_DIMM_HEADER_SZ], sgt_len); - - /* force host FIFO dump */ - writel(0x00000001, mmio + PDC_20621_GENERAL_CTL); - - readl(dimm_mmio); /* MMIO PCI posting flush */ - - VPRINTK("ata pkt buf ofs %u, prd size %u, mmio copied\n", i, sgt_len); -} - -static void pdc20621_nodata_prep(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct pdc_port_priv *pp = ap->private_data; - void __iomem *mmio = ap->host_set->mmio_base; - struct pdc_host_priv *hpriv = ap->host_set->private_data; - void __iomem *dimm_mmio = hpriv->dimm_mmio; - unsigned int portno = ap->port_no; - unsigned int i; - - VPRINTK("ata%u: ENTER\n", ap->id); - - /* hard-code chip #0 */ - mmio += PDC_CHIP0_OFS; - - i = pdc20621_ata_pkt(&qc->tf, qc->dev->devno, &pp->dimm_buf[0], portno); - - if (qc->tf.flags & ATA_TFLAG_LBA48) - i = pdc_prep_lba48(&qc->tf, &pp->dimm_buf[0], i); - else - i = pdc_prep_lba28(&qc->tf, &pp->dimm_buf[0], i); - - pdc_pkt_footer(&qc->tf, &pp->dimm_buf[0], i); - - /* copy three S/G tables and two packets to DIMM MMIO window */ - memcpy_toio(dimm_mmio + (portno * PDC_DIMM_WINDOW_STEP), - &pp->dimm_buf, PDC_DIMM_HEADER_SZ); - - /* force host FIFO dump */ - writel(0x00000001, mmio + PDC_20621_GENERAL_CTL); - - readl(dimm_mmio); /* MMIO PCI posting flush */ - - VPRINTK("ata pkt buf ofs %u, mmio copied\n", i); -} - -static void pdc20621_qc_prep(struct ata_queued_cmd *qc) -{ - switch (qc->tf.protocol) { - case ATA_PROT_DMA: - pdc20621_dma_prep(qc); - break; - case ATA_PROT_NODATA: - pdc20621_nodata_prep(qc); - break; - default: - break; - } -} - -static void __pdc20621_push_hdma(struct ata_queued_cmd *qc, - unsigned int seq, - u32 pkt_ofs) -{ - struct ata_port *ap = qc->ap; - struct ata_host_set *host_set = ap->host_set; - void __iomem *mmio = host_set->mmio_base; - - /* hard-code chip #0 */ - mmio += PDC_CHIP0_OFS; - - writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4)); - readl(mmio + PDC_20621_SEQCTL + (seq * 4)); /* flush */ - - writel(pkt_ofs, mmio + PDC_HDMA_PKT_SUBMIT); - readl(mmio + PDC_HDMA_PKT_SUBMIT); /* flush */ -} - -static void pdc20621_push_hdma(struct ata_queued_cmd *qc, - unsigned int seq, - u32 pkt_ofs) -{ - struct ata_port *ap = qc->ap; - struct pdc_host_priv *pp = ap->host_set->private_data; - unsigned int idx = pp->hdma_prod & PDC_HDMA_Q_MASK; - - if (!pp->doing_hdma) { - __pdc20621_push_hdma(qc, seq, pkt_ofs); - pp->doing_hdma = 1; - return; - } - - pp->hdma[idx].qc = qc; - pp->hdma[idx].seq = seq; - pp->hdma[idx].pkt_ofs = pkt_ofs; - pp->hdma_prod++; -} - -static void pdc20621_pop_hdma(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct pdc_host_priv *pp = ap->host_set->private_data; - unsigned int idx = pp->hdma_cons & PDC_HDMA_Q_MASK; - - /* if nothing on queue, we're done */ - if (pp->hdma_prod == pp->hdma_cons) { - pp->doing_hdma = 0; - return; - } - - __pdc20621_push_hdma(pp->hdma[idx].qc, pp->hdma[idx].seq, - pp->hdma[idx].pkt_ofs); - pp->hdma_cons++; -} - -#ifdef ATA_VERBOSE_DEBUG -static void pdc20621_dump_hdma(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - unsigned int port_no = ap->port_no; - struct pdc_host_priv *hpriv = ap->host_set->private_data; - void *dimm_mmio = hpriv->dimm_mmio; - - dimm_mmio += (port_no * PDC_DIMM_WINDOW_STEP); - dimm_mmio += PDC_DIMM_HOST_PKT; - - printk(KERN_ERR "HDMA[0] == 0x%08X\n", readl(dimm_mmio)); - printk(KERN_ERR "HDMA[1] == 0x%08X\n", readl(dimm_mmio + 4)); - printk(KERN_ERR "HDMA[2] == 0x%08X\n", readl(dimm_mmio + 8)); - printk(KERN_ERR "HDMA[3] == 0x%08X\n", readl(dimm_mmio + 12)); -} -#else -static inline void pdc20621_dump_hdma(struct ata_queued_cmd *qc) { } -#endif /* ATA_VERBOSE_DEBUG */ - -static void pdc20621_packet_start(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct ata_host_set *host_set = ap->host_set; - unsigned int port_no = ap->port_no; - void __iomem *mmio = host_set->mmio_base; - unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE); - u8 seq = (u8) (port_no + 1); - unsigned int port_ofs; - - /* hard-code chip #0 */ - mmio += PDC_CHIP0_OFS; - - VPRINTK("ata%u: ENTER\n", ap->id); - - wmb(); /* flush PRD, pkt writes */ - - port_ofs = PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * port_no); - - /* if writing, we (1) DMA to DIMM, then (2) do ATA command */ - if (rw && qc->tf.protocol == ATA_PROT_DMA) { - seq += 4; - - pdc20621_dump_hdma(qc); - pdc20621_push_hdma(qc, seq, port_ofs + PDC_DIMM_HOST_PKT); - VPRINTK("queued ofs 0x%x (%u), seq %u\n", - port_ofs + PDC_DIMM_HOST_PKT, - port_ofs + PDC_DIMM_HOST_PKT, - seq); - } else { - writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4)); - readl(mmio + PDC_20621_SEQCTL + (seq * 4)); /* flush */ - - writel(port_ofs + PDC_DIMM_ATA_PKT, - (void __iomem *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); - readl((void __iomem *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); - VPRINTK("submitted ofs 0x%x (%u), seq %u\n", - port_ofs + PDC_DIMM_ATA_PKT, - port_ofs + PDC_DIMM_ATA_PKT, - seq); - } -} - -static unsigned int pdc20621_qc_issue_prot(struct ata_queued_cmd *qc) -{ - switch (qc->tf.protocol) { - case ATA_PROT_DMA: - case ATA_PROT_NODATA: - pdc20621_packet_start(qc); - return 0; - - case ATA_PROT_ATAPI_DMA: - BUG(); - break; - - default: - break; - } - - return ata_qc_issue_prot(qc); -} - -static inline unsigned int pdc20621_host_intr( struct ata_port *ap, - struct ata_queued_cmd *qc, - unsigned int doing_hdma, - void __iomem *mmio) -{ - unsigned int port_no = ap->port_no; - unsigned int port_ofs = - PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * port_no); - u8 status; - unsigned int handled = 0; - - VPRINTK("ENTER\n"); - - if ((qc->tf.protocol == ATA_PROT_DMA) && /* read */ - (!(qc->tf.flags & ATA_TFLAG_WRITE))) { - - /* step two - DMA from DIMM to host */ - if (doing_hdma) { - VPRINTK("ata%u: read hdma, 0x%x 0x%x\n", ap->id, - readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); - /* get drive status; clear intr; complete txn */ - qc->err_mask |= ac_err_mask(ata_wait_idle(ap)); - ata_qc_complete(qc); - pdc20621_pop_hdma(qc); - } - - /* step one - exec ATA command */ - else { - u8 seq = (u8) (port_no + 1 + 4); - VPRINTK("ata%u: read ata, 0x%x 0x%x\n", ap->id, - readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); - - /* submit hdma pkt */ - pdc20621_dump_hdma(qc); - pdc20621_push_hdma(qc, seq, - port_ofs + PDC_DIMM_HOST_PKT); - } - handled = 1; - - } else if (qc->tf.protocol == ATA_PROT_DMA) { /* write */ - - /* step one - DMA from host to DIMM */ - if (doing_hdma) { - u8 seq = (u8) (port_no + 1); - VPRINTK("ata%u: write hdma, 0x%x 0x%x\n", ap->id, - readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); - - /* submit ata pkt */ - writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4)); - readl(mmio + PDC_20621_SEQCTL + (seq * 4)); - writel(port_ofs + PDC_DIMM_ATA_PKT, - (void __iomem *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); - readl((void __iomem *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); - } - - /* step two - execute ATA command */ - else { - VPRINTK("ata%u: write ata, 0x%x 0x%x\n", ap->id, - readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); - /* get drive status; clear intr; complete txn */ - qc->err_mask |= ac_err_mask(ata_wait_idle(ap)); - ata_qc_complete(qc); - pdc20621_pop_hdma(qc); - } - handled = 1; - - /* command completion, but no data xfer */ - } else if (qc->tf.protocol == ATA_PROT_NODATA) { - - status = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000); - DPRINTK("BUS_NODATA (drv_stat 0x%X)\n", status); - qc->err_mask |= ac_err_mask(status); - ata_qc_complete(qc); - handled = 1; - - } else { - ap->stats.idle_irq++; - } - - return handled; -} - -static void pdc20621_irq_clear(struct ata_port *ap) -{ - struct ata_host_set *host_set = ap->host_set; - void __iomem *mmio = host_set->mmio_base; - - mmio += PDC_CHIP0_OFS; - - readl(mmio + PDC_20621_SEQMASK); -} - -static irqreturn_t pdc20621_interrupt (int irq, void *dev_instance, struct pt_regs *regs) -{ - struct ata_host_set *host_set = dev_instance; - struct ata_port *ap; - u32 mask = 0; - unsigned int i, tmp, port_no; - unsigned int handled = 0; - void __iomem *mmio_base; - - VPRINTK("ENTER\n"); - - if (!host_set || !host_set->mmio_base) { - VPRINTK("QUICK EXIT\n"); - return IRQ_NONE; - } - - mmio_base = host_set->mmio_base; - - /* reading should also clear interrupts */ - mmio_base += PDC_CHIP0_OFS; - mask = readl(mmio_base + PDC_20621_SEQMASK); - VPRINTK("mask == 0x%x\n", mask); - - if (mask == 0xffffffff) { - VPRINTK("QUICK EXIT 2\n"); - return IRQ_NONE; - } - mask &= 0xffff; /* only 16 tags possible */ - if (!mask) { - VPRINTK("QUICK EXIT 3\n"); - return IRQ_NONE; - } - - spin_lock(&host_set->lock); - - for (i = 1; i < 9; i++) { - port_no = i - 1; - if (port_no > 3) - port_no -= 4; - if (port_no >= host_set->n_ports) - ap = NULL; - else - ap = host_set->ports[port_no]; - tmp = mask & (1 << i); - VPRINTK("seq %u, port_no %u, ap %p, tmp %x\n", i, port_no, ap, tmp); - if (tmp && ap && - !(ap->flags & ATA_FLAG_DISABLED)) { - struct ata_queued_cmd *qc; - - qc = ata_qc_from_tag(ap, ap->active_tag); - if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) - handled += pdc20621_host_intr(ap, qc, (i > 4), - mmio_base); - } - } - - spin_unlock(&host_set->lock); - - VPRINTK("mask == 0x%x\n", mask); - - VPRINTK("EXIT\n"); - - return IRQ_RETVAL(handled); -} - -static void pdc_eng_timeout(struct ata_port *ap) -{ - u8 drv_stat; - struct ata_host_set *host_set = ap->host_set; - struct ata_queued_cmd *qc; - unsigned long flags; - - DPRINTK("ENTER\n"); - - spin_lock_irqsave(&host_set->lock, flags); - - qc = ata_qc_from_tag(ap, ap->active_tag); - - switch (qc->tf.protocol) { - case ATA_PROT_DMA: - case ATA_PROT_NODATA: - ata_port_printk(ap, KERN_ERR, "command timeout\n"); - qc->err_mask |= __ac_err_mask(ata_wait_idle(ap)); - break; - - default: - drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000); - - ata_port_printk(ap, KERN_ERR, - "unknown timeout, cmd 0x%x stat 0x%x\n", - qc->tf.command, drv_stat); - - qc->err_mask |= ac_err_mask(drv_stat); - break; - } - - spin_unlock_irqrestore(&host_set->lock, flags); - ata_eh_qc_complete(qc); - DPRINTK("EXIT\n"); -} - -static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf) -{ - WARN_ON (tf->protocol == ATA_PROT_DMA || - tf->protocol == ATA_PROT_NODATA); - ata_tf_load(ap, tf); -} - - -static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf) -{ - WARN_ON (tf->protocol == ATA_PROT_DMA || - tf->protocol == ATA_PROT_NODATA); - ata_exec_command(ap, tf); -} - - -static void pdc_sata_setup_port(struct ata_ioports *port, unsigned long base) -{ - port->cmd_addr = base; - port->data_addr = base; - port->feature_addr = - port->error_addr = base + 0x4; - port->nsect_addr = base + 0x8; - port->lbal_addr = base + 0xc; - port->lbam_addr = base + 0x10; - port->lbah_addr = base + 0x14; - port->device_addr = base + 0x18; - port->command_addr = - port->status_addr = base + 0x1c; - port->altstatus_addr = - port->ctl_addr = base + 0x38; -} - - -#ifdef ATA_VERBOSE_DEBUG -static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, void *psource, - u32 offset, u32 size) -{ - u32 window_size; - u16 idx; - u8 page_mask; - long dist; - void __iomem *mmio = pe->mmio_base; - struct pdc_host_priv *hpriv = pe->private_data; - void __iomem *dimm_mmio = hpriv->dimm_mmio; - - /* hard-code chip #0 */ - mmio += PDC_CHIP0_OFS; - - page_mask = 0x00; - window_size = 0x2000 * 4; /* 32K byte uchar size */ - idx = (u16) (offset / window_size); - - writel(0x01, mmio + PDC_GENERAL_CTLR); - readl(mmio + PDC_GENERAL_CTLR); - writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); - readl(mmio + PDC_DIMM_WINDOW_CTLR); - - offset -= (idx * window_size); - idx++; - dist = ((long) (window_size - (offset + size))) >= 0 ? size : - (long) (window_size - offset); - memcpy_fromio((char *) psource, (char *) (dimm_mmio + offset / 4), - dist); - - psource += dist; - size -= dist; - for (; (long) size >= (long) window_size ;) { - writel(0x01, mmio + PDC_GENERAL_CTLR); - readl(mmio + PDC_GENERAL_CTLR); - writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); - readl(mmio + PDC_DIMM_WINDOW_CTLR); - memcpy_fromio((char *) psource, (char *) (dimm_mmio), - window_size / 4); - psource += window_size; - size -= window_size; - idx ++; - } - - if (size) { - writel(0x01, mmio + PDC_GENERAL_CTLR); - readl(mmio + PDC_GENERAL_CTLR); - writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); - readl(mmio + PDC_DIMM_WINDOW_CTLR); - memcpy_fromio((char *) psource, (char *) (dimm_mmio), - size / 4); - } -} -#endif - - -static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, void *psource, - u32 offset, u32 size) -{ - u32 window_size; - u16 idx; - u8 page_mask; - long dist; - void __iomem *mmio = pe->mmio_base; - struct pdc_host_priv *hpriv = pe->private_data; - void __iomem *dimm_mmio = hpriv->dimm_mmio; - - /* hard-code chip #0 */ - mmio += PDC_CHIP0_OFS; - - page_mask = 0x00; - window_size = 0x2000 * 4; /* 32K byte uchar size */ - idx = (u16) (offset / window_size); - - writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); - readl(mmio + PDC_DIMM_WINDOW_CTLR); - offset -= (idx * window_size); - idx++; - dist = ((long)(s32)(window_size - (offset + size))) >= 0 ? size : - (long) (window_size - offset); - memcpy_toio(dimm_mmio + offset / 4, psource, dist); - writel(0x01, mmio + PDC_GENERAL_CTLR); - readl(mmio + PDC_GENERAL_CTLR); - - psource += dist; - size -= dist; - for (; (long) size >= (long) window_size ;) { - writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); - readl(mmio + PDC_DIMM_WINDOW_CTLR); - memcpy_toio(dimm_mmio, psource, window_size / 4); - writel(0x01, mmio + PDC_GENERAL_CTLR); - readl(mmio + PDC_GENERAL_CTLR); - psource += window_size; - size -= window_size; - idx ++; - } - - if (size) { - writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); - readl(mmio + PDC_DIMM_WINDOW_CTLR); - memcpy_toio(dimm_mmio, psource, size / 4); - writel(0x01, mmio + PDC_GENERAL_CTLR); - readl(mmio + PDC_GENERAL_CTLR); - } -} - - -static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, u32 device, - u32 subaddr, u32 *pdata) -{ - void __iomem *mmio = pe->mmio_base; - u32 i2creg = 0; - u32 status; - u32 count =0; - - /* hard-code chip #0 */ - mmio += PDC_CHIP0_OFS; - - i2creg |= device << 24; - i2creg |= subaddr << 16; - - /* Set the device and subaddress */ - writel(i2creg, mmio + PDC_I2C_ADDR_DATA_OFFSET); - readl(mmio + PDC_I2C_ADDR_DATA_OFFSET); - - /* Write Control to perform read operation, mask int */ - writel(PDC_I2C_READ | PDC_I2C_START | PDC_I2C_MASK_INT, - mmio + PDC_I2C_CONTROL_OFFSET); - - for (count = 0; count <= 1000; count ++) { - status = readl(mmio + PDC_I2C_CONTROL_OFFSET); - if (status & PDC_I2C_COMPLETE) { - status = readl(mmio + PDC_I2C_ADDR_DATA_OFFSET); - break; - } else if (count == 1000) - return 0; - } - - *pdata = (status >> 8) & 0x000000ff; - return 1; -} - - -static int pdc20621_detect_dimm(struct ata_probe_ent *pe) -{ - u32 data=0 ; - if (pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, - PDC_DIMM_SPD_SYSTEM_FREQ, &data)) { - if (data == 100) - return 100; - } else - return 0; - - if (pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, 9, &data)) { - if(data <= 0x75) - return 133; - } else - return 0; - - return 0; -} - - -static int pdc20621_prog_dimm0(struct ata_probe_ent *pe) -{ - u32 spd0[50]; - u32 data = 0; - int size, i; - u8 bdimmsize; - void __iomem *mmio = pe->mmio_base; - static const struct { - unsigned int reg; - unsigned int ofs; - } pdc_i2c_read_data [] = { - { PDC_DIMM_SPD_TYPE, 11 }, - { PDC_DIMM_SPD_FRESH_RATE, 12 }, - { PDC_DIMM_SPD_COLUMN_NUM, 4 }, - { PDC_DIMM_SPD_ATTRIBUTE, 21 }, - { PDC_DIMM_SPD_ROW_NUM, 3 }, - { PDC_DIMM_SPD_BANK_NUM, 17 }, - { PDC_DIMM_SPD_MODULE_ROW, 5 }, - { PDC_DIMM_SPD_ROW_PRE_CHARGE, 27 }, - { PDC_DIMM_SPD_ROW_ACTIVE_DELAY, 28 }, - { PDC_DIMM_SPD_RAS_CAS_DELAY, 29 }, - { PDC_DIMM_SPD_ACTIVE_PRECHARGE, 30 }, - { PDC_DIMM_SPD_CAS_LATENCY, 18 }, - }; - - /* hard-code chip #0 */ - mmio += PDC_CHIP0_OFS; - - for(i=0; i spd0[28]) - ? spd0[29] : spd0[28]) + 9) / 10) - 1) << 10; - data |= ((spd0[30] - spd0[29] + 9) / 10 - 2) << 12; - - if (spd0[18] & 0x08) - data |= ((0x03) << 14); - else if (spd0[18] & 0x04) - data |= ((0x02) << 14); - else if (spd0[18] & 0x01) - data |= ((0x01) << 14); - else - data |= (0 << 14); - - /* - Calculate the size of bDIMMSize (power of 2) and - merge the DIMM size by program start/end address. - */ - - bdimmsize = spd0[4] + (spd0[5] / 2) + spd0[3] + (spd0[17] / 2) + 3; - size = (1 << bdimmsize) >> 20; /* size = xxx(MB) */ - data |= (((size / 16) - 1) << 16); - data |= (0 << 23); - data |= 8; - writel(data, mmio + PDC_DIMM0_CONTROL_OFFSET); - readl(mmio + PDC_DIMM0_CONTROL_OFFSET); - return size; -} - - -static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe) -{ - u32 data, spd0; - int error, i; - void __iomem *mmio = pe->mmio_base; - - /* hard-code chip #0 */ - mmio += PDC_CHIP0_OFS; - - /* - Set To Default : DIMM Module Global Control Register (0x022259F1) - DIMM Arbitration Disable (bit 20) - DIMM Data/Control Output Driving Selection (bit12 - bit15) - Refresh Enable (bit 17) - */ - - data = 0x022259F1; - writel(data, mmio + PDC_SDRAM_CONTROL_OFFSET); - readl(mmio + PDC_SDRAM_CONTROL_OFFSET); - - /* Turn on for ECC */ - pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, - PDC_DIMM_SPD_TYPE, &spd0); - if (spd0 == 0x02) { - data |= (0x01 << 16); - writel(data, mmio + PDC_SDRAM_CONTROL_OFFSET); - readl(mmio + PDC_SDRAM_CONTROL_OFFSET); - printk(KERN_ERR "Local DIMM ECC Enabled\n"); - } - - /* DIMM Initialization Select/Enable (bit 18/19) */ - data &= (~(1<<18)); - data |= (1<<19); - writel(data, mmio + PDC_SDRAM_CONTROL_OFFSET); - - error = 1; - for (i = 1; i <= 10; i++) { /* polling ~5 secs */ - data = readl(mmio + PDC_SDRAM_CONTROL_OFFSET); - if (!(data & (1<<19))) { - error = 0; - break; - } - msleep(i*100); - } - return error; -} - - -static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe) -{ - int speed, size, length; - u32 addr,spd0,pci_status; - u32 tmp=0; - u32 time_period=0; - u32 tcount=0; - u32 ticks=0; - u32 clock=0; - u32 fparam=0; - void __iomem *mmio = pe->mmio_base; - - /* hard-code chip #0 */ - mmio += PDC_CHIP0_OFS; - - /* Initialize PLL based upon PCI Bus Frequency */ - - /* Initialize Time Period Register */ - writel(0xffffffff, mmio + PDC_TIME_PERIOD); - time_period = readl(mmio + PDC_TIME_PERIOD); - VPRINTK("Time Period Register (0x40): 0x%x\n", time_period); - - /* Enable timer */ - writel(0x00001a0, mmio + PDC_TIME_CONTROL); - readl(mmio + PDC_TIME_CONTROL); - - /* Wait 3 seconds */ - msleep(3000); - - /* - When timer is enabled, counter is decreased every internal - clock cycle. - */ - - tcount = readl(mmio + PDC_TIME_COUNTER); - VPRINTK("Time Counter Register (0x44): 0x%x\n", tcount); - - /* - If SX4 is on PCI-X bus, after 3 seconds, the timer counter - register should be >= (0xffffffff - 3x10^8). - */ - if(tcount >= PCI_X_TCOUNT) { - ticks = (time_period - tcount); - VPRINTK("Num counters 0x%x (%d)\n", ticks, ticks); - - clock = (ticks / 300000); - VPRINTK("10 * Internal clk = 0x%x (%d)\n", clock, clock); - - clock = (clock * 33); - VPRINTK("10 * Internal clk * 33 = 0x%x (%d)\n", clock, clock); - - /* PLL F Param (bit 22:16) */ - fparam = (1400000 / clock) - 2; - VPRINTK("PLL F Param: 0x%x (%d)\n", fparam, fparam); - - /* OD param = 0x2 (bit 31:30), R param = 0x5 (bit 29:25) */ - pci_status = (0x8a001824 | (fparam << 16)); - } else - pci_status = PCI_PLL_INIT; - - /* Initialize PLL. */ - VPRINTK("pci_status: 0x%x\n", pci_status); - writel(pci_status, mmio + PDC_CTL_STATUS); - readl(mmio + PDC_CTL_STATUS); - - /* - Read SPD of DIMM by I2C interface, - and program the DIMM Module Controller. - */ - if (!(speed = pdc20621_detect_dimm(pe))) { - printk(KERN_ERR "Detect Local DIMM Fail\n"); - return 1; /* DIMM error */ - } - VPRINTK("Local DIMM Speed = %d\n", speed); - - /* Programming DIMM0 Module Control Register (index_CID0:80h) */ - size = pdc20621_prog_dimm0(pe); - VPRINTK("Local DIMM Size = %dMB\n",size); - - /* Programming DIMM Module Global Control Register (index_CID0:88h) */ - if (pdc20621_prog_dimm_global(pe)) { - printk(KERN_ERR "Programming DIMM Module Global Control Register Fail\n"); - return 1; - } - -#ifdef ATA_VERBOSE_DEBUG - { - u8 test_parttern1[40] = {0x55,0xAA,'P','r','o','m','i','s','e',' ', - 'N','o','t',' ','Y','e','t',' ','D','e','f','i','n','e','d',' ', - '1','.','1','0', - '9','8','0','3','1','6','1','2',0,0}; - u8 test_parttern2[40] = {0}; - - pdc20621_put_to_dimm(pe, (void *) test_parttern2, 0x10040, 40); - pdc20621_put_to_dimm(pe, (void *) test_parttern2, 0x40, 40); - - pdc20621_put_to_dimm(pe, (void *) test_parttern1, 0x10040, 40); - pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x40, 40); - printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], - test_parttern2[1], &(test_parttern2[2])); - pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x10040, - 40); - printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], - test_parttern2[1], &(test_parttern2[2])); - - pdc20621_put_to_dimm(pe, (void *) test_parttern1, 0x40, 40); - pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x40, 40); - printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], - test_parttern2[1], &(test_parttern2[2])); - } -#endif - - /* ECC initiliazation. */ - - pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, - PDC_DIMM_SPD_TYPE, &spd0); - if (spd0 == 0x02) { - VPRINTK("Start ECC initialization\n"); - addr = 0; - length = size * 1024 * 1024; - while (addr < length) { - pdc20621_put_to_dimm(pe, (void *) &tmp, addr, - sizeof(u32)); - addr += sizeof(u32); - } - VPRINTK("Finish ECC initialization\n"); - } - return 0; -} - - -static void pdc_20621_init(struct ata_probe_ent *pe) -{ - u32 tmp; - void __iomem *mmio = pe->mmio_base; - - /* hard-code chip #0 */ - mmio += PDC_CHIP0_OFS; - - /* - * Select page 0x40 for our 32k DIMM window - */ - tmp = readl(mmio + PDC_20621_DIMM_WINDOW) & 0xffff0000; - tmp |= PDC_PAGE_WINDOW; /* page 40h; arbitrarily selected */ - writel(tmp, mmio + PDC_20621_DIMM_WINDOW); - - /* - * Reset Host DMA - */ - tmp = readl(mmio + PDC_HDMA_CTLSTAT); - tmp |= PDC_RESET; - writel(tmp, mmio + PDC_HDMA_CTLSTAT); - readl(mmio + PDC_HDMA_CTLSTAT); /* flush */ - - udelay(10); - - tmp = readl(mmio + PDC_HDMA_CTLSTAT); - tmp &= ~PDC_RESET; - writel(tmp, mmio + PDC_HDMA_CTLSTAT); - readl(mmio + PDC_HDMA_CTLSTAT); /* flush */ -} - -static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) -{ - static int printed_version; - struct ata_probe_ent *probe_ent = NULL; - unsigned long base; - void __iomem *mmio_base; - void __iomem *dimm_mmio = NULL; - struct pdc_host_priv *hpriv = NULL; - unsigned int board_idx = (unsigned int) ent->driver_data; - int pci_dev_busy = 0; - int rc; - - if (!printed_version++) - dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); - - rc = pci_enable_device(pdev); - if (rc) - return rc; - - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pci_dev_busy = 1; - goto err_out; - } - - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out_regions; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out_regions; - - probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) { - rc = -ENOMEM; - goto err_out_regions; - } - - memset(probe_ent, 0, sizeof(*probe_ent)); - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - mmio_base = pci_iomap(pdev, 3, 0); - if (mmio_base == NULL) { - rc = -ENOMEM; - goto err_out_free_ent; - } - base = (unsigned long) mmio_base; - - hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL); - if (!hpriv) { - rc = -ENOMEM; - goto err_out_iounmap; - } - memset(hpriv, 0, sizeof(*hpriv)); - - dimm_mmio = pci_iomap(pdev, 4, 0); - if (!dimm_mmio) { - kfree(hpriv); - rc = -ENOMEM; - goto err_out_iounmap; - } - - hpriv->dimm_mmio = dimm_mmio; - - probe_ent->sht = pdc_port_info[board_idx].sht; - probe_ent->host_flags = pdc_port_info[board_idx].host_flags; - probe_ent->pio_mask = pdc_port_info[board_idx].pio_mask; - probe_ent->mwdma_mask = pdc_port_info[board_idx].mwdma_mask; - probe_ent->udma_mask = pdc_port_info[board_idx].udma_mask; - probe_ent->port_ops = pdc_port_info[board_idx].port_ops; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->mmio_base = mmio_base; - - probe_ent->private_data = hpriv; - base += PDC_CHIP0_OFS; - - probe_ent->n_ports = 4; - pdc_sata_setup_port(&probe_ent->port[0], base + 0x200); - pdc_sata_setup_port(&probe_ent->port[1], base + 0x280); - pdc_sata_setup_port(&probe_ent->port[2], base + 0x300); - pdc_sata_setup_port(&probe_ent->port[3], base + 0x380); - - pci_set_master(pdev); - - /* initialize adapter */ - /* initialize local dimm */ - if (pdc20621_dimm_init(probe_ent)) { - rc = -ENOMEM; - goto err_out_iounmap_dimm; - } - pdc_20621_init(probe_ent); - - /* FIXME: check ata_device_add return value */ - ata_device_add(probe_ent); - kfree(probe_ent); - - return 0; - -err_out_iounmap_dimm: /* only get to this label if 20621 */ - kfree(hpriv); - pci_iounmap(pdev, dimm_mmio); -err_out_iounmap: - pci_iounmap(pdev, mmio_base); -err_out_free_ent: - kfree(probe_ent); -err_out_regions: - pci_release_regions(pdev); -err_out: - if (!pci_dev_busy) - pci_disable_device(pdev); - return rc; -} - - -static int __init pdc_sata_init(void) -{ - return pci_register_driver(&pdc_sata_pci_driver); -} - - -static void __exit pdc_sata_exit(void) -{ - pci_unregister_driver(&pdc_sata_pci_driver); -} - - -MODULE_AUTHOR("Jeff Garzik"); -MODULE_DESCRIPTION("Promise SATA low-level driver"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, pdc_sata_pci_tbl); -MODULE_VERSION(DRV_VERSION); - -module_init(pdc_sata_init); -module_exit(pdc_sata_exit); diff --git a/drivers/scsi/sata_uli.c b/drivers/scsi/sata_uli.c deleted file mode 100644 index 654aae2b25c..00000000000 --- a/drivers/scsi/sata_uli.c +++ /dev/null @@ -1,300 +0,0 @@ -/* - * sata_uli.c - ULi Electronics SATA - * - * - * 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 02139, USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - * Hardware documentation available under NDA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "sata_uli" -#define DRV_VERSION "1.0" - -enum { - uli_5289 = 0, - uli_5287 = 1, - uli_5281 = 2, - - uli_max_ports = 4, - - /* PCI configuration registers */ - ULI5287_BASE = 0x90, /* sata0 phy SCR registers */ - ULI5287_OFFS = 0x10, /* offset from sata0->sata1 phy regs */ - ULI5281_BASE = 0x60, /* sata0 phy SCR registers */ - ULI5281_OFFS = 0x60, /* offset from sata0->sata1 phy regs */ -}; - -struct uli_priv { - unsigned int scr_cfg_addr[uli_max_ports]; -}; - -static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static u32 uli_scr_read (struct ata_port *ap, unsigned int sc_reg); -static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); - -static const struct pci_device_id uli_pci_tbl[] = { - { PCI_VENDOR_ID_AL, 0x5289, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5289 }, - { PCI_VENDOR_ID_AL, 0x5287, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5287 }, - { PCI_VENDOR_ID_AL, 0x5281, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5281 }, - { } /* terminate list */ -}; - - -static struct pci_driver uli_pci_driver = { - .name = DRV_NAME, - .id_table = uli_pci_tbl, - .probe = uli_init_one, - .remove = ata_pci_remove_one, -}; - -static struct scsi_host_template uli_sht = { - .module = THIS_MODULE, - .name = DRV_NAME, - .ioctl = ata_scsi_ioctl, - .queuecommand = ata_scsi_queuecmd, - .can_queue = ATA_DEF_QUEUE, - .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = LIBATA_MAX_PRD, - .cmd_per_lun = ATA_SHT_CMD_PER_LUN, - .emulated = ATA_SHT_EMULATED, - .use_clustering = ATA_SHT_USE_CLUSTERING, - .proc_name = DRV_NAME, - .dma_boundary = ATA_DMA_BOUNDARY, - .slave_configure = ata_scsi_slave_config, - .slave_destroy = ata_scsi_slave_destroy, - .bios_param = ata_std_bios_param, -}; - -static const struct ata_port_operations uli_ops = { - .port_disable = ata_port_disable, - - .tf_load = ata_tf_load, - .tf_read = ata_tf_read, - .check_status = ata_check_status, - .exec_command = ata_exec_command, - .dev_select = ata_std_dev_select, - - .bmdma_setup = ata_bmdma_setup, - .bmdma_start = ata_bmdma_start, - .bmdma_stop = ata_bmdma_stop, - .bmdma_status = ata_bmdma_status, - .qc_prep = ata_qc_prep, - .qc_issue = ata_qc_issue_prot, - .data_xfer = ata_pio_data_xfer, - - .freeze = ata_bmdma_freeze, - .thaw = ata_bmdma_thaw, - .error_handler = ata_bmdma_error_handler, - .post_internal_cmd = ata_bmdma_post_internal_cmd, - - .irq_handler = ata_interrupt, - .irq_clear = ata_bmdma_irq_clear, - - .scr_read = uli_scr_read, - .scr_write = uli_scr_write, - - .port_start = ata_port_start, - .port_stop = ata_port_stop, - .host_stop = ata_host_stop, -}; - -static struct ata_port_info uli_port_info = { - .sht = &uli_sht, - .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, - .pio_mask = 0x1f, /* pio0-4 */ - .udma_mask = 0x7f, /* udma0-6 */ - .port_ops = &uli_ops, -}; - - -MODULE_AUTHOR("Peer Chen"); -MODULE_DESCRIPTION("low-level driver for ULi Electronics SATA controller"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, uli_pci_tbl); -MODULE_VERSION(DRV_VERSION); - -static unsigned int get_scr_cfg_addr(struct ata_port *ap, unsigned int sc_reg) -{ - struct uli_priv *hpriv = ap->host_set->private_data; - return hpriv->scr_cfg_addr[ap->port_no] + (4 * sc_reg); -} - -static u32 uli_scr_cfg_read (struct ata_port *ap, unsigned int sc_reg) -{ - struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); - unsigned int cfg_addr = get_scr_cfg_addr(ap, sc_reg); - u32 val; - - pci_read_config_dword(pdev, cfg_addr, &val); - return val; -} - -static void uli_scr_cfg_write (struct ata_port *ap, unsigned int scr, u32 val) -{ - struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); - unsigned int cfg_addr = get_scr_cfg_addr(ap, scr); - - pci_write_config_dword(pdev, cfg_addr, val); -} - -static u32 uli_scr_read (struct ata_port *ap, unsigned int sc_reg) -{ - if (sc_reg > SCR_CONTROL) - return 0xffffffffU; - - return uli_scr_cfg_read(ap, sc_reg); -} - -static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) -{ - if (sc_reg > SCR_CONTROL) //SCR_CONTROL=2, SCR_ERROR=1, SCR_STATUS=0 - return; - - uli_scr_cfg_write(ap, sc_reg, val); -} - -static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) -{ - static int printed_version; - struct ata_probe_ent *probe_ent; - struct ata_port_info *ppi; - int rc; - unsigned int board_idx = (unsigned int) ent->driver_data; - int pci_dev_busy = 0; - struct uli_priv *hpriv; - - if (!printed_version++) - dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n"); - - rc = pci_enable_device(pdev); - if (rc) - return rc; - - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pci_dev_busy = 1; - goto err_out; - } - - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out_regions; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out_regions; - - ppi = &uli_port_info; - probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); - if (!probe_ent) { - rc = -ENOMEM; - goto err_out_regions; - } - - hpriv = kzalloc(sizeof(*hpriv), GFP_KERNEL); - if (!hpriv) { - rc = -ENOMEM; - goto err_out_probe_ent; - } - - probe_ent->private_data = hpriv; - - switch (board_idx) { - case uli_5287: - hpriv->scr_cfg_addr[0] = ULI5287_BASE; - hpriv->scr_cfg_addr[1] = ULI5287_BASE + ULI5287_OFFS; - probe_ent->n_ports = 4; - - probe_ent->port[2].cmd_addr = pci_resource_start(pdev, 0) + 8; - probe_ent->port[2].altstatus_addr = - probe_ent->port[2].ctl_addr = - (pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS) + 4; - probe_ent->port[2].bmdma_addr = pci_resource_start(pdev, 4) + 16; - hpriv->scr_cfg_addr[2] = ULI5287_BASE + ULI5287_OFFS*4; - - probe_ent->port[3].cmd_addr = pci_resource_start(pdev, 2) + 8; - probe_ent->port[3].altstatus_addr = - probe_ent->port[3].ctl_addr = - (pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS) + 4; - probe_ent->port[3].bmdma_addr = pci_resource_start(pdev, 4) + 24; - hpriv->scr_cfg_addr[3] = ULI5287_BASE + ULI5287_OFFS*5; - - ata_std_ports(&probe_ent->port[2]); - ata_std_ports(&probe_ent->port[3]); - break; - - case uli_5289: - hpriv->scr_cfg_addr[0] = ULI5287_BASE; - hpriv->scr_cfg_addr[1] = ULI5287_BASE + ULI5287_OFFS; - break; - - case uli_5281: - hpriv->scr_cfg_addr[0] = ULI5281_BASE; - hpriv->scr_cfg_addr[1] = ULI5281_BASE + ULI5281_OFFS; - break; - - default: - BUG(); - break; - } - - pci_set_master(pdev); - pci_intx(pdev, 1); - - /* FIXME: check ata_device_add return value */ - ata_device_add(probe_ent); - kfree(probe_ent); - - return 0; - -err_out_probe_ent: - kfree(probe_ent); -err_out_regions: - pci_release_regions(pdev); -err_out: - if (!pci_dev_busy) - pci_disable_device(pdev); - return rc; - -} - -static int __init uli_init(void) -{ - return pci_register_driver(&uli_pci_driver); -} - -static void __exit uli_exit(void) -{ - pci_unregister_driver(&uli_pci_driver); -} - - -module_init(uli_init); -module_exit(uli_exit); diff --git a/drivers/scsi/sata_via.c b/drivers/scsi/sata_via.c deleted file mode 100644 index 0bf1dbea640..00000000000 --- a/drivers/scsi/sata_via.c +++ /dev/null @@ -1,394 +0,0 @@ -/* - * sata_via.c - VIA Serial ATA controllers - * - * Maintained by: Jeff Garzik - * Please ALWAYS copy linux-ide@vger.kernel.org - on emails. - * - * Copyright 2003-2004 Red Hat, Inc. All rights reserved. - * Copyright 2003-2004 Jeff Garzik - * - * - * 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 02139, USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - * Hardware documentation available under NDA. - * - * - * To-do list: - * - VT6421 PATA support - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "sata_via" -#define DRV_VERSION "2.0" - -enum board_ids_enum { - vt6420, - vt6421, -}; - -enum { - SATA_CHAN_ENAB = 0x40, /* SATA channel enable */ - SATA_INT_GATE = 0x41, /* SATA interrupt gating */ - SATA_NATIVE_MODE = 0x42, /* Native mode enable */ - SATA_PATA_SHARING = 0x49, /* PATA/SATA sharing func ctrl */ - - PORT0 = (1 << 1), - PORT1 = (1 << 0), - ALL_PORTS = PORT0 | PORT1, - N_PORTS = 2, - - NATIVE_MODE_ALL = (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4), - - SATA_EXT_PHY = (1 << 6), /* 0==use PATA, 1==ext phy */ - SATA_2DEV = (1 << 5), /* SATA is master/slave */ -}; - -static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static u32 svia_scr_read (struct ata_port *ap, unsigned int sc_reg); -static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); - -static const struct pci_device_id svia_pci_tbl[] = { - { 0x1106, 0x3149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt6420 }, - { 0x1106, 0x3249, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt6421 }, - - { } /* terminate list */ -}; - -static struct pci_driver svia_pci_driver = { - .name = DRV_NAME, - .id_table = svia_pci_tbl, - .probe = svia_init_one, - .remove = ata_pci_remove_one, -}; - -static struct scsi_host_template svia_sht = { - .module = THIS_MODULE, - .name = DRV_NAME, - .ioctl = ata_scsi_ioctl, - .queuecommand = ata_scsi_queuecmd, - .can_queue = ATA_DEF_QUEUE, - .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = LIBATA_MAX_PRD, - .cmd_per_lun = ATA_SHT_CMD_PER_LUN, - .emulated = ATA_SHT_EMULATED, - .use_clustering = ATA_SHT_USE_CLUSTERING, - .proc_name = DRV_NAME, - .dma_boundary = ATA_DMA_BOUNDARY, - .slave_configure = ata_scsi_slave_config, - .slave_destroy = ata_scsi_slave_destroy, - .bios_param = ata_std_bios_param, -}; - -static const struct ata_port_operations svia_sata_ops = { - .port_disable = ata_port_disable, - - .tf_load = ata_tf_load, - .tf_read = ata_tf_read, - .check_status = ata_check_status, - .exec_command = ata_exec_command, - .dev_select = ata_std_dev_select, - - .bmdma_setup = ata_bmdma_setup, - .bmdma_start = ata_bmdma_start, - .bmdma_stop = ata_bmdma_stop, - .bmdma_status = ata_bmdma_status, - - .qc_prep = ata_qc_prep, - .qc_issue = ata_qc_issue_prot, - .data_xfer = ata_pio_data_xfer, - - .freeze = ata_bmdma_freeze, - .thaw = ata_bmdma_thaw, - .error_handler = ata_bmdma_error_handler, - .post_internal_cmd = ata_bmdma_post_internal_cmd, - - .irq_handler = ata_interrupt, - .irq_clear = ata_bmdma_irq_clear, - - .scr_read = svia_scr_read, - .scr_write = svia_scr_write, - - .port_start = ata_port_start, - .port_stop = ata_port_stop, - .host_stop = ata_host_stop, -}; - -static struct ata_port_info svia_port_info = { - .sht = &svia_sht, - .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, - .pio_mask = 0x1f, - .mwdma_mask = 0x07, - .udma_mask = 0x7f, - .port_ops = &svia_sata_ops, -}; - -MODULE_AUTHOR("Jeff Garzik"); -MODULE_DESCRIPTION("SCSI low-level driver for VIA SATA controllers"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, svia_pci_tbl); -MODULE_VERSION(DRV_VERSION); - -static u32 svia_scr_read (struct ata_port *ap, unsigned int sc_reg) -{ - if (sc_reg > SCR_CONTROL) - return 0xffffffffU; - return inl(ap->ioaddr.scr_addr + (4 * sc_reg)); -} - -static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) -{ - if (sc_reg > SCR_CONTROL) - return; - outl(val, ap->ioaddr.scr_addr + (4 * sc_reg)); -} - -static const unsigned int svia_bar_sizes[] = { - 8, 4, 8, 4, 16, 256 -}; - -static const unsigned int vt6421_bar_sizes[] = { - 16, 16, 16, 16, 32, 128 -}; - -static unsigned long svia_scr_addr(unsigned long addr, unsigned int port) -{ - return addr + (port * 128); -} - -static unsigned long vt6421_scr_addr(unsigned long addr, unsigned int port) -{ - return addr + (port * 64); -} - -static void vt6421_init_addrs(struct ata_probe_ent *probe_ent, - struct pci_dev *pdev, - unsigned int port) -{ - unsigned long reg_addr = pci_resource_start(pdev, port); - unsigned long bmdma_addr = pci_resource_start(pdev, 4) + (port * 8); - unsigned long scr_addr; - - probe_ent->port[port].cmd_addr = reg_addr; - probe_ent->port[port].altstatus_addr = - probe_ent->port[port].ctl_addr = (reg_addr + 8) | ATA_PCI_CTL_OFS; - probe_ent->port[port].bmdma_addr = bmdma_addr; - - scr_addr = vt6421_scr_addr(pci_resource_start(pdev, 5), port); - probe_ent->port[port].scr_addr = scr_addr; - - ata_std_ports(&probe_ent->port[port]); -} - -static struct ata_probe_ent *vt6420_init_probe_ent(struct pci_dev *pdev) -{ - struct ata_probe_ent *probe_ent; - struct ata_port_info *ppi = &svia_port_info; - - probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); - if (!probe_ent) - return NULL; - - probe_ent->port[0].scr_addr = - svia_scr_addr(pci_resource_start(pdev, 5), 0); - probe_ent->port[1].scr_addr = - svia_scr_addr(pci_resource_start(pdev, 5), 1); - - return probe_ent; -} - -static struct ata_probe_ent *vt6421_init_probe_ent(struct pci_dev *pdev) -{ - struct ata_probe_ent *probe_ent; - unsigned int i; - - probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); - if (!probe_ent) - return NULL; - - memset(probe_ent, 0, sizeof(*probe_ent)); - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - probe_ent->sht = &svia_sht; - probe_ent->host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY; - probe_ent->port_ops = &svia_sata_ops; - probe_ent->n_ports = N_PORTS; - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->pio_mask = 0x1f; - probe_ent->mwdma_mask = 0x07; - probe_ent->udma_mask = 0x7f; - - for (i = 0; i < N_PORTS; i++) - vt6421_init_addrs(probe_ent, pdev, i); - - return probe_ent; -} - -static void svia_configure(struct pci_dev *pdev) -{ - u8 tmp8; - - pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &tmp8); - dev_printk(KERN_INFO, &pdev->dev, "routed to hard irq line %d\n", - (int) (tmp8 & 0xf0) == 0xf0 ? 0 : tmp8 & 0x0f); - - /* make sure SATA channels are enabled */ - pci_read_config_byte(pdev, SATA_CHAN_ENAB, &tmp8); - if ((tmp8 & ALL_PORTS) != ALL_PORTS) { - dev_printk(KERN_DEBUG, &pdev->dev, - "enabling SATA channels (0x%x)\n", - (int) tmp8); - tmp8 |= ALL_PORTS; - pci_write_config_byte(pdev, SATA_CHAN_ENAB, tmp8); - } - - /* make sure interrupts for each channel sent to us */ - pci_read_config_byte(pdev, SATA_INT_GATE, &tmp8); - if ((tmp8 & ALL_PORTS) != ALL_PORTS) { - dev_printk(KERN_DEBUG, &pdev->dev, - "enabling SATA channel interrupts (0x%x)\n", - (int) tmp8); - tmp8 |= ALL_PORTS; - pci_write_config_byte(pdev, SATA_INT_GATE, tmp8); - } - - /* make sure native mode is enabled */ - pci_read_config_byte(pdev, SATA_NATIVE_MODE, &tmp8); - if ((tmp8 & NATIVE_MODE_ALL) != NATIVE_MODE_ALL) { - dev_printk(KERN_DEBUG, &pdev->dev, - "enabling SATA channel native mode (0x%x)\n", - (int) tmp8); - tmp8 |= NATIVE_MODE_ALL; - pci_write_config_byte(pdev, SATA_NATIVE_MODE, tmp8); - } -} - -static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) -{ - static int printed_version; - unsigned int i; - int rc; - struct ata_probe_ent *probe_ent; - int board_id = (int) ent->driver_data; - const int *bar_sizes; - int pci_dev_busy = 0; - u8 tmp8; - - if (!printed_version++) - dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); - - rc = pci_enable_device(pdev); - if (rc) - return rc; - - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pci_dev_busy = 1; - goto err_out; - } - - if (board_id == vt6420) { - pci_read_config_byte(pdev, SATA_PATA_SHARING, &tmp8); - if (tmp8 & SATA_2DEV) { - dev_printk(KERN_ERR, &pdev->dev, - "SATA master/slave not supported (0x%x)\n", - (int) tmp8); - rc = -EIO; - goto err_out_regions; - } - - bar_sizes = &svia_bar_sizes[0]; - } else { - bar_sizes = &vt6421_bar_sizes[0]; - } - - for (i = 0; i < ARRAY_SIZE(svia_bar_sizes); i++) - if ((pci_resource_start(pdev, i) == 0) || - (pci_resource_len(pdev, i) < bar_sizes[i])) { - dev_printk(KERN_ERR, &pdev->dev, - "invalid PCI BAR %u (sz 0x%llx, val 0x%llx)\n", - i, - (unsigned long long)pci_resource_start(pdev, i), - (unsigned long long)pci_resource_len(pdev, i)); - rc = -ENODEV; - goto err_out_regions; - } - - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out_regions; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out_regions; - - if (board_id == vt6420) - probe_ent = vt6420_init_probe_ent(pdev); - else - probe_ent = vt6421_init_probe_ent(pdev); - - if (!probe_ent) { - dev_printk(KERN_ERR, &pdev->dev, "out of memory\n"); - rc = -ENOMEM; - goto err_out_regions; - } - - svia_configure(pdev); - - pci_set_master(pdev); - - /* FIXME: check ata_device_add return value */ - ata_device_add(probe_ent); - kfree(probe_ent); - - return 0; - -err_out_regions: - pci_release_regions(pdev); -err_out: - if (!pci_dev_busy) - pci_disable_device(pdev); - return rc; -} - -static int __init svia_init(void) -{ - return pci_register_driver(&svia_pci_driver); -} - -static void __exit svia_exit(void) -{ - pci_unregister_driver(&svia_pci_driver); -} - -module_init(svia_init); -module_exit(svia_exit); - diff --git a/drivers/scsi/sata_vsc.c b/drivers/scsi/sata_vsc.c deleted file mode 100644 index 4c69a705a48..00000000000 --- a/drivers/scsi/sata_vsc.c +++ /dev/null @@ -1,482 +0,0 @@ -/* - * sata_vsc.c - Vitesse VSC7174 4 port DPA SATA - * - * Maintained by: Jeremy Higdon @ SGI - * Please ALWAYS copy linux-ide@vger.kernel.org - * on emails. - * - * Copyright 2004 SGI - * - * Bits from Jeff Garzik, Copyright RedHat, Inc. - * - * - * 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 02139, USA. - * - * - * libata documentation is available via 'make {ps|pdf}docs', - * as Documentation/DocBook/libata.* - * - * Vitesse hardware documentation presumably available under NDA. - * Intel 31244 (same hardware interface) documentation presumably - * available from http://developer.intel.com/ - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "sata_vsc" -#define DRV_VERSION "2.0" - -enum { - /* Interrupt register offsets (from chip base address) */ - VSC_SATA_INT_STAT_OFFSET = 0x00, - VSC_SATA_INT_MASK_OFFSET = 0x04, - - /* Taskfile registers offsets */ - VSC_SATA_TF_CMD_OFFSET = 0x00, - VSC_SATA_TF_DATA_OFFSET = 0x00, - VSC_SATA_TF_ERROR_OFFSET = 0x04, - VSC_SATA_TF_FEATURE_OFFSET = 0x06, - VSC_SATA_TF_NSECT_OFFSET = 0x08, - VSC_SATA_TF_LBAL_OFFSET = 0x0c, - VSC_SATA_TF_LBAM_OFFSET = 0x10, - VSC_SATA_TF_LBAH_OFFSET = 0x14, - VSC_SATA_TF_DEVICE_OFFSET = 0x18, - VSC_SATA_TF_STATUS_OFFSET = 0x1c, - VSC_SATA_TF_COMMAND_OFFSET = 0x1d, - VSC_SATA_TF_ALTSTATUS_OFFSET = 0x28, - VSC_SATA_TF_CTL_OFFSET = 0x29, - - /* DMA base */ - VSC_SATA_UP_DESCRIPTOR_OFFSET = 0x64, - VSC_SATA_UP_DATA_BUFFER_OFFSET = 0x6C, - VSC_SATA_DMA_CMD_OFFSET = 0x70, - - /* SCRs base */ - VSC_SATA_SCR_STATUS_OFFSET = 0x100, - VSC_SATA_SCR_ERROR_OFFSET = 0x104, - VSC_SATA_SCR_CONTROL_OFFSET = 0x108, - - /* Port stride */ - VSC_SATA_PORT_OFFSET = 0x200, - - /* Error interrupt status bit offsets */ - VSC_SATA_INT_ERROR_CRC = 0x40, - VSC_SATA_INT_ERROR_T = 0x20, - VSC_SATA_INT_ERROR_P = 0x10, - VSC_SATA_INT_ERROR_R = 0x8, - VSC_SATA_INT_ERROR_E = 0x4, - VSC_SATA_INT_ERROR_M = 0x2, - VSC_SATA_INT_PHY_CHANGE = 0x1, - VSC_SATA_INT_ERROR = (VSC_SATA_INT_ERROR_CRC | VSC_SATA_INT_ERROR_T | \ - VSC_SATA_INT_ERROR_P | VSC_SATA_INT_ERROR_R | \ - VSC_SATA_INT_ERROR_E | VSC_SATA_INT_ERROR_M | \ - VSC_SATA_INT_PHY_CHANGE), -}; - - -#define is_vsc_sata_int_err(port_idx, int_status) \ - (int_status & (VSC_SATA_INT_ERROR << (8 * port_idx))) - - -static u32 vsc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg) -{ - if (sc_reg > SCR_CONTROL) - return 0xffffffffU; - return readl((void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4)); -} - - -static void vsc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, - u32 val) -{ - if (sc_reg > SCR_CONTROL) - return; - writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4)); -} - - -static void vsc_intr_mask_update(struct ata_port *ap, u8 ctl) -{ - void __iomem *mask_addr; - u8 mask; - - mask_addr = ap->host_set->mmio_base + - VSC_SATA_INT_MASK_OFFSET + ap->port_no; - mask = readb(mask_addr); - if (ctl & ATA_NIEN) - mask |= 0x80; - else - mask &= 0x7F; - writeb(mask, mask_addr); -} - - -static void vsc_sata_tf_load(struct ata_port *ap, const struct ata_taskfile *tf) -{ - struct ata_ioports *ioaddr = &ap->ioaddr; - unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; - - /* - * The only thing the ctl register is used for is SRST. - * That is not enabled or disabled via tf_load. - * However, if ATA_NIEN is changed, then we need to change the interrupt register. - */ - if ((tf->ctl & ATA_NIEN) != (ap->last_ctl & ATA_NIEN)) { - ap->last_ctl = tf->ctl; - vsc_intr_mask_update(ap, tf->ctl & ATA_NIEN); - } - if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) { - writew(tf->feature | (((u16)tf->hob_feature) << 8), ioaddr->feature_addr); - writew(tf->nsect | (((u16)tf->hob_nsect) << 8), ioaddr->nsect_addr); - writew(tf->lbal | (((u16)tf->hob_lbal) << 8), ioaddr->lbal_addr); - writew(tf->lbam | (((u16)tf->hob_lbam) << 8), ioaddr->lbam_addr); - writew(tf->lbah | (((u16)tf->hob_lbah) << 8), ioaddr->lbah_addr); - } else if (is_addr) { - writew(tf->feature, ioaddr->feature_addr); - writew(tf->nsect, ioaddr->nsect_addr); - writew(tf->lbal, ioaddr->lbal_addr); - writew(tf->lbam, ioaddr->lbam_addr); - writew(tf->lbah, ioaddr->lbah_addr); - } - - if (tf->flags & ATA_TFLAG_DEVICE) - writeb(tf->device, ioaddr->device_addr); - - ata_wait_idle(ap); -} - - -static void vsc_sata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) -{ - struct ata_ioports *ioaddr = &ap->ioaddr; - u16 nsect, lbal, lbam, lbah, feature; - - tf->command = ata_check_status(ap); - tf->device = readw(ioaddr->device_addr); - feature = readw(ioaddr->error_addr); - nsect = readw(ioaddr->nsect_addr); - lbal = readw(ioaddr->lbal_addr); - lbam = readw(ioaddr->lbam_addr); - lbah = readw(ioaddr->lbah_addr); - - tf->feature = feature; - tf->nsect = nsect; - tf->lbal = lbal; - tf->lbam = lbam; - tf->lbah = lbah; - - if (tf->flags & ATA_TFLAG_LBA48) { - tf->hob_feature = feature >> 8; - tf->hob_nsect = nsect >> 8; - tf->hob_lbal = lbal >> 8; - tf->hob_lbam = lbam >> 8; - tf->hob_lbah = lbah >> 8; - } -} - - -/* - * vsc_sata_interrupt - * - * Read the interrupt register and process for the devices that have them pending. - */ -static irqreturn_t vsc_sata_interrupt (int irq, void *dev_instance, - struct pt_regs *regs) -{ - struct ata_host_set *host_set = dev_instance; - unsigned int i; - unsigned int handled = 0; - u32 int_status; - - spin_lock(&host_set->lock); - - int_status = readl(host_set->mmio_base + VSC_SATA_INT_STAT_OFFSET); - - for (i = 0; i < host_set->n_ports; i++) { - if (int_status & ((u32) 0xFF << (8 * i))) { - struct ata_port *ap; - - ap = host_set->ports[i]; - - if (is_vsc_sata_int_err(i, int_status)) { - u32 err_status; - printk(KERN_DEBUG "%s: ignoring interrupt(s)\n", __FUNCTION__); - err_status = ap ? vsc_sata_scr_read(ap, SCR_ERROR) : 0; - vsc_sata_scr_write(ap, SCR_ERROR, err_status); - handled++; - } - - if (ap && !(ap->flags & ATA_FLAG_DISABLED)) { - struct ata_queued_cmd *qc; - - qc = ata_qc_from_tag(ap, ap->active_tag); - if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) - handled += ata_host_intr(ap, qc); - else if (is_vsc_sata_int_err(i, int_status)) { - /* - * On some chips (i.e. Intel 31244), an error - * interrupt will sneak in at initialization - * time (phy state changes). Clearing the SCR - * error register is not required, but it prevents - * the phy state change interrupts from recurring - * later. - */ - u32 err_status; - err_status = vsc_sata_scr_read(ap, SCR_ERROR); - printk(KERN_DEBUG "%s: clearing interrupt, " - "status %x; sata err status %x\n", - __FUNCTION__, - int_status, err_status); - vsc_sata_scr_write(ap, SCR_ERROR, err_status); - /* Clear interrupt status */ - ata_chk_status(ap); - handled++; - } - } - } - } - - spin_unlock(&host_set->lock); - - return IRQ_RETVAL(handled); -} - - -static struct scsi_host_template vsc_sata_sht = { - .module = THIS_MODULE, - .name = DRV_NAME, - .ioctl = ata_scsi_ioctl, - .queuecommand = ata_scsi_queuecmd, - .can_queue = ATA_DEF_QUEUE, - .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = LIBATA_MAX_PRD, - .cmd_per_lun = ATA_SHT_CMD_PER_LUN, - .emulated = ATA_SHT_EMULATED, - .use_clustering = ATA_SHT_USE_CLUSTERING, - .proc_name = DRV_NAME, - .dma_boundary = ATA_DMA_BOUNDARY, - .slave_configure = ata_scsi_slave_config, - .slave_destroy = ata_scsi_slave_destroy, - .bios_param = ata_std_bios_param, -}; - - -static const struct ata_port_operations vsc_sata_ops = { - .port_disable = ata_port_disable, - .tf_load = vsc_sata_tf_load, - .tf_read = vsc_sata_tf_read, - .exec_command = ata_exec_command, - .check_status = ata_check_status, - .dev_select = ata_std_dev_select, - .bmdma_setup = ata_bmdma_setup, - .bmdma_start = ata_bmdma_start, - .bmdma_stop = ata_bmdma_stop, - .bmdma_status = ata_bmdma_status, - .qc_prep = ata_qc_prep, - .qc_issue = ata_qc_issue_prot, - .data_xfer = ata_mmio_data_xfer, - .freeze = ata_bmdma_freeze, - .thaw = ata_bmdma_thaw, - .error_handler = ata_bmdma_error_handler, - .post_internal_cmd = ata_bmdma_post_internal_cmd, - .irq_handler = vsc_sata_interrupt, - .irq_clear = ata_bmdma_irq_clear, - .scr_read = vsc_sata_scr_read, - .scr_write = vsc_sata_scr_write, - .port_start = ata_port_start, - .port_stop = ata_port_stop, - .host_stop = ata_pci_host_stop, -}; - -static void __devinit vsc_sata_setup_port(struct ata_ioports *port, unsigned long base) -{ - port->cmd_addr = base + VSC_SATA_TF_CMD_OFFSET; - port->data_addr = base + VSC_SATA_TF_DATA_OFFSET; - port->error_addr = base + VSC_SATA_TF_ERROR_OFFSET; - port->feature_addr = base + VSC_SATA_TF_FEATURE_OFFSET; - port->nsect_addr = base + VSC_SATA_TF_NSECT_OFFSET; - port->lbal_addr = base + VSC_SATA_TF_LBAL_OFFSET; - port->lbam_addr = base + VSC_SATA_TF_LBAM_OFFSET; - port->lbah_addr = base + VSC_SATA_TF_LBAH_OFFSET; - port->device_addr = base + VSC_SATA_TF_DEVICE_OFFSET; - port->status_addr = base + VSC_SATA_TF_STATUS_OFFSET; - port->command_addr = base + VSC_SATA_TF_COMMAND_OFFSET; - port->altstatus_addr = base + VSC_SATA_TF_ALTSTATUS_OFFSET; - port->ctl_addr = base + VSC_SATA_TF_CTL_OFFSET; - port->bmdma_addr = base + VSC_SATA_DMA_CMD_OFFSET; - port->scr_addr = base + VSC_SATA_SCR_STATUS_OFFSET; - writel(0, base + VSC_SATA_UP_DESCRIPTOR_OFFSET); - writel(0, base + VSC_SATA_UP_DATA_BUFFER_OFFSET); -} - - -static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) -{ - static int printed_version; - struct ata_probe_ent *probe_ent = NULL; - unsigned long base; - int pci_dev_busy = 0; - void __iomem *mmio_base; - int rc; - - if (!printed_version++) - dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); - - rc = pci_enable_device(pdev); - if (rc) - return rc; - - /* - * Check if we have needed resource mapped. - */ - if (pci_resource_len(pdev, 0) == 0) { - rc = -ENODEV; - goto err_out; - } - - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pci_dev_busy = 1; - goto err_out; - } - - /* - * Use 32 bit DMA mask, because 64 bit address support is poor. - */ - rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); - if (rc) - goto err_out_regions; - rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); - if (rc) - goto err_out_regions; - - probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) { - rc = -ENOMEM; - goto err_out_regions; - } - memset(probe_ent, 0, sizeof(*probe_ent)); - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - mmio_base = pci_iomap(pdev, 0, 0); - if (mmio_base == NULL) { - rc = -ENOMEM; - goto err_out_free_ent; - } - base = (unsigned long) mmio_base; - - /* - * Due to a bug in the chip, the default cache line size can't be used - */ - pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x80); - - probe_ent->sht = &vsc_sata_sht; - probe_ent->host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO; - probe_ent->port_ops = &vsc_sata_ops; - probe_ent->n_ports = 4; - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->mmio_base = mmio_base; - - /* We don't care much about the PIO/UDMA masks, but the core won't like us - * if we don't fill these - */ - probe_ent->pio_mask = 0x1f; - probe_ent->mwdma_mask = 0x07; - probe_ent->udma_mask = 0x7f; - - /* We have 4 ports per PCI function */ - vsc_sata_setup_port(&probe_ent->port[0], base + 1 * VSC_SATA_PORT_OFFSET); - vsc_sata_setup_port(&probe_ent->port[1], base + 2 * VSC_SATA_PORT_OFFSET); - vsc_sata_setup_port(&probe_ent->port[2], base + 3 * VSC_SATA_PORT_OFFSET); - vsc_sata_setup_port(&probe_ent->port[3], base + 4 * VSC_SATA_PORT_OFFSET); - - pci_set_master(pdev); - - /* - * Config offset 0x98 is "Extended Control and Status Register 0" - * Default value is (1 << 28). All bits except bit 28 are reserved in - * DPA mode. If bit 28 is set, LED 0 reflects all ports' activity. - * If bit 28 is clear, each port has its own LED. - */ - pci_write_config_dword(pdev, 0x98, 0); - - /* FIXME: check ata_device_add return value */ - ata_device_add(probe_ent); - kfree(probe_ent); - - return 0; - -err_out_free_ent: - kfree(probe_ent); -err_out_regions: - pci_release_regions(pdev); -err_out: - if (!pci_dev_busy) - pci_disable_device(pdev); - return rc; -} - - -static const struct pci_device_id vsc_sata_pci_tbl[] = { - { PCI_VENDOR_ID_VITESSE, 0x7174, - PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 }, - { PCI_VENDOR_ID_INTEL, 0x3200, - PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 }, - { } /* terminate list */ -}; - - -static struct pci_driver vsc_sata_pci_driver = { - .name = DRV_NAME, - .id_table = vsc_sata_pci_tbl, - .probe = vsc_sata_init_one, - .remove = ata_pci_remove_one, -}; - - -static int __init vsc_sata_init(void) -{ - return pci_register_driver(&vsc_sata_pci_driver); -} - - -static void __exit vsc_sata_exit(void) -{ - pci_unregister_driver(&vsc_sata_pci_driver); -} - - -MODULE_AUTHOR("Jeremy Higdon"); -MODULE_DESCRIPTION("low-level driver for Vitesse VSC7174 SATA controller"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, vsc_sata_pci_tbl); -MODULE_VERSION(DRV_VERSION); - -module_init(vsc_sata_init); -module_exit(vsc_sata_exit); -- cgit v1.2.3 From 04846f25920d4b05d6040c531cc601049260db52 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Wed, 9 Aug 2006 17:31:16 +0200 Subject: [SCSI] limit recursion when flushing shost->starved_list Attached is a patch that should limit a possible recursion that can lead to a stack overflow like follows: Kernel stack overflow. CPU: 3 Not tainted Process zfcperp0.0.d819 (pid: 13897, task: 000000003e0d8cc8, ksp: 000000003499dbb8) Krnl PSW : 0404000180000000 000000000030f8b2 (get_device+0x12/0x48) Krnl GPRS: 00000000135a1980 000000000030f758 000000003ed6c1e8 0000000000000005 0000000000000000 000000000044a780 000000003dbf7000 0000000034e15800 000000003621c048 070000003499c108 000000003499c1a0 000000003ed6c000 0000000040895000 00000000408ab630 000000003499c0a0 000000003499c0a0 Krnl Code: a7 fb ff e8 a7 19 00 00 b9 02 00 22 e3 e0 f0 98 00 24 a7 84 Call Trace: ([<000000004089edc2>] scsi_request_fn+0x13e/0x650 [scsi_mod]) [<00000000002c5ff4>] blk_run_queue+0xd4/0x1a4 [<000000004089ff8c>] scsi_queue_insert+0x22c/0x2a4 [scsi_mod] [<000000004089779a>] scsi_dispatch_cmd+0x8a/0x3d0 [scsi_mod] [<000000004089f1ec>] scsi_request_fn+0x568/0x650 [scsi_mod] ... [<000000004089f1ec>] scsi_request_fn+0x568/0x650 [scsi_mod] [<00000000002c5ff4>] blk_run_queue+0xd4/0x1a4 [<000000004089ff8c>] scsi_queue_insert+0x22c/0x2a4 [scsi_mod] [<000000004089779a>] scsi_dispatch_cmd+0x8a/0x3d0 [scsi_mod] [<000000004089f1ec>] scsi_request_fn+0x568/0x650 [scsi_mod] [<00000000002c5ff4>] blk_run_queue+0xd4/0x1a4 [<000000004089fa9e>] scsi_run_host_queues+0x196/0x230 [scsi_mod] [<00000000409eba28>] zfcp_erp_thread+0x2638/0x3080 [zfcp] [<0000000000107462>] kernel_thread_starter+0x6/0xc [<000000000010745c>] kernel_thread_starter+0x0/0xc <0>Kernel panic - not syncing: Corrupt kernel stack, can't continue. This stack overflow occurred during tests on s390 using zfcp. Recursion depth for this panic was 19. Usually recursion between blk_run_queue and a request_fn is avoided using QUEUE_FLAG_REENTER. But this does not help if the scsi stack tries to flush the starved_list of a scsi_host. Limit recursion depth when flushing the starved_list of a scsi_host. Signed-off-by: Andreas Herrmann Signed-off-by: James Bottomley --- drivers/scsi/scsi_lib.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 077c1c69121..d6743b959a7 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -551,7 +551,15 @@ static void scsi_run_queue(struct request_queue *q) list_del_init(&sdev->starved_entry); spin_unlock_irqrestore(shost->host_lock, flags); - blk_run_queue(sdev->request_queue); + + if (test_bit(QUEUE_FLAG_REENTER, &q->queue_flags) && + !test_and_set_bit(QUEUE_FLAG_REENTER, + &sdev->request_queue->queue_flags)) { + blk_run_queue(sdev->request_queue); + clear_bit(QUEUE_FLAG_REENTER, + &sdev->request_queue->queue_flags); + } else + blk_run_queue(sdev->request_queue); spin_lock_irqsave(shost->host_lock, flags); if (unlikely(!list_empty(&sdev->starved_entry))) -- cgit v1.2.3 From c8f7b073e0e81499474a84ee2a90f77f7805c7f8 Mon Sep 17 00:00:00 2001 From: Mark Haverkamp Date: Thu, 3 Aug 2006 08:02:24 -0700 Subject: [SCSI] aacraid: interruptible ioctl Received from Mark Salyzyn This patch allows the FSACTL_SEND_LARGE_FIB, FSACTL_SENDFIB and FSACTL_SEND_RAW_SRB ioctl calls into the aacraid driver to be interruptible. Only necessary if the adapter and/or the management software has gone into some sort of misbehavior and the system is being rebooted, thus permitting the user management software applications to be killed relatively cleanly. The FIB queue resource is held out of the free queue until the adapter finally, if ever, completes the command. Signed-off-by: Mark Haverkamp Signed-off-by: James Bottomley --- drivers/scsi/aacraid/commctrl.c | 23 ++++++++++++++++++----- drivers/scsi/aacraid/commsup.c | 15 ++++++++++++--- drivers/scsi/aacraid/dpcsup.c | 10 ++++++++-- 3 files changed, 38 insertions(+), 10 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c index 255421de9d1..14d7aa9b7df 100644 --- a/drivers/scsi/aacraid/commctrl.c +++ b/drivers/scsi/aacraid/commctrl.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include /* ssleep prototype */ #include #include #include @@ -140,7 +140,8 @@ cleanup: fibptr->hw_fib_pa = hw_fib_pa; fibptr->hw_fib = hw_fib; } - aac_fib_free(fibptr); + if (retval != -EINTR) + aac_fib_free(fibptr); return retval; } @@ -621,7 +622,13 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg) actual_fibsize = sizeof (struct aac_srb) + (((user_srbcmd->sg.count & 0xff) - 1) * sizeof (struct sgentry)); if(actual_fibsize != fibsize){ // User made a mistake - should not continue - dprintk((KERN_DEBUG"aacraid: Bad Size specified in Raw SRB command\n")); + dprintk((KERN_DEBUG"aacraid: Bad Size specified in " + "Raw SRB command calculated fibsize=%d " + "user_srbcmd->sg.count=%d aac_srb=%d sgentry=%d " + "issued fibsize=%d\n", + actual_fibsize, user_srbcmd->sg.count, + sizeof(struct aac_srb), sizeof(struct sgentry), + fibsize)); rcode = -EINVAL; goto cleanup; } @@ -663,6 +670,10 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg) psg->count = cpu_to_le32(sg_indx+1); status = aac_fib_send(ScsiPortCommand, srbfib, actual_fibsize, FsaNormal, 1, 1, NULL, NULL); } + if (status == -EINTR) { + rcode = -EINTR; + goto cleanup; + } if (status != 0){ dprintk((KERN_DEBUG"aacraid: Could not send raw srb fib to hba\n")); @@ -696,8 +707,10 @@ cleanup: for(i=0; i <= sg_indx; i++){ kfree(sg_list[i]); } - aac_fib_complete(srbfib); - aac_fib_free(srbfib); + if (rcode != -EINTR) { + aac_fib_complete(srbfib); + aac_fib_free(srbfib); + } return rcode; } diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index 3f27419c66a..c67da132113 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -464,6 +464,8 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size, dprintk((KERN_DEBUG " hw_fib pa being sent=%lx\n",(ulong)fibptr->hw_fib_pa)); dprintk((KERN_DEBUG " fib being sent=%p\n",fibptr)); + if (!dev->queues) + return -ENODEV; q = &dev->queues->queue[AdapNormCmdQueue]; if(wait) @@ -527,8 +529,15 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size, } udelay(5); } - } else - down(&fibptr->event_wait); + } else if (down_interruptible(&fibptr->event_wait)) { + spin_lock_irqsave(&fibptr->event_lock, flags); + if (fibptr->done == 0) { + fibptr->done = 2; /* Tell interrupt we aborted */ + spin_unlock_irqrestore(&fibptr->event_lock, flags); + return -EINTR; + } + spin_unlock_irqrestore(&fibptr->event_lock, flags); + } BUG_ON(fibptr->done == 0); if((fibptr->flags & FIB_CONTEXT_FLAG_TIMED_OUT)){ @@ -795,7 +804,7 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) /* Sniff for container changes */ - if (!dev) + if (!dev || !dev->fsa_dev) return; container = (u32)-1; diff --git a/drivers/scsi/aacraid/dpcsup.c b/drivers/scsi/aacraid/dpcsup.c index b2a5c7262f3..8335f07b772 100644 --- a/drivers/scsi/aacraid/dpcsup.c +++ b/drivers/scsi/aacraid/dpcsup.c @@ -124,10 +124,15 @@ unsigned int aac_response_normal(struct aac_queue * q) } else { unsigned long flagv; spin_lock_irqsave(&fib->event_lock, flagv); - fib->done = 1; + if (!fib->done) + fib->done = 1; up(&fib->event_wait); spin_unlock_irqrestore(&fib->event_lock, flagv); FIB_COUNTER_INCREMENT(aac_config.NormalRecved); + if (fib->done == 2) { + aac_fib_complete(fib); + aac_fib_free(fib); + } } consumed++; spin_lock_irqsave(q->lock, flags); @@ -316,7 +321,8 @@ unsigned int aac_intr_normal(struct aac_dev * dev, u32 Index) unsigned long flagv; dprintk((KERN_INFO "event_wait up\n")); spin_lock_irqsave(&fib->event_lock, flagv); - fib->done = 1; + if (!fib->done) + fib->done = 1; up(&fib->event_wait); spin_unlock_irqrestore(&fib->event_lock, flagv); FIB_COUNTER_INCREMENT(aac_config.NormalRecved); -- cgit v1.2.3 From 8c23cd7457151fc8ace79ec700a8aeaa9fc5b3d9 Mon Sep 17 00:00:00 2001 From: Mark Haverkamp Date: Tue, 8 Aug 2006 08:52:14 -0700 Subject: [SCSI] aacraid: Restart adapter on firmware assert (Update 2) Received from Mark Salyzyn If the adapter should be in a blinkled (Firmware Assert) state when the driver loads, we will perform a warm restart of the Adapter Firmware to see if we can rescue the adapter. Possible causes of a blinkled can occur on some early release motherboard BIOSes, transitory PCI bus problems on embedded systems or non-x86 based architectures, transitory startup failures of early release drives or transitory hardware failures; some of which can bite the adapter later at runtime. Future enhancements will include recovery during runtime. Fixed extra whitespace space issue. Signed-off-by: Mark Haverkamp Signed-off-by: James Bottomley --- drivers/scsi/aacraid/aacraid.h | 1 + drivers/scsi/aacraid/rkt.c | 29 ++++++++++++++++++++++------- drivers/scsi/aacraid/rx.c | 29 ++++++++++++++++++++++------- 3 files changed, 45 insertions(+), 14 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index d0eecd4bec8..05f80982efa 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -1670,6 +1670,7 @@ extern struct aac_common aac_config; #define RCV_TEMP_READINGS 0x00000025 #define GET_COMM_PREFERRED_SETTINGS 0x00000026 #define IOP_RESET 0x00001000 +#define IOP_RESET_ALWAYS 0x00001001 #define RE_INIT_ADAPTER 0x000000ee /* diff --git a/drivers/scsi/aacraid/rkt.c b/drivers/scsi/aacraid/rkt.c index 458ea897fd7..f850c3a7cce 100644 --- a/drivers/scsi/aacraid/rkt.c +++ b/drivers/scsi/aacraid/rkt.c @@ -395,6 +395,25 @@ static int aac_rkt_send(struct fib * fib) return 0; } +static int aac_rkt_restart_adapter(struct aac_dev *dev) +{ + u32 var; + + printk(KERN_ERR "%s%d: adapter kernel panic'd.\n", + dev->name, dev->id); + + if (aac_rkt_check_health(dev) <= 0) + return 1; + if (rkt_sync_cmd(dev, IOP_RESET, 0, 0, 0, 0, 0, 0, + &var, NULL, NULL, NULL, NULL)) + return 1; + if (var != 0x00000001) + return 1; + if (rkt_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC) + return 1; + return 0; +} + /** * aac_rkt_init - initialize an i960 based AAC card * @dev: device to configure @@ -417,6 +436,9 @@ int aac_rkt_init(struct aac_dev *dev) /* * Check to see if the board panic'd while booting. */ + if (rkt_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC) + if (aac_rkt_restart_adapter(dev)) + goto error_iounmap; /* * Check to see if the board failed any self tests. */ @@ -431,13 +453,6 @@ int aac_rkt_init(struct aac_dev *dev) printk(KERN_ERR "%s%d: adapter monitor panic.\n", dev->name, instance); goto error_iounmap; } - /* - * Check to see if the board panic'd while booting. - */ - if (rkt_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC) { - printk(KERN_ERR "%s%d: adapter kernel panic'd.\n", dev->name, instance); - goto error_iounmap; - } start = jiffies; /* * Wait for the adapter to be up and running. Wait up to 3 minutes diff --git a/drivers/scsi/aacraid/rx.c b/drivers/scsi/aacraid/rx.c index 035018db69b..c715c4b2442 100644 --- a/drivers/scsi/aacraid/rx.c +++ b/drivers/scsi/aacraid/rx.c @@ -394,6 +394,25 @@ static int aac_rx_send(struct fib * fib) return 0; } +static int aac_rx_restart_adapter(struct aac_dev *dev) +{ + u32 var; + + printk(KERN_ERR "%s%d: adapter kernel panic'd.\n", + dev->name, dev->id); + + if (aac_rx_check_health(dev) <= 0) + return 1; + if (rx_sync_cmd(dev, IOP_RESET, 0, 0, 0, 0, 0, 0, + &var, NULL, NULL, NULL, NULL)) + return 1; + if (var != 0x00000001) + return 1; + if (rx_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC) + return 1; + return 0; +} + /** * aac_rx_init - initialize an i960 based AAC card * @dev: device to configure @@ -416,6 +435,9 @@ int aac_rx_init(struct aac_dev *dev) /* * Check to see if the board panic'd while booting. */ + if (rx_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC) + if (aac_rx_restart_adapter(dev)) + goto error_iounmap; /* * Check to see if the board failed any self tests. */ @@ -423,13 +445,6 @@ int aac_rx_init(struct aac_dev *dev) printk(KERN_ERR "%s%d: adapter self-test failed.\n", dev->name, instance); goto error_iounmap; } - /* - * Check to see if the board panic'd while booting. - */ - if (rx_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC) { - printk(KERN_ERR "%s%d: adapter kernel panic.\n", dev->name, instance); - goto error_iounmap; - } /* * Check to see if the monitor panic'd while booting. */ -- cgit v1.2.3 From 90ee346651524eb275405d410f5d3bb6765a2d93 Mon Sep 17 00:00:00 2001 From: Mark Haverkamp Date: Thu, 3 Aug 2006 08:03:07 -0700 Subject: [SCSI] aacraid: Check for unlikely errors Received from Mark Salyzyn The enclosed patch cleans up some code fragments, adds some paranoia (unproven causes of potential driver failures). Signed-off-by: Mark Haverkamp Signed-off-by: James Bottomley --- drivers/scsi/aacraid/aachba.c | 15 +++++++++++++-- drivers/scsi/aacraid/comminit.c | 2 +- drivers/scsi/aacraid/linit.c | 4 ++++ 3 files changed, 18 insertions(+), 3 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index 83b5c7d085f..699351c15cc 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -489,6 +489,8 @@ int aac_probe_container(struct aac_dev *dev, int cid) unsigned instance; fsa_dev_ptr = dev->fsa_dev; + if (!fsa_dev_ptr) + return -ENOMEM; instance = dev->scsi_host_ptr->unique_id; if (!(fibptr = aac_fib_alloc(dev))) @@ -1392,6 +1394,7 @@ static int aac_synchronize(struct scsi_cmnd *scsicmd, int cid) struct scsi_cmnd *cmd; struct scsi_device *sdev = scsicmd->device; int active = 0; + struct aac_dev *aac; unsigned long flags; /* @@ -1413,11 +1416,11 @@ static int aac_synchronize(struct scsi_cmnd *scsicmd, int cid) if (active) return SCSI_MLQUEUE_DEVICE_BUSY; + aac = (struct aac_dev *)scsicmd->device->host->hostdata; /* * Allocate and initialize a Fib */ - if (!(cmd_fibcontext = - aac_fib_alloc((struct aac_dev *)scsicmd->device->host->hostdata))) + if (!(cmd_fibcontext = aac_fib_alloc(aac))) return SCSI_MLQUEUE_HOST_BUSY; aac_fib_init(cmd_fibcontext); @@ -1470,6 +1473,8 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) struct aac_dev *dev = (struct aac_dev *)host->hostdata; struct fsa_dev_info *fsa_dev_ptr = dev->fsa_dev; + if (fsa_dev_ptr == NULL) + return -1; /* * If the bus, id or lun is out of range, return fail * Test does not apply to ID 16, the pseudo id for the controller @@ -1782,6 +1787,8 @@ static int query_disk(struct aac_dev *dev, void __user *arg) struct fsa_dev_info *fsa_dev_ptr; fsa_dev_ptr = dev->fsa_dev; + if (!fsa_dev_ptr) + return -ENODEV; if (copy_from_user(&qd, arg, sizeof (struct aac_query_disk))) return -EFAULT; if (qd.cnum == -1) @@ -1843,6 +1850,10 @@ static int delete_disk(struct aac_dev *dev, void __user *arg) struct fsa_dev_info *fsa_dev_ptr; fsa_dev_ptr = dev->fsa_dev; + if (!fsa_dev_ptr) + return -ENODEV; + if (!fsa_dev_ptr) + return -ENODEV; if (copy_from_user(&dd, arg, sizeof (struct aac_delete_disk))) return -EFAULT; diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c index 1cd3584ba7f..87a95509676 100644 --- a/drivers/scsi/aacraid/comminit.c +++ b/drivers/scsi/aacraid/comminit.c @@ -180,7 +180,7 @@ int aac_send_shutdown(struct aac_dev * dev) -2 /* Timeout silently */, 1, NULL, NULL); - if (status == 0) + if (status >= 0) aac_fib_complete(fibctx); aac_fib_free(fibctx); return status; diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index e42a479ce64..9d8b550a91c 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -1013,6 +1013,10 @@ static void __devexit aac_remove_one(struct pci_dev *pdev) list_del(&aac->entry); scsi_host_put(shost); pci_disable_device(pdev); + if (list_empty(&aac_devices)) { + unregister_chrdev(aac_cfg_major, "aac"); + aac_cfg_major = -1; + } } static struct pci_driver aac_pci_driver = { -- cgit v1.2.3 From 8c867b257d159ca04602d7087fa29f846785f9ea Mon Sep 17 00:00:00 2001 From: Mark Haverkamp Date: Thu, 3 Aug 2006 08:03:30 -0700 Subject: [SCSI] aacraid: Reset adapter in recovery timeout Received from Mark Salyzyn If the adapter is in blinkled (Firmware Assert) when error recovery timeout actions have been triggered, perform an adapter warm reset and restart the initialization. Signed-off-by: Mark Haverkamp Signed-off-by: James Bottomley --- drivers/scsi/aacraid/aachba.c | 39 ++++-- drivers/scsi/aacraid/aacraid.h | 4 +- drivers/scsi/aacraid/commctrl.c | 2 +- drivers/scsi/aacraid/commsup.c | 258 ++++++++++++++++++++++++++++++++++++++++ drivers/scsi/aacraid/linit.c | 14 +-- 5 files changed, 296 insertions(+), 21 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index 699351c15cc..37c55ddce21 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -175,7 +175,7 @@ MODULE_PARM_DESC(acbsize, "Request a specific adapter control block (FIB) size. * * Query config status, and commit the configuration if needed. */ -int aac_get_config_status(struct aac_dev *dev) +int aac_get_config_status(struct aac_dev *dev, int commit_flag) { int status = 0; struct fib * fibptr; @@ -219,7 +219,7 @@ int aac_get_config_status(struct aac_dev *dev) aac_fib_complete(fibptr); /* Send a CT_COMMIT_CONFIG to enable discovery of devices */ if (status >= 0) { - if (commit == 1) { + if ((commit == 1) || commit_flag) { struct aac_commit_config * dinfo; aac_fib_init(fibptr); dinfo = (struct aac_commit_config *) fib_data(fibptr); @@ -784,8 +784,9 @@ int aac_get_adapter_info(struct aac_dev* dev) dev->maximum_num_channels = le32_to_cpu(bus_info->BusCount); } - tmp = le32_to_cpu(dev->adapter_info.kernelrev); - printk(KERN_INFO "%s%d: kernel %d.%d-%d[%d] %.*s\n", + if (!dev->in_reset) { + tmp = le32_to_cpu(dev->adapter_info.kernelrev); + printk(KERN_INFO "%s%d: kernel %d.%d-%d[%d] %.*s\n", dev->name, dev->id, tmp>>24, @@ -794,20 +795,21 @@ int aac_get_adapter_info(struct aac_dev* dev) le32_to_cpu(dev->adapter_info.kernelbuild), (int)sizeof(dev->supplement_adapter_info.BuildDate), dev->supplement_adapter_info.BuildDate); - tmp = le32_to_cpu(dev->adapter_info.monitorrev); - printk(KERN_INFO "%s%d: monitor %d.%d-%d[%d]\n", + tmp = le32_to_cpu(dev->adapter_info.monitorrev); + printk(KERN_INFO "%s%d: monitor %d.%d-%d[%d]\n", dev->name, dev->id, tmp>>24,(tmp>>16)&0xff,tmp&0xff, le32_to_cpu(dev->adapter_info.monitorbuild)); - tmp = le32_to_cpu(dev->adapter_info.biosrev); - printk(KERN_INFO "%s%d: bios %d.%d-%d[%d]\n", + tmp = le32_to_cpu(dev->adapter_info.biosrev); + printk(KERN_INFO "%s%d: bios %d.%d-%d[%d]\n", dev->name, dev->id, tmp>>24,(tmp>>16)&0xff,tmp&0xff, le32_to_cpu(dev->adapter_info.biosbuild)); - if (le32_to_cpu(dev->adapter_info.serial[0]) != 0xBAD0) - printk(KERN_INFO "%s%d: serial %x\n", - dev->name, dev->id, - le32_to_cpu(dev->adapter_info.serial[0])); + if (le32_to_cpu(dev->adapter_info.serial[0]) != 0xBAD0) + printk(KERN_INFO "%s%d: serial %x\n", + dev->name, dev->id, + le32_to_cpu(dev->adapter_info.serial[0])); + } dev->nondasd_support = 0; dev->raid_scsi_mode = 0; @@ -1417,6 +1419,9 @@ static int aac_synchronize(struct scsi_cmnd *scsicmd, int cid) return SCSI_MLQUEUE_DEVICE_BUSY; aac = (struct aac_dev *)scsicmd->device->host->hostdata; + if (aac->in_reset) + return SCSI_MLQUEUE_HOST_BUSY; + /* * Allocate and initialize a Fib */ @@ -1504,6 +1509,8 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) case INQUIRY: case READ_CAPACITY: case TEST_UNIT_READY: + if (dev->in_reset) + return -1; spin_unlock_irq(host->host_lock); aac_probe_container(dev, cid); if ((fsa_dev_ptr[cid].valid & 1) == 0) @@ -1529,6 +1536,8 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) } } else { /* check for physical non-dasd devices */ if(dev->nondasd_support == 1){ + if (dev->in_reset) + return -1; return aac_send_srb_fib(scsicmd); } else { scsicmd->result = DID_NO_CONNECT << 16; @@ -1584,6 +1593,8 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) scsicmd->scsi_done(scsicmd); return 0; } + if (dev->in_reset) + return -1; setinqstr(dev, (void *) (inq_data.inqd_vid), fsa_dev_ptr[cid].type); inq_data.inqd_pdt = INQD_PDT_DA; /* Direct/random access device */ aac_internal_transfer(scsicmd, &inq_data, 0, sizeof(inq_data)); @@ -1739,6 +1750,8 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) case READ_10: case READ_12: case READ_16: + if (dev->in_reset) + return -1; /* * Hack to keep track of ordinal number of the device that * corresponds to a container. Needed to convert @@ -1757,6 +1770,8 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) case WRITE_10: case WRITE_12: case WRITE_16: + if (dev->in_reset) + return -1; return aac_write(scsicmd, cid); case SYNCHRONIZE_CACHE: diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index 05f80982efa..8924c183d9c 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -1029,6 +1029,7 @@ struct aac_dev init->InitStructRevision==cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION_4) u8 raw_io_64; u8 printf_enabled; + u8 in_reset; }; #define aac_adapter_interrupt(dev) \ @@ -1789,7 +1790,7 @@ void aac_consumer_free(struct aac_dev * dev, struct aac_queue * q, u32 qnum); int aac_fib_complete(struct fib * context); #define fib_data(fibctx) ((void *)(fibctx)->hw_fib->data) struct aac_dev *aac_init_adapter(struct aac_dev *dev); -int aac_get_config_status(struct aac_dev *dev); +int aac_get_config_status(struct aac_dev *dev, int commit_flag); int aac_get_containers(struct aac_dev *dev); int aac_scsi_cmd(struct scsi_cmnd *cmd); int aac_dev_ioctl(struct aac_dev *dev, int cmd, void __user *arg); @@ -1800,6 +1801,7 @@ int aac_sa_init(struct aac_dev *dev); unsigned int aac_response_normal(struct aac_queue * q); unsigned int aac_command_normal(struct aac_queue * q); unsigned int aac_intr_normal(struct aac_dev * dev, u32 Index); +int aac_check_health(struct aac_dev * dev); int aac_command_thread(void *data); int aac_close_fib_context(struct aac_dev * dev, struct aac_fib_context *fibctx); int aac_fib_adapter_complete(struct fib * fibptr, unsigned short size); diff --git a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c index 14d7aa9b7df..da1d3a9212f 100644 --- a/drivers/scsi/aacraid/commctrl.c +++ b/drivers/scsi/aacraid/commctrl.c @@ -298,7 +298,7 @@ return_fib: spin_unlock_irqrestore(&dev->fib_lock, flags); /* If someone killed the AIF aacraid thread, restart it */ status = !dev->aif_thread; - if (status && dev->queues && dev->fsa_dev) { + if (status && !dev->in_reset && dev->queues && dev->fsa_dev) { /* Be paranoid, be very paranoid! */ kthread_stop(dev->thread); ssleep(1); diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index c67da132113..53add53be0b 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -40,8 +40,10 @@ #include #include #include +#include #include #include +#include #include #include "aacraid.h" @@ -1054,6 +1056,262 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) } +static int _aac_reset_adapter(struct aac_dev *aac) +{ + int index, quirks; + u32 ret; + int retval; + struct Scsi_Host *host; + struct scsi_device *dev; + struct scsi_cmnd *command; + struct scsi_cmnd *command_list; + + /* + * Assumptions: + * - host is locked. + * - in_reset is asserted, so no new i/o is getting to the + * card. + * - The card is dead. + */ + host = aac->scsi_host_ptr; + scsi_block_requests(host); + aac_adapter_disable_int(aac); + spin_unlock_irq(host->host_lock); + kthread_stop(aac->thread); + + /* + * If a positive health, means in a known DEAD PANIC + * state and the adapter could be reset to `try again'. + */ + retval = aac_adapter_check_health(aac); + if (retval == 0) + retval = aac_adapter_sync_cmd(aac, IOP_RESET_ALWAYS, + 0, 0, 0, 0, 0, 0, &ret, NULL, NULL, NULL, NULL); + if (retval) + retval = aac_adapter_sync_cmd(aac, IOP_RESET, + 0, 0, 0, 0, 0, 0, &ret, NULL, NULL, NULL, NULL); + + if (retval) + goto out; + if (ret != 0x00000001) { + retval = -ENODEV; + goto out; + } + + index = aac->cardtype; + + /* + * Re-initialize the adapter, first free resources, then carefully + * apply the initialization sequence to come back again. Only risk + * is a change in Firmware dropping cache, it is assumed the caller + * will ensure that i/o is queisced and the card is flushed in that + * case. + */ + aac_fib_map_free(aac); + aac->hw_fib_va = NULL; + aac->hw_fib_pa = 0; + pci_free_consistent(aac->pdev, aac->comm_size, aac->comm_addr, aac->comm_phys); + aac->comm_addr = NULL; + aac->comm_phys = 0; + kfree(aac->queues); + aac->queues = NULL; + free_irq(aac->pdev->irq, aac); + kfree(aac->fsa_dev); + aac->fsa_dev = NULL; + if (aac_get_driver_ident(index)->quirks & AAC_QUIRK_31BIT) { + if (((retval = pci_set_dma_mask(aac->pdev, DMA_32BIT_MASK))) || + ((retval = pci_set_consistent_dma_mask(aac->pdev, DMA_32BIT_MASK)))) + goto out; + } else { + if (((retval = pci_set_dma_mask(aac->pdev, 0x7FFFFFFFULL))) || + ((retval = pci_set_consistent_dma_mask(aac->pdev, 0x7FFFFFFFULL)))) + goto out; + } + if ((retval = (*(aac_get_driver_ident(index)->init))(aac))) + goto out; + if (aac_get_driver_ident(index)->quirks & AAC_QUIRK_31BIT) + if ((retval = pci_set_dma_mask(aac->pdev, DMA_32BIT_MASK))) + goto out; + aac->thread = kthread_run(aac_command_thread, aac, aac->name); + if (IS_ERR(aac->thread)) { + retval = PTR_ERR(aac->thread); + goto out; + } + (void)aac_get_adapter_info(aac); + quirks = aac_get_driver_ident(index)->quirks; + if ((quirks & AAC_QUIRK_34SG) && (host->sg_tablesize > 34)) { + host->sg_tablesize = 34; + host->max_sectors = (host->sg_tablesize * 8) + 112; + } + if ((quirks & AAC_QUIRK_17SG) && (host->sg_tablesize > 17)) { + host->sg_tablesize = 17; + host->max_sectors = (host->sg_tablesize * 8) + 112; + } + aac_get_config_status(aac, 1); + aac_get_containers(aac); + /* + * This is where the assumption that the Adapter is quiesced + * is important. + */ + command_list = NULL; + __shost_for_each_device(dev, host) { + unsigned long flags; + spin_lock_irqsave(&dev->list_lock, flags); + list_for_each_entry(command, &dev->cmd_list, list) + if (command->SCp.phase == AAC_OWNER_FIRMWARE) { + command->SCp.buffer = (struct scatterlist *)command_list; + command_list = command; + } + spin_unlock_irqrestore(&dev->list_lock, flags); + } + while ((command = command_list)) { + command_list = (struct scsi_cmnd *)command->SCp.buffer; + command->SCp.buffer = NULL; + command->result = DID_OK << 16 + | COMMAND_COMPLETE << 8 + | SAM_STAT_TASK_SET_FULL; + command->SCp.phase = AAC_OWNER_ERROR_HANDLER; + command->scsi_done(command); + } + retval = 0; + +out: + aac->in_reset = 0; + scsi_unblock_requests(host); + spin_lock_irq(host->host_lock); + return retval; +} + +int aac_check_health(struct aac_dev * aac) +{ + int BlinkLED; + unsigned long time_now, flagv = 0; + struct list_head * entry; + struct Scsi_Host * host; + + /* Extending the scope of fib_lock slightly to protect aac->in_reset */ + if (spin_trylock_irqsave(&aac->fib_lock, flagv) == 0) + return 0; + + if (aac->in_reset || !(BlinkLED = aac_adapter_check_health(aac))) { + spin_unlock_irqrestore(&aac->fib_lock, flagv); + return 0; /* OK */ + } + + aac->in_reset = 1; + + /* Fake up an AIF: + * aac_aifcmd.command = AifCmdEventNotify = 1 + * aac_aifcmd.seqnum = 0xFFFFFFFF + * aac_aifcmd.data[0] = AifEnExpEvent = 23 + * aac_aifcmd.data[1] = AifExeFirmwarePanic = 3 + * aac.aifcmd.data[2] = AifHighPriority = 3 + * aac.aifcmd.data[3] = BlinkLED + */ + + time_now = jiffies/HZ; + entry = aac->fib_list.next; + + /* + * For each Context that is on the + * fibctxList, make a copy of the + * fib, and then set the event to wake up the + * thread that is waiting for it. + */ + while (entry != &aac->fib_list) { + /* + * Extract the fibctx + */ + struct aac_fib_context *fibctx = list_entry(entry, struct aac_fib_context, next); + struct hw_fib * hw_fib; + struct fib * fib; + /* + * Check if the queue is getting + * backlogged + */ + if (fibctx->count > 20) { + /* + * It's *not* jiffies folks, + * but jiffies / HZ, so do not + * panic ... + */ + u32 time_last = fibctx->jiffies; + /* + * Has it been > 2 minutes + * since the last read off + * the queue? + */ + if ((time_now - time_last) > aif_timeout) { + entry = entry->next; + aac_close_fib_context(aac, fibctx); + continue; + } + } + /* + * Warning: no sleep allowed while + * holding spinlock + */ + hw_fib = kmalloc(sizeof(struct hw_fib), GFP_ATOMIC); + fib = kmalloc(sizeof(struct fib), GFP_ATOMIC); + if (fib && hw_fib) { + struct aac_aifcmd * aif; + + memset(hw_fib, 0, sizeof(struct hw_fib)); + memset(fib, 0, sizeof(struct fib)); + fib->hw_fib = hw_fib; + fib->dev = aac; + aac_fib_init(fib); + fib->type = FSAFS_NTC_FIB_CONTEXT; + fib->size = sizeof (struct fib); + fib->data = hw_fib->data; + aif = (struct aac_aifcmd *)hw_fib->data; + aif->command = cpu_to_le32(AifCmdEventNotify); + aif->seqnum = cpu_to_le32(0xFFFFFFFF); + aif->data[0] = cpu_to_le32(AifEnExpEvent); + aif->data[1] = cpu_to_le32(AifExeFirmwarePanic); + aif->data[2] = cpu_to_le32(AifHighPriority); + aif->data[3] = cpu_to_le32(BlinkLED); + + /* + * Put the FIB onto the + * fibctx's fibs + */ + list_add_tail(&fib->fiblink, &fibctx->fib_list); + fibctx->count++; + /* + * Set the event to wake up the + * thread that will waiting. + */ + up(&fibctx->wait_sem); + } else { + printk(KERN_WARNING "aifd: didn't allocate NewFib.\n"); + kfree(fib); + kfree(hw_fib); + } + entry = entry->next; + } + + spin_unlock_irqrestore(&aac->fib_lock, flagv); + + if (BlinkLED < 0) { + printk(KERN_ERR "%s: Host adapter dead %d\n", aac->name, BlinkLED); + goto out; + } + + printk(KERN_ERR "%s: Host adapter BLINK LED 0x%x\n", aac->name, BlinkLED); + + host = aac->scsi_host_ptr; + spin_lock_irqsave(host->host_lock, flagv); + BlinkLED = _aac_reset_adapter(aac); + spin_unlock_irqrestore(host->host_lock, flagv); + return BlinkLED; + +out: + aac->in_reset = 0; + return BlinkLED; +} + + /** * aac_command_thread - command processing thread * @dev: Adapter to monitor diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index 9d8b550a91c..d67058f8081 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -454,17 +454,17 @@ static int aac_eh_reset(struct scsi_cmnd* cmd) printk(KERN_ERR "%s: Host adapter reset request. SCSI hang ?\n", AAC_DRIVERNAME); aac = (struct aac_dev *)host->hostdata; - if (aac_adapter_check_health(aac)) { - printk(KERN_ERR "%s: Host adapter appears dead\n", - AAC_DRIVERNAME); - return -ENODEV; - } + + if ((count = aac_check_health(aac))) + return count; /* * Wait for all commands to complete to this specific * target (block maximum 60 seconds). */ for (count = 60; count; --count) { - int active = 0; + int active = aac->in_reset; + + if (active == 0) __shost_for_each_device(dev, host) { spin_lock_irqsave(&dev->list_lock, flags); list_for_each_entry(command, &dev->cmd_list, list) { @@ -933,7 +933,7 @@ static int __devinit aac_probe_one(struct pci_dev *pdev, else shost->max_channel = 0; - aac_get_config_status(aac); + aac_get_config_status(aac, 0); aac_get_containers(aac); list_add(&aac->entry, insert); -- cgit v1.2.3 From 84961f28e9d13a4b193d0c8545f3c060c1890ff3 Mon Sep 17 00:00:00 2001 From: dave wysochanski Date: Wed, 9 Aug 2006 14:56:32 -0400 Subject: [SCSI] Don't add scsi_device for devices that return PQ=1, PDT=0x1f Some targets may return slight variations of PQ and PDT to indicate no LUN mapped. USB UFI setting PDT=0x1f but having reserved bits for PQ is one example, and NetApp targets returning PQ=1 and PDT=0x1f is another. Both instances seem like reasonable responses according to SPC-3 and UFI specs. The current scsi_probe_and_add_lun() code adds a scsi_device for targets that return PQ=1 and PDT=0x1f. This causes LUNs of type "UNKNOWN" to show up in /proc/scsi/scsi when no LUNs are mapped. In addition, subsequent rescans fail to recognize LUNs that may be added on the target, unless preceded by a write to the delete attribute of the "UNKNOWN" LUN. This patch addresses this problem by skipping over the scsi_add_lun() when PQ=1,PDT=0x1f is encountered, and just returns SCSI_SCAN_TARGET_PRESENT. Signed-off-by: Dave Wysochanski Signed-off-by: Christoph Hellwig Signed-off-by: James Bottomley --- drivers/scsi/scsi_scan.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 114e2067dce..a24d3461fc7 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -893,11 +893,26 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, } /* - * Non-standard SCSI targets may set the PDT to 0x1f (unknown or - * no device type) instead of using the Peripheral Qualifier to - * indicate that no LUN is present. For example, USB UFI does this. + * Some targets may set slight variations of PQ and PDT to signal + * that no LUN is present, so don't add sdev in these cases. + * Two specific examples are: + * 1) NetApp targets: return PQ=1, PDT=0x1f + * 2) USB UFI: returns PDT=0x1f, with the PQ bits being "reserved" + * in the UFI 1.0 spec (we cannot rely on reserved bits). + * + * References: + * 1) SCSI SPC-3, pp. 145-146 + * PQ=1: "A peripheral device having the specified peripheral + * device type is not connected to this logical unit. However, the + * device server is capable of supporting the specified peripheral + * device type on this logical unit." + * PDT=0x1f: "Unknown or no device type" + * 2) USB UFI 1.0, p. 20 + * PDT=00h Direct-access device (floppy) + * PDT=1Fh none (no FDD connected to the requested logical unit) */ - if (starget->pdt_1f_for_no_lun && (result[0] & 0x1f) == 0x1f) { + if (((result[0] >> 5) == 1 || starget->pdt_1f_for_no_lun) && + (result[0] & 0x1f) == 0x1f) { SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: peripheral device type" " of 31, no device added\n")); -- cgit v1.2.3 From a2f5d4d94f0ab9560b9a99d73d5b86b377c7f201 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Thu, 10 Aug 2006 21:41:13 -0400 Subject: [SCSI] remove unnecessary includes of linux/config.h from drivers/scsi/ kbuild includes this automatically these days. Signed-off-by: Dave Jones Signed-off-by: James Bottomley --- drivers/scsi/aic7xxx_old.c | 2 -- drivers/scsi/dpt_i2o.c | 1 - drivers/scsi/hptiop.c | 1 - drivers/scsi/libata-eh.c | 1 - drivers/scsi/scsi.h | 2 -- 5 files changed, 7 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aic7xxx_old.c b/drivers/scsi/aic7xxx_old.c index 3f85b5e978f..ba3bccafe11 100644 --- a/drivers/scsi/aic7xxx_old.c +++ b/drivers/scsi/aic7xxx_old.c @@ -249,8 +249,6 @@ #include #include /* for kmalloc() */ -#include /* for CONFIG_PCI */ - #define AIC7XXX_C_VERSION "5.2.6" #define ALL_TARGETS -1 diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c index e1337339cac..45806336ce0 100644 --- a/drivers/scsi/dpt_i2o.c +++ b/drivers/scsi/dpt_i2o.c @@ -46,7 +46,6 @@ MODULE_DESCRIPTION("Adaptec I2O RAID Driver"); #include #include /* for kmalloc() */ -#include /* for CONFIG_PCI */ #include /* for PCI support */ #include #include diff --git a/drivers/scsi/hptiop.c b/drivers/scsi/hptiop.c index ab2f8b26790..6b41c2ef6e2 100644 --- a/drivers/scsi/hptiop.c +++ b/drivers/scsi/hptiop.c @@ -15,7 +15,6 @@ * * For more information, visit http://www.highpoint-tech.com */ -#include #include #include #include diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c index 4b6aa30f4d6..b3095fd9286 100644 --- a/drivers/scsi/libata-eh.c +++ b/drivers/scsi/libata-eh.c @@ -32,7 +32,6 @@ * */ -#include #include #include #include diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h index f51e466893e..d5a55fae60e 100644 --- a/drivers/scsi/scsi.h +++ b/drivers/scsi/scsi.h @@ -20,8 +20,6 @@ #ifndef _SCSI_H #define _SCSI_H -#include /* for CONFIG_SCSI_LOGGING */ - #include #include #include -- cgit v1.2.3 From 016131b8fffa1085b4ad165ab228116fdc278ebe Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 14 Aug 2006 08:20:25 -0400 Subject: [SCSI] fc transport: convert fc_host symbolic_name attribute to a dynamic attribute Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_fc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index b03aa85108e..c1c5cdffca3 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -815,7 +815,6 @@ fc_private_host_rd_attr_cast(node_name, "0x%llx\n", 20, unsigned long long); fc_private_host_rd_attr_cast(port_name, "0x%llx\n", 20, unsigned long long); fc_private_host_rd_attr_cast(permanent_port_name, "0x%llx\n", 20, unsigned long long); -fc_private_host_rd_attr(symbolic_name, "%s\n", (FC_SYMBOLIC_NAME_SIZE +1)); fc_private_host_rd_attr(maxframe_size, "%u bytes\n", 20); fc_private_host_rd_attr(serial_number, "%s\n", (FC_SERIAL_NUMBER_SIZE +1)); @@ -858,6 +857,7 @@ fc_host_rd_attr(port_id, "0x%06x\n", 20); fc_host_rd_enum_attr(port_type, FC_PORTTYPE_MAX_NAMELEN); fc_host_rd_enum_attr(port_state, FC_PORTSTATE_MAX_NAMELEN); fc_host_rd_attr_cast(fabric_name, "0x%llx\n", 20, unsigned long long); +fc_host_rd_attr(symbolic_name, "%s\n", FC_SYMBOLIC_NAME_SIZE + 1); /* Private Host Attributes */ @@ -1223,7 +1223,6 @@ fc_attach_transport(struct fc_function_template *ft) SETUP_HOST_ATTRIBUTE_RD(permanent_port_name); SETUP_HOST_ATTRIBUTE_RD(supported_classes); SETUP_HOST_ATTRIBUTE_RD(supported_fc4s); - SETUP_HOST_ATTRIBUTE_RD(symbolic_name); SETUP_HOST_ATTRIBUTE_RD(supported_speeds); SETUP_HOST_ATTRIBUTE_RD(maxframe_size); SETUP_HOST_ATTRIBUTE_RD(serial_number); @@ -1234,6 +1233,7 @@ fc_attach_transport(struct fc_function_template *ft) SETUP_HOST_ATTRIBUTE_RD(active_fc4s); SETUP_HOST_ATTRIBUTE_RD(speed); SETUP_HOST_ATTRIBUTE_RD(fabric_name); + SETUP_HOST_ATTRIBUTE_RD(symbolic_name); /* Transport-managed attributes */ SETUP_PRIVATE_HOST_ATTRIBUTE_RW(tgtid_bind_type); -- cgit v1.2.3 From 2b6ee9b5295460017fc1bc3d60545512df280908 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 14 Aug 2006 23:09:23 -0700 Subject: [SCSI] aic7*: cleanup MODULE_PARM_DESC strings Modify beginning string to be more readable. Remove one trailing newline. Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: James Bottomley --- drivers/scsi/aic7xxx/aic79xx_osm.c | 4 ++-- drivers/scsi/aic7xxx/aic7xxx_osm.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c index 998999c0a97..c7eeaced324 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm.c +++ b/drivers/scsi/aic7xxx/aic79xx_osm.c @@ -321,7 +321,7 @@ MODULE_LICENSE("Dual BSD/GPL"); MODULE_VERSION(AIC79XX_DRIVER_VERSION); module_param(aic79xx, charp, 0444); MODULE_PARM_DESC(aic79xx, -"period delimited, options string.\n" +"period-delimited options string:\n" " verbose Enable verbose/diagnostic logging\n" " allow_memio Allow device registers to be memory mapped\n" " debug Bitmask of debug values to enable\n" @@ -346,7 +346,7 @@ MODULE_PARM_DESC(aic79xx, " Shorten the selection timeout to 128ms\n" "\n" " options aic79xx 'aic79xx=verbose.tag_info:{{}.{}.{..10}}.seltime:1'\n" -"\n"); +); static void ahd_linux_handle_scsi_status(struct ahd_softc *, struct scsi_device *, diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c index aa4be8a3141..e5bb4d87b30 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c @@ -341,7 +341,7 @@ MODULE_LICENSE("Dual BSD/GPL"); MODULE_VERSION(AIC7XXX_DRIVER_VERSION); module_param(aic7xxx, charp, 0444); MODULE_PARM_DESC(aic7xxx, -"period delimited, options string.\n" +"period-delimited options string:\n" " verbose Enable verbose/diagnostic logging\n" " allow_memio Allow device registers to be memory mapped\n" " debug Bitmask of debug values to enable\n" -- cgit v1.2.3 From f3d7271c5ac9029d19fc0252a85bc045334382cc Mon Sep 17 00:00:00 2001 From: Henrik Kretzschmar Date: Tue, 15 Aug 2006 11:17:21 +0200 Subject: [SCSI] convert to PCI_DEVICE() macro Convert the pci_device_id-table of the megaraid_sas-driver to the PCI_DEVICE-macro, to safe some lines. Signed-off-by: Henrik Kretzschmar Acked-by: "Patro, Sumant" Signed-off-by: James Bottomley --- drivers/scsi/megaraid/megaraid_sas.c | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c index a8c9627a15c..b36307d2e28 100644 --- a/drivers/scsi/megaraid/megaraid_sas.c +++ b/drivers/scsi/megaraid/megaraid_sas.c @@ -53,31 +53,15 @@ MODULE_DESCRIPTION("LSI Logic MegaRAID SAS Driver"); */ static struct pci_device_id megasas_pci_table[] = { - { - PCI_VENDOR_ID_LSI_LOGIC, - PCI_DEVICE_ID_LSI_SAS1064R, /* xscale IOP */ - PCI_ANY_ID, - PCI_ANY_ID, - }, - { - PCI_VENDOR_ID_LSI_LOGIC, - PCI_DEVICE_ID_LSI_SAS1078R, /* ppc IOP */ - PCI_ANY_ID, - PCI_ANY_ID, - }, - { - PCI_VENDOR_ID_LSI_LOGIC, - PCI_DEVICE_ID_LSI_VERDE_ZCR, /* xscale IOP, vega */ - PCI_ANY_ID, - PCI_ANY_ID, - }, - { - PCI_VENDOR_ID_DELL, - PCI_DEVICE_ID_DELL_PERC5, /* xscale IOP */ - PCI_ANY_ID, - PCI_ANY_ID, - }, - {0} /* Terminating entry */ + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1064R)}, + /* xscale IOP */ + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1078R)}, + /* ppc IOP */ + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_VERDE_ZCR)}, + /* xscale IOP, vega */ + {PCI_DEVICE(PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_PERC5)}, + /* xscale IOP */ + {} }; MODULE_DEVICE_TABLE(pci, megasas_pci_table); -- cgit v1.2.3 From b8d08210126a7b769b857720a59721a453a57a1e Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 17 Aug 2006 08:00:43 -0400 Subject: [SCSI] fc transport: add fc_host system_hostname attribute and u64_to_wwn() This patch updates the fc transport for the following: - Addition of a new attribute "system_hostname" which can be used to set the fully qualified hostname that the fc_host is attached to. The fc_host can then register this string as the FDMI-based host name attribute. Note: for NPIV, a fc_host could be associated with a system which is not the local system. - Add the inline function u64_to_wwn(), which is the inverse of the existing wwn_to_u64() function. - Slight reorg, just to keep dynamic attributes with each other, etc Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_fc.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index c1c5cdffca3..79d31ca2b74 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -301,8 +301,6 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev, fc_host->supported_classes = FC_COS_UNSPECIFIED; memset(fc_host->supported_fc4s, 0, sizeof(fc_host->supported_fc4s)); - memset(fc_host->symbolic_name, 0, - sizeof(fc_host->symbolic_name)); fc_host->supported_speeds = FC_PORTSPEED_UNKNOWN; fc_host->maxframe_size = -1; memset(fc_host->serial_number, 0, @@ -315,6 +313,8 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev, sizeof(fc_host->active_fc4s)); fc_host->speed = FC_PORTSPEED_UNKNOWN; fc_host->fabric_name = -1; + memset(fc_host->symbolic_name, 0, sizeof(fc_host->symbolic_name)); + memset(fc_host->system_hostname, 0, sizeof(fc_host->system_hostname)); fc_host->tgtid_bind_type = FC_TGTID_BIND_BY_WWPN; @@ -688,6 +688,25 @@ store_fc_host_##field(struct class_device *cdev, const char *buf, \ return count; \ } +#define fc_host_store_str_function(field, slen) \ +static ssize_t \ +store_fc_host_##field(struct class_device *cdev, const char *buf, \ + size_t count) \ +{ \ + struct Scsi_Host *shost = transport_class_to_shost(cdev); \ + struct fc_internal *i = to_fc_internal(shost->transportt); \ + unsigned int cnt=count; \ + \ + /* count may include a LF at end of string */ \ + if (buf[cnt-1] == '\n') \ + cnt--; \ + if (cnt > ((slen) - 1)) \ + return -EINVAL; \ + memcpy(fc_host_##field(shost), buf, cnt); \ + i->f->set_host_##field(shost); \ + return count; \ +} + #define fc_host_rd_attr(field, format_string, sz) \ fc_host_show_function(field, format_string, sz, ) \ static FC_CLASS_DEVICE_ATTR(host, field, S_IRUGO, \ @@ -859,6 +878,12 @@ fc_host_rd_enum_attr(port_state, FC_PORTSTATE_MAX_NAMELEN); fc_host_rd_attr_cast(fabric_name, "0x%llx\n", 20, unsigned long long); fc_host_rd_attr(symbolic_name, "%s\n", FC_SYMBOLIC_NAME_SIZE + 1); +fc_private_host_show_function(system_hostname, "%s\n", + FC_SYMBOLIC_NAME_SIZE + 1, ) +fc_host_store_str_function(system_hostname, FC_SYMBOLIC_NAME_SIZE) +static FC_CLASS_DEVICE_ATTR(host, system_hostname, S_IRUGO | S_IWUSR, + show_fc_host_system_hostname, store_fc_host_system_hostname); + /* Private Host Attributes */ @@ -1234,6 +1259,7 @@ fc_attach_transport(struct fc_function_template *ft) SETUP_HOST_ATTRIBUTE_RD(speed); SETUP_HOST_ATTRIBUTE_RD(fabric_name); SETUP_HOST_ATTRIBUTE_RD(symbolic_name); + SETUP_HOST_ATTRIBUTE_RW(system_hostname); /* Transport-managed attributes */ SETUP_PRIVATE_HOST_ATTRIBUTE_RW(tgtid_bind_type); -- cgit v1.2.3 From 4041b9cd87d97a7c73a5bf5a9305dffee2599386 Mon Sep 17 00:00:00 2001 From: Michal Piotrowski Date: Thu, 17 Aug 2006 13:28:22 +0000 Subject: [SCSI] megaraid_sas: pci_module_init to pci_register_driver conversion Signed-off-by: Michal Piotrowski Acked-by: "Patro, Sumant" Signed-off-by: James Bottomley --- drivers/scsi/megaraid/megaraid_sas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c index b36307d2e28..4cab5b534b2 100644 --- a/drivers/scsi/megaraid/megaraid_sas.c +++ b/drivers/scsi/megaraid/megaraid_sas.c @@ -2838,7 +2838,7 @@ static int __init megasas_init(void) /* * Register ourselves as PCI hotplug module */ - rval = pci_module_init(&megasas_pci_driver); + rval = pci_register_driver(&megasas_pci_driver); if (rval) { printk(KERN_DEBUG "megasas: PCI hotplug regisration failed \n"); -- cgit v1.2.3 From d2afb3ae04e36dbc6e9eb2d8bd54406ff7b6b3bd Mon Sep 17 00:00:00 2001 From: Daniel Walker Date: Mon, 14 Aug 2006 23:09:23 -0700 Subject: [SCSI] BusLogic gcc 4.1 warning fixes - Reworked all the very long lines in that block (this drivers full of them though) - Returns an error in three places that it didn't before. - Properly clean up after a scsi_add_host() failure. Signed-off-by: Daniel Walker Signed-off-by: Andrew Morton Signed-off-by: James Bottomley --- drivers/scsi/BusLogic.c | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c index 16a12a3b7b2..59d1adaed73 100644 --- a/drivers/scsi/BusLogic.c +++ b/drivers/scsi/BusLogic.c @@ -2176,6 +2176,7 @@ static int __init BusLogic_init(void) { int BusLogicHostAdapterCount = 0, DriverOptionsIndex = 0, ProbeIndex; struct BusLogic_HostAdapter *PrototypeHostAdapter; + int ret = 0; #ifdef MODULE if (BusLogic) @@ -2282,25 +2283,49 @@ static int __init BusLogic_init(void) perform Target Device Inquiry. */ if (BusLogic_ReadHostAdapterConfiguration(HostAdapter) && - BusLogic_ReportHostAdapterConfiguration(HostAdapter) && BusLogic_AcquireResources(HostAdapter) && BusLogic_CreateInitialCCBs(HostAdapter) && BusLogic_InitializeHostAdapter(HostAdapter) && BusLogic_TargetDeviceInquiry(HostAdapter)) { + BusLogic_ReportHostAdapterConfiguration(HostAdapter) && + BusLogic_AcquireResources(HostAdapter) && + BusLogic_CreateInitialCCBs(HostAdapter) && + BusLogic_InitializeHostAdapter(HostAdapter) && + BusLogic_TargetDeviceInquiry(HostAdapter)) { /* Initialization has been completed successfully. Release and re-register usage of the I/O Address range so that the Model Name of the Host Adapter will appear, and initialize the SCSI Host structure. */ - release_region(HostAdapter->IO_Address, HostAdapter->AddressCount); - if (!request_region(HostAdapter->IO_Address, HostAdapter->AddressCount, HostAdapter->FullModelName)) { - printk(KERN_WARNING "BusLogic: Release and re-register of " "port 0x%04lx failed \n", (unsigned long) HostAdapter->IO_Address); + release_region(HostAdapter->IO_Address, + HostAdapter->AddressCount); + if (!request_region(HostAdapter->IO_Address, + HostAdapter->AddressCount, + HostAdapter->FullModelName)) { + printk(KERN_WARNING + "BusLogic: Release and re-register of " + "port 0x%04lx failed \n", + (unsigned long)HostAdapter->IO_Address); BusLogic_DestroyCCBs(HostAdapter); BusLogic_ReleaseResources(HostAdapter); list_del(&HostAdapter->host_list); scsi_host_put(Host); + ret = -ENOMEM; } else { - BusLogic_InitializeHostStructure(HostAdapter, Host); - scsi_add_host(Host, HostAdapter->PCI_Device ? &HostAdapter->PCI_Device->dev : NULL); - scsi_scan_host(Host); - BusLogicHostAdapterCount++; + BusLogic_InitializeHostStructure(HostAdapter, + Host); + if (scsi_add_host(Host, HostAdapter->PCI_Device + ? &HostAdapter->PCI_Device->dev + : NULL)) { + printk(KERN_WARNING + "BusLogic: scsi_add_host()" + "failed!\n"); + BusLogic_DestroyCCBs(HostAdapter); + BusLogic_ReleaseResources(HostAdapter); + list_del(&HostAdapter->host_list); + scsi_host_put(Host); + ret = -ENODEV; + } else { + scsi_scan_host(Host); + BusLogicHostAdapterCount++; + } } } else { /* @@ -2315,12 +2340,13 @@ static int __init BusLogic_init(void) BusLogic_ReleaseResources(HostAdapter); list_del(&HostAdapter->host_list); scsi_host_put(Host); + ret = -ENODEV; } } kfree(PrototypeHostAdapter); kfree(BusLogic_ProbeInfoList); BusLogic_ProbeInfoList = NULL; - return 0; + return ret; } @@ -2954,6 +2980,7 @@ static int BusLogic_QueueCommand(struct scsi_cmnd *Command, void (*CompletionRou } +#if 0 /* BusLogic_AbortCommand aborts Command if possible. */ @@ -3024,6 +3051,7 @@ static int BusLogic_AbortCommand(struct scsi_cmnd *Command) return SUCCESS; } +#endif /* BusLogic_ResetHostAdapter resets Host Adapter if possible, marking all currently executing SCSI Commands as having been Reset. -- cgit v1.2.3 From f4ad7b5807385ad1fed0347d966e51a797cd1013 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 25 Aug 2006 13:48:18 -0500 Subject: [SCSI] scsi_transport_sas: remove local_attached flag This flag denotes local attachment of the phy. There are two problems with it: 1) It's actually redundant ... you can get the same information simply by seeing whether a host is the phys parent 2) we condition a lot of phy parameters on it on the false assumption that we can only control local phys. I'm wiring up phy resets in the aic94xx now, and it will be able to reset non-local phys as well. I fixed 2) by moving the local check into the reset and stats function of the mptsas, since that seems to be the only HBA that can't (currently) control non-local phys. Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_sas.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 5a625c3fdda..d518c1207fb 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -266,9 +266,6 @@ show_sas_phy_##field(struct class_device *cdev, char *buf) \ struct sas_internal *i = to_sas_internal(shost->transportt); \ int error; \ \ - if (!phy->local_attached) \ - return -EINVAL; \ - \ error = i->f->get_linkerrors ? i->f->get_linkerrors(phy) : 0; \ if (error) \ return error; \ @@ -299,9 +296,6 @@ static ssize_t do_sas_phy_reset(struct class_device *cdev, struct sas_internal *i = to_sas_internal(shost->transportt); int error; - if (!phy->local_attached) - return -EINVAL; - error = i->f->phy_reset(phy, hard_reset); if (error) return error; @@ -849,7 +843,7 @@ show_sas_rphy_enclosure_identifier(struct class_device *cdev, char *buf) * Only devices behind an expander are supported, because the * enclosure identifier is a SMP feature. */ - if (phy->local_attached) + if (scsi_is_sas_phy_local(phy)) return -EINVAL; error = i->f->get_enclosure_identifier(rphy, &identifier); @@ -870,7 +864,7 @@ show_sas_rphy_bay_identifier(struct class_device *cdev, char *buf) struct sas_internal *i = to_sas_internal(shost->transportt); int val; - if (phy->local_attached) + if (scsi_is_sas_phy_local(phy)) return -EINVAL; val = i->f->get_bay_identifier(rphy); -- cgit v1.2.3 From 2908d778ab3e244900c310974e1fc1c69066e450 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Tue, 29 Aug 2006 09:22:51 -0500 Subject: [SCSI] aic94xx: new driver This is the end point of the separate aic94xx driver based on the original driver and transport class from Luben Tuikov The log of the separate development is: Alexis Bruemmer: o aic94xx: fix hotplug/unplug for expanderless systems o aic94xx: disable split completion timer/setting by default o aic94xx: wide port off expander support o aic94xx: remove various inline functions o aic94xx: use bitops o aic94xx: remove queue comment o aic94xx: remove sas_common.c o aic94xx: sas remove depot's o aic94xx: use available list_for_each_entry_safe_reverse() o aic94xx: sas header file merge James Bottomley: o aic94xx: fix TF_TMF_NO_CTX processing o aic94xx: convert to request_firmware interface o aic94xx: fix hotplug/unplug o aic94xx: add link error counts to the expander phys o aic94xx: add transport class phy reset capability o aic94xx: remove local_attached flag o Remove README o Fixup Makefile variable for libsas rename o Rename sas->libsas o aic94xx: correct return code for sas_discover_event o aic94xx: use parent backlink port o aic94xx: remove channel abstraction o aic94xx: fix routing algorithms o aic94xx: add backlink port o aic94xx: fix cascaded expander properties o aic94xx: fix sleep under lock o aic94xx: fix panic on module removal in complex topology o aic94xx: make use of the new sas_port o rename sas_port to asd_sas_port o Fix for eh_strategy_handler move o aic94xx: move entirely over to correct transport class formulation o remove last vestages of sas_rphy_alloc() o update for eh_timed_out move o Preliminary expander support for aic94xx o sas: remove event thread o minor warning cleanups o remove last vestiges of id mapping arrays o Further updates o Convert aic94xx over entirely to the transport class end device and o update aic94xx/sas to use the new sas transport class end device o [PATCH] aic94xx: attaching to the sas transport class o Add missing completion removal from prior patch o [PATCH] aic94xx: attaching to the sas transport class o Build fixes from akpm Jeff Garzik: o [scsi aic94xx] Remove ->owner from PCI info table Luben Tuikov: o initial aic94xx driver Mike Anderson: o aic94xx: fix panic on module insertion o aic94xx: stub out SATA_DEV case o aic94xx: compile warning cleanups o aic94xx: sas_alloc_task o aic94xx: ref count update o aic94xx nexus loss time value o [PATCH] aic94xx: driver assertion in non-x86 BIOS env Randy Dunlap: o libsas: externs not needed Robert Tarte: o aic94xx: sequence patch - fixes SATA support Signed-off-by: James Bottomley --- drivers/scsi/Kconfig | 5 +- drivers/scsi/Makefile | 2 + drivers/scsi/aic94xx/Kconfig | 41 + drivers/scsi/aic94xx/Makefile | 39 + drivers/scsi/aic94xx/aic94xx.h | 114 ++ drivers/scsi/aic94xx/aic94xx_dev.c | 353 +++++ drivers/scsi/aic94xx/aic94xx_dump.c | 959 +++++++++++++ drivers/scsi/aic94xx/aic94xx_dump.h | 52 + drivers/scsi/aic94xx/aic94xx_hwi.c | 1376 ++++++++++++++++++ drivers/scsi/aic94xx/aic94xx_hwi.h | 397 ++++++ drivers/scsi/aic94xx/aic94xx_init.c | 860 ++++++++++++ drivers/scsi/aic94xx/aic94xx_reg.c | 332 +++++ drivers/scsi/aic94xx/aic94xx_reg.h | 302 ++++ drivers/scsi/aic94xx/aic94xx_reg_def.h | 2398 ++++++++++++++++++++++++++++++++ drivers/scsi/aic94xx/aic94xx_sas.h | 785 +++++++++++ drivers/scsi/aic94xx/aic94xx_scb.c | 732 ++++++++++ drivers/scsi/aic94xx/aic94xx_sds.c | 1136 +++++++++++++++ drivers/scsi/aic94xx/aic94xx_seq.c | 1401 +++++++++++++++++++ drivers/scsi/aic94xx/aic94xx_seq.h | 70 + drivers/scsi/aic94xx/aic94xx_task.c | 642 +++++++++ drivers/scsi/aic94xx/aic94xx_tmf.c | 636 +++++++++ drivers/scsi/libsas/Kconfig | 39 + drivers/scsi/libsas/Makefile | 36 + drivers/scsi/libsas/sas_discover.c | 749 ++++++++++ drivers/scsi/libsas/sas_dump.c | 76 + drivers/scsi/libsas/sas_dump.h | 42 + drivers/scsi/libsas/sas_event.c | 75 + drivers/scsi/libsas/sas_expander.c | 1862 +++++++++++++++++++++++++ drivers/scsi/libsas/sas_init.c | 227 +++ drivers/scsi/libsas/sas_internal.h | 146 ++ drivers/scsi/libsas/sas_phy.c | 157 +++ drivers/scsi/libsas/sas_port.c | 279 ++++ drivers/scsi/libsas/sas_scsi_host.c | 786 +++++++++++ 33 files changed, 17105 insertions(+), 1 deletion(-) create mode 100644 drivers/scsi/aic94xx/Kconfig create mode 100644 drivers/scsi/aic94xx/Makefile create mode 100644 drivers/scsi/aic94xx/aic94xx.h create mode 100644 drivers/scsi/aic94xx/aic94xx_dev.c create mode 100644 drivers/scsi/aic94xx/aic94xx_dump.c create mode 100644 drivers/scsi/aic94xx/aic94xx_dump.h create mode 100644 drivers/scsi/aic94xx/aic94xx_hwi.c create mode 100644 drivers/scsi/aic94xx/aic94xx_hwi.h create mode 100644 drivers/scsi/aic94xx/aic94xx_init.c create mode 100644 drivers/scsi/aic94xx/aic94xx_reg.c create mode 100644 drivers/scsi/aic94xx/aic94xx_reg.h create mode 100644 drivers/scsi/aic94xx/aic94xx_reg_def.h create mode 100644 drivers/scsi/aic94xx/aic94xx_sas.h create mode 100644 drivers/scsi/aic94xx/aic94xx_scb.c create mode 100644 drivers/scsi/aic94xx/aic94xx_sds.c create mode 100644 drivers/scsi/aic94xx/aic94xx_seq.c create mode 100644 drivers/scsi/aic94xx/aic94xx_seq.h create mode 100644 drivers/scsi/aic94xx/aic94xx_task.c create mode 100644 drivers/scsi/aic94xx/aic94xx_tmf.c create mode 100644 drivers/scsi/libsas/Kconfig create mode 100644 drivers/scsi/libsas/Makefile create mode 100644 drivers/scsi/libsas/sas_discover.c create mode 100644 drivers/scsi/libsas/sas_dump.c create mode 100644 drivers/scsi/libsas/sas_dump.h create mode 100644 drivers/scsi/libsas/sas_event.c create mode 100644 drivers/scsi/libsas/sas_expander.c create mode 100644 drivers/scsi/libsas/sas_init.c create mode 100644 drivers/scsi/libsas/sas_internal.h create mode 100644 drivers/scsi/libsas/sas_phy.c create mode 100644 drivers/scsi/libsas/sas_port.c create mode 100644 drivers/scsi/libsas/sas_scsi_host.c (limited to 'drivers/scsi') diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index d61662c1a0e..7de5fdfdab6 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -209,7 +209,7 @@ config SCSI_LOGGING there should be no noticeable performance impact as long as you have logging turned off. -menu "SCSI Transport Attributes" +menu "SCSI Transports" depends on SCSI config SCSI_SPI_ATTRS @@ -242,6 +242,8 @@ config SCSI_SAS_ATTRS If you wish to export transport-specific information about each attached SAS device to sysfs, say Y. +source "drivers/scsi/libsas/Kconfig" + endmenu menu "SCSI low-level drivers" @@ -431,6 +433,7 @@ config SCSI_AIC7XXX_OLD module will be called aic7xxx_old. source "drivers/scsi/aic7xxx/Kconfig.aic79xx" +source "drivers/scsi/aic94xx/Kconfig" # All the I2O code and drivers do not seem to be 64bit safe. config SCSI_DPT_I2O diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index b2de9bfdfdc..83da70decdd 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_SCSI_SPI_ATTRS) += scsi_transport_spi.o obj-$(CONFIG_SCSI_FC_ATTRS) += scsi_transport_fc.o obj-$(CONFIG_SCSI_ISCSI_ATTRS) += scsi_transport_iscsi.o obj-$(CONFIG_SCSI_SAS_ATTRS) += scsi_transport_sas.o +obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas/ obj-$(CONFIG_ISCSI_TCP) += libiscsi.o iscsi_tcp.o obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o @@ -68,6 +69,7 @@ obj-$(CONFIG_SCSI_AIC7XXX) += aic7xxx/ obj-$(CONFIG_SCSI_AIC79XX) += aic7xxx/ obj-$(CONFIG_SCSI_AACRAID) += aacraid/ obj-$(CONFIG_SCSI_AIC7XXX_OLD) += aic7xxx_old.o +obj-$(CONFIG_SCSI_AIC94XX) += aic94xx/ obj-$(CONFIG_SCSI_IPS) += ips.o obj-$(CONFIG_SCSI_FD_MCS) += fd_mcs.o obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o diff --git a/drivers/scsi/aic94xx/Kconfig b/drivers/scsi/aic94xx/Kconfig new file mode 100644 index 00000000000..0ed391d8ee8 --- /dev/null +++ b/drivers/scsi/aic94xx/Kconfig @@ -0,0 +1,41 @@ +# +# Kernel configuration file for aic94xx SAS/SATA driver. +# +# Copyright (c) 2005 Adaptec, Inc. All rights reserved. +# Copyright (c) 2005 Luben Tuikov +# +# This file is licensed under GPLv2. +# +# This file is part of the aic94xx driver. +# +# The aic94xx driver 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; version 2 of the +# License. +# +# The aic94xx driver 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 Aic94xx Driver; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# + +config SCSI_AIC94XX + tristate "Adaptec AIC94xx SAS/SATA support" + depends on PCI + select SCSI_SAS_LIBSAS + help + This driver supports Adaptec's SAS/SATA 3Gb/s 64 bit PCI-X + AIC94xx chip based host adapters. + +config AIC94XX_DEBUG + bool "Compile in debug mode" + default y + depends on SCSI_AIC94XX + help + Compiles the aic94xx driver in debug mode. In debug mode, + the driver prints some messages to the console. diff --git a/drivers/scsi/aic94xx/Makefile b/drivers/scsi/aic94xx/Makefile new file mode 100644 index 00000000000..e6b70123940 --- /dev/null +++ b/drivers/scsi/aic94xx/Makefile @@ -0,0 +1,39 @@ +# +# Makefile for Adaptec aic94xx SAS/SATA driver. +# +# Copyright (C) 2005 Adaptec, Inc. All rights reserved. +# Copyright (C) 2005 Luben Tuikov +# +# This file is licensed under GPLv2. +# +# This file is part of the the aic94xx driver. +# +# The aic94xx driver 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; version 2 of the +# License. +# +# The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +ifeq ($(CONFIG_AIC94XX_DEBUG),y) + EXTRA_CFLAGS += -DASD_DEBUG -DASD_ENTER_EXIT +endif + +obj-$(CONFIG_SCSI_AIC94XX) += aic94xx.o +aic94xx-y += aic94xx_init.o \ + aic94xx_hwi.o \ + aic94xx_reg.o \ + aic94xx_sds.o \ + aic94xx_seq.o \ + aic94xx_dump.o \ + aic94xx_scb.o \ + aic94xx_dev.o \ + aic94xx_tmf.o \ + aic94xx_task.o diff --git a/drivers/scsi/aic94xx/aic94xx.h b/drivers/scsi/aic94xx/aic94xx.h new file mode 100644 index 00000000000..cb7caf1c9ce --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx.h @@ -0,0 +1,114 @@ +/* + * Aic94xx SAS/SATA driver header file. + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * This file is part of the aic94xx driver. + * + * The aic94xx driver 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; version 2 of the + * License. + * + * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: //depot/aic94xx/aic94xx.h#31 $ + */ + +#ifndef _AIC94XX_H_ +#define _AIC94XX_H_ + +#include +#include +#include + +#define ASD_DRIVER_NAME "aic94xx" +#define ASD_DRIVER_DESCRIPTION "Adaptec aic94xx SAS/SATA driver" + +#define asd_printk(fmt, ...) printk(KERN_NOTICE ASD_DRIVER_NAME ": " fmt, ## __VA_ARGS__) + +#ifdef ASD_ENTER_EXIT +#define ENTER printk(KERN_NOTICE "%s: ENTER %s\n", ASD_DRIVER_NAME, \ + __FUNCTION__) +#define EXIT printk(KERN_NOTICE "%s: --EXIT %s\n", ASD_DRIVER_NAME, \ + __FUNCTION__) +#else +#define ENTER +#define EXIT +#endif + +#ifdef ASD_DEBUG +#define ASD_DPRINTK asd_printk +#else +#define ASD_DPRINTK(fmt, ...) +#endif + +/* 2*ITNL timeout + 1 second */ +#define AIC94XX_SCB_TIMEOUT (5*HZ) + +extern kmem_cache_t *asd_dma_token_cache; +extern kmem_cache_t *asd_ascb_cache; +extern char sas_addr_str[2*SAS_ADDR_SIZE + 1]; + +static inline void asd_stringify_sas_addr(char *p, const u8 *sas_addr) +{ + int i; + for (i = 0; i < SAS_ADDR_SIZE; i++, p += 2) + snprintf(p, 3, "%02X", sas_addr[i]); + *p = '\0'; +} + +static inline void asd_destringify_sas_addr(u8 *sas_addr, const char *p) +{ + int i; + for (i = 0; i < SAS_ADDR_SIZE; i++) { + u8 h, l; + if (!*p) + break; + h = isdigit(*p) ? *p-'0' : *p-'A'+10; + p++; + l = isdigit(*p) ? *p-'0' : *p-'A'+10; + p++; + sas_addr[i] = (h<<4) | l; + } +} + +struct asd_ha_struct; +struct asd_ascb; + +int asd_read_ocm(struct asd_ha_struct *asd_ha); +int asd_read_flash(struct asd_ha_struct *asd_ha); + +int asd_dev_found(struct domain_device *dev); +void asd_dev_gone(struct domain_device *dev); + +void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id); + +int asd_execute_task(struct sas_task *, int num, unsigned long gfp_flags); + +/* ---------- TMFs ---------- */ +int asd_abort_task(struct sas_task *); +int asd_abort_task_set(struct domain_device *, u8 *lun); +int asd_clear_aca(struct domain_device *, u8 *lun); +int asd_clear_task_set(struct domain_device *, u8 *lun); +int asd_lu_reset(struct domain_device *, u8 *lun); +int asd_query_task(struct sas_task *); + +/* ---------- Adapter and Port management ---------- */ +int asd_clear_nexus_port(struct asd_sas_port *port); +int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha); + +/* ---------- Phy Management ---------- */ +int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func); + +#endif diff --git a/drivers/scsi/aic94xx/aic94xx_dev.c b/drivers/scsi/aic94xx/aic94xx_dev.c new file mode 100644 index 00000000000..6f8901b748f --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx_dev.c @@ -0,0 +1,353 @@ +/* + * Aic94xx SAS/SATA DDB management + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * This file is part of the aic94xx driver. + * + * The aic94xx driver 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; version 2 of the + * License. + * + * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: //depot/aic94xx/aic94xx_dev.c#21 $ + */ + +#include "aic94xx.h" +#include "aic94xx_hwi.h" +#include "aic94xx_reg.h" +#include "aic94xx_sas.h" + +#define FIND_FREE_DDB(_ha) find_first_zero_bit((_ha)->hw_prof.ddb_bitmap, \ + (_ha)->hw_prof.max_ddbs) +#define SET_DDB(_ddb, _ha) set_bit(_ddb, (_ha)->hw_prof.ddb_bitmap) +#define CLEAR_DDB(_ddb, _ha) clear_bit(_ddb, (_ha)->hw_prof.ddb_bitmap) + +static inline int asd_get_ddb(struct asd_ha_struct *asd_ha) +{ + unsigned long flags; + int ddb, i; + + spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags); + ddb = FIND_FREE_DDB(asd_ha); + if (ddb >= asd_ha->hw_prof.max_ddbs) { + ddb = -ENOMEM; + spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags); + goto out; + } + SET_DDB(ddb, asd_ha); + spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags); + + for (i = 0; i < sizeof(struct asd_ddb_ssp_smp_target_port); i+= 4) + asd_ddbsite_write_dword(asd_ha, ddb, i, 0); +out: + return ddb; +} + +#define INIT_CONN_TAG offsetof(struct asd_ddb_ssp_smp_target_port, init_conn_tag) +#define DEST_SAS_ADDR offsetof(struct asd_ddb_ssp_smp_target_port, dest_sas_addr) +#define SEND_QUEUE_HEAD offsetof(struct asd_ddb_ssp_smp_target_port, send_queue_head) +#define DDB_TYPE offsetof(struct asd_ddb_ssp_smp_target_port, ddb_type) +#define CONN_MASK offsetof(struct asd_ddb_ssp_smp_target_port, conn_mask) +#define DDB_TARG_FLAGS offsetof(struct asd_ddb_ssp_smp_target_port, flags) +#define DDB_TARG_FLAGS2 offsetof(struct asd_ddb_stp_sata_target_port, flags2) +#define EXEC_QUEUE_TAIL offsetof(struct asd_ddb_ssp_smp_target_port, exec_queue_tail) +#define SEND_QUEUE_TAIL offsetof(struct asd_ddb_ssp_smp_target_port, send_queue_tail) +#define SISTER_DDB offsetof(struct asd_ddb_ssp_smp_target_port, sister_ddb) +#define MAX_CCONN offsetof(struct asd_ddb_ssp_smp_target_port, max_concurrent_conn) +#define NUM_CTX offsetof(struct asd_ddb_ssp_smp_target_port, num_contexts) +#define ATA_CMD_SCBPTR offsetof(struct asd_ddb_stp_sata_target_port, ata_cmd_scbptr) +#define SATA_TAG_ALLOC_MASK offsetof(struct asd_ddb_stp_sata_target_port, sata_tag_alloc_mask) +#define NUM_SATA_TAGS offsetof(struct asd_ddb_stp_sata_target_port, num_sata_tags) +#define SATA_STATUS offsetof(struct asd_ddb_stp_sata_target_port, sata_status) +#define NCQ_DATA_SCB_PTR offsetof(struct asd_ddb_stp_sata_target_port, ncq_data_scb_ptr) +#define ITNL_TIMEOUT offsetof(struct asd_ddb_ssp_smp_target_port, itnl_timeout) + +static inline void asd_free_ddb(struct asd_ha_struct *asd_ha, int ddb) +{ + unsigned long flags; + + if (!ddb || ddb >= 0xFFFF) + return; + asd_ddbsite_write_byte(asd_ha, ddb, DDB_TYPE, DDB_TYPE_UNUSED); + spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags); + CLEAR_DDB(ddb, asd_ha); + spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags); +} + +static inline void asd_set_ddb_type(struct domain_device *dev) +{ + struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; + int ddb = (int) (unsigned long) dev->lldd_dev; + + if (dev->dev_type == SATA_PM_PORT) + asd_ddbsite_write_byte(asd_ha,ddb, DDB_TYPE, DDB_TYPE_PM_PORT); + else if (dev->tproto) + asd_ddbsite_write_byte(asd_ha,ddb, DDB_TYPE, DDB_TYPE_TARGET); + else + asd_ddbsite_write_byte(asd_ha,ddb,DDB_TYPE,DDB_TYPE_INITIATOR); +} + +static int asd_init_sata_tag_ddb(struct domain_device *dev) +{ + struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; + int ddb, i; + + ddb = asd_get_ddb(asd_ha); + if (ddb < 0) + return ddb; + + for (i = 0; i < sizeof(struct asd_ddb_sata_tag); i += 2) + asd_ddbsite_write_word(asd_ha, ddb, i, 0xFFFF); + + asd_ddbsite_write_word(asd_ha, (int) (unsigned long) dev->lldd_dev, + SISTER_DDB, ddb); + return 0; +} + +static inline int asd_init_sata(struct domain_device *dev) +{ + struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; + int ddb = (int) (unsigned long) dev->lldd_dev; + u32 qdepth = 0; + int res = 0; + + asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF); + if ((dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM_PORT) && + dev->sata_dev.identify_device && + dev->sata_dev.identify_device[10] != 0) { + u16 w75 = le16_to_cpu(dev->sata_dev.identify_device[75]); + u16 w76 = le16_to_cpu(dev->sata_dev.identify_device[76]); + + if (w76 & 0x100) /* NCQ? */ + qdepth = (w75 & 0x1F) + 1; + asd_ddbsite_write_dword(asd_ha, ddb, SATA_TAG_ALLOC_MASK, + (1<dev_type == SATA_DEV || dev->dev_type == SATA_PM || + dev->dev_type == SATA_PM_PORT) { + struct dev_to_host_fis *fis = (struct dev_to_host_fis *) + dev->frame_rcvd; + asd_ddbsite_write_byte(asd_ha, ddb, SATA_STATUS, fis->status); + } + asd_ddbsite_write_word(asd_ha, ddb, NCQ_DATA_SCB_PTR, 0xFFFF); + if (qdepth > 0) + res = asd_init_sata_tag_ddb(dev); + return res; +} + +static int asd_init_target_ddb(struct domain_device *dev) +{ + int ddb, i; + struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; + u8 flags = 0; + + ddb = asd_get_ddb(asd_ha); + if (ddb < 0) + return ddb; + + dev->lldd_dev = (void *) (unsigned long) ddb; + + asd_ddbsite_write_byte(asd_ha, ddb, 0, DDB_TP_CONN_TYPE); + asd_ddbsite_write_byte(asd_ha, ddb, 1, 0); + asd_ddbsite_write_word(asd_ha, ddb, INIT_CONN_TAG, 0xFFFF); + for (i = 0; i < SAS_ADDR_SIZE; i++) + asd_ddbsite_write_byte(asd_ha, ddb, DEST_SAS_ADDR+i, + dev->sas_addr[i]); + asd_ddbsite_write_word(asd_ha, ddb, SEND_QUEUE_HEAD, 0xFFFF); + asd_set_ddb_type(dev); + asd_ddbsite_write_byte(asd_ha, ddb, CONN_MASK, dev->port->phy_mask); + if (dev->port->oob_mode != SATA_OOB_MODE) { + flags |= OPEN_REQUIRED; + if ((dev->dev_type == SATA_DEV) || + (dev->tproto & SAS_PROTO_STP)) { + struct smp_resp *rps_resp = &dev->sata_dev.rps_resp; + if (rps_resp->frame_type == SMP_RESPONSE && + rps_resp->function == SMP_REPORT_PHY_SATA && + rps_resp->result == SMP_RESP_FUNC_ACC) { + if (rps_resp->rps.affil_valid) + flags |= STP_AFFIL_POL; + if (rps_resp->rps.affil_supp) + flags |= SUPPORTS_AFFIL; + } + } else { + flags |= CONCURRENT_CONN_SUPP; + if (!dev->parent && + (dev->dev_type == EDGE_DEV || + dev->dev_type == FANOUT_DEV)) + asd_ddbsite_write_byte(asd_ha, ddb, MAX_CCONN, + 4); + else + asd_ddbsite_write_byte(asd_ha, ddb, MAX_CCONN, + dev->pathways); + asd_ddbsite_write_byte(asd_ha, ddb, NUM_CTX, 1); + } + } + if (dev->dev_type == SATA_PM) + flags |= SATA_MULTIPORT; + asd_ddbsite_write_byte(asd_ha, ddb, DDB_TARG_FLAGS, flags); + + flags = 0; + if (dev->tproto & SAS_PROTO_STP) + flags |= STP_CL_POL_NO_TX; + asd_ddbsite_write_byte(asd_ha, ddb, DDB_TARG_FLAGS2, flags); + + asd_ddbsite_write_word(asd_ha, ddb, EXEC_QUEUE_TAIL, 0xFFFF); + asd_ddbsite_write_word(asd_ha, ddb, SEND_QUEUE_TAIL, 0xFFFF); + asd_ddbsite_write_word(asd_ha, ddb, SISTER_DDB, 0xFFFF); + + if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTO_STP)) { + i = asd_init_sata(dev); + if (i < 0) { + asd_free_ddb(asd_ha, ddb); + return i; + } + } + + if (dev->dev_type == SAS_END_DEV) { + struct sas_end_device *rdev = rphy_to_end_device(dev->rphy); + if (rdev->I_T_nexus_loss_timeout > 0) + asd_ddbsite_write_word(asd_ha, ddb, ITNL_TIMEOUT, + min(rdev->I_T_nexus_loss_timeout, + (u16)ITNL_TIMEOUT_CONST)); + else + asd_ddbsite_write_word(asd_ha, ddb, ITNL_TIMEOUT, + (u16)ITNL_TIMEOUT_CONST); + } + return 0; +} + +static int asd_init_sata_pm_table_ddb(struct domain_device *dev) +{ + struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; + int ddb, i; + + ddb = asd_get_ddb(asd_ha); + if (ddb < 0) + return ddb; + + for (i = 0; i < 32; i += 2) + asd_ddbsite_write_word(asd_ha, ddb, i, 0xFFFF); + + asd_ddbsite_write_word(asd_ha, (int) (unsigned long) dev->lldd_dev, + SISTER_DDB, ddb); + + return 0; +} + +#define PM_PORT_FLAGS offsetof(struct asd_ddb_sata_pm_port, pm_port_flags) +#define PARENT_DDB offsetof(struct asd_ddb_sata_pm_port, parent_ddb) + +/** + * asd_init_sata_pm_port_ddb -- SATA Port Multiplier Port + * dev: pointer to domain device + * + * For SATA Port Multiplier Ports we need to allocate one SATA Port + * Multiplier Port DDB and depending on whether the target on it + * supports SATA II NCQ, one SATA Tag DDB. + */ +static int asd_init_sata_pm_port_ddb(struct domain_device *dev) +{ + int ddb, i, parent_ddb, pmtable_ddb; + struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; + u8 flags; + + ddb = asd_get_ddb(asd_ha); + if (ddb < 0) + return ddb; + + asd_set_ddb_type(dev); + flags = (dev->sata_dev.port_no << 4) | PM_PORT_SET; + asd_ddbsite_write_byte(asd_ha, ddb, PM_PORT_FLAGS, flags); + asd_ddbsite_write_word(asd_ha, ddb, SISTER_DDB, 0xFFFF); + asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF); + asd_init_sata(dev); + + parent_ddb = (int) (unsigned long) dev->parent->lldd_dev; + asd_ddbsite_write_word(asd_ha, ddb, PARENT_DDB, parent_ddb); + pmtable_ddb = asd_ddbsite_read_word(asd_ha, parent_ddb, SISTER_DDB); + asd_ddbsite_write_word(asd_ha, pmtable_ddb, dev->sata_dev.port_no,ddb); + + if (asd_ddbsite_read_byte(asd_ha, ddb, NUM_SATA_TAGS) > 0) { + i = asd_init_sata_tag_ddb(dev); + if (i < 0) { + asd_free_ddb(asd_ha, ddb); + return i; + } + } + return 0; +} + +static int asd_init_initiator_ddb(struct domain_device *dev) +{ + return -ENODEV; +} + +/** + * asd_init_sata_pm_ddb -- SATA Port Multiplier + * dev: pointer to domain device + * + * For STP and direct-attached SATA Port Multipliers we need + * one target port DDB entry and one SATA PM table DDB entry. + */ +static int asd_init_sata_pm_ddb(struct domain_device *dev) +{ + int res = 0; + + res = asd_init_target_ddb(dev); + if (res) + goto out; + res = asd_init_sata_pm_table_ddb(dev); + if (res) + asd_free_ddb(dev->port->ha->lldd_ha, + (int) (unsigned long) dev->lldd_dev); +out: + return res; +} + +int asd_dev_found(struct domain_device *dev) +{ + int res = 0; + + switch (dev->dev_type) { + case SATA_PM: + res = asd_init_sata_pm_ddb(dev); + break; + case SATA_PM_PORT: + res = asd_init_sata_pm_port_ddb(dev); + break; + default: + if (dev->tproto) + res = asd_init_target_ddb(dev); + else + res = asd_init_initiator_ddb(dev); + } + return res; +} + +void asd_dev_gone(struct domain_device *dev) +{ + int ddb, sister_ddb; + struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; + + ddb = (int) (unsigned long) dev->lldd_dev; + sister_ddb = asd_ddbsite_read_word(asd_ha, ddb, SISTER_DDB); + + if (sister_ddb != 0xFFFF) + asd_free_ddb(asd_ha, sister_ddb); + asd_free_ddb(asd_ha, ddb); + dev->lldd_dev = NULL; +} diff --git a/drivers/scsi/aic94xx/aic94xx_dump.c b/drivers/scsi/aic94xx/aic94xx_dump.c new file mode 100644 index 00000000000..e6ade5996d9 --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx_dump.c @@ -0,0 +1,959 @@ +/* + * Aic94xx SAS/SATA driver dump interface. + * + * Copyright (C) 2004 Adaptec, Inc. All rights reserved. + * Copyright (C) 2004 David Chaw + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * This file is part of the aic94xx driver. + * + * The aic94xx driver 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; version 2 of the + * License. + * + * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * 2005/07/14/LT Complete overhaul of this file. Update pages, register + * locations, names, etc. Make use of macros. Print more information. + * Print all cseq and lseq mip and mdp. + * + */ + +#include "linux/pci.h" +#include "aic94xx.h" +#include "aic94xx_reg.h" +#include "aic94xx_reg_def.h" +#include "aic94xx_sas.h" + +#include "aic94xx_dump.h" + +#ifdef ASD_DEBUG + +#define MD(x) (1 << (x)) +#define MODE_COMMON (1 << 31) +#define MODE_0_7 (0xFF) + +static const struct lseq_cio_regs { + char *name; + u32 offs; + u8 width; + u32 mode; +} LSEQmCIOREGS[] = { + {"LmMnSCBPTR", 0x20, 16, MD(0)|MD(1)|MD(2)|MD(3)|MD(4) }, + {"LmMnDDBPTR", 0x22, 16, MD(0)|MD(1)|MD(2)|MD(3)|MD(4) }, + {"LmREQMBX", 0x30, 32, MODE_COMMON }, + {"LmRSPMBX", 0x34, 32, MODE_COMMON }, + {"LmMnINT", 0x38, 32, MODE_0_7 }, + {"LmMnINTEN", 0x3C, 32, MODE_0_7 }, + {"LmXMTPRIMD", 0x40, 32, MODE_COMMON }, + {"LmXMTPRIMCS", 0x44, 8, MODE_COMMON }, + {"LmCONSTAT", 0x45, 8, MODE_COMMON }, + {"LmMnDMAERRS", 0x46, 8, MD(0)|MD(1) }, + {"LmMnSGDMAERRS", 0x47, 8, MD(0)|MD(1) }, + {"LmMnEXPHDRP", 0x48, 8, MD(0) }, + {"LmMnSASAALIGN", 0x48, 8, MD(1) }, + {"LmMnMSKHDRP", 0x49, 8, MD(0) }, + {"LmMnSTPALIGN", 0x49, 8, MD(1) }, + {"LmMnRCVHDRP", 0x4A, 8, MD(0) }, + {"LmMnXMTHDRP", 0x4A, 8, MD(1) }, + {"LmALIGNMODE", 0x4B, 8, MD(1) }, + {"LmMnEXPRCVCNT", 0x4C, 32, MD(0) }, + {"LmMnXMTCNT", 0x4C, 32, MD(1) }, + {"LmMnCURRTAG", 0x54, 16, MD(0) }, + {"LmMnPREVTAG", 0x56, 16, MD(0) }, + {"LmMnACKOFS", 0x58, 8, MD(1) }, + {"LmMnXFRLVL", 0x59, 8, MD(0)|MD(1) }, + {"LmMnSGDMACTL", 0x5A, 8, MD(0)|MD(1) }, + {"LmMnSGDMASTAT", 0x5B, 8, MD(0)|MD(1) }, + {"LmMnDDMACTL", 0x5C, 8, MD(0)|MD(1) }, + {"LmMnDDMASTAT", 0x5D, 8, MD(0)|MD(1) }, + {"LmMnDDMAMODE", 0x5E, 16, MD(0)|MD(1) }, + {"LmMnPIPECTL", 0x61, 8, MD(0)|MD(1) }, + {"LmMnACTSCB", 0x62, 16, MD(0)|MD(1) }, + {"LmMnSGBHADR", 0x64, 8, MD(0)|MD(1) }, + {"LmMnSGBADR", 0x65, 8, MD(0)|MD(1) }, + {"LmMnSGDCNT", 0x66, 8, MD(0)|MD(1) }, + {"LmMnSGDMADR", 0x68, 32, MD(0)|MD(1) }, + {"LmMnSGDMADR", 0x6C, 32, MD(0)|MD(1) }, + {"LmMnXFRCNT", 0x70, 32, MD(0)|MD(1) }, + {"LmMnXMTCRC", 0x74, 32, MD(1) }, + {"LmCURRTAG", 0x74, 16, MD(0) }, + {"LmPREVTAG", 0x76, 16, MD(0) }, + {"LmMnDPSEL", 0x7B, 8, MD(0)|MD(1) }, + {"LmDPTHSTAT", 0x7C, 8, MODE_COMMON }, + {"LmMnHOLDLVL", 0x7D, 8, MD(0) }, + {"LmMnSATAFS", 0x7E, 8, MD(1) }, + {"LmMnCMPLTSTAT", 0x7F, 8, MD(0)|MD(1) }, + {"LmPRMSTAT0", 0x80, 32, MODE_COMMON }, + {"LmPRMSTAT1", 0x84, 32, MODE_COMMON }, + {"LmGPRMINT", 0x88, 8, MODE_COMMON }, + {"LmMnCURRSCB", 0x8A, 16, MD(0) }, + {"LmPRMICODE", 0x8C, 32, MODE_COMMON }, + {"LmMnRCVCNT", 0x90, 16, MD(0) }, + {"LmMnBUFSTAT", 0x92, 16, MD(0) }, + {"LmMnXMTHDRSIZE",0x92, 8, MD(1) }, + {"LmMnXMTSIZE", 0x93, 8, MD(1) }, + {"LmMnTGTXFRCNT", 0x94, 32, MD(0) }, + {"LmMnEXPROFS", 0x98, 32, MD(0) }, + {"LmMnXMTROFS", 0x98, 32, MD(1) }, + {"LmMnRCVROFS", 0x9C, 32, MD(0) }, + {"LmCONCTL", 0xA0, 16, MODE_COMMON }, + {"LmBITLTIMER", 0xA2, 16, MODE_COMMON }, + {"LmWWNLOW", 0xA8, 32, MODE_COMMON }, + {"LmWWNHIGH", 0xAC, 32, MODE_COMMON }, + {"LmMnFRMERR", 0xB0, 32, MD(0) }, + {"LmMnFRMERREN", 0xB4, 32, MD(0) }, + {"LmAWTIMER", 0xB8, 16, MODE_COMMON }, + {"LmAWTCTL", 0xBA, 8, MODE_COMMON }, + {"LmMnHDRCMPS", 0xC0, 32, MD(0) }, + {"LmMnXMTSTAT", 0xC4, 8, MD(1) }, + {"LmHWTSTATEN", 0xC5, 8, MODE_COMMON }, + {"LmMnRRDYRC", 0xC6, 8, MD(0) }, + {"LmMnRRDYTC", 0xC6, 8, MD(1) }, + {"LmHWTSTAT", 0xC7, 8, MODE_COMMON }, + {"LmMnDATABUFADR",0xC8, 16, MD(0)|MD(1) }, + {"LmDWSSTATUS", 0xCB, 8, MODE_COMMON }, + {"LmMnACTSTAT", 0xCE, 16, MD(0)|MD(1) }, + {"LmMnREQSCB", 0xD2, 16, MD(0)|MD(1) }, + {"LmXXXPRIM", 0xD4, 32, MODE_COMMON }, + {"LmRCVASTAT", 0xD9, 8, MODE_COMMON }, + {"LmINTDIS1", 0xDA, 8, MODE_COMMON }, + {"LmPSTORESEL", 0xDB, 8, MODE_COMMON }, + {"LmPSTORE", 0xDC, 32, MODE_COMMON }, + {"LmPRIMSTAT0EN", 0xE0, 32, MODE_COMMON }, + {"LmPRIMSTAT1EN", 0xE4, 32, MODE_COMMON }, + {"LmDONETCTL", 0xF2, 16, MODE_COMMON }, + {NULL, 0, 0, 0 } +}; +/* +static struct lseq_cio_regs LSEQmOOBREGS[] = { + {"OOB_BFLTR" ,0x100, 8, MD(5)}, + {"OOB_INIT_MIN" ,0x102,16, MD(5)}, + {"OOB_INIT_MAX" ,0x104,16, MD(5)}, + {"OOB_INIT_NEG" ,0x106,16, MD(5)}, + {"OOB_SAS_MIN" ,0x108,16, MD(5)}, + {"OOB_SAS_MAX" ,0x10A,16, MD(5)}, + {"OOB_SAS_NEG" ,0x10C,16, MD(5)}, + {"OOB_WAKE_MIN" ,0x10E,16, MD(5)}, + {"OOB_WAKE_MAX" ,0x110,16, MD(5)}, + {"OOB_WAKE_NEG" ,0x112,16, MD(5)}, + {"OOB_IDLE_MAX" ,0x114,16, MD(5)}, + {"OOB_BURST_MAX" ,0x116,16, MD(5)}, + {"OOB_XMIT_BURST" ,0x118, 8, MD(5)}, + {"OOB_SEND_PAIRS" ,0x119, 8, MD(5)}, + {"OOB_INIT_IDLE" ,0x11A, 8, MD(5)}, + {"OOB_INIT_NEGO" ,0x11C, 8, MD(5)}, + {"OOB_SAS_IDLE" ,0x11E, 8, MD(5)}, + {"OOB_SAS_NEGO" ,0x120, 8, MD(5)}, + {"OOB_WAKE_IDLE" ,0x122, 8, MD(5)}, + {"OOB_WAKE_NEGO" ,0x124, 8, MD(5)}, + {"OOB_DATA_KBITS" ,0x126, 8, MD(5)}, + {"OOB_BURST_DATA" ,0x128,32, MD(5)}, + {"OOB_ALIGN_0_DATA" ,0x12C,32, MD(5)}, + {"OOB_ALIGN_1_DATA" ,0x130,32, MD(5)}, + {"OOB_SYNC_DATA" ,0x134,32, MD(5)}, + {"OOB_D10_2_DATA" ,0x138,32, MD(5)}, + {"OOB_PHY_RST_CNT" ,0x13C,32, MD(5)}, + {"OOB_SIG_GEN" ,0x140, 8, MD(5)}, + {"OOB_XMIT" ,0x141, 8, MD(5)}, + {"FUNCTION_MAKS" ,0x142, 8, MD(5)}, + {"OOB_MODE" ,0x143, 8, MD(5)}, + {"CURRENT_STATUS" ,0x144, 8, MD(5)}, + {"SPEED_MASK" ,0x145, 8, MD(5)}, + {"PRIM_COUNT" ,0x146, 8, MD(5)}, + {"OOB_SIGNALS" ,0x148, 8, MD(5)}, + {"OOB_DATA_DET" ,0x149, 8, MD(5)}, + {"OOB_TIME_OUT" ,0x14C, 8, MD(5)}, + {"OOB_TIMER_ENABLE" ,0x14D, 8, MD(5)}, + {"OOB_STATUS" ,0x14E, 8, MD(5)}, + {"HOT_PLUG_DELAY" ,0x150, 8, MD(5)}, + {"RCD_DELAY" ,0x151, 8, MD(5)}, + {"COMSAS_TIMER" ,0x152, 8, MD(5)}, + {"SNTT_DELAY" ,0x153, 8, MD(5)}, + {"SPD_CHNG_DELAY" ,0x154, 8, MD(5)}, + {"SNLT_DELAY" ,0x155, 8, MD(5)}, + {"SNWT_DELAY" ,0x156, 8, MD(5)}, + {"ALIGN_DELAY" ,0x157, 8, MD(5)}, + {"INT_ENABLE_0" ,0x158, 8, MD(5)}, + {"INT_ENABLE_1" ,0x159, 8, MD(5)}, + {"INT_ENABLE_2" ,0x15A, 8, MD(5)}, + {"INT_ENABLE_3" ,0x15B, 8, MD(5)}, + {"OOB_TEST_REG" ,0x15C, 8, MD(5)}, + {"PHY_CONTROL_0" ,0x160, 8, MD(5)}, + {"PHY_CONTROL_1" ,0x161, 8, MD(5)}, + {"PHY_CONTROL_2" ,0x162, 8, MD(5)}, + {"PHY_CONTROL_3" ,0x163, 8, MD(5)}, + {"PHY_OOB_CAL_TX" ,0x164, 8, MD(5)}, + {"PHY_OOB_CAL_RX" ,0x165, 8, MD(5)}, + {"OOB_PHY_CAL_TX" ,0x166, 8, MD(5)}, + {"OOB_PHY_CAL_RX" ,0x167, 8, MD(5)}, + {"PHY_CONTROL_4" ,0x168, 8, MD(5)}, + {"PHY_TEST" ,0x169, 8, MD(5)}, + {"PHY_PWR_CTL" ,0x16A, 8, MD(5)}, + {"PHY_PWR_DELAY" ,0x16B, 8, MD(5)}, + {"OOB_SM_CON" ,0x16C, 8, MD(5)}, + {"ADDR_TRAP_1" ,0x16D, 8, MD(5)}, + {"ADDR_NEXT_1" ,0x16E, 8, MD(5)}, + {"NEXT_ST_1" ,0x16F, 8, MD(5)}, + {"OOB_SM_STATE" ,0x170, 8, MD(5)}, + {"ADDR_TRAP_2" ,0x171, 8, MD(5)}, + {"ADDR_NEXT_2" ,0x172, 8, MD(5)}, + {"NEXT_ST_2" ,0x173, 8, MD(5)}, + {NULL, 0, 0, 0 } +}; +*/ +#define STR_8BIT " %30s[0x%04x]:0x%02x\n" +#define STR_16BIT " %30s[0x%04x]:0x%04x\n" +#define STR_32BIT " %30s[0x%04x]:0x%08x\n" +#define STR_64BIT " %30s[0x%04x]:0x%llx\n" + +#define PRINT_REG_8bit(_ha, _n, _r) asd_printk(STR_8BIT, #_n, _n, \ + asd_read_reg_byte(_ha, _r)) +#define PRINT_REG_16bit(_ha, _n, _r) asd_printk(STR_16BIT, #_n, _n, \ + asd_read_reg_word(_ha, _r)) +#define PRINT_REG_32bit(_ha, _n, _r) asd_printk(STR_32BIT, #_n, _n, \ + asd_read_reg_dword(_ha, _r)) + +#define PRINT_CREG_8bit(_ha, _n) asd_printk(STR_8BIT, #_n, _n, \ + asd_read_reg_byte(_ha, C##_n)) +#define PRINT_CREG_16bit(_ha, _n) asd_printk(STR_16BIT, #_n, _n, \ + asd_read_reg_word(_ha, C##_n)) +#define PRINT_CREG_32bit(_ha, _n) asd_printk(STR_32BIT, #_n, _n, \ + asd_read_reg_dword(_ha, C##_n)) + +#define MSTR_8BIT " Mode:%02d %30s[0x%04x]:0x%02x\n" +#define MSTR_16BIT " Mode:%02d %30s[0x%04x]:0x%04x\n" +#define MSTR_32BIT " Mode:%02d %30s[0x%04x]:0x%08x\n" + +#define PRINT_MREG_8bit(_ha, _m, _n, _r) asd_printk(MSTR_8BIT, _m, #_n, _n, \ + asd_read_reg_byte(_ha, _r)) +#define PRINT_MREG_16bit(_ha, _m, _n, _r) asd_printk(MSTR_16BIT, _m, #_n, _n, \ + asd_read_reg_word(_ha, _r)) +#define PRINT_MREG_32bit(_ha, _m, _n, _r) asd_printk(MSTR_32BIT, _m, #_n, _n, \ + asd_read_reg_dword(_ha, _r)) + +/* can also be used for MD when the register is mode aware already */ +#define PRINT_MIS_byte(_ha, _n) asd_printk(STR_8BIT, #_n,CSEQ_##_n-CMAPPEDSCR,\ + asd_read_reg_byte(_ha, CSEQ_##_n)) +#define PRINT_MIS_word(_ha, _n) asd_printk(STR_16BIT,#_n,CSEQ_##_n-CMAPPEDSCR,\ + asd_read_reg_word(_ha, CSEQ_##_n)) +#define PRINT_MIS_dword(_ha, _n) \ + asd_printk(STR_32BIT,#_n,CSEQ_##_n-CMAPPEDSCR,\ + asd_read_reg_dword(_ha, CSEQ_##_n)) +#define PRINT_MIS_qword(_ha, _n) \ + asd_printk(STR_64BIT, #_n,CSEQ_##_n-CMAPPEDSCR, \ + (unsigned long long)(((u64)asd_read_reg_dword(_ha, CSEQ_##_n)) \ + | (((u64)asd_read_reg_dword(_ha, (CSEQ_##_n)+4))<<32))) + +#define CMDP_REG(_n, _m) (_m*(CSEQ_PAGE_SIZE*2)+CSEQ_##_n) +#define PRINT_CMDP_word(_ha, _n) \ +asd_printk("%20s 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n", \ + #_n, \ + asd_read_reg_word(_ha, CMDP_REG(_n, 0)), \ + asd_read_reg_word(_ha, CMDP_REG(_n, 1)), \ + asd_read_reg_word(_ha, CMDP_REG(_n, 2)), \ + asd_read_reg_word(_ha, CMDP_REG(_n, 3)), \ + asd_read_reg_word(_ha, CMDP_REG(_n, 4)), \ + asd_read_reg_word(_ha, CMDP_REG(_n, 5)), \ + asd_read_reg_word(_ha, CMDP_REG(_n, 6)), \ + asd_read_reg_word(_ha, CMDP_REG(_n, 7))) + +#define PRINT_CMDP_byte(_ha, _n) \ +asd_printk("%20s 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n", \ + #_n, \ + asd_read_reg_byte(_ha, CMDP_REG(_n, 0)), \ + asd_read_reg_byte(_ha, CMDP_REG(_n, 1)), \ + asd_read_reg_byte(_ha, CMDP_REG(_n, 2)), \ + asd_read_reg_byte(_ha, CMDP_REG(_n, 3)), \ + asd_read_reg_byte(_ha, CMDP_REG(_n, 4)), \ + asd_read_reg_byte(_ha, CMDP_REG(_n, 5)), \ + asd_read_reg_byte(_ha, CMDP_REG(_n, 6)), \ + asd_read_reg_byte(_ha, CMDP_REG(_n, 7))) + +static void asd_dump_cseq_state(struct asd_ha_struct *asd_ha) +{ + int mode; + + asd_printk("CSEQ STATE\n"); + + asd_printk("ARP2 REGISTERS\n"); + + PRINT_CREG_32bit(asd_ha, ARP2CTL); + PRINT_CREG_32bit(asd_ha, ARP2INT); + PRINT_CREG_32bit(asd_ha, ARP2INTEN); + PRINT_CREG_8bit(asd_ha, MODEPTR); + PRINT_CREG_8bit(asd_ha, ALTMODE); + PRINT_CREG_8bit(asd_ha, FLAG); + PRINT_CREG_8bit(asd_ha, ARP2INTCTL); + PRINT_CREG_16bit(asd_ha, STACK); + PRINT_CREG_16bit(asd_ha, PRGMCNT); + PRINT_CREG_16bit(asd_ha, ACCUM); + PRINT_CREG_16bit(asd_ha, SINDEX); + PRINT_CREG_16bit(asd_ha, DINDEX); + PRINT_CREG_8bit(asd_ha, SINDIR); + PRINT_CREG_8bit(asd_ha, DINDIR); + PRINT_CREG_8bit(asd_ha, JUMLDIR); + PRINT_CREG_8bit(asd_ha, ARP2HALTCODE); + PRINT_CREG_16bit(asd_ha, CURRADDR); + PRINT_CREG_16bit(asd_ha, LASTADDR); + PRINT_CREG_16bit(asd_ha, NXTLADDR); + + asd_printk("IOP REGISTERS\n"); + + PRINT_REG_32bit(asd_ha, BISTCTL1, CBISTCTL); + PRINT_CREG_32bit(asd_ha, MAPPEDSCR); + + asd_printk("CIO REGISTERS\n"); + + for (mode = 0; mode < 9; mode++) + PRINT_MREG_16bit(asd_ha, mode, MnSCBPTR, CMnSCBPTR(mode)); + PRINT_MREG_16bit(asd_ha, 15, MnSCBPTR, CMnSCBPTR(15)); + + for (mode = 0; mode < 9; mode++) + PRINT_MREG_16bit(asd_ha, mode, MnDDBPTR, CMnDDBPTR(mode)); + PRINT_MREG_16bit(asd_ha, 15, MnDDBPTR, CMnDDBPTR(15)); + + for (mode = 0; mode < 8; mode++) + PRINT_MREG_32bit(asd_ha, mode, MnREQMBX, CMnREQMBX(mode)); + for (mode = 0; mode < 8; mode++) + PRINT_MREG_32bit(asd_ha, mode, MnRSPMBX, CMnRSPMBX(mode)); + for (mode = 0; mode < 8; mode++) + PRINT_MREG_32bit(asd_ha, mode, MnINT, CMnINT(mode)); + for (mode = 0; mode < 8; mode++) + PRINT_MREG_32bit(asd_ha, mode, MnINTEN, CMnINTEN(mode)); + + PRINT_CREG_8bit(asd_ha, SCRATCHPAGE); + for (mode = 0; mode < 8; mode++) + PRINT_MREG_8bit(asd_ha, mode, MnSCRATCHPAGE, + CMnSCRATCHPAGE(mode)); + + PRINT_REG_32bit(asd_ha, CLINKCON, CLINKCON); + PRINT_REG_8bit(asd_ha, CCONMSK, CCONMSK); + PRINT_REG_8bit(asd_ha, CCONEXIST, CCONEXIST); + PRINT_REG_16bit(asd_ha, CCONMODE, CCONMODE); + PRINT_REG_32bit(asd_ha, CTIMERCALC, CTIMERCALC); + PRINT_REG_8bit(asd_ha, CINTDIS, CINTDIS); + + asd_printk("SCRATCH MEMORY\n"); + + asd_printk("MIP 4 >>>>>\n"); + PRINT_MIS_word(asd_ha, Q_EXE_HEAD); + PRINT_MIS_word(asd_ha, Q_EXE_TAIL); + PRINT_MIS_word(asd_ha, Q_DONE_HEAD); + PRINT_MIS_word(asd_ha, Q_DONE_TAIL); + PRINT_MIS_word(asd_ha, Q_SEND_HEAD); + PRINT_MIS_word(asd_ha, Q_SEND_TAIL); + PRINT_MIS_word(asd_ha, Q_DMA2CHIM_HEAD); + PRINT_MIS_word(asd_ha, Q_DMA2CHIM_TAIL); + PRINT_MIS_word(asd_ha, Q_COPY_HEAD); + PRINT_MIS_word(asd_ha, Q_COPY_TAIL); + PRINT_MIS_word(asd_ha, REG0); + PRINT_MIS_word(asd_ha, REG1); + PRINT_MIS_dword(asd_ha, REG2); + PRINT_MIS_byte(asd_ha, LINK_CTL_Q_MAP); + PRINT_MIS_byte(asd_ha, MAX_CSEQ_MODE); + PRINT_MIS_byte(asd_ha, FREE_LIST_HACK_COUNT); + + asd_printk("MIP 5 >>>>\n"); + PRINT_MIS_qword(asd_ha, EST_NEXUS_REQ_QUEUE); + PRINT_MIS_qword(asd_ha, EST_NEXUS_REQ_COUNT); + PRINT_MIS_word(asd_ha, Q_EST_NEXUS_HEAD); + PRINT_MIS_word(asd_ha, Q_EST_NEXUS_TAIL); + PRINT_MIS_word(asd_ha, NEED_EST_NEXUS_SCB); + PRINT_MIS_byte(asd_ha, EST_NEXUS_REQ_HEAD); + PRINT_MIS_byte(asd_ha, EST_NEXUS_REQ_TAIL); + PRINT_MIS_byte(asd_ha, EST_NEXUS_SCB_OFFSET); + + asd_printk("MIP 6 >>>>\n"); + PRINT_MIS_word(asd_ha, INT_ROUT_RET_ADDR0); + PRINT_MIS_word(asd_ha, INT_ROUT_RET_ADDR1); + PRINT_MIS_word(asd_ha, INT_ROUT_SCBPTR); + PRINT_MIS_byte(asd_ha, INT_ROUT_MODE); + PRINT_MIS_byte(asd_ha, ISR_SCRATCH_FLAGS); + PRINT_MIS_word(asd_ha, ISR_SAVE_SINDEX); + PRINT_MIS_word(asd_ha, ISR_SAVE_DINDEX); + PRINT_MIS_word(asd_ha, Q_MONIRTT_HEAD); + PRINT_MIS_word(asd_ha, Q_MONIRTT_TAIL); + PRINT_MIS_byte(asd_ha, FREE_SCB_MASK); + PRINT_MIS_word(asd_ha, BUILTIN_FREE_SCB_HEAD); + PRINT_MIS_word(asd_ha, BUILTIN_FREE_SCB_TAIL); + PRINT_MIS_word(asd_ha, EXTENDED_FREE_SCB_HEAD); + PRINT_MIS_word(asd_ha, EXTENDED_FREE_SCB_TAIL); + + asd_printk("MIP 7 >>>>\n"); + PRINT_MIS_qword(asd_ha, EMPTY_REQ_QUEUE); + PRINT_MIS_qword(asd_ha, EMPTY_REQ_COUNT); + PRINT_MIS_word(asd_ha, Q_EMPTY_HEAD); + PRINT_MIS_word(asd_ha, Q_EMPTY_TAIL); + PRINT_MIS_word(asd_ha, NEED_EMPTY_SCB); + PRINT_MIS_byte(asd_ha, EMPTY_REQ_HEAD); + PRINT_MIS_byte(asd_ha, EMPTY_REQ_TAIL); + PRINT_MIS_byte(asd_ha, EMPTY_SCB_OFFSET); + PRINT_MIS_word(asd_ha, PRIMITIVE_DATA); + PRINT_MIS_dword(asd_ha, TIMEOUT_CONST); + + asd_printk("MDP 0 >>>>\n"); + asd_printk("%-20s %6s %6s %6s %6s %6s %6s %6s %6s\n", + "Mode: ", "0", "1", "2", "3", "4", "5", "6", "7"); + PRINT_CMDP_word(asd_ha, LRM_SAVE_SINDEX); + PRINT_CMDP_word(asd_ha, LRM_SAVE_SCBPTR); + PRINT_CMDP_word(asd_ha, Q_LINK_HEAD); + PRINT_CMDP_word(asd_ha, Q_LINK_TAIL); + PRINT_CMDP_byte(asd_ha, LRM_SAVE_SCRPAGE); + + asd_printk("MDP 0 Mode 8 >>>>\n"); + PRINT_MIS_word(asd_ha, RET_ADDR); + PRINT_MIS_word(asd_ha, RET_SCBPTR); + PRINT_MIS_word(asd_ha, SAVE_SCBPTR); + PRINT_MIS_word(asd_ha, EMPTY_TRANS_CTX); + PRINT_MIS_word(asd_ha, RESP_LEN); + PRINT_MIS_word(asd_ha, TMF_SCBPTR); + PRINT_MIS_word(asd_ha, GLOBAL_PREV_SCB); + PRINT_MIS_word(asd_ha, GLOBAL_HEAD); + PRINT_MIS_word(asd_ha, CLEAR_LU_HEAD); + PRINT_MIS_byte(asd_ha, TMF_OPCODE); + PRINT_MIS_byte(asd_ha, SCRATCH_FLAGS); + PRINT_MIS_word(asd_ha, HSB_SITE); + PRINT_MIS_word(asd_ha, FIRST_INV_SCB_SITE); + PRINT_MIS_word(asd_ha, FIRST_INV_DDB_SITE); + + asd_printk("MDP 1 Mode 8 >>>>\n"); + PRINT_MIS_qword(asd_ha, LUN_TO_CLEAR); + PRINT_MIS_qword(asd_ha, LUN_TO_CHECK); + + asd_printk("MDP 2 Mode 8 >>>>\n"); + PRINT_MIS_qword(asd_ha, HQ_NEW_POINTER); + PRINT_MIS_qword(asd_ha, HQ_DONE_BASE); + PRINT_MIS_dword(asd_ha, HQ_DONE_POINTER); + PRINT_MIS_byte(asd_ha, HQ_DONE_PASS); +} + +#define PRINT_LREG_8bit(_h, _lseq, _n) \ + asd_printk(STR_8BIT, #_n, _n, asd_read_reg_byte(_h, Lm##_n(_lseq))) +#define PRINT_LREG_16bit(_h, _lseq, _n) \ + asd_printk(STR_16BIT, #_n, _n, asd_read_reg_word(_h, Lm##_n(_lseq))) +#define PRINT_LREG_32bit(_h, _lseq, _n) \ + asd_printk(STR_32BIT, #_n, _n, asd_read_reg_dword(_h, Lm##_n(_lseq))) + +#define PRINT_LMIP_byte(_h, _lseq, _n) \ + asd_printk(STR_8BIT, #_n, LmSEQ_##_n(_lseq)-LmSCRATCH(_lseq), \ + asd_read_reg_byte(_h, LmSEQ_##_n(_lseq))) +#define PRINT_LMIP_word(_h, _lseq, _n) \ + asd_printk(STR_16BIT, #_n, LmSEQ_##_n(_lseq)-LmSCRATCH(_lseq), \ + asd_read_reg_word(_h, LmSEQ_##_n(_lseq))) +#define PRINT_LMIP_dword(_h, _lseq, _n) \ + asd_printk(STR_32BIT, #_n, LmSEQ_##_n(_lseq)-LmSCRATCH(_lseq), \ + asd_read_reg_dword(_h, LmSEQ_##_n(_lseq))) +#define PRINT_LMIP_qword(_h, _lseq, _n) \ + asd_printk(STR_64BIT, #_n, LmSEQ_##_n(_lseq)-LmSCRATCH(_lseq), \ + (unsigned long long)(((unsigned long long) \ + asd_read_reg_dword(_h, LmSEQ_##_n(_lseq))) \ + | (((unsigned long long) \ + asd_read_reg_dword(_h, LmSEQ_##_n(_lseq)+4))<<32))) + +static void asd_print_lseq_cio_reg(struct asd_ha_struct *asd_ha, + u32 lseq_cio_addr, int i) +{ + switch (LSEQmCIOREGS[i].width) { + case 8: + asd_printk("%20s[0x%x]: 0x%02x\n", LSEQmCIOREGS[i].name, + LSEQmCIOREGS[i].offs, + asd_read_reg_byte(asd_ha, lseq_cio_addr + + LSEQmCIOREGS[i].offs)); + + break; + case 16: + asd_printk("%20s[0x%x]: 0x%04x\n", LSEQmCIOREGS[i].name, + LSEQmCIOREGS[i].offs, + asd_read_reg_word(asd_ha, lseq_cio_addr + + LSEQmCIOREGS[i].offs)); + + break; + case 32: + asd_printk("%20s[0x%x]: 0x%08x\n", LSEQmCIOREGS[i].name, + LSEQmCIOREGS[i].offs, + asd_read_reg_dword(asd_ha, lseq_cio_addr + + LSEQmCIOREGS[i].offs)); + break; + } +} + +static void asd_dump_lseq_state(struct asd_ha_struct *asd_ha, int lseq) +{ + u32 moffs; + int mode; + + asd_printk("LSEQ %d STATE\n", lseq); + + asd_printk("LSEQ%d: ARP2 REGISTERS\n", lseq); + PRINT_LREG_32bit(asd_ha, lseq, ARP2CTL); + PRINT_LREG_32bit(asd_ha, lseq, ARP2INT); + PRINT_LREG_32bit(asd_ha, lseq, ARP2INTEN); + PRINT_LREG_8bit(asd_ha, lseq, MODEPTR); + PRINT_LREG_8bit(asd_ha, lseq, ALTMODE); + PRINT_LREG_8bit(asd_ha, lseq, FLAG); + PRINT_LREG_8bit(asd_ha, lseq, ARP2INTCTL); + PRINT_LREG_16bit(asd_ha, lseq, STACK); + PRINT_LREG_16bit(asd_ha, lseq, PRGMCNT); + PRINT_LREG_16bit(asd_ha, lseq, ACCUM); + PRINT_LREG_16bit(asd_ha, lseq, SINDEX); + PRINT_LREG_16bit(asd_ha, lseq, DINDEX); + PRINT_LREG_8bit(asd_ha, lseq, SINDIR); + PRINT_LREG_8bit(asd_ha, lseq, DINDIR); + PRINT_LREG_8bit(asd_ha, lseq, JUMLDIR); + PRINT_LREG_8bit(asd_ha, lseq, ARP2HALTCODE); + PRINT_LREG_16bit(asd_ha, lseq, CURRADDR); + PRINT_LREG_16bit(asd_ha, lseq, LASTADDR); + PRINT_LREG_16bit(asd_ha, lseq, NXTLADDR); + + asd_printk("LSEQ%d: IOP REGISTERS\n", lseq); + + PRINT_LREG_32bit(asd_ha, lseq, MODECTL); + PRINT_LREG_32bit(asd_ha, lseq, DBGMODE); + PRINT_LREG_32bit(asd_ha, lseq, CONTROL); + PRINT_REG_32bit(asd_ha, BISTCTL0, LmBISTCTL0(lseq)); + PRINT_REG_32bit(asd_ha, BISTCTL1, LmBISTCTL1(lseq)); + + asd_printk("LSEQ%d: CIO REGISTERS\n", lseq); + asd_printk("Mode common:\n"); + + for (mode = 0; mode < 8; mode++) { + u32 lseq_cio_addr = LmSEQ_PHY_BASE(mode, lseq); + int i; + + for (i = 0; LSEQmCIOREGS[i].name; i++) + if (LSEQmCIOREGS[i].mode == MODE_COMMON) + asd_print_lseq_cio_reg(asd_ha,lseq_cio_addr,i); + } + + asd_printk("Mode unique:\n"); + for (mode = 0; mode < 8; mode++) { + u32 lseq_cio_addr = LmSEQ_PHY_BASE(mode, lseq); + int i; + + asd_printk("Mode %d\n", mode); + for (i = 0; LSEQmCIOREGS[i].name; i++) { + if (!(LSEQmCIOREGS[i].mode & (1 << mode))) + continue; + asd_print_lseq_cio_reg(asd_ha, lseq_cio_addr, i); + } + } + + asd_printk("SCRATCH MEMORY\n"); + + asd_printk("LSEQ%d MIP 0 >>>>\n", lseq); + PRINT_LMIP_word(asd_ha, lseq, Q_TGTXFR_HEAD); + PRINT_LMIP_word(asd_ha, lseq, Q_TGTXFR_TAIL); + PRINT_LMIP_byte(asd_ha, lseq, LINK_NUMBER); + PRINT_LMIP_byte(asd_ha, lseq, SCRATCH_FLAGS); + PRINT_LMIP_qword(asd_ha, lseq, CONNECTION_STATE); + PRINT_LMIP_word(asd_ha, lseq, CONCTL); + PRINT_LMIP_byte(asd_ha, lseq, CONSTAT); + PRINT_LMIP_byte(asd_ha, lseq, CONNECTION_MODES); + PRINT_LMIP_word(asd_ha, lseq, REG1_ISR); + PRINT_LMIP_word(asd_ha, lseq, REG2_ISR); + PRINT_LMIP_word(asd_ha, lseq, REG3_ISR); + PRINT_LMIP_qword(asd_ha, lseq,REG0_ISR); + + asd_printk("LSEQ%d MIP 1 >>>>\n", lseq); + PRINT_LMIP_word(asd_ha, lseq, EST_NEXUS_SCBPTR0); + PRINT_LMIP_word(asd_ha, lseq, EST_NEXUS_SCBPTR1); + PRINT_LMIP_word(asd_ha, lseq, EST_NEXUS_SCBPTR2); + PRINT_LMIP_word(asd_ha, lseq, EST_NEXUS_SCBPTR3); + PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_OPCODE0); + PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_OPCODE1); + PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_OPCODE2); + PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_OPCODE3); + PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_HEAD); + PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_TAIL); + PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_BUF_AVAIL); + PRINT_LMIP_dword(asd_ha, lseq, TIMEOUT_CONST); + PRINT_LMIP_word(asd_ha, lseq, ISR_SAVE_SINDEX); + PRINT_LMIP_word(asd_ha, lseq, ISR_SAVE_DINDEX); + + asd_printk("LSEQ%d MIP 2 >>>>\n", lseq); + PRINT_LMIP_word(asd_ha, lseq, EMPTY_SCB_PTR0); + PRINT_LMIP_word(asd_ha, lseq, EMPTY_SCB_PTR1); + PRINT_LMIP_word(asd_ha, lseq, EMPTY_SCB_PTR2); + PRINT_LMIP_word(asd_ha, lseq, EMPTY_SCB_PTR3); + PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_OPCD0); + PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_OPCD1); + PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_OPCD2); + PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_OPCD3); + PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_HEAD); + PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_TAIL); + PRINT_LMIP_byte(asd_ha, lseq, EMPTY_BUFS_AVAIL); + + asd_printk("LSEQ%d MIP 3 >>>>\n", lseq); + PRINT_LMIP_dword(asd_ha, lseq, DEV_PRES_TMR_TOUT_CONST); + PRINT_LMIP_dword(asd_ha, lseq, SATA_INTERLOCK_TIMEOUT); + PRINT_LMIP_dword(asd_ha, lseq, SRST_ASSERT_TIMEOUT); + PRINT_LMIP_dword(asd_ha, lseq, RCV_FIS_TIMEOUT); + PRINT_LMIP_dword(asd_ha, lseq, ONE_MILLISEC_TIMEOUT); + PRINT_LMIP_dword(asd_ha, lseq, TEN_MS_COMINIT_TIMEOUT); + PRINT_LMIP_dword(asd_ha, lseq, SMP_RCV_TIMEOUT); + + for (mode = 0; mode < 3; mode++) { + asd_printk("LSEQ%d MDP 0 MODE %d >>>>\n", lseq, mode); + moffs = mode * LSEQ_MODE_SCRATCH_SIZE; + + asd_printk(STR_16BIT, "RET_ADDR", 0, + asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR(lseq) + + moffs)); + asd_printk(STR_16BIT, "REG0_MODE", 2, + asd_read_reg_word(asd_ha, LmSEQ_REG0_MODE(lseq) + + moffs)); + asd_printk(STR_16BIT, "MODE_FLAGS", 4, + asd_read_reg_word(asd_ha, LmSEQ_MODE_FLAGS(lseq) + + moffs)); + asd_printk(STR_16BIT, "RET_ADDR2", 0x6, + asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR2(lseq) + + moffs)); + asd_printk(STR_16BIT, "RET_ADDR1", 0x8, + asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR1(lseq) + + moffs)); + asd_printk(STR_8BIT, "OPCODE_TO_CSEQ", 0xB, + asd_read_reg_byte(asd_ha, LmSEQ_OPCODE_TO_CSEQ(lseq) + + moffs)); + asd_printk(STR_16BIT, "DATA_TO_CSEQ", 0xC, + asd_read_reg_word(asd_ha, LmSEQ_DATA_TO_CSEQ(lseq) + + moffs)); + } + + asd_printk("LSEQ%d MDP 0 MODE 5 >>>>\n", lseq); + moffs = LSEQ_MODE5_PAGE0_OFFSET; + asd_printk(STR_16BIT, "RET_ADDR", 0, + asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR(lseq) + moffs)); + asd_printk(STR_16BIT, "REG0_MODE", 2, + asd_read_reg_word(asd_ha, LmSEQ_REG0_MODE(lseq) + moffs)); + asd_printk(STR_16BIT, "MODE_FLAGS", 4, + asd_read_reg_word(asd_ha, LmSEQ_MODE_FLAGS(lseq) + moffs)); + asd_printk(STR_16BIT, "RET_ADDR2", 0x6, + asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR2(lseq) + moffs)); + asd_printk(STR_16BIT, "RET_ADDR1", 0x8, + asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR1(lseq) + moffs)); + asd_printk(STR_8BIT, "OPCODE_TO_CSEQ", 0xB, + asd_read_reg_byte(asd_ha, LmSEQ_OPCODE_TO_CSEQ(lseq) + moffs)); + asd_printk(STR_16BIT, "DATA_TO_CSEQ", 0xC, + asd_read_reg_word(asd_ha, LmSEQ_DATA_TO_CSEQ(lseq) + moffs)); + + asd_printk("LSEQ%d MDP 0 MODE 0 >>>>\n", lseq); + PRINT_LMIP_word(asd_ha, lseq, FIRST_INV_DDB_SITE); + PRINT_LMIP_word(asd_ha, lseq, EMPTY_TRANS_CTX); + PRINT_LMIP_word(asd_ha, lseq, RESP_LEN); + PRINT_LMIP_word(asd_ha, lseq, FIRST_INV_SCB_SITE); + PRINT_LMIP_dword(asd_ha, lseq, INTEN_SAVE); + PRINT_LMIP_byte(asd_ha, lseq, LINK_RST_FRM_LEN); + PRINT_LMIP_byte(asd_ha, lseq, LINK_RST_PROTOCOL); + PRINT_LMIP_byte(asd_ha, lseq, RESP_STATUS); + PRINT_LMIP_byte(asd_ha, lseq, LAST_LOADED_SGE); + PRINT_LMIP_byte(asd_ha, lseq, SAVE_SCBPTR); + + asd_printk("LSEQ%d MDP 0 MODE 1 >>>>\n", lseq); + PRINT_LMIP_word(asd_ha, lseq, Q_XMIT_HEAD); + PRINT_LMIP_word(asd_ha, lseq, M1_EMPTY_TRANS_CTX); + PRINT_LMIP_word(asd_ha, lseq, INI_CONN_TAG); + PRINT_LMIP_byte(asd_ha, lseq, FAILED_OPEN_STATUS); + PRINT_LMIP_byte(asd_ha, lseq, XMIT_REQUEST_TYPE); + PRINT_LMIP_byte(asd_ha, lseq, M1_RESP_STATUS); + PRINT_LMIP_byte(asd_ha, lseq, M1_LAST_LOADED_SGE); + PRINT_LMIP_word(asd_ha, lseq, M1_SAVE_SCBPTR); + + asd_printk("LSEQ%d MDP 0 MODE 2 >>>>\n", lseq); + PRINT_LMIP_word(asd_ha, lseq, PORT_COUNTER); + PRINT_LMIP_word(asd_ha, lseq, PM_TABLE_PTR); + PRINT_LMIP_word(asd_ha, lseq, SATA_INTERLOCK_TMR_SAVE); + PRINT_LMIP_word(asd_ha, lseq, IP_BITL); + PRINT_LMIP_word(asd_ha, lseq, COPY_SMP_CONN_TAG); + PRINT_LMIP_byte(asd_ha, lseq, P0M2_OFFS1AH); + + asd_printk("LSEQ%d MDP 0 MODE 4/5 >>>>\n", lseq); + PRINT_LMIP_byte(asd_ha, lseq, SAVED_OOB_STATUS); + PRINT_LMIP_byte(asd_ha, lseq, SAVED_OOB_MODE); + PRINT_LMIP_word(asd_ha, lseq, Q_LINK_HEAD); + PRINT_LMIP_byte(asd_ha, lseq, LINK_RST_ERR); + PRINT_LMIP_byte(asd_ha, lseq, SAVED_OOB_SIGNALS); + PRINT_LMIP_byte(asd_ha, lseq, SAS_RESET_MODE); + PRINT_LMIP_byte(asd_ha, lseq, LINK_RESET_RETRY_COUNT); + PRINT_LMIP_byte(asd_ha, lseq, NUM_LINK_RESET_RETRIES); + PRINT_LMIP_word(asd_ha, lseq, OOB_INT_ENABLES); + PRINT_LMIP_word(asd_ha, lseq, NOTIFY_TIMER_TIMEOUT); + PRINT_LMIP_word(asd_ha, lseq, NOTIFY_TIMER_DOWN_COUNT); + + asd_printk("LSEQ%d MDP 1 MODE 0 >>>>\n", lseq); + PRINT_LMIP_qword(asd_ha, lseq, SG_LIST_PTR_ADDR0); + PRINT_LMIP_qword(asd_ha, lseq, SG_LIST_PTR_ADDR1); + + asd_printk("LSEQ%d MDP 1 MODE 1 >>>>\n", lseq); + PRINT_LMIP_qword(asd_ha, lseq, M1_SG_LIST_PTR_ADDR0); + PRINT_LMIP_qword(asd_ha, lseq, M1_SG_LIST_PTR_ADDR1); + + asd_printk("LSEQ%d MDP 1 MODE 2 >>>>\n", lseq); + PRINT_LMIP_dword(asd_ha, lseq, INVALID_DWORD_COUNT); + PRINT_LMIP_dword(asd_ha, lseq, DISPARITY_ERROR_COUNT); + PRINT_LMIP_dword(asd_ha, lseq, LOSS_OF_SYNC_COUNT); + + asd_printk("LSEQ%d MDP 1 MODE 4/5 >>>>\n", lseq); + PRINT_LMIP_dword(asd_ha, lseq, FRAME_TYPE_MASK); + PRINT_LMIP_dword(asd_ha, lseq, HASHED_SRC_ADDR_MASK_PRINT); + PRINT_LMIP_byte(asd_ha, lseq, NUM_FILL_BYTES_MASK); + PRINT_LMIP_word(asd_ha, lseq, TAG_MASK); + PRINT_LMIP_word(asd_ha, lseq, TARGET_PORT_XFER_TAG); + PRINT_LMIP_dword(asd_ha, lseq, DATA_OFFSET); + + asd_printk("LSEQ%d MDP 2 MODE 0 >>>>\n", lseq); + PRINT_LMIP_dword(asd_ha, lseq, SMP_RCV_TIMER_TERM_TS); + PRINT_LMIP_byte(asd_ha, lseq, DEVICE_BITS); + PRINT_LMIP_word(asd_ha, lseq, SDB_DDB); + PRINT_LMIP_word(asd_ha, lseq, SDB_NUM_TAGS); + PRINT_LMIP_word(asd_ha, lseq, SDB_CURR_TAG); + + asd_printk("LSEQ%d MDP 2 MODE 1 >>>>\n", lseq); + PRINT_LMIP_qword(asd_ha, lseq, TX_ID_ADDR_FRAME); + PRINT_LMIP_dword(asd_ha, lseq, OPEN_TIMER_TERM_TS); + PRINT_LMIP_dword(asd_ha, lseq, SRST_AS_TIMER_TERM_TS); + PRINT_LMIP_dword(asd_ha, lseq, LAST_LOADED_SG_EL); + + asd_printk("LSEQ%d MDP 2 MODE 2 >>>>\n", lseq); + PRINT_LMIP_dword(asd_ha, lseq, CLOSE_TIMER_TERM_TS); + PRINT_LMIP_dword(asd_ha, lseq, BREAK_TIMER_TERM_TS); + PRINT_LMIP_dword(asd_ha, lseq, DWS_RESET_TIMER_TERM_TS); + PRINT_LMIP_dword(asd_ha, lseq, SATA_INTERLOCK_TIMER_TERM_TS); + PRINT_LMIP_dword(asd_ha, lseq, MCTL_TIMER_TERM_TS); + + asd_printk("LSEQ%d MDP 2 MODE 4/5 >>>>\n", lseq); + PRINT_LMIP_dword(asd_ha, lseq, COMINIT_TIMER_TERM_TS); + PRINT_LMIP_dword(asd_ha, lseq, RCV_ID_TIMER_TERM_TS); + PRINT_LMIP_dword(asd_ha, lseq, RCV_FIS_TIMER_TERM_TS); + PRINT_LMIP_dword(asd_ha, lseq, DEV_PRES_TIMER_TERM_TS); +} + +/** + * asd_dump_ddb_site -- dump a CSEQ DDB site + * @asd_ha: pointer to host adapter structure + * @site_no: site number of interest + */ +void asd_dump_target_ddb(struct asd_ha_struct *asd_ha, u16 site_no) +{ + if (site_no >= asd_ha->hw_prof.max_ddbs) + return; + +#define DDB_FIELDB(__name) \ + asd_ddbsite_read_byte(asd_ha, site_no, \ + offsetof(struct asd_ddb_ssp_smp_target_port, __name)) +#define DDB2_FIELDB(__name) \ + asd_ddbsite_read_byte(asd_ha, site_no, \ + offsetof(struct asd_ddb_stp_sata_target_port, __name)) +#define DDB_FIELDW(__name) \ + asd_ddbsite_read_word(asd_ha, site_no, \ + offsetof(struct asd_ddb_ssp_smp_target_port, __name)) + +#define DDB_FIELDD(__name) \ + asd_ddbsite_read_dword(asd_ha, site_no, \ + offsetof(struct asd_ddb_ssp_smp_target_port, __name)) + + asd_printk("DDB: 0x%02x\n", site_no); + asd_printk("conn_type: 0x%02x\n", DDB_FIELDB(conn_type)); + asd_printk("conn_rate: 0x%02x\n", DDB_FIELDB(conn_rate)); + asd_printk("init_conn_tag: 0x%04x\n", be16_to_cpu(DDB_FIELDW(init_conn_tag))); + asd_printk("send_queue_head: 0x%04x\n", be16_to_cpu(DDB_FIELDW(send_queue_head))); + asd_printk("sq_suspended: 0x%02x\n", DDB_FIELDB(sq_suspended)); + asd_printk("DDB Type: 0x%02x\n", DDB_FIELDB(ddb_type)); + asd_printk("AWT Default: 0x%04x\n", DDB_FIELDW(awt_def)); + asd_printk("compat_features: 0x%02x\n", DDB_FIELDB(compat_features)); + asd_printk("Pathway Blocked Count: 0x%02x\n", + DDB_FIELDB(pathway_blocked_count)); + asd_printk("arb_wait_time: 0x%04x\n", DDB_FIELDW(arb_wait_time)); + asd_printk("more_compat_features: 0x%08x\n", + DDB_FIELDD(more_compat_features)); + asd_printk("Conn Mask: 0x%02x\n", DDB_FIELDB(conn_mask)); + asd_printk("flags: 0x%02x\n", DDB_FIELDB(flags)); + asd_printk("flags2: 0x%02x\n", DDB2_FIELDB(flags2)); + asd_printk("ExecQ Tail: 0x%04x\n",DDB_FIELDW(exec_queue_tail)); + asd_printk("SendQ Tail: 0x%04x\n",DDB_FIELDW(send_queue_tail)); + asd_printk("Active Task Count: 0x%04x\n", + DDB_FIELDW(active_task_count)); + asd_printk("ITNL Reason: 0x%02x\n", DDB_FIELDB(itnl_reason)); + asd_printk("ITNL Timeout Const: 0x%04x\n", DDB_FIELDW(itnl_timeout)); + asd_printk("ITNL timestamp: 0x%08x\n", DDB_FIELDD(itnl_timestamp)); +} + +void asd_dump_ddb_0(struct asd_ha_struct *asd_ha) +{ +#define DDB0_FIELDB(__name) \ + asd_ddbsite_read_byte(asd_ha, 0, \ + offsetof(struct asd_ddb_seq_shared, __name)) +#define DDB0_FIELDW(__name) \ + asd_ddbsite_read_word(asd_ha, 0, \ + offsetof(struct asd_ddb_seq_shared, __name)) + +#define DDB0_FIELDD(__name) \ + asd_ddbsite_read_dword(asd_ha,0 , \ + offsetof(struct asd_ddb_seq_shared, __name)) + +#define DDB0_FIELDA(__name, _o) \ + asd_ddbsite_read_byte(asd_ha, 0, \ + offsetof(struct asd_ddb_seq_shared, __name)+_o) + + + asd_printk("DDB: 0\n"); + asd_printk("q_free_ddb_head:%04x\n", DDB0_FIELDW(q_free_ddb_head)); + asd_printk("q_free_ddb_tail:%04x\n", DDB0_FIELDW(q_free_ddb_tail)); + asd_printk("q_free_ddb_cnt:%04x\n", DDB0_FIELDW(q_free_ddb_cnt)); + asd_printk("q_used_ddb_head:%04x\n", DDB0_FIELDW(q_used_ddb_head)); + asd_printk("q_used_ddb_tail:%04x\n", DDB0_FIELDW(q_used_ddb_tail)); + asd_printk("shared_mem_lock:%04x\n", DDB0_FIELDW(shared_mem_lock)); + asd_printk("smp_conn_tag:%04x\n", DDB0_FIELDW(smp_conn_tag)); + asd_printk("est_nexus_buf_cnt:%04x\n", DDB0_FIELDW(est_nexus_buf_cnt)); + asd_printk("est_nexus_buf_thresh:%04x\n", + DDB0_FIELDW(est_nexus_buf_thresh)); + asd_printk("conn_not_active:%02x\n", DDB0_FIELDB(conn_not_active)); + asd_printk("phy_is_up:%02x\n", DDB0_FIELDB(phy_is_up)); + asd_printk("port_map_by_links:%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + DDB0_FIELDA(port_map_by_links, 0), + DDB0_FIELDA(port_map_by_links, 1), + DDB0_FIELDA(port_map_by_links, 2), + DDB0_FIELDA(port_map_by_links, 3), + DDB0_FIELDA(port_map_by_links, 4), + DDB0_FIELDA(port_map_by_links, 5), + DDB0_FIELDA(port_map_by_links, 6), + DDB0_FIELDA(port_map_by_links, 7)); +} + +static void asd_dump_scb_site(struct asd_ha_struct *asd_ha, u16 site_no) +{ + +#define SCB_FIELDB(__name) \ + asd_scbsite_read_byte(asd_ha, site_no, sizeof(struct scb_header) \ + + offsetof(struct initiate_ssp_task, __name)) +#define SCB_FIELDW(__name) \ + asd_scbsite_read_word(asd_ha, site_no, sizeof(struct scb_header) \ + + offsetof(struct initiate_ssp_task, __name)) +#define SCB_FIELDD(__name) \ + asd_scbsite_read_dword(asd_ha, site_no, sizeof(struct scb_header) \ + + offsetof(struct initiate_ssp_task, __name)) + + asd_printk("Total Xfer Len: 0x%08x.\n", SCB_FIELDD(total_xfer_len)); + asd_printk("Frame Type: 0x%02x.\n", SCB_FIELDB(ssp_frame.frame_type)); + asd_printk("Tag: 0x%04x.\n", SCB_FIELDW(ssp_frame.tag)); + asd_printk("Target Port Xfer Tag: 0x%04x.\n", + SCB_FIELDW(ssp_frame.tptt)); + asd_printk("Data Offset: 0x%08x.\n", SCB_FIELDW(ssp_frame.data_offs)); + asd_printk("Retry Count: 0x%02x.\n", SCB_FIELDB(retry_count)); +} + +/** + * asd_dump_scb_sites -- dump currently used CSEQ SCB sites + * @asd_ha: pointer to host adapter struct + */ +void asd_dump_scb_sites(struct asd_ha_struct *asd_ha) +{ + u16 site_no; + + for (site_no = 0; site_no < asd_ha->hw_prof.max_scbs; site_no++) { + u8 opcode; + + if (!SCB_SITE_VALID(site_no)) + continue; + + /* We are only interested in SCB sites currently used. + */ + opcode = asd_scbsite_read_byte(asd_ha, site_no, + offsetof(struct scb_header, + opcode)); + if (opcode == 0xFF) + continue; + + asd_printk("\nSCB: 0x%x\n", site_no); + asd_dump_scb_site(asd_ha, site_no); + } +} + +/** + * ads_dump_seq_state -- dump CSEQ and LSEQ states + * @asd_ha: pointer to host adapter structure + * @lseq_mask: mask of LSEQs of interest + */ +void asd_dump_seq_state(struct asd_ha_struct *asd_ha, u8 lseq_mask) +{ + int lseq; + + asd_dump_cseq_state(asd_ha); + + if (lseq_mask != 0) + for_each_sequencer(lseq_mask, lseq_mask, lseq) + asd_dump_lseq_state(asd_ha, lseq); +} + +void asd_dump_frame_rcvd(struct asd_phy *phy, + struct done_list_struct *dl) +{ + unsigned long flags; + int i; + + switch ((dl->status_block[1] & 0x70) >> 3) { + case SAS_PROTO_STP: + ASD_DPRINTK("STP proto device-to-host FIS:\n"); + break; + default: + case SAS_PROTO_SSP: + ASD_DPRINTK("SAS proto IDENTIFY:\n"); + break; + } + spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags); + for (i = 0; i < phy->sas_phy.frame_rcvd_size; i+=4) + ASD_DPRINTK("%02x: %02x %02x %02x %02x\n", + i, + phy->frame_rcvd[i], + phy->frame_rcvd[i+1], + phy->frame_rcvd[i+2], + phy->frame_rcvd[i+3]); + spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags); +} + +static inline void asd_dump_scb(struct asd_ascb *ascb, int ind) +{ + asd_printk("scb%d: vaddr: 0x%p, dma_handle: 0x%llx, next: 0x%llx, " + "index:%d, opcode:0x%02x\n", + ind, ascb->dma_scb.vaddr, + (unsigned long long)ascb->dma_scb.dma_handle, + (unsigned long long) + le64_to_cpu(ascb->scb->header.next_scb), + le16_to_cpu(ascb->scb->header.index), + ascb->scb->header.opcode); +} + +void asd_dump_scb_list(struct asd_ascb *ascb, int num) +{ + int i = 0; + + asd_printk("dumping %d scbs:\n", num); + + asd_dump_scb(ascb, i++); + --num; + + if (num > 0 && !list_empty(&ascb->list)) { + struct list_head *el; + + list_for_each(el, &ascb->list) { + struct asd_ascb *s = list_entry(el, struct asd_ascb, + list); + asd_dump_scb(s, i++); + if (--num <= 0) + break; + } + } +} + +#endif /* ASD_DEBUG */ diff --git a/drivers/scsi/aic94xx/aic94xx_dump.h b/drivers/scsi/aic94xx/aic94xx_dump.h new file mode 100644 index 00000000000..0c388e7da6b --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx_dump.h @@ -0,0 +1,52 @@ +/* + * Aic94xx SAS/SATA driver dump header file. + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * This file is part of the aic94xx driver. + * + * The aic94xx driver 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; version 2 of the + * License. + * + * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _AIC94XX_DUMP_H_ +#define _AIC94XX_DUMP_H_ + +#ifdef ASD_DEBUG + +void asd_dump_ddb_0(struct asd_ha_struct *asd_ha); +void asd_dump_target_ddb(struct asd_ha_struct *asd_ha, u16 site_no); +void asd_dump_scb_sites(struct asd_ha_struct *asd_ha); +void asd_dump_seq_state(struct asd_ha_struct *asd_ha, u8 lseq_mask); +void asd_dump_frame_rcvd(struct asd_phy *phy, + struct done_list_struct *dl); +void asd_dump_scb_list(struct asd_ascb *ascb, int num); +#else /* ASD_DEBUG */ + +static inline void asd_dump_ddb_0(struct asd_ha_struct *asd_ha) { } +static inline void asd_dump_target_ddb(struct asd_ha_struct *asd_ha, + u16 site_no) { } +static inline void asd_dump_scb_sites(struct asd_ha_struct *asd_ha) { } +static inline void asd_dump_seq_state(struct asd_ha_struct *asd_ha, + u8 lseq_mask) { } +static inline void asd_dump_frame_rcvd(struct asd_phy *phy, + struct done_list_struct *dl) { } +static inline void asd_dump_scb_list(struct asd_ascb *ascb, int num) { } +#endif /* ASD_DEBUG */ + +#endif /* _AIC94XX_DUMP_H_ */ diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.c b/drivers/scsi/aic94xx/aic94xx_hwi.c new file mode 100644 index 00000000000..075cea85b56 --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx_hwi.c @@ -0,0 +1,1376 @@ +/* + * Aic94xx SAS/SATA driver hardware interface. + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * This file is part of the aic94xx driver. + * + * The aic94xx driver 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; version 2 of the + * License. + * + * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include + +#include "aic94xx.h" +#include "aic94xx_reg.h" +#include "aic94xx_hwi.h" +#include "aic94xx_seq.h" +#include "aic94xx_dump.h" + +u32 MBAR0_SWB_SIZE; + +/* ---------- Initialization ---------- */ + +static void asd_get_user_sas_addr(struct asd_ha_struct *asd_ha) +{ + extern char sas_addr_str[]; + /* If the user has specified a WWN it overrides other settings + */ + if (sas_addr_str[0] != '\0') + asd_destringify_sas_addr(asd_ha->hw_prof.sas_addr, + sas_addr_str); + else if (asd_ha->hw_prof.sas_addr[0] != 0) + asd_stringify_sas_addr(sas_addr_str, asd_ha->hw_prof.sas_addr); +} + +static void asd_propagate_sas_addr(struct asd_ha_struct *asd_ha) +{ + int i; + + for (i = 0; i < ASD_MAX_PHYS; i++) { + if (asd_ha->hw_prof.phy_desc[i].sas_addr[0] == 0) + continue; + /* Set a phy's address only if it has none. + */ + ASD_DPRINTK("setting phy%d addr to %llx\n", i, + SAS_ADDR(asd_ha->hw_prof.sas_addr)); + memcpy(asd_ha->hw_prof.phy_desc[i].sas_addr, + asd_ha->hw_prof.sas_addr, SAS_ADDR_SIZE); + } +} + +/* ---------- PHY initialization ---------- */ + +static void asd_init_phy_identify(struct asd_phy *phy) +{ + phy->identify_frame = phy->id_frm_tok->vaddr; + + memset(phy->identify_frame, 0, sizeof(*phy->identify_frame)); + + phy->identify_frame->dev_type = SAS_END_DEV; + if (phy->sas_phy.role & PHY_ROLE_INITIATOR) + phy->identify_frame->initiator_bits = phy->sas_phy.iproto; + if (phy->sas_phy.role & PHY_ROLE_TARGET) + phy->identify_frame->target_bits = phy->sas_phy.tproto; + memcpy(phy->identify_frame->sas_addr, phy->phy_desc->sas_addr, + SAS_ADDR_SIZE); + phy->identify_frame->phy_id = phy->sas_phy.id; +} + +static int asd_init_phy(struct asd_phy *phy) +{ + struct asd_ha_struct *asd_ha = phy->sas_phy.ha->lldd_ha; + struct asd_sas_phy *sas_phy = &phy->sas_phy; + + sas_phy->enabled = 1; + sas_phy->class = SAS; + sas_phy->iproto = SAS_PROTO_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 = PHY_LINKRATE_NONE; + + phy->id_frm_tok = asd_alloc_coherent(asd_ha, + sizeof(*phy->identify_frame), + GFP_KERNEL); + if (!phy->id_frm_tok) { + asd_printk("no mem for IDENTIFY for phy%d\n", sas_phy->id); + return -ENOMEM; + } else + asd_init_phy_identify(phy); + + memset(phy->frame_rcvd, 0, sizeof(phy->frame_rcvd)); + + return 0; +} + +static int asd_init_phys(struct asd_ha_struct *asd_ha) +{ + u8 i; + u8 phy_mask = asd_ha->hw_prof.enabled_phys; + + for (i = 0; i < ASD_MAX_PHYS; i++) { + struct asd_phy *phy = &asd_ha->phys[i]; + + phy->phy_desc = &asd_ha->hw_prof.phy_desc[i]; + + phy->sas_phy.enabled = 0; + phy->sas_phy.id = i; + phy->sas_phy.sas_addr = &phy->phy_desc->sas_addr[0]; + phy->sas_phy.frame_rcvd = &phy->frame_rcvd[0]; + phy->sas_phy.ha = &asd_ha->sas_ha; + phy->sas_phy.lldd_phy = phy; + } + + /* Now enable and initialize only the enabled phys. */ + for_each_phy(phy_mask, phy_mask, i) { + int err = asd_init_phy(&asd_ha->phys[i]); + if (err) + return err; + } + + return 0; +} + +/* ---------- Sliding windows ---------- */ + +static int asd_init_sw(struct asd_ha_struct *asd_ha) +{ + struct pci_dev *pcidev = asd_ha->pcidev; + int err; + u32 v; + + /* Unlock MBARs */ + err = pci_read_config_dword(pcidev, PCI_CONF_MBAR_KEY, &v); + if (err) { + asd_printk("couldn't access conf. space of %s\n", + pci_name(pcidev)); + goto Err; + } + if (v) + err = pci_write_config_dword(pcidev, PCI_CONF_MBAR_KEY, v); + if (err) { + asd_printk("couldn't write to MBAR_KEY of %s\n", + pci_name(pcidev)); + goto Err; + } + + /* Set sliding windows A, B and C to point to proper internal + * memory regions. + */ + pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWA, REG_BASE_ADDR); + pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWB, + REG_BASE_ADDR_CSEQCIO); + pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWC, REG_BASE_ADDR_EXSI); + asd_ha->io_handle[0].swa_base = REG_BASE_ADDR; + asd_ha->io_handle[0].swb_base = REG_BASE_ADDR_CSEQCIO; + asd_ha->io_handle[0].swc_base = REG_BASE_ADDR_EXSI; + MBAR0_SWB_SIZE = asd_ha->io_handle[0].len - 0x80; + if (!asd_ha->iospace) { + /* MBAR1 will point to OCM (On Chip Memory) */ + pci_write_config_dword(pcidev, PCI_CONF_MBAR1, OCM_BASE_ADDR); + asd_ha->io_handle[1].swa_base = OCM_BASE_ADDR; + } + spin_lock_init(&asd_ha->iolock); +Err: + return err; +} + +/* ---------- SCB initialization ---------- */ + +/** + * asd_init_scbs - manually allocate the first SCB. + * @asd_ha: pointer to host adapter structure + * + * This allocates the very first SCB which would be sent to the + * sequencer for execution. Its bus address is written to + * CSEQ_Q_NEW_POINTER, mode page 2, mode 8. Since the bus address of + * the _next_ scb to be DMA-ed to the host adapter is read from the last + * SCB DMA-ed to the host adapter, we have to always stay one step + * ahead of the sequencer and keep one SCB already allocated. + */ +static int asd_init_scbs(struct asd_ha_struct *asd_ha) +{ + struct asd_seq_data *seq = &asd_ha->seq; + int bitmap_bytes; + + /* allocate the index array and bitmap */ + asd_ha->seq.tc_index_bitmap_bits = asd_ha->hw_prof.max_scbs; + asd_ha->seq.tc_index_array = kzalloc(asd_ha->seq.tc_index_bitmap_bits* + sizeof(void *), GFP_KERNEL); + if (!asd_ha->seq.tc_index_array) + return -ENOMEM; + + bitmap_bytes = (asd_ha->seq.tc_index_bitmap_bits+7)/8; + bitmap_bytes = BITS_TO_LONGS(bitmap_bytes*8)*sizeof(unsigned long); + asd_ha->seq.tc_index_bitmap = kzalloc(bitmap_bytes, GFP_KERNEL); + if (!asd_ha->seq.tc_index_bitmap) + return -ENOMEM; + + spin_lock_init(&seq->tc_index_lock); + + seq->next_scb.size = sizeof(struct scb); + seq->next_scb.vaddr = dma_pool_alloc(asd_ha->scb_pool, GFP_KERNEL, + &seq->next_scb.dma_handle); + if (!seq->next_scb.vaddr) { + kfree(asd_ha->seq.tc_index_bitmap); + kfree(asd_ha->seq.tc_index_array); + asd_ha->seq.tc_index_bitmap = NULL; + asd_ha->seq.tc_index_array = NULL; + return -ENOMEM; + } + + seq->pending = 0; + spin_lock_init(&seq->pend_q_lock); + INIT_LIST_HEAD(&seq->pend_q); + + return 0; +} + +static inline void asd_get_max_scb_ddb(struct asd_ha_struct *asd_ha) +{ + asd_ha->hw_prof.max_scbs = asd_get_cmdctx_size(asd_ha)/ASD_SCB_SIZE; + asd_ha->hw_prof.max_ddbs = asd_get_devctx_size(asd_ha)/ASD_DDB_SIZE; + ASD_DPRINTK("max_scbs:%d, max_ddbs:%d\n", + asd_ha->hw_prof.max_scbs, + asd_ha->hw_prof.max_ddbs); +} + +/* ---------- Done List initialization ---------- */ + +static void asd_dl_tasklet_handler(unsigned long); + +static int asd_init_dl(struct asd_ha_struct *asd_ha) +{ + asd_ha->seq.actual_dl + = asd_alloc_coherent(asd_ha, + ASD_DL_SIZE * sizeof(struct done_list_struct), + GFP_KERNEL); + if (!asd_ha->seq.actual_dl) + return -ENOMEM; + asd_ha->seq.dl = asd_ha->seq.actual_dl->vaddr; + asd_ha->seq.dl_toggle = ASD_DEF_DL_TOGGLE; + asd_ha->seq.dl_next = 0; + tasklet_init(&asd_ha->seq.dl_tasklet, asd_dl_tasklet_handler, + (unsigned long) asd_ha); + + return 0; +} + +/* ---------- EDB and ESCB init ---------- */ + +static int asd_alloc_edbs(struct asd_ha_struct *asd_ha, unsigned int gfp_flags) +{ + struct asd_seq_data *seq = &asd_ha->seq; + int i; + + seq->edb_arr = kmalloc(seq->num_edbs*sizeof(*seq->edb_arr), gfp_flags); + if (!seq->edb_arr) + return -ENOMEM; + + for (i = 0; i < seq->num_edbs; i++) { + seq->edb_arr[i] = asd_alloc_coherent(asd_ha, ASD_EDB_SIZE, + gfp_flags); + if (!seq->edb_arr[i]) + goto Err_unroll; + memset(seq->edb_arr[i]->vaddr, 0, ASD_EDB_SIZE); + } + + ASD_DPRINTK("num_edbs:%d\n", seq->num_edbs); + + return 0; + +Err_unroll: + for (i-- ; i >= 0; i--) + asd_free_coherent(asd_ha, seq->edb_arr[i]); + kfree(seq->edb_arr); + seq->edb_arr = NULL; + + return -ENOMEM; +} + +static int asd_alloc_escbs(struct asd_ha_struct *asd_ha, + unsigned int gfp_flags) +{ + struct asd_seq_data *seq = &asd_ha->seq; + struct asd_ascb *escb; + int i, escbs; + + seq->escb_arr = kmalloc(seq->num_escbs*sizeof(*seq->escb_arr), + gfp_flags); + if (!seq->escb_arr) + return -ENOMEM; + + escbs = seq->num_escbs; + escb = asd_ascb_alloc_list(asd_ha, &escbs, gfp_flags); + if (!escb) { + asd_printk("couldn't allocate list of escbs\n"); + goto Err; + } + seq->num_escbs -= escbs; /* subtract what was not allocated */ + ASD_DPRINTK("num_escbs:%d\n", seq->num_escbs); + + for (i = 0; i < seq->num_escbs; i++, escb = list_entry(escb->list.next, + struct asd_ascb, + list)) { + seq->escb_arr[i] = escb; + escb->scb->header.opcode = EMPTY_SCB; + } + + return 0; +Err: + kfree(seq->escb_arr); + seq->escb_arr = NULL; + return -ENOMEM; + +} + +static void asd_assign_edbs2escbs(struct asd_ha_struct *asd_ha) +{ + struct asd_seq_data *seq = &asd_ha->seq; + int i, k, z = 0; + + for (i = 0; i < seq->num_escbs; i++) { + struct asd_ascb *ascb = seq->escb_arr[i]; + struct empty_scb *escb = &ascb->scb->escb; + + ascb->edb_index = z; + + escb->num_valid = ASD_EDBS_PER_SCB; + + for (k = 0; k < ASD_EDBS_PER_SCB; k++) { + struct sg_el *eb = &escb->eb[k]; + struct asd_dma_tok *edb = seq->edb_arr[z++]; + + memset(eb, 0, sizeof(*eb)); + eb->bus_addr = cpu_to_le64(((u64) edb->dma_handle)); + eb->size = cpu_to_le32(((u32) edb->size)); + } + } +} + +/** + * asd_init_escbs -- allocate and initialize empty scbs + * @asd_ha: pointer to host adapter structure + * + * An empty SCB has sg_elements of ASD_EDBS_PER_SCB (7) buffers. + * They transport sense data, etc. + */ +static int asd_init_escbs(struct asd_ha_struct *asd_ha) +{ + struct asd_seq_data *seq = &asd_ha->seq; + int err = 0; + + /* Allocate two empty data buffers (edb) per sequencer. */ + int edbs = 2*(1+asd_ha->hw_prof.num_phys); + + seq->num_escbs = (edbs+ASD_EDBS_PER_SCB-1)/ASD_EDBS_PER_SCB; + seq->num_edbs = seq->num_escbs * ASD_EDBS_PER_SCB; + + err = asd_alloc_edbs(asd_ha, GFP_KERNEL); + if (err) { + asd_printk("couldn't allocate edbs\n"); + return err; + } + + err = asd_alloc_escbs(asd_ha, GFP_KERNEL); + if (err) { + asd_printk("couldn't allocate escbs\n"); + return err; + } + + asd_assign_edbs2escbs(asd_ha); + /* In order to insure that normal SCBs do not overfill sequencer + * memory and leave no space for escbs (halting condition), + * we increment pending here by the number of escbs. However, + * escbs are never pending. + */ + seq->pending = seq->num_escbs; + seq->can_queue = 1 + (asd_ha->hw_prof.max_scbs - seq->pending)/2; + + return 0; +} + +/* ---------- HW initialization ---------- */ + +/** + * asd_chip_hardrst -- hard reset the chip + * @asd_ha: pointer to host adapter structure + * + * This takes 16 cycles and is synchronous to CFCLK, which runs + * at 200 MHz, so this should take at most 80 nanoseconds. + */ +int asd_chip_hardrst(struct asd_ha_struct *asd_ha) +{ + int i; + int count = 100; + u32 reg; + + for (i = 0 ; i < 4 ; i++) { + asd_write_reg_dword(asd_ha, COMBIST, HARDRST); + } + + do { + udelay(1); + reg = asd_read_reg_dword(asd_ha, CHIMINT); + if (reg & HARDRSTDET) { + asd_write_reg_dword(asd_ha, CHIMINT, + HARDRSTDET|PORRSTDET); + return 0; + } + } while (--count > 0); + + return -ENODEV; +} + +/** + * asd_init_chip -- initialize the chip + * @asd_ha: pointer to host adapter structure + * + * Hard resets the chip, disables HA interrupts, downloads the sequnecer + * microcode and starts the sequencers. The caller has to explicitly + * enable HA interrupts with asd_enable_ints(asd_ha). + */ +static int asd_init_chip(struct asd_ha_struct *asd_ha) +{ + int err; + + err = asd_chip_hardrst(asd_ha); + if (err) { + asd_printk("couldn't hard reset %s\n", + pci_name(asd_ha->pcidev)); + goto out; + } + + asd_disable_ints(asd_ha); + + err = asd_init_seqs(asd_ha); + if (err) { + asd_printk("couldn't init seqs for %s\n", + pci_name(asd_ha->pcidev)); + goto out; + } + + err = asd_start_seqs(asd_ha); + if (err) { + asd_printk("coudln't start seqs for %s\n", + pci_name(asd_ha->pcidev)); + goto out; + } +out: + return err; +} + +#define MAX_DEVS ((OCM_MAX_SIZE) / (ASD_DDB_SIZE)) + +static int max_devs = 0; +module_param_named(max_devs, max_devs, int, S_IRUGO); +MODULE_PARM_DESC(max_devs, "\n" + "\tMaximum number of SAS devices to support (not LUs).\n" + "\tDefault: 2176, Maximum: 65663.\n"); + +static int max_cmnds = 0; +module_param_named(max_cmnds, max_cmnds, int, S_IRUGO); +MODULE_PARM_DESC(max_cmnds, "\n" + "\tMaximum number of commands queuable.\n" + "\tDefault: 512, Maximum: 66047.\n"); + +static void asd_extend_devctx_ocm(struct asd_ha_struct *asd_ha) +{ + unsigned long dma_addr = OCM_BASE_ADDR; + u32 d; + + dma_addr -= asd_ha->hw_prof.max_ddbs * ASD_DDB_SIZE; + asd_write_reg_addr(asd_ha, DEVCTXBASE, (dma_addr_t) dma_addr); + d = asd_read_reg_dword(asd_ha, CTXDOMAIN); + d |= 4; + asd_write_reg_dword(asd_ha, CTXDOMAIN, d); + asd_ha->hw_prof.max_ddbs += MAX_DEVS; +} + +static int asd_extend_devctx(struct asd_ha_struct *asd_ha) +{ + dma_addr_t dma_handle; + unsigned long dma_addr; + u32 d; + int size; + + asd_extend_devctx_ocm(asd_ha); + + asd_ha->hw_prof.ddb_ext = NULL; + if (max_devs <= asd_ha->hw_prof.max_ddbs || max_devs > 0xFFFF) { + max_devs = asd_ha->hw_prof.max_ddbs; + return 0; + } + + size = (max_devs - asd_ha->hw_prof.max_ddbs + 1) * ASD_DDB_SIZE; + + asd_ha->hw_prof.ddb_ext = asd_alloc_coherent(asd_ha, size, GFP_KERNEL); + if (!asd_ha->hw_prof.ddb_ext) { + asd_printk("couldn't allocate memory for %d devices\n", + max_devs); + max_devs = asd_ha->hw_prof.max_ddbs; + return -ENOMEM; + } + dma_handle = asd_ha->hw_prof.ddb_ext->dma_handle; + dma_addr = ALIGN((unsigned long) dma_handle, ASD_DDB_SIZE); + dma_addr -= asd_ha->hw_prof.max_ddbs * ASD_DDB_SIZE; + dma_handle = (dma_addr_t) dma_addr; + asd_write_reg_addr(asd_ha, DEVCTXBASE, dma_handle); + d = asd_read_reg_dword(asd_ha, CTXDOMAIN); + d &= ~4; + asd_write_reg_dword(asd_ha, CTXDOMAIN, d); + + asd_ha->hw_prof.max_ddbs = max_devs; + + return 0; +} + +static int asd_extend_cmdctx(struct asd_ha_struct *asd_ha) +{ + dma_addr_t dma_handle; + unsigned long dma_addr; + u32 d; + int size; + + asd_ha->hw_prof.scb_ext = NULL; + if (max_cmnds <= asd_ha->hw_prof.max_scbs || max_cmnds > 0xFFFF) { + max_cmnds = asd_ha->hw_prof.max_scbs; + return 0; + } + + size = (max_cmnds - asd_ha->hw_prof.max_scbs + 1) * ASD_SCB_SIZE; + + asd_ha->hw_prof.scb_ext = asd_alloc_coherent(asd_ha, size, GFP_KERNEL); + if (!asd_ha->hw_prof.scb_ext) { + asd_printk("couldn't allocate memory for %d commands\n", + max_cmnds); + max_cmnds = asd_ha->hw_prof.max_scbs; + return -ENOMEM; + } + dma_handle = asd_ha->hw_prof.scb_ext->dma_handle; + dma_addr = ALIGN((unsigned long) dma_handle, ASD_SCB_SIZE); + dma_addr -= asd_ha->hw_prof.max_scbs * ASD_SCB_SIZE; + dma_handle = (dma_addr_t) dma_addr; + asd_write_reg_addr(asd_ha, CMDCTXBASE, dma_handle); + d = asd_read_reg_dword(asd_ha, CTXDOMAIN); + d &= ~1; + asd_write_reg_dword(asd_ha, CTXDOMAIN, d); + + asd_ha->hw_prof.max_scbs = max_cmnds; + + return 0; +} + +/** + * asd_init_ctxmem -- initialize context memory + * asd_ha: pointer to host adapter structure + * + * This function sets the maximum number of SCBs and + * DDBs which can be used by the sequencer. This is normally + * 512 and 128 respectively. If support for more SCBs or more DDBs + * is required then CMDCTXBASE, DEVCTXBASE and CTXDOMAIN are + * initialized here to extend context memory to point to host memory, + * thus allowing unlimited support for SCBs and DDBs -- only limited + * by host memory. + */ +static int asd_init_ctxmem(struct asd_ha_struct *asd_ha) +{ + int bitmap_bytes; + + asd_get_max_scb_ddb(asd_ha); + asd_extend_devctx(asd_ha); + asd_extend_cmdctx(asd_ha); + + /* The kernel wants bitmaps to be unsigned long sized. */ + bitmap_bytes = (asd_ha->hw_prof.max_ddbs+7)/8; + bitmap_bytes = BITS_TO_LONGS(bitmap_bytes*8)*sizeof(unsigned long); + asd_ha->hw_prof.ddb_bitmap = kzalloc(bitmap_bytes, GFP_KERNEL); + if (!asd_ha->hw_prof.ddb_bitmap) + return -ENOMEM; + spin_lock_init(&asd_ha->hw_prof.ddb_lock); + + return 0; +} + +int asd_init_hw(struct asd_ha_struct *asd_ha) +{ + int err; + u32 v; + + err = asd_init_sw(asd_ha); + if (err) + return err; + + err = pci_read_config_dword(asd_ha->pcidev, PCIC_HSTPCIX_CNTRL, &v); + if (err) { + asd_printk("couldn't read PCIC_HSTPCIX_CNTRL of %s\n", + pci_name(asd_ha->pcidev)); + return err; + } + pci_write_config_dword(asd_ha->pcidev, PCIC_HSTPCIX_CNTRL, + v | SC_TMR_DIS); + if (err) { + asd_printk("couldn't disable split completion timer of %s\n", + pci_name(asd_ha->pcidev)); + return err; + } + + err = asd_read_ocm(asd_ha); + if (err) { + asd_printk("couldn't read ocm(%d)\n", err); + /* While suspicios, it is not an error that we + * couldn't read the OCM. */ + } + + err = asd_read_flash(asd_ha); + if (err) { + asd_printk("couldn't read flash(%d)\n", err); + /* While suspicios, it is not an error that we + * couldn't read FLASH memory. + */ + } + + asd_init_ctxmem(asd_ha); + + asd_get_user_sas_addr(asd_ha); + if (!asd_ha->hw_prof.sas_addr[0]) { + asd_printk("No SAS Address provided for %s\n", + pci_name(asd_ha->pcidev)); + err = -ENODEV; + goto Out; + } + + asd_propagate_sas_addr(asd_ha); + + err = asd_init_phys(asd_ha); + if (err) { + asd_printk("couldn't initialize phys for %s\n", + pci_name(asd_ha->pcidev)); + goto Out; + } + + err = asd_init_scbs(asd_ha); + if (err) { + asd_printk("couldn't initialize scbs for %s\n", + pci_name(asd_ha->pcidev)); + goto Out; + } + + err = asd_init_dl(asd_ha); + if (err) { + asd_printk("couldn't initialize the done list:%d\n", + err); + goto Out; + } + + err = asd_init_escbs(asd_ha); + if (err) { + asd_printk("couldn't initialize escbs\n"); + goto Out; + } + + err = asd_init_chip(asd_ha); + if (err) { + asd_printk("couldn't init the chip\n"); + goto Out; + } +Out: + return err; +} + +/* ---------- Chip reset ---------- */ + +/** + * asd_chip_reset -- reset the host adapter, etc + * @asd_ha: pointer to host adapter structure of interest + * + * Called from the ISR. Hard reset the chip. Let everything + * timeout. This should be no different than hot-unplugging the + * host adapter. Once everything times out we'll init the chip with + * a call to asd_init_chip() and enable interrupts with asd_enable_ints(). + * XXX finish. + */ +static void asd_chip_reset(struct asd_ha_struct *asd_ha) +{ + struct sas_ha_struct *sas_ha = &asd_ha->sas_ha; + + ASD_DPRINTK("chip reset for %s\n", pci_name(asd_ha->pcidev)); + asd_chip_hardrst(asd_ha); + sas_ha->notify_ha_event(sas_ha, HAE_RESET); +} + +/* ---------- Done List Routines ---------- */ + +static void asd_dl_tasklet_handler(unsigned long data) +{ + struct asd_ha_struct *asd_ha = (struct asd_ha_struct *) data; + struct asd_seq_data *seq = &asd_ha->seq; + unsigned long flags; + + while (1) { + struct done_list_struct *dl = &seq->dl[seq->dl_next]; + struct asd_ascb *ascb; + + if ((dl->toggle & DL_TOGGLE_MASK) != seq->dl_toggle) + break; + + /* find the aSCB */ + spin_lock_irqsave(&seq->tc_index_lock, flags); + ascb = asd_tc_index_find(seq, (int)le16_to_cpu(dl->index)); + spin_unlock_irqrestore(&seq->tc_index_lock, flags); + if (unlikely(!ascb)) { + ASD_DPRINTK("BUG:sequencer:dl:no ascb?!\n"); + goto next_1; + } else if (ascb->scb->header.opcode == EMPTY_SCB) { + goto out; + } else if (!ascb->uldd_timer && !del_timer(&ascb->timer)) { + goto next_1; + } + spin_lock_irqsave(&seq->pend_q_lock, flags); + list_del_init(&ascb->list); + seq->pending--; + spin_unlock_irqrestore(&seq->pend_q_lock, flags); + out: + ascb->tasklet_complete(ascb, dl); + + next_1: + seq->dl_next = (seq->dl_next + 1) & (ASD_DL_SIZE-1); + if (!seq->dl_next) + seq->dl_toggle ^= DL_TOGGLE_MASK; + } +} + +/* ---------- Interrupt Service Routines ---------- */ + +/** + * asd_process_donelist_isr -- schedule processing of done list entries + * @asd_ha: pointer to host adapter structure + */ +static inline void asd_process_donelist_isr(struct asd_ha_struct *asd_ha) +{ + tasklet_schedule(&asd_ha->seq.dl_tasklet); +} + +/** + * asd_com_sas_isr -- process device communication interrupt (COMINT) + * @asd_ha: pointer to host adapter structure + */ +static inline void asd_com_sas_isr(struct asd_ha_struct *asd_ha) +{ + u32 comstat = asd_read_reg_dword(asd_ha, COMSTAT); + + /* clear COMSTAT int */ + asd_write_reg_dword(asd_ha, COMSTAT, 0xFFFFFFFF); + + if (comstat & CSBUFPERR) { + asd_printk("%s: command/status buffer dma parity error\n", + pci_name(asd_ha->pcidev)); + } else if (comstat & CSERR) { + int i; + u32 dmaerr = asd_read_reg_dword(asd_ha, DMAERR); + dmaerr &= 0xFF; + asd_printk("%s: command/status dma error, DMAERR: 0x%02x, " + "CSDMAADR: 0x%04x, CSDMAADR+4: 0x%04x\n", + pci_name(asd_ha->pcidev), + dmaerr, + asd_read_reg_dword(asd_ha, CSDMAADR), + asd_read_reg_dword(asd_ha, CSDMAADR+4)); + asd_printk("CSBUFFER:\n"); + for (i = 0; i < 8; i++) { + asd_printk("%08x %08x %08x %08x\n", + asd_read_reg_dword(asd_ha, CSBUFFER), + asd_read_reg_dword(asd_ha, CSBUFFER+4), + asd_read_reg_dword(asd_ha, CSBUFFER+8), + asd_read_reg_dword(asd_ha, CSBUFFER+12)); + } + asd_dump_seq_state(asd_ha, 0); + } else if (comstat & OVLYERR) { + u32 dmaerr = asd_read_reg_dword(asd_ha, DMAERR); + dmaerr = (dmaerr >> 8) & 0xFF; + asd_printk("%s: overlay dma error:0x%x\n", + pci_name(asd_ha->pcidev), + dmaerr); + } + asd_chip_reset(asd_ha); +} + +static inline void asd_arp2_err(struct asd_ha_struct *asd_ha, u32 dchstatus) +{ + static const char *halt_code[256] = { + "UNEXPECTED_INTERRUPT0", + "UNEXPECTED_INTERRUPT1", + "UNEXPECTED_INTERRUPT2", + "UNEXPECTED_INTERRUPT3", + "UNEXPECTED_INTERRUPT4", + "UNEXPECTED_INTERRUPT5", + "UNEXPECTED_INTERRUPT6", + "UNEXPECTED_INTERRUPT7", + "UNEXPECTED_INTERRUPT8", + "UNEXPECTED_INTERRUPT9", + "UNEXPECTED_INTERRUPT10", + [11 ... 19] = "unknown[11,19]", + "NO_FREE_SCB_AVAILABLE", + "INVALID_SCB_OPCODE", + "INVALID_MBX_OPCODE", + "INVALID_ATA_STATE", + "ATA_QUEUE_FULL", + "ATA_TAG_TABLE_FAULT", + "ATA_TAG_MASK_FAULT", + "BAD_LINK_QUEUE_STATE", + "DMA2CHIM_QUEUE_ERROR", + "EMPTY_SCB_LIST_FULL", + "unknown[30]", + "IN_USE_SCB_ON_FREE_LIST", + "BAD_OPEN_WAIT_STATE", + "INVALID_STP_AFFILIATION", + "unknown[34]", + "EXEC_QUEUE_ERROR", + "TOO_MANY_EMPTIES_NEEDED", + "EMPTY_REQ_QUEUE_ERROR", + "Q_MONIRTT_MGMT_ERROR", + "TARGET_MODE_FLOW_ERROR", + "DEVICE_QUEUE_NOT_FOUND", + "START_IRTT_TIMER_ERROR", + "ABORT_TASK_ILLEGAL_REQ", + [43 ... 255] = "unknown[43,255]" + }; + + if (dchstatus & CSEQINT) { + u32 arp2int = asd_read_reg_dword(asd_ha, CARP2INT); + + if (arp2int & (ARP2WAITTO|ARP2ILLOPC|ARP2PERR|ARP2CIOPERR)) { + asd_printk("%s: CSEQ arp2int:0x%x\n", + pci_name(asd_ha->pcidev), + arp2int); + } else if (arp2int & ARP2HALTC) + asd_printk("%s: CSEQ halted: %s\n", + pci_name(asd_ha->pcidev), + halt_code[(arp2int>>16)&0xFF]); + else + asd_printk("%s: CARP2INT:0x%x\n", + pci_name(asd_ha->pcidev), + arp2int); + } + if (dchstatus & LSEQINT_MASK) { + int lseq; + u8 lseq_mask = dchstatus & LSEQINT_MASK; + + for_each_sequencer(lseq_mask, lseq_mask, lseq) { + u32 arp2int = asd_read_reg_dword(asd_ha, + LmARP2INT(lseq)); + if (arp2int & (ARP2WAITTO | ARP2ILLOPC | ARP2PERR + | ARP2CIOPERR)) { + asd_printk("%s: LSEQ%d arp2int:0x%x\n", + pci_name(asd_ha->pcidev), + lseq, arp2int); + /* XXX we should only do lseq reset */ + } else if (arp2int & ARP2HALTC) + asd_printk("%s: LSEQ%d halted: %s\n", + pci_name(asd_ha->pcidev), + lseq,halt_code[(arp2int>>16)&0xFF]); + else + asd_printk("%s: LSEQ%d ARP2INT:0x%x\n", + pci_name(asd_ha->pcidev), lseq, + arp2int); + } + } + asd_chip_reset(asd_ha); +} + +/** + * asd_dch_sas_isr -- process device channel interrupt (DEVINT) + * @asd_ha: pointer to host adapter structure + */ +static inline void asd_dch_sas_isr(struct asd_ha_struct *asd_ha) +{ + u32 dchstatus = asd_read_reg_dword(asd_ha, DCHSTATUS); + + if (dchstatus & CFIFTOERR) { + asd_printk("%s: CFIFTOERR\n", pci_name(asd_ha->pcidev)); + asd_chip_reset(asd_ha); + } else + asd_arp2_err(asd_ha, dchstatus); +} + +/** + * ads_rbi_exsi_isr -- process external system interface interrupt (INITERR) + * @asd_ha: pointer to host adapter structure + */ +static inline void asd_rbi_exsi_isr(struct asd_ha_struct *asd_ha) +{ + u32 stat0r = asd_read_reg_dword(asd_ha, ASISTAT0R); + + if (!(stat0r & ASIERR)) { + asd_printk("hmm, EXSI interrupted but no error?\n"); + return; + } + + if (stat0r & ASIFMTERR) { + asd_printk("ASI SEEPROM format error for %s\n", + pci_name(asd_ha->pcidev)); + } else if (stat0r & ASISEECHKERR) { + u32 stat1r = asd_read_reg_dword(asd_ha, ASISTAT1R); + asd_printk("ASI SEEPROM checksum 0x%x error for %s\n", + stat1r & CHECKSUM_MASK, + pci_name(asd_ha->pcidev)); + } else { + u32 statr = asd_read_reg_dword(asd_ha, ASIERRSTATR); + + if (!(statr & CPI2ASIMSTERR_MASK)) { + ASD_DPRINTK("hmm, ASIERR?\n"); + return; + } else { + u32 addr = asd_read_reg_dword(asd_ha, ASIERRADDR); + u32 data = asd_read_reg_dword(asd_ha, ASIERRDATAR); + + asd_printk("%s: CPI2 xfer err: addr: 0x%x, wdata: 0x%x, " + "count: 0x%x, byteen: 0x%x, targerr: 0x%x " + "master id: 0x%x, master err: 0x%x\n", + pci_name(asd_ha->pcidev), + addr, data, + (statr & CPI2ASIBYTECNT_MASK) >> 16, + (statr & CPI2ASIBYTEEN_MASK) >> 12, + (statr & CPI2ASITARGERR_MASK) >> 8, + (statr & CPI2ASITARGMID_MASK) >> 4, + (statr & CPI2ASIMSTERR_MASK)); + } + } + asd_chip_reset(asd_ha); +} + +/** + * asd_hst_pcix_isr -- process host interface interrupts + * @asd_ha: pointer to host adapter structure + * + * Asserted on PCIX errors: target abort, etc. + */ +static inline void asd_hst_pcix_isr(struct asd_ha_struct *asd_ha) +{ + u16 status; + u32 pcix_status; + u32 ecc_status; + + pci_read_config_word(asd_ha->pcidev, PCI_STATUS, &status); + pci_read_config_dword(asd_ha->pcidev, PCIX_STATUS, &pcix_status); + pci_read_config_dword(asd_ha->pcidev, ECC_CTRL_STAT, &ecc_status); + + if (status & PCI_STATUS_DETECTED_PARITY) + asd_printk("parity error for %s\n", pci_name(asd_ha->pcidev)); + else if (status & PCI_STATUS_REC_MASTER_ABORT) + asd_printk("master abort for %s\n", pci_name(asd_ha->pcidev)); + else if (status & PCI_STATUS_REC_TARGET_ABORT) + asd_printk("target abort for %s\n", pci_name(asd_ha->pcidev)); + else if (status & PCI_STATUS_PARITY) + asd_printk("data parity for %s\n", pci_name(asd_ha->pcidev)); + else if (pcix_status & RCV_SCE) { + asd_printk("received split completion error for %s\n", + pci_name(asd_ha->pcidev)); + pci_write_config_dword(asd_ha->pcidev,PCIX_STATUS,pcix_status); + /* XXX: Abort task? */ + return; + } else if (pcix_status & UNEXP_SC) { + asd_printk("unexpected split completion for %s\n", + pci_name(asd_ha->pcidev)); + pci_write_config_dword(asd_ha->pcidev,PCIX_STATUS,pcix_status); + /* ignore */ + return; + } else if (pcix_status & SC_DISCARD) + asd_printk("split completion discarded for %s\n", + pci_name(asd_ha->pcidev)); + else if (ecc_status & UNCOR_ECCERR) + asd_printk("uncorrectable ECC error for %s\n", + pci_name(asd_ha->pcidev)); + asd_chip_reset(asd_ha); +} + +/** + * asd_hw_isr -- host adapter interrupt service routine + * @irq: ignored + * @dev_id: pointer to host adapter structure + * @regs: ignored + * + * The ISR processes done list entries and level 3 error handling. + */ +irqreturn_t asd_hw_isr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct asd_ha_struct *asd_ha = dev_id; + u32 chimint = asd_read_reg_dword(asd_ha, CHIMINT); + + if (!chimint) + return IRQ_NONE; + + asd_write_reg_dword(asd_ha, CHIMINT, chimint); + (void) asd_read_reg_dword(asd_ha, CHIMINT); + + if (chimint & DLAVAIL) + asd_process_donelist_isr(asd_ha); + if (chimint & COMINT) + asd_com_sas_isr(asd_ha); + if (chimint & DEVINT) + asd_dch_sas_isr(asd_ha); + if (chimint & INITERR) + asd_rbi_exsi_isr(asd_ha); + if (chimint & HOSTERR) + asd_hst_pcix_isr(asd_ha); + + return IRQ_HANDLED; +} + +/* ---------- SCB handling ---------- */ + +static inline struct asd_ascb *asd_ascb_alloc(struct asd_ha_struct *asd_ha, + unsigned int gfp_flags) +{ + extern kmem_cache_t *asd_ascb_cache; + struct asd_seq_data *seq = &asd_ha->seq; + struct asd_ascb *ascb; + unsigned long flags; + + ascb = kmem_cache_alloc(asd_ascb_cache, gfp_flags); + + if (ascb) { + memset(ascb, 0, sizeof(*ascb)); + ascb->dma_scb.size = sizeof(struct scb); + ascb->dma_scb.vaddr = dma_pool_alloc(asd_ha->scb_pool, + gfp_flags, + &ascb->dma_scb.dma_handle); + if (!ascb->dma_scb.vaddr) { + kmem_cache_free(asd_ascb_cache, ascb); + return NULL; + } + memset(ascb->dma_scb.vaddr, 0, sizeof(struct scb)); + asd_init_ascb(asd_ha, ascb); + + spin_lock_irqsave(&seq->tc_index_lock, flags); + ascb->tc_index = asd_tc_index_get(seq, ascb); + spin_unlock_irqrestore(&seq->tc_index_lock, flags); + if (ascb->tc_index == -1) + goto undo; + + ascb->scb->header.index = cpu_to_le16((u16)ascb->tc_index); + } + + return ascb; +undo: + dma_pool_free(asd_ha->scb_pool, ascb->dma_scb.vaddr, + ascb->dma_scb.dma_handle); + kmem_cache_free(asd_ascb_cache, ascb); + ASD_DPRINTK("no index for ascb\n"); + return NULL; +} + +/** + * asd_ascb_alloc_list -- allocate a list of aSCBs + * @asd_ha: pointer to host adapter structure + * @num: pointer to integer number of aSCBs + * @gfp_flags: GFP_ flags. + * + * This is the only function which is used to allocate aSCBs. + * It can allocate one or many. If more than one, then they form + * a linked list in two ways: by their list field of the ascb struct + * and by the next_scb field of the scb_header. + * + * Returns NULL if no memory was available, else pointer to a list + * of ascbs. When this function returns, @num would be the number + * of SCBs which were not able to be allocated, 0 if all requested + * were able to be allocated. + */ +struct asd_ascb *asd_ascb_alloc_list(struct asd_ha_struct + *asd_ha, int *num, + unsigned int gfp_flags) +{ + struct asd_ascb *first = NULL; + + for ( ; *num > 0; --*num) { + struct asd_ascb *ascb = asd_ascb_alloc(asd_ha, gfp_flags); + + if (!ascb) + break; + else if (!first) + first = ascb; + else { + struct asd_ascb *last = list_entry(first->list.prev, + struct asd_ascb, + list); + list_add_tail(&ascb->list, &first->list); + last->scb->header.next_scb = + cpu_to_le64(((u64)ascb->dma_scb.dma_handle)); + } + } + + return first; +} + +/** + * asd_swap_head_scb -- swap the head scb + * @asd_ha: pointer to host adapter structure + * @ascb: pointer to the head of an ascb list + * + * The sequencer knows the DMA address of the next SCB to be DMAed to + * the host adapter, from initialization or from the last list DMAed. + * seq->next_scb keeps the address of this SCB. The sequencer will + * DMA to the host adapter this list of SCBs. But the head (first + * element) of this list is not known to the sequencer. Here we swap + * the head of the list with the known SCB (memcpy()). + * Only one memcpy() is required per list so it is in our interest + * to keep the list of SCB as long as possible so that the ratio + * of number of memcpy calls to the number of SCB DMA-ed is as small + * as possible. + * + * LOCKING: called with the pending list lock held. + */ +static inline void asd_swap_head_scb(struct asd_ha_struct *asd_ha, + struct asd_ascb *ascb) +{ + struct asd_seq_data *seq = &asd_ha->seq; + struct asd_ascb *last = list_entry(ascb->list.prev, + struct asd_ascb, + list); + struct asd_dma_tok t = ascb->dma_scb; + + memcpy(seq->next_scb.vaddr, ascb->scb, sizeof(*ascb->scb)); + ascb->dma_scb = seq->next_scb; + ascb->scb = ascb->dma_scb.vaddr; + seq->next_scb = t; + last->scb->header.next_scb = + cpu_to_le64(((u64)seq->next_scb.dma_handle)); +} + +/** + * asd_start_timers -- (add and) start timers of SCBs + * @list: pointer to struct list_head of the scbs + * @to: timeout in jiffies + * + * If an SCB in the @list has no timer function, assign the default + * one, then start the timer of the SCB. This function is + * intended to be called from asd_post_ascb_list(), just prior to + * posting the SCBs to the sequencer. + */ +static inline void asd_start_scb_timers(struct list_head *list) +{ + struct asd_ascb *ascb; + list_for_each_entry(ascb, list, list) { + if (!ascb->uldd_timer) { + ascb->timer.data = (unsigned long) ascb; + ascb->timer.function = asd_ascb_timedout; + ascb->timer.expires = jiffies + AIC94XX_SCB_TIMEOUT; + add_timer(&ascb->timer); + } + } +} + +/** + * asd_post_ascb_list -- post a list of 1 or more aSCBs to the host adapter + * @asd_ha: pointer to a host adapter structure + * @ascb: pointer to the first aSCB in the list + * @num: number of aSCBs in the list (to be posted) + * + * See queueing comment in asd_post_escb_list(). + * + * Additional note on queuing: In order to minimize the ratio of memcpy() + * to the number of ascbs sent, we try to batch-send as many ascbs as possible + * in one go. + * Two cases are possible: + * A) can_queue >= num, + * B) can_queue < num. + * Case A: we can send the whole batch at once. Increment "pending" + * in the beginning of this function, when it is checked, in order to + * eliminate races when this function is called by multiple processes. + * Case B: should never happen if the managing layer considers + * lldd_queue_size. + */ +int asd_post_ascb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb, + int num) +{ + unsigned long flags; + LIST_HEAD(list); + int can_queue; + + spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags); + can_queue = asd_ha->hw_prof.max_scbs - asd_ha->seq.pending; + if (can_queue >= num) + asd_ha->seq.pending += num; + else + can_queue = 0; + + if (!can_queue) { + spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags); + asd_printk("%s: scb queue full\n", pci_name(asd_ha->pcidev)); + return -SAS_QUEUE_FULL; + } + + asd_swap_head_scb(asd_ha, ascb); + + __list_add(&list, ascb->list.prev, &ascb->list); + + asd_start_scb_timers(&list); + + asd_ha->seq.scbpro += num; + list_splice_init(&list, asd_ha->seq.pend_q.prev); + asd_write_reg_dword(asd_ha, SCBPRO, (u32)asd_ha->seq.scbpro); + spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags); + + return 0; +} + +/** + * asd_post_escb_list -- post a list of 1 or more empty scb + * @asd_ha: pointer to a host adapter structure + * @ascb: pointer to the first empty SCB in the list + * @num: number of aSCBs in the list (to be posted) + * + * This is essentially the same as asd_post_ascb_list, but we do not + * increment pending, add those to the pending list or get indexes. + * See asd_init_escbs() and asd_init_post_escbs(). + * + * Since sending a list of ascbs is a superset of sending a single + * ascb, this function exists to generalize this. More specifically, + * when sending a list of those, we want to do only a _single_ + * memcpy() at swap head, as opposed to for each ascb sent (in the + * case of sending them one by one). That is, we want to minimize the + * ratio of memcpy() operations to the number of ascbs sent. The same + * logic applies to asd_post_ascb_list(). + */ +int asd_post_escb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb, + int num) +{ + unsigned long flags; + + spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags); + asd_swap_head_scb(asd_ha, ascb); + asd_ha->seq.scbpro += num; + asd_write_reg_dword(asd_ha, SCBPRO, (u32)asd_ha->seq.scbpro); + spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags); + + return 0; +} + +/* ---------- LED ---------- */ + +/** + * asd_turn_led -- turn on/off an LED + * @asd_ha: pointer to host adapter structure + * @phy_id: the PHY id whose LED we want to manupulate + * @op: 1 to turn on, 0 to turn off + */ +void asd_turn_led(struct asd_ha_struct *asd_ha, int phy_id, int op) +{ + if (phy_id < ASD_MAX_PHYS) { + u32 v = asd_read_reg_dword(asd_ha, LmCONTROL(phy_id)); + if (op) + v |= LEDPOL; + else + v &= ~LEDPOL; + asd_write_reg_dword(asd_ha, LmCONTROL(phy_id), v); + } +} + +/** + * asd_control_led -- enable/disable an LED on the board + * @asd_ha: pointer to host adapter structure + * @phy_id: integer, the phy id + * @op: integer, 1 to enable, 0 to disable the LED + * + * First we output enable the LED, then we set the source + * to be an external module. + */ +void asd_control_led(struct asd_ha_struct *asd_ha, int phy_id, int op) +{ + if (phy_id < ASD_MAX_PHYS) { + u32 v; + + v = asd_read_reg_dword(asd_ha, GPIOOER); + if (op) + v |= (1 << phy_id); + else + v &= ~(1 << phy_id); + asd_write_reg_dword(asd_ha, GPIOOER, v); + + v = asd_read_reg_dword(asd_ha, GPIOCNFGR); + if (op) + v |= (1 << phy_id); + else + v &= ~(1 << phy_id); + asd_write_reg_dword(asd_ha, GPIOCNFGR, v); + } +} + +/* ---------- PHY enable ---------- */ + +static int asd_enable_phy(struct asd_ha_struct *asd_ha, int phy_id) +{ + struct asd_phy *phy = &asd_ha->phys[phy_id]; + + asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, INT_ENABLE_2), 0); + asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, HOT_PLUG_DELAY), + HOTPLUG_DELAY_TIMEOUT); + + /* Get defaults from manuf. sector */ + /* XXX we need defaults for those in case MS is broken. */ + asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_0), + phy->phy_desc->phy_control_0); + asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_1), + phy->phy_desc->phy_control_1); + asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_2), + phy->phy_desc->phy_control_2); + asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_3), + phy->phy_desc->phy_control_3); + + asd_write_reg_dword(asd_ha, LmSEQ_TEN_MS_COMINIT_TIMEOUT(phy_id), + ASD_COMINIT_TIMEOUT); + + asd_write_reg_addr(asd_ha, LmSEQ_TX_ID_ADDR_FRAME(phy_id), + phy->id_frm_tok->dma_handle); + + asd_control_led(asd_ha, phy_id, 1); + + return 0; +} + +int asd_enable_phys(struct asd_ha_struct *asd_ha, const u8 phy_mask) +{ + u8 phy_m; + u8 i; + int num = 0, k; + struct asd_ascb *ascb; + struct asd_ascb *ascb_list; + + if (!phy_mask) { + asd_printk("%s called with phy_mask of 0!?\n", __FUNCTION__); + return 0; + } + + for_each_phy(phy_mask, phy_m, i) { + num++; + asd_enable_phy(asd_ha, i); + } + + k = num; + ascb_list = asd_ascb_alloc_list(asd_ha, &k, GFP_KERNEL); + if (!ascb_list) { + asd_printk("no memory for control phy ascb list\n"); + return -ENOMEM; + } + num -= k; + + ascb = ascb_list; + for_each_phy(phy_mask, phy_m, i) { + asd_build_control_phy(ascb, i, ENABLE_PHY); + ascb = list_entry(ascb->list.next, struct asd_ascb, list); + } + ASD_DPRINTK("posting %d control phy scbs\n", num); + k = asd_post_ascb_list(asd_ha, ascb_list, num); + if (k) + asd_ascb_free_list(ascb_list); + + return k; +} diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.h b/drivers/scsi/aic94xx/aic94xx_hwi.h new file mode 100644 index 00000000000..c7d505388fe --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx_hwi.h @@ -0,0 +1,397 @@ +/* + * Aic94xx SAS/SATA driver hardware interface header file. + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * This file is part of the aic94xx driver. + * + * The aic94xx driver 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; version 2 of the + * License. + * + * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _AIC94XX_HWI_H_ +#define _AIC94XX_HWI_H_ + +#include +#include +#include + +#include + +#include "aic94xx.h" +#include "aic94xx_sas.h" + +/* Define ASD_MAX_PHYS to the maximum phys ever. Currently 8. */ +#define ASD_MAX_PHYS 8 +#define ASD_PCBA_SN_SIZE 12 + +/* Those are to be further named properly, the "RAZORx" part, and + * subsequently included in include/linux/pci_ids.h. + */ +#define PCI_DEVICE_ID_ADAPTEC2_RAZOR10 0x410 +#define PCI_DEVICE_ID_ADAPTEC2_RAZOR12 0x412 +#define PCI_DEVICE_ID_ADAPTEC2_RAZOR1E 0x41E +#define PCI_DEVICE_ID_ADAPTEC2_RAZOR30 0x430 +#define PCI_DEVICE_ID_ADAPTEC2_RAZOR32 0x432 +#define PCI_DEVICE_ID_ADAPTEC2_RAZOR3E 0x43E +#define PCI_DEVICE_ID_ADAPTEC2_RAZOR3F 0x43F + +struct asd_ha_addrspace { + void __iomem *addr; + unsigned long start; /* pci resource start */ + unsigned long len; /* pci resource len */ + unsigned long flags; /* pci resource flags */ + + /* addresses internal to the host adapter */ + u32 swa_base; /* mmspace 1 (MBAR1) uses this only */ + u32 swb_base; + u32 swc_base; +}; + +struct bios_struct { + int present; + u8 maj; + u8 min; + u32 bld; +}; + +struct unit_element_struct { + u16 num; + u16 size; + void *area; +}; + +struct flash_struct { + u32 bar; + int present; + int wide; + u8 manuf; + u8 dev_id; + u8 sec_prot; + + u32 dir_offs; +}; + +struct asd_phy_desc { + /* From CTRL-A settings, then set to what is appropriate */ + u8 sas_addr[SAS_ADDR_SIZE]; + u8 max_sas_lrate; + u8 min_sas_lrate; + u8 max_sata_lrate; + u8 min_sata_lrate; + u8 flags; +#define ASD_CRC_DIS 1 +#define ASD_SATA_SPINUP_HOLD 2 + + u8 phy_control_0; /* mode 5 reg 0x160 */ + u8 phy_control_1; /* mode 5 reg 0x161 */ + u8 phy_control_2; /* mode 5 reg 0x162 */ + u8 phy_control_3; /* mode 5 reg 0x163 */ +}; + +struct asd_dma_tok { + void *vaddr; + dma_addr_t dma_handle; + size_t size; +}; + +struct hw_profile { + struct bios_struct bios; + struct unit_element_struct ue; + struct flash_struct flash; + + u8 sas_addr[SAS_ADDR_SIZE]; + char pcba_sn[ASD_PCBA_SN_SIZE+1]; + + u8 enabled_phys; /* mask of enabled phys */ + struct asd_phy_desc phy_desc[ASD_MAX_PHYS]; + u32 max_scbs; /* absolute sequencer scb queue size */ + struct asd_dma_tok *scb_ext; + u32 max_ddbs; + struct asd_dma_tok *ddb_ext; + + spinlock_t ddb_lock; + void *ddb_bitmap; + + int num_phys; /* ENABLEABLE */ + int max_phys; /* REPORTED + ENABLEABLE */ + + unsigned addr_range; /* max # of addrs; max # of possible ports */ + unsigned port_name_base; + unsigned dev_name_base; + unsigned sata_name_base; +}; + +struct asd_ascb { + struct list_head list; + struct asd_ha_struct *ha; + + struct scb *scb; /* equals dma_scb->vaddr */ + struct asd_dma_tok dma_scb; + struct asd_dma_tok *sg_arr; + + void (*tasklet_complete)(struct asd_ascb *, struct done_list_struct *); + u8 uldd_timer:1; + + /* internally generated command */ + struct timer_list timer; + struct completion completion; + u8 tag_valid:1; + __be16 tag; /* error recovery only */ + + /* If this is an Empty SCB, index of first edb in seq->edb_arr. */ + int edb_index; + + /* Used by the timer timeout function. */ + int tc_index; + + void *uldd_task; +}; + +#define ASD_DL_SIZE_BITS 0x8 +#define ASD_DL_SIZE (1<<(2+ASD_DL_SIZE_BITS)) +#define ASD_DEF_DL_TOGGLE 0x01 + +struct asd_seq_data { + spinlock_t pend_q_lock; + u16 scbpro; + int pending; + struct list_head pend_q; + int can_queue; /* per adapter */ + struct asd_dma_tok next_scb; /* next scb to be delivered to CSEQ */ + + spinlock_t tc_index_lock; + void **tc_index_array; + void *tc_index_bitmap; + int tc_index_bitmap_bits; + + struct tasklet_struct dl_tasklet; + struct done_list_struct *dl; /* array of done list entries, equals */ + struct asd_dma_tok *actual_dl; /* actual_dl->vaddr */ + int dl_toggle; + int dl_next; + + int num_edbs; + struct asd_dma_tok **edb_arr; + int num_escbs; + struct asd_ascb **escb_arr; /* array of pointers to escbs */ +}; + +/* This is the Host Adapter structure. It describes the hardware + * SAS adapter. + */ +struct asd_ha_struct { + struct pci_dev *pcidev; + const char *name; + + struct sas_ha_struct sas_ha; + + u8 revision_id; + + int iospace; + spinlock_t iolock; + struct asd_ha_addrspace io_handle[2]; + + struct hw_profile hw_prof; + + struct asd_phy phys[ASD_MAX_PHYS]; + struct asd_sas_port ports[ASD_MAX_PHYS]; + + struct dma_pool *scb_pool; + + struct asd_seq_data seq; /* sequencer related */ +}; + +/* ---------- Common macros ---------- */ + +#define ASD_BUSADDR_LO(__dma_handle) ((u32)(__dma_handle)) +#define ASD_BUSADDR_HI(__dma_handle) (((sizeof(dma_addr_t))==8) \ + ? ((u32)((__dma_handle) >> 32)) \ + : ((u32)0)) + +#define dev_to_asd_ha(__dev) pci_get_drvdata(to_pci_dev(__dev)) +#define SCB_SITE_VALID(__site_no) (((__site_no) & 0xF0FF) != 0x00FF \ + && ((__site_no) & 0xF0FF) > 0x001F) +/* For each bit set in __lseq_mask, set __lseq to equal the bit + * position of the set bit and execute the statement following. + * __mc is the temporary mask, used as a mask "counter". + */ +#define for_each_sequencer(__lseq_mask, __mc, __lseq) \ + for ((__mc)=(__lseq_mask),(__lseq)=0;(__mc)!=0;(__lseq++),(__mc)>>=1)\ + if (((__mc) & 1)) +#define for_each_phy(__lseq_mask, __mc, __lseq) \ + for ((__mc)=(__lseq_mask),(__lseq)=0;(__mc)!=0;(__lseq++),(__mc)>>=1)\ + if (((__mc) & 1)) + +#define PHY_ENABLED(_HA, _I) ((_HA)->hw_prof.enabled_phys & (1<<(_I))) + +/* ---------- DMA allocs ---------- */ + +static inline struct asd_dma_tok *asd_dmatok_alloc(unsigned int flags) +{ + return kmem_cache_alloc(asd_dma_token_cache, flags); +} + +static inline void asd_dmatok_free(struct asd_dma_tok *token) +{ + kmem_cache_free(asd_dma_token_cache, token); +} + +static inline struct asd_dma_tok *asd_alloc_coherent(struct asd_ha_struct * + asd_ha, size_t size, + unsigned int flags) +{ + struct asd_dma_tok *token = asd_dmatok_alloc(flags); + if (token) { + token->size = size; + token->vaddr = dma_alloc_coherent(&asd_ha->pcidev->dev, + token->size, + &token->dma_handle, + flags); + if (!token->vaddr) { + asd_dmatok_free(token); + token = NULL; + } + } + return token; +} + +static inline void asd_free_coherent(struct asd_ha_struct *asd_ha, + struct asd_dma_tok *token) +{ + if (token) { + dma_free_coherent(&asd_ha->pcidev->dev, token->size, + token->vaddr, token->dma_handle); + asd_dmatok_free(token); + } +} + +static inline void asd_init_ascb(struct asd_ha_struct *asd_ha, + struct asd_ascb *ascb) +{ + INIT_LIST_HEAD(&ascb->list); + ascb->scb = ascb->dma_scb.vaddr; + ascb->ha = asd_ha; + ascb->timer.function = NULL; + init_timer(&ascb->timer); + ascb->tc_index = -1; + init_completion(&ascb->completion); +} + +/* Must be called with the tc_index_lock held! + */ +static inline void asd_tc_index_release(struct asd_seq_data *seq, int index) +{ + seq->tc_index_array[index] = NULL; + clear_bit(index, seq->tc_index_bitmap); +} + +/* Must be called with the tc_index_lock held! + */ +static inline int asd_tc_index_get(struct asd_seq_data *seq, void *ptr) +{ + int index; + + index = find_first_zero_bit(seq->tc_index_bitmap, + seq->tc_index_bitmap_bits); + if (index == seq->tc_index_bitmap_bits) + return -1; + + seq->tc_index_array[index] = ptr; + set_bit(index, seq->tc_index_bitmap); + + return index; +} + +/* Must be called with the tc_index_lock held! + */ +static inline void *asd_tc_index_find(struct asd_seq_data *seq, int index) +{ + return seq->tc_index_array[index]; +} + +/** + * asd_ascb_free -- free a single aSCB after is has completed + * @ascb: pointer to the aSCB of interest + * + * This frees an aSCB after it has been executed/completed by + * the sequencer. + */ +static inline void asd_ascb_free(struct asd_ascb *ascb) +{ + if (ascb) { + struct asd_ha_struct *asd_ha = ascb->ha; + unsigned long flags; + + BUG_ON(!list_empty(&ascb->list)); + spin_lock_irqsave(&ascb->ha->seq.tc_index_lock, flags); + asd_tc_index_release(&ascb->ha->seq, ascb->tc_index); + spin_unlock_irqrestore(&ascb->ha->seq.tc_index_lock, flags); + dma_pool_free(asd_ha->scb_pool, ascb->dma_scb.vaddr, + ascb->dma_scb.dma_handle); + kmem_cache_free(asd_ascb_cache, ascb); + } +} + +/** + * asd_ascb_list_free -- free a list of ascbs + * @ascb_list: a list of ascbs + * + * This function will free a list of ascbs allocated by asd_ascb_alloc_list. + * It is used when say the scb queueing function returned QUEUE_FULL, + * and we do not need the ascbs any more. + */ +static inline void asd_ascb_free_list(struct asd_ascb *ascb_list) +{ + LIST_HEAD(list); + struct list_head *n, *pos; + + __list_add(&list, ascb_list->list.prev, &ascb_list->list); + list_for_each_safe(pos, n, &list) { + list_del_init(pos); + asd_ascb_free(list_entry(pos, struct asd_ascb, list)); + } +} + +/* ---------- Function declarations ---------- */ + +int asd_init_hw(struct asd_ha_struct *asd_ha); +irqreturn_t asd_hw_isr(int irq, void *dev_id, struct pt_regs *regs); + + +struct asd_ascb *asd_ascb_alloc_list(struct asd_ha_struct + *asd_ha, int *num, + unsigned int gfp_mask); + +int asd_post_ascb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb, + int num); +int asd_post_escb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb, + int num); + +int asd_init_post_escbs(struct asd_ha_struct *asd_ha); +void asd_build_control_phy(struct asd_ascb *ascb, int phy_id, u8 subfunc); +void asd_control_led(struct asd_ha_struct *asd_ha, int phy_id, int op); +void asd_turn_led(struct asd_ha_struct *asd_ha, int phy_id, int op); +int asd_enable_phys(struct asd_ha_struct *asd_ha, const u8 phy_mask); +void asd_build_initiate_link_adm_task(struct asd_ascb *ascb, int phy_id, + u8 subfunc); + +void asd_ascb_timedout(unsigned long data); +int asd_chip_hardrst(struct asd_ha_struct *asd_ha); + +#endif diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c new file mode 100644 index 00000000000..3ec2e46f80c --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -0,0 +1,860 @@ +/* + * Aic94xx SAS/SATA driver initialization. + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * This file is part of the aic94xx driver. + * + * The aic94xx driver 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; version 2 of the + * License. + * + * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "aic94xx.h" +#include "aic94xx_reg.h" +#include "aic94xx_hwi.h" +#include "aic94xx_seq.h" + +/* The format is "version.release.patchlevel" */ +#define ASD_DRIVER_VERSION "1.0.2" + +static int use_msi = 0; +module_param_named(use_msi, use_msi, int, S_IRUGO); +MODULE_PARM_DESC(use_msi, "\n" + "\tEnable(1) or disable(0) using PCI MSI.\n" + "\tDefault: 0"); + +static int lldd_max_execute_num = 0; +module_param_named(collector, lldd_max_execute_num, int, S_IRUGO); +MODULE_PARM_DESC(collector, "\n" + "\tIf greater than one, tells the SAS Layer to run in Task Collector\n" + "\tMode. If 1 or 0, tells the SAS Layer to run in Direct Mode.\n" + "\tThe aic94xx SAS LLDD supports both modes.\n" + "\tDefault: 0 (Direct Mode).\n"); + +char sas_addr_str[2*SAS_ADDR_SIZE + 1] = ""; + +static struct scsi_transport_template *aic94xx_transport_template; + +static struct scsi_host_template aic94xx_sht = { + .module = THIS_MODULE, + /* .name is initialized */ + .name = "aic94xx", + .queuecommand = sas_queuecommand, + .target_alloc = sas_target_alloc, + .slave_configure = sas_slave_configure, + .slave_destroy = sas_slave_destroy, + .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, +}; + +static int __devinit asd_map_memio(struct asd_ha_struct *asd_ha) +{ + int err, i; + struct asd_ha_addrspace *io_handle; + + asd_ha->iospace = 0; + for (i = 0; i < 3; i += 2) { + io_handle = &asd_ha->io_handle[i==0?0:1]; + io_handle->start = pci_resource_start(asd_ha->pcidev, i); + io_handle->len = pci_resource_len(asd_ha->pcidev, i); + io_handle->flags = pci_resource_flags(asd_ha->pcidev, i); + err = -ENODEV; + if (!io_handle->start || !io_handle->len) { + asd_printk("MBAR%d start or length for %s is 0.\n", + i==0?0:1, pci_name(asd_ha->pcidev)); + goto Err; + } + err = pci_request_region(asd_ha->pcidev, i, ASD_DRIVER_NAME); + if (err) { + asd_printk("couldn't reserve memory region for %s\n", + pci_name(asd_ha->pcidev)); + goto Err; + } + if (io_handle->flags & IORESOURCE_CACHEABLE) + io_handle->addr = ioremap(io_handle->start, + io_handle->len); + else + io_handle->addr = ioremap_nocache(io_handle->start, + io_handle->len); + if (!io_handle->addr) { + asd_printk("couldn't map MBAR%d of %s\n", i==0?0:1, + pci_name(asd_ha->pcidev)); + goto Err_unreq; + } + } + + return 0; +Err_unreq: + pci_release_region(asd_ha->pcidev, i); +Err: + if (i > 0) { + io_handle = &asd_ha->io_handle[0]; + iounmap(io_handle->addr); + pci_release_region(asd_ha->pcidev, 0); + } + return err; +} + +static void __devexit asd_unmap_memio(struct asd_ha_struct *asd_ha) +{ + struct asd_ha_addrspace *io_handle; + + io_handle = &asd_ha->io_handle[1]; + iounmap(io_handle->addr); + pci_release_region(asd_ha->pcidev, 2); + + io_handle = &asd_ha->io_handle[0]; + iounmap(io_handle->addr); + pci_release_region(asd_ha->pcidev, 0); +} + +static int __devinit asd_map_ioport(struct asd_ha_struct *asd_ha) +{ + int i = PCI_IOBAR_OFFSET, err; + struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0]; + + asd_ha->iospace = 1; + io_handle->start = pci_resource_start(asd_ha->pcidev, i); + io_handle->len = pci_resource_len(asd_ha->pcidev, i); + io_handle->flags = pci_resource_flags(asd_ha->pcidev, i); + io_handle->addr = (void __iomem *) io_handle->start; + if (!io_handle->start || !io_handle->len) { + asd_printk("couldn't get IO ports for %s\n", + pci_name(asd_ha->pcidev)); + return -ENODEV; + } + err = pci_request_region(asd_ha->pcidev, i, ASD_DRIVER_NAME); + if (err) { + asd_printk("couldn't reserve io space for %s\n", + pci_name(asd_ha->pcidev)); + } + + return err; +} + +static void __devexit asd_unmap_ioport(struct asd_ha_struct *asd_ha) +{ + pci_release_region(asd_ha->pcidev, PCI_IOBAR_OFFSET); +} + +static int __devinit asd_map_ha(struct asd_ha_struct *asd_ha) +{ + int err; + u16 cmd_reg; + + err = pci_read_config_word(asd_ha->pcidev, PCI_COMMAND, &cmd_reg); + if (err) { + asd_printk("couldn't read command register of %s\n", + pci_name(asd_ha->pcidev)); + goto Err; + } + + err = -ENODEV; + if (cmd_reg & PCI_COMMAND_MEMORY) { + if ((err = asd_map_memio(asd_ha))) + goto Err; + } else if (cmd_reg & PCI_COMMAND_IO) { + if ((err = asd_map_ioport(asd_ha))) + goto Err; + asd_printk("%s ioport mapped -- upgrade your hardware\n", + pci_name(asd_ha->pcidev)); + } else { + asd_printk("no proper device access to %s\n", + pci_name(asd_ha->pcidev)); + goto Err; + } + + return 0; +Err: + return err; +} + +static void __devexit asd_unmap_ha(struct asd_ha_struct *asd_ha) +{ + if (asd_ha->iospace) + asd_unmap_ioport(asd_ha); + else + asd_unmap_memio(asd_ha); +} + +static const char *asd_dev_rev[30] = { + [0] = "A0", + [1] = "A1", + [8] = "B0", +}; + +static int __devinit asd_common_setup(struct asd_ha_struct *asd_ha) +{ + int err, i; + + err = pci_read_config_byte(asd_ha->pcidev, PCI_REVISION_ID, + &asd_ha->revision_id); + if (err) { + asd_printk("couldn't read REVISION ID register of %s\n", + pci_name(asd_ha->pcidev)); + goto Err; + } + err = -ENODEV; + if (asd_ha->revision_id < AIC9410_DEV_REV_B0) { + asd_printk("%s is revision %s (%X), which is not supported\n", + pci_name(asd_ha->pcidev), + asd_dev_rev[asd_ha->revision_id], + asd_ha->revision_id); + goto Err; + } + /* Provide some sane default values. */ + asd_ha->hw_prof.max_scbs = 512; + asd_ha->hw_prof.max_ddbs = 128; + asd_ha->hw_prof.num_phys = ASD_MAX_PHYS; + /* All phys are enabled, by default. */ + asd_ha->hw_prof.enabled_phys = 0xFF; + for (i = 0; i < ASD_MAX_PHYS; i++) { + asd_ha->hw_prof.phy_desc[i].max_sas_lrate = PHY_LINKRATE_3; + asd_ha->hw_prof.phy_desc[i].min_sas_lrate = PHY_LINKRATE_1_5; + asd_ha->hw_prof.phy_desc[i].max_sata_lrate= PHY_LINKRATE_1_5; + asd_ha->hw_prof.phy_desc[i].min_sata_lrate= PHY_LINKRATE_1_5; + } + + return 0; +Err: + return err; +} + +static int __devinit asd_aic9410_setup(struct asd_ha_struct *asd_ha) +{ + int err = asd_common_setup(asd_ha); + + if (err) + return err; + + asd_ha->hw_prof.addr_range = 8; + asd_ha->hw_prof.port_name_base = 0; + asd_ha->hw_prof.dev_name_base = 8; + asd_ha->hw_prof.sata_name_base = 16; + + return 0; +} + +static int __devinit asd_aic9405_setup(struct asd_ha_struct *asd_ha) +{ + int err = asd_common_setup(asd_ha); + + if (err) + return err; + + asd_ha->hw_prof.addr_range = 4; + asd_ha->hw_prof.port_name_base = 0; + asd_ha->hw_prof.dev_name_base = 4; + asd_ha->hw_prof.sata_name_base = 8; + + return 0; +} + +static ssize_t asd_show_dev_rev(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev); + return snprintf(buf, PAGE_SIZE, "%s\n", + asd_dev_rev[asd_ha->revision_id]); +} +static DEVICE_ATTR(revision, S_IRUGO, asd_show_dev_rev, NULL); + +static ssize_t asd_show_dev_bios_build(struct device *dev, + struct device_attribute *attr,char *buf) +{ + struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", asd_ha->hw_prof.bios.bld); +} +static DEVICE_ATTR(bios_build, S_IRUGO, asd_show_dev_bios_build, NULL); + +static ssize_t asd_show_dev_pcba_sn(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev); + return snprintf(buf, PAGE_SIZE, "%s\n", asd_ha->hw_prof.pcba_sn); +} +static DEVICE_ATTR(pcba_sn, S_IRUGO, asd_show_dev_pcba_sn, NULL); + +static void asd_create_dev_attrs(struct asd_ha_struct *asd_ha) +{ + device_create_file(&asd_ha->pcidev->dev, &dev_attr_revision); + device_create_file(&asd_ha->pcidev->dev, &dev_attr_bios_build); + device_create_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn); +} + +static void asd_remove_dev_attrs(struct asd_ha_struct *asd_ha) +{ + device_remove_file(&asd_ha->pcidev->dev, &dev_attr_revision); + device_remove_file(&asd_ha->pcidev->dev, &dev_attr_bios_build); + device_remove_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn); +} + +/* The first entry, 0, is used for dynamic ids, the rest for devices + * we know about. + */ +static struct asd_pcidev_struct { + const char * name; + int (*setup)(struct asd_ha_struct *asd_ha); +} asd_pcidev_data[] = { + /* Id 0 is used for dynamic ids. */ + { .name = "Adaptec AIC-94xx SAS/SATA Host Adapter", + .setup = asd_aic9410_setup + }, + { .name = "Adaptec AIC-9410W SAS/SATA Host Adapter", + .setup = asd_aic9410_setup + }, + { .name = "Adaptec AIC-9405W SAS/SATA Host Adapter", + .setup = asd_aic9405_setup + }, +}; + +static inline int asd_create_ha_caches(struct asd_ha_struct *asd_ha) +{ + asd_ha->scb_pool = dma_pool_create(ASD_DRIVER_NAME "_scb_pool", + &asd_ha->pcidev->dev, + sizeof(struct scb), + 8, 0); + if (!asd_ha->scb_pool) { + asd_printk("couldn't create scb pool\n"); + return -ENOMEM; + } + + return 0; +} + +/** + * asd_free_edbs -- free empty data buffers + * asd_ha: pointer to host adapter structure + */ +static inline void asd_free_edbs(struct asd_ha_struct *asd_ha) +{ + struct asd_seq_data *seq = &asd_ha->seq; + int i; + + for (i = 0; i < seq->num_edbs; i++) + asd_free_coherent(asd_ha, seq->edb_arr[i]); + kfree(seq->edb_arr); + seq->edb_arr = NULL; +} + +static inline void asd_free_escbs(struct asd_ha_struct *asd_ha) +{ + struct asd_seq_data *seq = &asd_ha->seq; + int i; + + for (i = 0; i < seq->num_escbs; i++) { + if (!list_empty(&seq->escb_arr[i]->list)) + list_del_init(&seq->escb_arr[i]->list); + + asd_ascb_free(seq->escb_arr[i]); + } + kfree(seq->escb_arr); + seq->escb_arr = NULL; +} + +static inline void asd_destroy_ha_caches(struct asd_ha_struct *asd_ha) +{ + int i; + + if (asd_ha->hw_prof.ddb_ext) + asd_free_coherent(asd_ha, asd_ha->hw_prof.ddb_ext); + if (asd_ha->hw_prof.scb_ext) + asd_free_coherent(asd_ha, asd_ha->hw_prof.scb_ext); + + if (asd_ha->hw_prof.ddb_bitmap) + kfree(asd_ha->hw_prof.ddb_bitmap); + asd_ha->hw_prof.ddb_bitmap = NULL; + + for (i = 0; i < ASD_MAX_PHYS; i++) { + struct asd_phy *phy = &asd_ha->phys[i]; + + asd_free_coherent(asd_ha, phy->id_frm_tok); + } + if (asd_ha->seq.escb_arr) + asd_free_escbs(asd_ha); + if (asd_ha->seq.edb_arr) + asd_free_edbs(asd_ha); + if (asd_ha->hw_prof.ue.area) { + kfree(asd_ha->hw_prof.ue.area); + asd_ha->hw_prof.ue.area = NULL; + } + if (asd_ha->seq.tc_index_array) { + kfree(asd_ha->seq.tc_index_array); + kfree(asd_ha->seq.tc_index_bitmap); + asd_ha->seq.tc_index_array = NULL; + asd_ha->seq.tc_index_bitmap = NULL; + } + if (asd_ha->seq.actual_dl) { + asd_free_coherent(asd_ha, asd_ha->seq.actual_dl); + asd_ha->seq.actual_dl = NULL; + asd_ha->seq.dl = NULL; + } + if (asd_ha->seq.next_scb.vaddr) { + dma_pool_free(asd_ha->scb_pool, asd_ha->seq.next_scb.vaddr, + asd_ha->seq.next_scb.dma_handle); + asd_ha->seq.next_scb.vaddr = NULL; + } + dma_pool_destroy(asd_ha->scb_pool); + asd_ha->scb_pool = NULL; +} + +kmem_cache_t *asd_dma_token_cache; +kmem_cache_t *asd_ascb_cache; + +static int asd_create_global_caches(void) +{ + if (!asd_dma_token_cache) { + asd_dma_token_cache + = kmem_cache_create(ASD_DRIVER_NAME "_dma_token", + sizeof(struct asd_dma_tok), + 0, + SLAB_HWCACHE_ALIGN, + NULL, NULL); + if (!asd_dma_token_cache) { + asd_printk("couldn't create dma token cache\n"); + return -ENOMEM; + } + } + + if (!asd_ascb_cache) { + asd_ascb_cache = kmem_cache_create(ASD_DRIVER_NAME "_ascb", + sizeof(struct asd_ascb), + 0, + SLAB_HWCACHE_ALIGN, + NULL, NULL); + if (!asd_ascb_cache) { + asd_printk("couldn't create ascb cache\n"); + goto Err; + } + } + + return 0; +Err: + kmem_cache_destroy(asd_dma_token_cache); + asd_dma_token_cache = NULL; + return -ENOMEM; +} + +static void asd_destroy_global_caches(void) +{ + if (asd_dma_token_cache) + kmem_cache_destroy(asd_dma_token_cache); + asd_dma_token_cache = NULL; + + if (asd_ascb_cache) + kmem_cache_destroy(asd_ascb_cache); + asd_ascb_cache = NULL; +} + +static int asd_register_sas_ha(struct asd_ha_struct *asd_ha) +{ + int i; + struct asd_sas_phy **sas_phys = + kmalloc(ASD_MAX_PHYS * sizeof(struct asd_sas_phy), GFP_KERNEL); + struct asd_sas_port **sas_ports = + kmalloc(ASD_MAX_PHYS * sizeof(struct asd_sas_port), GFP_KERNEL); + + if (!sas_phys || !sas_ports) { + kfree(sas_phys); + kfree(sas_ports); + return -ENOMEM; + } + + asd_ha->sas_ha.sas_ha_name = (char *) asd_ha->name; + asd_ha->sas_ha.lldd_module = THIS_MODULE; + asd_ha->sas_ha.sas_addr = &asd_ha->hw_prof.sas_addr[0]; + + for (i = 0; i < ASD_MAX_PHYS; i++) { + sas_phys[i] = &asd_ha->phys[i].sas_phy; + sas_ports[i] = &asd_ha->ports[i]; + } + + asd_ha->sas_ha.sas_phy = sas_phys; + asd_ha->sas_ha.sas_port= sas_ports; + asd_ha->sas_ha.num_phys= ASD_MAX_PHYS; + + asd_ha->sas_ha.lldd_queue_size = asd_ha->seq.can_queue; + + return sas_register_ha(&asd_ha->sas_ha); +} + +static int asd_unregister_sas_ha(struct asd_ha_struct *asd_ha) +{ + int err; + + err = sas_unregister_ha(&asd_ha->sas_ha); + + sas_remove_host(asd_ha->sas_ha.core.shost); + scsi_remove_host(asd_ha->sas_ha.core.shost); + scsi_host_put(asd_ha->sas_ha.core.shost); + + kfree(asd_ha->sas_ha.sas_phy); + kfree(asd_ha->sas_ha.sas_port); + + return err; +} + +static int __devinit asd_pci_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct asd_pcidev_struct *asd_dev; + unsigned asd_id = (unsigned) id->driver_data; + struct asd_ha_struct *asd_ha; + struct Scsi_Host *shost; + int err; + + if (asd_id >= ARRAY_SIZE(asd_pcidev_data)) { + asd_printk("wrong driver_data in PCI table\n"); + return -ENODEV; + } + + if ((err = pci_enable_device(dev))) { + asd_printk("couldn't enable device %s\n", pci_name(dev)); + return err; + } + + pci_set_master(dev); + + err = -ENOMEM; + + shost = scsi_host_alloc(&aic94xx_sht, sizeof(void *)); + if (!shost) + goto Err; + + asd_dev = &asd_pcidev_data[asd_id]; + + asd_ha = kzalloc(sizeof(*asd_ha), GFP_KERNEL); + if (!asd_ha) { + asd_printk("out of memory\n"); + goto Err; + } + asd_ha->pcidev = dev; + asd_ha->sas_ha.pcidev = asd_ha->pcidev; + asd_ha->sas_ha.lldd_ha = asd_ha; + + asd_ha->name = asd_dev->name; + asd_printk("found %s, device %s\n", asd_ha->name, pci_name(dev)); + + SHOST_TO_SAS_HA(shost) = &asd_ha->sas_ha; + asd_ha->sas_ha.core.shost = shost; + shost->transportt = aic94xx_transport_template; + shost->max_id = ~0; + shost->max_lun = ~0; + shost->max_cmd_len = 16; + + err = scsi_add_host(shost, &dev->dev); + if (err) { + scsi_host_put(shost); + goto Err_free; + } + + + + err = asd_dev->setup(asd_ha); + if (err) + goto Err_free; + + err = -ENODEV; + if (!pci_set_dma_mask(dev, DMA_64BIT_MASK) + && !pci_set_consistent_dma_mask(dev, DMA_64BIT_MASK)) + ; + else if (!pci_set_dma_mask(dev, DMA_32BIT_MASK) + && !pci_set_consistent_dma_mask(dev, DMA_32BIT_MASK)) + ; + else { + asd_printk("no suitable DMA mask for %s\n", pci_name(dev)); + goto Err_free; + } + + pci_set_drvdata(dev, asd_ha); + + err = asd_map_ha(asd_ha); + if (err) + goto Err_free; + + err = asd_create_ha_caches(asd_ha); + if (err) + goto Err_unmap; + + err = asd_init_hw(asd_ha); + if (err) + goto Err_free_cache; + + asd_printk("device %s: SAS addr %llx, PCBA SN %s, %d phys, %d enabled " + "phys, flash %s, BIOS %s%d\n", + pci_name(dev), SAS_ADDR(asd_ha->hw_prof.sas_addr), + asd_ha->hw_prof.pcba_sn, asd_ha->hw_prof.max_phys, + asd_ha->hw_prof.num_phys, + asd_ha->hw_prof.flash.present ? "present" : "not present", + asd_ha->hw_prof.bios.present ? "build " : "not present", + asd_ha->hw_prof.bios.bld); + + if (use_msi) + pci_enable_msi(asd_ha->pcidev); + + err = request_irq(asd_ha->pcidev->irq, asd_hw_isr, SA_SHIRQ, + ASD_DRIVER_NAME, asd_ha); + if (err) { + asd_printk("couldn't get irq %d for %s\n", + asd_ha->pcidev->irq, pci_name(asd_ha->pcidev)); + goto Err_irq; + } + asd_enable_ints(asd_ha); + + err = asd_init_post_escbs(asd_ha); + if (err) { + asd_printk("couldn't post escbs for %s\n", + pci_name(asd_ha->pcidev)); + goto Err_escbs; + } + ASD_DPRINTK("escbs posted\n"); + + asd_create_dev_attrs(asd_ha); + + err = asd_register_sas_ha(asd_ha); + if (err) + goto Err_reg_sas; + + err = asd_enable_phys(asd_ha, asd_ha->hw_prof.enabled_phys); + if (err) { + asd_printk("coudln't enable phys, err:%d\n", err); + goto Err_en_phys; + } + ASD_DPRINTK("enabled phys\n"); + /* give the phy enabling interrupt event time to come in (1s + * is empirically about all it takes) */ + ssleep(1); + /* Wait for discovery to finish */ + scsi_flush_work(asd_ha->sas_ha.core.shost); + + return 0; +Err_en_phys: + asd_unregister_sas_ha(asd_ha); +Err_reg_sas: + asd_remove_dev_attrs(asd_ha); +Err_escbs: + asd_disable_ints(asd_ha); + free_irq(dev->irq, asd_ha); +Err_irq: + if (use_msi) + pci_disable_msi(dev); + asd_chip_hardrst(asd_ha); +Err_free_cache: + asd_destroy_ha_caches(asd_ha); +Err_unmap: + asd_unmap_ha(asd_ha); +Err_free: + kfree(asd_ha); + scsi_remove_host(shost); +Err: + pci_disable_device(dev); + return err; +} + +static void asd_free_queues(struct asd_ha_struct *asd_ha) +{ + unsigned long flags; + LIST_HEAD(pending); + struct list_head *n, *pos; + + spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags); + asd_ha->seq.pending = 0; + list_splice_init(&asd_ha->seq.pend_q, &pending); + spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags); + + if (!list_empty(&pending)) + ASD_DPRINTK("Uh-oh! Pending is not empty!\n"); + + list_for_each_safe(pos, n, &pending) { + struct asd_ascb *ascb = list_entry(pos, struct asd_ascb, list); + list_del_init(pos); + ASD_DPRINTK("freeing from pending\n"); + asd_ascb_free(ascb); + } +} + +static void asd_turn_off_leds(struct asd_ha_struct *asd_ha) +{ + u8 phy_mask = asd_ha->hw_prof.enabled_phys; + u8 i; + + for_each_phy(phy_mask, phy_mask, i) { + asd_turn_led(asd_ha, i, 0); + asd_control_led(asd_ha, i, 0); + } +} + +static void __devexit asd_pci_remove(struct pci_dev *dev) +{ + struct asd_ha_struct *asd_ha = pci_get_drvdata(dev); + + if (!asd_ha) + return; + + asd_unregister_sas_ha(asd_ha); + + asd_disable_ints(asd_ha); + + asd_remove_dev_attrs(asd_ha); + + /* XXX more here as needed */ + + free_irq(dev->irq, asd_ha); + if (use_msi) + pci_disable_msi(asd_ha->pcidev); + asd_turn_off_leds(asd_ha); + asd_chip_hardrst(asd_ha); + asd_free_queues(asd_ha); + asd_destroy_ha_caches(asd_ha); + asd_unmap_ha(asd_ha); + kfree(asd_ha); + pci_disable_device(dev); + return; +} + +static ssize_t asd_version_show(struct device_driver *driver, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", ASD_DRIVER_VERSION); +} +static DRIVER_ATTR(version, S_IRUGO, asd_version_show, NULL); + +static void asd_create_driver_attrs(struct device_driver *driver) +{ + driver_create_file(driver, &driver_attr_version); +} + +static void asd_remove_driver_attrs(struct device_driver *driver) +{ + driver_remove_file(driver, &driver_attr_version); +} + +static struct sas_domain_function_template aic94xx_transport_functions = { + .lldd_port_formed = asd_update_port_links, + + .lldd_dev_found = asd_dev_found, + .lldd_dev_gone = asd_dev_gone, + + .lldd_execute_task = asd_execute_task, + + .lldd_abort_task = asd_abort_task, + .lldd_abort_task_set = asd_abort_task_set, + .lldd_clear_aca = asd_clear_aca, + .lldd_clear_task_set = asd_clear_task_set, + .lldd_I_T_nexus_reset = NULL, + .lldd_lu_reset = asd_lu_reset, + .lldd_query_task = asd_query_task, + + .lldd_clear_nexus_port = asd_clear_nexus_port, + .lldd_clear_nexus_ha = asd_clear_nexus_ha, + + .lldd_control_phy = asd_control_phy, +}; + +static const struct pci_device_id aic94xx_pci_table[] __devinitdata = { + {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR10), + 0, 0, 1}, + {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR12), + 0, 0, 1}, + {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR1E), + 0, 0, 1}, + {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR30), + 0, 0, 2}, + {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR32), + 0, 0, 2}, + {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR3E), + 0, 0, 2}, + {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR3F), + 0, 0, 2}, + {} +}; + +MODULE_DEVICE_TABLE(pci, aic94xx_pci_table); + +static struct pci_driver aic94xx_pci_driver = { + .name = ASD_DRIVER_NAME, + .id_table = aic94xx_pci_table, + .probe = asd_pci_probe, + .remove = __devexit_p(asd_pci_remove), +}; + +static int __init aic94xx_init(void) +{ + int err; + + + asd_printk("%s version %s loaded\n", ASD_DRIVER_DESCRIPTION, + ASD_DRIVER_VERSION); + + err = asd_create_global_caches(); + if (err) + return err; + + aic94xx_transport_template = + sas_domain_attach_transport(&aic94xx_transport_functions); + if (err) + goto out_destroy_caches; + + err = pci_register_driver(&aic94xx_pci_driver); + if (err) + goto out_release_transport; + + asd_create_driver_attrs(&aic94xx_pci_driver.driver); + + return err; + + out_release_transport: + sas_release_transport(aic94xx_transport_template); + out_destroy_caches: + asd_destroy_global_caches(); + + return err; +} + +static void __exit aic94xx_exit(void) +{ + asd_remove_driver_attrs(&aic94xx_pci_driver.driver); + pci_unregister_driver(&aic94xx_pci_driver); + sas_release_transport(aic94xx_transport_template); + asd_destroy_global_caches(); + asd_printk("%s version %s unloaded\n", ASD_DRIVER_DESCRIPTION, + ASD_DRIVER_VERSION); +} + +module_init(aic94xx_init); +module_exit(aic94xx_exit); + +MODULE_AUTHOR("Luben Tuikov "); +MODULE_DESCRIPTION(ASD_DRIVER_DESCRIPTION); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(ASD_DRIVER_VERSION); diff --git a/drivers/scsi/aic94xx/aic94xx_reg.c b/drivers/scsi/aic94xx/aic94xx_reg.c new file mode 100644 index 00000000000..f210dac3203 --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx_reg.c @@ -0,0 +1,332 @@ +/* + * Aic94xx SAS/SATA driver register access. + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * This file is part of the aic94xx driver. + * + * The aic94xx driver 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; version 2 of the + * License. + * + * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include "aic94xx_reg.h" +#include "aic94xx.h" + +/* Writing to device address space. + * Offset comes before value to remind that the operation of + * this function is *offs = val. + */ +static inline void asd_write_byte(struct asd_ha_struct *asd_ha, + unsigned long offs, u8 val) +{ + if (unlikely(asd_ha->iospace)) + outb(val, + (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF)); + else + writeb(val, asd_ha->io_handle[0].addr + offs); + wmb(); +} + +static inline void asd_write_word(struct asd_ha_struct *asd_ha, + unsigned long offs, u16 val) +{ + if (unlikely(asd_ha->iospace)) + outw(val, + (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF)); + else + writew(val, asd_ha->io_handle[0].addr + offs); + wmb(); +} + +static inline void asd_write_dword(struct asd_ha_struct *asd_ha, + unsigned long offs, u32 val) +{ + if (unlikely(asd_ha->iospace)) + outl(val, + (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF)); + else + writel(val, asd_ha->io_handle[0].addr + offs); + wmb(); +} + +/* Reading from device address space. + */ +static inline u8 asd_read_byte(struct asd_ha_struct *asd_ha, + unsigned long offs) +{ + u8 val; + if (unlikely(asd_ha->iospace)) + val = inb((unsigned long) asd_ha->io_handle[0].addr + + (offs & 0xFF)); + else + val = readb(asd_ha->io_handle[0].addr + offs); + rmb(); + return val; +} + +static inline u16 asd_read_word(struct asd_ha_struct *asd_ha, + unsigned long offs) +{ + u16 val; + if (unlikely(asd_ha->iospace)) + val = inw((unsigned long)asd_ha->io_handle[0].addr + + (offs & 0xFF)); + else + val = readw(asd_ha->io_handle[0].addr + offs); + rmb(); + return val; +} + +static inline u32 asd_read_dword(struct asd_ha_struct *asd_ha, + unsigned long offs) +{ + u32 val; + if (unlikely(asd_ha->iospace)) + val = inl((unsigned long) asd_ha->io_handle[0].addr + + (offs & 0xFF)); + else + val = readl(asd_ha->io_handle[0].addr + offs); + rmb(); + return val; +} + +static inline u32 asd_mem_offs_swa(void) +{ + return 0; +} + +static inline u32 asd_mem_offs_swc(void) +{ + return asd_mem_offs_swa() + MBAR0_SWA_SIZE; +} + +static inline u32 asd_mem_offs_swb(void) +{ + return asd_mem_offs_swc() + MBAR0_SWC_SIZE + 0x20; +} + +/* We know that the register wanted is in the range + * of the sliding window. + */ +#define ASD_READ_SW(ww, type, ord) \ +static inline type asd_read_##ww##_##ord (struct asd_ha_struct *asd_ha,\ + u32 reg) \ +{ \ + struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0]; \ + u32 map_offs=(reg - io_handle-> ww##_base )+asd_mem_offs_##ww ();\ + return asd_read_##ord (asd_ha, (unsigned long) map_offs); \ +} + +#define ASD_WRITE_SW(ww, type, ord) \ +static inline void asd_write_##ww##_##ord (struct asd_ha_struct *asd_ha,\ + u32 reg, type val) \ +{ \ + struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0]; \ + u32 map_offs=(reg - io_handle-> ww##_base )+asd_mem_offs_##ww ();\ + asd_write_##ord (asd_ha, (unsigned long) map_offs, val); \ +} + +ASD_READ_SW(swa, u8, byte); +ASD_READ_SW(swa, u16, word); +ASD_READ_SW(swa, u32, dword); + +ASD_READ_SW(swb, u8, byte); +ASD_READ_SW(swb, u16, word); +ASD_READ_SW(swb, u32, dword); + +ASD_READ_SW(swc, u8, byte); +ASD_READ_SW(swc, u16, word); +ASD_READ_SW(swc, u32, dword); + +ASD_WRITE_SW(swa, u8, byte); +ASD_WRITE_SW(swa, u16, word); +ASD_WRITE_SW(swa, u32, dword); + +ASD_WRITE_SW(swb, u8, byte); +ASD_WRITE_SW(swb, u16, word); +ASD_WRITE_SW(swb, u32, dword); + +ASD_WRITE_SW(swc, u8, byte); +ASD_WRITE_SW(swc, u16, word); +ASD_WRITE_SW(swc, u32, dword); + +/* + * A word about sliding windows: + * MBAR0 is divided into sliding windows A, C and B, in that order. + * SWA starts at offset 0 of MBAR0, up to 0x57, with size 0x58 bytes. + * SWC starts at offset 0x58 of MBAR0, up to 0x60, with size 0x8 bytes. + * From 0x60 to 0x7F, we have a copy of PCI config space 0x60-0x7F. + * SWB starts at offset 0x80 of MBAR0 and extends to the end of MBAR0. + * See asd_init_sw() in aic94xx_hwi.c + * + * We map the most common registers we'd access of the internal 4GB + * host adapter memory space. If a register/internal memory location + * is wanted which is not mapped, we slide SWB, by paging it, + * see asd_move_swb() in aic94xx_reg.c. + */ + +/** + * asd_move_swb -- move sliding window B + * @asd_ha: pointer to host adapter structure + * @reg: register desired to be within range of the new window + */ +static inline void asd_move_swb(struct asd_ha_struct *asd_ha, u32 reg) +{ + u32 base = reg & ~(MBAR0_SWB_SIZE-1); + pci_write_config_dword(asd_ha->pcidev, PCI_CONF_MBAR0_SWB, base); + asd_ha->io_handle[0].swb_base = base; +} + +static void __asd_write_reg_byte(struct asd_ha_struct *asd_ha, u32 reg, u8 val) +{ + struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; + BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR); + if (io_handle->swa_base <= reg + && reg < io_handle->swa_base + MBAR0_SWA_SIZE) + asd_write_swa_byte (asd_ha, reg,val); + else if (io_handle->swb_base <= reg + && reg < io_handle->swb_base + MBAR0_SWB_SIZE) + asd_write_swb_byte (asd_ha, reg, val); + else if (io_handle->swc_base <= reg + && reg < io_handle->swc_base + MBAR0_SWC_SIZE) + asd_write_swc_byte (asd_ha, reg, val); + else { + /* Ok, we have to move SWB */ + asd_move_swb(asd_ha, reg); + asd_write_swb_byte (asd_ha, reg, val); + } +} + +#define ASD_WRITE_REG(type, ord) \ +void asd_write_reg_##ord (struct asd_ha_struct *asd_ha, u32 reg, type val)\ +{ \ + struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; \ + unsigned long flags; \ + BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR); \ + spin_lock_irqsave(&asd_ha->iolock, flags); \ + if (io_handle->swa_base <= reg \ + && reg < io_handle->swa_base + MBAR0_SWA_SIZE) \ + asd_write_swa_##ord (asd_ha, reg,val); \ + else if (io_handle->swb_base <= reg \ + && reg < io_handle->swb_base + MBAR0_SWB_SIZE) \ + asd_write_swb_##ord (asd_ha, reg, val); \ + else if (io_handle->swc_base <= reg \ + && reg < io_handle->swc_base + MBAR0_SWC_SIZE) \ + asd_write_swc_##ord (asd_ha, reg, val); \ + else { \ + /* Ok, we have to move SWB */ \ + asd_move_swb(asd_ha, reg); \ + asd_write_swb_##ord (asd_ha, reg, val); \ + } \ + spin_unlock_irqrestore(&asd_ha->iolock, flags); \ +} + +ASD_WRITE_REG(u8, byte); +ASD_WRITE_REG(u16,word); +ASD_WRITE_REG(u32,dword); + +static u8 __asd_read_reg_byte(struct asd_ha_struct *asd_ha, u32 reg) +{ + struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; + u8 val; + BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR); + if (io_handle->swa_base <= reg + && reg < io_handle->swa_base + MBAR0_SWA_SIZE) + val = asd_read_swa_byte (asd_ha, reg); + else if (io_handle->swb_base <= reg + && reg < io_handle->swb_base + MBAR0_SWB_SIZE) + val = asd_read_swb_byte (asd_ha, reg); + else if (io_handle->swc_base <= reg + && reg < io_handle->swc_base + MBAR0_SWC_SIZE) + val = asd_read_swc_byte (asd_ha, reg); + else { + /* Ok, we have to move SWB */ + asd_move_swb(asd_ha, reg); + val = asd_read_swb_byte (asd_ha, reg); + } + return val; +} + +#define ASD_READ_REG(type, ord) \ +type asd_read_reg_##ord (struct asd_ha_struct *asd_ha, u32 reg) \ +{ \ + struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; \ + type val; \ + unsigned long flags; \ + BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR); \ + spin_lock_irqsave(&asd_ha->iolock, flags); \ + if (io_handle->swa_base <= reg \ + && reg < io_handle->swa_base + MBAR0_SWA_SIZE) \ + val = asd_read_swa_##ord (asd_ha, reg); \ + else if (io_handle->swb_base <= reg \ + && reg < io_handle->swb_base + MBAR0_SWB_SIZE) \ + val = asd_read_swb_##ord (asd_ha, reg); \ + else if (io_handle->swc_base <= reg \ + && reg < io_handle->swc_base + MBAR0_SWC_SIZE) \ + val = asd_read_swc_##ord (asd_ha, reg); \ + else { \ + /* Ok, we have to move SWB */ \ + asd_move_swb(asd_ha, reg); \ + val = asd_read_swb_##ord (asd_ha, reg); \ + } \ + spin_unlock_irqrestore(&asd_ha->iolock, flags); \ + return val; \ +} + +ASD_READ_REG(u8, byte); +ASD_READ_REG(u16,word); +ASD_READ_REG(u32,dword); + +/** + * asd_read_reg_string -- read a string of bytes from io space memory + * @asd_ha: pointer to host adapter structure + * @dst: pointer to a destination buffer where data will be written to + * @offs: start offset (register) to read from + * @count: number of bytes to read + */ +void asd_read_reg_string(struct asd_ha_struct *asd_ha, void *dst, + u32 offs, int count) +{ + u8 *p = dst; + unsigned long flags; + + spin_lock_irqsave(&asd_ha->iolock, flags); + for ( ; count > 0; count--, offs++, p++) + *p = __asd_read_reg_byte(asd_ha, offs); + spin_unlock_irqrestore(&asd_ha->iolock, flags); +} + +/** + * asd_write_reg_string -- write a string of bytes to io space memory + * @asd_ha: pointer to host adapter structure + * @src: pointer to source buffer where data will be read from + * @offs: start offset (register) to write to + * @count: number of bytes to write + */ +void asd_write_reg_string(struct asd_ha_struct *asd_ha, void *src, + u32 offs, int count) +{ + u8 *p = src; + unsigned long flags; + + spin_lock_irqsave(&asd_ha->iolock, flags); + for ( ; count > 0; count--, offs++, p++) + __asd_write_reg_byte(asd_ha, offs, *p); + spin_unlock_irqrestore(&asd_ha->iolock, flags); +} diff --git a/drivers/scsi/aic94xx/aic94xx_reg.h b/drivers/scsi/aic94xx/aic94xx_reg.h new file mode 100644 index 00000000000..2279307fd27 --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx_reg.h @@ -0,0 +1,302 @@ +/* + * Aic94xx SAS/SATA driver hardware registers definitions. + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * This file is part of the aic94xx driver. + * + * The aic94xx driver 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; version 2 of the + * License. + * + * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _AIC94XX_REG_H_ +#define _AIC94XX_REG_H_ + +#include +#include "aic94xx_hwi.h" + +/* Values */ +#define AIC9410_DEV_REV_B0 0x8 + +/* MBAR0, SWA, SWB, SWC, internal memory space addresses */ +#define REG_BASE_ADDR 0xB8000000 +#define REG_BASE_ADDR_CSEQCIO 0xB8002000 +#define REG_BASE_ADDR_EXSI 0xB8042800 + +#define MBAR0_SWA_SIZE 0x58 +extern u32 MBAR0_SWB_SIZE; +#define MBAR0_SWC_SIZE 0x8 + +/* MBAR1, points to On Chip Memory */ +#define OCM_BASE_ADDR 0xA0000000 +#define OCM_MAX_SIZE 0x20000 + +/* Smallest address possible to reference */ +#define ALL_BASE_ADDR OCM_BASE_ADDR + +/* PCI configuration space registers */ +#define PCI_IOBAR_OFFSET 4 + +#define PCI_CONF_MBAR1 0x6C +#define PCI_CONF_MBAR0_SWA 0x70 +#define PCI_CONF_MBAR0_SWB 0x74 +#define PCI_CONF_MBAR0_SWC 0x78 +#define PCI_CONF_MBAR_KEY 0x7C +#define PCI_CONF_FLSH_BAR 0xB8 + +#include "aic94xx_reg_def.h" + +u8 asd_read_reg_byte(struct asd_ha_struct *asd_ha, u32 reg); +u16 asd_read_reg_word(struct asd_ha_struct *asd_ha, u32 reg); +u32 asd_read_reg_dword(struct asd_ha_struct *asd_ha, u32 reg); + +void asd_write_reg_byte(struct asd_ha_struct *asd_ha, u32 reg, u8 val); +void asd_write_reg_word(struct asd_ha_struct *asd_ha, u32 reg, u16 val); +void asd_write_reg_dword(struct asd_ha_struct *asd_ha, u32 reg, u32 val); + +void asd_read_reg_string(struct asd_ha_struct *asd_ha, void *dst, + u32 offs, int count); +void asd_write_reg_string(struct asd_ha_struct *asd_ha, void *src, + u32 offs, int count); + +#define ASD_READ_OCM(type, ord, S) \ +static inline type asd_read_ocm_##ord (struct asd_ha_struct *asd_ha, \ + u32 offs) \ +{ \ + struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[1]; \ + type val = read##S (io_handle->addr + (unsigned long) offs); \ + rmb(); \ + return val; \ +} + +ASD_READ_OCM(u8, byte, b); +ASD_READ_OCM(u16,word, w); +ASD_READ_OCM(u32,dword,l); + +#define ASD_WRITE_OCM(type, ord, S) \ +static inline void asd_write_ocm_##ord (struct asd_ha_struct *asd_ha, \ + u32 offs, type val) \ +{ \ + struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[1]; \ + write##S (val, io_handle->addr + (unsigned long) offs); \ + return; \ +} + +ASD_WRITE_OCM(u8, byte, b); +ASD_WRITE_OCM(u16,word, w); +ASD_WRITE_OCM(u32,dword,l); + +#define ASD_DDBSITE_READ(type, ord) \ +static inline type asd_ddbsite_read_##ord (struct asd_ha_struct *asd_ha, \ + u16 ddb_site_no, \ + u16 offs) \ +{ \ + asd_write_reg_word(asd_ha, ALTCIOADR, MnDDB_SITE + offs); \ + asd_write_reg_word(asd_ha, ADDBPTR, ddb_site_no); \ + return asd_read_reg_##ord (asd_ha, CTXACCESS); \ +} + +ASD_DDBSITE_READ(u32, dword); +ASD_DDBSITE_READ(u16, word); + +static inline u8 asd_ddbsite_read_byte(struct asd_ha_struct *asd_ha, + u16 ddb_site_no, + u16 offs) +{ + if (offs & 1) + return asd_ddbsite_read_word(asd_ha, ddb_site_no, + offs & ~1) >> 8; + else + return asd_ddbsite_read_word(asd_ha, ddb_site_no, + offs) & 0xFF; +} + + +#define ASD_DDBSITE_WRITE(type, ord) \ +static inline void asd_ddbsite_write_##ord (struct asd_ha_struct *asd_ha, \ + u16 ddb_site_no, \ + u16 offs, type val) \ +{ \ + asd_write_reg_word(asd_ha, ALTCIOADR, MnDDB_SITE + offs); \ + asd_write_reg_word(asd_ha, ADDBPTR, ddb_site_no); \ + asd_write_reg_##ord (asd_ha, CTXACCESS, val); \ +} + +ASD_DDBSITE_WRITE(u32, dword); +ASD_DDBSITE_WRITE(u16, word); + +static inline void asd_ddbsite_write_byte(struct asd_ha_struct *asd_ha, + u16 ddb_site_no, + u16 offs, u8 val) +{ + u16 base = offs & ~1; + u16 rval = asd_ddbsite_read_word(asd_ha, ddb_site_no, base); + if (offs & 1) + rval = (val << 8) | (rval & 0xFF); + else + rval = (rval & 0xFF00) | val; + asd_ddbsite_write_word(asd_ha, ddb_site_no, base, rval); +} + + +#define ASD_SCBSITE_READ(type, ord) \ +static inline type asd_scbsite_read_##ord (struct asd_ha_struct *asd_ha, \ + u16 scb_site_no, \ + u16 offs) \ +{ \ + asd_write_reg_word(asd_ha, ALTCIOADR, MnSCB_SITE + offs); \ + asd_write_reg_word(asd_ha, ASCBPTR, scb_site_no); \ + return asd_read_reg_##ord (asd_ha, CTXACCESS); \ +} + +ASD_SCBSITE_READ(u32, dword); +ASD_SCBSITE_READ(u16, word); + +static inline u8 asd_scbsite_read_byte(struct asd_ha_struct *asd_ha, + u16 scb_site_no, + u16 offs) +{ + if (offs & 1) + return asd_scbsite_read_word(asd_ha, scb_site_no, + offs & ~1) >> 8; + else + return asd_scbsite_read_word(asd_ha, scb_site_no, + offs) & 0xFF; +} + + +#define ASD_SCBSITE_WRITE(type, ord) \ +static inline void asd_scbsite_write_##ord (struct asd_ha_struct *asd_ha, \ + u16 scb_site_no, \ + u16 offs, type val) \ +{ \ + asd_write_reg_word(asd_ha, ALTCIOADR, MnSCB_SITE + offs); \ + asd_write_reg_word(asd_ha, ASCBPTR, scb_site_no); \ + asd_write_reg_##ord (asd_ha, CTXACCESS, val); \ +} + +ASD_SCBSITE_WRITE(u32, dword); +ASD_SCBSITE_WRITE(u16, word); + +static inline void asd_scbsite_write_byte(struct asd_ha_struct *asd_ha, + u16 scb_site_no, + u16 offs, u8 val) +{ + u16 base = offs & ~1; + u16 rval = asd_scbsite_read_word(asd_ha, scb_site_no, base); + if (offs & 1) + rval = (val << 8) | (rval & 0xFF); + else + rval = (rval & 0xFF00) | val; + asd_scbsite_write_word(asd_ha, scb_site_no, base, rval); +} + +/** + * asd_ddbsite_update_word -- atomically update a word in a ddb site + * @asd_ha: pointer to host adapter structure + * @ddb_site_no: the DDB site number + * @offs: the offset into the DDB + * @oldval: old value found in that offset + * @newval: the new value to replace it + * + * This function is used when the sequencers are running and we need to + * update a DDB site atomically without expensive pausing and upausing + * of the sequencers and accessing the DDB site through the CIO bus. + * + * Return 0 on success; -EFAULT on parity error; -EAGAIN if the old value + * is different than the current value at that offset. + */ +static inline int asd_ddbsite_update_word(struct asd_ha_struct *asd_ha, + u16 ddb_site_no, u16 offs, + u16 oldval, u16 newval) +{ + u8 done; + u16 oval = asd_ddbsite_read_word(asd_ha, ddb_site_no, offs); + if (oval != oldval) + return -EAGAIN; + asd_write_reg_word(asd_ha, AOLDDATA, oldval); + asd_write_reg_word(asd_ha, ANEWDATA, newval); + do { + done = asd_read_reg_byte(asd_ha, ATOMICSTATCTL); + } while (!(done & ATOMICDONE)); + if (done & ATOMICERR) + return -EFAULT; /* parity error */ + else if (done & ATOMICWIN) + return 0; /* success */ + else + return -EAGAIN; /* oldval different than current value */ +} + +static inline int asd_ddbsite_update_byte(struct asd_ha_struct *asd_ha, + u16 ddb_site_no, u16 offs, + u8 _oldval, u8 _newval) +{ + u16 base = offs & ~1; + u16 oval; + u16 nval = asd_ddbsite_read_word(asd_ha, ddb_site_no, base); + if (offs & 1) { + if ((nval >> 8) != _oldval) + return -EAGAIN; + nval = (_newval << 8) | (nval & 0xFF); + oval = (_oldval << 8) | (nval & 0xFF); + } else { + if ((nval & 0xFF) != _oldval) + return -EAGAIN; + nval = (nval & 0xFF00) | _newval; + oval = (nval & 0xFF00) | _oldval; + } + return asd_ddbsite_update_word(asd_ha, ddb_site_no, base, oval, nval); +} + +static inline void asd_write_reg_addr(struct asd_ha_struct *asd_ha, u32 reg, + dma_addr_t dma_handle) +{ + asd_write_reg_dword(asd_ha, reg, ASD_BUSADDR_LO(dma_handle)); + asd_write_reg_dword(asd_ha, reg+4, ASD_BUSADDR_HI(dma_handle)); +} + +static inline u32 asd_get_cmdctx_size(struct asd_ha_struct *asd_ha) +{ + /* DCHREVISION returns 0, possibly broken */ + u32 ctxmemsize = asd_read_reg_dword(asd_ha, LmMnINT(0,0)) & CTXMEMSIZE; + return ctxmemsize ? 65536 : 32768; +} + +static inline u32 asd_get_devctx_size(struct asd_ha_struct *asd_ha) +{ + u32 ctxmemsize = asd_read_reg_dword(asd_ha, LmMnINT(0,0)) & CTXMEMSIZE; + return ctxmemsize ? 8192 : 4096; +} + +static inline void asd_disable_ints(struct asd_ha_struct *asd_ha) +{ + asd_write_reg_dword(asd_ha, CHIMINTEN, RST_CHIMINTEN); +} + +static inline void asd_enable_ints(struct asd_ha_struct *asd_ha) +{ + /* Enable COM SAS interrupt on errors, COMSTAT */ + asd_write_reg_dword(asd_ha, COMSTATEN, + EN_CSBUFPERR | EN_CSERR | EN_OVLYERR); + /* Enable DCH SAS CFIFTOERR */ + asd_write_reg_dword(asd_ha, DCHSTATUS, EN_CFIFTOERR); + /* Enable Host Device interrupts */ + asd_write_reg_dword(asd_ha, CHIMINTEN, SET_CHIMINTEN); +} + +#endif diff --git a/drivers/scsi/aic94xx/aic94xx_reg_def.h b/drivers/scsi/aic94xx/aic94xx_reg_def.h new file mode 100644 index 00000000000..b79f45f3ad4 --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx_reg_def.h @@ -0,0 +1,2398 @@ +/* + * Aic94xx SAS/SATA driver hardware registers defintions. + * + * Copyright (C) 2004 Adaptec, Inc. All rights reserved. + * Copyright (C) 2004 David Chaw + * Copyright (C) 2005 Luben Tuikov + * + * Luben Tuikov: Some register value updates to make it work with the window + * agnostic register r/w functions. Some register corrections, sizes, + * etc. + * + * This file is licensed under GPLv2. + * + * This file is part of the aic94xx driver. + * + * The aic94xx driver 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; version 2 of the + * License. + * + * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: //depot/aic94xx/aic94xx_reg_def.h#27 $ + * + */ + +#ifndef _ADP94XX_REG_DEF_H_ +#define _ADP94XX_REG_DEF_H_ + +/* + * Common definitions. + */ +#define CSEQ_MODE_PAGE_SIZE 0x200 /* CSEQ mode page size */ +#define LmSEQ_MODE_PAGE_SIZE 0x200 /* LmSEQ mode page size */ +#define LmSEQ_HOST_REG_SIZE 0x4000 /* LmSEQ Host Register size */ + +/********************* COM_SAS registers definition *************************/ + +/* The base is REG_BASE_ADDR, defined in aic94xx_reg.h. + */ + +/* + * CHIM Registers, Address Range : (0x00-0xFF) + */ +#define COMBIST (REG_BASE_ADDR + 0x00) + +/* bits 31:24 */ +#define L7BLKRST 0x80000000 +#define L6BLKRST 0x40000000 +#define L5BLKRST 0x20000000 +#define L4BLKRST 0x10000000 +#define L3BLKRST 0x08000000 +#define L2BLKRST 0x04000000 +#define L1BLKRST 0x02000000 +#define L0BLKRST 0x01000000 +#define LmBLKRST 0xFF000000 +#define LmBLKRST_COMBIST(phyid) (1 << (24 + phyid)) + +#define OCMBLKRST 0x00400000 +#define CTXMEMBLKRST 0x00200000 +#define CSEQBLKRST 0x00100000 +#define EXSIBLKRST 0x00040000 +#define DPIBLKRST 0x00020000 +#define DFIFBLKRST 0x00010000 +#define HARDRST 0x00000200 +#define COMBLKRST 0x00000100 +#define FRCDFPERR 0x00000080 +#define FRCCIOPERR 0x00000020 +#define FRCBISTERR 0x00000010 +#define COMBISTEN 0x00000004 +#define COMBISTDONE 0x00000002 /* ro */ +#define COMBISTFAIL 0x00000001 /* ro */ + +#define COMSTAT (REG_BASE_ADDR + 0x04) + +#define REQMBXREAD 0x00000040 +#define RSPMBXAVAIL 0x00000020 +#define CSBUFPERR 0x00000008 +#define OVLYERR 0x00000004 +#define CSERR 0x00000002 +#define OVLYDMADONE 0x00000001 + +#define COMSTAT_MASK (REQMBXREAD | RSPMBXAVAIL | \ + CSBUFPERR | OVLYERR | CSERR |\ + OVLYDMADONE) + +#define COMSTATEN (REG_BASE_ADDR + 0x08) + +#define EN_REQMBXREAD 0x00000040 +#define EN_RSPMBXAVAIL 0x00000020 +#define EN_CSBUFPERR 0x00000008 +#define EN_OVLYERR 0x00000004 +#define EN_CSERR 0x00000002 +#define EN_OVLYDONE 0x00000001 + +#define SCBPRO (REG_BASE_ADDR + 0x0C) + +#define SCBCONS_MASK 0xFFFF0000 +#define SCBPRO_MASK 0x0000FFFF + +#define CHIMREQMBX (REG_BASE_ADDR + 0x10) + +#define CHIMRSPMBX (REG_BASE_ADDR + 0x14) + +#define CHIMINT (REG_BASE_ADDR + 0x18) + +#define EXT_INT0 0x00000800 +#define EXT_INT1 0x00000400 +#define PORRSTDET 0x00000200 +#define HARDRSTDET 0x00000100 +#define DLAVAILQ 0x00000080 /* ro */ +#define HOSTERR 0x00000040 +#define INITERR 0x00000020 +#define DEVINT 0x00000010 +#define COMINT 0x00000008 +#define DEVTIMER2 0x00000004 +#define DEVTIMER1 0x00000002 +#define DLAVAIL 0x00000001 + +#define CHIMINT_MASK (HOSTERR | INITERR | DEVINT | COMINT |\ + DEVTIMER2 | DEVTIMER1 | DLAVAIL) + +#define DEVEXCEPT_MASK (HOSTERR | INITERR | DEVINT | COMINT) + +#define CHIMINTEN (REG_BASE_ADDR + 0x1C) + +#define RST_EN_EXT_INT1 0x01000000 +#define RST_EN_EXT_INT0 0x00800000 +#define RST_EN_HOSTERR 0x00400000 +#define RST_EN_INITERR 0x00200000 +#define RST_EN_DEVINT 0x00100000 +#define RST_EN_COMINT 0x00080000 +#define RST_EN_DEVTIMER2 0x00040000 +#define RST_EN_DEVTIMER1 0x00020000 +#define RST_EN_DLAVAIL 0x00010000 +#define SET_EN_EXT_INT1 0x00000100 +#define SET_EN_EXT_INT0 0x00000080 +#define SET_EN_HOSTERR 0x00000040 +#define SET_EN_INITERR 0x00000020 +#define SET_EN_DEVINT 0x00000010 +#define SET_EN_COMINT 0x00000008 +#define SET_EN_DEVTIMER2 0x00000004 +#define SET_EN_DEVTIMER1 0x00000002 +#define SET_EN_DLAVAIL 0x00000001 + +#define RST_CHIMINTEN (RST_EN_HOSTERR | RST_EN_INITERR | \ + RST_EN_DEVINT | RST_EN_COMINT | \ + RST_EN_DEVTIMER2 | RST_EN_DEVTIMER1 |\ + RST_EN_DLAVAIL) + +#define SET_CHIMINTEN (SET_EN_HOSTERR | SET_EN_INITERR |\ + SET_EN_DEVINT | SET_EN_COMINT |\ + SET_EN_DLAVAIL) + +#define OVLYDMACTL (REG_BASE_ADDR + 0x20) + +#define OVLYADR_MASK 0x07FF0000 +#define OVLYLSEQ_MASK 0x0000FF00 +#define OVLYCSEQ 0x00000080 +#define OVLYHALTERR 0x00000040 +#define PIOCMODE 0x00000020 +#define RESETOVLYDMA 0x00000008 /* wo */ +#define STARTOVLYDMA 0x00000004 +#define STOPOVLYDMA 0x00000002 /* wo */ +#define OVLYDMAACT 0x00000001 /* ro */ + +#define OVLYDMACNT (REG_BASE_ADDR + 0x24) + +#define OVLYDOMAIN1 0x20000000 /* ro */ +#define OVLYDOMAIN0 0x10000000 +#define OVLYBUFADR_MASK 0x007F0000 +#define OVLYDMACNT_MASK 0x00003FFF + +#define OVLYDMAADR (REG_BASE_ADDR + 0x28) + +#define DMAERR (REG_BASE_ADDR + 0x30) + +#define OVLYERRSTAT_MASK 0x0000FF00 /* ro */ +#define CSERRSTAT_MASK 0x000000FF /* ro */ + +#define SPIODATA (REG_BASE_ADDR + 0x34) + +/* 0x38 - 0x3C are reserved */ + +#define T1CNTRLR (REG_BASE_ADDR + 0x40) + +#define T1DONE 0x00010000 /* ro */ +#define TIMER64 0x00000400 +#define T1ENABLE 0x00000200 +#define T1RELOAD 0x00000100 +#define T1PRESCALER_MASK 0x00000003 + +#define T1CMPR (REG_BASE_ADDR + 0x44) + +#define T1CNTR (REG_BASE_ADDR + 0x48) + +#define T2CNTRLR (REG_BASE_ADDR + 0x4C) + +#define T2DONE 0x00010000 /* ro */ +#define T2ENABLE 0x00000200 +#define T2RELOAD 0x00000100 +#define T2PRESCALER_MASK 0x00000003 + +#define T2CMPR (REG_BASE_ADDR + 0x50) + +#define T2CNTR (REG_BASE_ADDR + 0x54) + +/* 0x58h - 0xFCh are reserved */ + +/* + * DCH_SAS Registers, Address Range : (0x800-0xFFF) + */ +#define CMDCTXBASE (REG_BASE_ADDR + 0x800) + +#define DEVCTXBASE (REG_BASE_ADDR + 0x808) + +#define CTXDOMAIN (REG_BASE_ADDR + 0x810) + +#define DEVCTXDOMAIN1 0x00000008 /* ro */ +#define DEVCTXDOMAIN0 0x00000004 +#define CMDCTXDOMAIN1 0x00000002 /* ro */ +#define CMDCTXDOMAIN0 0x00000001 + +#define DCHCTL (REG_BASE_ADDR + 0x814) + +#define OCMBISTREPAIR 0x00080000 +#define OCMBISTEN 0x00040000 +#define OCMBISTDN 0x00020000 /* ro */ +#define OCMBISTFAIL 0x00010000 /* ro */ +#define DDBBISTEN 0x00004000 +#define DDBBISTDN 0x00002000 /* ro */ +#define DDBBISTFAIL 0x00001000 /* ro */ +#define SCBBISTEN 0x00000400 +#define SCBBISTDN 0x00000200 /* ro */ +#define SCBBISTFAIL 0x00000100 /* ro */ + +#define MEMSEL_MASK 0x000000E0 +#define MEMSEL_CCM_LSEQ 0x00000000 +#define MEMSEL_CCM_IOP 0x00000020 +#define MEMSEL_CCM_SASCTL 0x00000040 +#define MEMSEL_DCM_LSEQ 0x00000060 +#define MEMSEL_DCM_IOP 0x00000080 +#define MEMSEL_OCM 0x000000A0 + +#define FRCERR 0x00000010 +#define AUTORLS 0x00000001 + +#define DCHREVISION (REG_BASE_ADDR + 0x818) + +#define DCHREVISION_MASK 0x000000FF + +#define DCHSTATUS (REG_BASE_ADDR + 0x81C) + +#define EN_CFIFTOERR 0x00020000 +#define CFIFTOERR 0x00000200 +#define CSEQINT 0x00000100 /* ro */ +#define LSEQ7INT 0x00000080 /* ro */ +#define LSEQ6INT 0x00000040 /* ro */ +#define LSEQ5INT 0x00000020 /* ro */ +#define LSEQ4INT 0x00000010 /* ro */ +#define LSEQ3INT 0x00000008 /* ro */ +#define LSEQ2INT 0x00000004 /* ro */ +#define LSEQ1INT 0x00000002 /* ro */ +#define LSEQ0INT 0x00000001 /* ro */ + +#define LSEQINT_MASK (LSEQ7INT | LSEQ6INT | LSEQ5INT |\ + LSEQ4INT | LSEQ3INT | LSEQ2INT |\ + LSEQ1INT | LSEQ0INT) + +#define DCHDFIFDEBUG (REG_BASE_ADDR + 0x820) +#define ENFAIRMST 0x00FF0000 +#define DISWRMST9 0x00000200 +#define DISWRMST8 0x00000100 +#define DISRDMST 0x000000FF + +#define ATOMICSTATCTL (REG_BASE_ADDR + 0x824) +/* 8 bit wide */ +#define AUTOINC 0x80 +#define ATOMICERR 0x04 +#define ATOMICWIN 0x02 +#define ATOMICDONE 0x01 + + +#define ALTCIOADR (REG_BASE_ADDR + 0x828) +/* 16 bit; bits 8:0 define CIO addr space of CSEQ */ + +#define ASCBPTR (REG_BASE_ADDR + 0x82C) +/* 16 bit wide */ + +#define ADDBPTR (REG_BASE_ADDR + 0x82E) +/* 16 bit wide */ + +#define ANEWDATA (REG_BASE_ADDR + 0x830) +/* 16 bit */ + +#define AOLDDATA (REG_BASE_ADDR + 0x834) +/* 16 bit */ + +#define CTXACCESS (REG_BASE_ADDR + 0x838) +/* 32 bit */ + +/* 0x83Ch - 0xFFCh are reserved */ + +/* + * ARP2 External Processor Registers, Address Range : (0x00-0x1F) + */ +#define ARP2CTL 0x00 + +#define FRCSCRPERR 0x00040000 +#define FRCARP2PERR 0x00020000 +#define FRCARP2ILLOPC 0x00010000 +#define ENWAITTO 0x00008000 +#define PERRORDIS 0x00004000 +#define FAILDIS 0x00002000 +#define CIOPERRDIS 0x00001000 +#define BREAKEN3 0x00000800 +#define BREAKEN2 0x00000400 +#define BREAKEN1 0x00000200 +#define BREAKEN0 0x00000100 +#define EPAUSE 0x00000008 +#define PAUSED 0x00000004 /* ro */ +#define STEP 0x00000002 +#define ARP2RESET 0x00000001 /* wo */ + +#define ARP2INT 0x04 + +#define HALTCODE_MASK 0x00FF0000 /* ro */ +#define ARP2WAITTO 0x00000100 +#define ARP2HALTC 0x00000080 +#define ARP2ILLOPC 0x00000040 +#define ARP2PERR 0x00000020 +#define ARP2CIOPERR 0x00000010 +#define ARP2BREAK3 0x00000008 +#define ARP2BREAK2 0x00000004 +#define ARP2BREAK1 0x00000002 +#define ARP2BREAK0 0x00000001 + +#define ARP2INTEN 0x08 + +#define EN_ARP2WAITTO 0x00000100 +#define EN_ARP2HALTC 0x00000080 +#define EN_ARP2ILLOPC 0x00000040 +#define EN_ARP2PERR 0x00000020 +#define EN_ARP2CIOPERR 0x00000010 +#define EN_ARP2BREAK3 0x00000008 +#define EN_ARP2BREAK2 0x00000004 +#define EN_ARP2BREAK1 0x00000002 +#define EN_ARP2BREAK0 0x00000001 + +#define ARP2BREAKADR01 0x0C + +#define BREAKADR1_MASK 0x0FFF0000 +#define BREAKADR0_MASK 0x00000FFF + +#define ARP2BREAKADR23 0x10 + +#define BREAKADR3_MASK 0x0FFF0000 +#define BREAKADR2_MASK 0x00000FFF + +/* 0x14h - 0x1Ch are reserved */ + +/* + * ARP2 Registers, Address Range : (0x00-0x1F) + * The definitions have the same address offset for CSEQ and LmSEQ + * CIO Bus Registers. + */ +#define MODEPTR 0x00 + +#define DSTMODE 0xF0 +#define SRCMODE 0x0F + +#define ALTMODE 0x01 + +#define ALTDMODE 0xF0 +#define ALTSMODE 0x0F + +#define ATOMICXCHG 0x02 + +#define FLAG 0x04 + +#define INTCODE_MASK 0xF0 +#define ALTMODEV2 0x04 +#define CARRY_INT 0x02 +#define CARRY 0x01 + +#define ARP2INTCTL 0x05 + +#define PAUSEDIS 0x80 +#define RSTINTCTL 0x40 +#define POPALTMODE 0x08 +#define ALTMODEV 0x04 +#define INTMASK 0x02 +#define IRET 0x01 + +#define STACK 0x06 + +#define FUNCTION1 0x07 + +#define PRGMCNT 0x08 + +#define ACCUM 0x0A + +#define SINDEX 0x0C + +#define DINDEX 0x0E + +#define ALLONES 0x10 + +#define ALLZEROS 0x11 + +#define SINDIR 0x12 + +#define DINDIR 0x13 + +#define JUMLDIR 0x14 + +#define ARP2HALTCODE 0x15 + +#define CURRADDR 0x16 + +#define LASTADDR 0x18 + +#define NXTLADDR 0x1A + +#define DBGPORTPTR 0x1C + +#define DBGPORT 0x1D + +/* + * CIO Registers. + * The definitions have the same address offset for CSEQ and LmSEQ + * CIO Bus Registers. + */ +#define MnSCBPTR 0x20 + +#define MnDDBPTR 0x22 + +#define SCRATCHPAGE 0x24 + +#define MnSCRATCHPAGE 0x25 + +#define SCRATCHPAGESV 0x26 + +#define MnSCRATCHPAGESV 0x27 + +#define MnDMAERRS 0x46 + +#define MnSGDMAERRS 0x47 + +#define MnSGBUF 0x53 + +#define MnSGDMASTAT 0x5b + +#define MnDDMACTL 0x5c /* RAZOR.rspec.fm rev 1.5 is wrong */ + +#define MnDDMASTAT 0x5d /* RAZOR.rspec.fm rev 1.5 is wrong */ + +#define MnDDMAMODE 0x5e /* RAZOR.rspec.fm rev 1.5 is wrong */ + +#define MnDMAENG 0x60 + +#define MnPIPECTL 0x61 + +#define MnSGBADR 0x65 + +#define MnSCB_SITE 0x100 + +#define MnDDB_SITE 0x180 + +/* + * The common definitions below have the same address offset for both + * CSEQ and LmSEQ. + */ +#define BISTCTL0 0x4C + +#define BISTCTL1 0x50 + +#define MAPPEDSCR 0x800 + +/* + * CSEQ Host Register, Address Range : (0x000-0xFFC) + */ +#define CSEQ_HOST_REG_BASE_ADR 0xB8001000 + +#define CARP2CTL (CSEQ_HOST_REG_BASE_ADR + ARP2CTL) + +#define CARP2INT (CSEQ_HOST_REG_BASE_ADR + ARP2INT) + +#define CARP2INTEN (CSEQ_HOST_REG_BASE_ADR + ARP2INTEN) + +#define CARP2BREAKADR01 (CSEQ_HOST_REG_BASE_ADR+ARP2BREAKADR01) + +#define CARP2BREAKADR23 (CSEQ_HOST_REG_BASE_ADR+ARP2BREAKADR23) + +#define CBISTCTL (CSEQ_HOST_REG_BASE_ADR + BISTCTL1) + +#define CSEQRAMBISTEN 0x00000040 +#define CSEQRAMBISTDN 0x00000020 /* ro */ +#define CSEQRAMBISTFAIL 0x00000010 /* ro */ +#define CSEQSCRBISTEN 0x00000004 +#define CSEQSCRBISTDN 0x00000002 /* ro */ +#define CSEQSCRBISTFAIL 0x00000001 /* ro */ + +#define CMAPPEDSCR (CSEQ_HOST_REG_BASE_ADR + MAPPEDSCR) + +/* + * CSEQ CIO Bus Registers, Address Range : (0x0000-0x1FFC) + * 16 modes, each mode is 512 bytes. + * Unless specified, the register should valid for all modes. + */ +#define CSEQ_CIO_REG_BASE_ADR REG_BASE_ADDR_CSEQCIO + +#define CSEQm_CIO_REG(Mode, Reg) \ + (CSEQ_CIO_REG_BASE_ADR + \ + ((u32) (Mode) * CSEQ_MODE_PAGE_SIZE) + (u32) (Reg)) + +#define CMODEPTR (CSEQ_CIO_REG_BASE_ADR + MODEPTR) + +#define CALTMODE (CSEQ_CIO_REG_BASE_ADR + ALTMODE) + +#define CATOMICXCHG (CSEQ_CIO_REG_BASE_ADR + ATOMICXCHG) + +#define CFLAG (CSEQ_CIO_REG_BASE_ADR + FLAG) + +#define CARP2INTCTL (CSEQ_CIO_REG_BASE_ADR + ARP2INTCTL) + +#define CSTACK (CSEQ_CIO_REG_BASE_ADR + STACK) + +#define CFUNCTION1 (CSEQ_CIO_REG_BASE_ADR + FUNCTION1) + +#define CPRGMCNT (CSEQ_CIO_REG_BASE_ADR + PRGMCNT) + +#define CACCUM (CSEQ_CIO_REG_BASE_ADR + ACCUM) + +#define CSINDEX (CSEQ_CIO_REG_BASE_ADR + SINDEX) + +#define CDINDEX (CSEQ_CIO_REG_BASE_ADR + DINDEX) + +#define CALLONES (CSEQ_CIO_REG_BASE_ADR + ALLONES) + +#define CALLZEROS (CSEQ_CIO_REG_BASE_ADR + ALLZEROS) + +#define CSINDIR (CSEQ_CIO_REG_BASE_ADR + SINDIR) + +#define CDINDIR (CSEQ_CIO_REG_BASE_ADR + DINDIR) + +#define CJUMLDIR (CSEQ_CIO_REG_BASE_ADR + JUMLDIR) + +#define CARP2HALTCODE (CSEQ_CIO_REG_BASE_ADR + ARP2HALTCODE) + +#define CCURRADDR (CSEQ_CIO_REG_BASE_ADR + CURRADDR) + +#define CLASTADDR (CSEQ_CIO_REG_BASE_ADR + LASTADDR) + +#define CNXTLADDR (CSEQ_CIO_REG_BASE_ADR + NXTLADDR) + +#define CDBGPORTPTR (CSEQ_CIO_REG_BASE_ADR + DBGPORTPTR) + +#define CDBGPORT (CSEQ_CIO_REG_BASE_ADR + DBGPORT) + +#define CSCRATCHPAGE (CSEQ_CIO_REG_BASE_ADR + SCRATCHPAGE) + +#define CMnSCBPTR(Mode) CSEQm_CIO_REG(Mode, MnSCBPTR) + +#define CMnDDBPTR(Mode) CSEQm_CIO_REG(Mode, MnDDBPTR) + +#define CMnSCRATCHPAGE(Mode) CSEQm_CIO_REG(Mode, MnSCRATCHPAGE) + +#define CLINKCON (CSEQ_CIO_REG_BASE_ADR + 0x28) + +#define CCIOAACESS (CSEQ_CIO_REG_BASE_ADR + 0x2C) + +/* mode 0-7 */ +#define MnREQMBX 0x30 +#define CMnREQMBX(Mode) CSEQm_CIO_REG(Mode, 0x30) + +/* mode 8 */ +#define CSEQCON CSEQm_CIO_REG(8, 0x30) + +/* mode 0-7 */ +#define MnRSPMBX 0x34 +#define CMnRSPMBX(Mode) CSEQm_CIO_REG(Mode, 0x34) + +/* mode 8 */ +#define CSEQCOMCTL CSEQm_CIO_REG(8, 0x34) + +/* mode 8 */ +#define CSEQCOMSTAT CSEQm_CIO_REG(8, 0x35) + +/* mode 8 */ +#define CSEQCOMINTEN CSEQm_CIO_REG(8, 0x36) + +/* mode 8 */ +#define CSEQCOMDMACTL CSEQm_CIO_REG(8, 0x37) + +#define CSHALTERR 0x10 +#define RESETCSDMA 0x08 /* wo */ +#define STARTCSDMA 0x04 +#define STOPCSDMA 0x02 /* wo */ +#define CSDMAACT 0x01 /* ro */ + +/* mode 0-7 */ +#define MnINT 0x38 +#define CMnINT(Mode) CSEQm_CIO_REG(Mode, 0x38) + +#define CMnREQMBXE 0x02 +#define CMnRSPMBXF 0x01 +#define CMnINT_MASK 0x00000003 + +/* mode 8 */ +#define CSEQREQMBX CSEQm_CIO_REG(8, 0x38) + +/* mode 0-7 */ +#define MnINTEN 0x3C +#define CMnINTEN(Mode) CSEQm_CIO_REG(Mode, 0x3C) + +#define EN_CMnRSPMBXF 0x01 + +/* mode 8 */ +#define CSEQRSPMBX CSEQm_CIO_REG(8, 0x3C) + +/* mode 8 */ +#define CSDMAADR CSEQm_CIO_REG(8, 0x40) + +/* mode 8 */ +#define CSDMACNT CSEQm_CIO_REG(8, 0x48) + +/* mode 8 */ +#define CSEQDLCTL CSEQm_CIO_REG(8, 0x4D) + +#define DONELISTEND 0x10 +#define DONELISTSIZE_MASK 0x0F +#define DONELISTSIZE_8ELEM 0x01 +#define DONELISTSIZE_16ELEM 0x02 +#define DONELISTSIZE_32ELEM 0x03 +#define DONELISTSIZE_64ELEM 0x04 +#define DONELISTSIZE_128ELEM 0x05 +#define DONELISTSIZE_256ELEM 0x06 +#define DONELISTSIZE_512ELEM 0x07 +#define DONELISTSIZE_1024ELEM 0x08 +#define DONELISTSIZE_2048ELEM 0x09 +#define DONELISTSIZE_4096ELEM 0x0A +#define DONELISTSIZE_8192ELEM 0x0B +#define DONELISTSIZE_16384ELEM 0x0C + +/* mode 8 */ +#define CSEQDLOFFS CSEQm_CIO_REG(8, 0x4E) + +/* mode 11 */ +#define CM11INTVEC0 CSEQm_CIO_REG(11, 0x50) + +/* mode 11 */ +#define CM11INTVEC1 CSEQm_CIO_REG(11, 0x52) + +/* mode 11 */ +#define CM11INTVEC2 CSEQm_CIO_REG(11, 0x54) + +#define CCONMSK (CSEQ_CIO_REG_BASE_ADR + 0x60) + +#define CCONEXIST (CSEQ_CIO_REG_BASE_ADR + 0x61) + +#define CCONMODE (CSEQ_CIO_REG_BASE_ADR + 0x62) + +#define CTIMERCALC (CSEQ_CIO_REG_BASE_ADR + 0x64) + +#define CINTDIS (CSEQ_CIO_REG_BASE_ADR + 0x68) + +/* mode 8, 32x32 bits, 128 bytes of mapped buffer */ +#define CSBUFFER CSEQm_CIO_REG(8, 0x80) + +#define CSCRATCH (CSEQ_CIO_REG_BASE_ADR + 0x1C0) + +/* mode 0-8 */ +#define CMnSCRATCH(Mode) CSEQm_CIO_REG(Mode, 0x1E0) + +/* + * CSEQ Mapped Instruction RAM Page, Address Range : (0x0000-0x1FFC) + */ +#define CSEQ_RAM_REG_BASE_ADR 0xB8004000 + +/* + * The common definitions below have the same address offset for all the Link + * sequencers. + */ +#define MODECTL 0x40 + +#define DBGMODE 0x44 + +#define CONTROL 0x48 +#define LEDTIMER 0x00010000 +#define LEDTIMERS_10us 0x00000000 +#define LEDTIMERS_1ms 0x00000800 +#define LEDTIMERS_100ms 0x00001000 +#define LEDMODE_TXRX 0x00000000 +#define LEDMODE_CONNECTED 0x00000200 +#define LEDPOL 0x00000100 + +#define LSEQRAM 0x1000 + +/* + * LmSEQ Host Registers, Address Range : (0x0000-0x3FFC) + */ +#define LSEQ0_HOST_REG_BASE_ADR 0xB8020000 +#define LSEQ1_HOST_REG_BASE_ADR 0xB8024000 +#define LSEQ2_HOST_REG_BASE_ADR 0xB8028000 +#define LSEQ3_HOST_REG_BASE_ADR 0xB802C000 +#define LSEQ4_HOST_REG_BASE_ADR 0xB8030000 +#define LSEQ5_HOST_REG_BASE_ADR 0xB8034000 +#define LSEQ6_HOST_REG_BASE_ADR 0xB8038000 +#define LSEQ7_HOST_REG_BASE_ADR 0xB803C000 + +#define LmARP2CTL(LinkNum) (LSEQ0_HOST_REG_BASE_ADR + \ + ((LinkNum)*LmSEQ_HOST_REG_SIZE) + \ + ARP2CTL) + +#define LmARP2INT(LinkNum) (LSEQ0_HOST_REG_BASE_ADR + \ + ((LinkNum)*LmSEQ_HOST_REG_SIZE) + \ + ARP2INT) + +#define LmARP2INTEN(LinkNum) (LSEQ0_HOST_REG_BASE_ADR + \ + ((LinkNum)*LmSEQ_HOST_REG_SIZE) + \ + ARP2INTEN) + +#define LmDBGMODE(LinkNum) (LSEQ0_HOST_REG_BASE_ADR + \ + ((LinkNum)*LmSEQ_HOST_REG_SIZE) + \ + DBGMODE) + +#define LmCONTROL(LinkNum) (LSEQ0_HOST_REG_BASE_ADR + \ + ((LinkNum)*LmSEQ_HOST_REG_SIZE) + \ + CONTROL) + +#define LmARP2BREAKADR01(LinkNum) (LSEQ0_HOST_REG_BASE_ADR + \ + ((LinkNum)*LmSEQ_HOST_REG_SIZE) + \ + ARP2BREAKADR01) + +#define LmARP2BREAKADR23(LinkNum) (LSEQ0_HOST_REG_BASE_ADR + \ + ((LinkNum)*LmSEQ_HOST_REG_SIZE) + \ + ARP2BREAKADR23) + +#define LmMODECTL(LinkNum) (LSEQ0_HOST_REG_BASE_ADR + \ + ((LinkNum)*LmSEQ_HOST_REG_SIZE) + \ + MODECTL) + +#define LmAUTODISCI 0x08000000 +#define LmDSBLBITLT 0x04000000 +#define LmDSBLANTT 0x02000000 +#define LmDSBLCRTT 0x01000000 +#define LmDSBLCONT 0x00000100 +#define LmPRIMODE 0x00000080 +#define LmDSBLHOLD 0x00000040 +#define LmDISACK 0x00000020 +#define LmBLIND48 0x00000010 +#define LmRCVMODE_MASK 0x0000000C +#define LmRCVMODE_PLD 0x00000000 +#define LmRCVMODE_HPC 0x00000004 + +#define LmDBGMODE(LinkNum) (LSEQ0_HOST_REG_BASE_ADR + \ + ((LinkNum)*LmSEQ_HOST_REG_SIZE) + \ + DBGMODE) + +#define LmFRCPERR 0x80000000 +#define LmMEMSEL_MASK 0x30000000 +#define LmFRCRBPERR 0x00000000 +#define LmFRCTBPERR 0x10000000 +#define LmFRCSGBPERR 0x20000000 +#define LmFRCARBPERR 0x30000000 +#define LmRCVIDW 0x00080000 +#define LmINVDWERR 0x00040000 +#define LmRCVDISP 0x00004000 +#define LmDISPERR 0x00002000 +#define LmDSBLDSCR 0x00000800 +#define LmDSBLSCR 0x00000400 +#define LmFRCNAK 0x00000200 +#define LmFRCROFS 0x00000100 +#define LmFRCCRC 0x00000080 +#define LmFRMTYPE_MASK 0x00000070 +#define LmSG_DATA 0x00000000 +#define LmSG_COMMAND 0x00000010 +#define LmSG_TASK 0x00000020 +#define LmSG_TGTXFER 0x00000030 +#define LmSG_RESPONSE 0x00000040 +#define LmSG_IDENADDR 0x00000050 +#define LmSG_OPENADDR 0x00000060 +#define LmDISCRCGEN 0x00000008 +#define LmDISCRCCHK 0x00000004 +#define LmSSXMTFRM 0x00000002 +#define LmSSRCVFRM 0x00000001 + +#define LmCONTROL(LinkNum) (LSEQ0_HOST_REG_BASE_ADR + \ + ((LinkNum)*LmSEQ_HOST_REG_SIZE) + \ + CONTROL) + +#define LmSTEPXMTFRM 0x00000002 +#define LmSTEPRCVFRM 0x00000001 + +#define LmBISTCTL0(LinkNum) (LSEQ0_HOST_REG_BASE_ADR + \ + ((LinkNum)*LmSEQ_HOST_REG_SIZE) + \ + BISTCTL0) + +#define ARBBISTEN 0x40000000 +#define ARBBISTDN 0x20000000 /* ro */ +#define ARBBISTFAIL 0x10000000 /* ro */ +#define TBBISTEN 0x00000400 +#define TBBISTDN 0x00000200 /* ro */ +#define TBBISTFAIL 0x00000100 /* ro */ +#define RBBISTEN 0x00000040 +#define RBBISTDN 0x00000020 /* ro */ +#define RBBISTFAIL 0x00000010 /* ro */ +#define SGBISTEN 0x00000004 +#define SGBISTDN 0x00000002 /* ro */ +#define SGBISTFAIL 0x00000001 /* ro */ + +#define LmBISTCTL1(LinkNum) (LSEQ0_HOST_REG_BASE_ADR + \ + ((LinkNum)*LmSEQ_HOST_REG_SIZE) +\ + BISTCTL1) + +#define LmRAMPAGE1 0x00000200 +#define LmRAMPAGE0 0x00000100 +#define LmIMEMBISTEN 0x00000040 +#define LmIMEMBISTDN 0x00000020 /* ro */ +#define LmIMEMBISTFAIL 0x00000010 /* ro */ +#define LmSCRBISTEN 0x00000004 +#define LmSCRBISTDN 0x00000002 /* ro */ +#define LmSCRBISTFAIL 0x00000001 /* ro */ +#define LmRAMPAGE (LmRAMPAGE1 + LmRAMPAGE0) +#define LmRAMPAGE_LSHIFT 0x8 + +#define LmSCRATCH(LinkNum) (LSEQ0_HOST_REG_BASE_ADR + \ + ((LinkNum) * LmSEQ_HOST_REG_SIZE) +\ + MAPPEDSCR) + +#define LmSEQRAM(LinkNum) (LSEQ0_HOST_REG_BASE_ADR + \ + ((LinkNum) * LmSEQ_HOST_REG_SIZE) +\ + LSEQRAM) + +/* + * LmSEQ CIO Bus Register, Address Range : (0x0000-0xFFC) + * 8 modes, each mode is 512 bytes. + * Unless specified, the register should valid for all modes. + */ +#define LmSEQ_CIOBUS_REG_BASE 0x2000 + +#define LmSEQ_PHY_BASE(Mode, LinkNum) \ + (LSEQ0_HOST_REG_BASE_ADR + \ + (LmSEQ_HOST_REG_SIZE * (u32) (LinkNum)) + \ + LmSEQ_CIOBUS_REG_BASE + \ + ((u32) (Mode) * LmSEQ_MODE_PAGE_SIZE)) + +#define LmSEQ_PHY_REG(Mode, LinkNum, Reg) \ + (LmSEQ_PHY_BASE(Mode, LinkNum) + (u32) (Reg)) + +#define LmMODEPTR(LinkNum) LmSEQ_PHY_REG(0, LinkNum, MODEPTR) + +#define LmALTMODE(LinkNum) LmSEQ_PHY_REG(0, LinkNum, ALTMODE) + +#define LmATOMICXCHG(LinkNum) LmSEQ_PHY_REG(0, LinkNum, ATOMICXCHG) + +#define LmFLAG(LinkNum) LmSEQ_PHY_REG(0, LinkNum, FLAG) + +#define LmARP2INTCTL(LinkNum) LmSEQ_PHY_REG(0, LinkNum, ARP2INTCTL) + +#define LmSTACK(LinkNum) LmSEQ_PHY_REG(0, LinkNum, STACK) + +#define LmFUNCTION1(LinkNum) LmSEQ_PHY_REG(0, LinkNum, FUNCTION1) + +#define LmPRGMCNT(LinkNum) LmSEQ_PHY_REG(0, LinkNum, PRGMCNT) + +#define LmACCUM(LinkNum) LmSEQ_PHY_REG(0, LinkNum, ACCUM) + +#define LmSINDEX(LinkNum) LmSEQ_PHY_REG(0, LinkNum, SINDEX) + +#define LmDINDEX(LinkNum) LmSEQ_PHY_REG(0, LinkNum, DINDEX) + +#define LmALLONES(LinkNum) LmSEQ_PHY_REG(0, LinkNum, ALLONES) + +#define LmALLZEROS(LinkNum) LmSEQ_PHY_REG(0, LinkNum, ALLZEROS) + +#define LmSINDIR(LinkNum) LmSEQ_PHY_REG(0, LinkNum, SINDIR) + +#define LmDINDIR(LinkNum) LmSEQ_PHY_REG(0, LinkNum, DINDIR) + +#define LmJUMLDIR(LinkNum) LmSEQ_PHY_REG(0, LinkNum, JUMLDIR) + +#define LmARP2HALTCODE(LinkNum) LmSEQ_PHY_REG(0, LinkNum, ARP2HALTCODE) + +#define LmCURRADDR(LinkNum) LmSEQ_PHY_REG(0, LinkNum, CURRADDR) + +#define LmLASTADDR(LinkNum) LmSEQ_PHY_REG(0, LinkNum, LASTADDR) + +#define LmNXTLADDR(LinkNum) LmSEQ_PHY_REG(0, LinkNum, NXTLADDR) + +#define LmDBGPORTPTR(LinkNum) LmSEQ_PHY_REG(0, LinkNum, DBGPORTPTR) + +#define LmDBGPORT(LinkNum) LmSEQ_PHY_REG(0, LinkNum, DBGPORT) + +#define LmSCRATCHPAGE(LinkNum) LmSEQ_PHY_REG(0, LinkNum, SCRATCHPAGE) + +#define LmMnSCRATCHPAGE(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, \ + MnSCRATCHPAGE) + +#define LmTIMERCALC(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0x28) + +#define LmREQMBX(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0x30) + +#define LmRSPMBX(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0x34) + +#define LmMnINT(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0x38) + +#define CTXMEMSIZE 0x80000000 /* ro */ +#define LmACKREQ 0x08000000 +#define LmNAKREQ 0x04000000 +#define LmMnXMTERR 0x02000000 +#define LmM5OOBSVC 0x01000000 +#define LmHWTINT 0x00800000 +#define LmMnCTXDONE 0x00100000 +#define LmM2REQMBXF 0x00080000 +#define LmM2RSPMBXE 0x00040000 +#define LmMnDMAERR 0x00020000 +#define LmRCVPRIM 0x00010000 +#define LmRCVERR 0x00008000 +#define LmADDRRCV 0x00004000 +#define LmMnHDRMISS 0x00002000 +#define LmMnWAITSCB 0x00001000 +#define LmMnRLSSCB 0x00000800 +#define LmMnSAVECTX 0x00000400 +#define LmMnFETCHSG 0x00000200 +#define LmMnLOADCTX 0x00000100 +#define LmMnCFGICL 0x00000080 +#define LmMnCFGSATA 0x00000040 +#define LmMnCFGEXPSATA 0x00000020 +#define LmMnCFGCMPLT 0x00000010 +#define LmMnCFGRBUF 0x00000008 +#define LmMnSAVETTR 0x00000004 +#define LmMnCFGRDAT 0x00000002 +#define LmMnCFGHDR 0x00000001 + +#define LmMnINTEN(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0x3C) + +#define EN_LmACKREQ 0x08000000 +#define EN_LmNAKREQ 0x04000000 +#define EN_LmMnXMTERR 0x02000000 +#define EN_LmM5OOBSVC 0x01000000 +#define EN_LmHWTINT 0x00800000 +#define EN_LmMnCTXDONE 0x00100000 +#define EN_LmM2REQMBXF 0x00080000 +#define EN_LmM2RSPMBXE 0x00040000 +#define EN_LmMnDMAERR 0x00020000 +#define EN_LmRCVPRIM 0x00010000 +#define EN_LmRCVERR 0x00008000 +#define EN_LmADDRRCV 0x00004000 +#define EN_LmMnHDRMISS 0x00002000 +#define EN_LmMnWAITSCB 0x00001000 +#define EN_LmMnRLSSCB 0x00000800 +#define EN_LmMnSAVECTX 0x00000400 +#define EN_LmMnFETCHSG 0x00000200 +#define EN_LmMnLOADCTX 0x00000100 +#define EN_LmMnCFGICL 0x00000080 +#define EN_LmMnCFGSATA 0x00000040 +#define EN_LmMnCFGEXPSATA 0x00000020 +#define EN_LmMnCFGCMPLT 0x00000010 +#define EN_LmMnCFGRBUF 0x00000008 +#define EN_LmMnSAVETTR 0x00000004 +#define EN_LmMnCFGRDAT 0x00000002 +#define EN_LmMnCFGHDR 0x00000001 + +#define LmM0INTEN_MASK (EN_LmMnCFGCMPLT | EN_LmMnCFGRBUF | \ + EN_LmMnSAVETTR | EN_LmMnCFGRDAT | \ + EN_LmMnCFGHDR | EN_LmRCVERR | \ + EN_LmADDRRCV | EN_LmMnHDRMISS | \ + EN_LmMnRLSSCB | EN_LmMnSAVECTX | \ + EN_LmMnFETCHSG | EN_LmMnLOADCTX | \ + EN_LmHWTINT | EN_LmMnCTXDONE | \ + EN_LmRCVPRIM | EN_LmMnCFGSATA | \ + EN_LmMnCFGEXPSATA | EN_LmMnDMAERR) + +#define LmM1INTEN_MASK (EN_LmMnCFGCMPLT | EN_LmADDRRCV | \ + EN_LmMnRLSSCB | EN_LmMnSAVECTX | \ + EN_LmMnFETCHSG | EN_LmMnLOADCTX | \ + EN_LmMnXMTERR | EN_LmHWTINT | \ + EN_LmMnCTXDONE | EN_LmRCVPRIM | \ + EN_LmRCVERR | EN_LmMnDMAERR) + +#define LmM2INTEN_MASK (EN_LmADDRRCV | EN_LmHWTINT | \ + EN_LmM2REQMBXF | EN_LmRCVPRIM | \ + EN_LmRCVERR) + +#define LmM5INTEN_MASK (EN_LmADDRRCV | EN_LmM5OOBSVC | \ + EN_LmHWTINT | EN_LmRCVPRIM | \ + EN_LmRCVERR) + +#define LmXMTPRIMD(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0x40) + +#define LmXMTPRIMCS(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0x44) + +#define LmCONSTAT(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0x45) + +#define LmMnDMAERRS(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0x46) + +#define LmMnSGDMAERRS(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0x47) + +#define LmM0EXPHDRP(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0x48) + +#define LmM1SASALIGN(LinkNum) LmSEQ_PHY_REG(1, LinkNum, 0x48) +#define SAS_ALIGN_DEFAULT 0xFF + +#define LmM0MSKHDRP(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0x49) + +#define LmM1STPALIGN(LinkNum) LmSEQ_PHY_REG(1, LinkNum, 0x49) +#define STP_ALIGN_DEFAULT 0x1F + +#define LmM0RCVHDRP(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0x4A) + +#define LmM1XMTHDRP(LinkNum) LmSEQ_PHY_REG(1, LinkNum, 0x4A) + +#define LmM0ICLADR(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0x4B) + +#define LmM1ALIGNMODE(LinkNum) LmSEQ_PHY_REG(1, LinkNum, 0x4B) + +#define LmDISALIGN 0x20 +#define LmROTSTPALIGN 0x10 +#define LmSTPALIGN 0x08 +#define LmROTNOTIFY 0x04 +#define LmDUALALIGN 0x02 +#define LmROTALIGN 0x01 + +#define LmM0EXPRCVNT(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0x4C) + +#define LmM1XMTCNT(LinkNum) LmSEQ_PHY_REG(1, LinkNum, 0x4C) + +#define LmMnBUFSTAT(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0x4E) + +#define LmMnBUFPERR 0x01 + +/* mode 0-1 */ +#define LmMnXFRLVL(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0x59) + +#define LmMnXFRLVL_128 0x05 +#define LmMnXFRLVL_256 0x04 +#define LmMnXFRLVL_512 0x03 +#define LmMnXFRLVL_1024 0x02 +#define LmMnXFRLVL_1536 0x01 +#define LmMnXFRLVL_2048 0x00 + + /* mode 0-1 */ +#define LmMnSGDMACTL(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0x5A) + +#define LmMnRESETSG 0x04 +#define LmMnSTOPSG 0x02 +#define LmMnSTARTSG 0x01 + +/* mode 0-1 */ +#define LmMnSGDMASTAT(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0x5B) + +/* mode 0-1 */ +#define LmMnDDMACTL(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0x5C) + +#define LmMnFLUSH 0x40 /* wo */ +#define LmMnRLSRTRY 0x20 /* wo */ +#define LmMnDISCARD 0x10 /* wo */ +#define LmMnRESETDAT 0x08 /* wo */ +#define LmMnSUSDAT 0x04 /* wo */ +#define LmMnSTOPDAT 0x02 /* wo */ +#define LmMnSTARTDAT 0x01 /* wo */ + +/* mode 0-1 */ +#define LmMnDDMASTAT(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0x5D) + +#define LmMnDPEMPTY 0x80 +#define LmMnFLUSHING 0x40 +#define LmMnDDMAREQ 0x20 +#define LmMnHDMAREQ 0x10 +#define LmMnDATFREE 0x08 +#define LmMnDATSUS 0x04 +#define LmMnDATACT 0x02 +#define LmMnDATEN 0x01 + +/* mode 0-1 */ +#define LmMnDDMAMODE(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0x5E) + +#define LmMnDMATYPE_NORMAL 0x0000 +#define LmMnDMATYPE_HOST_ONLY_TX 0x0001 +#define LmMnDMATYPE_DEVICE_ONLY_TX 0x0002 +#define LmMnDMATYPE_INVALID 0x0003 +#define LmMnDMATYPE_MASK 0x0003 + +#define LmMnDMAWRAP 0x0004 +#define LmMnBITBUCKET 0x0008 +#define LmMnDISHDR 0x0010 +#define LmMnSTPCRC 0x0020 +#define LmXTEST 0x0040 +#define LmMnDISCRC 0x0080 +#define LmMnENINTLK 0x0100 +#define LmMnADDRFRM 0x0400 +#define LmMnENXMTCRC 0x0800 + +/* mode 0-1 */ +#define LmMnXFRCNT(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0x70) + +/* mode 0-1 */ +#define LmMnDPSEL(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0x7B) +#define LmMnDPSEL_MASK 0x07 +#define LmMnEOLPRE 0x40 +#define LmMnEOSPRE 0x80 + +/* Registers used in conjunction with LmMnDPSEL and LmMnDPACC registers */ +/* Receive Mode n = 0 */ +#define LmMnHRADDR 0x00 +#define LmMnHBYTECNT 0x01 +#define LmMnHREWIND 0x02 +#define LmMnDWADDR 0x03 +#define LmMnDSPACECNT 0x04 +#define LmMnDFRMSIZE 0x05 + +/* Registers used in conjunction with LmMnDPSEL and LmMnDPACC registers */ +/* Transmit Mode n = 1 */ +#define LmMnHWADDR 0x00 +#define LmMnHSPACECNT 0x01 +/* #define LmMnHREWIND 0x02 */ +#define LmMnDRADDR 0x03 +#define LmMnDBYTECNT 0x04 +/* #define LmMnDFRMSIZE 0x05 */ + +/* mode 0-1 */ +#define LmMnDPACC(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0x78) +#define LmMnDPACC_MASK 0x00FFFFFF + +/* mode 0-1 */ +#define LmMnHOLDLVL(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0x7D) + +#define LmPRMSTAT0(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0x80) +#define LmPRMSTAT0BYTE0 0x80 +#define LmPRMSTAT0BYTE1 0x81 +#define LmPRMSTAT0BYTE2 0x82 +#define LmPRMSTAT0BYTE3 0x83 + +#define LmFRAMERCVD 0x80000000 +#define LmXFRRDYRCVD 0x40000000 +#define LmUNKNOWNP 0x20000000 +#define LmBREAK 0x10000000 +#define LmDONE 0x08000000 +#define LmOPENACPT 0x04000000 +#define LmOPENRJCT 0x02000000 +#define LmOPENRTRY 0x01000000 +#define LmCLOSERV1 0x00800000 +#define LmCLOSERV0 0x00400000 +#define LmCLOSENORM 0x00200000 +#define LmCLOSECLAF 0x00100000 +#define LmNOTIFYRV2 0x00080000 +#define LmNOTIFYRV1 0x00040000 +#define LmNOTIFYRV0 0x00020000 +#define LmNOTIFYSPIN 0x00010000 +#define LmBROADRV4 0x00008000 +#define LmBROADRV3 0x00004000 +#define LmBROADRV2 0x00002000 +#define LmBROADRV1 0x00001000 +#define LmBROADSES 0x00000800 +#define LmBROADRVCH1 0x00000400 +#define LmBROADRVCH0 0x00000200 +#define LmBROADCH 0x00000100 +#define LmAIPRVWP 0x00000080 +#define LmAIPWP 0x00000040 +#define LmAIPWD 0x00000020 +#define LmAIPWC 0x00000010 +#define LmAIPRV2 0x00000008 +#define LmAIPRV1 0x00000004 +#define LmAIPRV0 0x00000002 +#define LmAIPNRML 0x00000001 + +#define LmBROADCAST_MASK (LmBROADCH | LmBROADRVCH0 | \ + LmBROADRVCH1) + +#define LmPRMSTAT1(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0x84) +#define LmPRMSTAT1BYTE0 0x84 +#define LmPRMSTAT1BYTE1 0x85 +#define LmPRMSTAT1BYTE2 0x86 +#define LmPRMSTAT1BYTE3 0x87 + +#define LmFRMRCVDSTAT 0x80000000 +#define LmBREAK_DET 0x04000000 +#define LmCLOSE_DET 0x02000000 +#define LmDONE_DET 0x01000000 +#define LmXRDY 0x00040000 +#define LmSYNCSRST 0x00020000 +#define LmSYNC 0x00010000 +#define LmXHOLD 0x00008000 +#define LmRRDY 0x00004000 +#define LmHOLD 0x00002000 +#define LmROK 0x00001000 +#define LmRIP 0x00000800 +#define LmCRBLK 0x00000400 +#define LmACK 0x00000200 +#define LmNAK 0x00000100 +#define LmHARDRST 0x00000080 +#define LmERROR 0x00000040 +#define LmRERR 0x00000020 +#define LmPMREQP 0x00000010 +#define LmPMREQS 0x00000008 +#define LmPMACK 0x00000004 +#define LmPMNAK 0x00000002 +#define LmDMAT 0x00000001 + +/* mode 1 */ +#define LmMnSATAFS(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0x7E) +#define LmMnXMTSIZE(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0x93) + +/* mode 0 */ +#define LmMnFRMERR(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0xB0) + +#define LmACRCERR 0x00000800 +#define LmPHYOVRN 0x00000400 +#define LmOBOVRN 0x00000200 +#define LmMnZERODATA 0x00000100 +#define LmSATAINTLK 0x00000080 +#define LmMnCRCERR 0x00000020 +#define LmRRDYOVRN 0x00000010 +#define LmMISSSOAF 0x00000008 +#define LmMISSSOF 0x00000004 +#define LmMISSEOAF 0x00000002 +#define LmMISSEOF 0x00000001 + +#define LmFRMERREN(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0xB4) + +#define EN_LmACRCERR 0x00000800 +#define EN_LmPHYOVRN 0x00000400 +#define EN_LmOBOVRN 0x00000200 +#define EN_LmMnZERODATA 0x00000100 +#define EN_LmSATAINTLK 0x00000080 +#define EN_LmFRMBAD 0x00000040 +#define EN_LmMnCRCERR 0x00000020 +#define EN_LmRRDYOVRN 0x00000010 +#define EN_LmMISSSOAF 0x00000008 +#define EN_LmMISSSOF 0x00000004 +#define EN_LmMISSEOAF 0x00000002 +#define EN_LmMISSEOF 0x00000001 + +#define LmFRMERREN_MASK (EN_LmSATAINTLK | EN_LmMnCRCERR | \ + EN_LmRRDYOVRN | EN_LmMISSSOF | \ + EN_LmMISSEOAF | EN_LmMISSEOF | \ + EN_LmACRCERR | LmPHYOVRN | \ + EN_LmOBOVRN | EN_LmMnZERODATA) + +#define LmHWTSTATEN(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0xC5) + +#define EN_LmDONETO 0x80 +#define EN_LmINVDISP 0x40 +#define EN_LmINVDW 0x20 +#define EN_LmDWSEVENT 0x08 +#define EN_LmCRTTTO 0x04 +#define EN_LmANTTTO 0x02 +#define EN_LmBITLTTO 0x01 + +#define LmHWTSTATEN_MASK (EN_LmINVDISP | EN_LmINVDW | \ + EN_LmDWSEVENT | EN_LmCRTTTO | \ + EN_LmANTTTO | EN_LmDONETO | \ + EN_LmBITLTTO) + +#define LmHWTSTAT(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0xC7) + +#define LmDONETO 0x80 +#define LmINVDISP 0x40 +#define LmINVDW 0x20 +#define LmDWSEVENT 0x08 +#define LmCRTTTO 0x04 +#define LmANTTTO 0x02 +#define LmBITLTTO 0x01 + +#define LmMnDATABUFADR(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0xC8) +#define LmDATABUFADR_MASK 0x0FFF + +#define LmMnDATABUF(LinkNum, Mode) LmSEQ_PHY_REG(Mode, LinkNum, 0xCA) + +#define LmPRIMSTAT0EN(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0xE0) + +#define EN_LmUNKNOWNP 0x20000000 +#define EN_LmBREAK 0x10000000 +#define EN_LmDONE 0x08000000 +#define EN_LmOPENACPT 0x04000000 +#define EN_LmOPENRJCT 0x02000000 +#define EN_LmOPENRTRY 0x01000000 +#define EN_LmCLOSERV1 0x00800000 +#define EN_LmCLOSERV0 0x00400000 +#define EN_LmCLOSENORM 0x00200000 +#define EN_LmCLOSECLAF 0x00100000 +#define EN_LmNOTIFYRV2 0x00080000 +#define EN_LmNOTIFYRV1 0x00040000 +#define EN_LmNOTIFYRV0 0x00020000 +#define EN_LmNOTIFYSPIN 0x00010000 +#define EN_LmBROADRV4 0x00008000 +#define EN_LmBROADRV3 0x00004000 +#define EN_LmBROADRV2 0x00002000 +#define EN_LmBROADRV1 0x00001000 +#define EN_LmBROADRV0 0x00000800 +#define EN_LmBROADRVCH1 0x00000400 +#define EN_LmBROADRVCH0 0x00000200 +#define EN_LmBROADCH 0x00000100 +#define EN_LmAIPRVWP 0x00000080 +#define EN_LmAIPWP 0x00000040 +#define EN_LmAIPWD 0x00000020 +#define EN_LmAIPWC 0x00000010 +#define EN_LmAIPRV2 0x00000008 +#define EN_LmAIPRV1 0x00000004 +#define EN_LmAIPRV0 0x00000002 +#define EN_LmAIPNRML 0x00000001 + +#define LmPRIMSTAT0EN_MASK (EN_LmBREAK | \ + EN_LmDONE | EN_LmOPENACPT | \ + EN_LmOPENRJCT | EN_LmOPENRTRY | \ + EN_LmCLOSERV1 | EN_LmCLOSERV0 | \ + EN_LmCLOSENORM | EN_LmCLOSECLAF | \ + EN_LmBROADRV4 | EN_LmBROADRV3 | \ + EN_LmBROADRV2 | EN_LmBROADRV1 | \ + EN_LmBROADRV0 | EN_LmBROADRVCH1 | \ + EN_LmBROADRVCH0 | EN_LmBROADCH | \ + EN_LmAIPRVWP | EN_LmAIPWP | \ + EN_LmAIPWD | EN_LmAIPWC | \ + EN_LmAIPRV2 | EN_LmAIPRV1 | \ + EN_LmAIPRV0 | EN_LmAIPNRML) + +#define LmPRIMSTAT1EN(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0xE4) + +#define EN_LmXRDY 0x00040000 +#define EN_LmSYNCSRST 0x00020000 +#define EN_LmSYNC 0x00010000 +#define EN_LmXHOLD 0x00008000 +#define EN_LmRRDY 0x00004000 +#define EN_LmHOLD 0x00002000 +#define EN_LmROK 0x00001000 +#define EN_LmRIP 0x00000800 +#define EN_LmCRBLK 0x00000400 +#define EN_LmACK 0x00000200 +#define EN_LmNAK 0x00000100 +#define EN_LmHARDRST 0x00000080 +#define EN_LmERROR 0x00000040 +#define EN_LmRERR 0x00000020 +#define EN_LmPMREQP 0x00000010 +#define EN_LmPMREQS 0x00000008 +#define EN_LmPMACK 0x00000004 +#define EN_LmPMNAK 0x00000002 +#define EN_LmDMAT 0x00000001 + +#define LmPRIMSTAT1EN_MASK (EN_LmHARDRST | \ + EN_LmSYNCSRST | \ + EN_LmPMREQP | EN_LmPMREQS | \ + EN_LmPMACK | EN_LmPMNAK) + +#define LmSMSTATE(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0xE8) + +#define LmSMSTATEBRK(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0xEC) + +#define LmSMDBGCTL(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0xF0) + + +/* + * LmSEQ CIO Bus Mode 3 Register. + * Mode 3: Configuration and Setup, IOP Context SCB. + */ +#define LmM3SATATIMER(LinkNum) LmSEQ_PHY_REG(3, LinkNum, 0x48) + +#define LmM3INTVEC0(LinkNum) LmSEQ_PHY_REG(3, LinkNum, 0x90) + +#define LmM3INTVEC1(LinkNum) LmSEQ_PHY_REG(3, LinkNum, 0x92) + +#define LmM3INTVEC2(LinkNum) LmSEQ_PHY_REG(3, LinkNum, 0x94) + +#define LmM3INTVEC3(LinkNum) LmSEQ_PHY_REG(3, LinkNum, 0x96) + +#define LmM3INTVEC4(LinkNum) LmSEQ_PHY_REG(3, LinkNum, 0x98) + +#define LmM3INTVEC5(LinkNum) LmSEQ_PHY_REG(3, LinkNum, 0x9A) + +#define LmM3INTVEC6(LinkNum) LmSEQ_PHY_REG(3, LinkNum, 0x9C) + +#define LmM3INTVEC7(LinkNum) LmSEQ_PHY_REG(3, LinkNum, 0x9E) + +#define LmM3INTVEC8(LinkNum) LmSEQ_PHY_REG(3, LinkNum, 0xA4) + +#define LmM3INTVEC9(LinkNum) LmSEQ_PHY_REG(3, LinkNum, 0xA6) + +#define LmM3INTVEC10(LinkNum) LmSEQ_PHY_REG(3, LinkNum, 0xB0) + +#define LmM3FRMGAP(LinkNum) LmSEQ_PHY_REG(3, LinkNum, 0xB4) + +#define LmBITL_TIMER(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0xA2) + +#define LmWWN(LinkNum) LmSEQ_PHY_REG(0, LinkNum, 0xA8) + + +/* + * LmSEQ CIO Bus Mode 5 Registers. + * Mode 5: Phy/OOB Control and Status. + */ +#define LmSEQ_OOB_REG(phy_id, reg) LmSEQ_PHY_REG(5, (phy_id), (reg)) + +#define OOB_BFLTR 0x100 + +#define BFLTR_THR_MASK 0xF0 +#define BFLTR_TC_MASK 0x0F + +#define OOB_INIT_MIN 0x102 + +#define OOB_INIT_MAX 0x104 + +#define OOB_INIT_NEG 0x106 + +#define OOB_SAS_MIN 0x108 + +#define OOB_SAS_MAX 0x10A + +#define OOB_SAS_NEG 0x10C + +#define OOB_WAKE_MIN 0x10E + +#define OOB_WAKE_MAX 0x110 + +#define OOB_WAKE_NEG 0x112 + +#define OOB_IDLE_MAX 0x114 + +#define OOB_BURST_MAX 0x116 + +#define OOB_DATA_KBITS 0x126 + +#define OOB_ALIGN_0_DATA 0x12C + +#define OOB_ALIGN_1_DATA 0x130 + +#define D10_2_DATA_k 0x00 +#define SYNC_DATA_k 0x02 +#define ALIGN_1_DATA_k 0x04 +#define ALIGN_0_DATA_k 0x08 +#define BURST_DATA_k 0x10 + +#define OOB_PHY_RESET_COUNT 0x13C + +#define OOB_SIG_GEN 0x140 + +#define START_OOB 0x80 +#define START_DWS 0x40 +#define ALIGN_CNT3 0x30 +#define ALIGN_CNT2 0x20 +#define ALIGN_CNT1 0x10 +#define ALIGN_CNT4 0x00 +#define STOP_DWS 0x08 +#define SEND_COMSAS 0x04 +#define SEND_COMINIT 0x02 +#define SEND_COMWAKE 0x01 + +#define OOB_XMIT 0x141 + +#define TX_ENABLE 0x80 +#define XMIT_OOB_BURST 0x10 +#define XMIT_D10_2 0x08 +#define XMIT_SYNC 0x04 +#define XMIT_ALIGN_1 0x02 +#define XMIT_ALIGN_0 0x01 + +#define FUNCTION_MASK 0x142 + +#define SAS_MODE_DIS 0x80 +#define SATA_MODE_DIS 0x40 +#define SPINUP_HOLD_DIS 0x20 +#define HOT_PLUG_DIS 0x10 +#define SATA_PS_DIS 0x08 +#define FUNCTION_MASK_DEFAULT (SPINUP_HOLD_DIS | SATA_PS_DIS) + +#define OOB_MODE 0x143 + +#define SAS_MODE 0x80 +#define SATA_MODE 0x40 +#define SLOW_CLK 0x20 +#define FORCE_XMIT_15 0x08 +#define PHY_SPEED_60 0x04 +#define PHY_SPEED_30 0x02 +#define PHY_SPEED_15 0x01 + +#define CURRENT_STATUS 0x144 + +#define CURRENT_OOB_DONE 0x80 +#define CURRENT_LOSS_OF_SIGNAL 0x40 +#define CURRENT_SPINUP_HOLD 0x20 +#define CURRENT_HOT_PLUG_CNCT 0x10 +#define CURRENT_GTO_TIMEOUT 0x08 +#define CURRENT_OOB_TIMEOUT 0x04 +#define CURRENT_DEVICE_PRESENT 0x02 +#define CURRENT_OOB_ERROR 0x01 + +#define CURRENT_OOB1_ERROR (CURRENT_HOT_PLUG_CNCT | \ + CURRENT_GTO_TIMEOUT) + +#define CURRENT_OOB2_ERROR (CURRENT_HOT_PLUG_CNCT | \ + CURRENT_OOB_ERROR) + +#define DEVICE_ADDED_W_CNT (CURRENT_OOB_DONE | \ + CURRENT_HOT_PLUG_CNCT | \ + CURRENT_DEVICE_PRESENT) + +#define DEVICE_ADDED_WO_CNT (CURRENT_OOB_DONE | \ + CURRENT_DEVICE_PRESENT) + +#define DEVICE_REMOVED CURRENT_LOSS_OF_SIGNAL + +#define CURRENT_PHY_MASK (CURRENT_OOB_DONE | \ + CURRENT_LOSS_OF_SIGNAL | \ + CURRENT_SPINUP_HOLD | \ + CURRENT_HOT_PLUG_CNCT | \ + CURRENT_GTO_TIMEOUT | \ + CURRENT_DEVICE_PRESENT | \ + CURRENT_OOB_ERROR ) + +#define CURRENT_ERR_MASK (CURRENT_LOSS_OF_SIGNAL | \ + CURRENT_GTO_TIMEOUT | \ + CURRENT_OOB_TIMEOUT | \ + CURRENT_OOB_ERROR ) + +#define SPEED_MASK 0x145 + +#define SATA_SPEED_30_DIS 0x10 +#define SATA_SPEED_15_DIS 0x08 +#define SAS_SPEED_60_DIS 0x04 +#define SAS_SPEED_30_DIS 0x02 +#define SAS_SPEED_15_DIS 0x01 +#define SAS_SPEED_MASK_DEFAULT 0x00 + +#define OOB_TIMER_ENABLE 0x14D + +#define HOT_PLUG_EN 0x80 +#define RCD_EN 0x40 +#define COMTIMER_EN 0x20 +#define SNTT_EN 0x10 +#define SNLT_EN 0x04 +#define SNWT_EN 0x02 +#define ALIGN_EN 0x01 + +#define OOB_STATUS 0x14E + +#define OOB_DONE 0x80 +#define LOSS_OF_SIGNAL 0x40 /* ro */ +#define SPINUP_HOLD 0x20 +#define HOT_PLUG_CNCT 0x10 /* ro */ +#define GTO_TIMEOUT 0x08 /* ro */ +#define OOB_TIMEOUT 0x04 /* ro */ +#define DEVICE_PRESENT 0x02 /* ro */ +#define OOB_ERROR 0x01 /* ro */ + +#define OOB_STATUS_ERROR_MASK (LOSS_OF_SIGNAL | GTO_TIMEOUT | \ + OOB_TIMEOUT | OOB_ERROR) + +#define OOB_STATUS_CLEAR 0x14F + +#define OOB_DONE_CLR 0x80 +#define LOSS_OF_SIGNAL_CLR 0x40 +#define SPINUP_HOLD_CLR 0x20 +#define HOT_PLUG_CNCT_CLR 0x10 +#define GTO_TIMEOUT_CLR 0x08 +#define OOB_TIMEOUT_CLR 0x04 +#define OOB_ERROR_CLR 0x01 + +#define HOT_PLUG_DELAY 0x150 +/* In 5 ms units. 20 = 100 ms. */ +#define HOTPLUG_DELAY_TIMEOUT 20 + + +#define INT_ENABLE_2 0x15A + +#define OOB_DONE_EN 0x80 +#define LOSS_OF_SIGNAL_EN 0x40 +#define SPINUP_HOLD_EN 0x20 +#define HOT_PLUG_CNCT_EN 0x10 +#define GTO_TIMEOUT_EN 0x08 +#define OOB_TIMEOUT_EN 0x04 +#define DEVICE_PRESENT_EN 0x02 +#define OOB_ERROR_EN 0x01 + +#define PHY_CONTROL_0 0x160 + +#define PHY_LOWPWREN_TX 0x80 +#define PHY_LOWPWREN_RX 0x40 +#define SPARE_REG_160_B5 0x20 +#define OFFSET_CANCEL_RX 0x10 + +/* bits 3:2 */ +#define PHY_RXCOMCENTER_60V 0x00 +#define PHY_RXCOMCENTER_70V 0x04 +#define PHY_RXCOMCENTER_80V 0x08 +#define PHY_RXCOMCENTER_90V 0x0C +#define PHY_RXCOMCENTER_MASK 0x0C + +#define PHY_RESET 0x02 +#define SAS_DEFAULT_SEL 0x01 + +#define PHY_CONTROL_1 0x161 + +/* bits 2:0 */ +#define SATA_PHY_DETLEVEL_50mv 0x00 +#define SATA_PHY_DETLEVEL_75mv 0x01 +#define SATA_PHY_DETLEVEL_100mv 0x02 +#define SATA_PHY_DETLEVEL_125mv 0x03 +#define SATA_PHY_DETLEVEL_150mv 0x04 +#define SATA_PHY_DETLEVEL_175mv 0x05 +#define SATA_PHY_DETLEVEL_200mv 0x06 +#define SATA_PHY_DETLEVEL_225mv 0x07 +#define SATA_PHY_DETLEVEL_MASK 0x07 + +/* bits 5:3 */ +#define SAS_PHY_DETLEVEL_50mv 0x00 +#define SAS_PHY_DETLEVEL_75mv 0x08 +#define SAS_PHY_DETLEVEL_100mv 0x10 +#define SAS_PHY_DETLEVEL_125mv 0x11 +#define SAS_PHY_DETLEVEL_150mv 0x20 +#define SAS_PHY_DETLEVEL_175mv 0x21 +#define SAS_PHY_DETLEVEL_200mv 0x30 +#define SAS_PHY_DETLEVEL_225mv 0x31 +#define SAS_PHY_DETLEVEL_MASK 0x38 + +#define PHY_CONTROL_2 0x162 + +/* bits 7:5 */ +#define SATA_PHY_DRV_400mv 0x00 +#define SATA_PHY_DRV_450mv 0x20 +#define SATA_PHY_DRV_500mv 0x40 +#define SATA_PHY_DRV_550mv 0x60 +#define SATA_PHY_DRV_600mv 0x80 +#define SATA_PHY_DRV_650mv 0xA0 +#define SATA_PHY_DRV_725mv 0xC0 +#define SATA_PHY_DRV_800mv 0xE0 +#define SATA_PHY_DRV_MASK 0xE0 + +/* bits 4:3 */ +#define SATA_PREEMP_0 0x00 +#define SATA_PREEMP_1 0x08 +#define SATA_PREEMP_2 0x10 +#define SATA_PREEMP_3 0x18 +#define SATA_PREEMP_MASK 0x18 + +#define SATA_CMSH1P5 0x04 + +/* bits 1:0 */ +#define SATA_SLEW_0 0x00 +#define SATA_SLEW_1 0x01 +#define SATA_SLEW_2 0x02 +#define SATA_SLEW_3 0x03 +#define SATA_SLEW_MASK 0x03 + +#define PHY_CONTROL_3 0x163 + +/* bits 7:5 */ +#define SAS_PHY_DRV_400mv 0x00 +#define SAS_PHY_DRV_450mv 0x20 +#define SAS_PHY_DRV_500mv 0x40 +#define SAS_PHY_DRV_550mv 0x60 +#define SAS_PHY_DRV_600mv 0x80 +#define SAS_PHY_DRV_650mv 0xA0 +#define SAS_PHY_DRV_725mv 0xC0 +#define SAS_PHY_DRV_800mv 0xE0 +#define SAS_PHY_DRV_MASK 0xE0 + +/* bits 4:3 */ +#define SAS_PREEMP_0 0x00 +#define SAS_PREEMP_1 0x08 +#define SAS_PREEMP_2 0x10 +#define SAS_PREEMP_3 0x18 +#define SAS_PREEMP_MASK 0x18 + +#define SAS_CMSH1P5 0x04 + +/* bits 1:0 */ +#define SAS_SLEW_0 0x00 +#define SAS_SLEW_1 0x01 +#define SAS_SLEW_2 0x02 +#define SAS_SLEW_3 0x03 +#define SAS_SLEW_MASK 0x03 + +#define PHY_CONTROL_4 0x168 + +#define PHY_DONE_CAL_TX 0x80 +#define PHY_DONE_CAL_RX 0x40 +#define RX_TERM_LOAD_DIS 0x20 +#define TX_TERM_LOAD_DIS 0x10 +#define AUTO_TERM_CAL_DIS 0x08 +#define PHY_SIGDET_FLTR_EN 0x04 +#define OSC_FREQ 0x02 +#define PHY_START_CAL 0x01 + +/* + * HST_PCIX2 Registers, Addresss Range: (0x00-0xFC) + */ +#define PCIX_REG_BASE_ADR 0xB8040000 + +#define PCIC_VENDOR_ID 0x00 + +#define PCIC_DEVICE_ID 0x02 + +#define PCIC_COMMAND 0x04 + +#define INT_DIS 0x0400 +#define FBB_EN 0x0200 /* ro */ +#define SERR_EN 0x0100 +#define STEP_EN 0x0080 /* ro */ +#define PERR_EN 0x0040 +#define VGA_EN 0x0020 /* ro */ +#define MWI_EN 0x0010 +#define SPC_EN 0x0008 +#define MST_EN 0x0004 +#define MEM_EN 0x0002 +#define IO_EN 0x0001 + +#define PCIC_STATUS 0x06 + +#define PERR_DET 0x8000 +#define SERR_GEN 0x4000 +#define MABT_DET 0x2000 +#define TABT_DET 0x1000 +#define TABT_GEN 0x0800 +#define DPERR_DET 0x0100 +#define CAP_LIST 0x0010 +#define INT_STAT 0x0008 + +#define PCIC_DEVREV_ID 0x08 + +#define PCIC_CLASS_CODE 0x09 + +#define PCIC_CACHELINE_SIZE 0x0C + +#define PCIC_MBAR0 0x10 + +#define PCIC_MBAR0_OFFSET 0 + +#define PCIC_MBAR1 0x18 + +#define PCIC_MBAR1_OFFSET 2 + +#define PCIC_IOBAR 0x20 + +#define PCIC_IOBAR_OFFSET 4 + +#define PCIC_SUBVENDOR_ID 0x2C + +#define PCIC_SUBSYTEM_ID 0x2E + +#define PCIX_STATUS 0x44 +#define RCV_SCE 0x20000000 +#define UNEXP_SC 0x00080000 +#define SC_DISCARD 0x00040000 + +#define ECC_CTRL_STAT 0x48 +#define UNCOR_ECCERR 0x00000008 + +#define PCIC_PM_CSR 0x5C + +#define PWR_STATE_D0 0 +#define PWR_STATE_D1 1 /* not supported */ +#define PWR_STATE_D2 2 /* not supported */ +#define PWR_STATE_D3 3 + +#define PCIC_BASE1 0x6C /* internal use only */ + +#define BASE1_RSVD 0xFFFFFFF8 + +#define PCIC_BASEA 0x70 /* internal use only */ + +#define BASEA_RSVD 0xFFFFFFC0 +#define BASEA_START 0 + +#define PCIC_BASEB 0x74 /* internal use only */ + +#define BASEB_RSVD 0xFFFFFF80 +#define BASEB_IOMAP_MASK 0x7F +#define BASEB_START 0x80 + +#define PCIC_BASEC 0x78 /* internal use only */ + +#define BASEC_RSVD 0xFFFFFFFC +#define BASEC_MASK 0x03 +#define BASEC_START 0x58 + +#define PCIC_MBAR_KEY 0x7C /* internal use only */ + +#define MBAR_KEY_MASK 0xFFFFFFFF + +#define PCIC_HSTPCIX_CNTRL 0xA0 + +#define REWIND_DIS 0x0800 +#define SC_TMR_DIS 0x04000000 + +#define PCIC_MBAR0_MASK 0xA8 +#define PCIC_MBAR0_SIZE_MASK 0x1FFFE000 +#define PCIC_MBAR0_SIZE_SHIFT 13 +#define PCIC_MBAR0_SIZE(val) \ + (((val) & PCIC_MBAR0_SIZE_MASK) >> PCIC_MBAR0_SIZE_SHIFT) + +#define PCIC_FLASH_MBAR 0xB8 + +#define PCIC_INTRPT_STAT 0xD4 + +#define PCIC_TP_CTRL 0xFC + +/* + * EXSI Registers, Addresss Range: (0x00-0xFC) + */ +#define EXSI_REG_BASE_ADR REG_BASE_ADDR_EXSI + +#define EXSICNFGR (EXSI_REG_BASE_ADR + 0x00) + +#define OCMINITIALIZED 0x80000000 +#define ASIEN 0x00400000 +#define HCMODE 0x00200000 +#define PCIDEF 0x00100000 +#define COMSTOCK 0x00080000 +#define SEEPROMEND 0x00040000 +#define MSTTIMEN 0x00020000 +#define XREGEX 0x00000200 +#define NVRAMW 0x00000100 +#define NVRAMEX 0x00000080 +#define SRAMW 0x00000040 +#define SRAMEX 0x00000020 +#define FLASHW 0x00000010 +#define FLASHEX 0x00000008 +#define SEEPROMCFG 0x00000004 +#define SEEPROMTYP 0x00000002 +#define SEEPROMEX 0x00000001 + + +#define EXSICNTRLR (EXSI_REG_BASE_ADR + 0x04) + +#define MODINT_EN 0x00000001 + + +#define PMSTATR (EXSI_REG_BASE_ADR + 0x10) + +#define FLASHRST 0x00000002 +#define FLASHRDY 0x00000001 + + +#define FLCNFGR (EXSI_REG_BASE_ADR + 0x14) + +#define FLWEH_MASK 0x30000000 +#define FLWESU_MASK 0x0C000000 +#define FLWEPW_MASK 0x03F00000 +#define FLOEH_MASK 0x000C0000 +#define FLOESU_MASK 0x00030000 +#define FLOEPW_MASK 0x0000FC00 +#define FLCSH_MASK 0x00000300 +#define FLCSSU_MASK 0x000000C0 +#define FLCSPW_MASK 0x0000003F + +#define SRCNFGR (EXSI_REG_BASE_ADR + 0x18) + +#define SRWEH_MASK 0x30000000 +#define SRWESU_MASK 0x0C000000 +#define SRWEPW_MASK 0x03F00000 + +#define SROEH_MASK 0x000C0000 +#define SROESU_MASK 0x00030000 +#define SROEPW_MASK 0x0000FC00 +#define SRCSH_MASK 0x00000300 +#define SRCSSU_MASK 0x000000C0 +#define SRCSPW_MASK 0x0000003F + +#define NVCNFGR (EXSI_REG_BASE_ADR + 0x1C) + +#define NVWEH_MASK 0x30000000 +#define NVWESU_MASK 0x0C000000 +#define NVWEPW_MASK 0x03F00000 +#define NVOEH_MASK 0x000C0000 +#define NVOESU_MASK 0x00030000 +#define NVOEPW_MASK 0x0000FC00 +#define NVCSH_MASK 0x00000300 +#define NVCSSU_MASK 0x000000C0 +#define NVCSPW_MASK 0x0000003F + +#define XRCNFGR (EXSI_REG_BASE_ADR + 0x20) + +#define XRWEH_MASK 0x30000000 +#define XRWESU_MASK 0x0C000000 +#define XRWEPW_MASK 0x03F00000 +#define XROEH_MASK 0x000C0000 +#define XROESU_MASK 0x00030000 +#define XROEPW_MASK 0x0000FC00 +#define XRCSH_MASK 0x00000300 +#define XRCSSU_MASK 0x000000C0 +#define XRCSPW_MASK 0x0000003F + +#define XREGADDR (EXSI_REG_BASE_ADR + 0x24) + +#define XRADDRINCEN 0x80000000 +#define XREGADD_MASK 0x007FFFFF + + +#define XREGDATAR (EXSI_REG_BASE_ADR + 0x28) + +#define XREGDATA_MASK 0x0000FFFF + +#define GPIOOER (EXSI_REG_BASE_ADR + 0x40) + +#define GPIOODENR (EXSI_REG_BASE_ADR + 0x44) + +#define GPIOINVR (EXSI_REG_BASE_ADR + 0x48) + +#define GPIODATAOR (EXSI_REG_BASE_ADR + 0x4C) + +#define GPIODATAIR (EXSI_REG_BASE_ADR + 0x50) + +#define GPIOCNFGR (EXSI_REG_BASE_ADR + 0x54) + +#define GPIO_EXTSRC 0x00000001 + +#define SCNTRLR (EXSI_REG_BASE_ADR + 0xA0) + +#define SXFERDONE 0x00000100 +#define SXFERCNT_MASK 0x000000E0 +#define SCMDTYP_MASK 0x0000001C +#define SXFERSTART 0x00000002 +#define SXFEREN 0x00000001 + +#define SRATER (EXSI_REG_BASE_ADR + 0xA4) + +#define SADDRR (EXSI_REG_BASE_ADR + 0xA8) + +#define SADDR_MASK 0x0000FFFF + +#define SDATAOR (EXSI_REG_BASE_ADR + 0xAC) + +#define SDATAOR0 (EXSI_REG_BASE_ADR + 0xAC) +#define SDATAOR1 (EXSI_REG_BASE_ADR + 0xAD) +#define SDATAOR2 (EXSI_REG_BASE_ADR + 0xAE) +#define SDATAOR3 (EXSI_REG_BASE_ADR + 0xAF) + +#define SDATAIR (EXSI_REG_BASE_ADR + 0xB0) + +#define SDATAIR0 (EXSI_REG_BASE_ADR + 0xB0) +#define SDATAIR1 (EXSI_REG_BASE_ADR + 0xB1) +#define SDATAIR2 (EXSI_REG_BASE_ADR + 0xB2) +#define SDATAIR3 (EXSI_REG_BASE_ADR + 0xB3) + +#define ASISTAT0R (EXSI_REG_BASE_ADR + 0xD0) +#define ASIFMTERR 0x00000400 +#define ASISEECHKERR 0x00000200 +#define ASIERR 0x00000100 + +#define ASISTAT1R (EXSI_REG_BASE_ADR + 0xD4) +#define CHECKSUM_MASK 0x0000FFFF + +#define ASIERRADDR (EXSI_REG_BASE_ADR + 0xD8) +#define ASIERRDATAR (EXSI_REG_BASE_ADR + 0xDC) +#define ASIERRSTATR (EXSI_REG_BASE_ADR + 0xE0) +#define CPI2ASIBYTECNT_MASK 0x00070000 +#define CPI2ASIBYTEEN_MASK 0x0000F000 +#define CPI2ASITARGERR_MASK 0x00000F00 +#define CPI2ASITARGMID_MASK 0x000000F0 +#define CPI2ASIMSTERR_MASK 0x0000000F + +/* + * XSRAM, External SRAM (DWord and any BE pattern accessible) + */ +#define XSRAM_REG_BASE_ADDR 0xB8100000 +#define XSRAM_SIZE 0x100000 + +/* + * NVRAM Registers, Address Range: (0x00000 - 0x3FFFF). + */ +#define NVRAM_REG_BASE_ADR 0xBF800000 +#define NVRAM_MAX_BASE_ADR 0x003FFFFF + +/* OCM base address */ +#define OCM_BASE_ADDR 0xA0000000 +#define OCM_MAX_SIZE 0x20000 + +/* + * Sequencers (Central and Link) Scratch RAM page definitions. + */ + +/* + * The Central Management Sequencer (CSEQ) Scratch Memory is a 1024 + * byte memory. It is dword accessible and has byte parity + * protection. The CSEQ accesses it in 32 byte windows, either as mode + * dependent or mode independent memory. Each mode has 96 bytes, + * (three 32 byte pages 0-2, not contiguous), leaving 128 bytes of + * Mode Independent memory (four 32 byte pages 3-7). Note that mode + * dependent scratch memory, Mode 8, page 0-3 overlaps mode + * independent scratch memory, pages 0-3. + * - 896 bytes of mode dependent scratch, 96 bytes per Modes 0-7, and + * 128 bytes in mode 8, + * - 259 bytes of mode independent scratch, common to modes 0-15. + * + * Sequencer scratch RAM is 1024 bytes. This scratch memory is + * divided into mode dependent and mode independent scratch with this + * memory further subdivided into pages of size 32 bytes. There are 5 + * pages (160 bytes) of mode independent scratch and 3 pages of + * dependent scratch memory for modes 0-7 (768 bytes). Mode 8 pages + * 0-2 dependent scratch overlap with pages 0-2 of mode independent + * scratch memory. + * + * The host accesses this scratch in a different manner from the + * central sequencer. The sequencer has to use CSEQ registers CSCRPAGE + * and CMnSCRPAGE to access the scratch memory. A flat mapping of the + * scratch memory is avaliable for software convenience and to prevent + * corruption while the sequencer is running. This memory is mapped + * onto addresses 800h - BFFh, total of 400h bytes. + * + * These addresses are mapped as follows: + * + * 800h-83Fh Mode Dependent Scratch Mode 0 Pages 0-1 + * 840h-87Fh Mode Dependent Scratch Mode 1 Pages 0-1 + * 880h-8BFh Mode Dependent Scratch Mode 2 Pages 0-1 + * 8C0h-8FFh Mode Dependent Scratch Mode 3 Pages 0-1 + * 900h-93Fh Mode Dependent Scratch Mode 4 Pages 0-1 + * 940h-97Fh Mode Dependent Scratch Mode 5 Pages 0-1 + * 980h-9BFh Mode Dependent Scratch Mode 6 Pages 0-1 + * 9C0h-9FFh Mode Dependent Scratch Mode 7 Pages 0-1 + * A00h-A5Fh Mode Dependent Scratch Mode 8 Pages 0-2 + * Mode Independent Scratch Pages 0-2 + * A60h-A7Fh Mode Dependent Scratch Mode 8 Page 3 + * Mode Independent Scratch Page 3 + * A80h-AFFh Mode Independent Scratch Pages 4-7 + * B00h-B1Fh Mode Dependent Scratch Mode 0 Page 2 + * B20h-B3Fh Mode Dependent Scratch Mode 1 Page 2 + * B40h-B5Fh Mode Dependent Scratch Mode 2 Page 2 + * B60h-B7Fh Mode Dependent Scratch Mode 3 Page 2 + * B80h-B9Fh Mode Dependent Scratch Mode 4 Page 2 + * BA0h-BBFh Mode Dependent Scratch Mode 5 Page 2 + * BC0h-BDFh Mode Dependent Scratch Mode 6 Page 2 + * BE0h-BFFh Mode Dependent Scratch Mode 7 Page 2 + */ + +/* General macros */ +#define CSEQ_PAGE_SIZE 32 /* Scratch page size (in bytes) */ + +/* All macros start with offsets from base + 0x800 (CMAPPEDSCR). + * Mode dependent scratch page 0, mode 0. + * For modes 1-7 you have to do arithmetic. */ +#define CSEQ_LRM_SAVE_SINDEX (CMAPPEDSCR + 0x0000) +#define CSEQ_LRM_SAVE_SCBPTR (CMAPPEDSCR + 0x0002) +#define CSEQ_Q_LINK_HEAD (CMAPPEDSCR + 0x0004) +#define CSEQ_Q_LINK_TAIL (CMAPPEDSCR + 0x0006) +#define CSEQ_LRM_SAVE_SCRPAGE (CMAPPEDSCR + 0x0008) + +/* Mode dependent scratch page 0 mode 8 macros. */ +#define CSEQ_RET_ADDR (CMAPPEDSCR + 0x0200) +#define CSEQ_RET_SCBPTR (CMAPPEDSCR + 0x0202) +#define CSEQ_SAVE_SCBPTR (CMAPPEDSCR + 0x0204) +#define CSEQ_EMPTY_TRANS_CTX (CMAPPEDSCR + 0x0206) +#define CSEQ_RESP_LEN (CMAPPEDSCR + 0x0208) +#define CSEQ_TMF_SCBPTR (CMAPPEDSCR + 0x020A) +#define CSEQ_GLOBAL_PREV_SCB (CMAPPEDSCR + 0x020C) +#define CSEQ_GLOBAL_HEAD (CMAPPEDSCR + 0x020E) +#define CSEQ_CLEAR_LU_HEAD (CMAPPEDSCR + 0x0210) +#define CSEQ_TMF_OPCODE (CMAPPEDSCR + 0x0212) +#define CSEQ_SCRATCH_FLAGS (CMAPPEDSCR + 0x0213) +#define CSEQ_HSB_SITE (CMAPPEDSCR + 0x021A) +#define CSEQ_FIRST_INV_SCB_SITE (CMAPPEDSCR + 0x021C) +#define CSEQ_FIRST_INV_DDB_SITE (CMAPPEDSCR + 0x021E) + +/* Mode dependent scratch page 1 mode 8 macros. */ +#define CSEQ_LUN_TO_CLEAR (CMAPPEDSCR + 0x0220) +#define CSEQ_LUN_TO_CHECK (CMAPPEDSCR + 0x0228) + +/* Mode dependent scratch page 2 mode 8 macros */ +#define CSEQ_HQ_NEW_POINTER (CMAPPEDSCR + 0x0240) +#define CSEQ_HQ_DONE_BASE (CMAPPEDSCR + 0x0248) +#define CSEQ_HQ_DONE_POINTER (CMAPPEDSCR + 0x0250) +#define CSEQ_HQ_DONE_PASS (CMAPPEDSCR + 0x0254) + +/* Mode independent scratch page 4 macros. */ +#define CSEQ_Q_EXE_HEAD (CMAPPEDSCR + 0x0280) +#define CSEQ_Q_EXE_TAIL (CMAPPEDSCR + 0x0282) +#define CSEQ_Q_DONE_HEAD (CMAPPEDSCR + 0x0284) +#define CSEQ_Q_DONE_TAIL (CMAPPEDSCR + 0x0286) +#define CSEQ_Q_SEND_HEAD (CMAPPEDSCR + 0x0288) +#define CSEQ_Q_SEND_TAIL (CMAPPEDSCR + 0x028A) +#define CSEQ_Q_DMA2CHIM_HEAD (CMAPPEDSCR + 0x028C) +#define CSEQ_Q_DMA2CHIM_TAIL (CMAPPEDSCR + 0x028E) +#define CSEQ_Q_COPY_HEAD (CMAPPEDSCR + 0x0290) +#define CSEQ_Q_COPY_TAIL (CMAPPEDSCR + 0x0292) +#define CSEQ_REG0 (CMAPPEDSCR + 0x0294) +#define CSEQ_REG1 (CMAPPEDSCR + 0x0296) +#define CSEQ_REG2 (CMAPPEDSCR + 0x0298) +#define CSEQ_LINK_CTL_Q_MAP (CMAPPEDSCR + 0x029C) +#define CSEQ_MAX_CSEQ_MODE (CMAPPEDSCR + 0x029D) +#define CSEQ_FREE_LIST_HACK_COUNT (CMAPPEDSCR + 0x029E) + +/* Mode independent scratch page 5 macros. */ +#define CSEQ_EST_NEXUS_REQ_QUEUE (CMAPPEDSCR + 0x02A0) +#define CSEQ_EST_NEXUS_REQ_COUNT (CMAPPEDSCR + 0x02A8) +#define CSEQ_Q_EST_NEXUS_HEAD (CMAPPEDSCR + 0x02B0) +#define CSEQ_Q_EST_NEXUS_TAIL (CMAPPEDSCR + 0x02B2) +#define CSEQ_NEED_EST_NEXUS_SCB (CMAPPEDSCR + 0x02B4) +#define CSEQ_EST_NEXUS_REQ_HEAD (CMAPPEDSCR + 0x02B6) +#define CSEQ_EST_NEXUS_REQ_TAIL (CMAPPEDSCR + 0x02B7) +#define CSEQ_EST_NEXUS_SCB_OFFSET (CMAPPEDSCR + 0x02B8) + +/* Mode independent scratch page 6 macros. */ +#define CSEQ_INT_ROUT_RET_ADDR0 (CMAPPEDSCR + 0x02C0) +#define CSEQ_INT_ROUT_RET_ADDR1 (CMAPPEDSCR + 0x02C2) +#define CSEQ_INT_ROUT_SCBPTR (CMAPPEDSCR + 0x02C4) +#define CSEQ_INT_ROUT_MODE (CMAPPEDSCR + 0x02C6) +#define CSEQ_ISR_SCRATCH_FLAGS (CMAPPEDSCR + 0x02C7) +#define CSEQ_ISR_SAVE_SINDEX (CMAPPEDSCR + 0x02C8) +#define CSEQ_ISR_SAVE_DINDEX (CMAPPEDSCR + 0x02CA) +#define CSEQ_Q_MONIRTT_HEAD (CMAPPEDSCR + 0x02D0) +#define CSEQ_Q_MONIRTT_TAIL (CMAPPEDSCR + 0x02D2) +#define CSEQ_FREE_SCB_MASK (CMAPPEDSCR + 0x02D5) +#define CSEQ_BUILTIN_FREE_SCB_HEAD (CMAPPEDSCR + 0x02D6) +#define CSEQ_BUILTIN_FREE_SCB_TAIL (CMAPPEDSCR + 0x02D8) +#define CSEQ_EXTENDED_FREE_SCB_HEAD (CMAPPEDSCR + 0x02DA) +#define CSEQ_EXTENDED_FREE_SCB_TAIL (CMAPPEDSCR + 0x02DC) + +/* Mode independent scratch page 7 macros. */ +#define CSEQ_EMPTY_REQ_QUEUE (CMAPPEDSCR + 0x02E0) +#define CSEQ_EMPTY_REQ_COUNT (CMAPPEDSCR + 0x02E8) +#define CSEQ_Q_EMPTY_HEAD (CMAPPEDSCR + 0x02F0) +#define CSEQ_Q_EMPTY_TAIL (CMAPPEDSCR + 0x02F2) +#define CSEQ_NEED_EMPTY_SCB (CMAPPEDSCR + 0x02F4) +#define CSEQ_EMPTY_REQ_HEAD (CMAPPEDSCR + 0x02F6) +#define CSEQ_EMPTY_REQ_TAIL (CMAPPEDSCR + 0x02F7) +#define CSEQ_EMPTY_SCB_OFFSET (CMAPPEDSCR + 0x02F8) +#define CSEQ_PRIMITIVE_DATA (CMAPPEDSCR + 0x02FA) +#define CSEQ_TIMEOUT_CONST (CMAPPEDSCR + 0x02FC) + +/*************************************************************************** +* Link m Sequencer scratch RAM is 512 bytes. +* This scratch memory is divided into mode dependent and mode +* independent scratch with this memory further subdivided into +* pages of size 32 bytes. There are 4 pages (128 bytes) of +* mode independent scratch and 4 pages of dependent scratch +* memory for modes 0-2 (384 bytes). +* +* The host accesses this scratch in a different manner from the +* link sequencer. The sequencer has to use LSEQ registers +* LmSCRPAGE and LmMnSCRPAGE to access the scratch memory. A flat +* mapping of the scratch memory is avaliable for software +* convenience and to prevent corruption while the sequencer is +* running. This memory is mapped onto addresses 800h - 9FFh. +* +* These addresses are mapped as follows: +* +* 800h-85Fh Mode Dependent Scratch Mode 0 Pages 0-2 +* 860h-87Fh Mode Dependent Scratch Mode 0 Page 3 +* Mode Dependent Scratch Mode 5 Page 0 +* 880h-8DFh Mode Dependent Scratch Mode 1 Pages 0-2 +* 8E0h-8FFh Mode Dependent Scratch Mode 1 Page 3 +* Mode Dependent Scratch Mode 5 Page 1 +* 900h-95Fh Mode Dependent Scratch Mode 2 Pages 0-2 +* 960h-97Fh Mode Dependent Scratch Mode 2 Page 3 +* Mode Dependent Scratch Mode 5 Page 2 +* 980h-9DFh Mode Independent Scratch Pages 0-3 +* 9E0h-9FFh Mode Independent Scratch Page 3 +* Mode Dependent Scratch Mode 5 Page 3 +* +****************************************************************************/ +/* General macros */ +#define LSEQ_MODE_SCRATCH_SIZE 0x80 /* Size of scratch RAM per mode */ +#define LSEQ_PAGE_SIZE 0x20 /* Scratch page size (in bytes) */ +#define LSEQ_MODE5_PAGE0_OFFSET 0x60 + +/* Common mode dependent scratch page 0 macros for modes 0,1,2, and 5 */ +/* Indexed using LSEQ_MODE_SCRATCH_SIZE * mode, for modes 0,1,2. */ +#define LmSEQ_RET_ADDR(LinkNum) (LmSCRATCH(LinkNum) + 0x0000) +#define LmSEQ_REG0_MODE(LinkNum) (LmSCRATCH(LinkNum) + 0x0002) +#define LmSEQ_MODE_FLAGS(LinkNum) (LmSCRATCH(LinkNum) + 0x0004) + +/* Mode flag macros (byte 0) */ +#define SAS_SAVECTX_OCCURRED 0x80 +#define SAS_OOBSVC_OCCURRED 0x40 +#define SAS_OOB_DEVICE_PRESENT 0x20 +#define SAS_CFGHDR_OCCURRED 0x10 +#define SAS_RCV_INTS_ARE_DISABLED 0x08 +#define SAS_OOB_HOT_PLUG_CNCT 0x04 +#define SAS_AWAIT_OPEN_CONNECTION 0x02 +#define SAS_CFGCMPLT_OCCURRED 0x01 + +/* Mode flag macros (byte 1) */ +#define SAS_RLSSCB_OCCURRED 0x80 +#define SAS_FORCED_HEADER_MISS 0x40 + +#define LmSEQ_RET_ADDR2(LinkNum) (LmSCRATCH(LinkNum) + 0x0006) +#define LmSEQ_RET_ADDR1(LinkNum) (LmSCRATCH(LinkNum) + 0x0008) +#define LmSEQ_OPCODE_TO_CSEQ(LinkNum) (LmSCRATCH(LinkNum) + 0x000B) +#define LmSEQ_DATA_TO_CSEQ(LinkNum) (LmSCRATCH(LinkNum) + 0x000C) + +/* Mode dependent scratch page 0 macros for mode 0 (non-common) */ +/* Absolute offsets */ +#define LmSEQ_FIRST_INV_DDB_SITE(LinkNum) (LmSCRATCH(LinkNum) + 0x000E) +#define LmSEQ_EMPTY_TRANS_CTX(LinkNum) (LmSCRATCH(LinkNum) + 0x0010) +#define LmSEQ_RESP_LEN(LinkNum) (LmSCRATCH(LinkNum) + 0x0012) +#define LmSEQ_FIRST_INV_SCB_SITE(LinkNum) (LmSCRATCH(LinkNum) + 0x0014) +#define LmSEQ_INTEN_SAVE(LinkNum) (LmSCRATCH(LinkNum) + 0x0016) +#define LmSEQ_LINK_RST_FRM_LEN(LinkNum) (LmSCRATCH(LinkNum) + 0x001A) +#define LmSEQ_LINK_RST_PROTOCOL(LinkNum) (LmSCRATCH(LinkNum) + 0x001B) +#define LmSEQ_RESP_STATUS(LinkNum) (LmSCRATCH(LinkNum) + 0x001C) +#define LmSEQ_LAST_LOADED_SGE(LinkNum) (LmSCRATCH(LinkNum) + 0x001D) +#define LmSEQ_SAVE_SCBPTR(LinkNum) (LmSCRATCH(LinkNum) + 0x001E) + +/* Mode dependent scratch page 0 macros for mode 1 (non-common) */ +/* Absolute offsets */ +#define LmSEQ_Q_XMIT_HEAD(LinkNum) (LmSCRATCH(LinkNum) + 0x008E) +#define LmSEQ_M1_EMPTY_TRANS_CTX(LinkNum) (LmSCRATCH(LinkNum) + 0x0090) +#define LmSEQ_INI_CONN_TAG(LinkNum) (LmSCRATCH(LinkNum) + 0x0092) +#define LmSEQ_FAILED_OPEN_STATUS(LinkNum) (LmSCRATCH(LinkNum) + 0x009A) +#define LmSEQ_XMIT_REQUEST_TYPE(LinkNum) (LmSCRATCH(LinkNum) + 0x009B) +#define LmSEQ_M1_RESP_STATUS(LinkNum) (LmSCRATCH(LinkNum) + 0x009C) +#define LmSEQ_M1_LAST_LOADED_SGE(LinkNum) (LmSCRATCH(LinkNum) + 0x009D) +#define LmSEQ_M1_SAVE_SCBPTR(LinkNum) (LmSCRATCH(LinkNum) + 0x009E) + +/* Mode dependent scratch page 0 macros for mode 2 (non-common) */ +#define LmSEQ_PORT_COUNTER(LinkNum) (LmSCRATCH(LinkNum) + 0x010E) +#define LmSEQ_PM_TABLE_PTR(LinkNum) (LmSCRATCH(LinkNum) + 0x0110) +#define LmSEQ_SATA_INTERLOCK_TMR_SAVE(LinkNum) (LmSCRATCH(LinkNum) + 0x0112) +#define LmSEQ_IP_BITL(LinkNum) (LmSCRATCH(LinkNum) + 0x0114) +#define LmSEQ_COPY_SMP_CONN_TAG(LinkNum) (LmSCRATCH(LinkNum) + 0x0116) +#define LmSEQ_P0M2_OFFS1AH(LinkNum) (LmSCRATCH(LinkNum) + 0x011A) + +/* Mode dependent scratch page 0 macros for modes 4/5 (non-common) */ +/* Absolute offsets */ +#define LmSEQ_SAVED_OOB_STATUS(LinkNum) (LmSCRATCH(LinkNum) + 0x006E) +#define LmSEQ_SAVED_OOB_MODE(LinkNum) (LmSCRATCH(LinkNum) + 0x006F) +#define LmSEQ_Q_LINK_HEAD(LinkNum) (LmSCRATCH(LinkNum) + 0x0070) +#define LmSEQ_LINK_RST_ERR(LinkNum) (LmSCRATCH(LinkNum) + 0x0072) +#define LmSEQ_SAVED_OOB_SIGNALS(LinkNum) (LmSCRATCH(LinkNum) + 0x0073) +#define LmSEQ_SAS_RESET_MODE(LinkNum) (LmSCRATCH(LinkNum) + 0x0074) +#define LmSEQ_LINK_RESET_RETRY_COUNT(LinkNum) (LmSCRATCH(LinkNum) + 0x0075) +#define LmSEQ_NUM_LINK_RESET_RETRIES(LinkNum) (LmSCRATCH(LinkNum) + 0x0076) +#define LmSEQ_OOB_INT_ENABLES(LinkNum) (LmSCRATCH(LinkNum) + 0x007A) +#define LmSEQ_NOTIFY_TIMER_TIMEOUT(LinkNum) (LmSCRATCH(LinkNum) + 0x007C) +#define LmSEQ_NOTIFY_TIMER_DOWN_COUNT(LinkNum) (LmSCRATCH(LinkNum) + 0x007E) + +/* Mode dependent scratch page 1, mode 0 and mode 1 */ +#define LmSEQ_SG_LIST_PTR_ADDR0(LinkNum) (LmSCRATCH(LinkNum) + 0x0020) +#define LmSEQ_SG_LIST_PTR_ADDR1(LinkNum) (LmSCRATCH(LinkNum) + 0x0030) +#define LmSEQ_M1_SG_LIST_PTR_ADDR0(LinkNum) (LmSCRATCH(LinkNum) + 0x00A0) +#define LmSEQ_M1_SG_LIST_PTR_ADDR1(LinkNum) (LmSCRATCH(LinkNum) + 0x00B0) + +/* Mode dependent scratch page 1 macros for mode 2 */ +/* Absolute offsets */ +#define LmSEQ_INVALID_DWORD_COUNT(LinkNum) (LmSCRATCH(LinkNum) + 0x0120) +#define LmSEQ_DISPARITY_ERROR_COUNT(LinkNum) (LmSCRATCH(LinkNum) + 0x0124) +#define LmSEQ_LOSS_OF_SYNC_COUNT(LinkNum) (LmSCRATCH(LinkNum) + 0x0128) + +/* Mode dependent scratch page 1 macros for mode 4/5 */ +#define LmSEQ_FRAME_TYPE_MASK(LinkNum) (LmSCRATCH(LinkNum) + 0x00E0) +#define LmSEQ_HASHED_DEST_ADDR_MASK(LinkNum) (LmSCRATCH(LinkNum) + 0x00E1) +#define LmSEQ_HASHED_SRC_ADDR_MASK_PRINT(LinkNum) (LmSCRATCH(LinkNum) + 0x00E4) +#define LmSEQ_HASHED_SRC_ADDR_MASK(LinkNum) (LmSCRATCH(LinkNum) + 0x00E5) +#define LmSEQ_NUM_FILL_BYTES_MASK(LinkNum) (LmSCRATCH(LinkNum) + 0x00EB) +#define LmSEQ_TAG_MASK(LinkNum) (LmSCRATCH(LinkNum) + 0x00F0) +#define LmSEQ_TARGET_PORT_XFER_TAG(LinkNum) (LmSCRATCH(LinkNum) + 0x00F2) +#define LmSEQ_DATA_OFFSET(LinkNum) (LmSCRATCH(LinkNum) + 0x00F4) + +/* Mode dependent scratch page 2 macros for mode 0 */ +/* Absolute offsets */ +#define LmSEQ_SMP_RCV_TIMER_TERM_TS(LinkNum) (LmSCRATCH(LinkNum) + 0x0040) +#define LmSEQ_DEVICE_BITS(LinkNum) (LmSCRATCH(LinkNum) + 0x005B) +#define LmSEQ_SDB_DDB(LinkNum) (LmSCRATCH(LinkNum) + 0x005C) +#define LmSEQ_SDB_NUM_TAGS(LinkNum) (LmSCRATCH(LinkNum) + 0x005E) +#define LmSEQ_SDB_CURR_TAG(LinkNum) (LmSCRATCH(LinkNum) + 0x005F) + +/* Mode dependent scratch page 2 macros for mode 1 */ +/* Absolute offsets */ +/* byte 0 bits 1-0 are domain select. */ +#define LmSEQ_TX_ID_ADDR_FRAME(LinkNum) (LmSCRATCH(LinkNum) + 0x00C0) +#define LmSEQ_OPEN_TIMER_TERM_TS(LinkNum) (LmSCRATCH(LinkNum) + 0x00C8) +#define LmSEQ_SRST_AS_TIMER_TERM_TS(LinkNum) (LmSCRATCH(LinkNum) + 0x00CC) +#define LmSEQ_LAST_LOADED_SG_EL(LinkNum) (LmSCRATCH(LinkNum) + 0x00D4) + +/* Mode dependent scratch page 2 macros for mode 2 */ +/* Absolute offsets */ +#define LmSEQ_STP_SHUTDOWN_TIMER_TERM_TS(LinkNum) (LmSCRATCH(LinkNum) + 0x0140) +#define LmSEQ_CLOSE_TIMER_TERM_TS(LinkNum) (LmSCRATCH(LinkNum) + 0x0144) +#define LmSEQ_BREAK_TIMER_TERM_TS(LinkNum) (LmSCRATCH(LinkNum) + 0x0148) +#define LmSEQ_DWS_RESET_TIMER_TERM_TS(LinkNum) (LmSCRATCH(LinkNum) + 0x014C) +#define LmSEQ_SATA_INTERLOCK_TIMER_TERM_TS(LinkNum) \ + (LmSCRATCH(LinkNum) + 0x0150) +#define LmSEQ_MCTL_TIMER_TERM_TS(LinkNum) (LmSCRATCH(LinkNum) + 0x0154) + +/* Mode dependent scratch page 2 macros for mode 5 */ +#define LmSEQ_COMINIT_TIMER_TERM_TS(LinkNum) (LmSCRATCH(LinkNum) + 0x0160) +#define LmSEQ_RCV_ID_TIMER_TERM_TS(LinkNum) (LmSCRATCH(LinkNum) + 0x0164) +#define LmSEQ_RCV_FIS_TIMER_TERM_TS(LinkNum) (LmSCRATCH(LinkNum) + 0x0168) +#define LmSEQ_DEV_PRES_TIMER_TERM_TS(LinkNum) (LmSCRATCH(LinkNum) + 0x016C) + +/* Mode dependent scratch page 3 macros for modes 0 and 1 */ +/* None defined */ + +/* Mode dependent scratch page 3 macros for modes 2 and 5 */ +/* None defined */ + +/* Mode Independent Scratch page 0 macros. */ +#define LmSEQ_Q_TGTXFR_HEAD(LinkNum) (LmSCRATCH(LinkNum) + 0x0180) +#define LmSEQ_Q_TGTXFR_TAIL(LinkNum) (LmSCRATCH(LinkNum) + 0x0182) +#define LmSEQ_LINK_NUMBER(LinkNum) (LmSCRATCH(LinkNum) + 0x0186) +#define LmSEQ_SCRATCH_FLAGS(LinkNum) (LmSCRATCH(LinkNum) + 0x0187) +/* + * Currently only bit 0, SAS_DWSAQD, is used. + */ +#define SAS_DWSAQD 0x01 /* + * DWSSTATUS: DWSAQD + * bit las read in ISR. + */ +#define LmSEQ_CONNECTION_STATE(LinkNum) (LmSCRATCH(LinkNum) + 0x0188) +/* Connection states (byte 0) */ +#define SAS_WE_OPENED_CS 0x01 +#define SAS_DEVICE_OPENED_CS 0x02 +#define SAS_WE_SENT_DONE_CS 0x04 +#define SAS_DEVICE_SENT_DONE_CS 0x08 +#define SAS_WE_SENT_CLOSE_CS 0x10 +#define SAS_DEVICE_SENT_CLOSE_CS 0x20 +#define SAS_WE_SENT_BREAK_CS 0x40 +#define SAS_DEVICE_SENT_BREAK_CS 0x80 +/* Connection states (byte 1) */ +#define SAS_OPN_TIMEOUT_OR_OPN_RJCT_CS 0x01 +#define SAS_AIP_RECEIVED_CS 0x02 +#define SAS_CREDIT_TIMEOUT_OCCURRED_CS 0x04 +#define SAS_ACKNAK_TIMEOUT_OCCURRED_CS 0x08 +#define SAS_SMPRSP_TIMEOUT_OCCURRED_CS 0x10 +#define SAS_DONE_TIMEOUT_OCCURRED_CS 0x20 +/* Connection states (byte 2) */ +#define SAS_SMP_RESPONSE_RECEIVED_CS 0x01 +#define SAS_INTLK_TIMEOUT_OCCURRED_CS 0x02 +#define SAS_DEVICE_SENT_DMAT_CS 0x04 +#define SAS_DEVICE_SENT_SYNCSRST_CS 0x08 +#define SAS_CLEARING_AFFILIATION_CS 0x20 +#define SAS_RXTASK_ACTIVE_CS 0x40 +#define SAS_TXTASK_ACTIVE_CS 0x80 +/* Connection states (byte 3) */ +#define SAS_PHY_LOSS_OF_SIGNAL_CS 0x01 +#define SAS_DWS_TIMER_EXPIRED_CS 0x02 +#define SAS_LINK_RESET_NOT_COMPLETE_CS 0x04 +#define SAS_PHY_DISABLED_CS 0x08 +#define SAS_LINK_CTL_TASK_ACTIVE_CS 0x10 +#define SAS_PHY_EVENT_TASK_ACTIVE_CS 0x20 +#define SAS_DEVICE_SENT_ID_FRAME_CS 0x40 +#define SAS_DEVICE_SENT_REG_FIS_CS 0x40 +#define SAS_DEVICE_SENT_HARD_RESET_CS 0x80 +#define SAS_PHY_IS_DOWN_FLAGS (SAS_PHY_LOSS_OF_SIGNAL_CS|\ + SAS_DWS_TIMER_EXPIRED_CS |\ + SAS_LINK_RESET_NOT_COMPLETE_CS|\ + SAS_PHY_DISABLED_CS) + +#define SAS_LINK_CTL_PHY_EVENT_FLAGS (SAS_LINK_CTL_TASK_ACTIVE_CS |\ + SAS_PHY_EVENT_TASK_ACTIVE_CS |\ + SAS_DEVICE_SENT_ID_FRAME_CS |\ + SAS_DEVICE_SENT_HARD_RESET_CS) + +#define LmSEQ_CONCTL(LinkNum) (LmSCRATCH(LinkNum) + 0x018C) +#define LmSEQ_CONSTAT(LinkNum) (LmSCRATCH(LinkNum) + 0x018E) +#define LmSEQ_CONNECTION_MODES(LinkNum) (LmSCRATCH(LinkNum) + 0x018F) +#define LmSEQ_REG1_ISR(LinkNum) (LmSCRATCH(LinkNum) + 0x0192) +#define LmSEQ_REG2_ISR(LinkNum) (LmSCRATCH(LinkNum) + 0x0194) +#define LmSEQ_REG3_ISR(LinkNum) (LmSCRATCH(LinkNum) + 0x0196) +#define LmSEQ_REG0_ISR(LinkNum) (LmSCRATCH(LinkNum) + 0x0198) + +/* Mode independent scratch page 1 macros. */ +#define LmSEQ_EST_NEXUS_SCBPTR0(LinkNum) (LmSCRATCH(LinkNum) + 0x01A0) +#define LmSEQ_EST_NEXUS_SCBPTR1(LinkNum) (LmSCRATCH(LinkNum) + 0x01A2) +#define LmSEQ_EST_NEXUS_SCBPTR2(LinkNum) (LmSCRATCH(LinkNum) + 0x01A4) +#define LmSEQ_EST_NEXUS_SCBPTR3(LinkNum) (LmSCRATCH(LinkNum) + 0x01A6) +#define LmSEQ_EST_NEXUS_SCB_OPCODE0(LinkNum) (LmSCRATCH(LinkNum) + 0x01A8) +#define LmSEQ_EST_NEXUS_SCB_OPCODE1(LinkNum) (LmSCRATCH(LinkNum) + 0x01A9) +#define LmSEQ_EST_NEXUS_SCB_OPCODE2(LinkNum) (LmSCRATCH(LinkNum) + 0x01AA) +#define LmSEQ_EST_NEXUS_SCB_OPCODE3(LinkNum) (LmSCRATCH(LinkNum) + 0x01AB) +#define LmSEQ_EST_NEXUS_SCB_HEAD(LinkNum) (LmSCRATCH(LinkNum) + 0x01AC) +#define LmSEQ_EST_NEXUS_SCB_TAIL(LinkNum) (LmSCRATCH(LinkNum) + 0x01AD) +#define LmSEQ_EST_NEXUS_BUF_AVAIL(LinkNum) (LmSCRATCH(LinkNum) + 0x01AE) +#define LmSEQ_TIMEOUT_CONST(LinkNum) (LmSCRATCH(LinkNum) + 0x01B8) +#define LmSEQ_ISR_SAVE_SINDEX(LinkNum) (LmSCRATCH(LinkNum) + 0x01BC) +#define LmSEQ_ISR_SAVE_DINDEX(LinkNum) (LmSCRATCH(LinkNum) + 0x01BE) + +/* Mode independent scratch page 2 macros. */ +#define LmSEQ_EMPTY_SCB_PTR0(LinkNum) (LmSCRATCH(LinkNum) + 0x01C0) +#define LmSEQ_EMPTY_SCB_PTR1(LinkNum) (LmSCRATCH(LinkNum) + 0x01C2) +#define LmSEQ_EMPTY_SCB_PTR2(LinkNum) (LmSCRATCH(LinkNum) + 0x01C4) +#define LmSEQ_EMPTY_SCB_PTR3(LinkNum) (LmSCRATCH(LinkNum) + 0x01C6) +#define LmSEQ_EMPTY_SCB_OPCD0(LinkNum) (LmSCRATCH(LinkNum) + 0x01C8) +#define LmSEQ_EMPTY_SCB_OPCD1(LinkNum) (LmSCRATCH(LinkNum) + 0x01C9) +#define LmSEQ_EMPTY_SCB_OPCD2(LinkNum) (LmSCRATCH(LinkNum) + 0x01CA) +#define LmSEQ_EMPTY_SCB_OPCD3(LinkNum) (LmSCRATCH(LinkNum) + 0x01CB) +#define LmSEQ_EMPTY_SCB_HEAD(LinkNum) (LmSCRATCH(LinkNum) + 0x01CC) +#define LmSEQ_EMPTY_SCB_TAIL(LinkNum) (LmSCRATCH(LinkNum) + 0x01CD) +#define LmSEQ_EMPTY_BUFS_AVAIL(LinkNum) (LmSCRATCH(LinkNum) + 0x01CE) +#define LmSEQ_ATA_SCR_REGS(LinkNum) (LmSCRATCH(LinkNum) + 0x01D4) + +/* Mode independent scratch page 3 macros. */ +#define LmSEQ_DEV_PRES_TMR_TOUT_CONST(LinkNum) (LmSCRATCH(LinkNum) + 0x01E0) +#define LmSEQ_SATA_INTERLOCK_TIMEOUT(LinkNum) (LmSCRATCH(LinkNum) + 0x01E4) +#define LmSEQ_STP_SHUTDOWN_TIMEOUT(LinkNum) (LmSCRATCH(LinkNum) + 0x01E8) +#define LmSEQ_SRST_ASSERT_TIMEOUT(LinkNum) (LmSCRATCH(LinkNum) + 0x01EC) +#define LmSEQ_RCV_FIS_TIMEOUT(LinkNum) (LmSCRATCH(LinkNum) + 0x01F0) +#define LmSEQ_ONE_MILLISEC_TIMEOUT(LinkNum) (LmSCRATCH(LinkNum) + 0x01F4) +#define LmSEQ_TEN_MS_COMINIT_TIMEOUT(LinkNum) (LmSCRATCH(LinkNum) + 0x01F8) +#define LmSEQ_SMP_RCV_TIMEOUT(LinkNum) (LmSCRATCH(LinkNum) + 0x01FC) + +#endif diff --git a/drivers/scsi/aic94xx/aic94xx_sas.h b/drivers/scsi/aic94xx/aic94xx_sas.h new file mode 100644 index 00000000000..64d23171234 --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx_sas.h @@ -0,0 +1,785 @@ +/* + * Aic94xx SAS/SATA driver SAS definitions and hardware interface header file. + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * This file is part of the aic94xx driver. + * + * The aic94xx driver 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; version 2 of the + * License. + * + * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _AIC94XX_SAS_H_ +#define _AIC94XX_SAS_H_ + +#include + +/* ---------- DDBs ---------- */ +/* DDBs are device descriptor blocks which describe a device in the + * domain that this sequencer can maintain low-level connections for + * us. They are be 64 bytes. + */ + +struct asd_ddb_ssp_smp_target_port { + u8 conn_type; /* byte 0 */ +#define DDB_TP_CONN_TYPE 0x81 /* Initiator port and addr frame type 0x01 */ + + u8 conn_rate; + __be16 init_conn_tag; + u8 dest_sas_addr[8]; /* bytes 4-11 */ + + __le16 send_queue_head; + u8 sq_suspended; + u8 ddb_type; /* DDB_TYPE_TARGET */ +#define DDB_TYPE_UNUSED 0xFF +#define DDB_TYPE_TARGET 0xFE +#define DDB_TYPE_INITIATOR 0xFD +#define DDB_TYPE_PM_PORT 0xFC + + __le16 _r_a; + __be16 awt_def; + + u8 compat_features; /* byte 20 */ + u8 pathway_blocked_count; + __be16 arb_wait_time; + __be32 more_compat_features; /* byte 24 */ + + u8 conn_mask; + u8 flags; /* concurrent conn:2,2 and open:0(1) */ +#define CONCURRENT_CONN_SUPP 0x04 +#define OPEN_REQUIRED 0x01 + + u16 _r_b; + __le16 exec_queue_tail; + __le16 send_queue_tail; + __le16 sister_ddb; + + __le16 _r_c; + + u8 max_concurrent_conn; + u8 num_concurrent_conn; + u8 num_contexts; + + u8 _r_d; + + __le16 active_task_count; + + u8 _r_e[9]; + + u8 itnl_reason; /* I_T nexus loss reason */ + + __le16 _r_f; + + __le16 itnl_timeout; +#define ITNL_TIMEOUT_CONST 0x7D0 /* 2 seconds */ + + __le32 itnl_timestamp; +} __attribute__ ((packed)); + +struct asd_ddb_stp_sata_target_port { + u8 conn_type; /* byte 0 */ + u8 conn_rate; + __be16 init_conn_tag; + u8 dest_sas_addr[8]; /* bytes 4-11 */ + + __le16 send_queue_head; + u8 sq_suspended; + u8 ddb_type; /* DDB_TYPE_TARGET */ + + __le16 _r_a; + + __be16 awt_def; + u8 compat_features; /* byte 20 */ + u8 pathway_blocked_count; + __be16 arb_wait_time; + __be32 more_compat_features; /* byte 24 */ + + u8 conn_mask; + u8 flags; /* concurrent conn:2,2 and open:0(1) */ +#define SATA_MULTIPORT 0x80 +#define SUPPORTS_AFFIL 0x40 +#define STP_AFFIL_POL 0x20 + + u8 _r_b; + u8 flags2; /* STP close policy:0 */ +#define STP_CL_POL_NO_TX 0x00 +#define STP_CL_POL_BTW_CMDS 0x01 + + __le16 exec_queue_tail; + __le16 send_queue_tail; + __le16 sister_ddb; + __le16 ata_cmd_scbptr; + __le32 sata_tag_alloc_mask; + __le16 active_task_count; + __le16 _r_c; + __le32 sata_sactive; + u8 num_sata_tags; + u8 sata_status; + u8 sata_ending_status; + u8 itnl_reason; /* I_T nexus loss reason */ + __le16 ncq_data_scb_ptr; + __le16 itnl_timeout; + __le32 itnl_timestamp; +} __attribute__ ((packed)); + +/* This struct asd_ddb_init_port, describes the device descriptor block + * of an initiator port (when the sequencer is operating in target mode). + * Bytes [0,11] and [20,27] are from the OPEN address frame. + * The sequencer allocates an initiator port DDB entry. + */ +struct asd_ddb_init_port { + u8 conn_type; /* byte 0 */ + u8 conn_rate; + __be16 init_conn_tag; /* BE */ + u8 dest_sas_addr[8]; + __le16 send_queue_head; /* LE, byte 12 */ + u8 sq_suspended; + u8 ddb_type; /* DDB_TYPE_INITIATOR */ + __le16 _r_a; + __be16 awt_def; /* BE */ + u8 compat_features; + u8 pathway_blocked_count; + __be16 arb_wait_time; /* BE */ + __be32 more_compat_features; /* BE */ + u8 conn_mask; + u8 flags; /* == 5 */ + u16 _r_b; + __le16 exec_queue_tail; /* execution queue tail */ + __le16 send_queue_tail; + __le16 sister_ddb; + __le16 init_resp_timeout; /* initiator response timeout */ + __le32 _r_c; + __le16 active_tasks; /* active task count */ + __le16 init_list; /* initiator list link pointer */ + __le32 _r_d; + u8 max_conn_to[3]; /* from Conn-Disc mode page, in us, LE */ + u8 itnl_reason; /* I_T nexus loss reason */ + __le16 bus_inact_to; /* from Conn-Disc mode page, in 100 us, LE */ + __le16 itnl_to; /* from the Protocol Specific Port Ctrl MP */ + __le32 itnl_timestamp; +} __attribute__ ((packed)); + +/* This struct asd_ddb_sata_tag, describes a look-up table to be used + * by the sequencers. SATA II, IDENTIFY DEVICE data, word 76, bit 8: + * NCQ support. This table is used by the sequencers to find the + * corresponding SCB, given a SATA II tag value. + */ +struct asd_ddb_sata_tag { + __le16 scb_pointer[32]; +} __attribute__ ((packed)); + +/* This struct asd_ddb_sata_pm_table, describes a port number to + * connection handle look-up table. SATA targets attached to a port + * multiplier require a 4-bit port number value. There is one DDB + * entry of this type for each SATA port multiplier (sister DDB). + * Given a SATA PM port number, this table gives us the SATA PM Port + * DDB of the SATA port multiplier port (i.e. the SATA target + * discovered on the port). + */ +struct asd_ddb_sata_pm_table { + __le16 ddb_pointer[16]; + __le16 _r_a[16]; +} __attribute__ ((packed)); + +/* This struct asd_ddb_sata_pm_port, describes the SATA port multiplier + * port format DDB. + */ +struct asd_ddb_sata_pm_port { + u8 _r_a[15]; + u8 ddb_type; + u8 _r_b[13]; + u8 pm_port_flags; +#define PM_PORT_MASK 0xF0 +#define PM_PORT_SET 0x02 + u8 _r_c[6]; + __le16 sister_ddb; + __le16 ata_cmd_scbptr; + __le32 sata_tag_alloc_mask; + __le16 active_task_count; + __le16 parent_ddb; + __le32 sata_sactive; + u8 num_sata_tags; + u8 sata_status; + u8 sata_ending_status; + u8 _r_d[9]; +} __attribute__ ((packed)); + +/* This struct asd_ddb_seq_shared, describes a DDB shared by the + * central and link sequencers. port_map_by_links is indexed phy + * number [0,7]; each byte is a bit mask of all the phys that are in + * the same port as the indexed phy. + */ +struct asd_ddb_seq_shared { + __le16 q_free_ddb_head; + __le16 q_free_ddb_tail; + __le16 q_free_ddb_cnt; + __le16 q_used_ddb_head; + __le16 q_used_ddb_tail; + __le16 shared_mem_lock; + __le16 smp_conn_tag; + __le16 est_nexus_buf_cnt; + __le16 est_nexus_buf_thresh; + u32 _r_a; + u8 settable_max_contexts; + u8 _r_b[23]; + u8 conn_not_active; + u8 phy_is_up; + u8 _r_c[8]; + u8 port_map_by_links[8]; +} __attribute__ ((packed)); + +/* ---------- SG Element ---------- */ + +/* This struct sg_el, describes the hardware scatter gather buffer + * element. All entries are little endian. In an SCB, there are 2 of + * this, plus one more, called a link element of this indicating a + * sublist if needed. + * + * A link element has only the bus address set and the flags (DS) bit + * valid. The bus address points to the start of the sublist. + * + * If a sublist is needed, then that sublist should also include the 2 + * sg_el embedded in the SCB, in which case next_sg_offset is 32, + * since sizeof(sg_el) = 16; EOS should be 1 and EOL 0 in this case. + */ +struct sg_el { + __le64 bus_addr; + __le32 size; + __le16 _r; + u8 next_sg_offs; + u8 flags; +#define ASD_SG_EL_DS_MASK 0x30 +#define ASD_SG_EL_DS_OCM 0x10 +#define ASD_SG_EL_DS_HM 0x00 +#define ASD_SG_EL_LIST_MASK 0xC0 +#define ASD_SG_EL_LIST_EOL 0x40 +#define ASD_SG_EL_LIST_EOS 0x80 +} __attribute__ ((packed)); + +/* ---------- SCBs ---------- */ + +/* An SCB (sequencer control block) is comprised of a common header + * and a task part, for a total of 128 bytes. All fields are in LE + * order, unless otherwise noted. + */ + +/* This struct scb_header, defines the SCB header format. + */ +struct scb_header { + __le64 next_scb; + __le16 index; /* transaction context */ + u8 opcode; +} __attribute__ ((packed)); + +/* SCB opcodes: Execution queue + */ +#define INITIATE_SSP_TASK 0x00 +#define INITIATE_LONG_SSP_TASK 0x01 +#define INITIATE_BIDIR_SSP_TASK 0x02 +#define ABORT_TASK 0x03 +#define INITIATE_SSP_TMF 0x04 +#define SSP_TARG_GET_DATA 0x05 +#define SSP_TARG_GET_DATA_GOOD 0x06 +#define SSP_TARG_SEND_RESP 0x07 +#define QUERY_SSP_TASK 0x08 +#define INITIATE_ATA_TASK 0x09 +#define INITIATE_ATAPI_TASK 0x0a +#define CONTROL_ATA_DEV 0x0b +#define INITIATE_SMP_TASK 0x0c +#define SMP_TARG_SEND_RESP 0x0f + +/* SCB opcodes: Send Queue + */ +#define SSP_TARG_SEND_DATA 0x40 +#define SSP_TARG_SEND_DATA_GOOD 0x41 + +/* SCB opcodes: Link Queue + */ +#define CONTROL_PHY 0x80 +#define SEND_PRIMITIVE 0x81 +#define INITIATE_LINK_ADM_TASK 0x82 + +/* SCB opcodes: other + */ +#define EMPTY_SCB 0xc0 +#define INITIATE_SEQ_ADM_TASK 0xc1 +#define EST_ICL_TARG_WINDOW 0xc2 +#define COPY_MEM 0xc3 +#define CLEAR_NEXUS 0xc4 +#define INITIATE_DDB_ADM_TASK 0xc6 +#define ESTABLISH_NEXUS_ESCB 0xd0 + +#define LUN_SIZE 8 + +/* See SAS spec, task IU + */ +struct ssp_task_iu { + u8 lun[LUN_SIZE]; /* BE */ + u16 _r_a; + u8 tmf; + u8 _r_b; + __be16 tag; /* BE */ + u8 _r_c[14]; +} __attribute__ ((packed)); + +/* See SAS spec, command IU + */ +struct ssp_command_iu { + u8 lun[LUN_SIZE]; + u8 _r_a; + u8 efb_prio_attr; /* enable first burst, task prio & attr */ +#define EFB_MASK 0x80 +#define TASK_PRIO_MASK 0x78 +#define TASK_ATTR_MASK 0x07 + + u8 _r_b; + u8 add_cdb_len; /* in dwords, since bit 0,1 are reserved */ + union { + u8 cdb[16]; + struct { + __le64 long_cdb_addr; /* bus address, LE */ + __le32 long_cdb_size; /* LE */ + u8 _r_c[3]; + u8 eol_ds; /* eol:6,6, ds:5,4 */ + } long_cdb; /* sequencer extension */ + }; +} __attribute__ ((packed)); + +struct xfer_rdy_iu { + __be32 requested_offset; /* BE */ + __be32 write_data_len; /* BE */ + __be32 _r_a; +} __attribute__ ((packed)); + +/* ---------- SCB tasks ---------- */ + +/* This is both ssp_task and long_ssp_task + */ +struct initiate_ssp_task { + u8 proto_conn_rate; /* proto:6,4, conn_rate:3,0 */ + __le32 total_xfer_len; + struct ssp_frame_hdr ssp_frame; + struct ssp_command_iu ssp_cmd; + __le16 sister_scb; /* 0xFFFF */ + __le16 conn_handle; /* index to DDB for the intended target */ + u8 data_dir; /* :1,0 */ +#define DATA_DIR_NONE 0x00 +#define DATA_DIR_IN 0x01 +#define DATA_DIR_OUT 0x02 +#define DATA_DIR_BYRECIPIENT 0x03 + + u8 _r_a; + u8 retry_count; + u8 _r_b[5]; + struct sg_el sg_element[3]; /* 2 real and 1 link */ +} __attribute__ ((packed)); + +/* This defines both ata_task and atapi_task. + * ata: C bit of FIS should be 1, + * atapi: C bit of FIS should be 1, and command register should be 0xA0, + * to indicate a packet command. + */ +struct initiate_ata_task { + u8 proto_conn_rate; + __le32 total_xfer_len; + struct host_to_dev_fis fis; + __le32 data_offs; + u8 atapi_packet[16]; + u8 _r_a[12]; + __le16 sister_scb; + __le16 conn_handle; + u8 ata_flags; /* CSMI:6,6, DTM:4,4, QT:3,3, data dir:1,0 */ +#define CSMI_TASK 0x40 +#define DATA_XFER_MODE_DMA 0x10 +#define ATA_Q_TYPE_MASK 0x08 +#define ATA_Q_TYPE_UNTAGGED 0x00 +#define ATA_Q_TYPE_NCQ 0x08 + + u8 _r_b; + u8 retry_count; + u8 _r_c; + u8 flags; +#define STP_AFFIL_POLICY 0x20 +#define SET_AFFIL_POLICY 0x10 +#define RET_PARTIAL_SGLIST 0x02 + + u8 _r_d[3]; + struct sg_el sg_element[3]; +} __attribute__ ((packed)); + +struct initiate_smp_task { + u8 proto_conn_rate; + u8 _r_a[40]; + struct sg_el smp_req; + __le16 sister_scb; + __le16 conn_handle; + u8 _r_c[8]; + struct sg_el smp_resp; + u8 _r_d[32]; +} __attribute__ ((packed)); + +struct control_phy { + u8 phy_id; + u8 sub_func; +#define DISABLE_PHY 0x00 +#define ENABLE_PHY 0x01 +#define RELEASE_SPINUP_HOLD 0x02 +#define ENABLE_PHY_NO_SAS_OOB 0x03 +#define ENABLE_PHY_NO_SATA_OOB 0x04 +#define PHY_NO_OP 0x05 +#define EXECUTE_HARD_RESET 0x81 + + u8 func_mask; + u8 speed_mask; + u8 hot_plug_delay; + u8 port_type; + u8 flags; +#define DEV_PRES_TIMER_OVERRIDE_ENABLE 0x01 +#define DISABLE_PHY_IF_OOB_FAILS 0x02 + + __le32 timeout_override; + u8 link_reset_retries; + u8 _r_a[47]; + __le16 conn_handle; + u8 _r_b[56]; +} __attribute__ ((packed)); + +struct control_ata_dev { + u8 proto_conn_rate; + __le32 _r_a; + struct host_to_dev_fis fis; + u8 _r_b[32]; + __le16 sister_scb; + __le16 conn_handle; + u8 ata_flags; /* 0 */ + u8 _r_c[55]; +} __attribute__ ((packed)); + +struct empty_scb { + u8 num_valid; + __le32 _r_a; +#define ASD_EDBS_PER_SCB 7 +/* header+data+CRC+DMA suffix data */ +#define ASD_EDB_SIZE (24+1024+4+16) + struct sg_el eb[ASD_EDBS_PER_SCB]; +#define ELEMENT_NOT_VALID 0xC0 +} __attribute__ ((packed)); + +struct initiate_link_adm { + u8 phy_id; + u8 sub_func; +#define GET_LINK_ERROR_COUNT 0x00 +#define RESET_LINK_ERROR_COUNT 0x01 +#define ENABLE_NOTIFY_SPINUP_INTS 0x02 + + u8 _r_a[57]; + __le16 conn_handle; + u8 _r_b[56]; +} __attribute__ ((packed)); + +struct copy_memory { + u8 _r_a; + __le16 xfer_len; + __le16 _r_b; + __le64 src_busaddr; + u8 src_ds; /* See definition of sg_el */ + u8 _r_c[45]; + __le16 conn_handle; + __le64 _r_d; + __le64 dest_busaddr; + u8 dest_ds; /* See definition of sg_el */ + u8 _r_e[39]; +} __attribute__ ((packed)); + +struct abort_task { + u8 proto_conn_rate; + __le32 _r_a; + struct ssp_frame_hdr ssp_frame; + struct ssp_task_iu ssp_task; + __le16 sister_scb; + __le16 conn_handle; + u8 flags; /* ovrd_itnl_timer:3,3, suspend_data_trans:2,2 */ +#define SUSPEND_DATA_TRANS 0x04 + + u8 _r_b; + u8 retry_count; + u8 _r_c[5]; + __le16 index; /* Transaction context of task to be queried */ + __le16 itnl_to; + u8 _r_d[44]; +} __attribute__ ((packed)); + +struct clear_nexus { + u8 nexus; +#define NEXUS_ADAPTER 0x00 +#define NEXUS_PORT 0x01 +#define NEXUS_I_T 0x02 +#define NEXUS_I_T_L 0x03 +#define NEXUS_TAG 0x04 +#define NEXUS_TRANS_CX 0x05 +#define NEXUS_SATA_TAG 0x06 +#define NEXUS_T_L 0x07 +#define NEXUS_L 0x08 +#define NEXUS_T_TAG 0x09 + + __le32 _r_a; + u8 flags; +#define SUSPEND_TX 0x80 +#define RESUME_TX 0x40 +#define SEND_Q 0x04 +#define EXEC_Q 0x02 +#define NOTINQ 0x01 + + u8 _r_b[3]; + u8 conn_mask; + u8 _r_c[19]; + struct ssp_task_iu ssp_task; /* LUN and TAG */ + __le16 _r_d; + __le16 conn_handle; + __le64 _r_e; + __le16 index; /* Transaction context of task to be cleared */ + __le16 context; /* Clear nexus context */ + u8 _r_f[44]; +} __attribute__ ((packed)); + +struct initiate_ssp_tmf { + u8 proto_conn_rate; + __le32 _r_a; + struct ssp_frame_hdr ssp_frame; + struct ssp_task_iu ssp_task; + __le16 sister_scb; + __le16 conn_handle; + u8 flags; /* itnl override and suspend data tx */ +#define OVERRIDE_ITNL_TIMER 8 + + u8 _r_b; + u8 retry_count; + u8 _r_c[5]; + __le16 index; /* Transaction context of task to be queried */ + __le16 itnl_to; + u8 _r_d[44]; +} __attribute__ ((packed)); + +/* Transmits an arbitrary primitive on the link. + * Used for NOTIFY and BROADCAST. + */ +struct send_prim { + u8 phy_id; + u8 wait_transmit; /* :0,0 */ + u8 xmit_flags; +#define XMTPSIZE_MASK 0xF0 +#define XMTPSIZE_SINGLE 0x10 +#define XMTPSIZE_REPEATED 0x20 +#define XMTPSIZE_CONT 0x20 +#define XMTPSIZE_TRIPLE 0x30 +#define XMTPSIZE_REDUNDANT 0x60 +#define XMTPSIZE_INF 0 + +#define XMTCONTEN 0x04 +#define XMTPFRM 0x02 /* Transmit at the next frame boundary */ +#define XMTPIMM 0x01 /* Transmit immediately */ + + __le16 _r_a; + u8 prim[4]; /* K, D0, D1, D2 */ + u8 _r_b[50]; + __le16 conn_handle; + u8 _r_c[56]; +} __attribute__ ((packed)); + +/* This describes both SSP Target Get Data and SSP Target Get Data And + * Send Good Response SCBs. Used when the sequencer is operating in + * target mode... + */ +struct ssp_targ_get_data { + u8 proto_conn_rate; + __le32 total_xfer_len; + struct ssp_frame_hdr ssp_frame; + struct xfer_rdy_iu xfer_rdy; + u8 lun[LUN_SIZE]; + __le64 _r_a; + __le16 sister_scb; + __le16 conn_handle; + u8 data_dir; /* 01b */ + u8 _r_b; + u8 retry_count; + u8 _r_c[5]; + struct sg_el sg_element[3]; +} __attribute__ ((packed)); + +/* ---------- The actual SCB struct ---------- */ + +struct scb { + struct scb_header header; + union { + struct initiate_ssp_task ssp_task; + struct initiate_ata_task ata_task; + struct initiate_smp_task smp_task; + struct control_phy control_phy; + struct control_ata_dev control_ata_dev; + struct empty_scb escb; + struct initiate_link_adm link_adm; + struct copy_memory cp_mem; + struct abort_task abort_task; + struct clear_nexus clear_nexus; + struct initiate_ssp_tmf ssp_tmf; + }; +} __attribute__ ((packed)); + +/* ---------- Done List ---------- */ +/* The done list entry opcode field is defined below. + * The mnemonic encoding and meaning is as follows: + * TC - Task Complete, status was received and acknowledged + * TF - Task Failed, indicates an error prior to receiving acknowledgment + * for the command: + * - no conn, + * - NACK or R_ERR received in response to this command, + * - credit blocked or not available, or in the case of SMP request, + * - no SMP response was received. + * In these four cases it is known that the target didn't receive the + * command. + * TI - Task Interrupted, error after the command was acknowledged. It is + * known that the command was received by the target. + * TU - Task Unacked, command was transmitted but neither ACK (R_OK) nor NAK + * (R_ERR) was received due to loss of signal, broken connection, loss of + * dword sync or other reason. The application client should send the + * appropriate task query. + * TA - Task Aborted, see TF. + * _RESP - The completion includes an empty buffer containing status. + * TO - Timeout. + */ +#define TC_NO_ERROR 0x00 +#define TC_UNDERRUN 0x01 +#define TC_OVERRUN 0x02 +#define TF_OPEN_TO 0x03 +#define TF_OPEN_REJECT 0x04 +#define TI_BREAK 0x05 +#define TI_PROTO_ERR 0x06 +#define TC_SSP_RESP 0x07 +#define TI_PHY_DOWN 0x08 +#define TF_PHY_DOWN 0x09 +#define TC_LINK_ADM_RESP 0x0a +#define TC_CSMI 0x0b +#define TC_ATA_RESP 0x0c +#define TU_PHY_DOWN 0x0d +#define TU_BREAK 0x0e +#define TI_SATA_TO 0x0f +#define TI_NAK 0x10 +#define TC_CONTROL_PHY 0x11 +#define TF_BREAK 0x12 +#define TC_RESUME 0x13 +#define TI_ACK_NAK_TO 0x14 +#define TF_SMPRSP_TO 0x15 +#define TF_SMP_XMIT_RCV_ERR 0x16 +#define TC_PARTIAL_SG_LIST 0x17 +#define TU_ACK_NAK_TO 0x18 +#define TU_SATA_TO 0x19 +#define TF_NAK_RECV 0x1a +#define TA_I_T_NEXUS_LOSS 0x1b +#define TC_ATA_R_ERR_RECV 0x1c +#define TF_TMF_NO_CTX 0x1d +#define TA_ON_REQ 0x1e +#define TF_TMF_NO_TAG 0x1f +#define TF_TMF_TAG_FREE 0x20 +#define TF_TMF_TASK_DONE 0x21 +#define TF_TMF_NO_CONN_HANDLE 0x22 +#define TC_TASK_CLEARED 0x23 +#define TI_SYNCS_RECV 0x24 +#define TU_SYNCS_RECV 0x25 +#define TF_IRTT_TO 0x26 +#define TF_NO_SMP_CONN 0x27 +#define TF_IU_SHORT 0x28 +#define TF_DATA_OFFS_ERR 0x29 +#define TF_INV_CONN_HANDLE 0x2a +#define TF_REQUESTED_N_PENDING 0x2b + +/* 0xc1 - 0xc7: empty buffer received, + 0xd1 - 0xd7: establish nexus empty buffer received +*/ +/* This is the ESCB mask */ +#define ESCB_RECVD 0xC0 + + +/* This struct done_list_struct defines the done list entry. + * All fields are LE. + */ +struct done_list_struct { + __le16 index; /* aka transaction context */ + u8 opcode; + u8 status_block[4]; + u8 toggle; /* bit 0 */ +#define DL_TOGGLE_MASK 0x01 +} __attribute__ ((packed)); + +/* ---------- PHYS ---------- */ + +struct asd_phy { + struct asd_sas_phy sas_phy; + struct asd_phy_desc *phy_desc; /* hw profile */ + + struct sas_identify_frame *identify_frame; + struct asd_dma_tok *id_frm_tok; + + u8 frame_rcvd[ASD_EDB_SIZE]; +}; + + +#define ASD_SCB_SIZE sizeof(struct scb) +#define ASD_DDB_SIZE sizeof(struct asd_ddb_ssp_smp_target_port) + +/* Define this to 0 if you do not want NOTIFY (ENABLE SPINIP) sent. + * Default: 0x10 (it's a mask) + */ +#define ASD_NOTIFY_ENABLE_SPINUP 0x10 + +/* If enabled, set this to the interval between transmission + * of NOTIFY (ENABLE SPINUP). In units of 200 us. + */ +#define ASD_NOTIFY_TIMEOUT 2500 + +/* Initial delay after OOB, before we transmit NOTIFY (ENABLE SPINUP). + * If 0, transmit immediately. In milliseconds. + */ +#define ASD_NOTIFY_DOWN_COUNT 0 + +/* Device present timer timeout constant, 10 ms. */ +#define ASD_DEV_PRESENT_TIMEOUT 0x2710 + +#define ASD_SATA_INTERLOCK_TIMEOUT 0 + +/* How long to wait before shutting down an STP connection, unless + * an STP target sent frame(s). 50 usec. + * IGNORED by the sequencer (i.e. value 0 always). + */ +#define ASD_STP_SHUTDOWN_TIMEOUT 0x0 + +/* ATA soft reset timer timeout. 5 usec. */ +#define ASD_SRST_ASSERT_TIMEOUT 0x05 + +/* 31 sec */ +#define ASD_RCV_FIS_TIMEOUT 0x01D905C0 + +#define ASD_ONE_MILLISEC_TIMEOUT 0x03e8 + +/* COMINIT timer */ +#define ASD_TEN_MILLISEC_TIMEOUT 0x2710 +#define ASD_COMINIT_TIMEOUT ASD_TEN_MILLISEC_TIMEOUT + +/* 1 sec */ +#define ASD_SMP_RCV_TIMEOUT 0x000F4240 + +#endif diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c new file mode 100644 index 00000000000..fc1b7438a91 --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx_scb.c @@ -0,0 +1,732 @@ +/* + * Aic94xx SAS/SATA driver SCB management. + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * This file is part of the aic94xx driver. + * + * The aic94xx driver 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; version 2 of the + * License. + * + * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +#include "aic94xx.h" +#include "aic94xx_reg.h" +#include "aic94xx_hwi.h" +#include "aic94xx_seq.h" + +#include "aic94xx_dump.h" + +/* ---------- EMPTY SCB ---------- */ + +#define DL_PHY_MASK 7 +#define BYTES_DMAED 0 +#define PRIMITIVE_RECVD 0x08 +#define PHY_EVENT 0x10 +#define LINK_RESET_ERROR 0x18 +#define TIMER_EVENT 0x20 +#define REQ_TASK_ABORT 0xF0 +#define REQ_DEVICE_RESET 0xF1 +#define SIGNAL_NCQ_ERROR 0xF2 +#define CLEAR_NCQ_ERROR 0xF3 + +#define PHY_EVENTS_STATUS (CURRENT_LOSS_OF_SIGNAL | CURRENT_OOB_DONE \ + | CURRENT_SPINUP_HOLD | CURRENT_GTO_TIMEOUT \ + | CURRENT_OOB_ERROR) + +static inline void get_lrate_mode(struct asd_phy *phy, u8 oob_mode) +{ + switch (oob_mode & 7) { + case PHY_SPEED_60: + /* FIXME: sas transport class doesn't have this */ + phy->sas_phy.linkrate = PHY_LINKRATE_6; + phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS; + break; + case PHY_SPEED_30: + phy->sas_phy.linkrate = PHY_LINKRATE_3; + phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS; + break; + case PHY_SPEED_15: + phy->sas_phy.linkrate = PHY_LINKRATE_1_5; + phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS; + break; + } + if (oob_mode & SAS_MODE) + phy->sas_phy.oob_mode = SAS_OOB_MODE; + else if (oob_mode & SATA_MODE) + phy->sas_phy.oob_mode = SATA_OOB_MODE; +} + +static inline void asd_phy_event_tasklet(struct asd_ascb *ascb, + struct done_list_struct *dl) +{ + struct asd_ha_struct *asd_ha = ascb->ha; + struct sas_ha_struct *sas_ha = &asd_ha->sas_ha; + int phy_id = dl->status_block[0] & DL_PHY_MASK; + struct asd_phy *phy = &asd_ha->phys[phy_id]; + + u8 oob_status = dl->status_block[1] & PHY_EVENTS_STATUS; + u8 oob_mode = dl->status_block[2]; + + switch (oob_status) { + case CURRENT_LOSS_OF_SIGNAL: + /* directly attached device was removed */ + ASD_DPRINTK("phy%d: device unplugged\n", phy_id); + asd_turn_led(asd_ha, phy_id, 0); + sas_phy_disconnected(&phy->sas_phy); + sas_ha->notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL); + break; + case CURRENT_OOB_DONE: + /* hot plugged device */ + asd_turn_led(asd_ha, phy_id, 1); + get_lrate_mode(phy, oob_mode); + ASD_DPRINTK("phy%d device plugged: lrate:0x%x, proto:0x%x\n", + phy_id, phy->sas_phy.linkrate, phy->sas_phy.iproto); + sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE); + break; + case CURRENT_SPINUP_HOLD: + /* hot plug SATA, no COMWAKE sent */ + asd_turn_led(asd_ha, phy_id, 1); + sas_ha->notify_phy_event(&phy->sas_phy, PHYE_SPINUP_HOLD); + break; + case CURRENT_GTO_TIMEOUT: + case CURRENT_OOB_ERROR: + ASD_DPRINTK("phy%d error while OOB: oob status:0x%x\n", phy_id, + dl->status_block[1]); + asd_turn_led(asd_ha, phy_id, 0); + sas_phy_disconnected(&phy->sas_phy); + sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_ERROR); + break; + } +} + +/* If phys are enabled sparsely, this will do the right thing. */ +static inline unsigned ord_phy(struct asd_ha_struct *asd_ha, + struct asd_phy *phy) +{ + u8 enabled_mask = asd_ha->hw_prof.enabled_phys; + int i, k = 0; + + for_each_phy(enabled_mask, enabled_mask, i) { + if (&asd_ha->phys[i] == phy) + return k; + k++; + } + return 0; +} + +/** + * asd_get_attached_sas_addr -- extract/generate attached SAS address + * phy: pointer to asd_phy + * sas_addr: pointer to buffer where the SAS address is to be written + * + * This function extracts the SAS address from an IDENTIFY frame + * received. If OOB is SATA, then a SAS address is generated from the + * HA tables. + * + * LOCKING: the frame_rcvd_lock needs to be held since this parses the frame + * buffer. + */ +static inline void asd_get_attached_sas_addr(struct asd_phy *phy, u8 *sas_addr) +{ + if (phy->sas_phy.frame_rcvd[0] == 0x34 + && phy->sas_phy.oob_mode == SATA_OOB_MODE) { + struct asd_ha_struct *asd_ha = phy->sas_phy.ha->lldd_ha; + /* FIS device-to-host */ + u64 addr = be64_to_cpu(*(__be64 *)phy->phy_desc->sas_addr); + + addr += asd_ha->hw_prof.sata_name_base + ord_phy(asd_ha, phy); + *(__be64 *)sas_addr = cpu_to_be64(addr); + } else { + struct sas_identify_frame *idframe = + (void *) phy->sas_phy.frame_rcvd; + memcpy(sas_addr, idframe->sas_addr, SAS_ADDR_SIZE); + } +} + +static inline void asd_bytes_dmaed_tasklet(struct asd_ascb *ascb, + struct done_list_struct *dl, + int edb_id, int phy_id) +{ + unsigned long flags; + int edb_el = edb_id + ascb->edb_index; + struct asd_dma_tok *edb = ascb->ha->seq.edb_arr[edb_el]; + struct asd_phy *phy = &ascb->ha->phys[phy_id]; + struct sas_ha_struct *sas_ha = phy->sas_phy.ha; + u16 size = ((dl->status_block[3] & 7) << 8) | dl->status_block[2]; + + size = min(size, (u16) sizeof(phy->frame_rcvd)); + + spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags); + memcpy(phy->sas_phy.frame_rcvd, edb->vaddr, size); + phy->sas_phy.frame_rcvd_size = size; + asd_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr); + spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags); + asd_dump_frame_rcvd(phy, dl); + sas_ha->notify_port_event(&phy->sas_phy, PORTE_BYTES_DMAED); +} + +static inline void asd_link_reset_err_tasklet(struct asd_ascb *ascb, + struct done_list_struct *dl, + int phy_id) +{ + struct asd_ha_struct *asd_ha = ascb->ha; + struct sas_ha_struct *sas_ha = &asd_ha->sas_ha; + struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id]; + u8 lr_error = dl->status_block[1]; + u8 retries_left = dl->status_block[2]; + + switch (lr_error) { + case 0: + ASD_DPRINTK("phy%d: Receive ID timer expired\n", phy_id); + break; + case 1: + ASD_DPRINTK("phy%d: Loss of signal\n", phy_id); + break; + case 2: + ASD_DPRINTK("phy%d: Loss of dword sync\n", phy_id); + break; + case 3: + ASD_DPRINTK("phy%d: Receive FIS timeout\n", phy_id); + break; + default: + ASD_DPRINTK("phy%d: unknown link reset error code: 0x%x\n", + phy_id, lr_error); + break; + } + + asd_turn_led(asd_ha, phy_id, 0); + sas_phy_disconnected(sas_phy); + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + + if (retries_left == 0) { + int num = 1; + struct asd_ascb *cp = asd_ascb_alloc_list(ascb->ha, &num, + GFP_ATOMIC); + if (!cp) { + asd_printk("%s: out of memory\n", __FUNCTION__); + goto out; + } + ASD_DPRINTK("phy%d: retries:0 performing link reset seq\n", + phy_id); + asd_build_control_phy(cp, phy_id, ENABLE_PHY); + if (asd_post_ascb_list(ascb->ha, cp, 1) != 0) + asd_ascb_free(cp); + } +out: + ; +} + +static inline void asd_primitive_rcvd_tasklet(struct asd_ascb *ascb, + struct done_list_struct *dl, + int phy_id) +{ + unsigned long flags; + struct sas_ha_struct *sas_ha = &ascb->ha->sas_ha; + struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id]; + u8 reg = dl->status_block[1]; + u32 cont = dl->status_block[2] << ((reg & 3)*8); + + reg &= ~3; + switch (reg) { + case LmPRMSTAT0BYTE0: + switch (cont) { + case LmBROADCH: + case LmBROADRVCH0: + case LmBROADRVCH1: + case LmBROADSES: + ASD_DPRINTK("phy%d: BROADCAST change received:%d\n", + phy_id, cont); + spin_lock_irqsave(&sas_phy->sas_prim_lock, flags); + sas_phy->sas_prim = ffs(cont); + spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags); + sas_ha->notify_port_event(sas_phy,PORTE_BROADCAST_RCVD); + break; + + case LmUNKNOWNP: + ASD_DPRINTK("phy%d: unknown BREAK\n", phy_id); + break; + + default: + ASD_DPRINTK("phy%d: primitive reg:0x%x, cont:0x%04x\n", + phy_id, reg, cont); + break; + } + break; + case LmPRMSTAT1BYTE0: + switch (cont) { + case LmHARDRST: + ASD_DPRINTK("phy%d: HARD_RESET primitive rcvd\n", + phy_id); + /* The sequencer disables all phys on that port. + * We have to re-enable the phys ourselves. */ + sas_ha->notify_port_event(sas_phy, PORTE_HARD_RESET); + break; + + default: + ASD_DPRINTK("phy%d: primitive reg:0x%x, cont:0x%04x\n", + phy_id, reg, cont); + break; + } + break; + default: + ASD_DPRINTK("unknown primitive register:0x%x\n", + dl->status_block[1]); + break; + } +} + +/** + * asd_invalidate_edb -- invalidate an EDB and if necessary post the ESCB + * @ascb: pointer to Empty SCB + * @edb_id: index [0,6] to the empty data buffer which is to be invalidated + * + * After an EDB has been invalidated, if all EDBs in this ESCB have been + * invalidated, the ESCB is posted back to the sequencer. + * Context is tasklet/IRQ. + */ +void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id) +{ + struct asd_seq_data *seq = &ascb->ha->seq; + struct empty_scb *escb = &ascb->scb->escb; + struct sg_el *eb = &escb->eb[edb_id]; + struct asd_dma_tok *edb = seq->edb_arr[ascb->edb_index + edb_id]; + + memset(edb->vaddr, 0, ASD_EDB_SIZE); + eb->flags |= ELEMENT_NOT_VALID; + escb->num_valid--; + + if (escb->num_valid == 0) { + int i; + /* ASD_DPRINTK("reposting escb: vaddr: 0x%p, " + "dma_handle: 0x%08llx, next: 0x%08llx, " + "index:%d, opcode:0x%02x\n", + ascb->dma_scb.vaddr, + (u64)ascb->dma_scb.dma_handle, + le64_to_cpu(ascb->scb->header.next_scb), + le16_to_cpu(ascb->scb->header.index), + ascb->scb->header.opcode); + */ + escb->num_valid = ASD_EDBS_PER_SCB; + for (i = 0; i < ASD_EDBS_PER_SCB; i++) + escb->eb[i].flags = 0; + if (!list_empty(&ascb->list)) + list_del_init(&ascb->list); + i = asd_post_escb_list(ascb->ha, ascb, 1); + if (i) + asd_printk("couldn't post escb, err:%d\n", i); + } +} + +static void escb_tasklet_complete(struct asd_ascb *ascb, + struct done_list_struct *dl) +{ + struct asd_ha_struct *asd_ha = ascb->ha; + struct sas_ha_struct *sas_ha = &asd_ha->sas_ha; + int edb = (dl->opcode & DL_PHY_MASK) - 1; /* [0xc1,0xc7] -> [0,6] */ + u8 sb_opcode = dl->status_block[0]; + int phy_id = sb_opcode & DL_PHY_MASK; + struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id]; + + if (edb > 6 || edb < 0) { + ASD_DPRINTK("edb is 0x%x! dl->opcode is 0x%x\n", + edb, dl->opcode); + ASD_DPRINTK("sb_opcode : 0x%x, phy_id: 0x%x\n", + sb_opcode, phy_id); + ASD_DPRINTK("escb: vaddr: 0x%p, " + "dma_handle: 0x%llx, next: 0x%llx, " + "index:%d, opcode:0x%02x\n", + ascb->dma_scb.vaddr, + (unsigned long long)ascb->dma_scb.dma_handle, + (unsigned long long) + le64_to_cpu(ascb->scb->header.next_scb), + le16_to_cpu(ascb->scb->header.index), + ascb->scb->header.opcode); + } + + sb_opcode &= ~DL_PHY_MASK; + + switch (sb_opcode) { + case BYTES_DMAED: + ASD_DPRINTK("%s: phy%d: BYTES_DMAED\n", __FUNCTION__, phy_id); + asd_bytes_dmaed_tasklet(ascb, dl, edb, phy_id); + break; + case PRIMITIVE_RECVD: + ASD_DPRINTK("%s: phy%d: PRIMITIVE_RECVD\n", __FUNCTION__, + phy_id); + asd_primitive_rcvd_tasklet(ascb, dl, phy_id); + break; + case PHY_EVENT: + ASD_DPRINTK("%s: phy%d: PHY_EVENT\n", __FUNCTION__, phy_id); + asd_phy_event_tasklet(ascb, dl); + break; + case LINK_RESET_ERROR: + ASD_DPRINTK("%s: phy%d: LINK_RESET_ERROR\n", __FUNCTION__, + phy_id); + asd_link_reset_err_tasklet(ascb, dl, phy_id); + break; + case TIMER_EVENT: + ASD_DPRINTK("%s: phy%d: TIMER_EVENT, lost dw sync\n", + __FUNCTION__, phy_id); + asd_turn_led(asd_ha, phy_id, 0); + /* the device is gone */ + sas_phy_disconnected(sas_phy); + sas_ha->notify_port_event(sas_phy, PORTE_TIMER_EVENT); + break; + case REQ_TASK_ABORT: + ASD_DPRINTK("%s: phy%d: REQ_TASK_ABORT\n", __FUNCTION__, + phy_id); + break; + case REQ_DEVICE_RESET: + ASD_DPRINTK("%s: phy%d: REQ_DEVICE_RESET\n", __FUNCTION__, + phy_id); + break; + case SIGNAL_NCQ_ERROR: + ASD_DPRINTK("%s: phy%d: SIGNAL_NCQ_ERROR\n", __FUNCTION__, + phy_id); + break; + case CLEAR_NCQ_ERROR: + ASD_DPRINTK("%s: phy%d: CLEAR_NCQ_ERROR\n", __FUNCTION__, + phy_id); + break; + default: + ASD_DPRINTK("%s: phy%d: unknown event:0x%x\n", __FUNCTION__, + phy_id, sb_opcode); + ASD_DPRINTK("edb is 0x%x! dl->opcode is 0x%x\n", + edb, dl->opcode); + ASD_DPRINTK("sb_opcode : 0x%x, phy_id: 0x%x\n", + sb_opcode, phy_id); + ASD_DPRINTK("escb: vaddr: 0x%p, " + "dma_handle: 0x%llx, next: 0x%llx, " + "index:%d, opcode:0x%02x\n", + ascb->dma_scb.vaddr, + (unsigned long long)ascb->dma_scb.dma_handle, + (unsigned long long) + le64_to_cpu(ascb->scb->header.next_scb), + le16_to_cpu(ascb->scb->header.index), + ascb->scb->header.opcode); + + break; + } + + asd_invalidate_edb(ascb, edb); +} + +int asd_init_post_escbs(struct asd_ha_struct *asd_ha) +{ + struct asd_seq_data *seq = &asd_ha->seq; + int i; + + for (i = 0; i < seq->num_escbs; i++) + seq->escb_arr[i]->tasklet_complete = escb_tasklet_complete; + + ASD_DPRINTK("posting %d escbs\n", i); + return asd_post_escb_list(asd_ha, seq->escb_arr[0], seq->num_escbs); +} + +/* ---------- CONTROL PHY ---------- */ + +#define CONTROL_PHY_STATUS (CURRENT_DEVICE_PRESENT | CURRENT_OOB_DONE \ + | CURRENT_SPINUP_HOLD | CURRENT_GTO_TIMEOUT \ + | CURRENT_OOB_ERROR) + +/** + * control_phy_tasklet_complete -- tasklet complete for CONTROL PHY ascb + * @ascb: pointer to an ascb + * @dl: pointer to the done list entry + * + * This function completes a CONTROL PHY scb and frees the ascb. + * A note on LEDs: + * - an LED blinks if there is IO though it, + * - if a device is connected to the LED, it is lit, + * - if no device is connected to the LED, is is dimmed (off). + */ +static void control_phy_tasklet_complete(struct asd_ascb *ascb, + struct done_list_struct *dl) +{ + struct asd_ha_struct *asd_ha = ascb->ha; + struct scb *scb = ascb->scb; + struct control_phy *control_phy = &scb->control_phy; + u8 phy_id = control_phy->phy_id; + struct asd_phy *phy = &ascb->ha->phys[phy_id]; + + u8 status = dl->status_block[0]; + u8 oob_status = dl->status_block[1]; + u8 oob_mode = dl->status_block[2]; + /* u8 oob_signals= dl->status_block[3]; */ + + if (status != 0) { + ASD_DPRINTK("%s: phy%d status block opcode:0x%x\n", + __FUNCTION__, phy_id, status); + goto out; + } + + switch (control_phy->sub_func) { + case DISABLE_PHY: + asd_ha->hw_prof.enabled_phys &= ~(1 << phy_id); + asd_turn_led(asd_ha, phy_id, 0); + asd_control_led(asd_ha, phy_id, 0); + ASD_DPRINTK("%s: disable phy%d\n", __FUNCTION__, phy_id); + break; + + case ENABLE_PHY: + asd_control_led(asd_ha, phy_id, 1); + if (oob_status & CURRENT_OOB_DONE) { + asd_ha->hw_prof.enabled_phys |= (1 << phy_id); + get_lrate_mode(phy, oob_mode); + asd_turn_led(asd_ha, phy_id, 1); + ASD_DPRINTK("%s: phy%d, lrate:0x%x, proto:0x%x\n", + __FUNCTION__, phy_id,phy->sas_phy.linkrate, + phy->sas_phy.iproto); + } else if (oob_status & CURRENT_SPINUP_HOLD) { + asd_ha->hw_prof.enabled_phys |= (1 << phy_id); + asd_turn_led(asd_ha, phy_id, 1); + ASD_DPRINTK("%s: phy%d, spinup hold\n", __FUNCTION__, + phy_id); + } else if (oob_status & CURRENT_ERR_MASK) { + asd_turn_led(asd_ha, phy_id, 0); + ASD_DPRINTK("%s: phy%d: error: oob status:0x%02x\n", + __FUNCTION__, phy_id, oob_status); + } else if (oob_status & (CURRENT_HOT_PLUG_CNCT + | CURRENT_DEVICE_PRESENT)) { + asd_ha->hw_prof.enabled_phys |= (1 << phy_id); + asd_turn_led(asd_ha, phy_id, 1); + ASD_DPRINTK("%s: phy%d: hot plug or device present\n", + __FUNCTION__, phy_id); + } else { + asd_ha->hw_prof.enabled_phys |= (1 << phy_id); + asd_turn_led(asd_ha, phy_id, 0); + ASD_DPRINTK("%s: phy%d: no device present: " + "oob_status:0x%x\n", + __FUNCTION__, phy_id, oob_status); + } + break; + case RELEASE_SPINUP_HOLD: + case PHY_NO_OP: + case EXECUTE_HARD_RESET: + ASD_DPRINTK("%s: phy%d: sub_func:0x%x\n", __FUNCTION__, + phy_id, control_phy->sub_func); + /* XXX finish */ + break; + default: + ASD_DPRINTK("%s: phy%d: sub_func:0x%x?\n", __FUNCTION__, + phy_id, control_phy->sub_func); + break; + } +out: + asd_ascb_free(ascb); +} + +static inline void set_speed_mask(u8 *speed_mask, struct asd_phy_desc *pd) +{ + /* disable all speeds, then enable defaults */ + *speed_mask = SAS_SPEED_60_DIS | SAS_SPEED_30_DIS | SAS_SPEED_15_DIS + | SATA_SPEED_30_DIS | SATA_SPEED_15_DIS; + + switch (pd->max_sas_lrate) { + case PHY_LINKRATE_6: + *speed_mask &= ~SAS_SPEED_60_DIS; + default: + case PHY_LINKRATE_3: + *speed_mask &= ~SAS_SPEED_30_DIS; + case PHY_LINKRATE_1_5: + *speed_mask &= ~SAS_SPEED_15_DIS; + } + + switch (pd->min_sas_lrate) { + case PHY_LINKRATE_6: + *speed_mask |= SAS_SPEED_30_DIS; + case PHY_LINKRATE_3: + *speed_mask |= SAS_SPEED_15_DIS; + default: + case PHY_LINKRATE_1_5: + /* nothing to do */ + ; + } + + switch (pd->max_sata_lrate) { + case PHY_LINKRATE_3: + *speed_mask &= ~SATA_SPEED_30_DIS; + default: + case PHY_LINKRATE_1_5: + *speed_mask &= ~SATA_SPEED_15_DIS; + } + + switch (pd->min_sata_lrate) { + case PHY_LINKRATE_3: + *speed_mask |= SATA_SPEED_15_DIS; + default: + case PHY_LINKRATE_1_5: + /* nothing to do */ + ; + } +} + +/** + * asd_build_control_phy -- build a CONTROL PHY SCB + * @ascb: pointer to an ascb + * @phy_id: phy id to control, integer + * @subfunc: subfunction, what to actually to do the phy + * + * This function builds a CONTROL PHY scb. No allocation of any kind + * is performed. @ascb is allocated with the list function. + * The caller can override the ascb->tasklet_complete to point + * to its own callback function. It must call asd_ascb_free() + * at its tasklet complete function. + * See the default implementation. + */ +void asd_build_control_phy(struct asd_ascb *ascb, int phy_id, u8 subfunc) +{ + struct asd_phy *phy = &ascb->ha->phys[phy_id]; + struct scb *scb = ascb->scb; + struct control_phy *control_phy = &scb->control_phy; + + scb->header.opcode = CONTROL_PHY; + control_phy->phy_id = (u8) phy_id; + control_phy->sub_func = subfunc; + + switch (subfunc) { + case EXECUTE_HARD_RESET: /* 0x81 */ + case ENABLE_PHY: /* 0x01 */ + /* decide hot plug delay */ + control_phy->hot_plug_delay = HOTPLUG_DELAY_TIMEOUT; + + /* decide speed mask */ + set_speed_mask(&control_phy->speed_mask, phy->phy_desc); + + /* initiator port settings are in the hi nibble */ + if (phy->sas_phy.role == PHY_ROLE_INITIATOR) + control_phy->port_type = SAS_PROTO_ALL << 4; + else if (phy->sas_phy.role == PHY_ROLE_TARGET) + control_phy->port_type = SAS_PROTO_ALL; + else + control_phy->port_type = + (SAS_PROTO_ALL << 4) | SAS_PROTO_ALL; + + /* link reset retries, this should be nominal */ + control_phy->link_reset_retries = 10; + + case RELEASE_SPINUP_HOLD: /* 0x02 */ + /* decide the func_mask */ + control_phy->func_mask = FUNCTION_MASK_DEFAULT; + if (phy->phy_desc->flags & ASD_SATA_SPINUP_HOLD) + control_phy->func_mask &= ~SPINUP_HOLD_DIS; + else + control_phy->func_mask |= SPINUP_HOLD_DIS; + } + + control_phy->conn_handle = cpu_to_le16(0xFFFF); + + ascb->tasklet_complete = control_phy_tasklet_complete; +} + +/* ---------- INITIATE LINK ADM TASK ---------- */ + +static void link_adm_tasklet_complete(struct asd_ascb *ascb, + struct done_list_struct *dl) +{ + u8 opcode = dl->opcode; + struct initiate_link_adm *link_adm = &ascb->scb->link_adm; + u8 phy_id = link_adm->phy_id; + + if (opcode != TC_NO_ERROR) { + asd_printk("phy%d: link adm task 0x%x completed with error " + "0x%x\n", phy_id, link_adm->sub_func, opcode); + } + ASD_DPRINTK("phy%d: link adm task 0x%x: 0x%x\n", + phy_id, link_adm->sub_func, opcode); + + asd_ascb_free(ascb); +} + +void asd_build_initiate_link_adm_task(struct asd_ascb *ascb, int phy_id, + u8 subfunc) +{ + struct scb *scb = ascb->scb; + struct initiate_link_adm *link_adm = &scb->link_adm; + + scb->header.opcode = INITIATE_LINK_ADM_TASK; + + link_adm->phy_id = phy_id; + link_adm->sub_func = subfunc; + link_adm->conn_handle = cpu_to_le16(0xFFFF); + + ascb->tasklet_complete = link_adm_tasklet_complete; +} + +/* ---------- SCB timer ---------- */ + +/** + * asd_ascb_timedout -- called when a pending SCB's timer has expired + * @data: unsigned long, a pointer to the ascb in question + * + * This is the default timeout function which does the most necessary. + * Upper layers can implement their own timeout function, say to free + * resources they have with this SCB, and then call this one at the + * end of their timeout function. To do this, one should initialize + * the ascb->timer.{function, data, expires} prior to calling the post + * funcion. The timer is started by the post function. + */ +void asd_ascb_timedout(unsigned long data) +{ + struct asd_ascb *ascb = (void *) data; + struct asd_seq_data *seq = &ascb->ha->seq; + unsigned long flags; + + ASD_DPRINTK("scb:0x%x timed out\n", ascb->scb->header.opcode); + + spin_lock_irqsave(&seq->pend_q_lock, flags); + seq->pending--; + list_del_init(&ascb->list); + spin_unlock_irqrestore(&seq->pend_q_lock, flags); + + asd_ascb_free(ascb); +} + +/* ---------- CONTROL PHY ---------- */ + +/* Given the spec value, return a driver value. */ +static const int phy_func_table[] = { + [PHY_FUNC_NOP] = PHY_NO_OP, + [PHY_FUNC_LINK_RESET] = ENABLE_PHY, + [PHY_FUNC_HARD_RESET] = EXECUTE_HARD_RESET, + [PHY_FUNC_DISABLE] = DISABLE_PHY, + [PHY_FUNC_RELEASE_SPINUP_HOLD] = RELEASE_SPINUP_HOLD, +}; + +int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func) +{ + struct asd_ha_struct *asd_ha = phy->ha->lldd_ha; + struct asd_ascb *ascb; + int res = 1; + + if (func == PHY_FUNC_CLEAR_ERROR_LOG) + return -ENOSYS; + + ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); + if (!ascb) + return -ENOMEM; + + asd_build_control_phy(ascb, phy->id, phy_func_table[func]); + res = asd_post_ascb_list(asd_ha, ascb , 1); + if (res) + asd_ascb_free(ascb); + + return res; +} diff --git a/drivers/scsi/aic94xx/aic94xx_sds.c b/drivers/scsi/aic94xx/aic94xx_sds.c new file mode 100644 index 00000000000..eec1e0db0e0 --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx_sds.c @@ -0,0 +1,1136 @@ +/* + * Aic94xx SAS/SATA driver access to shared data structures and memory + * maps. + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * This file is part of the aic94xx driver. + * + * The aic94xx driver 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; version 2 of the + * License. + * + * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include + +#include "aic94xx.h" +#include "aic94xx_reg.h" + +/* ---------- OCM stuff ---------- */ + +struct asd_ocm_dir_ent { + u8 type; + u8 offs[3]; + u8 _r1; + u8 size[3]; +} __attribute__ ((packed)); + +struct asd_ocm_dir { + char sig[2]; + u8 _r1[2]; + u8 major; /* 0 */ + u8 minor; /* 0 */ + u8 _r2; + u8 num_de; + struct asd_ocm_dir_ent entry[15]; +} __attribute__ ((packed)); + +#define OCM_DE_OCM_DIR 0x00 +#define OCM_DE_WIN_DRVR 0x01 +#define OCM_DE_BIOS_CHIM 0x02 +#define OCM_DE_RAID_ENGN 0x03 +#define OCM_DE_BIOS_INTL 0x04 +#define OCM_DE_BIOS_CHIM_OSM 0x05 +#define OCM_DE_BIOS_CHIM_DYNAMIC 0x06 +#define OCM_DE_ADDC2C_RES0 0x07 +#define OCM_DE_ADDC2C_RES1 0x08 +#define OCM_DE_ADDC2C_RES2 0x09 +#define OCM_DE_ADDC2C_RES3 0x0A + +#define OCM_INIT_DIR_ENTRIES 5 +/*************************************************************************** +* OCM dircetory default +***************************************************************************/ +static struct asd_ocm_dir OCMDirInit = +{ + .sig = {0x4D, 0x4F}, /* signature */ + .num_de = OCM_INIT_DIR_ENTRIES, /* no. of directory entries */ +}; + +/*************************************************************************** +* OCM dircetory Entries default +***************************************************************************/ +static struct asd_ocm_dir_ent OCMDirEntriesInit[OCM_INIT_DIR_ENTRIES] = +{ + { + .type = (OCM_DE_ADDC2C_RES0), /* Entry type */ + .offs = {128}, /* Offset */ + .size = {0, 4}, /* size */ + }, + { + .type = (OCM_DE_ADDC2C_RES1), /* Entry type */ + .offs = {128, 4}, /* Offset */ + .size = {0, 4}, /* size */ + }, + { + .type = (OCM_DE_ADDC2C_RES2), /* Entry type */ + .offs = {128, 8}, /* Offset */ + .size = {0, 4}, /* size */ + }, + { + .type = (OCM_DE_ADDC2C_RES3), /* Entry type */ + .offs = {128, 12}, /* Offset */ + .size = {0, 4}, /* size */ + }, + { + .type = (OCM_DE_WIN_DRVR), /* Entry type */ + .offs = {128, 16}, /* Offset */ + .size = {128, 235, 1}, /* size */ + }, +}; + +struct asd_bios_chim_struct { + char sig[4]; + u8 major; /* 1 */ + u8 minor; /* 0 */ + u8 bios_major; + u8 bios_minor; + __le32 bios_build; + u8 flags; + u8 pci_slot; + __le16 ue_num; + __le16 ue_size; + u8 _r[14]; + /* The unit element array is right here. + */ +} __attribute__ ((packed)); + +/** + * asd_read_ocm_seg - read an on chip memory (OCM) segment + * @asd_ha: pointer to the host adapter structure + * @buffer: where to write the read data + * @offs: offset into OCM where to read from + * @size: how many bytes to read + * + * Return the number of bytes not read. Return 0 on success. + */ +static int asd_read_ocm_seg(struct asd_ha_struct *asd_ha, void *buffer, + u32 offs, int size) +{ + u8 *p = buffer; + if (unlikely(asd_ha->iospace)) + asd_read_reg_string(asd_ha, buffer, offs+OCM_BASE_ADDR, size); + else { + for ( ; size > 0; size--, offs++, p++) + *p = asd_read_ocm_byte(asd_ha, offs); + } + return size; +} + +static int asd_read_ocm_dir(struct asd_ha_struct *asd_ha, + struct asd_ocm_dir *dir, u32 offs) +{ + int err = asd_read_ocm_seg(asd_ha, dir, offs, sizeof(*dir)); + if (err) { + ASD_DPRINTK("couldn't read ocm segment\n"); + return err; + } + + if (dir->sig[0] != 'M' || dir->sig[1] != 'O') { + ASD_DPRINTK("no valid dir signature(%c%c) at start of OCM\n", + dir->sig[0], dir->sig[1]); + return -ENOENT; + } + if (dir->major != 0) { + asd_printk("unsupported major version of ocm dir:0x%x\n", + dir->major); + return -ENOENT; + } + dir->num_de &= 0xf; + return 0; +} + +/** + * asd_write_ocm_seg - write an on chip memory (OCM) segment + * @asd_ha: pointer to the host adapter structure + * @buffer: where to read the write data + * @offs: offset into OCM to write to + * @size: how many bytes to write + * + * Return the number of bytes not written. Return 0 on success. + */ +static void asd_write_ocm_seg(struct asd_ha_struct *asd_ha, void *buffer, + u32 offs, int size) +{ + u8 *p = buffer; + if (unlikely(asd_ha->iospace)) + asd_write_reg_string(asd_ha, buffer, offs+OCM_BASE_ADDR, size); + else { + for ( ; size > 0; size--, offs++, p++) + asd_write_ocm_byte(asd_ha, offs, *p); + } + return; +} + +#define THREE_TO_NUM(X) ((X)[0] | ((X)[1] << 8) | ((X)[2] << 16)) + +static int asd_find_dir_entry(struct asd_ocm_dir *dir, u8 type, + u32 *offs, u32 *size) +{ + int i; + struct asd_ocm_dir_ent *ent; + + for (i = 0; i < dir->num_de; i++) { + if (dir->entry[i].type == type) + break; + } + if (i >= dir->num_de) + return -ENOENT; + ent = &dir->entry[i]; + *offs = (u32) THREE_TO_NUM(ent->offs); + *size = (u32) THREE_TO_NUM(ent->size); + return 0; +} + +#define OCM_BIOS_CHIM_DE 2 +#define BC_BIOS_PRESENT 1 + +static int asd_get_bios_chim(struct asd_ha_struct *asd_ha, + struct asd_ocm_dir *dir) +{ + int err; + struct asd_bios_chim_struct *bc_struct; + u32 offs, size; + + err = asd_find_dir_entry(dir, OCM_BIOS_CHIM_DE, &offs, &size); + if (err) { + ASD_DPRINTK("couldn't find BIOS_CHIM dir ent\n"); + goto out; + } + err = -ENOMEM; + bc_struct = kmalloc(sizeof(*bc_struct), GFP_KERNEL); + if (!bc_struct) { + asd_printk("no memory for bios_chim struct\n"); + goto out; + } + err = asd_read_ocm_seg(asd_ha, (void *)bc_struct, offs, + sizeof(*bc_struct)); + if (err) { + ASD_DPRINTK("couldn't read ocm segment\n"); + goto out2; + } + if (strncmp(bc_struct->sig, "SOIB", 4) + && strncmp(bc_struct->sig, "IPSA", 4)) { + ASD_DPRINTK("BIOS_CHIM entry has no valid sig(%c%c%c%c)\n", + bc_struct->sig[0], bc_struct->sig[1], + bc_struct->sig[2], bc_struct->sig[3]); + err = -ENOENT; + goto out2; + } + if (bc_struct->major != 1) { + asd_printk("BIOS_CHIM unsupported major version:0x%x\n", + bc_struct->major); + err = -ENOENT; + goto out2; + } + if (bc_struct->flags & BC_BIOS_PRESENT) { + asd_ha->hw_prof.bios.present = 1; + asd_ha->hw_prof.bios.maj = bc_struct->bios_major; + asd_ha->hw_prof.bios.min = bc_struct->bios_minor; + asd_ha->hw_prof.bios.bld = le32_to_cpu(bc_struct->bios_build); + ASD_DPRINTK("BIOS present (%d,%d), %d\n", + asd_ha->hw_prof.bios.maj, + asd_ha->hw_prof.bios.min, + asd_ha->hw_prof.bios.bld); + } + asd_ha->hw_prof.ue.num = le16_to_cpu(bc_struct->ue_num); + asd_ha->hw_prof.ue.size= le16_to_cpu(bc_struct->ue_size); + ASD_DPRINTK("ue num:%d, ue size:%d\n", asd_ha->hw_prof.ue.num, + asd_ha->hw_prof.ue.size); + size = asd_ha->hw_prof.ue.num * asd_ha->hw_prof.ue.size; + if (size > 0) { + err = -ENOMEM; + asd_ha->hw_prof.ue.area = kmalloc(size, GFP_KERNEL); + if (!asd_ha->hw_prof.ue.area) + goto out2; + err = asd_read_ocm_seg(asd_ha, (void *)asd_ha->hw_prof.ue.area, + offs + sizeof(*bc_struct), size); + if (err) { + kfree(asd_ha->hw_prof.ue.area); + asd_ha->hw_prof.ue.area = NULL; + asd_ha->hw_prof.ue.num = 0; + asd_ha->hw_prof.ue.size = 0; + ASD_DPRINTK("couldn't read ue entries(%d)\n", err); + } + } +out2: + kfree(bc_struct); +out: + return err; +} + +static void +asd_hwi_initialize_ocm_dir (struct asd_ha_struct *asd_ha) +{ + int i; + + /* Zero OCM */ + for (i = 0; i < OCM_MAX_SIZE; i += 4) + asd_write_ocm_dword(asd_ha, i, 0); + + /* Write Dir */ + asd_write_ocm_seg(asd_ha, &OCMDirInit, 0, + sizeof(struct asd_ocm_dir)); + + /* Write Dir Entries */ + for (i = 0; i < OCM_INIT_DIR_ENTRIES; i++) + asd_write_ocm_seg(asd_ha, &OCMDirEntriesInit[i], + sizeof(struct asd_ocm_dir) + + (i * sizeof(struct asd_ocm_dir_ent)) + , sizeof(struct asd_ocm_dir_ent)); + +} + +static int +asd_hwi_check_ocm_access (struct asd_ha_struct *asd_ha) +{ + struct pci_dev *pcidev = asd_ha->pcidev; + u32 reg; + int err = 0; + u32 v; + + /* check if OCM has been initialized by BIOS */ + reg = asd_read_reg_dword(asd_ha, EXSICNFGR); + + if (!(reg & OCMINITIALIZED)) { + err = pci_read_config_dword(pcidev, PCIC_INTRPT_STAT, &v); + if (err) { + asd_printk("couldn't access PCIC_INTRPT_STAT of %s\n", + pci_name(pcidev)); + goto out; + } + + printk(KERN_INFO "OCM is not initialized by BIOS," + "reinitialize it and ignore it, current IntrptStatus" + "is 0x%x\n", v); + + if (v) + err = pci_write_config_dword(pcidev, + PCIC_INTRPT_STAT, v); + if (err) { + asd_printk("couldn't write PCIC_INTRPT_STAT of %s\n", + pci_name(pcidev)); + goto out; + } + + asd_hwi_initialize_ocm_dir(asd_ha); + + } +out: + return err; +} + +/** + * asd_read_ocm - read on chip memory (OCM) + * @asd_ha: pointer to the host adapter structure + */ +int asd_read_ocm(struct asd_ha_struct *asd_ha) +{ + int err; + struct asd_ocm_dir *dir; + + if (asd_hwi_check_ocm_access(asd_ha)) + return -1; + + dir = kmalloc(sizeof(*dir), GFP_KERNEL); + if (!dir) { + asd_printk("no memory for ocm dir\n"); + return -ENOMEM; + } + + err = asd_read_ocm_dir(asd_ha, dir, 0); + if (err) + goto out; + + err = asd_get_bios_chim(asd_ha, dir); +out: + kfree(dir); + return err; +} + +/* ---------- FLASH stuff ---------- */ + +#define FLASH_RESET 0xF0 +#define FLASH_MANUF_AMD 1 + +#define FLASH_SIZE 0x200000 +#define FLASH_DIR_COOKIE "*** ADAPTEC FLASH DIRECTORY *** " +#define FLASH_NEXT_ENTRY_OFFS 0x2000 +#define FLASH_MAX_DIR_ENTRIES 32 + +#define FLASH_DE_TYPE_MASK 0x3FFFFFFF +#define FLASH_DE_MS 0x120 +#define FLASH_DE_CTRL_A_USER 0xE0 + +struct asd_flash_de { + __le32 type; + __le32 offs; + __le32 pad_size; + __le32 image_size; + __le32 chksum; + u8 _r[12]; + u8 version[32]; +} __attribute__ ((packed)); + +struct asd_flash_dir { + u8 cookie[32]; + __le32 rev; /* 2 */ + __le32 chksum; + __le32 chksum_antidote; + __le32 bld; + u8 bld_id[32]; /* build id data */ + u8 ver_data[32]; /* date and time of build */ + __le32 ae_mask; + __le32 v_mask; + __le32 oc_mask; + u8 _r[20]; + struct asd_flash_de dir_entry[FLASH_MAX_DIR_ENTRIES]; +} __attribute__ ((packed)); + +struct asd_manuf_sec { + char sig[2]; /* 'S', 'M' */ + u16 offs_next; + u8 maj; /* 0 */ + u8 min; /* 0 */ + u16 chksum; + u16 size; + u8 _r[6]; + u8 sas_addr[SAS_ADDR_SIZE]; + u8 pcba_sn[ASD_PCBA_SN_SIZE]; + /* Here start the other segments */ + u8 linked_list[0]; +} __attribute__ ((packed)); + +struct asd_manuf_phy_desc { + u8 state; /* low 4 bits */ +#define MS_PHY_STATE_ENABLEABLE 0 +#define MS_PHY_STATE_REPORTED 1 +#define MS_PHY_STATE_HIDDEN 2 + u8 phy_id; + u16 _r; + u8 phy_control_0; /* mode 5 reg 0x160 */ + u8 phy_control_1; /* mode 5 reg 0x161 */ + u8 phy_control_2; /* mode 5 reg 0x162 */ + u8 phy_control_3; /* mode 5 reg 0x163 */ +} __attribute__ ((packed)); + +struct asd_manuf_phy_param { + char sig[2]; /* 'P', 'M' */ + u16 next; + u8 maj; /* 0 */ + u8 min; /* 2 */ + u8 num_phy_desc; /* 8 */ + u8 phy_desc_size; /* 8 */ + u8 _r[3]; + u8 usage_model_id; + u32 _r2; + struct asd_manuf_phy_desc phy_desc[ASD_MAX_PHYS]; +} __attribute__ ((packed)); + +#if 0 +static const char *asd_sb_type[] = { + "unknown", + "SGPIO", + [2 ... 0x7F] = "unknown", + [0x80] = "ADPT_I2C", + [0x81 ... 0xFF] = "VENDOR_UNIQUExx" +}; +#endif + +struct asd_ms_sb_desc { + u8 type; + u8 node_desc_index; + u8 conn_desc_index; + u8 _recvd[0]; +} __attribute__ ((packed)); + +#if 0 +static const char *asd_conn_type[] = { + [0 ... 7] = "unknown", + "SFF8470", + "SFF8482", + "SFF8484", + [0x80] = "PCIX_DAUGHTER0", + [0x81] = "SAS_DAUGHTER0", + [0x82 ... 0xFF] = "VENDOR_UNIQUExx" +}; + +static const char *asd_conn_location[] = { + "unknown", + "internal", + "external", + "board_to_board", +}; +#endif + +struct asd_ms_conn_desc { + u8 type; + u8 location; + u8 num_sideband_desc; + u8 size_sideband_desc; + u32 _resvd; + u8 name[16]; + struct asd_ms_sb_desc sb_desc[0]; +} __attribute__ ((packed)); + +struct asd_nd_phy_desc { + u8 vp_attch_type; + u8 attch_specific[0]; +} __attribute__ ((packed)); + +#if 0 +static const char *asd_node_type[] = { + "IOP", + "IO_CONTROLLER", + "EXPANDER", + "PORT_MULTIPLIER", + "PORT_MULTIPLEXER", + "MULTI_DROP_I2C_BUS", +}; +#endif + +struct asd_ms_node_desc { + u8 type; + u8 num_phy_desc; + u8 size_phy_desc; + u8 _resvd; + u8 name[16]; + struct asd_nd_phy_desc phy_desc[0]; +} __attribute__ ((packed)); + +struct asd_ms_conn_map { + char sig[2]; /* 'M', 'C' */ + __le16 next; + u8 maj; /* 0 */ + u8 min; /* 0 */ + __le16 cm_size; /* size of this struct */ + u8 num_conn; + u8 conn_size; + u8 num_nodes; + u8 usage_model_id; + u32 _resvd; + struct asd_ms_conn_desc conn_desc[0]; + struct asd_ms_node_desc node_desc[0]; +} __attribute__ ((packed)); + +struct asd_ctrla_phy_entry { + u8 sas_addr[SAS_ADDR_SIZE]; + u8 sas_link_rates; /* max in hi bits, min in low bits */ + u8 flags; + u8 sata_link_rates; + u8 _r[5]; +} __attribute__ ((packed)); + +struct asd_ctrla_phy_settings { + u8 id0; /* P'h'y */ + u8 _r; + u16 next; + u8 num_phys; /* number of PHYs in the PCI function */ + u8 _r2[3]; + struct asd_ctrla_phy_entry phy_ent[ASD_MAX_PHYS]; +} __attribute__ ((packed)); + +struct asd_ll_el { + u8 id0; + u8 id1; + __le16 next; + u8 something_here[0]; +} __attribute__ ((packed)); + +static int asd_poll_flash(struct asd_ha_struct *asd_ha) +{ + int c; + u8 d; + + for (c = 5000; c > 0; c--) { + d = asd_read_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar); + d ^= asd_read_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar); + if (!d) + return 0; + udelay(5); + } + return -ENOENT; +} + +static int asd_reset_flash(struct asd_ha_struct *asd_ha) +{ + int err; + + err = asd_poll_flash(asd_ha); + if (err) + return err; + asd_write_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar, FLASH_RESET); + err = asd_poll_flash(asd_ha); + + return err; +} + +static inline int asd_read_flash_seg(struct asd_ha_struct *asd_ha, + void *buffer, u32 offs, int size) +{ + asd_read_reg_string(asd_ha, buffer, asd_ha->hw_prof.flash.bar+offs, + size); + return 0; +} + +/** + * asd_find_flash_dir - finds and reads the flash directory + * @asd_ha: pointer to the host adapter structure + * @flash_dir: pointer to flash directory structure + * + * If found, the flash directory segment will be copied to + * @flash_dir. Return 1 if found, 0 if not. + */ +static int asd_find_flash_dir(struct asd_ha_struct *asd_ha, + struct asd_flash_dir *flash_dir) +{ + u32 v; + for (v = 0; v < FLASH_SIZE; v += FLASH_NEXT_ENTRY_OFFS) { + asd_read_flash_seg(asd_ha, flash_dir, v, + sizeof(FLASH_DIR_COOKIE)-1); + if (memcmp(flash_dir->cookie, FLASH_DIR_COOKIE, + sizeof(FLASH_DIR_COOKIE)-1) == 0) { + asd_ha->hw_prof.flash.dir_offs = v; + asd_read_flash_seg(asd_ha, flash_dir, v, + sizeof(*flash_dir)); + return 1; + } + } + return 0; +} + +static int asd_flash_getid(struct asd_ha_struct *asd_ha) +{ + int err = 0; + u32 reg, inc; + + reg = asd_read_reg_dword(asd_ha, EXSICNFGR); + + if (!(reg & FLASHEX)) { + ASD_DPRINTK("flash doesn't exist\n"); + return -ENOENT; + } + if (pci_read_config_dword(asd_ha->pcidev, PCI_CONF_FLSH_BAR, + &asd_ha->hw_prof.flash.bar)) { + asd_printk("couldn't read PCI_CONF_FLSH_BAR of %s\n", + pci_name(asd_ha->pcidev)); + return -ENOENT; + } + asd_ha->hw_prof.flash.present = 1; + asd_ha->hw_prof.flash.wide = reg & FLASHW ? 1 : 0; + err = asd_reset_flash(asd_ha); + if (err) { + ASD_DPRINTK("couldn't reset flash(%d)\n", err); + return err; + } + /* Get flash info. This would most likely be AMD Am29LV family flash. + * First try the sequence for word mode. It is the same as for + * 008B (byte mode only), 160B (word mode) and 800D (word mode). + */ + reg = asd_ha->hw_prof.flash.bar; + inc = asd_ha->hw_prof.flash.wide ? 2 : 1; + asd_write_reg_byte(asd_ha, reg + 0x555, 0xAA); + asd_write_reg_byte(asd_ha, reg + 0x2AA, 0x55); + asd_write_reg_byte(asd_ha, reg + 0x555, 0x90); + asd_ha->hw_prof.flash.manuf = asd_read_reg_byte(asd_ha, reg); + asd_ha->hw_prof.flash.dev_id= asd_read_reg_byte(asd_ha,reg+inc); + asd_ha->hw_prof.flash.sec_prot = asd_read_reg_byte(asd_ha,reg+inc+inc); + /* Get out of autoselect mode. */ + err = asd_reset_flash(asd_ha); + + if (asd_ha->hw_prof.flash.manuf == FLASH_MANUF_AMD) { + ASD_DPRINTK("0Found FLASH(%d) manuf:%d, dev_id:0x%x, " + "sec_prot:%d\n", + asd_ha->hw_prof.flash.wide ? 16 : 8, + asd_ha->hw_prof.flash.manuf, + asd_ha->hw_prof.flash.dev_id, + asd_ha->hw_prof.flash.sec_prot); + return 0; + } + + /* Ok, try the sequence for byte mode of 160B and 800D. + * We may actually never need this. + */ + asd_write_reg_byte(asd_ha, reg + 0xAAA, 0xAA); + asd_write_reg_byte(asd_ha, reg + 0x555, 0x55); + asd_write_reg_byte(asd_ha, reg + 0xAAA, 0x90); + asd_ha->hw_prof.flash.manuf = asd_read_reg_byte(asd_ha, reg); + asd_ha->hw_prof.flash.dev_id = asd_read_reg_byte(asd_ha, reg + 2); + asd_ha->hw_prof.flash.sec_prot = asd_read_reg_byte(asd_ha, reg + 4); + err = asd_reset_flash(asd_ha); + + if (asd_ha->hw_prof.flash.manuf == FLASH_MANUF_AMD) { + ASD_DPRINTK("1Found FLASH(%d) manuf:%d, dev_id:0x%x, " + "sec_prot:%d\n", + asd_ha->hw_prof.flash.wide ? 16 : 8, + asd_ha->hw_prof.flash.manuf, + asd_ha->hw_prof.flash.dev_id, + asd_ha->hw_prof.flash.sec_prot); + return 0; + } + + return -ENOENT; +} + +static u16 asd_calc_flash_chksum(u16 *p, int size) +{ + u16 chksum = 0; + + while (size-- > 0) + chksum += *p++; + + return chksum; +} + + +static int asd_find_flash_de(struct asd_flash_dir *flash_dir, u32 entry_type, + u32 *offs, u32 *size) +{ + int i; + struct asd_flash_de *de; + + for (i = 0; i < FLASH_MAX_DIR_ENTRIES; i++) { + u32 type = le32_to_cpu(flash_dir->dir_entry[i].type); + + type &= FLASH_DE_TYPE_MASK; + if (type == entry_type) + break; + } + if (i >= FLASH_MAX_DIR_ENTRIES) + return -ENOENT; + de = &flash_dir->dir_entry[i]; + *offs = le32_to_cpu(de->offs); + *size = le32_to_cpu(de->pad_size); + return 0; +} + +static int asd_validate_ms(struct asd_manuf_sec *ms) +{ + if (ms->sig[0] != 'S' || ms->sig[1] != 'M') { + ASD_DPRINTK("manuf sec: no valid sig(%c%c)\n", + ms->sig[0], ms->sig[1]); + return -ENOENT; + } + if (ms->maj != 0) { + asd_printk("unsupported manuf. sector. major version:%x\n", + ms->maj); + return -ENOENT; + } + ms->offs_next = le16_to_cpu((__force __le16) ms->offs_next); + ms->chksum = le16_to_cpu((__force __le16) ms->chksum); + ms->size = le16_to_cpu((__force __le16) ms->size); + + if (asd_calc_flash_chksum((u16 *)ms, ms->size/2)) { + asd_printk("failed manuf sector checksum\n"); + } + + return 0; +} + +static int asd_ms_get_sas_addr(struct asd_ha_struct *asd_ha, + struct asd_manuf_sec *ms) +{ + memcpy(asd_ha->hw_prof.sas_addr, ms->sas_addr, SAS_ADDR_SIZE); + return 0; +} + +static int asd_ms_get_pcba_sn(struct asd_ha_struct *asd_ha, + struct asd_manuf_sec *ms) +{ + memcpy(asd_ha->hw_prof.pcba_sn, ms->pcba_sn, ASD_PCBA_SN_SIZE); + asd_ha->hw_prof.pcba_sn[ASD_PCBA_SN_SIZE] = '\0'; + return 0; +} + +/** + * asd_find_ll_by_id - find a linked list entry by its id + * @start: void pointer to the first element in the linked list + * @id0: the first byte of the id (offs 0) + * @id1: the second byte of the id (offs 1) + * + * @start has to be the _base_ element start, since the + * linked list entries's offset is from this pointer. + * Some linked list entries use only the first id, in which case + * you can pass 0xFF for the second. + */ +static void *asd_find_ll_by_id(void * const start, const u8 id0, const u8 id1) +{ + struct asd_ll_el *el = start; + + do { + switch (id1) { + default: + if (el->id1 == id1) + case 0xFF: + if (el->id0 == id0) + return el; + } + el = start + le16_to_cpu(el->next); + } while (el != start); + + return NULL; +} + +/** + * asd_ms_get_phy_params - get phy parameters from the manufacturing sector + * @asd_ha: pointer to the host adapter structure + * @manuf_sec: pointer to the manufacturing sector + * + * The manufacturing sector contans also the linked list of sub-segments, + * since when it was read, its size was taken from the flash directory, + * not from the structure size. + * + * HIDDEN phys do not count in the total count. REPORTED phys cannot + * be enabled but are reported and counted towards the total. + * ENEBLEABLE phys are enabled by default and count towards the total. + * The absolute total phy number is ASD_MAX_PHYS. hw_prof->num_phys + * merely specifies the number of phys the host adapter decided to + * report. E.g., it is possible for phys 0, 1 and 2 to be HIDDEN, + * phys 3, 4 and 5 to be REPORTED and phys 6 and 7 to be ENEBLEABLE. + * In this case ASD_MAX_PHYS is 8, hw_prof->num_phys is 5, and only 2 + * are actually enabled (enabled by default, max number of phys + * enableable in this case). + */ +static int asd_ms_get_phy_params(struct asd_ha_struct *asd_ha, + struct asd_manuf_sec *manuf_sec) +{ + int i; + int en_phys = 0; + int rep_phys = 0; + struct asd_manuf_phy_param *phy_param; + struct asd_manuf_phy_param dflt_phy_param; + + phy_param = asd_find_ll_by_id(manuf_sec, 'P', 'M'); + if (!phy_param) { + ASD_DPRINTK("ms: no phy parameters found\n"); + ASD_DPRINTK("ms: Creating default phy parameters\n"); + dflt_phy_param.sig[0] = 'P'; + dflt_phy_param.sig[1] = 'M'; + dflt_phy_param.maj = 0; + dflt_phy_param.min = 2; + dflt_phy_param.num_phy_desc = 8; + dflt_phy_param.phy_desc_size = sizeof(struct asd_manuf_phy_desc); + for (i =0; i < ASD_MAX_PHYS; i++) { + dflt_phy_param.phy_desc[i].state = 0; + dflt_phy_param.phy_desc[i].phy_id = i; + dflt_phy_param.phy_desc[i].phy_control_0 = 0xf6; + dflt_phy_param.phy_desc[i].phy_control_1 = 0x10; + dflt_phy_param.phy_desc[i].phy_control_2 = 0x43; + dflt_phy_param.phy_desc[i].phy_control_3 = 0xeb; + } + + phy_param = &dflt_phy_param; + + } + + if (phy_param->maj != 0) { + asd_printk("unsupported manuf. phy param major version:0x%x\n", + phy_param->maj); + return -ENOENT; + } + + ASD_DPRINTK("ms: num_phy_desc: %d\n", phy_param->num_phy_desc); + asd_ha->hw_prof.enabled_phys = 0; + for (i = 0; i < phy_param->num_phy_desc; i++) { + struct asd_manuf_phy_desc *pd = &phy_param->phy_desc[i]; + switch (pd->state & 0xF) { + case MS_PHY_STATE_HIDDEN: + ASD_DPRINTK("ms: phy%d: HIDDEN\n", i); + continue; + case MS_PHY_STATE_REPORTED: + ASD_DPRINTK("ms: phy%d: REPORTED\n", i); + asd_ha->hw_prof.enabled_phys &= ~(1 << i); + rep_phys++; + continue; + case MS_PHY_STATE_ENABLEABLE: + ASD_DPRINTK("ms: phy%d: ENEBLEABLE\n", i); + asd_ha->hw_prof.enabled_phys |= (1 << i); + en_phys++; + break; + } + asd_ha->hw_prof.phy_desc[i].phy_control_0 = pd->phy_control_0; + asd_ha->hw_prof.phy_desc[i].phy_control_1 = pd->phy_control_1; + asd_ha->hw_prof.phy_desc[i].phy_control_2 = pd->phy_control_2; + asd_ha->hw_prof.phy_desc[i].phy_control_3 = pd->phy_control_3; + } + asd_ha->hw_prof.max_phys = rep_phys + en_phys; + asd_ha->hw_prof.num_phys = en_phys; + ASD_DPRINTK("ms: max_phys:0x%x, num_phys:0x%x\n", + asd_ha->hw_prof.max_phys, asd_ha->hw_prof.num_phys); + ASD_DPRINTK("ms: enabled_phys:0x%x\n", asd_ha->hw_prof.enabled_phys); + return 0; +} + +static int asd_ms_get_connector_map(struct asd_ha_struct *asd_ha, + struct asd_manuf_sec *manuf_sec) +{ + struct asd_ms_conn_map *cm; + + cm = asd_find_ll_by_id(manuf_sec, 'M', 'C'); + if (!cm) { + ASD_DPRINTK("ms: no connector map found\n"); + return 0; + } + + if (cm->maj != 0) { + ASD_DPRINTK("ms: unsupported: connector map major version 0x%x" + "\n", cm->maj); + return -ENOENT; + } + + /* XXX */ + + return 0; +} + + +/** + * asd_process_ms - find and extract information from the manufacturing sector + * @asd_ha: pointer to the host adapter structure + * @flash_dir: pointer to the flash directory + */ +static int asd_process_ms(struct asd_ha_struct *asd_ha, + struct asd_flash_dir *flash_dir) +{ + int err; + struct asd_manuf_sec *manuf_sec; + u32 offs, size; + + err = asd_find_flash_de(flash_dir, FLASH_DE_MS, &offs, &size); + if (err) { + ASD_DPRINTK("Couldn't find the manuf. sector\n"); + goto out; + } + + if (size == 0) + goto out; + + err = -ENOMEM; + manuf_sec = kmalloc(size, GFP_KERNEL); + if (!manuf_sec) { + ASD_DPRINTK("no mem for manuf sector\n"); + goto out; + } + + err = asd_read_flash_seg(asd_ha, (void *)manuf_sec, offs, size); + if (err) { + ASD_DPRINTK("couldn't read manuf sector at 0x%x, size 0x%x\n", + offs, size); + goto out2; + } + + err = asd_validate_ms(manuf_sec); + if (err) { + ASD_DPRINTK("couldn't validate manuf sector\n"); + goto out2; + } + + err = asd_ms_get_sas_addr(asd_ha, manuf_sec); + if (err) { + ASD_DPRINTK("couldn't read the SAS_ADDR\n"); + goto out2; + } + ASD_DPRINTK("manuf sect SAS_ADDR %llx\n", + SAS_ADDR(asd_ha->hw_prof.sas_addr)); + + err = asd_ms_get_pcba_sn(asd_ha, manuf_sec); + if (err) { + ASD_DPRINTK("couldn't read the PCBA SN\n"); + goto out2; + } + ASD_DPRINTK("manuf sect PCBA SN %s\n", asd_ha->hw_prof.pcba_sn); + + err = asd_ms_get_phy_params(asd_ha, manuf_sec); + if (err) { + ASD_DPRINTK("ms: couldn't get phy parameters\n"); + goto out2; + } + + err = asd_ms_get_connector_map(asd_ha, manuf_sec); + if (err) { + ASD_DPRINTK("ms: couldn't get connector map\n"); + goto out2; + } + +out2: + kfree(manuf_sec); +out: + return err; +} + +static int asd_process_ctrla_phy_settings(struct asd_ha_struct *asd_ha, + struct asd_ctrla_phy_settings *ps) +{ + int i; + for (i = 0; i < ps->num_phys; i++) { + struct asd_ctrla_phy_entry *pe = &ps->phy_ent[i]; + + if (!PHY_ENABLED(asd_ha, i)) + continue; + if (*(u64 *)pe->sas_addr == 0) { + asd_ha->hw_prof.enabled_phys &= ~(1 << i); + continue; + } + /* This is the SAS address which should be sent in IDENTIFY. */ + memcpy(asd_ha->hw_prof.phy_desc[i].sas_addr, pe->sas_addr, + SAS_ADDR_SIZE); + asd_ha->hw_prof.phy_desc[i].max_sas_lrate = + (pe->sas_link_rates & 0xF0) >> 4; + asd_ha->hw_prof.phy_desc[i].min_sas_lrate = + (pe->sas_link_rates & 0x0F); + asd_ha->hw_prof.phy_desc[i].max_sata_lrate = + (pe->sata_link_rates & 0xF0) >> 4; + asd_ha->hw_prof.phy_desc[i].min_sata_lrate = + (pe->sata_link_rates & 0x0F); + asd_ha->hw_prof.phy_desc[i].flags = pe->flags; + ASD_DPRINTK("ctrla: phy%d: sas_addr: %llx, sas rate:0x%x-0x%x," + " sata rate:0x%x-0x%x, flags:0x%x\n", + i, + SAS_ADDR(asd_ha->hw_prof.phy_desc[i].sas_addr), + asd_ha->hw_prof.phy_desc[i].max_sas_lrate, + asd_ha->hw_prof.phy_desc[i].min_sas_lrate, + asd_ha->hw_prof.phy_desc[i].max_sata_lrate, + asd_ha->hw_prof.phy_desc[i].min_sata_lrate, + asd_ha->hw_prof.phy_desc[i].flags); + } + + return 0; +} + +/** + * asd_process_ctrl_a_user - process CTRL-A user settings + * @asd_ha: pointer to the host adapter structure + * @flash_dir: pointer to the flash directory + */ +static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha, + struct asd_flash_dir *flash_dir) +{ + int err, i; + u32 offs, size; + struct asd_ll_el *el; + struct asd_ctrla_phy_settings *ps; + struct asd_ctrla_phy_settings dflt_ps; + + err = asd_find_flash_de(flash_dir, FLASH_DE_CTRL_A_USER, &offs, &size); + if (err) { + ASD_DPRINTK("couldn't find CTRL-A user settings section\n"); + ASD_DPRINTK("Creating default CTRL-A user settings section\n"); + + dflt_ps.id0 = 'h'; + dflt_ps.num_phys = 8; + for (i =0; i < ASD_MAX_PHYS; i++) { + memcpy(dflt_ps.phy_ent[i].sas_addr, + asd_ha->hw_prof.sas_addr, SAS_ADDR_SIZE); + dflt_ps.phy_ent[i].sas_link_rates = 0x98; + dflt_ps.phy_ent[i].flags = 0x0; + dflt_ps.phy_ent[i].sata_link_rates = 0x0; + } + + size = sizeof(struct asd_ctrla_phy_settings); + ps = &dflt_ps; + } + + if (size == 0) + goto out; + + err = -ENOMEM; + el = kmalloc(size, GFP_KERNEL); + if (!el) { + ASD_DPRINTK("no mem for ctrla user settings section\n"); + goto out; + } + + err = asd_read_flash_seg(asd_ha, (void *)el, offs, size); + if (err) { + ASD_DPRINTK("couldn't read ctrla phy settings section\n"); + goto out2; + } + + err = -ENOENT; + ps = asd_find_ll_by_id(el, 'h', 0xFF); + if (!ps) { + ASD_DPRINTK("couldn't find ctrla phy settings struct\n"); + goto out2; + } + + err = asd_process_ctrla_phy_settings(asd_ha, ps); + if (err) { + ASD_DPRINTK("couldn't process ctrla phy settings\n"); + goto out2; + } +out2: + kfree(el); +out: + return err; +} + +/** + * asd_read_flash - read flash memory + * @asd_ha: pointer to the host adapter structure + */ +int asd_read_flash(struct asd_ha_struct *asd_ha) +{ + int err; + struct asd_flash_dir *flash_dir; + + err = asd_flash_getid(asd_ha); + if (err) + return err; + + flash_dir = kmalloc(sizeof(*flash_dir), GFP_KERNEL); + if (!flash_dir) + return -ENOMEM; + + err = -ENOENT; + if (!asd_find_flash_dir(asd_ha, flash_dir)) { + ASD_DPRINTK("couldn't find flash directory\n"); + goto out; + } + + if (le32_to_cpu(flash_dir->rev) != 2) { + asd_printk("unsupported flash dir version:0x%x\n", + le32_to_cpu(flash_dir->rev)); + goto out; + } + + err = asd_process_ms(asd_ha, flash_dir); + if (err) { + ASD_DPRINTK("couldn't process manuf sector settings\n"); + goto out; + } + + err = asd_process_ctrl_a_user(asd_ha, flash_dir); + if (err) { + ASD_DPRINTK("couldn't process CTRL-A user settings\n"); + goto out; + } + +out: + kfree(flash_dir); + return err; +} diff --git a/drivers/scsi/aic94xx/aic94xx_seq.c b/drivers/scsi/aic94xx/aic94xx_seq.c new file mode 100644 index 00000000000..9050c6f3f6b --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx_seq.c @@ -0,0 +1,1401 @@ +/* + * Aic94xx SAS/SATA driver sequencer interface. + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * Parts of this code adapted from David Chaw's adp94xx_seq.c. + * + * This file is licensed under GPLv2. + * + * This file is part of the aic94xx driver. + * + * The aic94xx driver 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; version 2 of the + * License. + * + * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include "aic94xx_reg.h" +#include "aic94xx_hwi.h" + +#include "aic94xx_seq.h" +#include "aic94xx_dump.h" + +/* It takes no more than 0.05 us for an instruction + * to complete. So waiting for 1 us should be more than + * plenty. + */ +#define PAUSE_DELAY 1 +#define PAUSE_TRIES 1000 + +static const struct firmware *sequencer_fw; +static const char *sequencer_version; +static u16 cseq_vecs[CSEQ_NUM_VECS], lseq_vecs[LSEQ_NUM_VECS], mode2_task, + cseq_idle_loop, lseq_idle_loop; +static u8 *cseq_code, *lseq_code; +static u32 cseq_code_size, lseq_code_size; + +static u16 first_scb_site_no = 0xFFFF; +static u16 last_scb_site_no; + +/* ---------- Pause/Unpause CSEQ/LSEQ ---------- */ + +/** + * asd_pause_cseq - pause the central sequencer + * @asd_ha: pointer to host adapter structure + * + * Return 0 on success, negative on failure. + */ +int asd_pause_cseq(struct asd_ha_struct *asd_ha) +{ + int count = PAUSE_TRIES; + u32 arp2ctl; + + arp2ctl = asd_read_reg_dword(asd_ha, CARP2CTL); + if (arp2ctl & PAUSED) + return 0; + + asd_write_reg_dword(asd_ha, CARP2CTL, arp2ctl | EPAUSE); + do { + arp2ctl = asd_read_reg_dword(asd_ha, CARP2CTL); + if (arp2ctl & PAUSED) + return 0; + udelay(PAUSE_DELAY); + } while (--count > 0); + + ASD_DPRINTK("couldn't pause CSEQ\n"); + return -1; +} + +/** + * asd_unpause_cseq - unpause the central sequencer. + * @asd_ha: pointer to host adapter structure. + * + * Return 0 on success, negative on error. + */ +int asd_unpause_cseq(struct asd_ha_struct *asd_ha) +{ + u32 arp2ctl; + int count = PAUSE_TRIES; + + arp2ctl = asd_read_reg_dword(asd_ha, CARP2CTL); + if (!(arp2ctl & PAUSED)) + return 0; + + asd_write_reg_dword(asd_ha, CARP2CTL, arp2ctl & ~EPAUSE); + do { + arp2ctl = asd_read_reg_dword(asd_ha, CARP2CTL); + if (!(arp2ctl & PAUSED)) + return 0; + udelay(PAUSE_DELAY); + } while (--count > 0); + + ASD_DPRINTK("couldn't unpause the CSEQ\n"); + return -1; +} + +/** + * asd_seq_pause_lseq - pause a link sequencer + * @asd_ha: pointer to a host adapter structure + * @lseq: link sequencer of interest + * + * Return 0 on success, negative on error. + */ +static inline int asd_seq_pause_lseq(struct asd_ha_struct *asd_ha, int lseq) +{ + u32 arp2ctl; + int count = PAUSE_TRIES; + + arp2ctl = asd_read_reg_dword(asd_ha, LmARP2CTL(lseq)); + if (arp2ctl & PAUSED) + return 0; + + asd_write_reg_dword(asd_ha, LmARP2CTL(lseq), arp2ctl | EPAUSE); + do { + arp2ctl = asd_read_reg_dword(asd_ha, LmARP2CTL(lseq)); + if (arp2ctl & PAUSED) + return 0; + udelay(PAUSE_DELAY); + } while (--count > 0); + + ASD_DPRINTK("couldn't pause LSEQ %d\n", lseq); + return -1; +} + +/** + * asd_pause_lseq - pause the link sequencer(s) + * @asd_ha: pointer to host adapter structure + * @lseq_mask: mask of link sequencers of interest + * + * Return 0 on success, negative on failure. + */ +int asd_pause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask) +{ + int lseq; + int err = 0; + + for_each_sequencer(lseq_mask, lseq_mask, lseq) { + err = asd_seq_pause_lseq(asd_ha, lseq); + if (err) + return err; + } + + return err; +} + +/** + * asd_seq_unpause_lseq - unpause a link sequencer + * @asd_ha: pointer to host adapter structure + * @lseq: link sequencer of interest + * + * Return 0 on success, negative on error. + */ +static inline int asd_seq_unpause_lseq(struct asd_ha_struct *asd_ha, int lseq) +{ + u32 arp2ctl; + int count = PAUSE_TRIES; + + arp2ctl = asd_read_reg_dword(asd_ha, LmARP2CTL(lseq)); + if (!(arp2ctl & PAUSED)) + return 0; + + asd_write_reg_dword(asd_ha, LmARP2CTL(lseq), arp2ctl & ~EPAUSE); + do { + arp2ctl = asd_read_reg_dword(asd_ha, LmARP2CTL(lseq)); + if (!(arp2ctl & PAUSED)) + return 0; + udelay(PAUSE_DELAY); + } while (--count > 0); + + ASD_DPRINTK("couldn't unpause LSEQ %d\n", lseq); + return 0; +} + + +/** + * asd_unpause_lseq - unpause the link sequencer(s) + * @asd_ha: pointer to host adapter structure + * @lseq_mask: mask of link sequencers of interest + * + * Return 0 on success, negative on failure. + */ +int asd_unpause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask) +{ + int lseq; + int err = 0; + + for_each_sequencer(lseq_mask, lseq_mask, lseq) { + err = asd_seq_unpause_lseq(asd_ha, lseq); + if (err) + return err; + } + + return err; +} + +/* ---------- Downloading CSEQ/LSEQ microcode ---------- */ + +static int asd_verify_cseq(struct asd_ha_struct *asd_ha, const u8 *_prog, + u32 size) +{ + u32 addr = CSEQ_RAM_REG_BASE_ADR; + const u32 *prog = (u32 *) _prog; + u32 i; + + for (i = 0; i < size; i += 4, prog++, addr += 4) { + u32 val = asd_read_reg_dword(asd_ha, addr); + + if (le32_to_cpu(*prog) != val) { + asd_printk("%s: cseq verify failed at %u " + "read:0x%x, wanted:0x%x\n", + pci_name(asd_ha->pcidev), + i, val, le32_to_cpu(*prog)); + return -1; + } + } + ASD_DPRINTK("verified %d bytes, passed\n", size); + return 0; +} + +/** + * asd_verify_lseq - verify the microcode of a link sequencer + * @asd_ha: pointer to host adapter structure + * @_prog: pointer to the microcode + * @size: size of the microcode in bytes + * @lseq: link sequencer of interest + * + * The link sequencer code is accessed in 4 KB pages, which are selected + * by setting LmRAMPAGE (bits 8 and 9) of the LmBISTCTL1 register. + * The 10 KB LSEQm instruction code is mapped, page at a time, at + * LmSEQRAM address. + */ +static int asd_verify_lseq(struct asd_ha_struct *asd_ha, const u8 *_prog, + u32 size, int lseq) +{ +#define LSEQ_CODEPAGE_SIZE 4096 + int pages = (size + LSEQ_CODEPAGE_SIZE - 1) / LSEQ_CODEPAGE_SIZE; + u32 page; + const u32 *prog = (u32 *) _prog; + + for (page = 0; page < pages; page++) { + u32 i; + + asd_write_reg_dword(asd_ha, LmBISTCTL1(lseq), + page << LmRAMPAGE_LSHIFT); + for (i = 0; size > 0 && i < LSEQ_CODEPAGE_SIZE; + i += 4, prog++, size-=4) { + + u32 val = asd_read_reg_dword(asd_ha, LmSEQRAM(lseq)+i); + + if (le32_to_cpu(*prog) != val) { + asd_printk("%s: LSEQ%d verify failed " + "page:%d, offs:%d\n", + pci_name(asd_ha->pcidev), + lseq, page, i); + return -1; + } + } + } + ASD_DPRINTK("LSEQ%d verified %d bytes, passed\n", lseq, + (int)((u8 *)prog-_prog)); + return 0; +} + +/** + * asd_verify_seq -- verify CSEQ/LSEQ microcode + * @asd_ha: pointer to host adapter structure + * @prog: pointer to microcode + * @size: size of the microcode + * @lseq_mask: if 0, verify CSEQ microcode, else mask of LSEQs of interest + * + * Return 0 if microcode is correct, negative on mismatch. + */ +static int asd_verify_seq(struct asd_ha_struct *asd_ha, const u8 *prog, + u32 size, u8 lseq_mask) +{ + if (lseq_mask == 0) + return asd_verify_cseq(asd_ha, prog, size); + else { + int lseq, err; + + for_each_sequencer(lseq_mask, lseq_mask, lseq) { + err = asd_verify_lseq(asd_ha, prog, size, lseq); + if (err) + return err; + } + } + + return 0; +} +#define ASD_DMA_MODE_DOWNLOAD +#ifdef ASD_DMA_MODE_DOWNLOAD +/* This is the size of the CSEQ Mapped instruction page */ +#define MAX_DMA_OVLY_COUNT ((1U << 14)-1) +static int asd_download_seq(struct asd_ha_struct *asd_ha, + const u8 * const prog, u32 size, u8 lseq_mask) +{ + u32 comstaten; + u32 reg; + int page; + const int pages = (size + MAX_DMA_OVLY_COUNT - 1) / MAX_DMA_OVLY_COUNT; + struct asd_dma_tok *token; + int err = 0; + + if (size % 4) { + asd_printk("sequencer program not multiple of 4\n"); + return -1; + } + + asd_pause_cseq(asd_ha); + asd_pause_lseq(asd_ha, 0xFF); + + /* save, disable and clear interrupts */ + comstaten = asd_read_reg_dword(asd_ha, COMSTATEN); + asd_write_reg_dword(asd_ha, COMSTATEN, 0); + asd_write_reg_dword(asd_ha, COMSTAT, COMSTAT_MASK); + + asd_write_reg_dword(asd_ha, CHIMINTEN, RST_CHIMINTEN); + asd_write_reg_dword(asd_ha, CHIMINT, CHIMINT_MASK); + + token = asd_alloc_coherent(asd_ha, MAX_DMA_OVLY_COUNT, GFP_KERNEL); + if (!token) { + asd_printk("out of memory for dma SEQ download\n"); + err = -ENOMEM; + goto out; + } + ASD_DPRINTK("dma-ing %d bytes\n", size); + + for (page = 0; page < pages; page++) { + int i; + u32 left = min(size-page*MAX_DMA_OVLY_COUNT, + (u32)MAX_DMA_OVLY_COUNT); + + memcpy(token->vaddr, prog + page*MAX_DMA_OVLY_COUNT, left); + asd_write_reg_addr(asd_ha, OVLYDMAADR, token->dma_handle); + asd_write_reg_dword(asd_ha, OVLYDMACNT, left); + reg = !page ? RESETOVLYDMA : 0; + reg |= (STARTOVLYDMA | OVLYHALTERR); + reg |= (lseq_mask ? (((u32)lseq_mask) << 8) : OVLYCSEQ); + /* Start DMA. */ + asd_write_reg_dword(asd_ha, OVLYDMACTL, reg); + + for (i = PAUSE_TRIES*100; i > 0; i--) { + u32 dmadone = asd_read_reg_dword(asd_ha, OVLYDMACTL); + if (!(dmadone & OVLYDMAACT)) + break; + udelay(PAUSE_DELAY); + } + } + + reg = asd_read_reg_dword(asd_ha, COMSTAT); + if (!(reg & OVLYDMADONE) || (reg & OVLYERR) + || (asd_read_reg_dword(asd_ha, CHIMINT) & DEVEXCEPT_MASK)){ + asd_printk("%s: error DMA-ing sequencer code\n", + pci_name(asd_ha->pcidev)); + err = -ENODEV; + } + + asd_free_coherent(asd_ha, token); + out: + asd_write_reg_dword(asd_ha, COMSTATEN, comstaten); + + return err ? : asd_verify_seq(asd_ha, prog, size, lseq_mask); +} +#else /* ASD_DMA_MODE_DOWNLOAD */ +static int asd_download_seq(struct asd_ha_struct *asd_ha, const u8 *_prog, + u32 size, u8 lseq_mask) +{ + int i; + u32 reg = 0; + const u32 *prog = (u32 *) _prog; + + if (size % 4) { + asd_printk("sequencer program not multiple of 4\n"); + return -1; + } + + asd_pause_cseq(asd_ha); + asd_pause_lseq(asd_ha, 0xFF); + + reg |= (lseq_mask ? (((u32)lseq_mask) << 8) : OVLYCSEQ); + reg |= PIOCMODE; + + asd_write_reg_dword(asd_ha, OVLYDMACNT, size); + asd_write_reg_dword(asd_ha, OVLYDMACTL, reg); + + ASD_DPRINTK("downloading %s sequencer%s in PIO mode...\n", + lseq_mask ? "LSEQ" : "CSEQ", lseq_mask ? "s" : ""); + + for (i = 0; i < size; i += 4, prog++) + asd_write_reg_dword(asd_ha, SPIODATA, *prog); + + reg = (reg & ~PIOCMODE) | OVLYHALTERR; + asd_write_reg_dword(asd_ha, OVLYDMACTL, reg); + + return asd_verify_seq(asd_ha, _prog, size, lseq_mask); +} +#endif /* ASD_DMA_MODE_DOWNLOAD */ + +/** + * asd_seq_download_seqs - download the sequencer microcode + * @asd_ha: pointer to host adapter structure + * + * Download the central and link sequencer microcode. + */ +static int asd_seq_download_seqs(struct asd_ha_struct *asd_ha) +{ + int err; + + if (!asd_ha->hw_prof.enabled_phys) { + asd_printk("%s: no enabled phys!\n", pci_name(asd_ha->pcidev)); + return -ENODEV; + } + + /* Download the CSEQ */ + ASD_DPRINTK("downloading CSEQ...\n"); + err = asd_download_seq(asd_ha, cseq_code, cseq_code_size, 0); + if (err) { + asd_printk("CSEQ download failed:%d\n", err); + return err; + } + + /* Download the Link Sequencers code. All of the Link Sequencers + * microcode can be downloaded at the same time. + */ + ASD_DPRINTK("downloading LSEQs...\n"); + err = asd_download_seq(asd_ha, lseq_code, lseq_code_size, + asd_ha->hw_prof.enabled_phys); + if (err) { + /* Try it one at a time */ + u8 lseq; + u8 lseq_mask = asd_ha->hw_prof.enabled_phys; + + for_each_sequencer(lseq_mask, lseq_mask, lseq) { + err = asd_download_seq(asd_ha, lseq_code, + lseq_code_size, 1<> 8; + asd_write_reg_byte(asd_ha, CSEQ_FREE_SCB_MASK, (u8)cmdctx); + } + asd_write_reg_word(asd_ha, CSEQ_BUILTIN_FREE_SCB_HEAD, + first_scb_site_no); + asd_write_reg_word(asd_ha, CSEQ_BUILTIN_FREE_SCB_TAIL, + last_scb_site_no); + asd_write_reg_word(asd_ha, CSEQ_EXTENDED_FREE_SCB_HEAD, 0xFFFF); + asd_write_reg_word(asd_ha, CSEQ_EXTENDED_FREE_SCB_TAIL, 0xFFFF); + + /* CSEQ Mode independent, page 7 setup. */ + asd_write_reg_dword(asd_ha, CSEQ_EMPTY_REQ_QUEUE, 0); + asd_write_reg_dword(asd_ha, CSEQ_EMPTY_REQ_QUEUE+4, 0); + asd_write_reg_dword(asd_ha, CSEQ_EMPTY_REQ_COUNT, 0); + asd_write_reg_dword(asd_ha, CSEQ_EMPTY_REQ_COUNT+4, 0); + asd_write_reg_word(asd_ha, CSEQ_Q_EMPTY_HEAD, 0xFFFF); + asd_write_reg_word(asd_ha, CSEQ_Q_EMPTY_TAIL, 0xFFFF); + asd_write_reg_word(asd_ha, CSEQ_NEED_EMPTY_SCB, 0); + asd_write_reg_byte(asd_ha, CSEQ_EMPTY_REQ_HEAD, 0); + asd_write_reg_byte(asd_ha, CSEQ_EMPTY_REQ_TAIL, 0); + asd_write_reg_byte(asd_ha, CSEQ_EMPTY_SCB_OFFSET, 0); + asd_write_reg_word(asd_ha, CSEQ_PRIMITIVE_DATA, 0); + asd_write_reg_dword(asd_ha, CSEQ_TIMEOUT_CONST, 0); +} + +/** + * asd_init_cseq_mdp - initialize CSEQ Mode dependent pages + * @asd_ha: pointer to host adapter structure + */ +static void asd_init_cseq_mdp(struct asd_ha_struct *asd_ha) +{ + int i; + int moffs; + + moffs = CSEQ_PAGE_SIZE * 2; + + /* CSEQ Mode dependent, modes 0-7, page 0 setup. */ + for (i = 0; i < 8; i++) { + asd_write_reg_word(asd_ha, i*moffs+CSEQ_LRM_SAVE_SINDEX, 0); + asd_write_reg_word(asd_ha, i*moffs+CSEQ_LRM_SAVE_SCBPTR, 0); + asd_write_reg_word(asd_ha, i*moffs+CSEQ_Q_LINK_HEAD, 0xFFFF); + asd_write_reg_word(asd_ha, i*moffs+CSEQ_Q_LINK_TAIL, 0xFFFF); + asd_write_reg_byte(asd_ha, i*moffs+CSEQ_LRM_SAVE_SCRPAGE, 0); + } + + /* CSEQ Mode dependent, mode 0-7, page 1 and 2 shall be ignored. */ + + /* CSEQ Mode dependent, mode 8, page 0 setup. */ + asd_write_reg_word(asd_ha, CSEQ_RET_ADDR, 0xFFFF); + asd_write_reg_word(asd_ha, CSEQ_RET_SCBPTR, 0); + asd_write_reg_word(asd_ha, CSEQ_SAVE_SCBPTR, 0); + asd_write_reg_word(asd_ha, CSEQ_EMPTY_TRANS_CTX, 0); + asd_write_reg_word(asd_ha, CSEQ_RESP_LEN, 0); + asd_write_reg_word(asd_ha, CSEQ_TMF_SCBPTR, 0); + asd_write_reg_word(asd_ha, CSEQ_GLOBAL_PREV_SCB, 0); + asd_write_reg_word(asd_ha, CSEQ_GLOBAL_HEAD, 0); + asd_write_reg_word(asd_ha, CSEQ_CLEAR_LU_HEAD, 0); + asd_write_reg_byte(asd_ha, CSEQ_TMF_OPCODE, 0); + asd_write_reg_byte(asd_ha, CSEQ_SCRATCH_FLAGS, 0); + asd_write_reg_word(asd_ha, CSEQ_HSB_SITE, 0); + asd_write_reg_word(asd_ha, CSEQ_FIRST_INV_SCB_SITE, + (u16)last_scb_site_no+1); + asd_write_reg_word(asd_ha, CSEQ_FIRST_INV_DDB_SITE, + (u16)asd_ha->hw_prof.max_ddbs); + + /* CSEQ Mode dependent, mode 8, page 1 setup. */ + asd_write_reg_dword(asd_ha, CSEQ_LUN_TO_CLEAR, 0); + asd_write_reg_dword(asd_ha, CSEQ_LUN_TO_CLEAR + 4, 0); + asd_write_reg_dword(asd_ha, CSEQ_LUN_TO_CHECK, 0); + asd_write_reg_dword(asd_ha, CSEQ_LUN_TO_CHECK + 4, 0); + + /* CSEQ Mode dependent, mode 8, page 2 setup. */ + /* Tell the sequencer the bus address of the first SCB. */ + asd_write_reg_addr(asd_ha, CSEQ_HQ_NEW_POINTER, + asd_ha->seq.next_scb.dma_handle); + ASD_DPRINTK("First SCB dma_handle: 0x%llx\n", + (unsigned long long)asd_ha->seq.next_scb.dma_handle); + + /* Tell the sequencer the first Done List entry address. */ + asd_write_reg_addr(asd_ha, CSEQ_HQ_DONE_BASE, + asd_ha->seq.actual_dl->dma_handle); + + /* Initialize the Q_DONE_POINTER with the least significant + * 4 bytes of the first Done List address. */ + asd_write_reg_dword(asd_ha, CSEQ_HQ_DONE_POINTER, + ASD_BUSADDR_LO(asd_ha->seq.actual_dl->dma_handle)); + + asd_write_reg_byte(asd_ha, CSEQ_HQ_DONE_PASS, ASD_DEF_DL_TOGGLE); + + /* CSEQ Mode dependent, mode 8, page 3 shall be ignored. */ +} + +/** + * asd_init_cseq_scratch -- setup and init CSEQ + * @asd_ha: pointer to host adapter structure + * + * Setup and initialize Central sequencers. Initialiaze the mode + * independent and dependent scratch page to the default settings. + */ +static void asd_init_cseq_scratch(struct asd_ha_struct *asd_ha) +{ + asd_init_cseq_mip(asd_ha); + asd_init_cseq_mdp(asd_ha); +} + +/** + * asd_init_lseq_mip -- initialize LSEQ Mode independent pages 0-3 + * @asd_ha: pointer to host adapter structure + */ +static void asd_init_lseq_mip(struct asd_ha_struct *asd_ha, u8 lseq) +{ + int i; + + /* LSEQ Mode independent page 0 setup. */ + asd_write_reg_word(asd_ha, LmSEQ_Q_TGTXFR_HEAD(lseq), 0xFFFF); + asd_write_reg_word(asd_ha, LmSEQ_Q_TGTXFR_TAIL(lseq), 0xFFFF); + asd_write_reg_byte(asd_ha, LmSEQ_LINK_NUMBER(lseq), lseq); + asd_write_reg_byte(asd_ha, LmSEQ_SCRATCH_FLAGS(lseq), + ASD_NOTIFY_ENABLE_SPINUP); + asd_write_reg_dword(asd_ha, LmSEQ_CONNECTION_STATE(lseq),0x08000000); + asd_write_reg_word(asd_ha, LmSEQ_CONCTL(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_CONSTAT(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_CONNECTION_MODES(lseq), 0); + asd_write_reg_word(asd_ha, LmSEQ_REG1_ISR(lseq), 0); + asd_write_reg_word(asd_ha, LmSEQ_REG2_ISR(lseq), 0); + asd_write_reg_word(asd_ha, LmSEQ_REG3_ISR(lseq), 0); + asd_write_reg_dword(asd_ha, LmSEQ_REG0_ISR(lseq), 0); + asd_write_reg_dword(asd_ha, LmSEQ_REG0_ISR(lseq)+4, 0); + + /* LSEQ Mode independent page 1 setup. */ + asd_write_reg_word(asd_ha, LmSEQ_EST_NEXUS_SCBPTR0(lseq), 0xFFFF); + asd_write_reg_word(asd_ha, LmSEQ_EST_NEXUS_SCBPTR1(lseq), 0xFFFF); + asd_write_reg_word(asd_ha, LmSEQ_EST_NEXUS_SCBPTR2(lseq), 0xFFFF); + asd_write_reg_word(asd_ha, LmSEQ_EST_NEXUS_SCBPTR3(lseq), 0xFFFF); + asd_write_reg_byte(asd_ha, LmSEQ_EST_NEXUS_SCB_OPCODE0(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_EST_NEXUS_SCB_OPCODE1(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_EST_NEXUS_SCB_OPCODE2(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_EST_NEXUS_SCB_OPCODE3(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_EST_NEXUS_SCB_HEAD(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_EST_NEXUS_SCB_TAIL(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_EST_NEXUS_BUF_AVAIL(lseq), 0); + asd_write_reg_dword(asd_ha, LmSEQ_TIMEOUT_CONST(lseq), 0); + asd_write_reg_word(asd_ha, LmSEQ_ISR_SAVE_SINDEX(lseq), 0); + asd_write_reg_word(asd_ha, LmSEQ_ISR_SAVE_DINDEX(lseq), 0); + + /* LSEQ Mode Independent page 2 setup. */ + asd_write_reg_word(asd_ha, LmSEQ_EMPTY_SCB_PTR0(lseq), 0xFFFF); + asd_write_reg_word(asd_ha, LmSEQ_EMPTY_SCB_PTR1(lseq), 0xFFFF); + asd_write_reg_word(asd_ha, LmSEQ_EMPTY_SCB_PTR2(lseq), 0xFFFF); + asd_write_reg_word(asd_ha, LmSEQ_EMPTY_SCB_PTR3(lseq), 0xFFFF); + asd_write_reg_byte(asd_ha, LmSEQ_EMPTY_SCB_OPCD0(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_EMPTY_SCB_OPCD1(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_EMPTY_SCB_OPCD2(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_EMPTY_SCB_OPCD3(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_EMPTY_SCB_HEAD(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_EMPTY_SCB_TAIL(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_EMPTY_BUFS_AVAIL(lseq), 0); + for (i = 0; i < 12; i += 4) + asd_write_reg_dword(asd_ha, LmSEQ_ATA_SCR_REGS(lseq) + i, 0); + + /* LSEQ Mode Independent page 3 setup. */ + + /* Device present timer timeout */ + asd_write_reg_dword(asd_ha, LmSEQ_DEV_PRES_TMR_TOUT_CONST(lseq), + ASD_DEV_PRESENT_TIMEOUT); + + /* SATA interlock timer disabled */ + asd_write_reg_dword(asd_ha, LmSEQ_SATA_INTERLOCK_TIMEOUT(lseq), + ASD_SATA_INTERLOCK_TIMEOUT); + + /* STP shutdown timer timeout constant, IGNORED by the sequencer, + * always 0. */ + asd_write_reg_dword(asd_ha, LmSEQ_STP_SHUTDOWN_TIMEOUT(lseq), + ASD_STP_SHUTDOWN_TIMEOUT); + + asd_write_reg_dword(asd_ha, LmSEQ_SRST_ASSERT_TIMEOUT(lseq), + ASD_SRST_ASSERT_TIMEOUT); + + asd_write_reg_dword(asd_ha, LmSEQ_RCV_FIS_TIMEOUT(lseq), + ASD_RCV_FIS_TIMEOUT); + + asd_write_reg_dword(asd_ha, LmSEQ_ONE_MILLISEC_TIMEOUT(lseq), + ASD_ONE_MILLISEC_TIMEOUT); + + /* COM_INIT timer */ + asd_write_reg_dword(asd_ha, LmSEQ_TEN_MS_COMINIT_TIMEOUT(lseq), + ASD_TEN_MILLISEC_TIMEOUT); + + asd_write_reg_dword(asd_ha, LmSEQ_SMP_RCV_TIMEOUT(lseq), + ASD_SMP_RCV_TIMEOUT); +} + +/** + * asd_init_lseq_mdp -- initialize LSEQ mode dependent pages. + * @asd_ha: pointer to host adapter structure + */ +static void asd_init_lseq_mdp(struct asd_ha_struct *asd_ha, int lseq) +{ + int i; + u32 moffs; + u16 ret_addr[] = { + 0xFFFF, /* mode 0 */ + 0xFFFF, /* mode 1 */ + mode2_task, /* mode 2 */ + 0, + 0xFFFF, /* mode 4/5 */ + 0xFFFF, /* mode 4/5 */ + }; + + /* + * Mode 0,1,2 and 4/5 have common field on page 0 for the first + * 14 bytes. + */ + for (i = 0; i < 3; i++) { + moffs = i * LSEQ_MODE_SCRATCH_SIZE; + asd_write_reg_word(asd_ha, LmSEQ_RET_ADDR(lseq)+moffs, + ret_addr[i]); + asd_write_reg_word(asd_ha, LmSEQ_REG0_MODE(lseq)+moffs, 0); + asd_write_reg_word(asd_ha, LmSEQ_MODE_FLAGS(lseq)+moffs, 0); + asd_write_reg_word(asd_ha, LmSEQ_RET_ADDR2(lseq)+moffs,0xFFFF); + asd_write_reg_word(asd_ha, LmSEQ_RET_ADDR1(lseq)+moffs,0xFFFF); + asd_write_reg_byte(asd_ha, LmSEQ_OPCODE_TO_CSEQ(lseq)+moffs,0); + asd_write_reg_word(asd_ha, LmSEQ_DATA_TO_CSEQ(lseq)+moffs,0); + } + /* + * Mode 5 page 0 overlaps the same scratch page with Mode 0 page 3. + */ + asd_write_reg_word(asd_ha, + LmSEQ_RET_ADDR(lseq)+LSEQ_MODE5_PAGE0_OFFSET, + ret_addr[5]); + asd_write_reg_word(asd_ha, + LmSEQ_REG0_MODE(lseq)+LSEQ_MODE5_PAGE0_OFFSET,0); + asd_write_reg_word(asd_ha, + LmSEQ_MODE_FLAGS(lseq)+LSEQ_MODE5_PAGE0_OFFSET, 0); + asd_write_reg_word(asd_ha, + LmSEQ_RET_ADDR2(lseq)+LSEQ_MODE5_PAGE0_OFFSET,0xFFFF); + asd_write_reg_word(asd_ha, + LmSEQ_RET_ADDR1(lseq)+LSEQ_MODE5_PAGE0_OFFSET,0xFFFF); + asd_write_reg_byte(asd_ha, + LmSEQ_OPCODE_TO_CSEQ(lseq)+LSEQ_MODE5_PAGE0_OFFSET,0); + asd_write_reg_word(asd_ha, + LmSEQ_DATA_TO_CSEQ(lseq)+LSEQ_MODE5_PAGE0_OFFSET, 0); + + /* LSEQ Mode dependent 0, page 0 setup. */ + asd_write_reg_word(asd_ha, LmSEQ_FIRST_INV_DDB_SITE(lseq), + (u16)asd_ha->hw_prof.max_ddbs); + asd_write_reg_word(asd_ha, LmSEQ_EMPTY_TRANS_CTX(lseq), 0); + asd_write_reg_word(asd_ha, LmSEQ_RESP_LEN(lseq), 0); + asd_write_reg_word(asd_ha, LmSEQ_FIRST_INV_SCB_SITE(lseq), + (u16)last_scb_site_no+1); + asd_write_reg_word(asd_ha, LmSEQ_INTEN_SAVE(lseq), + (u16) LmM0INTEN_MASK & 0xFFFF0000 >> 16); + asd_write_reg_word(asd_ha, LmSEQ_INTEN_SAVE(lseq) + 2, + (u16) LmM0INTEN_MASK & 0xFFFF); + asd_write_reg_byte(asd_ha, LmSEQ_LINK_RST_FRM_LEN(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_LINK_RST_PROTOCOL(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_RESP_STATUS(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_LAST_LOADED_SGE(lseq), 0); + asd_write_reg_word(asd_ha, LmSEQ_SAVE_SCBPTR(lseq), 0); + + /* LSEQ mode dependent, mode 1, page 0 setup. */ + asd_write_reg_word(asd_ha, LmSEQ_Q_XMIT_HEAD(lseq), 0xFFFF); + asd_write_reg_word(asd_ha, LmSEQ_M1_EMPTY_TRANS_CTX(lseq), 0); + asd_write_reg_word(asd_ha, LmSEQ_INI_CONN_TAG(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_FAILED_OPEN_STATUS(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_XMIT_REQUEST_TYPE(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_M1_RESP_STATUS(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_M1_LAST_LOADED_SGE(lseq), 0); + asd_write_reg_word(asd_ha, LmSEQ_M1_SAVE_SCBPTR(lseq), 0); + + /* LSEQ Mode dependent mode 2, page 0 setup */ + asd_write_reg_word(asd_ha, LmSEQ_PORT_COUNTER(lseq), 0); + asd_write_reg_word(asd_ha, LmSEQ_PM_TABLE_PTR(lseq), 0); + asd_write_reg_word(asd_ha, LmSEQ_SATA_INTERLOCK_TMR_SAVE(lseq), 0); + asd_write_reg_word(asd_ha, LmSEQ_IP_BITL(lseq), 0); + asd_write_reg_word(asd_ha, LmSEQ_COPY_SMP_CONN_TAG(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_P0M2_OFFS1AH(lseq), 0); + + /* LSEQ Mode dependent, mode 4/5, page 0 setup. */ + asd_write_reg_byte(asd_ha, LmSEQ_SAVED_OOB_STATUS(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_SAVED_OOB_MODE(lseq), 0); + asd_write_reg_word(asd_ha, LmSEQ_Q_LINK_HEAD(lseq), 0xFFFF); + asd_write_reg_byte(asd_ha, LmSEQ_LINK_RST_ERR(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_SAVED_OOB_SIGNALS(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_SAS_RESET_MODE(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_LINK_RESET_RETRY_COUNT(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_NUM_LINK_RESET_RETRIES(lseq), 0); + asd_write_reg_word(asd_ha, LmSEQ_OOB_INT_ENABLES(lseq), 0); + /* + * Set the desired interval between transmissions of the NOTIFY + * (ENABLE SPINUP) primitive. Must be initilized to val - 1. + */ + asd_write_reg_word(asd_ha, LmSEQ_NOTIFY_TIMER_TIMEOUT(lseq), + ASD_NOTIFY_TIMEOUT - 1); + /* No delay for the first NOTIFY to be sent to the attached target. */ + asd_write_reg_word(asd_ha, LmSEQ_NOTIFY_TIMER_DOWN_COUNT(lseq), + ASD_NOTIFY_DOWN_COUNT); + + /* LSEQ Mode dependent, mode 0 and 1, page 1 setup. */ + for (i = 0; i < 2; i++) { + int j; + /* Start from Page 1 of Mode 0 and 1. */ + moffs = LSEQ_PAGE_SIZE + i*LSEQ_MODE_SCRATCH_SIZE; + /* All the fields of page 1 can be intialized to 0. */ + for (j = 0; j < LSEQ_PAGE_SIZE; j += 4) + asd_write_reg_dword(asd_ha, LmSCRATCH(lseq)+moffs+j,0); + } + + /* LSEQ Mode dependent, mode 2, page 1 setup. */ + asd_write_reg_dword(asd_ha, LmSEQ_INVALID_DWORD_COUNT(lseq), 0); + asd_write_reg_dword(asd_ha, LmSEQ_DISPARITY_ERROR_COUNT(lseq), 0); + asd_write_reg_dword(asd_ha, LmSEQ_LOSS_OF_SYNC_COUNT(lseq), 0); + + /* LSEQ Mode dependent, mode 4/5, page 1. */ + for (i = 0; i < LSEQ_PAGE_SIZE; i+=4) + asd_write_reg_dword(asd_ha, LmSEQ_FRAME_TYPE_MASK(lseq)+i, 0); + asd_write_reg_byte(asd_ha, LmSEQ_FRAME_TYPE_MASK(lseq), 0xFF); + asd_write_reg_byte(asd_ha, LmSEQ_HASHED_DEST_ADDR_MASK(lseq), 0xFF); + asd_write_reg_byte(asd_ha, LmSEQ_HASHED_DEST_ADDR_MASK(lseq)+1,0xFF); + asd_write_reg_byte(asd_ha, LmSEQ_HASHED_DEST_ADDR_MASK(lseq)+2,0xFF); + asd_write_reg_byte(asd_ha, LmSEQ_HASHED_SRC_ADDR_MASK(lseq), 0xFF); + asd_write_reg_byte(asd_ha, LmSEQ_HASHED_SRC_ADDR_MASK(lseq)+1, 0xFF); + asd_write_reg_byte(asd_ha, LmSEQ_HASHED_SRC_ADDR_MASK(lseq)+2, 0xFF); + asd_write_reg_dword(asd_ha, LmSEQ_DATA_OFFSET(lseq), 0xFFFFFFFF); + + /* LSEQ Mode dependent, mode 0, page 2 setup. */ + asd_write_reg_dword(asd_ha, LmSEQ_SMP_RCV_TIMER_TERM_TS(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_DEVICE_BITS(lseq), 0); + asd_write_reg_word(asd_ha, LmSEQ_SDB_DDB(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_SDB_NUM_TAGS(lseq), 0); + asd_write_reg_byte(asd_ha, LmSEQ_SDB_CURR_TAG(lseq), 0); + + /* LSEQ Mode Dependent 1, page 2 setup. */ + asd_write_reg_dword(asd_ha, LmSEQ_TX_ID_ADDR_FRAME(lseq), 0); + asd_write_reg_dword(asd_ha, LmSEQ_TX_ID_ADDR_FRAME(lseq)+4, 0); + asd_write_reg_dword(asd_ha, LmSEQ_OPEN_TIMER_TERM_TS(lseq), 0); + asd_write_reg_dword(asd_ha, LmSEQ_SRST_AS_TIMER_TERM_TS(lseq), 0); + asd_write_reg_dword(asd_ha, LmSEQ_LAST_LOADED_SG_EL(lseq), 0); + + /* LSEQ Mode Dependent 2, page 2 setup. */ + /* The LmSEQ_STP_SHUTDOWN_TIMER_TERM_TS is IGNORED by the sequencer, + * i.e. always 0. */ + asd_write_reg_dword(asd_ha, LmSEQ_STP_SHUTDOWN_TIMER_TERM_TS(lseq),0); + asd_write_reg_dword(asd_ha, LmSEQ_CLOSE_TIMER_TERM_TS(lseq), 0); + asd_write_reg_dword(asd_ha, LmSEQ_BREAK_TIMER_TERM_TS(lseq), 0); + asd_write_reg_dword(asd_ha, LmSEQ_DWS_RESET_TIMER_TERM_TS(lseq), 0); + asd_write_reg_dword(asd_ha,LmSEQ_SATA_INTERLOCK_TIMER_TERM_TS(lseq),0); + asd_write_reg_dword(asd_ha, LmSEQ_MCTL_TIMER_TERM_TS(lseq), 0); + + /* LSEQ Mode Dependent 4/5, page 2 setup. */ + asd_write_reg_dword(asd_ha, LmSEQ_COMINIT_TIMER_TERM_TS(lseq), 0); + asd_write_reg_dword(asd_ha, LmSEQ_RCV_ID_TIMER_TERM_TS(lseq), 0); + asd_write_reg_dword(asd_ha, LmSEQ_RCV_FIS_TIMER_TERM_TS(lseq), 0); + asd_write_reg_dword(asd_ha, LmSEQ_DEV_PRES_TIMER_TERM_TS(lseq), 0); +} + +/** + * asd_init_lseq_scratch -- setup and init link sequencers + * @asd_ha: pointer to host adapter struct + */ +static void asd_init_lseq_scratch(struct asd_ha_struct *asd_ha) +{ + u8 lseq; + u8 lseq_mask; + + lseq_mask = asd_ha->hw_prof.enabled_phys; + for_each_sequencer(lseq_mask, lseq_mask, lseq) { + asd_init_lseq_mip(asd_ha, lseq); + asd_init_lseq_mdp(asd_ha, lseq); + } +} + +/** + * asd_init_scb_sites -- initialize sequencer SCB sites (memory). + * @asd_ha: pointer to host adapter structure + * + * This should be done before initializing common CSEQ and LSEQ + * scratch since those areas depend on some computed values here, + * last_scb_site_no, etc. + */ +static void asd_init_scb_sites(struct asd_ha_struct *asd_ha) +{ + u16 site_no; + u16 max_scbs = 0; + + for (site_no = asd_ha->hw_prof.max_scbs-1; + site_no != (u16) -1; + site_no--) { + u16 i; + + /* Initialize all fields in the SCB site to 0. */ + for (i = 0; i < ASD_SCB_SIZE; i += 4) + asd_scbsite_write_dword(asd_ha, site_no, i, 0); + + /* Workaround needed by SEQ to fix a SATA issue is to exclude + * certain SCB sites from the free list. */ + if (!SCB_SITE_VALID(site_no)) + continue; + + if (last_scb_site_no == 0) + last_scb_site_no = site_no; + + /* For every SCB site, we need to initialize the + * following fields: Q_NEXT, SCB_OPCODE, SCB_FLAGS, + * and SG Element Flag. */ + + /* Q_NEXT field of the last SCB is invalidated. */ + asd_scbsite_write_word(asd_ha, site_no, 0, first_scb_site_no); + + /* Initialize SCB Site Opcode field to invalid. */ + asd_scbsite_write_byte(asd_ha, site_no, + offsetof(struct scb_header, opcode), + 0xFF); + + /* Initialize SCB Site Flags field to mean a response + * frame has been received. This means inadvertent + * frames received to be dropped. */ + asd_scbsite_write_byte(asd_ha, site_no, 0x49, 0x01); + + first_scb_site_no = site_no; + max_scbs++; + } + asd_ha->hw_prof.max_scbs = max_scbs; + ASD_DPRINTK("max_scbs:%d\n", asd_ha->hw_prof.max_scbs); + ASD_DPRINTK("first_scb_site_no:0x%x\n", first_scb_site_no); + ASD_DPRINTK("last_scb_site_no:0x%x\n", last_scb_site_no); +} + +/** + * asd_init_cseq_cio - initialize CSEQ CIO registers + * @asd_ha: pointer to host adapter structure + */ +static void asd_init_cseq_cio(struct asd_ha_struct *asd_ha) +{ + int i; + + asd_write_reg_byte(asd_ha, CSEQCOMINTEN, 0); + asd_write_reg_byte(asd_ha, CSEQDLCTL, ASD_DL_SIZE_BITS); + asd_write_reg_byte(asd_ha, CSEQDLOFFS, 0); + asd_write_reg_byte(asd_ha, CSEQDLOFFS+1, 0); + asd_ha->seq.scbpro = 0; + asd_write_reg_dword(asd_ha, SCBPRO, 0); + asd_write_reg_dword(asd_ha, CSEQCON, 0); + + /* Intialize CSEQ Mode 11 Interrupt Vectors. + * The addresses are 16 bit wide and in dword units. + * The values of their macros are in byte units. + * Thus we have to divide by 4. */ + asd_write_reg_word(asd_ha, CM11INTVEC0, cseq_vecs[0]); + asd_write_reg_word(asd_ha, CM11INTVEC1, cseq_vecs[1]); + asd_write_reg_word(asd_ha, CM11INTVEC2, cseq_vecs[2]); + + /* Enable ARP2HALTC (ARP2 Halted from Halt Code Write). */ + asd_write_reg_byte(asd_ha, CARP2INTEN, EN_ARP2HALTC); + + /* Initialize CSEQ Scratch Page to 0x04. */ + asd_write_reg_byte(asd_ha, CSCRATCHPAGE, 0x04); + + /* Initialize CSEQ Mode[0-8] Dependent registers. */ + /* Initialize Scratch Page to 0. */ + for (i = 0; i < 9; i++) + asd_write_reg_byte(asd_ha, CMnSCRATCHPAGE(i), 0); + + /* Reset the ARP2 Program Count. */ + asd_write_reg_word(asd_ha, CPRGMCNT, cseq_idle_loop); + + for (i = 0; i < 8; i++) { + /* Intialize Mode n Link m Interrupt Enable. */ + asd_write_reg_dword(asd_ha, CMnINTEN(i), EN_CMnRSPMBXF); + /* Initialize Mode n Request Mailbox. */ + asd_write_reg_dword(asd_ha, CMnREQMBX(i), 0); + } +} + +/** + * asd_init_lseq_cio -- initialize LmSEQ CIO registers + * @asd_ha: pointer to host adapter structure + */ +static void asd_init_lseq_cio(struct asd_ha_struct *asd_ha, int lseq) +{ + u8 *sas_addr; + int i; + + /* Enable ARP2HALTC (ARP2 Halted from Halt Code Write). */ + asd_write_reg_dword(asd_ha, LmARP2INTEN(lseq), EN_ARP2HALTC); + + asd_write_reg_byte(asd_ha, LmSCRATCHPAGE(lseq), 0); + + /* Initialize Mode 0,1, and 2 SCRATCHPAGE to 0. */ + for (i = 0; i < 3; i++) + asd_write_reg_byte(asd_ha, LmMnSCRATCHPAGE(lseq, i), 0); + + /* Initialize Mode 5 SCRATCHPAGE to 0. */ + asd_write_reg_byte(asd_ha, LmMnSCRATCHPAGE(lseq, 5), 0); + + asd_write_reg_dword(asd_ha, LmRSPMBX(lseq), 0); + /* Initialize Mode 0,1,2 and 5 Interrupt Enable and + * Interrupt registers. */ + asd_write_reg_dword(asd_ha, LmMnINTEN(lseq, 0), LmM0INTEN_MASK); + asd_write_reg_dword(asd_ha, LmMnINT(lseq, 0), 0xFFFFFFFF); + /* Mode 1 */ + asd_write_reg_dword(asd_ha, LmMnINTEN(lseq, 1), LmM1INTEN_MASK); + asd_write_reg_dword(asd_ha, LmMnINT(lseq, 1), 0xFFFFFFFF); + /* Mode 2 */ + asd_write_reg_dword(asd_ha, LmMnINTEN(lseq, 2), LmM2INTEN_MASK); + asd_write_reg_dword(asd_ha, LmMnINT(lseq, 2), 0xFFFFFFFF); + /* Mode 5 */ + asd_write_reg_dword(asd_ha, LmMnINTEN(lseq, 5), LmM5INTEN_MASK); + asd_write_reg_dword(asd_ha, LmMnINT(lseq, 5), 0xFFFFFFFF); + + /* Enable HW Timer status. */ + asd_write_reg_byte(asd_ha, LmHWTSTATEN(lseq), LmHWTSTATEN_MASK); + + /* Enable Primitive Status 0 and 1. */ + asd_write_reg_dword(asd_ha, LmPRIMSTAT0EN(lseq), LmPRIMSTAT0EN_MASK); + asd_write_reg_dword(asd_ha, LmPRIMSTAT1EN(lseq), LmPRIMSTAT1EN_MASK); + + /* Enable Frame Error. */ + asd_write_reg_dword(asd_ha, LmFRMERREN(lseq), LmFRMERREN_MASK); + asd_write_reg_byte(asd_ha, LmMnHOLDLVL(lseq, 0), 0x50); + + /* Initialize Mode 0 Transfer Level to 512. */ + asd_write_reg_byte(asd_ha, LmMnXFRLVL(lseq, 0), LmMnXFRLVL_512); + /* Initialize Mode 1 Transfer Level to 256. */ + asd_write_reg_byte(asd_ha, LmMnXFRLVL(lseq, 1), LmMnXFRLVL_256); + + /* Initialize Program Count. */ + asd_write_reg_word(asd_ha, LmPRGMCNT(lseq), lseq_idle_loop); + + /* Enable Blind SG Move. */ + asd_write_reg_dword(asd_ha, LmMODECTL(lseq), LmBLIND48); + asd_write_reg_word(asd_ha, LmM3SATATIMER(lseq), + ASD_SATA_INTERLOCK_TIMEOUT); + + (void) asd_read_reg_dword(asd_ha, LmREQMBX(lseq)); + + /* Clear Primitive Status 0 and 1. */ + asd_write_reg_dword(asd_ha, LmPRMSTAT0(lseq), 0xFFFFFFFF); + asd_write_reg_dword(asd_ha, LmPRMSTAT1(lseq), 0xFFFFFFFF); + + /* Clear HW Timer status. */ + asd_write_reg_byte(asd_ha, LmHWTSTAT(lseq), 0xFF); + + /* Clear DMA Errors for Mode 0 and 1. */ + asd_write_reg_byte(asd_ha, LmMnDMAERRS(lseq, 0), 0xFF); + asd_write_reg_byte(asd_ha, LmMnDMAERRS(lseq, 1), 0xFF); + + /* Clear SG DMA Errors for Mode 0 and 1. */ + asd_write_reg_byte(asd_ha, LmMnSGDMAERRS(lseq, 0), 0xFF); + asd_write_reg_byte(asd_ha, LmMnSGDMAERRS(lseq, 1), 0xFF); + + /* Clear Mode 0 Buffer Parity Error. */ + asd_write_reg_byte(asd_ha, LmMnBUFSTAT(lseq, 0), LmMnBUFPERR); + + /* Clear Mode 0 Frame Error register. */ + asd_write_reg_dword(asd_ha, LmMnFRMERR(lseq, 0), 0xFFFFFFFF); + + /* Reset LSEQ external interrupt arbiter. */ + asd_write_reg_byte(asd_ha, LmARP2INTCTL(lseq), RSTINTCTL); + + /* Set the Phy SAS for the LmSEQ WWN. */ + sas_addr = asd_ha->phys[lseq].phy_desc->sas_addr; + for (i = 0; i < SAS_ADDR_SIZE; i++) + asd_write_reg_byte(asd_ha, LmWWN(lseq) + i, sas_addr[i]); + + /* Set the Transmit Size to 1024 bytes, 0 = 256 Dwords. */ + asd_write_reg_byte(asd_ha, LmMnXMTSIZE(lseq, 1), 0); + + /* Set the Bus Inactivity Time Limit Timer. */ + asd_write_reg_word(asd_ha, LmBITL_TIMER(lseq), 9); + + /* Enable SATA Port Multiplier. */ + asd_write_reg_byte(asd_ha, LmMnSATAFS(lseq, 1), 0x80); + + /* Initialize Interrupt Vector[0-10] address in Mode 3. + * See the comment on CSEQ_INT_* */ + asd_write_reg_word(asd_ha, LmM3INTVEC0(lseq), lseq_vecs[0]); + asd_write_reg_word(asd_ha, LmM3INTVEC1(lseq), lseq_vecs[1]); + asd_write_reg_word(asd_ha, LmM3INTVEC2(lseq), lseq_vecs[2]); + asd_write_reg_word(asd_ha, LmM3INTVEC3(lseq), lseq_vecs[3]); + asd_write_reg_word(asd_ha, LmM3INTVEC4(lseq), lseq_vecs[4]); + asd_write_reg_word(asd_ha, LmM3INTVEC5(lseq), lseq_vecs[5]); + asd_write_reg_word(asd_ha, LmM3INTVEC6(lseq), lseq_vecs[6]); + asd_write_reg_word(asd_ha, LmM3INTVEC7(lseq), lseq_vecs[7]); + asd_write_reg_word(asd_ha, LmM3INTVEC8(lseq), lseq_vecs[8]); + asd_write_reg_word(asd_ha, LmM3INTVEC9(lseq), lseq_vecs[9]); + asd_write_reg_word(asd_ha, LmM3INTVEC10(lseq), lseq_vecs[10]); + /* + * Program the Link LED control, applicable only for + * Chip Rev. B or later. + */ + asd_write_reg_dword(asd_ha, LmCONTROL(lseq), + (LEDTIMER | LEDMODE_TXRX | LEDTIMERS_100ms)); + + /* Set the Align Rate for SAS and STP mode. */ + asd_write_reg_byte(asd_ha, LmM1SASALIGN(lseq), SAS_ALIGN_DEFAULT); + asd_write_reg_byte(asd_ha, LmM1STPALIGN(lseq), STP_ALIGN_DEFAULT); +} + + +/** + * asd_post_init_cseq -- clear CSEQ Mode n Int. status and Response mailbox + * @asd_ha: pointer to host adapter struct + */ +static void asd_post_init_cseq(struct asd_ha_struct *asd_ha) +{ + int i; + + for (i = 0; i < 8; i++) + asd_write_reg_dword(asd_ha, CMnINT(i), 0xFFFFFFFF); + for (i = 0; i < 8; i++) + asd_read_reg_dword(asd_ha, CMnRSPMBX(i)); + /* Reset the external interrupt arbiter. */ + asd_write_reg_byte(asd_ha, CARP2INTCTL, RSTINTCTL); +} + +/** + * asd_init_ddb_0 -- initialize DDB 0 + * @asd_ha: pointer to host adapter structure + * + * Initialize DDB site 0 which is used internally by the sequencer. + */ +static void asd_init_ddb_0(struct asd_ha_struct *asd_ha) +{ + int i; + + /* Zero out the DDB explicitly */ + for (i = 0; i < sizeof(struct asd_ddb_seq_shared); i+=4) + asd_ddbsite_write_dword(asd_ha, 0, i, 0); + + asd_ddbsite_write_word(asd_ha, 0, + offsetof(struct asd_ddb_seq_shared, q_free_ddb_head), 0); + asd_ddbsite_write_word(asd_ha, 0, + offsetof(struct asd_ddb_seq_shared, q_free_ddb_tail), + asd_ha->hw_prof.max_ddbs-1); + asd_ddbsite_write_word(asd_ha, 0, + offsetof(struct asd_ddb_seq_shared, q_free_ddb_cnt), 0); + asd_ddbsite_write_word(asd_ha, 0, + offsetof(struct asd_ddb_seq_shared, q_used_ddb_head), 0xFFFF); + asd_ddbsite_write_word(asd_ha, 0, + offsetof(struct asd_ddb_seq_shared, q_used_ddb_tail), 0xFFFF); + asd_ddbsite_write_word(asd_ha, 0, + offsetof(struct asd_ddb_seq_shared, shared_mem_lock), 0); + asd_ddbsite_write_word(asd_ha, 0, + offsetof(struct asd_ddb_seq_shared, smp_conn_tag), 0); + asd_ddbsite_write_word(asd_ha, 0, + offsetof(struct asd_ddb_seq_shared, est_nexus_buf_cnt), 0); + asd_ddbsite_write_word(asd_ha, 0, + offsetof(struct asd_ddb_seq_shared, est_nexus_buf_thresh), + asd_ha->hw_prof.num_phys * 2); + asd_ddbsite_write_byte(asd_ha, 0, + offsetof(struct asd_ddb_seq_shared, settable_max_contexts),0); + asd_ddbsite_write_byte(asd_ha, 0, + offsetof(struct asd_ddb_seq_shared, conn_not_active), 0xFF); + asd_ddbsite_write_byte(asd_ha, 0, + offsetof(struct asd_ddb_seq_shared, phy_is_up), 0x00); + /* DDB 0 is reserved */ + set_bit(0, asd_ha->hw_prof.ddb_bitmap); +} + +/** + * asd_seq_setup_seqs -- setup and initialize central and link sequencers + * @asd_ha: pointer to host adapter structure + */ +static void asd_seq_setup_seqs(struct asd_ha_struct *asd_ha) +{ + int lseq; + u8 lseq_mask; + + /* Initialize SCB sites. Done first to compute some values which + * the rest of the init code depends on. */ + asd_init_scb_sites(asd_ha); + + /* Initialize CSEQ Scratch RAM registers. */ + asd_init_cseq_scratch(asd_ha); + + /* Initialize LmSEQ Scratch RAM registers. */ + asd_init_lseq_scratch(asd_ha); + + /* Initialize CSEQ CIO registers. */ + asd_init_cseq_cio(asd_ha); + + asd_init_ddb_0(asd_ha); + + /* Initialize LmSEQ CIO registers. */ + lseq_mask = asd_ha->hw_prof.enabled_phys; + for_each_sequencer(lseq_mask, lseq_mask, lseq) + asd_init_lseq_cio(asd_ha, lseq); + asd_post_init_cseq(asd_ha); +} + + +/** + * asd_seq_start_cseq -- start the central sequencer, CSEQ + * @asd_ha: pointer to host adapter structure + */ +static int asd_seq_start_cseq(struct asd_ha_struct *asd_ha) +{ + /* Reset the ARP2 instruction to location zero. */ + asd_write_reg_word(asd_ha, CPRGMCNT, cseq_idle_loop); + + /* Unpause the CSEQ */ + return asd_unpause_cseq(asd_ha); +} + +/** + * asd_seq_start_lseq -- start a link sequencer + * @asd_ha: pointer to host adapter structure + * @lseq: the link sequencer of interest + */ +static int asd_seq_start_lseq(struct asd_ha_struct *asd_ha, int lseq) +{ + /* Reset the ARP2 instruction to location zero. */ + asd_write_reg_word(asd_ha, LmPRGMCNT(lseq), lseq_idle_loop); + + /* Unpause the LmSEQ */ + return asd_seq_unpause_lseq(asd_ha, lseq); +} + +static int asd_request_firmware(struct asd_ha_struct *asd_ha) +{ + int err, i; + struct sequencer_file_header header, *hdr_ptr; + u32 csum = 0; + u16 *ptr_cseq_vecs, *ptr_lseq_vecs; + + if (sequencer_fw) + /* already loaded */ + return 0; + + err = request_firmware(&sequencer_fw, + SAS_RAZOR_SEQUENCER_FW_FILE, + &asd_ha->pcidev->dev); + if (err) + return err; + + hdr_ptr = (struct sequencer_file_header *)sequencer_fw->data; + + header.csum = le32_to_cpu(hdr_ptr->csum); + header.major = le32_to_cpu(hdr_ptr->major); + header.minor = le32_to_cpu(hdr_ptr->minor); + sequencer_version = hdr_ptr->version; + header.cseq_table_offset = le32_to_cpu(hdr_ptr->cseq_table_offset); + header.cseq_table_size = le32_to_cpu(hdr_ptr->cseq_table_size); + header.lseq_table_offset = le32_to_cpu(hdr_ptr->lseq_table_offset); + header.lseq_table_size = le32_to_cpu(hdr_ptr->lseq_table_size); + header.cseq_code_offset = le32_to_cpu(hdr_ptr->cseq_code_offset); + header.cseq_code_size = le32_to_cpu(hdr_ptr->cseq_code_size); + header.lseq_code_offset = le32_to_cpu(hdr_ptr->lseq_code_offset); + header.lseq_code_size = le32_to_cpu(hdr_ptr->lseq_code_size); + header.mode2_task = le16_to_cpu(hdr_ptr->mode2_task); + header.cseq_idle_loop = le16_to_cpu(hdr_ptr->cseq_idle_loop); + header.lseq_idle_loop = le16_to_cpu(hdr_ptr->lseq_idle_loop); + + for (i = sizeof(header.csum); i < sequencer_fw->size; i++) + csum += sequencer_fw->data[i]; + + if (csum != header.csum) { + asd_printk("Firmware file checksum mismatch\n"); + return -EINVAL; + } + + if (header.cseq_table_size != CSEQ_NUM_VECS || + header.lseq_table_size != LSEQ_NUM_VECS) { + asd_printk("Firmware file table size mismatch\n"); + return -EINVAL; + } + + ptr_cseq_vecs = (u16 *)&sequencer_fw->data[header.cseq_table_offset]; + ptr_lseq_vecs = (u16 *)&sequencer_fw->data[header.lseq_table_offset]; + mode2_task = header.mode2_task; + cseq_idle_loop = header.cseq_idle_loop; + lseq_idle_loop = header.lseq_idle_loop; + + for (i = 0; i < CSEQ_NUM_VECS; i++) + cseq_vecs[i] = le16_to_cpu(ptr_cseq_vecs[i]); + + for (i = 0; i < LSEQ_NUM_VECS; i++) + lseq_vecs[i] = le16_to_cpu(ptr_lseq_vecs[i]); + + cseq_code = &sequencer_fw->data[header.cseq_code_offset]; + cseq_code_size = header.cseq_code_size; + lseq_code = &sequencer_fw->data[header.lseq_code_offset]; + lseq_code_size = header.lseq_code_size; + + return 0; +} + +int asd_init_seqs(struct asd_ha_struct *asd_ha) +{ + int err; + + err = asd_request_firmware(asd_ha); + + if (err) { + asd_printk("Failed to load sequencer firmware file %s, error %d\n", + SAS_RAZOR_SEQUENCER_FW_FILE, err); + return err; + } + + asd_printk("using sequencer %s\n", sequencer_version); + err = asd_seq_download_seqs(asd_ha); + if (err) { + asd_printk("couldn't download sequencers for %s\n", + pci_name(asd_ha->pcidev)); + return err; + } + + asd_seq_setup_seqs(asd_ha); + + return 0; +} + +int asd_start_seqs(struct asd_ha_struct *asd_ha) +{ + int err; + u8 lseq_mask; + int lseq; + + err = asd_seq_start_cseq(asd_ha); + if (err) { + asd_printk("couldn't start CSEQ for %s\n", + pci_name(asd_ha->pcidev)); + return err; + } + + lseq_mask = asd_ha->hw_prof.enabled_phys; + for_each_sequencer(lseq_mask, lseq_mask, lseq) { + err = asd_seq_start_lseq(asd_ha, lseq); + if (err) { + asd_printk("coudln't start LSEQ %d for %s\n", lseq, + pci_name(asd_ha->pcidev)); + return err; + } + } + + return 0; +} + +/** + * asd_update_port_links -- update port_map_by_links and phy_is_up + * @sas_phy: pointer to the phy which has been added to a port + * + * 1) When a link reset has completed and we got BYTES DMAED with a + * valid frame we call this function for that phy, to indicate that + * the phy is up, i.e. we update the phy_is_up in DDB 0. The + * sequencer checks phy_is_up when pending SCBs are to be sent, and + * when an open address frame has been received. + * + * 2) When we know of ports, we call this function to update the map + * of phys participaing in that port, i.e. we update the + * port_map_by_links in DDB 0. When a HARD_RESET primitive has been + * received, the sequencer disables all phys in that port. + * port_map_by_links is also used as the conn_mask byte in the + * initiator/target port DDB. + */ +void asd_update_port_links(struct asd_sas_phy *sas_phy) +{ + struct asd_ha_struct *asd_ha = sas_phy->ha->lldd_ha; + const u8 phy_mask = (u8) sas_phy->port->phy_mask; + u8 phy_is_up; + u8 mask; + int i, err; + + for_each_phy(phy_mask, mask, i) + asd_ddbsite_write_byte(asd_ha, 0, + offsetof(struct asd_ddb_seq_shared, + port_map_by_links)+i,phy_mask); + + for (i = 0; i < 12; i++) { + phy_is_up = asd_ddbsite_read_byte(asd_ha, 0, + offsetof(struct asd_ddb_seq_shared, phy_is_up)); + err = asd_ddbsite_update_byte(asd_ha, 0, + offsetof(struct asd_ddb_seq_shared, phy_is_up), + phy_is_up, + phy_is_up | phy_mask); + if (!err) + break; + else if (err == -EFAULT) { + asd_printk("phy_is_up: parity error in DDB 0\n"); + break; + } + } + + if (err) + asd_printk("couldn't update DDB 0:error:%d\n", err); +} diff --git a/drivers/scsi/aic94xx/aic94xx_seq.h b/drivers/scsi/aic94xx/aic94xx_seq.h new file mode 100644 index 00000000000..42281c36153 --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx_seq.h @@ -0,0 +1,70 @@ +/* + * Aic94xx SAS/SATA driver sequencer interface header file. + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * This file is part of the aic94xx driver. + * + * The aic94xx driver 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; version 2 of the + * License. + * + * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _AIC94XX_SEQ_H_ +#define _AIC94XX_SEQ_H_ + +#define CSEQ_NUM_VECS 3 +#define LSEQ_NUM_VECS 11 + +#define SAS_RAZOR_SEQUENCER_FW_FILE "aic94xx-seq.fw" + +/* Note: All quantites in the sequencer file are little endian */ +struct sequencer_file_header { + /* Checksum of the entire contents of the sequencer excluding + * these four bytes */ + u32 csum; + /* numeric major version */ + u32 major; + /* numeric minor version */ + u32 minor; + /* version string printed by driver */ + char version[16]; + u32 cseq_table_offset; + u32 cseq_table_size; + u32 lseq_table_offset; + u32 lseq_table_size; + u32 cseq_code_offset; + u32 cseq_code_size; + u32 lseq_code_offset; + u32 lseq_code_size; + u16 mode2_task; + u16 cseq_idle_loop; + u16 lseq_idle_loop; +} __attribute__((packed)); + +#ifdef __KERNEL__ +int asd_pause_cseq(struct asd_ha_struct *asd_ha); +int asd_unpause_cseq(struct asd_ha_struct *asd_ha); +int asd_pause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask); +int asd_unpause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask); +int asd_init_seqs(struct asd_ha_struct *asd_ha); +int asd_start_seqs(struct asd_ha_struct *asd_ha); + +void asd_update_port_links(struct asd_sas_phy *phy); +#endif + +#endif diff --git a/drivers/scsi/aic94xx/aic94xx_task.c b/drivers/scsi/aic94xx/aic94xx_task.c new file mode 100644 index 00000000000..285e70dae93 --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx_task.c @@ -0,0 +1,642 @@ +/* + * Aic94xx SAS/SATA Tasks + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * This file is part of the aic94xx driver. + * + * The aic94xx driver 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; version 2 of the + * License. + * + * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include "aic94xx.h" +#include "aic94xx_sas.h" +#include "aic94xx_hwi.h" + +static void asd_unbuild_ata_ascb(struct asd_ascb *a); +static void asd_unbuild_smp_ascb(struct asd_ascb *a); +static void asd_unbuild_ssp_ascb(struct asd_ascb *a); + +static inline void asd_can_dequeue(struct asd_ha_struct *asd_ha, int num) +{ + unsigned long flags; + + spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags); + asd_ha->seq.can_queue += num; + spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags); +} + +/* PCI_DMA_... to our direction translation. + */ +static const u8 data_dir_flags[] = { + [PCI_DMA_BIDIRECTIONAL] = DATA_DIR_BYRECIPIENT, /* UNSPECIFIED */ + [PCI_DMA_TODEVICE] = DATA_DIR_OUT, /* OUTBOUND */ + [PCI_DMA_FROMDEVICE] = DATA_DIR_IN, /* INBOUND */ + [PCI_DMA_NONE] = DATA_DIR_NONE, /* NO TRANSFER */ +}; + +static inline int asd_map_scatterlist(struct sas_task *task, + struct sg_el *sg_arr, + unsigned long gfp_flags) +{ + struct asd_ascb *ascb = task->lldd_task; + struct asd_ha_struct *asd_ha = ascb->ha; + struct scatterlist *sc; + int num_sg, res; + + if (task->data_dir == PCI_DMA_NONE) + return 0; + + if (task->num_scatter == 0) { + void *p = task->scatter; + dma_addr_t dma = pci_map_single(asd_ha->pcidev, p, + task->total_xfer_len, + task->data_dir); + sg_arr[0].bus_addr = cpu_to_le64((u64)dma); + sg_arr[0].size = cpu_to_le32(task->total_xfer_len); + sg_arr[0].flags |= ASD_SG_EL_LIST_EOL; + return 0; + } + + num_sg = pci_map_sg(asd_ha->pcidev, task->scatter, task->num_scatter, + task->data_dir); + if (num_sg == 0) + return -ENOMEM; + + if (num_sg > 3) { + int i; + + ascb->sg_arr = asd_alloc_coherent(asd_ha, + num_sg*sizeof(struct sg_el), + gfp_flags); + if (!ascb->sg_arr) { + res = -ENOMEM; + goto err_unmap; + } + for (sc = task->scatter, i = 0; i < num_sg; i++, sc++) { + struct sg_el *sg = + &((struct sg_el *)ascb->sg_arr->vaddr)[i]; + sg->bus_addr = cpu_to_le64((u64)sg_dma_address(sc)); + sg->size = cpu_to_le32((u32)sg_dma_len(sc)); + if (i == num_sg-1) + sg->flags |= ASD_SG_EL_LIST_EOL; + } + + for (sc = task->scatter, i = 0; i < 2; i++, sc++) { + sg_arr[i].bus_addr = + cpu_to_le64((u64)sg_dma_address(sc)); + sg_arr[i].size = cpu_to_le32((u32)sg_dma_len(sc)); + } + sg_arr[1].next_sg_offs = 2 * sizeof(*sg_arr); + sg_arr[1].flags |= ASD_SG_EL_LIST_EOS; + + memset(&sg_arr[2], 0, sizeof(*sg_arr)); + sg_arr[2].bus_addr=cpu_to_le64((u64)ascb->sg_arr->dma_handle); + } else { + int i; + for (sc = task->scatter, i = 0; i < num_sg; i++, sc++) { + sg_arr[i].bus_addr = + cpu_to_le64((u64)sg_dma_address(sc)); + sg_arr[i].size = cpu_to_le32((u32)sg_dma_len(sc)); + } + sg_arr[i-1].flags |= ASD_SG_EL_LIST_EOL; + } + + return 0; +err_unmap: + pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter, + task->data_dir); + return res; +} + +static inline void asd_unmap_scatterlist(struct asd_ascb *ascb) +{ + struct asd_ha_struct *asd_ha = ascb->ha; + struct sas_task *task = ascb->uldd_task; + + if (task->data_dir == PCI_DMA_NONE) + return; + + if (task->num_scatter == 0) { + dma_addr_t dma = (dma_addr_t) + le64_to_cpu(ascb->scb->ssp_task.sg_element[0].bus_addr); + pci_unmap_single(ascb->ha->pcidev, dma, task->total_xfer_len, + task->data_dir); + return; + } + + asd_free_coherent(asd_ha, ascb->sg_arr); + pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter, + task->data_dir); +} + +/* ---------- Task complete tasklet ---------- */ + +static void asd_get_response_tasklet(struct asd_ascb *ascb, + struct done_list_struct *dl) +{ + struct asd_ha_struct *asd_ha = ascb->ha; + struct sas_task *task = ascb->uldd_task; + struct task_status_struct *ts = &task->task_status; + unsigned long flags; + struct tc_resp_sb_struct { + __le16 index_escb; + u8 len_lsb; + u8 flags; + } __attribute__ ((packed)) *resp_sb = (void *) dl->status_block; + +/* int size = ((resp_sb->flags & 7) << 8) | resp_sb->len_lsb; */ + int edb_id = ((resp_sb->flags & 0x70) >> 4)-1; + struct asd_ascb *escb; + struct asd_dma_tok *edb; + void *r; + + spin_lock_irqsave(&asd_ha->seq.tc_index_lock, flags); + escb = asd_tc_index_find(&asd_ha->seq, + (int)le16_to_cpu(resp_sb->index_escb)); + spin_unlock_irqrestore(&asd_ha->seq.tc_index_lock, flags); + + if (!escb) { + ASD_DPRINTK("Uh-oh! No escb for this dl?!\n"); + return; + } + + ts->buf_valid_size = 0; + edb = asd_ha->seq.edb_arr[edb_id + escb->edb_index]; + r = edb->vaddr; + if (task->task_proto == SAS_PROTO_SSP) { + struct ssp_response_iu *iu = + r + 16 + sizeof(struct ssp_frame_hdr); + + ts->residual = le32_to_cpu(*(__le32 *)r); + ts->resp = SAS_TASK_COMPLETE; + if (iu->datapres == 0) + ts->stat = iu->status; + else if (iu->datapres == 1) + ts->stat = iu->resp_data[3]; + else if (iu->datapres == 2) { + ts->stat = SAM_CHECK_COND; + ts->buf_valid_size = min((u32) SAS_STATUS_BUF_SIZE, + be32_to_cpu(iu->sense_data_len)); + memcpy(ts->buf, iu->sense_data, ts->buf_valid_size); + if (iu->status != SAM_CHECK_COND) { + ASD_DPRINTK("device %llx sent sense data, but " + "stat(0x%x) is not CHECK_CONDITION" + "\n", + SAS_ADDR(task->dev->sas_addr), + ts->stat); + } + } + } else { + struct ata_task_resp *resp = (void *) &ts->buf[0]; + + ts->residual = le32_to_cpu(*(__le32 *)r); + + if (SAS_STATUS_BUF_SIZE >= sizeof(*resp)) { + resp->frame_len = le16_to_cpu(*(__le16 *)(r+6)); + memcpy(&resp->ending_fis[0], r+16, 24); + ts->buf_valid_size = sizeof(*resp); + } + } + + asd_invalidate_edb(escb, edb_id); +} + +static void asd_task_tasklet_complete(struct asd_ascb *ascb, + struct done_list_struct *dl) +{ + struct sas_task *task = ascb->uldd_task; + struct task_status_struct *ts = &task->task_status; + unsigned long flags; + u8 opcode = dl->opcode; + + asd_can_dequeue(ascb->ha, 1); + +Again: + switch (opcode) { + case TC_NO_ERROR: + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAM_GOOD; + break; + case TC_UNDERRUN: + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_UNDERRUN; + ts->residual = le32_to_cpu(*(__le32 *)dl->status_block); + break; + case TC_OVERRUN: + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_OVERRUN; + ts->residual = 0; + break; + case TC_SSP_RESP: + case TC_ATA_RESP: + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_PROTO_RESPONSE; + asd_get_response_tasklet(ascb, dl); + break; + case TF_OPEN_REJECT: + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + if (dl->status_block[1] & 2) + ts->open_rej_reason = 1 + dl->status_block[2]; + else if (dl->status_block[1] & 1) + ts->open_rej_reason = (dl->status_block[2] >> 4)+10; + else + ts->open_rej_reason = SAS_OREJ_UNKNOWN; + break; + case TF_OPEN_TO: + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_TO; + break; + case TF_PHY_DOWN: + case TU_PHY_DOWN: + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_PHY_DOWN; + break; + case TI_PHY_DOWN: + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_PHY_DOWN; + break; + case TI_BREAK: + case TI_PROTO_ERR: + case TI_NAK: + case TI_ACK_NAK_TO: + case TF_SMP_XMIT_RCV_ERR: + case TC_ATA_R_ERR_RECV: + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_INTERRUPTED; + break; + case TF_BREAK: + case TU_BREAK: + case TU_ACK_NAK_TO: + case TF_SMPRSP_TO: + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + case TF_NAK_RECV: + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_NAK_R_ERR; + break; + case TA_I_T_NEXUS_LOSS: + opcode = dl->status_block[0]; + goto Again; + break; + case TF_INV_CONN_HANDLE: + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEVICE_UNKNOWN; + break; + case TF_REQUESTED_N_PENDING: + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_PENDING; + break; + case TC_TASK_CLEARED: + case TA_ON_REQ: + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_ABORTED_TASK; + break; + + case TF_NO_SMP_CONN: + case TF_TMF_NO_CTX: + case TF_TMF_NO_TAG: + case TF_TMF_TAG_FREE: + case TF_TMF_TASK_DONE: + case TF_TMF_NO_CONN_HANDLE: + case TF_IRTT_TO: + case TF_IU_SHORT: + case TF_DATA_OFFS_ERR: + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + + case TC_LINK_ADM_RESP: + case TC_CONTROL_PHY: + case TC_RESUME: + case TC_PARTIAL_SG_LIST: + default: + ASD_DPRINTK("%s: dl opcode: 0x%x?\n", __FUNCTION__, opcode); + break; + } + + switch (task->task_proto) { + case SATA_PROTO: + case SAS_PROTO_STP: + asd_unbuild_ata_ascb(ascb); + break; + case SAS_PROTO_SMP: + asd_unbuild_smp_ascb(ascb); + break; + case SAS_PROTO_SSP: + asd_unbuild_ssp_ascb(ascb); + default: + break; + } + + spin_lock_irqsave(&task->task_state_lock, flags); + task->task_state_flags &= ~SAS_TASK_STATE_PENDING; + task->task_state_flags |= SAS_TASK_STATE_DONE; + if (unlikely((task->task_state_flags & SAS_TASK_STATE_ABORTED))) { + spin_unlock_irqrestore(&task->task_state_lock, flags); + ASD_DPRINTK("task 0x%p done with opcode 0x%x resp 0x%x " + "stat 0x%x but aborted by upper layer!\n", + task, opcode, ts->resp, ts->stat); + complete(&ascb->completion); + } else { + spin_unlock_irqrestore(&task->task_state_lock, flags); + task->lldd_task = NULL; + asd_ascb_free(ascb); + mb(); + task->task_done(task); + } +} + +/* ---------- ATA ---------- */ + +static int asd_build_ata_ascb(struct asd_ascb *ascb, struct sas_task *task, + unsigned long gfp_flags) +{ + struct domain_device *dev = task->dev; + struct scb *scb; + u8 flags; + int res = 0; + + scb = ascb->scb; + + if (unlikely(task->ata_task.device_control_reg_update)) + scb->header.opcode = CONTROL_ATA_DEV; + else if (dev->sata_dev.command_set == ATA_COMMAND_SET) + scb->header.opcode = INITIATE_ATA_TASK; + else + scb->header.opcode = INITIATE_ATAPI_TASK; + + scb->ata_task.proto_conn_rate = (1 << 5); /* STP */ + if (dev->port->oob_mode == SAS_OOB_MODE) + scb->ata_task.proto_conn_rate |= dev->linkrate; + + scb->ata_task.total_xfer_len = cpu_to_le32(task->total_xfer_len); + scb->ata_task.fis = task->ata_task.fis; + scb->ata_task.fis.fis_type = 0x27; + if (likely(!task->ata_task.device_control_reg_update)) + scb->ata_task.fis.flags |= 0x80; /* C=1: update ATA cmd reg */ + scb->ata_task.fis.flags &= 0xF0; /* PM_PORT field shall be 0 */ + if (dev->sata_dev.command_set == ATAPI_COMMAND_SET) + memcpy(scb->ata_task.atapi_packet, task->ata_task.atapi_packet, + 16); + scb->ata_task.sister_scb = cpu_to_le16(0xFFFF); + scb->ata_task.conn_handle = cpu_to_le16( + (u16)(unsigned long)dev->lldd_dev); + + if (likely(!task->ata_task.device_control_reg_update)) { + flags = 0; + if (task->ata_task.dma_xfer) + flags |= DATA_XFER_MODE_DMA; + if (task->ata_task.use_ncq && + dev->sata_dev.command_set != ATAPI_COMMAND_SET) + flags |= ATA_Q_TYPE_NCQ; + flags |= data_dir_flags[task->data_dir]; + scb->ata_task.ata_flags = flags; + + scb->ata_task.retry_count = task->ata_task.retry_count; + + flags = 0; + if (task->ata_task.set_affil_pol) + flags |= SET_AFFIL_POLICY; + if (task->ata_task.stp_affil_pol) + flags |= STP_AFFIL_POLICY; + scb->ata_task.flags = flags; + } + ascb->tasklet_complete = asd_task_tasklet_complete; + + if (likely(!task->ata_task.device_control_reg_update)) + res = asd_map_scatterlist(task, scb->ata_task.sg_element, + gfp_flags); + + return res; +} + +static void asd_unbuild_ata_ascb(struct asd_ascb *a) +{ + asd_unmap_scatterlist(a); +} + +/* ---------- SMP ---------- */ + +static int asd_build_smp_ascb(struct asd_ascb *ascb, struct sas_task *task, + unsigned long gfp_flags) +{ + struct asd_ha_struct *asd_ha = ascb->ha; + struct domain_device *dev = task->dev; + struct scb *scb; + + pci_map_sg(asd_ha->pcidev, &task->smp_task.smp_req, 1, + PCI_DMA_FROMDEVICE); + pci_map_sg(asd_ha->pcidev, &task->smp_task.smp_resp, 1, + PCI_DMA_FROMDEVICE); + + scb = ascb->scb; + + scb->header.opcode = INITIATE_SMP_TASK; + + scb->smp_task.proto_conn_rate = dev->linkrate; + + scb->smp_task.smp_req.bus_addr = + cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_req)); + scb->smp_task.smp_req.size = + cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_req)-4); + + scb->smp_task.smp_resp.bus_addr = + cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_resp)); + scb->smp_task.smp_resp.size = + cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_resp)-4); + + scb->smp_task.sister_scb = cpu_to_le16(0xFFFF); + scb->smp_task.conn_handle = cpu_to_le16((u16) + (unsigned long)dev->lldd_dev); + + ascb->tasklet_complete = asd_task_tasklet_complete; + + return 0; +} + +static void asd_unbuild_smp_ascb(struct asd_ascb *a) +{ + struct sas_task *task = a->uldd_task; + + BUG_ON(!task); + pci_unmap_sg(a->ha->pcidev, &task->smp_task.smp_req, 1, + PCI_DMA_FROMDEVICE); + pci_unmap_sg(a->ha->pcidev, &task->smp_task.smp_resp, 1, + PCI_DMA_FROMDEVICE); +} + +/* ---------- SSP ---------- */ + +static int asd_build_ssp_ascb(struct asd_ascb *ascb, struct sas_task *task, + unsigned long gfp_flags) +{ + struct domain_device *dev = task->dev; + struct scb *scb; + int res = 0; + + scb = ascb->scb; + + scb->header.opcode = INITIATE_SSP_TASK; + + scb->ssp_task.proto_conn_rate = (1 << 4); /* SSP */ + scb->ssp_task.proto_conn_rate |= dev->linkrate; + scb->ssp_task.total_xfer_len = cpu_to_le32(task->total_xfer_len); + scb->ssp_task.ssp_frame.frame_type = SSP_DATA; + memcpy(scb->ssp_task.ssp_frame.hashed_dest_addr, dev->hashed_sas_addr, + HASHED_SAS_ADDR_SIZE); + memcpy(scb->ssp_task.ssp_frame.hashed_src_addr, + dev->port->ha->hashed_sas_addr, HASHED_SAS_ADDR_SIZE); + scb->ssp_task.ssp_frame.tptt = cpu_to_be16(0xFFFF); + + memcpy(scb->ssp_task.ssp_cmd.lun, task->ssp_task.LUN, 8); + if (task->ssp_task.enable_first_burst) + scb->ssp_task.ssp_cmd.efb_prio_attr |= EFB_MASK; + scb->ssp_task.ssp_cmd.efb_prio_attr |= (task->ssp_task.task_prio << 3); + scb->ssp_task.ssp_cmd.efb_prio_attr |= (task->ssp_task.task_attr & 7); + memcpy(scb->ssp_task.ssp_cmd.cdb, task->ssp_task.cdb, 16); + + scb->ssp_task.sister_scb = cpu_to_le16(0xFFFF); + scb->ssp_task.conn_handle = cpu_to_le16( + (u16)(unsigned long)dev->lldd_dev); + scb->ssp_task.data_dir = data_dir_flags[task->data_dir]; + scb->ssp_task.retry_count = scb->ssp_task.retry_count; + + ascb->tasklet_complete = asd_task_tasklet_complete; + + res = asd_map_scatterlist(task, scb->ssp_task.sg_element, gfp_flags); + + return res; +} + +static void asd_unbuild_ssp_ascb(struct asd_ascb *a) +{ + asd_unmap_scatterlist(a); +} + +/* ---------- Execute Task ---------- */ + +static inline int asd_can_queue(struct asd_ha_struct *asd_ha, int num) +{ + int res = 0; + unsigned long flags; + + spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags); + if ((asd_ha->seq.can_queue - num) < 0) + res = -SAS_QUEUE_FULL; + else + asd_ha->seq.can_queue -= num; + spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags); + + return res; +} + +int asd_execute_task(struct sas_task *task, const int num, + unsigned long gfp_flags) +{ + int res = 0; + LIST_HEAD(alist); + struct sas_task *t = task; + struct asd_ascb *ascb = NULL, *a; + struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha; + + res = asd_can_queue(asd_ha, num); + if (res) + return res; + + res = num; + ascb = asd_ascb_alloc_list(asd_ha, &res, gfp_flags); + if (res) { + res = -ENOMEM; + goto out_err; + } + + __list_add(&alist, ascb->list.prev, &ascb->list); + list_for_each_entry(a, &alist, list) { + a->uldd_task = t; + t->lldd_task = a; + t = list_entry(t->list.next, struct sas_task, list); + } + list_for_each_entry(a, &alist, list) { + t = a->uldd_task; + a->uldd_timer = 1; + if (t->task_proto & SAS_PROTO_STP) + t->task_proto = SAS_PROTO_STP; + switch (t->task_proto) { + case SATA_PROTO: + case SAS_PROTO_STP: + res = asd_build_ata_ascb(a, t, gfp_flags); + break; + case SAS_PROTO_SMP: + res = asd_build_smp_ascb(a, t, gfp_flags); + break; + case SAS_PROTO_SSP: + res = asd_build_ssp_ascb(a, t, gfp_flags); + break; + default: + asd_printk("unknown sas_task proto: 0x%x\n", + t->task_proto); + res = -ENOMEM; + break; + } + if (res) + goto out_err_unmap; + } + list_del_init(&alist); + + res = asd_post_ascb_list(asd_ha, ascb, num); + if (unlikely(res)) { + a = NULL; + __list_add(&alist, ascb->list.prev, &ascb->list); + goto out_err_unmap; + } + + return 0; +out_err_unmap: + { + struct asd_ascb *b = a; + list_for_each_entry(a, &alist, list) { + if (a == b) + break; + t = a->uldd_task; + switch (t->task_proto) { + case SATA_PROTO: + case SAS_PROTO_STP: + asd_unbuild_ata_ascb(a); + break; + case SAS_PROTO_SMP: + asd_unbuild_smp_ascb(a); + break; + case SAS_PROTO_SSP: + asd_unbuild_ssp_ascb(a); + default: + break; + } + t->lldd_task = NULL; + } + } + list_del_init(&alist); +out_err: + if (ascb) + asd_ascb_free_list(ascb); + asd_can_dequeue(asd_ha, num); + return res; +} diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c new file mode 100644 index 00000000000..61234384503 --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx_tmf.c @@ -0,0 +1,636 @@ +/* + * Aic94xx Task Management Functions + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * This file is part of the aic94xx driver. + * + * The aic94xx driver 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; version 2 of the + * License. + * + * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include "aic94xx.h" +#include "aic94xx_sas.h" +#include "aic94xx_hwi.h" + +/* ---------- Internal enqueue ---------- */ + +static int asd_enqueue_internal(struct asd_ascb *ascb, + void (*tasklet_complete)(struct asd_ascb *, + struct done_list_struct *), + void (*timed_out)(unsigned long)) +{ + int res; + + ascb->tasklet_complete = tasklet_complete; + ascb->uldd_timer = 1; + + ascb->timer.data = (unsigned long) ascb; + ascb->timer.function = timed_out; + ascb->timer.expires = jiffies + AIC94XX_SCB_TIMEOUT; + + add_timer(&ascb->timer); + + res = asd_post_ascb_list(ascb->ha, ascb, 1); + if (unlikely(res)) + del_timer(&ascb->timer); + return res; +} + +static inline void asd_timedout_common(unsigned long data) +{ + struct asd_ascb *ascb = (void *) data; + struct asd_seq_data *seq = &ascb->ha->seq; + unsigned long flags; + + spin_lock_irqsave(&seq->pend_q_lock, flags); + seq->pending--; + list_del_init(&ascb->list); + spin_unlock_irqrestore(&seq->pend_q_lock, flags); +} + +/* ---------- CLEAR NEXUS ---------- */ + +static void asd_clear_nexus_tasklet_complete(struct asd_ascb *ascb, + struct done_list_struct *dl) +{ + ASD_DPRINTK("%s: here\n", __FUNCTION__); + if (!del_timer(&ascb->timer)) { + ASD_DPRINTK("%s: couldn't delete timer\n", __FUNCTION__); + return; + } + ASD_DPRINTK("%s: opcode: 0x%x\n", __FUNCTION__, dl->opcode); + ascb->uldd_task = (void *) (unsigned long) dl->opcode; + complete(&ascb->completion); +} + +static void asd_clear_nexus_timedout(unsigned long data) +{ + struct asd_ascb *ascb = (void *) data; + + ASD_DPRINTK("%s: here\n", __FUNCTION__); + asd_timedout_common(data); + ascb->uldd_task = (void *) TMF_RESP_FUNC_FAILED; + complete(&ascb->completion); +} + +#define CLEAR_NEXUS_PRE \ + ASD_DPRINTK("%s: PRE\n", __FUNCTION__); \ + res = 1; \ + ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); \ + if (!ascb) \ + return -ENOMEM; \ + \ + scb = ascb->scb; \ + scb->header.opcode = CLEAR_NEXUS + +#define CLEAR_NEXUS_POST \ + ASD_DPRINTK("%s: POST\n", __FUNCTION__); \ + res = asd_enqueue_internal(ascb, asd_clear_nexus_tasklet_complete, \ + asd_clear_nexus_timedout); \ + if (res) \ + goto out_err; \ + ASD_DPRINTK("%s: clear nexus posted, waiting...\n", __FUNCTION__); \ + wait_for_completion(&ascb->completion); \ + res = (int) (unsigned long) ascb->uldd_task; \ + if (res == TC_NO_ERROR) \ + res = TMF_RESP_FUNC_COMPLETE; \ +out_err: \ + asd_ascb_free(ascb); \ + return res + +int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha) +{ + struct asd_ha_struct *asd_ha = sas_ha->lldd_ha; + struct asd_ascb *ascb; + struct scb *scb; + int res; + + CLEAR_NEXUS_PRE; + scb->clear_nexus.nexus = NEXUS_ADAPTER; + CLEAR_NEXUS_POST; +} + +int asd_clear_nexus_port(struct asd_sas_port *port) +{ + struct asd_ha_struct *asd_ha = port->ha->lldd_ha; + struct asd_ascb *ascb; + struct scb *scb; + int res; + + CLEAR_NEXUS_PRE; + scb->clear_nexus.nexus = NEXUS_PORT; + scb->clear_nexus.conn_mask = port->phy_mask; + CLEAR_NEXUS_POST; +} + +#if 0 +static int asd_clear_nexus_I_T(struct domain_device *dev) +{ + struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; + struct asd_ascb *ascb; + struct scb *scb; + int res; + + CLEAR_NEXUS_PRE; + scb->clear_nexus.nexus = NEXUS_I_T; + scb->clear_nexus.flags = SEND_Q | EXEC_Q | NOTINQ; + if (dev->tproto) + scb->clear_nexus.flags |= SUSPEND_TX; + scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long) + dev->lldd_dev); + CLEAR_NEXUS_POST; +} +#endif + +static int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun) +{ + struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; + struct asd_ascb *ascb; + struct scb *scb; + int res; + + CLEAR_NEXUS_PRE; + scb->clear_nexus.nexus = NEXUS_I_T_L; + scb->clear_nexus.flags = SEND_Q | EXEC_Q | NOTINQ; + if (dev->tproto) + scb->clear_nexus.flags |= SUSPEND_TX; + memcpy(scb->clear_nexus.ssp_task.lun, lun, 8); + scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long) + dev->lldd_dev); + CLEAR_NEXUS_POST; +} + +static int asd_clear_nexus_tag(struct sas_task *task) +{ + struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha; + struct asd_ascb *tascb = task->lldd_task; + struct asd_ascb *ascb; + struct scb *scb; + int res; + + CLEAR_NEXUS_PRE; + scb->clear_nexus.nexus = NEXUS_TAG; + memcpy(scb->clear_nexus.ssp_task.lun, task->ssp_task.LUN, 8); + scb->clear_nexus.ssp_task.tag = tascb->tag; + if (task->dev->tproto) + scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long) + task->dev->lldd_dev); + CLEAR_NEXUS_POST; +} + +static int asd_clear_nexus_index(struct sas_task *task) +{ + struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha; + struct asd_ascb *tascb = task->lldd_task; + struct asd_ascb *ascb; + struct scb *scb; + int res; + + CLEAR_NEXUS_PRE; + scb->clear_nexus.nexus = NEXUS_TRANS_CX; + if (task->dev->tproto) + scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long) + task->dev->lldd_dev); + scb->clear_nexus.index = cpu_to_le16(tascb->tc_index); + CLEAR_NEXUS_POST; +} + +/* ---------- TMFs ---------- */ + +static void asd_tmf_timedout(unsigned long data) +{ + struct asd_ascb *ascb = (void *) data; + + ASD_DPRINTK("tmf timed out\n"); + asd_timedout_common(data); + ascb->uldd_task = (void *) TMF_RESP_FUNC_FAILED; + complete(&ascb->completion); +} + +static int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb, + struct done_list_struct *dl) +{ + struct asd_ha_struct *asd_ha = ascb->ha; + unsigned long flags; + struct tc_resp_sb_struct { + __le16 index_escb; + u8 len_lsb; + u8 flags; + } __attribute__ ((packed)) *resp_sb = (void *) dl->status_block; + + int edb_id = ((resp_sb->flags & 0x70) >> 4)-1; + struct asd_ascb *escb; + struct asd_dma_tok *edb; + struct ssp_frame_hdr *fh; + struct ssp_response_iu *ru; + int res = TMF_RESP_FUNC_FAILED; + + ASD_DPRINTK("tmf resp tasklet\n"); + + spin_lock_irqsave(&asd_ha->seq.tc_index_lock, flags); + escb = asd_tc_index_find(&asd_ha->seq, + (int)le16_to_cpu(resp_sb->index_escb)); + spin_unlock_irqrestore(&asd_ha->seq.tc_index_lock, flags); + + if (!escb) { + ASD_DPRINTK("Uh-oh! No escb for this dl?!\n"); + return res; + } + + edb = asd_ha->seq.edb_arr[edb_id + escb->edb_index]; + ascb->tag = *(__be16 *)(edb->vaddr+4); + fh = edb->vaddr + 16; + ru = edb->vaddr + 16 + sizeof(*fh); + res = ru->status; + if (ru->datapres == 1) /* Response data present */ + res = ru->resp_data[3]; +#if 0 + ascb->tag = fh->tag; +#endif + ascb->tag_valid = 1; + + asd_invalidate_edb(escb, edb_id); + return res; +} + +static void asd_tmf_tasklet_complete(struct asd_ascb *ascb, + struct done_list_struct *dl) +{ + if (!del_timer(&ascb->timer)) + return; + + ASD_DPRINTK("tmf tasklet complete\n"); + + if (dl->opcode == TC_SSP_RESP) + ascb->uldd_task = (void *) (unsigned long) + asd_get_tmf_resp_tasklet(ascb, dl); + else + ascb->uldd_task = (void *) 0xFF00 + (unsigned long) dl->opcode; + + complete(&ascb->completion); +} + +static inline int asd_clear_nexus(struct sas_task *task) +{ + int res = TMF_RESP_FUNC_FAILED; + struct asd_ascb *tascb = task->lldd_task; + unsigned long flags; + + ASD_DPRINTK("task not done, clearing nexus\n"); + if (tascb->tag_valid) + res = asd_clear_nexus_tag(task); + else + res = asd_clear_nexus_index(task); + wait_for_completion_timeout(&tascb->completion, + AIC94XX_SCB_TIMEOUT); + ASD_DPRINTK("came back from clear nexus\n"); + spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_STATE_DONE) + res = TMF_RESP_FUNC_COMPLETE; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + return res; +} + +/** + * asd_abort_task -- ABORT TASK TMF + * @task: the task to be aborted + * + * Before calling ABORT TASK the task state flags should be ORed with + * SAS_TASK_STATE_ABORTED (unless SAS_TASK_STATE_DONE is set) under + * the task_state_lock IRQ spinlock, then ABORT TASK *must* be called. + * + * Implements the ABORT TASK TMF, I_T_L_Q nexus. + * Returns: SAS TMF responses (see sas_task.h), + * -ENOMEM, + * -SAS_QUEUE_FULL. + * + * When ABORT TASK returns, the caller of ABORT TASK checks first the + * task->task_state_flags, and then the return value of ABORT TASK. + * + * If the task has task state bit SAS_TASK_STATE_DONE set, then the + * task was completed successfully prior to it being aborted. The + * caller of ABORT TASK has responsibility to call task->task_done() + * xor free the task, depending on their framework. The return code + * is TMF_RESP_FUNC_FAILED in this case. + * + * Else the SAS_TASK_STATE_DONE bit is not set, + * If the return code is TMF_RESP_FUNC_COMPLETE, then + * the task was aborted successfully. The caller of + * ABORT TASK has responsibility to call task->task_done() + * to finish the task, xor free the task depending on their + * framework. + * else + * the ABORT TASK returned some kind of error. The task + * was _not_ cancelled. Nothing can be assumed. + * The caller of ABORT TASK may wish to retry. + */ +int asd_abort_task(struct sas_task *task) +{ + struct asd_ascb *tascb = task->lldd_task; + struct asd_ha_struct *asd_ha = tascb->ha; + int res = 1; + unsigned long flags; + struct asd_ascb *ascb = NULL; + struct scb *scb; + + spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_STATE_DONE) { + spin_unlock_irqrestore(&task->task_state_lock, flags); + res = TMF_RESP_FUNC_COMPLETE; + ASD_DPRINTK("%s: task 0x%p done\n", __FUNCTION__, task); + goto out_done; + } + spin_unlock_irqrestore(&task->task_state_lock, flags); + + ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); + if (!ascb) + return -ENOMEM; + scb = ascb->scb; + + scb->header.opcode = ABORT_TASK; + + switch (task->task_proto) { + case SATA_PROTO: + case SAS_PROTO_STP: + scb->abort_task.proto_conn_rate = (1 << 5); /* STP */ + break; + case SAS_PROTO_SSP: + scb->abort_task.proto_conn_rate = (1 << 4); /* SSP */ + scb->abort_task.proto_conn_rate |= task->dev->linkrate; + break; + case SAS_PROTO_SMP: + break; + default: + break; + } + + if (task->task_proto == SAS_PROTO_SSP) { + scb->abort_task.ssp_frame.frame_type = SSP_TASK; + memcpy(scb->abort_task.ssp_frame.hashed_dest_addr, + task->dev->hashed_sas_addr, HASHED_SAS_ADDR_SIZE); + memcpy(scb->abort_task.ssp_frame.hashed_src_addr, + task->dev->port->ha->hashed_sas_addr, + HASHED_SAS_ADDR_SIZE); + scb->abort_task.ssp_frame.tptt = cpu_to_be16(0xFFFF); + + memcpy(scb->abort_task.ssp_task.lun, task->ssp_task.LUN, 8); + scb->abort_task.ssp_task.tmf = TMF_ABORT_TASK; + scb->abort_task.ssp_task.tag = cpu_to_be16(0xFFFF); + } + + scb->abort_task.sister_scb = cpu_to_le16(0xFFFF); + scb->abort_task.conn_handle = cpu_to_le16( + (u16)(unsigned long)task->dev->lldd_dev); + scb->abort_task.retry_count = 1; + scb->abort_task.index = cpu_to_le16((u16)tascb->tc_index); + scb->abort_task.itnl_to = cpu_to_le16(ITNL_TIMEOUT_CONST); + + res = asd_enqueue_internal(ascb, asd_tmf_tasklet_complete, + asd_tmf_timedout); + if (res) + goto out; + wait_for_completion(&ascb->completion); + ASD_DPRINTK("tmf came back\n"); + + res = (int) (unsigned long) ascb->uldd_task; + tascb->tag = ascb->tag; + tascb->tag_valid = ascb->tag_valid; + + spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_STATE_DONE) { + spin_unlock_irqrestore(&task->task_state_lock, flags); + res = TMF_RESP_FUNC_COMPLETE; + ASD_DPRINTK("%s: task 0x%p done\n", __FUNCTION__, task); + goto out_done; + } + spin_unlock_irqrestore(&task->task_state_lock, flags); + + switch (res) { + /* The task to be aborted has been sent to the device. + * We got a Response IU for the ABORT TASK TMF. */ + case TC_NO_ERROR + 0xFF00: + case TMF_RESP_FUNC_COMPLETE: + case TMF_RESP_FUNC_FAILED: + res = asd_clear_nexus(task); + break; + case TMF_RESP_INVALID_FRAME: + case TMF_RESP_OVERLAPPED_TAG: + case TMF_RESP_FUNC_ESUPP: + case TMF_RESP_NO_LUN: + goto out_done; break; + } + /* In the following we assume that the managing layer + * will _never_ make a mistake, when issuing ABORT TASK. + */ + switch (res) { + default: + res = asd_clear_nexus(task); + /* fallthrough */ + case TC_NO_ERROR + 0xFF00: + case TMF_RESP_FUNC_COMPLETE: + break; + /* The task hasn't been sent to the device xor we never got + * a (sane) Response IU for the ABORT TASK TMF. + */ + case TF_NAK_RECV + 0xFF00: + res = TMF_RESP_INVALID_FRAME; + break; + case TF_TMF_TASK_DONE + 0xFF00: /* done but not reported yet */ + res = TMF_RESP_FUNC_FAILED; + wait_for_completion_timeout(&tascb->completion, + AIC94XX_SCB_TIMEOUT); + spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_STATE_DONE) + res = TMF_RESP_FUNC_COMPLETE; + spin_unlock_irqrestore(&task->task_state_lock, flags); + goto out_done; + case TF_TMF_NO_TAG + 0xFF00: + case TF_TMF_TAG_FREE + 0xFF00: /* the tag is in the free list */ + case TF_TMF_NO_CONN_HANDLE + 0xFF00: /* no such device */ + res = TMF_RESP_FUNC_COMPLETE; + goto out_done; + case TF_TMF_NO_CTX + 0xFF00: /* not in seq, or proto != SSP */ + res = TMF_RESP_FUNC_ESUPP; + goto out; + } +out_done: + if (res == TMF_RESP_FUNC_COMPLETE) { + task->lldd_task = NULL; + mb(); + asd_ascb_free(tascb); + } +out: + asd_ascb_free(ascb); + ASD_DPRINTK("task 0x%p aborted, res: 0x%x\n", task, res); + return res; +} + +/** + * asd_initiate_ssp_tmf -- send a TMF to an I_T_L or I_T_L_Q nexus + * @dev: pointer to struct domain_device of interest + * @lun: pointer to u8[8] which is the LUN + * @tmf: the TMF to be performed (see sas_task.h or the SAS spec) + * @index: the transaction context of the task to be queried if QT TMF + * + * This function is used to send ABORT TASK SET, CLEAR ACA, + * CLEAR TASK SET, LU RESET and QUERY TASK TMFs. + * + * No SCBs should be queued to the I_T_L nexus when this SCB is + * pending. + * + * Returns: TMF response code (see sas_task.h or the SAS spec) + */ +static int asd_initiate_ssp_tmf(struct domain_device *dev, u8 *lun, + int tmf, int index) +{ + struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; + struct asd_ascb *ascb; + int res = 1; + struct scb *scb; + + if (!(dev->tproto & SAS_PROTO_SSP)) + return TMF_RESP_FUNC_ESUPP; + + ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); + if (!ascb) + return -ENOMEM; + scb = ascb->scb; + + if (tmf == TMF_QUERY_TASK) + scb->header.opcode = QUERY_SSP_TASK; + else + scb->header.opcode = INITIATE_SSP_TMF; + + scb->ssp_tmf.proto_conn_rate = (1 << 4); /* SSP */ + scb->ssp_tmf.proto_conn_rate |= dev->linkrate; + /* SSP frame header */ + scb->ssp_tmf.ssp_frame.frame_type = SSP_TASK; + memcpy(scb->ssp_tmf.ssp_frame.hashed_dest_addr, + dev->hashed_sas_addr, HASHED_SAS_ADDR_SIZE); + memcpy(scb->ssp_tmf.ssp_frame.hashed_src_addr, + dev->port->ha->hashed_sas_addr, HASHED_SAS_ADDR_SIZE); + scb->ssp_tmf.ssp_frame.tptt = cpu_to_be16(0xFFFF); + /* SSP Task IU */ + memcpy(scb->ssp_tmf.ssp_task.lun, lun, 8); + scb->ssp_tmf.ssp_task.tmf = tmf; + + scb->ssp_tmf.sister_scb = cpu_to_le16(0xFFFF); + scb->ssp_tmf.conn_handle= cpu_to_le16((u16)(unsigned long) + dev->lldd_dev); + scb->ssp_tmf.retry_count = 1; + scb->ssp_tmf.itnl_to = cpu_to_le16(ITNL_TIMEOUT_CONST); + if (tmf == TMF_QUERY_TASK) + scb->ssp_tmf.index = cpu_to_le16(index); + + res = asd_enqueue_internal(ascb, asd_tmf_tasklet_complete, + asd_tmf_timedout); + if (res) + goto out_err; + wait_for_completion(&ascb->completion); + res = (int) (unsigned long) ascb->uldd_task; + + switch (res) { + case TC_NO_ERROR + 0xFF00: + res = TMF_RESP_FUNC_COMPLETE; + break; + case TF_NAK_RECV + 0xFF00: + res = TMF_RESP_INVALID_FRAME; + break; + case TF_TMF_TASK_DONE + 0xFF00: + res = TMF_RESP_FUNC_FAILED; + break; + case TF_TMF_NO_TAG + 0xFF00: + case TF_TMF_TAG_FREE + 0xFF00: /* the tag is in the free list */ + case TF_TMF_NO_CONN_HANDLE + 0xFF00: /* no such device */ + res = TMF_RESP_FUNC_COMPLETE; + break; + case TF_TMF_NO_CTX + 0xFF00: /* not in seq, or proto != SSP */ + res = TMF_RESP_FUNC_ESUPP; + break; + default: + ASD_DPRINTK("%s: converting result 0x%x to TMF_RESP_FUNC_FAILED\n", + __FUNCTION__, res); + res = TMF_RESP_FUNC_FAILED; + break; + } +out_err: + asd_ascb_free(ascb); + return res; +} + +int asd_abort_task_set(struct domain_device *dev, u8 *lun) +{ + int res = asd_initiate_ssp_tmf(dev, lun, TMF_ABORT_TASK_SET, 0); + + if (res == TMF_RESP_FUNC_COMPLETE) + asd_clear_nexus_I_T_L(dev, lun); + return res; +} + +int asd_clear_aca(struct domain_device *dev, u8 *lun) +{ + int res = asd_initiate_ssp_tmf(dev, lun, TMF_CLEAR_ACA, 0); + + if (res == TMF_RESP_FUNC_COMPLETE) + asd_clear_nexus_I_T_L(dev, lun); + return res; +} + +int asd_clear_task_set(struct domain_device *dev, u8 *lun) +{ + int res = asd_initiate_ssp_tmf(dev, lun, TMF_CLEAR_TASK_SET, 0); + + if (res == TMF_RESP_FUNC_COMPLETE) + asd_clear_nexus_I_T_L(dev, lun); + return res; +} + +int asd_lu_reset(struct domain_device *dev, u8 *lun) +{ + int res = asd_initiate_ssp_tmf(dev, lun, TMF_LU_RESET, 0); + + if (res == TMF_RESP_FUNC_COMPLETE) + asd_clear_nexus_I_T_L(dev, lun); + return res; +} + +/** + * asd_query_task -- send a QUERY TASK TMF to an I_T_L_Q nexus + * task: pointer to sas_task struct of interest + * + * Returns: TMF_RESP_FUNC_COMPLETE if the task is not in the task set, + * or TMF_RESP_FUNC_SUCC if the task is in the task set. + * + * Normally the management layer sets the task to aborted state, + * and then calls query task and then abort task. + */ +int asd_query_task(struct sas_task *task) +{ + struct asd_ascb *ascb = task->lldd_task; + int index; + + if (ascb) { + index = ascb->tc_index; + return asd_initiate_ssp_tmf(task->dev, task->ssp_task.LUN, + TMF_QUERY_TASK, index); + } + return TMF_RESP_FUNC_COMPLETE; +} diff --git a/drivers/scsi/libsas/Kconfig b/drivers/scsi/libsas/Kconfig new file mode 100644 index 00000000000..aafdc92f831 --- /dev/null +++ b/drivers/scsi/libsas/Kconfig @@ -0,0 +1,39 @@ +# +# Kernel configuration file for the SAS Class +# +# Copyright (C) 2005 Adaptec, Inc. All rights reserved. +# Copyright (C) 2005 Luben Tuikov +# +# This file is licensed under GPLv2. +# +# 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; version 2 of the +# License. +# +# 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; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA +# + +config SCSI_SAS_LIBSAS + tristate "SAS Domain Transport Attributes" + depends on SCSI + select SCSI_SAS_ATTRS + help + This provides transport specific helpers for SAS drivers which + use the domain device construct (like the aic94xxx). + +config SCSI_SAS_LIBSAS_DEBUG + bool "Compile the SAS Domain Transport Attributes in debug mode" + default y + depends on SCSI_SAS_LIBSAS + help + Compiles the SAS Layer in debug mode. In debug mode, the + SAS Layer prints diagnostic and debug messages. diff --git a/drivers/scsi/libsas/Makefile b/drivers/scsi/libsas/Makefile new file mode 100644 index 00000000000..44d972a3b4b --- /dev/null +++ b/drivers/scsi/libsas/Makefile @@ -0,0 +1,36 @@ +# +# Kernel Makefile for the libsas helpers +# +# Copyright (C) 2005 Adaptec, Inc. All rights reserved. +# Copyright (C) 2005 Luben Tuikov +# +# This file is licensed under GPLv2. +# +# 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; version 2 of the +# License. +# +# 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; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA + +ifeq ($(CONFIG_SCSI_SAS_LIBSAS_DEBUG),y) + EXTRA_CFLAGS += -DSAS_DEBUG +endif + +obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas.o +libsas-y += sas_init.o \ + sas_phy.o \ + sas_port.o \ + sas_event.o \ + sas_dump.o \ + sas_discover.o \ + sas_expander.o \ + sas_scsi_host.o diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c new file mode 100644 index 00000000000..d977bd492d8 --- /dev/null +++ b/drivers/scsi/libsas/sas_discover.c @@ -0,0 +1,749 @@ +/* + * Serial Attached SCSI (SAS) Discover process + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * 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 of the + * License, 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; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include "sas_internal.h" + +#include +#include +#include "../scsi_sas_internal.h" + +/* ---------- Basic task processing for discovery purposes ---------- */ + +void sas_init_dev(struct domain_device *dev) +{ + INIT_LIST_HEAD(&dev->siblings); + INIT_LIST_HEAD(&dev->dev_list_node); + switch (dev->dev_type) { + case SAS_END_DEV: + break; + case EDGE_DEV: + case FANOUT_DEV: + INIT_LIST_HEAD(&dev->ex_dev.children); + break; + case SATA_DEV: + case SATA_PM: + case SATA_PM_PORT: + INIT_LIST_HEAD(&dev->sata_dev.children); + break; + default: + break; + } +} + +static void sas_task_timedout(unsigned long _task) +{ + struct sas_task *task = (void *) _task; + unsigned long flags; + + spin_lock_irqsave(&task->task_state_lock, flags); + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) + task->task_state_flags |= SAS_TASK_STATE_ABORTED; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + complete(&task->completion); +} + +static void sas_disc_task_done(struct sas_task *task) +{ + if (!del_timer(&task->timer)) + return; + complete(&task->completion); +} + +#define SAS_DEV_TIMEOUT 10 + +/** + * sas_execute_task -- Basic task processing for discovery + * @task: the task to be executed + * @buffer: pointer to buffer to do I/O + * @size: size of @buffer + * @pci_dma_dir: PCI_DMA_... + */ +static int sas_execute_task(struct sas_task *task, void *buffer, int size, + int pci_dma_dir) +{ + int res = 0; + struct scatterlist *scatter = NULL; + struct task_status_struct *ts = &task->task_status; + int num_scatter = 0; + int retries = 0; + struct sas_internal *i = + to_sas_internal(task->dev->port->ha->core.shost->transportt); + + if (pci_dma_dir != PCI_DMA_NONE) { + scatter = kzalloc(sizeof(*scatter), GFP_KERNEL); + if (!scatter) + goto out; + + sg_init_one(scatter, buffer, size); + num_scatter = 1; + } + + task->task_proto = task->dev->tproto; + task->scatter = scatter; + task->num_scatter = num_scatter; + task->total_xfer_len = size; + task->data_dir = pci_dma_dir; + task->task_done = sas_disc_task_done; + + for (retries = 0; retries < 5; retries++) { + task->task_state_flags = SAS_TASK_STATE_PENDING; + init_completion(&task->completion); + + task->timer.data = (unsigned long) task; + task->timer.function = sas_task_timedout; + task->timer.expires = jiffies + SAS_DEV_TIMEOUT*HZ; + add_timer(&task->timer); + + res = i->dft->lldd_execute_task(task, 1, GFP_KERNEL); + if (res) { + del_timer(&task->timer); + SAS_DPRINTK("executing SAS discovery task failed:%d\n", + res); + goto ex_err; + } + wait_for_completion(&task->completion); + res = -ETASK; + if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { + int res2; + SAS_DPRINTK("task aborted, flags:0x%x\n", + task->task_state_flags); + res2 = i->dft->lldd_abort_task(task); + SAS_DPRINTK("came back from abort task\n"); + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { + if (res2 == TMF_RESP_FUNC_COMPLETE) + continue; /* Retry the task */ + else + goto ex_err; + } + } + if (task->task_status.stat == SAM_BUSY || + task->task_status.stat == SAM_TASK_SET_FULL || + task->task_status.stat == SAS_QUEUE_FULL) { + SAS_DPRINTK("task: q busy, sleeping...\n"); + schedule_timeout_interruptible(HZ); + } else if (task->task_status.stat == SAM_CHECK_COND) { + struct scsi_sense_hdr shdr; + + if (!scsi_normalize_sense(ts->buf, ts->buf_valid_size, + &shdr)) { + SAS_DPRINTK("couldn't normalize sense\n"); + continue; + } + if ((shdr.sense_key == 6 && shdr.asc == 0x29) || + (shdr.sense_key == 2 && shdr.asc == 4 && + shdr.ascq == 1)) { + SAS_DPRINTK("device %016llx LUN: %016llx " + "powering up or not ready yet, " + "sleeping...\n", + SAS_ADDR(task->dev->sas_addr), + SAS_ADDR(task->ssp_task.LUN)); + + schedule_timeout_interruptible(5*HZ); + } else if (shdr.sense_key == 1) { + res = 0; + break; + } else if (shdr.sense_key == 5) { + break; + } else { + SAS_DPRINTK("dev %016llx LUN: %016llx " + "sense key:0x%x ASC:0x%x ASCQ:0x%x" + "\n", + SAS_ADDR(task->dev->sas_addr), + SAS_ADDR(task->ssp_task.LUN), + shdr.sense_key, + shdr.asc, shdr.ascq); + } + } else if (task->task_status.resp != SAS_TASK_COMPLETE || + task->task_status.stat != SAM_GOOD) { + SAS_DPRINTK("task finished with resp:0x%x, " + "stat:0x%x\n", + task->task_status.resp, + task->task_status.stat); + goto ex_err; + } else { + res = 0; + break; + } + } +ex_err: + if (pci_dma_dir != PCI_DMA_NONE) + kfree(scatter); +out: + return res; +} + +/* ---------- Domain device discovery ---------- */ + +/** + * sas_get_port_device -- Discover devices which caused port creation + * @port: pointer to struct sas_port of interest + * + * Devices directly attached to a HA port, have no parent. This is + * how we know they are (domain) "root" devices. All other devices + * do, and should have their "parent" pointer set appropriately as + * soon as a child device is discovered. + */ +static int sas_get_port_device(struct asd_sas_port *port) +{ + unsigned long flags; + struct asd_sas_phy *phy; + struct sas_rphy *rphy; + struct domain_device *dev; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + spin_lock_irqsave(&port->phy_list_lock, flags); + if (list_empty(&port->phy_list)) { + spin_unlock_irqrestore(&port->phy_list_lock, flags); + kfree(dev); + return -ENODEV; + } + phy = container_of(port->phy_list.next, struct asd_sas_phy, port_phy_el); + spin_lock(&phy->frame_rcvd_lock); + memcpy(dev->frame_rcvd, phy->frame_rcvd, min(sizeof(dev->frame_rcvd), + (size_t)phy->frame_rcvd_size)); + spin_unlock(&phy->frame_rcvd_lock); + spin_unlock_irqrestore(&port->phy_list_lock, flags); + + if (dev->frame_rcvd[0] == 0x34 && port->oob_mode == SATA_OOB_MODE) { + struct dev_to_host_fis *fis = + (struct dev_to_host_fis *) dev->frame_rcvd; + if (fis->interrupt_reason == 1 && fis->lbal == 1 && + fis->byte_count_low==0x69 && fis->byte_count_high == 0x96 + && (fis->device & ~0x10) == 0) + dev->dev_type = SATA_PM; + else + dev->dev_type = SATA_DEV; + dev->tproto = SATA_PROTO; + } else { + struct sas_identify_frame *id = + (struct sas_identify_frame *) dev->frame_rcvd; + dev->dev_type = id->dev_type; + dev->iproto = id->initiator_bits; + dev->tproto = id->target_bits; + } + + sas_init_dev(dev); + + switch (dev->dev_type) { + case SAS_END_DEV: + rphy = sas_end_device_alloc(port->port); + break; + case EDGE_DEV: + rphy = sas_expander_alloc(port->port, + SAS_EDGE_EXPANDER_DEVICE); + break; + case FANOUT_DEV: + rphy = sas_expander_alloc(port->port, + SAS_FANOUT_EXPANDER_DEVICE); + break; + case SATA_DEV: + default: + printk("ERROR: Unidentified device type %d\n", dev->dev_type); + rphy = NULL; + break; + } + + if (!rphy) { + kfree(dev); + return -ENODEV; + } + rphy->identify.phy_identifier = phy->phy->identify.phy_identifier; + memcpy(dev->sas_addr, port->attached_sas_addr, SAS_ADDR_SIZE); + sas_fill_in_rphy(dev, rphy); + sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr); + port->port_dev = dev; + dev->port = port; + dev->linkrate = port->linkrate; + dev->min_linkrate = port->linkrate; + dev->max_linkrate = port->linkrate; + dev->pathways = port->num_phys; + memset(port->disc.fanout_sas_addr, 0, SAS_ADDR_SIZE); + memset(port->disc.eeds_a, 0, SAS_ADDR_SIZE); + memset(port->disc.eeds_b, 0, SAS_ADDR_SIZE); + port->disc.max_level = 0; + + dev->rphy = rphy; + spin_lock(&port->dev_list_lock); + list_add_tail(&dev->dev_list_node, &port->dev_list); + spin_unlock(&port->dev_list_lock); + + return 0; +} + +/* ---------- Discover and Revalidate ---------- */ + +/* ---------- SATA ---------- */ + +static void sas_get_ata_command_set(struct domain_device *dev) +{ + struct dev_to_host_fis *fis = + (struct dev_to_host_fis *) dev->frame_rcvd; + + if ((fis->sector_count == 1 && /* ATA */ + fis->lbal == 1 && + fis->lbam == 0 && + fis->lbah == 0 && + fis->device == 0) + || + (fis->sector_count == 0 && /* CE-ATA (mATA) */ + fis->lbal == 0 && + fis->lbam == 0xCE && + fis->lbah == 0xAA && + (fis->device & ~0x10) == 0)) + + dev->sata_dev.command_set = ATA_COMMAND_SET; + + else if ((fis->interrupt_reason == 1 && /* ATAPI */ + fis->lbal == 1 && + fis->byte_count_low == 0x14 && + fis->byte_count_high == 0xEB && + (fis->device & ~0x10) == 0)) + + dev->sata_dev.command_set = ATAPI_COMMAND_SET; + + else if ((fis->sector_count == 1 && /* SEMB */ + fis->lbal == 1 && + fis->lbam == 0x3C && + fis->lbah == 0xC3 && + fis->device == 0) + || + (fis->interrupt_reason == 1 && /* SATA PM */ + fis->lbal == 1 && + fis->byte_count_low == 0x69 && + fis->byte_count_high == 0x96 && + (fis->device & ~0x10) == 0)) + + /* Treat it as a superset? */ + dev->sata_dev.command_set = ATAPI_COMMAND_SET; +} + +/** + * sas_issue_ata_cmd -- Basic SATA command processing for discovery + * @dev: the device to send the command to + * @command: the command register + * @features: the features register + * @buffer: pointer to buffer to do I/O + * @size: size of @buffer + * @pci_dma_dir: PCI_DMA_... + */ +static int sas_issue_ata_cmd(struct domain_device *dev, u8 command, + u8 features, void *buffer, int size, + int pci_dma_dir) +{ + int res = 0; + struct sas_task *task; + struct dev_to_host_fis *d2h_fis = (struct dev_to_host_fis *) + &dev->frame_rcvd[0]; + + res = -ENOMEM; + task = sas_alloc_task(GFP_KERNEL); + if (!task) + goto out; + + task->dev = dev; + + task->ata_task.fis.command = command; + task->ata_task.fis.features = features; + task->ata_task.fis.device = d2h_fis->device; + task->ata_task.retry_count = 1; + + res = sas_execute_task(task, buffer, size, pci_dma_dir); + + sas_free_task(task); +out: + return res; +} + +static void sas_sata_propagate_sas_addr(struct domain_device *dev) +{ + unsigned long flags; + struct asd_sas_port *port = dev->port; + struct asd_sas_phy *phy; + + BUG_ON(dev->parent); + + memcpy(port->attached_sas_addr, dev->sas_addr, SAS_ADDR_SIZE); + spin_lock_irqsave(&port->phy_list_lock, flags); + list_for_each_entry(phy, &port->phy_list, port_phy_el) + memcpy(phy->attached_sas_addr, dev->sas_addr, SAS_ADDR_SIZE); + spin_unlock_irqrestore(&port->phy_list_lock, flags); +} + +#define ATA_IDENTIFY_DEV 0xEC +#define ATA_IDENTIFY_PACKET_DEV 0xA1 +#define ATA_SET_FEATURES 0xEF +#define ATA_FEATURE_PUP_STBY_SPIN_UP 0x07 + +/** + * sas_discover_sata_dev -- discover a STP/SATA device (SATA_DEV) + * @dev: STP/SATA device of interest (ATA/ATAPI) + * + * The LLDD has already been notified of this device, so that we can + * send FISes to it. Here we try to get IDENTIFY DEVICE or IDENTIFY + * PACKET DEVICE, if ATAPI device, so that the LLDD can fine-tune its + * performance for this device. + */ +static int sas_discover_sata_dev(struct domain_device *dev) +{ + int res; + __le16 *identify_x; + u8 command; + + identify_x = kzalloc(512, GFP_KERNEL); + if (!identify_x) + return -ENOMEM; + + if (dev->sata_dev.command_set == ATA_COMMAND_SET) { + dev->sata_dev.identify_device = identify_x; + command = ATA_IDENTIFY_DEV; + } else { + dev->sata_dev.identify_packet_device = identify_x; + command = ATA_IDENTIFY_PACKET_DEV; + } + + res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512, + PCI_DMA_FROMDEVICE); + if (res) + goto out_err; + + /* lives on the media? */ + if (le16_to_cpu(identify_x[0]) & 4) { + /* incomplete response */ + SAS_DPRINTK("sending SET FEATURE/PUP_STBY_SPIN_UP to " + "dev %llx\n", SAS_ADDR(dev->sas_addr)); + if (!le16_to_cpu(identify_x[83] & (1<<6))) + goto cont1; + res = sas_issue_ata_cmd(dev, ATA_SET_FEATURES, + ATA_FEATURE_PUP_STBY_SPIN_UP, + NULL, 0, PCI_DMA_NONE); + if (res) + goto cont1; + + schedule_timeout_interruptible(5*HZ); /* More time? */ + res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512, + PCI_DMA_FROMDEVICE); + if (res) + goto out_err; + } +cont1: + /* Get WWN */ + if (dev->port->oob_mode != SATA_OOB_MODE) { + memcpy(dev->sas_addr, dev->sata_dev.rps_resp.rps.stp_sas_addr, + SAS_ADDR_SIZE); + } else if (dev->sata_dev.command_set == ATA_COMMAND_SET && + (le16_to_cpu(dev->sata_dev.identify_device[108]) & 0xF000) + == 0x5000) { + int i; + + for (i = 0; i < 4; i++) { + dev->sas_addr[2*i] = + (le16_to_cpu(dev->sata_dev.identify_device[108+i]) & 0xFF00) >> 8; + dev->sas_addr[2*i+1] = + le16_to_cpu(dev->sata_dev.identify_device[108+i]) & 0x00FF; + } + } + sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr); + if (!dev->parent) + sas_sata_propagate_sas_addr(dev); + + /* XXX Hint: register this SATA device with SATL. + When this returns, dev->sata_dev->lu is alive and + present. + sas_satl_register_dev(dev); + */ + return 0; +out_err: + dev->sata_dev.identify_packet_device = NULL; + dev->sata_dev.identify_device = NULL; + kfree(identify_x); + return res; +} + +static int sas_discover_sata_pm(struct domain_device *dev) +{ + return -ENODEV; +} + +int sas_notify_lldd_dev_found(struct domain_device *dev) +{ + int res = 0; + struct sas_ha_struct *sas_ha = dev->port->ha; + struct Scsi_Host *shost = sas_ha->core.shost; + struct sas_internal *i = to_sas_internal(shost->transportt); + + if (i->dft->lldd_dev_found) { + res = i->dft->lldd_dev_found(dev); + if (res) { + printk("sas: driver on pcidev %s cannot handle " + "device %llx, error:%d\n", + pci_name(sas_ha->pcidev), + SAS_ADDR(dev->sas_addr), res); + } + } + return res; +} + + +void sas_notify_lldd_dev_gone(struct domain_device *dev) +{ + struct sas_ha_struct *sas_ha = dev->port->ha; + struct Scsi_Host *shost = sas_ha->core.shost; + struct sas_internal *i = to_sas_internal(shost->transportt); + + if (i->dft->lldd_dev_gone) + i->dft->lldd_dev_gone(dev); +} + +/* ---------- Common/dispatchers ---------- */ + +/** + * sas_discover_sata -- discover an STP/SATA domain device + * @dev: pointer to struct domain_device of interest + * + * First we notify the LLDD of this device, so we can send frames to + * it. Then depending on the type of device we call the appropriate + * discover functions. Once device discover is done, we notify the + * LLDD so that it can fine-tune its parameters for the device, by + * removing it and then adding it. That is, the second time around, + * the driver would have certain fields, that it is looking at, set. + * Finally we initialize the kobj so that the device can be added to + * the system at registration time. Devices directly attached to a HA + * port, have no parents. All other devices do, and should have their + * "parent" pointer set appropriately before calling this function. + */ +int sas_discover_sata(struct domain_device *dev) +{ + int res; + + sas_get_ata_command_set(dev); + + res = sas_notify_lldd_dev_found(dev); + if (res) + return res; + + switch (dev->dev_type) { + case SATA_DEV: + res = sas_discover_sata_dev(dev); + break; + case SATA_PM: + res = sas_discover_sata_pm(dev); + break; + default: + break; + } + + sas_notify_lldd_dev_gone(dev); + if (!res) { + sas_notify_lldd_dev_found(dev); + } + return res; +} + +/** + * sas_discover_end_dev -- discover an end device (SSP, etc) + * @end: pointer to domain device of interest + * + * See comment in sas_discover_sata(). + */ +int sas_discover_end_dev(struct domain_device *dev) +{ + int res; + + res = sas_notify_lldd_dev_found(dev); + if (res) + return res; + + res = sas_rphy_add(dev->rphy); + if (res) + goto out_err; + + /* do this to get the end device port attributes which will have + * been scanned in sas_rphy_add */ + sas_notify_lldd_dev_gone(dev); + sas_notify_lldd_dev_found(dev); + + return 0; + +out_err: + sas_notify_lldd_dev_gone(dev); + return res; +} + +/* ---------- Device registration and unregistration ---------- */ + +static inline void sas_unregister_common_dev(struct domain_device *dev) +{ + sas_notify_lldd_dev_gone(dev); + if (!dev->parent) + dev->port->port_dev = NULL; + else + list_del_init(&dev->siblings); + list_del_init(&dev->dev_list_node); +} + +void sas_unregister_dev(struct domain_device *dev) +{ + if (dev->rphy) { + sas_remove_children(&dev->rphy->dev); + sas_rphy_delete(dev->rphy); + dev->rphy = NULL; + } + if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) { + /* remove the phys and ports, everything else should be gone */ + kfree(dev->ex_dev.ex_phy); + dev->ex_dev.ex_phy = NULL; + } + sas_unregister_common_dev(dev); +} + +void sas_unregister_domain_devices(struct asd_sas_port *port) +{ + struct domain_device *dev, *n; + + list_for_each_entry_safe_reverse(dev,n,&port->dev_list,dev_list_node) + sas_unregister_dev(dev); + + port->port->rphy = NULL; + +} + +/* ---------- Discovery and Revalidation ---------- */ + +/** + * sas_discover_domain -- discover the domain + * @port: port to the domain of interest + * + * NOTE: this process _must_ quit (return) as soon as any connection + * errors are encountered. Connection recovery is done elsewhere. + * Discover process only interrogates devices in order to discover the + * domain. + */ +static void sas_discover_domain(void *data) +{ + int error = 0; + struct asd_sas_port *port = data; + + sas_begin_event(DISCE_DISCOVER_DOMAIN, &port->disc.disc_event_lock, + &port->disc.pending); + + if (port->port_dev) + return ; + else { + error = sas_get_port_device(port); + if (error) + return; + } + + SAS_DPRINTK("DOING DISCOVERY on port %d, pid:%d\n", port->id, + current->pid); + + switch (port->port_dev->dev_type) { + case SAS_END_DEV: + error = sas_discover_end_dev(port->port_dev); + break; + case EDGE_DEV: + case FANOUT_DEV: + error = sas_discover_root_expander(port->port_dev); + break; + case SATA_DEV: + case SATA_PM: + error = sas_discover_sata(port->port_dev); + break; + default: + SAS_DPRINTK("unhandled device %d\n", port->port_dev->dev_type); + break; + } + + if (error) { + kfree(port->port_dev); /* not kobject_register-ed yet */ + port->port_dev = NULL; + } + + SAS_DPRINTK("DONE DISCOVERY on port %d, pid:%d, result:%d\n", port->id, + current->pid, error); +} + +static void sas_revalidate_domain(void *data) +{ + int res = 0; + struct asd_sas_port *port = data; + + sas_begin_event(DISCE_REVALIDATE_DOMAIN, &port->disc.disc_event_lock, + &port->disc.pending); + + SAS_DPRINTK("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id, + current->pid); + if (port->port_dev) + res = sas_ex_revalidate_domain(port->port_dev); + + SAS_DPRINTK("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n", + port->id, current->pid, res); +} + +/* ---------- Events ---------- */ + +int sas_discover_event(struct asd_sas_port *port, enum discover_event ev) +{ + struct sas_discovery *disc; + + if (!port) + return 0; + disc = &port->disc; + + BUG_ON(ev >= DISC_NUM_EVENTS); + + sas_queue_event(ev, &disc->disc_event_lock, &disc->pending, + &disc->disc_work[ev], port->ha->core.shost); + + return 0; +} + +/** + * sas_init_disc -- initialize the discovery struct in the port + * @port: pointer to struct port + * + * Called when the ports are being initialized. + */ +void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port) +{ + int i; + + static void (*sas_event_fns[DISC_NUM_EVENTS])(void *) = { + [DISCE_DISCOVER_DOMAIN] = sas_discover_domain, + [DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain, + }; + + spin_lock_init(&disc->disc_event_lock); + disc->pending = 0; + for (i = 0; i < DISC_NUM_EVENTS; i++) + INIT_WORK(&disc->disc_work[i], sas_event_fns[i], port); +} diff --git a/drivers/scsi/libsas/sas_dump.c b/drivers/scsi/libsas/sas_dump.c new file mode 100644 index 00000000000..f1246d2c9be --- /dev/null +++ b/drivers/scsi/libsas/sas_dump.c @@ -0,0 +1,76 @@ +/* + * Serial Attached SCSI (SAS) Dump/Debugging routines + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * 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 of the + * License, 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; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "sas_dump.h" + +#ifdef SAS_DEBUG + +static const char *sas_hae_str[] = { + [0] = "HAE_RESET", +}; + +static const char *sas_porte_str[] = { + [0] = "PORTE_BYTES_DMAED", + [1] = "PORTE_BROADCAST_RCVD", + [2] = "PORTE_LINK_RESET_ERR", + [3] = "PORTE_TIMER_EVENT", + [4] = "PORTE_HARD_RESET", +}; + +static const char *sas_phye_str[] = { + [0] = "PHYE_LOSS_OF_SIGNAL", + [1] = "PHYE_OOB_DONE", + [2] = "PHYE_OOB_ERROR", + [3] = "PHYE_SPINUP_HOLD", +}; + +void sas_dprint_porte(int phyid, enum port_event pe) +{ + SAS_DPRINTK("phy%d: port event: %s\n", phyid, sas_porte_str[pe]); +} +void sas_dprint_phye(int phyid, enum phy_event pe) +{ + SAS_DPRINTK("phy%d: phy event: %s\n", phyid, sas_phye_str[pe]); +} + +void sas_dprint_hae(struct sas_ha_struct *sas_ha, enum ha_event he) +{ + SAS_DPRINTK("ha %s: %s event\n", pci_name(sas_ha->pcidev), + sas_hae_str[he]); +} + +void sas_dump_port(struct asd_sas_port *port) +{ + SAS_DPRINTK("port%d: class:0x%x\n", port->id, port->class); + SAS_DPRINTK("port%d: sas_addr:%llx\n", port->id, + SAS_ADDR(port->sas_addr)); + SAS_DPRINTK("port%d: attached_sas_addr:%llx\n", port->id, + SAS_ADDR(port->attached_sas_addr)); + SAS_DPRINTK("port%d: iproto:0x%x\n", port->id, port->iproto); + SAS_DPRINTK("port%d: tproto:0x%x\n", port->id, port->tproto); + SAS_DPRINTK("port%d: oob_mode:0x%x\n", port->id, port->oob_mode); + SAS_DPRINTK("port%d: num_phys:%d\n", port->id, port->num_phys); +} + +#endif /* SAS_DEBUG */ diff --git a/drivers/scsi/libsas/sas_dump.h b/drivers/scsi/libsas/sas_dump.h new file mode 100644 index 00000000000..47b45d4f525 --- /dev/null +++ b/drivers/scsi/libsas/sas_dump.h @@ -0,0 +1,42 @@ +/* + * Serial Attached SCSI (SAS) Dump/Debugging routines header file + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * 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 of the + * License, 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; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "sas_internal.h" + +#ifdef SAS_DEBUG + +void sas_dprint_porte(int phyid, enum port_event pe); +void sas_dprint_phye(int phyid, enum phy_event pe); +void sas_dprint_hae(struct sas_ha_struct *sas_ha, enum ha_event he); +void sas_dump_port(struct asd_sas_port *port); + +#else /* SAS_DEBUG */ + +static inline void sas_dprint_porte(int phyid, enum port_event pe) { } +static inline void sas_dprint_phye(int phyid, enum phy_event pe) { } +static inline void sas_dprint_hae(struct sas_ha_struct *sas_ha, + enum ha_event he) { } +static inline void sas_dump_port(struct asd_sas_port *port) { } + +#endif /* SAS_DEBUG */ diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c new file mode 100644 index 00000000000..19110ed1c89 --- /dev/null +++ b/drivers/scsi/libsas/sas_event.c @@ -0,0 +1,75 @@ +/* + * Serial Attached SCSI (SAS) Event processing + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * 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 of the + * License, 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; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include "sas_internal.h" +#include "sas_dump.h" + +static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event) +{ + BUG_ON(event >= HA_NUM_EVENTS); + + sas_queue_event(event, &sas_ha->event_lock, &sas_ha->pending, + &sas_ha->ha_events[event], sas_ha->core.shost); +} + +static void notify_port_event(struct asd_sas_phy *phy, enum port_event event) +{ + struct sas_ha_struct *ha = phy->ha; + + BUG_ON(event >= PORT_NUM_EVENTS); + + sas_queue_event(event, &ha->event_lock, &phy->port_events_pending, + &phy->port_events[event], ha->core.shost); +} + +static void notify_phy_event(struct asd_sas_phy *phy, enum phy_event event) +{ + struct sas_ha_struct *ha = phy->ha; + + BUG_ON(event >= PHY_NUM_EVENTS); + + sas_queue_event(event, &ha->event_lock, &phy->phy_events_pending, + &phy->phy_events[event], ha->core.shost); +} + +int sas_init_events(struct sas_ha_struct *sas_ha) +{ + static void (*sas_ha_event_fns[HA_NUM_EVENTS])(void *) = { + [HAE_RESET] = sas_hae_reset, + }; + + int i; + + spin_lock_init(&sas_ha->event_lock); + + for (i = 0; i < HA_NUM_EVENTS; i++) + INIT_WORK(&sas_ha->ha_events[i], sas_ha_event_fns[i], sas_ha); + + sas_ha->notify_ha_event = notify_ha_event; + sas_ha->notify_port_event = notify_port_event; + sas_ha->notify_phy_event = notify_phy_event; + + return 0; +} diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c new file mode 100644 index 00000000000..b653a263f76 --- /dev/null +++ b/drivers/scsi/libsas/sas_expander.c @@ -0,0 +1,1862 @@ +/* + * Serial Attached SCSI (SAS) Expander discovery and configuration + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * 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 of the + * License, 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; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include + +#include "sas_internal.h" + +#include +#include +#include "../scsi_sas_internal.h" + +static int sas_discover_expander(struct domain_device *dev); +static int sas_configure_routing(struct domain_device *dev, u8 *sas_addr); +static int sas_configure_phy(struct domain_device *dev, int phy_id, + u8 *sas_addr, int include); +static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr); + +#if 0 +/* FIXME: smp needs to migrate into the sas class */ +static ssize_t smp_portal_read(struct kobject *, char *, loff_t, size_t); +static ssize_t smp_portal_write(struct kobject *, char *, loff_t, size_t); +#endif + +/* ---------- SMP task management ---------- */ + +static void smp_task_timedout(unsigned long _task) +{ + struct sas_task *task = (void *) _task; + unsigned long flags; + + spin_lock_irqsave(&task->task_state_lock, flags); + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) + task->task_state_flags |= SAS_TASK_STATE_ABORTED; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + complete(&task->completion); +} + +static void smp_task_done(struct sas_task *task) +{ + if (!del_timer(&task->timer)) + return; + complete(&task->completion); +} + +/* Give it some long enough timeout. In seconds. */ +#define SMP_TIMEOUT 10 + +static int smp_execute_task(struct domain_device *dev, void *req, int req_size, + void *resp, int resp_size) +{ + int res; + struct sas_task *task = sas_alloc_task(GFP_KERNEL); + struct sas_internal *i = + to_sas_internal(dev->port->ha->core.shost->transportt); + + if (!task) + return -ENOMEM; + + task->dev = dev; + task->task_proto = dev->tproto; + sg_init_one(&task->smp_task.smp_req, req, req_size); + sg_init_one(&task->smp_task.smp_resp, resp, resp_size); + + task->task_done = smp_task_done; + + task->timer.data = (unsigned long) task; + task->timer.function = smp_task_timedout; + task->timer.expires = jiffies + SMP_TIMEOUT*HZ; + add_timer(&task->timer); + + res = i->dft->lldd_execute_task(task, 1, GFP_KERNEL); + + if (res) { + del_timer(&task->timer); + SAS_DPRINTK("executing SMP task failed:%d\n", res); + goto ex_err; + } + + wait_for_completion(&task->completion); + res = -ETASK; + if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { + SAS_DPRINTK("smp task timed out or aborted\n"); + i->dft->lldd_abort_task(task); + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { + SAS_DPRINTK("SMP task aborted and not done\n"); + goto ex_err; + } + } + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAM_GOOD) + res = 0; + else + SAS_DPRINTK("%s: task to dev %016llx response: 0x%x " + "status 0x%x\n", __FUNCTION__, + SAS_ADDR(dev->sas_addr), + task->task_status.resp, + task->task_status.stat); +ex_err: + sas_free_task(task); + return res; +} + +/* ---------- Allocations ---------- */ + +static inline void *alloc_smp_req(int size) +{ + u8 *p = kzalloc(size, GFP_KERNEL); + if (p) + p[0] = SMP_REQUEST; + return p; +} + +static inline void *alloc_smp_resp(int size) +{ + return kzalloc(size, GFP_KERNEL); +} + +/* ---------- Expander configuration ---------- */ + +static void sas_set_ex_phy(struct domain_device *dev, int phy_id, + void *disc_resp) +{ + struct expander_device *ex = &dev->ex_dev; + struct ex_phy *phy = &ex->ex_phy[phy_id]; + struct smp_resp *resp = disc_resp; + struct discover_resp *dr = &resp->disc; + struct sas_rphy *rphy = dev->rphy; + int rediscover = (phy->phy != NULL); + + if (!rediscover) { + phy->phy = sas_phy_alloc(&rphy->dev, phy_id); + + /* FIXME: error_handling */ + BUG_ON(!phy->phy); + } + + switch (resp->result) { + case SMP_RESP_PHY_VACANT: + phy->phy_state = PHY_VACANT; + return; + default: + phy->phy_state = PHY_NOT_PRESENT; + return; + case SMP_RESP_FUNC_ACC: + phy->phy_state = PHY_EMPTY; /* do not know yet */ + break; + } + + phy->phy_id = phy_id; + phy->attached_dev_type = dr->attached_dev_type; + phy->linkrate = dr->linkrate; + phy->attached_sata_host = dr->attached_sata_host; + phy->attached_sata_dev = dr->attached_sata_dev; + phy->attached_sata_ps = dr->attached_sata_ps; + phy->attached_iproto = dr->iproto << 1; + phy->attached_tproto = dr->tproto << 1; + memcpy(phy->attached_sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE); + phy->attached_phy_id = dr->attached_phy_id; + phy->phy_change_count = dr->change_count; + phy->routing_attr = dr->routing_attr; + phy->virtual = dr->virtual; + phy->last_da_index = -1; + + phy->phy->identify.initiator_port_protocols = phy->attached_iproto; + phy->phy->identify.target_port_protocols = phy->attached_tproto; + phy->phy->identify.phy_identifier = phy_id; + phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; + phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS; + phy->phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS; + phy->phy->maximum_linkrate = SAS_LINK_RATE_3_0_GBPS; + switch (phy->linkrate) { + case PHY_LINKRATE_1_5: + phy->phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS; + break; + case PHY_LINKRATE_3: + phy->phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS; + break; + case PHY_LINKRATE_6: + phy->phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS; + break; + default: + phy->phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; + break; + } + + if (!rediscover) + sas_phy_add(phy->phy); + + SAS_DPRINTK("ex %016llx phy%02d:%c attached: %016llx\n", + SAS_ADDR(dev->sas_addr), phy->phy_id, + phy->routing_attr == TABLE_ROUTING ? 'T' : + phy->routing_attr == DIRECT_ROUTING ? 'D' : + phy->routing_attr == SUBTRACTIVE_ROUTING ? 'S' : '?', + SAS_ADDR(phy->attached_sas_addr)); + + return; +} + +#define DISCOVER_REQ_SIZE 16 +#define DISCOVER_RESP_SIZE 56 + +static int sas_ex_phy_discover(struct domain_device *dev, int single) +{ + struct expander_device *ex = &dev->ex_dev; + int res = 0; + u8 *disc_req; + u8 *disc_resp; + + disc_req = alloc_smp_req(DISCOVER_REQ_SIZE); + if (!disc_req) + return -ENOMEM; + + disc_resp = alloc_smp_req(DISCOVER_RESP_SIZE); + if (!disc_resp) { + kfree(disc_req); + return -ENOMEM; + } + + disc_req[1] = SMP_DISCOVER; + + if (0 <= single && single < ex->num_phys) { + disc_req[9] = single; + res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, + disc_resp, DISCOVER_RESP_SIZE); + if (res) + goto out_err; + sas_set_ex_phy(dev, single, disc_resp); + } else { + int i; + + for (i = 0; i < ex->num_phys; i++) { + disc_req[9] = i; + res = smp_execute_task(dev, disc_req, + DISCOVER_REQ_SIZE, disc_resp, + DISCOVER_RESP_SIZE); + if (res) + goto out_err; + sas_set_ex_phy(dev, i, disc_resp); + } + } +out_err: + kfree(disc_resp); + kfree(disc_req); + return res; +} + +static int sas_expander_discover(struct domain_device *dev) +{ + struct expander_device *ex = &dev->ex_dev; + int res = -ENOMEM; + + ex->ex_phy = kzalloc(sizeof(*ex->ex_phy)*ex->num_phys, GFP_KERNEL); + if (!ex->ex_phy) + return -ENOMEM; + + res = sas_ex_phy_discover(dev, -1); + if (res) + goto out_err; + + return 0; + out_err: + kfree(ex->ex_phy); + ex->ex_phy = NULL; + return res; +} + +#define MAX_EXPANDER_PHYS 128 + +static void ex_assign_report_general(struct domain_device *dev, + struct smp_resp *resp) +{ + struct report_general_resp *rg = &resp->rg; + + dev->ex_dev.ex_change_count = be16_to_cpu(rg->change_count); + dev->ex_dev.max_route_indexes = be16_to_cpu(rg->route_indexes); + dev->ex_dev.num_phys = min(rg->num_phys, (u8)MAX_EXPANDER_PHYS); + dev->ex_dev.conf_route_table = rg->conf_route_table; + dev->ex_dev.configuring = rg->configuring; + memcpy(dev->ex_dev.enclosure_logical_id, rg->enclosure_logical_id, 8); +} + +#define RG_REQ_SIZE 8 +#define RG_RESP_SIZE 32 + +static int sas_ex_general(struct domain_device *dev) +{ + u8 *rg_req; + struct smp_resp *rg_resp; + int res; + int i; + + rg_req = alloc_smp_req(RG_REQ_SIZE); + if (!rg_req) + return -ENOMEM; + + rg_resp = alloc_smp_resp(RG_RESP_SIZE); + if (!rg_resp) { + kfree(rg_req); + return -ENOMEM; + } + + rg_req[1] = SMP_REPORT_GENERAL; + + for (i = 0; i < 5; i++) { + res = smp_execute_task(dev, rg_req, RG_REQ_SIZE, rg_resp, + RG_RESP_SIZE); + + if (res) { + SAS_DPRINTK("RG to ex %016llx failed:0x%x\n", + SAS_ADDR(dev->sas_addr), res); + goto out; + } else if (rg_resp->result != SMP_RESP_FUNC_ACC) { + SAS_DPRINTK("RG:ex %016llx returned SMP result:0x%x\n", + SAS_ADDR(dev->sas_addr), rg_resp->result); + res = rg_resp->result; + goto out; + } + + ex_assign_report_general(dev, rg_resp); + + if (dev->ex_dev.configuring) { + SAS_DPRINTK("RG: ex %llx self-configuring...\n", + SAS_ADDR(dev->sas_addr)); + schedule_timeout_interruptible(5*HZ); + } else + break; + } +out: + kfree(rg_req); + kfree(rg_resp); + return res; +} + +static void ex_assign_manuf_info(struct domain_device *dev, void + *_mi_resp) +{ + u8 *mi_resp = _mi_resp; + struct sas_rphy *rphy = dev->rphy; + struct sas_expander_device *edev = rphy_to_expander_device(rphy); + + memcpy(edev->vendor_id, mi_resp + 12, SAS_EXPANDER_VENDOR_ID_LEN); + memcpy(edev->product_id, mi_resp + 20, SAS_EXPANDER_PRODUCT_ID_LEN); + memcpy(edev->product_rev, mi_resp + 36, + SAS_EXPANDER_PRODUCT_REV_LEN); + + if (mi_resp[8] & 1) { + memcpy(edev->component_vendor_id, mi_resp + 40, + SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN); + edev->component_id = mi_resp[48] << 8 | mi_resp[49]; + edev->component_revision_id = mi_resp[50]; + } +} + +#define MI_REQ_SIZE 8 +#define MI_RESP_SIZE 64 + +static int sas_ex_manuf_info(struct domain_device *dev) +{ + u8 *mi_req; + u8 *mi_resp; + int res; + + mi_req = alloc_smp_req(MI_REQ_SIZE); + if (!mi_req) + return -ENOMEM; + + mi_resp = alloc_smp_resp(MI_RESP_SIZE); + if (!mi_resp) { + kfree(mi_req); + return -ENOMEM; + } + + mi_req[1] = SMP_REPORT_MANUF_INFO; + + res = smp_execute_task(dev, mi_req, MI_REQ_SIZE, mi_resp,MI_RESP_SIZE); + if (res) { + SAS_DPRINTK("MI: ex %016llx failed:0x%x\n", + SAS_ADDR(dev->sas_addr), res); + goto out; + } else if (mi_resp[2] != SMP_RESP_FUNC_ACC) { + SAS_DPRINTK("MI ex %016llx returned SMP result:0x%x\n", + SAS_ADDR(dev->sas_addr), mi_resp[2]); + goto out; + } + + ex_assign_manuf_info(dev, mi_resp); +out: + kfree(mi_req); + kfree(mi_resp); + return res; +} + +#define PC_REQ_SIZE 44 +#define PC_RESP_SIZE 8 + +int sas_smp_phy_control(struct domain_device *dev, int phy_id, + enum phy_func phy_func) +{ + u8 *pc_req; + u8 *pc_resp; + int res; + + pc_req = alloc_smp_req(PC_REQ_SIZE); + if (!pc_req) + return -ENOMEM; + + pc_resp = alloc_smp_resp(PC_RESP_SIZE); + if (!pc_resp) { + kfree(pc_req); + return -ENOMEM; + } + + pc_req[1] = SMP_PHY_CONTROL; + pc_req[9] = phy_id; + pc_req[10]= phy_func; + + res = smp_execute_task(dev, pc_req, PC_REQ_SIZE, pc_resp,PC_RESP_SIZE); + + kfree(pc_resp); + kfree(pc_req); + return res; +} + +static void sas_ex_disable_phy(struct domain_device *dev, int phy_id) +{ + struct expander_device *ex = &dev->ex_dev; + struct ex_phy *phy = &ex->ex_phy[phy_id]; + + sas_smp_phy_control(dev, phy_id, PHY_FUNC_DISABLE); + phy->linkrate = PHY_DISABLED; +} + +static void sas_ex_disable_port(struct domain_device *dev, u8 *sas_addr) +{ + struct expander_device *ex = &dev->ex_dev; + int i; + + for (i = 0; i < ex->num_phys; i++) { + struct ex_phy *phy = &ex->ex_phy[i]; + + if (phy->phy_state == PHY_VACANT || + phy->phy_state == PHY_NOT_PRESENT) + continue; + + if (SAS_ADDR(phy->attached_sas_addr) == SAS_ADDR(sas_addr)) + sas_ex_disable_phy(dev, i); + } +} + +static int sas_dev_present_in_domain(struct asd_sas_port *port, + u8 *sas_addr) +{ + struct domain_device *dev; + + if (SAS_ADDR(port->sas_addr) == SAS_ADDR(sas_addr)) + return 1; + list_for_each_entry(dev, &port->dev_list, dev_list_node) { + if (SAS_ADDR(dev->sas_addr) == SAS_ADDR(sas_addr)) + return 1; + } + return 0; +} + +#define RPEL_REQ_SIZE 16 +#define RPEL_RESP_SIZE 32 +int sas_smp_get_phy_events(struct sas_phy *phy) +{ + int res; + struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); + struct domain_device *dev = sas_find_dev_by_rphy(rphy); + u8 *req = alloc_smp_req(RPEL_REQ_SIZE); + u8 *resp = kzalloc(RPEL_RESP_SIZE, GFP_KERNEL); + + if (!resp) + return -ENOMEM; + + req[1] = SMP_REPORT_PHY_ERR_LOG; + req[9] = phy->number; + + res = smp_execute_task(dev, req, RPEL_REQ_SIZE, + resp, RPEL_RESP_SIZE); + + if (!res) + goto out; + + phy->invalid_dword_count = scsi_to_u32(&resp[12]); + phy->running_disparity_error_count = scsi_to_u32(&resp[16]); + phy->loss_of_dword_sync_count = scsi_to_u32(&resp[20]); + phy->phy_reset_problem_count = scsi_to_u32(&resp[24]); + + out: + kfree(resp); + return res; + +} + +#define RPS_REQ_SIZE 16 +#define RPS_RESP_SIZE 60 + +static int sas_get_report_phy_sata(struct domain_device *dev, + int phy_id, + struct smp_resp *rps_resp) +{ + int res; + u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE); + + if (!rps_req) + return -ENOMEM; + + rps_req[1] = SMP_REPORT_PHY_SATA; + rps_req[9] = phy_id; + + res = smp_execute_task(dev, rps_req, RPS_REQ_SIZE, + rps_resp, RPS_RESP_SIZE); + + kfree(rps_req); + return 0; +} + +static void sas_ex_get_linkrate(struct domain_device *parent, + struct domain_device *child, + struct ex_phy *parent_phy) +{ + struct expander_device *parent_ex = &parent->ex_dev; + struct sas_port *port; + int i; + + child->pathways = 0; + + port = parent_phy->port; + + for (i = 0; i < parent_ex->num_phys; i++) { + struct ex_phy *phy = &parent_ex->ex_phy[i]; + + if (phy->phy_state == PHY_VACANT || + phy->phy_state == PHY_NOT_PRESENT) + continue; + + if (SAS_ADDR(phy->attached_sas_addr) == + SAS_ADDR(child->sas_addr)) { + + child->min_linkrate = min(parent->min_linkrate, + phy->linkrate); + child->max_linkrate = max(parent->max_linkrate, + phy->linkrate); + child->pathways++; + sas_port_add_phy(port, phy->phy); + } + } + child->linkrate = min(parent_phy->linkrate, child->max_linkrate); + child->pathways = min(child->pathways, parent->pathways); +} + +static struct domain_device *sas_ex_discover_end_dev( + struct domain_device *parent, int phy_id) +{ + struct expander_device *parent_ex = &parent->ex_dev; + struct ex_phy *phy = &parent_ex->ex_phy[phy_id]; + struct domain_device *child = NULL; + struct sas_rphy *rphy; + int res; + + if (phy->attached_sata_host || phy->attached_sata_ps) + return NULL; + + child = kzalloc(sizeof(*child), GFP_KERNEL); + if (!child) + return NULL; + + child->parent = parent; + child->port = parent->port; + child->iproto = phy->attached_iproto; + memcpy(child->sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); + sas_hash_addr(child->hashed_sas_addr, child->sas_addr); + phy->port = sas_port_alloc(&parent->rphy->dev, phy_id); + BUG_ON(!phy->port); + /* FIXME: better error handling*/ + BUG_ON(sas_port_add(phy->port) != 0); + sas_ex_get_linkrate(parent, child, phy); + + if ((phy->attached_tproto & SAS_PROTO_STP) || phy->attached_sata_dev) { + child->dev_type = SATA_DEV; + if (phy->attached_tproto & SAS_PROTO_STP) + child->tproto = phy->attached_tproto; + if (phy->attached_sata_dev) + child->tproto |= SATA_DEV; + res = sas_get_report_phy_sata(parent, phy_id, + &child->sata_dev.rps_resp); + if (res) { + SAS_DPRINTK("report phy sata to %016llx:0x%x returned " + "0x%x\n", SAS_ADDR(parent->sas_addr), + phy_id, res); + kfree(child); + return NULL; + } + memcpy(child->frame_rcvd, &child->sata_dev.rps_resp.rps.fis, + sizeof(struct dev_to_host_fis)); + sas_init_dev(child); + res = sas_discover_sata(child); + if (res) { + SAS_DPRINTK("sas_discover_sata() for device %16llx at " + "%016llx:0x%x returned 0x%x\n", + SAS_ADDR(child->sas_addr), + SAS_ADDR(parent->sas_addr), phy_id, res); + kfree(child); + return NULL; + } + } else if (phy->attached_tproto & SAS_PROTO_SSP) { + child->dev_type = SAS_END_DEV; + rphy = sas_end_device_alloc(phy->port); + /* FIXME: error handling */ + BUG_ON(!rphy); + child->tproto = phy->attached_tproto; + sas_init_dev(child); + + child->rphy = rphy; + sas_fill_in_rphy(child, rphy); + + spin_lock(&parent->port->dev_list_lock); + list_add_tail(&child->dev_list_node, &parent->port->dev_list); + spin_unlock(&parent->port->dev_list_lock); + + res = sas_discover_end_dev(child); + if (res) { + SAS_DPRINTK("sas_discover_end_dev() for device %16llx " + "at %016llx:0x%x returned 0x%x\n", + SAS_ADDR(child->sas_addr), + SAS_ADDR(parent->sas_addr), phy_id, res); + /* FIXME: this kfrees list elements without removing them */ + //kfree(child); + return NULL; + } + } else { + SAS_DPRINTK("target proto 0x%x at %016llx:0x%x not handled\n", + phy->attached_tproto, SAS_ADDR(parent->sas_addr), + phy_id); + } + + list_add_tail(&child->siblings, &parent_ex->children); + return child; +} + +static struct domain_device *sas_ex_discover_expander( + struct domain_device *parent, int phy_id) +{ + struct sas_expander_device *parent_ex = rphy_to_expander_device(parent->rphy); + struct ex_phy *phy = &parent->ex_dev.ex_phy[phy_id]; + struct domain_device *child = NULL; + struct sas_rphy *rphy; + struct sas_expander_device *edev; + struct asd_sas_port *port; + int res; + + if (phy->routing_attr == DIRECT_ROUTING) { + SAS_DPRINTK("ex %016llx:0x%x:D <--> ex %016llx:0x%x is not " + "allowed\n", + SAS_ADDR(parent->sas_addr), phy_id, + SAS_ADDR(phy->attached_sas_addr), + phy->attached_phy_id); + return NULL; + } + child = kzalloc(sizeof(*child), GFP_KERNEL); + if (!child) + return NULL; + + phy->port = sas_port_alloc(&parent->rphy->dev, phy_id); + /* FIXME: better error handling */ + BUG_ON(sas_port_add(phy->port) != 0); + + + switch (phy->attached_dev_type) { + case EDGE_DEV: + rphy = sas_expander_alloc(phy->port, + SAS_EDGE_EXPANDER_DEVICE); + break; + case FANOUT_DEV: + rphy = sas_expander_alloc(phy->port, + SAS_FANOUT_EXPANDER_DEVICE); + break; + default: + rphy = NULL; /* shut gcc up */ + BUG(); + } + port = parent->port; + child->rphy = rphy; + edev = rphy_to_expander_device(rphy); + child->dev_type = phy->attached_dev_type; + child->parent = parent; + child->port = port; + child->iproto = phy->attached_iproto; + child->tproto = phy->attached_tproto; + memcpy(child->sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); + sas_hash_addr(child->hashed_sas_addr, child->sas_addr); + sas_ex_get_linkrate(parent, child, phy); + edev->level = parent_ex->level + 1; + parent->port->disc.max_level = max(parent->port->disc.max_level, + edev->level); + sas_init_dev(child); + sas_fill_in_rphy(child, rphy); + sas_rphy_add(rphy); + + spin_lock(&parent->port->dev_list_lock); + list_add_tail(&child->dev_list_node, &parent->port->dev_list); + spin_unlock(&parent->port->dev_list_lock); + + res = sas_discover_expander(child); + if (res) { + kfree(child); + return NULL; + } + list_add_tail(&child->siblings, &parent->ex_dev.children); + return child; +} + +static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) +{ + struct expander_device *ex = &dev->ex_dev; + struct ex_phy *ex_phy = &ex->ex_phy[phy_id]; + struct domain_device *child = NULL; + int res = 0; + + /* Phy state */ + if (ex_phy->linkrate == PHY_SPINUP_HOLD) { + if (!sas_smp_phy_control(dev, phy_id, PHY_FUNC_LINK_RESET)) + res = sas_ex_phy_discover(dev, phy_id); + if (res) + return res; + } + + /* Parent and domain coherency */ + if (!dev->parent && (SAS_ADDR(ex_phy->attached_sas_addr) == + SAS_ADDR(dev->port->sas_addr))) { + sas_add_parent_port(dev, phy_id); + return 0; + } + if (dev->parent && (SAS_ADDR(ex_phy->attached_sas_addr) == + SAS_ADDR(dev->parent->sas_addr))) { + sas_add_parent_port(dev, phy_id); + if (ex_phy->routing_attr == TABLE_ROUTING) + sas_configure_phy(dev, phy_id, dev->port->sas_addr, 1); + return 0; + } + + if (sas_dev_present_in_domain(dev->port, ex_phy->attached_sas_addr)) + sas_ex_disable_port(dev, ex_phy->attached_sas_addr); + + if (ex_phy->attached_dev_type == NO_DEVICE) { + if (ex_phy->routing_attr == DIRECT_ROUTING) { + memset(ex_phy->attached_sas_addr, 0, SAS_ADDR_SIZE); + sas_configure_routing(dev, ex_phy->attached_sas_addr); + } + return 0; + } else if (ex_phy->linkrate == PHY_LINKRATE_UNKNOWN) + return 0; + + if (ex_phy->attached_dev_type != SAS_END_DEV && + ex_phy->attached_dev_type != FANOUT_DEV && + ex_phy->attached_dev_type != EDGE_DEV) { + SAS_DPRINTK("unknown device type(0x%x) attached to ex %016llx " + "phy 0x%x\n", ex_phy->attached_dev_type, + SAS_ADDR(dev->sas_addr), + phy_id); + return 0; + } + + res = sas_configure_routing(dev, ex_phy->attached_sas_addr); + if (res) { + SAS_DPRINTK("configure routing for dev %016llx " + "reported 0x%x. Forgotten\n", + SAS_ADDR(ex_phy->attached_sas_addr), res); + sas_disable_routing(dev, ex_phy->attached_sas_addr); + return res; + } + + switch (ex_phy->attached_dev_type) { + case SAS_END_DEV: + child = sas_ex_discover_end_dev(dev, phy_id); + break; + case FANOUT_DEV: + if (SAS_ADDR(dev->port->disc.fanout_sas_addr)) { + SAS_DPRINTK("second fanout expander %016llx phy 0x%x " + "attached to ex %016llx phy 0x%x\n", + SAS_ADDR(ex_phy->attached_sas_addr), + ex_phy->attached_phy_id, + SAS_ADDR(dev->sas_addr), + phy_id); + sas_ex_disable_phy(dev, phy_id); + break; + } else + memcpy(dev->port->disc.fanout_sas_addr, + ex_phy->attached_sas_addr, SAS_ADDR_SIZE); + /* fallthrough */ + case EDGE_DEV: + child = sas_ex_discover_expander(dev, phy_id); + break; + default: + break; + } + + if (child) { + int i; + + for (i = 0; i < ex->num_phys; i++) { + if (ex->ex_phy[i].phy_state == PHY_VACANT || + ex->ex_phy[i].phy_state == PHY_NOT_PRESENT) + continue; + + if (SAS_ADDR(ex->ex_phy[i].attached_sas_addr) == + SAS_ADDR(child->sas_addr)) + ex->ex_phy[i].phy_state= PHY_DEVICE_DISCOVERED; + } + } + + return res; +} + +static int sas_find_sub_addr(struct domain_device *dev, u8 *sub_addr) +{ + struct expander_device *ex = &dev->ex_dev; + int i; + + for (i = 0; i < ex->num_phys; i++) { + struct ex_phy *phy = &ex->ex_phy[i]; + + if (phy->phy_state == PHY_VACANT || + phy->phy_state == PHY_NOT_PRESENT) + continue; + + if ((phy->attached_dev_type == EDGE_DEV || + phy->attached_dev_type == FANOUT_DEV) && + phy->routing_attr == SUBTRACTIVE_ROUTING) { + + memcpy(sub_addr, phy->attached_sas_addr,SAS_ADDR_SIZE); + + return 1; + } + } + return 0; +} + +static int sas_check_level_subtractive_boundary(struct domain_device *dev) +{ + struct expander_device *ex = &dev->ex_dev; + struct domain_device *child; + u8 sub_addr[8] = {0, }; + + list_for_each_entry(child, &ex->children, siblings) { + if (child->dev_type != EDGE_DEV && + child->dev_type != FANOUT_DEV) + continue; + if (sub_addr[0] == 0) { + sas_find_sub_addr(child, sub_addr); + continue; + } else { + u8 s2[8]; + + if (sas_find_sub_addr(child, s2) && + (SAS_ADDR(sub_addr) != SAS_ADDR(s2))) { + + SAS_DPRINTK("ex %016llx->%016llx-?->%016llx " + "diverges from subtractive " + "boundary %016llx\n", + SAS_ADDR(dev->sas_addr), + SAS_ADDR(child->sas_addr), + SAS_ADDR(s2), + SAS_ADDR(sub_addr)); + + sas_ex_disable_port(child, s2); + } + } + } + return 0; +} +/** + * sas_ex_discover_devices -- discover devices attached to this expander + * dev: pointer to the expander domain device + * single: if you want to do a single phy, else set to -1; + * + * Configure this expander for use with its devices and register the + * devices of this expander. + */ +static int sas_ex_discover_devices(struct domain_device *dev, int single) +{ + struct expander_device *ex = &dev->ex_dev; + int i = 0, end = ex->num_phys; + int res = 0; + + if (0 <= single && single < end) { + i = single; + end = i+1; + } + + for ( ; i < end; i++) { + struct ex_phy *ex_phy = &ex->ex_phy[i]; + + if (ex_phy->phy_state == PHY_VACANT || + ex_phy->phy_state == PHY_NOT_PRESENT || + ex_phy->phy_state == PHY_DEVICE_DISCOVERED) + continue; + + switch (ex_phy->linkrate) { + case PHY_DISABLED: + case PHY_RESET_PROBLEM: + case PHY_PORT_SELECTOR: + continue; + default: + res = sas_ex_discover_dev(dev, i); + if (res) + break; + continue; + } + } + + if (!res) + sas_check_level_subtractive_boundary(dev); + + return res; +} + +static int sas_check_ex_subtractive_boundary(struct domain_device *dev) +{ + struct expander_device *ex = &dev->ex_dev; + int i; + u8 *sub_sas_addr = NULL; + + if (dev->dev_type != EDGE_DEV) + return 0; + + for (i = 0; i < ex->num_phys; i++) { + struct ex_phy *phy = &ex->ex_phy[i]; + + if (phy->phy_state == PHY_VACANT || + phy->phy_state == PHY_NOT_PRESENT) + continue; + + if ((phy->attached_dev_type == FANOUT_DEV || + phy->attached_dev_type == EDGE_DEV) && + phy->routing_attr == SUBTRACTIVE_ROUTING) { + + if (!sub_sas_addr) + sub_sas_addr = &phy->attached_sas_addr[0]; + else if (SAS_ADDR(sub_sas_addr) != + SAS_ADDR(phy->attached_sas_addr)) { + + SAS_DPRINTK("ex %016llx phy 0x%x " + "diverges(%016llx) on subtractive " + "boundary(%016llx). Disabled\n", + SAS_ADDR(dev->sas_addr), i, + SAS_ADDR(phy->attached_sas_addr), + SAS_ADDR(sub_sas_addr)); + sas_ex_disable_phy(dev, i); + } + } + } + return 0; +} + +static void sas_print_parent_topology_bug(struct domain_device *child, + struct ex_phy *parent_phy, + struct ex_phy *child_phy) +{ + static const char ra_char[] = { + [DIRECT_ROUTING] = 'D', + [SUBTRACTIVE_ROUTING] = 'S', + [TABLE_ROUTING] = 'T', + }; + static const char *ex_type[] = { + [EDGE_DEV] = "edge", + [FANOUT_DEV] = "fanout", + }; + struct domain_device *parent = child->parent; + + sas_printk("%s ex %016llx phy 0x%x <--> %s ex %016llx phy 0x%x " + "has %c:%c routing link!\n", + + ex_type[parent->dev_type], + SAS_ADDR(parent->sas_addr), + parent_phy->phy_id, + + ex_type[child->dev_type], + SAS_ADDR(child->sas_addr), + child_phy->phy_id, + + ra_char[parent_phy->routing_attr], + ra_char[child_phy->routing_attr]); +} + +static int sas_check_eeds(struct domain_device *child, + struct ex_phy *parent_phy, + struct ex_phy *child_phy) +{ + int res = 0; + struct domain_device *parent = child->parent; + + if (SAS_ADDR(parent->port->disc.fanout_sas_addr) != 0) { + res = -ENODEV; + SAS_DPRINTK("edge ex %016llx phy S:0x%x <--> edge ex %016llx " + "phy S:0x%x, while there is a fanout ex %016llx\n", + SAS_ADDR(parent->sas_addr), + parent_phy->phy_id, + SAS_ADDR(child->sas_addr), + child_phy->phy_id, + SAS_ADDR(parent->port->disc.fanout_sas_addr)); + } else if (SAS_ADDR(parent->port->disc.eeds_a) == 0) { + memcpy(parent->port->disc.eeds_a, parent->sas_addr, + SAS_ADDR_SIZE); + memcpy(parent->port->disc.eeds_b, child->sas_addr, + SAS_ADDR_SIZE); + } else if (((SAS_ADDR(parent->port->disc.eeds_a) == + SAS_ADDR(parent->sas_addr)) || + (SAS_ADDR(parent->port->disc.eeds_a) == + SAS_ADDR(child->sas_addr))) + && + ((SAS_ADDR(parent->port->disc.eeds_b) == + SAS_ADDR(parent->sas_addr)) || + (SAS_ADDR(parent->port->disc.eeds_b) == + SAS_ADDR(child->sas_addr)))) + ; + else { + res = -ENODEV; + SAS_DPRINTK("edge ex %016llx phy 0x%x <--> edge ex %016llx " + "phy 0x%x link forms a third EEDS!\n", + SAS_ADDR(parent->sas_addr), + parent_phy->phy_id, + SAS_ADDR(child->sas_addr), + child_phy->phy_id); + } + + return res; +} + +/* Here we spill over 80 columns. It is intentional. + */ +static int sas_check_parent_topology(struct domain_device *child) +{ + struct expander_device *child_ex = &child->ex_dev; + struct expander_device *parent_ex; + int i; + int res = 0; + + if (!child->parent) + return 0; + + if (child->parent->dev_type != EDGE_DEV && + child->parent->dev_type != FANOUT_DEV) + return 0; + + parent_ex = &child->parent->ex_dev; + + for (i = 0; i < parent_ex->num_phys; i++) { + struct ex_phy *parent_phy = &parent_ex->ex_phy[i]; + struct ex_phy *child_phy; + + if (parent_phy->phy_state == PHY_VACANT || + parent_phy->phy_state == PHY_NOT_PRESENT) + continue; + + if (SAS_ADDR(parent_phy->attached_sas_addr) != SAS_ADDR(child->sas_addr)) + continue; + + child_phy = &child_ex->ex_phy[parent_phy->attached_phy_id]; + + switch (child->parent->dev_type) { + case EDGE_DEV: + if (child->dev_type == FANOUT_DEV) { + if (parent_phy->routing_attr != SUBTRACTIVE_ROUTING || + child_phy->routing_attr != TABLE_ROUTING) { + sas_print_parent_topology_bug(child, parent_phy, child_phy); + res = -ENODEV; + } + } else if (parent_phy->routing_attr == SUBTRACTIVE_ROUTING) { + if (child_phy->routing_attr == SUBTRACTIVE_ROUTING) { + res = sas_check_eeds(child, parent_phy, child_phy); + } else if (child_phy->routing_attr != TABLE_ROUTING) { + sas_print_parent_topology_bug(child, parent_phy, child_phy); + res = -ENODEV; + } + } else if (parent_phy->routing_attr == TABLE_ROUTING && + child_phy->routing_attr != SUBTRACTIVE_ROUTING) { + sas_print_parent_topology_bug(child, parent_phy, child_phy); + res = -ENODEV; + } + break; + case FANOUT_DEV: + if (parent_phy->routing_attr != TABLE_ROUTING || + child_phy->routing_attr != SUBTRACTIVE_ROUTING) { + sas_print_parent_topology_bug(child, parent_phy, child_phy); + res = -ENODEV; + } + break; + default: + break; + } + } + + return res; +} + +#define RRI_REQ_SIZE 16 +#define RRI_RESP_SIZE 44 + +static int sas_configure_present(struct domain_device *dev, int phy_id, + u8 *sas_addr, int *index, int *present) +{ + int i, res = 0; + struct expander_device *ex = &dev->ex_dev; + struct ex_phy *phy = &ex->ex_phy[phy_id]; + u8 *rri_req; + u8 *rri_resp; + + *present = 0; + *index = 0; + + rri_req = alloc_smp_req(RRI_REQ_SIZE); + if (!rri_req) + return -ENOMEM; + + rri_resp = alloc_smp_resp(RRI_RESP_SIZE); + if (!rri_resp) { + kfree(rri_req); + return -ENOMEM; + } + + rri_req[1] = SMP_REPORT_ROUTE_INFO; + rri_req[9] = phy_id; + + for (i = 0; i < ex->max_route_indexes ; i++) { + *(__be16 *)(rri_req+6) = cpu_to_be16(i); + res = smp_execute_task(dev, rri_req, RRI_REQ_SIZE, rri_resp, + RRI_RESP_SIZE); + if (res) + goto out; + res = rri_resp[2]; + if (res == SMP_RESP_NO_INDEX) { + SAS_DPRINTK("overflow of indexes: dev %016llx " + "phy 0x%x index 0x%x\n", + SAS_ADDR(dev->sas_addr), phy_id, i); + goto out; + } else if (res != SMP_RESP_FUNC_ACC) { + SAS_DPRINTK("%s: dev %016llx phy 0x%x index 0x%x " + "result 0x%x\n", __FUNCTION__, + SAS_ADDR(dev->sas_addr), phy_id, i, res); + goto out; + } + if (SAS_ADDR(sas_addr) != 0) { + if (SAS_ADDR(rri_resp+16) == SAS_ADDR(sas_addr)) { + *index = i; + if ((rri_resp[12] & 0x80) == 0x80) + *present = 0; + else + *present = 1; + goto out; + } else if (SAS_ADDR(rri_resp+16) == 0) { + *index = i; + *present = 0; + goto out; + } + } else if (SAS_ADDR(rri_resp+16) == 0 && + phy->last_da_index < i) { + phy->last_da_index = i; + *index = i; + *present = 0; + goto out; + } + } + res = -1; +out: + kfree(rri_req); + kfree(rri_resp); + return res; +} + +#define CRI_REQ_SIZE 44 +#define CRI_RESP_SIZE 8 + +static int sas_configure_set(struct domain_device *dev, int phy_id, + u8 *sas_addr, int index, int include) +{ + int res; + u8 *cri_req; + u8 *cri_resp; + + cri_req = alloc_smp_req(CRI_REQ_SIZE); + if (!cri_req) + return -ENOMEM; + + cri_resp = alloc_smp_resp(CRI_RESP_SIZE); + if (!cri_resp) { + kfree(cri_req); + return -ENOMEM; + } + + cri_req[1] = SMP_CONF_ROUTE_INFO; + *(__be16 *)(cri_req+6) = cpu_to_be16(index); + cri_req[9] = phy_id; + if (SAS_ADDR(sas_addr) == 0 || !include) + cri_req[12] |= 0x80; + memcpy(cri_req+16, sas_addr, SAS_ADDR_SIZE); + + res = smp_execute_task(dev, cri_req, CRI_REQ_SIZE, cri_resp, + CRI_RESP_SIZE); + if (res) + goto out; + res = cri_resp[2]; + if (res == SMP_RESP_NO_INDEX) { + SAS_DPRINTK("overflow of indexes: dev %016llx phy 0x%x " + "index 0x%x\n", + SAS_ADDR(dev->sas_addr), phy_id, index); + } +out: + kfree(cri_req); + kfree(cri_resp); + return res; +} + +static int sas_configure_phy(struct domain_device *dev, int phy_id, + u8 *sas_addr, int include) +{ + int index; + int present; + int res; + + res = sas_configure_present(dev, phy_id, sas_addr, &index, &present); + if (res) + return res; + if (include ^ present) + return sas_configure_set(dev, phy_id, sas_addr, index,include); + + return res; +} + +/** + * sas_configure_parent -- configure routing table of parent + * parent: parent expander + * child: child expander + * sas_addr: SAS port identifier of device directly attached to child + */ +static int sas_configure_parent(struct domain_device *parent, + struct domain_device *child, + u8 *sas_addr, int include) +{ + struct expander_device *ex_parent = &parent->ex_dev; + int res = 0; + int i; + + if (parent->parent) { + res = sas_configure_parent(parent->parent, parent, sas_addr, + include); + if (res) + return res; + } + + if (ex_parent->conf_route_table == 0) { + SAS_DPRINTK("ex %016llx has self-configuring routing table\n", + SAS_ADDR(parent->sas_addr)); + return 0; + } + + for (i = 0; i < ex_parent->num_phys; i++) { + struct ex_phy *phy = &ex_parent->ex_phy[i]; + + if ((phy->routing_attr == TABLE_ROUTING) && + (SAS_ADDR(phy->attached_sas_addr) == + SAS_ADDR(child->sas_addr))) { + res = sas_configure_phy(parent, i, sas_addr, include); + if (res) + return res; + } + } + + return res; +} + +/** + * sas_configure_routing -- configure routing + * dev: expander device + * sas_addr: port identifier of device directly attached to the expander device + */ +static int sas_configure_routing(struct domain_device *dev, u8 *sas_addr) +{ + if (dev->parent) + return sas_configure_parent(dev->parent, dev, sas_addr, 1); + return 0; +} + +static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr) +{ + if (dev->parent) + return sas_configure_parent(dev->parent, dev, sas_addr, 0); + return 0; +} + +#if 0 +#define SMP_BIN_ATTR_NAME "smp_portal" + +static void sas_ex_smp_hook(struct domain_device *dev) +{ + struct expander_device *ex_dev = &dev->ex_dev; + struct bin_attribute *bin_attr = &ex_dev->smp_bin_attr; + + memset(bin_attr, 0, sizeof(*bin_attr)); + + bin_attr->attr.name = SMP_BIN_ATTR_NAME; + bin_attr->attr.owner = THIS_MODULE; + bin_attr->attr.mode = 0600; + + bin_attr->size = 0; + bin_attr->private = NULL; + bin_attr->read = smp_portal_read; + bin_attr->write= smp_portal_write; + bin_attr->mmap = NULL; + + ex_dev->smp_portal_pid = -1; + init_MUTEX(&ex_dev->smp_sema); +} +#endif + +/** + * sas_discover_expander -- expander discovery + * @ex: pointer to expander domain device + * + * See comment in sas_discover_sata(). + */ +static int sas_discover_expander(struct domain_device *dev) +{ + int res; + + res = sas_notify_lldd_dev_found(dev); + if (res) + return res; + + res = sas_ex_general(dev); + if (res) + goto out_err; + res = sas_ex_manuf_info(dev); + if (res) + goto out_err; + + res = sas_expander_discover(dev); + if (res) { + SAS_DPRINTK("expander %016llx discovery failed(0x%x)\n", + SAS_ADDR(dev->sas_addr), res); + goto out_err; + } + + sas_check_ex_subtractive_boundary(dev); + res = sas_check_parent_topology(dev); + if (res) + goto out_err; + return 0; +out_err: + sas_notify_lldd_dev_gone(dev); + return res; +} + +static int sas_ex_level_discovery(struct asd_sas_port *port, const int level) +{ + int res = 0; + struct domain_device *dev; + + list_for_each_entry(dev, &port->dev_list, dev_list_node) { + if (dev->dev_type == EDGE_DEV || + dev->dev_type == FANOUT_DEV) { + struct sas_expander_device *ex = + rphy_to_expander_device(dev->rphy); + + if (level == ex->level) + res = sas_ex_discover_devices(dev, -1); + else if (level > 0) + res = sas_ex_discover_devices(port->port_dev, -1); + + } + } + + return res; +} + +static int sas_ex_bfs_disc(struct asd_sas_port *port) +{ + int res; + int level; + + do { + level = port->disc.max_level; + res = sas_ex_level_discovery(port, level); + mb(); + } while (level < port->disc.max_level); + + return res; +} + +int sas_discover_root_expander(struct domain_device *dev) +{ + int res; + struct sas_expander_device *ex = rphy_to_expander_device(dev->rphy); + + sas_rphy_add(dev->rphy); + + ex->level = dev->port->disc.max_level; /* 0 */ + res = sas_discover_expander(dev); + if (!res) + sas_ex_bfs_disc(dev->port); + + return res; +} + +/* ---------- Domain revalidation ---------- */ + +static int sas_get_phy_discover(struct domain_device *dev, + int phy_id, struct smp_resp *disc_resp) +{ + int res; + u8 *disc_req; + + disc_req = alloc_smp_req(DISCOVER_REQ_SIZE); + if (!disc_req) + return -ENOMEM; + + disc_req[1] = SMP_DISCOVER; + disc_req[9] = phy_id; + + res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, + disc_resp, DISCOVER_RESP_SIZE); + if (res) + goto out; + else if (disc_resp->result != SMP_RESP_FUNC_ACC) { + res = disc_resp->result; + goto out; + } +out: + kfree(disc_req); + return res; +} + +static int sas_get_phy_change_count(struct domain_device *dev, + int phy_id, int *pcc) +{ + int res; + struct smp_resp *disc_resp; + + disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE); + if (!disc_resp) + return -ENOMEM; + + res = sas_get_phy_discover(dev, phy_id, disc_resp); + if (!res) + *pcc = disc_resp->disc.change_count; + + kfree(disc_resp); + return res; +} + +static int sas_get_phy_attached_sas_addr(struct domain_device *dev, + int phy_id, u8 *attached_sas_addr) +{ + int res; + struct smp_resp *disc_resp; + struct discover_resp *dr; + + disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE); + if (!disc_resp) + return -ENOMEM; + dr = &disc_resp->disc; + + res = sas_get_phy_discover(dev, phy_id, disc_resp); + if (!res) { + memcpy(attached_sas_addr,disc_resp->disc.attached_sas_addr,8); + if (dr->attached_dev_type == 0) + memset(attached_sas_addr, 0, 8); + } + kfree(disc_resp); + return res; +} + +static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id, + int from_phy) +{ + struct expander_device *ex = &dev->ex_dev; + int res = 0; + int i; + + for (i = from_phy; i < ex->num_phys; i++) { + int phy_change_count = 0; + + res = sas_get_phy_change_count(dev, i, &phy_change_count); + if (res) + goto out; + else if (phy_change_count != ex->ex_phy[i].phy_change_count) { + ex->ex_phy[i].phy_change_count = phy_change_count; + *phy_id = i; + return 0; + } + } +out: + return res; +} + +static int sas_get_ex_change_count(struct domain_device *dev, int *ecc) +{ + int res; + u8 *rg_req; + struct smp_resp *rg_resp; + + rg_req = alloc_smp_req(RG_REQ_SIZE); + if (!rg_req) + return -ENOMEM; + + rg_resp = alloc_smp_resp(RG_RESP_SIZE); + if (!rg_resp) { + kfree(rg_req); + return -ENOMEM; + } + + rg_req[1] = SMP_REPORT_GENERAL; + + res = smp_execute_task(dev, rg_req, RG_REQ_SIZE, rg_resp, + RG_RESP_SIZE); + if (res) + goto out; + if (rg_resp->result != SMP_RESP_FUNC_ACC) { + res = rg_resp->result; + goto out; + } + + *ecc = be16_to_cpu(rg_resp->rg.change_count); +out: + kfree(rg_resp); + kfree(rg_req); + return res; +} + +static int sas_find_bcast_dev(struct domain_device *dev, + struct domain_device **src_dev) +{ + struct expander_device *ex = &dev->ex_dev; + int ex_change_count = -1; + int res; + + res = sas_get_ex_change_count(dev, &ex_change_count); + if (res) + goto out; + if (ex_change_count != -1 && + ex_change_count != ex->ex_change_count) { + *src_dev = dev; + ex->ex_change_count = ex_change_count; + } else { + struct domain_device *ch; + + list_for_each_entry(ch, &ex->children, siblings) { + if (ch->dev_type == EDGE_DEV || + ch->dev_type == FANOUT_DEV) { + res = sas_find_bcast_dev(ch, src_dev); + if (src_dev) + return res; + } + } + } +out: + return res; +} + +static void sas_unregister_ex_tree(struct domain_device *dev) +{ + struct expander_device *ex = &dev->ex_dev; + struct domain_device *child, *n; + + list_for_each_entry_safe(child, n, &ex->children, siblings) { + if (child->dev_type == EDGE_DEV || + child->dev_type == FANOUT_DEV) + sas_unregister_ex_tree(child); + else + sas_unregister_dev(child); + } + sas_unregister_dev(dev); +} + +static void sas_unregister_devs_sas_addr(struct domain_device *parent, + int phy_id) +{ + struct expander_device *ex_dev = &parent->ex_dev; + struct ex_phy *phy = &ex_dev->ex_phy[phy_id]; + struct domain_device *child, *n; + + list_for_each_entry_safe(child, n, &ex_dev->children, siblings) { + if (SAS_ADDR(child->sas_addr) == + SAS_ADDR(phy->attached_sas_addr)) { + if (child->dev_type == EDGE_DEV || + child->dev_type == FANOUT_DEV) + sas_unregister_ex_tree(child); + else + sas_unregister_dev(child); + break; + } + } + sas_disable_routing(parent, phy->attached_sas_addr); + memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); + sas_port_delete_phy(phy->port, phy->phy); + if (phy->port->num_phys == 0) + sas_port_delete(phy->port); + phy->port = NULL; +} + +static int sas_discover_bfs_by_root_level(struct domain_device *root, + const int level) +{ + struct expander_device *ex_root = &root->ex_dev; + struct domain_device *child; + int res = 0; + + list_for_each_entry(child, &ex_root->children, siblings) { + if (child->dev_type == EDGE_DEV || + child->dev_type == FANOUT_DEV) { + struct sas_expander_device *ex = + rphy_to_expander_device(child->rphy); + + if (level > ex->level) + res = sas_discover_bfs_by_root_level(child, + level); + else if (level == ex->level) + res = sas_ex_discover_devices(child, -1); + } + } + return res; +} + +static int sas_discover_bfs_by_root(struct domain_device *dev) +{ + int res; + struct sas_expander_device *ex = rphy_to_expander_device(dev->rphy); + int level = ex->level+1; + + res = sas_ex_discover_devices(dev, -1); + if (res) + goto out; + do { + res = sas_discover_bfs_by_root_level(dev, level); + mb(); + level += 1; + } while (level <= dev->port->disc.max_level); +out: + return res; +} + +static int sas_discover_new(struct domain_device *dev, int phy_id) +{ + struct ex_phy *ex_phy = &dev->ex_dev.ex_phy[phy_id]; + struct domain_device *child; + int res; + + SAS_DPRINTK("ex %016llx phy%d new device attached\n", + SAS_ADDR(dev->sas_addr), phy_id); + res = sas_ex_phy_discover(dev, phy_id); + if (res) + goto out; + res = sas_ex_discover_devices(dev, phy_id); + if (res) + goto out; + list_for_each_entry(child, &dev->ex_dev.children, siblings) { + if (SAS_ADDR(child->sas_addr) == + SAS_ADDR(ex_phy->attached_sas_addr)) { + if (child->dev_type == EDGE_DEV || + child->dev_type == FANOUT_DEV) + res = sas_discover_bfs_by_root(child); + break; + } + } +out: + return res; +} + +static int sas_rediscover_dev(struct domain_device *dev, int phy_id) +{ + struct expander_device *ex = &dev->ex_dev; + struct ex_phy *phy = &ex->ex_phy[phy_id]; + u8 attached_sas_addr[8]; + int res; + + res = sas_get_phy_attached_sas_addr(dev, phy_id, attached_sas_addr); + switch (res) { + case SMP_RESP_NO_PHY: + phy->phy_state = PHY_NOT_PRESENT; + sas_unregister_devs_sas_addr(dev, phy_id); + goto out; break; + case SMP_RESP_PHY_VACANT: + phy->phy_state = PHY_VACANT; + sas_unregister_devs_sas_addr(dev, phy_id); + goto out; break; + case SMP_RESP_FUNC_ACC: + break; + } + + if (SAS_ADDR(attached_sas_addr) == 0) { + phy->phy_state = PHY_EMPTY; + sas_unregister_devs_sas_addr(dev, phy_id); + } else if (SAS_ADDR(attached_sas_addr) == + SAS_ADDR(phy->attached_sas_addr)) { + SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter\n", + SAS_ADDR(dev->sas_addr), phy_id); + } else + res = sas_discover_new(dev, phy_id); +out: + return res; +} + +static int sas_rediscover(struct domain_device *dev, const int phy_id) +{ + struct expander_device *ex = &dev->ex_dev; + struct ex_phy *changed_phy = &ex->ex_phy[phy_id]; + int res = 0; + int i; + + SAS_DPRINTK("ex %016llx phy%d originated BROADCAST(CHANGE)\n", + SAS_ADDR(dev->sas_addr), phy_id); + + if (SAS_ADDR(changed_phy->attached_sas_addr) != 0) { + for (i = 0; i < ex->num_phys; i++) { + struct ex_phy *phy = &ex->ex_phy[i]; + + if (i == phy_id) + continue; + if (SAS_ADDR(phy->attached_sas_addr) == + SAS_ADDR(changed_phy->attached_sas_addr)) { + SAS_DPRINTK("phy%d part of wide port with " + "phy%d\n", phy_id, i); + goto out; + } + } + res = sas_rediscover_dev(dev, phy_id); + } else + res = sas_discover_new(dev, phy_id); +out: + return res; +} + +/** + * sas_revalidate_domain -- revalidate the domain + * @port: port to the domain of interest + * + * NOTE: this process _must_ quit (return) as soon as any connection + * errors are encountered. Connection recovery is done elsewhere. + * Discover process only interrogates devices in order to discover the + * domain. + */ +int sas_ex_revalidate_domain(struct domain_device *port_dev) +{ + int res; + struct domain_device *dev = NULL; + + res = sas_find_bcast_dev(port_dev, &dev); + if (res) + goto out; + if (dev) { + struct expander_device *ex = &dev->ex_dev; + int i = 0, phy_id; + + do { + phy_id = -1; + res = sas_find_bcast_phy(dev, &phy_id, i); + if (phy_id == -1) + break; + res = sas_rediscover(dev, phy_id); + i = phy_id + 1; + } while (i < ex->num_phys); + } +out: + return res; +} + +#if 0 +/* ---------- SMP portal ---------- */ + +static ssize_t smp_portal_write(struct kobject *kobj, char *buf, loff_t offs, + size_t size) +{ + struct domain_device *dev = to_dom_device(kobj); + struct expander_device *ex = &dev->ex_dev; + + if (offs != 0) + return -EFBIG; + else if (size == 0) + return 0; + + down_interruptible(&ex->smp_sema); + if (ex->smp_req) + kfree(ex->smp_req); + ex->smp_req = kzalloc(size, GFP_USER); + if (!ex->smp_req) { + up(&ex->smp_sema); + return -ENOMEM; + } + memcpy(ex->smp_req, buf, size); + ex->smp_req_size = size; + ex->smp_portal_pid = current->pid; + up(&ex->smp_sema); + + return size; +} + +static ssize_t smp_portal_read(struct kobject *kobj, char *buf, loff_t offs, + size_t size) +{ + struct domain_device *dev = to_dom_device(kobj); + struct expander_device *ex = &dev->ex_dev; + u8 *smp_resp; + int res = -EINVAL; + + /* XXX: sysfs gives us an offset of 0x10 or 0x8 while in fact + * it should be 0. + */ + + down_interruptible(&ex->smp_sema); + if (!ex->smp_req || ex->smp_portal_pid != current->pid) + goto out; + + res = 0; + if (size == 0) + goto out; + + res = -ENOMEM; + smp_resp = alloc_smp_resp(size); + if (!smp_resp) + goto out; + res = smp_execute_task(dev, ex->smp_req, ex->smp_req_size, + smp_resp, size); + if (!res) { + memcpy(buf, smp_resp, size); + res = size; + } + + kfree(smp_resp); +out: + kfree(ex->smp_req); + ex->smp_req = NULL; + ex->smp_req_size = 0; + ex->smp_portal_pid = -1; + up(&ex->smp_sema); + return res; +} +#endif diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c new file mode 100644 index 00000000000..b961664b810 --- /dev/null +++ b/drivers/scsi/libsas/sas_init.c @@ -0,0 +1,227 @@ +/* + * Serial Attached SCSI (SAS) Transport Layer initialization + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * 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 of the + * License, 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; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sas_internal.h" + +#include "../scsi_sas_internal.h" + +kmem_cache_t *sas_task_cache; + +/*------------ SAS addr hash -----------*/ +void sas_hash_addr(u8 *hashed, const u8 *sas_addr) +{ + const u32 poly = 0x00DB2777; + u32 r = 0; + int i; + + for (i = 0; i < 8; i++) { + int b; + for (b = 7; b >= 0; b--) { + r <<= 1; + if ((1 << b) & sas_addr[i]) { + if (!(r & 0x01000000)) + r ^= poly; + } else if (r & 0x01000000) + r ^= poly; + } + } + + hashed[0] = (r >> 16) & 0xFF; + hashed[1] = (r >> 8) & 0xFF ; + hashed[2] = r & 0xFF; +} + + +/* ---------- HA events ---------- */ + +void sas_hae_reset(void *data) +{ + struct sas_ha_struct *ha = data; + + sas_begin_event(HAE_RESET, &ha->event_lock, + &ha->pending); +} + +int sas_register_ha(struct sas_ha_struct *sas_ha) +{ + int error = 0; + + spin_lock_init(&sas_ha->phy_port_lock); + sas_hash_addr(sas_ha->hashed_sas_addr, sas_ha->sas_addr); + + if (sas_ha->lldd_queue_size == 0) + sas_ha->lldd_queue_size = 1; + else if (sas_ha->lldd_queue_size == -1) + sas_ha->lldd_queue_size = 128; /* Sanity */ + + error = sas_register_phys(sas_ha); + if (error) { + printk(KERN_NOTICE "couldn't register sas phys:%d\n", error); + return error; + } + + error = sas_register_ports(sas_ha); + if (error) { + printk(KERN_NOTICE "couldn't register sas ports:%d\n", error); + goto Undo_phys; + } + + error = sas_init_events(sas_ha); + if (error) { + printk(KERN_NOTICE "couldn't start event thread:%d\n", error); + goto Undo_ports; + } + + if (sas_ha->lldd_max_execute_num > 1) { + error = sas_init_queue(sas_ha); + if (error) { + printk(KERN_NOTICE "couldn't start queue thread:%d, " + "running in direct mode\n", error); + sas_ha->lldd_max_execute_num = 1; + } + } + + return 0; + +Undo_ports: + sas_unregister_ports(sas_ha); +Undo_phys: + + return error; +} + +int sas_unregister_ha(struct sas_ha_struct *sas_ha) +{ + if (sas_ha->lldd_max_execute_num > 1) { + sas_shutdown_queue(sas_ha); + } + + sas_unregister_ports(sas_ha); + + return 0; +} + +static int sas_get_linkerrors(struct sas_phy *phy) +{ + if (scsi_is_sas_phy_local(phy)) + /* FIXME: we have no local phy stats + * gathering at this time */ + return -EINVAL; + + return sas_smp_get_phy_events(phy); +} + +static int sas_phy_reset(struct sas_phy *phy, int hard_reset) +{ + int ret; + enum phy_func reset_type; + + if (hard_reset) + reset_type = PHY_FUNC_HARD_RESET; + else + reset_type = PHY_FUNC_LINK_RESET; + + if (scsi_is_sas_phy_local(phy)) { + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); + struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); + struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; + struct sas_internal *i = + to_sas_internal(sas_ha->core.shost->transportt); + + ret = i->dft->lldd_control_phy(asd_phy, reset_type); + } else { + struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); + struct domain_device *ddev = sas_find_dev_by_rphy(rphy); + ret = sas_smp_phy_control(ddev, phy->number, reset_type); + } + return ret; +} + +static struct sas_function_template sft = { + .phy_reset = sas_phy_reset, + .get_linkerrors = sas_get_linkerrors, +}; + +struct scsi_transport_template * +sas_domain_attach_transport(struct sas_domain_function_template *dft) +{ + struct scsi_transport_template *stt = sas_attach_transport(&sft); + struct sas_internal *i; + + if (!stt) + return stt; + + i = to_sas_internal(stt); + i->dft = dft; + stt->create_work_queue = 1; + stt->eh_timed_out = sas_scsi_timed_out; + stt->eh_strategy_handler = sas_scsi_recover_host; + + return stt; +} +EXPORT_SYMBOL_GPL(sas_domain_attach_transport); + + +void sas_domain_release_transport(struct scsi_transport_template *stt) +{ + sas_release_transport(stt); +} +EXPORT_SYMBOL_GPL(sas_domain_release_transport); + +/* ---------- SAS Class register/unregister ---------- */ + +static int __init sas_class_init(void) +{ + sas_task_cache = kmem_cache_create("sas_task", sizeof(struct sas_task), + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + if (!sas_task_cache) + return -ENOMEM; + + return 0; +} + +static void __exit sas_class_exit(void) +{ + kmem_cache_destroy(sas_task_cache); +} + +MODULE_AUTHOR("Luben Tuikov "); +MODULE_DESCRIPTION("SAS Transport Layer"); +MODULE_LICENSE("GPL v2"); + +module_init(sas_class_init); +module_exit(sas_class_exit); + +EXPORT_SYMBOL_GPL(sas_register_ha); +EXPORT_SYMBOL_GPL(sas_unregister_ha); diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h new file mode 100644 index 00000000000..89c39768084 --- /dev/null +++ b/drivers/scsi/libsas/sas_internal.h @@ -0,0 +1,146 @@ +/* + * Serial Attached SCSI (SAS) class internal header file + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * 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 of the + * License, 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; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + */ + +#ifndef _SAS_INTERNAL_H_ +#define _SAS_INTERNAL_H_ + +#include +#include +#include +#include + +#define sas_printk(fmt, ...) printk(KERN_NOTICE "sas: " fmt, ## __VA_ARGS__) + +#ifdef SAS_DEBUG +#define SAS_DPRINTK(fmt, ...) printk(KERN_NOTICE "sas: " fmt, ## __VA_ARGS__) +#else +#define SAS_DPRINTK(fmt, ...) +#endif + +void sas_scsi_recover_host(struct Scsi_Host *shost); + +int sas_show_class(enum sas_class class, char *buf); +int sas_show_proto(enum sas_proto proto, char *buf); +int sas_show_linkrate(enum sas_phy_linkrate linkrate, char *buf); +int sas_show_oob_mode(enum sas_oob_mode oob_mode, char *buf); + +int sas_register_phys(struct sas_ha_struct *sas_ha); +void sas_unregister_phys(struct sas_ha_struct *sas_ha); + +int sas_register_ports(struct sas_ha_struct *sas_ha); +void sas_unregister_ports(struct sas_ha_struct *sas_ha); + +enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *); + +int sas_init_queue(struct sas_ha_struct *sas_ha); +int sas_init_events(struct sas_ha_struct *sas_ha); +void sas_shutdown_queue(struct sas_ha_struct *sas_ha); + +void sas_deform_port(struct asd_sas_phy *phy); + +void sas_porte_bytes_dmaed(void *); +void sas_porte_broadcast_rcvd(void *); +void sas_porte_link_reset_err(void *); +void sas_porte_timer_event(void *); +void sas_porte_hard_reset(void *); + +int sas_notify_lldd_dev_found(struct domain_device *); +void sas_notify_lldd_dev_gone(struct domain_device *); + +int sas_smp_phy_control(struct domain_device *dev, int phy_id, + enum phy_func phy_func); +int sas_smp_get_phy_events(struct sas_phy *phy); + +struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); + +void sas_hae_reset(void *); + +static inline void sas_queue_event(int event, spinlock_t *lock, + unsigned long *pending, + struct work_struct *work, + struct Scsi_Host *shost) +{ + unsigned long flags; + + spin_lock_irqsave(lock, flags); + if (test_bit(event, pending)) { + spin_unlock_irqrestore(lock, flags); + return; + } + __set_bit(event, pending); + spin_unlock_irqrestore(lock, flags); + scsi_queue_work(shost, work); +} + +static inline void sas_begin_event(int event, spinlock_t *lock, + unsigned long *pending) +{ + unsigned long flags; + + spin_lock_irqsave(lock, flags); + __clear_bit(event, pending); + spin_unlock_irqrestore(lock, flags); +} + +static inline void sas_fill_in_rphy(struct domain_device *dev, + struct sas_rphy *rphy) +{ + rphy->identify.sas_address = SAS_ADDR(dev->sas_addr); + rphy->identify.initiator_port_protocols = dev->iproto; + rphy->identify.target_port_protocols = dev->tproto; + switch (dev->dev_type) { + case SATA_DEV: + /* FIXME: need sata device type */ + case SAS_END_DEV: + rphy->identify.device_type = SAS_END_DEVICE; + break; + case EDGE_DEV: + rphy->identify.device_type = SAS_EDGE_EXPANDER_DEVICE; + break; + case FANOUT_DEV: + rphy->identify.device_type = SAS_FANOUT_EXPANDER_DEVICE; + break; + default: + rphy->identify.device_type = SAS_PHY_UNUSED; + break; + } +} + +static inline void sas_add_parent_port(struct domain_device *dev, int phy_id) +{ + struct expander_device *ex = &dev->ex_dev; + struct ex_phy *ex_phy = &ex->ex_phy[phy_id]; + + if (!ex->parent_port) { + ex->parent_port = sas_port_alloc(&dev->rphy->dev, phy_id); + /* FIXME: error handling */ + BUG_ON(!ex->parent_port); + BUG_ON(sas_port_add(ex->parent_port)); + sas_port_mark_backlink(ex->parent_port); + } + sas_port_add_phy(ex->parent_port, ex_phy->phy); +} + +#endif /* _SAS_INTERNAL_H_ */ diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c new file mode 100644 index 00000000000..024ab00e70d --- /dev/null +++ b/drivers/scsi/libsas/sas_phy.c @@ -0,0 +1,157 @@ +/* + * Serial Attached SCSI (SAS) Phy class + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * 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 of the + * License, 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; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "sas_internal.h" +#include +#include +#include +#include "../scsi_sas_internal.h" + +/* ---------- Phy events ---------- */ + +static void sas_phye_loss_of_signal(void *data) +{ + struct asd_sas_phy *phy = data; + + sas_begin_event(PHYE_LOSS_OF_SIGNAL, &phy->ha->event_lock, + &phy->phy_events_pending); + phy->error = 0; + sas_deform_port(phy); +} + +static void sas_phye_oob_done(void *data) +{ + struct asd_sas_phy *phy = data; + + sas_begin_event(PHYE_OOB_DONE, &phy->ha->event_lock, + &phy->phy_events_pending); + phy->error = 0; +} + +static void sas_phye_oob_error(void *data) +{ + struct asd_sas_phy *phy = data; + struct sas_ha_struct *sas_ha = phy->ha; + struct asd_sas_port *port = phy->port; + struct sas_internal *i = + to_sas_internal(sas_ha->core.shost->transportt); + + sas_begin_event(PHYE_OOB_ERROR, &phy->ha->event_lock, + &phy->phy_events_pending); + + sas_deform_port(phy); + + if (!port && phy->enabled && i->dft->lldd_control_phy) { + phy->error++; + switch (phy->error) { + case 1: + case 2: + i->dft->lldd_control_phy(phy, PHY_FUNC_HARD_RESET); + break; + case 3: + default: + phy->error = 0; + phy->enabled = 0; + i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE); + break; + } + } +} + +static void sas_phye_spinup_hold(void *data) +{ + struct asd_sas_phy *phy = data; + struct sas_ha_struct *sas_ha = phy->ha; + struct sas_internal *i = + to_sas_internal(sas_ha->core.shost->transportt); + + sas_begin_event(PHYE_SPINUP_HOLD, &phy->ha->event_lock, + &phy->phy_events_pending); + + phy->error = 0; + i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD); +} + +/* ---------- Phy class registration ---------- */ + +int sas_register_phys(struct sas_ha_struct *sas_ha) +{ + int i; + + static void (*sas_phy_event_fns[PHY_NUM_EVENTS])(void *) = { + [PHYE_LOSS_OF_SIGNAL] = sas_phye_loss_of_signal, + [PHYE_OOB_DONE] = sas_phye_oob_done, + [PHYE_OOB_ERROR] = sas_phye_oob_error, + [PHYE_SPINUP_HOLD] = sas_phye_spinup_hold, + }; + + static void (*sas_port_event_fns[PORT_NUM_EVENTS])(void *) = { + [PORTE_BYTES_DMAED] = sas_porte_bytes_dmaed, + [PORTE_BROADCAST_RCVD] = sas_porte_broadcast_rcvd, + [PORTE_LINK_RESET_ERR] = sas_porte_link_reset_err, + [PORTE_TIMER_EVENT] = sas_porte_timer_event, + [PORTE_HARD_RESET] = sas_porte_hard_reset, + }; + + /* Now register the phys. */ + for (i = 0; i < sas_ha->num_phys; i++) { + int k; + struct asd_sas_phy *phy = sas_ha->sas_phy[i]; + + phy->error = 0; + INIT_LIST_HEAD(&phy->port_phy_el); + for (k = 0; k < PORT_NUM_EVENTS; k++) + INIT_WORK(&phy->port_events[k], sas_port_event_fns[k], + phy); + + for (k = 0; k < PHY_NUM_EVENTS; k++) + INIT_WORK(&phy->phy_events[k], sas_phy_event_fns[k], + phy); + phy->port = NULL; + phy->ha = sas_ha; + spin_lock_init(&phy->frame_rcvd_lock); + spin_lock_init(&phy->sas_prim_lock); + phy->frame_rcvd_size = 0; + + phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, + i); + if (!phy->phy) + return -ENOMEM; + + phy->phy->identify.initiator_port_protocols = + phy->iproto; + phy->phy->identify.target_port_protocols = phy->tproto; + phy->phy->identify.sas_address = SAS_ADDR(sas_ha->sas_addr); + phy->phy->identify.phy_identifier = i; + phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; + phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS; + phy->phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS; + phy->phy->maximum_linkrate = SAS_LINK_RATE_3_0_GBPS; + phy->phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; + + sas_phy_add(phy->phy); + } + + return 0; +} diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c new file mode 100644 index 00000000000..253cdcf306a --- /dev/null +++ b/drivers/scsi/libsas/sas_port.c @@ -0,0 +1,279 @@ +/* + * Serial Attached SCSI (SAS) Port class + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * 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 of the + * License, 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; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "sas_internal.h" + +#include +#include +#include "../scsi_sas_internal.h" + +/** + * sas_form_port -- add this phy to a port + * @phy: the phy of interest + * + * This function adds this phy to an existing port, thus creating a wide + * port, or it creates a port and adds the phy to the port. + */ +static void sas_form_port(struct asd_sas_phy *phy) +{ + int i; + struct sas_ha_struct *sas_ha = phy->ha; + struct asd_sas_port *port = phy->port; + struct sas_internal *si = + to_sas_internal(sas_ha->core.shost->transportt); + + if (port) { + if (memcmp(port->attached_sas_addr, phy->attached_sas_addr, + SAS_ADDR_SIZE) == 0) + sas_deform_port(phy); + else { + SAS_DPRINTK("%s: phy%d belongs to port%d already(%d)!\n", + __FUNCTION__, phy->id, phy->port->id, + phy->port->num_phys); + return; + } + } + + /* find a port */ + spin_lock(&sas_ha->phy_port_lock); + for (i = 0; i < sas_ha->num_phys; i++) { + port = sas_ha->sas_port[i]; + spin_lock(&port->phy_list_lock); + if (*(u64 *) port->sas_addr && + memcmp(port->attached_sas_addr, + phy->attached_sas_addr, SAS_ADDR_SIZE) == 0 && + port->num_phys > 0) { + /* wide port */ + SAS_DPRINTK("phy%d matched wide port%d\n", phy->id, + port->id); + break; + } else if (*(u64 *) port->sas_addr == 0 && port->num_phys==0) { + memcpy(port->sas_addr, phy->sas_addr, SAS_ADDR_SIZE); + break; + } + spin_unlock(&port->phy_list_lock); + } + + if (i >= sas_ha->num_phys) { + printk(KERN_NOTICE "%s: couldn't find a free port, bug?\n", + __FUNCTION__); + spin_unlock(&sas_ha->phy_port_lock); + return; + } + + /* add the phy to the port */ + list_add_tail(&phy->port_phy_el, &port->phy_list); + phy->port = port; + port->num_phys++; + port->phy_mask |= (1U << phy->id); + + if (!port->phy) + port->phy = phy->phy; + + SAS_DPRINTK("phy%d added to port%d, phy_mask:0x%x\n", phy->id, + port->id, port->phy_mask); + + if (*(u64 *)port->attached_sas_addr == 0) { + port->class = phy->class; + memcpy(port->attached_sas_addr, phy->attached_sas_addr, + SAS_ADDR_SIZE); + port->iproto = phy->iproto; + port->tproto = phy->tproto; + port->oob_mode = phy->oob_mode; + port->linkrate = phy->linkrate; + } else + port->linkrate = max(port->linkrate, phy->linkrate); + spin_unlock(&port->phy_list_lock); + spin_unlock(&sas_ha->phy_port_lock); + + if (!port->port) { + port->port = sas_port_alloc(phy->phy->dev.parent, port->id); + BUG_ON(!port->port); + sas_port_add(port->port); + } + sas_port_add_phy(port->port, phy->phy); + + if (port->port_dev) + port->port_dev->pathways = port->num_phys; + + /* Tell the LLDD about this port formation. */ + if (si->dft->lldd_port_formed) + si->dft->lldd_port_formed(phy); + + sas_discover_event(phy->port, DISCE_DISCOVER_DOMAIN); +} + +/** + * sas_deform_port -- remove this phy from the port it belongs to + * @phy: the phy of interest + * + * This is called when the physical link to the other phy has been + * lost (on this phy), in Event thread context. We cannot delay here. + */ +void sas_deform_port(struct asd_sas_phy *phy) +{ + struct sas_ha_struct *sas_ha = phy->ha; + struct asd_sas_port *port = phy->port; + struct sas_internal *si = + to_sas_internal(sas_ha->core.shost->transportt); + + if (!port) + return; /* done by a phy event */ + + if (port->port_dev) + port->port_dev->pathways--; + + if (port->num_phys == 1) { + sas_unregister_domain_devices(port); + sas_port_delete(port->port); + port->port = NULL; + } else + sas_port_delete_phy(port->port, phy->phy); + + + if (si->dft->lldd_port_deformed) + si->dft->lldd_port_deformed(phy); + + spin_lock(&sas_ha->phy_port_lock); + spin_lock(&port->phy_list_lock); + + list_del_init(&phy->port_phy_el); + phy->port = NULL; + port->num_phys--; + port->phy_mask &= ~(1U << phy->id); + + if (port->num_phys == 0) { + INIT_LIST_HEAD(&port->phy_list); + memset(port->sas_addr, 0, SAS_ADDR_SIZE); + memset(port->attached_sas_addr, 0, SAS_ADDR_SIZE); + port->class = 0; + port->iproto = 0; + port->tproto = 0; + port->oob_mode = 0; + port->phy_mask = 0; + } + spin_unlock(&port->phy_list_lock); + spin_unlock(&sas_ha->phy_port_lock); + + return; +} + +/* ---------- SAS port events ---------- */ + +void sas_porte_bytes_dmaed(void *data) +{ + struct asd_sas_phy *phy = data; + + sas_begin_event(PORTE_BYTES_DMAED, &phy->ha->event_lock, + &phy->port_events_pending); + + sas_form_port(phy); +} + +void sas_porte_broadcast_rcvd(void *data) +{ + unsigned long flags; + u32 prim; + struct asd_sas_phy *phy = data; + + sas_begin_event(PORTE_BROADCAST_RCVD, &phy->ha->event_lock, + &phy->port_events_pending); + + spin_lock_irqsave(&phy->sas_prim_lock, flags); + prim = phy->sas_prim; + spin_unlock_irqrestore(&phy->sas_prim_lock, flags); + + SAS_DPRINTK("broadcast received: %d\n", prim); + sas_discover_event(phy->port, DISCE_REVALIDATE_DOMAIN); +} + +void sas_porte_link_reset_err(void *data) +{ + struct asd_sas_phy *phy = data; + + sas_begin_event(PORTE_LINK_RESET_ERR, &phy->ha->event_lock, + &phy->port_events_pending); + + sas_deform_port(phy); +} + +void sas_porte_timer_event(void *data) +{ + struct asd_sas_phy *phy = data; + + sas_begin_event(PORTE_TIMER_EVENT, &phy->ha->event_lock, + &phy->port_events_pending); + + sas_deform_port(phy); +} + +void sas_porte_hard_reset(void *data) +{ + struct asd_sas_phy *phy = data; + + sas_begin_event(PORTE_HARD_RESET, &phy->ha->event_lock, + &phy->port_events_pending); + + sas_deform_port(phy); +} + +/* ---------- SAS port registration ---------- */ + +static void sas_init_port(struct asd_sas_port *port, + struct sas_ha_struct *sas_ha, int i) +{ + port->id = i; + INIT_LIST_HEAD(&port->dev_list); + spin_lock_init(&port->phy_list_lock); + INIT_LIST_HEAD(&port->phy_list); + port->num_phys = 0; + port->phy_mask = 0; + port->ha = sas_ha; + + spin_lock_init(&port->dev_list_lock); +} + +int sas_register_ports(struct sas_ha_struct *sas_ha) +{ + int i; + + /* initialize the ports and discovery */ + for (i = 0; i < sas_ha->num_phys; i++) { + struct asd_sas_port *port = sas_ha->sas_port[i]; + + sas_init_port(port, sas_ha, i); + sas_init_disc(&port->disc, port); + } + return 0; +} + +void sas_unregister_ports(struct sas_ha_struct *sas_ha) +{ + int i; + + for (i = 0; i < sas_ha->num_phys; i++) + if (sas_ha->sas_phy[i]->port) + sas_deform_port(sas_ha->sas_phy[i]); + +} diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c new file mode 100644 index 00000000000..43e0e4e3693 --- /dev/null +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -0,0 +1,786 @@ +/* + * Serial Attached SCSI (SAS) class SCSI Host glue. + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * 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 of the + * License, 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; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + */ + +#include "sas_internal.h" + +#include +#include +#include +#include +#include +#include +#include "../scsi_sas_internal.h" + +#include +#include +#include + +/* ---------- SCSI Host glue ---------- */ + +#define TO_SAS_TASK(_scsi_cmd) ((void *)(_scsi_cmd)->host_scribble) +#define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0) + +static void sas_scsi_task_done(struct sas_task *task) +{ + struct task_status_struct *ts = &task->task_status; + struct scsi_cmnd *sc = task->uldd_task; + unsigned ts_flags = task->task_state_flags; + int hs = 0, stat = 0; + + if (unlikely(!sc)) { + SAS_DPRINTK("task_done called with non existing SCSI cmnd!\n"); + list_del_init(&task->list); + sas_free_task(task); + return; + } + + if (ts->resp == SAS_TASK_UNDELIVERED) { + /* transport error */ + hs = DID_NO_CONNECT; + } else { /* ts->resp == SAS_TASK_COMPLETE */ + /* task delivered, what happened afterwards? */ + switch (ts->stat) { + case SAS_DEV_NO_RESPONSE: + case SAS_INTERRUPTED: + case SAS_PHY_DOWN: + case SAS_NAK_R_ERR: + case SAS_OPEN_TO: + hs = DID_NO_CONNECT; + break; + case SAS_DATA_UNDERRUN: + sc->resid = ts->residual; + if (sc->request_bufflen - sc->resid < sc->underflow) + hs = DID_ERROR; + break; + case SAS_DATA_OVERRUN: + hs = DID_ERROR; + break; + case SAS_QUEUE_FULL: + hs = DID_SOFT_ERROR; /* retry */ + break; + case SAS_DEVICE_UNKNOWN: + hs = DID_BAD_TARGET; + break; + case SAS_SG_ERR: + hs = DID_PARITY; + break; + case SAS_OPEN_REJECT: + if (ts->open_rej_reason == SAS_OREJ_RSVD_RETRY) + hs = DID_SOFT_ERROR; /* retry */ + else + hs = DID_ERROR; + break; + case SAS_PROTO_RESPONSE: + SAS_DPRINTK("LLDD:%s sent SAS_PROTO_RESP for an SSP " + "task; please report this\n", + task->dev->port->ha->sas_ha_name); + break; + case SAS_ABORTED_TASK: + hs = DID_ABORT; + break; + case SAM_CHECK_COND: + memcpy(sc->sense_buffer, ts->buf, + max(SCSI_SENSE_BUFFERSIZE, ts->buf_valid_size)); + stat = SAM_CHECK_COND; + break; + default: + stat = ts->stat; + break; + } + } + ASSIGN_SAS_TASK(sc, NULL); + sc->result = (hs << 16) | stat; + list_del_init(&task->list); + sas_free_task(task); + /* This is very ugly but this is how SCSI Core works. */ + if (ts_flags & SAS_TASK_STATE_ABORTED) + scsi_finish_command(sc); + else + sc->scsi_done(sc); +} + +static enum task_attribute sas_scsi_get_task_attr(struct scsi_cmnd *cmd) +{ + enum task_attribute ta = TASK_ATTR_SIMPLE; + if (cmd->request && blk_rq_tagged(cmd->request)) { + if (cmd->device->ordered_tags && + (cmd->request->flags & REQ_HARDBARRIER)) + ta = TASK_ATTR_HOQ; + } + return ta; +} + +static struct sas_task *sas_create_task(struct scsi_cmnd *cmd, + struct domain_device *dev, + unsigned long gfp_flags) +{ + struct sas_task *task = sas_alloc_task(gfp_flags); + struct scsi_lun lun; + + if (!task) + return NULL; + + *(u32 *)cmd->sense_buffer = 0; + task->uldd_task = cmd; + ASSIGN_SAS_TASK(cmd, task); + + task->dev = dev; + task->task_proto = task->dev->tproto; /* BUG_ON(!SSP) */ + + task->ssp_task.retry_count = 1; + int_to_scsilun(cmd->device->lun, &lun); + memcpy(task->ssp_task.LUN, &lun.scsi_lun, 8); + task->ssp_task.task_attr = sas_scsi_get_task_attr(cmd); + memcpy(task->ssp_task.cdb, cmd->cmnd, 16); + + task->scatter = cmd->request_buffer; + task->num_scatter = cmd->use_sg; + task->total_xfer_len = cmd->request_bufflen; + task->data_dir = cmd->sc_data_direction; + + task->task_done = sas_scsi_task_done; + + return task; +} + +static int sas_queue_up(struct sas_task *task) +{ + struct sas_ha_struct *sas_ha = task->dev->port->ha; + struct scsi_core *core = &sas_ha->core; + unsigned long flags; + LIST_HEAD(list); + + spin_lock_irqsave(&core->task_queue_lock, flags); + if (sas_ha->lldd_queue_size < core->task_queue_size + 1) { + spin_unlock_irqrestore(&core->task_queue_lock, flags); + return -SAS_QUEUE_FULL; + } + list_add_tail(&task->list, &core->task_queue); + core->task_queue_size += 1; + spin_unlock_irqrestore(&core->task_queue_lock, flags); + up(&core->queue_thread_sema); + + return 0; +} + +/** + * sas_queuecommand -- Enqueue a command for processing + * @parameters: See SCSI Core documentation + * + * Note: XXX: Remove the host unlock/lock pair when SCSI Core can + * call us without holding an IRQ spinlock... + */ +int sas_queuecommand(struct scsi_cmnd *cmd, + void (*scsi_done)(struct scsi_cmnd *)) +{ + int res = 0; + struct domain_device *dev = cmd_to_domain_dev(cmd); + struct Scsi_Host *host = cmd->device->host; + struct sas_internal *i = to_sas_internal(host->transportt); + + spin_unlock_irq(host->host_lock); + + { + struct sas_ha_struct *sas_ha = dev->port->ha; + struct sas_task *task; + + res = -ENOMEM; + task = sas_create_task(cmd, dev, GFP_ATOMIC); + if (!task) + goto out; + + cmd->scsi_done = scsi_done; + /* Queue up, Direct Mode or Task Collector Mode. */ + if (sas_ha->lldd_max_execute_num < 2) + res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); + else + res = sas_queue_up(task); + + /* Examine */ + if (res) { + SAS_DPRINTK("lldd_execute_task returned: %d\n", res); + ASSIGN_SAS_TASK(cmd, NULL); + sas_free_task(task); + if (res == -SAS_QUEUE_FULL) { + cmd->result = DID_SOFT_ERROR << 16; /* retry */ + res = 0; + scsi_done(cmd); + } + goto out; + } + } +out: + spin_lock_irq(host->host_lock); + return res; +} + +static void sas_scsi_clear_queue_lu(struct list_head *error_q, struct scsi_cmnd *my_cmd) +{ + struct scsi_cmnd *cmd, *n; + + list_for_each_entry_safe(cmd, n, error_q, eh_entry) { + if (cmd == my_cmd) + list_del_init(&cmd->eh_entry); + } +} + +static void sas_scsi_clear_queue_I_T(struct list_head *error_q, + struct domain_device *dev) +{ + struct scsi_cmnd *cmd, *n; + + list_for_each_entry_safe(cmd, n, error_q, eh_entry) { + struct domain_device *x = cmd_to_domain_dev(cmd); + + if (x == dev) + list_del_init(&cmd->eh_entry); + } +} + +static void sas_scsi_clear_queue_port(struct list_head *error_q, + struct asd_sas_port *port) +{ + struct scsi_cmnd *cmd, *n; + + list_for_each_entry_safe(cmd, n, error_q, eh_entry) { + struct domain_device *dev = cmd_to_domain_dev(cmd); + struct asd_sas_port *x = dev->port; + + if (x == port) + list_del_init(&cmd->eh_entry); + } +} + +enum task_disposition { + TASK_IS_DONE, + TASK_IS_ABORTED, + TASK_IS_AT_LU, + TASK_IS_NOT_AT_LU, +}; + +static enum task_disposition sas_scsi_find_task(struct sas_task *task) +{ + struct sas_ha_struct *ha = task->dev->port->ha; + unsigned long flags; + int i, res; + struct sas_internal *si = + to_sas_internal(task->dev->port->ha->core.shost->transportt); + + if (ha->lldd_max_execute_num > 1) { + struct scsi_core *core = &ha->core; + struct sas_task *t, *n; + + spin_lock_irqsave(&core->task_queue_lock, flags); + list_for_each_entry_safe(t, n, &core->task_queue, list) { + if (task == t) { + list_del_init(&t->list); + spin_unlock_irqrestore(&core->task_queue_lock, + flags); + SAS_DPRINTK("%s: task 0x%p aborted from " + "task_queue\n", + __FUNCTION__, task); + return TASK_IS_ABORTED; + } + } + spin_unlock_irqrestore(&core->task_queue_lock, flags); + } + + for (i = 0; i < 5; i++) { + SAS_DPRINTK("%s: aborting task 0x%p\n", __FUNCTION__, task); + res = si->dft->lldd_abort_task(task); + + spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_STATE_DONE) { + spin_unlock_irqrestore(&task->task_state_lock, flags); + SAS_DPRINTK("%s: task 0x%p is done\n", __FUNCTION__, + task); + return TASK_IS_DONE; + } + spin_unlock_irqrestore(&task->task_state_lock, flags); + + if (res == TMF_RESP_FUNC_COMPLETE) { + SAS_DPRINTK("%s: task 0x%p is aborted\n", + __FUNCTION__, task); + return TASK_IS_ABORTED; + } else if (si->dft->lldd_query_task) { + SAS_DPRINTK("%s: querying task 0x%p\n", + __FUNCTION__, task); + res = si->dft->lldd_query_task(task); + if (res == TMF_RESP_FUNC_SUCC) { + SAS_DPRINTK("%s: task 0x%p at LU\n", + __FUNCTION__, task); + return TASK_IS_AT_LU; + } else if (res == TMF_RESP_FUNC_COMPLETE) { + SAS_DPRINTK("%s: task 0x%p not at LU\n", + __FUNCTION__, task); + return TASK_IS_NOT_AT_LU; + } + } + } + return res; +} + +static int sas_recover_lu(struct domain_device *dev, struct scsi_cmnd *cmd) +{ + int res = TMF_RESP_FUNC_FAILED; + struct scsi_lun lun; + struct sas_internal *i = + to_sas_internal(dev->port->ha->core.shost->transportt); + + int_to_scsilun(cmd->device->lun, &lun); + + SAS_DPRINTK("eh: device %llx LUN %x has the task\n", + SAS_ADDR(dev->sas_addr), + cmd->device->lun); + + if (i->dft->lldd_abort_task_set) + res = i->dft->lldd_abort_task_set(dev, lun.scsi_lun); + + if (res == TMF_RESP_FUNC_FAILED) { + if (i->dft->lldd_clear_task_set) + res = i->dft->lldd_clear_task_set(dev, lun.scsi_lun); + } + + if (res == TMF_RESP_FUNC_FAILED) { + if (i->dft->lldd_lu_reset) + res = i->dft->lldd_lu_reset(dev, lun.scsi_lun); + } + + return res; +} + +static int sas_recover_I_T(struct domain_device *dev) +{ + int res = TMF_RESP_FUNC_FAILED; + struct sas_internal *i = + to_sas_internal(dev->port->ha->core.shost->transportt); + + SAS_DPRINTK("I_T nexus reset for dev %016llx\n", + SAS_ADDR(dev->sas_addr)); + + if (i->dft->lldd_I_T_nexus_reset) + res = i->dft->lldd_I_T_nexus_reset(dev); + + return res; +} + +void sas_scsi_recover_host(struct Scsi_Host *shost) +{ + struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); + unsigned long flags; + LIST_HEAD(error_q); + struct scsi_cmnd *cmd, *n; + enum task_disposition res = TASK_IS_DONE; + int tmf_resp; + struct sas_internal *i = to_sas_internal(shost->transportt); + + spin_lock_irqsave(shost->host_lock, flags); + list_splice_init(&shost->eh_cmd_q, &error_q); + spin_unlock_irqrestore(shost->host_lock, flags); + + SAS_DPRINTK("Enter %s\n", __FUNCTION__); + + /* All tasks on this list were marked SAS_TASK_STATE_ABORTED + * by sas_scsi_timed_out() callback. + */ +Again: + SAS_DPRINTK("going over list...\n"); + list_for_each_entry_safe(cmd, n, &error_q, eh_entry) { + struct sas_task *task = TO_SAS_TASK(cmd); + + SAS_DPRINTK("trying to find task 0x%p\n", task); + list_del_init(&cmd->eh_entry); + res = sas_scsi_find_task(task); + + cmd->eh_eflags = 0; + shost->host_failed--; + + switch (res) { + case TASK_IS_DONE: + SAS_DPRINTK("%s: task 0x%p is done\n", __FUNCTION__, + task); + task->task_done(task); + continue; + case TASK_IS_ABORTED: + SAS_DPRINTK("%s: task 0x%p is aborted\n", + __FUNCTION__, task); + task->task_done(task); + continue; + case TASK_IS_AT_LU: + SAS_DPRINTK("task 0x%p is at LU: lu recover\n", task); + tmf_resp = sas_recover_lu(task->dev, cmd); + if (tmf_resp == TMF_RESP_FUNC_COMPLETE) { + SAS_DPRINTK("dev %016llx LU %x is " + "recovered\n", + SAS_ADDR(task->dev), + cmd->device->lun); + task->task_done(task); + sas_scsi_clear_queue_lu(&error_q, cmd); + goto Again; + } + /* fallthrough */ + case TASK_IS_NOT_AT_LU: + SAS_DPRINTK("task 0x%p is not at LU: I_T recover\n", + task); + tmf_resp = sas_recover_I_T(task->dev); + if (tmf_resp == TMF_RESP_FUNC_COMPLETE) { + SAS_DPRINTK("I_T %016llx recovered\n", + SAS_ADDR(task->dev->sas_addr)); + task->task_done(task); + sas_scsi_clear_queue_I_T(&error_q, task->dev); + goto Again; + } + /* Hammer time :-) */ + if (i->dft->lldd_clear_nexus_port) { + struct asd_sas_port *port = task->dev->port; + SAS_DPRINTK("clearing nexus for port:%d\n", + port->id); + res = i->dft->lldd_clear_nexus_port(port); + if (res == TMF_RESP_FUNC_COMPLETE) { + SAS_DPRINTK("clear nexus port:%d " + "succeeded\n", port->id); + task->task_done(task); + sas_scsi_clear_queue_port(&error_q, + port); + goto Again; + } + } + if (i->dft->lldd_clear_nexus_ha) { + SAS_DPRINTK("clear nexus ha\n"); + res = i->dft->lldd_clear_nexus_ha(ha); + if (res == TMF_RESP_FUNC_COMPLETE) { + SAS_DPRINTK("clear nexus ha " + "succeeded\n"); + task->task_done(task); + goto out; + } + } + /* If we are here -- this means that no amount + * of effort could recover from errors. Quite + * possibly the HA just disappeared. + */ + SAS_DPRINTK("error from device %llx, LUN %x " + "couldn't be recovered in any way\n", + SAS_ADDR(task->dev->sas_addr), + cmd->device->lun); + + task->task_done(task); + goto clear_q; + } + } +out: + SAS_DPRINTK("--- Exit %s\n", __FUNCTION__); + return; +clear_q: + SAS_DPRINTK("--- Exit %s -- clear_q\n", __FUNCTION__); + list_for_each_entry_safe(cmd, n, &error_q, eh_entry) { + struct sas_task *task = TO_SAS_TASK(cmd); + list_del_init(&cmd->eh_entry); + task->task_done(task); + } +} + +enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) +{ + struct sas_task *task = TO_SAS_TASK(cmd); + unsigned long flags; + + if (!task) { + SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_HANDLED\n", + cmd, task); + return EH_HANDLED; + } + + spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_STATE_DONE) { + spin_unlock_irqrestore(&task->task_state_lock, flags); + SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_HANDLED\n", + cmd, task); + return EH_HANDLED; + } + task->task_state_flags |= SAS_TASK_STATE_ABORTED; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_NOT_HANDLED\n", + cmd, task); + + return EH_NOT_HANDLED; +} + +struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy) +{ + struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent); + struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); + struct domain_device *found_dev = NULL; + int i; + + spin_lock(&ha->phy_port_lock); + for (i = 0; i < ha->num_phys; i++) { + struct asd_sas_port *port = ha->sas_port[i]; + struct domain_device *dev; + + spin_lock(&port->dev_list_lock); + list_for_each_entry(dev, &port->dev_list, dev_list_node) { + if (rphy == dev->rphy) { + found_dev = dev; + spin_unlock(&port->dev_list_lock); + goto found; + } + } + spin_unlock(&port->dev_list_lock); + } + found: + spin_unlock(&ha->phy_port_lock); + + return found_dev; +} + +static inline struct domain_device *sas_find_target(struct scsi_target *starget) +{ + struct sas_rphy *rphy = dev_to_rphy(starget->dev.parent); + + return sas_find_dev_by_rphy(rphy); +} + +int sas_target_alloc(struct scsi_target *starget) +{ + struct domain_device *found_dev = sas_find_target(starget); + + if (!found_dev) + return -ENODEV; + + starget->hostdata = found_dev; + return 0; +} + +#define SAS_DEF_QD 32 +#define SAS_MAX_QD 64 + +int sas_slave_configure(struct scsi_device *scsi_dev) +{ + struct domain_device *dev = sdev_to_domain_dev(scsi_dev); + struct sas_ha_struct *sas_ha; + + BUG_ON(dev->rphy->identify.device_type != SAS_END_DEVICE); + + sas_ha = dev->port->ha; + + sas_read_port_mode_page(scsi_dev); + + if (scsi_dev->tagged_supported) { + scsi_set_tag_type(scsi_dev, MSG_SIMPLE_TAG); + scsi_activate_tcq(scsi_dev, SAS_DEF_QD); + } else { + SAS_DPRINTK("device %llx, LUN %x doesn't support " + "TCQ\n", SAS_ADDR(dev->sas_addr), + scsi_dev->lun); + scsi_dev->tagged_supported = 0; + scsi_set_tag_type(scsi_dev, 0); + scsi_deactivate_tcq(scsi_dev, 1); + } + + return 0; +} + +void sas_slave_destroy(struct scsi_device *scsi_dev) +{ +} + +int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth) +{ + int res = min(new_depth, SAS_MAX_QD); + + if (scsi_dev->tagged_supported) + scsi_adjust_queue_depth(scsi_dev, scsi_get_tag_type(scsi_dev), + res); + else { + struct domain_device *dev = sdev_to_domain_dev(scsi_dev); + sas_printk("device %llx LUN %x queue depth changed to 1\n", + SAS_ADDR(dev->sas_addr), + scsi_dev->lun); + scsi_adjust_queue_depth(scsi_dev, 0, 1); + res = 1; + } + + return res; +} + +int sas_change_queue_type(struct scsi_device *scsi_dev, int qt) +{ + if (!scsi_dev->tagged_supported) + return 0; + + scsi_deactivate_tcq(scsi_dev, 1); + + scsi_set_tag_type(scsi_dev, qt); + scsi_activate_tcq(scsi_dev, scsi_dev->queue_depth); + + return qt; +} + +int sas_bios_param(struct scsi_device *scsi_dev, + struct block_device *bdev, + sector_t capacity, int *hsc) +{ + hsc[0] = 255; + hsc[1] = 63; + sector_div(capacity, 255*63); + hsc[2] = capacity; + + return 0; +} + +/* ---------- Task Collector Thread implementation ---------- */ + +static void sas_queue(struct sas_ha_struct *sas_ha) +{ + struct scsi_core *core = &sas_ha->core; + unsigned long flags; + LIST_HEAD(q); + int can_queue; + int res; + struct sas_internal *i = to_sas_internal(core->shost->transportt); + + spin_lock_irqsave(&core->task_queue_lock, flags); + while (!core->queue_thread_kill && + !list_empty(&core->task_queue)) { + + can_queue = sas_ha->lldd_queue_size - core->task_queue_size; + if (can_queue >= 0) { + can_queue = core->task_queue_size; + list_splice_init(&core->task_queue, &q); + } else { + struct list_head *a, *n; + + can_queue = sas_ha->lldd_queue_size; + list_for_each_safe(a, n, &core->task_queue) { + list_move_tail(a, &q); + if (--can_queue == 0) + break; + } + can_queue = sas_ha->lldd_queue_size; + } + core->task_queue_size -= can_queue; + spin_unlock_irqrestore(&core->task_queue_lock, flags); + { + struct sas_task *task = list_entry(q.next, + struct sas_task, + list); + list_del_init(&q); + res = i->dft->lldd_execute_task(task, can_queue, + GFP_KERNEL); + if (unlikely(res)) + __list_add(&q, task->list.prev, &task->list); + } + spin_lock_irqsave(&core->task_queue_lock, flags); + if (res) { + list_splice_init(&q, &core->task_queue); /*at head*/ + core->task_queue_size += can_queue; + } + } + spin_unlock_irqrestore(&core->task_queue_lock, flags); +} + +static DECLARE_COMPLETION(queue_th_comp); + +/** + * sas_queue_thread -- The Task Collector thread + * @_sas_ha: pointer to struct sas_ha + */ +static int sas_queue_thread(void *_sas_ha) +{ + struct sas_ha_struct *sas_ha = _sas_ha; + struct scsi_core *core = &sas_ha->core; + + daemonize("sas_queue_%d", core->shost->host_no); + current->flags |= PF_NOFREEZE; + + complete(&queue_th_comp); + + while (1) { + down_interruptible(&core->queue_thread_sema); + sas_queue(sas_ha); + if (core->queue_thread_kill) + break; + } + + complete(&queue_th_comp); + + return 0; +} + +int sas_init_queue(struct sas_ha_struct *sas_ha) +{ + int res; + struct scsi_core *core = &sas_ha->core; + + spin_lock_init(&core->task_queue_lock); + core->task_queue_size = 0; + INIT_LIST_HEAD(&core->task_queue); + init_MUTEX_LOCKED(&core->queue_thread_sema); + + res = kernel_thread(sas_queue_thread, sas_ha, 0); + if (res >= 0) + wait_for_completion(&queue_th_comp); + + return res < 0 ? res : 0; +} + +void sas_shutdown_queue(struct sas_ha_struct *sas_ha) +{ + unsigned long flags; + struct scsi_core *core = &sas_ha->core; + struct sas_task *task, *n; + + init_completion(&queue_th_comp); + core->queue_thread_kill = 1; + up(&core->queue_thread_sema); + wait_for_completion(&queue_th_comp); + + if (!list_empty(&core->task_queue)) + SAS_DPRINTK("HA: %llx: scsi core task queue is NOT empty!?\n", + SAS_ADDR(sas_ha->sas_addr)); + + spin_lock_irqsave(&core->task_queue_lock, flags); + list_for_each_entry_safe(task, n, &core->task_queue, list) { + struct scsi_cmnd *cmd = task->uldd_task; + + list_del_init(&task->list); + + ASSIGN_SAS_TASK(cmd, NULL); + sas_free_task(task); + cmd->result = DID_ABORT << 16; + cmd->scsi_done(cmd); + } + spin_unlock_irqrestore(&core->task_queue_lock, flags); +} + +EXPORT_SYMBOL_GPL(sas_queuecommand); +EXPORT_SYMBOL_GPL(sas_target_alloc); +EXPORT_SYMBOL_GPL(sas_slave_configure); +EXPORT_SYMBOL_GPL(sas_slave_destroy); +EXPORT_SYMBOL_GPL(sas_change_queue_depth); +EXPORT_SYMBOL_GPL(sas_change_queue_type); +EXPORT_SYMBOL_GPL(sas_bios_param); -- cgit v1.2.3 From bc229b3663dcd7d8f266cb13b0839efdee6d95b5 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Mon, 28 Aug 2006 17:08:21 -0500 Subject: [SCSI] aic94xx: add MODULE_FIRMWARE tag Add a tag which shows what the firmware file we're requesting is. Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx_seq.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aic94xx/aic94xx_seq.c b/drivers/scsi/aic94xx/aic94xx_seq.c index 9050c6f3f6b..d9b6da5fd06 100644 --- a/drivers/scsi/aic94xx/aic94xx_seq.c +++ b/drivers/scsi/aic94xx/aic94xx_seq.c @@ -28,6 +28,7 @@ #include #include +#include #include #include "aic94xx_reg.h" #include "aic94xx_hwi.h" @@ -1399,3 +1400,5 @@ void asd_update_port_links(struct asd_sas_phy *sas_phy) if (err) asd_printk("couldn't update DDB 0:error:%d\n", err); } + +MODULE_FIRMWARE(SAS_RAZOR_SEQUENCER_FW_FILE); -- cgit v1.2.3 From f19eaa7f53736449a6eac89c3863eca2c64d5913 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 30 Aug 2006 14:18:33 -0700 Subject: [SCSI] aic94xx: Increase can_queue for better performance This patch sets can_queue in the aic94xx driver's scsi_host to better performing values than what's there currently. It seems that asd_ha->seq.can_queue reflects the number of requests that can be queued per controller; so long as there's one scsi_host per controller, it seems logical that the scsi_host ought to have the same can_queue value. To the best of my (still limited) knowledge, this method provides the correct value. The effect of leaving this value set to 1 is terrible performance in the case of either (a) certain Maxtor SAS drives flying solo or (b) flooding several disks with I/O simultaneously (md-raid). There may be more scenarios where we see similar problems that I haven't uncovered. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx_init.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index 3ec2e46f80c..69aa7088753 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -620,6 +620,8 @@ static int __devinit asd_pci_probe(struct pci_dev *dev, asd_ha->hw_prof.bios.present ? "build " : "not present", asd_ha->hw_prof.bios.bld); + shost->can_queue = asd_ha->seq.can_queue; + if (use_msi) pci_enable_msi(asd_ha->pcidev); -- cgit v1.2.3 From 86e33a296c2c9ed6eece0bfff4ac776f42040504 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 30 Aug 2006 09:45:51 -0400 Subject: [SCSI] add shared tag map helpers This patch adds support for sharing tag maps at the host level (i.e. either every queue [LUN] has its own tag map or there's a single one for the entire host). This formulation is primarily intended to help single issue queue hardware, like the aic7xxx Signed-off-by: James Bottomley --- drivers/scsi/hosts.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index f244d4f6597..68ef1636678 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -265,6 +265,9 @@ static void scsi_host_dev_release(struct device *dev) destroy_workqueue(shost->work_q); scsi_destroy_command_freelist(shost); + if (shost->bqt) + blk_free_tags(shost->bqt); + kfree(shost->shost_data); if (parent) -- cgit v1.2.3 From 85b6c720b0931101c8bcc3a5abdc2b8514b0fb4b Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Thu, 31 Aug 2006 18:15:22 -0400 Subject: [SCSI] sd: fix cache flushing on module removal (and individual device removal) The fix isn't actually in sd: it's in scsi_device_get(). I modified it to allow devices to be returned in SDEV_CANCEL, but not SDEV_DEL. This means that the device_remove_driver, which occurs in device_del() in scsi_remove_device() after the device has gone into SDEV_CANCEL is now effective at flushing the cache. Signed-off-by: James Bottomley --- drivers/scsi/scsi.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 94df671d776..37843927e47 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -851,14 +851,14 @@ EXPORT_SYMBOL(scsi_track_queue_full); */ int scsi_device_get(struct scsi_device *sdev) { - if (sdev->sdev_state == SDEV_DEL || sdev->sdev_state == SDEV_CANCEL) + if (sdev->sdev_state == SDEV_DEL) return -ENXIO; if (!get_device(&sdev->sdev_gendev)) return -ENXIO; - if (!try_module_get(sdev->host->hostt->module)) { - put_device(&sdev->sdev_gendev); - return -ENXIO; - } + /* We can fail this if we're doing SCSI operations + * from module exit (like cache flush) */ + try_module_get(sdev->host->hostt->module); + return 0; } EXPORT_SYMBOL(scsi_device_get); @@ -873,7 +873,10 @@ EXPORT_SYMBOL(scsi_device_get); */ void scsi_device_put(struct scsi_device *sdev) { - module_put(sdev->host->hostt->module); + /* The module refcount will be zero if scsi_device_get() + * was called from a module removal routine */ + if (likely(module_refcount(sdev->host->hostt->module) != 0)) + module_put(sdev->host->hostt->module); put_device(&sdev->sdev_gendev); } EXPORT_SYMBOL(scsi_device_put); -- cgit v1.2.3 From e5b3cd42960a10c1bc3701d4f00767463c88ec9d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 21 Aug 2006 15:53:25 -0400 Subject: [SCSI] SCSI: sanitize INQUIRY strings Sanitize the Vendor, Product, and Revision strings contained in an INQUIRY result by setting all non-graphic or non-ASCII characters to ' '. Since the standard disallows such characters, this will affect only non-compliant devices. To help maintain backward compatibility, NUL characters are treated specially. They are taken as string terminators; they and all the following characters are set to ' '. If some valid characters get erased as a result... well, we weren't seeing them before so we haven't lost anything. The primary purpose of this change is to allow blacklist entries to match devices with illegal Vendor or Product strings. In addition, the patch updates a couple of function prototypes, giving inq_result its correct type (unsigned char *). Signed-off-by: Alan Stern Signed-off-by: James Bottomley --- drivers/scsi/scsi_scan.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index a24d3461fc7..31d05ab0b2f 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -396,6 +396,32 @@ void scsi_target_reap(struct scsi_target *starget) return; } +/** + * sanitize_inquiry_string - remove non-graphical chars from an INQUIRY result string + * @s: INQUIRY result string to sanitize + * @len: length of the string + * + * Description: + * The SCSI spec says that INQUIRY vendor, product, and revision + * strings must consist entirely of graphic ASCII characters, + * padded on the right with spaces. Since not all devices obey + * this rule, we will replace non-graphic or non-ASCII characters + * with spaces. Exception: a NUL character is interpreted as a + * string terminator, so all the following characters are set to + * spaces. + **/ +static void sanitize_inquiry_string(unsigned char *s, int len) +{ + int terminated = 0; + + for (; len > 0; (--len, ++s)) { + if (*s == 0) + terminated = 1; + if (terminated || *s < 0x20 || *s > 0x7e) + *s = ' '; + } +} + /** * scsi_probe_lun - probe a single LUN using a SCSI INQUIRY * @sdev: scsi_device to probe @@ -410,7 +436,7 @@ void scsi_target_reap(struct scsi_target *starget) * INQUIRY data is in @inq_result; the scsi_level and INQUIRY length * are copied to the scsi_device any flags value is stored in *@bflags. **/ -static int scsi_probe_lun(struct scsi_device *sdev, char *inq_result, +static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result, int result_len, int *bflags) { unsigned char scsi_cmd[MAX_COMMAND_SIZE]; @@ -469,7 +495,11 @@ static int scsi_probe_lun(struct scsi_device *sdev, char *inq_result, } if (result == 0) { - response_len = (unsigned char) inq_result[4] + 5; + sanitize_inquiry_string(&inq_result[8], 8); + sanitize_inquiry_string(&inq_result[16], 16); + sanitize_inquiry_string(&inq_result[32], 4); + + response_len = inq_result[4] + 5; if (response_len > 255) response_len = first_inquiry_len; /* sanity */ @@ -575,7 +605,8 @@ static int scsi_probe_lun(struct scsi_device *sdev, char *inq_result, * SCSI_SCAN_NO_RESPONSE: could not allocate or setup a scsi_device * SCSI_SCAN_LUN_PRESENT: a new scsi_device was allocated and initialized **/ -static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) +static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, + int *bflags) { /* * XXX do not save the inquiry, since it can change underneath us, -- cgit v1.2.3 From ffd0436ed2e5a741c8d30062b489b989acf0a526 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Thu, 31 Aug 2006 18:09:24 -0400 Subject: [SCSI] libiscsi, iscsi_tcp, iscsi_iser: check that burst lengths are valid. iSCSI RFC states that the first burst length must be smaller than the max burst length. We currently assume targets will be good, but that may not be the case, so this patch adds a check. This patch also moves the unsol data out offset to the lib so the LLDs do not have to track it. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/iscsi_tcp.c | 30 +++++++----------------------- drivers/scsi/iscsi_tcp.h | 1 - drivers/scsi/libiscsi.c | 25 +++++++++++++++++-------- 3 files changed, 24 insertions(+), 32 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 058f094f945..a97a3a4e99e 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -1264,19 +1264,6 @@ iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, r2t->data_count); } -static void -iscsi_unsolicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) -{ - struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; - struct iscsi_data_task *dtask; - - dtask = tcp_ctask->dtask = &tcp_ctask->unsol_dtask; - iscsi_prep_unsolicit_data_pdu(ctask, &dtask->hdr, - tcp_ctask->r2t_data_count); - iscsi_buf_init_iov(&tcp_ctask->headbuf, (char*)&dtask->hdr, - sizeof(struct iscsi_hdr)); -} - /** * iscsi_tcp_cmd_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands * @conn: iscsi connection @@ -1326,14 +1313,11 @@ iscsi_tcp_cmd_init(struct iscsi_cmd_task *ctask) if (ctask->unsol_count) tcp_ctask->xmstate |= XMSTATE_UNS_HDR | XMSTATE_UNS_INIT; - tcp_ctask->r2t_data_count = ctask->total_length - - ctask->imm_count - - ctask->unsol_count; - debug_scsi("cmd [itt 0x%x total %d imm %d imm_data %d " - "r2t_data %d]\n", + debug_scsi("cmd [itt 0x%x total %d imm_data %d " + "unsol count %d, unsol offset %d]\n", ctask->itt, ctask->total_length, ctask->imm_count, - ctask->unsol_count, tcp_ctask->r2t_data_count); + ctask->unsol_count, ctask->unsol_offset); } else tcp_ctask->xmstate = XMSTATE_R_HDR; @@ -1531,8 +1515,10 @@ handle_xmstate_uns_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) tcp_ctask->xmstate |= XMSTATE_UNS_DATA; if (tcp_ctask->xmstate & XMSTATE_UNS_INIT) { - iscsi_unsolicit_data_init(conn, ctask); - dtask = tcp_ctask->dtask; + dtask = tcp_ctask->dtask = &tcp_ctask->unsol_dtask; + iscsi_prep_unsolicit_data_pdu(ctask, &dtask->hdr); + iscsi_buf_init_iov(&tcp_ctask->headbuf, (char*)&dtask->hdr, + sizeof(struct iscsi_hdr)); if (conn->hdrdgst_en) iscsi_hdr_digest(conn, &tcp_ctask->headbuf, (u8*)dtask->hdrext); @@ -1720,7 +1706,6 @@ data_out_done: * Done with this R2T. Check if there are more * outstanding R2Ts ready to be processed. */ - BUG_ON(tcp_ctask->r2t_data_count - r2t->data_length < 0); if (conn->datadgst_en) { rc = iscsi_digest_final_send(conn, ctask, &dtask->digestbuf, &dtask->digest, 1); @@ -1732,7 +1717,6 @@ data_out_done: debug_tcp("r2t done dout digest 0x%x\n", dtask->digest); } - tcp_ctask->r2t_data_count -= r2t->data_length; tcp_ctask->r2t = NULL; spin_lock_bh(&session->lock); __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t, sizeof(void*)); diff --git a/drivers/scsi/iscsi_tcp.h b/drivers/scsi/iscsi_tcp.h index 6a4ee704e46..aace8f70dfd 100644 --- a/drivers/scsi/iscsi_tcp.h +++ b/drivers/scsi/iscsi_tcp.h @@ -157,7 +157,6 @@ struct iscsi_tcp_cmd_task { struct scatterlist *bad_sg; /* assert statement */ int sg_count; /* SG's to process */ uint32_t exp_r2tsn; - int r2t_data_count; /* R2T Data-Out bytes */ int data_offset; struct iscsi_r2t_info *r2t; /* in progress R2T */ struct iscsi_queue r2tpool; diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 5884cd26d53..a7c6e70f4ef 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -68,8 +68,7 @@ iscsi_check_assign_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr) EXPORT_SYMBOL_GPL(iscsi_check_assign_cmdsn); void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask, - struct iscsi_data *hdr, - int transport_data_cnt) + struct iscsi_data *hdr) { struct iscsi_conn *conn = ctask->conn; @@ -82,14 +81,12 @@ void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask, hdr->itt = ctask->hdr->itt; hdr->exp_statsn = cpu_to_be32(conn->exp_statsn); - - hdr->offset = cpu_to_be32(ctask->total_length - - transport_data_cnt - - ctask->unsol_count); + hdr->offset = cpu_to_be32(ctask->unsol_offset); if (ctask->unsol_count > conn->max_xmit_dlength) { hton24(hdr->dlength, conn->max_xmit_dlength); ctask->data_count = conn->max_xmit_dlength; + ctask->unsol_offset += ctask->data_count; hdr->flags = 0; } else { hton24(hdr->dlength, ctask->unsol_count); @@ -125,6 +122,7 @@ static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) memcpy(hdr->cdb, sc->cmnd, sc->cmd_len); memset(&hdr->cdb[sc->cmd_len], 0, MAX_COMMAND_SIZE - sc->cmd_len); + ctask->data_count = 0; if (sc->sc_data_direction == DMA_TO_DEVICE) { hdr->flags |= ISCSI_FLAG_CMD_WRITE; /* @@ -143,6 +141,7 @@ static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) */ ctask->imm_count = 0; ctask->unsol_count = 0; + ctask->unsol_offset = 0; ctask->unsol_datasn = 0; if (session->imm_data_en) { @@ -156,9 +155,12 @@ static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) } else zero_data(ctask->hdr->dlength); - if (!session->initial_r2t_en) + if (!session->initial_r2t_en) { ctask->unsol_count = min(session->first_burst, ctask->total_length) - ctask->imm_count; + ctask->unsol_offset = ctask->imm_count; + } + if (!ctask->unsol_count) /* No unsolicit Data-Out's */ ctask->hdr->flags |= ISCSI_FLAG_CMD_FINAL; @@ -1520,11 +1522,18 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn) struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_session *session = conn->session; - if (session == NULL) { + if (!session) { printk(KERN_ERR "iscsi: can't start unbound connection\n"); return -EPERM; } + if (session->first_burst > session->max_burst) { + printk("iscsi: invalid burst lengths: " + "first_burst %d max_burst %d\n", + session->first_burst, session->max_burst); + return -EINVAL; + } + spin_lock_bh(&session->lock); conn->c_stage = ISCSI_CONN_STARTED; session->state = ISCSI_STATE_LOGGED_IN; -- cgit v1.2.3 From 60ecebf5a10e42f5e2d6e07eb9e24bdee8500b81 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Thu, 31 Aug 2006 18:09:25 -0400 Subject: [SCSI] add refcouting around ctask usage in main IO patch It is possible that a ctask could be completing and getting cleaned up at the same time, we are finishing up the last data transfer. This could then result in the data transfer code using stale or invalid values. This patch adds a refcount to the ctask. When the count goes to zero then we know the transmit thread and recv thread or softirq are not touching it and we can safely release it. The eh should not need to grab a reference because it only cleans up a task if it has both the xmit mutex and recv lock (or recv side suspended). Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/libiscsi.c | 59 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 9 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index a7c6e70f4ef..9584cbc082f 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -179,16 +179,15 @@ EXPORT_SYMBOL_GPL(iscsi_prep_scsi_cmd_pdu); /** * iscsi_complete_command - return command back to scsi-ml - * @session: iscsi session * @ctask: iscsi cmd task * * Must be called with session lock. * This function returns the scsi command to scsi-ml and returns * the cmd task to the pool of available cmd tasks. */ -static void iscsi_complete_command(struct iscsi_session *session, - struct iscsi_cmd_task *ctask) +static void iscsi_complete_command(struct iscsi_cmd_task *ctask) { + struct iscsi_session *session = ctask->conn->session; struct scsi_cmnd *sc = ctask->sc; ctask->state = ISCSI_TASK_COMPLETED; @@ -198,6 +197,35 @@ static void iscsi_complete_command(struct iscsi_session *session, sc->scsi_done(sc); } +static void __iscsi_get_ctask(struct iscsi_cmd_task *ctask) +{ + atomic_inc(&ctask->refcount); +} + +static void iscsi_get_ctask(struct iscsi_cmd_task *ctask) +{ + spin_lock_bh(&ctask->conn->session->lock); + __iscsi_get_ctask(ctask); + spin_unlock_bh(&ctask->conn->session->lock); +} + +static void __iscsi_put_ctask(struct iscsi_cmd_task *ctask) +{ + struct iscsi_conn *conn = ctask->conn; + + if (atomic_dec_and_test(&ctask->refcount)) { + conn->session->tt->cleanup_cmd_task(conn, ctask); + iscsi_complete_command(ctask); + } +} + +static void iscsi_put_ctask(struct iscsi_cmd_task *ctask) +{ + spin_lock_bh(&ctask->conn->session->lock); + __iscsi_put_ctask(ctask); + spin_unlock_bh(&ctask->conn->session->lock); +} + /** * iscsi_cmd_rsp - SCSI Command Response processing * @conn: iscsi connection @@ -274,7 +302,7 @@ out: (long)sc, sc->result, ctask->itt); conn->scsirsp_pdus_cnt++; - iscsi_complete_command(conn->session, ctask); + __iscsi_put_ctask(ctask); return rc; } @@ -338,7 +366,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, BUG_ON((void*)ctask != ctask->sc->SCp.ptr); if (hdr->flags & ISCSI_FLAG_DATA_STATUS) { conn->scsirsp_pdus_cnt++; - iscsi_complete_command(session, ctask); + __iscsi_put_ctask(ctask); } break; case ISCSI_OP_R2T: @@ -563,7 +591,9 @@ static int iscsi_data_xmit(struct iscsi_conn *conn) BUG_ON(conn->ctask && conn->mtask); if (conn->ctask) { + iscsi_get_ctask(conn->ctask); rc = tt->xmit_cmd_task(conn, conn->ctask); + iscsi_put_ctask(conn->ctask); if (rc) goto again; /* done with this in-progress ctask */ @@ -604,12 +634,19 @@ static int iscsi_data_xmit(struct iscsi_conn *conn) struct iscsi_cmd_task, running); conn->ctask->state = ISCSI_TASK_RUNNING; list_move_tail(conn->xmitqueue.next, &conn->run_list); + __iscsi_get_ctask(conn->ctask); spin_unlock_bh(&conn->session->lock); rc = tt->xmit_cmd_task(conn, conn->ctask); if (rc) goto again; + spin_lock_bh(&conn->session->lock); + __iscsi_put_ctask(conn->ctask); + if (rc) { + spin_unlock_bh(&conn->session->lock); + goto again; + } } spin_unlock_bh(&conn->session->lock); /* done with this ctask */ @@ -659,6 +696,7 @@ enum { FAILURE_SESSION_FAILED, FAILURE_SESSION_FREED, FAILURE_WINDOW_CLOSED, + FAILURE_OOM, FAILURE_SESSION_TERMINATE, FAILURE_SESSION_IN_RECOVERY, FAILURE_SESSION_RECOVERY_TIMEOUT, @@ -717,10 +755,15 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) conn = session->leadconn; - __kfifo_get(session->cmdpool.queue, (void*)&ctask, sizeof(void*)); + if (!__kfifo_get(session->cmdpool.queue, (void*)&ctask, + sizeof(void*))) { + reason = FAILURE_OOM; + goto reject; + } sc->SCp.phase = session->age; sc->SCp.ptr = (char *)ctask; + atomic_set(&ctask->refcount, 1); ctask->state = ISCSI_TASK_PENDING; ctask->mtask = NULL; ctask->conn = conn; @@ -1057,13 +1100,11 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, sc = ctask->sc; if (!sc) return; - - conn->session->tt->cleanup_cmd_task(conn, ctask); iscsi_ctask_mtask_cleanup(ctask); sc->result = err; sc->resid = sc->request_bufflen; - iscsi_complete_command(conn->session, ctask); + __iscsi_put_ctask(ctask); } int iscsi_eh_abort(struct scsi_cmnd *sc) -- cgit v1.2.3 From 98a9416af08385f8497e9c1595113a81aefa5d49 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Thu, 31 Aug 2006 18:09:26 -0400 Subject: [SCSI] attempt to complete r2t with data len greater than max burst A couple targets like string bean and MDS, send r2ts with a data len greater than the max burst we agreed to. We were being strict in our enforcing of the iscsi rfc in that code path, but there is no driver limitation that prevents us from fullfilling the request. To allow those targets to work we will ignore the max_burst length and send as much data as the target asks for assuming it has consciously decided to override its max burst length. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/iscsi_tcp.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index a97a3a4e99e..d6927f1a6b6 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -358,8 +358,11 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) int r2tsn = be32_to_cpu(rhdr->r2tsn); int rc; - if (tcp_conn->in.datalen) + if (tcp_conn->in.datalen) { + printk(KERN_ERR "iscsi_tcp: invalid R2t with datalen %d\n", + tcp_conn->in.datalen); return ISCSI_ERR_DATALEN; + } if (tcp_ctask->exp_r2tsn && tcp_ctask->exp_r2tsn != r2tsn) return ISCSI_ERR_R2TSN; @@ -385,15 +388,23 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) r2t->exp_statsn = rhdr->statsn; r2t->data_length = be32_to_cpu(rhdr->data_length); - if (r2t->data_length == 0 || - r2t->data_length > session->max_burst) { + if (r2t->data_length == 0) { + printk(KERN_ERR "iscsi_tcp: invalid R2T with zero data len\n"); spin_unlock(&session->lock); return ISCSI_ERR_DATALEN; } + if (r2t->data_length > session->max_burst) + debug_scsi("invalid R2T with data len %u and max burst %u." + "Attempting to execute request.\n", + r2t->data_length, session->max_burst); + r2t->data_offset = be32_to_cpu(rhdr->data_offset); if (r2t->data_offset + r2t->data_length > ctask->total_length) { spin_unlock(&session->lock); + printk(KERN_ERR "iscsi_tcp: invalid R2T with data len %u at " + "offset %u and total length %d\n", r2t->data_length, + r2t->data_offset, ctask->total_length); return ISCSI_ERR_DATALEN; } -- cgit v1.2.3 From 62f383003c22cd34920d0412465eddcb1223da0d Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Thu, 31 Aug 2006 18:09:27 -0400 Subject: [SCSI] iscsi_tcp: fix padding, data digests, and IO at weird offsets iscsi_tcp calculates padding by using the expected transfer length. This has the problem where if we have immediate data = no and initial R2T = yes, and the transfer length ended up needing padding then we send: 1. header 2. padding which should have gone after data 3. data Besides this bug, we also assume the target will always ask for nice transfer lengths and the first burst length will always be a nice value. As far as I can tell form the RFC this is not a requirement. It would be silly to do this, but if someone did it we will end doing bad things. Finally the last bug in that bit of code is in our handling of the recalculation of data digests when we do not send a whole iscsi_buf in one try. The bug here is that we call crypto_digest_final on a iscsi_sendpage error, then when we send the rest of the iscsi_buf, we doiscsi_data_digest_init and this causes the previous data digest to be lost. And to make matters worse, some of these bugs are replicated over and over and over again for immediate data, solicited data and unsolicited data. So the attached patch made over the iscsi git tree (see kernel.org/git for details) which I updated today to include the patches I said I merged, consolidates the sending of data, padding and digests and calculation of data digests and fixes the above bugs. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/iscsi_tcp.c | 636 ++++++++++++++++++----------------------------- drivers/scsi/iscsi_tcp.h | 33 ++- drivers/scsi/libiscsi.c | 37 ++- 3 files changed, 286 insertions(+), 420 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index d6927f1a6b6..290c1d76cd4 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -281,7 +281,6 @@ iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, { struct iscsi_data *hdr; struct scsi_cmnd *sc = ctask->sc; - struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; hdr = &r2t->dtask.hdr; memset(hdr, 0, sizeof(struct iscsi_data)); @@ -336,10 +335,12 @@ iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, sg_count += sg->length; } BUG_ON(r2t->sg == NULL); - } else - iscsi_buf_init_iov(&tcp_ctask->sendbuf, + } else { + iscsi_buf_init_iov(&r2t->sendbuf, (char*)sc->request_buffer + r2t->data_offset, r2t->data_count); + r2t->sg = NULL; + } } /** @@ -503,7 +504,6 @@ iscsi_tcp_hdr_recv(struct iscsi_conn *conn) goto copy_hdr; spin_lock(&session->lock); - iscsi_tcp_cleanup_ctask(conn, tcp_conn->in.ctask); rc = __iscsi_complete_pdu(conn, hdr, NULL, 0); spin_unlock(&session->lock); break; @@ -676,15 +676,15 @@ iscsi_tcp_copy(struct iscsi_conn *conn) } static inline void -partial_sg_digest_update(struct iscsi_tcp_conn *tcp_conn, - struct scatterlist *sg, int offset, int length) +partial_sg_digest_update(struct crypto_tfm *tfm, struct scatterlist *sg, + int offset, int length) { struct scatterlist temp; memcpy(&temp, sg, sizeof(struct scatterlist)); temp.offset = offset; temp.length = length; - crypto_digest_update(tcp_conn->data_rx_tfm, &temp, 1); + crypto_digest_update(tfm, &temp, 1); } static void @@ -751,7 +751,8 @@ static int iscsi_scsi_data_in(struct iscsi_conn *conn) tcp_conn->data_rx_tfm, &sg[i], 1); else - partial_sg_digest_update(tcp_conn, + partial_sg_digest_update( + tcp_conn->data_rx_tfm, &sg[i], sg[i].offset + offset, sg[i].length - offset); @@ -765,7 +766,8 @@ static int iscsi_scsi_data_in(struct iscsi_conn *conn) /* * data-in is complete, but buffer not... */ - partial_sg_digest_update(tcp_conn, &sg[i], + partial_sg_digest_update(tcp_conn->data_rx_tfm, + &sg[i], sg[i].offset, sg[i].length-rc); rc = 0; break; @@ -783,7 +785,6 @@ done: (long)sc, sc->result, ctask->itt, tcp_conn->in.hdr->flags); spin_lock(&conn->session->lock); - iscsi_tcp_cleanup_ctask(conn, ctask); __iscsi_complete_pdu(conn, tcp_conn->in.hdr, NULL, 0); spin_unlock(&conn->session->lock); } @@ -803,9 +804,6 @@ iscsi_data_recv(struct iscsi_conn *conn) rc = iscsi_scsi_data_in(conn); break; case ISCSI_OP_SCSI_CMD_RSP: - spin_lock(&conn->session->lock); - iscsi_tcp_cleanup_ctask(conn, tcp_conn->in.ctask); - spin_unlock(&conn->session->lock); case ISCSI_OP_TEXT_RSP: case ISCSI_OP_LOGIN_RSP: case ISCSI_OP_ASYNC_EVENT: @@ -1188,37 +1186,12 @@ iscsi_sendpage(struct iscsi_conn *conn, struct iscsi_buf *buf, static inline void iscsi_data_digest_init(struct iscsi_tcp_conn *tcp_conn, - struct iscsi_cmd_task *ctask) + struct iscsi_tcp_cmd_task *tcp_ctask) { - struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; - - BUG_ON(!tcp_conn->data_tx_tfm); crypto_digest_init(tcp_conn->data_tx_tfm); tcp_ctask->digest_count = 4; } -static int -iscsi_digest_final_send(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, - struct iscsi_buf *buf, uint32_t *digest, int final) -{ - struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; - struct iscsi_tcp_conn *tcp_conn = conn->dd_data; - int rc = 0; - int sent = 0; - - if (final) - crypto_digest_final(tcp_conn->data_tx_tfm, (u8*)digest); - - iscsi_buf_init_iov(buf, (char*)digest, 4); - rc = iscsi_sendpage(conn, buf, &tcp_ctask->digest_count, &sent); - if (rc) { - tcp_ctask->datadigest = *digest; - tcp_ctask->xmstate |= XMSTATE_DATA_DIGEST; - } else - tcp_ctask->digest_count = 4; - return rc; -} - /** * iscsi_solicit_data_cont - initialize next Data-Out * @conn: iscsi connection @@ -1236,7 +1209,6 @@ static void iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, struct iscsi_r2t_info *r2t, int left) { - struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; struct iscsi_data *hdr; struct scsi_cmnd *sc = ctask->sc; int new_offset; @@ -1265,14 +1237,30 @@ iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, iscsi_buf_init_iov(&r2t->headbuf, (char*)hdr, sizeof(struct iscsi_hdr)); - if (sc->use_sg && !iscsi_buf_left(&r2t->sendbuf)) { - BUG_ON(tcp_ctask->bad_sg == r2t->sg); + if (iscsi_buf_left(&r2t->sendbuf)) + return; + + if (sc->use_sg) { iscsi_buf_init_sg(&r2t->sendbuf, r2t->sg); r2t->sg += 1; - } else - iscsi_buf_init_iov(&tcp_ctask->sendbuf, + } else { + iscsi_buf_init_iov(&r2t->sendbuf, (char*)sc->request_buffer + new_offset, r2t->data_count); + r2t->sg = NULL; + } +} + +static void iscsi_set_padding(struct iscsi_tcp_cmd_task *tcp_ctask, + unsigned long len) +{ + tcp_ctask->pad_count = len & (ISCSI_PAD_LEN - 1); + if (!tcp_ctask->pad_count) + return; + + tcp_ctask->pad_count = ISCSI_PAD_LEN - tcp_ctask->pad_count; + debug_scsi("write padding %d bytes\n", tcp_ctask->pad_count); + tcp_ctask->xmstate |= XMSTATE_W_PAD; } /** @@ -1300,31 +1288,16 @@ iscsi_tcp_cmd_init(struct iscsi_cmd_task *ctask) if (sc->use_sg) { struct scatterlist *sg = sc->request_buffer; - iscsi_buf_init_sg(&tcp_ctask->sendbuf, - &sg[tcp_ctask->sg_count++]); - tcp_ctask->sg = sg; + iscsi_buf_init_sg(&tcp_ctask->sendbuf, sg); + tcp_ctask->sg = sg + 1; tcp_ctask->bad_sg = sg + sc->use_sg; - } else + } else { iscsi_buf_init_iov(&tcp_ctask->sendbuf, sc->request_buffer, sc->request_bufflen); - - if (ctask->imm_count) - tcp_ctask->xmstate |= XMSTATE_IMM_DATA; - - tcp_ctask->pad_count = ctask->total_length & (ISCSI_PAD_LEN-1); - if (tcp_ctask->pad_count) { - tcp_ctask->pad_count = ISCSI_PAD_LEN - - tcp_ctask->pad_count; - debug_scsi("write padding %d bytes\n", - tcp_ctask->pad_count); - tcp_ctask->xmstate |= XMSTATE_W_PAD; + tcp_ctask->sg = NULL; + tcp_ctask->bad_sg = NULL; } - - if (ctask->unsol_count) - tcp_ctask->xmstate |= XMSTATE_UNS_HDR | - XMSTATE_UNS_INIT; - debug_scsi("cmd [itt 0x%x total %d imm_data %d " "unsol count %d, unsol offset %d]\n", ctask->itt, ctask->total_length, ctask->imm_count, @@ -1410,8 +1383,8 @@ iscsi_tcp_mtask_xmit(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask) } static inline int -handle_xmstate_r_hdr(struct iscsi_conn *conn, - struct iscsi_tcp_cmd_task *tcp_ctask) +iscsi_send_read_hdr(struct iscsi_conn *conn, + struct iscsi_tcp_cmd_task *tcp_ctask) { int rc; @@ -1429,7 +1402,7 @@ handle_xmstate_r_hdr(struct iscsi_conn *conn, } static inline int -handle_xmstate_w_hdr(struct iscsi_conn *conn, +iscsi_send_write_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) { struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; @@ -1440,85 +1413,125 @@ handle_xmstate_w_hdr(struct iscsi_conn *conn, iscsi_hdr_digest(conn, &tcp_ctask->headbuf, (u8*)tcp_ctask->hdrext); rc = iscsi_sendhdr(conn, &tcp_ctask->headbuf, ctask->imm_count); - if (rc) + if (rc) { tcp_ctask->xmstate |= XMSTATE_W_HDR; - return rc; + return rc; + } + + if (ctask->imm_count) { + tcp_ctask->xmstate |= XMSTATE_IMM_DATA; + iscsi_set_padding(tcp_ctask, ctask->imm_count); + + if (ctask->conn->datadgst_en) { + iscsi_data_digest_init(ctask->conn->dd_data, tcp_ctask); + tcp_ctask->immdigest = 0; + } + } + + if (ctask->unsol_count) + tcp_ctask->xmstate |= XMSTATE_UNS_HDR | XMSTATE_UNS_INIT; + return 0; } -static inline int -handle_xmstate_data_digest(struct iscsi_conn *conn, - struct iscsi_cmd_task *ctask) +static int +iscsi_send_padding(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) { struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; - int rc; + struct iscsi_tcp_conn *tcp_conn = conn->dd_data; + int sent = 0, rc; - tcp_ctask->xmstate &= ~XMSTATE_DATA_DIGEST; - debug_tcp("resent data digest 0x%x\n", tcp_ctask->datadigest); - rc = iscsi_digest_final_send(conn, ctask, &tcp_ctask->immbuf, - &tcp_ctask->datadigest, 0); + if (tcp_ctask->xmstate & XMSTATE_W_PAD) { + iscsi_buf_init_iov(&tcp_ctask->sendbuf, (char*)&tcp_ctask->pad, + tcp_ctask->pad_count); + if (conn->datadgst_en) + crypto_digest_update(tcp_conn->data_tx_tfm, + &tcp_ctask->sendbuf.sg, 1); + } else if (!(tcp_ctask->xmstate & XMSTATE_W_RESEND_PAD)) + return 0; + + tcp_ctask->xmstate &= ~XMSTATE_W_PAD; + tcp_ctask->xmstate &= ~XMSTATE_W_RESEND_PAD; + debug_scsi("sending %d pad bytes for itt 0x%x\n", + tcp_ctask->pad_count, ctask->itt); + rc = iscsi_sendpage(conn, &tcp_ctask->sendbuf, &tcp_ctask->pad_count, + &sent); if (rc) { - tcp_ctask->xmstate |= XMSTATE_DATA_DIGEST; - debug_tcp("resent data digest 0x%x fail!\n", - tcp_ctask->datadigest); + debug_scsi("padding send failed %d\n", rc); + tcp_ctask->xmstate |= XMSTATE_W_RESEND_PAD; } - return rc; } -static inline int -handle_xmstate_imm_data(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) +static int +iscsi_send_digest(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, + struct iscsi_buf *buf, uint32_t *digest) { - struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; - struct iscsi_tcp_conn *tcp_conn = conn->dd_data; - int rc; + struct iscsi_tcp_cmd_task *tcp_ctask; + struct iscsi_tcp_conn *tcp_conn; + int rc, sent = 0; - BUG_ON(!ctask->imm_count); - tcp_ctask->xmstate &= ~XMSTATE_IMM_DATA; + if (!conn->datadgst_en) + return 0; - if (conn->datadgst_en) { - iscsi_data_digest_init(tcp_conn, ctask); - tcp_ctask->immdigest = 0; - } + tcp_ctask = ctask->dd_data; + tcp_conn = conn->dd_data; - for (;;) { - rc = iscsi_sendpage(conn, &tcp_ctask->sendbuf, - &ctask->imm_count, &tcp_ctask->sent); - if (rc) { - tcp_ctask->xmstate |= XMSTATE_IMM_DATA; - if (conn->datadgst_en) { - crypto_digest_final(tcp_conn->data_tx_tfm, - (u8*)&tcp_ctask->immdigest); - debug_tcp("tx imm sendpage fail 0x%x\n", - tcp_ctask->datadigest); - } - return rc; - } - if (conn->datadgst_en) - crypto_digest_update(tcp_conn->data_tx_tfm, - &tcp_ctask->sendbuf.sg, 1); + if (!(tcp_ctask->xmstate & XMSTATE_W_RESEND_DATA_DIGEST)) { + crypto_digest_final(tcp_conn->data_tx_tfm, (u8*)digest); + iscsi_buf_init_iov(buf, (char*)digest, 4); + } + tcp_ctask->xmstate &= ~XMSTATE_W_RESEND_DATA_DIGEST; - if (!ctask->imm_count) - break; - iscsi_buf_init_sg(&tcp_ctask->sendbuf, - &tcp_ctask->sg[tcp_ctask->sg_count++]); + rc = iscsi_sendpage(conn, buf, &tcp_ctask->digest_count, &sent); + if (!rc) + debug_scsi("sent digest 0x%x for itt 0x%x\n", *digest, + ctask->itt); + else { + debug_scsi("sending digest 0x%x failed for itt 0x%x!\n", + *digest, ctask->itt); + tcp_ctask->xmstate |= XMSTATE_W_RESEND_DATA_DIGEST; } + return rc; +} - if (conn->datadgst_en && !(tcp_ctask->xmstate & XMSTATE_W_PAD)) { - rc = iscsi_digest_final_send(conn, ctask, &tcp_ctask->immbuf, - &tcp_ctask->immdigest, 1); - if (rc) { - debug_tcp("sending imm digest 0x%x fail!\n", - tcp_ctask->immdigest); - return rc; +static int +iscsi_send_data(struct iscsi_cmd_task *ctask, struct iscsi_buf *sendbuf, + struct scatterlist **sg, int *sent, int *count, + struct iscsi_buf *digestbuf, uint32_t *digest) +{ + struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; + struct iscsi_conn *conn = ctask->conn; + struct iscsi_tcp_conn *tcp_conn = conn->dd_data; + int rc, buf_sent, offset; + + while (*count) { + buf_sent = 0; + offset = sendbuf->sent; + + rc = iscsi_sendpage(conn, sendbuf, count, &buf_sent); + *sent = *sent + buf_sent; + if (buf_sent && conn->datadgst_en) + partial_sg_digest_update(tcp_conn->data_tx_tfm, + &sendbuf->sg, sendbuf->sg.offset + offset, + buf_sent); + if (!iscsi_buf_left(sendbuf) && *sg != tcp_ctask->bad_sg) { + iscsi_buf_init_sg(sendbuf, *sg); + *sg = *sg + 1; } - debug_tcp("sending imm digest 0x%x\n", tcp_ctask->immdigest); + + if (rc) + return rc; } - return 0; + rc = iscsi_send_padding(conn, ctask); + if (rc) + return rc; + + return iscsi_send_digest(conn, ctask, digestbuf, digest); } -static inline int -handle_xmstate_uns_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) +static int +iscsi_send_unsol_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) { struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; struct iscsi_data_task *dtask; @@ -1526,14 +1539,21 @@ handle_xmstate_uns_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) tcp_ctask->xmstate |= XMSTATE_UNS_DATA; if (tcp_ctask->xmstate & XMSTATE_UNS_INIT) { - dtask = tcp_ctask->dtask = &tcp_ctask->unsol_dtask; + dtask = &tcp_ctask->unsol_dtask; + iscsi_prep_unsolicit_data_pdu(ctask, &dtask->hdr); iscsi_buf_init_iov(&tcp_ctask->headbuf, (char*)&dtask->hdr, sizeof(struct iscsi_hdr)); if (conn->hdrdgst_en) iscsi_hdr_digest(conn, &tcp_ctask->headbuf, (u8*)dtask->hdrext); + if (conn->datadgst_en) { + iscsi_data_digest_init(ctask->conn->dd_data, tcp_ctask); + dtask->digest = 0; + } + tcp_ctask->xmstate &= ~XMSTATE_UNS_INIT; + iscsi_set_padding(tcp_ctask, ctask->data_count); } rc = iscsi_sendhdr(conn, &tcp_ctask->headbuf, ctask->data_count); @@ -1548,247 +1568,128 @@ handle_xmstate_uns_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) return 0; } -static inline int -handle_xmstate_uns_data(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) +static int +iscsi_send_unsol_pdu(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) { struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; - struct iscsi_data_task *dtask = tcp_ctask->dtask; - struct iscsi_tcp_conn *tcp_conn = conn->dd_data; int rc; - BUG_ON(!ctask->data_count); - tcp_ctask->xmstate &= ~XMSTATE_UNS_DATA; - - if (conn->datadgst_en) { - iscsi_data_digest_init(tcp_conn, ctask); - dtask->digest = 0; + if (tcp_ctask->xmstate & XMSTATE_UNS_HDR) { + BUG_ON(!ctask->unsol_count); + tcp_ctask->xmstate &= ~XMSTATE_UNS_HDR; +send_hdr: + rc = iscsi_send_unsol_hdr(conn, ctask); + if (rc) + return rc; } - for (;;) { + if (tcp_ctask->xmstate & XMSTATE_UNS_DATA) { + struct iscsi_data_task *dtask = &tcp_ctask->unsol_dtask; int start = tcp_ctask->sent; - rc = iscsi_sendpage(conn, &tcp_ctask->sendbuf, - &ctask->data_count, &tcp_ctask->sent); - if (rc) { - ctask->unsol_count -= tcp_ctask->sent - start; - tcp_ctask->xmstate |= XMSTATE_UNS_DATA; - /* will continue with this ctask later.. */ - if (conn->datadgst_en) { - crypto_digest_final(tcp_conn->data_tx_tfm, - (u8 *)&dtask->digest); - debug_tcp("tx uns data fail 0x%x\n", - dtask->digest); - } - return rc; - } - - BUG_ON(tcp_ctask->sent > ctask->total_length); + rc = iscsi_send_data(ctask, &tcp_ctask->sendbuf, &tcp_ctask->sg, + &tcp_ctask->sent, &ctask->data_count, + &dtask->digestbuf, &dtask->digest); ctask->unsol_count -= tcp_ctask->sent - start; - + if (rc) + return rc; + tcp_ctask->xmstate &= ~XMSTATE_UNS_DATA; /* - * XXX:we may run here with un-initial sendbuf. - * so pass it + * Done with the Data-Out. Next, check if we need + * to send another unsolicited Data-Out. */ - if (conn->datadgst_en && tcp_ctask->sent - start > 0) - crypto_digest_update(tcp_conn->data_tx_tfm, - &tcp_ctask->sendbuf.sg, 1); - - if (!ctask->data_count) - break; - iscsi_buf_init_sg(&tcp_ctask->sendbuf, - &tcp_ctask->sg[tcp_ctask->sg_count++]); - } - BUG_ON(ctask->unsol_count < 0); - - /* - * Done with the Data-Out. Next, check if we need - * to send another unsolicited Data-Out. - */ - if (ctask->unsol_count) { - if (conn->datadgst_en) { - rc = iscsi_digest_final_send(conn, ctask, - &dtask->digestbuf, - &dtask->digest, 1); - if (rc) { - debug_tcp("send uns digest 0x%x fail\n", - dtask->digest); - return rc; - } - debug_tcp("sending uns digest 0x%x, more uns\n", - dtask->digest); - } - tcp_ctask->xmstate |= XMSTATE_UNS_INIT; - return 1; - } - - if (conn->datadgst_en && !(tcp_ctask->xmstate & XMSTATE_W_PAD)) { - rc = iscsi_digest_final_send(conn, ctask, - &dtask->digestbuf, - &dtask->digest, 1); - if (rc) { - debug_tcp("send last uns digest 0x%x fail\n", - dtask->digest); - return rc; + if (ctask->unsol_count) { + debug_scsi("sending more uns\n"); + tcp_ctask->xmstate |= XMSTATE_UNS_INIT; + goto send_hdr; } - debug_tcp("sending uns digest 0x%x\n",dtask->digest); } - return 0; } -static inline int -handle_xmstate_sol_data(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) +static int iscsi_send_sol_pdu(struct iscsi_conn *conn, + struct iscsi_cmd_task *ctask) { - struct iscsi_session *session = conn->session; - struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; - struct iscsi_r2t_info *r2t = tcp_ctask->r2t; - struct iscsi_data_task *dtask = &r2t->dtask; + struct iscsi_session *session = conn->session; + struct iscsi_r2t_info *r2t; + struct iscsi_data_task *dtask; int left, rc; - tcp_ctask->xmstate &= ~XMSTATE_SOL_DATA; - tcp_ctask->dtask = dtask; - - if (conn->datadgst_en) { - iscsi_data_digest_init(tcp_conn, ctask); - dtask->digest = 0; - } -solicit_again: - /* - * send Data-Out within this R2T sequence. - */ - if (!r2t->data_count) - goto data_out_done; - - rc = iscsi_sendpage(conn, &r2t->sendbuf, &r2t->data_count, &r2t->sent); - if (rc) { + if (tcp_ctask->xmstate & XMSTATE_SOL_HDR) { + tcp_ctask->xmstate &= ~XMSTATE_SOL_HDR; tcp_ctask->xmstate |= XMSTATE_SOL_DATA; - /* will continue with this ctask later.. */ - if (conn->datadgst_en) { - crypto_digest_final(tcp_conn->data_tx_tfm, - (u8 *)&dtask->digest); - debug_tcp("r2t data send fail 0x%x\n", dtask->digest); - } - return rc; - } + if (!tcp_ctask->r2t) + __kfifo_get(tcp_ctask->r2tqueue, (void*)&tcp_ctask->r2t, + sizeof(void*)); +send_hdr: + r2t = tcp_ctask->r2t; + dtask = &r2t->dtask; - BUG_ON(r2t->data_count < 0); - if (conn->datadgst_en) - crypto_digest_update(tcp_conn->data_tx_tfm, &r2t->sendbuf.sg, - 1); - - if (r2t->data_count) { - BUG_ON(ctask->sc->use_sg == 0); - if (!iscsi_buf_left(&r2t->sendbuf)) { - BUG_ON(tcp_ctask->bad_sg == r2t->sg); - iscsi_buf_init_sg(&r2t->sendbuf, r2t->sg); - r2t->sg += 1; - } - goto solicit_again; - } + if (conn->hdrdgst_en) + iscsi_hdr_digest(conn, &r2t->headbuf, + (u8*)dtask->hdrext); -data_out_done: - /* - * Done with this Data-Out. Next, check if we have - * to send another Data-Out for this R2T. - */ - BUG_ON(r2t->data_length - r2t->sent < 0); - left = r2t->data_length - r2t->sent; - if (left) { if (conn->datadgst_en) { - rc = iscsi_digest_final_send(conn, ctask, - &dtask->digestbuf, - &dtask->digest, 1); - if (rc) { - debug_tcp("send r2t data digest 0x%x" - "fail\n", dtask->digest); - return rc; - } - debug_tcp("r2t data send digest 0x%x\n", - dtask->digest); + iscsi_data_digest_init(conn->dd_data, tcp_ctask); + dtask->digest = 0; } - iscsi_solicit_data_cont(conn, ctask, r2t, left); - tcp_ctask->xmstate |= XMSTATE_SOL_DATA; - tcp_ctask->xmstate &= ~XMSTATE_SOL_HDR; - return 1; - } - /* - * Done with this R2T. Check if there are more - * outstanding R2Ts ready to be processed. - */ - if (conn->datadgst_en) { - rc = iscsi_digest_final_send(conn, ctask, &dtask->digestbuf, - &dtask->digest, 1); + rc = iscsi_sendhdr(conn, &r2t->headbuf, r2t->data_count); if (rc) { - debug_tcp("send last r2t data digest 0x%x" - "fail\n", dtask->digest); + tcp_ctask->xmstate &= ~XMSTATE_SOL_DATA; + tcp_ctask->xmstate |= XMSTATE_SOL_HDR; return rc; } - debug_tcp("r2t done dout digest 0x%x\n", dtask->digest); - } - tcp_ctask->r2t = NULL; - spin_lock_bh(&session->lock); - __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t, sizeof(void*)); - spin_unlock_bh(&session->lock); - if (__kfifo_get(tcp_ctask->r2tqueue, (void*)&r2t, sizeof(void*))) { - tcp_ctask->r2t = r2t; - tcp_ctask->xmstate |= XMSTATE_SOL_DATA; - tcp_ctask->xmstate &= ~XMSTATE_SOL_HDR; - return 1; + iscsi_set_padding(tcp_ctask, r2t->data_count); + debug_scsi("sol dout [dsn %d itt 0x%x dlen %d sent %d]\n", + r2t->solicit_datasn - 1, ctask->itt, r2t->data_count, + r2t->sent); } - return 0; -} + if (tcp_ctask->xmstate & XMSTATE_SOL_DATA) { + r2t = tcp_ctask->r2t; + dtask = &r2t->dtask; -static inline int -handle_xmstate_w_pad(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) -{ - struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; - struct iscsi_tcp_conn *tcp_conn = conn->dd_data; - struct iscsi_data_task *dtask = tcp_ctask->dtask; - int sent = 0, rc; + rc = iscsi_send_data(ctask, &r2t->sendbuf, &r2t->sg, + &r2t->sent, &r2t->data_count, + &dtask->digestbuf, &dtask->digest); + if (rc) + return rc; + tcp_ctask->xmstate &= ~XMSTATE_SOL_DATA; - tcp_ctask->xmstate &= ~XMSTATE_W_PAD; - iscsi_buf_init_iov(&tcp_ctask->sendbuf, (char*)&tcp_ctask->pad, - tcp_ctask->pad_count); - rc = iscsi_sendpage(conn, &tcp_ctask->sendbuf, &tcp_ctask->pad_count, - &sent); - if (rc) { - tcp_ctask->xmstate |= XMSTATE_W_PAD; - return rc; - } + /* + * Done with this Data-Out. Next, check if we have + * to send another Data-Out for this R2T. + */ + BUG_ON(r2t->data_length - r2t->sent < 0); + left = r2t->data_length - r2t->sent; + if (left) { + iscsi_solicit_data_cont(conn, ctask, r2t, left); + tcp_ctask->xmstate |= XMSTATE_SOL_DATA; + tcp_ctask->xmstate &= ~XMSTATE_SOL_HDR; + goto send_hdr; + } - if (conn->datadgst_en) { - crypto_digest_update(tcp_conn->data_tx_tfm, - &tcp_ctask->sendbuf.sg, 1); - /* imm data? */ - if (!dtask) { - rc = iscsi_digest_final_send(conn, ctask, - &tcp_ctask->immbuf, - &tcp_ctask->immdigest, 1); - if (rc) { - debug_tcp("send padding digest 0x%x" - "fail!\n", tcp_ctask->immdigest); - return rc; - } - debug_tcp("done with padding, digest 0x%x\n", - tcp_ctask->datadigest); - } else { - rc = iscsi_digest_final_send(conn, ctask, - &dtask->digestbuf, - &dtask->digest, 1); - if (rc) { - debug_tcp("send padding digest 0x%x" - "fail\n", dtask->digest); - return rc; - } - debug_tcp("done with padding, digest 0x%x\n", - dtask->digest); + /* + * Done with this R2T. Check if there are more + * outstanding R2Ts ready to be processed. + */ + spin_lock_bh(&session->lock); + tcp_ctask->r2t = NULL; + __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t, + sizeof(void*)); + if (__kfifo_get(tcp_ctask->r2tqueue, (void*)&r2t, + sizeof(void*))) { + tcp_ctask->r2t = r2t; + tcp_ctask->xmstate |= XMSTATE_SOL_DATA; + tcp_ctask->xmstate &= ~XMSTATE_SOL_HDR; + spin_unlock_bh(&session->lock); + goto send_hdr; } + spin_unlock_bh(&session->lock); } - return 0; } @@ -1808,85 +1709,30 @@ iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) return rc; if (tcp_ctask->xmstate & XMSTATE_R_HDR) - return handle_xmstate_r_hdr(conn, tcp_ctask); + return iscsi_send_read_hdr(conn, tcp_ctask); if (tcp_ctask->xmstate & XMSTATE_W_HDR) { - rc = handle_xmstate_w_hdr(conn, ctask); - if (rc) - return rc; - } - - /* XXX: for data digest xmit recover */ - if (tcp_ctask->xmstate & XMSTATE_DATA_DIGEST) { - rc = handle_xmstate_data_digest(conn, ctask); + rc = iscsi_send_write_hdr(conn, ctask); if (rc) return rc; } if (tcp_ctask->xmstate & XMSTATE_IMM_DATA) { - rc = handle_xmstate_imm_data(conn, ctask); + rc = iscsi_send_data(ctask, &tcp_ctask->sendbuf, &tcp_ctask->sg, + &tcp_ctask->sent, &ctask->imm_count, + &tcp_ctask->immbuf, &tcp_ctask->immdigest); if (rc) return rc; + tcp_ctask->xmstate &= ~XMSTATE_IMM_DATA; } - if (tcp_ctask->xmstate & XMSTATE_UNS_HDR) { - BUG_ON(!ctask->unsol_count); - tcp_ctask->xmstate &= ~XMSTATE_UNS_HDR; -unsolicit_head_again: - rc = handle_xmstate_uns_hdr(conn, ctask); - if (rc) - return rc; - } - - if (tcp_ctask->xmstate & XMSTATE_UNS_DATA) { - rc = handle_xmstate_uns_data(conn, ctask); - if (rc == 1) - goto unsolicit_head_again; - else if (rc) - return rc; - goto done; - } - - if (tcp_ctask->xmstate & XMSTATE_SOL_HDR) { - struct iscsi_r2t_info *r2t; - - tcp_ctask->xmstate &= ~XMSTATE_SOL_HDR; - tcp_ctask->xmstate |= XMSTATE_SOL_DATA; - if (!tcp_ctask->r2t) - __kfifo_get(tcp_ctask->r2tqueue, (void*)&tcp_ctask->r2t, - sizeof(void*)); -solicit_head_again: - r2t = tcp_ctask->r2t; - if (conn->hdrdgst_en) - iscsi_hdr_digest(conn, &r2t->headbuf, - (u8*)r2t->dtask.hdrext); - rc = iscsi_sendhdr(conn, &r2t->headbuf, r2t->data_count); - if (rc) { - tcp_ctask->xmstate &= ~XMSTATE_SOL_DATA; - tcp_ctask->xmstate |= XMSTATE_SOL_HDR; - return rc; - } - - debug_scsi("sol dout [dsn %d itt 0x%x dlen %d sent %d]\n", - r2t->solicit_datasn - 1, ctask->itt, r2t->data_count, - r2t->sent); - } - - if (tcp_ctask->xmstate & XMSTATE_SOL_DATA) { - rc = handle_xmstate_sol_data(conn, ctask); - if (rc == 1) - goto solicit_head_again; - if (rc) - return rc; - } + rc = iscsi_send_unsol_pdu(conn, ctask); + if (rc) + return rc; -done: - /* - * Last thing to check is whether we need to send write - * padding. Note that we check for xmstate equality, not just the bit. - */ - if (tcp_ctask->xmstate == XMSTATE_W_PAD) - rc = handle_xmstate_w_pad(conn, ctask); + rc = iscsi_send_sol_pdu(conn, ctask); + if (rc) + return rc; return rc; } diff --git a/drivers/scsi/iscsi_tcp.h b/drivers/scsi/iscsi_tcp.h index aace8f70dfd..7e40e94d9fd 100644 --- a/drivers/scsi/iscsi_tcp.h +++ b/drivers/scsi/iscsi_tcp.h @@ -31,23 +31,21 @@ #define IN_PROGRESS_DDIGEST_RECV 0x3 /* xmit state machine */ -#define XMSTATE_IDLE 0x0 -#define XMSTATE_R_HDR 0x1 -#define XMSTATE_W_HDR 0x2 -#define XMSTATE_IMM_HDR 0x4 -#define XMSTATE_IMM_DATA 0x8 -#define XMSTATE_UNS_INIT 0x10 -#define XMSTATE_UNS_HDR 0x20 -#define XMSTATE_UNS_DATA 0x40 -#define XMSTATE_SOL_HDR 0x80 -#define XMSTATE_SOL_DATA 0x100 -#define XMSTATE_W_PAD 0x200 -#define XMSTATE_DATA_DIGEST 0x400 - -#define ISCSI_CONN_RCVBUF_MIN 262144 -#define ISCSI_CONN_SNDBUF_MIN 262144 +#define XMSTATE_IDLE 0x0 +#define XMSTATE_R_HDR 0x1 +#define XMSTATE_W_HDR 0x2 +#define XMSTATE_IMM_HDR 0x4 +#define XMSTATE_IMM_DATA 0x8 +#define XMSTATE_UNS_INIT 0x10 +#define XMSTATE_UNS_HDR 0x20 +#define XMSTATE_UNS_DATA 0x40 +#define XMSTATE_SOL_HDR 0x80 +#define XMSTATE_SOL_DATA 0x100 +#define XMSTATE_W_PAD 0x200 +#define XMSTATE_W_RESEND_PAD 0x400 +#define XMSTATE_W_RESEND_DATA_DIGEST 0x800 + #define ISCSI_PAD_LEN 4 -#define ISCSI_R2T_MAX 16 #define ISCSI_SG_TABLESIZE SG_ALL #define ISCSI_TCP_MAX_CMD_LEN 16 @@ -162,13 +160,10 @@ struct iscsi_tcp_cmd_task { struct iscsi_queue r2tpool; struct kfifo *r2tqueue; struct iscsi_r2t_info **r2ts; - uint32_t datadigest; /* for recover digest */ int digest_count; uint32_t immdigest; /* for imm data */ struct iscsi_buf immbuf; /* for imm data digest */ - struct iscsi_data_task *dtask; /* data task in progress*/ struct iscsi_data_task unsol_dtask; /* unsol data task */ - int digest_offset; /* for partial buff digest */ }; #endif /* ISCSI_H */ diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 9584cbc082f..fb65311c81d 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -325,6 +325,30 @@ static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr) wake_up(&conn->ehwait); } +static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr, + char *data, int datalen) +{ + struct iscsi_reject *reject = (struct iscsi_reject *)hdr; + struct iscsi_hdr rejected_pdu; + uint32_t itt; + + conn->exp_statsn = be32_to_cpu(reject->statsn) + 1; + + if (reject->reason == ISCSI_REASON_DATA_DIGEST_ERROR) { + if (ntoh24(reject->dlength) > datalen) + return ISCSI_ERR_PROTO; + + if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) { + memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr)); + itt = rejected_pdu.itt & ISCSI_ITT_MASK; + printk(KERN_ERR "itt 0x%x had pdu (op 0x%x) rejected " + "due to DataDigest error.\n", itt, + rejected_pdu.opcode); + } + } + return 0; +} + /** * __iscsi_complete_pdu - complete pdu * @conn: iscsi conn @@ -436,6 +460,11 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, break; } } else if (itt == ISCSI_RESERVED_TAG) { + rc = iscsi_check_assign_cmdsn(session, + (struct iscsi_nopin*)hdr); + if (rc) + goto done; + switch(opcode) { case ISCSI_OP_NOOP_IN: if (datalen) { @@ -443,11 +472,6 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, break; } - rc = iscsi_check_assign_cmdsn(session, - (struct iscsi_nopin*)hdr); - if (rc) - break; - if (hdr->ttt == ISCSI_RESERVED_TAG) break; @@ -455,7 +479,8 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, rc = ISCSI_ERR_CONN_FAILED; break; case ISCSI_OP_REJECT: - /* we need sth like iscsi_reject_rsp()*/ + rc = iscsi_handle_reject(conn, hdr, data, datalen); + break; case ISCSI_OP_ASYNC_EVENT: conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; /* we need sth like iscsi_async_event_rsp() */ -- cgit v1.2.3 From dd8c0d958621e3137f3e3302f7b8952041a4a1d7 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Thu, 31 Aug 2006 18:09:28 -0400 Subject: [SCSI] scsi_tcp: rm data rx and tx tfms We currently allocated seperate tfms for data and header digests. There is no reason for this since we can never calculate a rx header and digest at the same time. Same for sends. So this patch removes the data tfms and has the send and recv sides use the rx_tfm or tx_tfm. I also made the connection creation code preallocate the tfms because I thought I hit a bug where I changed the digests settings during a relogin but could not allocate the tfm and then we just failed. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/iscsi_tcp.c | 102 +++++++++++++++++------------------------------ drivers/scsi/iscsi_tcp.h | 8 +--- 2 files changed, 38 insertions(+), 72 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 290c1d76cd4..82399f71028 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -693,7 +693,7 @@ iscsi_recv_digest_update(struct iscsi_tcp_conn *tcp_conn, char* buf, int len) struct scatterlist tmp; sg_init_one(&tmp, buf, len); - crypto_digest_update(tcp_conn->data_rx_tfm, &tmp, 1); + crypto_digest_update(tcp_conn->rx_tfm, &tmp, 1); } static int iscsi_scsi_data_in(struct iscsi_conn *conn) @@ -748,11 +748,11 @@ static int iscsi_scsi_data_in(struct iscsi_conn *conn) if (conn->datadgst_en) { if (!offset) crypto_digest_update( - tcp_conn->data_rx_tfm, + tcp_conn->rx_tfm, &sg[i], 1); else partial_sg_digest_update( - tcp_conn->data_rx_tfm, + tcp_conn->rx_tfm, &sg[i], sg[i].offset + offset, sg[i].length - offset); @@ -766,7 +766,7 @@ static int iscsi_scsi_data_in(struct iscsi_conn *conn) /* * data-in is complete, but buffer not... */ - partial_sg_digest_update(tcp_conn->data_rx_tfm, + partial_sg_digest_update(tcp_conn->rx_tfm, &sg[i], sg[i].offset, sg[i].length-rc); rc = 0; @@ -885,10 +885,8 @@ more: */ rc = iscsi_tcp_hdr_recv(conn); if (!rc && tcp_conn->in.datalen) { - if (conn->datadgst_en) { - BUG_ON(!tcp_conn->data_rx_tfm); - crypto_digest_init(tcp_conn->data_rx_tfm); - } + if (conn->datadgst_en) + crypto_digest_init(tcp_conn->rx_tfm); tcp_conn->in_progress = IN_PROGRESS_DATA_RECV; } else if (rc) { iscsi_conn_failure(conn, rc); @@ -940,10 +938,10 @@ more: tcp_conn->in.padding); memset(pad, 0, tcp_conn->in.padding); sg_init_one(&sg, pad, tcp_conn->in.padding); - crypto_digest_update(tcp_conn->data_rx_tfm, + crypto_digest_update(tcp_conn->rx_tfm, &sg, 1); } - crypto_digest_final(tcp_conn->data_rx_tfm, + crypto_digest_final(tcp_conn->rx_tfm, (u8 *) & tcp_conn->in.datadgst); debug_tcp("rx digest 0x%x\n", tcp_conn->in.datadgst); tcp_conn->in_progress = IN_PROGRESS_DDIGEST_RECV; @@ -1188,7 +1186,7 @@ static inline void iscsi_data_digest_init(struct iscsi_tcp_conn *tcp_conn, struct iscsi_tcp_cmd_task *tcp_ctask) { - crypto_digest_init(tcp_conn->data_tx_tfm); + crypto_digest_init(tcp_conn->tx_tfm); tcp_ctask->digest_count = 4; } @@ -1444,7 +1442,7 @@ iscsi_send_padding(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) iscsi_buf_init_iov(&tcp_ctask->sendbuf, (char*)&tcp_ctask->pad, tcp_ctask->pad_count); if (conn->datadgst_en) - crypto_digest_update(tcp_conn->data_tx_tfm, + crypto_digest_update(tcp_conn->tx_tfm, &tcp_ctask->sendbuf.sg, 1); } else if (!(tcp_ctask->xmstate & XMSTATE_W_RESEND_PAD)) return 0; @@ -1477,7 +1475,7 @@ iscsi_send_digest(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, tcp_conn = conn->dd_data; if (!(tcp_ctask->xmstate & XMSTATE_W_RESEND_DATA_DIGEST)) { - crypto_digest_final(tcp_conn->data_tx_tfm, (u8*)digest); + crypto_digest_final(tcp_conn->tx_tfm, (u8*)digest); iscsi_buf_init_iov(buf, (char*)digest, 4); } tcp_ctask->xmstate &= ~XMSTATE_W_RESEND_DATA_DIGEST; @@ -1511,7 +1509,7 @@ iscsi_send_data(struct iscsi_cmd_task *ctask, struct iscsi_buf *sendbuf, rc = iscsi_sendpage(conn, sendbuf, count, &buf_sent); *sent = *sent + buf_sent; if (buf_sent && conn->datadgst_en) - partial_sg_digest_update(tcp_conn->data_tx_tfm, + partial_sg_digest_update(tcp_conn->tx_tfm, &sendbuf->sg, sendbuf->sg.offset + offset, buf_sent); if (!iscsi_buf_left(sendbuf) && *sg != tcp_ctask->bad_sg) { @@ -1547,10 +1545,6 @@ iscsi_send_unsol_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) if (conn->hdrdgst_en) iscsi_hdr_digest(conn, &tcp_ctask->headbuf, (u8*)dtask->hdrext); - if (conn->datadgst_en) { - iscsi_data_digest_init(ctask->conn->dd_data, tcp_ctask); - dtask->digest = 0; - } tcp_ctask->xmstate &= ~XMSTATE_UNS_INIT; iscsi_set_padding(tcp_ctask, ctask->data_count); @@ -1563,6 +1557,12 @@ iscsi_send_unsol_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) return rc; } + if (conn->datadgst_en) { + dtask = &tcp_ctask->unsol_dtask; + iscsi_data_digest_init(ctask->conn->dd_data, tcp_ctask); + dtask->digest = 0; + } + debug_scsi("uns dout [itt 0x%x dlen %d sent %d]\n", ctask->itt, ctask->unsol_count, tcp_ctask->sent); return 0; @@ -1629,12 +1629,6 @@ send_hdr: if (conn->hdrdgst_en) iscsi_hdr_digest(conn, &r2t->headbuf, (u8*)dtask->hdrext); - - if (conn->datadgst_en) { - iscsi_data_digest_init(conn->dd_data, tcp_ctask); - dtask->digest = 0; - } - rc = iscsi_sendhdr(conn, &r2t->headbuf, r2t->data_count); if (rc) { tcp_ctask->xmstate &= ~XMSTATE_SOL_DATA; @@ -1642,6 +1636,11 @@ send_hdr: return rc; } + if (conn->datadgst_en) { + iscsi_data_digest_init(conn->dd_data, tcp_ctask); + dtask->digest = 0; + } + iscsi_set_padding(tcp_ctask, r2t->data_count); debug_scsi("sol dout [dsn %d itt 0x%x dlen %d sent %d]\n", r2t->solicit_datasn - 1, ctask->itt, r2t->data_count, @@ -1764,8 +1763,20 @@ iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx) /* initial operational parameters */ tcp_conn->hdr_size = sizeof(struct iscsi_hdr); + tcp_conn->tx_tfm = crypto_alloc_tfm("crc32c", 0); + if (!tcp_conn->tx_tfm) + goto free_tcp_conn; + + tcp_conn->rx_tfm = crypto_alloc_tfm("crc32c", 0); + if (!tcp_conn->rx_tfm) + goto free_tx_tfm; + return cls_conn; +free_tx_tfm: + crypto_free_tfm(tcp_conn->tx_tfm); +free_tcp_conn: + kfree(tcp_conn); tcp_conn_alloc_fail: iscsi_conn_teardown(cls_conn); return NULL; @@ -1807,10 +1818,6 @@ iscsi_tcp_conn_destroy(struct iscsi_cls_conn *cls_conn) crypto_free_tfm(tcp_conn->tx_tfm); if (tcp_conn->rx_tfm) crypto_free_tfm(tcp_conn->rx_tfm); - if (tcp_conn->data_tx_tfm) - crypto_free_tfm(tcp_conn->data_tx_tfm); - if (tcp_conn->data_rx_tfm) - crypto_free_tfm(tcp_conn->data_rx_tfm); } kfree(tcp_conn); @@ -1968,48 +1975,11 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, case ISCSI_PARAM_HDRDGST_EN: iscsi_set_param(cls_conn, param, buf, buflen); tcp_conn->hdr_size = sizeof(struct iscsi_hdr); - if (conn->hdrdgst_en) { + if (conn->hdrdgst_en) tcp_conn->hdr_size += sizeof(__u32); - if (!tcp_conn->tx_tfm) - tcp_conn->tx_tfm = crypto_alloc_tfm("crc32c", - 0); - if (!tcp_conn->tx_tfm) - return -ENOMEM; - if (!tcp_conn->rx_tfm) - tcp_conn->rx_tfm = crypto_alloc_tfm("crc32c", - 0); - if (!tcp_conn->rx_tfm) { - crypto_free_tfm(tcp_conn->tx_tfm); - return -ENOMEM; - } - } else { - if (tcp_conn->tx_tfm) - crypto_free_tfm(tcp_conn->tx_tfm); - if (tcp_conn->rx_tfm) - crypto_free_tfm(tcp_conn->rx_tfm); - } break; case ISCSI_PARAM_DATADGST_EN: iscsi_set_param(cls_conn, param, buf, buflen); - if (conn->datadgst_en) { - if (!tcp_conn->data_tx_tfm) - tcp_conn->data_tx_tfm = - crypto_alloc_tfm("crc32c", 0); - if (!tcp_conn->data_tx_tfm) - return -ENOMEM; - if (!tcp_conn->data_rx_tfm) - tcp_conn->data_rx_tfm = - crypto_alloc_tfm("crc32c", 0); - if (!tcp_conn->data_rx_tfm) { - crypto_free_tfm(tcp_conn->data_tx_tfm); - return -ENOMEM; - } - } else { - if (tcp_conn->data_tx_tfm) - crypto_free_tfm(tcp_conn->data_tx_tfm); - if (tcp_conn->data_rx_tfm) - crypto_free_tfm(tcp_conn->data_rx_tfm); - } tcp_conn->sendpage = conn->datadgst_en ? sock_no_sendpage : tcp_conn->sock->ops->sendpage; break; diff --git a/drivers/scsi/iscsi_tcp.h b/drivers/scsi/iscsi_tcp.h index 7e40e94d9fd..609f4778d12 100644 --- a/drivers/scsi/iscsi_tcp.h +++ b/drivers/scsi/iscsi_tcp.h @@ -81,10 +81,6 @@ struct iscsi_tcp_conn { * stop to terminate */ /* iSCSI connection-wide sequencing */ int hdr_size; /* PDU header size */ - - struct crypto_tfm *rx_tfm; /* CRC32C (Rx) */ - struct crypto_tfm *data_rx_tfm; /* CRC32C (Rx) for data */ - /* control data */ struct iscsi_tcp_recv in; /* TCP receive context */ int in_progress; /* connection state machine */ @@ -94,9 +90,9 @@ struct iscsi_tcp_conn { void (*old_state_change)(struct sock *); void (*old_write_space)(struct sock *); - /* xmit */ + /* data and header digests */ struct crypto_tfm *tx_tfm; /* CRC32C (Tx) */ - struct crypto_tfm *data_tx_tfm; /* CRC32C (Tx) for data */ + struct crypto_tfm *rx_tfm; /* CRC32C (Rx) */ /* MIB custom statistics */ uint32_t sendpage_failures_cnt; -- cgit v1.2.3 From 753e7d3866748799e4a8769cd27ea7202654211b Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Thu, 31 Aug 2006 18:09:29 -0400 Subject: [SCSI] iscsi_tcp: fix header resend This patch built over the last ones fixes a bug in the partial header resend code, where we add on another 4 bytes to the send length on the resend. We want just the header plus digest. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/iscsi_tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 82399f71028..541912a5b88 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -109,7 +109,7 @@ iscsi_hdr_digest(struct iscsi_conn *conn, struct iscsi_buf *buf, struct iscsi_tcp_conn *tcp_conn = conn->dd_data; crypto_digest_digest(tcp_conn->tx_tfm, &buf->sg, 1, crc); - buf->sg.length += sizeof(uint32_t); + buf->sg.length = tcp_conn->hdr_size; } static inline int -- cgit v1.2.3 From d5390f5f788f01788e9dfd41ad516a2908901610 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Thu, 31 Aug 2006 18:09:30 -0400 Subject: [SCSI] iscsi_tcp: update header size during relogin When we relogin to a target, we have not yet negotiated digests so we must reset the hdr_size var. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/iscsi_tcp.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 541912a5b88..5d292d0b65e 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -1827,9 +1827,11 @@ static void iscsi_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) { struct iscsi_conn *conn = cls_conn->dd_data; + struct iscsi_tcp_conn *tcp_conn = conn->dd_data; iscsi_conn_stop(cls_conn, flag); iscsi_tcp_release_conn(conn); + tcp_conn->hdr_size = sizeof(struct iscsi_hdr); } static int -- cgit v1.2.3 From db98ccde0881b8247acb52dece6d94ed770a7aa5 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Thu, 31 Aug 2006 18:09:31 -0400 Subject: [SCSI] libiscsi: only check burst lengths when sending unsol data The first burst length is only relevant if immedate data = Yes or if Initial R2T is No Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/libiscsi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index fb65311c81d..864c6284e83 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -1593,7 +1593,8 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn) return -EPERM; } - if (session->first_burst > session->max_burst) { + if ((session->imm_data_en || !session->initial_r2t_en) && + session->first_burst > session->max_burst) { printk("iscsi: invalid burst lengths: " "first_burst %d max_burst %d\n", session->first_burst, session->max_burst); -- cgit v1.2.3 From ca5186842a6d85e982e3d572ecd407453d0c5116 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Thu, 31 Aug 2006 18:09:32 -0400 Subject: [SCSI] iscsi_tcp: fix partial digest recv When a digest is spread across two network buffers, we currently ignore this and try to check the digest with the partial buffer. Or course this fails. This patch has use iscsi_tcp_copy to copy the whole digest before testing it. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/iscsi_tcp.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 5d292d0b65e..d91e8949c71 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -648,10 +648,9 @@ iscsi_ctask_copy(struct iscsi_tcp_conn *tcp_conn, struct iscsi_cmd_task *ctask, * byte counters. **/ static inline int -iscsi_tcp_copy(struct iscsi_conn *conn) +iscsi_tcp_copy(struct iscsi_conn *conn, int buf_size) { struct iscsi_tcp_conn *tcp_conn = conn->dd_data; - int buf_size = tcp_conn->in.datalen; int buf_left = buf_size - tcp_conn->data_copied; int size = min(tcp_conn->in.copy, buf_left); int rc; @@ -812,7 +811,7 @@ iscsi_data_recv(struct iscsi_conn *conn) * Collect data segment to the connection's data * placeholder */ - if (iscsi_tcp_copy(conn)) { + if (iscsi_tcp_copy(conn, tcp_conn->in.datalen)) { rc = -EAGAIN; goto exit; } @@ -899,10 +898,15 @@ more: debug_tcp("extra data_recv offset %d copy %d\n", tcp_conn->in.offset, tcp_conn->in.copy); - skb_copy_bits(tcp_conn->in.skb, tcp_conn->in.offset, - &recv_digest, 4); - tcp_conn->in.offset += 4; - tcp_conn->in.copy -= 4; + rc = iscsi_tcp_copy(conn, sizeof(uint32_t)); + if (rc) { + if (rc == -EAGAIN) + goto again; + iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); + return 0; + } + + memcpy(&recv_digest, conn->data, sizeof(uint32_t)); if (recv_digest != tcp_conn->in.datadgst) { debug_tcp("iscsi_tcp: data digest error!" "0x%x != 0x%x\n", recv_digest, @@ -942,9 +946,10 @@ more: &sg, 1); } crypto_digest_final(tcp_conn->rx_tfm, - (u8 *) & tcp_conn->in.datadgst); + (u8 *) &tcp_conn->in.datadgst); debug_tcp("rx digest 0x%x\n", tcp_conn->in.datadgst); tcp_conn->in_progress = IN_PROGRESS_DDIGEST_RECV; + tcp_conn->data_copied = 0; } else tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; } -- cgit v1.2.3 From f47f2cf5d4acf929a3aaa6957c3fc4622c358703 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Thu, 31 Aug 2006 18:09:33 -0400 Subject: [SCSI] libiscsi: check that command ptr is set before accessing it If the scsi eh sends a TUR and the session is down we could return SCSI_ML_HOST_BUSY. scsi eh will ignore this and send ask us to abort the command and we blindly accesst the command ptr. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/libiscsi.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 864c6284e83..12b5c180074 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -192,6 +192,8 @@ static void iscsi_complete_command(struct iscsi_cmd_task *ctask) ctask->state = ISCSI_TASK_COMPLETED; ctask->sc = NULL; + /* SCSI eh reuses commands to verify us */ + sc->SCp.ptr = NULL; list_del_init(&ctask->running); __kfifo_put(session->cmdpool.queue, (void*)&ctask, sizeof(void*)); sc->scsi_done(sc); @@ -737,6 +739,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) sc->scsi_done = done; sc->result = 0; + sc->SCp.ptr = NULL; host = sc->device->host; session = iscsi_hostdata(host->hostdata); @@ -801,9 +804,10 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) list_add_tail(&ctask->running, &conn->xmitqueue); debug_scsi( - "ctask enq [%s cid %d sc %lx itt 0x%x len %d cmdsn %d win %d]\n", + "ctask enq [%s cid %d sc %p cdb 0x%x itt 0x%x len %d cmdsn %d " + "win %d]\n", sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read", - conn->id, (long)sc, ctask->itt, sc->request_bufflen, + conn->id, sc, sc->cmnd[0], ctask->itt, sc->request_bufflen, session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1); spin_unlock(&session->lock); @@ -1134,11 +1138,24 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, int iscsi_eh_abort(struct scsi_cmnd *sc) { - struct iscsi_cmd_task *ctask = (struct iscsi_cmd_task *)sc->SCp.ptr; - struct iscsi_conn *conn = ctask->conn; - struct iscsi_session *session = conn->session; + struct iscsi_cmd_task *ctask; + struct iscsi_conn *conn; + struct iscsi_session *session; int rc; + /* + * if session was ISCSI_STATE_IN_RECOVERY then we may not have + * got the command. + */ + if (!sc->SCp.ptr) { + debug_scsi("sc never reached iscsi layer or it completed.\n"); + return SUCCESS; + } + + ctask = (struct iscsi_cmd_task *)sc->SCp.ptr; + conn = ctask->conn; + session = conn->session; + conn->eh_abort_cnt++; debug_scsi("aborting [sc %p itt 0x%x]\n", sc, ctask->itt); -- cgit v1.2.3 From e648f63c6520d6e572573149c16a64d2c5ad7ec5 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Thu, 31 Aug 2006 18:09:34 -0400 Subject: [SCSI] libiscsi: don't call into lld to cleanup task In the normal IO path we should not be calling back into the LLD since the LLD will have cleaned up the task before or after calling complete pdu. For the fail_command path we still need to do this to force the cleanup. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/libiscsi.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 12b5c180074..c542d0e95e6 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -213,12 +213,8 @@ static void iscsi_get_ctask(struct iscsi_cmd_task *ctask) static void __iscsi_put_ctask(struct iscsi_cmd_task *ctask) { - struct iscsi_conn *conn = ctask->conn; - - if (atomic_dec_and_test(&ctask->refcount)) { - conn->session->tt->cleanup_cmd_task(conn, ctask); + if (atomic_dec_and_test(&ctask->refcount)) iscsi_complete_command(ctask); - } } static void iscsi_put_ctask(struct iscsi_cmd_task *ctask) @@ -1129,10 +1125,13 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, sc = ctask->sc; if (!sc) return; + + conn->session->tt->cleanup_cmd_task(conn, ctask); iscsi_ctask_mtask_cleanup(ctask); sc->result = err; sc->resid = sc->request_bufflen; + /* release ref from queuecommand */ __iscsi_put_ctask(ctask); } -- cgit v1.2.3 From 01dfc7fc56f4b7ec0e5344ab44fcf673ebfbf7fa Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Thu, 31 Aug 2006 18:09:35 -0400 Subject: [SCSI] iscsi class: update version Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_iscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 2ecd1418857..7b0019cccce 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -34,7 +34,7 @@ #define ISCSI_SESSION_ATTRS 11 #define ISCSI_CONN_ATTRS 11 #define ISCSI_HOST_ATTRS 0 -#define ISCSI_TRANSPORT_VERSION "1.1-646" +#define ISCSI_TRANSPORT_VERSION "2.0-685" struct iscsi_internal { int daemon_pid; -- cgit v1.2.3 From 69bdd88ca2670c321fef774e77059516f836c6f2 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 1 Sep 2006 15:50:23 +0200 Subject: [SCSI] Wrong size information for devices with disabled read access When accessing a device with disabled read access the capacity is set randomly to 1GB. This makes it impossible to userspace tools to detect invalid device capacities. Signed-off-by: Mike Anderson Acked-by: Chris Mason Signed-off-by: Hannes Reinecke Signed-off-by: James Bottomley --- drivers/scsi/sd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 98bd3aab973..638cff41d43 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1215,7 +1215,7 @@ repeat: /* Either no media are present but the drive didn't tell us, or they are present but the read capacity command fails */ /* sdkp->media_present = 0; -- not always correct */ - sdkp->capacity = 0x200000; /* 1 GB - random */ + sdkp->capacity = 0; /* unknown mapped to zero - as usual */ return; } else if (the_result && longrc) { -- cgit v1.2.3 From 5a25ba1677ab8d63890016a8c1bca68a3e0fbc7d Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Fri, 1 Sep 2006 03:12:19 -0400 Subject: [SCSI] Add Promise SuperTrak driver Add Promise SuperTrak 'stex' driver, supporting SuperTrak EX8350/8300/16350/16300 controllers. The controller's firmware accepts SCSI commands, handing them to the underlying RAID or JBOD disks. The driver consisted of the following cleanups and fixes, beyond its initial submission: Ed Lin: stex: cleanup and minor fixes stex: add new device ids stex: update internal copy code path stex: add hard reset function stex: adjust command timeout in slave_config routine stex: use more efficient method for unload/shutdown flush Jeff Garzik: [SCSI] Add Promise SuperTrak 'shasta' driver. Rename drivers/scsi/shasta.c to stex.c ("SuperTrak EX"). [SCSI] stex: update with community comments from 'Promise SuperTrak' thread [SCSI] stex: Fix warning, trim trailing whitespace. [SCSI] stex: remove last remnants of "shasta" project code name [SCSI] stex: removed 6-byte command emulation [SCSI] stex: minor cleanups [SCSI] stex: minor fixes: irq flag, error return value [SCSI] stex: use dma_alloc_coherent() Signed-off-by: Jeff Garzik Signed-off-by: James Bottomley --- drivers/scsi/Kconfig | 7 + drivers/scsi/Makefile | 1 + drivers/scsi/stex.c | 1316 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1324 insertions(+) create mode 100644 drivers/scsi/stex.c (limited to 'drivers/scsi') diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 7de5fdfdab6..c8c606589ea 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1070,6 +1070,13 @@ config 53C700_LE_ON_BE depends on SCSI_LASI700 default y +config SCSI_STEX + tristate "Promise SuperTrak EX Series support" + depends on PCI && SCSI + ---help--- + This driver supports Promise SuperTrak EX8350/8300/16350/16300 + Storage controllers. + config SCSI_SYM53C8XX_2 tristate "SYM53C8XX Version 2 SCSI support" depends on PCI && SCSI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 83da70decdd..fd9aeb1ba07 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -141,6 +141,7 @@ obj-$(CONFIG_SCSI_SATA_ULI) += libata.o sata_uli.o obj-$(CONFIG_SCSI_SATA_MV) += libata.o sata_mv.o obj-$(CONFIG_SCSI_PDC_ADMA) += libata.o pdc_adma.o obj-$(CONFIG_SCSI_HPTIOP) += hptiop.o +obj-$(CONFIG_SCSI_STEX) += stex.o obj-$(CONFIG_ARM) += arm/ diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c new file mode 100644 index 00000000000..fd093302bf1 --- /dev/null +++ b/drivers/scsi/stex.c @@ -0,0 +1,1316 @@ +/* + * SuperTrak EX Series Storage Controller driver for Linux + * + * Copyright (C) 2005, 2006 Promise Technology Inc. + * + * 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 of the License, or (at your option) any later version. + * + * Written By: + * Ed Lin + * + * Version: 2.9.0.13 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "stex" +#define ST_DRIVER_VERSION "2.9.0.13" +#define ST_VER_MAJOR 2 +#define ST_VER_MINOR 9 +#define ST_OEM 0 +#define ST_BUILD_VER 13 + +enum { + /* MU register offset */ + IMR0 = 0x10, /* MU_INBOUND_MESSAGE_REG0 */ + IMR1 = 0x14, /* MU_INBOUND_MESSAGE_REG1 */ + OMR0 = 0x18, /* MU_OUTBOUND_MESSAGE_REG0 */ + OMR1 = 0x1c, /* MU_OUTBOUND_MESSAGE_REG1 */ + IDBL = 0x20, /* MU_INBOUND_DOORBELL */ + IIS = 0x24, /* MU_INBOUND_INTERRUPT_STATUS */ + IIM = 0x28, /* MU_INBOUND_INTERRUPT_MASK */ + ODBL = 0x2c, /* MU_OUTBOUND_DOORBELL */ + OIS = 0x30, /* MU_OUTBOUND_INTERRUPT_STATUS */ + OIM = 0x3c, /* MU_OUTBOUND_INTERRUPT_MASK */ + + /* MU register value */ + MU_INBOUND_DOORBELL_HANDSHAKE = 1, + MU_INBOUND_DOORBELL_REQHEADCHANGED = 2, + MU_INBOUND_DOORBELL_STATUSTAILCHANGED = 4, + MU_INBOUND_DOORBELL_HMUSTOPPED = 8, + MU_INBOUND_DOORBELL_RESET = 16, + + MU_OUTBOUND_DOORBELL_HANDSHAKE = 1, + MU_OUTBOUND_DOORBELL_REQUESTTAILCHANGED = 2, + MU_OUTBOUND_DOORBELL_STATUSHEADCHANGED = 4, + MU_OUTBOUND_DOORBELL_BUSCHANGE = 8, + MU_OUTBOUND_DOORBELL_HASEVENT = 16, + + /* MU status code */ + MU_STATE_STARTING = 1, + MU_STATE_FMU_READY_FOR_HANDSHAKE = 2, + MU_STATE_SEND_HANDSHAKE_FRAME = 3, + MU_STATE_STARTED = 4, + MU_STATE_RESETTING = 5, + + MU_MAX_DELAY_TIME = 240000, + MU_HANDSHAKE_SIGNATURE = 0x55aaaa55, + HMU_PARTNER_TYPE = 2, + + /* firmware returned values */ + SRB_STATUS_SUCCESS = 0x01, + SRB_STATUS_ERROR = 0x04, + SRB_STATUS_BUSY = 0x05, + SRB_STATUS_INVALID_REQUEST = 0x06, + SRB_STATUS_SELECTION_TIMEOUT = 0x0A, + SRB_SEE_SENSE = 0x80, + + /* task attribute */ + TASK_ATTRIBUTE_SIMPLE = 0x0, + TASK_ATTRIBUTE_HEADOFQUEUE = 0x1, + TASK_ATTRIBUTE_ORDERED = 0x2, + TASK_ATTRIBUTE_ACA = 0x4, + + /* request count, etc. */ + MU_MAX_REQUEST = 32, + TAG_BITMAP_LENGTH = MU_MAX_REQUEST, + + /* one message wasted, use MU_MAX_REQUEST+1 + to handle MU_MAX_REQUEST messages */ + MU_REQ_COUNT = (MU_MAX_REQUEST + 1), + MU_STATUS_COUNT = (MU_MAX_REQUEST + 1), + + STEX_CDB_LENGTH = MAX_COMMAND_SIZE, + REQ_VARIABLE_LEN = 1024, + STATUS_VAR_LEN = 128, + ST_CAN_QUEUE = MU_MAX_REQUEST, + ST_CMD_PER_LUN = MU_MAX_REQUEST, + ST_MAX_SG = 32, + + /* sg flags */ + SG_CF_EOT = 0x80, /* end of table */ + SG_CF_64B = 0x40, /* 64 bit item */ + SG_CF_HOST = 0x20, /* sg in host memory */ + + ST_MAX_ARRAY_SUPPORTED = 16, + ST_MAX_TARGET_NUM = (ST_MAX_ARRAY_SUPPORTED+1), + ST_MAX_LUN_PER_TARGET = 16, + + st_shasta = 0, + st_vsc = 1, + + PASSTHRU_REQ_TYPE = 0x00000001, + PASSTHRU_REQ_NO_WAKEUP = 0x00000100, + ST_INTERNAL_TIMEOUT = 30, + + /* vendor specific commands of Promise */ + ARRAY_CMD = 0xe0, + CONTROLLER_CMD = 0xe1, + DEBUGGING_CMD = 0xe2, + PASSTHRU_CMD = 0xe3, + + PASSTHRU_GET_ADAPTER = 0x05, + PASSTHRU_GET_DRVVER = 0x10, + CTLR_POWER_STATE_CHANGE = 0x0e, + CTLR_POWER_SAVING = 0x01, + + PASSTHRU_SIGNATURE = 0x4e415041, + + INQUIRY_EVPD = 0x01, +}; + +struct st_sgitem { + u8 ctrl; /* SG_CF_xxx */ + u8 reserved[3]; + __le32 count; + __le32 addr; + __le32 addr_hi; +}; + +struct st_sgtable { + __le16 sg_count; + __le16 max_sg_count; + __le32 sz_in_byte; + struct st_sgitem table[ST_MAX_SG]; +}; + +struct handshake_frame { + __le32 rb_phy; /* request payload queue physical address */ + __le32 rb_phy_hi; + __le16 req_sz; /* size of each request payload */ + __le16 req_cnt; /* count of reqs the buffer can hold */ + __le16 status_sz; /* size of each status payload */ + __le16 status_cnt; /* count of status the buffer can hold */ + __le32 hosttime; /* seconds from Jan 1, 1970 (GMT) */ + __le32 hosttime_hi; + u8 partner_type; /* who sends this frame */ + u8 reserved0[7]; + __le32 partner_ver_major; + __le32 partner_ver_minor; + __le32 partner_ver_oem; + __le32 partner_ver_build; + u32 reserved1[4]; +}; + +struct req_msg { + __le16 tag; + u8 lun; + u8 target; + u8 task_attr; + u8 task_manage; + u8 prd_entry; + u8 payload_sz; /* payload size in 4-byte */ + u8 cdb[STEX_CDB_LENGTH]; + u8 variable[REQ_VARIABLE_LEN]; +}; + +struct status_msg { + __le16 tag; + u8 lun; + u8 target; + u8 srb_status; + u8 scsi_status; + u8 reserved; + u8 payload_sz; /* payload size in 4-byte */ + u8 variable[STATUS_VAR_LEN]; +}; + +struct ver_info { + u32 major; + u32 minor; + u32 oem; + u32 build; + u32 reserved[2]; +}; + +struct st_frame { + u32 base[6]; + u32 rom_addr; + + struct ver_info drv_ver; + struct ver_info bios_ver; + + u32 bus; + u32 slot; + u32 irq_level; + u32 irq_vec; + u32 id; + u32 subid; + + u32 dimm_size; + u8 dimm_type; + u8 reserved[3]; + + u32 channel; + u32 reserved1; +}; + +struct st_drvver { + u32 major; + u32 minor; + u32 oem; + u32 build; + u32 signature[2]; + u8 console_id; + u8 host_no; + u8 reserved0[2]; + u32 reserved[3]; +}; + +#define MU_REQ_BUFFER_SIZE (MU_REQ_COUNT * sizeof(struct req_msg)) +#define MU_STATUS_BUFFER_SIZE (MU_STATUS_COUNT * sizeof(struct status_msg)) +#define MU_BUFFER_SIZE (MU_REQ_BUFFER_SIZE + MU_STATUS_BUFFER_SIZE) +#define STEX_BUFFER_SIZE (MU_BUFFER_SIZE + sizeof(struct st_frame)) + +struct st_ccb { + struct req_msg *req; + struct scsi_cmnd *cmd; + + void *sense_buffer; + unsigned int sense_bufflen; + int sg_count; + + u32 req_type; + u8 srb_status; + u8 scsi_status; +}; + +struct st_hba { + void __iomem *mmio_base; /* iomapped PCI memory space */ + void *dma_mem; + dma_addr_t dma_handle; + + struct Scsi_Host *host; + struct pci_dev *pdev; + + u32 tag; + u32 req_head; + u32 req_tail; + u32 status_head; + u32 status_tail; + + struct status_msg *status_buffer; + void *copy_buffer; /* temp buffer for driver-handled commands */ + struct st_ccb ccb[MU_MAX_REQUEST]; + struct st_ccb *wait_ccb; + wait_queue_head_t waitq; + + unsigned int mu_status; + int out_req_cnt; + + unsigned int cardtype; +}; + +static const char console_inq_page[] = +{ + 0x03,0x00,0x03,0x03,0xFA,0x00,0x00,0x30, + 0x50,0x72,0x6F,0x6D,0x69,0x73,0x65,0x20, /* "Promise " */ + 0x52,0x41,0x49,0x44,0x20,0x43,0x6F,0x6E, /* "RAID Con" */ + 0x73,0x6F,0x6C,0x65,0x20,0x20,0x20,0x20, /* "sole " */ + 0x31,0x2E,0x30,0x30,0x20,0x20,0x20,0x20, /* "1.00 " */ + 0x53,0x58,0x2F,0x52,0x53,0x41,0x46,0x2D, /* "SX/RSAF-" */ + 0x54,0x45,0x31,0x2E,0x30,0x30,0x20,0x20, /* "TE1.00 " */ + 0x0C,0x20,0x20,0x20,0x20,0x20,0x20,0x20 +}; + +MODULE_AUTHOR("Ed Lin"); +MODULE_DESCRIPTION("Promise Technology SuperTrak EX Controllers"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(ST_DRIVER_VERSION); + +static void stex_gettime(__le32 *time) +{ + struct timeval tv; + do_gettimeofday(&tv); + + *time = cpu_to_le32(tv.tv_sec & 0xffffffff); + *(time + 1) = cpu_to_le32((tv.tv_sec >> 16) >> 16); +} + +static u16 __stex_alloc_tag(unsigned long *bitmap) +{ + int i; + i = find_first_zero_bit(bitmap, TAG_BITMAP_LENGTH); + if (i < TAG_BITMAP_LENGTH) + __set_bit(i, bitmap); + return (u16)i; +} + +static u16 stex_alloc_tag(struct st_hba *hba, unsigned long *bitmap) +{ + unsigned long flags; + u16 tag; + + spin_lock_irqsave(hba->host->host_lock, flags); + tag = __stex_alloc_tag(bitmap); + spin_unlock_irqrestore(hba->host->host_lock, flags); + return tag; +} + +static void __stex_free_tag(unsigned long *bitmap, u16 tag) +{ + __clear_bit((int)tag, bitmap); +} + +static void stex_free_tag(struct st_hba *hba, unsigned long *bitmap, u16 tag) +{ + unsigned long flags; + + spin_lock_irqsave(hba->host->host_lock, flags); + __stex_free_tag(bitmap, tag); + spin_unlock_irqrestore(hba->host->host_lock, flags); +} + +static struct status_msg *stex_get_status(struct st_hba *hba) +{ + struct status_msg *status = + hba->status_buffer + hba->status_tail; + + ++hba->status_tail; + hba->status_tail %= MU_STATUS_COUNT; + + return status; +} + +static void stex_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq) +{ + cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; + + cmd->sense_buffer[0] = 0x70; /* fixed format, current */ + cmd->sense_buffer[2] = sk; + cmd->sense_buffer[7] = 18 - 8; /* additional sense length */ + cmd->sense_buffer[12] = asc; + cmd->sense_buffer[13] = ascq; +} + +static void stex_invalid_field(struct scsi_cmnd *cmd, + void (*done)(struct scsi_cmnd *)) +{ + /* "Invalid field in cbd" */ + stex_set_sense(cmd, ILLEGAL_REQUEST, 0x24, 0x0); + done(cmd); +} + +static struct req_msg *stex_alloc_req(struct st_hba *hba) +{ + struct req_msg *req = ((struct req_msg *)hba->dma_mem) + + hba->req_head; + + ++hba->req_head; + hba->req_head %= MU_REQ_COUNT; + + return req; +} + +static int stex_map_sg(struct st_hba *hba, + struct req_msg *req, struct st_ccb *ccb) +{ + struct pci_dev *pdev = hba->pdev; + struct scsi_cmnd *cmd; + dma_addr_t dma_handle; + struct scatterlist *src; + struct st_sgtable *dst; + int i; + + cmd = ccb->cmd; + dst = (struct st_sgtable *)req->variable; + dst->max_sg_count = cpu_to_le16(ST_MAX_SG); + dst->sz_in_byte = cpu_to_le32(cmd->request_bufflen); + + if (cmd->use_sg) { + int n_elem; + + src = (struct scatterlist *) cmd->request_buffer; + n_elem = pci_map_sg(pdev, src, + cmd->use_sg, cmd->sc_data_direction); + if (n_elem <= 0) + return -EIO; + + ccb->sg_count = n_elem; + dst->sg_count = cpu_to_le16((u16)n_elem); + + for (i = 0; i < n_elem; i++, src++) { + dst->table[i].count = cpu_to_le32((u32)sg_dma_len(src)); + dst->table[i].addr = + cpu_to_le32(sg_dma_address(src) & 0xffffffff); + dst->table[i].addr_hi = + cpu_to_le32((sg_dma_address(src) >> 16) >> 16); + dst->table[i].ctrl = SG_CF_64B | SG_CF_HOST; + } + dst->table[--i].ctrl |= SG_CF_EOT; + return 0; + } + + dma_handle = pci_map_single(pdev, cmd->request_buffer, + cmd->request_bufflen, cmd->sc_data_direction); + cmd->SCp.dma_handle = dma_handle; + + ccb->sg_count = 1; + dst->sg_count = cpu_to_le16(1); + dst->table[0].addr = cpu_to_le32(dma_handle & 0xffffffff); + dst->table[0].addr_hi = cpu_to_le32((dma_handle >> 16) >> 16); + dst->table[0].count = cpu_to_le32((u32)cmd->request_bufflen); + dst->table[0].ctrl = SG_CF_EOT | SG_CF_64B | SG_CF_HOST; + + return 0; +} + +static void stex_internal_copy(struct scsi_cmnd *cmd, + const void *src, size_t *count, int sg_count) +{ + size_t lcount; + size_t len; + void *s, *d, *base = NULL; + if (*count > cmd->request_bufflen) + *count = cmd->request_bufflen; + lcount = *count; + while (lcount) { + len = lcount; + s = (void *)src; + if (cmd->use_sg) { + size_t offset = *count - lcount; + s += offset; + base = scsi_kmap_atomic_sg(cmd->request_buffer, + sg_count, &offset, &len); + if (base == NULL) { + *count -= lcount; + return; + } + d = base + offset; + } else + d = cmd->request_buffer; + + memcpy(d, s, len); + + lcount -= len; + if (cmd->use_sg) + scsi_kunmap_atomic_sg(base); + } +} + +static int stex_direct_copy(struct scsi_cmnd *cmd, + const void *src, size_t count) +{ + struct st_hba *hba = (struct st_hba *) &cmd->device->host->hostdata[0]; + size_t cp_len = count; + int n_elem = 0; + + if (cmd->use_sg) { + n_elem = pci_map_sg(hba->pdev, cmd->request_buffer, + cmd->use_sg, cmd->sc_data_direction); + if (n_elem <= 0) + return 0; + } + + stex_internal_copy(cmd, src, &cp_len, n_elem); + + if (cmd->use_sg) + pci_unmap_sg(hba->pdev, cmd->request_buffer, + cmd->use_sg, cmd->sc_data_direction); + return cp_len == count; +} + +static void stex_controller_info(struct st_hba *hba, struct st_ccb *ccb) +{ + struct st_frame *p; + size_t count = sizeof(struct st_frame); + + p = hba->copy_buffer; + memset(p->base, 0, sizeof(u32)*6); + *(unsigned long *)(p->base) = pci_resource_start(hba->pdev, 0); + p->rom_addr = 0; + + p->drv_ver.major = ST_VER_MAJOR; + p->drv_ver.minor = ST_VER_MINOR; + p->drv_ver.oem = ST_OEM; + p->drv_ver.build = ST_BUILD_VER; + + p->bus = hba->pdev->bus->number; + p->slot = hba->pdev->devfn; + p->irq_level = 0; + p->irq_vec = hba->pdev->irq; + p->id = hba->pdev->vendor << 16 | hba->pdev->device; + p->subid = + hba->pdev->subsystem_vendor << 16 | hba->pdev->subsystem_device; + + stex_internal_copy(ccb->cmd, p, &count, ccb->sg_count); +} + +static void +stex_send_cmd(struct st_hba *hba, struct req_msg *req, u16 tag) +{ + req->tag = cpu_to_le16(tag); + req->task_attr = TASK_ATTRIBUTE_SIMPLE; + req->task_manage = 0; /* not supported yet */ + req->payload_sz = (u8)(sizeof(struct req_msg)/sizeof(u32)); + + hba->ccb[tag].req = req; + hba->out_req_cnt++; + + writel(hba->req_head, hba->mmio_base + IMR0); + writel(MU_INBOUND_DOORBELL_REQHEADCHANGED, hba->mmio_base + IDBL); + readl(hba->mmio_base + IDBL); /* flush */ +} + +static int +stex_slave_config(struct scsi_device *sdev) +{ + sdev->use_10_for_rw = 1; + sdev->use_10_for_ms = 1; + sdev->timeout = 60 * HZ; + return 0; +} + +static void +stex_slave_destroy(struct scsi_device *sdev) +{ + struct st_hba *hba = (struct st_hba *) sdev->host->hostdata; + struct req_msg *req; + unsigned long flags; + unsigned long before; + u16 tag; + + if (sdev->type != TYPE_DISK) + return; + + before = jiffies; + while ((tag = stex_alloc_tag(hba, (unsigned long *)&hba->tag)) + == TAG_BITMAP_LENGTH) { + if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) + return; + msleep(10); + } + + spin_lock_irqsave(hba->host->host_lock, flags); + req = stex_alloc_req(hba); + memset(req->cdb, 0, STEX_CDB_LENGTH); + + req->target = sdev->id; + req->lun = sdev->channel; /* firmware lun issue work around */ + req->cdb[0] = SYNCHRONIZE_CACHE; + + hba->ccb[tag].cmd = NULL; + hba->ccb[tag].sg_count = 0; + hba->ccb[tag].sense_bufflen = 0; + hba->ccb[tag].sense_buffer = NULL; + hba->ccb[tag].req_type |= PASSTHRU_REQ_TYPE; + + stex_send_cmd(hba, req, tag); + spin_unlock_irqrestore(hba->host->host_lock, flags); + + wait_event_timeout(hba->waitq, + !(hba->ccb[tag].req_type), ST_INTERNAL_TIMEOUT * HZ); + if (hba->ccb[tag].req_type & PASSTHRU_REQ_TYPE) + return; + + stex_free_tag(hba, (unsigned long *)&hba->tag, tag); +} + +static int +stex_queuecommand(struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *)) +{ + struct st_hba *hba; + struct Scsi_Host *host; + unsigned int id,lun; + struct req_msg *req; + u16 tag; + host = cmd->device->host; + id = cmd->device->id; + lun = cmd->device->channel; /* firmware lun issue work around */ + hba = (struct st_hba *) &host->hostdata[0]; + + switch (cmd->cmnd[0]) { + case MODE_SENSE_10: + { + static char ms10_caching_page[12] = + { 0, 0x12, 0, 0, 0, 0, 0, 0, 0x8, 0xa, 0x4, 0 }; + unsigned char page; + page = cmd->cmnd[2] & 0x3f; + if (page == 0x8 || page == 0x3f) { + stex_direct_copy(cmd, ms10_caching_page, + sizeof(ms10_caching_page)); + cmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8; + done(cmd); + } else + stex_invalid_field(cmd, done); + return 0; + } + case INQUIRY: + if (id != ST_MAX_ARRAY_SUPPORTED) + break; + if (lun == 0 && (cmd->cmnd[1] & INQUIRY_EVPD) == 0) { + stex_direct_copy(cmd, console_inq_page, + sizeof(console_inq_page)); + cmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8; + done(cmd); + } else + stex_invalid_field(cmd, done); + return 0; + case PASSTHRU_CMD: + if (cmd->cmnd[1] == PASSTHRU_GET_DRVVER) { + struct st_drvver ver; + ver.major = ST_VER_MAJOR; + ver.minor = ST_VER_MINOR; + ver.oem = ST_OEM; + ver.build = ST_BUILD_VER; + ver.signature[0] = PASSTHRU_SIGNATURE; + ver.console_id = ST_MAX_ARRAY_SUPPORTED; + ver.host_no = hba->host->host_no; + cmd->result = stex_direct_copy(cmd, &ver, sizeof(ver)) ? + DID_OK << 16 | COMMAND_COMPLETE << 8 : + DID_ERROR << 16 | COMMAND_COMPLETE << 8; + done(cmd); + return 0; + } + default: + break; + } + + cmd->scsi_done = done; + + if (unlikely((tag = __stex_alloc_tag((unsigned long *)&hba->tag)) + == TAG_BITMAP_LENGTH)) + return SCSI_MLQUEUE_HOST_BUSY; + + req = stex_alloc_req(hba); + req->lun = lun; + req->target = id; + + /* cdb */ + memcpy(req->cdb, cmd->cmnd, STEX_CDB_LENGTH); + + hba->ccb[tag].cmd = cmd; + hba->ccb[tag].sense_bufflen = SCSI_SENSE_BUFFERSIZE; + hba->ccb[tag].sense_buffer = cmd->sense_buffer; + hba->ccb[tag].req_type = 0; + + if (cmd->sc_data_direction != DMA_NONE) + stex_map_sg(hba, req, &hba->ccb[tag]); + + stex_send_cmd(hba, req, tag); + return 0; +} + +static void stex_unmap_sg(struct st_hba *hba, struct scsi_cmnd *cmd) +{ + if (cmd->sc_data_direction != DMA_NONE) { + if (cmd->use_sg) + pci_unmap_sg(hba->pdev, cmd->request_buffer, + cmd->use_sg, cmd->sc_data_direction); + else + pci_unmap_single(hba->pdev, cmd->SCp.dma_handle, + cmd->request_bufflen, cmd->sc_data_direction); + } +} + +static void stex_scsi_done(struct st_ccb *ccb) +{ + struct scsi_cmnd *cmd = ccb->cmd; + int result; + + if (ccb->srb_status == SRB_STATUS_SUCCESS || ccb->srb_status == 0) { + result = ccb->scsi_status; + switch (ccb->scsi_status) { + case SAM_STAT_GOOD: + result |= DID_OK << 16 | COMMAND_COMPLETE << 8; + break; + case SAM_STAT_CHECK_CONDITION: + result |= DRIVER_SENSE << 24; + break; + case SAM_STAT_BUSY: + result |= DID_BUS_BUSY << 16 | COMMAND_COMPLETE << 8; + break; + default: + result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8; + break; + } + } + else if (ccb->srb_status & SRB_SEE_SENSE) + result = DRIVER_SENSE << 24 | SAM_STAT_CHECK_CONDITION; + else switch (ccb->srb_status) { + case SRB_STATUS_SELECTION_TIMEOUT: + result = DID_NO_CONNECT << 16 | COMMAND_COMPLETE << 8; + break; + case SRB_STATUS_BUSY: + result = DID_BUS_BUSY << 16 | COMMAND_COMPLETE << 8; + break; + case SRB_STATUS_INVALID_REQUEST: + case SRB_STATUS_ERROR: + default: + result = DID_ERROR << 16 | COMMAND_COMPLETE << 8; + break; + } + + cmd->result = result; + cmd->scsi_done(cmd); +} + +static void stex_copy_data(struct st_ccb *ccb, + struct status_msg *resp, unsigned int variable) +{ + size_t count = variable; + if (resp->scsi_status != SAM_STAT_GOOD) { + if (ccb->sense_buffer != NULL) + memcpy(ccb->sense_buffer, resp->variable, + min(variable, ccb->sense_bufflen)); + return; + } + + if (ccb->cmd == NULL) + return; + stex_internal_copy(ccb->cmd, resp->variable, &count, ccb->sg_count); +} + +static void stex_mu_intr(struct st_hba *hba, u32 doorbell) +{ + void __iomem *base = hba->mmio_base; + struct status_msg *resp; + struct st_ccb *ccb; + unsigned int size; + u16 tag; + + if (!(doorbell & MU_OUTBOUND_DOORBELL_STATUSHEADCHANGED)) + return; + + /* status payloads */ + hba->status_head = readl(base + OMR1); + if (unlikely(hba->status_head >= MU_STATUS_COUNT)) { + printk(KERN_WARNING DRV_NAME "(%s): invalid status head\n", + pci_name(hba->pdev)); + return; + } + + if (unlikely(hba->mu_status != MU_STATE_STARTED || + hba->out_req_cnt <= 0)) { + hba->status_tail = hba->status_head; + goto update_status; + } + + while (hba->status_tail != hba->status_head) { + resp = stex_get_status(hba); + tag = le16_to_cpu(resp->tag); + if (unlikely(tag >= TAG_BITMAP_LENGTH)) { + printk(KERN_WARNING DRV_NAME + "(%s): invalid tag\n", pci_name(hba->pdev)); + continue; + } + if (unlikely((hba->tag & (1 << tag)) == 0)) { + printk(KERN_WARNING DRV_NAME + "(%s): null tag\n", pci_name(hba->pdev)); + continue; + } + + hba->out_req_cnt--; + ccb = &hba->ccb[tag]; + if (hba->wait_ccb == ccb) + hba->wait_ccb = NULL; + if (unlikely(ccb->req == NULL)) { + printk(KERN_WARNING DRV_NAME + "(%s): lagging req\n", pci_name(hba->pdev)); + __stex_free_tag((unsigned long *)&hba->tag, tag); + stex_unmap_sg(hba, ccb->cmd); /* ??? */ + continue; + } + + size = resp->payload_sz * sizeof(u32); /* payload size */ + if (unlikely(size < sizeof(*resp) - STATUS_VAR_LEN || + size > sizeof(*resp))) { + printk(KERN_WARNING DRV_NAME "(%s): bad status size\n", + pci_name(hba->pdev)); + } else { + size -= sizeof(*resp) - STATUS_VAR_LEN; /* copy size */ + if (size) + stex_copy_data(ccb, resp, size); + } + + ccb->srb_status = resp->srb_status; + ccb->scsi_status = resp->scsi_status; + + if (ccb->req_type & PASSTHRU_REQ_TYPE) { + if (ccb->req_type & PASSTHRU_REQ_NO_WAKEUP) { + ccb->req_type = 0; + continue; + } + ccb->req_type = 0; + if (waitqueue_active(&hba->waitq)) + wake_up(&hba->waitq); + continue; + } + if (ccb->cmd->cmnd[0] == PASSTHRU_CMD && + ccb->cmd->cmnd[1] == PASSTHRU_GET_ADAPTER) + stex_controller_info(hba, ccb); + __stex_free_tag((unsigned long *)&hba->tag, tag); + stex_unmap_sg(hba, ccb->cmd); + stex_scsi_done(ccb); + } + +update_status: + writel(hba->status_head, base + IMR1); + readl(base + IMR1); /* flush */ +} + +static irqreturn_t stex_intr(int irq, void *__hba, struct pt_regs *regs) +{ + struct st_hba *hba = __hba; + void __iomem *base = hba->mmio_base; + u32 data; + unsigned long flags; + int handled = 0; + + spin_lock_irqsave(hba->host->host_lock, flags); + + data = readl(base + ODBL); + + if (data && data != 0xffffffff) { + /* clear the interrupt */ + writel(data, base + ODBL); + readl(base + ODBL); /* flush */ + stex_mu_intr(hba, data); + handled = 1; + } + + spin_unlock_irqrestore(hba->host->host_lock, flags); + + return IRQ_RETVAL(handled); +} + +static int stex_handshake(struct st_hba *hba) +{ + void __iomem *base = hba->mmio_base; + struct handshake_frame *h; + dma_addr_t status_phys; + int i; + + if (readl(base + OMR0) != MU_HANDSHAKE_SIGNATURE) { + writel(MU_INBOUND_DOORBELL_HANDSHAKE, base + IDBL); + readl(base + IDBL); + for (i = 0; readl(base + OMR0) != MU_HANDSHAKE_SIGNATURE + && i < MU_MAX_DELAY_TIME; i++) { + rmb(); + msleep(1); + } + + if (i == MU_MAX_DELAY_TIME) { + printk(KERN_ERR DRV_NAME + "(%s): no handshake signature\n", + pci_name(hba->pdev)); + return -1; + } + } + + udelay(10); + + h = (struct handshake_frame *)(hba->dma_mem + MU_REQ_BUFFER_SIZE); + h->rb_phy = cpu_to_le32(hba->dma_handle); + h->rb_phy_hi = cpu_to_le32((hba->dma_handle >> 16) >> 16); + h->req_sz = cpu_to_le16(sizeof(struct req_msg)); + h->req_cnt = cpu_to_le16(MU_REQ_COUNT); + h->status_sz = cpu_to_le16(sizeof(struct status_msg)); + h->status_cnt = cpu_to_le16(MU_STATUS_COUNT); + stex_gettime(&h->hosttime); + h->partner_type = HMU_PARTNER_TYPE; + + status_phys = hba->dma_handle + MU_REQ_BUFFER_SIZE; + writel(status_phys, base + IMR0); + readl(base + IMR0); + writel((status_phys >> 16) >> 16, base + IMR1); + readl(base + IMR1); + + writel((status_phys >> 16) >> 16, base + OMR0); /* old fw compatible */ + readl(base + OMR0); + writel(MU_INBOUND_DOORBELL_HANDSHAKE, base + IDBL); + readl(base + IDBL); /* flush */ + + udelay(10); + for (i = 0; readl(base + OMR0) != MU_HANDSHAKE_SIGNATURE + && i < MU_MAX_DELAY_TIME; i++) { + rmb(); + msleep(1); + } + + if (i == MU_MAX_DELAY_TIME) { + printk(KERN_ERR DRV_NAME + "(%s): no signature after handshake frame\n", + pci_name(hba->pdev)); + return -1; + } + + writel(0, base + IMR0); + readl(base + IMR0); + writel(0, base + OMR0); + readl(base + OMR0); + writel(0, base + IMR1); + readl(base + IMR1); + writel(0, base + OMR1); + readl(base + OMR1); /* flush */ + hba->mu_status = MU_STATE_STARTED; + return 0; +} + +static int stex_abort(struct scsi_cmnd *cmd) +{ + struct Scsi_Host *host = cmd->device->host; + struct st_hba *hba = (struct st_hba *)host->hostdata; + u16 tag; + void __iomem *base; + u32 data; + int result = SUCCESS; + unsigned long flags; + base = hba->mmio_base; + spin_lock_irqsave(host->host_lock, flags); + + for (tag = 0; tag < MU_MAX_REQUEST; tag++) + if (hba->ccb[tag].cmd == cmd && (hba->tag & (1 << tag))) { + hba->wait_ccb = &(hba->ccb[tag]); + break; + } + if (tag >= MU_MAX_REQUEST) + goto out; + + data = readl(base + ODBL); + if (data == 0 || data == 0xffffffff) + goto fail_out; + + writel(data, base + ODBL); + readl(base + ODBL); /* flush */ + + stex_mu_intr(hba, data); + + if (hba->wait_ccb == NULL) { + printk(KERN_WARNING DRV_NAME + "(%s): lost interrupt\n", pci_name(hba->pdev)); + goto out; + } + +fail_out: + hba->wait_ccb->req = NULL; /* nullify the req's future return */ + hba->wait_ccb = NULL; + result = FAILED; +out: + spin_unlock_irqrestore(host->host_lock, flags); + return result; +} + +static void stex_hard_reset(struct st_hba *hba) +{ + struct pci_bus *bus; + int i; + u16 pci_cmd; + u8 pci_bctl; + + for (i = 0; i < 16; i++) + pci_read_config_dword(hba->pdev, i * 4, + &hba->pdev->saved_config_space[i]); + + /* Reset secondary bus. Our controller(MU/ATU) is the only device on + secondary bus. Consult Intel 80331/3 developer's manual for detail */ + bus = hba->pdev->bus; + pci_read_config_byte(bus->self, PCI_BRIDGE_CONTROL, &pci_bctl); + pci_bctl |= PCI_BRIDGE_CTL_BUS_RESET; + pci_write_config_byte(bus->self, PCI_BRIDGE_CONTROL, pci_bctl); + msleep(1); + pci_bctl &= ~PCI_BRIDGE_CTL_BUS_RESET; + pci_write_config_byte(bus->self, PCI_BRIDGE_CONTROL, pci_bctl); + + for (i = 0; i < MU_MAX_DELAY_TIME; i++) { + pci_read_config_word(hba->pdev, PCI_COMMAND, &pci_cmd); + if (pci_cmd & PCI_COMMAND_MASTER) + break; + msleep(1); + } + + ssleep(5); + for (i = 0; i < 16; i++) + pci_write_config_dword(hba->pdev, i * 4, + hba->pdev->saved_config_space[i]); +} + +static int stex_reset(struct scsi_cmnd *cmd) +{ + struct st_hba *hba; + unsigned long flags; + hba = (struct st_hba *) &cmd->device->host->hostdata[0]; + + hba->mu_status = MU_STATE_RESETTING; + + if (hba->cardtype == st_shasta) + stex_hard_reset(hba); + + if (stex_handshake(hba)) { + printk(KERN_WARNING DRV_NAME + "(%s): resetting: handshake failed\n", + pci_name(hba->pdev)); + return FAILED; + } + spin_lock_irqsave(hba->host->host_lock, flags); + hba->tag = 0; + hba->req_head = 0; + hba->req_tail = 0; + hba->status_head = 0; + hba->status_tail = 0; + hba->out_req_cnt = 0; + spin_unlock_irqrestore(hba->host->host_lock, flags); + + return SUCCESS; +} + +static int stex_biosparam(struct scsi_device *sdev, + struct block_device *bdev, sector_t capacity, int geom[]) +{ + int heads = 255, sectors = 63, cylinders; + + if (capacity < 0x200000) { + heads = 64; + sectors = 32; + } + + cylinders = sector_div(capacity, heads * sectors); + + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + + return 0; +} + +static struct scsi_host_template driver_template = { + .module = THIS_MODULE, + .name = DRV_NAME, + .proc_name = DRV_NAME, + .bios_param = stex_biosparam, + .queuecommand = stex_queuecommand, + .slave_configure = stex_slave_config, + .slave_destroy = stex_slave_destroy, + .eh_abort_handler = stex_abort, + .eh_host_reset_handler = stex_reset, + .can_queue = ST_CAN_QUEUE, + .this_id = -1, + .sg_tablesize = ST_MAX_SG, + .cmd_per_lun = ST_CMD_PER_LUN, +}; + +static int stex_set_dma_mask(struct pci_dev * pdev) +{ + int ret; + if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK) + && !pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK)) + return 0; + ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (!ret) + ret = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + return ret; +} + +static int __devinit +stex_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct st_hba *hba; + struct Scsi_Host *host; + int err; + + err = pci_enable_device(pdev); + if (err) + return err; + + pci_set_master(pdev); + + host = scsi_host_alloc(&driver_template, sizeof(struct st_hba)); + + if (!host) { + printk(KERN_ERR DRV_NAME "(%s): scsi_host_alloc failed\n", + pci_name(pdev)); + err = -ENOMEM; + goto out_disable; + } + + hba = (struct st_hba *)host->hostdata; + memset(hba, 0, sizeof(struct st_hba)); + + err = pci_request_regions(pdev, DRV_NAME); + if (err < 0) { + printk(KERN_ERR DRV_NAME "(%s): request regions failed\n", + pci_name(pdev)); + goto out_scsi_host_put; + } + + hba->mmio_base = ioremap(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if ( !hba->mmio_base) { + printk(KERN_ERR DRV_NAME "(%s): memory map failed\n", + pci_name(pdev)); + err = -ENOMEM; + goto out_release_regions; + } + + err = stex_set_dma_mask(pdev); + if (err) { + printk(KERN_ERR DRV_NAME "(%s): set dma mask failed\n", + pci_name(pdev)); + goto out_iounmap; + } + + hba->dma_mem = dma_alloc_coherent(&pdev->dev, + STEX_BUFFER_SIZE, &hba->dma_handle, GFP_KERNEL); + if (!hba->dma_mem) { + err = -ENOMEM; + printk(KERN_ERR DRV_NAME "(%s): dma mem alloc failed\n", + pci_name(pdev)); + goto out_iounmap; + } + + hba->status_buffer = + (struct status_msg *)(hba->dma_mem + MU_REQ_BUFFER_SIZE); + hba->copy_buffer = hba->dma_mem + MU_BUFFER_SIZE; + hba->mu_status = MU_STATE_STARTING; + + hba->cardtype = (unsigned int) id->driver_data; + + /* firmware uses id/lun pair for a logical drive, but lun would be + always 0 if CONFIG_SCSI_MULTI_LUN not configured, so we use + channel to map lun here */ + host->max_channel = ST_MAX_LUN_PER_TARGET - 1; + host->max_id = ST_MAX_TARGET_NUM; + host->max_lun = 1; + host->unique_id = host->host_no; + host->max_cmd_len = STEX_CDB_LENGTH; + + hba->host = host; + hba->pdev = pdev; + init_waitqueue_head(&hba->waitq); + + err = request_irq(pdev->irq, stex_intr, IRQF_SHARED, DRV_NAME, hba); + if (err) { + printk(KERN_ERR DRV_NAME "(%s): request irq failed\n", + pci_name(pdev)); + goto out_pci_free; + } + + err = stex_handshake(hba); + if (err) + goto out_free_irq; + + pci_set_drvdata(pdev, hba); + + err = scsi_add_host(host, &pdev->dev); + if (err) { + printk(KERN_ERR DRV_NAME "(%s): scsi_add_host failed\n", + pci_name(pdev)); + goto out_free_irq; + } + + scsi_scan_host(host); + + return 0; + +out_free_irq: + free_irq(pdev->irq, hba); +out_pci_free: + dma_free_coherent(&pdev->dev, STEX_BUFFER_SIZE, + hba->dma_mem, hba->dma_handle); +out_iounmap: + iounmap(hba->mmio_base); +out_release_regions: + pci_release_regions(pdev); +out_scsi_host_put: + scsi_host_put(host); +out_disable: + pci_disable_device(pdev); + + return err; +} + +static void stex_hba_stop(struct st_hba *hba) +{ + struct req_msg *req; + unsigned long flags; + unsigned long before; + u16 tag; + + before = jiffies; + while ((tag = stex_alloc_tag(hba, (unsigned long *)&hba->tag)) + == TAG_BITMAP_LENGTH) { + if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) + return; + msleep(10); + } + + spin_lock_irqsave(hba->host->host_lock, flags); + req = stex_alloc_req(hba); + memset(req->cdb, 0, STEX_CDB_LENGTH); + + req->cdb[0] = CONTROLLER_CMD; + req->cdb[1] = CTLR_POWER_STATE_CHANGE; + req->cdb[2] = CTLR_POWER_SAVING; + + hba->ccb[tag].cmd = NULL; + hba->ccb[tag].sg_count = 0; + hba->ccb[tag].sense_bufflen = 0; + hba->ccb[tag].sense_buffer = NULL; + hba->ccb[tag].req_type |= PASSTHRU_REQ_TYPE; + + stex_send_cmd(hba, req, tag); + spin_unlock_irqrestore(hba->host->host_lock, flags); + + wait_event_timeout(hba->waitq, + !(hba->ccb[tag].req_type), ST_INTERNAL_TIMEOUT * HZ); + if (hba->ccb[tag].req_type & PASSTHRU_REQ_TYPE) + return; + + stex_free_tag(hba, (unsigned long *)&hba->tag, tag); +} + +static void stex_hba_free(struct st_hba *hba) +{ + free_irq(hba->pdev->irq, hba); + + iounmap(hba->mmio_base); + + pci_release_regions(hba->pdev); + + dma_free_coherent(&hba->pdev->dev, STEX_BUFFER_SIZE, + hba->dma_mem, hba->dma_handle); +} + +static void stex_remove(struct pci_dev *pdev) +{ + struct st_hba *hba = pci_get_drvdata(pdev); + + scsi_remove_host(hba->host); + + pci_set_drvdata(pdev, NULL); + + stex_hba_stop(hba); + + stex_hba_free(hba); + + scsi_host_put(hba->host); + + pci_disable_device(pdev); +} + +static void stex_shutdown(struct pci_dev *pdev) +{ + struct st_hba *hba = pci_get_drvdata(pdev); + + stex_hba_stop(hba); +} + +static struct pci_device_id stex_pci_tbl[] = { + { 0x105a, 0x8350, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, + { 0x105a, 0xc350, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, + { 0x105a, 0xf350, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, + { 0x105a, 0x4301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, + { 0x105a, 0x4302, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, + { 0x105a, 0x8301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, + { 0x105a, 0x8302, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, + { 0x1725, 0x7250, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_vsc }, + { } /* terminate list */ +}; +MODULE_DEVICE_TABLE(pci, stex_pci_tbl); + +static struct pci_driver stex_pci_driver = { + .name = DRV_NAME, + .id_table = stex_pci_tbl, + .probe = stex_probe, + .remove = __devexit_p(stex_remove), + .shutdown = stex_shutdown, +}; + +static int __init stex_init(void) +{ + printk(KERN_INFO DRV_NAME + ": Promise SuperTrak EX Driver version: %s\n", + ST_DRIVER_VERSION); + + return pci_register_driver(&stex_pci_driver); +} + +static void __exit stex_exit(void) +{ + pci_unregister_driver(&stex_pci_driver); +} + +module_init(stex_init); +module_exit(stex_exit); -- cgit v1.2.3 From cf355883f506051a8ce3ac4539752829320b6c8c Mon Sep 17 00:00:00 2001 From: Ed Lin Date: Fri, 1 Sep 2006 14:31:51 +0800 Subject: [SCSI] stex: add shared tags from block Use block shared tags entirely within the driver. In the case of shutdown, assume that there are no other outstanding commands, so tag 0 is fine. Signed-off-by: Ed Lin Signed-off-by: James Bottomley --- drivers/scsi/stex.c | 177 +++++++++++++++++----------------------------------- 1 file changed, 57 insertions(+), 120 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index fd093302bf1..15fb99f224e 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c @@ -34,6 +34,7 @@ #include #include #include +#include #define DRV_NAME "stex" #define ST_DRIVER_VERSION "2.9.0.13" @@ -95,7 +96,6 @@ enum { /* request count, etc. */ MU_MAX_REQUEST = 32, - TAG_BITMAP_LENGTH = MU_MAX_REQUEST, /* one message wasted, use MU_MAX_REQUEST+1 to handle MU_MAX_REQUEST messages */ @@ -265,7 +265,6 @@ struct st_hba { struct Scsi_Host *host; struct pci_dev *pdev; - u32 tag; u32 req_head; u32 req_tail; u32 status_head; @@ -309,40 +308,6 @@ static void stex_gettime(__le32 *time) *(time + 1) = cpu_to_le32((tv.tv_sec >> 16) >> 16); } -static u16 __stex_alloc_tag(unsigned long *bitmap) -{ - int i; - i = find_first_zero_bit(bitmap, TAG_BITMAP_LENGTH); - if (i < TAG_BITMAP_LENGTH) - __set_bit(i, bitmap); - return (u16)i; -} - -static u16 stex_alloc_tag(struct st_hba *hba, unsigned long *bitmap) -{ - unsigned long flags; - u16 tag; - - spin_lock_irqsave(hba->host->host_lock, flags); - tag = __stex_alloc_tag(bitmap); - spin_unlock_irqrestore(hba->host->host_lock, flags); - return tag; -} - -static void __stex_free_tag(unsigned long *bitmap, u16 tag) -{ - __clear_bit((int)tag, bitmap); -} - -static void stex_free_tag(struct st_hba *hba, unsigned long *bitmap, u16 tag) -{ - unsigned long flags; - - spin_lock_irqsave(hba->host->host_lock, flags); - __stex_free_tag(bitmap, tag); - spin_unlock_irqrestore(hba->host->host_lock, flags); -} - static struct status_msg *stex_get_status(struct st_hba *hba) { struct status_msg *status = @@ -534,58 +499,32 @@ stex_send_cmd(struct st_hba *hba, struct req_msg *req, u16 tag) readl(hba->mmio_base + IDBL); /* flush */ } +static int +stex_slave_alloc(struct scsi_device *sdev) +{ + /* Cheat: usually extracted from Inquiry data */ + sdev->tagged_supported = 1; + + scsi_activate_tcq(sdev, sdev->host->can_queue); + + return 0; +} + static int stex_slave_config(struct scsi_device *sdev) { sdev->use_10_for_rw = 1; sdev->use_10_for_ms = 1; sdev->timeout = 60 * HZ; + sdev->tagged_supported = 1; + return 0; } static void stex_slave_destroy(struct scsi_device *sdev) { - struct st_hba *hba = (struct st_hba *) sdev->host->hostdata; - struct req_msg *req; - unsigned long flags; - unsigned long before; - u16 tag; - - if (sdev->type != TYPE_DISK) - return; - - before = jiffies; - while ((tag = stex_alloc_tag(hba, (unsigned long *)&hba->tag)) - == TAG_BITMAP_LENGTH) { - if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) - return; - msleep(10); - } - - spin_lock_irqsave(hba->host->host_lock, flags); - req = stex_alloc_req(hba); - memset(req->cdb, 0, STEX_CDB_LENGTH); - - req->target = sdev->id; - req->lun = sdev->channel; /* firmware lun issue work around */ - req->cdb[0] = SYNCHRONIZE_CACHE; - - hba->ccb[tag].cmd = NULL; - hba->ccb[tag].sg_count = 0; - hba->ccb[tag].sense_bufflen = 0; - hba->ccb[tag].sense_buffer = NULL; - hba->ccb[tag].req_type |= PASSTHRU_REQ_TYPE; - - stex_send_cmd(hba, req, tag); - spin_unlock_irqrestore(hba->host->host_lock, flags); - - wait_event_timeout(hba->waitq, - !(hba->ccb[tag].req_type), ST_INTERNAL_TIMEOUT * HZ); - if (hba->ccb[tag].req_type & PASSTHRU_REQ_TYPE) - return; - - stex_free_tag(hba, (unsigned long *)&hba->tag, tag); + scsi_deactivate_tcq(sdev, 1); } static int @@ -650,8 +589,9 @@ stex_queuecommand(struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *)) cmd->scsi_done = done; - if (unlikely((tag = __stex_alloc_tag((unsigned long *)&hba->tag)) - == TAG_BITMAP_LENGTH)) + tag = cmd->request->tag; + + if (unlikely(tag >= host->can_queue)) return SCSI_MLQUEUE_HOST_BUSY; req = stex_alloc_req(hba); @@ -771,26 +711,18 @@ static void stex_mu_intr(struct st_hba *hba, u32 doorbell) while (hba->status_tail != hba->status_head) { resp = stex_get_status(hba); tag = le16_to_cpu(resp->tag); - if (unlikely(tag >= TAG_BITMAP_LENGTH)) { + if (unlikely(tag >= hba->host->can_queue)) { printk(KERN_WARNING DRV_NAME "(%s): invalid tag\n", pci_name(hba->pdev)); continue; } - if (unlikely((hba->tag & (1 << tag)) == 0)) { - printk(KERN_WARNING DRV_NAME - "(%s): null tag\n", pci_name(hba->pdev)); - continue; - } - hba->out_req_cnt--; ccb = &hba->ccb[tag]; if (hba->wait_ccb == ccb) hba->wait_ccb = NULL; if (unlikely(ccb->req == NULL)) { printk(KERN_WARNING DRV_NAME "(%s): lagging req\n", pci_name(hba->pdev)); - __stex_free_tag((unsigned long *)&hba->tag, tag); - stex_unmap_sg(hba, ccb->cmd); /* ??? */ continue; } @@ -808,7 +740,15 @@ static void stex_mu_intr(struct st_hba *hba, u32 doorbell) ccb->srb_status = resp->srb_status; ccb->scsi_status = resp->scsi_status; - if (ccb->req_type & PASSTHRU_REQ_TYPE) { + if (likely(ccb->cmd != NULL)) { + if (unlikely(ccb->cmd->cmnd[0] == PASSTHRU_CMD && + ccb->cmd->cmnd[1] == PASSTHRU_GET_ADAPTER)) + stex_controller_info(hba, ccb); + stex_unmap_sg(hba, ccb->cmd); + stex_scsi_done(ccb); + hba->out_req_cnt--; + } else if (ccb->req_type & PASSTHRU_REQ_TYPE) { + hba->out_req_cnt--; if (ccb->req_type & PASSTHRU_REQ_NO_WAKEUP) { ccb->req_type = 0; continue; @@ -816,14 +756,7 @@ static void stex_mu_intr(struct st_hba *hba, u32 doorbell) ccb->req_type = 0; if (waitqueue_active(&hba->waitq)) wake_up(&hba->waitq); - continue; } - if (ccb->cmd->cmnd[0] == PASSTHRU_CMD && - ccb->cmd->cmnd[1] == PASSTHRU_GET_ADAPTER) - stex_controller_info(hba, ccb); - __stex_free_tag((unsigned long *)&hba->tag, tag); - stex_unmap_sg(hba, ccb->cmd); - stex_scsi_done(ccb); } update_status: @@ -933,21 +866,24 @@ static int stex_abort(struct scsi_cmnd *cmd) { struct Scsi_Host *host = cmd->device->host; struct st_hba *hba = (struct st_hba *)host->hostdata; - u16 tag; + u16 tag = cmd->request->tag; void __iomem *base; u32 data; int result = SUCCESS; unsigned long flags; base = hba->mmio_base; spin_lock_irqsave(host->host_lock, flags); - - for (tag = 0; tag < MU_MAX_REQUEST; tag++) - if (hba->ccb[tag].cmd == cmd && (hba->tag & (1 << tag))) { - hba->wait_ccb = &(hba->ccb[tag]); - break; - } - if (tag >= MU_MAX_REQUEST) - goto out; + if (tag < host->can_queue && hba->ccb[tag].cmd == cmd) + hba->wait_ccb = &hba->ccb[tag]; + else { + for (tag = 0; tag < host->can_queue; tag++) + if (hba->ccb[tag].cmd == cmd) { + hba->wait_ccb = &hba->ccb[tag]; + break; + } + if (tag >= host->can_queue) + goto out; + } data = readl(base + ODBL); if (data == 0 || data == 0xffffffff) @@ -965,6 +901,7 @@ static int stex_abort(struct scsi_cmnd *cmd) } fail_out: + stex_unmap_sg(hba, cmd); hba->wait_ccb->req = NULL; /* nullify the req's future return */ hba->wait_ccb = NULL; result = FAILED; @@ -1025,7 +962,6 @@ static int stex_reset(struct scsi_cmnd *cmd) return FAILED; } spin_lock_irqsave(hba->host->host_lock, flags); - hba->tag = 0; hba->req_head = 0; hba->req_tail = 0; hba->status_head = 0; @@ -1061,6 +997,7 @@ static struct scsi_host_template driver_template = { .proc_name = DRV_NAME, .bios_param = stex_biosparam, .queuecommand = stex_queuecommand, + .slave_alloc = stex_slave_alloc, .slave_configure = stex_slave_config, .slave_destroy = stex_slave_destroy, .eh_abort_handler = stex_abort, @@ -1171,6 +1108,14 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err) goto out_free_irq; + scsi_init_shared_tag_map(host, ST_CAN_QUEUE); + if (host->bqt == NULL) { + err = -ENOMEM; + printk(KERN_ERR DRV_NAME "(%s): init shared queue failed\n", + pci_name(pdev)); + goto out_free_irq; + } + pci_set_drvdata(pdev, hba); err = scsi_add_host(host, &pdev->dev); @@ -1206,15 +1151,7 @@ static void stex_hba_stop(struct st_hba *hba) struct req_msg *req; unsigned long flags; unsigned long before; - u16 tag; - - before = jiffies; - while ((tag = stex_alloc_tag(hba, (unsigned long *)&hba->tag)) - == TAG_BITMAP_LENGTH) { - if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) - return; - msleep(10); - } + u16 tag = 0; spin_lock_irqsave(hba->host->host_lock, flags); req = stex_alloc_req(hba); @@ -1233,12 +1170,12 @@ static void stex_hba_stop(struct st_hba *hba) stex_send_cmd(hba, req, tag); spin_unlock_irqrestore(hba->host->host_lock, flags); - wait_event_timeout(hba->waitq, - !(hba->ccb[tag].req_type), ST_INTERNAL_TIMEOUT * HZ); - if (hba->ccb[tag].req_type & PASSTHRU_REQ_TYPE) - return; - - stex_free_tag(hba, (unsigned long *)&hba->tag, tag); + before = jiffies; + while (hba->ccb[tag].req_type & PASSTHRU_REQ_TYPE) { + if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) + return; + msleep(10); + } } static void stex_hba_free(struct st_hba *hba) -- cgit v1.2.3 From deb81d80ba27da8dfabc29ccb5977db8f4942a0a Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 1 Sep 2006 09:28:48 -0400 Subject: [SCSI] add failure return to scsi_init_shared_tag_map() And use it in the stex driver. Signed-off-by: James Bottomley --- drivers/scsi/stex.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index 15fb99f224e..3cf3106a29b 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c @@ -1108,9 +1108,8 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err) goto out_free_irq; - scsi_init_shared_tag_map(host, ST_CAN_QUEUE); - if (host->bqt == NULL) { - err = -ENOMEM; + err = scsi_init_shared_tag_map(host, ST_CAN_QUEUE); + if (err) { printk(KERN_ERR DRV_NAME "(%s): init shared queue failed\n", pci_name(pdev)); goto out_free_irq; -- cgit v1.2.3 From 84314fd4740ad73550c76dee4a9578979d84af48 Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 18 Aug 2006 17:30:09 -0400 Subject: [SCSI] SCSI and FC Transport: add netlink support for posting of transport events This patch formally adds support for the posting of FC events via netlink. It is a followup to the original RFC at: http://marc.theaimsgroup.com/?l=linux-scsi&m=114530667923464&w=2 and the initial posting at: http://marc.theaimsgroup.com/?l=linux-scsi&m=115507374832500&w=2 The patch has been updated to optimize the send path, per the discussions in the initial posting. Per discussions at the Storage Summit and at OLS, we are to use netlink for async events from transports. Also per discussions, to avoid a netlink protocol per transport, I've create a single NETLINK_SCSITRANSPORT protocol, which can then be used by all transports. This patch: - Creates new files scsi_netlink.c and scsi_netlink.h, which contains the single and shared definitions for the SCSI Transport. It is tied into the base SCSI subsystem intialization. Contains a single interface routine, scsi_send_transport_event(), for a transport to send an event (via multicast to a protocol specific group). - Creates a new scsi_netlink_fc.h file, which contains the FC netlink event messages - Adds 3 new routines to the fc transport: fc_get_event_number() - to get a FC event # fc_host_post_event() - to send a simple FC event (32 bits of data) fc_host_post_vendor_event() - to send a Vendor unique event, with arbitrary amounts of data. Note: the separation of event number allows for a LLD to send a standard event, followed by vendor-specific data for the event. Note: This patch assumes 2 prior fc transport patches have been installed: http://marc.theaimsgroup.com/?l=linux-scsi&m=115555807316329&w=2 http://marc.theaimsgroup.com/?l=linux-scsi&m=115581614930261&w=2 Sorry - next time I'll do something like making these individual patches of the same posting when I know they'll be posted closely together. Signed-off-by: James Smart Tidy up configuration not to make SCSI always select NET Signed-off-by: James Bottomley --- drivers/scsi/Kconfig | 6 ++ drivers/scsi/Makefile | 1 + drivers/scsi/scsi.c | 3 + drivers/scsi/scsi_netlink.c | 199 ++++++++++++++++++++++++++++++++++++++ drivers/scsi/scsi_priv.h | 11 +++ drivers/scsi/scsi_transport_fc.c | 200 ++++++++++++++++++++++++++++++++++++++- 6 files changed, 419 insertions(+), 1 deletion(-) create mode 100644 drivers/scsi/scsi_netlink.c (limited to 'drivers/scsi') diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index c8c606589ea..4d1998d23f0 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -27,6 +27,11 @@ config SCSI However, do not compile this as a module if your root file system (the one containing the directory /) is located on a SCSI device. +config SCSI_NETLINK + tristate + default n + select NET + config SCSI_PROC_FS bool "legacy /proc/scsi/ support" depends on SCSI && PROC_FS @@ -222,6 +227,7 @@ config SCSI_SPI_ATTRS config SCSI_FC_ATTRS tristate "FiberChannel Transport Attributes" depends on SCSI + select SCSI_NETLINK help If you wish to export transport-specific information about each attached FiberChannel device to sysfs, say Y. diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index fd9aeb1ba07..8fc2c594b53 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -159,6 +159,7 @@ scsi_mod-y += scsi.o hosts.o scsi_ioctl.o constants.o \ scsicam.o scsi_error.o scsi_lib.o \ scsi_scan.o scsi_sysfs.o \ scsi_devinfo.o +scsi_mod-$(CONFIG_SCSI_NETLINK) += scsi_netlink.o scsi_mod-$(CONFIG_SYSCTL) += scsi_sysctl.o scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 37843927e47..eedfd059b82 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -1118,6 +1118,8 @@ static int __init init_scsi(void) for_each_possible_cpu(i) INIT_LIST_HEAD(&per_cpu(scsi_done_q, i)); + scsi_netlink_init(); + printk(KERN_NOTICE "SCSI subsystem initialized\n"); return 0; @@ -1138,6 +1140,7 @@ cleanup_queue: static void __exit exit_scsi(void) { + scsi_netlink_exit(); scsi_sysfs_unregister(); scsi_exit_sysctl(); scsi_exit_hosts(); diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c new file mode 100644 index 00000000000..1b59b27e887 --- /dev/null +++ b/drivers/scsi/scsi_netlink.c @@ -0,0 +1,199 @@ +/* + * scsi_netlink.c - SCSI Transport Netlink Interface + * + * Copyright (C) 2006 James Smart, Emulex Corporation + * + * 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 of the License, 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; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include + +#include +#include "scsi_priv.h" + +struct sock *scsi_nl_sock = NULL; +EXPORT_SYMBOL_GPL(scsi_nl_sock); + + +/** + * scsi_nl_rcv_msg - + * Receive message handler. Extracts message from a receive buffer. + * Validates message header and calls appropriate transport message handler + * + * @skb: socket receive buffer + * + **/ +static void +scsi_nl_rcv_msg(struct sk_buff *skb) +{ + struct nlmsghdr *nlh; + struct scsi_nl_hdr *hdr; + uint32_t rlen; + int err; + + while (skb->len >= NLMSG_SPACE(0)) { + err = 0; + + nlh = (struct nlmsghdr *) skb->data; + if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) || + (skb->len < nlh->nlmsg_len)) { + printk(KERN_WARNING "%s: discarding partial skb\n", + __FUNCTION__); + return; + } + + rlen = NLMSG_ALIGN(nlh->nlmsg_len); + if (rlen > skb->len) + rlen = skb->len; + + if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) { + err = -EBADMSG; + goto next_msg; + } + + hdr = NLMSG_DATA(nlh); + if ((hdr->version != SCSI_NL_VERSION) || + (hdr->magic != SCSI_NL_MAGIC)) { + err = -EPROTOTYPE; + goto next_msg; + } + + if (security_netlink_recv(skb, CAP_SYS_ADMIN)) { + err = -EPERM; + goto next_msg; + } + + if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) { + printk(KERN_WARNING "%s: discarding partial message\n", + __FUNCTION__); + return; + } + + /* + * We currently don't support anyone sending us a message + */ + +next_msg: + if ((err) || (nlh->nlmsg_flags & NLM_F_ACK)) + netlink_ack(skb, nlh, err); + + skb_pull(skb, rlen); + } +} + + +/** + * scsi_nl_rcv_msg - + * Receive handler for a socket. Extracts a received message buffer from + * the socket, and starts message processing. + * + * @sk: socket + * @len: unused + * + **/ +static void +scsi_nl_rcv(struct sock *sk, int len) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&sk->sk_receive_queue))) { + scsi_nl_rcv_msg(skb); + kfree_skb(skb); + } +} + + +/** + * scsi_nl_rcv_event - + * Event handler for a netlink socket. + * + * @this: event notifier block + * @event: event type + * @ptr: event payload + * + **/ +static int +scsi_nl_rcv_event(struct notifier_block *this, unsigned long event, void *ptr) +{ + struct netlink_notify *n = ptr; + + if (n->protocol != NETLINK_SCSITRANSPORT) + return NOTIFY_DONE; + + /* + * Currently, we are not tracking PID's, etc. There is nothing + * to handle. + */ + + return NOTIFY_DONE; +} + +static struct notifier_block scsi_netlink_notifier = { + .notifier_call = scsi_nl_rcv_event, +}; + + +/** + * scsi_netlink_init - + * Called by SCSI subsystem to intialize the SCSI transport netlink + * interface + * + **/ +void +scsi_netlink_init(void) +{ + int error; + + error = netlink_register_notifier(&scsi_netlink_notifier); + if (error) { + printk(KERN_ERR "%s: register of event handler failed - %d\n", + __FUNCTION__, error); + return; + } + + scsi_nl_sock = netlink_kernel_create(NETLINK_SCSITRANSPORT, + SCSI_NL_GRP_CNT, scsi_nl_rcv, THIS_MODULE); + if (!scsi_nl_sock) { + printk(KERN_ERR "%s: register of recieve handler failed\n", + __FUNCTION__); + netlink_unregister_notifier(&scsi_netlink_notifier); + } + + return; +} + + +/** + * scsi_netlink_exit - + * Called by SCSI subsystem to disable the SCSI transport netlink + * interface + * + **/ +void +scsi_netlink_exit(void) +{ + if (scsi_nl_sock) { + sock_release(scsi_nl_sock->sk_socket); + netlink_unregister_notifier(&scsi_netlink_notifier); + } + + return; +} + + diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index ae24c85aaee..5d023d44e5e 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -8,6 +8,7 @@ struct scsi_cmnd; struct scsi_device; struct scsi_host_template; struct Scsi_Host; +struct scsi_nl_hdr; /* @@ -110,6 +111,16 @@ extern void __scsi_remove_device(struct scsi_device *); extern struct bus_type scsi_bus_type; +/* scsi_netlink.c */ +#ifdef CONFIG_SCSI_NETLINK +extern void scsi_netlink_init(void); +extern void scsi_netlink_exit(void); +extern struct sock *scsi_nl_sock; +#else +static inline void scsi_netlink_init(void) {} +static inline void scsi_netlink_exit(void) {} +#endif + /* * internal scsi timeout functions: for use by mid-layer and transport * classes. diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 79d31ca2b74..05989f13055 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -32,6 +32,9 @@ #include #include #include +#include +#include +#include #include "scsi_priv.h" static int fc_queue_work(struct Scsi_Host *, struct work_struct *); @@ -93,6 +96,29 @@ fc_enum_name_search(port_type, fc_port_type, fc_port_type_names) #define FC_PORTTYPE_MAX_NAMELEN 50 +/* Convert fc_host_event_code values to ascii string name */ +static const struct { + enum fc_host_event_code value; + char *name; +} fc_host_event_code_names[] = { + { FCH_EVT_LIP, "lip" }, + { FCH_EVT_LINKUP, "link_up" }, + { FCH_EVT_LINKDOWN, "link_down" }, + { FCH_EVT_LIPRESET, "lip_reset" }, + { FCH_EVT_RSCN, "rscn" }, + { FCH_EVT_ADAPTER_CHANGE, "adapter_chg" }, + { FCH_EVT_PORT_UNKNOWN, "port_unknown" }, + { FCH_EVT_PORT_ONLINE, "port_online" }, + { FCH_EVT_PORT_OFFLINE, "port_offline" }, + { FCH_EVT_PORT_FABRIC, "port_fabric" }, + { FCH_EVT_LINK_UNKNOWN, "link_unknown" }, + { FCH_EVT_VENDOR_UNIQUE, "vendor_unique" }, +}; +fc_enum_name_search(host_event_code, fc_host_event_code, + fc_host_event_code_names) +#define FC_HOST_EVENT_CODE_MAX_NAMELEN 30 + + /* Convert fc_port_state values to ascii string name */ static struct { enum fc_port_state value; @@ -377,10 +403,182 @@ MODULE_PARM_DESC(dev_loss_tmo, " exceeded, the scsi target is removed. Value should be" " between 1 and SCSI_DEVICE_BLOCK_MAX_TIMEOUT."); +/** + * Netlink Infrastructure + **/ + +static atomic_t fc_event_seq; + +/** + * fc_get_event_number - Obtain the next sequential FC event number + * + * Notes: + * We could have inline'd this, but it would have required fc_event_seq to + * be exposed. For now, live with the subroutine call. + * Atomic used to avoid lock/unlock... + **/ +u32 +fc_get_event_number(void) +{ + return atomic_add_return(1, &fc_event_seq); +} +EXPORT_SYMBOL(fc_get_event_number); + + +/** + * fc_host_post_event - called to post an even on an fc_host. + * + * @shost: host the event occurred on + * @event_number: fc event number obtained from get_fc_event_number() + * @event_code: fc_host event being posted + * @event_data: 32bits of data for the event being posted + * + * Notes: + * This routine assumes no locks are held on entry. + **/ +void +fc_host_post_event(struct Scsi_Host *shost, u32 event_number, + enum fc_host_event_code event_code, u32 event_data) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + struct fc_nl_event *event; + const char *name; + u32 len, skblen; + int err; + + if (!scsi_nl_sock) { + err = -ENOENT; + goto send_fail; + } + + len = FC_NL_MSGALIGN(sizeof(*event)); + skblen = NLMSG_SPACE(len); + + skb = alloc_skb(skblen, GFP_KERNEL); + if (!skb) { + err = -ENOBUFS; + goto send_fail; + } + + nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG, + skblen - sizeof(*nlh), 0); + if (!nlh) { + err = -ENOBUFS; + goto send_fail_skb; + } + event = NLMSG_DATA(nlh); + + INIT_SCSI_NL_HDR(&event->snlh, SCSI_NL_TRANSPORT_FC, + FC_NL_ASYNC_EVENT, len); + event->seconds = get_seconds(); + event->vendor_id = 0; + event->host_no = shost->host_no; + event->event_datalen = sizeof(u32); /* bytes */ + event->event_num = event_number; + event->event_code = event_code; + event->event_data = event_data; + + err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS); + if (err && (err != -ESRCH)) /* filter no recipient errors */ + /* nlmsg_multicast already kfree_skb'd */ + goto send_fail; + + return; + +send_fail_skb: + kfree_skb(skb); +send_fail: + name = get_fc_host_event_code_name(event_code); + printk(KERN_WARNING + "%s: Dropped Event : host %d %s data 0x%08x - err %d\n", + __FUNCTION__, shost->host_no, + (name) ? name : "", event_data, err); + return; +} +EXPORT_SYMBOL(fc_host_post_event); + + +/** + * fc_host_post_vendor_event - called to post a vendor unique event on + * a fc_host + * + * @shost: host the event occurred on + * @event_number: fc event number obtained from get_fc_event_number() + * @data_len: amount, in bytes, of vendor unique data + * @data_buf: pointer to vendor unique data + * + * Notes: + * This routine assumes no locks are held on entry. + **/ +void +fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number, + u32 data_len, char * data_buf, u32 vendor_id) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + struct fc_nl_event *event; + u32 len, skblen; + int err; + + if (!scsi_nl_sock) { + err = -ENOENT; + goto send_vendor_fail; + } + + len = FC_NL_MSGALIGN(sizeof(*event) + data_len); + skblen = NLMSG_SPACE(len); + + skb = alloc_skb(skblen, GFP_KERNEL); + if (!skb) { + err = -ENOBUFS; + goto send_vendor_fail; + } + + nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG, + skblen - sizeof(*nlh), 0); + if (!nlh) { + err = -ENOBUFS; + goto send_vendor_fail_skb; + } + event = NLMSG_DATA(nlh); + + INIT_SCSI_NL_HDR(&event->snlh, SCSI_NL_TRANSPORT_FC, + FC_NL_ASYNC_EVENT, len); + event->seconds = get_seconds(); + event->vendor_id = vendor_id; + event->host_no = shost->host_no; + event->event_datalen = data_len; /* bytes */ + event->event_num = event_number; + event->event_code = FCH_EVT_VENDOR_UNIQUE; + memcpy(&event->event_data, data_buf, data_len); + + err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS); + if (err && (err != -ESRCH)) /* filter no recipient errors */ + /* nlmsg_multicast already kfree_skb'd */ + goto send_vendor_fail; + + return; + +send_vendor_fail_skb: + kfree_skb(skb); +send_vendor_fail: + printk(KERN_WARNING + "%s: Dropped Event : host %d vendor_unique - err %d\n", + __FUNCTION__, shost->host_no, err); + return; +} +EXPORT_SYMBOL(fc_host_post_vendor_event); + + static __init int fc_transport_init(void) { - int error = transport_class_register(&fc_host_class); + int error; + + atomic_set(&fc_event_seq, 0); + + error = transport_class_register(&fc_host_class); if (error) return error; error = transport_class_register(&fc_rport_class); -- cgit v1.2.3 From f14e2e29cdd07f80de6dec168dc2bb39de37eec3 Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 22 Aug 2006 09:55:23 -0400 Subject: [SCSI] SCSI & FC transport: extend event vendor id's to 64bits During discussions with Mike Christie, I became convinced that we needed a larger vendor id. This patch extends the id from 32 to 64 bits. This applies on top of the prior patches that add SCSI transport events via netlink. Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_fc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 05989f13055..293188cbff8 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -513,7 +513,7 @@ EXPORT_SYMBOL(fc_host_post_event); **/ void fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number, - u32 data_len, char * data_buf, u32 vendor_id) + u32 data_len, char * data_buf, u64 vendor_id) { struct sk_buff *skb; struct nlmsghdr *nlh; -- cgit v1.2.3 From d2873e4c1ef293ee6d66456fb84448e258a487fa Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 18 Aug 2006 17:46:43 -0400 Subject: [SCSI] lpfc 8.1.10 : Add support to post events via new FC event interfaces Add support to post events via new FC event interfaces Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc.h | 2 ++ drivers/scsi/lpfc/lpfc_els.c | 5 +++++ drivers/scsi/lpfc/lpfc_hbadisc.c | 6 ++++++ drivers/scsi/lpfc/lpfc_init.c | 6 ++++++ 4 files changed, 19 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index d44f9aac6b8..4a4048d7ba9 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -391,3 +391,5 @@ struct rnidrsp { struct list_head list; uint32_t data; }; + +#define FC_REG_DUMP_EVENT 0x10 /* Register for Dump events */ diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 3567de61316..71864cdc6c7 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -2506,6 +2506,7 @@ lpfc_els_rcv_rscn(struct lpfc_hba * phba, uint32_t *lp; IOCB_t *icmd; uint32_t payload_len, cmd; + int i; icmd = &cmdiocb->iocb; pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; @@ -2524,6 +2525,10 @@ lpfc_els_rcv_rscn(struct lpfc_hba * phba, phba->brd_no, phba->fc_flag, payload_len, *lp, phba->fc_rscn_id_cnt); + for (i = 0; i < payload_len/sizeof(uint32_t); i++) + fc_host_post_event(phba->host, fc_get_event_number(), + FCH_EVT_RSCN, lp[i]); + /* If we are about to begin discovery, just ACC the RSCN. * Discovery processing will satisfy it. */ diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index b2f1552f184..53821e5778b 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -340,6 +340,9 @@ lpfc_linkdown(struct lpfc_hba * phba) spin_unlock_irq(phba->host->host_lock); } + fc_host_post_event(phba->host, fc_get_event_number(), + FCH_EVT_LINKDOWN, 0); + /* Clean up any firmware default rpi's */ if ((mb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL))) { lpfc_unreg_did(phba, 0xffffffff, mb); @@ -427,6 +430,9 @@ lpfc_linkup(struct lpfc_hba * phba) struct list_head *listp, *node_list[7]; int i; + fc_host_post_event(phba->host, fc_get_event_number(), + FCH_EVT_LINKUP, 0); + spin_lock_irq(phba->host->host_lock); phba->hba_state = LPFC_LINK_UP; phba->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI | FC_ABORT_DISCOVERY | diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index f6948ffe689..84e7fc595f5 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -511,6 +511,7 @@ lpfc_handle_eratt(struct lpfc_hba * phba) { struct lpfc_sli *psli = &phba->sli; struct lpfc_sli_ring *pring; + uint32_t event_data; if (phba->work_hs & HS_FFER6) { /* Re-establishing Link */ @@ -555,6 +556,11 @@ lpfc_handle_eratt(struct lpfc_hba * phba) phba->brd_no, phba->work_hs, phba->work_status[0], phba->work_status[1]); + event_data = FC_REG_DUMP_EVENT; + fc_host_post_vendor_event(phba->host, fc_get_event_number(), + sizeof(event_data), (char *) &event_data, + SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX); + psli->sli_flag &= ~LPFC_SLI2_ACTIVE; lpfc_offline(phba); phba->hba_state = LPFC_HBA_ERROR; -- cgit v1.2.3 From ae36764a230ff6a278ed93735acf5fcda08f2786 Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 18 Aug 2006 17:46:53 -0400 Subject: [SCSI] lpfc 8.1.10 : Add support to return adapter symbolic name Add support to return adapter symbolic name (now that attribute is dynamic) Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_attr.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index d384c16f4a8..c6d683d86cf 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -1204,6 +1204,15 @@ lpfc_get_host_fabric_name (struct Scsi_Host *shost) fc_host_fabric_name(shost) = node_name; } +static void +lpfc_get_host_symbolic_name (struct Scsi_Host *shost) +{ + struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata; + + spin_lock_irq(shost->host_lock); + lpfc_get_hba_sym_node_name(phba, fc_host_symbolic_name(shost)); + spin_unlock_irq(shost->host_lock); +} static struct fc_host_statistics * lpfc_get_stats(struct Scsi_Host *shost) @@ -1486,7 +1495,6 @@ struct fc_function_template lpfc_transport_functions = { .show_host_port_name = 1, .show_host_supported_classes = 1, .show_host_supported_fc4s = 1, - .show_host_symbolic_name = 1, .show_host_supported_speeds = 1, .show_host_maxframe_size = 1, @@ -1509,6 +1517,9 @@ struct fc_function_template lpfc_transport_functions = { .get_host_fabric_name = lpfc_get_host_fabric_name, .show_host_fabric_name = 1, + .get_host_symbolic_name = lpfc_get_host_symbolic_name, + .show_host_symbolic_name = 1, + /* * The LPFC driver treats linkdown handling as target loss events * so there are no sysfs handlers for link_down_tmo. -- cgit v1.2.3 From 0f29b966d60e9a4f5ecff9f3832257b38aea4f13 Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 18 Aug 2006 17:33:29 -0400 Subject: [SCSI] FC transport: Add dev_loss_tmo callbacks, and new fast_io_fail_tmo w/ callback This patch adds the following functionality to the FC transport: - dev_loss_tmo LLDD callback : Called to essentially confirm the deletion of an rport. Thus, it is called whenever the dev_loss_tmo fires, or when the rport is deleted due to other circumstances (module unload, etc). It is expected that the callback will initiate the termination of any outstanding i/o on the rport. - fast_io_fail_tmo and LLD callback: There are some cases where it may take a long while to truly determine device loss, but the system is in a multipathing configuration that if the i/o was failed quickly (faster than dev_loss_tmo), it could be redirected to a different path and completed sooner. Many thanks to Mike Reed who cleaned up the initial RFC in support of this post. The original RFC is at: http://marc.theaimsgroup.com/?l=linux-scsi&m=115505981027246&w=2 Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_fc.c | 134 +++++++++++++++++++++++++++++++++++---- 1 file changed, 123 insertions(+), 11 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 293188cbff8..4ab176ed480 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -242,6 +242,7 @@ fc_bitfield_name_search(remote_port_roles, fc_remote_port_role_names) static void fc_timeout_deleted_rport(void *data); +static void fc_timeout_fail_rport_io(void *data); static void fc_scsi_scan_rport(void *data); /* @@ -249,7 +250,7 @@ static void fc_scsi_scan_rport(void *data); * Increase these values if you add attributes */ #define FC_STARGET_NUM_ATTRS 3 -#define FC_RPORT_NUM_ATTRS 9 +#define FC_RPORT_NUM_ATTRS 10 #define FC_HOST_NUM_ATTRS 17 struct fc_internal { @@ -622,11 +623,14 @@ store_fc_rport_##field(struct class_device *cdev, const char *buf, \ struct fc_rport *rport = transport_class_to_rport(cdev); \ struct Scsi_Host *shost = rport_to_shost(rport); \ struct fc_internal *i = to_fc_internal(shost->transportt); \ + char *cp; \ if ((rport->port_state == FC_PORTSTATE_BLOCKED) || \ (rport->port_state == FC_PORTSTATE_DELETED) || \ (rport->port_state == FC_PORTSTATE_NOTPRESENT)) \ return -EBUSY; \ - val = simple_strtoul(buf, NULL, 0); \ + val = simple_strtoul(buf, &cp, 0); \ + if (*cp && (*cp != '\n')) \ + return -EINVAL; \ i->f->set_rport_##field(rport, val); \ return count; \ } @@ -708,6 +712,13 @@ static FC_CLASS_DEVICE_ATTR(rport, title, S_IRUGO, \ if (i->f->show_rport_##field) \ count++ +#define SETUP_PRIVATE_RPORT_ATTRIBUTE_RW(field) \ +{ \ + i->private_rport_attrs[count] = class_device_attr_rport_##field; \ + i->rport_attrs[count] = &i->private_rport_attrs[count]; \ + count++; \ +} + /* The FC Transport Remote Port Attributes: */ @@ -740,12 +751,14 @@ store_fc_rport_dev_loss_tmo(struct class_device *cdev, const char *buf, struct fc_rport *rport = transport_class_to_rport(cdev); struct Scsi_Host *shost = rport_to_shost(rport); struct fc_internal *i = to_fc_internal(shost->transportt); + char *cp; if ((rport->port_state == FC_PORTSTATE_BLOCKED) || (rport->port_state == FC_PORTSTATE_DELETED) || (rport->port_state == FC_PORTSTATE_NOTPRESENT)) return -EBUSY; - val = simple_strtoul(buf, NULL, 0); - if ((val < 0) || (val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)) + val = simple_strtoul(buf, &cp, 0); + if ((*cp && (*cp != '\n')) || + (val < 0) || (val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)) return -EINVAL; i->f->set_rport_dev_loss_tmo(rport, val); return count; @@ -795,6 +808,44 @@ static FC_CLASS_DEVICE_ATTR(rport, roles, S_IRUGO, fc_private_rport_rd_enum_attr(port_state, FC_PORTSTATE_MAX_NAMELEN); fc_private_rport_rd_attr(scsi_target_id, "%d\n", 20); +/* + * fast_io_fail_tmo attribute + */ +static ssize_t +show_fc_rport_fast_io_fail_tmo (struct class_device *cdev, char *buf) +{ + struct fc_rport *rport = transport_class_to_rport(cdev); + + if (rport->fast_io_fail_tmo == -1) + return snprintf(buf, 5, "off\n"); + return snprintf(buf, 20, "%d\n", rport->fast_io_fail_tmo); +} + +static ssize_t +store_fc_rport_fast_io_fail_tmo(struct class_device *cdev, const char *buf, + size_t count) +{ + int val; + char *cp; + struct fc_rport *rport = transport_class_to_rport(cdev); + + if ((rport->port_state == FC_PORTSTATE_BLOCKED) || + (rport->port_state == FC_PORTSTATE_DELETED) || + (rport->port_state == FC_PORTSTATE_NOTPRESENT)) + return -EBUSY; + if (strncmp(buf, "off", 3) == 0) + rport->fast_io_fail_tmo = -1; + else { + val = simple_strtoul(buf, &cp, 0); + if ((*cp && (*cp != '\n')) || + (val < 0) || (val >= rport->dev_loss_tmo)) + return -EINVAL; + rport->fast_io_fail_tmo = val; + } + return count; +} +static FC_CLASS_DEVICE_ATTR(rport, fast_io_fail_tmo, S_IRUGO | S_IWUSR, + show_fc_rport_fast_io_fail_tmo, store_fc_rport_fast_io_fail_tmo); /* @@ -880,8 +931,11 @@ store_fc_host_##field(struct class_device *cdev, const char *buf, \ int val; \ struct Scsi_Host *shost = transport_class_to_shost(cdev); \ struct fc_internal *i = to_fc_internal(shost->transportt); \ + char *cp; \ \ - val = simple_strtoul(buf, NULL, 0); \ + val = simple_strtoul(buf, &cp, 0); \ + if (*cp && (*cp != '\n')) \ + return -EINVAL; \ i->f->set_host_##field(shost, val); \ return count; \ } @@ -1481,6 +1535,8 @@ fc_attach_transport(struct fc_function_template *ft) SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(roles); SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(port_state); SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(scsi_target_id); + if (ft->terminate_rport_io) + SETUP_PRIVATE_RPORT_ATTRIBUTE_RW(fast_io_fail_tmo); BUG_ON(count > FC_RPORT_NUM_ATTRS); @@ -1552,7 +1608,7 @@ fc_flush_work(struct Scsi_Host *shost) * @delay: jiffies to delay the work queuing * * Return value: - * 0 on success / != 0 for error + * 1 on success / 0 already queued / < 0 for error **/ static int fc_queue_devloss_work(struct Scsi_Host *shost, struct work_struct *work, @@ -1567,6 +1623,9 @@ fc_queue_devloss_work(struct Scsi_Host *shost, struct work_struct *work, return -EINVAL; } + if (delay == 0) + return queue_work(fc_host_devloss_work_q(shost), work); + return queue_delayed_work(fc_host_devloss_work_q(shost), work, delay); } @@ -1659,10 +1718,23 @@ fc_starget_delete(void *data) struct fc_rport *rport = (struct fc_rport *)data; struct Scsi_Host *shost = rport_to_shost(rport); unsigned long flags; + struct fc_internal *i = to_fc_internal(shost->transportt); + + /* + * Involve the LLDD if possible. All io on the rport is to + * be terminated, either as part of the dev_loss_tmo callback + * processing, or via the terminate_rport_io function. + */ + if (i->f->dev_loss_tmo_callbk) + i->f->dev_loss_tmo_callbk(rport); + else if (i->f->terminate_rport_io) + i->f->terminate_rport_io(rport); spin_lock_irqsave(shost->host_lock, flags); if (rport->flags & FC_RPORT_DEVLOSS_PENDING) { spin_unlock_irqrestore(shost->host_lock, flags); + if (!cancel_delayed_work(&rport->fail_io_work)) + fc_flush_devloss(shost); if (!cancel_delayed_work(&rport->dev_loss_work)) fc_flush_devloss(shost); spin_lock_irqsave(shost->host_lock, flags); @@ -1685,10 +1757,7 @@ fc_rport_final_delete(void *data) struct fc_rport *rport = (struct fc_rport *)data; struct device *dev = &rport->dev; struct Scsi_Host *shost = rport_to_shost(rport); - - /* Delete SCSI target and sdevs */ - if (rport->scsi_target_id != -1) - fc_starget_delete(data); + struct fc_internal *i = to_fc_internal(shost->transportt); /* * if a scan is pending, flush the SCSI Host work_q so that @@ -1697,6 +1766,14 @@ fc_rport_final_delete(void *data) if (rport->flags & FC_RPORT_SCAN_PENDING) scsi_flush_work(shost); + /* Delete SCSI target and sdevs */ + if (rport->scsi_target_id != -1) + fc_starget_delete(data); + else if (i->f->dev_loss_tmo_callbk) + i->f->dev_loss_tmo_callbk(rport); + else if (i->f->terminate_rport_io) + i->f->terminate_rport_io(rport); + transport_remove_device(dev); device_del(dev); transport_destroy_device(dev); @@ -1748,8 +1825,10 @@ fc_rport_create(struct Scsi_Host *shost, int channel, if (fci->f->dd_fcrport_size) rport->dd_data = &rport[1]; rport->channel = channel; + rport->fast_io_fail_tmo = -1; INIT_WORK(&rport->dev_loss_work, fc_timeout_deleted_rport, rport); + INIT_WORK(&rport->fail_io_work, fc_timeout_fail_rport_io, rport); INIT_WORK(&rport->scan_work, fc_scsi_scan_rport, rport); INIT_WORK(&rport->stgt_delete_work, fc_starget_delete, rport); INIT_WORK(&rport->rport_delete_work, fc_rport_final_delete, rport); @@ -1913,11 +1992,13 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, /* restart the target */ /* - * Stop the target timer first. Take no action + * Stop the target timers first. Take no action * on the del_timer failure as the state * machine state change will validate the * transaction. */ + if (!cancel_delayed_work(&rport->fail_io_work)) + fc_flush_devloss(shost); if (!cancel_delayed_work(work)) fc_flush_devloss(shost); @@ -2061,6 +2142,7 @@ void fc_remote_port_delete(struct fc_rport *rport) { struct Scsi_Host *shost = rport_to_shost(rport); + struct fc_internal *i = to_fc_internal(shost->transportt); int timeout = rport->dev_loss_tmo; unsigned long flags; @@ -2091,6 +2173,12 @@ fc_remote_port_delete(struct fc_rport *rport) scsi_target_block(&rport->dev); + /* see if we need to kill io faster than waiting for device loss */ + if ((rport->fast_io_fail_tmo != -1) && + (rport->fast_io_fail_tmo < timeout) && (i->f->terminate_rport_io)) + fc_queue_devloss_work(shost, &rport->fail_io_work, + rport->fast_io_fail_tmo * HZ); + /* cap the length the devices can be blocked until they are deleted */ fc_queue_devloss_work(shost, &rport->dev_loss_work, timeout * HZ); } @@ -2150,6 +2238,8 @@ fc_remote_port_rolechg(struct fc_rport *rport, u32 roles) * machine state change will validate the * transaction. */ + if (!cancel_delayed_work(&rport->fail_io_work)) + fc_flush_devloss(shost); if (!cancel_delayed_work(&rport->dev_loss_work)) fc_flush_devloss(shost); @@ -2270,6 +2360,28 @@ fc_timeout_deleted_rport(void *data) fc_queue_work(shost, &rport->stgt_delete_work); } +/** + * fc_timeout_fail_rport_io - Timeout handler for a fast io failing on a + * disconnected SCSI target. + * + * @data: rport to terminate io on. + * + * Notes: Only requests the failure of the io, not that all are flushed + * prior to returning. + **/ +static void +fc_timeout_fail_rport_io(void *data) +{ + struct fc_rport *rport = (struct fc_rport *)data; + struct Scsi_Host *shost = rport_to_shost(rport); + struct fc_internal *i = to_fc_internal(shost->transportt); + + if (rport->port_state != FC_PORTSTATE_BLOCKED) + return; + + i->f->terminate_rport_io(rport); +} + /** * fc_scsi_scan_rport - called to perform a scsi scan on a remote port. * -- cgit v1.2.3 From c01f32087960edd60a302ad62ad6b8b525e4aeec Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 18 Aug 2006 17:47:08 -0400 Subject: [SCSI] lpfc 8.1.10 : Add support for dev_loss_tmo_callbk and fast_io_fail_tmo_callbk Add support for new dev_loss_tmo callback Goodness is that it removes code for a parallel nodev timer that existed in the driver Add support for the new fast_io_fail callback Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc.h | 3 + drivers/scsi/lpfc/lpfc_attr.c | 155 ++++++++++++++++++++++++++------ drivers/scsi/lpfc/lpfc_crtn.h | 3 + drivers/scsi/lpfc/lpfc_ct.c | 25 ------ drivers/scsi/lpfc/lpfc_disc.h | 6 +- drivers/scsi/lpfc/lpfc_hbadisc.c | 178 ++++++++++++++----------------------- drivers/scsi/lpfc/lpfc_nportdisc.c | 2 +- drivers/scsi/lpfc/lpfc_scsi.c | 10 +-- 8 files changed, 209 insertions(+), 173 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 4a4048d7ba9..efec44d267c 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -285,6 +285,7 @@ struct lpfc_hba { uint32_t cfg_log_verbose; uint32_t cfg_lun_queue_depth; uint32_t cfg_nodev_tmo; + uint32_t cfg_devloss_tmo; uint32_t cfg_hba_queue_depth; uint32_t cfg_fcp_class; uint32_t cfg_use_adisc; @@ -303,6 +304,8 @@ struct lpfc_hba { uint32_t cfg_sg_seg_cnt; uint32_t cfg_sg_dma_buf_size; + uint32_t dev_loss_tmo_changed; + lpfc_vpd_t vpd; /* vital product data */ struct Scsi_Host *host; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index c6d683d86cf..0de69324212 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -39,6 +39,9 @@ #include "lpfc_compat.h" #include "lpfc_crtn.h" +#define LPFC_DEF_DEVLOSS_TMO 30 +#define LPFC_MIN_DEVLOSS_TMO 1 +#define LPFC_MAX_DEVLOSS_TMO 255 static void lpfc_jedec_to_ascii(int incr, char hdw[]) @@ -558,6 +561,123 @@ MODULE_PARM_DESC(lpfc_poll, "FCP ring polling mode control:" static CLASS_DEVICE_ATTR(lpfc_poll, S_IRUGO | S_IWUSR, lpfc_poll_show, lpfc_poll_store); +/* +# lpfc_nodev_tmo: If set, it will hold all I/O errors on devices that disappear +# until the timer expires. Value range is [0,255]. Default value is 30. +*/ +static int lpfc_nodev_tmo = LPFC_DEF_DEVLOSS_TMO; +static int lpfc_devloss_tmo = LPFC_DEF_DEVLOSS_TMO; +module_param(lpfc_nodev_tmo, int, 0); +MODULE_PARM_DESC(lpfc_nodev_tmo, + "Seconds driver will hold I/O waiting " + "for a device to come back"); +static ssize_t +lpfc_nodev_tmo_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata; + int val = 0; + val = phba->cfg_devloss_tmo; + return snprintf(buf, PAGE_SIZE, "%d\n", + phba->cfg_devloss_tmo); +} + +static int +lpfc_nodev_tmo_init(struct lpfc_hba *phba, int val) +{ + static int warned; + if (phba->cfg_devloss_tmo != LPFC_DEF_DEVLOSS_TMO) { + phba->cfg_nodev_tmo = phba->cfg_devloss_tmo; + if (!warned && val != LPFC_DEF_DEVLOSS_TMO) { + warned = 1; + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "%d:0402 Ignoring nodev_tmo module " + "parameter because devloss_tmo is" + " set.\n", + phba->brd_no); + } + return 0; + } + + if (val >= LPFC_MIN_DEVLOSS_TMO && val <= LPFC_MAX_DEVLOSS_TMO) { + phba->cfg_nodev_tmo = val; + phba->cfg_devloss_tmo = val; + return 0; + } + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "%d:0400 lpfc_nodev_tmo attribute cannot be set to %d, " + "allowed range is [%d, %d]\n", + phba->brd_no, val, + LPFC_MIN_DEVLOSS_TMO, LPFC_MAX_DEVLOSS_TMO); + phba->cfg_nodev_tmo = LPFC_DEF_DEVLOSS_TMO; + return -EINVAL; +} + +static int +lpfc_nodev_tmo_set(struct lpfc_hba *phba, int val) +{ + if (phba->dev_loss_tmo_changed || + (lpfc_devloss_tmo != LPFC_DEF_DEVLOSS_TMO)) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "%d:0401 Ignoring change to nodev_tmo " + "because devloss_tmo is set.\n", + phba->brd_no); + return 0; + } + + if (val >= LPFC_MIN_DEVLOSS_TMO && val <= LPFC_MAX_DEVLOSS_TMO) { + phba->cfg_nodev_tmo = val; + phba->cfg_devloss_tmo = val; + return 0; + } + + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "%d:0403 lpfc_nodev_tmo attribute cannot be set to %d, " + "allowed range is [%d, %d]\n", + phba->brd_no, val, LPFC_MIN_DEVLOSS_TMO, + LPFC_MAX_DEVLOSS_TMO); + return -EINVAL; +} + +lpfc_param_store(nodev_tmo) + +static CLASS_DEVICE_ATTR(lpfc_nodev_tmo, S_IRUGO | S_IWUSR, + lpfc_nodev_tmo_show, lpfc_nodev_tmo_store); + +/* +# lpfc_devloss_tmo: If set, it will hold all I/O errors on devices that +# disappear until the timer expires. Value range is [0,255]. Default +# value is 30. +*/ +module_param(lpfc_devloss_tmo, int, 0); +MODULE_PARM_DESC(lpfc_devloss_tmo, + "Seconds driver will hold I/O waiting " + "for a device to come back"); +lpfc_param_init(devloss_tmo, LPFC_DEF_DEVLOSS_TMO, + LPFC_MIN_DEVLOSS_TMO, LPFC_MAX_DEVLOSS_TMO) +lpfc_param_show(devloss_tmo) +static int +lpfc_devloss_tmo_set(struct lpfc_hba *phba, int val) +{ + if (val >= LPFC_MIN_DEVLOSS_TMO && val <= LPFC_MAX_DEVLOSS_TMO) { + phba->cfg_nodev_tmo = val; + phba->cfg_devloss_tmo = val; + phba->dev_loss_tmo_changed = 1; + return 0; + } + + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "%d:0404 lpfc_devloss_tmo attribute cannot be set to" + " %d, allowed range is [%d, %d]\n", + phba->brd_no, val, LPFC_MIN_DEVLOSS_TMO, + LPFC_MAX_DEVLOSS_TMO); + return -EINVAL; +} + +lpfc_param_store(devloss_tmo) +static CLASS_DEVICE_ATTR(lpfc_devloss_tmo, S_IRUGO | S_IWUSR, + lpfc_devloss_tmo_show, lpfc_devloss_tmo_store); + /* # lpfc_log_verbose: Only turn this flag on if you are willing to risk being # deluged with LOTS of information. @@ -616,14 +736,6 @@ LPFC_ATTR_R(hba_queue_depth, 8192, 32, 8192, LPFC_ATTR_R(scan_down, 1, 0, 1, "Start scanning for devices from highest ALPA to lowest"); -/* -# lpfc_nodev_tmo: If set, it will hold all I/O errors on devices that disappear -# until the timer expires. Value range is [0,255]. Default value is 30. -# NOTE: this MUST be less then the SCSI Layer command timeout - 1. -*/ -LPFC_ATTR_RW(nodev_tmo, 30, 0, 255, - "Seconds driver will hold I/O waiting for a device to come back"); - /* # lpfc_topology: link topology for init link # 0x0 = attempt loop mode then point-to-point @@ -737,6 +849,7 @@ struct class_device_attribute *lpfc_host_attrs[] = { &class_device_attr_lpfc_lun_queue_depth, &class_device_attr_lpfc_hba_queue_depth, &class_device_attr_lpfc_nodev_tmo, + &class_device_attr_lpfc_devloss_tmo, &class_device_attr_lpfc_fcp_class, &class_device_attr_lpfc_use_adisc, &class_device_attr_lpfc_ack0, @@ -1449,28 +1562,13 @@ lpfc_get_starget_port_name(struct scsi_target *starget) fc_starget_port_name(starget) = port_name; } -static void -lpfc_get_rport_loss_tmo(struct fc_rport *rport) -{ - /* - * Return the driver's global value for device loss timeout plus - * five seconds to allow the driver's nodev timer to run. - */ - rport->dev_loss_tmo = lpfc_nodev_tmo + 5; -} - static void lpfc_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout) { - /* - * The driver doesn't have a per-target timeout setting. Set - * this value globally. lpfc_nodev_tmo should be greater then 0. - */ if (timeout) - lpfc_nodev_tmo = timeout; + rport->dev_loss_tmo = timeout; else - lpfc_nodev_tmo = 1; - rport->dev_loss_tmo = lpfc_nodev_tmo + 5; + rport->dev_loss_tmo = 1; } @@ -1532,7 +1630,6 @@ struct fc_function_template lpfc_transport_functions = { .show_rport_maxframe_size = 1, .show_rport_supported_classes = 1, - .get_rport_dev_loss_tmo = lpfc_get_rport_loss_tmo, .set_rport_dev_loss_tmo = lpfc_set_rport_loss_tmo, .show_rport_dev_loss_tmo = 1, @@ -1546,6 +1643,8 @@ struct fc_function_template lpfc_transport_functions = { .show_starget_port_name = 1, .issue_fc_host_lip = lpfc_issue_lip, + .dev_loss_tmo_callbk = lpfc_dev_loss_tmo_callbk, + .terminate_rport_io = lpfc_terminate_rport_io, }; void @@ -1561,13 +1660,13 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) lpfc_ack0_init(phba, lpfc_ack0); lpfc_topology_init(phba, lpfc_topology); lpfc_scan_down_init(phba, lpfc_scan_down); - lpfc_nodev_tmo_init(phba, lpfc_nodev_tmo); lpfc_link_speed_init(phba, lpfc_link_speed); lpfc_fdmi_on_init(phba, lpfc_fdmi_on); lpfc_discovery_threads_init(phba, lpfc_discovery_threads); lpfc_max_luns_init(phba, lpfc_max_luns); lpfc_poll_tmo_init(phba, lpfc_poll_tmo); - + lpfc_devloss_tmo_init(phba, lpfc_devloss_tmo); + lpfc_nodev_tmo_init(phba, lpfc_nodev_tmo); phba->cfg_poll = lpfc_poll; /* diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 2a176467f71..3d684496acd 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -18,6 +18,7 @@ * included with this package. * *******************************************************************/ +struct fc_rport; void lpfc_dump_mem(struct lpfc_hba *, LPFC_MBOXQ_t *, uint16_t); void lpfc_read_nv(struct lpfc_hba *, LPFC_MBOXQ_t *); int lpfc_read_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, @@ -200,6 +201,8 @@ extern struct scsi_host_template lpfc_template; extern struct fc_function_template lpfc_transport_functions; void lpfc_get_hba_sym_node_name(struct lpfc_hba * phba, uint8_t * symbp); +void lpfc_terminate_rport_io(struct fc_rport *); +void lpfc_dev_loss_tmo_callbk(struct fc_rport *rport); #define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code) #define HBA_EVENT_RSCN 5 diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index bbb7310210b..ae410645899 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -324,7 +324,6 @@ lpfc_ns_rsp(struct lpfc_hba * phba, struct lpfc_dmabuf * mp, uint32_t Size) struct lpfc_sli_ct_request *Response = (struct lpfc_sli_ct_request *) mp->virt; struct lpfc_nodelist *ndlp = NULL; - struct lpfc_nodelist *next_ndlp; struct lpfc_dmabuf *mlast, *next_mp; uint32_t *ctptr = (uint32_t *) & Response->un.gid.PortType; uint32_t Did; @@ -399,30 +398,6 @@ nsout1: * current driver state. */ if (phba->hba_state == LPFC_HBA_READY) { - - /* - * Switch ports that connect a loop of multiple targets need - * special consideration. The driver wants to unregister the - * rpi only on the target that was pulled from the loop. On - * RSCN, the driver wants to rediscover an NPort only if the - * driver flagged it as NLP_NPR_2B_DISC. Provided adisc is - * not enabled and the NPort is not capable of retransmissions - * (FC Tape) prevent timing races with the scsi error handler by - * unregistering the Nport's RPI. This action causes all - * outstanding IO to flush back to the midlayer. - */ - list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list, - nlp_listp) { - if (!(ndlp->nlp_flag & NLP_NPR_2B_DISC) && - (lpfc_rscn_payload_check(phba, ndlp->nlp_DID))) { - if ((phba->cfg_use_adisc == 0) && - !(ndlp->nlp_fcp_info & - NLP_FCP_2_DEVICE)) { - lpfc_unreg_rpi(phba, ndlp); - ndlp->nlp_flag &= ~NLP_NPR_ADISC; - } - } - } lpfc_els_flush_rscn(phba); spin_lock_irq(phba->host->host_lock); phba->fc_flag |= FC_RSCN_MODE; /* we are still in RSCN mode */ diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h index 41cf5d3ea6c..9766f909c9c 100644 --- a/drivers/scsi/lpfc/lpfc_disc.h +++ b/drivers/scsi/lpfc/lpfc_disc.h @@ -30,7 +30,6 @@ /* worker thread events */ enum lpfc_work_type { - LPFC_EVT_NODEV_TMO, LPFC_EVT_ONLINE, LPFC_EVT_OFFLINE, LPFC_EVT_WARM_START, @@ -74,11 +73,9 @@ struct lpfc_nodelist { #define NLP_FCP_2_DEVICE 0x10 /* FCP-2 device */ struct timer_list nlp_delayfunc; /* Used for delayed ELS cmds */ - struct timer_list nlp_tmofunc; /* Used for nodev tmo */ struct fc_rport *rport; /* Corresponding FC transport port structure */ struct lpfc_hba *nlp_phba; - struct lpfc_work_evt nodev_timeout_evt; struct lpfc_work_evt els_retry_evt; unsigned long last_ramp_up_time; /* jiffy of last ramp up */ unsigned long last_q_full_time; /* jiffy of last queue full */ @@ -102,7 +99,6 @@ struct lpfc_nodelist { #define NLP_LOGO_SND 0x100 /* sent LOGO request for this entry */ #define NLP_RNID_SND 0x400 /* sent RNID request for this entry */ #define NLP_ELS_SND_MASK 0x7e0 /* sent ELS request for this entry */ -#define NLP_NODEV_TMO 0x10000 /* nodev timeout is running for node */ #define NLP_DELAY_TMO 0x20000 /* delay timeout is running for node */ #define NLP_NPR_2B_DISC 0x40000 /* node is included in num_disc_nodes */ #define NLP_RCV_PLOGI 0x80000 /* Rcv'ed PLOGI from remote system */ @@ -169,7 +165,7 @@ struct lpfc_nodelist { */ /* * For a Link Down, all nodes on the ADISC, PLOGI, unmapped or mapped - * lists will receive a DEVICE_RECOVERY event. If the linkdown or nodev timers + * lists will receive a DEVICE_RECOVERY event. If the linkdown or devloss timers * expire, all effected nodes will receive a DEVICE_RM event. */ /* diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 53821e5778b..97973af980a 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -56,28 +56,63 @@ static uint8_t lpfcAlpaArray[] = { static void lpfc_disc_timeout_handler(struct lpfc_hba *); -static void -lpfc_process_nodev_timeout(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) +void +lpfc_terminate_rport_io(struct fc_rport *rport) { - uint8_t *name = (uint8_t *)&ndlp->nlp_portname; - int warn_on = 0; + struct lpfc_rport_data *rdata; + struct lpfc_nodelist * ndlp; + struct lpfc_hba *phba; - spin_lock_irq(phba->host->host_lock); - if (!(ndlp->nlp_flag & NLP_NODEV_TMO)) { - spin_unlock_irq(phba->host->host_lock); + rdata = rport->dd_data; + ndlp = rdata->pnode; + + if (!ndlp) { + if (rport->roles & FC_RPORT_ROLE_FCP_TARGET) + printk(KERN_ERR "Cannot find remote node" + " to terminate I/O Data x%x\n", + rport->port_id); return; } - /* - * If a discovery event readded nodev_timer after timer - * firing and before processing the timer, cancel the - * nlp_tmofunc. - */ - spin_unlock_irq(phba->host->host_lock); - del_timer_sync(&ndlp->nlp_tmofunc); + phba = ndlp->nlp_phba; + spin_lock_irq(phba->host->host_lock); + if (ndlp->nlp_sid != NLP_NO_SID) { + lpfc_sli_abort_iocb(phba, &phba->sli.ring[phba->sli.fcp_ring], + ndlp->nlp_sid, 0, 0, LPFC_CTX_TGT); + } + spin_unlock_irq(phba->host->host_lock); + + return; +} + +/* + * This function will be called when dev_loss_tmo fire. + */ +void +lpfc_dev_loss_tmo_callbk(struct fc_rport *rport) +{ + struct lpfc_rport_data *rdata; + struct lpfc_nodelist * ndlp; + uint8_t *name; + int warn_on = 0; + struct lpfc_hba *phba; + + rdata = rport->dd_data; + ndlp = rdata->pnode; - ndlp->nlp_flag &= ~NLP_NODEV_TMO; + if (!ndlp) { + if (rport->roles & FC_RPORT_ROLE_FCP_TARGET) + printk(KERN_ERR "Cannot find remote node" + " for rport in dev_loss_tmo_callbk x%x\n", + rport->port_id); + return; + } + + name = (uint8_t *)&ndlp->nlp_portname; + phba = ndlp->nlp_phba; + + spin_lock_irq(phba->host->host_lock); if (ndlp->nlp_sid != NLP_NO_SID) { warn_on = 1; @@ -85,11 +120,14 @@ lpfc_process_nodev_timeout(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) lpfc_sli_abort_iocb(phba, &phba->sli.ring[phba->sli.fcp_ring], ndlp->nlp_sid, 0, 0, LPFC_CTX_TGT); } + if (phba->fc_flag & FC_UNLOADING) + warn_on = 0; + spin_unlock_irq(phba->host->host_lock); if (warn_on) { lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, - "%d:0203 Nodev timeout on " + "%d:0203 Devloss timeout on " "WWPN %x:%x:%x:%x:%x:%x:%x:%x " "NPort x%x Data: x%x x%x x%x\n", phba->brd_no, @@ -99,7 +137,7 @@ lpfc_process_nodev_timeout(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) ndlp->nlp_state, ndlp->nlp_rpi); } else { lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, - "%d:0204 Nodev timeout on " + "%d:0204 Devloss timeout on " "WWPN %x:%x:%x:%x:%x:%x:%x:%x " "NPort x%x Data: x%x x%x x%x\n", phba->brd_no, @@ -109,7 +147,12 @@ lpfc_process_nodev_timeout(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) ndlp->nlp_state, ndlp->nlp_rpi); } - lpfc_disc_state_machine(phba, ndlp, NULL, NLP_EVT_DEVICE_RM); + ndlp->rport = NULL; + rdata->pnode = NULL; + + if (!(phba->fc_flag & FC_UNLOADING)) + lpfc_disc_state_machine(phba, ndlp, NULL, NLP_EVT_DEVICE_RM); + return; } @@ -127,11 +170,6 @@ lpfc_work_list_done(struct lpfc_hba * phba) spin_unlock_irq(phba->host->host_lock); free_evt = 1; switch (evtp->evt) { - case LPFC_EVT_NODEV_TMO: - ndlp = (struct lpfc_nodelist *)(evtp->evt_arg1); - lpfc_process_nodev_timeout(phba, ndlp); - free_evt = 0; - break; case LPFC_EVT_ELS_RETRY: ndlp = (struct lpfc_nodelist *)(evtp->evt_arg1); lpfc_els_retry_delay_handler(ndlp); @@ -377,16 +415,6 @@ lpfc_linkdown(struct lpfc_hba * phba) rc = lpfc_disc_state_machine(phba, ndlp, NULL, NLP_EVT_DEVICE_RECOVERY); - /* Check config parameter use-adisc or FCP-2 */ - if ((rc != NLP_STE_FREED_NODE) && - (phba->cfg_use_adisc == 0) && - !(ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE)) { - /* We know we will have to relogin, so - * unreglogin the rpi right now to fail - * any outstanding I/Os quickly. - */ - lpfc_unreg_rpi(phba, ndlp); - } } } @@ -1104,8 +1132,11 @@ lpfc_unregister_remote_port(struct lpfc_hba * phba, struct fc_rport *rport = ndlp->rport; struct lpfc_rport_data *rdata = rport->dd_data; - ndlp->rport = NULL; - rdata->pnode = NULL; + if (rport->scsi_target_id == -1) { + ndlp->rport = NULL; + rdata->pnode = NULL; + } + fc_remote_port_delete(rport); return; @@ -1233,17 +1264,6 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) list_add_tail(&nlp->nlp_listp, &phba->fc_nlpunmap_list); phba->fc_unmap_cnt++; phba->nport_event_cnt++; - /* stop nodev tmo if running */ - if (nlp->nlp_flag & NLP_NODEV_TMO) { - nlp->nlp_flag &= ~NLP_NODEV_TMO; - spin_unlock_irq(phba->host->host_lock); - del_timer_sync(&nlp->nlp_tmofunc); - spin_lock_irq(phba->host->host_lock); - if (!list_empty(&nlp->nodev_timeout_evt.evt_listp)) - list_del_init(&nlp->nodev_timeout_evt. - evt_listp); - - } nlp->nlp_flag &= ~NLP_NODEV_REMOVE; nlp->nlp_type |= NLP_FC_NODE; break; @@ -1254,17 +1274,6 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) list_add_tail(&nlp->nlp_listp, &phba->fc_nlpmap_list); phba->fc_map_cnt++; phba->nport_event_cnt++; - /* stop nodev tmo if running */ - if (nlp->nlp_flag & NLP_NODEV_TMO) { - nlp->nlp_flag &= ~NLP_NODEV_TMO; - spin_unlock_irq(phba->host->host_lock); - del_timer_sync(&nlp->nlp_tmofunc); - spin_lock_irq(phba->host->host_lock); - if (!list_empty(&nlp->nodev_timeout_evt.evt_listp)) - list_del_init(&nlp->nodev_timeout_evt. - evt_listp); - - } nlp->nlp_flag &= ~NLP_NODEV_REMOVE; break; case NLP_NPR_LIST: @@ -1273,11 +1282,6 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) list_add_tail(&nlp->nlp_listp, &phba->fc_npr_list); phba->fc_npr_cnt++; - if (!(nlp->nlp_flag & NLP_NODEV_TMO)) - mod_timer(&nlp->nlp_tmofunc, - jiffies + HZ * phba->cfg_nodev_tmo); - - nlp->nlp_flag |= NLP_NODEV_TMO; nlp->nlp_flag &= ~NLP_RCV_PLOGI; break; case NLP_JUST_DQ: @@ -1307,7 +1311,8 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) * already. If we have, and it's a scsi entity, be * sure to unblock any attached scsi devices */ - if (!nlp->rport) + if ((!nlp->rport) || (nlp->rport->port_state == + FC_PORTSTATE_BLOCKED)) lpfc_register_remote_port(phba, nlp); /* @@ -1581,15 +1586,12 @@ lpfc_freenode(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) lpfc_els_abort(phba,ndlp,0); spin_lock_irq(phba->host->host_lock); - ndlp->nlp_flag &= ~(NLP_NODEV_TMO|NLP_DELAY_TMO); + ndlp->nlp_flag &= ~NLP_DELAY_TMO; spin_unlock_irq(phba->host->host_lock); - del_timer_sync(&ndlp->nlp_tmofunc); ndlp->nlp_last_elscmd = 0; del_timer_sync(&ndlp->nlp_delayfunc); - if (!list_empty(&ndlp->nodev_timeout_evt.evt_listp)) - list_del_init(&ndlp->nodev_timeout_evt.evt_listp); if (!list_empty(&ndlp->els_retry_evt.evt_listp)) list_del_init(&ndlp->els_retry_evt.evt_listp); @@ -1606,16 +1608,6 @@ lpfc_freenode(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) int lpfc_nlp_remove(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) { - if (ndlp->nlp_flag & NLP_NODEV_TMO) { - spin_lock_irq(phba->host->host_lock); - ndlp->nlp_flag &= ~NLP_NODEV_TMO; - spin_unlock_irq(phba->host->host_lock); - del_timer_sync(&ndlp->nlp_tmofunc); - if (!list_empty(&ndlp->nodev_timeout_evt.evt_listp)) - list_del_init(&ndlp->nodev_timeout_evt.evt_listp); - - } - if (ndlp->nlp_flag & NLP_DELAY_TMO) { lpfc_cancel_retry_delay_tmo(phba, ndlp); @@ -2430,34 +2422,6 @@ lpfc_disc_timeout_handler(struct lpfc_hba *phba) return; } -static void -lpfc_nodev_timeout(unsigned long ptr) -{ - struct lpfc_hba *phba; - struct lpfc_nodelist *ndlp; - unsigned long iflag; - struct lpfc_work_evt *evtp; - - ndlp = (struct lpfc_nodelist *)ptr; - phba = ndlp->nlp_phba; - evtp = &ndlp->nodev_timeout_evt; - spin_lock_irqsave(phba->host->host_lock, iflag); - - if (!list_empty(&evtp->evt_listp)) { - spin_unlock_irqrestore(phba->host->host_lock, iflag); - return; - } - evtp->evt_arg1 = ndlp; - evtp->evt = LPFC_EVT_NODEV_TMO; - list_add_tail(&evtp->evt_listp, &phba->work_list); - if (phba->work_wait) - wake_up(phba->work_wait); - - spin_unlock_irqrestore(phba->host->host_lock, iflag); - return; -} - - /* * This routine handles processing a NameServer REG_LOGIN mailbox * command upon completion. It is setup in the LPFC_MBOXQ @@ -2581,11 +2545,7 @@ lpfc_nlp_init(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, uint32_t did) { memset(ndlp, 0, sizeof (struct lpfc_nodelist)); - INIT_LIST_HEAD(&ndlp->nodev_timeout_evt.evt_listp); INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp); - init_timer(&ndlp->nlp_tmofunc); - ndlp->nlp_tmofunc.function = lpfc_nodev_timeout; - ndlp->nlp_tmofunc.data = (unsigned long)ndlp; init_timer(&ndlp->nlp_delayfunc); ndlp->nlp_delayfunc.function = lpfc_els_retry_delay; ndlp->nlp_delayfunc.data = (unsigned long)ndlp; diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index 20449a8dd53..d5f415007db 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -1813,7 +1813,7 @@ lpfc_device_recov_npr_node(struct lpfc_hba * phba, */ /* * For a Link Down, all nodes on the ADISC, PLOGI, unmapped or mapped - * lists will receive a DEVICE_RECOVERY event. If the linkdown or nodev timers + * lists will receive a DEVICE_RECOVERY event. If the linkdown or devloss timers * expire, all effected nodes will receive a DEVICE_RM event. */ /* diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index a8816a8738f..97ae98dc95d 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -935,7 +935,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) schedule_timeout_uninterruptible(LPFC_ABORT_WAIT*HZ); spin_lock_irq(phba->host->host_lock); if (++loop_count - > (2 * phba->cfg_nodev_tmo)/LPFC_ABORT_WAIT) + > (2 * phba->cfg_devloss_tmo)/LPFC_ABORT_WAIT) break; } @@ -978,7 +978,7 @@ lpfc_reset_lun_handler(struct scsi_cmnd *cmnd) spin_lock_irq(shost->host_lock); /* * If target is not in a MAPPED state, delay the reset until - * target is rediscovered or nodev timeout expires. + * target is rediscovered or devloss timeout expires. */ while ( 1 ) { if (!pnode) @@ -1050,7 +1050,7 @@ lpfc_reset_lun_handler(struct scsi_cmnd *cmnd) spin_lock_irq(phba->host->host_lock); if (++loopcnt - > (2 * phba->cfg_nodev_tmo)/LPFC_RESET_WAIT) + > (2 * phba->cfg_devloss_tmo)/LPFC_RESET_WAIT) break; cnt = lpfc_sli_sum_iocb(phba, @@ -1151,7 +1151,7 @@ lpfc_reset_bus_handler(struct scsi_cmnd *cmnd) spin_lock_irq(phba->host->host_lock); if (++loopcnt - > (2 * phba->cfg_nodev_tmo)/LPFC_RESET_WAIT) + > (2 * phba->cfg_devloss_tmo)/LPFC_RESET_WAIT) break; cnt = lpfc_sli_sum_iocb(phba, @@ -1249,7 +1249,7 @@ lpfc_slave_configure(struct scsi_device *sdev) * target pointer is stored in the starget_data for the * driver's sysfs entry point functions. */ - rport->dev_loss_tmo = phba->cfg_nodev_tmo + 5; + rport->dev_loss_tmo = phba->cfg_devloss_tmo; if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) { lpfc_sli_poll_fcp_ring(phba); -- cgit v1.2.3 From c3f28afa61343e3e010e3014aa0d6eba271c1558 Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 18 Aug 2006 17:47:18 -0400 Subject: [SCSI] lpfc 8.1.10 : Add support for new lpfc soft_wwpn attribute Add support for a new lpfc soft_wwpn sysfs attribute Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc.h | 3 + drivers/scsi/lpfc/lpfc_attr.c | 117 +++++++++++++++++++++++++++++++++++++++ drivers/scsi/lpfc/lpfc_hbadisc.c | 2 + drivers/scsi/lpfc/lpfc_init.c | 2 + 4 files changed, 124 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index efec44d267c..3f7f5f8abd7 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -303,6 +303,7 @@ struct lpfc_hba { uint32_t cfg_poll_tmo; uint32_t cfg_sg_seg_cnt; uint32_t cfg_sg_dma_buf_size; + uint64_t cfg_soft_wwpn; uint32_t dev_loss_tmo_changed; @@ -354,6 +355,8 @@ struct lpfc_hba { #define VPD_PORT 0x8 /* valid vpd port data */ #define VPD_MASK 0xf /* mask for any vpd data */ + uint8_t soft_wwpn_enable; + struct timer_list fcp_poll_timer; struct timer_list els_tmofunc; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 0de69324212..9496e87c135 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -551,6 +551,119 @@ static CLASS_DEVICE_ATTR(board_mode, S_IRUGO | S_IWUSR, lpfc_board_mode_show, lpfc_board_mode_store); static CLASS_DEVICE_ATTR(issue_reset, S_IWUSR, NULL, lpfc_issue_reset); + +static char *lpfc_soft_wwpn_key = "C99G71SL8032A"; + +static ssize_t +lpfc_soft_wwpn_enable_store(struct class_device *cdev, const char *buf, + size_t count) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata; + unsigned int cnt = count; + + /* + * We're doing a simple sanity check for soft_wwpn setting. + * We require that the user write a specific key to enable + * the soft_wwpn attribute to be settable. Once the attribute + * is written, the enable key resets. If further updates are + * desired, the key must be written again to re-enable the + * attribute. + * + * The "key" is not secret - it is a hardcoded string shown + * here. The intent is to protect against the random user or + * application that is just writing attributes. + */ + + /* count may include a LF at end of string */ + if (buf[cnt-1] == '\n') + cnt--; + + if ((cnt != strlen(lpfc_soft_wwpn_key)) || + (strncmp(buf, lpfc_soft_wwpn_key, strlen(lpfc_soft_wwpn_key)) != 0)) + return -EINVAL; + + phba->soft_wwpn_enable = 1; + return count; +} +static CLASS_DEVICE_ATTR(lpfc_soft_wwpn_enable, S_IWUSR, NULL, + lpfc_soft_wwpn_enable_store); + +static ssize_t +lpfc_soft_wwpn_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata; + return snprintf(buf, PAGE_SIZE, "0x%llx\n", phba->cfg_soft_wwpn); +} + + +static ssize_t +lpfc_soft_wwpn_store(struct class_device *cdev, const char *buf, size_t count) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata; + struct completion online_compl; + int stat1=0, stat2=0; + unsigned int i, j, cnt=count; + u8 wwpn[8]; + + /* count may include a LF at end of string */ + if (buf[cnt-1] == '\n') + cnt--; + + if (!phba->soft_wwpn_enable || (cnt < 16) || (cnt > 18) || + ((cnt == 17) && (*buf++ != 'x')) || + ((cnt == 18) && ((*buf++ != '0') || (*buf++ != 'x')))) + return -EINVAL; + + phba->soft_wwpn_enable = 0; + + memset(wwpn, 0, sizeof(wwpn)); + + /* Validate and store the new name */ + for (i=0, j=0; i < 16; i++) { + if ((*buf >= 'a') && (*buf <= 'f')) + j = ((j << 4) | ((*buf++ -'a') + 10)); + else if ((*buf >= 'A') && (*buf <= 'F')) + j = ((j << 4) | ((*buf++ -'A') + 10)); + else if ((*buf >= '0') && (*buf <= '9')) + j = ((j << 4) | (*buf++ -'0')); + else + return -EINVAL; + if (i % 2) { + wwpn[i/2] = j & 0xff; + j = 0; + } + } + phba->cfg_soft_wwpn = wwn_to_u64(wwpn); + fc_host_port_name(host) = phba->cfg_soft_wwpn; + + dev_printk(KERN_NOTICE, &phba->pcidev->dev, + "lpfc%d: Reinitializing to use soft_wwpn\n", phba->brd_no); + + init_completion(&online_compl); + lpfc_workq_post_event(phba, &stat1, &online_compl, LPFC_EVT_OFFLINE); + wait_for_completion(&online_compl); + if (stat1) + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "%d:0463 lpfc_soft_wwpn attribute set failed to reinit " + "adapter - %d\n", phba->brd_no, stat1); + + init_completion(&online_compl); + lpfc_workq_post_event(phba, &stat2, &online_compl, LPFC_EVT_ONLINE); + wait_for_completion(&online_compl); + if (stat2) + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "%d:0464 lpfc_soft_wwpn attribute set failed to reinit " + "adapter - %d\n", phba->brd_no, stat2); + + return (stat1 || stat2) ? -EIO : count; +} +static CLASS_DEVICE_ATTR(lpfc_soft_wwpn, S_IRUGO | S_IWUSR,\ + lpfc_soft_wwpn_show, lpfc_soft_wwpn_store); + + static int lpfc_poll = 0; module_param(lpfc_poll, int, 0); MODULE_PARM_DESC(lpfc_poll, "FCP ring polling mode control:" @@ -832,6 +945,7 @@ LPFC_ATTR_R(max_luns, 255, 0, 65535, LPFC_ATTR_RW(poll_tmo, 10, 1, 255, "Milliseconds driver will wait between polling FCP ring"); + struct class_device_attribute *lpfc_host_attrs[] = { &class_device_attr_info, &class_device_attr_serialnum, @@ -867,6 +981,8 @@ struct class_device_attribute *lpfc_host_attrs[] = { &class_device_attr_issue_reset, &class_device_attr_lpfc_poll, &class_device_attr_lpfc_poll_tmo, + &class_device_attr_lpfc_soft_wwpn, + &class_device_attr_lpfc_soft_wwpn_enable, NULL, }; @@ -1668,6 +1784,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) lpfc_devloss_tmo_init(phba, lpfc_devloss_tmo); lpfc_nodev_tmo_init(phba, lpfc_nodev_tmo); phba->cfg_poll = lpfc_poll; + phba->cfg_soft_wwpn = 0L; /* * The total number of segments is the configuration value plus 2 diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 97973af980a..d586c3d3b0d 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -672,6 +672,8 @@ lpfc_mbx_cmpl_read_sparam(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) memcpy((uint8_t *) & phba->fc_sparam, (uint8_t *) mp->virt, sizeof (struct serv_parm)); + if (phba->cfg_soft_wwpn) + u64_to_wwn(phba->cfg_soft_wwpn, phba->fc_sparam.portName.u.wwn); memcpy((uint8_t *) & phba->fc_nodename, (uint8_t *) & phba->fc_sparam.nodeName, sizeof (struct lpfc_name)); diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 84e7fc595f5..4cdf3464267 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -268,6 +268,8 @@ lpfc_config_port_post(struct lpfc_hba * phba) kfree(mp); pmb->context1 = NULL; + if (phba->cfg_soft_wwpn) + u64_to_wwn(phba->cfg_soft_wwpn, phba->fc_sparam.portName.u.wwn); memcpy(&phba->fc_nodename, &phba->fc_sparam.nodeName, sizeof (struct lpfc_name)); memcpy(&phba->fc_portname, &phba->fc_sparam.portName, -- cgit v1.2.3 From 26dacd0c9b2dc1dc987c376aeee4e80691a7dd0b Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 18 Aug 2006 17:47:24 -0400 Subject: [SCSI] lpfc 8.1.10 : Change version number to 8.1.10 Change version number to 8.1.10 Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index c7091ea29f3..ac417908b40 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -18,7 +18,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "8.1.9" +#define LPFC_DRIVER_VERSION "8.1.10" #define LPFC_DRIVER_NAME "lpfc" -- cgit v1.2.3 From 884d25cc4fda20908fd4ef93dbb41d817984b68b Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Tue, 5 Sep 2006 16:26:41 -0500 Subject: [SCSI] Fix refcount breakage with 'echo "1" > scan' when target already present Spotted by: Dan Aloni The problem is there's inconsistent locking semantic usage of scsi_alloc_target(). Two callers assume the target comes back with reference unincremented and the third assumes its incremented. Fix by always making the reference incremented on return. Also fix path in target alloc that could consistently increment the parent lock. Finally document scsi_alloc_target() so its callers know what the expectations are. Signed-off-by: James Bottomley --- drivers/scsi/scsi_scan.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 31d05ab0b2f..fd9e281c3bf 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -266,6 +266,18 @@ static struct scsi_target *__scsi_find_target(struct device *parent, return found_starget; } +/** + * scsi_alloc_target - allocate a new or find an existing target + * @parent: parent of the target (need not be a scsi host) + * @channel: target channel number (zero if no channels) + * @id: target id number + * + * Return an existing target if one exists, provided it hasn't already + * gone into STARGET_DEL state, otherwise allocate a new target. + * + * The target is returned with an incremented reference, so the caller + * is responsible for both reaping and doing a last put + */ static struct scsi_target *scsi_alloc_target(struct device *parent, int channel, uint id) { @@ -331,14 +343,15 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, return NULL; } } + get_device(dev); return starget; found: found_target->reap_ref++; spin_unlock_irqrestore(shost->host_lock, flags); - put_device(parent); if (found_target->state != STARGET_DEL) { + put_device(parent); kfree(starget); return found_target; } @@ -1341,7 +1354,6 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, if (!starget) return ERR_PTR(-ENOMEM); - get_device(&starget->dev); mutex_lock(&shost->scan_mutex); if (scsi_host_scan_allowed(shost)) scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata); @@ -1400,7 +1412,6 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel, if (!starget) return; - get_device(&starget->dev); if (lun != SCAN_WILD_CARD) { /* * Scan for a specific host/chan/id/lun. @@ -1582,7 +1593,8 @@ struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost) if (sdev) { sdev->sdev_gendev.parent = get_device(&starget->dev); sdev->borken = 0; - } + } else + scsi_target_reap(starget); put_device(&starget->dev); out: mutex_unlock(&shost->scan_mutex); -- cgit v1.2.3 From f479ab87936563a286b8aa0e39003c40fa31c6da Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 6 Sep 2006 09:00:29 -0500 Subject: [SCSI] fix up non-modular SCSI The recent change to the way scsi_device_get()/put() work broke the non modular build (we do a module_refcount on a NULL). Fix this by checking for non-null before checking module_refcount(). Signed-off-by: James Bottomley --- drivers/scsi/scsi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index eedfd059b82..c35f5fc0d66 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -873,10 +873,12 @@ EXPORT_SYMBOL(scsi_device_get); */ void scsi_device_put(struct scsi_device *sdev) { + struct module *module = sdev->host->hostt->module; + /* The module refcount will be zero if scsi_device_get() * was called from a module removal routine */ - if (likely(module_refcount(sdev->host->hostt->module) != 0)) - module_put(sdev->host->hostt->module); + if (module && module_refcount(module) != 0) + module_put(module); put_device(&sdev->sdev_gendev); } EXPORT_SYMBOL(scsi_device_put); -- cgit v1.2.3 From b4620233d6a3510564c561a5a2a365a1d8a34b68 Mon Sep 17 00:00:00 2001 From: Henrik Kretzschmar Date: Wed, 6 Sep 2006 10:49:48 +0200 Subject: [SCSI] scsi-driver ultrastore replace Scsi_Cmnd with struct scsi_cmnd Signed-off-by: Henrik Kretzschmar Signed-off-by: James Bottomley --- drivers/scsi/ultrastor.c | 23 ++++++++++++----------- drivers/scsi/ultrastor.h | 12 +++++++----- 2 files changed, 19 insertions(+), 16 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/ultrastor.c b/drivers/scsi/ultrastor.c index e681681ab7a..0372aa9fa19 100644 --- a/drivers/scsi/ultrastor.c +++ b/drivers/scsi/ultrastor.c @@ -196,8 +196,8 @@ struct mscp { u32 sense_data PACKED; /* The following fields are for software only. They are included in the MSCP structure because they are associated with SCSI requests. */ - void (*done)(Scsi_Cmnd *); - Scsi_Cmnd *SCint; + void (*done) (struct scsi_cmnd *); + struct scsi_cmnd *SCint; ultrastor_sg_list sglist[ULTRASTOR_24F_MAX_SG]; /* use larger size for 24F */ }; @@ -289,7 +289,7 @@ static const unsigned short ultrastor_ports_14f[] = { static void ultrastor_interrupt(int, void *, struct pt_regs *); static irqreturn_t do_ultrastor_interrupt(int, void *, struct pt_regs *); -static inline void build_sg_list(struct mscp *, Scsi_Cmnd *SCpnt); +static inline void build_sg_list(struct mscp *, struct scsi_cmnd *SCpnt); /* Always called with host lock held */ @@ -673,7 +673,7 @@ static const char *ultrastor_info(struct Scsi_Host * shpnt) return buf; } -static inline void build_sg_list(struct mscp *mscp, Scsi_Cmnd *SCpnt) +static inline void build_sg_list(struct mscp *mscp, struct scsi_cmnd *SCpnt) { struct scatterlist *sl; long transfer_length = 0; @@ -694,7 +694,8 @@ static inline void build_sg_list(struct mscp *mscp, Scsi_Cmnd *SCpnt) mscp->transfer_data_length = transfer_length; } -static int ultrastor_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +static int ultrastor_queuecommand(struct scsi_cmnd *SCpnt, + void (*done) (struct scsi_cmnd *)) { struct mscp *my_mscp; #if ULTRASTOR_MAX_CMDS > 1 @@ -833,7 +834,7 @@ retry: */ -static int ultrastor_abort(Scsi_Cmnd *SCpnt) +static int ultrastor_abort(struct scsi_cmnd *SCpnt) { #if ULTRASTOR_DEBUG & UD_ABORT char out[108]; @@ -843,7 +844,7 @@ static int ultrastor_abort(Scsi_Cmnd *SCpnt) unsigned int mscp_index; unsigned char old_aborted; unsigned long flags; - void (*done)(Scsi_Cmnd *); + void (*done)(struct scsi_cmnd *); struct Scsi_Host *host = SCpnt->device->host; if(config.slot) @@ -960,7 +961,7 @@ static int ultrastor_abort(Scsi_Cmnd *SCpnt) return SUCCESS; } -static int ultrastor_host_reset(Scsi_Cmnd * SCpnt) +static int ultrastor_host_reset(struct scsi_cmnd * SCpnt) { unsigned long flags; int i; @@ -1045,8 +1046,8 @@ static void ultrastor_interrupt(int irq, void *dev_id, struct pt_regs *regs) unsigned int mscp_index; #endif struct mscp *mscp; - void (*done)(Scsi_Cmnd *); - Scsi_Cmnd *SCtmp; + void (*done) (struct scsi_cmnd *); + struct scsi_cmnd *SCtmp; #if ULTRASTOR_MAX_CMDS == 1 mscp = &config.mscp[0]; @@ -1079,7 +1080,7 @@ static void ultrastor_interrupt(int irq, void *dev_id, struct pt_regs *regs) return; } if (icm_status == 3) { - void (*done)(Scsi_Cmnd *) = mscp->done; + void (*done)(struct scsi_cmnd *) = mscp->done; if (done) { mscp->done = NULL; mscp->SCint->result = DID_ABORT << 16; diff --git a/drivers/scsi/ultrastor.h b/drivers/scsi/ultrastor.h index da759a11def..a692905f95f 100644 --- a/drivers/scsi/ultrastor.h +++ b/drivers/scsi/ultrastor.h @@ -14,11 +14,13 @@ #define _ULTRASTOR_H static int ultrastor_detect(struct scsi_host_template *); -static const char *ultrastor_info(struct Scsi_Host * shpnt); -static int ultrastor_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); -static int ultrastor_abort(Scsi_Cmnd *); -static int ultrastor_host_reset(Scsi_Cmnd *); -static int ultrastor_biosparam(struct scsi_device *, struct block_device *, sector_t, int *); +static const char *ultrastor_info(struct Scsi_Host *shpnt); +static int ultrastor_queuecommand(struct scsi_cmnd *, + void (*done)(struct scsi_cmnd *)); +static int ultrastor_abort(struct scsi_cmnd *); +static int ultrastor_host_reset(struct scsi_cmnd *); +static int ultrastor_biosparam(struct scsi_device *, struct block_device *, + sector_t, int *); #define ULTRASTOR_14F_MAX_SG 16 -- cgit v1.2.3 From 88edf74610bd894b93438f389688bc8b4a2d3414 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 6 Sep 2006 17:36:13 -0500 Subject: [SCSI] SAS: consolidate linkspeed definitions At the moment we have two separate linkspeed enumerations covering roughly the same values. This patch consolidates on a single one enum sas_linkspeed in scsi_transport_sas.h and uses it everywhere in the aic94xx driver. Eventually I'll get around to removing the duplicated fields in asd_sas_phy and sas_phy ... Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx_hwi.c | 2 +- drivers/scsi/aic94xx/aic94xx_init.c | 12 ++++++++---- drivers/scsi/aic94xx/aic94xx_scb.c | 26 +++++++++++++------------- drivers/scsi/libsas/sas_expander.c | 27 +++++++-------------------- drivers/scsi/libsas/sas_internal.h | 2 +- 5 files changed, 30 insertions(+), 39 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.c b/drivers/scsi/aic94xx/aic94xx_hwi.c index 075cea85b56..a2420135110 100644 --- a/drivers/scsi/aic94xx/aic94xx_hwi.c +++ b/drivers/scsi/aic94xx/aic94xx_hwi.c @@ -96,7 +96,7 @@ static int asd_init_phy(struct asd_phy *phy) sas_phy->type = PHY_TYPE_PHYSICAL; sas_phy->role = PHY_ROLE_INITIATOR; sas_phy->oob_mode = OOB_NOT_CONNECTED; - sas_phy->linkrate = PHY_LINKRATE_NONE; + sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN; phy->id_frm_tok = asd_alloc_coherent(asd_ha, sizeof(*phy->identify_frame), diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index 69aa7088753..302b54fddf3 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -240,10 +240,14 @@ static int __devinit asd_common_setup(struct asd_ha_struct *asd_ha) /* All phys are enabled, by default. */ asd_ha->hw_prof.enabled_phys = 0xFF; for (i = 0; i < ASD_MAX_PHYS; i++) { - asd_ha->hw_prof.phy_desc[i].max_sas_lrate = PHY_LINKRATE_3; - asd_ha->hw_prof.phy_desc[i].min_sas_lrate = PHY_LINKRATE_1_5; - asd_ha->hw_prof.phy_desc[i].max_sata_lrate= PHY_LINKRATE_1_5; - asd_ha->hw_prof.phy_desc[i].min_sata_lrate= PHY_LINKRATE_1_5; + asd_ha->hw_prof.phy_desc[i].max_sas_lrate = + SAS_LINK_RATE_3_0_GBPS; + asd_ha->hw_prof.phy_desc[i].min_sas_lrate = + SAS_LINK_RATE_1_5_GBPS; + asd_ha->hw_prof.phy_desc[i].max_sata_lrate = + SAS_LINK_RATE_1_5_GBPS; + asd_ha->hw_prof.phy_desc[i].min_sata_lrate = + SAS_LINK_RATE_1_5_GBPS; } return 0; diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c index fc1b7438a91..ef8ca08b545 100644 --- a/drivers/scsi/aic94xx/aic94xx_scb.c +++ b/drivers/scsi/aic94xx/aic94xx_scb.c @@ -55,15 +55,15 @@ static inline void get_lrate_mode(struct asd_phy *phy, u8 oob_mode) switch (oob_mode & 7) { case PHY_SPEED_60: /* FIXME: sas transport class doesn't have this */ - phy->sas_phy.linkrate = PHY_LINKRATE_6; + phy->sas_phy.linkrate = SAS_LINK_RATE_6_0_GBPS; phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS; break; case PHY_SPEED_30: - phy->sas_phy.linkrate = PHY_LINKRATE_3; + phy->sas_phy.linkrate = SAS_LINK_RATE_3_0_GBPS; phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS; break; case PHY_SPEED_15: - phy->sas_phy.linkrate = PHY_LINKRATE_1_5; + phy->sas_phy.linkrate = SAS_LINK_RATE_1_5_GBPS; phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS; break; } @@ -540,39 +540,39 @@ static inline void set_speed_mask(u8 *speed_mask, struct asd_phy_desc *pd) | SATA_SPEED_30_DIS | SATA_SPEED_15_DIS; switch (pd->max_sas_lrate) { - case PHY_LINKRATE_6: + case SAS_LINK_RATE_6_0_GBPS: *speed_mask &= ~SAS_SPEED_60_DIS; default: - case PHY_LINKRATE_3: + case SAS_LINK_RATE_3_0_GBPS: *speed_mask &= ~SAS_SPEED_30_DIS; - case PHY_LINKRATE_1_5: + case SAS_LINK_RATE_1_5_GBPS: *speed_mask &= ~SAS_SPEED_15_DIS; } switch (pd->min_sas_lrate) { - case PHY_LINKRATE_6: + case SAS_LINK_RATE_6_0_GBPS: *speed_mask |= SAS_SPEED_30_DIS; - case PHY_LINKRATE_3: + case SAS_LINK_RATE_3_0_GBPS: *speed_mask |= SAS_SPEED_15_DIS; default: - case PHY_LINKRATE_1_5: + case SAS_LINK_RATE_1_5_GBPS: /* nothing to do */ ; } switch (pd->max_sata_lrate) { - case PHY_LINKRATE_3: + case SAS_LINK_RATE_3_0_GBPS: *speed_mask &= ~SATA_SPEED_30_DIS; default: - case PHY_LINKRATE_1_5: + case SAS_LINK_RATE_1_5_GBPS: *speed_mask &= ~SATA_SPEED_15_DIS; } switch (pd->min_sata_lrate) { - case PHY_LINKRATE_3: + case SAS_LINK_RATE_3_0_GBPS: *speed_mask |= SATA_SPEED_15_DIS; default: - case PHY_LINKRATE_1_5: + case SAS_LINK_RATE_1_5_GBPS: /* nothing to do */ ; } diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index b653a263f76..02e796ee027 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -191,20 +191,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS; phy->phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS; phy->phy->maximum_linkrate = SAS_LINK_RATE_3_0_GBPS; - switch (phy->linkrate) { - case PHY_LINKRATE_1_5: - phy->phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS; - break; - case PHY_LINKRATE_3: - phy->phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS; - break; - case PHY_LINKRATE_6: - phy->phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS; - break; - default: - phy->phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; - break; - } + phy->phy->negotiated_linkrate = phy->linkrate; if (!rediscover) sas_phy_add(phy->phy); @@ -450,7 +437,7 @@ static void sas_ex_disable_phy(struct domain_device *dev, int phy_id) struct ex_phy *phy = &ex->ex_phy[phy_id]; sas_smp_phy_control(dev, phy_id, PHY_FUNC_DISABLE); - phy->linkrate = PHY_DISABLED; + phy->linkrate = SAS_PHY_DISABLED; } static void sas_ex_disable_port(struct domain_device *dev, u8 *sas_addr) @@ -743,7 +730,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) int res = 0; /* Phy state */ - if (ex_phy->linkrate == PHY_SPINUP_HOLD) { + if (ex_phy->linkrate == SAS_SATA_SPINUP_HOLD) { if (!sas_smp_phy_control(dev, phy_id, PHY_FUNC_LINK_RESET)) res = sas_ex_phy_discover(dev, phy_id); if (res) @@ -773,7 +760,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) sas_configure_routing(dev, ex_phy->attached_sas_addr); } return 0; - } else if (ex_phy->linkrate == PHY_LINKRATE_UNKNOWN) + } else if (ex_phy->linkrate == SAS_LINK_RATE_UNKNOWN) return 0; if (ex_phy->attached_dev_type != SAS_END_DEV && @@ -922,9 +909,9 @@ static int sas_ex_discover_devices(struct domain_device *dev, int single) continue; switch (ex_phy->linkrate) { - case PHY_DISABLED: - case PHY_RESET_PROBLEM: - case PHY_PORT_SELECTOR: + case SAS_PHY_DISABLED: + case SAS_PHY_RESET_PROBLEM: + case SAS_SATA_PORT_SELECTOR: continue; default: res = sas_ex_discover_dev(dev, i); diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 89c39768084..0d69ede4b94 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -43,7 +43,7 @@ void sas_scsi_recover_host(struct Scsi_Host *shost); int sas_show_class(enum sas_class class, char *buf); int sas_show_proto(enum sas_proto proto, char *buf); -int sas_show_linkrate(enum sas_phy_linkrate linkrate, char *buf); +int sas_show_linkrate(enum sas_linkrate linkrate, char *buf); int sas_show_oob_mode(enum sas_oob_mode oob_mode, char *buf); int sas_register_phys(struct sas_ha_struct *sas_ha); -- cgit v1.2.3 From d24e1eeb3a16e4944288c2f3bf082e1513f4b425 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 6 Sep 2006 19:25:22 -0500 Subject: [SCSI] scsi_transport_sas: make minimum and maximum linkrate settable quantities According to SPEC, the minimum_linkrate and maximum_linkrate should be settable by the user. This patch introduces a callback that allows the sas class to pass these settings on to the driver. Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_sas.c | 73 +++++++++++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 6 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index d518c1207fb..b5b0c2cba96 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -77,6 +77,24 @@ get_sas_##title##_names(u32 table_key, char *buf) \ return len; \ } +#define sas_bitfield_name_set(title, table) \ +static ssize_t \ +set_sas_##title##_names(u32 *table_key, const char *buf) \ +{ \ + ssize_t len = 0; \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(table); i++) { \ + len = strlen(table[i].name); \ + if (strncmp(buf, table[i].name, len) == 0 && \ + (buf[len] == '\n' || buf[len] == '\0')) { \ + *table_key = table[i].value; \ + return 0; \ + } \ + } \ + return -EINVAL; \ +} + #define sas_bitfield_name_search(title, table) \ static ssize_t \ get_sas_##title##_names(u32 table_key, char *buf) \ @@ -131,7 +149,7 @@ static struct { { SAS_LINK_RATE_6_0_GBPS, "6.0 Gbit" }, }; sas_bitfield_name_search(linkspeed, sas_linkspeed_names) - +sas_bitfield_name_set(linkspeed, sas_linkspeed_names) /* * SAS host attributes @@ -253,10 +271,39 @@ show_sas_phy_##field(struct class_device *cdev, char *buf) \ return get_sas_linkspeed_names(phy->field, buf); \ } +/* Fudge to tell if we're minimum or maximum */ +#define sas_phy_store_linkspeed(field) \ +static ssize_t \ +store_sas_phy_##field(struct class_device *cdev, const char *buf, \ + size_t count) \ +{ \ + struct sas_phy *phy = transport_class_to_phy(cdev); \ + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); \ + struct sas_internal *i = to_sas_internal(shost->transportt); \ + u32 value; \ + struct sas_phy_linkrates rates = {0}; \ + int error; \ + \ + error = set_sas_linkspeed_names(&value, buf); \ + if (error) \ + return error; \ + rates.field = value; \ + error = i->f->set_phy_speed(phy, &rates); \ + \ + return error ? error : count; \ +} + +#define sas_phy_linkspeed_rw_attr(field) \ + sas_phy_show_linkspeed(field) \ + sas_phy_store_linkspeed(field) \ +static CLASS_DEVICE_ATTR(field, S_IRUGO, show_sas_phy_##field, \ + store_sas_phy_##field) + #define sas_phy_linkspeed_attr(field) \ sas_phy_show_linkspeed(field) \ static CLASS_DEVICE_ATTR(field, S_IRUGO, show_sas_phy_##field, NULL) + #define sas_phy_show_linkerror(field) \ static ssize_t \ show_sas_phy_##field(struct class_device *cdev, char *buf) \ @@ -326,9 +373,9 @@ sas_phy_simple_attr(identify.phy_identifier, phy_identifier, "%d\n", u8); //sas_phy_simple_attr(port_identifier, port_identifier, "%d\n", int); sas_phy_linkspeed_attr(negotiated_linkrate); sas_phy_linkspeed_attr(minimum_linkrate_hw); -sas_phy_linkspeed_attr(minimum_linkrate); +sas_phy_linkspeed_rw_attr(minimum_linkrate); sas_phy_linkspeed_attr(maximum_linkrate_hw); -sas_phy_linkspeed_attr(maximum_linkrate); +sas_phy_linkspeed_rw_attr(maximum_linkrate); sas_phy_linkerror_attr(invalid_dword_count); sas_phy_linkerror_attr(running_disparity_error_count); sas_phy_linkerror_attr(loss_of_dword_sync_count); @@ -1310,13 +1357,23 @@ static int sas_user_scan(struct Scsi_Host *shost, uint channel, * Setup / Teardown code */ -#define SETUP_TEMPLATE(attrb, field, perm, test) \ +#define SETUP_TEMPLATE(attrb, field, perm, test) \ i->private_##attrb[count] = class_device_attr_##field; \ i->private_##attrb[count].attr.mode = perm; \ i->attrb[count] = &i->private_##attrb[count]; \ if (test) \ count++ +#define SETUP_TEMPLATE_RW(attrb, field, perm, test, ro_test, ro_perm) \ + i->private_##attrb[count] = class_device_attr_##field; \ + i->private_##attrb[count].attr.mode = perm; \ + if (ro_test) { \ + i->private_##attrb[count].attr.mode = ro_perm; \ + i->private_##attrb[count].store = NULL; \ + } \ + i->attrb[count] = &i->private_##attrb[count]; \ + if (test) \ + count++ #define SETUP_RPORT_ATTRIBUTE(field) \ SETUP_TEMPLATE(rphy_attrs, field, S_IRUGO, 1) @@ -1327,6 +1384,10 @@ static int sas_user_scan(struct Scsi_Host *shost, uint channel, #define SETUP_PHY_ATTRIBUTE(field) \ SETUP_TEMPLATE(phy_attrs, field, S_IRUGO, 1) +#define SETUP_PHY_ATTRIBUTE_RW(field) \ + SETUP_TEMPLATE_RW(phy_attrs, field, S_IRUGO | S_IWUSR, 1, \ + !i->f->set_phy_speed, S_IRUGO) + #define SETUP_PORT_ATTRIBUTE(field) \ SETUP_TEMPLATE(port_attrs, field, S_IRUGO, 1) @@ -1407,9 +1468,9 @@ sas_attach_transport(struct sas_function_template *ft) //SETUP_PHY_ATTRIBUTE(port_identifier); SETUP_PHY_ATTRIBUTE(negotiated_linkrate); SETUP_PHY_ATTRIBUTE(minimum_linkrate_hw); - SETUP_PHY_ATTRIBUTE(minimum_linkrate); + SETUP_PHY_ATTRIBUTE_RW(minimum_linkrate); SETUP_PHY_ATTRIBUTE(maximum_linkrate_hw); - SETUP_PHY_ATTRIBUTE(maximum_linkrate); + SETUP_PHY_ATTRIBUTE_RW(maximum_linkrate); SETUP_PHY_ATTRIBUTE(invalid_dword_count); SETUP_PHY_ATTRIBUTE(running_disparity_error_count); -- cgit v1.2.3 From a01e70e570a72b8a8c9a58062e4f5bdcd3986222 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 6 Sep 2006 19:28:07 -0500 Subject: [SCSI] aci94xx: implement link rate setting This patch implements the ability to set the minimum and maximum linkrates for both libsas (for expanders) and aic94xx (for the host phys). It also tidies up the setting of the hardware min and max to make sure they're updated when the expander emits a change broadcast. Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx.h | 2 +- drivers/scsi/aic94xx/aic94xx_scb.c | 30 ++++++++++++++++++++++++-- drivers/scsi/libsas/sas_expander.c | 20 +++++++++++------ drivers/scsi/libsas/sas_init.c | 44 ++++++++++++++++++++++++++++++++++++-- drivers/scsi/libsas/sas_internal.h | 2 +- drivers/scsi/libsas/sas_phy.c | 15 +++++++------ 6 files changed, 93 insertions(+), 20 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aic94xx/aic94xx.h b/drivers/scsi/aic94xx/aic94xx.h index cb7caf1c9ce..1bd5b4ecf3d 100644 --- a/drivers/scsi/aic94xx/aic94xx.h +++ b/drivers/scsi/aic94xx/aic94xx.h @@ -109,6 +109,6 @@ int asd_clear_nexus_port(struct asd_sas_port *port); int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha); /* ---------- Phy Management ---------- */ -int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func); +int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func, void *arg); #endif diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c index ef8ca08b545..7ee49b51b72 100644 --- a/drivers/scsi/aic94xx/aic94xx_scb.c +++ b/drivers/scsi/aic94xx/aic94xx_scb.c @@ -52,6 +52,8 @@ static inline void get_lrate_mode(struct asd_phy *phy, u8 oob_mode) { + struct sas_phy *sas_phy = phy->sas_phy.phy; + switch (oob_mode & 7) { case PHY_SPEED_60: /* FIXME: sas transport class doesn't have this */ @@ -67,6 +69,12 @@ static inline void get_lrate_mode(struct asd_phy *phy, u8 oob_mode) phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS; break; } + sas_phy->negotiated_linkrate = phy->sas_phy.linkrate; + sas_phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS; + sas_phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; + sas_phy->maximum_linkrate = phy->phy_desc->max_sas_lrate; + sas_phy->minimum_linkrate = phy->phy_desc->min_sas_lrate; + if (oob_mode & SAS_MODE) phy->sas_phy.oob_mode = SAS_OOB_MODE; else if (oob_mode & SATA_MODE) @@ -710,14 +718,32 @@ static const int phy_func_table[] = { [PHY_FUNC_RELEASE_SPINUP_HOLD] = RELEASE_SPINUP_HOLD, }; -int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func) +int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func, void *arg) { struct asd_ha_struct *asd_ha = phy->ha->lldd_ha; + struct asd_phy_desc *pd = asd_ha->phys[phy->id].phy_desc; struct asd_ascb *ascb; + struct sas_phy_linkrates *rates; int res = 1; - if (func == PHY_FUNC_CLEAR_ERROR_LOG) + switch (func) { + case PHY_FUNC_CLEAR_ERROR_LOG: return -ENOSYS; + case PHY_FUNC_SET_LINK_RATE: + rates = arg; + if (rates->minimum_linkrate) { + pd->min_sas_lrate = rates->minimum_linkrate; + pd->min_sata_lrate = rates->minimum_linkrate; + } + if (rates->maximum_linkrate) { + pd->max_sas_lrate = rates->maximum_linkrate; + pd->max_sata_lrate = rates->maximum_linkrate; + } + func = PHY_FUNC_LINK_RESET; + break; + default: + break; + } ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); if (!ascb) diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 02e796ee027..30b8014bcc7 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -187,10 +187,10 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, phy->phy->identify.initiator_port_protocols = phy->attached_iproto; phy->phy->identify.target_port_protocols = phy->attached_tproto; phy->phy->identify.phy_identifier = phy_id; - phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; - phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS; - phy->phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS; - phy->phy->maximum_linkrate = SAS_LINK_RATE_3_0_GBPS; + phy->phy->minimum_linkrate_hw = dr->hmin_linkrate; + phy->phy->maximum_linkrate_hw = dr->hmax_linkrate; + phy->phy->minimum_linkrate = dr->pmin_linkrate; + phy->phy->maximum_linkrate = dr->pmax_linkrate; phy->phy->negotiated_linkrate = phy->linkrate; if (!rediscover) @@ -404,7 +404,8 @@ out: #define PC_RESP_SIZE 8 int sas_smp_phy_control(struct domain_device *dev, int phy_id, - enum phy_func phy_func) + enum phy_func phy_func, + struct sas_phy_linkrates *rates) { u8 *pc_req; u8 *pc_resp; @@ -423,6 +424,10 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id, pc_req[1] = SMP_PHY_CONTROL; pc_req[9] = phy_id; pc_req[10]= phy_func; + if (rates) { + pc_req[32] = rates->minimum_linkrate << 4; + pc_req[33] = rates->maximum_linkrate << 4; + } res = smp_execute_task(dev, pc_req, PC_REQ_SIZE, pc_resp,PC_RESP_SIZE); @@ -436,7 +441,7 @@ static void sas_ex_disable_phy(struct domain_device *dev, int phy_id) struct expander_device *ex = &dev->ex_dev; struct ex_phy *phy = &ex->ex_phy[phy_id]; - sas_smp_phy_control(dev, phy_id, PHY_FUNC_DISABLE); + sas_smp_phy_control(dev, phy_id, PHY_FUNC_DISABLE, NULL); phy->linkrate = SAS_PHY_DISABLED; } @@ -731,7 +736,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) /* Phy state */ if (ex_phy->linkrate == SAS_SATA_SPINUP_HOLD) { - if (!sas_smp_phy_control(dev, phy_id, PHY_FUNC_LINK_RESET)) + if (!sas_smp_phy_control(dev, phy_id, PHY_FUNC_LINK_RESET, NULL)) res = sas_ex_phy_discover(dev, phy_id); if (res) return res; @@ -1706,6 +1711,7 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id) SAS_ADDR(phy->attached_sas_addr)) { SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter\n", SAS_ADDR(dev->sas_addr), phy_id); + sas_ex_phy_discover(dev, phy_id); } else res = sas_discover_new(dev, phy_id); out: diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index b961664b810..c836a237fb7 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -159,17 +159,57 @@ static int sas_phy_reset(struct sas_phy *phy, int hard_reset) struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt); - ret = i->dft->lldd_control_phy(asd_phy, reset_type); + ret = i->dft->lldd_control_phy(asd_phy, reset_type, NULL); } else { struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); struct domain_device *ddev = sas_find_dev_by_rphy(rphy); - ret = sas_smp_phy_control(ddev, phy->number, reset_type); + ret = sas_smp_phy_control(ddev, phy->number, reset_type, NULL); } return ret; } +static int sas_set_phy_speed(struct sas_phy *phy, + struct sas_phy_linkrates *rates) +{ + int ret; + + if ((rates->minimum_linkrate && + rates->minimum_linkrate > phy->maximum_linkrate) || + (rates->maximum_linkrate && + rates->maximum_linkrate < phy->minimum_linkrate)) + return -EINVAL; + + if (rates->minimum_linkrate && + rates->minimum_linkrate < phy->minimum_linkrate_hw) + rates->minimum_linkrate = phy->minimum_linkrate_hw; + + if (rates->maximum_linkrate && + rates->maximum_linkrate > phy->maximum_linkrate_hw) + rates->maximum_linkrate = phy->maximum_linkrate_hw; + + if (scsi_is_sas_phy_local(phy)) { + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); + struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); + struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; + struct sas_internal *i = + to_sas_internal(sas_ha->core.shost->transportt); + + ret = i->dft->lldd_control_phy(asd_phy, PHY_FUNC_SET_LINK_RATE, + rates); + } else { + struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); + struct domain_device *ddev = sas_find_dev_by_rphy(rphy); + ret = sas_smp_phy_control(ddev, phy->number, + PHY_FUNC_LINK_RESET, rates); + + } + + return ret; +} + static struct sas_function_template sft = { .phy_reset = sas_phy_reset, + .set_phy_speed = sas_set_phy_speed, .get_linkerrors = sas_get_linkerrors, }; diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 0d69ede4b94..bffcee47492 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -70,7 +70,7 @@ int sas_notify_lldd_dev_found(struct domain_device *); void sas_notify_lldd_dev_gone(struct domain_device *); int sas_smp_phy_control(struct domain_device *dev, int phy_id, - enum phy_func phy_func); + enum phy_func phy_func, struct sas_phy_linkrates *); int sas_smp_get_phy_events(struct sas_phy *phy); struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c index 024ab00e70d..9340cdbae4a 100644 --- a/drivers/scsi/libsas/sas_phy.c +++ b/drivers/scsi/libsas/sas_phy.c @@ -67,13 +67,14 @@ static void sas_phye_oob_error(void *data) switch (phy->error) { case 1: case 2: - i->dft->lldd_control_phy(phy, PHY_FUNC_HARD_RESET); + i->dft->lldd_control_phy(phy, PHY_FUNC_HARD_RESET, + NULL); break; case 3: default: phy->error = 0; phy->enabled = 0; - i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE); + i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL); break; } } @@ -90,7 +91,7 @@ static void sas_phye_spinup_hold(void *data) &phy->phy_events_pending); phy->error = 0; - i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD); + i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL); } /* ---------- Phy class registration ---------- */ @@ -144,10 +145,10 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) phy->phy->identify.target_port_protocols = phy->tproto; phy->phy->identify.sas_address = SAS_ADDR(sas_ha->sas_addr); phy->phy->identify.phy_identifier = i; - phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; - phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS; - phy->phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS; - phy->phy->maximum_linkrate = SAS_LINK_RATE_3_0_GBPS; + phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN; + phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN; + phy->phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN; + phy->phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN; phy->phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; sas_phy_add(phy->phy); -- cgit v1.2.3 From 2b7cbe20174695bca1afe2a8f755e1eb299f4768 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Thu, 7 Sep 2006 15:14:46 -0500 Subject: [SCSI] fix up SCSI netlink build CONFIG_SCSI_NETLINK can become a bool since the item its selecting (CONFIG_NET) cannot be a module. Signed-off-by: James Bottomley --- drivers/scsi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 4d1998d23f0..a6f920d218a 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -28,7 +28,7 @@ config SCSI (the one containing the directory /) is located on a SCSI device. config SCSI_NETLINK - tristate + bool default n select NET -- cgit v1.2.3 From a506b44bb5000b2652490a906c3e58beb2a8f6bb Mon Sep 17 00:00:00 2001 From: Daniel Walker Date: Sat, 9 Sep 2006 09:31:03 -0700 Subject: [SCSI] fix compile error on module_refcount LD .tmp_vmlinux1 drivers/built-in.o(.text+0x8e1f9): In function `scsi_device_put': drivers/scsi/scsi.c:887: undefined reference to `module_refcount' make: *** [.tmp_vmlinux1] Error 1 There are only two users of module_refcount() outside of kernel/module.c and the other one uses ifdef's similar to this. Signed-Off-By: Daniel Walker Signed-off-by: James Bottomley --- drivers/scsi/scsi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index c35f5fc0d66..c51b5769eac 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -875,10 +875,12 @@ void scsi_device_put(struct scsi_device *sdev) { struct module *module = sdev->host->hostt->module; +#ifdef CONFIG_MODULE_UNLOAD /* The module refcount will be zero if scsi_device_get() * was called from a module removal routine */ if (module && module_refcount(module) != 0) module_put(module); +#endif put_device(&sdev->sdev_gendev); } EXPORT_SYMBOL(scsi_device_put); -- cgit v1.2.3 From 65396410af63db90d6428c678ff84aa652c3c1ec Mon Sep 17 00:00:00 2001 From: Henrik Kretzschmar Date: Tue, 12 Sep 2006 23:49:33 +0200 Subject: [SCSI] wd33c93: Scsi_Cmnd convertion Changes obsolete typedef'd Scsi_Cmnd to struct scsi_cmnd. Signed-off-by: Henrik Kretzschmar Signed-off-by: James Bottomley --- drivers/scsi/a2091.c | 6 +++--- drivers/scsi/a2091.h | 4 ---- drivers/scsi/a3000.c | 8 ++++---- drivers/scsi/a3000.h | 4 ---- drivers/scsi/gvp11.c | 8 ++++---- drivers/scsi/gvp11.h | 4 ---- drivers/scsi/mvme147.c | 6 +++--- drivers/scsi/mvme147.h | 4 ---- drivers/scsi/sgiwd93.c | 8 ++++---- 9 files changed, 18 insertions(+), 34 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/a2091.c b/drivers/scsi/a2091.c index fddfa2ebcd7..08540692860 100644 --- a/drivers/scsi/a2091.c +++ b/drivers/scsi/a2091.c @@ -40,7 +40,7 @@ static irqreturn_t a2091_intr (int irq, void *_instance, struct pt_regs *fp) return IRQ_HANDLED; } -static int dma_setup (Scsi_Cmnd *cmd, int dir_in) +static int dma_setup(struct scsi_cmnd *cmd, int dir_in) { unsigned short cntr = CNTR_PDMD | CNTR_INTEN; unsigned long addr = virt_to_bus(cmd->SCp.ptr); @@ -115,7 +115,7 @@ static int dma_setup (Scsi_Cmnd *cmd, int dir_in) return 0; } -static void dma_stop (struct Scsi_Host *instance, Scsi_Cmnd *SCpnt, +static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, int status) { /* disable SCSI interrupts */ @@ -217,7 +217,7 @@ int __init a2091_detect(struct scsi_host_template *tpnt) return num_a2091; } -static int a2091_bus_reset(Scsi_Cmnd *cmd) +static int a2091_bus_reset(struct scsi_cmnd *cmd) { /* FIXME perform bus-specific reset */ diff --git a/drivers/scsi/a2091.h b/drivers/scsi/a2091.h index 22d6a13dd8b..fe809bc88d7 100644 --- a/drivers/scsi/a2091.h +++ b/drivers/scsi/a2091.h @@ -13,10 +13,6 @@ int a2091_detect(struct scsi_host_template *); int a2091_release(struct Scsi_Host *); -const char *wd33c93_info(void); -int wd33c93_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); -int wd33c93_abort(Scsi_Cmnd *); -int wd33c93_reset(Scsi_Cmnd *, unsigned int); #ifndef CMD_PER_LUN #define CMD_PER_LUN 2 diff --git a/drivers/scsi/a3000.c b/drivers/scsi/a3000.c index ae9ab4b136a..7bf46d40b56 100644 --- a/drivers/scsi/a3000.c +++ b/drivers/scsi/a3000.c @@ -44,7 +44,7 @@ static irqreturn_t a3000_intr (int irq, void *dummy, struct pt_regs *fp) return IRQ_NONE; } -static int dma_setup (Scsi_Cmnd *cmd, int dir_in) +static int dma_setup(struct scsi_cmnd *cmd, int dir_in) { unsigned short cntr = CNTR_PDMD | CNTR_INTEN; unsigned long addr = virt_to_bus(cmd->SCp.ptr); @@ -110,8 +110,8 @@ static int dma_setup (Scsi_Cmnd *cmd, int dir_in) return 0; } -static void dma_stop (struct Scsi_Host *instance, Scsi_Cmnd *SCpnt, - int status) +static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, + int status) { /* disable SCSI interrupts */ unsigned short cntr = CNTR_PDMD; @@ -205,7 +205,7 @@ fail_register: return 0; } -static int a3000_bus_reset(Scsi_Cmnd *cmd) +static int a3000_bus_reset(struct scsi_cmnd *cmd) { /* FIXME perform bus-specific reset */ diff --git a/drivers/scsi/a3000.h b/drivers/scsi/a3000.h index 5535a65150a..44a4ec7b465 100644 --- a/drivers/scsi/a3000.h +++ b/drivers/scsi/a3000.h @@ -13,10 +13,6 @@ int a3000_detect(struct scsi_host_template *); int a3000_release(struct Scsi_Host *); -const char *wd33c93_info(void); -int wd33c93_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); -int wd33c93_abort(Scsi_Cmnd *); -int wd33c93_reset(Scsi_Cmnd *, unsigned int); #ifndef CMD_PER_LUN #define CMD_PER_LUN 2 diff --git a/drivers/scsi/gvp11.c b/drivers/scsi/gvp11.c index a0d831b1bad..18dbe5c27da 100644 --- a/drivers/scsi/gvp11.c +++ b/drivers/scsi/gvp11.c @@ -47,7 +47,7 @@ void gvp11_setup (char *str, int *ints) gvp11_xfer_mask = ints[1]; } -static int dma_setup (Scsi_Cmnd *cmd, int dir_in) +static int dma_setup(struct scsi_cmnd *cmd, int dir_in) { unsigned short cntr = GVP11_DMAC_INT_ENABLE; unsigned long addr = virt_to_bus(cmd->SCp.ptr); @@ -142,8 +142,8 @@ static int dma_setup (Scsi_Cmnd *cmd, int dir_in) return 0; } -static void dma_stop (struct Scsi_Host *instance, Scsi_Cmnd *SCpnt, - int status) +static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, + int status) { /* stop DMA */ DMA(instance)->SP_DMA = 1; @@ -341,7 +341,7 @@ release: return num_gvp11; } -static int gvp11_bus_reset(Scsi_Cmnd *cmd) +static int gvp11_bus_reset(struct scsi_cmnd *cmd) { /* FIXME perform bus-specific reset */ diff --git a/drivers/scsi/gvp11.h b/drivers/scsi/gvp11.h index 575d219d14b..bf22859a503 100644 --- a/drivers/scsi/gvp11.h +++ b/drivers/scsi/gvp11.h @@ -13,10 +13,6 @@ int gvp11_detect(struct scsi_host_template *); int gvp11_release(struct Scsi_Host *); -const char *wd33c93_info(void); -int wd33c93_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); -int wd33c93_abort(Scsi_Cmnd *); -int wd33c93_reset(Scsi_Cmnd *, unsigned int); #ifndef CMD_PER_LUN #define CMD_PER_LUN 2 diff --git a/drivers/scsi/mvme147.c b/drivers/scsi/mvme147.c index cb367c2c5c7..9b991b746d1 100644 --- a/drivers/scsi/mvme147.c +++ b/drivers/scsi/mvme147.c @@ -29,7 +29,7 @@ static irqreturn_t mvme147_intr (int irq, void *dummy, struct pt_regs *fp) return IRQ_HANDLED; } -static int dma_setup (Scsi_Cmnd *cmd, int dir_in) +static int dma_setup(struct scsi_cmnd *cmd, int dir_in) { unsigned char flags = 0x01; unsigned long addr = virt_to_bus(cmd->SCp.ptr); @@ -57,7 +57,7 @@ static int dma_setup (Scsi_Cmnd *cmd, int dir_in) return 0; } -static void dma_stop (struct Scsi_Host *instance, Scsi_Cmnd *SCpnt, +static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, int status) { m147_pcc->dma_cntrl = 0; @@ -112,7 +112,7 @@ int mvme147_detect(struct scsi_host_template *tpnt) return 0; } -static int mvme147_bus_reset(Scsi_Cmnd *cmd) +static int mvme147_bus_reset(struct scsi_cmnd *cmd) { /* FIXME perform bus-specific reset */ diff --git a/drivers/scsi/mvme147.h b/drivers/scsi/mvme147.h index 2f56d69bd18..32aee85434d 100644 --- a/drivers/scsi/mvme147.h +++ b/drivers/scsi/mvme147.h @@ -12,10 +12,6 @@ int mvme147_detect(struct scsi_host_template *); int mvme147_release(struct Scsi_Host *); -const char *wd33c93_info(void); -int wd33c93_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); -int wd33c93_abort(Scsi_Cmnd *); -int wd33c93_reset(Scsi_Cmnd *, unsigned int); #ifndef CMD_PER_LUN #define CMD_PER_LUN 2 diff --git a/drivers/scsi/sgiwd93.c b/drivers/scsi/sgiwd93.c index 7cd366fcc57..4f1db6f2aae 100644 --- a/drivers/scsi/sgiwd93.c +++ b/drivers/scsi/sgiwd93.c @@ -97,7 +97,7 @@ static irqreturn_t sgiwd93_intr(int irq, void *dev_id, struct pt_regs *regs) } static inline -void fill_hpc_entries(struct hpc_chunk *hcp, Scsi_Cmnd *cmd, int datainp) +void fill_hpc_entries(struct hpc_chunk *hcp, struct scsi_cmnd *cmd, int datainp) { unsigned long len = cmd->SCp.this_residual; void *addr = cmd->SCp.ptr; @@ -129,7 +129,7 @@ void fill_hpc_entries(struct hpc_chunk *hcp, Scsi_Cmnd *cmd, int datainp) hcp->desc.cntinfo = HPCDMA_EOX; } -static int dma_setup(Scsi_Cmnd *cmd, int datainp) +static int dma_setup(struct scsi_cmnd *cmd, int datainp) { struct ip22_hostdata *hdata = HDATA(cmd->device->host); struct hpc3_scsiregs *hregs = @@ -163,7 +163,7 @@ static int dma_setup(Scsi_Cmnd *cmd, int datainp) return 0; } -static void dma_stop(struct Scsi_Host *instance, Scsi_Cmnd *SCpnt, +static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, int status) { struct ip22_hostdata *hdata = HDATA(instance); @@ -305,7 +305,7 @@ static int sgiwd93_release(struct Scsi_Host *instance) return 1; } -static int sgiwd93_bus_reset(Scsi_Cmnd *cmd) +static int sgiwd93_bus_reset(struct scsi_cmnd *cmd) { /* FIXME perform bus-specific reset */ -- cgit v1.2.3 From dc64ddf4918f0da52df10d83c2a5941a547c2035 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 24 Aug 2006 18:45:50 +1000 Subject: [SCSI] iscsi: Use crypto_hash interface instead of crypto_digest This patch converts ISCSI to use the new crypto_hash interface instead of crypto_digest. It's a fairly straightforward substitution. Signed-off-by: Herbert Xu --- drivers/scsi/iscsi_tcp.c | 134 ++++++++++++++++++++++++++--------------------- drivers/scsi/iscsi_tcp.h | 9 ++-- 2 files changed, 78 insertions(+), 65 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 058f094f945..66a1ae1d698 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -26,6 +26,7 @@ * Zhenyu Wang */ +#include #include #include #include @@ -107,8 +108,11 @@ iscsi_hdr_digest(struct iscsi_conn *conn, struct iscsi_buf *buf, u8* crc) { struct iscsi_tcp_conn *tcp_conn = conn->dd_data; + struct hash_desc desc; - crypto_digest_digest(tcp_conn->tx_tfm, &buf->sg, 1, crc); + desc.tfm = tcp_conn->tx_tfm; + desc.flags = 0; + crypto_hash_digest(&desc, &buf->sg, buf->sg.length, crc); buf->sg.length += sizeof(uint32_t); } @@ -452,11 +456,14 @@ iscsi_tcp_hdr_recv(struct iscsi_conn *conn) } if (conn->hdrdgst_en) { + struct hash_desc desc; struct scatterlist sg; sg_init_one(&sg, (u8 *)hdr, sizeof(struct iscsi_hdr) + ahslen); - crypto_digest_digest(tcp_conn->rx_tfm, &sg, 1, (u8 *)&cdgst); + desc.tfm = tcp_conn->rx_tfm; + desc.flags = 0; + crypto_hash_digest(&desc, &sg, sg.length, (u8 *)&cdgst); rdgst = *(uint32_t*)((char*)hdr + sizeof(struct iscsi_hdr) + ahslen); if (cdgst != rdgst) { @@ -673,7 +680,7 @@ partial_sg_digest_update(struct iscsi_tcp_conn *tcp_conn, memcpy(&temp, sg, sizeof(struct scatterlist)); temp.offset = offset; temp.length = length; - crypto_digest_update(tcp_conn->data_rx_tfm, &temp, 1); + crypto_hash_update(&tcp_conn->data_rx_hash, &temp, length); } static void @@ -682,7 +689,7 @@ iscsi_recv_digest_update(struct iscsi_tcp_conn *tcp_conn, char* buf, int len) struct scatterlist tmp; sg_init_one(&tmp, buf, len); - crypto_digest_update(tcp_conn->data_rx_tfm, &tmp, 1); + crypto_hash_update(&tcp_conn->data_rx_hash, &tmp, len); } static int iscsi_scsi_data_in(struct iscsi_conn *conn) @@ -736,9 +743,9 @@ static int iscsi_scsi_data_in(struct iscsi_conn *conn) if (!rc) { if (conn->datadgst_en) { if (!offset) - crypto_digest_update( - tcp_conn->data_rx_tfm, - &sg[i], 1); + crypto_hash_update( + &tcp_conn->data_rx_hash, + &sg[i], sg[i].length); else partial_sg_digest_update(tcp_conn, &sg[i], @@ -877,8 +884,7 @@ more: rc = iscsi_tcp_hdr_recv(conn); if (!rc && tcp_conn->in.datalen) { if (conn->datadgst_en) { - BUG_ON(!tcp_conn->data_rx_tfm); - crypto_digest_init(tcp_conn->data_rx_tfm); + crypto_hash_init(&tcp_conn->data_rx_hash); } tcp_conn->in_progress = IN_PROGRESS_DATA_RECV; } else if (rc) { @@ -931,11 +937,11 @@ more: tcp_conn->in.padding); memset(pad, 0, tcp_conn->in.padding); sg_init_one(&sg, pad, tcp_conn->in.padding); - crypto_digest_update(tcp_conn->data_rx_tfm, - &sg, 1); + crypto_hash_update(&tcp_conn->data_rx_hash, + &sg, sg.length); } - crypto_digest_final(tcp_conn->data_rx_tfm, - (u8 *) & tcp_conn->in.datadgst); + crypto_hash_final(&tcp_conn->data_rx_hash, + (u8 *)&tcp_conn->in.datadgst); debug_tcp("rx digest 0x%x\n", tcp_conn->in.datadgst); tcp_conn->in_progress = IN_PROGRESS_DDIGEST_RECV; } else @@ -1181,8 +1187,7 @@ iscsi_data_digest_init(struct iscsi_tcp_conn *tcp_conn, { struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; - BUG_ON(!tcp_conn->data_tx_tfm); - crypto_digest_init(tcp_conn->data_tx_tfm); + crypto_hash_init(&tcp_conn->data_tx_hash); tcp_ctask->digest_count = 4; } @@ -1196,7 +1201,7 @@ iscsi_digest_final_send(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, int sent = 0; if (final) - crypto_digest_final(tcp_conn->data_tx_tfm, (u8*)digest); + crypto_hash_final(&tcp_conn->data_tx_hash, (u8 *)digest); iscsi_buf_init_iov(buf, (char*)digest, 4); rc = iscsi_sendpage(conn, buf, &tcp_ctask->digest_count, &sent); @@ -1491,16 +1496,17 @@ handle_xmstate_imm_data(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) if (rc) { tcp_ctask->xmstate |= XMSTATE_IMM_DATA; if (conn->datadgst_en) { - crypto_digest_final(tcp_conn->data_tx_tfm, - (u8*)&tcp_ctask->immdigest); + crypto_hash_final(&tcp_conn->data_tx_hash, + (u8 *)&tcp_ctask->immdigest); debug_tcp("tx imm sendpage fail 0x%x\n", tcp_ctask->datadigest); } return rc; } if (conn->datadgst_en) - crypto_digest_update(tcp_conn->data_tx_tfm, - &tcp_ctask->sendbuf.sg, 1); + crypto_hash_update(&tcp_conn->data_tx_hash, + &tcp_ctask->sendbuf.sg, + tcp_ctask->sendbuf.sg.length); if (!ctask->imm_count) break; @@ -1577,8 +1583,8 @@ handle_xmstate_uns_data(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) tcp_ctask->xmstate |= XMSTATE_UNS_DATA; /* will continue with this ctask later.. */ if (conn->datadgst_en) { - crypto_digest_final(tcp_conn->data_tx_tfm, - (u8 *)&dtask->digest); + crypto_hash_final(&tcp_conn->data_tx_hash, + (u8 *)&dtask->digest); debug_tcp("tx uns data fail 0x%x\n", dtask->digest); } @@ -1593,8 +1599,9 @@ handle_xmstate_uns_data(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) * so pass it */ if (conn->datadgst_en && tcp_ctask->sent - start > 0) - crypto_digest_update(tcp_conn->data_tx_tfm, - &tcp_ctask->sendbuf.sg, 1); + crypto_hash_update(&tcp_conn->data_tx_hash, + &tcp_ctask->sendbuf.sg, + tcp_ctask->sendbuf.sg.length); if (!ctask->data_count) break; @@ -1668,7 +1675,7 @@ solicit_again: tcp_ctask->xmstate |= XMSTATE_SOL_DATA; /* will continue with this ctask later.. */ if (conn->datadgst_en) { - crypto_digest_final(tcp_conn->data_tx_tfm, + crypto_hash_final(&tcp_conn->data_tx_hash, (u8 *)&dtask->digest); debug_tcp("r2t data send fail 0x%x\n", dtask->digest); } @@ -1677,8 +1684,8 @@ solicit_again: BUG_ON(r2t->data_count < 0); if (conn->datadgst_en) - crypto_digest_update(tcp_conn->data_tx_tfm, &r2t->sendbuf.sg, - 1); + crypto_hash_update(&tcp_conn->data_tx_hash, &r2t->sendbuf.sg, + r2t->sendbuf.sg.length); if (r2t->data_count) { BUG_ON(ctask->sc->use_sg == 0); @@ -1766,8 +1773,9 @@ handle_xmstate_w_pad(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) } if (conn->datadgst_en) { - crypto_digest_update(tcp_conn->data_tx_tfm, - &tcp_ctask->sendbuf.sg, 1); + crypto_hash_update(&tcp_conn->data_tx_hash, + &tcp_ctask->sendbuf.sg, + tcp_ctask->sendbuf.sg.length); /* imm data? */ if (!dtask) { rc = iscsi_digest_final_send(conn, ctask, @@ -1963,13 +1971,13 @@ iscsi_tcp_conn_destroy(struct iscsi_cls_conn *cls_conn) /* now free tcp_conn */ if (digest) { if (tcp_conn->tx_tfm) - crypto_free_tfm(tcp_conn->tx_tfm); + crypto_free_hash(tcp_conn->tx_tfm); if (tcp_conn->rx_tfm) - crypto_free_tfm(tcp_conn->rx_tfm); - if (tcp_conn->data_tx_tfm) - crypto_free_tfm(tcp_conn->data_tx_tfm); - if (tcp_conn->data_rx_tfm) - crypto_free_tfm(tcp_conn->data_rx_tfm); + crypto_free_hash(tcp_conn->rx_tfm); + if (tcp_conn->data_tx_hash.tfm) + crypto_free_hash(tcp_conn->data_tx_hash.tfm); + if (tcp_conn->data_rx_hash.tfm) + crypto_free_hash(tcp_conn->data_rx_hash.tfm); } kfree(tcp_conn); @@ -2130,44 +2138,48 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, if (conn->hdrdgst_en) { tcp_conn->hdr_size += sizeof(__u32); if (!tcp_conn->tx_tfm) - tcp_conn->tx_tfm = crypto_alloc_tfm("crc32c", - 0); - if (!tcp_conn->tx_tfm) - return -ENOMEM; + tcp_conn->tx_tfm = + crypto_alloc_hash("crc32c", 0, + CRYPTO_ALG_ASYNC); + if (IS_ERR(tcp_conn->tx_tfm)) + return PTR_ERR(tcp_conn->tx_tfm); if (!tcp_conn->rx_tfm) - tcp_conn->rx_tfm = crypto_alloc_tfm("crc32c", - 0); - if (!tcp_conn->rx_tfm) { - crypto_free_tfm(tcp_conn->tx_tfm); - return -ENOMEM; + tcp_conn->rx_tfm = + crypto_alloc_hash("crc32c", 0, + CRYPTO_ALG_ASYNC); + if (IS_ERR(tcp_conn->rx_tfm)) { + crypto_free_hash(tcp_conn->tx_tfm); + return PTR_ERR(tcp_conn->rx_tfm); } } else { if (tcp_conn->tx_tfm) - crypto_free_tfm(tcp_conn->tx_tfm); + crypto_free_hash(tcp_conn->tx_tfm); if (tcp_conn->rx_tfm) - crypto_free_tfm(tcp_conn->rx_tfm); + crypto_free_hash(tcp_conn->rx_tfm); } break; case ISCSI_PARAM_DATADGST_EN: iscsi_set_param(cls_conn, param, buf, buflen); if (conn->datadgst_en) { - if (!tcp_conn->data_tx_tfm) - tcp_conn->data_tx_tfm = - crypto_alloc_tfm("crc32c", 0); - if (!tcp_conn->data_tx_tfm) - return -ENOMEM; - if (!tcp_conn->data_rx_tfm) - tcp_conn->data_rx_tfm = - crypto_alloc_tfm("crc32c", 0); - if (!tcp_conn->data_rx_tfm) { - crypto_free_tfm(tcp_conn->data_tx_tfm); - return -ENOMEM; + if (!tcp_conn->data_tx_hash.tfm) + tcp_conn->data_tx_hash.tfm = + crypto_alloc_hash("crc32c", 0, + CRYPTO_ALG_ASYNC); + if (IS_ERR(tcp_conn->data_tx_hash.tfm)) + return PTR_ERR(tcp_conn->data_tx_hash.tfm); + if (!tcp_conn->data_rx_hash.tfm) + tcp_conn->data_rx_hash.tfm = + crypto_alloc_hash("crc32c", 0, + CRYPTO_ALG_ASYNC); + if (IS_ERR(tcp_conn->data_rx_hash.tfm)) { + crypto_free_hash(tcp_conn->data_tx_hash.tfm); + return PTR_ERR(tcp_conn->data_rx_hash.tfm); } } else { - if (tcp_conn->data_tx_tfm) - crypto_free_tfm(tcp_conn->data_tx_tfm); - if (tcp_conn->data_rx_tfm) - crypto_free_tfm(tcp_conn->data_rx_tfm); + if (tcp_conn->data_tx_hash.tfm) + crypto_free_hash(tcp_conn->data_tx_hash.tfm); + if (tcp_conn->data_rx_hash.tfm) + crypto_free_hash(tcp_conn->data_rx_hash.tfm); } tcp_conn->sendpage = conn->datadgst_en ? sock_no_sendpage : tcp_conn->sock->ops->sendpage; diff --git a/drivers/scsi/iscsi_tcp.h b/drivers/scsi/iscsi_tcp.h index 6a4ee704e46..e35701305fc 100644 --- a/drivers/scsi/iscsi_tcp.h +++ b/drivers/scsi/iscsi_tcp.h @@ -51,6 +51,7 @@ #define ISCSI_SG_TABLESIZE SG_ALL #define ISCSI_TCP_MAX_CMD_LEN 16 +struct crypto_hash; struct socket; /* Socket connection recieve helper */ @@ -84,8 +85,8 @@ struct iscsi_tcp_conn { /* iSCSI connection-wide sequencing */ int hdr_size; /* PDU header size */ - struct crypto_tfm *rx_tfm; /* CRC32C (Rx) */ - struct crypto_tfm *data_rx_tfm; /* CRC32C (Rx) for data */ + struct crypto_hash *rx_tfm; /* CRC32C (Rx) */ + struct hash_desc data_rx_hash; /* CRC32C (Rx) for data */ /* control data */ struct iscsi_tcp_recv in; /* TCP receive context */ @@ -97,8 +98,8 @@ struct iscsi_tcp_conn { void (*old_write_space)(struct sock *); /* xmit */ - struct crypto_tfm *tx_tfm; /* CRC32C (Tx) */ - struct crypto_tfm *data_tx_tfm; /* CRC32C (Tx) for data */ + struct crypto_hash *tx_tfm; /* CRC32C (Tx) */ + struct hash_desc data_tx_hash; /* CRC32C (Tx) for data */ /* MIB custom statistics */ uint32_t sendpage_failures_cnt; -- cgit v1.2.3 From f2d719c65ad8f10afa7bec11315faa7badf4ecb9 Mon Sep 17 00:00:00 2001 From: Alexis Bruemmer Date: Thu, 7 Sep 2006 14:32:16 -0700 Subject: [SCSI] aic94xx: Removes Reliance on FLASH Manufacture IDs This patch removes the reliance on FLASH Manufacture IDs for validation. Signed-off-by: Alexis Bruemmer Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx_sds.c | 51 ++------------------------------------ 1 file changed, 2 insertions(+), 49 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aic94xx/aic94xx_sds.c b/drivers/scsi/aic94xx/aic94xx_sds.c index eec1e0db0e0..83574b5b4e6 100644 --- a/drivers/scsi/aic94xx/aic94xx_sds.c +++ b/drivers/scsi/aic94xx/aic94xx_sds.c @@ -376,7 +376,6 @@ out: /* ---------- FLASH stuff ---------- */ #define FLASH_RESET 0xF0 -#define FLASH_MANUF_AMD 1 #define FLASH_SIZE 0x200000 #define FLASH_DIR_COOKIE "*** ADAPTEC FLASH DIRECTORY *** " @@ -627,7 +626,7 @@ static int asd_find_flash_dir(struct asd_ha_struct *asd_ha, static int asd_flash_getid(struct asd_ha_struct *asd_ha) { int err = 0; - u32 reg, inc; + u32 reg; reg = asd_read_reg_dword(asd_ha, EXSICNFGR); @@ -648,53 +647,7 @@ static int asd_flash_getid(struct asd_ha_struct *asd_ha) ASD_DPRINTK("couldn't reset flash(%d)\n", err); return err; } - /* Get flash info. This would most likely be AMD Am29LV family flash. - * First try the sequence for word mode. It is the same as for - * 008B (byte mode only), 160B (word mode) and 800D (word mode). - */ - reg = asd_ha->hw_prof.flash.bar; - inc = asd_ha->hw_prof.flash.wide ? 2 : 1; - asd_write_reg_byte(asd_ha, reg + 0x555, 0xAA); - asd_write_reg_byte(asd_ha, reg + 0x2AA, 0x55); - asd_write_reg_byte(asd_ha, reg + 0x555, 0x90); - asd_ha->hw_prof.flash.manuf = asd_read_reg_byte(asd_ha, reg); - asd_ha->hw_prof.flash.dev_id= asd_read_reg_byte(asd_ha,reg+inc); - asd_ha->hw_prof.flash.sec_prot = asd_read_reg_byte(asd_ha,reg+inc+inc); - /* Get out of autoselect mode. */ - err = asd_reset_flash(asd_ha); - - if (asd_ha->hw_prof.flash.manuf == FLASH_MANUF_AMD) { - ASD_DPRINTK("0Found FLASH(%d) manuf:%d, dev_id:0x%x, " - "sec_prot:%d\n", - asd_ha->hw_prof.flash.wide ? 16 : 8, - asd_ha->hw_prof.flash.manuf, - asd_ha->hw_prof.flash.dev_id, - asd_ha->hw_prof.flash.sec_prot); - return 0; - } - - /* Ok, try the sequence for byte mode of 160B and 800D. - * We may actually never need this. - */ - asd_write_reg_byte(asd_ha, reg + 0xAAA, 0xAA); - asd_write_reg_byte(asd_ha, reg + 0x555, 0x55); - asd_write_reg_byte(asd_ha, reg + 0xAAA, 0x90); - asd_ha->hw_prof.flash.manuf = asd_read_reg_byte(asd_ha, reg); - asd_ha->hw_prof.flash.dev_id = asd_read_reg_byte(asd_ha, reg + 2); - asd_ha->hw_prof.flash.sec_prot = asd_read_reg_byte(asd_ha, reg + 4); - err = asd_reset_flash(asd_ha); - - if (asd_ha->hw_prof.flash.manuf == FLASH_MANUF_AMD) { - ASD_DPRINTK("1Found FLASH(%d) manuf:%d, dev_id:0x%x, " - "sec_prot:%d\n", - asd_ha->hw_prof.flash.wide ? 16 : 8, - asd_ha->hw_prof.flash.manuf, - asd_ha->hw_prof.flash.dev_id, - asd_ha->hw_prof.flash.sec_prot); - return 0; - } - - return -ENOENT; + return 0; } static u16 asd_calc_flash_chksum(u16 *p, int size) -- cgit v1.2.3 From 10d19ae5e1715c27db7009df6d59179774e7b8a1 Mon Sep 17 00:00:00 2001 From: "malahal@us.ibm.com" Date: Thu, 7 Sep 2006 15:12:42 -0700 Subject: [SCSI] aic94xx: Fix for a typo in aic94xx_init() Signed-off-by: Malahal Naineni Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index 302b54fddf3..ee2ccad7048 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -828,7 +828,7 @@ static int __init aic94xx_init(void) aic94xx_transport_template = sas_domain_attach_transport(&aic94xx_transport_functions); - if (err) + if (!aic94xx_transport_template) goto out_destroy_caches; err = pci_register_driver(&aic94xx_pci_driver); -- cgit v1.2.3 From 5fcda4224529c4e550c917668d5e96c1d3e7039b Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Thu, 14 Sep 2006 17:04:58 -0500 Subject: [SCSI] aha152x: remove static host array Fix this driver not to use a static two element host array instead use a list. This should fix panic on multiple eject reinsert of the pcmcia version of this device. Signed-off-by: James Bottomley --- drivers/scsi/aha152x.c | 53 +++++++++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c index f974869ea32..fb6a476eb87 100644 --- a/drivers/scsi/aha152x.c +++ b/drivers/scsi/aha152x.c @@ -253,6 +253,7 @@ #include #include #include +#include #include #include @@ -262,6 +263,8 @@ #include #include "aha152x.h" +static LIST_HEAD(aha152x_host_list); + /* DEFINES */ @@ -423,8 +426,6 @@ MODULE_DEVICE_TABLE(isapnp, id_table); #endif /* !PCMCIA */ -static int registered_count=0; -static struct Scsi_Host *aha152x_host[2]; static struct scsi_host_template aha152x_driver_template; /* @@ -541,6 +542,7 @@ struct aha152x_hostdata { #ifdef __ISAPNP__ struct pnp_dev *pnpdev; #endif + struct list_head host_list; }; @@ -755,20 +757,9 @@ static inline Scsi_Cmnd *remove_SC(Scsi_Cmnd **SC, Scsi_Cmnd *SCp) return ptr; } -static inline struct Scsi_Host *lookup_irq(int irqno) -{ - int i; - - for(i=0; iirq==irqno) - return aha152x_host[i]; - - return NULL; -} - static irqreturn_t swintr(int irqno, void *dev_id, struct pt_regs *regs) { - struct Scsi_Host *shpnt = lookup_irq(irqno); + struct Scsi_Host *shpnt = (struct Scsi_Host *)dev_id; if (!shpnt) { printk(KERN_ERR "aha152x: catched software interrupt %d for unknown controller.\n", irqno); @@ -791,10 +782,11 @@ struct Scsi_Host *aha152x_probe_one(struct aha152x_setup *setup) return NULL; } - /* need to have host registered before triggering any interrupt */ - aha152x_host[registered_count] = shpnt; - memset(HOSTDATA(shpnt), 0, sizeof *HOSTDATA(shpnt)); + INIT_LIST_HEAD(&HOSTDATA(shpnt)->host_list); + + /* need to have host registered before triggering any interrupt */ + list_add_tail(&HOSTDATA(shpnt)->host_list, &aha152x_host_list); shpnt->io_port = setup->io_port; shpnt->n_io_port = IO_RANGE; @@ -907,12 +899,10 @@ struct Scsi_Host *aha152x_probe_one(struct aha152x_setup *setup) scsi_scan_host(shpnt); - registered_count++; - return shpnt; out_host_put: - aha152x_host[registered_count]=NULL; + list_del(&HOSTDATA(shpnt)->host_list); scsi_host_put(shpnt); return NULL; @@ -937,6 +927,7 @@ void aha152x_release(struct Scsi_Host *shpnt) #endif scsi_remove_host(shpnt); + list_del(&HOSTDATA(shpnt)->host_list); scsi_host_put(shpnt); } @@ -1459,9 +1450,12 @@ static struct work_struct aha152x_tq; */ static void run(void) { - int i; - for (i = 0; i0; + return 1; } static void __exit aha152x_exit(void) { - int i; + struct aha152x_hostdata *hd; + + list_for_each_entry(hd, &aha152x_host_list, host_list) { + struct Scsi_Host *shost = container_of((void *)hd, struct Scsi_Host, hostdata); - for(i=0; i Date: Fri, 15 Sep 2006 14:43:11 +0200 Subject: [SCSI] megaraid: Make megaraid_ioctl() check copy_to_user() return value Check copy_to_user() return value in drivers/scsi/megaraid.c::megadev_ioctl() This gets rid of this little warning: drivers/scsi/megaraid.c:3661: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result Signed-off-by: Jesper Juhl Acked-by: "Ju, Seokmann" Signed-off-by: James Bottomley --- drivers/scsi/megaraid.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index ccb0055ac73..b87bef69ba0 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -3656,8 +3656,9 @@ megadev_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, * Send the request sense data also, irrespective of * whether the user has asked for it or not. */ - copy_to_user(upthru->reqsensearea, - pthru->reqsensearea, 14); + if (copy_to_user(upthru->reqsensearea, + pthru->reqsensearea, 14)) + rval = -EFAULT; freemem_and_return: if( pthru->dataxferlen ) { -- cgit v1.2.3 From 231839102b54512ced7d3ee7fc9b8bcf5e3b583b Mon Sep 17 00:00:00 2001 From: Douglas Gilbert Date: Sat, 16 Sep 2006 20:30:47 -0400 Subject: [SCSI] scsi_debug version 1.80 See http://www.torque.net/sg/sdebug26.html for more information on the scsi_debug driver. ChangeLog: - add 'vpd_use_hostno' parameter to allow simulated hosts to see the same set of targets (and luns). For testing multipath software. - add 'fake_rw' parameter to ignore the data in READ and WRITE commands - add support for log subpages (new in SPC-4) - yield appropriate block descriptor for MODE SENSE commands (only for pdt=0 (i.e. disks)) - REQUEST SENSE response no longer shows the stopped power condition (SAT changed to agree with SPC-3) Signed-off-by: Douglas Gilbert Signed-off-by: James Bottomley --- drivers/scsi/scsi_debug.c | 230 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 189 insertions(+), 41 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index a80303c6b3f..9c0f35820e3 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -1,5 +1,4 @@ /* - * linux/kernel/scsi_debug.c * vvvvvvvvvvvvvvvvvvvvvvv Original vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv * Copyright (C) 1992 Eric Youngdale * Simulate a host adapter with 2 disks attached. Do a lot of checking @@ -8,7 +7,9 @@ * ^^^^^^^^^^^^^^^^^^^^^^^ Original ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * * This version is more generic, simulating a variable number of disk - * (or disk like devices) sharing a common amount of RAM + * (or disk like devices) sharing a common amount of RAM. To be more + * realistic, the simulated devices have the transport attributes of + * SAS disks. * * * For documentation see http://www.torque.net/sg/sdebug26.html @@ -50,8 +51,8 @@ #include "scsi_logging.h" #include "scsi_debug.h" -#define SCSI_DEBUG_VERSION "1.79" -static const char * scsi_debug_version_date = "20060604"; +#define SCSI_DEBUG_VERSION "1.80" +static const char * scsi_debug_version_date = "20060914"; /* Additional Sense Code (ASC) used */ #define NO_ADDITIONAL_SENSE 0x0 @@ -86,6 +87,8 @@ static const char * scsi_debug_version_date = "20060604"; #define DEF_D_SENSE 0 #define DEF_NO_LUN_0 0 #define DEF_VIRTUAL_GB 0 +#define DEF_FAKE_RW 0 +#define DEF_VPD_USE_HOSTNO 1 /* bit mask values for scsi_debug_opts */ #define SCSI_DEBUG_OPT_NOISE 1 @@ -127,6 +130,8 @@ static int scsi_debug_ptype = DEF_PTYPE; /* SCSI peripheral type (0==disk) */ static int scsi_debug_dsense = DEF_D_SENSE; static int scsi_debug_no_lun_0 = DEF_NO_LUN_0; static int scsi_debug_virtual_gb = DEF_VIRTUAL_GB; +static int scsi_debug_fake_rw = DEF_FAKE_RW; +static int scsi_debug_vpd_use_hostno = DEF_VPD_USE_HOSTNO; static int scsi_debug_cmnd_count = 0; @@ -423,6 +428,8 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) case READ_6: if ((errsts = check_readiness(SCpnt, 0, devip))) break; + if (scsi_debug_fake_rw) + break; if ((*cmd) == READ_16) { for (lba = 0, j = 0; j < 8; ++j) { if (j > 0) @@ -465,6 +472,8 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) case WRITE_6: if ((errsts = check_readiness(SCpnt, 0, devip))) break; + if (scsi_debug_fake_rw) + break; if ((*cmd) == WRITE_16) { for (lba = 0, j = 0; j < 8; ++j) { if (j > 0) @@ -941,6 +950,8 @@ static int resp_inquiry(struct scsi_cmnd * scp, int target, char lu_id_str[6]; int host_no = devip->sdbg_host->shost->host_no; + if (0 == scsi_debug_vpd_use_hostno) + host_no = 0; lu_id_num = devip->wlun ? -1 : (((host_no + 1) * 2000) + (devip->target * 1000) + devip->lun); target_dev_id = ((host_no + 1) * 2000) + @@ -1059,19 +1070,6 @@ static int resp_requests(struct scsi_cmnd * scp, arr[12] = THRESHOLD_EXCEEDED; arr[13] = 0xff; /* TEST set and MRIE==6 */ } - } else if (devip->stopped) { - if (want_dsense) { - arr[0] = 0x72; - arr[1] = 0x0; /* NO_SENSE in sense_key */ - arr[2] = LOW_POWER_COND_ON; - arr[3] = 0x0; /* TEST set and MRIE==6 */ - } else { - arr[0] = 0x70; - arr[2] = 0x0; /* NO_SENSE in sense_key */ - arr[7] = 0xa; /* 18 byte sense buffer */ - arr[12] = LOW_POWER_COND_ON; - arr[13] = 0x0; /* TEST set and MRIE==6 */ - } } else { memcpy(arr, sbuff, SDEBUG_SENSE_LEN); if ((cmd[1] & 1) && (! scsi_debug_dsense)) { @@ -1325,21 +1323,26 @@ static int resp_sas_sha_m_spg(unsigned char * p, int pcontrol) static int resp_mode_sense(struct scsi_cmnd * scp, int target, struct sdebug_dev_info * devip) { - unsigned char dbd; - int pcontrol, pcode, subpcode; + unsigned char dbd, llbaa; + int pcontrol, pcode, subpcode, bd_len; unsigned char dev_spec; - int alloc_len, msense_6, offset, len, errsts, target_dev_id; + int k, alloc_len, msense_6, offset, len, errsts, target_dev_id; unsigned char * ap; unsigned char arr[SDEBUG_MAX_MSENSE_SZ]; unsigned char *cmd = (unsigned char *)scp->cmnd; if ((errsts = check_readiness(scp, 1, devip))) return errsts; - dbd = cmd[1] & 0x8; + dbd = !!(cmd[1] & 0x8); pcontrol = (cmd[2] & 0xc0) >> 6; pcode = cmd[2] & 0x3f; subpcode = cmd[3]; msense_6 = (MODE_SENSE == cmd[0]); + llbaa = msense_6 ? 0 : !!(cmd[1] & 0x10); + if ((0 == scsi_debug_ptype) && (0 == dbd)) + bd_len = llbaa ? 16 : 8; + else + bd_len = 0; alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]); memset(arr, 0, SDEBUG_MAX_MSENSE_SZ); if (0x3 == pcontrol) { /* Saving values not supported */ @@ -1349,15 +1352,58 @@ static int resp_mode_sense(struct scsi_cmnd * scp, int target, } target_dev_id = ((devip->sdbg_host->shost->host_no + 1) * 2000) + (devip->target * 1000) - 3; - dev_spec = DEV_READONLY(target) ? 0x80 : 0x0; + /* set DPOFUA bit for disks */ + if (0 == scsi_debug_ptype) + dev_spec = (DEV_READONLY(target) ? 0x80 : 0x0) | 0x10; + else + dev_spec = 0x0; if (msense_6) { arr[2] = dev_spec; + arr[3] = bd_len; offset = 4; } else { arr[3] = dev_spec; + if (16 == bd_len) + arr[4] = 0x1; /* set LONGLBA bit */ + arr[7] = bd_len; /* assume 255 or less */ offset = 8; } ap = arr + offset; + if ((bd_len > 0) && (0 == sdebug_capacity)) { + if (scsi_debug_virtual_gb > 0) { + sdebug_capacity = 2048 * 1024; + sdebug_capacity *= scsi_debug_virtual_gb; + } else + sdebug_capacity = sdebug_store_sectors; + } + if (8 == bd_len) { + if (sdebug_capacity > 0xfffffffe) { + ap[0] = 0xff; + ap[1] = 0xff; + ap[2] = 0xff; + ap[3] = 0xff; + } else { + ap[0] = (sdebug_capacity >> 24) & 0xff; + ap[1] = (sdebug_capacity >> 16) & 0xff; + ap[2] = (sdebug_capacity >> 8) & 0xff; + ap[3] = sdebug_capacity & 0xff; + } + ap[6] = (SECT_SIZE_PER(target) >> 8) & 0xff; + ap[7] = SECT_SIZE_PER(target) & 0xff; + offset += bd_len; + ap = arr + offset; + } else if (16 == bd_len) { + unsigned long long capac = sdebug_capacity; + + for (k = 0; k < 8; ++k, capac >>= 8) + ap[7 - k] = capac & 0xff; + ap[12] = (SECT_SIZE_PER(target) >> 24) & 0xff; + ap[13] = (SECT_SIZE_PER(target) >> 16) & 0xff; + ap[14] = (SECT_SIZE_PER(target) >> 8) & 0xff; + ap[15] = SECT_SIZE_PER(target) & 0xff; + offset += bd_len; + ap = arr + offset; + } if ((subpcode > 0x0) && (subpcode < 0xff) && (0x19 != pcode)) { /* TODO: Control Extension page */ @@ -1471,7 +1517,7 @@ static int resp_mode_select(struct scsi_cmnd * scp, int mselect6, " IO sent=%d bytes\n", param_len, res); md_len = mselect6 ? (arr[0] + 1) : ((arr[0] << 8) + arr[1] + 2); bd_len = mselect6 ? arr[3] : ((arr[6] << 8) + arr[7]); - if ((md_len > 2) || (0 != bd_len)) { + if (md_len > 2) { mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_PARAM_LIST, 0); return check_condition_result; @@ -1544,7 +1590,7 @@ static int resp_ie_l_pg(unsigned char * arr) static int resp_log_sense(struct scsi_cmnd * scp, struct sdebug_dev_info * devip) { - int ppc, sp, pcontrol, pcode, alloc_len, errsts, len, n; + int ppc, sp, pcontrol, pcode, subpcode, alloc_len, errsts, len, n; unsigned char arr[SDEBUG_MAX_LSENSE_SZ]; unsigned char *cmd = (unsigned char *)scp->cmnd; @@ -1560,23 +1606,63 @@ static int resp_log_sense(struct scsi_cmnd * scp, } pcontrol = (cmd[2] & 0xc0) >> 6; pcode = cmd[2] & 0x3f; + subpcode = cmd[3] & 0xff; alloc_len = (cmd[7] << 8) + cmd[8]; arr[0] = pcode; - switch (pcode) { - case 0x0: /* Supported log pages log page */ - n = 4; - arr[n++] = 0x0; /* this page */ - arr[n++] = 0xd; /* Temperature */ - arr[n++] = 0x2f; /* Informational exceptions */ - arr[3] = n - 4; - break; - case 0xd: /* Temperature log page */ - arr[3] = resp_temp_l_pg(arr + 4); - break; - case 0x2f: /* Informational exceptions log page */ - arr[3] = resp_ie_l_pg(arr + 4); - break; - default: + if (0 == subpcode) { + switch (pcode) { + case 0x0: /* Supported log pages log page */ + n = 4; + arr[n++] = 0x0; /* this page */ + arr[n++] = 0xd; /* Temperature */ + arr[n++] = 0x2f; /* Informational exceptions */ + arr[3] = n - 4; + break; + case 0xd: /* Temperature log page */ + arr[3] = resp_temp_l_pg(arr + 4); + break; + case 0x2f: /* Informational exceptions log page */ + arr[3] = resp_ie_l_pg(arr + 4); + break; + default: + mk_sense_buffer(devip, ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB, 0); + return check_condition_result; + } + } else if (0xff == subpcode) { + arr[0] |= 0x40; + arr[1] = subpcode; + switch (pcode) { + case 0x0: /* Supported log pages and subpages log page */ + n = 4; + arr[n++] = 0x0; + arr[n++] = 0x0; /* 0,0 page */ + arr[n++] = 0x0; + arr[n++] = 0xff; /* this page */ + arr[n++] = 0xd; + arr[n++] = 0x0; /* Temperature */ + arr[n++] = 0x2f; + arr[n++] = 0x0; /* Informational exceptions */ + arr[3] = n - 4; + break; + case 0xd: /* Temperature subpages */ + n = 4; + arr[n++] = 0xd; + arr[n++] = 0x0; /* Temperature */ + arr[3] = n - 4; + break; + case 0x2f: /* Informational exceptions subpages */ + n = 4; + arr[n++] = 0x2f; + arr[n++] = 0x0; /* Informational exceptions */ + arr[3] = n - 4; + break; + default: + mk_sense_buffer(devip, ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB, 0); + return check_condition_result; + } + } else { mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); return check_condition_result; @@ -2151,11 +2237,18 @@ static int schedule_resp(struct scsi_cmnd * cmnd, } } +/* Note: The following macros create attribute files in the + /sys/module/scsi_debug/parameters directory. Unfortunately this + driver is unaware of a change and cannot trigger auxiliary actions + as it can when the corresponding attribute in the + /sys/bus/pseudo/drivers/scsi_debug directory is changed. + */ module_param_named(add_host, scsi_debug_add_host, int, S_IRUGO | S_IWUSR); module_param_named(delay, scsi_debug_delay, int, S_IRUGO | S_IWUSR); module_param_named(dev_size_mb, scsi_debug_dev_size_mb, int, S_IRUGO); module_param_named(dsense, scsi_debug_dsense, int, S_IRUGO | S_IWUSR); module_param_named(every_nth, scsi_debug_every_nth, int, S_IRUGO | S_IWUSR); +module_param_named(fake_rw, scsi_debug_fake_rw, int, S_IRUGO | S_IWUSR); module_param_named(max_luns, scsi_debug_max_luns, int, S_IRUGO | S_IWUSR); module_param_named(no_lun_0, scsi_debug_no_lun_0, int, S_IRUGO | S_IWUSR); module_param_named(num_parts, scsi_debug_num_parts, int, S_IRUGO); @@ -2164,6 +2257,8 @@ module_param_named(opts, scsi_debug_opts, int, S_IRUGO | S_IWUSR); module_param_named(ptype, scsi_debug_ptype, int, S_IRUGO | S_IWUSR); module_param_named(scsi_level, scsi_debug_scsi_level, int, S_IRUGO); module_param_named(virtual_gb, scsi_debug_virtual_gb, int, S_IRUGO | S_IWUSR); +module_param_named(vpd_use_hostno, scsi_debug_vpd_use_hostno, int, + S_IRUGO | S_IWUSR); MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert"); MODULE_DESCRIPTION("SCSI debug adapter driver"); @@ -2175,6 +2270,7 @@ MODULE_PARM_DESC(delay, "# of jiffies to delay response(def=1)"); MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs(def=8)"); MODULE_PARM_DESC(dsense, "use descriptor sense format(def=0 -> fixed)"); MODULE_PARM_DESC(every_nth, "timeout every nth command(def=100)"); +MODULE_PARM_DESC(fake_rw, "fake reads/writes instead of copying (def=0)"); MODULE_PARM_DESC(max_luns, "number of LUNs per target to simulate(def=1)"); MODULE_PARM_DESC(no_lun_0, "no LU number 0 (def=0 -> have lun 0)"); MODULE_PARM_DESC(num_parts, "number of partitions(def=0)"); @@ -2183,6 +2279,7 @@ MODULE_PARM_DESC(opts, "1->noise, 2->medium_error, 4->... (def=0)"); MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])"); MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=5[SPC-3])"); MODULE_PARM_DESC(virtual_gb, "virtual gigabyte size (def=0 -> use dev_size_mb)"); +MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)"); static char sdebug_info[256]; @@ -2334,6 +2431,24 @@ static ssize_t sdebug_dsense_store(struct device_driver * ddp, DRIVER_ATTR(dsense, S_IRUGO | S_IWUSR, sdebug_dsense_show, sdebug_dsense_store); +static ssize_t sdebug_fake_rw_show(struct device_driver * ddp, char * buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_fake_rw); +} +static ssize_t sdebug_fake_rw_store(struct device_driver * ddp, + const char * buf, size_t count) +{ + int n; + + if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) { + scsi_debug_fake_rw = n; + return count; + } + return -EINVAL; +} +DRIVER_ATTR(fake_rw, S_IRUGO | S_IWUSR, sdebug_fake_rw_show, + sdebug_fake_rw_store); + static ssize_t sdebug_no_lun_0_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_no_lun_0); @@ -2487,6 +2602,31 @@ static ssize_t sdebug_add_host_store(struct device_driver * ddp, DRIVER_ATTR(add_host, S_IRUGO | S_IWUSR, sdebug_add_host_show, sdebug_add_host_store); +static ssize_t sdebug_vpd_use_hostno_show(struct device_driver * ddp, + char * buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_vpd_use_hostno); +} +static ssize_t sdebug_vpd_use_hostno_store(struct device_driver * ddp, + const char * buf, size_t count) +{ + int n; + + if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) { + scsi_debug_vpd_use_hostno = n; + return count; + } + return -EINVAL; +} +DRIVER_ATTR(vpd_use_hostno, S_IRUGO | S_IWUSR, sdebug_vpd_use_hostno_show, + sdebug_vpd_use_hostno_store); + +/* Note: The following function creates attribute files in the + /sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these + files (over those found in the /sys/module/scsi_debug/parameters + directory) is that auxiliary actions can be triggered when an attribute + is changed. For example see: sdebug_add_host_store() above. + */ static int do_create_driverfs_files(void) { int ret; @@ -2496,23 +2636,31 @@ static int do_create_driverfs_files(void) ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dsense); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_every_nth); + ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_fake_rw); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_max_luns); - ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_tgts); + ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_parts); + ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_tgts); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ptype); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_opts); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_scsi_level); + ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb); + ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno); return ret; } static void do_remove_driverfs_files(void) { + driver_remove_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno); + driver_remove_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_scsi_level); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_opts); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ptype); - driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_parts); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_tgts); + driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_parts); + driver_remove_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_max_luns); + driver_remove_file(&sdebug_driverfs_driver, &driver_attr_fake_rw); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_every_nth); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dsense); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb); -- cgit v1.2.3 From 3e3c60e3a8c7013d55768aa7256bb5a7f66b0bb4 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 6 Sep 2006 09:04:40 -0500 Subject: [SCSI] aic7xxx: avoid checking SBLKCTL register for certain cards For cards that don't support LVD, checking the SBLKCTL register to determine the bus singalling doesn't work. So, check that the card supports LVD first (AHC_ULTRA2) before checking the register. Signed-off-by: James Bottomley --- drivers/scsi/aic7xxx/aic7xxx_osm.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c index e5bb4d87b30..0b3c01ac425 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c @@ -2539,15 +2539,23 @@ static void ahc_linux_set_iu(struct scsi_target *starget, int iu) static void ahc_linux_get_signalling(struct Scsi_Host *shost) { struct ahc_softc *ahc = *(struct ahc_softc **)shost->hostdata; - u8 mode = ahc_inb(ahc, SBLKCTL); + u8 mode; - if (mode & ENAB40) - spi_signalling(shost) = SPI_SIGNAL_LVD; - else if (mode & ENAB20) + if (!(ahc->features & AHC_ULTRA2)) { + /* non-LVD chipset, may not have SBLKCTL reg */ spi_signalling(shost) = ahc->features & AHC_HVD ? SPI_SIGNAL_HVD : SPI_SIGNAL_SE; + return; + } + + mode = ahc_inb(ahc, SBLKCTL); + + if (mode & ENAB40) + spi_signalling(shost) = SPI_SIGNAL_LVD; + else if (mode & ENAB20) + spi_signalling(shost) = SPI_SIGNAL_SE; else spi_signalling(shost) = SPI_SIGNAL_UNKNOWN; } -- cgit v1.2.3 From cf2b5d3fcab77a9390293920ec5b49e67eced200 Mon Sep 17 00:00:00 2001 From: Doug Ledford Date: Sun, 17 Sep 2006 07:38:15 +0200 Subject: [SCSI] aic7xxx: pause sequencer before touching SBLKCTL Some cards need to pause the sequencer before the SBLKCTL register is touched. This fixes a PCI related oops seen on powerpc macs with this card caused by trying to ascertain the bus signalling before beginning domain validation. Signed-off-by: James Bottomley --- drivers/scsi/aic7xxx/aic7xxx_osm.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c index 0b3c01ac425..64c8b88a429 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c @@ -2539,6 +2539,7 @@ static void ahc_linux_set_iu(struct scsi_target *starget, int iu) static void ahc_linux_get_signalling(struct Scsi_Host *shost) { struct ahc_softc *ahc = *(struct ahc_softc **)shost->hostdata; + unsigned long flags; u8 mode; if (!(ahc->features & AHC_ULTRA2)) { @@ -2550,7 +2551,11 @@ static void ahc_linux_get_signalling(struct Scsi_Host *shost) return; } + ahc_lock(ahc, &flags); + ahc_pause(ahc); mode = ahc_inb(ahc, SBLKCTL); + ahc_unpause(ahc); + ahc_unlock(ahc, &flags); if (mode & ENAB40) spi_signalling(shost) = SPI_SIGNAL_LVD; -- cgit v1.2.3 From 65101355450df2d935f8d56ac3abef279f28a0e2 Mon Sep 17 00:00:00 2001 From: Mark Haverkamp Date: Tue, 19 Sep 2006 08:59:23 -0700 Subject: [SCSI] aacraid: misc cleanup Received from Mark Salyzyn: Basically cleanup, nothing here will have an affect. Adjusting some error codes, removing superfluous definitions and code fragments. Signed-off-by: Mark Haverkamp Signed-off-by: James Bottomley --- drivers/scsi/aacraid/aachba.c | 8 ++++---- drivers/scsi/aacraid/aacraid.h | 1 - drivers/scsi/aacraid/commsup.c | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index 37c55ddce21..b14f7cac30e 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -1803,7 +1803,7 @@ static int query_disk(struct aac_dev *dev, void __user *arg) fsa_dev_ptr = dev->fsa_dev; if (!fsa_dev_ptr) - return -ENODEV; + return -EBUSY; if (copy_from_user(&qd, arg, sizeof (struct aac_query_disk))) return -EFAULT; if (qd.cnum == -1) @@ -1842,6 +1842,8 @@ static int force_delete_disk(struct aac_dev *dev, void __user *arg) struct fsa_dev_info *fsa_dev_ptr; fsa_dev_ptr = dev->fsa_dev; + if (!fsa_dev_ptr) + return -EBUSY; if (copy_from_user(&dd, arg, sizeof (struct aac_delete_disk))) return -EFAULT; @@ -1866,9 +1868,7 @@ static int delete_disk(struct aac_dev *dev, void __user *arg) fsa_dev_ptr = dev->fsa_dev; if (!fsa_dev_ptr) - return -ENODEV; - if (!fsa_dev_ptr) - return -ENODEV; + return -EBUSY; if (copy_from_user(&dd, arg, sizeof (struct aac_delete_disk))) return -EFAULT; diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index 8924c183d9c..e5f7be6f3cd 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -1525,7 +1525,6 @@ struct aac_get_name { __le32 count; /* sizeof(((struct aac_get_name_resp *)NULL)->data) */ }; -#define CT_OK 218 struct aac_get_name_resp { __le32 dummy0; __le32 dummy1; diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index 53add53be0b..907161d6e92 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -467,7 +467,7 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size, dprintk((KERN_DEBUG " fib being sent=%p\n",fibptr)); if (!dev->queues) - return -ENODEV; + return -EBUSY; q = &dev->queues->queue[AdapNormCmdQueue]; if(wait) -- cgit v1.2.3 From 653ba58d55feb708c6f97e6f3e84901b3a03c9c0 Mon Sep 17 00:00:00 2001 From: Mark Haverkamp Date: Tue, 19 Sep 2006 08:59:43 -0700 Subject: [SCSI] aacraid: expose physical devices Received from Mark Salyzyn: I am placing this functionality into an insmod parameter. Normally the physical components are exported to sg, and are blocked from showing up in sd. Note that the pass-through I/O path via the driver through the Firmware to the physical disks is not an optimized path, the card is designed for Hardware RAID, elevator sorting and caching. This should not be used as a means for utilizing the aacraid based controllers as a generic scsi/SATA/SAS controller, performance should suck by a few percentage points, any RAID meta-data on the drives will confuse the controller about who owns the drives and there is a high risk of destroying content in both directions. Unreliable and for experimentation or strange controlled circumstances only. Signed-off-by: Mark Haverkamp Signed-off-by: James Bottomley --- drivers/scsi/aacraid/aachba.c | 6 +++++- drivers/scsi/aacraid/linit.c | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index b14f7cac30e..ac108f9e267 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -169,6 +169,10 @@ MODULE_PARM_DESC(numacb, "Request a limit to the number of adapter control block int acbsize = -1; module_param(acbsize, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(acbsize, "Request a specific adapter control block (FIB) size. Valid values are 512, 2048, 4096 and 8192. Default is to use suggestion from Firmware."); + +int expose_physicals = 0; +module_param(expose_physicals, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(expose_physicals, "Expose physical components of the arrays. 0=off, 1=on"); /** * aac_get_config_status - check the adapter configuration * @common: adapter to query @@ -1535,7 +1539,7 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) return 0; } } else { /* check for physical non-dasd devices */ - if(dev->nondasd_support == 1){ + if ((dev->nondasd_support == 1) || expose_physicals) { if (dev->in_reset) return -1; return aac_send_srb_fib(scsicmd); diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index d67058f8081..6e4eafa4ece 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -82,6 +82,8 @@ static LIST_HEAD(aac_devices); static int aac_cfg_major = -1; char aac_driver_version[] = AAC_DRIVER_FULL_VERSION; +extern int expose_physicals; + /* * Because of the way Linux names scsi devices, the order in this table has * become important. Check for on-board Raid first, add-in cards second. @@ -394,6 +396,7 @@ static int aac_slave_configure(struct scsi_device *sdev) sdev->skip_ms_page_3f = 1; } if ((sdev->type == TYPE_DISK) && + !expose_physicals && (sdev_channel(sdev) != CONTAINER_CHANNEL)) { struct aac_dev *aac = (struct aac_dev *)sdev->host->hostdata; if (!aac->raid_scsi_mode || (sdev_channel(sdev) != 2)) @@ -928,7 +931,7 @@ static int __devinit aac_probe_one(struct pci_dev *pdev, * all containers are on the virtual channel 0 (CONTAINER_CHANNEL) * physical channels are address by their actual physical number+1 */ - if (aac->nondasd_support == 1) + if ((aac->nondasd_support == 1) || expose_physicals) shost->max_channel = aac->maximum_num_channels; else shost->max_channel = 0; -- cgit v1.2.3 From 76a7f8fdc0c2381ae1ba55ef71837712223ecb3c Mon Sep 17 00:00:00 2001 From: Mark Haverkamp Date: Tue, 19 Sep 2006 09:00:02 -0700 Subject: [SCSI] aacraid: merge rx and rkt code Received from Mark Salyzyn: The only real difference between the rkt and rx platform modules is the offset of the message registers. This patch recognizes this similarity and simplifies the driver to reduce it's code footprint and to improve maintainability by reducing the code duplication. Visibly, the 'rkt.c' portion of this patch looks more complicated than it really is. View it as retaining the rkt-only specifics of the interface. Signed-off-by: Mark Haverkamp Signed-off-by: James Bottomley --- drivers/scsi/aacraid/aacraid.h | 14 +- drivers/scsi/aacraid/comminit.c | 11 +- drivers/scsi/aacraid/linit.c | 12 +- drivers/scsi/aacraid/rkt.c | 461 ++-------------------------------------- drivers/scsi/aacraid/rx.c | 90 +++++--- drivers/scsi/aacraid/sa.c | 21 ++ 6 files changed, 112 insertions(+), 497 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index e5f7be6f3cd..eb3ed91bac7 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -494,6 +494,7 @@ struct adapter_ops int (*adapter_sync_cmd)(struct aac_dev *dev, u32 command, u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6, u32 *status, u32 *r1, u32 *r2, u32 *r3, u32 *r4); int (*adapter_check_health)(struct aac_dev *dev); int (*adapter_send)(struct fib * fib); + int (*adapter_ioremap)(struct aac_dev * dev, u32 size); }; /* @@ -682,14 +683,6 @@ struct rx_inbound { __le32 Mailbox[8]; }; -#define InboundMailbox0 IndexRegs.Mailbox[0] -#define InboundMailbox1 IndexRegs.Mailbox[1] -#define InboundMailbox2 IndexRegs.Mailbox[2] -#define InboundMailbox3 IndexRegs.Mailbox[3] -#define InboundMailbox4 IndexRegs.Mailbox[4] -#define InboundMailbox5 IndexRegs.Mailbox[5] -#define InboundMailbox6 IndexRegs.Mailbox[6] - #define INBOUNDDOORBELL_0 0x00000001 #define INBOUNDDOORBELL_1 0x00000002 #define INBOUNDDOORBELL_2 0x00000004 @@ -1010,6 +1003,8 @@ struct aac_dev struct rx_registers __iomem *rx; struct rkt_registers __iomem *rkt; } regs; + volatile void __iomem *base; + volatile struct rx_inbound __iomem *IndexRegs; u32 OIMR; /* Mask Register Cache */ /* * AIF thread states @@ -1050,6 +1045,9 @@ struct aac_dev #define aac_adapter_send(fib) \ ((fib)->dev)->a_ops.adapter_send(fib) +#define aac_adapter_ioremap(dev, size) \ + (dev)->a_ops.adapter_ioremap(dev, size) + #define FIB_CONTEXT_FLAG_TIMED_OUT (0x00000001) /* diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c index 87a95509676..d5cf8b91a0e 100644 --- a/drivers/scsi/aacraid/comminit.c +++ b/drivers/scsi/aacraid/comminit.c @@ -307,17 +307,12 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev) if (status[1] & AAC_OPT_NEW_COMM) dev->new_comm_interface = dev->a_ops.adapter_send != 0; if (dev->new_comm_interface && (status[2] > dev->base_size)) { - iounmap(dev->regs.sa); + aac_adapter_ioremap(dev, 0); dev->base_size = status[2]; - dprintk((KERN_DEBUG "ioremap(%lx,%d)\n", - host->base, status[2])); - dev->regs.sa = ioremap(host->base, status[2]); - if (dev->regs.sa == NULL) { + if (aac_adapter_ioremap(dev, status[2])) { /* remap failed, go back ... */ dev->new_comm_interface = 0; - dev->regs.sa = ioremap(host->base, - AAC_MIN_FOOTPRINT_SIZE); - if (dev->regs.sa == NULL) { + if (aac_adapter_ioremap(dev, AAC_MIN_FOOTPRINT_SIZE)) { printk(KERN_WARNING "aacraid: unable to map adapter.\n"); return NULL; diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index 6e4eafa4ece..359e7ddfdb4 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -867,13 +867,6 @@ static int __devinit aac_probe_one(struct pci_dev *pdev, * Map in the registers from the adapter. */ aac->base_size = AAC_MIN_FOOTPRINT_SIZE; - if ((aac->regs.sa = ioremap( - (unsigned long)aac->scsi_host_ptr->base, AAC_MIN_FOOTPRINT_SIZE)) - == NULL) { - printk(KERN_WARNING "%s: unable to map adapter.\n", - AAC_DRIVERNAME); - goto out_free_fibs; - } if ((*aac_drivers[index].init)(aac)) goto out_unmap; @@ -972,8 +965,7 @@ static int __devinit aac_probe_one(struct pci_dev *pdev, aac_fib_map_free(aac); pci_free_consistent(aac->pdev, aac->comm_size, aac->comm_addr, aac->comm_phys); kfree(aac->queues); - iounmap(aac->regs.sa); - out_free_fibs: + aac_adapter_ioremap(aac, 0); kfree(aac->fibs); kfree(aac->fsa_dev); out_free_host: @@ -1008,7 +1000,7 @@ static void __devexit aac_remove_one(struct pci_dev *pdev) kfree(aac->queues); free_irq(pdev->irq, aac); - iounmap(aac->regs.sa); + aac_adapter_ioremap(aac, 0); kfree(aac->fibs); kfree(aac->fsa_dev); diff --git a/drivers/scsi/aacraid/rkt.c b/drivers/scsi/aacraid/rkt.c index f850c3a7cce..643f23b5ded 100644 --- a/drivers/scsi/aacraid/rkt.c +++ b/drivers/scsi/aacraid/rkt.c @@ -28,389 +28,27 @@ * */ -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include #include #include "aacraid.h" -static irqreturn_t aac_rkt_intr(int irq, void *dev_id, struct pt_regs *regs) -{ - struct aac_dev *dev = dev_id; - - if (dev->new_comm_interface) { - u32 Index = rkt_readl(dev, MUnit.OutboundQueue); - if (Index == 0xFFFFFFFFL) - Index = rkt_readl(dev, MUnit.OutboundQueue); - if (Index != 0xFFFFFFFFL) { - do { - if (aac_intr_normal(dev, Index)) { - rkt_writel(dev, MUnit.OutboundQueue, Index); - rkt_writel(dev, MUnit.ODR, DoorBellAdapterNormRespReady); - } - Index = rkt_readl(dev, MUnit.OutboundQueue); - } while (Index != 0xFFFFFFFFL); - return IRQ_HANDLED; - } - } else { - unsigned long bellbits; - u8 intstat; - intstat = rkt_readb(dev, MUnit.OISR); - /* - * Read mask and invert because drawbridge is reversed. - * This allows us to only service interrupts that have - * been enabled. - * Check to see if this is our interrupt. If it isn't just return - */ - if (intstat & ~(dev->OIMR)) - { - bellbits = rkt_readl(dev, OutboundDoorbellReg); - if (bellbits & DoorBellPrintfReady) { - aac_printf(dev, rkt_readl (dev, IndexRegs.Mailbox[5])); - rkt_writel(dev, MUnit.ODR,DoorBellPrintfReady); - rkt_writel(dev, InboundDoorbellReg,DoorBellPrintfDone); - } - else if (bellbits & DoorBellAdapterNormCmdReady) { - rkt_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdReady); - aac_command_normal(&dev->queues->queue[HostNormCmdQueue]); -// rkt_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdReady); - } - else if (bellbits & DoorBellAdapterNormRespReady) { - rkt_writel(dev, MUnit.ODR,DoorBellAdapterNormRespReady); - aac_response_normal(&dev->queues->queue[HostNormRespQueue]); - } - else if (bellbits & DoorBellAdapterNormCmdNotFull) { - rkt_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdNotFull); - } - else if (bellbits & DoorBellAdapterNormRespNotFull) { - rkt_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdNotFull); - rkt_writel(dev, MUnit.ODR, DoorBellAdapterNormRespNotFull); - } - return IRQ_HANDLED; - } - } - return IRQ_NONE; -} - -/** - * aac_rkt_disable_interrupt - Disable interrupts - * @dev: Adapter - */ - -static void aac_rkt_disable_interrupt(struct aac_dev *dev) -{ - rkt_writeb(dev, MUnit.OIMR, dev->OIMR = 0xff); -} - -/** - * rkt_sync_cmd - send a command and wait - * @dev: Adapter - * @command: Command to execute - * @p1: first parameter - * @ret: adapter status - * - * This routine will send a synchronous command to the adapter and wait - * for its completion. - */ - -static int rkt_sync_cmd(struct aac_dev *dev, u32 command, - u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6, - u32 *status, u32 *r1, u32 *r2, u32 *r3, u32 *r4) -{ - unsigned long start; - int ok; - /* - * Write the command into Mailbox 0 - */ - rkt_writel(dev, InboundMailbox0, command); - /* - * Write the parameters into Mailboxes 1 - 6 - */ - rkt_writel(dev, InboundMailbox1, p1); - rkt_writel(dev, InboundMailbox2, p2); - rkt_writel(dev, InboundMailbox3, p3); - rkt_writel(dev, InboundMailbox4, p4); - /* - * Clear the synch command doorbell to start on a clean slate. - */ - rkt_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0); - /* - * Disable doorbell interrupts - */ - rkt_writeb(dev, MUnit.OIMR, dev->OIMR = 0xff); - /* - * Force the completion of the mask register write before issuing - * the interrupt. - */ - rkt_readb (dev, MUnit.OIMR); - /* - * Signal that there is a new synch command - */ - rkt_writel(dev, InboundDoorbellReg, INBOUNDDOORBELL_0); - - ok = 0; - start = jiffies; - - /* - * Wait up to 30 seconds - */ - while (time_before(jiffies, start+30*HZ)) - { - udelay(5); /* Delay 5 microseconds to let Mon960 get info. */ - /* - * Mon960 will set doorbell0 bit when it has completed the command. - */ - if (rkt_readl(dev, OutboundDoorbellReg) & OUTBOUNDDOORBELL_0) { - /* - * Clear the doorbell. - */ - rkt_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0); - ok = 1; - break; - } - /* - * Yield the processor in case we are slow - */ - msleep(1); - } - if (ok != 1) { - /* - * Restore interrupt mask even though we timed out - */ - if (dev->new_comm_interface) - rkt_writeb(dev, MUnit.OIMR, dev->OIMR = 0xf7); - else - rkt_writeb(dev, MUnit.OIMR, dev->OIMR = 0xfb); - return -ETIMEDOUT; - } - /* - * Pull the synch status from Mailbox 0. - */ - if (status) - *status = rkt_readl(dev, IndexRegs.Mailbox[0]); - if (r1) - *r1 = rkt_readl(dev, IndexRegs.Mailbox[1]); - if (r2) - *r2 = rkt_readl(dev, IndexRegs.Mailbox[2]); - if (r3) - *r3 = rkt_readl(dev, IndexRegs.Mailbox[3]); - if (r4) - *r4 = rkt_readl(dev, IndexRegs.Mailbox[4]); - /* - * Clear the synch command doorbell. - */ - rkt_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0); - /* - * Restore interrupt mask - */ - if (dev->new_comm_interface) - rkt_writeb(dev, MUnit.OIMR, dev->OIMR = 0xf7); - else - rkt_writeb(dev, MUnit.OIMR, dev->OIMR = 0xfb); - return 0; - -} - -/** - * aac_rkt_interrupt_adapter - interrupt adapter - * @dev: Adapter - * - * Send an interrupt to the i960 and breakpoint it. - */ - -static void aac_rkt_interrupt_adapter(struct aac_dev *dev) -{ - rkt_sync_cmd(dev, BREAKPOINT_REQUEST, 0, 0, 0, 0, 0, 0, - NULL, NULL, NULL, NULL, NULL); -} - /** - * aac_rkt_notify_adapter - send an event to the adapter - * @dev: Adapter - * @event: Event to send + * aac_rkt_ioremap + * @size: mapping resize request * - * Notify the i960 that something it probably cares about has - * happened. */ - -static void aac_rkt_notify_adapter(struct aac_dev *dev, u32 event) +static int aac_rkt_ioremap(struct aac_dev * dev, u32 size) { - switch (event) { - - case AdapNormCmdQue: - rkt_writel(dev, MUnit.IDR,INBOUNDDOORBELL_1); - break; - case HostNormRespNotFull: - rkt_writel(dev, MUnit.IDR,INBOUNDDOORBELL_4); - break; - case AdapNormRespQue: - rkt_writel(dev, MUnit.IDR,INBOUNDDOORBELL_2); - break; - case HostNormCmdNotFull: - rkt_writel(dev, MUnit.IDR,INBOUNDDOORBELL_3); - break; - case HostShutdown: -// rkt_sync_cmd(dev, HOST_CRASHING, 0, 0, 0, 0, 0, 0, -// NULL, NULL, NULL, NULL, NULL); - break; - case FastIo: - rkt_writel(dev, MUnit.IDR,INBOUNDDOORBELL_6); - break; - case AdapPrintfDone: - rkt_writel(dev, MUnit.IDR,INBOUNDDOORBELL_5); - break; - default: - BUG(); - break; + if (!size) { + iounmap(dev->regs.rkt); + return 0; } -} - -/** - * aac_rkt_start_adapter - activate adapter - * @dev: Adapter - * - * Start up processing on an i960 based AAC adapter - */ - -static void aac_rkt_start_adapter(struct aac_dev *dev) -{ - struct aac_init *init; - - init = dev->init; - init->HostElapsedSeconds = cpu_to_le32(get_seconds()); - // We can only use a 32 bit address here - rkt_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS, (u32)(ulong)dev->init_pa, - 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL); -} - -/** - * aac_rkt_check_health - * @dev: device to check if healthy - * - * Will attempt to determine if the specified adapter is alive and - * capable of handling requests, returning 0 if alive. - */ -static int aac_rkt_check_health(struct aac_dev *dev) -{ - u32 status = rkt_readl(dev, MUnit.OMRx[0]); - - /* - * Check to see if the board failed any self tests. - */ - if (status & SELF_TEST_FAILED) + dev->base = dev->regs.rkt = ioremap(dev->scsi_host_ptr->base, size); + if (dev->base == NULL) return -1; - /* - * Check to see if the board panic'd. - */ - if (status & KERNEL_PANIC) { - char * buffer; - struct POSTSTATUS { - __le32 Post_Command; - __le32 Post_Address; - } * post; - dma_addr_t paddr, baddr; - int ret; - - if ((status & 0xFF000000L) == 0xBC000000L) - return (status >> 16) & 0xFF; - buffer = pci_alloc_consistent(dev->pdev, 512, &baddr); - ret = -2; - if (buffer == NULL) - return ret; - post = pci_alloc_consistent(dev->pdev, - sizeof(struct POSTSTATUS), &paddr); - if (post == NULL) { - pci_free_consistent(dev->pdev, 512, buffer, baddr); - return ret; - } - memset(buffer, 0, 512); - post->Post_Command = cpu_to_le32(COMMAND_POST_RESULTS); - post->Post_Address = cpu_to_le32(baddr); - rkt_writel(dev, MUnit.IMRx[0], paddr); - rkt_sync_cmd(dev, COMMAND_POST_RESULTS, baddr, 0, 0, 0, 0, 0, - NULL, NULL, NULL, NULL, NULL); - pci_free_consistent(dev->pdev, sizeof(struct POSTSTATUS), - post, paddr); - if ((buffer[0] == '0') && ((buffer[1] == 'x') || (buffer[1] == 'X'))) { - ret = (buffer[2] <= '9') ? (buffer[2] - '0') : (buffer[2] - 'A' + 10); - ret <<= 4; - ret += (buffer[3] <= '9') ? (buffer[3] - '0') : (buffer[3] - 'A' + 10); - } - pci_free_consistent(dev->pdev, 512, buffer, baddr); - return ret; - } - /* - * Wait for the adapter to be up and running. - */ - if (!(status & KERNEL_UP_AND_RUNNING)) - return -3; - /* - * Everything is OK - */ - return 0; -} - -/** - * aac_rkt_send - * @fib: fib to issue - * - * Will send a fib, returning 0 if successful. - */ -static int aac_rkt_send(struct fib * fib) -{ - u64 addr = fib->hw_fib_pa; - struct aac_dev *dev = fib->dev; - volatile void __iomem *device = dev->regs.rkt; - u32 Index; - - dprintk((KERN_DEBUG "%p->aac_rkt_send(%p->%llx)\n", dev, fib, addr)); - Index = rkt_readl(dev, MUnit.InboundQueue); - if (Index == 0xFFFFFFFFL) - Index = rkt_readl(dev, MUnit.InboundQueue); - dprintk((KERN_DEBUG "Index = 0x%x\n", Index)); - if (Index == 0xFFFFFFFFL) - return Index; - device += Index; - dprintk((KERN_DEBUG "entry = %x %x %u\n", (u32)(addr & 0xffffffff), - (u32)(addr >> 32), (u32)le16_to_cpu(fib->hw_fib->header.Size))); - writel((u32)(addr & 0xffffffff), device); - device += sizeof(u32); - writel((u32)(addr >> 32), device); - device += sizeof(u32); - writel(le16_to_cpu(fib->hw_fib->header.Size), device); - rkt_writel(dev, MUnit.InboundQueue, Index); - dprintk((KERN_DEBUG "aac_rkt_send - return 0\n")); - return 0; -} - -static int aac_rkt_restart_adapter(struct aac_dev *dev) -{ - u32 var; - - printk(KERN_ERR "%s%d: adapter kernel panic'd.\n", - dev->name, dev->id); - - if (aac_rkt_check_health(dev) <= 0) - return 1; - if (rkt_sync_cmd(dev, IOP_RESET, 0, 0, 0, 0, 0, 0, - &var, NULL, NULL, NULL, NULL)) - return 1; - if (var != 0x00000001) - return 1; - if (rkt_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC) - return 1; + dev->IndexRegs = &dev->regs.rkt->IndexRegs; return 0; } @@ -425,74 +63,18 @@ static int aac_rkt_restart_adapter(struct aac_dev *dev) int aac_rkt_init(struct aac_dev *dev) { - unsigned long start; - unsigned long status; - int instance; - const char * name; - - instance = dev->id; - name = dev->name; + int retval; + extern int _aac_rx_init(struct aac_dev *dev); + extern void aac_rx_start_adapter(struct aac_dev *dev); - /* - * Check to see if the board panic'd while booting. - */ - if (rkt_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC) - if (aac_rkt_restart_adapter(dev)) - goto error_iounmap; - /* - * Check to see if the board failed any self tests. - */ - if (rkt_readl(dev, MUnit.OMRx[0]) & SELF_TEST_FAILED) { - printk(KERN_ERR "%s%d: adapter self-test failed.\n", dev->name, instance); - goto error_iounmap; - } - /* - * Check to see if the monitor panic'd while booting. - */ - if (rkt_readl(dev, MUnit.OMRx[0]) & MONITOR_PANIC) { - printk(KERN_ERR "%s%d: adapter monitor panic.\n", dev->name, instance); - goto error_iounmap; - } - start = jiffies; - /* - * Wait for the adapter to be up and running. Wait up to 3 minutes - */ - while (!(rkt_readl(dev, MUnit.OMRx[0]) & KERNEL_UP_AND_RUNNING)) - { - if(time_after(jiffies, start+startup_timeout*HZ)) - { - status = rkt_readl(dev, MUnit.OMRx[0]); - printk(KERN_ERR "%s%d: adapter kernel failed to start, init status = %lx.\n", - dev->name, instance, status); - goto error_iounmap; - } - msleep(1); - } - if (request_irq(dev->scsi_host_ptr->irq, aac_rkt_intr, IRQF_SHARED|IRQF_DISABLED, "aacraid", (void *)dev)<0) - { - printk(KERN_ERR "%s%d: Interrupt unavailable.\n", name, instance); - goto error_iounmap; - } /* * Fill in the function dispatch table. */ - dev->a_ops.adapter_interrupt = aac_rkt_interrupt_adapter; - dev->a_ops.adapter_disable_int = aac_rkt_disable_interrupt; - dev->a_ops.adapter_notify = aac_rkt_notify_adapter; - dev->a_ops.adapter_sync_cmd = rkt_sync_cmd; - dev->a_ops.adapter_check_health = aac_rkt_check_health; - dev->a_ops.adapter_send = aac_rkt_send; - - /* - * First clear out all interrupts. Then enable the one's that we - * can handle. - */ - rkt_writeb(dev, MUnit.OIMR, 0xff); - rkt_writel(dev, MUnit.ODR, 0xffffffff); - rkt_writeb(dev, MUnit.OIMR, dev->OIMR = 0xfb); + dev->a_ops.adapter_ioremap = aac_rkt_ioremap; - if (aac_init_adapter(dev) == NULL) - goto error_irq; + retval = _aac_rx_init(dev); + if (retval) + return retval; if (dev->new_comm_interface) { /* * FIB Setup has already been done, but we can minimize the @@ -509,20 +91,11 @@ int aac_rkt_init(struct aac_dev *dev) dev->init->MaxIoCommands = cpu_to_le32(246); dev->scsi_host_ptr->can_queue = 246 - AAC_NUM_MGT_FIB; } - rkt_writeb(dev, MUnit.OIMR, dev->OIMR = 0xf7); } /* * Tell the adapter that all is configured, and it can start * accepting requests */ - aac_rkt_start_adapter(dev); + aac_rx_start_adapter(dev); return 0; - -error_irq: - rkt_writeb(dev, MUnit.OIMR, dev->OIMR = 0xff); - free_irq(dev->scsi_host_ptr->irq, (void *)dev); - -error_iounmap: - - return -1; } diff --git a/drivers/scsi/aacraid/rx.c b/drivers/scsi/aacraid/rx.c index c715c4b2442..a1d214d770e 100644 --- a/drivers/scsi/aacraid/rx.c +++ b/drivers/scsi/aacraid/rx.c @@ -79,7 +79,7 @@ static irqreturn_t aac_rx_intr(int irq, void *dev_id, struct pt_regs *regs) { bellbits = rx_readl(dev, OutboundDoorbellReg); if (bellbits & DoorBellPrintfReady) { - aac_printf(dev, rx_readl (dev, IndexRegs.Mailbox[5])); + aac_printf(dev, readl (&dev->IndexRegs->Mailbox[5])); rx_writel(dev, MUnit.ODR,DoorBellPrintfReady); rx_writel(dev, InboundDoorbellReg,DoorBellPrintfDone); } @@ -134,14 +134,14 @@ static int rx_sync_cmd(struct aac_dev *dev, u32 command, /* * Write the command into Mailbox 0 */ - rx_writel(dev, InboundMailbox0, command); + writel(command, &dev->IndexRegs->Mailbox[0]); /* * Write the parameters into Mailboxes 1 - 6 */ - rx_writel(dev, InboundMailbox1, p1); - rx_writel(dev, InboundMailbox2, p2); - rx_writel(dev, InboundMailbox3, p3); - rx_writel(dev, InboundMailbox4, p4); + writel(p1, &dev->IndexRegs->Mailbox[1]); + writel(p2, &dev->IndexRegs->Mailbox[2]); + writel(p3, &dev->IndexRegs->Mailbox[3]); + writel(p4, &dev->IndexRegs->Mailbox[4]); /* * Clear the synch command doorbell to start on a clean slate. */ @@ -199,15 +199,15 @@ static int rx_sync_cmd(struct aac_dev *dev, u32 command, * Pull the synch status from Mailbox 0. */ if (status) - *status = rx_readl(dev, IndexRegs.Mailbox[0]); + *status = readl(&dev->IndexRegs->Mailbox[0]); if (r1) - *r1 = rx_readl(dev, IndexRegs.Mailbox[1]); + *r1 = readl(&dev->IndexRegs->Mailbox[1]); if (r2) - *r2 = rx_readl(dev, IndexRegs.Mailbox[2]); + *r2 = readl(&dev->IndexRegs->Mailbox[2]); if (r3) - *r3 = rx_readl(dev, IndexRegs.Mailbox[3]); + *r3 = readl(&dev->IndexRegs->Mailbox[3]); if (r4) - *r4 = rx_readl(dev, IndexRegs.Mailbox[4]); + *r4 = readl(&dev->IndexRegs->Mailbox[4]); /* * Clear the synch command doorbell. */ @@ -261,8 +261,6 @@ static void aac_rx_notify_adapter(struct aac_dev *dev, u32 event) rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_3); break; case HostShutdown: -// rx_sync_cmd(dev, HOST_CRASHING, 0, 0, 0, 0, 0, 0, -// NULL, NULL, NULL, NULL, NULL); break; case FastIo: rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_6); @@ -283,7 +281,7 @@ static void aac_rx_notify_adapter(struct aac_dev *dev, u32 event) * Start up processing on an i960 based AAC adapter */ -static void aac_rx_start_adapter(struct aac_dev *dev) +void aac_rx_start_adapter(struct aac_dev *dev) { struct aac_init *init; @@ -381,7 +379,7 @@ static int aac_rx_send(struct fib * fib) dprintk((KERN_DEBUG "Index = 0x%x\n", Index)); if (Index == 0xFFFFFFFFL) return Index; - device += Index; + device = dev->base + Index; dprintk((KERN_DEBUG "entry = %x %x %u\n", (u32)(addr & 0xffffffff), (u32)(addr >> 32), (u32)le16_to_cpu(fib->hw_fib->header.Size))); writel((u32)(addr & 0xffffffff), device); @@ -394,6 +392,24 @@ static int aac_rx_send(struct fib * fib) return 0; } +/** + * aac_rx_ioremap + * @size: mapping resize request + * + */ +static int aac_rx_ioremap(struct aac_dev * dev, u32 size) +{ + if (!size) { + iounmap(dev->regs.rx); + return 0; + } + dev->base = dev->regs.rx = ioremap(dev->scsi_host_ptr->base, size); + if (dev->base == NULL) + return -1; + dev->IndexRegs = &dev->regs.rx->IndexRegs; + return 0; +} + static int aac_rx_restart_adapter(struct aac_dev *dev) { u32 var; @@ -422,7 +438,7 @@ static int aac_rx_restart_adapter(struct aac_dev *dev) * to the comm region. */ -int aac_rx_init(struct aac_dev *dev) +int _aac_rx_init(struct aac_dev *dev) { unsigned long start; unsigned long status; @@ -432,23 +448,30 @@ int aac_rx_init(struct aac_dev *dev) instance = dev->id; name = dev->name; + if (aac_adapter_ioremap(dev, dev->base_size)) { + printk(KERN_WARNING "%s: unable to map adapter.\n", name); + goto error_iounmap; + } + /* * Check to see if the board panic'd while booting. */ - if (rx_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC) + status = rx_readl(dev, MUnit.OMRx[0]); + if (status & KERNEL_PANIC) if (aac_rx_restart_adapter(dev)) goto error_iounmap; /* * Check to see if the board failed any self tests. */ - if (rx_readl(dev, MUnit.OMRx[0]) & SELF_TEST_FAILED) { + status = rx_readl(dev, MUnit.OMRx[0]); + if (status & SELF_TEST_FAILED) { printk(KERN_ERR "%s%d: adapter self-test failed.\n", dev->name, instance); goto error_iounmap; } /* * Check to see if the monitor panic'd while booting. */ - if (rx_readl(dev, MUnit.OMRx[0]) & MONITOR_PANIC) { + if (status & MONITOR_PANIC) { printk(KERN_ERR "%s%d: adapter monitor panic.\n", dev->name, instance); goto error_iounmap; } @@ -456,12 +479,10 @@ int aac_rx_init(struct aac_dev *dev) /* * Wait for the adapter to be up and running. Wait up to 3 minutes */ - while ((!(rx_readl(dev, IndexRegs.Mailbox[7]) & KERNEL_UP_AND_RUNNING)) - || (!(rx_readl(dev, MUnit.OMRx[0]) & KERNEL_UP_AND_RUNNING))) + while (!((status = rx_readl(dev, MUnit.OMRx[0])) & KERNEL_UP_AND_RUNNING)) { if(time_after(jiffies, start+startup_timeout*HZ)) { - status = rx_readl(dev, IndexRegs.Mailbox[7]); printk(KERN_ERR "%s%d: adapter kernel failed to start, init status = %lx.\n", dev->name, instance, status); goto error_iounmap; @@ -496,11 +517,6 @@ int aac_rx_init(struct aac_dev *dev) if (dev->new_comm_interface) rx_writeb(dev, MUnit.OIMR, dev->OIMR = 0xf7); - /* - * Tell the adapter that all is configured, and it can start - * accepting requests - */ - aac_rx_start_adapter(dev); return 0; error_irq: @@ -511,3 +527,23 @@ error_iounmap: return -1; } + +int aac_rx_init(struct aac_dev *dev) +{ + int retval; + + /* + * Fill in the function dispatch table. + */ + dev->a_ops.adapter_ioremap = aac_rx_ioremap; + + retval = _aac_rx_init(dev); + if (!retval) { + /* + * Tell the adapter that all is configured, and it can + * start accepting requests + */ + aac_rx_start_adapter(dev); + } + return retval; +} diff --git a/drivers/scsi/aacraid/sa.c b/drivers/scsi/aacraid/sa.c index cd586cc8f9b..f906ead239d 100644 --- a/drivers/scsi/aacraid/sa.c +++ b/drivers/scsi/aacraid/sa.c @@ -280,6 +280,21 @@ static int aac_sa_check_health(struct aac_dev *dev) return 0; } +/** + * aac_sa_ioremap + * @size: mapping resize request + * + */ +static int aac_sa_ioremap(struct aac_dev * dev, u32 size) +{ + if (!size) { + iounmap(dev->regs.sa); + return 0; + } + dev->base = dev->regs.sa = ioremap(dev->scsi_host_ptr->base, size); + return (dev->base == NULL) ? -1 : 0; +} + /** * aac_sa_init - initialize an ARM based AAC card * @dev: device to configure @@ -299,6 +314,11 @@ int aac_sa_init(struct aac_dev *dev) instance = dev->id; name = dev->name; + if (aac_sa_ioremap(dev, dev->base_size)) { + printk(KERN_WARNING "%s: unable to map adapter.\n", name); + goto error_iounmap; + } + /* * Check to see if the board failed any self tests. */ @@ -341,6 +361,7 @@ int aac_sa_init(struct aac_dev *dev) dev->a_ops.adapter_notify = aac_sa_notify_adapter; dev->a_ops.adapter_sync_cmd = sa_sync_cmd; dev->a_ops.adapter_check_health = aac_sa_check_health; + dev->a_ops.adapter_ioremap = aac_sa_ioremap; /* * First clear out all interrupts. Then enable the one's that -- cgit v1.2.3 From ac5826ca91243272f97b3f01e80d71e3618f105f Mon Sep 17 00:00:00 2001 From: Mark Haverkamp Date: Tue, 19 Sep 2006 09:00:18 -0700 Subject: [SCSI] aacraid: remove scsi_remove_device Received from Mark Salyzyn: Until the system is stabilized, I am suggesting the enclosed modification to prevent the driver from tickling the panic. Once sysfs and friends are stabilized, the patch may be backed out. We have yet to evaluate if we really want to relinquish existing Scsi Devices in any case, holding on to them as configuration of arrays comes and goes makes some sense as well. As a result, we have opted to pull the lines rather than comment them in legacy. Signed-off-by: Mark Haverkamp Signed-off-by: James Bottomley --- drivers/scsi/aacraid/commsup.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index 907161d6e92..8734a045558 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -1033,13 +1033,7 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) if (device) { switch (device_config_needed) { case DELETE: - scsi_remove_device(device); - break; case CHANGE: - if (!dev->fsa_dev[container].valid) { - scsi_remove_device(device); - break; - } scsi_rescan_device(&device->sdev_gendev); default: -- cgit v1.2.3 From 1a68d41a334a406d4bd35999f0be4d47f193e477 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 18 Sep 2006 19:18:34 +0100 Subject: [SCSI] eata_pio cleanup and PCI fix This started as a PCI reference fixup but to do that I need to build it, to build it I need to fix it and its full of 32bitisms and uglies. It has been resurrected, I'm not sure if this is a thank you for the work on the license stuff or punishment for some unknown misdeed however 8). I've also fixed a memory scribble in the init code. One oddity - the changes from HZ * to constants are deliberate. Whoever originally wrote the code (or cleaned it up) used HZ for a cycle timing loop even though is not HZ related. I've put it back to the counts used in the old days when the driver was most used. Signed-off-by: Alan Cox Signed-off-by: James Bottomley --- drivers/scsi/eata_generic.h | 1 + drivers/scsi/eata_pio.c | 127 ++++++++++++++++++++++++-------------------- 2 files changed, 69 insertions(+), 59 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/eata_generic.h b/drivers/scsi/eata_generic.h index 34bce2c9e92..635c14861f8 100644 --- a/drivers/scsi/eata_generic.h +++ b/drivers/scsi/eata_generic.h @@ -364,6 +364,7 @@ typedef struct hstd { __u8 moresupport; /* HBA supports MORE flag */ struct Scsi_Host *next; struct Scsi_Host *prev; + struct pci_dev *pdev; /* PCI device or NULL for non PCI */ struct eata_sp sp; /* status packet */ struct eata_ccb ccb[0]; /* ccb array begins here */ }hostdata; diff --git a/drivers/scsi/eata_pio.c b/drivers/scsi/eata_pio.c index 771b01984cb..d312633db92 100644 --- a/drivers/scsi/eata_pio.c +++ b/drivers/scsi/eata_pio.c @@ -71,11 +71,11 @@ #include "eata_pio.h" -static uint ISAbases[MAXISA] = { +static unsigned int ISAbases[MAXISA] = { 0x1F0, 0x170, 0x330, 0x230 }; -static uint ISAirqs[MAXISA] = { +static unsigned int ISAirqs[MAXISA] = { 14, 12, 15, 11 }; @@ -84,7 +84,7 @@ static unsigned char EISAbases[] = { 1, 1, 1, 1, 1, 1, 1, 1 }; -static uint registered_HBAs; +static unsigned int registered_HBAs; static struct Scsi_Host *last_HBA; static struct Scsi_Host *first_HBA; static unsigned char reg_IRQ[16]; @@ -165,6 +165,7 @@ static int eata_pio_proc_info(struct Scsi_Host *shost, char *buffer, char **star static int eata_pio_release(struct Scsi_Host *sh) { + hostdata *hd = SD(sh); if (sh->irq && reg_IRQ[sh->irq] == 1) free_irq(sh->irq, NULL); else @@ -173,10 +174,13 @@ static int eata_pio_release(struct Scsi_Host *sh) if (sh->io_port && sh->n_io_port) release_region(sh->io_port, sh->n_io_port); } + /* At this point the PCI reference can go */ + if (hd->pdev) + pci_dev_put(hd->pdev); return 1; } -static void IncStat(struct scsi_pointer *SCp, uint Increment) +static void IncStat(struct scsi_pointer *SCp, unsigned int Increment) { SCp->ptr += Increment; if ((SCp->this_residual -= Increment) == 0) { @@ -190,46 +194,49 @@ static void IncStat(struct scsi_pointer *SCp, uint Increment) } } -static void eata_pio_int_handler(int irq, void *dev_id, struct pt_regs *regs); +static irqreturn_t eata_pio_int_handler(int irq, void *dev_id, struct pt_regs *regs); static irqreturn_t do_eata_pio_int_handler(int irq, void *dev_id, struct pt_regs *regs) { unsigned long flags; struct Scsi_Host *dev = dev_id; + irqreturn_t ret; spin_lock_irqsave(dev->host_lock, flags); - eata_pio_int_handler(irq, dev_id, regs); + ret = eata_pio_int_handler(irq, dev_id, regs); spin_unlock_irqrestore(dev->host_lock, flags); - return IRQ_HANDLED; + return ret; } -static void eata_pio_int_handler(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t eata_pio_int_handler(int irq, void *dev_id, struct pt_regs *regs) { - uint eata_stat = 0xfffff; + unsigned int eata_stat = 0xfffff; struct scsi_cmnd *cmd; hostdata *hd; struct eata_ccb *cp; - uint base; - uint x, z; + unsigned long base; + unsigned int x, z; struct Scsi_Host *sh; unsigned short zwickel = 0; unsigned char stat, odd; + irqreturn_t ret = IRQ_NONE; for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->prev) { if (sh->irq != irq) continue; - if (inb((uint) sh->base + HA_RSTATUS) & HA_SBUSY) + if (inb(sh->base + HA_RSTATUS) & HA_SBUSY) continue; int_counter++; + ret = IRQ_HANDLED; hd = SD(sh); cp = &hd->ccb[0]; cmd = cp->cmd; - base = (uint) cmd->device->host->base; + base = cmd->device->host->base; do { stat = inb(base + HA_RSTATUS); @@ -304,7 +311,7 @@ static void eata_pio_int_handler(int irq, void *dev_id, struct pt_regs *regs) if (!(inb(base + HA_RSTATUS) & HA_SERROR)) { cmd->result = (DID_OK << 16); hd->devflags |= (1 << cp->cp_id); - } else if (hd->devflags & 1 << cp->cp_id) + } else if (hd->devflags & (1 << cp->cp_id)) cmd->result = (DID_OK << 16) + 0x02; else cmd->result = (DID_NO_CONNECT << 16); @@ -313,7 +320,7 @@ static void eata_pio_int_handler(int irq, void *dev_id, struct pt_regs *regs) cp->status = FREE; eata_stat = inb(base + HA_RSTATUS); printk(KERN_CRIT "eata_pio: int_handler, freeing locked " "queueslot\n"); - return; + return ret; } #if DBG_INTR2 if (stat != 0x50) @@ -325,12 +332,12 @@ static void eata_pio_int_handler(int irq, void *dev_id, struct pt_regs *regs) cmd->scsi_done(cmd); } - return; + return ret; } -static inline uint eata_pio_send_command(uint base, unsigned char command) +static inline unsigned int eata_pio_send_command(unsigned long base, unsigned char command) { - uint loop = HZ / 2; + unsigned int loop = 50; while (inb(base + HA_RSTATUS) & HA_SBUSY) if (--loop == 0) @@ -349,8 +356,8 @@ static inline uint eata_pio_send_command(uint base, unsigned char command) static int eata_pio_queue(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) { - uint x, y; - uint base; + unsigned int x, y; + unsigned long base; hostdata *hd; struct Scsi_Host *sh; @@ -360,7 +367,7 @@ static int eata_pio_queue(struct scsi_cmnd *cmd, hd = HD(cmd); sh = cmd->device->host; - base = (uint) sh->base; + base = sh->base; /* use only slot 0, as 2001 can handle only one cmd at a time */ @@ -395,9 +402,9 @@ static int eata_pio_queue(struct scsi_cmnd *cmd, cp->DataIn = 0; /* Input mode */ cp->Interpret = (cmd->device->id == hd->hostid); - cp->cp_datalen = htonl((unsigned long) cmd->request_bufflen); + cp->cp_datalen = cpu_to_be32(cmd->request_bufflen); cp->Auto_Req_Sen = 0; - cp->cp_reqDMA = htonl(0); + cp->cp_reqDMA = 0; cp->reqlen = 0; cp->cp_id = cmd->device->id; @@ -406,7 +413,7 @@ static int eata_pio_queue(struct scsi_cmnd *cmd, cp->cp_identify = 1; memcpy(cp->cp_cdb, cmd->cmnd, COMMAND_SIZE(*cmd->cmnd)); - cp->cp_statDMA = htonl(0); + cp->cp_statDMA = 0; cp->cp_viraddr = cp; cp->cmd = cmd; @@ -445,14 +452,14 @@ static int eata_pio_queue(struct scsi_cmnd *cmd, DBG(DBG_QUEUE, scmd_printk(KERN_DEBUG, cmd, "Queued base %#.4lx pid: %ld " - "slot %d irq %d\n", (long) sh->base, cmd->pid, y, sh->irq)); + "slot %d irq %d\n", sh->base, cmd->pid, y, sh->irq)); return (0); } static int eata_pio_abort(struct scsi_cmnd *cmd) { - uint loop = HZ; + unsigned int loop = 100; DBG(DBG_ABNORM, scmd_printk(KERN_WARNING, cmd, "eata_pio_abort called pid: %ld\n", @@ -485,7 +492,7 @@ static int eata_pio_abort(struct scsi_cmnd *cmd) static int eata_pio_host_reset(struct scsi_cmnd *cmd) { - uint x, limit = 0; + unsigned int x, limit = 0; unsigned char success = 0; struct scsi_cmnd *sp; struct Scsi_Host *host = cmd->device->host; @@ -518,7 +525,7 @@ static int eata_pio_host_reset(struct scsi_cmnd *cmd) } /* hard reset the HBA */ - outb(EATA_CMD_RESET, (uint) cmd->device->host->base + HA_WCOMMAND); + outb(EATA_CMD_RESET, cmd->device->host->base + HA_WCOMMAND); DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: board reset done.\n")); HD(cmd)->state = RESET; @@ -558,7 +565,7 @@ static int eata_pio_host_reset(struct scsi_cmnd *cmd) } } -static char *get_pio_board_data(unsigned long base, uint irq, uint id, unsigned long cplen, unsigned short cppadlen) +static char *get_pio_board_data(unsigned long base, unsigned int irq, unsigned int id, unsigned long cplen, unsigned short cppadlen) { struct eata_ccb cp; static char buff[256]; @@ -570,8 +577,8 @@ static char *get_pio_board_data(unsigned long base, uint irq, uint id, unsigned cp.DataIn = 1; cp.Interpret = 1; /* Interpret command */ - cp.cp_datalen = htonl(254); - cp.cp_dataDMA = htonl(0); + cp.cp_datalen = cpu_to_be32(254); + cp.cp_dataDMA = cpu_to_be32(0); cp.cp_id = id; cp.cp_lun = 0; @@ -583,7 +590,7 @@ static char *get_pio_board_data(unsigned long base, uint irq, uint id, unsigned cp.cp_cdb[4] = 254; cp.cp_cdb[5] = 0; - if (eata_pio_send_command((uint) base, EATA_CMD_PIO_SEND_CP)) + if (eata_pio_send_command(base, EATA_CMD_PIO_SEND_CP)) return (NULL); while (!(inb(base + HA_RSTATUS) & HA_SDRQ)); outsw(base + HA_RDATA, &cp, cplen); @@ -604,7 +611,7 @@ static char *get_pio_board_data(unsigned long base, uint irq, uint id, unsigned } } -static int get_pio_conf_PIO(u32 base, struct get_conf *buf) +static int get_pio_conf_PIO(unsigned long base, struct get_conf *buf) { unsigned long loop = HZ / 2; int z; @@ -619,30 +626,30 @@ static int get_pio_conf_PIO(u32 base, struct get_conf *buf) if (--loop == 0) goto fail; - DBG(DBG_PIO && DBG_PROBE, printk(KERN_DEBUG "Issuing PIO READ CONFIG to HBA at %#x\n", base)); + DBG(DBG_PIO && DBG_PROBE, printk(KERN_DEBUG "Issuing PIO READ CONFIG to HBA at %#lx\n", base)); eata_pio_send_command(base, EATA_CMD_PIO_READ_CONFIG); - loop = HZ / 2; + loop = 50; for (p = (unsigned short *) buf; (long) p <= ((long) buf + (sizeof(struct get_conf) / 2)); p++) { while (!(inb(base + HA_RSTATUS) & HA_SDRQ)) if (--loop == 0) goto fail; - loop = HZ / 2; + loop = 50; *p = inw(base + HA_RDATA); } if (inb(base + HA_RSTATUS) & HA_SERROR) { DBG(DBG_PROBE, printk("eata_dma: get_conf_PIO, error during " - "transfer for HBA at %x\n", base)); + "transfer for HBA at %lx\n", base)); goto fail; } - if (htonl(EATA_SIGNATURE) != buf->signature) + if (cpu_to_be32(EATA_SIGNATURE) != buf->signature) goto fail; DBG(DBG_PIO && DBG_PROBE, printk(KERN_NOTICE "EATA Controller found " - "at %#4x EATA Level: %x\n", - base, (uint) (buf->version))); + "at %#4lx EATA Level: %x\n", + base, (unsigned int) (buf->version))); while (inb(base + HA_RSTATUS) & HA_SDRQ) inw(base + HA_RDATA); @@ -665,12 +672,12 @@ static int get_pio_conf_PIO(u32 base, struct get_conf *buf) static void print_pio_config(struct get_conf *gc) { printk("Please check values: (read config data)\n"); - printk("LEN: %d ver:%d OCS:%d TAR:%d TRNXFR:%d MORES:%d\n", (uint) ntohl(gc->len), gc->version, gc->OCS_enabled, gc->TAR_support, gc->TRNXFR, gc->MORE_support); - printk("HAAV:%d SCSIID0:%d ID1:%d ID2:%d QUEUE:%d SG:%d SEC:%d\n", gc->HAA_valid, gc->scsi_id[3], gc->scsi_id[2], gc->scsi_id[1], ntohs(gc->queuesiz), ntohs(gc->SGsiz), gc->SECOND); + printk("LEN: %d ver:%d OCS:%d TAR:%d TRNXFR:%d MORES:%d\n", be32_to_cpu(gc->len), gc->version, gc->OCS_enabled, gc->TAR_support, gc->TRNXFR, gc->MORE_support); + printk("HAAV:%d SCSIID0:%d ID1:%d ID2:%d QUEUE:%d SG:%d SEC:%d\n", gc->HAA_valid, gc->scsi_id[3], gc->scsi_id[2], gc->scsi_id[1], be16_to_cpu(gc->queuesiz), be16_to_cpu(gc->SGsiz), gc->SECOND); printk("IRQ:%d IRQT:%d FORCADR:%d MCH:%d RIDQ:%d\n", gc->IRQ, gc->IRQ_TR, gc->FORCADR, gc->MAX_CHAN, gc->ID_qest); } -static uint print_selftest(uint base) +static unsigned int print_selftest(unsigned int base) { unsigned char buffer[512]; #ifdef VERBOSE_SETUP @@ -697,7 +704,7 @@ static uint print_selftest(uint base) return (!(inb(base + HA_RSTATUS) & HA_SERROR)); } -static int register_pio_HBA(long base, struct get_conf *gc) +static int register_pio_HBA(long base, struct get_conf *gc, struct pci_dev *pdev) { unsigned long size = 0; char *buff; @@ -714,17 +721,17 @@ static int register_pio_HBA(long base, struct get_conf *gc) return 0; } - if ((buff = get_pio_board_data((uint) base, gc->IRQ, gc->scsi_id[3], cplen = (htonl(gc->cplen) + 1) / 2, cppadlen = (htons(gc->cppadlen) + 1) / 2)) == NULL) { - printk("HBA at %#lx didn't react on INQUIRY. Sorry.\n", (unsigned long) base); + if ((buff = get_pio_board_data(base, gc->IRQ, gc->scsi_id[3], cplen = (cpu_to_be32(gc->cplen) + 1) / 2, cppadlen = (cpu_to_be16(gc->cppadlen) + 1) / 2)) == NULL) { + printk("HBA at %#lx didn't react on INQUIRY. Sorry.\n", base); return 0; } if (!print_selftest(base) && !ALLOW_DMA_BOARDS) { - printk("HBA at %#lx failed while performing self test & setup.\n", (unsigned long) base); + printk("HBA at %#lx failed while performing self test & setup.\n", base); return 0; } - size = sizeof(hostdata) + (sizeof(struct eata_ccb) * ntohs(gc->queuesiz)); + size = sizeof(hostdata) + (sizeof(struct eata_ccb) * be16_to_cpu(gc->queuesiz)); sh = scsi_register(&driver_template, size); if (sh == NULL) @@ -749,8 +756,8 @@ static int register_pio_HBA(long base, struct get_conf *gc) hd = SD(sh); - memset(hd->ccb, 0, (sizeof(struct eata_ccb) * ntohs(gc->queuesiz))); - memset(hd->reads, 0, sizeof(unsigned long) * 26); + memset(hd->ccb, 0, (sizeof(struct eata_ccb) * be16_to_cpu(gc->queuesiz))); + memset(hd->reads, 0, sizeof(hd->reads)); strlcpy(SD(sh)->vendor, &buff[8], sizeof(SD(sh)->vendor)); strlcpy(SD(sh)->name, &buff[16], sizeof(SD(sh)->name)); @@ -761,7 +768,7 @@ static int register_pio_HBA(long base, struct get_conf *gc) SD(sh)->revision[4] = buff[35]; SD(sh)->revision[5] = 0; - switch (ntohl(gc->len)) { + switch (be32_to_cpu(gc->len)) { case 0x1c: SD(sh)->EATA_revision = 'a'; break; @@ -777,7 +784,7 @@ static int register_pio_HBA(long base, struct get_conf *gc) SD(sh)->EATA_revision = '?'; } - if (ntohl(gc->len) >= 0x22) { + if (be32_to_cpu(gc->len) >= 0x22) { if (gc->is_PCI) hd->bustype = IS_PCI; else if (gc->is_EISA) @@ -811,6 +818,8 @@ static int register_pio_HBA(long base, struct get_conf *gc) hd->channel = 0; + hd->pdev = pci_dev_get(pdev); /* Keep a PCI reference */ + sh->max_id = 8; sh->max_lun = 8; @@ -841,7 +850,7 @@ static void find_pio_ISA(struct get_conf *buf) continue; if (!get_pio_conf_PIO(ISAbases[i], buf)) continue; - if (!register_pio_HBA(ISAbases[i], buf)) + if (!register_pio_HBA(ISAbases[i], buf, NULL)) release_region(ISAbases[i], 9); else ISAbases[i] = 0; @@ -873,7 +882,7 @@ static void find_pio_EISA(struct get_conf *buf) if (get_pio_conf_PIO(base, buf)) { DBG(DBG_PROBE && DBG_EISA, print_pio_config(buf)); if (buf->IRQ) { - if (!register_pio_HBA(base, buf)) + if (!register_pio_HBA(base, buf, NULL)) release_region(base, 9); } else { printk(KERN_NOTICE "eata_dma: No valid IRQ. HBA " "removed from list\n"); @@ -896,9 +905,9 @@ static void find_pio_PCI(struct get_conf *buf) printk("eata_dma: kernel PCI support not enabled. Skipping scan for PCI HBAs.\n"); #else struct pci_dev *dev = NULL; - u32 base, x; + unsigned long base, x; - while ((dev = pci_find_device(PCI_VENDOR_ID_DPT, PCI_DEVICE_ID_DPT, dev)) != NULL) { + while ((dev = pci_get_device(PCI_VENDOR_ID_DPT, PCI_DEVICE_ID_DPT, dev)) != NULL) { DBG(DBG_PROBE && DBG_PCI, printk("eata_pio: find_PCI, HBA at %s\n", pci_name(dev))); if (pci_enable_device(dev)) continue; @@ -926,7 +935,7 @@ static void find_pio_PCI(struct get_conf *buf) * eventually remove it from the EISA and ISA list */ - if (!register_pio_HBA(base, buf)) { + if (!register_pio_HBA(base, buf, dev)) { release_region(base, 9); continue; } @@ -976,12 +985,12 @@ static int eata_pio_detect(struct scsi_host_template *tpnt) printk("Registered HBAs:\n"); printk("HBA no. Boardtype: Revis: EATA: Bus: BaseIO: IRQ: Ch: ID: Pr:" " QS: SG: CPL:\n"); for (i = 1; i <= registered_HBAs; i++) { - printk("scsi%-2d: %.10s v%s 2.0%c %s %#.4x %2d %d %d %c" + printk("scsi%-2d: %.10s v%s 2.0%c %s %#.4lx %2d %d %d %c" " %2d %2d %2d\n", HBA_ptr->host_no, SD(HBA_ptr)->name, SD(HBA_ptr)->revision, SD(HBA_ptr)->EATA_revision, (SD(HBA_ptr)->bustype == 'P') ? "PCI " : (SD(HBA_ptr)->bustype == 'E') ? "EISA" : "ISA ", - (uint) HBA_ptr->base, HBA_ptr->irq, SD(HBA_ptr)->channel, HBA_ptr->this_id, + HBA_ptr->base, HBA_ptr->irq, SD(HBA_ptr)->channel, HBA_ptr->this_id, SD(HBA_ptr)->primary ? 'Y' : 'N', HBA_ptr->can_queue, HBA_ptr->sg_tablesize, HBA_ptr->cmd_per_lun); HBA_ptr = SD(HBA_ptr)->next; -- cgit v1.2.3 From a07f353701acae77e023f6270e8af353b37af7c4 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 15 Sep 2006 15:34:32 +0100 Subject: [SCSI] Switch some more scsi drivers to pci_get_device and refcounted pci structures Signed-off-by: Alan Cox Signed-off-by: James Bottomley --- drivers/scsi/BusLogic.c | 15 ++++++++------- drivers/scsi/aic7xxx_old.c | 9 +++++++-- drivers/scsi/dpt_i2o.c | 6 +++++- 3 files changed, 20 insertions(+), 10 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c index 59d1adaed73..4ea49fd7965 100644 --- a/drivers/scsi/BusLogic.c +++ b/drivers/scsi/BusLogic.c @@ -662,7 +662,7 @@ static int __init BusLogic_InitializeMultiMasterProbeInfo(struct BusLogic_HostAd particular standard ISA I/O Address need not be probed. */ PrimaryProbeInfo->IO_Address = 0; - while ((PCI_Device = pci_find_device(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER, PCI_Device)) != NULL) { + while ((PCI_Device = pci_get_device(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER, PCI_Device)) != NULL) { struct BusLogic_HostAdapter *HostAdapter = PrototypeHostAdapter; struct BusLogic_PCIHostAdapterInformation PCIHostAdapterInformation; enum BusLogic_ISACompatibleIOPort ModifyIOAddressRequest; @@ -762,7 +762,7 @@ static int __init BusLogic_InitializeMultiMasterProbeInfo(struct BusLogic_HostAd PrimaryProbeInfo->Bus = Bus; PrimaryProbeInfo->Device = Device; PrimaryProbeInfo->IRQ_Channel = IRQ_Channel; - PrimaryProbeInfo->PCI_Device = PCI_Device; + PrimaryProbeInfo->PCI_Device = pci_dev_get(PCI_Device); PCIMultiMasterCount++; } else if (BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters) { struct BusLogic_ProbeInfo *ProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++]; @@ -773,7 +773,7 @@ static int __init BusLogic_InitializeMultiMasterProbeInfo(struct BusLogic_HostAd ProbeInfo->Bus = Bus; ProbeInfo->Device = Device; ProbeInfo->IRQ_Channel = IRQ_Channel; - ProbeInfo->PCI_Device = PCI_Device; + ProbeInfo->PCI_Device = pci_dev_get(PCI_Device); NonPrimaryPCIMultiMasterCount++; PCIMultiMasterCount++; } else @@ -823,7 +823,7 @@ static int __init BusLogic_InitializeMultiMasterProbeInfo(struct BusLogic_HostAd noting the PCI bus location and assigned IRQ Channel. */ PCI_Device = NULL; - while ((PCI_Device = pci_find_device(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC, PCI_Device)) != NULL) { + while ((PCI_Device = pci_get_device(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC, PCI_Device)) != NULL) { unsigned char Bus; unsigned char Device; unsigned int IRQ_Channel; @@ -850,7 +850,7 @@ static int __init BusLogic_InitializeMultiMasterProbeInfo(struct BusLogic_HostAd ProbeInfo->Bus = Bus; ProbeInfo->Device = Device; ProbeInfo->IRQ_Channel = IRQ_Channel; - ProbeInfo->PCI_Device = PCI_Device; + ProbeInfo->PCI_Device = pci_dev_get(PCI_Device); break; } } @@ -874,7 +874,7 @@ static int __init BusLogic_InitializeFlashPointProbeInfo(struct BusLogic_HostAda /* Interrogate PCI Configuration Space for any FlashPoint Host Adapters. */ - while ((PCI_Device = pci_find_device(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT, PCI_Device)) != NULL) { + while ((PCI_Device = pci_get_device(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT, PCI_Device)) != NULL) { unsigned char Bus; unsigned char Device; unsigned int IRQ_Channel; @@ -923,7 +923,7 @@ static int __init BusLogic_InitializeFlashPointProbeInfo(struct BusLogic_HostAda ProbeInfo->Bus = Bus; ProbeInfo->Device = Device; ProbeInfo->IRQ_Channel = IRQ_Channel; - ProbeInfo->PCI_Device = PCI_Device; + ProbeInfo->PCI_Device = pci_dev_get(PCI_Device); FlashPointCount++; } else BusLogic_Warning("BusLogic: Too many Host Adapters " "detected\n", NULL); @@ -1890,6 +1890,7 @@ static void BusLogic_ReleaseResources(struct BusLogic_HostAdapter *HostAdapter) */ if (HostAdapter->MailboxSpace) pci_free_consistent(HostAdapter->PCI_Device, HostAdapter->MailboxSize, HostAdapter->MailboxSpace, HostAdapter->MailboxSpaceHandle); + pci_dev_put(HostAdapter->PCI_Device); HostAdapter->MailboxSpace = NULL; HostAdapter->MailboxSpaceHandle = 0; HostAdapter->MailboxSize = 0; diff --git a/drivers/scsi/aic7xxx_old.c b/drivers/scsi/aic7xxx_old.c index ba3bccafe11..5dcef48d414 100644 --- a/drivers/scsi/aic7xxx_old.c +++ b/drivers/scsi/aic7xxx_old.c @@ -9194,7 +9194,7 @@ aic7xxx_detect(struct scsi_host_template *template) for (i = 0; i < ARRAY_SIZE(aic_pdevs); i++) { pdev = NULL; - while ((pdev = pci_find_device(aic_pdevs[i].vendor_id, + while ((pdev = pci_get_device(aic_pdevs[i].vendor_id, aic_pdevs[i].device_id, pdev))) { if (pci_enable_device(pdev)) @@ -9651,6 +9651,9 @@ aic7xxx_detect(struct scsi_host_template *template) */ aic7xxx_configure_bugs(temp_p); + /* Hold a pci device reference */ + pci_dev_get(temp_p->pdev); + if ( list_p == NULL ) { list_p = current_p = temp_p; @@ -10987,8 +10990,10 @@ aic7xxx_release(struct Scsi_Host *host) if(!p->pdev) release_region(p->base, MAXREG - MINREG); #ifdef CONFIG_PCI - else + else { pci_release_regions(p->pdev); + pci_dev_put(p->pdev); + } #endif prev = NULL; next = first_aic7xxx; diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c index 45806336ce0..7b3bd34faf4 100644 --- a/drivers/scsi/dpt_i2o.c +++ b/drivers/scsi/dpt_i2o.c @@ -184,7 +184,7 @@ static int adpt_detect(struct scsi_host_template* sht) PINFO("Detecting Adaptec I2O RAID controllers...\n"); /* search for all Adatpec I2O RAID cards */ - while ((pDev = pci_find_device( PCI_DPT_VENDOR_ID, PCI_ANY_ID, pDev))) { + while ((pDev = pci_get_device( PCI_DPT_VENDOR_ID, PCI_ANY_ID, pDev))) { if(pDev->device == PCI_DPT_DEVICE_ID || pDev->device == PCI_DPT_RAPTOR_DEVICE_ID){ if(adpt_install_hba(sht, pDev) ){ @@ -192,8 +192,11 @@ static int adpt_detect(struct scsi_host_template* sht) PERROR("Will not try to detect others.\n"); return hba_count-1; } + pci_dev_get(pDev); } } + if (pDev) + pci_dev_put(pDev); /* In INIT state, Activate IOPs */ for (pHba = hba_chain; pHba; pHba = pHba->next) { @@ -1075,6 +1078,7 @@ static void adpt_i2o_delete_hba(adpt_hba* pHba) } } } + pci_dev_put(pHba->pDev); kfree(pHba); if(hba_count <= 0){ -- cgit v1.2.3 From dfdc58ba354adb80d67c99f7be84f95a8e02e466 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 20 Sep 2006 12:00:18 -0400 Subject: [SCSI] SPI transport class: misc DV fixes Key more of the domain validation settings off the inquiry data from the disk (in particular, don't try IU or DT unless the disk claims to support them. Also add a new dv_in_progress flag to prevent recursive DV. Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_spi.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_transport_spi.c b/drivers/scsi/scsi_transport_spi.c index 29a9a53cdd1..9f070f0d0f2 100644 --- a/drivers/scsi/scsi_transport_spi.c +++ b/drivers/scsi/scsi_transport_spi.c @@ -47,6 +47,7 @@ /* Private data accessors (keep these out of the header file) */ #define spi_dv_pending(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_pending) +#define spi_dv_in_progress(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_in_progress) #define spi_dv_mutex(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_mutex) struct spi_internal { @@ -240,6 +241,7 @@ static int spi_setup_transport_attrs(struct transport_container *tc, spi_pcomp_en(starget) = 0; spi_hold_mcs(starget) = 0; spi_dv_pending(starget) = 0; + spi_dv_in_progress(starget) = 0; spi_initial_dv(starget) = 0; mutex_init(&spi_dv_mutex(starget)); @@ -830,28 +832,37 @@ spi_dv_device_internal(struct scsi_device *sdev, u8 *buffer) DV_SET(period, spi_min_period(starget)); /* try QAS requests; this should be harmless to set if the * target supports it */ - if (scsi_device_qas(sdev)) + if (scsi_device_qas(sdev)) { DV_SET(qas, 1); - /* Also try IU transfers */ - if (scsi_device_ius(sdev)) + } else { + DV_SET(qas, 0); + } + + if (scsi_device_ius(sdev) && spi_min_period(starget) < 9) { + /* This u320 (or u640). Set IU transfers */ DV_SET(iu, 1); - if (spi_min_period(starget) < 9) { - /* This u320 (or u640). Ignore the coupled parameters - * like DT and IU, but set the optional ones */ + /* Then set the optional parameters */ DV_SET(rd_strm, 1); DV_SET(wr_flow, 1); DV_SET(rti, 1); if (spi_min_period(starget) == 8) DV_SET(pcomp_en, 1); + } else { + DV_SET(iu, 0); } + /* now that we've done all this, actually check the bus * signal type (if known). Some devices are stupid on * a SE bus and still claim they can try LVD only settings */ if (i->f->get_signalling) i->f->get_signalling(shost); if (spi_signalling(shost) == SPI_SIGNAL_SE || - spi_signalling(shost) == SPI_SIGNAL_HVD) + spi_signalling(shost) == SPI_SIGNAL_HVD || + !scsi_device_dt(sdev)) { DV_SET(dt, 0); + } else { + DV_SET(dt, 1); + } /* Do the read only INQUIRY tests */ spi_dv_retrain(sdev, buffer, buffer + sdev->inquiry_len, spi_dv_device_compare_inquiry); @@ -907,6 +918,10 @@ spi_dv_device(struct scsi_device *sdev) if (unlikely(scsi_device_get(sdev))) return; + if (unlikely(spi_dv_in_progress(starget))) + return; + spi_dv_in_progress(starget) = 1; + buffer = kzalloc(len, GFP_KERNEL); if (unlikely(!buffer)) @@ -938,6 +953,7 @@ spi_dv_device(struct scsi_device *sdev) out_free: kfree(buffer); out_put: + spi_dv_in_progress(starget) = 0; scsi_device_put(sdev); } EXPORT_SYMBOL(spi_dv_device); -- cgit v1.2.3 From 1b73c4bb063c4aa0cdc25425809bb87f65ee75af Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sat, 23 Sep 2006 22:07:20 -0500 Subject: [SCSI] scsi_transport_fc: fixup netlink arguments nlmsg_multicast now takes an extra allocation flag, so add it to the use in the fibre channel transport class. Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_fc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 4ab176ed480..38c215a78f6 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -480,7 +480,8 @@ fc_host_post_event(struct Scsi_Host *shost, u32 event_number, event->event_code = event_code; event->event_data = event_data; - err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS); + err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS, + GFP_KERNEL); if (err && (err != -ESRCH)) /* filter no recipient errors */ /* nlmsg_multicast already kfree_skb'd */ goto send_fail; @@ -554,7 +555,8 @@ fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number, event->event_code = FCH_EVT_VENDOR_UNIQUE; memcpy(&event->event_data, data_buf, data_len); - err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS); + err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS, + GFP_KERNEL); if (err && (err != -ESRCH)) /* filter no recipient errors */ /* nlmsg_multicast already kfree_skb'd */ goto send_vendor_fail; -- cgit v1.2.3 From 6a3670c4f1b97373425340ab2dc0a66c25983d46 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 24 Sep 2006 23:45:29 +0100 Subject: [PATCH] missing include (free_irq() use) Signed-off-by: Al Viro Signed-off-by: Linus Torvalds --- drivers/scsi/aacraid/commsup.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index 8734a045558..19e42ac07cb 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From 8a1cdc9ca264c49ad0b896e43468fe6a4b0b646c Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 24 Sep 2006 20:22:52 -0600 Subject: [PATCH] Revert ABI-breaking change in /proc Some user tools parse /proc/scsi/scsi, so we can't yet change the names. Change the existing ones back to their old names, and add an admonition to not make the same mistake that I did. Andrew Morton reports that this was breaking YDL 4.1 userspace. Signed-off-by: Matthew Wilcox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/scsi/scsi.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index c51b5769eac..7a054f9d1ee 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -96,22 +96,26 @@ unsigned int scsi_logging_level; EXPORT_SYMBOL(scsi_logging_level); #endif +/* NB: These are exposed through /proc/scsi/scsi and form part of the ABI. + * You may not alter any existing entry (although adding new ones is + * encouraged once assigned by ANSI/INCITS T10 + */ static const char *const scsi_device_types[] = { - "Direct access ", - "Sequential access", + "Direct-Access ", + "Sequential-Access", "Printer ", "Processor ", "WORM ", - "CD/DVD ", + "CD-ROM ", "Scanner ", - "Optical memory ", - "Media changer ", + "Optical Device ", + "Medium Changer ", "Communications ", "ASC IT8 ", "ASC IT8 ", "RAID ", "Enclosure ", - "Direct access RBC", + "Direct-Access-RBC", "Optical card ", "Bridge controller", "Object storage ", -- cgit v1.2.3 From 3cc27547d6ee2d50ecdd11e9127bc3cd1947e8dd Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 25 Sep 2006 02:55:40 +0100 Subject: [PATCH] SCSI gfp_t annotations Signed-off-by: Al Viro Signed-off-by: Linus Torvalds --- drivers/scsi/aic94xx/aic94xx.h | 2 +- drivers/scsi/aic94xx/aic94xx_hwi.c | 8 ++++---- drivers/scsi/aic94xx/aic94xx_hwi.h | 6 +++--- drivers/scsi/aic94xx/aic94xx_task.c | 10 +++++----- drivers/scsi/libsas/sas_scsi_host.c | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aic94xx/aic94xx.h b/drivers/scsi/aic94xx/aic94xx.h index 1bd5b4ecf3d..71a031df7a3 100644 --- a/drivers/scsi/aic94xx/aic94xx.h +++ b/drivers/scsi/aic94xx/aic94xx.h @@ -94,7 +94,7 @@ void asd_dev_gone(struct domain_device *dev); void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id); -int asd_execute_task(struct sas_task *, int num, unsigned long gfp_flags); +int asd_execute_task(struct sas_task *, int num, gfp_t gfp_flags); /* ---------- TMFs ---------- */ int asd_abort_task(struct sas_task *); diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.c b/drivers/scsi/aic94xx/aic94xx_hwi.c index a2420135110..1d8c5e5f442 100644 --- a/drivers/scsi/aic94xx/aic94xx_hwi.c +++ b/drivers/scsi/aic94xx/aic94xx_hwi.c @@ -267,7 +267,7 @@ static int asd_init_dl(struct asd_ha_struct *asd_ha) /* ---------- EDB and ESCB init ---------- */ -static int asd_alloc_edbs(struct asd_ha_struct *asd_ha, unsigned int gfp_flags) +static int asd_alloc_edbs(struct asd_ha_struct *asd_ha, gfp_t gfp_flags) { struct asd_seq_data *seq = &asd_ha->seq; int i; @@ -298,7 +298,7 @@ Err_unroll: } static int asd_alloc_escbs(struct asd_ha_struct *asd_ha, - unsigned int gfp_flags) + gfp_t gfp_flags) { struct asd_seq_data *seq = &asd_ha->seq; struct asd_ascb *escb; @@ -1028,7 +1028,7 @@ irqreturn_t asd_hw_isr(int irq, void *dev_id, struct pt_regs *regs) /* ---------- SCB handling ---------- */ static inline struct asd_ascb *asd_ascb_alloc(struct asd_ha_struct *asd_ha, - unsigned int gfp_flags) + gfp_t gfp_flags) { extern kmem_cache_t *asd_ascb_cache; struct asd_seq_data *seq = &asd_ha->seq; @@ -1086,7 +1086,7 @@ undo: */ struct asd_ascb *asd_ascb_alloc_list(struct asd_ha_struct *asd_ha, int *num, - unsigned int gfp_flags) + gfp_t gfp_flags) { struct asd_ascb *first = NULL; diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.h b/drivers/scsi/aic94xx/aic94xx_hwi.h index c7d505388fe..8498144aa5e 100644 --- a/drivers/scsi/aic94xx/aic94xx_hwi.h +++ b/drivers/scsi/aic94xx/aic94xx_hwi.h @@ -242,7 +242,7 @@ struct asd_ha_struct { /* ---------- DMA allocs ---------- */ -static inline struct asd_dma_tok *asd_dmatok_alloc(unsigned int flags) +static inline struct asd_dma_tok *asd_dmatok_alloc(gfp_t flags) { return kmem_cache_alloc(asd_dma_token_cache, flags); } @@ -254,7 +254,7 @@ static inline void asd_dmatok_free(struct asd_dma_tok *token) static inline struct asd_dma_tok *asd_alloc_coherent(struct asd_ha_struct * asd_ha, size_t size, - unsigned int flags) + gfp_t flags) { struct asd_dma_tok *token = asd_dmatok_alloc(flags); if (token) { @@ -376,7 +376,7 @@ irqreturn_t asd_hw_isr(int irq, void *dev_id, struct pt_regs *regs); struct asd_ascb *asd_ascb_alloc_list(struct asd_ha_struct *asd_ha, int *num, - unsigned int gfp_mask); + gfp_t gfp_mask); int asd_post_ascb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb, int num); diff --git a/drivers/scsi/aic94xx/aic94xx_task.c b/drivers/scsi/aic94xx/aic94xx_task.c index 285e70dae93..d202ed5a670 100644 --- a/drivers/scsi/aic94xx/aic94xx_task.c +++ b/drivers/scsi/aic94xx/aic94xx_task.c @@ -53,7 +53,7 @@ static const u8 data_dir_flags[] = { static inline int asd_map_scatterlist(struct sas_task *task, struct sg_el *sg_arr, - unsigned long gfp_flags) + gfp_t gfp_flags) { struct asd_ascb *ascb = task->lldd_task; struct asd_ha_struct *asd_ha = ascb->ha; @@ -368,7 +368,7 @@ Again: /* ---------- ATA ---------- */ static int asd_build_ata_ascb(struct asd_ascb *ascb, struct sas_task *task, - unsigned long gfp_flags) + gfp_t gfp_flags) { struct domain_device *dev = task->dev; struct scb *scb; @@ -437,7 +437,7 @@ static void asd_unbuild_ata_ascb(struct asd_ascb *a) /* ---------- SMP ---------- */ static int asd_build_smp_ascb(struct asd_ascb *ascb, struct sas_task *task, - unsigned long gfp_flags) + gfp_t gfp_flags) { struct asd_ha_struct *asd_ha = ascb->ha; struct domain_device *dev = task->dev; @@ -487,7 +487,7 @@ static void asd_unbuild_smp_ascb(struct asd_ascb *a) /* ---------- SSP ---------- */ static int asd_build_ssp_ascb(struct asd_ascb *ascb, struct sas_task *task, - unsigned long gfp_flags) + gfp_t gfp_flags) { struct domain_device *dev = task->dev; struct scb *scb; @@ -550,7 +550,7 @@ static inline int asd_can_queue(struct asd_ha_struct *asd_ha, int num) } int asd_execute_task(struct sas_task *task, const int num, - unsigned long gfp_flags) + gfp_t gfp_flags) { int res = 0; LIST_HEAD(alist); diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 43e0e4e3693..7f9e89bcac7 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -134,7 +134,7 @@ static enum task_attribute sas_scsi_get_task_attr(struct scsi_cmnd *cmd) static struct sas_task *sas_create_task(struct scsi_cmnd *cmd, struct domain_device *dev, - unsigned long gfp_flags) + gfp_t gfp_flags) { struct sas_task *task = sas_alloc_task(gfp_flags); struct scsi_lun lun; -- cgit v1.2.3 From 355edd2e396ef919d14a605fb4e45466ee2b64d1 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 25 Sep 2006 02:57:22 +0100 Subject: [PATCH] fix idiocy in asd_init_lseq_mdp() To whoever had written that code: a) priority of >> is higher than that of & b) priority of typecast is higher than that of any binary operator c) learn the fscking C Signed-off-by: Al Viro Signed-off-by: Linus Torvalds --- drivers/scsi/aic94xx/aic94xx_seq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/aic94xx/aic94xx_seq.c b/drivers/scsi/aic94xx/aic94xx_seq.c index d9b6da5fd06..56e4b3ba6a0 100644 --- a/drivers/scsi/aic94xx/aic94xx_seq.c +++ b/drivers/scsi/aic94xx/aic94xx_seq.c @@ -764,7 +764,7 @@ static void asd_init_lseq_mdp(struct asd_ha_struct *asd_ha, int lseq) asd_write_reg_word(asd_ha, LmSEQ_FIRST_INV_SCB_SITE(lseq), (u16)last_scb_site_no+1); asd_write_reg_word(asd_ha, LmSEQ_INTEN_SAVE(lseq), - (u16) LmM0INTEN_MASK & 0xFFFF0000 >> 16); + (u16) ((LmM0INTEN_MASK & 0xFFFF0000) >> 16)); asd_write_reg_word(asd_ha, LmSEQ_INTEN_SAVE(lseq) + 2, (u16) LmM0INTEN_MASK & 0xFFFF); asd_write_reg_byte(asd_ha, LmSEQ_LINK_RST_FRM_LEN(lseq), 0); -- cgit v1.2.3