cregit-Linux how code gets into the kernel

Release 4.11 drivers/scsi/hptiop.c

Directory: drivers/scsi
/*
 * 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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team110100.00%2100.00%
Total110100.00%2100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team24100.00%1100.00%
Total24100.00%1100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team24100.00%1100.00%
Total24100.00%1100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team38100.00%2100.00%
Total38100.00%2100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team135100.00%2100.00%
Total135100.00%2100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team145100.00%3100.00%
Total145100.00%3100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team93100.00%2100.00%
Total93100.00%2100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team102100.00%2100.00%
Total102100.00%2100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team117100.00%1100.00%
Total117100.00%1100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team139100.00%1100.00%
Total139100.00%1100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team100100.00%2100.00%
Total100100.00%2100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team322100.00%2100.00%
Total322100.00%2100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team146100.00%2100.00%
Total146100.00%2100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team103100.00%3100.00%
Total103100.00%3100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team112100.00%3100.00%
Total112100.00%3100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team44100.00%3100.00%
Total44100.00%3100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team60100.00%3100.00%
Total60100.00%3100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team4797.92%150.00%
James Bottomley12.08%150.00%
Total48100.00%2100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team11697.48%375.00%
James Bottomley32.52%125.00%
Total119100.00%4100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team181100.00%3100.00%
Total181100.00%3100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team12793.38%375.00%
James Bottomley96.62%125.00%
Total136100.00%4100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team130100.00%3100.00%
Total130100.00%3100.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

PersonTokensPropCommitsCommitProp
HighPoint Linux Team208100.00%3100.00%
Total208100.00%3100.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 (