📄 sx8.c
字号:
/* * sx8.c: Driver for Promise SATA SX8 looks-like-I2O hardware * * Copyright 2004-2005 Red Hat, Inc. * * Author/maintainer: Jeff Garzik <jgarzik@pobox.com> * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. */#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <linux/blkdev.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/compiler.h>#include <linux/workqueue.h>#include <linux/bitops.h>#include <linux/delay.h>#include <linux/time.h>#include <linux/hdreg.h>#include <linux/dma-mapping.h>#include <linux/completion.h>#include <linux/scatterlist.h>#include <asm/io.h>#include <asm/uaccess.h>#if 0#define CARM_DEBUG#define CARM_VERBOSE_DEBUG#else#undef CARM_DEBUG#undef CARM_VERBOSE_DEBUG#endif#undef CARM_NDEBUG#define DRV_NAME "sx8"#define DRV_VERSION "1.0"#define PFX DRV_NAME ": "MODULE_AUTHOR("Jeff Garzik");MODULE_LICENSE("GPL");MODULE_DESCRIPTION("Promise SATA SX8 block driver");MODULE_VERSION(DRV_VERSION);/* * SX8 hardware has a single message queue for all ATA ports. * When this driver was written, the hardware (firmware?) would * corrupt data eventually, if more than one request was outstanding. * As one can imagine, having 8 ports bottlenecking on a single * command hurts performance. * * Based on user reports, later versions of the hardware (firmware?) * seem to be able to survive with more than one command queued. * * Therefore, we default to the safe option -- 1 command -- but * allow the user to increase this. * * SX8 should be able to support up to ~60 queued commands (CARM_MAX_REQ), * but problems seem to occur when you exceed ~30, even on newer hardware. */static int max_queue = 1;module_param(max_queue, int, 0444);MODULE_PARM_DESC(max_queue, "Maximum number of queued commands. (min==1, max==30, safe==1)");#define NEXT_RESP(idx) ((idx + 1) % RMSG_Q_LEN)/* 0xf is just arbitrary, non-zero noise; this is sorta like poisoning */#define TAG_ENCODE(tag) (((tag) << 16) | 0xf)#define TAG_DECODE(tag) (((tag) >> 16) & 0x1f)#define TAG_VALID(tag) ((((tag) & 0xf) == 0xf) && (TAG_DECODE(tag) < 32))/* note: prints function name for you */#ifdef CARM_DEBUG#define DPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args)#ifdef CARM_VERBOSE_DEBUG#define VPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args)#else#define VPRINTK(fmt, args...)#endif /* CARM_VERBOSE_DEBUG */#else#define DPRINTK(fmt, args...)#define VPRINTK(fmt, args...)#endif /* CARM_DEBUG */#ifdef CARM_NDEBUG#define assert(expr)#else#define assert(expr) \ if(unlikely(!(expr))) { \ printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \ #expr,__FILE__,__FUNCTION__,__LINE__); \ }#endif/* defines only for the constants which don't work well as enums */struct carm_host;enum { /* adapter-wide limits */ CARM_MAX_PORTS = 8, CARM_SHM_SIZE = (4096 << 7), CARM_MINORS_PER_MAJOR = 256 / CARM_MAX_PORTS, CARM_MAX_WAIT_Q = CARM_MAX_PORTS + 1, /* command message queue limits */ CARM_MAX_REQ = 64, /* max command msgs per host */ CARM_MSG_LOW_WATER = (CARM_MAX_REQ / 4), /* refill mark */ /* S/G limits, host-wide and per-request */ CARM_MAX_REQ_SG = 32, /* max s/g entries per request */ CARM_MAX_HOST_SG = 600, /* max s/g entries per host */ CARM_SG_LOW_WATER = (CARM_MAX_HOST_SG / 4), /* re-fill mark */ /* hardware registers */ CARM_IHQP = 0x1c, CARM_INT_STAT = 0x10, /* interrupt status */ CARM_INT_MASK = 0x14, /* interrupt mask */ CARM_HMUC = 0x18, /* host message unit control */ RBUF_ADDR_LO = 0x20, /* response msg DMA buf low 32 bits */ RBUF_ADDR_HI = 0x24, /* response msg DMA buf high 32 bits */ RBUF_BYTE_SZ = 0x28, CARM_RESP_IDX = 0x2c, CARM_CMS0 = 0x30, /* command message size reg 0 */ CARM_LMUC = 0x48, CARM_HMPHA = 0x6c, CARM_INITC = 0xb5, /* bits in CARM_INT_{STAT,MASK} */ INT_RESERVED = 0xfffffff0, INT_WATCHDOG = (1 << 3), /* watchdog timer */ INT_Q_OVERFLOW = (1 << 2), /* cmd msg q overflow */ INT_Q_AVAILABLE = (1 << 1), /* cmd msg q has free space */ INT_RESPONSE = (1 << 0), /* response msg available */ INT_ACK_MASK = INT_WATCHDOG | INT_Q_OVERFLOW, INT_DEF_MASK = INT_RESERVED | INT_Q_OVERFLOW | INT_RESPONSE, /* command messages, and related register bits */ CARM_HAVE_RESP = 0x01, CARM_MSG_READ = 1, CARM_MSG_WRITE = 2, CARM_MSG_VERIFY = 3, CARM_MSG_GET_CAPACITY = 4, CARM_MSG_FLUSH = 5, CARM_MSG_IOCTL = 6, CARM_MSG_ARRAY = 8, CARM_MSG_MISC = 9, CARM_CME = (1 << 2), CARM_RME = (1 << 1), CARM_WZBC = (1 << 0), CARM_RMI = (1 << 0), CARM_Q_FULL = (1 << 3), CARM_MSG_SIZE = 288, CARM_Q_LEN = 48, /* CARM_MSG_IOCTL messages */ CARM_IOC_SCAN_CHAN = 5, /* scan channels for devices */ CARM_IOC_GET_TCQ = 13, /* get tcq/ncq depth */ CARM_IOC_SET_TCQ = 14, /* set tcq/ncq depth */ IOC_SCAN_CHAN_NODEV = 0x1f, IOC_SCAN_CHAN_OFFSET = 0x40, /* CARM_MSG_ARRAY messages */ CARM_ARRAY_INFO = 0, ARRAY_NO_EXIST = (1 << 31), /* response messages */ RMSG_SZ = 8, /* sizeof(struct carm_response) */ RMSG_Q_LEN = 48, /* resp. msg list length */ RMSG_OK = 1, /* bit indicating msg was successful */ /* length of entire resp. msg buffer */ RBUF_LEN = RMSG_SZ * RMSG_Q_LEN, PDC_SHM_SIZE = (4096 << 7), /* length of entire h/w buffer */ /* CARM_MSG_MISC messages */ MISC_GET_FW_VER = 2, MISC_ALLOC_MEM = 3, MISC_SET_TIME = 5, /* MISC_GET_FW_VER feature bits */ FW_VER_4PORT = (1 << 2), /* 1=4 ports, 0=8 ports */ FW_VER_NON_RAID = (1 << 1), /* 1=non-RAID firmware, 0=RAID */ FW_VER_ZCR = (1 << 0), /* zero channel RAID (whatever that is) */ /* carm_host flags */ FL_NON_RAID = FW_VER_NON_RAID, FL_4PORT = FW_VER_4PORT, FL_FW_VER_MASK = (FW_VER_NON_RAID | FW_VER_4PORT), FL_DAC = (1 << 16), FL_DYN_MAJOR = (1 << 17),};enum { CARM_SG_BOUNDARY = 0xffffUL, /* s/g segment boundary */};enum scatter_gather_types { SGT_32BIT = 0, SGT_64BIT = 1,};enum host_states { HST_INVALID, /* invalid state; never used */ HST_ALLOC_BUF, /* setting up master SHM area */ HST_ERROR, /* we never leave here */ HST_PORT_SCAN, /* start dev scan */ HST_DEV_SCAN_START, /* start per-device probe */ HST_DEV_SCAN, /* continue per-device probe */ HST_DEV_ACTIVATE, /* activate devices we found */ HST_PROBE_FINISHED, /* probe is complete */ HST_PROBE_START, /* initiate probe */ HST_SYNC_TIME, /* tell firmware what time it is */ HST_GET_FW_VER, /* get firmware version, adapter port cnt */};#ifdef CARM_DEBUGstatic const char *state_name[] = { "HST_INVALID", "HST_ALLOC_BUF", "HST_ERROR", "HST_PORT_SCAN", "HST_DEV_SCAN_START", "HST_DEV_SCAN", "HST_DEV_ACTIVATE", "HST_PROBE_FINISHED", "HST_PROBE_START", "HST_SYNC_TIME", "HST_GET_FW_VER",};#endifstruct carm_port { unsigned int port_no; struct gendisk *disk; struct carm_host *host; /* attached device characteristics */ u64 capacity; char name[41]; u16 dev_geom_head; u16 dev_geom_sect; u16 dev_geom_cyl;};struct carm_request { unsigned int tag; int n_elem; unsigned int msg_type; unsigned int msg_subtype; unsigned int msg_bucket; struct request *rq; struct carm_port *port; struct scatterlist sg[CARM_MAX_REQ_SG];};struct carm_host { unsigned long flags; void __iomem *mmio; void *shm; dma_addr_t shm_dma; int major; int id; char name[32]; spinlock_t lock; struct pci_dev *pdev; unsigned int state; u32 fw_ver; struct request_queue *oob_q; unsigned int n_oob; unsigned int hw_sg_used; unsigned int resp_idx; unsigned int wait_q_prod; unsigned int wait_q_cons; struct request_queue *wait_q[CARM_MAX_WAIT_Q]; unsigned int n_msgs; u64 msg_alloc; struct carm_request req[CARM_MAX_REQ]; void *msg_base; dma_addr_t msg_dma; int cur_scan_dev; unsigned long dev_active; unsigned long dev_present; struct carm_port port[CARM_MAX_PORTS]; struct work_struct fsm_task; struct completion probe_comp;};struct carm_response { __le32 ret_handle; __le32 status;} __attribute__((packed));struct carm_msg_sg { __le32 start; __le32 len;} __attribute__((packed));struct carm_msg_rw { u8 type; u8 id; u8 sg_count; u8 sg_type; __le32 handle; __le32 lba; __le16 lba_count; __le16 lba_high; struct carm_msg_sg sg[32];} __attribute__((packed));struct carm_msg_allocbuf { u8 type; u8 subtype; u8 n_sg; u8 sg_type; __le32 handle; __le32 addr; __le32 len; __le32 evt_pool; __le32 n_evt; __le32 rbuf_pool; __le32 n_rbuf; __le32 msg_pool; __le32 n_msg; struct carm_msg_sg sg[8];} __attribute__((packed));struct carm_msg_ioctl { u8 type; u8 subtype; u8 array_id; u8 reserved1; __le32 handle; __le32 data_addr; u32 reserved2;} __attribute__((packed));struct carm_msg_sync_time { u8 type; u8 subtype; u16 reserved1; __le32 handle; u32 reserved2; __le32 timestamp;} __attribute__((packed));struct carm_msg_get_fw_ver { u8 type; u8 subtype; u16 reserved1; __le32 handle; __le32 data_addr; u32 reserved2;} __attribute__((packed));struct carm_fw_ver { __le32 version; u8 features; u8 reserved1; u16 reserved2;} __attribute__((packed));struct carm_array_info { __le32 size; __le16 size_hi; __le16 stripe_size; __le32 mode; __le16 stripe_blk_sz; __le16 reserved1; __le16 cyl; __le16 head; __le16 sect; u8 array_id; u8 reserved2; char name[40]; __le32 array_status; /* device list continues beyond this point? */} __attribute__((packed));static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);static void carm_remove_one (struct pci_dev *pdev);static int carm_bdev_getgeo(struct block_device *bdev, struct hd_geometry *geo);static struct pci_device_id carm_pci_tbl[] = { { PCI_VENDOR_ID_PROMISE, 0x8000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, { PCI_VENDOR_ID_PROMISE, 0x8002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, { } /* terminate list */};MODULE_DEVICE_TABLE(pci, carm_pci_tbl);static struct pci_driver carm_driver = { .name = DRV_NAME, .id_table = carm_pci_tbl, .probe = carm_init_one, .remove = carm_remove_one,};static struct block_device_operations carm_bd_ops = { .owner = THIS_MODULE, .getgeo = carm_bdev_getgeo,};static unsigned int carm_host_id;static unsigned long carm_major_alloc;static int carm_bdev_getgeo(struct block_device *bdev, struct hd_geometry *geo){ struct carm_port *port = bdev->bd_disk->private_data; geo->heads = (u8) port->dev_geom_head; geo->sectors = (u8) port->dev_geom_sect; geo->cylinders = port->dev_geom_cyl; return 0;}static const u32 msg_sizes[] = { 32, 64, 128, CARM_MSG_SIZE };static inline int carm_lookup_bucket(u32 msg_size){ int i; for (i = 0; i < ARRAY_SIZE(msg_sizes); i++) if (msg_size <= msg_sizes[i]) return i; return -ENOENT;}static void carm_init_buckets(void __iomem *mmio){ unsigned int i; for (i = 0; i < ARRAY_SIZE(msg_sizes); i++) writel(msg_sizes[i], mmio + CARM_CMS0 + (4 * i));}static inline void *carm_ref_msg(struct carm_host *host, unsigned int msg_idx){ return host->msg_base + (msg_idx * CARM_MSG_SIZE);}static inline dma_addr_t carm_ref_msg_dma(struct carm_host *host, unsigned int msg_idx){ return host->msg_dma + (msg_idx * CARM_MSG_SIZE);}static int carm_send_msg(struct carm_host *host, struct carm_request *crq){ void __iomem *mmio = host->mmio; u32 msg = (u32) carm_ref_msg_dma(host, crq->tag); u32 cm_bucket = crq->msg_bucket; u32 tmp; int rc = 0; VPRINTK("ENTER\n"); tmp = readl(mmio + CARM_HMUC); if (tmp & CARM_Q_FULL) {#if 0 tmp = readl(mmio + CARM_INT_MASK); tmp |= INT_Q_AVAILABLE; writel(tmp, mmio + CARM_INT_MASK); readl(mmio + CARM_INT_MASK); /* flush */#endif DPRINTK("host msg queue full\n"); rc = -EBUSY; } else { writel(msg | (cm_bucket << 1), mmio + CARM_IHQP); readl(mmio + CARM_IHQP); /* flush */ } return rc;}static struct carm_request *carm_get_request(struct carm_host *host){ unsigned int i; /* obey global hardware limit on S/G entries */ if (host->hw_sg_used >= (CARM_MAX_HOST_SG - CARM_MAX_REQ_SG)) return NULL; for (i = 0; i < max_queue; i++) if ((host->msg_alloc & (1ULL << i)) == 0) { struct carm_request *crq = &host->req[i]; crq->port = NULL; crq->n_elem = 0; host->msg_alloc |= (1ULL << i); host->n_msgs++; assert(host->n_msgs <= CARM_MAX_REQ); sg_init_table(crq->sg, CARM_MAX_REQ_SG); return crq; } DPRINTK("no request available, returning NULL\n"); return NULL;}static int carm_put_request(struct carm_host *host, struct carm_request *crq){ assert(crq->tag < max_queue); if (unlikely((host->msg_alloc & (1ULL << crq->tag)) == 0)) return -EINVAL; /* tried to clear a tag that was not active */ assert(host->hw_sg_used >= crq->n_elem); host->msg_alloc &= ~(1ULL << crq->tag); host->hw_sg_used -= crq->n_elem; host->n_msgs--; return 0;}static struct carm_request *carm_get_special(struct carm_host *host){ unsigned long flags; struct carm_request *crq = NULL; struct request *rq; int tries = 5000; while (tries-- > 0) { spin_lock_irqsave(&host->lock, flags); crq = carm_get_request(host); spin_unlock_irqrestore(&host->lock, flags); if (crq) break; msleep(10); } if (!crq) return NULL; rq = blk_get_request(host->oob_q, WRITE /* bogus */, GFP_KERNEL); if (!rq) { spin_lock_irqsave(&host->lock, flags); carm_put_request(host, crq); spin_unlock_irqrestore(&host->lock, flags); return NULL; } crq->rq = rq; return crq;}static int carm_array_info (struct carm_host *host, unsigned int array_idx){ struct carm_msg_ioctl *ioc; unsigned int idx; u32 msg_data; dma_addr_t msg_dma; struct carm_request *crq; int rc;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -