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

📄 qspi.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 <linux/sched.h>#include <asm/semaphore.h>#include <asm/errno.h>//#include <linux/slab.h> //ptet malloc.h sur l'uc#include <asm/coldfire.h>#include <asm-m68knommu/m5282sim.h>#include "qspi.h"#include "int.h"/* 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 /* max number of device connected to the spi bus */#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_CLOCK_POLARITY_BIT 0#define QSPI_DFLT_CLOCK_PHASE_BIT 0#define QSPI_DFLT_BITS_PER_TRANSFERT 8#define QSPI_DFLT_CLOCK_DELAY 1#define QSPI_DFLT_TRANSFER_DELAY 1#define QSPI_MAX_BAUD_RATE 255#define QSPI_MIN_BAUD_RATE 2#define QSPI_CS0_LOW 0x0000#define QSPI_CS0_HIGHT 0x0100#define QSPI_VECTOR 18#include "klog.h"/* Version Information */#define DRIVER_AUTHOR "simon"#define DRIVER_DESC "driver to use the QSPI on the 5282 motorola board"struct qspi_dev{	unsigned int in_use;	/* TODO : spi device settings must be independant 	 * from the global bus settings */	unsigned short chip_select;};struct qspi_bus{	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;		struct semaphore sem; /* to protect this struct */	wait_queue_head_t queue;	struct irq_info irq_info;		/* global array of qspi device private structure */	struct qspi_dev qspi_dev_array[QSPI_NB_DEVICE];};	/* qspi bus device descriptor */static struct qspi_bus qspi_bus_dev; static void qspi_bus_start (struct qspi_bus *);static void qspi_bus_stop (void);static void set_qmr (struct qspi_bus *);static void qspi_bus_dev_build (struct qspi_bus *);static void qspi_bus_dev_clean (struct qspi_bus *);static void qspi_irqhandler (int, void *, struct pt_regs *);static int __init qspi_init (void);static void __exit qspi_exit (void);static void qspi_irqhandler (int irq, void *dev_id, struct pt_regs *regs){	struct qspi_bus *bus_dev = (struct qspi_bus *) 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;		bus_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;		bus_dev->irq_exit_code = WRITE_COLLISION_FLAG;	}	/* normal case of a transfer completion */	if (QIR && COMPLETION_FLAG)	{		QIR |= COMPLETION_FLAG;		bus_dev->irq_exit_code = COMPLETION_FLAG;	}	wake_up_interruptible (&bus_dev->queue);		return;}/* method to open the qspi device */int qspi_open (int device_num){	struct qspi_bus *bus_dev = &qspi_bus_dev;	struct qspi_dev *dev;	int retval = 0;		MOD_INC_USE_COUNT;	/* is device_num a correct device number ? */	if (!(device_num < QSPI_NB_DEVICE))	{		retval = -ENODEV;		goto exit;	}		/* lock the bus */	if (down_interruptible (&bus_dev->sem))	{			retval = -ERESTARTSYS;		goto exit;	}		bus_dev->open_count++;	dev = &bus_dev->qspi_dev_array[device_num];		if (dev->in_use)	{		retval = -EBUSY;		goto exit_bus_sem;	}	/* by default set the chip select hight for this device */	dev->chip_select = (1 << device_num);	dev->in_use++;	/* unlock the bus */	up (&bus_dev->sem);	info("open spi device(%d)", device_num);	dbg("%s - bus open_count = %d", __FUNCTION__, bus_dev->open_count);	return (retval);exit_bus_sem :	up (&bus_dev->sem);	exit :	MOD_DEC_USE_COUNT;	return (retval);}/* the realease method is call when user want close () the device */int qspi_close (int device_num){	struct qspi_bus *bus_dev = &qspi_bus_dev;	struct qspi_dev *dev;	int retval = 0;	dbg("enter %s - device num %d", __FUNCTION__, device_num);			/* is device_num a correct device number ? */	if (!(device_num < QSPI_NB_DEVICE))	{		retval = -ENODEV;		goto exit;	}		/* lock the bus */	if (down_interruptible (&bus_dev->sem))	{			retval = -ERESTARTSYS;		goto exit;	}		dev = &bus_dev->qspi_dev_array[device_num];		if (!dev->in_use)	{		retval = -ENODEV;		goto exit_bus_sem;	}	dev->in_use--;	bus_dev->open_count--;	/* unlock the bus */	up (&bus_dev->sem);	MOD_DEC_USE_COUNT;	info("close spi device(%d)", device_num);	return (retval);	exit_bus_sem :	/* unlock the bus */	up (&bus_dev->sem);exit:	return (retval);}size_t qspi_write (int device_num, char *kbuffer, unsigned int bytes_to_write){	struct qspi_bus *bus_dev = &qspi_bus_dev;	struct qspi_dev *dev;	unsigned int bytes_write = 0;	unsigned int transfer_size = 0;	int i, retval = 0;	dbg("enter %s - device num %d", __FUNCTION__, device_num);/*		printk (KERN_DEBUG "kbuffer :\n");	for (i = 0; i < bytes_to_write; i++)	{		printk (KERN_DEBUG "%c", kbuffer[i]);	}	printk (KERN_DEBUG "\n\n");*/		/* is device_num a correct device number ? */	if (!(device_num < QSPI_NB_DEVICE))	{		retval = -ENODEV;		goto exit;	}		/* lock the bus */	if (down_interruptible (&bus_dev->sem))	{			retval = -ERESTARTSYS;		goto exit;	}	dev = &bus_dev->qspi_dev_array[device_num];	if ((!dev) || (!dev->in_use))	{		retval = -ENODEV;		goto exit_bus_sem;	}	if (!bytes_to_write)		goto exit_bus_sem;	/* 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;		dbg("%s - %d bytes to write", __FUNCTION__, bytes_to_write);	while (bytes_to_write)	{		transfer_size = (bytes_to_write < QSPI_QUEUE_SIZE) ? 			bytes_to_write : QSPI_QUEUE_SIZE;				dbg("%s - transfer size : %d", 			__FUNCTION__, transfer_size);		/* select the first command entry */		QAR = 0x20;			for (i = 0; i < transfer_size; i++)		{			/* set device chip select (low or hight) */				/* 3 set the DT and DSCK bits... enable or disable 			 * the delay before and after transfer */			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 */ 		retval = wait_event_interruptible (bus_dev->queue, 				bus_dev->irq_exit_code);				if (retval)		{			retval = -ERESTARTSYS;			goto exit_bus_sem;		}		/* polling mode */		 /*	do {} while ((QIR & 1) != 1);	 	QIR = 0xB00D;		*/			dbg("%s - irq exit code : %d", __FUNCTION__, 				bus_dev->irq_exit_code);		bus_dev->irq_exit_code = 0;		bytes_to_write -= transfer_size;		bytes_write += transfer_size;	}		retval = bytes_write;	dbg("%s - %d bytes successfully writes", 			__FUNCTION__, bytes_write);exit_bus_sem :	/* unlock the bus */	up (&bus_dev->sem);exit :	return (retval);}size_t qspi_read (int device_num, char *kbuffer, unsigned int bytes_to_read){	struct qspi_bus *bus_dev = &qspi_bus_dev;	struct qspi_dev *dev;	unsigned int transfer_size = 0;	unsigned int bytes_read = 0;	int i, retval = 0;	dbg("enter %s - device num %d", __FUNCTION__, device_num);	/* is device_num a correct device number ? */	if (!(device_num < QSPI_NB_DEVICE))	{		retval = -ENODEV;		goto exit;	}	/* lock the bus */	if (down_interruptible (&bus_dev->sem))	{			retval = -ERESTARTSYS;		goto exit;	}		dev = &bus_dev->qspi_dev_array[device_num];	if ((!dev) || (!dev->in_use))	{		retval = -ENODEV;		goto exit_bus_sem;	}	if (!bytes_to_read)		goto exit_bus_sem;	/* 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 - %d bytes to read", __FUNCTION__, bytes_to_read);	while (bytes_to_read)	{		transfer_size = (bytes_to_read < QSPI_QUEUE_SIZE) ? 			bytes_to_read : QSPI_QUEUE_SIZE;				dbg("%s - transfer size : %d", 			__FUNCTION__, transfer_size);				/* select the first command entry */		QAR = 0x0020;			for (i = 0; i < transfer_size; i++)		{			/* set CS0 active */				/* 3 set the DT and DSCK bits... enable or disable 			 * the delay before and after transfer */			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 */ 		retval = wait_event_interruptible (bus_dev->queue, 				bus_dev->irq_exit_code);		if (retval)		{			retval = -ERESTARTSYS;			goto exit_bus_sem;		}		/* take a look on the irq exit code */		/* reset the irq exit code */		bus_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;	}	retval = bytes_read;exit_bus_sem :	/* unlock the bus */	up (&bus_dev->sem);exit :	return (retval);}int qspi_ioctl (int device_num, unsigned int cmd, unsigned long arg){	struct qspi_bus *bus_dev = &qspi_bus_dev;	struct qspi_dev *dev;        int retval = 0;	dbg("enter %s - device num %d", __FUNCTION__, device_num);	/* is device_num a correct device number ? */	if (!(device_num < QSPI_NB_DEVICE))	{		retval = -ENODEV;		goto exit;	}	/* lock the bus */	if (down_interruptible (&bus_dev->sem))	{			retval = -ERESTARTSYS;		goto exit;	}		dev = &bus_dev->qspi_dev_array[device_num];	if ((!dev) || (!dev->in_use))	{		retval = -ENODEV;		goto exit_bus_sem;	}        dbg("%s - device %d - cmd 0x%.4x, arg %ld", 			__FUNCTION__, device_num, cmd, arg);	switch (cmd)        {		case CMD_CS_HIGHT :			dev->chip_select |= (1 << device_num);			retval = 0;			break;					case CMD_CS_LOW :			dev->chip_select &= ~(1 << device_num);			retval = 0;			break;				case CMD_CLK :			if (arg < 256)			{				bus_dev->baud_rate = arg; 				set_qmr (bus_dev);				dbg("%s - set qspi clock at %ld", 					__FUNCTION__, arg);				retval = 0;			}			else			{				retval = -EINVAL;			}			break;		default :			retval = -EINVAL;			break;        }exit_bus_sem :        /* unlock the bus */        up (&bus_dev->sem);exit :        return retval;}/* function to stop qspi transfer */static void qspi_bus_stop (void){	info("stop qspi bus");		QWR |= (1 << 15);	return;}static void set_qmr (struct qspi_bus *bus_dev){	if (!bus_dev)		return;	/* FIX 16 bits tranfer  */	QMR = ((1 << 15) | (bus_dev->bits_per_transfer << 10) | 		(bus_dev->clock_polarity_bit << 9) | 		(bus_dev->clock_phase_bit << 8) | bus_dev->baud_rate);	return;}/* function to initialize qspi register */static void qspi_bus_start (struct qspi_bus *bus_dev){	info("start qspi bus");	/* set the port QS configuration for SPI */	PQSPAR = 0x7f;	set_qmr (bus_dev);	dbg("%s - QMR = 0x%x", __FUNCTION__, QMR);		QDLYR = (bus_dev->transfer_delay | (bus_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;}static void qspi_bus_dev_build (struct qspi_bus *bus_dev){	int i;	dbg("enter %s", __FUNCTION__);	qspi_bus_dev_clean (bus_dev);		bus_dev->baud_rate = QSPI_DFLT_BAUD_RATE;	bus_dev->bits_per_transfer = QSPI_DFLT_BITS_PER_TRANSFERT;	bus_dev->clock_polarity_bit = QSPI_DFLT_CLOCK_POLARITY_BIT;	bus_dev->clock_phase_bit = QSPI_DFLT_CLOCK_PHASE_BIT;	bus_dev->clock_delay = QSPI_DFLT_CLOCK_DELAY;	bus_dev->transfer_delay = QSPI_DFLT_TRANSFER_DELAY;	bus_dev->irq_exit_code = 0;	init_waitqueue_head (&bus_dev->queue);	sema_init (&bus_dev->sem, 1);	bus_dev->open_count = 0;		for (i = 0; i < QSPI_NB_DEVICE; i++)	{		bus_dev->qspi_dev_array[i].in_use = 0;	}	return;}static void qspi_bus_dev_clean (struct qspi_bus *bus_dev){	memset (bus_dev, 0x00, sizeof (struct qspi_bus));		return;}static int __init qspi_init (void){	int retval = 0;	/* build the qspi bus structure */	qspi_bus_dev_build (&qspi_bus_dev);	/* initiate the structure to request our interrupt vector */	sprintf (qspi_bus_dev.irq_info.name, "qspi_int");	qspi_bus_dev.irq_info.vector = QSPI_VECTOR + INTC0_VECT_MIN;	qspi_bus_dev.irq_info.level = IRQ_LEVEL_DFLT; /* default level for the qspi interrupt */	qspi_bus_dev.irq_info.handler = qspi_irqhandler;	qspi_bus_dev.irq_info.dev_id = &qspi_bus_dev;	retval = enable_interrupt_vector (&qspi_bus_dev.irq_info);	if (retval)	{		err("can't enable qspi interrupt vector");		return (retval);	}			qspi_bus_stop ();	qspi_bus_start (&qspi_bus_dev);		info("init complete");	return (retval);}static void __exit qspi_exit (void){	qspi_bus_stop ();	disable_interrupt_vector (&qspi_bus_dev.irq_info);		info("exit complete");		return;}module_init (qspi_init);module_exit (qspi_exit);EXPORT_SYMBOL(qspi_open);EXPORT_SYMBOL(qspi_close);EXPORT_SYMBOL(qspi_read);EXPORT_SYMBOL(qspi_write);EXPORT_SYMBOL(qspi_ioctl);MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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