Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9eb46d2a authored by Ed Lin's avatar Ed Lin Committed by James Bottomley
Browse files

[SCSI] stex: add support for reset request from firmware



Add support for reset request from firmware for controllers
of st_shasta and st_yel type. Code adjustments necessary
for this change are also included.

Signed-off-by: default avatarEd Lin <ed.lin@promise.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@suse.de>
parent cbacfb5f
Loading
Loading
Loading
Loading
+166 −83
Original line number Diff line number Diff line
@@ -64,24 +64,24 @@ enum {
	YH2I_REQ_HI				= 0xc4,

	/* 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_INBOUND_DOORBELL_HANDSHAKE		= (1 << 0),
	MU_INBOUND_DOORBELL_REQHEADCHANGED	= (1 << 1),
	MU_INBOUND_DOORBELL_STATUSTAILCHANGED	= (1 << 2),
	MU_INBOUND_DOORBELL_HMUSTOPPED		= (1 << 3),
	MU_INBOUND_DOORBELL_RESET		= (1 << 4),

	MU_OUTBOUND_DOORBELL_HANDSHAKE		= (1 << 0),
	MU_OUTBOUND_DOORBELL_REQUESTTAILCHANGED	= (1 << 1),
	MU_OUTBOUND_DOORBELL_STATUSHEADCHANGED	= (1 << 2),
	MU_OUTBOUND_DOORBELL_BUSCHANGE		= (1 << 3),
	MU_OUTBOUND_DOORBELL_HASEVENT		= (1 << 4),
	MU_OUTBOUND_DOORBELL_REQUEST_RESET	= (1 << 27),

	/* 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_STATE_STARTED			= 2,
	MU_STATE_RESETTING			= 3,
	MU_STATE_FAILED				= 4,

	MU_MAX_DELAY				= 120,
	MU_HANDSHAKE_SIGNATURE			= 0x55aaaa55,
@@ -111,6 +111,8 @@ enum {

	SS_H2I_INT_RESET			= 0x100,

	SS_I2H_REQUEST_RESET			= 0x2000,

	SS_MU_OPERATIONAL			= 0x80000000,

	STEX_CDB_LENGTH				= 16,
@@ -312,6 +314,10 @@ struct st_hba {
	struct st_ccb *wait_ccb;
	__le32 *scratch;

	char work_q_name[20];
	struct workqueue_struct *work_q;
	struct work_struct reset_work;
	wait_queue_head_t reset_waitq;
	unsigned int mu_status;
	unsigned int cardtype;
	int msi_enabled;
@@ -578,6 +584,9 @@ stex_queuecommand(struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *))
	lun = cmd->device->lun;
	hba = (struct st_hba *) &host->hostdata[0];

	if (unlikely(hba->mu_status == MU_STATE_RESETTING))
		return SCSI_MLQUEUE_HOST_BUSY;

	switch (cmd->cmnd[0]) {
	case MODE_SENSE_10:
	{
@@ -842,7 +851,6 @@ static irqreturn_t stex_intr(int irq, void *__hba)
	void __iomem *base = hba->mmio_base;
	u32 data;
	unsigned long flags;
	int handled = 0;

	spin_lock_irqsave(hba->host->host_lock, flags);

@@ -853,12 +861,16 @@ static irqreturn_t stex_intr(int irq, void *__hba)
		writel(data, base + ODBL);
		readl(base + ODBL); /* flush */
		stex_mu_intr(hba, data);
		handled = 1;
		spin_unlock_irqrestore(hba->host->host_lock, flags);
		if (unlikely(data & MU_OUTBOUND_DOORBELL_REQUEST_RESET &&
			hba->cardtype == st_shasta))
			queue_work(hba->work_q, &hba->reset_work);
		return IRQ_HANDLED;
	}

	spin_unlock_irqrestore(hba->host->host_lock, flags);

	return IRQ_RETVAL(handled);
	return IRQ_NONE;
}

static void stex_ss_mu_intr(struct st_hba *hba)
@@ -940,7 +952,6 @@ static irqreturn_t stex_ss_intr(int irq, void *__hba)
	void __iomem *base = hba->mmio_base;
	u32 data;
	unsigned long flags;
	int handled = 0;

	spin_lock_irqsave(hba->host->host_lock, flags);

@@ -949,12 +960,15 @@ static irqreturn_t stex_ss_intr(int irq, void *__hba)
		/* clear the interrupt */
		writel(data, base + YI2H_INT_C);
		stex_ss_mu_intr(hba);
		handled = 1;
		spin_unlock_irqrestore(hba->host->host_lock, flags);
		if (unlikely(data & SS_I2H_REQUEST_RESET))
			queue_work(hba->work_q, &hba->reset_work);
		return IRQ_HANDLED;
	}

	spin_unlock_irqrestore(hba->host->host_lock, flags);

	return IRQ_RETVAL(handled);
	return IRQ_NONE;
}

