mirror of
https://github.com/torvalds/linux.git
synced 2024-12-23 11:21:33 +00:00
9dc9fd9484
This patch adds a bunch of fixes 1. Reduce sg table size to 64 (SG_MX) instead of default SG_ALL 2. clear task lists on phy down events 3. release all tasks on port deformation 4. release current task for device gone notification 5. Add sata abort handing 6. Add 10ms delay to each port reset (currently done serially and with interrupts disabled) [jejb: whitespace fixes and clean ups plus added description added dummy 94xx_clear_srs_irq function just to prevent the mismatch in the mvs_dispatch structure killing 94xx cards] Signed-off-by: Srinivas <satyasrinivasp@hcl.in> Cc: Andy Yan <ayan@marvell.com> Cc: qswang@marvell.com Cc: jfeng@marvell.com Signed-off-by: James Bottomley <James.Bottomley@suse.de>
413 lines
12 KiB
C
413 lines
12 KiB
C
/*
|
|
* Marvell 88SE64xx/88SE94xx main function head file
|
|
*
|
|
* Copyright 2007 Red Hat, Inc.
|
|
* Copyright 2008 Marvell. <kewei@marvell.com>
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#ifndef _MV_SAS_H_
|
|
#define _MV_SAS_H_
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/types.h>
|
|
#include <linux/ctype.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <scsi/libsas.h>
|
|
#include <scsi/scsi.h>
|
|
#include <scsi/scsi_tcq.h>
|
|
#include <scsi/sas_ata.h>
|
|
#include <linux/version.h>
|
|
#include "mv_defs.h"
|
|
|
|
#define DRV_NAME "mvsas"
|
|
#define DRV_VERSION "0.8.2"
|
|
#define _MV_DUMP 0
|
|
#define MVS_ID_NOT_MAPPED 0x7f
|
|
/* #define DISABLE_HOTPLUG_DMA_FIX */
|
|
// #define MAX_EXP_RUNNING_REQ 2
|
|
#define WIDE_PORT_MAX_PHY 4
|
|
#define MV_DISABLE_NCQ 0
|
|
#define mv_printk(fmt, arg ...) \
|
|
printk(KERN_DEBUG"%s %d:" fmt, __FILE__, __LINE__, ## arg)
|
|
#ifdef MV_DEBUG
|
|
#define mv_dprintk(format, arg...) \
|
|
printk(KERN_DEBUG"%s %d:" format, __FILE__, __LINE__, ## arg)
|
|
#else
|
|
#define mv_dprintk(format, arg...)
|
|
#endif
|
|
#define MV_MAX_U32 0xffffffff
|
|
|
|
extern struct mvs_tgt_initiator mvs_tgt;
|
|
extern struct mvs_info *tgt_mvi;
|
|
extern const struct mvs_dispatch mvs_64xx_dispatch;
|
|
extern const struct mvs_dispatch mvs_94xx_dispatch;
|
|
|
|
#define DEV_IS_EXPANDER(type) \
|
|
((type == EDGE_DEV) || (type == FANOUT_DEV))
|
|
|
|
#define bit(n) ((u32)1 << n)
|
|
|
|
#define for_each_phy(__lseq_mask, __mc, __lseq) \
|
|
for ((__mc) = (__lseq_mask), (__lseq) = 0; \
|
|
(__mc) != 0 ; \
|
|
(++__lseq), (__mc) >>= 1)
|
|
|
|
#define MV_INIT_DELAYED_WORK(w, f, d) INIT_DELAYED_WORK(w, f)
|
|
#define UNASSOC_D2H_FIS(id) \
|
|
((void *) mvi->rx_fis + 0x100 * id)
|
|
#define SATA_RECEIVED_FIS_LIST(reg_set) \
|
|
((void *) mvi->rx_fis + mvi->chip->fis_offs + 0x100 * reg_set)
|
|
#define SATA_RECEIVED_SDB_FIS(reg_set) \
|
|
(SATA_RECEIVED_FIS_LIST(reg_set) + 0x58)
|
|
#define SATA_RECEIVED_D2H_FIS(reg_set) \
|
|
(SATA_RECEIVED_FIS_LIST(reg_set) + 0x40)
|
|
#define SATA_RECEIVED_PIO_FIS(reg_set) \
|
|
(SATA_RECEIVED_FIS_LIST(reg_set) + 0x20)
|
|
#define SATA_RECEIVED_DMA_FIS(reg_set) \
|
|
(SATA_RECEIVED_FIS_LIST(reg_set) + 0x00)
|
|
|
|
enum dev_status {
|
|
MVS_DEV_NORMAL = 0x0,
|
|
MVS_DEV_EH = 0x1,
|
|
};
|
|
|
|
|
|
struct mvs_info;
|
|
|
|
struct mvs_dispatch {
|
|
char *name;
|
|
int (*chip_init)(struct mvs_info *mvi);
|
|
int (*spi_init)(struct mvs_info *mvi);
|
|
int (*chip_ioremap)(struct mvs_info *mvi);
|
|
void (*chip_iounmap)(struct mvs_info *mvi);
|
|
irqreturn_t (*isr)(struct mvs_info *mvi, int irq, u32 stat);
|
|
u32 (*isr_status)(struct mvs_info *mvi, int irq);
|
|
void (*interrupt_enable)(struct mvs_info *mvi);
|
|
void (*interrupt_disable)(struct mvs_info *mvi);
|
|
|
|
u32 (*read_phy_ctl)(struct mvs_info *mvi, u32 port);
|
|
void (*write_phy_ctl)(struct mvs_info *mvi, u32 port, u32 val);
|
|
|
|
u32 (*read_port_cfg_data)(struct mvs_info *mvi, u32 port);
|
|
void (*write_port_cfg_data)(struct mvs_info *mvi, u32 port, u32 val);
|
|
void (*write_port_cfg_addr)(struct mvs_info *mvi, u32 port, u32 addr);
|
|
|
|
u32 (*read_port_vsr_data)(struct mvs_info *mvi, u32 port);
|
|
void (*write_port_vsr_data)(struct mvs_info *mvi, u32 port, u32 val);
|
|
void (*write_port_vsr_addr)(struct mvs_info *mvi, u32 port, u32 addr);
|
|
|
|
u32 (*read_port_irq_stat)(struct mvs_info *mvi, u32 port);
|
|
void (*write_port_irq_stat)(struct mvs_info *mvi, u32 port, u32 val);
|
|
|
|
u32 (*read_port_irq_mask)(struct mvs_info *mvi, u32 port);
|
|
void (*write_port_irq_mask)(struct mvs_info *mvi, u32 port, u32 val);
|
|
|
|
void (*get_sas_addr)(void *buf, u32 buflen);
|
|
void (*command_active)(struct mvs_info *mvi, u32 slot_idx);
|
|
void (*clear_srs_irq)(struct mvs_info *mvi, u8 reg_set, u8 clear_all);
|
|
void (*issue_stop)(struct mvs_info *mvi, enum mvs_port_type type,
|
|
u32 tfs);
|
|
void (*start_delivery)(struct mvs_info *mvi, u32 tx);
|
|
u32 (*rx_update)(struct mvs_info *mvi);
|
|
void (*int_full)(struct mvs_info *mvi);
|
|
u8 (*assign_reg_set)(struct mvs_info *mvi, u8 *tfs);
|
|
void (*free_reg_set)(struct mvs_info *mvi, u8 *tfs);
|
|
u32 (*prd_size)(void);
|
|
u32 (*prd_count)(void);
|
|
void (*make_prd)(struct scatterlist *scatter, int nr, void *prd);
|
|
void (*detect_porttype)(struct mvs_info *mvi, int i);
|
|
int (*oob_done)(struct mvs_info *mvi, int i);
|
|
void (*fix_phy_info)(struct mvs_info *mvi, int i,
|
|
struct sas_identify_frame *id);
|
|
void (*phy_work_around)(struct mvs_info *mvi, int i);
|
|
void (*phy_set_link_rate)(struct mvs_info *mvi, u32 phy_id,
|
|
struct sas_phy_linkrates *rates);
|
|
u32 (*phy_max_link_rate)(void);
|
|
void (*phy_disable)(struct mvs_info *mvi, u32 phy_id);
|
|
void (*phy_enable)(struct mvs_info *mvi, u32 phy_id);
|
|
void (*phy_reset)(struct mvs_info *mvi, u32 phy_id, int hard);
|
|
void (*stp_reset)(struct mvs_info *mvi, u32 phy_id);
|
|
void (*clear_active_cmds)(struct mvs_info *mvi);
|
|
u32 (*spi_read_data)(struct mvs_info *mvi);
|
|
void (*spi_write_data)(struct mvs_info *mvi, u32 data);
|
|
int (*spi_buildcmd)(struct mvs_info *mvi,
|
|
u32 *dwCmd,
|
|
u8 cmd,
|
|
u8 read,
|
|
u8 length,
|
|
u32 addr
|
|
);
|
|
int (*spi_issuecmd)(struct mvs_info *mvi, u32 cmd);
|
|
int (*spi_waitdataready)(struct mvs_info *mvi, u32 timeout);
|
|
#ifndef DISABLE_HOTPLUG_DMA_FIX
|
|
void (*dma_fix)(dma_addr_t buf_dma, int buf_len, int from, void *prd);
|
|
#endif
|
|
|
|
};
|
|
|
|
struct mvs_chip_info {
|
|
u32 n_host;
|
|
u32 n_phy;
|
|
u32 fis_offs;
|
|
u32 fis_count;
|
|
u32 srs_sz;
|
|
u32 slot_width;
|
|
const struct mvs_dispatch *dispatch;
|
|
};
|
|
#define MVS_CHIP_SLOT_SZ (1U << mvi->chip->slot_width)
|
|
#define MVS_RX_FISL_SZ \
|
|
(mvi->chip->fis_offs + (mvi->chip->fis_count * 0x100))
|
|
#define MVS_CHIP_DISP (mvi->chip->dispatch)
|
|
|
|
struct mvs_err_info {
|
|
__le32 flags;
|
|
__le32 flags2;
|
|
};
|
|
|
|
struct mvs_cmd_hdr {
|
|
__le32 flags; /* PRD tbl len; SAS, SATA ctl */
|
|
__le32 lens; /* cmd, max resp frame len */
|
|
__le32 tags; /* targ port xfer tag; tag */
|
|
__le32 data_len; /* data xfer len */
|
|
__le64 cmd_tbl; /* command table address */
|
|
__le64 open_frame; /* open addr frame address */
|
|
__le64 status_buf; /* status buffer address */
|
|
__le64 prd_tbl; /* PRD tbl address */
|
|
__le32 reserved[4];
|
|
};
|
|
|
|
struct mvs_port {
|
|
struct asd_sas_port sas_port;
|
|
u8 port_attached;
|
|
u8 wide_port_phymap;
|
|
struct list_head list;
|
|
};
|
|
|
|
struct mvs_phy {
|
|
struct mvs_info *mvi;
|
|
struct mvs_port *port;
|
|
struct asd_sas_phy sas_phy;
|
|
struct sas_identify identify;
|
|
struct scsi_device *sdev;
|
|
struct timer_list timer;
|
|
u64 dev_sas_addr;
|
|
u64 att_dev_sas_addr;
|
|
u32 att_dev_info;
|
|
u32 dev_info;
|
|
u32 phy_type;
|
|
u32 phy_status;
|
|
u32 irq_status;
|
|
u32 frame_rcvd_size;
|
|
u8 frame_rcvd[32];
|
|
u8 phy_attached;
|
|
u8 phy_mode;
|
|
u8 reserved[2];
|
|
u32 phy_event;
|
|
enum sas_linkrate minimum_linkrate;
|
|
enum sas_linkrate maximum_linkrate;
|
|
};
|
|
|
|
struct mvs_device {
|
|
struct list_head dev_entry;
|
|
enum sas_dev_type dev_type;
|
|
struct mvs_info *mvi_info;
|
|
struct domain_device *sas_device;
|
|
struct timer_list timer;
|
|
u32 attached_phy;
|
|
u32 device_id;
|
|
u32 running_req;
|
|
u8 taskfileset;
|
|
u8 dev_status;
|
|
u16 reserved;
|
|
};
|
|
|
|
struct mvs_slot_info {
|
|
struct list_head entry;
|
|
union {
|
|
struct sas_task *task;
|
|
void *tdata;
|
|
};
|
|
u32 n_elem;
|
|
u32 tx;
|
|
u32 slot_tag;
|
|
|
|
/* DMA buffer for storing cmd tbl, open addr frame, status buffer,
|
|
* and PRD table
|
|
*/
|
|
void *buf;
|
|
dma_addr_t buf_dma;
|
|
#if _MV_DUMP
|
|
u32 cmd_size;
|
|
#endif
|
|
void *response;
|
|
struct mvs_port *port;
|
|
struct mvs_device *device;
|
|
void *open_frame;
|
|
};
|
|
|
|
struct mvs_info {
|
|
unsigned long flags;
|
|
|
|
/* host-wide lock */
|
|
spinlock_t lock;
|
|
|
|
/* our device */
|
|
struct pci_dev *pdev;
|
|
struct device *dev;
|
|
|
|
/* enhanced mode registers */
|
|
void __iomem *regs;
|
|
|
|
/* peripheral or soc registers */
|
|
void __iomem *regs_ex;
|
|
u8 sas_addr[SAS_ADDR_SIZE];
|
|
|
|
/* SCSI/SAS glue */
|
|
struct sas_ha_struct *sas;
|
|
struct Scsi_Host *shost;
|
|
|
|
/* TX (delivery) DMA ring */
|
|
__le32 *tx;
|
|
dma_addr_t tx_dma;
|
|
|
|
/* cached next-producer idx */
|
|
u32 tx_prod;
|
|
|
|
/* RX (completion) DMA ring */
|
|
__le32 *rx;
|
|
dma_addr_t rx_dma;
|
|
|
|
/* RX consumer idx */
|
|
u32 rx_cons;
|
|
|
|
/* RX'd FIS area */
|
|
__le32 *rx_fis;
|
|
dma_addr_t rx_fis_dma;
|
|
|
|
/* DMA command header slots */
|
|
struct mvs_cmd_hdr *slot;
|
|
dma_addr_t slot_dma;
|
|
|
|
u32 chip_id;
|
|
const struct mvs_chip_info *chip;
|
|
|
|
int tags_num;
|
|
DECLARE_BITMAP(tags, MVS_SLOTS);
|
|
/* further per-slot information */
|
|
struct mvs_phy phy[MVS_MAX_PHYS];
|
|
struct mvs_port port[MVS_MAX_PHYS];
|
|
u32 irq;
|
|
u32 exp_req;
|
|
u32 id;
|
|
u64 sata_reg_set;
|
|
struct list_head *hba_list;
|
|
struct list_head soc_entry;
|
|
struct list_head wq_list;
|
|
unsigned long instance;
|
|
u16 flashid;
|
|
u32 flashsize;
|
|
u32 flashsectSize;
|
|
|
|
void *addon;
|
|
struct mvs_device devices[MVS_MAX_DEVICES];
|
|
#ifndef DISABLE_HOTPLUG_DMA_FIX
|
|
void *bulk_buffer;
|
|
dma_addr_t bulk_buffer_dma;
|
|
#define TRASH_BUCKET_SIZE 0x20000
|
|
#endif
|
|
struct mvs_slot_info slot_info[0];
|
|
};
|
|
|
|
struct mvs_prv_info{
|
|
u8 n_host;
|
|
u8 n_phy;
|
|
u16 reserve;
|
|
struct mvs_info *mvi[2];
|
|
};
|
|
|
|
struct mvs_wq {
|
|
struct delayed_work work_q;
|
|
struct mvs_info *mvi;
|
|
void *data;
|
|
int handler;
|
|
struct list_head entry;
|
|
};
|
|
|
|
struct mvs_task_exec_info {
|
|
struct sas_task *task;
|
|
struct mvs_cmd_hdr *hdr;
|
|
struct mvs_port *port;
|
|
u32 tag;
|
|
int n_elem;
|
|
};
|
|
|
|
|
|
/******************** function prototype *********************/
|
|
void mvs_get_sas_addr(void *buf, u32 buflen);
|
|
void mvs_tag_clear(struct mvs_info *mvi, u32 tag);
|
|
void mvs_tag_free(struct mvs_info *mvi, u32 tag);
|
|
void mvs_tag_set(struct mvs_info *mvi, unsigned int tag);
|
|
int mvs_tag_alloc(struct mvs_info *mvi, u32 *tag_out);
|
|
void mvs_tag_init(struct mvs_info *mvi);
|
|
void mvs_iounmap(void __iomem *regs);
|
|
int mvs_ioremap(struct mvs_info *mvi, int bar, int bar_ex);
|
|
void mvs_phys_reset(struct mvs_info *mvi, u32 phy_mask, int hard);
|
|
int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
|
|
void *funcdata);
|
|
void __devinit mvs_set_sas_addr(struct mvs_info *mvi, int port_id,
|
|
u32 off_lo, u32 off_hi, u64 sas_addr);
|
|
int mvs_slave_alloc(struct scsi_device *scsi_dev);
|
|
int mvs_slave_configure(struct scsi_device *sdev);
|
|
void mvs_scan_start(struct Scsi_Host *shost);
|
|
int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time);
|
|
int mvs_queue_command(struct sas_task *task, const int num,
|
|
gfp_t gfp_flags);
|
|
int mvs_abort_task(struct sas_task *task);
|
|
int mvs_abort_task_set(struct domain_device *dev, u8 *lun);
|
|
int mvs_clear_aca(struct domain_device *dev, u8 *lun);
|
|
int mvs_clear_task_set(struct domain_device *dev, u8 * lun);
|
|
void mvs_port_formed(struct asd_sas_phy *sas_phy);
|
|
void mvs_port_deformed(struct asd_sas_phy *sas_phy);
|
|
int mvs_dev_found(struct domain_device *dev);
|
|
void mvs_dev_gone(struct domain_device *dev);
|
|
int mvs_lu_reset(struct domain_device *dev, u8 *lun);
|
|
int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags);
|
|
int mvs_I_T_nexus_reset(struct domain_device *dev);
|
|
int mvs_query_task(struct sas_task *task);
|
|
void mvs_release_task(struct mvs_info *mvi,
|
|
struct domain_device *dev);
|
|
void mvs_do_release_task(struct mvs_info *mvi, int phy_no,
|
|
struct domain_device *dev);
|
|
void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events);
|
|
void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st);
|
|
int mvs_int_rx(struct mvs_info *mvi, bool self_clear);
|
|
void mvs_hexdump(u32 size, u8 *data, u32 baseaddr);
|
|
#endif
|
|
|