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

📄 qspi_char.c

📁 mmc_qspi.tar.gz mmc block driver code for uClinux
💻 C
字号:
#include <linux/config.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/string.h>#include <asm/uaccess.h>#include <linux/wait.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <asm/semaphore.h>#include <linux/slab.h> //ptet malloc.h sur l'uc#include <asm/coldfire.h>#include <asm-m68knommu/m5282sim.h>#include "int.h"#define QSPI_CHAR#include "qspi.h"#define QSPI_VECTOR 18/* qspi register */#define PQSPAR *((volatile u8 *) (MCF_IPSBAR + 0x00100059))#define QSPI_BASE (MCF_IPSBAR + 0x340)#define QMR *((volatile u16 *) QSPI_BASE + 0x00)#define QDLYR *((volatile u16 *) (QSPI_BASE + 0x04))#define QWR *((volatile u16 *) (QSPI_BASE + 0x08))#define QIR *((volatile u16 *) (QSPI_BASE + 0x0C))#define QAR *((volatile u16 *) (QSPI_BASE + 0x10))#define QDR *((volatile u16 *) (QSPI_BASE + 0x14))#define QSPI_NB_DEVICE 4#define QSPI_MAX_TRANSFER_SIZE 1024 /* transfer size limit */#define QSPI_QUEUE_SIZE 16 /* queue size */#define QSPI_DFLT_BAUD_RATE 2 /* for a 16.5MHz qspi clock */#define QSPI_DFLT_BITS_PER_TRANSFERT 8#define QSPI_DFLT_CLOCK_POLARITY_BIT 0#define QSPI_DFLT_CLOCK_PHASE_BIT 0#define QSPI_DFLT_BITS_PER_TRANSFERT 8#define QSPI_DFLT_CLOCK_DELAY 2#define QSPI_DFLT_TRANSFER_DELAY 2#define QSPI_MAX_BAUD_RATE#define QSPI_MIN_BAUD_RATE#define QSPI_CS0_LOW 0x0000#define QSPI_CS0_HIGHT 0x0100/* various interrupt flag */#define COMPLETION_FLAG 0x01#define ABORT_FLAG 0x04#define WRITE_COLLISION_FLAG 0x08#include "klog.h"/* Version Information */#define DRIVER_AUTHOR "simon"#define DRIVER_DESC "driver to use the QSPI on the 5282 motorola board"/* enable dynamic major allocation by default...  * but user can manualy fix it by passing a parameter during  * the insmod process */static int qspi_major = 108;MODULE_PARM(qspi_major, "i");MODULE_PARM_DESC(qspi_major, "to fix manualy major number");struct qspi{	unsigned int open_count;	unsigned int irq_exit_code;		/* transfert information */	unsigned char bits_per_transfer;	unsigned char baud_rate;	unsigned char clock_polarity_bit; /* 0 or 1 */	unsigned char clock_phase_bit; /* 0 or 1*/	unsigned char clock_delay;	unsigned char transfer_delay;	unsigned short chip_select;	unsigned short device_num;		struct semaphore qspi_sem; /* to protect this struct */	wait_queue_head_t qspi_queue;	struct irq_info *qspi_irq_info;};	static void qspi_start (struct qspi *);static void qspi_stop (void);static void set_qmr (struct qspi *);static void qspi_irqhandler (int, void *, struct pt_regs *);static int qspi_open (struct inode *, struct file *);static ssize_t qspi_read (struct file *, char *, size_t, loff_t *);static ssize_t qspi_write (struct file *, const char *, size_t, loff_t *);static int qspi_ioctl (struct inode *, struct file *, unsigned int, unsigned long);static int qspi_release (struct inode *, struct file *);static int __init qspi_init (void);static void __exit qspi_exit (void);static struct file_operations qspi_fops = {	owner:		THIS_MODULE,	read:		qspi_read,	write:		qspi_write,	open:		qspi_open,	ioctl:		qspi_ioctl,	release:	qspi_release,};static void qspi_irqhandler (int irq, void *dev_id, struct pt_regs *regs){	struct qspi *dev = (struct qspi *) dev_id;	dbg("%s - QIR = 0x%x", __FUNCTION__, QIR);	/* case of a transfer abort by clearing the QDLYR[SPE] bit */	if (QIR && ABORT_FLAG)	{		QIR |= ABORT_FLAG;		dev->irq_exit_code = ABORT_FLAG;	}		/* attempt to write to the ram entry during the transfer */	if (QIR && WRITE_COLLISION_FLAG)	{		QIR |= WRITE_COLLISION_FLAG;		dev->irq_exit_code = WRITE_COLLISION_FLAG;	}	/* normal case of a transfer completion */	if (QIR && COMPLETION_FLAG)	{		QIR |= COMPLETION_FLAG;		dev->irq_exit_code = COMPLETION_FLAG;	}	wake_up_interruptible (&dev->qspi_queue);		return;}/* function to stop qspi transfer */void qspi_stop (void){	QWR |= (1 << 15);	return;}static void set_qmr (struct qspi *dev){	if (!dev)		return;	/* FIX 16 bits tranfer  */	QMR = ((1 << 15) | (dev->bits_per_transfer << 10) | 		(dev->clock_polarity_bit << 9) | 		(dev->clock_phase_bit << 8) | dev->baud_rate);	return;}/* function to initialize qspi register */static void qspi_start (struct qspi *dev){	/* set the port QS configuration for SPI */	PQSPAR = 0x7f;	set_qmr (dev);//	QMR = 0xa1a2;	dbg("%s - QMR = 0x%x", __FUNCTION__, QMR);		QDLYR = (dev->transfer_delay | (dev->clock_delay << 8));		dbg("%s - QDLYR = 0x%x", __FUNCTION__, QDLYR);	/* enable interruption for write colision access error, 	 * abort access error and qspi completed success...	 * clear any interrupts */	QIR = 0xB10D;	dbg("%s - QIR = 0x%x", __FUNCTION__, QIR);		return;}/* method to open the qspi device */static int qspi_open (struct inode *inode, struct file *file){	int minor = MINOR(inode->i_rdev);	struct qspi *dev = NULL;	int retval = 0;	struct irq_info *qspi_irq_info = NULL;		dev = (struct qspi *) file->private_data;	/* just for compatibility with older kernel version */	MOD_INC_USE_COUNT;	if (minor > (QSPI_NB_DEVICE - 1))	{		retval = -ENODEV;		goto exit;	}	/* if it is the first open, it is time to 	 * initiate some structure */	if (!dev)	{		dev = (struct qspi *) kmalloc 			(sizeof (struct qspi), GFP_KERNEL);				qspi_irq_info = (struct irq_info *) kmalloc 			(sizeof (struct irq_info), GFP_KERNEL);				if ((dev == NULL) || (qspi_irq_info == NULL))		{			retval = -ENOMEM;			goto exit;		}			memset (dev, 0x00, sizeof (struct qspi));		dev->baud_rate = QSPI_DFLT_BAUD_RATE;		dev->bits_per_transfer = QSPI_DFLT_BITS_PER_TRANSFERT;		dev->clock_polarity_bit = QSPI_DFLT_CLOCK_POLARITY_BIT;		dev->clock_phase_bit = QSPI_DFLT_CLOCK_PHASE_BIT;		dev->clock_delay = QSPI_DFLT_CLOCK_DELAY;		dev->transfer_delay = QSPI_DFLT_TRANSFER_DELAY;		dev->device_num = minor;		dev->chip_select &= ~(1 << dev->device_num);		dev->irq_exit_code = 0;		init_waitqueue_head (&dev->qspi_queue);		sema_init (&dev->qspi_sem, 1);		dev->open_count = 0;		file->private_data = dev;	}	/* lock this device */	if (down_interruptible (&dev->qspi_sem))	{			retval = -ERESTARTSYS;		goto exit_free;	}		/* initiate the qadc and enable the corresponding 	 * interruption */	if (dev->open_count == 0)	{		/* initiate the structure to request our interrupt vector */		sprintf (qspi_irq_info->name, "qspi_int");		qspi_irq_info->vector = QSPI_VECTOR + INTC0_VECT_MIN;		qspi_irq_info->level = IRQ_LEVEL_DFLT; /* default level for the qspi interrupt */		qspi_irq_info->handler = qspi_irqhandler;		qspi_irq_info->dev_id = (void *) dev;		if ((retval = enable_interrupt_vector (qspi_irq_info)))		{			dbg("%s - can't enable qspi interrupt vector", 					__FUNCTION__);			goto exit_sem;		}			dev->qspi_irq_info = qspi_irq_info;		qspi_stop ();		/* some qspi register init */		qspi_start (dev);	}			/* increment our own counter */	++dev->open_count;		info("open qspi device(%d)", minor);	dbg("%s - open_count = %d", __FUNCTION__, dev->open_count);exit_sem :	up (&dev->qspi_sem);	exit_free :	if (retval)		kfree (dev);	exit :	if (retval)		MOD_DEC_USE_COUNT;	return (retval);}int qspi_send (char *kbuffer, unsigned int bytes_to_write, struct qspi *dev){		unsigned int bytes_write = 0;	unsigned int transfer_size = 0;	int i, ret = 0;	while (bytes_to_write)	{		transfer_size = (bytes_to_write < QSPI_QUEUE_SIZE) ? 			bytes_to_write : QSPI_QUEUE_SIZE;		/* select the first command entry */		QAR = 0x20;			for (i = 0; i < transfer_size; i++)		{			/* set CS0 active */				QDR = (3 << 12) | dev->chip_select;		}		/* select the first data entry */		QAR = 0x00;			for (i = bytes_write; i < (bytes_write + transfer_size); i++)		{			QDR = kbuffer[i];		}			/* set the queue begining at the entry 0 and the end 		 * at nb_transfer */		QWR = (transfer_size - 1) << 8;		/* enable the transfer */		QDLYR |= (1 << 15);		/* sleep until a interruption send a wake up signal */ 		if ((ret = (wait_event_interruptible 					(dev->qspi_queue, dev->irq_exit_code))))		{			return (ret);		}		/* polling mode */		 /*	do {} while ((QIR & 1) != 1);	 	QIR = 0xB00D;		*/			dbg("%s - irq exit code : %d", __FUNCTION__, dev->irq_exit_code);		dev->irq_exit_code = 0;		bytes_to_write -= transfer_size;		bytes_write += transfer_size;	}	return (bytes_write);}int qspi_rcv (char *kbuffer, unsigned int bytes_to_read, struct qspi *dev){	unsigned int transfer_size = 0;	unsigned int bytes_read = 0;	int i, ret = 0;	while (bytes_to_read)	{		transfer_size = (bytes_to_read < QSPI_QUEUE_SIZE) ? 			bytes_to_read : QSPI_QUEUE_SIZE;				/* select the first command entry */		QAR = 0x0020;			for (i = 0; i < transfer_size; i++)		{			/* set CS0 active */				QDR =  (3 << 12) | dev->chip_select;		}			/* select the first data transmit entry */		QAR = 0x0000;			for (i = 0; i < transfer_size; i++)		{			QDR = 0xff;		}			/* set the queue begining at the entry 0 and the end 		 * at transfer */		QWR = ((transfer_size - 1) << 8);			/* enable the transfer */		QDLYR |= (1 << 15);		/* polling mode */	/* 	do {} while (!(QIR & 1));	 			QIR = 0xB00D;	*/			/* sleep until a interruption send us a wake up signal */ 		if ((ret = (wait_event_interruptible (dev->qspi_queue, 					dev->irq_exit_code))))		{			return (ret);		}		/* take a look on the irq exit code */		dev->irq_exit_code = 0;				/* select the first receive data entry */		QAR = 0x0010;		for (i = bytes_read; i < (bytes_read + transfer_size); i++)		{			kbuffer[i] = QDR;		}		bytes_to_read -= transfer_size;		bytes_read += transfer_size;	}	return (bytes_read);}/* method write to send data via the qspi */static ssize_t qspi_write (struct file *file, const char *buffer, size_t count, loff_t *ppos){	unsigned int bytes_to_write = 0;	int ret = 0;	char *kbuffer;	struct qspi *dev = (struct qspi *) file->private_data;	dbg("%s - size : %d", __FUNCTION__, count);		if (!dev)	{		return (-ENODEV);	}	/* FIX : dev->bits_per_transfer != 8 */	bytes_to_write = count;		if (!bytes_to_write)		goto exit;		/* limit the number of transfer to the queued spi lenght */	bytes_to_write = (bytes_to_write < QSPI_MAX_TRANSFER_SIZE) ? 		bytes_to_write : QSPI_MAX_TRANSFER_SIZE;		kbuffer = (char *) kmalloc (bytes_to_write * sizeof (char), GFP_KERNEL);        	dbg("%s - bytes to write : %d", __FUNCTION__, bytes_to_write);	if (copy_from_user (kbuffer, buffer, bytes_to_write))	{		dbg("%s - copy_from_user error", __FUNCTION__);		ret = -EFAULT;		goto exit_mem;	}		ret = qspi_send (kbuffer, bytes_to_write, dev);	exit_mem :	kfree (kbuffer);exit :	return (ret);}static ssize_t qspi_read (struct file *file, char *buffer, size_t count, loff_t *ppos){	unsigned int bytes_to_read = 0;	int ret = 0;	char *kbuffer;	struct qspi *dev = (struct qspi *) file->private_data;	dbg("%s - size : %d", __FUNCTION__, count);	if (!dev)		return (-ENODEV);	/* FIX if dev->bits_per_transfer != 8 */	bytes_to_read = count;		if (!bytes_to_read)		goto exit;		/* limit the number of transfer to the queued spi lenght */	bytes_to_read = (bytes_to_read < QSPI_MAX_TRANSFER_SIZE) ? 		bytes_to_read : QSPI_MAX_TRANSFER_SIZE;		dbg("%s - bytes_to_read : %d", __FUNCTION__, bytes_to_read);		kbuffer = (char *) kmalloc (bytes_to_read * sizeof (char), 			GFP_KERNEL);        	if ((ret = qspi_rcv (kbuffer, bytes_to_read, dev)) < 0)	{		goto exit_mem;	}	/* if no enough bytes are read, may be we must send 	 * a -EIO signal to the user */		if (copy_to_user(buffer, kbuffer, ret))	{		ret = -EFAULT;	}exit_mem :	kfree (kbuffer);exit :	return (ret);}/* user may be able to change qspi baud rate and delay  * with this method */static int qspi_ioctl (struct inode *inode, struct file *file, 		unsigned int cmd, unsigned long arg){        struct qspi *dev;        int retval = -ENOTTY;        dev = (struct qspi *) file->private_data;        /* lock this object */        down (&dev->qspi_sem);        dbg("%s - cmd 0x%.4x, arg %ld", __FUNCTION__, cmd, arg);	switch (cmd)        {		case CMD_CS_HIGHT :			dev->chip_select |= (1 << dev->device_num);			retval = 0;			break;					case CMD_CS_LOW :			dev->chip_select &= ~(1 << dev->device_num);			retval = 0;			break;		case CMD_CLK :			if (arg < 256)			{				dev->baud_rate = arg; 				set_qmr (dev);				dbg("%s - set qspi clock at %ld", 					__FUNCTION__, arg);				retval = 0;			}			else			{				retval = -EINVAL;			}			break;		default :			break;        }        /* unlock the device */        up (&dev->qspi_sem);        return retval;}/* the realease method is call when user want close () the device */static int qspi_release (struct inode *inode, struct file *file){	int minor = MINOR(inode->i_rdev);	struct qspi *dev = NULL;	int retval = 0;	dbg("enter %s", __FUNCTION__);		dev = (struct qspi *) file->private_data;	/* lock this device */	if (down_interruptible (&dev->qspi_sem))	{			retval = -ERESTARTSYS;		goto exit;	}		/* if they is no user for this device... 	 * we can free the irq and the adc structure */	if ((--dev->open_count) == 0)	{		qspi_stop ();		disable_interrupt_vector (dev->qspi_irq_info);		up (&dev->qspi_sem);		kfree (dev);	}		MOD_DEC_USE_COUNT;	info("close spi device(%d)", minor);exit:	return (retval);}static int __init qspi_init (void){	if (register_chrdev (qspi_major, "ppp", &qspi_fops) < 0)	{		dbg("can't get a major number");		return (-1);	}		info("init complete");		return (0);}static void __exit qspi_exit (void){	if (unregister_chrdev (qspi_major, "ppp"))		dbg("oops... can't release major number");		info("exit complete");}module_init (qspi_init);module_exit (qspi_exit);MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -