⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sx8.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *  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 + -