Files
scst/mvsas_tgt/mv_sas.c
Vladislav Bolkhovitin 6dab45204c scst: Leave out FSF mail address
This avoids that the following checkpatch complaint is triggered:

Do not include the paragraph about writing to the Free Software Foundation's
mailing address from the sample GPL notice. The FSF has changed addresses in
the past, and may do so again. Linux already includes a copy of the GPL.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>



git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@5572 d57e44dd-8a1f-0410-8b47-8ef2f437770f
2014-06-06 03:24:03 +00:00

2329 lines
60 KiB
C

/*
* Marvell 88SE64xx/88SE94xx main function
*
* 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.
*
* Changelog:
* - Praveen Murali <pmurali@logicube.com> May 15, 2012
* Cleanup and prepare the FIS index before issuing the ATA command
* (during prep: mvs_task_prep_ata). This is to overcome the drive
* detection issue where the SATA drives fail to get detected during
* hotplug since the ATA module (libATA) detects errors set in the FIS
* even though the SATA analyzer shows that the IDENTIFY command was
* successful.
*/
#include "mv_sas.h"
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
#include "sas_task.c"
#endif
static int mvs_find_tag(struct mvs_info *mvi, struct sas_task *task, u32 *tag)
{
if (task->lldd_task) {
struct mvs_slot_info *slot;
slot = task->lldd_task;
*tag = slot->slot_tag;
return 1;
}
return 0;
}
void mvs_tag_clear(struct mvs_info *mvi, u32 tag)
{
void *bitmap = mvi->tags;
clear_bit(tag, bitmap);
}
void mvs_tag_free(struct mvs_info *mvi, u32 tag)
{
mvs_tag_clear(mvi, tag);
}
void mvs_tag_set(struct mvs_info *mvi, unsigned int tag)
{
void *bitmap = mvi->tags;
set_bit(tag, bitmap);
}
inline int mvs_tag_alloc(struct mvs_info *mvi, u32 *tag_out)
{
unsigned int index, tag;
void *bitmap = mvi->tags;
index = find_first_zero_bit(bitmap, mvi->tags_num);
tag = index;
if (tag >= mvi->tags_num)
return -SAS_QUEUE_FULL;
mvs_tag_set(mvi, tag);
*tag_out = tag;
return 0;
}
void mvs_tag_init(struct mvs_info *mvi)
{
int i;
for (i = 0; i < mvi->tags_num; ++i)
mvs_tag_clear(mvi, i);
}
void mvs_hexdump(u32 size, u8 *data, u32 baseaddr)
{
u32 i;
u32 run;
u32 offset;
offset = 0;
while (size) {
printk(KERN_DEBUG"%08X : ", baseaddr + offset);
if (size >= 16)
run = 16;
else
run = size;
size -= run;
for (i = 0; i < 16; i++) {
if (i < run)
printk(KERN_DEBUG"%02X ", (u32)data[i]);
else
printk(KERN_DEBUG" ");
}
printk(KERN_DEBUG": ");
for (i = 0; i < run; i++)
printk(KERN_DEBUG"%c",
isalnum(data[i]) ? data[i] : '.');
printk(KERN_DEBUG"\n");
data = &data[16];
offset += run;
}
printk(KERN_DEBUG"\n");
}
#if (_MV_DUMP > 1)
static void mvs_hba_sb_dump(struct mvs_info *mvi, u32 tag,
enum sas_protocol proto)
{
u32 offset;
struct mvs_slot_info *slot = &mvi->slot_info[tag];
offset = slot->cmd_size + MVS_OAF_SZ +
MVS_CHIP_DISP->prd_size() * slot->n_elem;
dev_printk(KERN_DEBUG, mvi->dev, "+---->Status buffer[%d] :\n",
tag);
mvs_hexdump(32, (u8 *) slot->response,
(u32) slot->buf_dma + offset);
}
#endif
static void mvs_hba_memory_dump(struct mvs_info *mvi, u32 tag,
enum sas_protocol proto)
{
#if (_MV_DUMP > 1)
u32 sz, w_ptr;
u64 addr;
struct mvs_slot_info *slot = &mvi->slot_info[tag];
/*Delivery Queue */
sz = MVS_CHIP_SLOT_SZ;
w_ptr = slot->tx;
addr = mvi->tx_dma;
dev_printk(KERN_DEBUG, mvi->dev,
"Delivery Queue Size=%04d , WRT_PTR=%04X\n", sz, w_ptr);
dev_printk(KERN_DEBUG, mvi->dev,
"Delivery Queue Base Address=0x%llX (PA)"
"(tx_dma=0x%llX), Entry=%04d\n",
addr, (unsigned long long)mvi->tx_dma, w_ptr);
mvs_hexdump(sizeof(u32), (u8 *)(&mvi->tx[mvi->tx_prod]),
(u32) mvi->tx_dma + sizeof(u32) * w_ptr);
/*Command List */
addr = mvi->slot_dma;
dev_printk(KERN_DEBUG, mvi->dev,
"Command List Base Address=0x%llX (PA)"
"(slot_dma=0x%llX), Header=%03d\n",
addr, (unsigned long long)slot->buf_dma, tag);
dev_printk(KERN_DEBUG, mvi->dev, "Command Header[%03d]:\n", tag);
/*mvs_cmd_hdr */
mvs_hexdump(sizeof(struct mvs_cmd_hdr), (u8 *)(&mvi->slot[tag]),
(u32) mvi->slot_dma + tag * sizeof(struct mvs_cmd_hdr));
/*1.command table area */
dev_printk(KERN_DEBUG, mvi->dev, "+---->Command Table :\n");
mvs_hexdump(slot->cmd_size, (u8 *) slot->buf, (u32) slot->buf_dma);
/*2.open address frame area */
dev_printk(KERN_DEBUG, mvi->dev, "+---->Open Address Frame :\n");
mvs_hexdump(MVS_OAF_SZ, (u8 *) slot->buf + slot->cmd_size,
(u32) slot->buf_dma + slot->cmd_size);
/*3.status buffer */
mvs_hba_sb_dump(mvi, tag, proto);
/*4.PRD table */
dev_printk(KERN_DEBUG, mvi->dev, "+---->PRD table :\n");
mvs_hexdump(MVS_CHIP_DISP->prd_size() * slot->n_elem,
(u8 *) slot->buf + slot->cmd_size + MVS_OAF_SZ,
(u32) slot->buf_dma + slot->cmd_size + MVS_OAF_SZ);
#endif
}
static void mvs_hba_cq_dump(struct mvs_info *mvi)
{
#if (_MV_DUMP > 2)
u64 addr;
void __iomem *regs = mvi->regs;
u32 entry = mvi->rx_cons + 1;
u32 rx_desc = le32_to_cpu(mvi->rx[entry]);
/*Completion Queue */
addr = mr32(RX_HI) << 16 << 16 | mr32(RX_LO);
dev_printk(KERN_DEBUG, mvi->dev, "Completion Task = 0x%p\n",
mvi->slot_info[rx_desc & RXQ_SLOT_MASK].task);
dev_printk(KERN_DEBUG, mvi->dev,
"Completion List Base Address=0x%llX (PA), "
"CQ_Entry=%04d, CQ_WP=0x%08X\n",
addr, entry - 1, mvi->rx[0]);
mvs_hexdump(sizeof(u32), (u8 *)(&rx_desc),
mvi->rx_dma + sizeof(u32) * entry);
#endif
}
void mvs_get_sas_addr(void *buf, u32 buflen)
{
/*memcpy(buf, "\x50\x05\x04\x30\x11\xab\x64\x40", 8);*/
}
static struct mvs_info *mvs_find_dev_mvi(struct domain_device *dev)
{
unsigned long i = 0, j = 0, hi = 0;
struct sas_ha_struct *sha = dev->port->ha;
struct mvs_info *mvi = NULL;
struct asd_sas_phy *phy;
while (sha->sas_port[i]) {
if (sha->sas_port[i] == dev->port) {
phy = container_of(sha->sas_port[i]->phy_list.next,
struct asd_sas_phy, port_phy_el);
j = 0;
while (sha->sas_phy[j]) {
if (sha->sas_phy[j] == phy)
break;
j++;
}
break;
}
i++;
}
hi = j/((struct mvs_prv_info *)sha->lldd_ha)->n_phy;
mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[hi];
return mvi;
}
/* FIXME */
static int mvs_find_dev_phyno(struct domain_device *dev, int *phyno)
{
unsigned long i = 0, j = 0, n = 0, num = 0;
struct mvs_device *mvi_dev = dev->lldd_dev;
struct mvs_info *mvi = mvi_dev->mvi_info;
struct sas_ha_struct *sha = dev->port->ha;
while (sha->sas_port[i]) {
if (sha->sas_port[i] == dev->port) {
struct asd_sas_phy *phy;
list_for_each_entry(phy,
&sha->sas_port[i]->phy_list, port_phy_el) {
j = 0;
while (sha->sas_phy[j]) {
if (sha->sas_phy[j] == phy)
break;
j++;
}
phyno[n] = (j >= mvi->chip->n_phy) ?
(j - mvi->chip->n_phy) : j;
num++;
n++;
}
break;
}
i++;
}
return num;
}
static inline void mvs_free_reg_set(struct mvs_info *mvi,
struct mvs_device *dev)
{
if (!dev) {
mv_printk("device has been free.\n");
return;
}
if (dev->running_req != 0)
return;
if (dev->taskfileset == MVS_ID_NOT_MAPPED)
return;
MVS_CHIP_DISP->free_reg_set(mvi, &dev->taskfileset);
}
static inline u8 mvs_assign_reg_set(struct mvs_info *mvi,
struct mvs_device *dev)
{
if (dev->taskfileset != MVS_ID_NOT_MAPPED)
return 0;
return MVS_CHIP_DISP->assign_reg_set(mvi, &dev->taskfileset);
}
void mvs_phys_reset(struct mvs_info *mvi, u32 phy_mask, int hard)
{
u32 no;
for_each_phy(phy_mask, phy_mask, no) {
if (!(phy_mask & 1))
continue;
MVS_CHIP_DISP->phy_reset(mvi, no, hard);
}
}
/* FIXME: locking? */
int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
void *funcdata)
{
int rc = 0, phy_id = sas_phy->id;
u32 tmp, i = 0, hi;
struct sas_ha_struct *sha = sas_phy->ha;
struct mvs_info *mvi = NULL;
while (sha->sas_phy[i]) {
if (sha->sas_phy[i] == sas_phy)
break;
i++;
}
hi = i/((struct mvs_prv_info *)sha->lldd_ha)->n_phy;
mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[hi];
switch (func) {
case PHY_FUNC_SET_LINK_RATE:
MVS_CHIP_DISP->phy_set_link_rate(mvi, phy_id, funcdata);
break;
case PHY_FUNC_HARD_RESET:
tmp = MVS_CHIP_DISP->read_phy_ctl(mvi, phy_id);
if (tmp & PHY_RST_HARD)
break;
MVS_CHIP_DISP->phy_reset(mvi, phy_id, 1);
break;
case PHY_FUNC_LINK_RESET:
MVS_CHIP_DISP->phy_enable(mvi, phy_id);
MVS_CHIP_DISP->phy_reset(mvi, phy_id, 0);
break;
case PHY_FUNC_DISABLE:
MVS_CHIP_DISP->phy_disable(mvi, phy_id);
break;
case PHY_FUNC_RELEASE_SPINUP_HOLD:
default:
rc = -EOPNOTSUPP;
}
msleep(200);
return rc;
}
void __devinit mvs_set_sas_addr(struct mvs_info *mvi, int port_id,
u32 off_lo, u32 off_hi, u64 sas_addr)
{
u32 lo = (u32)sas_addr;
u32 hi = (u32)(sas_addr>>32);
MVS_CHIP_DISP->write_port_cfg_addr(mvi, port_id, off_lo);
MVS_CHIP_DISP->write_port_cfg_data(mvi, port_id, lo);
MVS_CHIP_DISP->write_port_cfg_addr(mvi, port_id, off_hi);
MVS_CHIP_DISP->write_port_cfg_data(mvi, port_id, hi);
}
static void mvs_bytes_dmaed(struct mvs_info *mvi, int i)
{
struct mvs_phy *phy = &mvi->phy[i];
struct asd_sas_phy *sas_phy = &phy->sas_phy;
struct sas_ha_struct *sas_ha;
if (!phy->phy_attached)
return;
if (!(phy->att_dev_info & PORT_DEV_TRGT_MASK)
&& phy->phy_type & PORT_TYPE_SAS) {
return;
}
sas_ha = mvi->sas;
sas_ha->notify_phy_event(sas_phy, PHYE_OOB_DONE);
if (sas_phy->phy) {
struct sas_phy *sphy = sas_phy->phy;
sphy->negotiated_linkrate = sas_phy->linkrate;
sphy->minimum_linkrate = phy->minimum_linkrate;
sphy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
sphy->maximum_linkrate = phy->maximum_linkrate;
sphy->maximum_linkrate_hw = MVS_CHIP_DISP->phy_max_link_rate();
}
if (phy->phy_type & PORT_TYPE_SAS) {
struct sas_identify_frame *id;
id = (struct sas_identify_frame *)phy->frame_rcvd;
id->dev_type = phy->identify.device_type;
id->initiator_bits = SAS_PROTOCOL_ALL;
id->target_bits = phy->identify.target_port_protocols;
} else if (phy->phy_type & PORT_TYPE_SATA) {
/*Nothing*/
}
mv_dprintk("phy %d byte dmaded.\n", i + mvi->id * mvi->chip->n_phy);
sas_phy->frame_rcvd_size = phy->frame_rcvd_size;
mvi->sas->notify_port_event(sas_phy,
PORTE_BYTES_DMAED);
}
int mvs_slave_alloc(struct scsi_device *scsi_dev)
{
struct domain_device *dev = sdev_to_domain_dev(scsi_dev);
if (dev_is_sata(dev)) {
/* We don't need to rescan targets
* if REPORT_LUNS request is failed
*/
if (scsi_dev->lun > 0)
return -ENXIO;
scsi_dev->tagged_supported = 1;
}
return sas_slave_alloc(scsi_dev);
}
int mvs_slave_configure(struct scsi_device *sdev)
{
int ret = sas_slave_configure(sdev);
if (ret)
return ret;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) || \
defined(CONFIG_SUSE_KERNEL) || \
(defined(RHEL_MAJOR) && \
(RHEL_MAJOR -0 > 6 || \
(RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 >= 1)))
{
struct domain_device *dev = sdev_to_domain_dev(sdev);
if (!dev_is_sata(dev))
sas_change_queue_depth(sdev, MVS_QUEUE_SIZE,
SCSI_QDEPTH_DEFAULT);
}
#endif
return 0;
}
void mvs_scan_start(struct Scsi_Host *shost)
{
int i, j;
unsigned short core_nr;
struct mvs_info *mvi;
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
for (j = 0; j < core_nr; j++) {
mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[j];
for (i = 0; i < mvi->chip->n_phy; ++i)
mvs_bytes_dmaed(mvi, i);
}
}
int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time)
{
/* give the phy enabling interrupt event time to come in (1s
* is empirically about all it takes) */
if (time < HZ)
return 0;
/* Wait for discovery to finish */
scsi_flush_work(shost);
return 1;
}
static int mvs_task_prep_smp(struct mvs_info *mvi,
struct mvs_task_exec_info *tei)
{
int elem, rc, i;
struct sas_task *task = tei->task;
struct mvs_cmd_hdr *hdr = tei->hdr;
struct domain_device *dev = task->dev;
struct asd_sas_port *sas_port = dev->port;
struct scatterlist *sg_req, *sg_resp;
u32 req_len, phy_mask, resp_len, tag = tei->tag;
void *buf_tmp;
u8 *buf_oaf;
dma_addr_t buf_tmp_dma;
void *buf_prd;
struct mvs_slot_info *slot = &mvi->slot_info[tag];
u32 flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
#if _MV_DUMP
u8 *buf_cmd;
void *from;
#endif
/*
* DMA-map SMP request, response buffers
*/
sg_req = &task->smp_task.smp_req;
elem = dma_map_sg(mvi->dev, sg_req, 1, PCI_DMA_TODEVICE);
if (!elem)
return -ENOMEM;
req_len = sg_dma_len(sg_req);
sg_resp = &task->smp_task.smp_resp;
elem = dma_map_sg(mvi->dev, sg_resp, 1, PCI_DMA_FROMDEVICE);
if (!elem) {
rc = -ENOMEM;
goto err_out;
}
resp_len = SB_RFB_MAX;
/* must be in dwords */
if ((req_len & 0x3) || (resp_len & 0x3)) {
rc = -EINVAL;
goto err_out_2;
}
/*
* arrange MVS_SLOT_BUF_SZ-sized DMA buffer according to our needs
*/
/* region 1: command table area (MVS_SSP_CMD_SZ bytes) ***** */
buf_tmp = slot->buf;
buf_tmp_dma = slot->buf_dma;
#if _MV_DUMP
buf_cmd = buf_tmp;
hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma);
buf_tmp += req_len;
buf_tmp_dma += req_len;
slot->cmd_size = req_len;
#else
hdr->cmd_tbl = cpu_to_le64(sg_dma_address(sg_req));
#endif
/* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
buf_oaf = buf_tmp;
hdr->open_frame = cpu_to_le64(buf_tmp_dma);
buf_tmp += MVS_OAF_SZ;
buf_tmp_dma += MVS_OAF_SZ;
/* region 3: PRD table *********************************** */
buf_prd = buf_tmp;
if (tei->n_elem)
hdr->prd_tbl = cpu_to_le64(buf_tmp_dma);
else
hdr->prd_tbl = 0;
i = MVS_CHIP_DISP->prd_size() * tei->n_elem;
buf_tmp += i;
buf_tmp_dma += i;
/* region 4: status buffer (larger the PRD, smaller this buf) ****** */
slot->response = buf_tmp;
hdr->status_buf = cpu_to_le64(buf_tmp_dma);
if (mvi->flags & MVF_FLAG_SOC)
hdr->reserved[0] = 0;
/*
* Fill in TX ring and command slot header
*/
slot->tx = mvi->tx_prod;
phy_mask = sas_port->phy_mask;
mvi->tx[mvi->tx_prod] = cpu_to_le32((TXQ_CMD_SMP << TXQ_CMD_SHIFT) |
TXQ_MODE_I | tag |
(phy_mask << TXQ_PHY_SHIFT));
hdr->flags |= flags;
hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | ((req_len - 4) / 4));
hdr->tags = cpu_to_le32(tag);
hdr->data_len = 0;
/* generate open address frame hdr (first 12 bytes) */
/* initiator, SMP, ftype 1h */
buf_oaf[0] = (1 << 7) | (PROTOCOL_SMP << 4) | 0x01;
buf_oaf[1] = dev->linkrate & 0xf;
*(u16 *)(buf_oaf + 2) = 0xFFFF; /* SAS SPEC */
memcpy(buf_oaf + 4, dev->sas_addr, SAS_ADDR_SIZE);
/* fill in PRD (scatter/gather) table, if any */
MVS_CHIP_DISP->make_prd(task->scatter, tei->n_elem, buf_prd);
#if _MV_DUMP
/* copy cmd table */
from = kmap_atomic(sg_page(sg_req), KM_IRQ0);
memcpy(buf_cmd, from + sg_req->offset, req_len);
kunmap_atomic(from, KM_IRQ0);
#endif
return 0;
err_out_2:
dma_unmap_sg(mvi->dev, &tei->task->smp_task.smp_resp, 1,
PCI_DMA_FROMDEVICE);
err_out:
dma_unmap_sg(mvi->dev, &tei->task->smp_task.smp_req, 1,
PCI_DMA_TODEVICE);
return rc;
}
static u32 mvs_get_ncq_tag(struct sas_task *task, u32 *tag)
{
struct ata_queued_cmd *qc = task->uldd_task;
if (qc) {
if (qc->tf.command == ATA_CMD_FPDMA_WRITE ||
qc->tf.command == ATA_CMD_FPDMA_READ) {
*tag = qc->tag;
return 1;
}
}
return 0;
}
static int mvs_task_prep_ata(struct mvs_info *mvi,
struct mvs_task_exec_info *tei)
{
struct sas_task *task = tei->task;
struct domain_device *dev = task->dev;
struct mvs_device *mvi_dev = dev->lldd_dev;
struct mvs_cmd_hdr *hdr = tei->hdr;
struct asd_sas_port *sas_port = dev->port;
struct mvs_slot_info *slot;
void *buf_prd;
u32 tag = tei->tag, hdr_tag;
u32 flags, del_q, phy_mask;
void *buf_tmp;
u8 *buf_cmd, *buf_oaf;
dma_addr_t buf_tmp_dma;
u32 i, req_len, resp_len;
const u32 max_resp_len = SB_RFB_MAX;
if (mvs_assign_reg_set(mvi, mvi_dev) == MVS_ID_NOT_MAPPED) {
mv_dprintk("Have not enough regiset for dev %d.\n",
mvi_dev->device_id);
return -EBUSY;
}
/* cleanup and prepare the allocated FIS index */
memset(SATA_RECEIVED_D2H_FIS(mvi_dev->taskfileset), 0,
sizeof(struct dev_to_host_fis));
slot = &mvi->slot_info[tag];
slot->tx = mvi->tx_prod;
phy_mask = sas_port->phy_mask;
del_q = TXQ_MODE_I | tag |
(TXQ_CMD_STP << TXQ_CMD_SHIFT) |
(phy_mask << TXQ_PHY_SHIFT) |
(mvi_dev->taskfileset << TXQ_SRS_SHIFT);
mvi->tx[mvi->tx_prod] = cpu_to_le32(del_q);
#ifndef DISABLE_HOTPLUG_DMA_FIX
if (task->data_dir == DMA_FROM_DEVICE)
flags = (MVS_CHIP_DISP->prd_count() << MCH_PRD_LEN_SHIFT);
else
flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
#else
flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
#endif
if (task->ata_task.use_ncq)
flags |= MCH_FPDMA;
if (dev->sata_dev.command_set == ATAPI_COMMAND_SET) {
if (task->ata_task.fis.command != ATA_CMD_ID_ATAPI)
flags |= MCH_ATAPI;
}
/* FIXME: fill in port multiplier number */
hdr->flags = cpu_to_le32(flags);
/* FIXME: the low order order 5 bits for the TAG if enable NCQ */
if (task->ata_task.use_ncq && mvs_get_ncq_tag(task, &hdr_tag))
task->ata_task.fis.sector_count |= (u8) (hdr_tag << 3);
else
hdr_tag = tag;
hdr->tags = cpu_to_le32(hdr_tag);
hdr->data_len = cpu_to_le32(task->total_xfer_len);
/*
* arrange MVS_SLOT_BUF_SZ-sized DMA buffer according to our needs
*/
/* region 1: command table area (MVS_ATA_CMD_SZ bytes) ************** */
buf_cmd = buf_tmp = slot->buf;
buf_tmp_dma = slot->buf_dma;
hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma);
buf_tmp += MVS_ATA_CMD_SZ;
buf_tmp_dma += MVS_ATA_CMD_SZ;
#if _MV_DUMP
slot->cmd_size = MVS_ATA_CMD_SZ;
#endif
/* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
/* used for STP. unused for SATA? */
buf_oaf = buf_tmp;
hdr->open_frame = cpu_to_le64(buf_tmp_dma);
buf_tmp += MVS_OAF_SZ;
buf_tmp_dma += MVS_OAF_SZ;
/* region 3: PRD table ********************************************* */
buf_prd = buf_tmp;
if (tei->n_elem)
hdr->prd_tbl = cpu_to_le64(buf_tmp_dma);
else
hdr->prd_tbl = 0;
i = MVS_CHIP_DISP->prd_size() * MVS_CHIP_DISP->prd_count();
buf_tmp += i;
buf_tmp_dma += i;
/* region 4: status buffer (larger the PRD, smaller this buf) ****** */
/* FIXME: probably unused, for SATA. kept here just in case
* we get a STP/SATA error information record
*/
slot->response = buf_tmp;
hdr->status_buf = cpu_to_le64(buf_tmp_dma);
if (mvi->flags & MVF_FLAG_SOC)
hdr->reserved[0] = 0;
req_len = sizeof(struct host_to_dev_fis);
resp_len = MVS_SLOT_BUF_SZ - MVS_ATA_CMD_SZ -
sizeof(struct mvs_err_info) - i;
/* request, response lengths */
resp_len = min(resp_len, max_resp_len);
hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | (req_len / 4));
if (likely(!task->ata_task.device_control_reg_update))
task->ata_task.fis.flags |= 0x80; /* C=1: update ATA cmd reg */
/* fill in command FIS and ATAPI CDB */
memcpy(buf_cmd, &task->ata_task.fis, sizeof(struct host_to_dev_fis));
if (dev->sata_dev.command_set == ATAPI_COMMAND_SET)
memcpy(buf_cmd + STP_ATAPI_CMD,
task->ata_task.atapi_packet, 16);
/* generate open address frame hdr (first 12 bytes) */
/* initiator, STP, ftype 1h */
buf_oaf[0] = (1 << 7) | (PROTOCOL_STP << 4) | 0x1;
buf_oaf[1] = dev->linkrate & 0xf;
*(u16 *)(buf_oaf + 2) = cpu_to_be16(mvi_dev->device_id + 1);
memcpy(buf_oaf + 4, dev->sas_addr, SAS_ADDR_SIZE);
/* fill in PRD (scatter/gather) table, if any */
MVS_CHIP_DISP->make_prd(task->scatter, tei->n_elem, buf_prd);
#ifndef DISABLE_HOTPLUG_DMA_FIX
if (task->data_dir == DMA_FROM_DEVICE)
MVS_CHIP_DISP->dma_fix(mvi->bulk_buffer_dma,
TRASH_BUCKET_SIZE, tei->n_elem, buf_prd);
#endif
return 0;
}
static int mvs_task_prep_ssp(struct mvs_info *mvi,
struct mvs_task_exec_info *tei, int is_tmf,
struct mvs_tmf_task *tmf)
{
struct sas_task *task = tei->task;
struct mvs_cmd_hdr *hdr = tei->hdr;
struct mvs_port *port = tei->port;
struct domain_device *dev = task->dev;
struct mvs_device *mvi_dev = dev->lldd_dev;
struct asd_sas_port *sas_port = dev->port;
struct mvs_slot_info *slot;
void *buf_prd;
struct ssp_frame_hdr *ssp_hdr;
void *buf_tmp;
u8 *buf_cmd, *buf_oaf, fburst = 0;
dma_addr_t buf_tmp_dma;
u32 flags;
u32 resp_len, req_len, i, tag = tei->tag;
const u32 max_resp_len = SB_RFB_MAX;
u32 phy_mask;
slot = &mvi->slot_info[tag];
slot->tx = mvi->tx_prod;
phy_mask = ((port->wide_port_phymap) ? port->wide_port_phymap :
sas_port->phy_mask) & TXQ_PHY_MASK;
mvi->tx[mvi->tx_prod] = cpu_to_le32(TXQ_MODE_I | tag |
(TXQ_CMD_SSP << TXQ_CMD_SHIFT) |
(phy_mask << TXQ_PHY_SHIFT));
flags = MCH_RETRY;
if (task->ssp_task.enable_first_burst) {
flags |= MCH_FBURST;
fburst = (1 << 7);
}
if (is_tmf)
flags |= (MCH_SSP_FR_TASK << MCH_SSP_FR_TYPE_SHIFT);
hdr->flags = cpu_to_le32(flags | (tei->n_elem << MCH_PRD_LEN_SHIFT));
hdr->tags = cpu_to_le32(tag);
hdr->data_len = cpu_to_le32(task->total_xfer_len);
/*
* arrange MVS_SLOT_BUF_SZ-sized DMA buffer according to our needs
*/
/* region 1: command table area (MVS_SSP_CMD_SZ bytes) ************** */
buf_cmd = buf_tmp = slot->buf;
buf_tmp_dma = slot->buf_dma;
hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma);
buf_tmp += MVS_SSP_CMD_SZ;
buf_tmp_dma += MVS_SSP_CMD_SZ;
#if _MV_DUMP
slot->cmd_size = MVS_SSP_CMD_SZ;
#endif
/* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
buf_oaf = buf_tmp;
hdr->open_frame = cpu_to_le64(buf_tmp_dma);
buf_tmp += MVS_OAF_SZ;
buf_tmp_dma += MVS_OAF_SZ;
/* region 3: PRD table ********************************************* */
buf_prd = buf_tmp;
if (tei->n_elem)
hdr->prd_tbl = cpu_to_le64(buf_tmp_dma);
else
hdr->prd_tbl = 0;
i = MVS_CHIP_DISP->prd_size() * tei->n_elem;
buf_tmp += i;
buf_tmp_dma += i;
/* region 4: status buffer (larger the PRD, smaller this buf) ****** */
slot->response = buf_tmp;
hdr->status_buf = cpu_to_le64(buf_tmp_dma);
if (mvi->flags & MVF_FLAG_SOC)
hdr->reserved[0] = 0;
resp_len = MVS_SLOT_BUF_SZ - MVS_SSP_CMD_SZ - MVS_OAF_SZ -
sizeof(struct mvs_err_info) - i;
resp_len = min(resp_len, max_resp_len);
req_len = sizeof(struct ssp_frame_hdr) + 28;
/* request, response lengths */
hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | (req_len / 4));
/* generate open address frame hdr (first 12 bytes) */
/* initiator, SSP, ftype 1h */
buf_oaf[0] = (1 << 7) | (PROTOCOL_SSP << 4) | 0x1;
buf_oaf[1] = dev->linkrate & 0xf;
*(u16 *)(buf_oaf + 2) = cpu_to_be16(mvi_dev->device_id + 1);
memcpy(buf_oaf + 4, dev->sas_addr, SAS_ADDR_SIZE);
/* fill in SSP frame header (Command Table.SSP frame header) */
ssp_hdr = (struct ssp_frame_hdr *)buf_cmd;
memcpy(ssp_hdr->hashed_dest_addr, dev->hashed_sas_addr,
HASHED_SAS_ADDR_SIZE);
memcpy(ssp_hdr->hashed_src_addr,
dev->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
ssp_hdr->tag = cpu_to_be16(tag);
if (is_tmf)
ssp_hdr->frame_type = SSP_TASK;
else
ssp_hdr->frame_type = SSP_COMMAND;
/* fill in IU for TASK and Command Frame */
buf_cmd += sizeof(*ssp_hdr);
memcpy(buf_cmd, &task->ssp_task.LUN, 8);
if (ssp_hdr->frame_type != SSP_TASK) {
buf_cmd[9] = fburst | task->ssp_task.task_attr |
(task->ssp_task.task_prio << 3);
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0)
/*
* See also patch "libsas: implement > 16 byte CDB support"
* (commit ID e73823f7a2c921dcf068d34ea03bd682498d9e42).
*/
memcpy(buf_cmd + 12, &task->ssp_task.cdb, 16);
#else
memcpy(buf_cmd + 12, &task->ssp_task.cmd->request->cmd, 16);
#endif
} else {
buf_cmd[10] = tmf->tmf;
switch (tmf->tmf) {
case TMF_ABORT_TASK:
case TMF_QUERY_TASK:
buf_cmd[12] =
(tmf->tag_of_task_to_be_managed >> 8) & 0xff;
buf_cmd[13] =
tmf->tag_of_task_to_be_managed & 0xff;
break;
default:
break;
}
}
/* fill in PRD (scatter/gather) table, if any */
MVS_CHIP_DISP->make_prd(task->scatter, tei->n_elem, buf_prd);
return 0;
}
#define DEV_IS_GONE(mvi_dev) ((!mvi_dev || (mvi_dev->dev_type == SAS_PHY_UNUSED)))
static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags,
struct completion *completion, int is_tmf,
struct mvs_tmf_task *tmf)
{
struct domain_device *dev = task->dev;
struct mvs_device *mvi_dev = dev->lldd_dev;
struct mvs_info *mvi = mvi_dev->mvi_info;
struct mvs_task_exec_info tei;
struct sas_task *t = task;
struct mvs_slot_info *slot;
u32 tag = 0xdeadbeef, rc, n_elem = 0;
u32 n = num, pass = 0;
unsigned long flags = 0;
if (!dev->port) {
struct task_status_struct *tsm = &t->task_status;
tsm->resp = SAS_TASK_UNDELIVERED;
tsm->stat = SAS_PHY_DOWN;
/*
* libsas will use dev->port, should
* not call task_done for sata
*/
if (dev->dev_type != SAS_SATA_DEV)
t->task_done(t);
return 0;
}
spin_lock_irqsave(&mvi->lock, flags);
do {
dev = t->dev;
mvi_dev = dev->lldd_dev;
if (DEV_IS_GONE(mvi_dev)) {
if (mvi_dev)
mv_dprintk("device %d not ready.\n",
mvi_dev->device_id);
else
mv_dprintk("device %016llx not ready.\n",
SAS_ADDR(dev->sas_addr));
rc = SAS_PHY_DOWN;
goto out_done;
}
if (dev->port->id >= mvi->chip->n_phy)
tei.port = &mvi->port[dev->port->id - mvi->chip->n_phy];
else
tei.port = &mvi->port[dev->port->id];
if (!tei.port->port_attached) {
if (sas_protocol_ata(t->task_proto)) {
mv_dprintk("port %d does not"
" attach device.\n", dev->port->id);
rc = SAS_PHY_DOWN;
goto out_done;
} else {
struct task_status_struct *ts = &t->task_status;
ts->resp = SAS_TASK_UNDELIVERED;
ts->stat = SAS_PHY_DOWN;
t->task_done(t);
if (n > 1)
t = list_entry(t->list.next,
struct sas_task, list);
continue;
}
}
if (!sas_protocol_ata(t->task_proto)) {
if (t->num_scatter) {
n_elem = dma_map_sg(mvi->dev,
t->scatter,
t->num_scatter,
t->data_dir);
if (!n_elem) {
rc = -ENOMEM;
goto err_out;
}
}
} else {
n_elem = t->num_scatter;
}
rc = mvs_tag_alloc(mvi, &tag);
if (rc)
goto err_out;
slot = &mvi->slot_info[tag];
t->lldd_task = NULL;
slot->n_elem = n_elem;
slot->slot_tag = tag;
memset(slot->buf, 0, MVS_SLOT_BUF_SZ);
tei.task = t;
tei.hdr = &mvi->slot[tag];
tei.tag = tag;
tei.n_elem = n_elem;
switch (t->task_proto) {
case SAS_PROTOCOL_SMP:
rc = mvs_task_prep_smp(mvi, &tei);
break;
case SAS_PROTOCOL_SSP:
rc = mvs_task_prep_ssp(mvi, &tei, is_tmf, tmf);
break;
case SAS_PROTOCOL_SATA:
case SAS_PROTOCOL_STP:
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
rc = mvs_task_prep_ata(mvi, &tei);
break;
default:
dev_printk(KERN_ERR, mvi->dev,
"unknown sas_task proto: 0x%x\n",
t->task_proto);
rc = -EINVAL;
break;
}
if (rc) {
mv_dprintk("rc is %x\n", rc);
goto err_out_tag;
}
slot->task = t;
slot->port = tei.port;
t->lldd_task = slot;
list_add_tail(&slot->entry, &tei.port->list);
/* TODO: select normal or high priority */
spin_lock(&t->task_state_lock);
t->task_state_flags |= SAS_TASK_AT_INITIATOR;
spin_unlock(&t->task_state_lock);
mvs_hba_memory_dump(mvi, tag, t->task_proto);
mvi_dev->running_req++;
++pass;
mvi->tx_prod = (mvi->tx_prod + 1) & (MVS_CHIP_SLOT_SZ - 1);
if (n > 1)
t = list_entry(t->list.next, struct sas_task, list);
} while (--n);
rc = 0;
goto out_done;
err_out_tag:
mvs_tag_free(mvi, tag);
err_out:
dev_printk(KERN_ERR, mvi->dev, "mvsas exec failed[%d]!\n", rc);
if (!sas_protocol_ata(t->task_proto))
if (n_elem)
dma_unmap_sg(mvi->dev, t->scatter, n_elem,
t->data_dir);
out_done:
if (likely(pass)) {
MVS_CHIP_DISP->start_delivery(mvi,
(mvi->tx_prod - 1) & (MVS_CHIP_SLOT_SZ - 1));
}
spin_unlock_irqrestore(&mvi->lock, flags);
return rc;
}
int mvs_queue_command(struct sas_task *task, const int num,
gfp_t gfp_flags)
{
return mvs_task_exec(task, num, gfp_flags, NULL, 0, NULL);
}
static void mvs_slot_free(struct mvs_info *mvi, u32 rx_desc)
{
u32 slot_idx = rx_desc & RXQ_SLOT_MASK;
mvs_tag_clear(mvi, slot_idx);
}
static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task,
struct mvs_slot_info *slot, u32 slot_idx)
{
if (!slot->task)
return;
if (!sas_protocol_ata(task->task_proto))
if (slot->n_elem)
dma_unmap_sg(mvi->dev, task->scatter,
slot->n_elem, task->data_dir);
switch (task->task_proto) {
case SAS_PROTOCOL_SMP:
dma_unmap_sg(mvi->dev, &task->smp_task.smp_resp, 1,
PCI_DMA_FROMDEVICE);
dma_unmap_sg(mvi->dev, &task->smp_task.smp_req, 1,
PCI_DMA_TODEVICE);
break;
case SAS_PROTOCOL_SATA:
case SAS_PROTOCOL_STP:
case SAS_PROTOCOL_SSP:
default:
/* do nothing */
break;
}
list_del_init(&slot->entry);
task->lldd_task = NULL;
slot->task = NULL;
slot->port = NULL;
slot->slot_tag = 0xFFFFFFFF;
mvs_slot_free(mvi, slot_idx);
}
static void mvs_update_wideport(struct mvs_info *mvi, int phy_no)
{
struct mvs_phy *phy = &mvi->phy[phy_no];
struct mvs_port *port = phy->port;
int j, no;
for_each_phy(port->wide_port_phymap, j, no) {
if (j & 1) {
MVS_CHIP_DISP->write_port_cfg_addr(mvi, no,
PHYR_WIDE_PORT);
MVS_CHIP_DISP->write_port_cfg_data(mvi, no,
port->wide_port_phymap);
} else {
MVS_CHIP_DISP->write_port_cfg_addr(mvi, no,
PHYR_WIDE_PORT);
MVS_CHIP_DISP->write_port_cfg_data(mvi, no,
0);
}
}
}
static u32 mvs_is_phy_ready(struct mvs_info *mvi, int i)
{
u32 tmp;
struct mvs_phy *phy = &mvi->phy[i];
struct mvs_port *port = phy->port;
tmp = MVS_CHIP_DISP->read_phy_ctl(mvi, i);
if ((tmp & PHY_READY_MASK) && !(phy->irq_status & PHYEV_POOF)) {
if (!port)
phy->phy_attached = 1;
return tmp;
}
#if 0/*def SUPPORT_TARGET*/
if ((phy->irq_status & (PHYEV_ID_DONE | PHYEV_RDY_CH))
&& (tmp & PHY_READY_MASK)) {
mv_dprintk("phy[%d] reset and check device.\n", i);
if (!port)
phy->phy_attached = 1;
return tmp;
}
#endif
if (port) {
if (phy->phy_type & PORT_TYPE_SAS) {
port->wide_port_phymap &= ~(1U << i);
if (!port->wide_port_phymap)
port->port_attached = 0;
mvs_update_wideport(mvi, i);
} else if (phy->phy_type & PORT_TYPE_SATA)
port->port_attached = 0;
phy->port = NULL;
phy->phy_attached = 0;
phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
}
return 0;
}
static void *mvs_get_d2h_reg(struct mvs_info *mvi, int i, void *buf)
{
u32 *s = (u32 *) buf;
if (!s)
return NULL;
MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG3);
s[3] = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, i));
MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG2);
s[2] = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, i));
MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG1);
s[1] = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, i));
MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG0);
s[0] = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, i));
/* Workaround: take some ATAPI devices for ATA */
if (((s[1] & 0x00FFFFFF) == 0x00EB1401) && (*(u8 *)&s[3] == 0x01))
s[1] = 0x00EB1401 | (*((u8 *)&s[1] + 3) & 0x10);
return s;
}
static u32 mvs_is_sig_fis_received(u32 irq_status)
{
return irq_status & PHYEV_SIG_FIS;
}
void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st)
{
struct mvs_phy *phy = &mvi->phy[i];
struct sas_identify_frame *id;
id = (struct sas_identify_frame *)phy->frame_rcvd;
if (get_st) {
phy->irq_status = MVS_CHIP_DISP->read_port_irq_stat(mvi, i);
phy->phy_status = mvs_is_phy_ready(mvi, i);
}
if (phy->phy_status) {
int oob_done = 0;
struct asd_sas_phy *sas_phy = &mvi->phy[i].sas_phy;
oob_done = MVS_CHIP_DISP->oob_done(mvi, i);
MVS_CHIP_DISP->fix_phy_info(mvi, i, id);
if (phy->phy_type & PORT_TYPE_SATA) {
phy->identify.target_port_protocols = SAS_PROTOCOL_STP;
if (mvs_is_sig_fis_received(phy->irq_status)) {
phy->phy_attached = 1;
phy->att_dev_sas_addr =
i + mvi->id * mvi->chip->n_phy;
if (oob_done)
sas_phy->oob_mode = SATA_OOB_MODE;
phy->frame_rcvd_size =
sizeof(struct dev_to_host_fis);
mvs_get_d2h_reg(mvi, i, id);
} else {
u32 tmp;
dev_printk(KERN_DEBUG, mvi->dev,
"Phy%d : No sig fis\n", i);
tmp = MVS_CHIP_DISP->read_port_irq_mask(mvi, i);
MVS_CHIP_DISP->write_port_irq_mask(mvi, i,
tmp | PHYEV_SIG_FIS);
phy->phy_attached = 0;
phy->phy_type &= ~PORT_TYPE_SATA;
MVS_CHIP_DISP->phy_reset(mvi, i, 0);
goto out_done;
}
} else if (phy->phy_type & PORT_TYPE_SAS
|| phy->att_dev_info & PORT_SSP_INIT_MASK) {
phy->phy_attached = 1;
phy->identify.device_type =
phy->att_dev_info & PORT_DEV_TYPE_MASK;
if (phy->identify.device_type == SAS_END_DEVICE)
phy->identify.target_port_protocols =
SAS_PROTOCOL_SSP;
else if (phy->identify.device_type != SAS_PHY_UNUSED)
phy->identify.target_port_protocols =
SAS_PROTOCOL_SMP;
if (oob_done)
sas_phy->oob_mode = SAS_OOB_MODE;
phy->frame_rcvd_size =
sizeof(struct sas_identify_frame);
#ifdef SUPPORT_TARGET
mvi->tgt_port[i].port_attr = mvst_check_port(mvi, i);
mv_dprintk("get port %d attr %x\n", i,
mvi->tgt_port[i].port_attr);
if (mvi->tgt_port[i].port_attr == MVST_TGT_PORT
|| mvi->tgt_port[i].port_attr ==
MVST_INIT_TGT_PORT) {
mv_dprintk("port %d is to be tgt port.\n", i);
mvi->tgt_port[i].port_attached = 1;
mvst_update_wideport(mvi, i);
}
#endif
}
memcpy(sas_phy->attached_sas_addr,
&phy->att_dev_sas_addr, SAS_ADDR_SIZE);
if (MVS_CHIP_DISP->phy_work_around)
MVS_CHIP_DISP->phy_work_around(mvi, i);
}
mv_dprintk("port %d attach dev info is %x\n",
i + mvi->id * mvi->chip->n_phy, phy->att_dev_info);
mv_dprintk("port %d attach sas addr is %llx\n",
i + mvi->id * mvi->chip->n_phy, phy->att_dev_sas_addr);
out_done:
if (get_st)
MVS_CHIP_DISP->write_port_irq_stat(mvi, i, phy->irq_status);
}
static void mvs_port_notify_formed(struct asd_sas_phy *sas_phy, int lock)
{
struct sas_ha_struct *sas_ha = sas_phy->ha;
struct mvs_info *mvi = NULL; int i = 0, hi;
struct mvs_phy *phy = sas_phy->lldd_phy;
struct asd_sas_port *sas_port = sas_phy->port;
struct mvs_port *port;
unsigned long flags = 0;
if (!sas_port)
return;
while (sas_ha->sas_phy[i]) {
if (sas_ha->sas_phy[i] == sas_phy)
break;
i++;
}
hi = i/((struct mvs_prv_info *)sas_ha->lldd_ha)->n_phy;
mvi = ((struct mvs_prv_info *)sas_ha->lldd_ha)->mvi[hi];
if (sas_port->id >= mvi->chip->n_phy)
port = &mvi->port[sas_port->id - mvi->chip->n_phy];
else
port = &mvi->port[sas_port->id];
if (lock)
spin_lock_irqsave(&mvi->lock, flags);
port->port_attached = 1;
phy->port = port;
if (phy->phy_type & PORT_TYPE_SAS) {
port->wide_port_phymap = sas_port->phy_mask;
mv_printk("set wide port phy map %x\n", sas_port->phy_mask);
mvs_update_wideport(mvi, sas_phy->id);
}
if (lock)
spin_unlock_irqrestore(&mvi->lock, flags);
}
static void mvs_port_notify_deformed(struct asd_sas_phy *sas_phy, int lock)
{
struct domain_device *dev;
struct mvs_phy *phy = sas_phy->lldd_phy;
struct mvs_info *mvi = phy->mvi;
struct asd_sas_port *port = sas_phy->port;
int phy_no = 0;
while (phy != &mvi->phy[phy_no]) {
phy_no++;
if (phy_no >= MVS_MAX_PHYS)
return;
}
list_for_each_entry(dev, &port->dev_list, dev_list_node)
mvs_do_release_task(phy->mvi, phy_no, NULL);
}
void mvs_port_formed(struct asd_sas_phy *sas_phy)
{
mvs_port_notify_formed(sas_phy, 1);
}
void mvs_port_deformed(struct asd_sas_phy *sas_phy)
{
mvs_port_notify_deformed(sas_phy, 1);
}
static struct mvs_device *mvs_alloc_dev(struct mvs_info *mvi)
{
u32 dev;
for (dev = 0; dev < MVS_MAX_DEVICES; dev++) {
if (mvi->devices[dev].dev_type == SAS_PHY_UNUSED) {
mvi->devices[dev].device_id = dev;
return &mvi->devices[dev];
}
}
if (dev == MVS_MAX_DEVICES)
mv_printk("max support %d devices, ignore ..\n",
MVS_MAX_DEVICES);
return NULL;
}
static void mvs_free_dev(struct mvs_device *mvi_dev)
{
u32 id = mvi_dev->device_id;
memset(mvi_dev, 0, sizeof(*mvi_dev));
mvi_dev->device_id = id;
mvi_dev->dev_type = SAS_PHY_UNUSED;
mvi_dev->dev_status = MVS_DEV_NORMAL;
mvi_dev->taskfileset = MVS_ID_NOT_MAPPED;
}
static int mvs_dev_found_notify(struct domain_device *dev, int lock)
{
unsigned long flags = 0;
int res = 0;
struct mvs_info *mvi = NULL;
struct domain_device *parent_dev = dev->parent;
struct mvs_device *mvi_device;
mvi = mvs_find_dev_mvi(dev);
if (lock)
spin_lock_irqsave(&mvi->lock, flags);
mvi_device = mvs_alloc_dev(mvi);
if (!mvi_device) {
res = -1;
goto found_out;
}
dev->lldd_dev = mvi_device;
mvi_device->dev_type = dev->dev_type;
mvi_device->mvi_info = mvi;
if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) {
int phy_id;
u8 phy_num = parent_dev->ex_dev.num_phys;
struct ex_phy *phy;
for (phy_id = 0; phy_id < phy_num; phy_id++) {
phy = &parent_dev->ex_dev.ex_phy[phy_id];
if (SAS_ADDR(phy->attached_sas_addr) ==
SAS_ADDR(dev->sas_addr)) {
mvi_device->attached_phy = phy_id;
break;
}
}
if (phy_id == phy_num) {
mv_printk("Error: no attached dev:%016llx"
"at ex:%016llx.\n",
SAS_ADDR(dev->sas_addr),
SAS_ADDR(parent_dev->sas_addr));
res = -1;
}
}
found_out:
if (lock)
spin_unlock_irqrestore(&mvi->lock, flags);
return res;
}
int mvs_dev_found(struct domain_device *dev)
{
return mvs_dev_found_notify(dev, 1);
}
static void mvs_dev_gone_notify(struct domain_device *dev)
{
unsigned long flags = 0;
struct mvs_device *mvi_dev = dev->lldd_dev;
struct mvs_info *mvi = mvi_dev->mvi_info;
spin_lock_irqsave(&mvi->lock, flags);
if (mvi_dev) {
mv_dprintk("found dev[%d:%x] is gone.\n",
mvi_dev->device_id, mvi_dev->dev_type);
mvs_release_task(mvi, dev);
mvs_free_reg_set(mvi, mvi_dev);
mvs_free_dev(mvi_dev);
} else {
mv_dprintk("found dev has gone.\n");
}
dev->lldd_dev = NULL;
spin_unlock_irqrestore(&mvi->lock, flags);
}
void mvs_dev_gone(struct domain_device *dev)
{
mvs_dev_gone_notify(dev);
}
static struct sas_task *mvs_alloc_task(void)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
struct sas_task *task = kzalloc(sizeof(struct sas_task), GFP_KERNEL);
#else
struct sas_task *task = sas_alloc_slow_task(GFP_KERNEL);
#endif
if (task) {
INIT_LIST_HEAD(&task->list);
spin_lock_init(&task->task_state_lock);
task->task_state_flags = SAS_TASK_STATE_PENDING;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
/*
* See also patch "libsas: trim sas_task of slow path
* infrastructure" (commit ID
* f0bf750c2d25c3a2131ececbff63c7878e0e3765).
*/
init_timer(&task->timer);
init_completion(&task->completion);
#else
init_timer(&task->slow_task->timer);
init_completion(&task->slow_task->completion);
#endif
}
return task;
}
static void mvs_free_task(struct sas_task *task)
{
if (task) {
BUG_ON(!list_empty(&task->list));
kfree(task);
}
}
static void mvs_task_done(struct sas_task *task)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
if (!del_timer(&task->timer))
return;
complete(&task->completion);
#else
if (!del_timer(&task->slow_task->timer))
return;
complete(&task->slow_task->completion);
#endif
}
static void mvs_tmf_timedout(unsigned long data)
{
struct sas_task *task = (struct sas_task *)data;
task->task_state_flags |= SAS_TASK_STATE_ABORTED;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
complete(&task->completion);
#else
complete(&task->slow_task->completion);
#endif
}
/* XXX */
#define MVS_TASK_TIMEOUT 20
static int mvs_exec_internal_tmf_task(struct domain_device *dev,
void *parameter, u32 para_len, struct mvs_tmf_task *tmf)
{
int res, retry;
struct sas_task *task = NULL;
struct timer_list *t;
for (retry = 0; retry < 3; retry++) {
task = mvs_alloc_task();
if (!task)
return -ENOMEM;
task->dev = dev;
task->task_proto = dev->tproto;
memcpy(&task->ssp_task, parameter, para_len);
task->task_done = mvs_task_done;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
t = &task->timer;
#else
t = &task->slow_task->timer;
#endif
t->data = (unsigned long) task;
t->function = mvs_tmf_timedout;
t->expires = jiffies + MVS_TASK_TIMEOUT*HZ;
add_timer(t);
res = mvs_task_exec(task, 1, GFP_KERNEL, NULL, 1, tmf);
if (res) {
del_timer(t);
mv_printk("executing internel task failed:%d\n", res);
goto ex_err;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
wait_for_completion(&task->completion);
#else
wait_for_completion(&task->slow_task->completion);
#endif
res = -TMF_RESP_FUNC_FAILED;
/* Even TMF timed out, return direct. */
if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
mv_printk("TMF task[%x] timeout.\n", tmf->tmf);
goto ex_err;
}
}
if (task->task_status.resp == SAS_TASK_COMPLETE &&
task->task_status.stat == SAM_STAT_GOOD) {
res = TMF_RESP_FUNC_COMPLETE;
break;
}
if (task->task_status.resp == SAS_TASK_COMPLETE &&
task->task_status.stat == SAS_DATA_UNDERRUN) {
/* no error, but return the number of bytes of
* underrun */
res = task->task_status.residual;
break;
}
if (task->task_status.resp == SAS_TASK_COMPLETE &&
task->task_status.stat == SAS_DATA_OVERRUN) {
mv_dprintk("blocked task error.\n");
res = -EMSGSIZE;
break;
} else {
mv_dprintk(" task to dev %016llx response: 0x%x "
"status 0x%x\n",
SAS_ADDR(dev->sas_addr),
task->task_status.resp,
task->task_status.stat);
mvs_free_task(task);
task = NULL;
}
}
ex_err:
BUG_ON(retry == 3 && task != NULL);
if (task != NULL)
mvs_free_task(task);
return res;
}
static int mvs_debug_issue_ssp_tmf(struct domain_device *dev,
u8 *lun, struct mvs_tmf_task *tmf)
{
struct sas_ssp_task ssp_task;
DECLARE_COMPLETION_ONSTACK(completion);
if (!(dev->tproto & SAS_PROTOCOL_SSP))
return TMF_RESP_FUNC_ESUPP;
strncpy((u8 *)&ssp_task.LUN, lun, 8);
return mvs_exec_internal_tmf_task(dev, &ssp_task,
sizeof(ssp_task), tmf);
}
/* Standard mandates link reset for ATA (type 0)
and hard reset for SSP (type 1) , only for RECOVERY */
static int mvs_debug_I_T_nexus_reset(struct domain_device *dev)
{
int rc;
int reset_type = (dev->dev_type == SAS_SATA_DEV ||
(dev->tproto & SAS_PROTOCOL_STP)) ? 0 : 1;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
struct sas_phy *phy = sas_find_local_phy(dev);
rc = sas_phy_reset(phy, reset_type);
/*
* See also commit "libsas: fix sas_find_local_phy(), take phy
* references" (f41a0c441c3fe43e79ebeb75584dbb5bfa83e5cd).
*/
#else
struct sas_phy *phy = sas_get_local_phy(dev);
rc = sas_phy_reset(phy, reset_type);
sas_put_local_phy(phy);
#endif
msleep(2000);
return rc;
}
/* mandatory SAM-3 */
int mvs_lu_reset(struct domain_device *dev, u8 *lun)
{
unsigned long flags;
int rc = TMF_RESP_FUNC_FAILED;
struct mvs_tmf_task tmf_task;
struct mvs_device *mvi_dev = dev->lldd_dev;
struct mvs_info *mvi = mvi_dev->mvi_info;
tmf_task.tmf = TMF_LU_RESET;
mvi_dev->dev_status = MVS_DEV_EH;
rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task);
if (rc == TMF_RESP_FUNC_COMPLETE) {
spin_lock_irqsave(&mvi->lock, flags);
mvs_release_task(mvi, dev);
spin_unlock_irqrestore(&mvi->lock, flags);
}
/* If failed, fall-through I_T_Nexus reset */
mv_printk("%s for device[%x]:rc= %d\n", __func__,
mvi_dev->device_id, rc);
return rc;
}
int mvs_I_T_nexus_reset(struct domain_device *dev)
{
unsigned long flags;
int rc = TMF_RESP_FUNC_FAILED;
struct mvs_device *mvi_dev = dev->lldd_dev;
struct mvs_info *mvi = mvi_dev->mvi_info;
if (mvi_dev->dev_status != MVS_DEV_EH)
return TMF_RESP_FUNC_COMPLETE;
else
mvi_dev->dev_status = MVS_DEV_NORMAL;
rc = mvs_debug_I_T_nexus_reset(dev);
mv_printk("%s for device[%x]:rc= %d\n",
__func__, mvi_dev->device_id, rc);
spin_lock_irqsave(&mvi->lock, flags);
mvs_release_task(mvi, dev);
spin_unlock_irqrestore(&mvi->lock, flags);
return rc;
}
/* optional SAM-3 */
int mvs_query_task(struct sas_task *task)
{
u32 tag;
struct scsi_lun lun;
struct mvs_tmf_task tmf_task;
int rc = TMF_RESP_FUNC_FAILED;
if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) {
struct scsi_cmnd *cmnd = (struct scsi_cmnd *)task->uldd_task;
struct domain_device *dev = task->dev;
struct mvs_device *mvi_dev = dev->lldd_dev;
struct mvs_info *mvi = mvi_dev->mvi_info;
int_to_scsilun(cmnd->device->lun, &lun);
rc = mvs_find_tag(mvi, task, &tag);
if (rc == 0) {
rc = TMF_RESP_FUNC_FAILED;
return rc;
}
tmf_task.tmf = TMF_QUERY_TASK;
tmf_task.tag_of_task_to_be_managed = cpu_to_le16(tag);
rc = mvs_debug_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task);
switch (rc) {
/* The task is still in Lun, release it then */
case TMF_RESP_FUNC_SUCC:
/* The task is not in Lun or failed, reset the phy */
case TMF_RESP_FUNC_FAILED:
case TMF_RESP_FUNC_COMPLETE:
break;
}
}
mv_printk("%s:rc= %d\n", __func__, rc);
return rc;
}
/* mandatory SAM-3, still need free task/slot info */
int mvs_abort_task(struct sas_task *task)
{
struct scsi_lun lun;
struct mvs_tmf_task tmf_task;
struct domain_device *dev = task->dev;
struct mvs_device *mvi_dev = dev->lldd_dev;
struct mvs_info *mvi = mvi_dev->mvi_info;
int rc = TMF_RESP_FUNC_FAILED;
u32 tag;
if (task->task_state_flags & SAS_TASK_STATE_DONE) {
rc = TMF_RESP_FUNC_COMPLETE;
goto out;
}
if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) {
struct scsi_cmnd *cmnd = (struct scsi_cmnd *)task->uldd_task;
int_to_scsilun(cmnd->device->lun, &lun);
rc = mvs_find_tag(mvi, task, &tag);
if (rc == 0) {
mv_printk("No such tag in %s\n", __func__);
rc = TMF_RESP_FUNC_FAILED;
return rc;
}
tmf_task.tmf = TMF_ABORT_TASK;
tmf_task.tag_of_task_to_be_managed = cpu_to_le16(tag);
rc = mvs_debug_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task);
/* if successful, clear the task and callback forwards.*/
if (rc == TMF_RESP_FUNC_COMPLETE) {
u32 slot_no;
struct mvs_slot_info *slot;
unsigned long flags;
if (task->lldd_task) {
slot = task->lldd_task;
slot_no = (u32) (slot - mvi->slot_info);
spin_lock_irqsave(&mvi->lock, flags);
mvs_slot_complete(mvi, slot_no, 1);
spin_unlock_irqrestore(&mvi->lock, flags);
}
}
} else if (task->task_proto & SAS_PROTOCOL_SATA ||
task->task_proto & SAS_PROTOCOL_STP) {
/* to do free register_set */
} else {
/* SMP */
}
out:
if (rc != TMF_RESP_FUNC_COMPLETE)
mv_printk("%s:rc= %d\n", __func__, rc);
return rc;
}
int mvs_abort_task_set(struct domain_device *dev, u8 *lun)
{
int rc = TMF_RESP_FUNC_FAILED;
struct mvs_tmf_task tmf_task;
tmf_task.tmf = TMF_ABORT_TASK_SET;
rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task);
return rc;
}
int mvs_clear_aca(struct domain_device *dev, u8 *lun)
{
int rc = TMF_RESP_FUNC_FAILED;
struct mvs_tmf_task tmf_task;
tmf_task.tmf = TMF_CLEAR_ACA;
rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task);
return rc;
}
int mvs_clear_task_set(struct domain_device *dev, u8 *lun)
{
int rc = TMF_RESP_FUNC_FAILED;
struct mvs_tmf_task tmf_task;
tmf_task.tmf = TMF_CLEAR_TASK_SET;
rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task);
return rc;
}
static int mvs_sata_done(struct mvs_info *mvi, struct sas_task *task,
u32 slot_idx, int err)
{
struct mvs_device *mvi_dev = task->dev->lldd_dev;
struct task_status_struct *tstat = &task->task_status;
struct ata_task_resp *resp = (struct ata_task_resp *)tstat->buf;
int stat = SAM_STAT_GOOD;
resp->frame_len = sizeof(struct dev_to_host_fis);
memcpy(&resp->ending_fis[0],
SATA_RECEIVED_D2H_FIS(mvi_dev->taskfileset),
sizeof(struct dev_to_host_fis));
tstat->buf_valid_size = sizeof(*resp);
if (unlikely(err))
stat = SAS_PROTO_RESPONSE;
return stat;
}
static int mvs_slot_err(struct mvs_info *mvi, struct sas_task *task,
u32 slot_idx)
{
struct mvs_slot_info *slot = &mvi->slot_info[slot_idx];
int stat;
u32 err_dw0 = le32_to_cpu(*(u32 *) (slot->response));
u32 tfs = 0;
enum mvs_port_type type = PORT_TYPE_SAS;
if (err_dw0 & CMD_ISS_STPD)
MVS_CHIP_DISP->issue_stop(mvi, type, tfs);
MVS_CHIP_DISP->command_active(mvi, slot_idx);
stat = SAM_STAT_CHECK_CONDITION;
switch (task->task_proto) {
case SAS_PROTOCOL_SSP:
stat = SAS_ABORTED_TASK;
break;
case SAS_PROTOCOL_SMP:
stat = SAM_STAT_CHECK_CONDITION;
break;
case SAS_PROTOCOL_SATA:
case SAS_PROTOCOL_STP:
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
{
task->ata_task.use_ncq = 0;
stat = SAS_PROTO_RESPONSE;
mvs_sata_done(mvi, task, slot_idx, 1);
}
break;
default:
break;
}
return stat;
}
int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags)
__releases(&mvi->lock)
__acquires(&mvi->lock)
{
u32 slot_idx = rx_desc & RXQ_SLOT_MASK;
struct mvs_slot_info *slot = &mvi->slot_info[slot_idx];
struct sas_task *task = slot->task;
struct mvs_device *mvi_dev = NULL;
struct task_status_struct *tstat;
struct domain_device *dev;
bool aborted;
void *to;
enum exec_status sts;
if (unlikely(!task || !task->lldd_task || !task->dev))
return -1;
tstat = &task->task_status;
dev = task->dev;
mvi_dev = dev->lldd_dev;
mvs_hba_cq_dump(mvi);
spin_lock(&task->task_state_lock);
task->task_state_flags &=
~(SAS_TASK_STATE_PENDING | SAS_TASK_AT_INITIATOR);
task->task_state_flags |= SAS_TASK_STATE_DONE;
/* race condition*/
aborted = task->task_state_flags & SAS_TASK_STATE_ABORTED;
spin_unlock(&task->task_state_lock);
memset(tstat, 0, sizeof(*tstat));
tstat->resp = SAS_TASK_COMPLETE;
if (unlikely(aborted)) {
tstat->stat = SAS_ABORTED_TASK;
if (mvi_dev)
mvi_dev->running_req--;
if (sas_protocol_ata(task->task_proto))
mvs_free_reg_set(mvi, mvi_dev);
mvs_slot_task_free(mvi, task, slot, slot_idx);
return -1;
}
if (unlikely(!mvi_dev || !slot->port->port_attached || flags)) {
mv_dprintk("port has not device.\n");
tstat->stat = SAS_PHY_DOWN;
goto out;
}
/*
if (unlikely((rx_desc & RXQ_ERR) || (*(u64 *) slot->response))) {
mv_dprintk("Find device[%016llx] RXQ_ERR %X,
err info:%016llx\n",
SAS_ADDR(task->dev->sas_addr),
rx_desc, (u64)(*(u64 *) slot->response));
}
*/
/* error info record present */
if (unlikely((rx_desc & RXQ_ERR) && (*(u64 *) slot->response))) {
if (task->task_proto & (SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP))
mvs_sata_done(mvi, task, slot_idx, 0);
tstat->stat = mvs_slot_err(mvi, task, slot_idx);
goto out;
}
switch (task->task_proto) {
case SAS_PROTOCOL_SSP:
/* hw says status == 0, datapres == 0 */
if (rx_desc & RXQ_GOOD) {
tstat->stat = SAM_STAT_GOOD;
tstat->resp = SAS_TASK_COMPLETE;
}
/* response frame present */
else if (rx_desc & RXQ_RSP) {
struct ssp_response_iu *iu = slot->response +
sizeof(struct mvs_err_info);
sas_ssp_task_response(mvi->dev, task, iu);
} else
tstat->stat = SAM_STAT_CHECK_CONDITION;
break;
case SAS_PROTOCOL_SMP: {
struct scatterlist *sg_resp = &task->smp_task.smp_resp;
tstat->stat = SAM_STAT_GOOD;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
to = kmap_atomic(sg_page(sg_resp), KM_IRQ0);
#else
to = kmap_atomic(sg_page(sg_resp));
#endif
memcpy(to + sg_resp->offset,
slot->response + sizeof(struct mvs_err_info),
sg_dma_len(sg_resp));
memcpy(to + sg_resp->offset,
slot->response + sizeof(struct mvs_err_info),
sg_resp->length);
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
kunmap_atomic(to, KM_IRQ0);
#else
kunmap_atomic(to);
#endif
break;
}
case SAS_PROTOCOL_SATA:
case SAS_PROTOCOL_STP:
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP: {
tstat->stat = mvs_sata_done(mvi, task, slot_idx, 0);
break;
}
default:
tstat->stat = SAM_STAT_CHECK_CONDITION;
break;
}
out:
if (mvi_dev) {
mvi_dev->running_req--;
if (sas_protocol_ata(task->task_proto))
mvs_free_reg_set(mvi, mvi_dev);
}
mvs_slot_task_free(mvi, task, slot, slot_idx);
sts = tstat->stat;
spin_unlock(&mvi->lock);
if (task->task_done)
task->task_done(task);
else
mv_dprintk("why has not task_done.\n");
spin_lock(&mvi->lock);
return sts;
}
void mvs_do_release_task(struct mvs_info *mvi,
int phy_no, struct domain_device *dev)
{
u32 slot_idx;
struct mvs_phy *phy;
struct mvs_port *port;
struct mvs_slot_info *slot, *slot2;
phy = &mvi->phy[phy_no];
port = phy->port;
if (!port)
return;
list_for_each_entry_safe(slot, slot2, &port->list, entry) {
struct sas_task *task;
slot_idx = (u32) (slot - mvi->slot_info);
task = slot->task;
if (dev && task->dev != dev)
continue;
mv_printk("Release slot [%x] tag[%x], task [%p]:\n",
slot_idx, slot->slot_tag, task);
mvs_slot_complete(mvi, slot_idx, 1);
}
}
void mvs_release_task(struct mvs_info *mvi,
struct domain_device *dev)
{
int i, phyno[WIDE_PORT_MAX_PHY], num;
/* housekeeper */
num = mvs_find_dev_phyno(dev, phyno);
for (i = 0; i < num; i++)
mvs_do_release_task(mvi, phyno[i], dev);
}
static void mvs_phy_disconnected(struct mvs_phy *phy)
{
phy->phy_attached = 0;
phy->att_dev_info = 0;
phy->att_dev_sas_addr = 0;
}
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
static void mvs_work_queue(struct work_struct *work)
{
struct delayed_work *dw = container_of(work, struct delayed_work, work);
#else
static void mvs_work_queue(void *arg)
{
struct delayed_work *dw = arg;
#endif
struct mvs_wq *mwq = container_of(dw, struct mvs_wq, work_q);
struct mvs_info *mvi = mwq->mvi;
u32 phy_no = (unsigned long) mwq->data;
struct sas_ha_struct *sas_ha = mvi->sas;
struct mvs_phy *phy = &mvi->phy[phy_no];
struct asd_sas_phy *sas_phy = &phy->sas_phy;
unsigned long flags;
spin_lock_irqsave(&mvi->lock, flags);
if (mwq->handler & PHY_PLUG_EVENT) {
if (phy->phy_event & PHY_PLUG_OUT) {
u32 tmp;
struct sas_identify_frame *id;
id = (struct sas_identify_frame *)phy->frame_rcvd;
tmp = MVS_CHIP_DISP->read_phy_ctl(mvi, phy_no);
phy->phy_event &= ~PHY_PLUG_OUT;
if (!(tmp & PHY_READY_MASK)) {
sas_phy_disconnected(sas_phy);
mvs_phy_disconnected(phy);
sas_ha->notify_phy_event(sas_phy,
PHYE_LOSS_OF_SIGNAL);
mv_dprintk("phy%d Removed Device\n", phy_no);
} else {
MVS_CHIP_DISP->detect_porttype(mvi, phy_no);
mvs_update_phyinfo(mvi, phy_no, 1);
mvs_bytes_dmaed(mvi, phy_no);
mvs_port_notify_formed(sas_phy, 0);
mv_dprintk("phy%d Attached Device\n", phy_no);
}
}
} else if (mwq->handler & EXP_BRCT_CHG) {
phy->phy_event &= ~EXP_BRCT_CHG;
sas_ha->notify_port_event(sas_phy,
PORTE_BROADCAST_RCVD);
mv_dprintk("phy%d Got Broadcast Change\n", phy_no);
}
list_del(&mwq->entry);
spin_unlock_irqrestore(&mvi->lock, flags);
kfree(mwq);
}
static int mvs_handle_event(struct mvs_info *mvi, void *data, int handler)
{
struct mvs_wq *mwq;
int ret = 0;
mwq = kmalloc(sizeof(struct mvs_wq), GFP_ATOMIC);
if (mwq) {
mwq->mvi = mvi;
mwq->data = data;
mwq->handler = handler;
MV_INIT_DELAYED_WORK(&mwq->work_q, mvs_work_queue, mwq);
list_add_tail(&mwq->entry, &mvi->wq_list);
schedule_delayed_work(&mwq->work_q, HZ * DISK_FLASH_HOLD_TIME);
} else
ret = -ENOMEM;
return ret;
}
static void mvs_sig_time_out(unsigned long tphy)
{
struct mvs_phy *phy = (struct mvs_phy *)tphy;
struct mvs_info *mvi = phy->mvi;
u8 phy_no;
for (phy_no = 0; phy_no < mvi->chip->n_phy; phy_no++) {
if (&mvi->phy[phy_no] == phy) {
mv_dprintk("Get signature time out, reset phy %d\n",
phy_no+mvi->id*mvi->chip->n_phy);
MVS_CHIP_DISP->phy_reset(mvi, phy_no, 1);
}
}
}
static void mvs_sig_remove_timer(struct mvs_phy *phy)
{
if (phy->timer.function)
del_timer(&phy->timer);
phy->timer.function = NULL;
}
void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
{
u32 tmp;
struct mvs_phy *phy = &mvi->phy[phy_no];
struct asd_sas_phy *sas_phy = &phy->sas_phy;
phy->irq_status = MVS_CHIP_DISP->read_port_irq_stat(mvi, phy_no);
mv_dprintk("port %d ctrl sts=0x%X.\n", phy_no+mvi->id*mvi->chip->n_phy,
MVS_CHIP_DISP->read_phy_ctl(mvi, phy_no));
mv_dprintk("Port %d irq sts = 0x%X\n", phy_no+mvi->id*mvi->chip->n_phy,
phy->irq_status);
/*
* events is port event now ,
* we need check the interrupt status which belongs to per port.
*/
if (phy->irq_status & PHYEV_DCDR_ERR) {
mv_dprintk("port %d STP decoding error.\n",
phy_no + mvi->id*mvi->chip->n_phy);
}
if (phy->irq_status & PHYEV_POOF) {
#ifdef SUPPORT_TARGET
/*if initiator plug out*/
mv_dprintk("port %d attr is %x\n",
phy_no + mvi->id*mvi->chip->n_phy,
mvi->tgt_port[phy_no].port_attr);
if (mvi->tgt_port[phy_no].port_attr == MVST_TGT_PORT
|| mvi->tgt_port[phy_no].port_attr ==
MVST_INIT_TGT_PORT) {
struct sas_ha_struct *sas_ha = mvi->sas;
phy->phy_event |= PHY_PLUG_OUT;
sas_phy_disconnected(sas_phy);
mvs_phy_disconnected(phy);
mv_dprintk("notify plug out on phy[%d]\n", phy_no +
mvi->id*mvi->chip->n_phy);
sas_ha->notify_phy_event(sas_phy, PHYE_LOSS_OF_SIGNAL);
} else
#endif
{
if (!(phy->phy_event & PHY_PLUG_OUT)) {
int dev_sata = phy->phy_type & PORT_TYPE_SATA;
int ready;
mvs_do_release_task(mvi, phy_no, NULL);
phy->phy_event |= PHY_PLUG_OUT;
mvs_handle_event(mvi,
(void *)(unsigned long)phy_no,
PHY_PLUG_EVENT);
ready = mvs_is_phy_ready(mvi, phy_no);
if (!ready)
mv_dprintk("phy%d Unplug Notice\n",
phy_no +
mvi->id * mvi->chip->n_phy);
if (ready || dev_sata) {
if (MVS_CHIP_DISP->stp_reset)
MVS_CHIP_DISP->stp_reset(mvi,
phy_no);
else
MVS_CHIP_DISP->phy_reset(mvi,
phy_no, 0);
return;
}
}
}
}
if (phy->irq_status & PHYEV_COMWAKE) {
tmp = MVS_CHIP_DISP->read_port_irq_mask(mvi, phy_no);
MVS_CHIP_DISP->write_port_irq_mask(mvi, phy_no,
tmp | PHYEV_SIG_FIS);
if (phy->timer.function == NULL) {
phy->timer.data = (unsigned long)phy;
phy->timer.function = mvs_sig_time_out;
phy->timer.expires = jiffies + 10*HZ;
add_timer(&phy->timer);
}
}
if (phy->irq_status & (PHYEV_SIG_FIS | PHYEV_ID_DONE)) {
phy->phy_status = mvs_is_phy_ready(mvi, phy_no);
mvs_sig_remove_timer(phy);
mv_dprintk("notify plug in on phy[%d]\n", phy_no);
if (phy->phy_status) {
mdelay(10);
MVS_CHIP_DISP->detect_porttype(mvi, phy_no);
if (phy->phy_type & PORT_TYPE_SATA) {
tmp = MVS_CHIP_DISP->read_port_irq_mask(
mvi, phy_no);
tmp &= ~PHYEV_SIG_FIS;
MVS_CHIP_DISP->write_port_irq_mask(mvi,
phy_no, tmp);
}
mvs_update_phyinfo(mvi, phy_no, 0);
mvs_bytes_dmaed(mvi, phy_no);
/* whether driver is going to handle hot plug */
if (phy->phy_event & PHY_PLUG_OUT) {
mvs_port_notify_formed(sas_phy, 0);
phy->phy_event &= ~PHY_PLUG_OUT;
}
} else {
mv_dprintk("plugin interrupt but phy%d is gone\n",
phy_no + mvi->id*mvi->chip->n_phy);
}
} else if (phy->irq_status & PHYEV_BROAD_CH) {
mv_dprintk("port %d broadcast change.\n",
phy_no + mvi->id*mvi->chip->n_phy);
#ifdef SUPPORT_EXP_LB
mvi->exp_brct = 1;
#endif
mvs_handle_event(mvi, (void *)(unsigned long)phy_no,
EXP_BRCT_CHG);
}
MVS_CHIP_DISP->write_port_irq_stat(mvi, phy_no, phy->irq_status);
}
int mvs_int_rx(struct mvs_info *mvi, bool self_clear)
{
u32 rx_prod_idx, rx_desc;
bool attn = false;
/* the first dword in the RX ring is special: it contains
* a mirror of the hardware's RX producer index, so that
* we don't have to stall the CPU reading that register.
* The actual RX ring is offset by one dword, due to this.
*/
rx_prod_idx = mvi->rx_cons;
mvi->rx_cons = le32_to_cpu(mvi->rx[0]);
if (mvi->rx_cons == 0xfff) /* h/w hasn't touched RX ring yet */
return 0;
/* The CMPL_Q may come late, read from register and try again
* note: if coalescing is enabled,
* it will need to read from register every time for sure
*/
if (unlikely(mvi->rx_cons == rx_prod_idx))
mvi->rx_cons = MVS_CHIP_DISP->rx_update(mvi) & RX_RING_SZ_MASK;
if (mvi->rx_cons == rx_prod_idx)
return 0;
while (mvi->rx_cons != rx_prod_idx) {
/* increment our internal RX consumer pointer */
rx_prod_idx = (rx_prod_idx + 1) & (MVS_RX_RING_SZ - 1);
rx_desc = le32_to_cpu(mvi->rx[rx_prod_idx + 1]);
#ifdef SUPPORT_TARGET
if ((mvs_tgt.tgt_rsp_ssp_cmd) && MVST_IN_TARGET_MODE(mvi)) {
u8 rc = 0;
u32 slot_idx = rx_desc & RXQ_SLOT_MASK;
struct mvs_slot_info *slot = &mvi->slot_info[slot_idx];
if (((!slot->slot_scst_mgmt_cmd) ||
(!slot->slot_scst_cmd)) &&
(rx_desc & RXQ_CMD_RX)) {
/* target command */
mvi->rx[rx_prod_idx + 1] = 0;
rc = mvs_tgt.tgt_rsp_ssp_cmd(mvi, rx_desc);
if (!rc)
continue;
}
}
#endif
if (likely(rx_desc & RXQ_DONE)) {
#ifdef SUPPORT_TARGET
u32 slot_idx = rx_desc & RXQ_SLOT_MASK;
struct mvs_cmd_header *cmd_hdr =
(struct mvs_cmd_header *)&mvi->slot[slot_idx];
/* check complete command type */
if (cmd_hdr->ssp_frame_type == MCH_SSP_FR_CMD) {
mvs_slot_complete(mvi, rx_desc, 0);
} else {
if (mvs_tgt.tgt_cmd_cmpl)
mvs_tgt.tgt_cmd_cmpl(mvi, rx_desc);
}
#else
mvs_slot_complete(mvi, rx_desc, 0);
#endif
}
if (rx_desc & RXQ_ATTN) {
attn = true;
} else if (rx_desc & RXQ_ERR) {
if (!(rx_desc & RXQ_DONE))
mvs_slot_complete(mvi, rx_desc, 0);
} else if (rx_desc & RXQ_SLOT_RESET) {
mvs_slot_free(mvi, rx_desc);
}
}
if (attn && self_clear)
MVS_CHIP_DISP->int_full(mvi);
return 0;
}