📄 sync_serial.c
字号:
/* * Simple synchronous serial port driver for ETRAX FS. * * Copyright (c) 2005 Axis Communications AB * * Author: Mikael Starvik * */#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/major.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/poll.h>#include <linux/init.h>#include <linux/timer.h>#include <linux/spinlock.h>#include <asm/io.h>#include <asm/arch/dma.h>#include <asm/arch/pinmux.h>#include <asm/arch/hwregs/reg_rdwr.h>#include <asm/arch/hwregs/sser_defs.h>#include <asm/arch/hwregs/dma_defs.h>#include <asm/arch/hwregs/dma.h>#include <asm/arch/hwregs/intr_vect_defs.h>#include <asm/arch/hwregs/intr_vect.h>#include <asm/arch/hwregs/reg_map.h>#include <asm/sync_serial.h>/* The receiver is a bit tricky beacuse of the continuous stream of data.*//* *//* Three DMA descriptors are linked together. Each DMA descriptor is *//* responsible for port->bufchunk of a common buffer. *//* *//* +---------------------------------------------+ *//* | +----------+ +----------+ +----------+ | *//* +-> | Descr[0] |-->| Descr[1] |-->| Descr[2] |-+ *//* +----------+ +----------+ +----------+ *//* | | | *//* v v v *//* +-------------------------------------+ *//* | BUFFER | *//* +-------------------------------------+ *//* |<- data_avail ->| *//* readp writep *//* *//* If the application keeps up the pace readp will be right after writep.*//* If the application can't keep the pace we have to throw away data. *//* The idea is that readp should be ready with the data pointed out by *//* Descr[i] when the DMA has filled in Descr[i+1]. *//* Otherwise we will discard *//* the rest of the data pointed out by Descr1 and set readp to the start *//* of Descr2 */#define SYNC_SERIAL_MAJOR 125/* IN_BUFFER_SIZE should be a multiple of 6 to make sure that 24 bit *//* words can be handled */#define IN_BUFFER_SIZE 12288#define IN_DESCR_SIZE 256#define NUM_IN_DESCR (IN_BUFFER_SIZE/IN_DESCR_SIZE)#define OUT_BUFFER_SIZE 4096#define DEFAULT_FRAME_RATE 0#define DEFAULT_WORD_RATE 7/* NOTE: Enabling some debug will likely cause overrun or underrun, * especially if manual mode is use. */#define DEBUG(x)#define DEBUGREAD(x)#define DEBUGWRITE(x)#define DEBUGPOLL(x)#define DEBUGRXINT(x)#define DEBUGTXINT(x)typedef struct sync_port{ reg_scope_instances regi_sser; reg_scope_instances regi_dmain; reg_scope_instances regi_dmaout; char started; /* 1 if port has been started */ char port_nbr; /* Port 0 or 1 */ char busy; /* 1 if port is busy */ char enabled; /* 1 if port is enabled */ char use_dma; /* 1 if port uses dma */ char tr_running; char init_irqs; int output; int input; volatile unsigned int out_count; /* Remaining bytes for current transfer */ unsigned char* outp; /* Current position in out_buffer */ volatile unsigned char* volatile readp; /* Next byte to be read by application */ volatile unsigned char* volatile writep; /* Next byte to be written by etrax */ unsigned int in_buffer_size; unsigned int inbufchunk; unsigned char out_buffer[OUT_BUFFER_SIZE] __attribute__ ((aligned(32))); unsigned char in_buffer[IN_BUFFER_SIZE]__attribute__ ((aligned(32))); unsigned char flip[IN_BUFFER_SIZE] __attribute__ ((aligned(32))); struct dma_descr_data* next_rx_desc; struct dma_descr_data* prev_rx_desc; int full; dma_descr_data in_descr[NUM_IN_DESCR] __attribute__ ((__aligned__(16))); dma_descr_context in_context __attribute__ ((__aligned__(32))); dma_descr_data out_descr __attribute__ ((__aligned__(16))); dma_descr_context out_context __attribute__ ((__aligned__(32))); wait_queue_head_t out_wait_q; wait_queue_head_t in_wait_q; spinlock_t lock;} sync_port;static int etrax_sync_serial_init(void);static void initialize_port(int portnbr);static inline int sync_data_avail(struct sync_port *port);static int sync_serial_open(struct inode *, struct file*);static int sync_serial_release(struct inode*, struct file*);static unsigned int sync_serial_poll(struct file *filp, poll_table *wait);static int sync_serial_ioctl(struct inode*, struct file*, unsigned int cmd, unsigned long arg);static ssize_t sync_serial_write(struct file * file, const char * buf, size_t count, loff_t *ppos);static ssize_t sync_serial_read(struct file *file, char *buf, size_t count, loff_t *ppos);#if (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \ defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)) || \ (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) && \ defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA))#define SYNC_SER_DMA#endifstatic void send_word(sync_port* port);static void start_dma(struct sync_port *port, const char* data, int count);static void start_dma_in(sync_port* port);#ifdef SYNC_SER_DMAstatic irqreturn_t tr_interrupt(int irq, void *dev_id, struct pt_regs * regs);static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs);#endif#if (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \ !defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)) || \ (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) && \ !defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA))#define SYNC_SER_MANUAL#endif#ifdef SYNC_SER_MANUALstatic irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs);#endif/* The ports */static struct sync_port ports[]={ { .regi_sser = regi_sser0, .regi_dmaout = regi_dma4, .regi_dmain = regi_dma5,#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA) .use_dma = 1,#else .use_dma = 0,#endif }, { .regi_sser = regi_sser1, .regi_dmaout = regi_dma6, .regi_dmain = regi_dma7,#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA) .use_dma = 1,#else .use_dma = 0,#endif }};#define NUMBER_OF_PORTS ARRAY_SIZE(ports)static const struct file_operations sync_serial_fops = { .owner = THIS_MODULE, .write = sync_serial_write, .read = sync_serial_read, .poll = sync_serial_poll, .ioctl = sync_serial_ioctl, .open = sync_serial_open, .release = sync_serial_release};static int __init etrax_sync_serial_init(void){ ports[0].enabled = 0; ports[1].enabled = 0; if (register_chrdev(SYNC_SERIAL_MAJOR,"sync serial", &sync_serial_fops) <0 ) { printk("unable to get major for synchronous serial port\n"); return -EBUSY; } /* Initialize Ports */#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) if (crisv32_pinmux_alloc_fixed(pinmux_sser0)) { printk("Unable to allocate pins for syncrhronous serial port 0\n"); return -EIO; } ports[0].enabled = 1; initialize_port(0);#endif#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) if (crisv32_pinmux_alloc_fixed(pinmux_sser1)) { printk("Unable to allocate pins for syncrhronous serial port 0\n"); return -EIO; } ports[1].enabled = 1; initialize_port(1);#endif printk("ETRAX FS synchronous serial port driver\n"); return 0;}static void __init initialize_port(int portnbr){ struct sync_port* port = &ports[portnbr]; reg_sser_rw_cfg cfg = {0}; reg_sser_rw_frm_cfg frm_cfg = {0}; reg_sser_rw_tr_cfg tr_cfg = {0}; reg_sser_rw_rec_cfg rec_cfg = {0}; DEBUG(printk("Init sync serial port %d\n", portnbr)); port->port_nbr = portnbr; port->init_irqs = 1; port->outp = port->out_buffer; port->output = 1; port->input = 0; port->readp = port->flip; port->writep = port->flip; port->in_buffer_size = IN_BUFFER_SIZE; port->inbufchunk = IN_DESCR_SIZE; port->next_rx_desc = &port->in_descr[0]; port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR-1]; port->prev_rx_desc->eol = 1; init_waitqueue_head(&port->out_wait_q); init_waitqueue_head(&port->in_wait_q); spin_lock_init(&port->lock); cfg.out_clk_src = regk_sser_intern_clk; cfg.out_clk_pol = regk_sser_pos; cfg.clk_od_mode = regk_sser_no; cfg.clk_dir = regk_sser_out; cfg.gate_clk = regk_sser_no; cfg.base_freq = regk_sser_f29_493; cfg.clk_div = 256; REG_WR(sser, port->regi_sser, rw_cfg, cfg); frm_cfg.wordrate = DEFAULT_WORD_RATE; frm_cfg.type = regk_sser_edge; frm_cfg.frame_pin_dir = regk_sser_out; frm_cfg.frame_pin_use = regk_sser_frm; frm_cfg.status_pin_dir = regk_sser_in; frm_cfg.status_pin_use = regk_sser_hold; frm_cfg.out_on = regk_sser_tr; frm_cfg.tr_delay = 1; REG_WR(sser, port->regi_sser, rw_frm_cfg, frm_cfg); tr_cfg.urun_stop = regk_sser_no; tr_cfg.sample_size = 7; tr_cfg.sh_dir = regk_sser_msbfirst; tr_cfg.use_dma = port->use_dma ? regk_sser_yes : regk_sser_no; tr_cfg.rate_ctrl = regk_sser_bulk; tr_cfg.data_pin_use = regk_sser_dout; tr_cfg.bulk_wspace = 1; REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); rec_cfg.sample_size = 7; rec_cfg.sh_dir = regk_sser_msbfirst; rec_cfg.use_dma = port->use_dma ? regk_sser_yes : regk_sser_no; rec_cfg.fifo_thr = regk_sser_inf; REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg);}static inline int sync_data_avail(struct sync_port *port){ int avail; unsigned char *start; unsigned char *end; start = (unsigned char*)port->readp; /* cast away volatile */ end = (unsigned char*)port->writep; /* cast away volatile */ /* 0123456789 0123456789 * ----- - ----- * ^rp ^wp ^wp ^rp */ if (end >= start) avail = end - start; else avail = port->in_buffer_size - (start - end); return avail;}static inline int sync_data_avail_to_end(struct sync_port *port){ int avail; unsigned char *start; unsigned char *end; start = (unsigned char*)port->readp; /* cast away volatile */ end = (unsigned char*)port->writep; /* cast away volatile */ /* 0123456789 0123456789 * ----- ----- * ^rp ^wp ^wp ^rp */ if (end >= start) avail = end - start; else avail = port->flip + port->in_buffer_size - start; return avail;}static int sync_serial_open(struct inode *inode, struct file *file){ int dev = iminor(inode); sync_port* port; reg_dma_rw_cfg cfg = {.en = regk_dma_yes}; reg_dma_rw_intr_mask intr_mask = {.data = regk_dma_yes}; DEBUG(printk("Open sync serial port %d\n", dev)); if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) { DEBUG(printk("Invalid minor %d\n", dev)); return -ENODEV; } port = &ports[dev]; /* Allow open this device twice (assuming one reader and one writer) */ if (port->busy == 2) { DEBUG(printk("Device is busy.. \n")); return -EBUSY; } if (port->init_irqs) { if (port->use_dma) { if (port == &ports[0]){#ifdef SYNC_SER_DMA if(request_irq(DMA4_INTR_VECT, tr_interrupt, 0, "synchronous serial 0 dma tr", &ports[0])) { printk(KERN_CRIT "Can't allocate sync serial port 0 IRQ"); return -EBUSY; } else if(request_irq(DMA5_INTR_VECT, rx_interrupt, 0, "synchronous serial 1 dma rx", &ports[0])) { free_irq(DMA4_INTR_VECT, &port[0]); printk(KERN_CRIT "Can't allocate sync serial port 0 IRQ"); return -EBUSY; } else if (crisv32_request_dma(SYNC_SER0_TX_DMA_NBR, "synchronous serial 0 dma tr", DMA_VERBOSE_ON_ERROR, 0, dma_sser0)) { free_irq(DMA4_INTR_VECT, &port[0]); free_irq(DMA5_INTR_VECT, &port[0]); printk(KERN_CRIT "Can't allocate sync serial port 0 TX DMA channel"); return -EBUSY; } else if (crisv32_request_dma(SYNC_SER0_RX_DMA_NBR, "synchronous serial 0 dma rec", DMA_VERBOSE_ON_ERROR, 0, dma_sser0)) { crisv32_free_dma(SYNC_SER0_TX_DMA_NBR); free_irq(DMA4_INTR_VECT, &port[0]); free_irq(DMA5_INTR_VECT, &port[0]); printk(KERN_CRIT "Can't allocate sync serial port 1 RX DMA channel"); return -EBUSY; }#endif } else if (port == &ports[1]){#ifdef SYNC_SER_DMA if (request_irq(DMA6_INTR_VECT, tr_interrupt, 0, "synchronous serial 1 dma tr", &ports[1])) { printk(KERN_CRIT "Can't allocate sync serial port 1 IRQ"); return -EBUSY; } else if (request_irq(DMA7_INTR_VECT, rx_interrupt, 0, "synchronous serial 1 dma rx", &ports[1])) { free_irq(DMA6_INTR_VECT, &ports[1]); printk(KERN_CRIT "Can't allocate sync serial port 3 IRQ"); return -EBUSY; } else if (crisv32_request_dma(SYNC_SER1_TX_DMA_NBR, "synchronous serial 1 dma tr", DMA_VERBOSE_ON_ERROR, 0, dma_sser1)) { free_irq(21, &ports[1]); free_irq(20, &ports[1]); printk(KERN_CRIT "Can't allocate sync serial port 3 TX DMA channel"); return -EBUSY;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -