Release 4.11 drivers/scsi/hptiop.c
/*
* HighPoint RR3xxx/4xxx controller driver for Linux
* Copyright (C) 2006-2015 HighPoint Technologies, Inc. All Rights Reserved.
*
* 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.
*
* Please report bugs/comments/suggestions to linux@highpoint-tech.com
*
* For more information, visit http://www.highpoint-tech.com
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <linux/gfp.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <asm/div64.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_host.h>
#include "hptiop.h"
MODULE_AUTHOR("HighPoint Technologies, Inc.");
MODULE_DESCRIPTION("HighPoint RocketRAID 3xxx/4xxx Controller Driver");
static char driver_name[] = "hptiop";
static const char driver_name_long[] = "RocketRAID 3xxx/4xxx Controller driver";
static const char driver_ver[] = "v1.10.0";
static int iop_send_sync_msg(struct hptiop_hba *hba, u32 msg, u32 millisec);
static void hptiop_finish_scsi_req(struct hptiop_hba *hba, u32 tag,
struct hpt_iop_request_scsi_command *req);
static void hptiop_host_request_callback_itl(struct hptiop_hba *hba, u32 tag);
static void hptiop_iop_request_callback_itl(struct hptiop_hba *hba, u32 tag);
static void hptiop_message_callback(struct hptiop_hba *hba, u32 msg);
static int iop_wait_ready_itl(struct hptiop_hba *hba, u32 millisec)
{
u32 req = 0;
int i;
for (i = 0; i < millisec; i++) {
req = readl(&hba->u.itl.iop->inbound_queue);
if (req != IOPMU_QUEUE_EMPTY)
break;
msleep(1);
}
if (req != IOPMU_QUEUE_EMPTY) {
writel(req, &hba->u.itl.iop->outbound_queue);
readl(&hba->u.itl.iop->outbound_intstatus);
return 0;
}
return -1;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 110 | 100.00% | 2 | 100.00% |
Total | 110 | 100.00% | 2 | 100.00% |
static int iop_wait_ready_mv(struct hptiop_hba *hba, u32 millisec)
{
return iop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_NOP, millisec);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 24 | 100.00% | 1 | 100.00% |
Total | 24 | 100.00% | 1 | 100.00% |
static int iop_wait_ready_mvfrey(struct hptiop_hba *hba, u32 millisec)
{
return iop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_NOP, millisec);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 24 | 100.00% | 1 | 100.00% |
Total | 24 | 100.00% | 1 | 100.00% |
static void hptiop_request_callback_itl(struct hptiop_hba *hba, u32 tag)
{
if (tag & IOPMU_QUEUE_ADDR_HOST_BIT)
hptiop_host_request_callback_itl(hba,
tag & ~IOPMU_QUEUE_ADDR_HOST_BIT);
else
hptiop_iop_request_callback_itl(hba, tag);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 38 | 100.00% | 2 | 100.00% |
Total | 38 | 100.00% | 2 | 100.00% |
static void hptiop_drain_outbound_queue_itl(struct hptiop_hba *hba)
{
u32 req;
while ((req = readl(&hba->u.itl.iop->outbound_queue)) !=
IOPMU_QUEUE_EMPTY) {
if (req & IOPMU_QUEUE_MASK_HOST_BITS)
hptiop_request_callback_itl(hba, req);
else {
struct hpt_iop_request_header __iomem * p;
p = (struct hpt_iop_request_header __iomem *)
((char __iomem *)hba->u.itl.iop + req);
if (readl(&p->flags) & IOP_REQUEST_FLAG_SYNC_REQUEST) {
if (readl(&p->context))
hptiop_request_callback_itl(hba, req);
else
writel(1, &p->context);
}
else
hptiop_request_callback_itl(hba, req);
}
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 135 | 100.00% | 2 | 100.00% |
Total | 135 | 100.00% | 2 | 100.00% |
static int iop_intr_itl(struct hptiop_hba *hba)
{
struct hpt_iopmu_itl __iomem *iop = hba->u.itl.iop;
void __iomem *plx = hba->u.itl.plx;
u32 status;
int ret = 0;
if (plx && readl(plx + 0x11C5C) & 0xf)
writel(1, plx + 0x11C60);
status = readl(&iop->outbound_intstatus);
if (status & IOPMU_OUTBOUND_INT_MSG0) {
u32 msg = readl(&iop->outbound_msgaddr0);
dprintk("received outbound msg %x\n", msg);
writel(IOPMU_OUTBOUND_INT_MSG0, &iop->outbound_intstatus);
hptiop_message_callback(hba, msg);
ret = 1;
}
if (status & IOPMU_OUTBOUND_INT_POSTQUEUE) {
hptiop_drain_outbound_queue_itl(hba);
ret = 1;
}
return ret;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 145 | 100.00% | 3 | 100.00% |
Total | 145 | 100.00% | 3 | 100.00% |
static u64 mv_outbound_read(struct hpt_iopmu_mv __iomem *mu)
{
u32 outbound_tail = readl(&mu->outbound_tail);
u32 outbound_head = readl(&mu->outbound_head);
if (outbound_tail != outbound_head) {
u64 p;
memcpy_fromio(&p, &mu->outbound_q[mu->outbound_tail], 8);
outbound_tail++;
if (outbound_tail == MVIOP_QUEUE_LEN)
outbound_tail = 0;
writel(outbound_tail, &mu->outbound_tail);
return p;
} else
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 93 | 100.00% | 2 | 100.00% |
Total | 93 | 100.00% | 2 | 100.00% |
static void mv_inbound_write(u64 p, struct hptiop_hba *hba)
{
u32 inbound_head = readl(&hba->u.mv.mu->inbound_head);
u32 head = inbound_head + 1;
if (head == MVIOP_QUEUE_LEN)
head = 0;
memcpy_toio(&hba->u.mv.mu->inbound_q[inbound_head], &p, 8);
writel(head, &hba->u.mv.mu->inbound_head);
writel(MVIOP_MU_INBOUND_INT_POSTQUEUE,
&hba->u.mv.regs->inbound_doorbell);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 102 | 100.00% | 2 | 100.00% |
Total | 102 | 100.00% | 2 | 100.00% |
static void hptiop_request_callback_mv(struct hptiop_hba *hba, u64 tag)
{
u32 req_type = (tag >> 5) & 0x7;
struct hpt_iop_request_scsi_command *req;
dprintk("hptiop_request_callback_mv: tag=%llx\n", tag);
BUG_ON((tag & MVIOP_MU_QUEUE_REQUEST_RETURN_CONTEXT) == 0);
switch (req_type) {
case IOP_REQUEST_TYPE_GET_CONFIG:
case IOP_REQUEST_TYPE_SET_CONFIG:
hba->msg_done = 1;
break;
case IOP_REQUEST_TYPE_SCSI_COMMAND:
req = hba->reqs[tag >> 8].req_virt;
if (likely(tag & MVIOP_MU_QUEUE_REQUEST_RESULT_BIT))
req->header.result = cpu_to_le32(IOP_RESULT_SUCCESS);
hptiop_finish_scsi_req(hba, tag>>8, req);
break;
default:
break;
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 117 | 100.00% | 1 | 100.00% |
Total | 117 | 100.00% | 1 | 100.00% |
static int iop_intr_mv(struct hptiop_hba *hba)
{
u32 status;
int ret = 0;
status = readl(&hba->u.mv.regs->outbound_doorbell);
writel(~status, &hba->u.mv.regs->outbound_doorbell);
if (status & MVIOP_MU_OUTBOUND_INT_MSG) {
u32 msg;
msg = readl(&hba->u.mv.mu->outbound_msg);
dprintk("received outbound msg %x\n", msg);
hptiop_message_callback(hba, msg);
ret = 1;
}
if (status & MVIOP_MU_OUTBOUND_INT_POSTQUEUE) {
u64 tag;
while ((tag = mv_outbound_read(hba->u.mv.mu)))
hptiop_request_callback_mv(hba, tag);
ret = 1;
}
return ret;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 139 | 100.00% | 1 | 100.00% |
Total | 139 | 100.00% | 1 | 100.00% |
static void hptiop_request_callback_mvfrey(struct hptiop_hba *hba, u32 _tag)
{
u32 req_type = _tag & 0xf;
struct hpt_iop_request_scsi_command *req;
switch (req_type) {
case IOP_REQUEST_TYPE_GET_CONFIG:
case IOP_REQUEST_TYPE_SET_CONFIG:
hba->msg_done = 1;
break;
case IOP_REQUEST_TYPE_SCSI_COMMAND:
req = hba->reqs[(_tag >> 4) & 0xff].req_virt;
if (likely(_tag & IOPMU_QUEUE_REQUEST_RESULT_BIT))
req->header.result = IOP_RESULT_SUCCESS;
hptiop_finish_scsi_req(hba, (_tag >> 4) & 0xff, req);
break;
default:
break;
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 100 | 100.00% | 2 | 100.00% |
Total | 100 | 100.00% | 2 | 100.00% |
static int iop_intr_mvfrey(struct hptiop_hba *hba)
{
u32 _tag, status, cptr, cur_rptr;
int ret = 0;
if (hba->initialized)
writel(0, &(hba->u.mvfrey.mu->pcie_f0_int_enable));
status = readl(&(hba->u.mvfrey.mu->f0_doorbell));
if (status) {
writel(status, &(hba->u.mvfrey.mu->f0_doorbell));
if (status & CPU_TO_F0_DRBL_MSG_BIT) {
u32 msg = readl(&(hba->u.mvfrey.mu->cpu_to_f0_msg_a));
dprintk("received outbound msg %x\n", msg);
hptiop_message_callback(hba, msg);
}
ret = 1;
}
status = readl(&(hba->u.mvfrey.mu->isr_cause));
if (status) {
writel(status, &(hba->u.mvfrey.mu->isr_cause));
do {
cptr = *hba->u.mvfrey.outlist_cptr & 0xff;
cur_rptr = hba->u.mvfrey.outlist_rptr;
while (cur_rptr != cptr) {
cur_rptr++;
if (cur_rptr == hba->u.mvfrey.list_count)
cur_rptr = 0;
_tag = hba->u.mvfrey.outlist[cur_rptr].val;
BUG_ON(!(_tag & IOPMU_QUEUE_MASK_HOST_BITS));
hptiop_request_callback_mvfrey(hba, _tag);
ret = 1;
}
hba->u.mvfrey.outlist_rptr = cur_rptr;
} while (cptr != (*hba->u.mvfrey.outlist_cptr & 0xff));
}
if (hba->initialized)
writel(0x1010, &(hba->u.mvfrey.mu->pcie_f0_int_enable));
return ret;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 322 | 100.00% | 2 | 100.00% |
Total | 322 | 100.00% | 2 | 100.00% |
static int iop_send_sync_request_itl(struct hptiop_hba *hba,
void __iomem *_req, u32 millisec)
{
struct hpt_iop_request_header __iomem *req = _req;
u32 i;
writel(readl(&req->flags) | IOP_REQUEST_FLAG_SYNC_REQUEST, &req->flags);
writel(0, &req->context);
writel((unsigned long)req - (unsigned long)hba->u.itl.iop,
&hba->u.itl.iop->inbound_queue);
readl(&hba->u.itl.iop->outbound_intstatus);
for (i = 0; i < millisec; i++) {
iop_intr_itl(hba);
if (readl(&req->context))
return 0;
msleep(1);
}
return -1;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 146 | 100.00% | 2 | 100.00% |
Total | 146 | 100.00% | 2 | 100.00% |
static int iop_send_sync_request_mv(struct hptiop_hba *hba,
u32 size_bits, u32 millisec)
{
struct hpt_iop_request_header *reqhdr = hba->u.mv.internal_req;
u32 i;
hba->msg_done = 0;
reqhdr->flags |= cpu_to_le32(IOP_REQUEST_FLAG_SYNC_REQUEST);
mv_inbound_write(hba->u.mv.internal_req_phy |
MVIOP_MU_QUEUE_ADDR_HOST_BIT | size_bits, hba);
for (i = 0; i < millisec; i++) {
iop_intr_mv(hba);
if (hba->msg_done)
return 0;
msleep(1);
}
return -1;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 103 | 100.00% | 3 | 100.00% |
Total | 103 | 100.00% | 3 | 100.00% |
static int iop_send_sync_request_mvfrey(struct hptiop_hba *hba,
u32 size_bits, u32 millisec)
{
struct hpt_iop_request_header *reqhdr =
hba->u.mvfrey.internal_req.req_virt;
u32 i;
hba->msg_done = 0;
reqhdr->flags |= cpu_to_le32(IOP_REQUEST_FLAG_SYNC_REQUEST);
hba->ops->post_req(hba, &(hba->u.mvfrey.internal_req));
for (i = 0; i < millisec; i++) {
iop_intr_mvfrey(hba);
if (hba->msg_done)
break;
msleep(1);
}
return hba->msg_done ? 0 : -1;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 112 | 100.00% | 3 | 100.00% |
Total | 112 | 100.00% | 3 | 100.00% |
static void hptiop_post_msg_itl(struct hptiop_hba *hba, u32 msg)
{
writel(msg, &hba->u.itl.iop->inbound_msgaddr0);
readl(&hba->u.itl.iop->outbound_intstatus);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 44 | 100.00% | 3 | 100.00% |
Total | 44 | 100.00% | 3 | 100.00% |
static void hptiop_post_msg_mv(struct hptiop_hba *hba, u32 msg)
{
writel(msg, &hba->u.mv.mu->inbound_msg);
writel(MVIOP_MU_INBOUND_INT_MSG, &hba->u.mv.regs->inbound_doorbell);
readl(&hba->u.mv.regs->inbound_doorbell);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 60 | 100.00% | 3 | 100.00% |
Total | 60 | 100.00% | 3 | 100.00% |
static void hptiop_post_msg_mvfrey(struct hptiop_hba *hba, u32 msg)
{
writel(msg, &(hba->u.mvfrey.mu->f0_to_cpu_msg_a));
readl(&(hba->u.mvfrey.mu->f0_to_cpu_msg_a));
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 47 | 97.92% | 1 | 50.00% |
James Bottomley | 1 | 2.08% | 1 | 50.00% |
Total | 48 | 100.00% | 2 | 100.00% |
static int iop_send_sync_msg(struct hptiop_hba *hba, u32 msg, u32 millisec)
{
u32 i;
hba->msg_done = 0;
hba->ops->disable_intr(hba);
hba->ops->post_msg(hba, msg);
for (i = 0; i < millisec; i++) {
spin_lock_irq(hba->host->host_lock);
hba->ops->iop_intr(hba);
spin_unlock_irq(hba->host->host_lock);
if (hba->msg_done)
break;
msleep(1);
}
hba->ops->enable_intr(hba);
return hba->msg_done? 0 : -1;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 116 | 97.48% | 3 | 75.00% |
James Bottomley | 3 | 2.52% | 1 | 25.00% |
Total | 119 | 100.00% | 4 | 100.00% |
static int iop_get_config_itl(struct hptiop_hba *hba,
struct hpt_iop_request_get_config *config)
{
u32 req32;
struct hpt_iop_request_get_config __iomem *req;
req32 = readl(&hba->u.itl.iop->inbound_queue);
if (req32 == IOPMU_QUEUE_EMPTY)
return -1;
req = (struct hpt_iop_request_get_config __iomem *)
((unsigned long)hba->u.itl.iop + req32);
writel(0, &req->header.flags);
writel(IOP_REQUEST_TYPE_GET_CONFIG, &req->header.type);
writel(sizeof(struct hpt_iop_request_get_config), &req->header.size);
writel(IOP_RESULT_PENDING, &req->header.result);
if (iop_send_sync_request_itl(hba, req, 20000)) {
dprintk("Get config send cmd failed\n");
return -1;
}
memcpy_fromio(config, req, sizeof(*config));
writel(req32, &hba->u.itl.iop->outbound_queue);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 181 | 100.00% | 3 | 100.00% |
Total | 181 | 100.00% | 3 | 100.00% |
static int iop_get_config_mv(struct hptiop_hba *hba,
struct hpt_iop_request_get_config *config)
{
struct hpt_iop_request_get_config *req = hba->u.mv.internal_req;
req->header.flags = cpu_to_le32(IOP_REQUEST_FLAG_OUTPUT_CONTEXT);
req->header.type = cpu_to_le32(IOP_REQUEST_TYPE_GET_CONFIG);
req->header.size =
cpu_to_le32(sizeof(struct hpt_iop_request_get_config));
req->header.result = cpu_to_le32(IOP_RESULT_PENDING);
req->header.context = cpu_to_le32(IOP_REQUEST_TYPE_GET_CONFIG<<5);
req->header.context_hi32 = 0;
if (iop_send_sync_request_mv(hba, 0, 20000)) {
dprintk("Get config send cmd failed\n");
return -1;
}
memcpy(config, req, sizeof(struct hpt_iop_request_get_config));
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 127 | 93.38% | 3 | 75.00% |
James Bottomley | 9 | 6.62% | 1 | 25.00% |
Total | 136 | 100.00% | 4 | 100.00% |
static int iop_get_config_mvfrey(struct hptiop_hba *hba,
struct hpt_iop_request_get_config *config)
{
struct hpt_iop_request_get_config *info = hba->u.mvfrey.config;
if (info->header.size != sizeof(struct hpt_iop_request_get_config) ||
info->header.type != IOP_REQUEST_TYPE_GET_CONFIG)
return -1;
config->interface_version = info->interface_version;
config->firmware_version = info->firmware_version;
config->max_requests = info->max_requests;
config->request_size = info->request_size;
config->max_sg_count = info->max_sg_count;
config->data_transfer_length = info->data_transfer_length;
config->alignment_mask = info->alignment_mask;
config->max_devices = info->max_devices;
config->sdram_size = info->sdram_size;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 130 | 100.00% | 3 | 100.00% |
Total | 130 | 100.00% | 3 | 100.00% |
static int iop_set_config_itl(struct hptiop_hba *hba,
struct hpt_iop_request_set_config *config)
{
u32 req32;
struct hpt_iop_request_set_config __iomem *req;
req32 = readl(&hba->u.itl.iop->inbound_queue);
if (req32 == IOPMU_QUEUE_EMPTY)
return -1;
req = (struct hpt_iop_request_set_config __iomem *)
((unsigned long)hba->u.itl.iop + req32);
memcpy_toio((u8 __iomem *)req + sizeof(struct hpt_iop_request_header),
(u8 *)config + sizeof(struct hpt_iop_request_header),
sizeof(struct hpt_iop_request_set_config) -
sizeof(struct hpt_iop_request_header));
writel(0, &req->header.flags);
writel(IOP_REQUEST_TYPE_SET_CONFIG, &req->header.type);
writel(sizeof(struct hpt_iop_request_set_config), &req->header.size);
writel(IOP_RESULT_PENDING, &req->header.result);
if (iop_send_sync_request_itl(hba, req, 20000)) {
dprintk("Set config send cmd failed\n");
return -1;
}
writel(req32, &hba->u.itl.iop->outbound_queue);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
HighPoint Linux Team | 208 | 100.00% | 3 | 100.00% |
Total | 208 | 100.00% | 3 | 100.00% |
static int iop_set_config_mv(struct hptiop_hba *hba,
struct hpt_iop_request_set_config *config)
{
struct hpt_iop_request_set_config *req = hba->u.mv.internal_req;
memcpy(req, config, sizeof(struct hpt_iop_request_set_config));
req->header.flags = cpu_to_le32(IOP_REQUEST_FLAG_OUTPUT_CONTEXT);
req->header.type = cpu_to_le32(IOP_REQUEST_TYPE_SET_CONFIG);
req->header.size =
cpu_to_le32(sizeof(struct hpt_iop_request_set_config));
req->header.result = cpu_to_le32(IOP_RESULT_PENDING);
req->header.context = cpu_to_le32(IOP_REQUEST_TYPE_SET_CONFIG<<5);
req->header.context_hi32 = 0;
if (