1ede1e6f8SHighPoint Linux Team /* 2ede1e6f8SHighPoint Linux Team * HighPoint RR3xxx controller driver for Linux 3db9b6e89SHighPoint Linux Team * Copyright (C) 2006-2007 HighPoint Technologies, Inc. All Rights Reserved. 4ede1e6f8SHighPoint Linux Team * 5ede1e6f8SHighPoint Linux Team * This program is free software; you can redistribute it and/or modify 6ede1e6f8SHighPoint Linux Team * it under the terms of the GNU General Public License as published by 7ede1e6f8SHighPoint Linux Team * the Free Software Foundation; version 2 of the License. 8ede1e6f8SHighPoint Linux Team * 9ede1e6f8SHighPoint Linux Team * This program is distributed in the hope that it will be useful, 10ede1e6f8SHighPoint Linux Team * but WITHOUT ANY WARRANTY; without even the implied warranty of 11ede1e6f8SHighPoint Linux Team * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12ede1e6f8SHighPoint Linux Team * GNU General Public License for more details. 13ede1e6f8SHighPoint Linux Team * 14ede1e6f8SHighPoint Linux Team * Please report bugs/comments/suggestions to linux@highpoint-tech.com 15ede1e6f8SHighPoint Linux Team * 16ede1e6f8SHighPoint Linux Team * For more information, visit http://www.highpoint-tech.com 17ede1e6f8SHighPoint Linux Team */ 18ede1e6f8SHighPoint Linux Team #include <linux/module.h> 19ede1e6f8SHighPoint Linux Team #include <linux/types.h> 20ede1e6f8SHighPoint Linux Team #include <linux/string.h> 21ede1e6f8SHighPoint Linux Team #include <linux/kernel.h> 22ede1e6f8SHighPoint Linux Team #include <linux/pci.h> 23ede1e6f8SHighPoint Linux Team #include <linux/interrupt.h> 24ede1e6f8SHighPoint Linux Team #include <linux/errno.h> 25ede1e6f8SHighPoint Linux Team #include <linux/delay.h> 26ede1e6f8SHighPoint Linux Team #include <linux/timer.h> 27ede1e6f8SHighPoint Linux Team #include <linux/spinlock.h> 28ede1e6f8SHighPoint Linux Team #include <linux/hdreg.h> 29ede1e6f8SHighPoint Linux Team #include <asm/uaccess.h> 30ede1e6f8SHighPoint Linux Team #include <asm/io.h> 31ede1e6f8SHighPoint Linux Team #include <asm/div64.h> 32ede1e6f8SHighPoint Linux Team #include <scsi/scsi_cmnd.h> 33ede1e6f8SHighPoint Linux Team #include <scsi/scsi_device.h> 34ede1e6f8SHighPoint Linux Team #include <scsi/scsi.h> 35ede1e6f8SHighPoint Linux Team #include <scsi/scsi_tcq.h> 36ede1e6f8SHighPoint Linux Team #include <scsi/scsi_host.h> 37ede1e6f8SHighPoint Linux Team 38ede1e6f8SHighPoint Linux Team #include "hptiop.h" 39ede1e6f8SHighPoint Linux Team 40ede1e6f8SHighPoint Linux Team MODULE_AUTHOR("HighPoint Technologies, Inc."); 41ede1e6f8SHighPoint Linux Team MODULE_DESCRIPTION("HighPoint RocketRAID 3xxx SATA Controller Driver"); 42ede1e6f8SHighPoint Linux Team 43ede1e6f8SHighPoint Linux Team static char driver_name[] = "hptiop"; 44ede1e6f8SHighPoint Linux Team static const char driver_name_long[] = "RocketRAID 3xxx SATA Controller driver"; 45db9b6e89SHighPoint Linux Team static const char driver_ver[] = "v1.2 (070830)"; 46ede1e6f8SHighPoint Linux Team 47ede1e6f8SHighPoint Linux Team static void hptiop_host_request_callback(struct hptiop_hba *hba, u32 tag); 48ede1e6f8SHighPoint Linux Team static void hptiop_iop_request_callback(struct hptiop_hba *hba, u32 tag); 49ede1e6f8SHighPoint Linux Team static void hptiop_message_callback(struct hptiop_hba *hba, u32 msg); 50ede1e6f8SHighPoint Linux Team 51ede1e6f8SHighPoint Linux Team static inline void hptiop_pci_posting_flush(struct hpt_iopmu __iomem *iop) 52ede1e6f8SHighPoint Linux Team { 53ede1e6f8SHighPoint Linux Team readl(&iop->outbound_intstatus); 54ede1e6f8SHighPoint Linux Team } 55ede1e6f8SHighPoint Linux Team 56ede1e6f8SHighPoint Linux Team static int iop_wait_ready(struct hpt_iopmu __iomem *iop, u32 millisec) 57ede1e6f8SHighPoint Linux Team { 58ede1e6f8SHighPoint Linux Team u32 req = 0; 59ede1e6f8SHighPoint Linux Team int i; 60ede1e6f8SHighPoint Linux Team 61ede1e6f8SHighPoint Linux Team for (i = 0; i < millisec; i++) { 62ede1e6f8SHighPoint Linux Team req = readl(&iop->inbound_queue); 63ede1e6f8SHighPoint Linux Team if (req != IOPMU_QUEUE_EMPTY) 64ede1e6f8SHighPoint Linux Team break; 65ede1e6f8SHighPoint Linux Team msleep(1); 66ede1e6f8SHighPoint Linux Team } 67ede1e6f8SHighPoint Linux Team 68ede1e6f8SHighPoint Linux Team if (req != IOPMU_QUEUE_EMPTY) { 69ede1e6f8SHighPoint Linux Team writel(req, &iop->outbound_queue); 70ede1e6f8SHighPoint Linux Team hptiop_pci_posting_flush(iop); 71ede1e6f8SHighPoint Linux Team return 0; 72ede1e6f8SHighPoint Linux Team } 73ede1e6f8SHighPoint Linux Team 74ede1e6f8SHighPoint Linux Team return -1; 75ede1e6f8SHighPoint Linux Team } 76ede1e6f8SHighPoint Linux Team 77ede1e6f8SHighPoint Linux Team static void hptiop_request_callback(struct hptiop_hba *hba, u32 tag) 78ede1e6f8SHighPoint Linux Team { 79db9b6e89SHighPoint Linux Team if (tag & IOPMU_QUEUE_ADDR_HOST_BIT) 80ede1e6f8SHighPoint Linux Team return hptiop_host_request_callback(hba, 81ede1e6f8SHighPoint Linux Team tag & ~IOPMU_QUEUE_ADDR_HOST_BIT); 82ede1e6f8SHighPoint Linux Team else 83ede1e6f8SHighPoint Linux Team return hptiop_iop_request_callback(hba, tag); 84ede1e6f8SHighPoint Linux Team } 85ede1e6f8SHighPoint Linux Team 86ede1e6f8SHighPoint Linux Team static inline void hptiop_drain_outbound_queue(struct hptiop_hba *hba) 87ede1e6f8SHighPoint Linux Team { 88ede1e6f8SHighPoint Linux Team u32 req; 89ede1e6f8SHighPoint Linux Team 90ede1e6f8SHighPoint Linux Team while ((req = readl(&hba->iop->outbound_queue)) != IOPMU_QUEUE_EMPTY) { 91ede1e6f8SHighPoint Linux Team 92ede1e6f8SHighPoint Linux Team if (req & IOPMU_QUEUE_MASK_HOST_BITS) 93ede1e6f8SHighPoint Linux Team hptiop_request_callback(hba, req); 94ede1e6f8SHighPoint Linux Team else { 95ede1e6f8SHighPoint Linux Team struct hpt_iop_request_header __iomem * p; 96ede1e6f8SHighPoint Linux Team 97ede1e6f8SHighPoint Linux Team p = (struct hpt_iop_request_header __iomem *) 98ede1e6f8SHighPoint Linux Team ((char __iomem *)hba->iop + req); 99ede1e6f8SHighPoint Linux Team 100ede1e6f8SHighPoint Linux Team if (readl(&p->flags) & IOP_REQUEST_FLAG_SYNC_REQUEST) { 101ede1e6f8SHighPoint Linux Team if (readl(&p->context)) 102ede1e6f8SHighPoint Linux Team hptiop_request_callback(hba, req); 103ede1e6f8SHighPoint Linux Team else 104ede1e6f8SHighPoint Linux Team writel(1, &p->context); 105ede1e6f8SHighPoint Linux Team } 106ede1e6f8SHighPoint Linux Team else 107ede1e6f8SHighPoint Linux Team hptiop_request_callback(hba, req); 108ede1e6f8SHighPoint Linux Team } 109ede1e6f8SHighPoint Linux Team } 110ede1e6f8SHighPoint Linux Team } 111ede1e6f8SHighPoint Linux Team 112ede1e6f8SHighPoint Linux Team static int __iop_intr(struct hptiop_hba *hba) 113ede1e6f8SHighPoint Linux Team { 114ede1e6f8SHighPoint Linux Team struct hpt_iopmu __iomem *iop = hba->iop; 115ede1e6f8SHighPoint Linux Team u32 status; 116ede1e6f8SHighPoint Linux Team int ret = 0; 117ede1e6f8SHighPoint Linux Team 118ede1e6f8SHighPoint Linux Team status = readl(&iop->outbound_intstatus); 119ede1e6f8SHighPoint Linux Team 120ede1e6f8SHighPoint Linux Team if (status & IOPMU_OUTBOUND_INT_MSG0) { 121ede1e6f8SHighPoint Linux Team u32 msg = readl(&iop->outbound_msgaddr0); 122ede1e6f8SHighPoint Linux Team dprintk("received outbound msg %x\n", msg); 123ede1e6f8SHighPoint Linux Team writel(IOPMU_OUTBOUND_INT_MSG0, &iop->outbound_intstatus); 124ede1e6f8SHighPoint Linux Team hptiop_message_callback(hba, msg); 125ede1e6f8SHighPoint Linux Team ret = 1; 126ede1e6f8SHighPoint Linux Team } 127ede1e6f8SHighPoint Linux Team 128ede1e6f8SHighPoint Linux Team if (status & IOPMU_OUTBOUND_INT_POSTQUEUE) { 129ede1e6f8SHighPoint Linux Team hptiop_drain_outbound_queue(hba); 130ede1e6f8SHighPoint Linux Team ret = 1; 131ede1e6f8SHighPoint Linux Team } 132ede1e6f8SHighPoint Linux Team 133ede1e6f8SHighPoint Linux Team return ret; 134ede1e6f8SHighPoint Linux Team } 135ede1e6f8SHighPoint Linux Team 136ede1e6f8SHighPoint Linux Team static int iop_send_sync_request(struct hptiop_hba *hba, 137ede1e6f8SHighPoint Linux Team void __iomem *_req, u32 millisec) 138ede1e6f8SHighPoint Linux Team { 139ede1e6f8SHighPoint Linux Team struct hpt_iop_request_header __iomem *req = _req; 140ede1e6f8SHighPoint Linux Team u32 i; 141ede1e6f8SHighPoint Linux Team 142ede1e6f8SHighPoint Linux Team writel(readl(&req->flags) | IOP_REQUEST_FLAG_SYNC_REQUEST, 143ede1e6f8SHighPoint Linux Team &req->flags); 144ede1e6f8SHighPoint Linux Team 145ede1e6f8SHighPoint Linux Team writel(0, &req->context); 146ede1e6f8SHighPoint Linux Team 147ede1e6f8SHighPoint Linux Team writel((unsigned long)req - (unsigned long)hba->iop, 148ede1e6f8SHighPoint Linux Team &hba->iop->inbound_queue); 149ede1e6f8SHighPoint Linux Team 150ede1e6f8SHighPoint Linux Team hptiop_pci_posting_flush(hba->iop); 151ede1e6f8SHighPoint Linux Team 152ede1e6f8SHighPoint Linux Team for (i = 0; i < millisec; i++) { 153ede1e6f8SHighPoint Linux Team __iop_intr(hba); 154ede1e6f8SHighPoint Linux Team if (readl(&req->context)) 155ede1e6f8SHighPoint Linux Team return 0; 156ede1e6f8SHighPoint Linux Team msleep(1); 157ede1e6f8SHighPoint Linux Team } 158ede1e6f8SHighPoint Linux Team 159ede1e6f8SHighPoint Linux Team return -1; 160ede1e6f8SHighPoint Linux Team } 161ede1e6f8SHighPoint Linux Team 162ede1e6f8SHighPoint Linux Team static int iop_send_sync_msg(struct hptiop_hba *hba, u32 msg, u32 millisec) 163ede1e6f8SHighPoint Linux Team { 164ede1e6f8SHighPoint Linux Team u32 i; 165ede1e6f8SHighPoint Linux Team 166ede1e6f8SHighPoint Linux Team hba->msg_done = 0; 167ede1e6f8SHighPoint Linux Team 168ede1e6f8SHighPoint Linux Team writel(msg, &hba->iop->inbound_msgaddr0); 169ede1e6f8SHighPoint Linux Team 170ede1e6f8SHighPoint Linux Team hptiop_pci_posting_flush(hba->iop); 171ede1e6f8SHighPoint Linux Team 172ede1e6f8SHighPoint Linux Team for (i = 0; i < millisec; i++) { 173ede1e6f8SHighPoint Linux Team spin_lock_irq(hba->host->host_lock); 174ede1e6f8SHighPoint Linux Team __iop_intr(hba); 175ede1e6f8SHighPoint Linux Team spin_unlock_irq(hba->host->host_lock); 176ede1e6f8SHighPoint Linux Team if (hba->msg_done) 177ede1e6f8SHighPoint Linux Team break; 178ede1e6f8SHighPoint Linux Team msleep(1); 179ede1e6f8SHighPoint Linux Team } 180ede1e6f8SHighPoint Linux Team 181ede1e6f8SHighPoint Linux Team return hba->msg_done? 0 : -1; 182ede1e6f8SHighPoint Linux Team } 183ede1e6f8SHighPoint Linux Team 184ede1e6f8SHighPoint Linux Team static int iop_get_config(struct hptiop_hba *hba, 185ede1e6f8SHighPoint Linux Team struct hpt_iop_request_get_config *config) 186ede1e6f8SHighPoint Linux Team { 187ede1e6f8SHighPoint Linux Team u32 req32; 188ede1e6f8SHighPoint Linux Team struct hpt_iop_request_get_config __iomem *req; 189ede1e6f8SHighPoint Linux Team 190ede1e6f8SHighPoint Linux Team req32 = readl(&hba->iop->inbound_queue); 191ede1e6f8SHighPoint Linux Team if (req32 == IOPMU_QUEUE_EMPTY) 192ede1e6f8SHighPoint Linux Team return -1; 193ede1e6f8SHighPoint Linux Team 194ede1e6f8SHighPoint Linux Team req = (struct hpt_iop_request_get_config __iomem *) 195ede1e6f8SHighPoint Linux Team ((unsigned long)hba->iop + req32); 196ede1e6f8SHighPoint Linux Team 197ede1e6f8SHighPoint Linux Team writel(0, &req->header.flags); 198ede1e6f8SHighPoint Linux Team writel(IOP_REQUEST_TYPE_GET_CONFIG, &req->header.type); 199ede1e6f8SHighPoint Linux Team writel(sizeof(struct hpt_iop_request_get_config), &req->header.size); 200ede1e6f8SHighPoint Linux Team writel(IOP_RESULT_PENDING, &req->header.result); 201ede1e6f8SHighPoint Linux Team 202ede1e6f8SHighPoint Linux Team if (iop_send_sync_request(hba, req, 20000)) { 203ede1e6f8SHighPoint Linux Team dprintk("Get config send cmd failed\n"); 204ede1e6f8SHighPoint Linux Team return -1; 205ede1e6f8SHighPoint Linux Team } 206ede1e6f8SHighPoint Linux Team 207ede1e6f8SHighPoint Linux Team memcpy_fromio(config, req, sizeof(*config)); 208ede1e6f8SHighPoint Linux Team writel(req32, &hba->iop->outbound_queue); 209ede1e6f8SHighPoint Linux Team return 0; 210ede1e6f8SHighPoint Linux Team } 211ede1e6f8SHighPoint Linux Team 212ede1e6f8SHighPoint Linux Team static int iop_set_config(struct hptiop_hba *hba, 213ede1e6f8SHighPoint Linux Team struct hpt_iop_request_set_config *config) 214ede1e6f8SHighPoint Linux Team { 215ede1e6f8SHighPoint Linux Team u32 req32; 216ede1e6f8SHighPoint Linux Team struct hpt_iop_request_set_config __iomem *req; 217ede1e6f8SHighPoint Linux Team 218ede1e6f8SHighPoint Linux Team req32 = readl(&hba->iop->inbound_queue); 219ede1e6f8SHighPoint Linux Team if (req32 == IOPMU_QUEUE_EMPTY) 220ede1e6f8SHighPoint Linux Team return -1; 221ede1e6f8SHighPoint Linux Team 222ede1e6f8SHighPoint Linux Team req = (struct hpt_iop_request_set_config __iomem *) 223ede1e6f8SHighPoint Linux Team ((unsigned long)hba->iop + req32); 224ede1e6f8SHighPoint Linux Team 225ede1e6f8SHighPoint Linux Team memcpy_toio((u8 __iomem *)req + sizeof(struct hpt_iop_request_header), 226ede1e6f8SHighPoint Linux Team (u8 *)config + sizeof(struct hpt_iop_request_header), 227ede1e6f8SHighPoint Linux Team sizeof(struct hpt_iop_request_set_config) - 228ede1e6f8SHighPoint Linux Team sizeof(struct hpt_iop_request_header)); 229ede1e6f8SHighPoint Linux Team 230ede1e6f8SHighPoint Linux Team writel(0, &req->header.flags); 231ede1e6f8SHighPoint Linux Team writel(IOP_REQUEST_TYPE_SET_CONFIG, &req->header.type); 232ede1e6f8SHighPoint Linux Team writel(sizeof(struct hpt_iop_request_set_config), &req->header.size); 233ede1e6f8SHighPoint Linux Team writel(IOP_RESULT_PENDING, &req->header.result); 234ede1e6f8SHighPoint Linux Team 235ede1e6f8SHighPoint Linux Team if (iop_send_sync_request(hba, req, 20000)) { 236ede1e6f8SHighPoint Linux Team dprintk("Set config send cmd failed\n"); 237ede1e6f8SHighPoint Linux Team return -1; 238ede1e6f8SHighPoint Linux Team } 239ede1e6f8SHighPoint Linux Team 240ede1e6f8SHighPoint Linux Team writel(req32, &hba->iop->outbound_queue); 241ede1e6f8SHighPoint Linux Team return 0; 242ede1e6f8SHighPoint Linux Team } 243ede1e6f8SHighPoint Linux Team 244ede1e6f8SHighPoint Linux Team static int hptiop_initialize_iop(struct hptiop_hba *hba) 245ede1e6f8SHighPoint Linux Team { 246ede1e6f8SHighPoint Linux Team struct hpt_iopmu __iomem *iop = hba->iop; 247ede1e6f8SHighPoint Linux Team 248ede1e6f8SHighPoint Linux Team /* enable interrupts */ 249ede1e6f8SHighPoint Linux Team writel(~(IOPMU_OUTBOUND_INT_POSTQUEUE | IOPMU_OUTBOUND_INT_MSG0), 250ede1e6f8SHighPoint Linux Team &iop->outbound_intmask); 251ede1e6f8SHighPoint Linux Team 252ede1e6f8SHighPoint Linux Team hba->initialized = 1; 253ede1e6f8SHighPoint Linux Team 254ede1e6f8SHighPoint Linux Team /* start background tasks */ 255ede1e6f8SHighPoint Linux Team if (iop_send_sync_msg(hba, 256ede1e6f8SHighPoint Linux Team IOPMU_INBOUND_MSG0_START_BACKGROUND_TASK, 5000)) { 257ede1e6f8SHighPoint Linux Team printk(KERN_ERR "scsi%d: fail to start background task\n", 258ede1e6f8SHighPoint Linux Team hba->host->host_no); 259ede1e6f8SHighPoint Linux Team return -1; 260ede1e6f8SHighPoint Linux Team } 261ede1e6f8SHighPoint Linux Team return 0; 262ede1e6f8SHighPoint Linux Team } 263ede1e6f8SHighPoint Linux Team 264ede1e6f8SHighPoint Linux Team static int hptiop_map_pci_bar(struct hptiop_hba *hba) 265ede1e6f8SHighPoint Linux Team { 266ede1e6f8SHighPoint Linux Team u32 mem_base_phy, length; 267ede1e6f8SHighPoint Linux Team void __iomem *mem_base_virt; 268ede1e6f8SHighPoint Linux Team struct pci_dev *pcidev = hba->pcidev; 269ede1e6f8SHighPoint Linux Team 270ede1e6f8SHighPoint Linux Team if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_MEM)) { 271ede1e6f8SHighPoint Linux Team printk(KERN_ERR "scsi%d: pci resource invalid\n", 272ede1e6f8SHighPoint Linux Team hba->host->host_no); 273ede1e6f8SHighPoint Linux Team return -1; 274ede1e6f8SHighPoint Linux Team } 275ede1e6f8SHighPoint Linux Team 276ede1e6f8SHighPoint Linux Team mem_base_phy = pci_resource_start(pcidev, 0); 277ede1e6f8SHighPoint Linux Team length = pci_resource_len(pcidev, 0); 278ede1e6f8SHighPoint Linux Team mem_base_virt = ioremap(mem_base_phy, length); 279ede1e6f8SHighPoint Linux Team 280ede1e6f8SHighPoint Linux Team if (!mem_base_virt) { 281ede1e6f8SHighPoint Linux Team printk(KERN_ERR "scsi%d: Fail to ioremap memory space\n", 282ede1e6f8SHighPoint Linux Team hba->host->host_no); 283ede1e6f8SHighPoint Linux Team return -1; 284ede1e6f8SHighPoint Linux Team } 285ede1e6f8SHighPoint Linux Team 286ede1e6f8SHighPoint Linux Team hba->iop = mem_base_virt; 287ede1e6f8SHighPoint Linux Team dprintk("hptiop_map_pci_bar: iop=%p\n", hba->iop); 288ede1e6f8SHighPoint Linux Team return 0; 289ede1e6f8SHighPoint Linux Team } 290ede1e6f8SHighPoint Linux Team 291ede1e6f8SHighPoint Linux Team static void hptiop_message_callback(struct hptiop_hba *hba, u32 msg) 292ede1e6f8SHighPoint Linux Team { 293ede1e6f8SHighPoint Linux Team dprintk("iop message 0x%x\n", msg); 294ede1e6f8SHighPoint Linux Team 295ede1e6f8SHighPoint Linux Team if (!hba->initialized) 296ede1e6f8SHighPoint Linux Team return; 297ede1e6f8SHighPoint Linux Team 298ede1e6f8SHighPoint Linux Team if (msg == IOPMU_INBOUND_MSG0_RESET) { 299ede1e6f8SHighPoint Linux Team atomic_set(&hba->resetting, 0); 300ede1e6f8SHighPoint Linux Team wake_up(&hba->reset_wq); 301ede1e6f8SHighPoint Linux Team } 302ede1e6f8SHighPoint Linux Team else if (msg <= IOPMU_INBOUND_MSG0_MAX) 303ede1e6f8SHighPoint Linux Team hba->msg_done = 1; 304ede1e6f8SHighPoint Linux Team } 305ede1e6f8SHighPoint Linux Team 306ede1e6f8SHighPoint Linux Team static inline struct hptiop_request *get_req(struct hptiop_hba *hba) 307ede1e6f8SHighPoint Linux Team { 308ede1e6f8SHighPoint Linux Team struct hptiop_request *ret; 309ede1e6f8SHighPoint Linux Team 310ede1e6f8SHighPoint Linux Team dprintk("get_req : req=%p\n", hba->req_list); 311ede1e6f8SHighPoint Linux Team 312ede1e6f8SHighPoint Linux Team ret = hba->req_list; 313ede1e6f8SHighPoint Linux Team if (ret) 314ede1e6f8SHighPoint Linux Team hba->req_list = ret->next; 315ede1e6f8SHighPoint Linux Team 316ede1e6f8SHighPoint Linux Team return ret; 317ede1e6f8SHighPoint Linux Team } 318ede1e6f8SHighPoint Linux Team 319ede1e6f8SHighPoint Linux Team static inline void free_req(struct hptiop_hba *hba, struct hptiop_request *req) 320ede1e6f8SHighPoint Linux Team { 321ede1e6f8SHighPoint Linux Team dprintk("free_req(%d, %p)\n", req->index, req); 322ede1e6f8SHighPoint Linux Team req->next = hba->req_list; 323ede1e6f8SHighPoint Linux Team hba->req_list = req; 324ede1e6f8SHighPoint Linux Team } 325ede1e6f8SHighPoint Linux Team 326db9b6e89SHighPoint Linux Team static void hptiop_host_request_callback(struct hptiop_hba *hba, u32 _tag) 327ede1e6f8SHighPoint Linux Team { 328ede1e6f8SHighPoint Linux Team struct hpt_iop_request_scsi_command *req; 329ede1e6f8SHighPoint Linux Team struct scsi_cmnd *scp; 330db9b6e89SHighPoint Linux Team u32 tag; 331ede1e6f8SHighPoint Linux Team 332db9b6e89SHighPoint Linux Team if (hba->iopintf_v2) { 333db9b6e89SHighPoint Linux Team tag = _tag & ~ IOPMU_QUEUE_REQUEST_RESULT_BIT; 334db9b6e89SHighPoint Linux Team req = hba->reqs[tag].req_virt; 335db9b6e89SHighPoint Linux Team if (likely(_tag & IOPMU_QUEUE_REQUEST_RESULT_BIT)) 336db9b6e89SHighPoint Linux Team req->header.result = IOP_RESULT_SUCCESS; 337db9b6e89SHighPoint Linux Team } else { 338db9b6e89SHighPoint Linux Team tag = _tag; 339db9b6e89SHighPoint Linux Team req = hba->reqs[tag].req_virt; 340db9b6e89SHighPoint Linux Team } 341db9b6e89SHighPoint Linux Team 342ede1e6f8SHighPoint Linux Team dprintk("hptiop_host_request_callback: req=%p, type=%d, " 343ede1e6f8SHighPoint Linux Team "result=%d, context=0x%x tag=%d\n", 344ede1e6f8SHighPoint Linux Team req, req->header.type, req->header.result, 345ede1e6f8SHighPoint Linux Team req->header.context, tag); 346ede1e6f8SHighPoint Linux Team 347ede1e6f8SHighPoint Linux Team BUG_ON(!req->header.result); 348ede1e6f8SHighPoint Linux Team BUG_ON(req->header.type != cpu_to_le32(IOP_REQUEST_TYPE_SCSI_COMMAND)); 349ede1e6f8SHighPoint Linux Team 350ede1e6f8SHighPoint Linux Team scp = hba->reqs[tag].scp; 351ede1e6f8SHighPoint Linux Team 352f9875496SFUJITA Tomonori if (HPT_SCP(scp)->mapped) 353f9875496SFUJITA Tomonori scsi_dma_unmap(scp); 354ede1e6f8SHighPoint Linux Team 355ede1e6f8SHighPoint Linux Team switch (le32_to_cpu(req->header.result)) { 356ede1e6f8SHighPoint Linux Team case IOP_RESULT_SUCCESS: 357ede1e6f8SHighPoint Linux Team scp->result = (DID_OK<<16); 358ede1e6f8SHighPoint Linux Team break; 359ede1e6f8SHighPoint Linux Team case IOP_RESULT_BAD_TARGET: 360ede1e6f8SHighPoint Linux Team scp->result = (DID_BAD_TARGET<<16); 361ede1e6f8SHighPoint Linux Team break; 362ede1e6f8SHighPoint Linux Team case IOP_RESULT_BUSY: 363ede1e6f8SHighPoint Linux Team scp->result = (DID_BUS_BUSY<<16); 364ede1e6f8SHighPoint Linux Team break; 365ede1e6f8SHighPoint Linux Team case IOP_RESULT_RESET: 366ede1e6f8SHighPoint Linux Team scp->result = (DID_RESET<<16); 367ede1e6f8SHighPoint Linux Team break; 368ede1e6f8SHighPoint Linux Team case IOP_RESULT_FAIL: 369ede1e6f8SHighPoint Linux Team scp->result = (DID_ERROR<<16); 370ede1e6f8SHighPoint Linux Team break; 371ede1e6f8SHighPoint Linux Team case IOP_RESULT_INVALID_REQUEST: 372ede1e6f8SHighPoint Linux Team scp->result = (DID_ABORT<<16); 373ede1e6f8SHighPoint Linux Team break; 374ede1e6f8SHighPoint Linux Team case IOP_RESULT_MODE_SENSE_CHECK_CONDITION: 375ede1e6f8SHighPoint Linux Team scp->result = SAM_STAT_CHECK_CONDITION; 376ede1e6f8SHighPoint Linux Team memset(&scp->sense_buffer, 377ede1e6f8SHighPoint Linux Team 0, sizeof(scp->sense_buffer)); 378ede1e6f8SHighPoint Linux Team memcpy(&scp->sense_buffer, 379ede1e6f8SHighPoint Linux Team &req->sg_list, le32_to_cpu(req->dataxfer_length)); 380ede1e6f8SHighPoint Linux Team break; 381ede1e6f8SHighPoint Linux Team 382ede1e6f8SHighPoint Linux Team default: 383ede1e6f8SHighPoint Linux Team scp->result = ((DRIVER_INVALID|SUGGEST_ABORT)<<24) | 384ede1e6f8SHighPoint Linux Team (DID_ABORT<<16); 385ede1e6f8SHighPoint Linux Team break; 386ede1e6f8SHighPoint Linux Team } 387ede1e6f8SHighPoint Linux Team 388ede1e6f8SHighPoint Linux Team dprintk("scsi_done(%p)\n", scp); 389ede1e6f8SHighPoint Linux Team scp->scsi_done(scp); 390ede1e6f8SHighPoint Linux Team free_req(hba, &hba->reqs[tag]); 391ede1e6f8SHighPoint Linux Team } 392ede1e6f8SHighPoint Linux Team 393ede1e6f8SHighPoint Linux Team void hptiop_iop_request_callback(struct hptiop_hba *hba, u32 tag) 394ede1e6f8SHighPoint Linux Team { 395ede1e6f8SHighPoint Linux Team struct hpt_iop_request_header __iomem *req; 396ede1e6f8SHighPoint Linux Team struct hpt_iop_request_ioctl_command __iomem *p; 397ede1e6f8SHighPoint Linux Team struct hpt_ioctl_k *arg; 398ede1e6f8SHighPoint Linux Team 399ede1e6f8SHighPoint Linux Team req = (struct hpt_iop_request_header __iomem *) 400ede1e6f8SHighPoint Linux Team ((unsigned long)hba->iop + tag); 401ede1e6f8SHighPoint Linux Team dprintk("hptiop_iop_request_callback: req=%p, type=%d, " 402ede1e6f8SHighPoint Linux Team "result=%d, context=0x%x tag=%d\n", 403ede1e6f8SHighPoint Linux Team req, readl(&req->type), readl(&req->result), 404ede1e6f8SHighPoint Linux Team readl(&req->context), tag); 405ede1e6f8SHighPoint Linux Team 406ede1e6f8SHighPoint Linux Team BUG_ON(!readl(&req->result)); 407ede1e6f8SHighPoint Linux Team BUG_ON(readl(&req->type) != IOP_REQUEST_TYPE_IOCTL_COMMAND); 408ede1e6f8SHighPoint Linux Team 409ede1e6f8SHighPoint Linux Team p = (struct hpt_iop_request_ioctl_command __iomem *)req; 410ede1e6f8SHighPoint Linux Team arg = (struct hpt_ioctl_k *)(unsigned long) 411ede1e6f8SHighPoint Linux Team (readl(&req->context) | 412ede1e6f8SHighPoint Linux Team ((u64)readl(&req->context_hi32)<<32)); 413ede1e6f8SHighPoint Linux Team 414ede1e6f8SHighPoint Linux Team if (readl(&req->result) == IOP_RESULT_SUCCESS) { 415ede1e6f8SHighPoint Linux Team arg->result = HPT_IOCTL_RESULT_OK; 416ede1e6f8SHighPoint Linux Team 417ede1e6f8SHighPoint Linux Team if (arg->outbuf_size) 418ede1e6f8SHighPoint Linux Team memcpy_fromio(arg->outbuf, 419ede1e6f8SHighPoint Linux Team &p->buf[(readl(&p->inbuf_size) + 3)& ~3], 420ede1e6f8SHighPoint Linux Team arg->outbuf_size); 421ede1e6f8SHighPoint Linux Team 422ede1e6f8SHighPoint Linux Team if (arg->bytes_returned) 423ede1e6f8SHighPoint Linux Team *arg->bytes_returned = arg->outbuf_size; 424ede1e6f8SHighPoint Linux Team } 425ede1e6f8SHighPoint Linux Team else 426ede1e6f8SHighPoint Linux Team arg->result = HPT_IOCTL_RESULT_FAILED; 427ede1e6f8SHighPoint Linux Team 428ede1e6f8SHighPoint Linux Team arg->done(arg); 429ede1e6f8SHighPoint Linux Team writel(tag, &hba->iop->outbound_queue); 430ede1e6f8SHighPoint Linux Team } 431ede1e6f8SHighPoint Linux Team 4327d12e780SDavid Howells static irqreturn_t hptiop_intr(int irq, void *dev_id) 433ede1e6f8SHighPoint Linux Team { 434ede1e6f8SHighPoint Linux Team struct hptiop_hba *hba = dev_id; 435ede1e6f8SHighPoint Linux Team int handled; 436ede1e6f8SHighPoint Linux Team unsigned long flags; 437ede1e6f8SHighPoint Linux Team 438ede1e6f8SHighPoint Linux Team spin_lock_irqsave(hba->host->host_lock, flags); 439ede1e6f8SHighPoint Linux Team handled = __iop_intr(hba); 440ede1e6f8SHighPoint Linux Team spin_unlock_irqrestore(hba->host->host_lock, flags); 441ede1e6f8SHighPoint Linux Team 442ede1e6f8SHighPoint Linux Team return handled; 443ede1e6f8SHighPoint Linux Team } 444ede1e6f8SHighPoint Linux Team 445ede1e6f8SHighPoint Linux Team static int hptiop_buildsgl(struct scsi_cmnd *scp, struct hpt_iopsg *psg) 446ede1e6f8SHighPoint Linux Team { 447ede1e6f8SHighPoint Linux Team struct Scsi_Host *host = scp->device->host; 448ede1e6f8SHighPoint Linux Team struct hptiop_hba *hba = (struct hptiop_hba *)host->hostdata; 449f9875496SFUJITA Tomonori struct scatterlist *sg; 450f9875496SFUJITA Tomonori int idx, nseg; 451ede1e6f8SHighPoint Linux Team 452f9875496SFUJITA Tomonori nseg = scsi_dma_map(scp); 453f9875496SFUJITA Tomonori BUG_ON(nseg < 0); 454f9875496SFUJITA Tomonori if (!nseg) 455f9875496SFUJITA Tomonori return 0; 456ede1e6f8SHighPoint Linux Team 457f9875496SFUJITA Tomonori HPT_SCP(scp)->sgcnt = nseg; 458ede1e6f8SHighPoint Linux Team HPT_SCP(scp)->mapped = 1; 459f9875496SFUJITA Tomonori 460ede1e6f8SHighPoint Linux Team BUG_ON(HPT_SCP(scp)->sgcnt > hba->max_sg_descriptors); 461ede1e6f8SHighPoint Linux Team 462f9875496SFUJITA Tomonori scsi_for_each_sg(scp, sg, HPT_SCP(scp)->sgcnt, idx) { 463f9875496SFUJITA Tomonori psg[idx].pci_address = cpu_to_le64(sg_dma_address(sg)); 464f9875496SFUJITA Tomonori psg[idx].size = cpu_to_le32(sg_dma_len(sg)); 465ede1e6f8SHighPoint Linux Team psg[idx].eot = (idx == HPT_SCP(scp)->sgcnt - 1) ? 466ede1e6f8SHighPoint Linux Team cpu_to_le32(1) : 0; 467ede1e6f8SHighPoint Linux Team } 468ede1e6f8SHighPoint Linux Team return HPT_SCP(scp)->sgcnt; 469ede1e6f8SHighPoint Linux Team } 470ede1e6f8SHighPoint Linux Team 471ede1e6f8SHighPoint Linux Team static int hptiop_queuecommand(struct scsi_cmnd *scp, 472ede1e6f8SHighPoint Linux Team void (*done)(struct scsi_cmnd *)) 473ede1e6f8SHighPoint Linux Team { 474ede1e6f8SHighPoint Linux Team struct Scsi_Host *host = scp->device->host; 475ede1e6f8SHighPoint Linux Team struct hptiop_hba *hba = (struct hptiop_hba *)host->hostdata; 476ede1e6f8SHighPoint Linux Team struct hpt_iop_request_scsi_command *req; 477ede1e6f8SHighPoint Linux Team int sg_count = 0; 478ede1e6f8SHighPoint Linux Team struct hptiop_request *_req; 479ede1e6f8SHighPoint Linux Team 480ede1e6f8SHighPoint Linux Team BUG_ON(!done); 481ede1e6f8SHighPoint Linux Team scp->scsi_done = done; 482ede1e6f8SHighPoint Linux Team 483ede1e6f8SHighPoint Linux Team _req = get_req(hba); 484ede1e6f8SHighPoint Linux Team if (_req == NULL) { 485ede1e6f8SHighPoint Linux Team dprintk("hptiop_queuecmd : no free req\n"); 4864f2ddba3SHighPoint Linux Team return SCSI_MLQUEUE_HOST_BUSY; 487ede1e6f8SHighPoint Linux Team } 488ede1e6f8SHighPoint Linux Team 489ede1e6f8SHighPoint Linux Team _req->scp = scp; 490ede1e6f8SHighPoint Linux Team 491ede1e6f8SHighPoint Linux Team dprintk("hptiop_queuecmd(scp=%p) %d/%d/%d/%d cdb=(%x-%x-%x) " 492ede1e6f8SHighPoint Linux Team "req_index=%d, req=%p\n", 493ede1e6f8SHighPoint Linux Team scp, 494ede1e6f8SHighPoint Linux Team host->host_no, scp->device->channel, 495ede1e6f8SHighPoint Linux Team scp->device->id, scp->device->lun, 496ede1e6f8SHighPoint Linux Team *((u32 *)&scp->cmnd), 497ede1e6f8SHighPoint Linux Team *((u32 *)&scp->cmnd + 1), 498ede1e6f8SHighPoint Linux Team *((u32 *)&scp->cmnd + 2), 499ede1e6f8SHighPoint Linux Team _req->index, _req->req_virt); 500ede1e6f8SHighPoint Linux Team 501ede1e6f8SHighPoint Linux Team scp->result = 0; 502ede1e6f8SHighPoint Linux Team 503ede1e6f8SHighPoint Linux Team if (scp->device->channel || scp->device->lun || 504ede1e6f8SHighPoint Linux Team scp->device->id > hba->max_devices) { 505ede1e6f8SHighPoint Linux Team scp->result = DID_BAD_TARGET << 16; 506ede1e6f8SHighPoint Linux Team free_req(hba, _req); 507ede1e6f8SHighPoint Linux Team goto cmd_done; 508ede1e6f8SHighPoint Linux Team } 509ede1e6f8SHighPoint Linux Team 510db9b6e89SHighPoint Linux Team req = _req->req_virt; 511ede1e6f8SHighPoint Linux Team 512ede1e6f8SHighPoint Linux Team /* build S/G table */ 513ede1e6f8SHighPoint Linux Team sg_count = hptiop_buildsgl(scp, req->sg_list); 514f9875496SFUJITA Tomonori if (!sg_count) 515ede1e6f8SHighPoint Linux Team HPT_SCP(scp)->mapped = 0; 516ede1e6f8SHighPoint Linux Team 517ede1e6f8SHighPoint Linux Team req->header.flags = cpu_to_le32(IOP_REQUEST_FLAG_OUTPUT_CONTEXT); 518ede1e6f8SHighPoint Linux Team req->header.type = cpu_to_le32(IOP_REQUEST_TYPE_SCSI_COMMAND); 519ede1e6f8SHighPoint Linux Team req->header.result = cpu_to_le32(IOP_RESULT_PENDING); 520ede1e6f8SHighPoint Linux Team req->header.context = cpu_to_le32(IOPMU_QUEUE_ADDR_HOST_BIT | 521ede1e6f8SHighPoint Linux Team (u32)_req->index); 522ede1e6f8SHighPoint Linux Team req->header.context_hi32 = 0; 523f9875496SFUJITA Tomonori req->dataxfer_length = cpu_to_le32(scsi_bufflen(scp)); 524ede1e6f8SHighPoint Linux Team req->channel = scp->device->channel; 525ede1e6f8SHighPoint Linux Team req->target = scp->device->id; 526ede1e6f8SHighPoint Linux Team req->lun = scp->device->lun; 527ede1e6f8SHighPoint Linux Team req->header.size = cpu_to_le32( 528ede1e6f8SHighPoint Linux Team sizeof(struct hpt_iop_request_scsi_command) 529ede1e6f8SHighPoint Linux Team - sizeof(struct hpt_iopsg) 530ede1e6f8SHighPoint Linux Team + sg_count * sizeof(struct hpt_iopsg)); 531ede1e6f8SHighPoint Linux Team 532ede1e6f8SHighPoint Linux Team memcpy(req->cdb, scp->cmnd, sizeof(req->cdb)); 533ede1e6f8SHighPoint Linux Team 534db9b6e89SHighPoint Linux Team if (hba->iopintf_v2) { 535db9b6e89SHighPoint Linux Team u32 size_bits; 536db9b6e89SHighPoint Linux Team if (req->header.size < 256) 537db9b6e89SHighPoint Linux Team size_bits = IOPMU_QUEUE_REQUEST_SIZE_BIT; 538db9b6e89SHighPoint Linux Team else if (req->header.size < 512) 539db9b6e89SHighPoint Linux Team size_bits = IOPMU_QUEUE_ADDR_HOST_BIT; 540db9b6e89SHighPoint Linux Team else 541db9b6e89SHighPoint Linux Team size_bits = IOPMU_QUEUE_REQUEST_SIZE_BIT | 542db9b6e89SHighPoint Linux Team IOPMU_QUEUE_ADDR_HOST_BIT; 543db9b6e89SHighPoint Linux Team writel(_req->req_shifted_phy | size_bits, &hba->iop->inbound_queue); 544db9b6e89SHighPoint Linux Team } else 545db9b6e89SHighPoint Linux Team writel(_req->req_shifted_phy | IOPMU_QUEUE_ADDR_HOST_BIT, 546ede1e6f8SHighPoint Linux Team &hba->iop->inbound_queue); 547ede1e6f8SHighPoint Linux Team 548ede1e6f8SHighPoint Linux Team return 0; 549ede1e6f8SHighPoint Linux Team 550ede1e6f8SHighPoint Linux Team cmd_done: 551ede1e6f8SHighPoint Linux Team dprintk("scsi_done(scp=%p)\n", scp); 552ede1e6f8SHighPoint Linux Team scp->scsi_done(scp); 553ede1e6f8SHighPoint Linux Team return 0; 554ede1e6f8SHighPoint Linux Team } 555ede1e6f8SHighPoint Linux Team 556ede1e6f8SHighPoint Linux Team static const char *hptiop_info(struct Scsi_Host *host) 557ede1e6f8SHighPoint Linux Team { 558ede1e6f8SHighPoint Linux Team return driver_name_long; 559ede1e6f8SHighPoint Linux Team } 560ede1e6f8SHighPoint Linux Team 561ede1e6f8SHighPoint Linux Team static int hptiop_reset_hba(struct hptiop_hba *hba) 562ede1e6f8SHighPoint Linux Team { 563ede1e6f8SHighPoint Linux Team if (atomic_xchg(&hba->resetting, 1) == 0) { 564ede1e6f8SHighPoint Linux Team atomic_inc(&hba->reset_count); 565ede1e6f8SHighPoint Linux Team writel(IOPMU_INBOUND_MSG0_RESET, 5668d4fbd3fSHighPoint Linux Team &hba->iop->inbound_msgaddr0); 567ede1e6f8SHighPoint Linux Team hptiop_pci_posting_flush(hba->iop); 568ede1e6f8SHighPoint Linux Team } 569ede1e6f8SHighPoint Linux Team 570ede1e6f8SHighPoint Linux Team wait_event_timeout(hba->reset_wq, 571ede1e6f8SHighPoint Linux Team atomic_read(&hba->resetting) == 0, 60 * HZ); 572ede1e6f8SHighPoint Linux Team 573ede1e6f8SHighPoint Linux Team if (atomic_read(&hba->resetting)) { 574ede1e6f8SHighPoint Linux Team /* IOP is in unkown state, abort reset */ 575ede1e6f8SHighPoint Linux Team printk(KERN_ERR "scsi%d: reset failed\n", hba->host->host_no); 576ede1e6f8SHighPoint Linux Team return -1; 577ede1e6f8SHighPoint Linux Team } 578ede1e6f8SHighPoint Linux Team 579ede1e6f8SHighPoint Linux Team if (iop_send_sync_msg(hba, 580ede1e6f8SHighPoint Linux Team IOPMU_INBOUND_MSG0_START_BACKGROUND_TASK, 5000)) { 581ede1e6f8SHighPoint Linux Team dprintk("scsi%d: fail to start background task\n", 582ede1e6f8SHighPoint Linux Team hba->host->host_no); 583ede1e6f8SHighPoint Linux Team } 584ede1e6f8SHighPoint Linux Team 585ede1e6f8SHighPoint Linux Team return 0; 586ede1e6f8SHighPoint Linux Team } 587ede1e6f8SHighPoint Linux Team 588ede1e6f8SHighPoint Linux Team static int hptiop_reset(struct scsi_cmnd *scp) 589ede1e6f8SHighPoint Linux Team { 590ede1e6f8SHighPoint Linux Team struct Scsi_Host * host = scp->device->host; 591ede1e6f8SHighPoint Linux Team struct hptiop_hba * hba = (struct hptiop_hba *)host->hostdata; 592ede1e6f8SHighPoint Linux Team 593ede1e6f8SHighPoint Linux Team printk(KERN_WARNING "hptiop_reset(%d/%d/%d) scp=%p\n", 594ede1e6f8SHighPoint Linux Team scp->device->host->host_no, scp->device->channel, 595ede1e6f8SHighPoint Linux Team scp->device->id, scp); 596ede1e6f8SHighPoint Linux Team 597ede1e6f8SHighPoint Linux Team return hptiop_reset_hba(hba)? FAILED : SUCCESS; 598ede1e6f8SHighPoint Linux Team } 599ede1e6f8SHighPoint Linux Team 600ede1e6f8SHighPoint Linux Team static int hptiop_adjust_disk_queue_depth(struct scsi_device *sdev, 601ede1e6f8SHighPoint Linux Team int queue_depth) 602ede1e6f8SHighPoint Linux Team { 603ede1e6f8SHighPoint Linux Team if(queue_depth > 256) 604ede1e6f8SHighPoint Linux Team queue_depth = 256; 605ede1e6f8SHighPoint Linux Team scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth); 606ede1e6f8SHighPoint Linux Team return queue_depth; 607ede1e6f8SHighPoint Linux Team } 608ede1e6f8SHighPoint Linux Team 609ede1e6f8SHighPoint Linux Team static ssize_t hptiop_show_version(struct class_device *class_dev, char *buf) 610ede1e6f8SHighPoint Linux Team { 611ede1e6f8SHighPoint Linux Team return snprintf(buf, PAGE_SIZE, "%s\n", driver_ver); 612ede1e6f8SHighPoint Linux Team } 613ede1e6f8SHighPoint Linux Team 614ede1e6f8SHighPoint Linux Team static ssize_t hptiop_show_fw_version(struct class_device *class_dev, char *buf) 615ede1e6f8SHighPoint Linux Team { 616ede1e6f8SHighPoint Linux Team struct Scsi_Host *host = class_to_shost(class_dev); 617ede1e6f8SHighPoint Linux Team struct hptiop_hba *hba = (struct hptiop_hba *)host->hostdata; 618ede1e6f8SHighPoint Linux Team 619ede1e6f8SHighPoint Linux Team return snprintf(buf, PAGE_SIZE, "%d.%d.%d.%d\n", 620ede1e6f8SHighPoint Linux Team hba->firmware_version >> 24, 621ede1e6f8SHighPoint Linux Team (hba->firmware_version >> 16) & 0xff, 622ede1e6f8SHighPoint Linux Team (hba->firmware_version >> 8) & 0xff, 623ede1e6f8SHighPoint Linux Team hba->firmware_version & 0xff); 624ede1e6f8SHighPoint Linux Team } 625ede1e6f8SHighPoint Linux Team 626ede1e6f8SHighPoint Linux Team static struct class_device_attribute hptiop_attr_version = { 627ede1e6f8SHighPoint Linux Team .attr = { 628ede1e6f8SHighPoint Linux Team .name = "driver-version", 629ede1e6f8SHighPoint Linux Team .mode = S_IRUGO, 630ede1e6f8SHighPoint Linux Team }, 631ede1e6f8SHighPoint Linux Team .show = hptiop_show_version, 632ede1e6f8SHighPoint Linux Team }; 633ede1e6f8SHighPoint Linux Team 634ede1e6f8SHighPoint Linux Team static struct class_device_attribute hptiop_attr_fw_version = { 635ede1e6f8SHighPoint Linux Team .attr = { 636ede1e6f8SHighPoint Linux Team .name = "firmware-version", 637ede1e6f8SHighPoint Linux Team .mode = S_IRUGO, 638ede1e6f8SHighPoint Linux Team }, 639ede1e6f8SHighPoint Linux Team .show = hptiop_show_fw_version, 640ede1e6f8SHighPoint Linux Team }; 641ede1e6f8SHighPoint Linux Team 642ede1e6f8SHighPoint Linux Team static struct class_device_attribute *hptiop_attrs[] = { 643ede1e6f8SHighPoint Linux Team &hptiop_attr_version, 644ede1e6f8SHighPoint Linux Team &hptiop_attr_fw_version, 645ede1e6f8SHighPoint Linux Team NULL 646ede1e6f8SHighPoint Linux Team }; 647ede1e6f8SHighPoint Linux Team 648ede1e6f8SHighPoint Linux Team static struct scsi_host_template driver_template = { 649ede1e6f8SHighPoint Linux Team .module = THIS_MODULE, 650ede1e6f8SHighPoint Linux Team .name = driver_name, 651ede1e6f8SHighPoint Linux Team .queuecommand = hptiop_queuecommand, 652ede1e6f8SHighPoint Linux Team .eh_device_reset_handler = hptiop_reset, 653ede1e6f8SHighPoint Linux Team .eh_bus_reset_handler = hptiop_reset, 654ede1e6f8SHighPoint Linux Team .info = hptiop_info, 655ede1e6f8SHighPoint Linux Team .unchecked_isa_dma = 0, 656ede1e6f8SHighPoint Linux Team .emulated = 0, 657ede1e6f8SHighPoint Linux Team .use_clustering = ENABLE_CLUSTERING, 6589cb83c75SFUJITA Tomonori .use_sg_chaining = ENABLE_SG_CHAINING, 659ede1e6f8SHighPoint Linux Team .proc_name = driver_name, 660ede1e6f8SHighPoint Linux Team .shost_attrs = hptiop_attrs, 661ede1e6f8SHighPoint Linux Team .this_id = -1, 662ede1e6f8SHighPoint Linux Team .change_queue_depth = hptiop_adjust_disk_queue_depth, 663ede1e6f8SHighPoint Linux Team }; 664ede1e6f8SHighPoint Linux Team 665ede1e6f8SHighPoint Linux Team static int __devinit hptiop_probe(struct pci_dev *pcidev, 666ede1e6f8SHighPoint Linux Team const struct pci_device_id *id) 667ede1e6f8SHighPoint Linux Team { 668ede1e6f8SHighPoint Linux Team struct Scsi_Host *host = NULL; 669ede1e6f8SHighPoint Linux Team struct hptiop_hba *hba; 670ede1e6f8SHighPoint Linux Team struct hpt_iop_request_get_config iop_config; 671ede1e6f8SHighPoint Linux Team struct hpt_iop_request_set_config set_config; 672ede1e6f8SHighPoint Linux Team dma_addr_t start_phy; 673ede1e6f8SHighPoint Linux Team void *start_virt; 674ede1e6f8SHighPoint Linux Team u32 offset, i, req_size; 675ede1e6f8SHighPoint Linux Team 676ede1e6f8SHighPoint Linux Team dprintk("hptiop_probe(%p)\n", pcidev); 677ede1e6f8SHighPoint Linux Team 678ede1e6f8SHighPoint Linux Team if (pci_enable_device(pcidev)) { 679ede1e6f8SHighPoint Linux Team printk(KERN_ERR "hptiop: fail to enable pci device\n"); 680ede1e6f8SHighPoint Linux Team return -ENODEV; 681ede1e6f8SHighPoint Linux Team } 682ede1e6f8SHighPoint Linux Team 683ede1e6f8SHighPoint Linux Team printk(KERN_INFO "adapter at PCI %d:%d:%d, IRQ %d\n", 684ede1e6f8SHighPoint Linux Team pcidev->bus->number, pcidev->devfn >> 3, pcidev->devfn & 7, 685ede1e6f8SHighPoint Linux Team pcidev->irq); 686ede1e6f8SHighPoint Linux Team 687ede1e6f8SHighPoint Linux Team pci_set_master(pcidev); 688ede1e6f8SHighPoint Linux Team 689ede1e6f8SHighPoint Linux Team /* Enable 64bit DMA if possible */ 690ede1e6f8SHighPoint Linux Team if (pci_set_dma_mask(pcidev, DMA_64BIT_MASK)) { 691ede1e6f8SHighPoint Linux Team if (pci_set_dma_mask(pcidev, DMA_32BIT_MASK)) { 692ede1e6f8SHighPoint Linux Team printk(KERN_ERR "hptiop: fail to set dma_mask\n"); 693ede1e6f8SHighPoint Linux Team goto disable_pci_device; 694ede1e6f8SHighPoint Linux Team } 695ede1e6f8SHighPoint Linux Team } 696ede1e6f8SHighPoint Linux Team 697ede1e6f8SHighPoint Linux Team if (pci_request_regions(pcidev, driver_name)) { 698ede1e6f8SHighPoint Linux Team printk(KERN_ERR "hptiop: pci_request_regions failed\n"); 699ede1e6f8SHighPoint Linux Team goto disable_pci_device; 700ede1e6f8SHighPoint Linux Team } 701ede1e6f8SHighPoint Linux Team 702ede1e6f8SHighPoint Linux Team host = scsi_host_alloc(&driver_template, sizeof(struct hptiop_hba)); 703ede1e6f8SHighPoint Linux Team if (!host) { 704ede1e6f8SHighPoint Linux Team printk(KERN_ERR "hptiop: fail to alloc scsi host\n"); 705ede1e6f8SHighPoint Linux Team goto free_pci_regions; 706ede1e6f8SHighPoint Linux Team } 707ede1e6f8SHighPoint Linux Team 708ede1e6f8SHighPoint Linux Team hba = (struct hptiop_hba *)host->hostdata; 709ede1e6f8SHighPoint Linux Team 710ede1e6f8SHighPoint Linux Team hba->pcidev = pcidev; 711ede1e6f8SHighPoint Linux Team hba->host = host; 712ede1e6f8SHighPoint Linux Team hba->initialized = 0; 713db9b6e89SHighPoint Linux Team hba->iopintf_v2 = 0; 714ede1e6f8SHighPoint Linux Team 715ede1e6f8SHighPoint Linux Team atomic_set(&hba->resetting, 0); 716ede1e6f8SHighPoint Linux Team atomic_set(&hba->reset_count, 0); 717ede1e6f8SHighPoint Linux Team 718ede1e6f8SHighPoint Linux Team init_waitqueue_head(&hba->reset_wq); 719ede1e6f8SHighPoint Linux Team init_waitqueue_head(&hba->ioctl_wq); 720ede1e6f8SHighPoint Linux Team 721ede1e6f8SHighPoint Linux Team host->max_lun = 1; 722ede1e6f8SHighPoint Linux Team host->max_channel = 0; 723ede1e6f8SHighPoint Linux Team host->io_port = 0; 724ede1e6f8SHighPoint Linux Team host->n_io_port = 0; 725ede1e6f8SHighPoint Linux Team host->irq = pcidev->irq; 726ede1e6f8SHighPoint Linux Team 727ede1e6f8SHighPoint Linux Team if (hptiop_map_pci_bar(hba)) 728ede1e6f8SHighPoint Linux Team goto free_scsi_host; 729ede1e6f8SHighPoint Linux Team 730ede1e6f8SHighPoint Linux Team if (iop_wait_ready(hba->iop, 20000)) { 731ede1e6f8SHighPoint Linux Team printk(KERN_ERR "scsi%d: firmware not ready\n", 732ede1e6f8SHighPoint Linux Team hba->host->host_no); 733ede1e6f8SHighPoint Linux Team goto unmap_pci_bar; 734ede1e6f8SHighPoint Linux Team } 735ede1e6f8SHighPoint Linux Team 736ede1e6f8SHighPoint Linux Team if (iop_get_config(hba, &iop_config)) { 737ede1e6f8SHighPoint Linux Team printk(KERN_ERR "scsi%d: get config failed\n", 738ede1e6f8SHighPoint Linux Team hba->host->host_no); 739ede1e6f8SHighPoint Linux Team goto unmap_pci_bar; 740ede1e6f8SHighPoint Linux Team } 741ede1e6f8SHighPoint Linux Team 742ede1e6f8SHighPoint Linux Team hba->max_requests = min(le32_to_cpu(iop_config.max_requests), 743ede1e6f8SHighPoint Linux Team HPTIOP_MAX_REQUESTS); 744ede1e6f8SHighPoint Linux Team hba->max_devices = le32_to_cpu(iop_config.max_devices); 745ede1e6f8SHighPoint Linux Team hba->max_request_size = le32_to_cpu(iop_config.request_size); 746ede1e6f8SHighPoint Linux Team hba->max_sg_descriptors = le32_to_cpu(iop_config.max_sg_count); 747ede1e6f8SHighPoint Linux Team hba->firmware_version = le32_to_cpu(iop_config.firmware_version); 748db9b6e89SHighPoint Linux Team hba->interface_version = le32_to_cpu(iop_config.interface_version); 749ede1e6f8SHighPoint Linux Team hba->sdram_size = le32_to_cpu(iop_config.sdram_size); 750ede1e6f8SHighPoint Linux Team 751db9b6e89SHighPoint Linux Team if (hba->firmware_version > 0x01020000 || 752db9b6e89SHighPoint Linux Team hba->interface_version > 0x01020000) 753db9b6e89SHighPoint Linux Team hba->iopintf_v2 = 1; 754db9b6e89SHighPoint Linux Team 755ede1e6f8SHighPoint Linux Team host->max_sectors = le32_to_cpu(iop_config.data_transfer_length) >> 9; 756ede1e6f8SHighPoint Linux Team host->max_id = le32_to_cpu(iop_config.max_devices); 757ede1e6f8SHighPoint Linux Team host->sg_tablesize = le32_to_cpu(iop_config.max_sg_count); 758ede1e6f8SHighPoint Linux Team host->can_queue = le32_to_cpu(iop_config.max_requests); 759ede1e6f8SHighPoint Linux Team host->cmd_per_lun = le32_to_cpu(iop_config.max_requests); 760ede1e6f8SHighPoint Linux Team host->max_cmd_len = 16; 761ede1e6f8SHighPoint Linux Team 762db9b6e89SHighPoint Linux Team req_size = sizeof(struct hpt_iop_request_scsi_command) 763db9b6e89SHighPoint Linux Team + sizeof(struct hpt_iopsg) * (hba->max_sg_descriptors - 1); 764db9b6e89SHighPoint Linux Team if ((req_size & 0x1f) != 0) 765db9b6e89SHighPoint Linux Team req_size = (req_size + 0x1f) & ~0x1f; 766db9b6e89SHighPoint Linux Team 767db9b6e89SHighPoint Linux Team memset(&set_config, 0, sizeof(struct hpt_iop_request_set_config)); 768ede1e6f8SHighPoint Linux Team set_config.iop_id = cpu_to_le32(host->host_no); 769db9b6e89SHighPoint Linux Team set_config.vbus_id = cpu_to_le16(host->host_no); 770db9b6e89SHighPoint Linux Team set_config.max_host_request_size = cpu_to_le16(req_size); 771ede1e6f8SHighPoint Linux Team 772ede1e6f8SHighPoint Linux Team if (iop_set_config(hba, &set_config)) { 773ede1e6f8SHighPoint Linux Team printk(KERN_ERR "scsi%d: set config failed\n", 774ede1e6f8SHighPoint Linux Team hba->host->host_no); 775ede1e6f8SHighPoint Linux Team goto unmap_pci_bar; 776ede1e6f8SHighPoint Linux Team } 777ede1e6f8SHighPoint Linux Team 778ede1e6f8SHighPoint Linux Team pci_set_drvdata(pcidev, host); 779ede1e6f8SHighPoint Linux Team 7801d6f359aSThomas Gleixner if (request_irq(pcidev->irq, hptiop_intr, IRQF_SHARED, 781ede1e6f8SHighPoint Linux Team driver_name, hba)) { 782ede1e6f8SHighPoint Linux Team printk(KERN_ERR "scsi%d: request irq %d failed\n", 783ede1e6f8SHighPoint Linux Team hba->host->host_no, pcidev->irq); 7843e74051bSChristoph Hellwig goto unmap_pci_bar; 785ede1e6f8SHighPoint Linux Team } 786ede1e6f8SHighPoint Linux Team 787ede1e6f8SHighPoint Linux Team /* Allocate request mem */ 788ede1e6f8SHighPoint Linux Team 789ede1e6f8SHighPoint Linux Team dprintk("req_size=%d, max_requests=%d\n", req_size, hba->max_requests); 790ede1e6f8SHighPoint Linux Team 791ede1e6f8SHighPoint Linux Team hba->req_size = req_size; 792ede1e6f8SHighPoint Linux Team start_virt = dma_alloc_coherent(&pcidev->dev, 793ede1e6f8SHighPoint Linux Team hba->req_size*hba->max_requests + 0x20, 794ede1e6f8SHighPoint Linux Team &start_phy, GFP_KERNEL); 795ede1e6f8SHighPoint Linux Team 796ede1e6f8SHighPoint Linux Team if (!start_virt) { 797ede1e6f8SHighPoint Linux Team printk(KERN_ERR "scsi%d: fail to alloc request mem\n", 798ede1e6f8SHighPoint Linux Team hba->host->host_no); 799ede1e6f8SHighPoint Linux Team goto free_request_irq; 800ede1e6f8SHighPoint Linux Team } 801ede1e6f8SHighPoint Linux Team 802ede1e6f8SHighPoint Linux Team hba->dma_coherent = start_virt; 803ede1e6f8SHighPoint Linux Team hba->dma_coherent_handle = start_phy; 804ede1e6f8SHighPoint Linux Team 805ede1e6f8SHighPoint Linux Team if ((start_phy & 0x1f) != 0) 806ede1e6f8SHighPoint Linux Team { 807ede1e6f8SHighPoint Linux Team offset = ((start_phy + 0x1f) & ~0x1f) - start_phy; 808ede1e6f8SHighPoint Linux Team start_phy += offset; 809ede1e6f8SHighPoint Linux Team start_virt += offset; 810ede1e6f8SHighPoint Linux Team } 811ede1e6f8SHighPoint Linux Team 812ede1e6f8SHighPoint Linux Team hba->req_list = start_virt; 813ede1e6f8SHighPoint Linux Team for (i = 0; i < hba->max_requests; i++) { 814ede1e6f8SHighPoint Linux Team hba->reqs[i].next = NULL; 815ede1e6f8SHighPoint Linux Team hba->reqs[i].req_virt = start_virt; 816ede1e6f8SHighPoint Linux Team hba->reqs[i].req_shifted_phy = start_phy >> 5; 817ede1e6f8SHighPoint Linux Team hba->reqs[i].index = i; 818ede1e6f8SHighPoint Linux Team free_req(hba, &hba->reqs[i]); 819ede1e6f8SHighPoint Linux Team start_virt = (char *)start_virt + hba->req_size; 820ede1e6f8SHighPoint Linux Team start_phy = start_phy + hba->req_size; 821ede1e6f8SHighPoint Linux Team } 822ede1e6f8SHighPoint Linux Team 823ede1e6f8SHighPoint Linux Team /* Enable Interrupt and start background task */ 824ede1e6f8SHighPoint Linux Team if (hptiop_initialize_iop(hba)) 825ede1e6f8SHighPoint Linux Team goto free_request_mem; 826ede1e6f8SHighPoint Linux Team 8273e74051bSChristoph Hellwig if (scsi_add_host(host, &pcidev->dev)) { 8283e74051bSChristoph Hellwig printk(KERN_ERR "scsi%d: scsi_add_host failed\n", 8293e74051bSChristoph Hellwig hba->host->host_no); 8303e74051bSChristoph Hellwig goto free_request_mem; 8313e74051bSChristoph Hellwig } 8323e74051bSChristoph Hellwig 833ede1e6f8SHighPoint Linux Team 834ede1e6f8SHighPoint Linux Team scsi_scan_host(host); 835ede1e6f8SHighPoint Linux Team 836ede1e6f8SHighPoint Linux Team dprintk("scsi%d: hptiop_probe successfully\n", hba->host->host_no); 837ede1e6f8SHighPoint Linux Team return 0; 838ede1e6f8SHighPoint Linux Team 839ede1e6f8SHighPoint Linux Team free_request_mem: 840ede1e6f8SHighPoint Linux Team dma_free_coherent(&hba->pcidev->dev, 841ede1e6f8SHighPoint Linux Team hba->req_size*hba->max_requests + 0x20, 842ede1e6f8SHighPoint Linux Team hba->dma_coherent, hba->dma_coherent_handle); 843ede1e6f8SHighPoint Linux Team 844ede1e6f8SHighPoint Linux Team free_request_irq: 845ede1e6f8SHighPoint Linux Team free_irq(hba->pcidev->irq, hba); 846ede1e6f8SHighPoint Linux Team 847ede1e6f8SHighPoint Linux Team unmap_pci_bar: 848ede1e6f8SHighPoint Linux Team iounmap(hba->iop); 849ede1e6f8SHighPoint Linux Team 850ede1e6f8SHighPoint Linux Team free_pci_regions: 851ede1e6f8SHighPoint Linux Team pci_release_regions(pcidev) ; 852ede1e6f8SHighPoint Linux Team 853ede1e6f8SHighPoint Linux Team free_scsi_host: 854ede1e6f8SHighPoint Linux Team scsi_host_put(host); 855ede1e6f8SHighPoint Linux Team 856ede1e6f8SHighPoint Linux Team disable_pci_device: 857ede1e6f8SHighPoint Linux Team pci_disable_device(pcidev); 858ede1e6f8SHighPoint Linux Team 859ede1e6f8SHighPoint Linux Team dprintk("scsi%d: hptiop_probe fail\n", host->host_no); 860ede1e6f8SHighPoint Linux Team return -ENODEV; 861ede1e6f8SHighPoint Linux Team } 862ede1e6f8SHighPoint Linux Team 863ede1e6f8SHighPoint Linux Team static void hptiop_shutdown(struct pci_dev *pcidev) 864ede1e6f8SHighPoint Linux Team { 865ede1e6f8SHighPoint Linux Team struct Scsi_Host *host = pci_get_drvdata(pcidev); 866ede1e6f8SHighPoint Linux Team struct hptiop_hba *hba = (struct hptiop_hba *)host->hostdata; 867ede1e6f8SHighPoint Linux Team struct hpt_iopmu __iomem *iop = hba->iop; 868ede1e6f8SHighPoint Linux Team u32 int_mask; 869ede1e6f8SHighPoint Linux Team 870ede1e6f8SHighPoint Linux Team dprintk("hptiop_shutdown(%p)\n", hba); 871ede1e6f8SHighPoint Linux Team 872ede1e6f8SHighPoint Linux Team /* stop the iop */ 873ede1e6f8SHighPoint Linux Team if (iop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_SHUTDOWN, 60000)) 874ede1e6f8SHighPoint Linux Team printk(KERN_ERR "scsi%d: shutdown the iop timeout\n", 875ede1e6f8SHighPoint Linux Team hba->host->host_no); 876ede1e6f8SHighPoint Linux Team 877ede1e6f8SHighPoint Linux Team /* disable all outbound interrupts */ 878ede1e6f8SHighPoint Linux Team int_mask = readl(&iop->outbound_intmask); 879ede1e6f8SHighPoint Linux Team writel(int_mask | 880ede1e6f8SHighPoint Linux Team IOPMU_OUTBOUND_INT_MSG0 | IOPMU_OUTBOUND_INT_POSTQUEUE, 881ede1e6f8SHighPoint Linux Team &iop->outbound_intmask); 882ede1e6f8SHighPoint Linux Team hptiop_pci_posting_flush(iop); 883ede1e6f8SHighPoint Linux Team } 884ede1e6f8SHighPoint Linux Team 885ede1e6f8SHighPoint Linux Team static void hptiop_remove(struct pci_dev *pcidev) 886ede1e6f8SHighPoint Linux Team { 887ede1e6f8SHighPoint Linux Team struct Scsi_Host *host = pci_get_drvdata(pcidev); 888ede1e6f8SHighPoint Linux Team struct hptiop_hba *hba = (struct hptiop_hba *)host->hostdata; 889ede1e6f8SHighPoint Linux Team 890ede1e6f8SHighPoint Linux Team dprintk("scsi%d: hptiop_remove\n", hba->host->host_no); 891ede1e6f8SHighPoint Linux Team 8924f2ddba3SHighPoint Linux Team scsi_remove_host(host); 8934f2ddba3SHighPoint Linux Team 894ede1e6f8SHighPoint Linux Team hptiop_shutdown(pcidev); 895ede1e6f8SHighPoint Linux Team 896ede1e6f8SHighPoint Linux Team free_irq(hba->pcidev->irq, hba); 897ede1e6f8SHighPoint Linux Team 898ede1e6f8SHighPoint Linux Team dma_free_coherent(&hba->pcidev->dev, 899ede1e6f8SHighPoint Linux Team hba->req_size * hba->max_requests + 0x20, 900ede1e6f8SHighPoint Linux Team hba->dma_coherent, 901ede1e6f8SHighPoint Linux Team hba->dma_coherent_handle); 902ede1e6f8SHighPoint Linux Team 903ede1e6f8SHighPoint Linux Team iounmap(hba->iop); 904ede1e6f8SHighPoint Linux Team 905ede1e6f8SHighPoint Linux Team pci_release_regions(hba->pcidev); 906ede1e6f8SHighPoint Linux Team pci_set_drvdata(hba->pcidev, NULL); 907ede1e6f8SHighPoint Linux Team pci_disable_device(hba->pcidev); 908ede1e6f8SHighPoint Linux Team 909ede1e6f8SHighPoint Linux Team scsi_host_put(host); 910ede1e6f8SHighPoint Linux Team } 911ede1e6f8SHighPoint Linux Team 912ede1e6f8SHighPoint Linux Team static struct pci_device_id hptiop_id_table[] = { 913db9b6e89SHighPoint Linux Team { PCI_VDEVICE(TTI, 0x3220) }, 914db9b6e89SHighPoint Linux Team { PCI_VDEVICE(TTI, 0x3320) }, 915db9b6e89SHighPoint Linux Team { PCI_VDEVICE(TTI, 0x3520) }, 916db9b6e89SHighPoint Linux Team { PCI_VDEVICE(TTI, 0x4320) }, 917ede1e6f8SHighPoint Linux Team {}, 918ede1e6f8SHighPoint Linux Team }; 919ede1e6f8SHighPoint Linux Team 920ede1e6f8SHighPoint Linux Team MODULE_DEVICE_TABLE(pci, hptiop_id_table); 921ede1e6f8SHighPoint Linux Team 922ede1e6f8SHighPoint Linux Team static struct pci_driver hptiop_pci_driver = { 923ede1e6f8SHighPoint Linux Team .name = driver_name, 924ede1e6f8SHighPoint Linux Team .id_table = hptiop_id_table, 925ede1e6f8SHighPoint Linux Team .probe = hptiop_probe, 926ede1e6f8SHighPoint Linux Team .remove = hptiop_remove, 927ede1e6f8SHighPoint Linux Team .shutdown = hptiop_shutdown, 928ede1e6f8SHighPoint Linux Team }; 929ede1e6f8SHighPoint Linux Team 930ede1e6f8SHighPoint Linux Team static int __init hptiop_module_init(void) 931ede1e6f8SHighPoint Linux Team { 932ede1e6f8SHighPoint Linux Team printk(KERN_INFO "%s %s\n", driver_name_long, driver_ver); 9333e74051bSChristoph Hellwig return pci_register_driver(&hptiop_pci_driver); 934ede1e6f8SHighPoint Linux Team } 935ede1e6f8SHighPoint Linux Team 936ede1e6f8SHighPoint Linux Team static void __exit hptiop_module_exit(void) 937ede1e6f8SHighPoint Linux Team { 938ede1e6f8SHighPoint Linux Team pci_unregister_driver(&hptiop_pci_driver); 939ede1e6f8SHighPoint Linux Team } 940ede1e6f8SHighPoint Linux Team 941ede1e6f8SHighPoint Linux Team 942ede1e6f8SHighPoint Linux Team module_init(hptiop_module_init); 943ede1e6f8SHighPoint Linux Team module_exit(hptiop_module_exit); 944ede1e6f8SHighPoint Linux Team 945ede1e6f8SHighPoint Linux Team MODULE_LICENSE("GPL"); 946db9b6e89SHighPoint Linux Team 947