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