static int stex_common_handshake(struct st_hba *hba)
@@ -1047,7 +1061,7 @@ static int stex_ss_handshake(struct st_hba *hba)
	struct st_msg_header *msg_h;
	struct handshake_frame *h;
	__le32 *scratch;
	u32 data;
	u32 data, scratch_size;
	unsigned long before;
	int ret = 0;

@@ -1075,13 +1089,16 @@ static int stex_ss_handshake(struct st_hba *hba)
	stex_gettime(&h->hosttime);
	h->partner_type = HMU_PARTNER_TYPE;
	h->extra_offset = h->extra_size = 0;
	h->scratch_size = cpu_to_le32((hba->sts_count+1)*sizeof(u32));
	scratch_size = (hba->sts_count+1)*sizeof(u32);
	h->scratch_size = cpu_to_le32(scratch_size);

	data = readl(base + YINT_EN);
	data &= ~4;
	writel(data, base + YINT_EN);
	writel((hba->dma_handle >> 16) >> 16, base + YH2I_REQ_HI);
	readl(base + YH2I_REQ_HI);
	writel(hba->dma_handle, base + YH2I_REQ);
	readl(base + YH2I_REQ); /* flush */

	scratch = hba->scratch;
	before = jiffies;
@@ -1097,7 +1114,7 @@ static int stex_ss_handshake(struct st_hba *hba)
		msleep(1);
	}

	*scratch = 0;
	memset(scratch, 0, scratch_size);
	msg_h->flag = 0;
	return ret;
}
@@ -1106,19 +1123,24 @@ static int stex_handshake(struct st_hba *hba)
{
	int err;
	unsigned long flags;
	unsigned int mu_status;

	err = (hba->cardtype == st_yel) ?
		stex_ss_handshake(hba) : stex_common_handshake(hba);
	if (err == 0) {
	spin_lock_irqsave(hba->host->host_lock, flags);
	mu_status = hba->mu_status;
	if (err == 0) {
		hba->req_head = 0;
		hba->req_tail = 0;
		hba->status_head = 0;
		hba->status_tail = 0;
		hba->out_req_cnt = 0;
		hba->mu_status = MU_STATE_STARTED;
	} else
		hba->mu_status = MU_STATE_FAILED;
	if (mu_status == MU_STATE_RESETTING)
		wake_up_all(&hba->reset_waitq);
	spin_unlock_irqrestore(hba->host->host_lock, flags);
	}
	return err;
}

@@ -1138,17 +1160,11 @@ static int stex_abort(struct scsi_cmnd *cmd)

	base = hba->mmio_base;
	spin_lock_irqsave(host->host_lock, flags);
	if (tag < host->can_queue && hba->ccb[tag].cmd == cmd)
	if (tag < host->can_queue &&
		hba->ccb[tag].req && 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)
	else
		goto out;
	}

	if (hba->cardtype == st_yel) {
		data = readl(base + YI2H_INT);
@@ -1222,6 +1238,37 @@ static void stex_hard_reset(struct st_hba *hba)
			hba->pdev->saved_config_space[i]);
}

static int stex_yos_reset(struct st_hba *hba)
{
	void __iomem *base;
	unsigned long flags, before;
	int ret = 0;

	base = hba->mmio_base;
	writel(MU_INBOUND_DOORBELL_RESET, base + IDBL);
	readl(base + IDBL); /* flush */
	before = jiffies;
	while (hba->out_req_cnt > 0) {
		if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) {
			printk(KERN_WARNING DRV_NAME
				"(%s): reset timeout\n", pci_name(hba->pdev));
			ret = -1;
			break;
		}
		msleep(1);
	}

	spin_lock_irqsave(hba->host->host_lock, flags);
	if (ret == -1)
		hba->mu_status = MU_STATE_FAILED;
	else
		hba->mu_status = MU_STATE_STARTED;
	wake_up_all(&hba->reset_waitq);
	spin_unlock_irqrestore(hba->host->host_lock, flags);

	return ret;
}

static void stex_ss_reset(struct st_hba *hba)
{
	writel(SS_H2I_INT_RESET, hba->mmio_base + YH2I_INT);
@@ -1229,66 +1276,86 @@ static void stex_ss_reset(struct st_hba *hba)
	ssleep(5);
}

