📄 qspi_char.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 + -