1ede1e6f8SHighPoint Linux Team /* 2ede1e6f8SHighPoint Linux Team * HighPoint RR3xxx controller driver for Linux 3ede1e6f8SHighPoint Linux Team * Copyright (C) 2006 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"; 45ede1e6f8SHighPoint Linux Team static const char driver_ver[] = "v1.0 (060426)"; 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 { 79ede1e6f8SHighPoint Linux Team if ((tag & IOPMU_QUEUE_MASK_HOST_BITS) == 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 326ede1e6f8SHighPoint 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; 330ede1e6f8SHighPoint Linux Team 331ede1e6f8SHighPoint Linux Team req = (struct hpt_iop_request_scsi_command *)hba->reqs[tag].req_virt; 332ede1e6f8SHighPoint Linux Team dprintk("hptiop_host_request_callback: req=%p, type=%d, " 333ede1e6f8SHighPoint Linux Team "result=%d, context=0x%x tag=%d\n", 334ede1e6f8SHighPoint Linux Team req, req->header.type, req->header.result, 335ede1e6f8SHighPoint Linux Team req->header.context, tag); 336ede1e6f8SHighPoint Linux Team 337ede1e6f8SHighPoint Linux Team BUG_ON(!req->header.result); 338ede1e6f8SHighPoint Linux Team BUG_ON(req->header.type != cpu_to_le32(IOP_REQUEST_TYPE_SCSI_COMMAND)); 339ede1e6f8SHighPoint Linux Team 340ede1e6f8SHighPoint Linux Team scp = hba->reqs[tag].scp; 341ede1e6f8SHighPoint Linux Team 342f9875496SFUJITA Tomonori if (HPT_SCP(scp)->mapped) 343f9875496SFUJITA Tomonori scsi_dma_unmap(scp); 344ede1e6f8SHighPoint Linux Team 345ede1e6f8SHighPoint Linux Team switch (le32_to_cpu(req->header.result)) { 346ede1e6f8SHighPoint Linux Team case IOP_RESULT_SUCCESS: 347ede1e6f8SHighPoint Linux Team scp->result = (DID_OK<<16); 348ede1e6f8SHighPoint Linux Team break; 349ede1e6f8SHighPoint Linux Team case IOP_RESULT_BAD_TARGET: 350ede1e6f8SHighPoint Linux Team scp->result = (DID_BAD_TARGET<<16); 351ede1e6f8SHighPoint Linux Team break; 352ede1e6f8SHighPoint Linux Team case IOP_RESULT_BUSY: 353ede1e6f8SHighPoint Linux Team scp->result = (DID_BUS_BUSY<<16); 354ede1e6f8SHighPoint Linux Team break; 355ede1e6f8SHighPoint Linux Team case IOP_RESULT_RESET: 356ede1e6f8SHighPoint Linux Team scp->result = (DID_RESET<<16); 357ede1e6f8SHighPoint Linux Team break; 358ede1e6f8SHighPoint Linux Team case IOP_RESULT_FAIL: 359ede1e6f8SHighPoint Linux Team scp->result = (DID_ERROR<<16); 360ede1e6f8SHighPoint Linux Team break; 361ede1e6f8SHighPoint Linux Team case IOP_RESULT_INVALID_REQUEST: 362ede1e6f8SHighPoint Linux Team scp->result = (DID_ABORT<<16); 363ede1e6f8SHighPoint Linux Team break; 364ede1e6f8SHighPoint Linux Team case IOP_RESULT_MODE_SENSE_CHECK_CONDITION: 365ede1e6f8SHighPoint Linux Team scp->result = SAM_STAT_CHECK_CONDITION; 366ede1e6f8SHighPoint Linux Team memset(&scp->sense_buffer, 367ede1e6f8SHighPoint Linux Team 0, sizeof(scp->sense_buffer)); 368ede1e6f8SHighPoint Linux Team memcpy(&scp->sense_buffer, 369ede1e6f8SHighPoint Linux Team &req->sg_list, le32_to_cpu(req->dataxfer_length)); 370ede1e6f8SHighPoint Linux Team break; 371ede1e6f8SHighPoint Linux Team 372ede1e6f8SHighPoint Linux Team default: 373ede1e6f8SHighPoint Linux Team scp->result = ((DRIVER_INVALID|SUGGEST_ABORT)<<24) | 374ede1e6f8SHighPoint Linux Team (DID_ABORT<<16); 375ede1e6f8SHighPoint Linux Team break; 376ede1e6f8SHighPoint Linux Team } 377ede1e6f8SHighPoint Linux Team 378ede1e6f8SHighPoint Linux Team dprintk("scsi_done(%p)\n", scp); 379ede1e6f8SHighPoint Linux Team scp->scsi_done(scp); 380ede1e6f8SHighPoint Linux Team free_req(hba, &hba->reqs[tag]); 381ede1e6f8SHighPoint Linux Team } 382ede1e6f8SHighPoint Linux Team 383ede1e6f8SHighPoint Linux Team void hptiop_iop_request_callback(struct hptiop_hba *hba, u32 tag) 384ede1e6f8SHighPoint Linux Team { 385ede1e6f8SHighPoint Linux Team struct hpt_iop_request_header __iomem *req; 386ede1e6f8SHighPoint Linux Team struct hpt_iop_request_ioctl_command __iomem *p; 387ede1e6f8SHighPoint Linux Team struct hpt_ioctl_k *arg; 388ede1e6f8SHighPoint Linux Team 389ede1e6f8SHighPoint Linux Team req = (struct hpt_iop_request_header __iomem *) 390ede1e6f8SHighPoint Linux Team ((unsigned long)hba->iop + tag); 391ede1e6f8SHighPoint Linux Team dprintk("hptiop_iop_request_callback: req=%p, type=%d, " 392ede1e6f8SHighPoint Linux Team "result=%d, context=0x%x tag=%d\n", 393ede1e6f8SHighPoint Linux Team req, readl(&req->type), readl(&req->result), 394ede1e6f8SHighPoint Linux Team readl(&req->context), tag); 395ede1e6f8SHighPoint Linux Team 396ede1e6f8SHighPoint Linux Team BUG_ON(!readl(&req->result)); 397ede1e6f8SHighPoint Linux Team BUG_ON(readl(&req->type) != IOP_REQUEST_TYPE_IOCTL_COMMAND); 398ede1e6f8SHighPoint Linux Team 399ede1e6f8SHighPoint Linux Team p = (struct hpt_iop_request_ioctl_command __iomem *)req; 400ede1e6f8SHighPoint Linux Team arg = (struct hpt_ioctl_k *)(unsigned long) 401ede1e6f8SHighPoint Linux Team (readl(&req->context) | 402ede1e6f8SHighPoint Linux Team ((u64)readl(&req->context_hi32)<<32)); 403ede1e6f8SHighPoint Linux Team 404ede1e6f8SHighPoint Linux Team if (readl(&req->result) == IOP_RESULT_SUCCESS) { 405ede1e6f8SHighPoint Linux Team arg->result = HPT_IOCTL_RESULT_OK; 406ede1e6f8SHighPoint Linux Team 407ede1e6f8SHighPoint Linux Team if (arg->outbuf_size) 408ede1e6f8SHighPoint Linux Team memcpy_fromio(arg->outbuf, 409ede1e6f8SHighPoint Linux Team &p->buf[(readl(&p->inbuf_size) + 3)& ~3], 410ede1e6f8SHighPoint Linux Team arg->outbuf_size); 411ede1e6f8SHighPoint Linux Team 412ede1e6f8SHighPoint Linux Team if (arg->bytes_returned) 413ede1e6f8SHighPoint Linux Team *arg->bytes_returned = arg->outbuf_size; 414ede1e6f8SHighPoint Linux Team } 415ede1e6f8SHighPoint Linux Team else 416ede1e6f8SHighPoint Linux Team arg->result = HPT_IOCTL_RESULT_FAILED; 417ede1e6f8SHighPoint Linux Team 418ede1e6f8SHighPoint Linux Team arg->done(arg); 419ede1e6f8SHighPoint Linux Team writel(tag, &hba->iop->outbound_queue); 420ede1e6f8SHighPoint Linux Team } 421ede1e6f8SHighPoint Linux Team 4227d12e780SDavid Howells static irqreturn_t hptiop_intr(int irq, void *dev_id) 423ede1e6f8SHighPoint Linux Team { 424ede1e6f8SHighPoint Linux Team struct hptiop_hba *hba = dev_id; 425ede1e6f8SHighPoint Linux Team int handled; 426ede1e6f8SHighPoint Linux Team unsigned long flags; 427ede1e6f8SHighPoint Linux Team 428ede1e6f8SHighPoint Linux Team spin_lock_irqsave(hba->host->host_lock, flags); 429ede1e6f8SHighPoint Linux Team handled = __iop_intr(hba); 430ede1e6f8SHighPoint Linux Team spin_unlock_irqrestore(hba->host->host_lock, flags); 431ede1e6f8SHighPoint Linux Team 432ede1e6f8SHighPoint Linux Team return handled; 433ede1e6f8SHighPoint Linux Team } 434ede1e6f8SHighPoint Linux Team 435ede1e6f8SHighPoint Linux Team static int hptiop_buildsgl(struct scsi_cmnd *scp, struct hpt_iopsg *psg) 436ede1e6f8SHighPoint Linux Team { 437ede1e6f8SHighPoint Linux Team struct Scsi_Host *host = scp->device->host; 438ede1e6f8SHighPoint Linux Team struct hptiop_hba *hba = (struct hptiop_hba *)host->hostdata; 439f9875496SFUJITA Tomonori struct scatterlist *sg; 440f9875496SFUJITA Tomonori int idx, nseg; 441ede1e6f8SHighPoint Linux Team 442f9875496SFUJITA Tomonori nseg = scsi_dma_map(scp); 443f9875496SFUJITA Tomonori BUG_ON(nseg < 0); 444f9875496SFUJITA Tomonori if (!nseg) 445f9875496SFUJITA Tomonori return 0; 446ede1e6f8SHighPoint Linux Team 447f9875496SFUJITA Tomonori HPT_SCP(scp)->sgcnt = nseg; 448ede1e6f8SHighPoint Linux Team HPT_SCP(scp)->mapped = 1; 449f9875496SFUJITA Tomonori 450ede1e6f8SHighPoint Linux Team BUG_ON(HPT_SCP(scp)->sgcnt > hba->max_sg_descriptors); 451ede1e6f8SHighPoint Linux Team 452f9875496SFUJITA Tomonori scsi_for_each_sg(scp, sg, HPT_SCP(scp)->sgcnt, idx) { 453f9875496SFUJITA Tomonori psg[idx].pci_address = cpu_to_le64(sg_dma_address(sg)); 454f9875496SFUJITA Tomonori psg[idx].size = cpu_to_le32(sg_dma_len(sg)); 455ede1e6f8SHighPoint Linux Team psg[idx].eot = (idx == HPT_SCP(scp)->sgcnt - 1) ? 456ede1e6f8SHighPoint Linux Team cpu_to_le32(1) : 0; 457ede1e6f8SHighPoint Linux Team } 458ede1e6f8SHighPoint Linux Team return HPT_SCP(scp)->sgcnt; 459ede1e6f8SHighPoint Linux Team } 460ede1e6f8SHighPoint Linux Team 461ede1e6f8SHighPoint Linux Team static int hptiop_queuecommand(struct scsi_cmnd *scp, 462ede1e6f8SHighPoint Linux Team void (*done)(struct scsi_cmnd *)) 463ede1e6f8SHighPoint Linux Team { 464ede1e6f8SHighPoint Linux Team struct Scsi_Host *host = scp->device->host; 465ede1e6f8SHighPoint Linux Team struct hptiop_hba *hba = (struct hptiop_hba *)host->hostdata; 466ede1e6f8SHighPoint Linux Team struct hpt_iop_request_scsi_command *req; 467ede1e6f8SHighPoint Linux Team int sg_count = 0; 468ede1e6f8SHighPoint Linux Team struct hptiop_request *_req; 469ede1e6f8SHighPoint Linux Team 470ede1e6f8SHighPoint Linux Team BUG_ON(!done); 471ede1e6f8SHighPoint Linux Team scp->scsi_done = done; 472ede1e6f8SHighPoint Linux Team 473ede1e6f8SHighPoint Linux Team _req = get_req(hba); 474ede1e6f8SHighPoint Linux Team if (_req == NULL) { 475ede1e6f8SHighPoint Linux Team dprintk("hptiop_queuecmd : no free req\n"); 4764f2ddba3SHighPoint Linux Team return SCSI_MLQUEUE_HOST_BUSY; 477ede1e6f8SHighPoint Linux Team } 478ede1e6f8SHighPoint Linux Team 479ede1e6f8SHighPoint Linux Team _req->scp = scp; 480ede1e6f8SHighPoint Linux Team 481ede1e6f8SHighPoint Linux Team dprintk("hptiop_queuecmd(scp=%p) %d/%d/%d/%d cdb=(%x-%x-%x) " 482ede1e6f8SHighPoint Linux Team "req_index=%d, req=%p\n", 483ede1e6f8SHighPoint Linux Team scp, 484ede1e6f8SHighPoint Linux Team host->host_no, scp->device->channel, 485ede1e6f8SHighPoint Linux Team scp->device->id, scp->device->lun, 486ede1e6f8SHighPoint Linux Team *((u32 *)&scp->cmnd), 487ede1e6f8SHighPoint Linux Team *((u32 *)&scp->cmnd + 1), 488ede1e6f8SHighPoint Linux Team *((u32 *)&scp->cmnd + 2), 489ede1e6f8SHighPoint Linux Team _req->index, _req->req_virt); 490ede1e6f8SHighPoint Linux Team 491ede1e6f8SHighPoint Linux Team scp->result = 0; 492ede1e6f8SHighPoint Linux Team 493ede1e6f8SHighPoint Linux Team if (scp->device->channel || scp->device->lun || 494ede1e6f8SHighPoint Linux Team scp->device->id > hba->max_devices) { 495ede1e6f8SHighPoint Linux Team scp->result = DID_BAD_TARGET << 16; 496ede1e6f8SHighPoint Linux Team free_req(hba, _req); 497ede1e6f8SHighPoint Linux Team goto cmd_done; 498ede1e6f8SHighPoint Linux Team } 499ede1e6f8SHighPoint Linux Team 500ede1e6f8SHighPoint Linux Team req = (struct hpt_iop_request_scsi_command *)_req->req_virt; 501ede1e6f8SHighPoint Linux Team 502ede1e6f8SHighPoint Linux Team /* build S/G table */ 503ede1e6f8SHighPoint Linux Team sg_count = hptiop_buildsgl(scp, req->sg_list); 504f9875496SFUJITA Tomonori if (!sg_count) 505ede1e6f8SHighPoint Linux Team HPT_SCP(scp)->mapped = 0; 506ede1e6f8SHighPoint Linux Team 507ede1e6f8SHighPoint Linux Team req->header.flags = cpu_to_le32(IOP_REQUEST_FLAG_OUTPUT_CONTEXT); 508ede1e6f8SHighPoint Linux Team req->header.type = cpu_to_le32(IOP_REQUEST_TYPE_SCSI_COMMAND); 509ede1e6f8SHighPoint Linux Team req->header.result = cpu_to_le32(IOP_RESULT_PENDING); 510ede1e6f8SHighPoint Linux Team req->header.context = cpu_to_le32(IOPMU_QUEUE_ADDR_HOST_BIT | 511ede1e6f8SHighPoint Linux Team (u32)_req->index); 512ede1e6f8SHighPoint Linux Team req->header.context_hi32 = 0; 513f9875496SFUJITA Tomonori req->dataxfer_length = cpu_to_le32(scsi_bufflen(scp)); 514ede1e6f8SHighPoint Linux Team req->channel = scp->device->channel; 515ede1e6f8SHighPoint Linux Team req->target = scp->device->id; 516ede1e6f8SHighPoint Linux Team req->lun = scp->device->lun; 517ede1e6f8SHighPoint Linux Team req->header.size = cpu_to_le32( 518ede1e6f8SHighPoint Linux Team sizeof(struct hpt_iop_request_scsi_command) 519ede1e6f8SHighPoint Linux Team - sizeof(struct hpt_iopsg) 520ede1e6f8SHighPoint Linux Team + sg_count * sizeof(struct hpt_iopsg)); 521ede1e6f8SHighPoint Linux Team 522ede1e6f8SHighPoint Linux Team memcpy(req->cdb, scp->cmnd, sizeof(req->cdb)); 523ede1e6f8SHighPoint Linux Team 524ede1e6f8SHighPoint Linux Team writel(IOPMU_QUEUE_ADDR_HOST_BIT | _req->req_shifted_phy, 525ede1e6f8SHighPoint Linux Team &hba->iop->inbound_queue); 526ede1e6f8SHighPoint Linux Team 527ede1e6f8SHighPoint Linux Team return 0; 528ede1e6f8SHighPoint Linux Team 529ede1e6f8SHighPoint Linux Team cmd_done: 530ede1e6f8SHighPoint Linux Team dprintk("scsi_done(scp=%p)\n", scp); 531ede1e6f8SHighPoint Linux Team scp->scsi_done(scp); 532ede1e6f8SHighPoint Linux Team return 0; 533ede1e6f8SHighPoint Linux Team } 534ede1e6f8SHighPoint Linux Team 535ede1e6f8SHighPoint Linux Team static const char *hptiop_info(struct Scsi_Host *host) 536ede1e6f8SHighPoint Linux Team { 537ede1e6f8SHighPoint Linux Team return driver_name_long; 538ede1e6f8SHighPoint Linux Team } 539ede1e6f8SHighPoint Linux Team 540ede1e6f8SHighPoint Linux Team static int hptiop_reset_hba(struct hptiop_hba *hba) 541ede1e6f8SHighPoint Linux Team { 542ede1e6f8SHighPoint Linux Team if (atomic_xchg(&hba->resetting, 1) == 0) { 543ede1e6f8SHighPoint Linux Team atomic_inc(&hba->reset_count); 544ede1e6f8SHighPoint Linux Team writel(IOPMU_INBOUND_MSG0_RESET, 5458d4fbd3fSHighPoint Linux Team &hba->iop->inbound_msgaddr0); 546ede1e6f8SHighPoint Linux Team hptiop_pci_posting_flush(hba->iop); 547ede1e6f8SHighPoint Linux Team } 548ede1e6f8SHighPoint Linux Team 549ede1e6f8SHighPoint Linux Team wait_event_timeout(hba->reset_wq, 550ede1e6f8SHighPoint Linux Team atomic_read(&hba->resetting) == 0, 60 * HZ); 551ede1e6f8SHighPoint Linux Team 552ede1e6f8SHighPoint Linux Team if (atomic_read(&hba->resetting)) { 553ede1e6f8SHighPoint Linux Team /* IOP is in unkown state, abort reset */ 554ede1e6f8SHighPoint Linux Team printk(KERN_ERR "scsi%d: reset failed\n", hba->host->host_no); 555ede1e6f8SHighPoint Linux Team return -1; 556ede1e6f8SHighPoint Linux Team } 557ede1e6f8SHighPoint Linux Team 558ede1e6f8SHighPoint Linux Team if (iop_send_sync_msg(hba, 559ede1e6f8SHighPoint Linux Team IOPMU_INBOUND_MSG0_START_BACKGROUND_TASK, 5000)) { 560ede1e6f8SHighPoint Linux Team dprintk("scsi%d: fail to start background task\n", 561ede1e6f8SHighPoint Linux Team hba->host->host_no); 562ede1e6f8SHighPoint Linux Team } 563ede1e6f8SHighPoint Linux Team 564ede1e6f8SHighPoint Linux Team return 0; 565ede1e6f8SHighPoint Linux Team } 566ede1e6f8SHighPoint Linux Team 567ede1e6f8SHighPoint Linux Team static int hptiop_reset(struct scsi_cmnd *scp) 568ede1e6f8SHighPoint Linux Team { 569ede1e6f8SHighPoint Linux Team struct Scsi_Host * host = scp->device->host; 570ede1e6f8SHighPoint Linux Team struct hptiop_hba * hba = (struct hptiop_hba *)host->hostdata; 571ede1e6f8SHighPoint Linux Team 572ede1e6f8SHighPoint Linux Team printk(KERN_WARNING "hptiop_reset(%d/%d/%d) scp=%p\n", 573ede1e6f8SHighPoint Linux Team scp->device->host->host_no, scp->device->channel, 574ede1e6f8SHighPoint Linux Team scp->device->id, scp); 575ede1e6f8SHighPoint Linux Team 576ede1e6f8SHighPoint Linux Team return hptiop_reset_hba(hba)? FAILED : SUCCESS; 577ede1e6f8SHighPoint Linux Team } 578ede1e6f8SHighPoint Linux Team 579ede1e6f8SHighPoint Linux Team static int hptiop_adjust_disk_queue_depth(struct scsi_device *sdev, 580ede1e6f8SHighPoint Linux Team int queue_depth) 581ede1e6f8SHighPoint Linux Team { 582ede1e6f8SHighPoint Linux Team if(queue_depth > 256) 583ede1e6f8SHighPoint Linux Team queue_depth = 256; 584ede1e6f8SHighPoint Linux Team scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth); 585ede1e6f8SHighPoint Linux Team return queue_depth; 586ede1e6f8SHighPoint Linux Team } 587ede1e6f8SHighPoint Linux Team 588ede1e6f8SHighPoint Linux Team static ssize_t hptiop_show_version(struct class_device *class_dev, char *buf) 589ede1e6f8SHighPoint Linux Team { 590ede1e6f8SHighPoint Linux Team return snprintf(buf, PAGE_SIZE, "%s\n", driver_ver); 591ede1e6f8SHighPoint Linux Team } 592ede1e6f8SHighPoint Linux Team 593ede1e6f8SHighPoint Linux Team static ssize_t hptiop_show_fw_version(struct class_device *class_dev, char *buf) 594ede1e6f8SHighPoint Linux Team { 595ede1e6f8SHighPoint Linux Team struct Scsi_Host *host = class_to_shost(class_dev); 596ede1e6f8SHighPoint Linux Team struct hptiop_hba *hba = (struct hptiop_hba *)host->hostdata; 597ede1e6f8SHighPoint Linux Team 598ede1e6f8SHighPoint Linux Team return snprintf(buf, PAGE_SIZE, "%d.%d.%d.%d\n", 599ede1e6f8SHighPoint Linux Team hba->firmware_version >> 24, 600ede1e6f8SHighPoint Linux Team (hba->firmware_version >> 16) & 0xff, 601ede1e6f8SHighPoint Linux Team (hba->firmware_version >> 8) & 0xff, 602ede1e6f8SHighPoint Linux Team hba->firmware_version & 0xff); 603ede1e6f8SHighPoint Linux Team } 604ede1e6f8SHighPoint Linux Team 605ede1e6f8SHighPoint Linux Team static struct class_device_attribute hptiop_attr_version = { 606ede1e6f8SHighPoint Linux Team .attr = { 607ede1e6f8SHighPoint Linux Team .name = "driver-version", 608ede1e6f8SHighPoint Linux Team .mode = S_IRUGO, 609ede1e6f8SHighPoint Linux Team }, 610ede1e6f8SHighPoint Linux Team .show = hptiop_show_version, 611ede1e6f8SHighPoint Linux Team }; 612ede1e6f8SHighPoint Linux Team 613ede1e6f8SHighPoint Linux Team static struct class_device_attribute hptiop_attr_fw_version = { 614ede1e6f8SHighPoint Linux Team .attr = { 615ede1e6f8SHighPoint Linux Team .name = "firmware-version", 616ede1e6f8SHighPoint Linux Team .mode = S_IRUGO, 617ede1e6f8SHighPoint Linux Team }, 618ede1e6f8SHighPoint Linux Team .show = hptiop_show_fw_version, 619ede1e6f8SHighPoint Linux Team }; 620ede1e6f8SHighPoint Linux Team 621ede1e6f8SHighPoint Linux Team static struct class_device_attribute *hptiop_attrs[] = { 622ede1e6f8SHighPoint Linux Team &hptiop_attr_version, 623ede1e6f8SHighPoint Linux Team &hptiop_attr_fw_version, 624ede1e6f8SHighPoint Linux Team NULL 625ede1e6f8SHighPoint Linux Team }; 626ede1e6f8SHighPoint Linux Team 627ede1e6f8SHighPoint Linux Team static struct scsi_host_template driver_template = { 628ede1e6f8SHighPoint Linux Team .module = THIS_MODULE, 629ede1e6f8SHighPoint Linux Team .name = driver_name, 630ede1e6f8SHighPoint Linux Team .queuecommand = hptiop_queuecommand, 631ede1e6f8SHighPoint Linux Team .eh_device_reset_handler = hptiop_reset, 632ede1e6f8SHighPoint Linux Team .eh_bus_reset_handler = hptiop_reset, 633ede1e6f8SHighPoint Linux Team .info = hptiop_info, 634ede1e6f8SHighPoint Linux Team .unchecked_isa_dma = 0, 635ede1e6f8SHighPoint Linux Team .emulated = 0, 636ede1e6f8SHighPoint Linux Team .use_clustering = ENABLE_CLUSTERING, 637ede1e6f8SHighPoint Linux Team .proc_name = driver_name, 638ede1e6f8SHighPoint Linux Team .shost_attrs = hptiop_attrs, 639ede1e6f8SHighPoint Linux Team .this_id = -1, 640ede1e6f8SHighPoint Linux Team .change_queue_depth = hptiop_adjust_disk_queue_depth, 641ede1e6f8SHighPoint Linux Team }; 642ede1e6f8SHighPoint Linux Team 643ede1e6f8SHighPoint Linux Team static int __devinit hptiop_probe(struct pci_dev *pcidev, 644ede1e6f8SHighPoint Linux Team const struct pci_device_id *id) 645ede1e6f8SHighPoint Linux Team { 646ede1e6f8SHighPoint Linux Team struct Scsi_Host *host = NULL; 647ede1e6f8SHighPoint Linux Team struct hptiop_hba *hba; 648ede1e6f8SHighPoint Linux Team struct hpt_iop_request_get_config iop_config; 649ede1e6f8SHighPoint Linux Team struct hpt_iop_request_set_config set_config; 650ede1e6f8SHighPoint Linux Team dma_addr_t start_phy; 651ede1e6f8SHighPoint Linux Team void *start_virt; 652ede1e6f8SHighPoint Linux Team u32 offset, i, req_size; 653ede1e6f8SHighPoint Linux Team 654ede1e6f8SHighPoint Linux Team dprintk("hptiop_probe(%p)\n", pcidev); 655ede1e6f8SHighPoint Linux Team 656ede1e6f8SHighPoint Linux Team if (pci_enable_device(pcidev)) { 657ede1e6f8SHighPoint Linux Team printk(KERN_ERR "hptiop: fail to enable pci device\n"); 658ede1e6f8SHighPoint Linux Team return -ENODEV; 659ede1e6f8SHighPoint Linux Team } 660ede1e6f8SHighPoint Linux Team 661ede1e6f8SHighPoint Linux Team printk(KERN_INFO "adapter at PCI %d:%d:%d, IRQ %d\n", 662ede1e6f8SHighPoint Linux Team pcidev->bus->number, pcidev->devfn >> 3, pcidev->devfn & 7, 663ede1e6f8SHighPoint Linux Team pcidev->irq); 664ede1e6f8SHighPoint Linux Team 665ede1e6f8SHighPoint Linux Team pci_set_master(pcidev); 666ede1e6f8SHighPoint Linux Team 667ede1e6f8SHighPoint Linux Team /* Enable 64bit DMA if possible */ 668ede1e6f8SHighPoint Linux Team if (pci_set_dma_mask(pcidev, DMA_64BIT_MASK)) { 669ede1e6f8SHighPoint Linux Team if (pci_set_dma_mask(pcidev, DMA_32BIT_MASK)) { 670ede1e6f8SHighPoint Linux Team printk(KERN_ERR "hptiop: fail to set dma_mask\n"); 671ede1e6f8SHighPoint Linux Team goto disable_pci_device; 672ede1e6f8SHighPoint Linux Team } 673ede1e6f8SHighPoint Linux Team } 674ede1e6f8SHighPoint Linux Team 675ede1e6f8SHighPoint Linux Team if (pci_request_regions(pcidev, driver_name)) { 676ede1e6f8SHighPoint Linux Team printk(KERN_ERR "hptiop: pci_request_regions failed\n"); 677ede1e6f8SHighPoint Linux Team goto disable_pci_device; 678ede1e6f8SHighPoint Linux Team } 679ede1e6f8SHighPoint Linux Team 680ede1e6f8SHighPoint Linux Team host = scsi_host_alloc(&driver_template, sizeof(struct hptiop_hba)); 681ede1e6f8SHighPoint Linux Team if (!host) { 682ede1e6f8SHighPoint Linux Team printk(KERN_ERR "hptiop: fail to alloc scsi host\n"); 683ede1e6f8SHighPoint Linux Team goto free_pci_regions; 684ede1e6f8SHighPoint Linux Team } 685ede1e6f8SHighPoint Linux Team 686ede1e6f8SHighPoint Linux Team hba = (struct hptiop_hba *)host->hostdata; 687ede1e6f8SHighPoint Linux Team 688ede1e6f8SHighPoint Linux Team hba->pcidev = pcidev; 689ede1e6f8SHighPoint Linux Team hba->host = host; 690ede1e6f8SHighPoint Linux Team hba->initialized = 0; 691ede1e6f8SHighPoint Linux Team 692ede1e6f8SHighPoint Linux Team atomic_set(&hba->resetting, 0); 693ede1e6f8SHighPoint Linux Team atomic_set(&hba->reset_count, 0); 694ede1e6f8SHighPoint Linux Team 695ede1e6f8SHighPoint Linux Team init_waitqueue_head(&hba->reset_wq); 696ede1e6f8SHighPoint Linux Team init_waitqueue_head(&hba->ioctl_wq); 697ede1e6f8SHighPoint Linux Team 698ede1e6f8SHighPoint Linux Team host->max_lun = 1; 699ede1e6f8SHighPoint Linux Team host->max_channel = 0; 700ede1e6f8SHighPoint Linux Team host->io_port = 0; 701ede1e6f8SHighPoint Linux Team host->n_io_port = 0; 702ede1e6f8SHighPoint Linux Team host->irq = pcidev->irq; 703ede1e6f8SHighPoint Linux Team 704ede1e6f8SHighPoint Linux Team if (hptiop_map_pci_bar(hba)) 705ede1e6f8SHighPoint Linux Team goto free_scsi_host; 706ede1e6f8SHighPoint Linux Team 707ede1e6f8SHighPoint Linux Team if (iop_wait_ready(hba->iop, 20000)) { 708ede1e6f8SHighPoint Linux Team printk(KERN_ERR "scsi%d: firmware not ready\n", 709ede1e6f8SHighPoint Linux Team hba->host->host_no); 710ede1e6f8SHighPoint Linux Team goto unmap_pci_bar; 711ede1e6f8SHighPoint Linux Team } 712ede1e6f8SHighPoint Linux Team 713ede1e6f8SHighPoint Linux Team if (iop_get_config(hba, &iop_config)) { 714ede1e6f8SHighPoint Linux Team printk(KERN_ERR "scsi%d: get config failed\n", 715ede1e6f8SHighPoint Linux Team hba->host->host_no); 716ede1e6f8SHighPoint Linux Team goto unmap_pci_bar; 717ede1e6f8SHighPoint Linux Team } 718ede1e6f8SHighPoint Linux Team 719ede1e6f8SHighPoint Linux Team hba->max_requests = min(le32_to_cpu(iop_config.max_requests), 720ede1e6f8SHighPoint Linux Team HPTIOP_MAX_REQUESTS); 721ede1e6f8SHighPoint Linux Team hba->max_devices = le32_to_cpu(iop_config.max_devices); 722ede1e6f8SHighPoint Linux Team hba->max_request_size = le32_to_cpu(iop_config.request_size); 723ede1e6f8SHighPoint Linux Team hba->max_sg_descriptors = le32_to_cpu(iop_config.max_sg_count); 724ede1e6f8SHighPoint Linux Team hba->firmware_version = le32_to_cpu(iop_config.firmware_version); 725ede1e6f8SHighPoint Linux Team hba->sdram_size = le32_to_cpu(iop_config.sdram_size); 726ede1e6f8SHighPoint Linux Team 727ede1e6f8SHighPoint Linux Team host->max_sectors = le32_to_cpu(iop_config.data_transfer_length) >> 9; 728ede1e6f8SHighPoint Linux Team host->max_id = le32_to_cpu(iop_config.max_devices); 729ede1e6f8SHighPoint Linux Team host->sg_tablesize = le32_to_cpu(iop_config.max_sg_count); 730ede1e6f8SHighPoint Linux Team host->can_queue = le32_to_cpu(iop_config.max_requests); 731ede1e6f8SHighPoint Linux Team host->cmd_per_lun = le32_to_cpu(iop_config.max_requests); 732ede1e6f8SHighPoint Linux Team host->max_cmd_len = 16; 733ede1e6f8SHighPoint Linux Team 734ede1e6f8SHighPoint Linux Team set_config.vbus_id = cpu_to_le32(host->host_no); 735ede1e6f8SHighPoint Linux Team set_config.iop_id = cpu_to_le32(host->host_no); 736ede1e6f8SHighPoint Linux Team 737ede1e6f8SHighPoint Linux Team if (iop_set_config(hba, &set_config)) { 738ede1e6f8SHighPoint Linux Team printk(KERN_ERR "scsi%d: set config failed\n", 739ede1e6f8SHighPoint Linux Team hba->host->host_no); 740ede1e6f8SHighPoint Linux Team goto unmap_pci_bar; 741ede1e6f8SHighPoint Linux Team } 742ede1e6f8SHighPoint Linux Team 743ede1e6f8SHighPoint Linux Team pci_set_drvdata(pcidev, host); 744ede1e6f8SHighPoint Linux Team 7451d6f359aSThomas Gleixner if (request_irq(pcidev->irq, hptiop_intr, IRQF_SHARED, 746ede1e6f8SHighPoint Linux Team driver_name, hba)) { 747ede1e6f8SHighPoint Linux Team printk(KERN_ERR "scsi%d: request irq %d failed\n", 748ede1e6f8SHighPoint Linux Team hba->host->host_no, pcidev->irq); 7493e74051bSChristoph Hellwig goto unmap_pci_bar; 750ede1e6f8SHighPoint Linux Team } 751ede1e6f8SHighPoint Linux Team 752ede1e6f8SHighPoint Linux Team /* Allocate request mem */ 753ede1e6f8SHighPoint Linux Team req_size = sizeof(struct hpt_iop_request_scsi_command) 754ede1e6f8SHighPoint Linux Team + sizeof(struct hpt_iopsg) * (hba->max_sg_descriptors - 1); 755ede1e6f8SHighPoint Linux Team if ((req_size& 0x1f) != 0) 756ede1e6f8SHighPoint Linux Team req_size = (req_size + 0x1f) & ~0x1f; 757ede1e6f8SHighPoint Linux Team 758ede1e6f8SHighPoint Linux Team dprintk("req_size=%d, max_requests=%d\n", req_size, hba->max_requests); 759ede1e6f8SHighPoint Linux Team 760ede1e6f8SHighPoint Linux Team hba->req_size = req_size; 761ede1e6f8SHighPoint Linux Team start_virt = dma_alloc_coherent(&pcidev->dev, 762ede1e6f8SHighPoint Linux Team hba->req_size*hba->max_requests + 0x20, 763ede1e6f8SHighPoint Linux Team &start_phy, GFP_KERNEL); 764ede1e6f8SHighPoint Linux Team 765ede1e6f8SHighPoint Linux Team if (!start_virt) { 766ede1e6f8SHighPoint Linux Team printk(KERN_ERR "scsi%d: fail to alloc request mem\n", 767ede1e6f8SHighPoint Linux Team hba->host->host_no); 768ede1e6f8SHighPoint Linux Team goto free_request_irq; 769ede1e6f8SHighPoint Linux Team } 770ede1e6f8SHighPoint Linux Team 771ede1e6f8SHighPoint Linux Team hba->dma_coherent = start_virt; 772ede1e6f8SHighPoint Linux Team hba->dma_coherent_handle = start_phy; 773ede1e6f8SHighPoint Linux Team 774ede1e6f8SHighPoint Linux Team if ((start_phy & 0x1f) != 0) 775ede1e6f8SHighPoint Linux Team { 776ede1e6f8SHighPoint Linux Team offset = ((start_phy + 0x1f) & ~0x1f) - start_phy; 777ede1e6f8SHighPoint Linux Team start_phy += offset; 778ede1e6f8SHighPoint Linux Team start_virt += offset; 779ede1e6f8SHighPoint Linux Team } 780ede1e6f8SHighPoint Linux Team 781ede1e6f8SHighPoint Linux Team hba->req_list = start_virt; 782ede1e6f8SHighPoint Linux Team for (i = 0; i < hba->max_requests; i++) { 783ede1e6f8SHighPoint Linux Team hba->reqs[i].next = NULL; 784ede1e6f8SHighPoint Linux Team hba->reqs[i].req_virt = start_virt; 785ede1e6f8SHighPoint Linux Team hba->reqs[i].req_shifted_phy = start_phy >> 5; 786ede1e6f8SHighPoint Linux Team hba->reqs[i].index = i; 787ede1e6f8SHighPoint Linux Team free_req(hba, &hba->reqs[i]); 788ede1e6f8SHighPoint Linux Team start_virt = (char *)start_virt + hba->req_size; 789ede1e6f8SHighPoint Linux Team start_phy = start_phy + hba->req_size; 790ede1e6f8SHighPoint Linux Team } 791ede1e6f8SHighPoint Linux Team 792ede1e6f8SHighPoint Linux Team /* Enable Interrupt and start background task */ 793ede1e6f8SHighPoint Linux Team if (hptiop_initialize_iop(hba)) 794ede1e6f8SHighPoint Linux Team goto free_request_mem; 795ede1e6f8SHighPoint Linux Team 7963e74051bSChristoph Hellwig if (scsi_add_host(host, &pcidev->dev)) { 7973e74051bSChristoph Hellwig printk(KERN_ERR "scsi%d: scsi_add_host failed\n", 7983e74051bSChristoph Hellwig hba->host->host_no); 7993e74051bSChristoph Hellwig goto free_request_mem; 8003e74051bSChristoph Hellwig } 8013e74051bSChristoph Hellwig 802ede1e6f8SHighPoint Linux Team 803ede1e6f8SHighPoint Linux Team scsi_scan_host(host); 804ede1e6f8SHighPoint Linux Team 805ede1e6f8SHighPoint Linux Team dprintk("scsi%d: hptiop_probe successfully\n", hba->host->host_no); 806ede1e6f8SHighPoint Linux Team return 0; 807ede1e6f8SHighPoint Linux Team 808ede1e6f8SHighPoint Linux Team free_request_mem: 809ede1e6f8SHighPoint Linux Team dma_free_coherent(&hba->pcidev->dev, 810ede1e6f8SHighPoint Linux Team hba->req_size*hba->max_requests + 0x20, 811ede1e6f8SHighPoint Linux Team hba->dma_coherent, hba->dma_coherent_handle); 812ede1e6f8SHighPoint Linux Team 813ede1e6f8SHighPoint Linux Team free_request_irq: 814ede1e6f8SHighPoint Linux Team free_irq(hba->pcidev->irq, hba); 815ede1e6f8SHighPoint Linux Team 816ede1e6f8SHighPoint Linux Team unmap_pci_bar: 817ede1e6f8SHighPoint Linux Team iounmap(hba->iop); 818ede1e6f8SHighPoint Linux Team 819ede1e6f8SHighPoint Linux Team free_pci_regions: 820ede1e6f8SHighPoint Linux Team pci_release_regions(pcidev) ; 821ede1e6f8SHighPoint Linux Team 822ede1e6f8SHighPoint Linux Team free_scsi_host: 823ede1e6f8SHighPoint Linux Team scsi_host_put(host); 824ede1e6f8SHighPoint Linux Team 825ede1e6f8SHighPoint Linux Team disable_pci_device: 826ede1e6f8SHighPoint Linux Team pci_disable_device(pcidev); 827ede1e6f8SHighPoint Linux Team 828ede1e6f8SHighPoint Linux Team dprintk("scsi%d: hptiop_probe fail\n", host->host_no); 829ede1e6f8SHighPoint Linux Team return -ENODEV; 830ede1e6f8SHighPoint Linux Team } 831ede1e6f8SHighPoint Linux Team 832ede1e6f8SHighPoint Linux Team static void hptiop_shutdown(struct pci_dev *pcidev) 833ede1e6f8SHighPoint Linux Team { 834ede1e6f8SHighPoint Linux Team struct Scsi_Host *host = pci_get_drvdata(pcidev); 835ede1e6f8SHighPoint Linux Team struct hptiop_hba *hba = (struct hptiop_hba *)host->hostdata; 836ede1e6f8SHighPoint Linux Team struct hpt_iopmu __iomem *iop = hba->iop; 837ede1e6f8SHighPoint Linux Team u32 int_mask; 838ede1e6f8SHighPoint Linux Team 839ede1e6f8SHighPoint Linux Team dprintk("hptiop_shutdown(%p)\n", hba); 840ede1e6f8SHighPoint Linux Team 841ede1e6f8SHighPoint Linux Team /* stop the iop */ 842ede1e6f8SHighPoint Linux Team if (iop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_SHUTDOWN, 60000)) 843ede1e6f8SHighPoint Linux Team printk(KERN_ERR "scsi%d: shutdown the iop timeout\n", 844ede1e6f8SHighPoint Linux Team hba->host->host_no); 845ede1e6f8SHighPoint Linux Team 846ede1e6f8SHighPoint Linux Team /* disable all outbound interrupts */ 847ede1e6f8SHighPoint Linux Team int_mask = readl(&iop->outbound_intmask); 848ede1e6f8SHighPoint Linux Team writel(int_mask | 849ede1e6f8SHighPoint Linux Team IOPMU_OUTBOUND_INT_MSG0 | IOPMU_OUTBOUND_INT_POSTQUEUE, 850ede1e6f8SHighPoint Linux Team &iop->outbound_intmask); 851ede1e6f8SHighPoint Linux Team hptiop_pci_posting_flush(iop); 852ede1e6f8SHighPoint Linux Team } 853ede1e6f8SHighPoint Linux Team 854ede1e6f8SHighPoint Linux Team static void hptiop_remove(struct pci_dev *pcidev) 855ede1e6f8SHighPoint Linux Team { 856ede1e6f8SHighPoint Linux Team struct Scsi_Host *host = pci_get_drvdata(pcidev); 857ede1e6f8SHighPoint Linux Team struct hptiop_hba *hba = (struct hptiop_hba *)host->hostdata; 858ede1e6f8SHighPoint Linux Team 859ede1e6f8SHighPoint Linux Team dprintk("scsi%d: hptiop_remove\n", hba->host->host_no); 860ede1e6f8SHighPoint Linux Team 8614f2ddba3SHighPoint Linux Team scsi_remove_host(host); 8624f2ddba3SHighPoint Linux Team 863ede1e6f8SHighPoint Linux Team hptiop_shutdown(pcidev); 864ede1e6f8SHighPoint Linux Team 865ede1e6f8SHighPoint Linux Team free_irq(hba->pcidev->irq, hba); 866ede1e6f8SHighPoint Linux Team 867ede1e6f8SHighPoint Linux Team dma_free_coherent(&hba->pcidev->dev, 868ede1e6f8SHighPoint Linux Team hba->req_size * hba->max_requests + 0x20, 869ede1e6f8SHighPoint Linux Team hba->dma_coherent, 870ede1e6f8SHighPoint Linux Team hba->dma_coherent_handle); 871ede1e6f8SHighPoint Linux Team 872ede1e6f8SHighPoint Linux Team iounmap(hba->iop); 873ede1e6f8SHighPoint Linux Team 874ede1e6f8SHighPoint Linux Team pci_release_regions(hba->pcidev); 875ede1e6f8SHighPoint Linux Team pci_set_drvdata(hba->pcidev, NULL); 876ede1e6f8SHighPoint Linux Team pci_disable_device(hba->pcidev); 877ede1e6f8SHighPoint Linux Team 878ede1e6f8SHighPoint Linux Team scsi_host_put(host); 879ede1e6f8SHighPoint Linux Team } 880ede1e6f8SHighPoint Linux Team 881ede1e6f8SHighPoint Linux Team static struct pci_device_id hptiop_id_table[] = { 882ede1e6f8SHighPoint Linux Team { PCI_DEVICE(0x1103, 0x3220) }, 883ede1e6f8SHighPoint Linux Team { PCI_DEVICE(0x1103, 0x3320) }, 884ede1e6f8SHighPoint Linux Team {}, 885ede1e6f8SHighPoint Linux Team }; 886ede1e6f8SHighPoint Linux Team 887ede1e6f8SHighPoint Linux Team MODULE_DEVICE_TABLE(pci, hptiop_id_table); 888ede1e6f8SHighPoint Linux Team 889ede1e6f8SHighPoint Linux Team static struct pci_driver hptiop_pci_driver = { 890ede1e6f8SHighPoint Linux Team .name = driver_name, 891ede1e6f8SHighPoint Linux Team .id_table = hptiop_id_table, 892ede1e6f8SHighPoint Linux Team .probe = hptiop_probe, 893ede1e6f8SHighPoint Linux Team .remove = hptiop_remove, 894ede1e6f8SHighPoint Linux Team .shutdown = hptiop_shutdown, 895ede1e6f8SHighPoint Linux Team }; 896ede1e6f8SHighPoint Linux Team 897ede1e6f8SHighPoint Linux Team static int __init hptiop_module_init(void) 898ede1e6f8SHighPoint Linux Team { 899ede1e6f8SHighPoint Linux Team printk(KERN_INFO "%s %s\n", driver_name_long, driver_ver); 9003e74051bSChristoph Hellwig return pci_register_driver(&hptiop_pci_driver); 901ede1e6f8SHighPoint Linux Team } 902ede1e6f8SHighPoint Linux Team 903ede1e6f8SHighPoint Linux Team static void __exit hptiop_module_exit(void) 904ede1e6f8SHighPoint Linux Team { 905ede1e6f8SHighPoint Linux Team pci_unregister_driver(&hptiop_pci_driver); 906ede1e6f8SHighPoint Linux Team } 907ede1e6f8SHighPoint Linux Team 908ede1e6f8SHighPoint Linux Team 909ede1e6f8SHighPoint Linux Team module_init(hptiop_module_init); 910ede1e6f8SHighPoint Linux Team module_exit(hptiop_module_exit); 911ede1e6f8SHighPoint Linux Team 912ede1e6f8SHighPoint Linux Team MODULE_LICENSE("GPL"); 913