static int stex_reset(struct scsi_cmnd *cmd)
static int stex_do_reset(struct st_hba *hba)
{
	struct st_hba *hba;
	void __iomem *base;
	unsigned long flags, before;
	struct st_ccb *ccb;
	unsigned long flags;
	unsigned int mu_status = MU_STATE_RESETTING;
	u16 tag;

	hba = (struct st_hba *) &cmd->device->host->hostdata[0];
	spin_lock_irqsave(hba->host->host_lock, flags);
	if (hba->mu_status == MU_STATE_STARTING) {
		spin_unlock_irqrestore(hba->host->host_lock, flags);
		printk(KERN_INFO DRV_NAME "(%s): request reset during init\n",
			pci_name(hba->pdev));
		return 0;
	}
	while (hba->mu_status == MU_STATE_RESETTING) {
		spin_unlock_irqrestore(hba->host->host_lock, flags);
		wait_event_timeout(hba->reset_waitq,
				   hba->mu_status != MU_STATE_RESETTING,
				   MU_MAX_DELAY * HZ);
		spin_lock_irqsave(hba->host->host_lock, flags);
		mu_status = hba->mu_status;
	}

	printk(KERN_INFO DRV_NAME
		"(%s): resetting host\n", pci_name(hba->pdev));
	scsi_print_command(cmd);
	if (mu_status != MU_STATE_RESETTING) {
		spin_unlock_irqrestore(hba->host->host_lock, flags);
		return (mu_status == MU_STATE_STARTED) ? 0 : -1;
	}

	hba->mu_status = MU_STATE_RESETTING;
	spin_unlock_irqrestore(hba->host->host_lock, flags);

	if (hba->cardtype == st_yosemite)
		return stex_yos_reset(hba);

	if (hba->cardtype == st_shasta)
		stex_hard_reset(hba);
	else if (hba->cardtype == st_yel)
		stex_ss_reset(hba);

	if (hba->cardtype != st_yosemite) {
		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);
	for (tag = 0; tag < hba->host->can_queue; tag++) {
		ccb = &hba->ccb[tag];
		if (ccb->req == NULL)
			continue;
		ccb->req = NULL;
		if (ccb->cmd) {
			scsi_dma_unmap(ccb->cmd);
			ccb->cmd->result = DID_RESET << 16;
			ccb->cmd->scsi_done(ccb->cmd);
			ccb->cmd = NULL;
		}
		return SUCCESS;
	}
	spin_unlock_irqrestore(hba->host->host_lock, flags);

	/* st_yosemite */
	writel(MU_INBOUND_DOORBELL_RESET, hba->mmio_base + IDBL);
	readl(hba->mmio_base + IDBL); /* flush */
	before = jiffies;
	while (hba->out_req_cnt > 0) {
		if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) {
			printk(KERN_WARNING DRV_NAME
				"(%s): reset timeout\n", pci_name(hba->pdev));
			return FAILED;
	if (stex_handshake(hba) == 0)
		return 0;

	printk(KERN_WARNING DRV_NAME "(%s): resetting: handshake failed\n",
		pci_name(hba->pdev));
	return -1;
}
		msleep(1);

static int stex_reset(struct scsi_cmnd *cmd)
{
	struct st_hba *hba;

	hba = (struct st_hba *) &cmd->device->host->hostdata[0];

	printk(KERN_INFO DRV_NAME
		"(%s): resetting host\n", pci_name(hba->pdev));
	scsi_print_command(cmd);

	return stex_do_reset(hba) ? FAILED : SUCCESS;
}

	base = hba->mmio_base;
	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 */
	spin_lock_irqsave(hba->host->host_lock, flags);
	hba->req_head = 0;
	hba->req_tail = 0;
	hba->status_head = 0;
	hba->status_tail = 0;
	hba->out_req_cnt = 0;
	hba->mu_status = MU_STATE_STARTED;
	spin_unlock_irqrestore(hba->host->host_lock, flags);
	return SUCCESS;
static void stex_reset_work(struct work_struct *work)
{
	struct st_hba *hba = container_of(work, struct st_hba, reset_work);

	stex_do_reset(hba);
}

static int stex_biosparam(struct scsi_device *sdev,
@@ -1583,12 +1650,24 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id)

	hba->host = host;
	hba->pdev = pdev;
	init_waitqueue_head(&hba->reset_waitq);

	snprintf(hba->work_q_name, sizeof(hba->work_q_name),
		 "stex_wq_%d", host->host_no);
	hba->work_q = create_singlethread_workqueue(hba->work_q_name);
	if (!hba->work_q) {
		printk(KERN_ERR DRV_NAME "(%s): create workqueue failed\n",
			pci_name(pdev));
		err = -ENOMEM;
		goto out_ccb_free;
	}
	INIT_WORK(&hba->reset_work, stex_reset_work);

	err = stex_request_irq(hba);
	if (err) {
		printk(KERN_ERR DRV_NAME "(%s): request irq failed\n",
			pci_name(pdev));
		goto out_ccb_free;
		goto out_free_wq;
	}

	err = stex_handshake(hba);
@@ -1617,6 +1696,8 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id)

out_free_irq:
	stex_free_irq(hba);
out_free_wq:
	destroy_workqueue(hba->work_q);
out_ccb_free:
	kfree(hba->ccb);
out_pci_free:
@@ -1684,6 +1765,8 @@ static void stex_hba_free(struct st_hba *hba)
{
	stex_free_irq(hba);

	destroy_workqueue(hba->work_q);

	iounmap(hba->mmio_base);

	pci_release_regions(hba->pdev);