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

📄 sync_serial.c

📁 linux内核源码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * 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 + -