2008-01-21 10:44:11 +00:00
|
|
|
/*
|
|
|
|
* Simple synchronous serial port driver for ETRAX 100LX.
|
|
|
|
*
|
|
|
|
* Synchronous serial ports are used for continuous streamed data like audio.
|
|
|
|
* The default setting for this driver is compatible with the STA 013 MP3
|
|
|
|
* decoder. The driver can easily be tuned to fit other audio encoder/decoders
|
|
|
|
* and SPI
|
|
|
|
*
|
|
|
|
* Copyright (c) 2001-2008 Axis Communications AB
|
|
|
|
*
|
|
|
|
* Author: Mikael Starvik, Johan Adolfsson
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
#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/interrupt.h>
|
|
|
|
#include <linux/poll.h>
|
|
|
|
#include <linux/init.h>
|
2010-09-11 16:18:22 +00:00
|
|
|
#include <linux/mutex.h>
|
2008-01-21 10:44:11 +00:00
|
|
|
#include <linux/timer.h>
|
|
|
|
#include <asm/irq.h>
|
|
|
|
#include <asm/dma.h>
|
|
|
|
#include <asm/io.h>
|
2008-10-21 15:45:58 +00:00
|
|
|
#include <arch/svinto.h>
|
2008-01-21 10:44:11 +00:00
|
|
|
#include <asm/uaccess.h>
|
|
|
|
#include <asm/sync_serial.h>
|
2008-10-21 15:45:58 +00:00
|
|
|
#include <arch/io_interface_mux.h>
|
2008-01-21 10:44:11 +00:00
|
|
|
|
2011-03-31 01:57:33 +00:00
|
|
|
/* The receiver is a bit tricky because of the continuous stream of data.*/
|
2008-01-21 10:44:11 +00:00
|
|
|
/* */
|
|
|
|
/* 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)
|
|
|
|
|
|
|
|
/* Define some macros to access ETRAX 100 registers */
|
|
|
|
#define SETF(var, reg, field, val) \
|
|
|
|
do { \
|
|
|
|
var = (var & ~IO_MASK_(reg##_, field##_)) | \
|
|
|
|
IO_FIELD_(reg##_, field##_, val); \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define SETS(var, reg, field, val) \
|
|
|
|
do { \
|
|
|
|
var = (var & ~IO_MASK_(reg##_, field##_)) | \
|
|
|
|
IO_STATE_(reg##_, field##_, _##val); \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
struct sync_port {
|
|
|
|
/* Etrax registers and bits*/
|
|
|
|
const volatile unsigned *const status;
|
|
|
|
volatile unsigned *const ctrl_data;
|
|
|
|
volatile unsigned *const output_dma_first;
|
|
|
|
volatile unsigned char *const output_dma_cmd;
|
|
|
|
volatile unsigned char *const output_dma_clr_irq;
|
|
|
|
volatile unsigned *const input_dma_first;
|
|
|
|
volatile unsigned char *const input_dma_cmd;
|
|
|
|
volatile unsigned *const input_dma_descr;
|
|
|
|
/* 8*4 */
|
|
|
|
volatile unsigned char *const input_dma_clr_irq;
|
|
|
|
volatile unsigned *const data_out;
|
|
|
|
const volatile unsigned *const data_in;
|
|
|
|
char data_avail_bit; /* In R_IRQ_MASK1_RD/SET/CLR */
|
|
|
|
char transmitter_ready_bit; /* In R_IRQ_MASK1_RD/SET/CLR */
|
|
|
|
char input_dma_descr_bit; /* In R_IRQ_MASK2_RD */
|
|
|
|
|
|
|
|
char output_dma_bit; /* In R_IRQ_MASK2_RD */
|
|
|
|
/* End of fields initialised in array */
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* Register shadow */
|
|
|
|
unsigned int ctrl_data_shadow;
|
|
|
|
/* Remaining bytes for current transfer */
|
|
|
|
volatile unsigned int out_count;
|
|
|
|
/* Current position in out_buffer */
|
|
|
|
unsigned char *outp;
|
|
|
|
/* 16*4 */
|
|
|
|
/* Next byte to be read by application */
|
|
|
|
volatile unsigned char *volatile readp;
|
|
|
|
/* Next byte to be written by etrax */
|
|
|
|
volatile unsigned char *volatile writep;
|
|
|
|
|
|
|
|
unsigned int in_buffer_size;
|
|
|
|
unsigned int inbufchunk;
|
|
|
|
struct etrax_dma_descr out_descr __attribute__ ((aligned(32)));
|
|
|
|
struct etrax_dma_descr in_descr[NUM_IN_DESCR] __attribute__ ((aligned(32)));
|
|
|
|
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 etrax_dma_descr *next_rx_desc;
|
|
|
|
struct etrax_dma_descr *prev_rx_desc;
|
|
|
|
int full;
|
|
|
|
|
|
|
|
wait_queue_head_t out_wait_q;
|
|
|
|
wait_queue_head_t in_wait_q;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2010-09-11 16:18:22 +00:00
|
|
|
static DEFINE_MUTEX(sync_serial_mutex);
|
2008-01-21 10:44:11 +00:00
|
|
|
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 *inode, struct file *file);
|
|
|
|
static int sync_serial_release(struct inode *inode, struct file *file);
|
|
|
|
static unsigned int sync_serial_poll(struct file *filp, poll_table *wait);
|
|
|
|
|
2011-08-03 23:21:14 +00:00
|
|
|
static long sync_serial_ioctl(struct file *file,
|
2008-01-21 10:44:11 +00:00
|
|
|
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
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void send_word(struct sync_port *port);
|
|
|
|
static void start_dma(struct sync_port *port, const char *data, int count);
|
|
|
|
static void start_dma_in(struct sync_port *port);
|
|
|
|
#ifdef SYNC_SER_DMA
|
|
|
|
static irqreturn_t tr_interrupt(int irq, void *dev_id);
|
|
|
|
static irqreturn_t rx_interrupt(int irq, void *dev_id);
|
|
|
|
#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_MANUAL
|
|
|
|
static irqreturn_t manual_interrupt(int irq, void *dev_id);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* The ports */
|
|
|
|
static struct sync_port ports[] = {
|
|
|
|
{
|
|
|
|
.status = R_SYNC_SERIAL1_STATUS,
|
|
|
|
.ctrl_data = R_SYNC_SERIAL1_CTRL,
|
|
|
|
.output_dma_first = R_DMA_CH8_FIRST,
|
|
|
|
.output_dma_cmd = R_DMA_CH8_CMD,
|
|
|
|
.output_dma_clr_irq = R_DMA_CH8_CLR_INTR,
|
|
|
|
.input_dma_first = R_DMA_CH9_FIRST,
|
|
|
|
.input_dma_cmd = R_DMA_CH9_CMD,
|
|
|
|
.input_dma_descr = R_DMA_CH9_DESCR,
|
|
|
|
.input_dma_clr_irq = R_DMA_CH9_CLR_INTR,
|
|
|
|
.data_out = R_SYNC_SERIAL1_TR_DATA,
|
|
|
|
.data_in = R_SYNC_SERIAL1_REC_DATA,
|
|
|
|
.data_avail_bit = IO_BITNR(R_IRQ_MASK1_RD, ser1_data),
|
|
|
|
.transmitter_ready_bit = IO_BITNR(R_IRQ_MASK1_RD, ser1_ready),
|
|
|
|
.input_dma_descr_bit = IO_BITNR(R_IRQ_MASK2_RD, dma9_descr),
|
|
|
|
.output_dma_bit = IO_BITNR(R_IRQ_MASK2_RD, dma8_eop),
|
|
|
|
.init_irqs = 1,
|
|
|
|
#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)
|
|
|
|
.use_dma = 1,
|
|
|
|
#else
|
|
|
|
.use_dma = 0,
|
|
|
|
#endif
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.status = R_SYNC_SERIAL3_STATUS,
|
|
|
|
.ctrl_data = R_SYNC_SERIAL3_CTRL,
|
|
|
|
.output_dma_first = R_DMA_CH4_FIRST,
|
|
|
|
.output_dma_cmd = R_DMA_CH4_CMD,
|
|
|
|
.output_dma_clr_irq = R_DMA_CH4_CLR_INTR,
|
|
|
|
.input_dma_first = R_DMA_CH5_FIRST,
|
|
|
|
.input_dma_cmd = R_DMA_CH5_CMD,
|
|
|
|
.input_dma_descr = R_DMA_CH5_DESCR,
|
|
|
|
.input_dma_clr_irq = R_DMA_CH5_CLR_INTR,
|
|
|
|
.data_out = R_SYNC_SERIAL3_TR_DATA,
|
|
|
|
.data_in = R_SYNC_SERIAL3_REC_DATA,
|
|
|
|
.data_avail_bit = IO_BITNR(R_IRQ_MASK1_RD, ser3_data),
|
|
|
|
.transmitter_ready_bit = IO_BITNR(R_IRQ_MASK1_RD, ser3_ready),
|
|
|
|
.input_dma_descr_bit = IO_BITNR(R_IRQ_MASK2_RD, dma5_descr),
|
|
|
|
.output_dma_bit = IO_BITNR(R_IRQ_MASK2_RD, dma4_eop),
|
|
|
|
.init_irqs = 1,
|
|
|
|
#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA)
|
|
|
|
.use_dma = 1,
|
|
|
|
#else
|
|
|
|
.use_dma = 0,
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Register shadows */
|
|
|
|
static unsigned sync_serial_prescale_shadow;
|
|
|
|
|
|
|
|
#define NUMBER_OF_PORTS 2
|
|
|
|
|
2009-10-01 22:43:56 +00:00
|
|
|
static const struct file_operations sync_serial_fops = {
|
2010-07-30 17:04:37 +00:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.write = sync_serial_write,
|
|
|
|
.read = sync_serial_read,
|
|
|
|
.poll = sync_serial_poll,
|
|
|
|
.unlocked_ioctl = sync_serial_ioctl,
|
|
|
|
.open = sync_serial_open,
|
llseek: automatically add .llseek fop
All file_operations should get a .llseek operation so we can make
nonseekable_open the default for future file operations without a
.llseek pointer.
The three cases that we can automatically detect are no_llseek, seq_lseek
and default_llseek. For cases where we can we can automatically prove that
the file offset is always ignored, we use noop_llseek, which maintains
the current behavior of not returning an error from a seek.
New drivers should normally not use noop_llseek but instead use no_llseek
and call nonseekable_open at open time. Existing drivers can be converted
to do the same when the maintainer knows for certain that no user code
relies on calling seek on the device file.
The generated code is often incorrectly indented and right now contains
comments that clarify for each added line why a specific variant was
chosen. In the version that gets submitted upstream, the comments will
be gone and I will manually fix the indentation, because there does not
seem to be a way to do that using coccinelle.
Some amount of new code is currently sitting in linux-next that should get
the same modifications, which I will do at the end of the merge window.
Many thanks to Julia Lawall for helping me learn to write a semantic
patch that does all this.
===== begin semantic patch =====
// This adds an llseek= method to all file operations,
// as a preparation for making no_llseek the default.
//
// The rules are
// - use no_llseek explicitly if we do nonseekable_open
// - use seq_lseek for sequential files
// - use default_llseek if we know we access f_pos
// - use noop_llseek if we know we don't access f_pos,
// but we still want to allow users to call lseek
//
@ open1 exists @
identifier nested_open;
@@
nested_open(...)
{
<+...
nonseekable_open(...)
...+>
}
@ open exists@
identifier open_f;
identifier i, f;
identifier open1.nested_open;
@@
int open_f(struct inode *i, struct file *f)
{
<+...
(
nonseekable_open(...)
|
nested_open(...)
)
...+>
}
@ read disable optional_qualifier exists @
identifier read_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
expression E;
identifier func;
@@
ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off)
{
<+...
(
*off = E
|
*off += E
|
func(..., off, ...)
|
E = *off
)
...+>
}
@ read_no_fpos disable optional_qualifier exists @
identifier read_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
@@
ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off)
{
... when != off
}
@ write @
identifier write_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
expression E;
identifier func;
@@
ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off)
{
<+...
(
*off = E
|
*off += E
|
func(..., off, ...)
|
E = *off
)
...+>
}
@ write_no_fpos @
identifier write_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
@@
ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off)
{
... when != off
}
@ fops0 @
identifier fops;
@@
struct file_operations fops = {
...
};
@ has_llseek depends on fops0 @
identifier fops0.fops;
identifier llseek_f;
@@
struct file_operations fops = {
...
.llseek = llseek_f,
...
};
@ has_read depends on fops0 @
identifier fops0.fops;
identifier read_f;
@@
struct file_operations fops = {
...
.read = read_f,
...
};
@ has_write depends on fops0 @
identifier fops0.fops;
identifier write_f;
@@
struct file_operations fops = {
...
.write = write_f,
...
};
@ has_open depends on fops0 @
identifier fops0.fops;
identifier open_f;
@@
struct file_operations fops = {
...
.open = open_f,
...
};
// use no_llseek if we call nonseekable_open
////////////////////////////////////////////
@ nonseekable1 depends on !has_llseek && has_open @
identifier fops0.fops;
identifier nso ~= "nonseekable_open";
@@
struct file_operations fops = {
... .open = nso, ...
+.llseek = no_llseek, /* nonseekable */
};
@ nonseekable2 depends on !has_llseek @
identifier fops0.fops;
identifier open.open_f;
@@
struct file_operations fops = {
... .open = open_f, ...
+.llseek = no_llseek, /* open uses nonseekable */
};
// use seq_lseek for sequential files
/////////////////////////////////////
@ seq depends on !has_llseek @
identifier fops0.fops;
identifier sr ~= "seq_read";
@@
struct file_operations fops = {
... .read = sr, ...
+.llseek = seq_lseek, /* we have seq_read */
};
// use default_llseek if there is a readdir
///////////////////////////////////////////
@ fops1 depends on !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier readdir_e;
@@
// any other fop is used that changes pos
struct file_operations fops = {
... .readdir = readdir_e, ...
+.llseek = default_llseek, /* readdir is present */
};
// use default_llseek if at least one of read/write touches f_pos
/////////////////////////////////////////////////////////////////
@ fops2 depends on !fops1 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read.read_f;
@@
// read fops use offset
struct file_operations fops = {
... .read = read_f, ...
+.llseek = default_llseek, /* read accesses f_pos */
};
@ fops3 depends on !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier write.write_f;
@@
// write fops use offset
struct file_operations fops = {
... .write = write_f, ...
+ .llseek = default_llseek, /* write accesses f_pos */
};
// Use noop_llseek if neither read nor write accesses f_pos
///////////////////////////////////////////////////////////
@ fops4 depends on !fops1 && !fops2 && !fops3 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read_no_fpos.read_f;
identifier write_no_fpos.write_f;
@@
// write fops use offset
struct file_operations fops = {
...
.write = write_f,
.read = read_f,
...
+.llseek = noop_llseek, /* read and write both use no f_pos */
};
@ depends on has_write && !has_read && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier write_no_fpos.write_f;
@@
struct file_operations fops = {
... .write = write_f, ...
+.llseek = noop_llseek, /* write uses no f_pos */
};
@ depends on has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read_no_fpos.read_f;
@@
struct file_operations fops = {
... .read = read_f, ...
+.llseek = noop_llseek, /* read uses no f_pos */
};
@ depends on !has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
@@
struct file_operations fops = {
...
+.llseek = noop_llseek, /* no read or write fn */
};
===== End semantic patch =====
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Julia Lawall <julia@diku.dk>
Cc: Christoph Hellwig <hch@infradead.org>
2010-08-15 16:52:59 +00:00
|
|
|
.release = sync_serial_release,
|
|
|
|
.llseek = noop_llseek,
|
2008-01-21 10:44:11 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int __init etrax_sync_serial_init(void)
|
|
|
|
{
|
|
|
|
ports[0].enabled = 0;
|
|
|
|
ports[1].enabled = 0;
|
|
|
|
|
|
|
|
#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0)
|
|
|
|
if (cris_request_io_interface(if_sync_serial_1, "sync_ser1")) {
|
|
|
|
printk(KERN_CRIT "ETRAX100LX sync_serial: "
|
|
|
|
"Could not allocate IO group for port %d\n", 0);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1)
|
|
|
|
if (cris_request_io_interface(if_sync_serial_3, "sync_ser3")) {
|
|
|
|
#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0)
|
|
|
|
cris_free_io_interface(if_sync_serial_1);
|
|
|
|
#endif
|
|
|
|
printk(KERN_CRIT "ETRAX100LX sync_serial: "
|
|
|
|
"Could not allocate IO group for port %d\n", 1);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (register_chrdev(SYNC_SERIAL_MAJOR, "sync serial",
|
|
|
|
&sync_serial_fops) < 0) {
|
|
|
|
#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1)
|
|
|
|
cris_free_io_interface(if_sync_serial_3);
|
|
|
|
#endif
|
|
|
|
#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0)
|
|
|
|
cris_free_io_interface(if_sync_serial_1);
|
|
|
|
#endif
|
|
|
|
printk("unable to get major for synchronous serial port\n");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Deselect synchronous serial ports while configuring. */
|
|
|
|
SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, async);
|
|
|
|
SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, async);
|
|
|
|
*R_GEN_CONFIG_II = gen_config_ii_shadow;
|
|
|
|
|
|
|
|
/* Initialize Ports */
|
|
|
|
#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0)
|
|
|
|
ports[0].enabled = 1;
|
|
|
|
SETS(port_pb_i2c_shadow, R_PORT_PB_I2C, syncser1, ss1extra);
|
|
|
|
SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, sync);
|
|
|
|
#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)
|
|
|
|
ports[0].use_dma = 1;
|
|
|
|
#else
|
|
|
|
ports[0].use_dma = 0;
|
|
|
|
#endif
|
|
|
|
initialize_port(0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1)
|
|
|
|
ports[1].enabled = 1;
|
|
|
|
SETS(port_pb_i2c_shadow, R_PORT_PB_I2C, syncser3, ss3extra);
|
|
|
|
SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, sync);
|
|
|
|
#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA)
|
|
|
|
ports[1].use_dma = 1;
|
|
|
|
#else
|
|
|
|
ports[1].use_dma = 0;
|
|
|
|
#endif
|
|
|
|
initialize_port(1);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
*R_PORT_PB_I2C = port_pb_i2c_shadow; /* Use PB4/PB7 */
|
|
|
|
|
|
|
|
/* Set up timing */
|
|
|
|
*R_SYNC_SERIAL_PRESCALE = sync_serial_prescale_shadow = (
|
|
|
|
IO_STATE(R_SYNC_SERIAL_PRESCALE, clk_sel_u1, codec) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL_PRESCALE, word_stb_sel_u1, external) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL_PRESCALE, clk_sel_u3, codec) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL_PRESCALE, word_stb_sel_u3, external) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL_PRESCALE, prescaler, div4) |
|
|
|
|
IO_FIELD(R_SYNC_SERIAL_PRESCALE, frame_rate,
|
|
|
|
DEFAULT_FRAME_RATE) |
|
|
|
|
IO_FIELD(R_SYNC_SERIAL_PRESCALE, word_rate, DEFAULT_WORD_RATE) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL_PRESCALE, warp_mode, normal));
|
|
|
|
|
|
|
|
/* Select synchronous ports */
|
|
|
|
*R_GEN_CONFIG_II = gen_config_ii_shadow;
|
|
|
|
|
|
|
|
printk(KERN_INFO "ETRAX 100LX synchronous serial port driver\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __init initialize_port(int portnbr)
|
|
|
|
{
|
|
|
|
struct sync_port *port = &ports[portnbr];
|
|
|
|
|
|
|
|
DEBUG(printk(KERN_DEBUG "Init sync serial port %d\n", portnbr));
|
|
|
|
|
|
|
|
port->started = 0;
|
|
|
|
port->port_nbr = portnbr;
|
|
|
|
port->busy = 0;
|
|
|
|
port->tr_running = 0;
|
|
|
|
|
|
|
|
port->out_count = 0;
|
|
|
|
port->outp = port->out_buffer;
|
|
|
|
|
|
|
|
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->ctrl = d_eol;
|
|
|
|
|
|
|
|
init_waitqueue_head(&port->out_wait_q);
|
|
|
|
init_waitqueue_head(&port->in_wait_q);
|
|
|
|
|
|
|
|
port->ctrl_data_shadow =
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, tr_baud, c115k2Hz) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, mode, master_output) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, error, ignore) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, rec_enable, disable) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, f_synctype, normal) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, f_syncsize, word) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, f_sync, on) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, clk_mode, normal) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, clk_halt, stopped) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, bitorder, msb) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, tr_enable, disable) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, buf_empty, lmt_8) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, buf_full, lmt_8) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, flow_ctrl, enabled) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, clk_polarity, neg) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, frame_polarity, normal)|
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, status_polarity, inverted)|
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, clk_driver, normal) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, frame_driver, normal) |
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, status_driver, normal)|
|
|
|
|
IO_STATE(R_SYNC_SERIAL1_CTRL, def_out0, high);
|
|
|
|
|
|
|
|
if (port->use_dma)
|
|
|
|
port->ctrl_data_shadow |= IO_STATE(R_SYNC_SERIAL1_CTRL,
|
|
|
|
dma_enable, on);
|
|
|
|
else
|
|
|
|
port->ctrl_data_shadow |= IO_STATE(R_SYNC_SERIAL1_CTRL,
|
|
|
|
dma_enable, off);
|
|
|
|
|
|
|
|
*port->ctrl_data = port->ctrl_data_shadow;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 = MINOR(inode->i_rdev);
|
|
|
|
struct sync_port *port;
|
|
|
|
int mode;
|
2008-05-15 15:10:18 +00:00
|
|
|
int err = -EBUSY;
|
2008-01-21 10:44:11 +00:00
|
|
|
|
2010-09-11 16:18:22 +00:00
|
|
|
mutex_lock(&sync_serial_mutex);
|
2008-01-21 10:44:11 +00:00
|
|
|
DEBUG(printk(KERN_DEBUG "Open sync serial port %d\n", dev));
|
|
|
|
|
|
|
|
if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) {
|
|
|
|
DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev));
|
2008-05-15 15:10:18 +00:00
|
|
|
err = -ENODEV;
|
|
|
|
goto out;
|
2008-01-21 10:44:11 +00:00
|
|
|
}
|
|
|
|
port = &ports[dev];
|
|
|
|
/* Allow open this device twice (assuming one reader and one writer) */
|
|
|
|
if (port->busy == 2) {
|
|
|
|
DEBUG(printk(KERN_DEBUG "Device is busy.. \n"));
|
2008-05-15 15:10:18 +00:00
|
|
|
goto out;
|
2008-01-21 10:44:11 +00:00
|
|
|
}
|
|
|
|
if (port->init_irqs) {
|
|
|
|
if (port->use_dma) {
|
|
|
|
if (port == &ports[0]) {
|
|
|
|
#ifdef SYNC_SER_DMA
|
|
|
|
if (request_irq(24, tr_interrupt, 0,
|
|
|
|
"synchronous serial 1 dma tr",
|
|
|
|
&ports[0])) {
|
|
|
|
printk(KERN_CRIT "Can't alloc "
|
|
|
|
"sync serial port 1 IRQ");
|
2008-05-15 15:10:18 +00:00
|
|
|
goto out;
|
2008-01-21 10:44:11 +00:00
|
|
|
} else if (request_irq(25, rx_interrupt, 0,
|
|
|
|
"synchronous serial 1 dma rx",
|
|
|
|
&ports[0])) {
|
|
|
|
free_irq(24, &port[0]);
|
|
|
|
printk(KERN_CRIT "Can't alloc "
|
|
|
|
"sync serial port 1 IRQ");
|
2008-05-15 15:10:18 +00:00
|
|
|
goto out;
|
2008-01-21 10:44:11 +00:00
|
|
|
} else if (cris_request_dma(8,
|
|
|
|
"synchronous serial 1 dma tr",
|
|
|
|
DMA_VERBOSE_ON_ERROR,
|
|
|
|
dma_ser1)) {
|
|
|
|
free_irq(24, &port[0]);
|
|
|
|
free_irq(25, &port[0]);
|
|
|
|
printk(KERN_CRIT "Can't alloc "
|
|
|
|
"sync serial port 1 "
|
|
|
|
"TX DMA channel");
|
2008-05-15 15:10:18 +00:00
|
|
|
goto out;
|
2008-01-21 10:44:11 +00:00
|
|
|
} else if (cris_request_dma(9,
|
|
|
|
"synchronous serial 1 dma rec",
|
|
|
|
DMA_VERBOSE_ON_ERROR,
|
|
|
|
dma_ser1)) {
|
|
|
|
cris_free_dma(8, NULL);
|
|
|
|
free_irq(24, &port[0]);
|
|
|
|
free_irq(25, &port[0]);
|
|
|
|
printk(KERN_CRIT "Can't alloc "
|
|
|
|
"sync serial port 1 "
|
|
|
|
"RX DMA channel");
|
2008-05-15 15:10:18 +00:00
|
|
|
goto out;
|
2008-01-21 10:44:11 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
RESET_DMA(8); WAIT_DMA(8);
|
|
|
|
RESET_DMA(9); WAIT_DMA(9);
|
|
|
|
*R_DMA_CH8_CLR_INTR =
|
|
|
|
IO_STATE(R_DMA_CH8_CLR_INTR, clr_eop,
|
|
|
|
do) |
|
|
|
|
IO_STATE(R_DMA_CH8_CLR_INTR, clr_descr,
|
|
|
|
do);
|
|
|
|
*R_DMA_CH9_CLR_INTR =
|
|
|
|
IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop,
|
|
|
|
do) |
|
|
|
|
IO_STATE(R_DMA_CH9_CLR_INTR, clr_descr,
|
|
|
|
do);
|
|
|
|
*R_IRQ_MASK2_SET =
|
|
|
|
IO_STATE(R_IRQ_MASK2_SET, dma8_eop,
|
|
|
|
set) |
|
|
|
|
IO_STATE(R_IRQ_MASK2_SET, dma9_descr,
|
|
|
|
set);
|
|
|
|
} else if (port == &ports[1]) {
|
|
|
|
#ifdef SYNC_SER_DMA
|
|
|
|
if (request_irq(20, tr_interrupt, 0,
|
|
|
|
"synchronous serial 3 dma tr",
|
|
|
|
&ports[1])) {
|
|
|
|
printk(KERN_CRIT "Can't alloc "
|
|
|
|
"sync serial port 3 IRQ");
|
2008-05-15 15:10:18 +00:00
|
|
|
goto out;
|
2008-01-21 10:44:11 +00:00
|
|
|
} else if (request_irq(21, rx_interrupt, 0,
|
|
|
|
"synchronous serial 3 dma rx",
|
|
|
|
&ports[1])) {
|
|
|
|
free_irq(20, &ports[1]);
|
|
|
|
printk(KERN_CRIT "Can't alloc "
|
|
|
|
"sync serial port 3 IRQ");
|
2008-05-15 15:10:18 +00:00
|
|
|
goto out;
|
2008-01-21 10:44:11 +00:00
|
|
|
} else if (cris_request_dma(4,
|
|
|
|
"synchronous serial 3 dma tr",
|
|
|
|
DMA_VERBOSE_ON_ERROR,
|
|
|
|
dma_ser3)) {
|
|
|
|
free_irq(21, &ports[1]);
|
|
|
|
free_irq(20, &ports[1]);
|
|
|
|
printk(KERN_CRIT "Can't alloc "
|
|
|
|
"sync serial port 3 "
|
|
|
|
"TX DMA channel");
|
2008-05-15 15:10:18 +00:00
|
|
|
goto out;
|
2008-01-21 10:44:11 +00:00
|
|
|
} else if (cris_request_dma(5,
|
|
|
|
"synchronous serial 3 dma rec",
|
|
|
|
DMA_VERBOSE_ON_ERROR,
|
|
|
|
dma_ser3)) {
|
|
|
|
cris_free_dma(4, NULL);
|
|
|
|
free_irq(21, &ports[1]);
|
|
|
|
free_irq(20, &ports[1]);
|
|
|
|
printk(KERN_CRIT "Can't alloc "
|
|
|
|
"sync serial port 3 "
|
|
|
|
"RX DMA channel");
|
2008-05-15 15:10:18 +00:00
|
|
|
goto out;
|
2008-01-21 10:44:11 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
RESET_DMA(4); WAIT_DMA(4);
|
|
|
|
RESET_DMA(5); WAIT_DMA(5);
|
|
|
|
*R_DMA_CH4_CLR_INTR =
|
|
|
|
IO_STATE(R_DMA_CH4_CLR_INTR, clr_eop,
|
|
|
|
do) |
|
|
|
|
IO_STATE(R_DMA_CH4_CLR_INTR, clr_descr,
|
|
|
|
do);
|
|
|
|
*R_DMA_CH5_CLR_INTR =
|
|
|
|
IO_STATE(R_DMA_CH5_CLR_INTR, clr_eop,
|
|
|
|
do) |
|
|
|
|
IO_STATE(R_DMA_CH5_CLR_INTR, clr_descr,
|
|
|
|
do);
|
|
|
|
*R_IRQ_MASK2_SET =
|
|
|
|
IO_STATE(R_IRQ_MASK2_SET, dma4_eop,
|
|
|
|
set) |
|
|
|
|
IO_STATE(R_IRQ_MASK2_SET, dma5_descr,
|
|
|
|
set);
|
|
|
|
}
|
|
|
|
start_dma_in(port);
|
|
|
|
port->init_irqs = 0;
|
|
|
|
} else { /* !port->use_dma */
|
|
|
|
#ifdef SYNC_SER_MANUAL
|
|
|
|
if (port == &ports[0]) {
|
|
|
|
if (request_irq(8,
|
|
|
|
manual_interrupt,
|
|
|
|
IRQF_SHARED | IRQF_DISABLED,
|
|
|
|
"synchronous serial manual irq",
|
|
|
|
&ports[0])) {
|
|
|
|
printk(KERN_CRIT "Can't alloc "
|
|
|
|
"sync serial manual irq");
|
2008-05-15 15:10:18 +00:00
|
|
|
goto out;
|
2008-01-21 10:44:11 +00:00
|
|
|
}
|
|
|
|
} else if (port == &ports[1]) {
|
|
|
|
if (request_irq(8,
|
|
|
|
manual_interrupt,
|
|
|
|
IRQF_SHARED | IRQF_DISABLED,
|
|
|
|
"synchronous serial manual irq",
|
|
|
|
&ports[1])) {
|
|
|
|
printk(KERN_CRIT "Can't alloc "
|
|
|
|
"sync serial manual irq");
|
2008-05-15 15:10:18 +00:00
|
|
|
goto out;
|
2008-01-21 10:44:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
port->init_irqs = 0;
|
|
|
|
#else
|
|
|
|
panic("sync_serial: Manual mode not supported.\n");
|
|
|
|
#endif /* SYNC_SER_MANUAL */
|
|
|
|
}
|
|
|
|
} /* port->init_irqs */
|
|
|
|
|
|
|
|
port->busy++;
|
|
|
|
/* Start port if we use it as input */
|
|
|
|
mode = IO_EXTRACT(R_SYNC_SERIAL1_CTRL, mode, port->ctrl_data_shadow);
|
|
|
|
if (mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, master_input) ||
|
|
|
|
mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, slave_input) ||
|
|
|
|
mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, master_bidir) ||
|
|
|
|
mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, slave_bidir)) {
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt,
|
|
|
|
running);
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable,
|
|
|
|
enable);
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable,
|
|
|
|
enable);
|
|
|
|
port->started = 1;
|
|
|
|
*port->ctrl_data = port->ctrl_data_shadow;
|
|
|
|
if (!port->use_dma)
|
|
|
|
*R_IRQ_MASK1_SET = 1 << port->data_avail_bit;
|
|
|
|
DEBUG(printk(KERN_DEBUG "sser%d rec started\n", dev));
|
|
|
|
}
|
2011-08-03 23:21:14 +00:00
|
|
|
err = 0;
|
2008-05-15 15:10:18 +00:00
|
|
|
|
|
|
|
out:
|
2010-09-11 16:18:22 +00:00
|
|
|
mutex_unlock(&sync_serial_mutex);
|
2011-08-03 23:21:14 +00:00
|
|
|
return err;
|
2008-01-21 10:44:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int sync_serial_release(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
int dev = MINOR(inode->i_rdev);
|
|
|
|
struct sync_port *port;
|
|
|
|
|
|
|
|
if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) {
|
|
|
|
DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev));
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
port = &ports[dev];
|
|
|
|
if (port->busy)
|
|
|
|
port->busy--;
|
|
|
|
if (!port->busy)
|
|
|
|
*R_IRQ_MASK1_CLR = ((1 << port->data_avail_bit) |
|
|
|
|
(1 << port->transmitter_ready_bit));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned int sync_serial_poll(struct file *file, poll_table *wait)
|
|
|
|
{
|
2013-01-23 22:07:38 +00:00
|
|
|
int dev = MINOR(file_inode(file)->i_rdev);
|
2008-01-21 10:44:11 +00:00
|
|
|
unsigned int mask = 0;
|
|
|
|
struct sync_port *port;
|
|
|
|
DEBUGPOLL(static unsigned int prev_mask = 0);
|
|
|
|
|
|
|
|
port = &ports[dev];
|
|
|
|
poll_wait(file, &port->out_wait_q, wait);
|
|
|
|
poll_wait(file, &port->in_wait_q, wait);
|
|
|
|
/* Some room to write */
|
|
|
|
if (port->out_count < OUT_BUFFER_SIZE)
|
|
|
|
mask |= POLLOUT | POLLWRNORM;
|
|
|
|
/* At least an inbufchunk of data */
|
|
|
|
if (sync_data_avail(port) >= port->inbufchunk)
|
|
|
|
mask |= POLLIN | POLLRDNORM;
|
|
|
|
|
|
|
|
DEBUGPOLL(if (mask != prev_mask)
|
|
|
|
printk(KERN_DEBUG "sync_serial_poll: mask 0x%08X %s %s\n",
|
|
|
|
mask,
|
|
|
|
mask & POLLOUT ? "POLLOUT" : "",
|
|
|
|
mask & POLLIN ? "POLLIN" : "");
|
|
|
|
prev_mask = mask;
|
|
|
|
);
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
2010-07-30 17:04:37 +00:00
|
|
|
static int sync_serial_ioctl_unlocked(struct file *file,
|
2008-01-21 10:44:11 +00:00
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
int return_val = 0;
|
|
|
|
unsigned long flags;
|
|
|
|
|
2013-01-23 22:07:38 +00:00
|
|
|
int dev = MINOR(file_inode(file)->i_rdev);
|
2008-01-21 10:44:11 +00:00
|
|
|
struct sync_port *port;
|
|
|
|
|
|
|
|
if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) {
|
|
|
|
DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
port = &ports[dev];
|
|
|
|
|
|
|
|
local_irq_save(flags);
|
|
|
|
/* Disable port while changing config */
|
|
|
|
if (dev) {
|
|
|
|
if (port->use_dma) {
|
|
|
|
RESET_DMA(4); WAIT_DMA(4);
|
|
|
|
port->tr_running = 0;
|
|
|
|
port->out_count = 0;
|
|
|
|
port->outp = port->out_buffer;
|
|
|
|
*R_DMA_CH4_CLR_INTR =
|
|
|
|
IO_STATE(R_DMA_CH4_CLR_INTR, clr_eop, do) |
|
|
|
|
IO_STATE(R_DMA_CH4_CLR_INTR, clr_descr, do);
|
|
|
|
}
|
|
|
|
SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, async);
|
|
|
|
} else {
|
|
|
|
if (port->use_dma) {
|
|
|
|
RESET_DMA(8); WAIT_DMA(8);
|
|
|
|
port->tr_running = 0;
|
|
|
|
port->out_count = 0;
|
|
|
|
port->outp = port->out_buffer;
|
|
|
|
*R_DMA_CH8_CLR_INTR =
|
|
|
|
IO_STATE(R_DMA_CH8_CLR_INTR, clr_eop, do) |
|
|
|
|
IO_STATE(R_DMA_CH8_CLR_INTR, clr_descr, do);
|
|
|
|
}
|
|
|
|
SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, async);
|
|
|
|
}
|
|
|
|
*R_GEN_CONFIG_II = gen_config_ii_shadow;
|
|
|
|
local_irq_restore(flags);
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case SSP_SPEED:
|
|
|
|
if (GET_SPEED(arg) == CODEC) {
|
|
|
|
if (dev)
|
|
|
|
SETS(sync_serial_prescale_shadow,
|
|
|
|
R_SYNC_SERIAL_PRESCALE, clk_sel_u3,
|
|
|
|
codec);
|
|
|
|
else
|
|
|
|
SETS(sync_serial_prescale_shadow,
|
|
|
|
R_SYNC_SERIAL_PRESCALE, clk_sel_u1,
|
|
|
|
codec);
|
|
|
|
|
|
|
|
SETF(sync_serial_prescale_shadow,
|
|
|
|
R_SYNC_SERIAL_PRESCALE, prescaler,
|
|
|
|
GET_FREQ(arg));
|
|
|
|
SETF(sync_serial_prescale_shadow,
|
|
|
|
R_SYNC_SERIAL_PRESCALE, frame_rate,
|
|
|
|
GET_FRAME_RATE(arg));
|
|
|
|
SETF(sync_serial_prescale_shadow,
|
|
|
|
R_SYNC_SERIAL_PRESCALE, word_rate,
|
|
|
|
GET_WORD_RATE(arg));
|
|
|
|
} else {
|
|
|
|
SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
tr_baud, GET_SPEED(arg));
|
|
|
|
if (dev)
|
|
|
|
SETS(sync_serial_prescale_shadow,
|
|
|
|
R_SYNC_SERIAL_PRESCALE, clk_sel_u3,
|
|
|
|
baudrate);
|
|
|
|
else
|
|
|
|
SETS(sync_serial_prescale_shadow,
|
|
|
|
R_SYNC_SERIAL_PRESCALE, clk_sel_u1,
|
|
|
|
baudrate);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SSP_MODE:
|
|
|
|
if (arg > 5)
|
|
|
|
return -EINVAL;
|
|
|
|
if (arg == MASTER_OUTPUT || arg == SLAVE_OUTPUT)
|
|
|
|
*R_IRQ_MASK1_CLR = 1 << port->data_avail_bit;
|
|
|
|
else if (!port->use_dma)
|
|
|
|
*R_IRQ_MASK1_SET = 1 << port->data_avail_bit;
|
|
|
|
SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, mode, arg);
|
|
|
|
break;
|
|
|
|
case SSP_FRAME_SYNC:
|
|
|
|
if (arg & NORMAL_SYNC)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
f_synctype, normal);
|
|
|
|
else if (arg & EARLY_SYNC)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
f_synctype, early);
|
|
|
|
|
|
|
|
if (arg & BIT_SYNC)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
f_syncsize, bit);
|
|
|
|
else if (arg & WORD_SYNC)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
f_syncsize, word);
|
|
|
|
else if (arg & EXTENDED_SYNC)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
f_syncsize, extended);
|
|
|
|
|
|
|
|
if (arg & SYNC_ON)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
f_sync, on);
|
|
|
|
else if (arg & SYNC_OFF)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
f_sync, off);
|
|
|
|
|
|
|
|
if (arg & WORD_SIZE_8)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
wordsize, size8bit);
|
|
|
|
else if (arg & WORD_SIZE_12)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
wordsize, size12bit);
|
|
|
|
else if (arg & WORD_SIZE_16)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
wordsize, size16bit);
|
|
|
|
else if (arg & WORD_SIZE_24)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
wordsize, size24bit);
|
|
|
|
else if (arg & WORD_SIZE_32)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
wordsize, size32bit);
|
|
|
|
|
|
|
|
if (arg & BIT_ORDER_MSB)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
bitorder, msb);
|
|
|
|
else if (arg & BIT_ORDER_LSB)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
bitorder, lsb);
|
|
|
|
|
|
|
|
if (arg & FLOW_CONTROL_ENABLE)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
flow_ctrl, enabled);
|
|
|
|
else if (arg & FLOW_CONTROL_DISABLE)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
flow_ctrl, disabled);
|
|
|
|
|
|
|
|
if (arg & CLOCK_NOT_GATED)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
clk_mode, normal);
|
|
|
|
else if (arg & CLOCK_GATED)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
clk_mode, gated);
|
|
|
|
|
|
|
|
break;
|
|
|
|
case SSP_IPOLARITY:
|
|
|
|
/* NOTE!! negedge is considered NORMAL */
|
|
|
|
if (arg & CLOCK_NORMAL)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
clk_polarity, neg);
|
|
|
|
else if (arg & CLOCK_INVERT)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
clk_polarity, pos);
|
|
|
|
|
|
|
|
if (arg & FRAME_NORMAL)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
frame_polarity, normal);
|
|
|
|
else if (arg & FRAME_INVERT)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
frame_polarity, inverted);
|
|
|
|
|
|
|
|
if (arg & STATUS_NORMAL)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
status_polarity, normal);
|
|
|
|
else if (arg & STATUS_INVERT)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
status_polarity, inverted);
|
|
|
|
break;
|
|
|
|
case SSP_OPOLARITY:
|
|
|
|
if (arg & CLOCK_NORMAL)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
clk_driver, normal);
|
|
|
|
else if (arg & CLOCK_INVERT)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
clk_driver, inverted);
|
|
|
|
|
|
|
|
if (arg & FRAME_NORMAL)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
frame_driver, normal);
|
|
|
|
else if (arg & FRAME_INVERT)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
frame_driver, inverted);
|
|
|
|
|
|
|
|
if (arg & STATUS_NORMAL)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
status_driver, normal);
|
|
|
|
else if (arg & STATUS_INVERT)
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
status_driver, inverted);
|
|
|
|
break;
|
|
|
|
case SSP_SPI:
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, flow_ctrl,
|
|
|
|
disabled);
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, bitorder,
|
|
|
|
msb);
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize,
|
|
|
|
size8bit);
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_sync, on);
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_syncsize,
|
|
|
|
word);
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_synctype,
|
|
|
|
normal);
|
|
|
|
if (arg & SPI_SLAVE) {
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
frame_polarity, inverted);
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
clk_polarity, neg);
|
|
|
|
SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
mode, SLAVE_INPUT);
|
|
|
|
} else {
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
frame_driver, inverted);
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
clk_driver, inverted);
|
|
|
|
SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL,
|
|
|
|
mode, MASTER_OUTPUT);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SSP_INBUFCHUNK:
|
|
|
|
#if 0
|
|
|
|
if (arg > port->in_buffer_size/NUM_IN_DESCR)
|
|
|
|
return -EINVAL;
|
|
|
|
port->inbufchunk = arg;
|
|
|
|
/* Make sure in_buffer_size is a multiple of inbufchunk */
|
|
|
|
port->in_buffer_size =
|
|
|
|
(port->in_buffer_size/port->inbufchunk) *
|
|
|
|
port->inbufchunk;
|
|
|
|
DEBUG(printk(KERN_DEBUG "inbufchunk %i in_buffer_size: %i\n",
|
|
|
|
port->inbufchunk, port->in_buffer_size));
|
|
|
|
if (port->use_dma) {
|
|
|
|
if (port->port_nbr == 0) {
|
|
|
|
RESET_DMA(9);
|
|
|
|
WAIT_DMA(9);
|
|
|
|
} else {
|
|
|
|
RESET_DMA(5);
|
|
|
|
WAIT_DMA(5);
|
|
|
|
}
|
|
|
|
start_dma_in(port);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return_val = -1;
|
|
|
|
}
|
|
|
|
/* Make sure we write the config without interruption */
|
|
|
|
local_irq_save(flags);
|
|
|
|
/* Set config and enable port */
|
|
|
|
*port->ctrl_data = port->ctrl_data_shadow;
|
|
|
|
nop(); nop(); nop(); nop();
|
|
|
|
*R_SYNC_SERIAL_PRESCALE = sync_serial_prescale_shadow;
|
|
|
|
nop(); nop(); nop(); nop();
|
|
|
|
if (dev)
|
|
|
|
SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, sync);
|
|
|
|
else
|
|
|
|
SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, sync);
|
|
|
|
|
|
|
|
*R_GEN_CONFIG_II = gen_config_ii_shadow;
|
|
|
|
/* Reset DMA. At readout from serial port the data could be shifted
|
|
|
|
* one byte if not resetting DMA.
|
|
|
|
*/
|
|
|
|
if (port->use_dma) {
|
|
|
|
if (port->port_nbr == 0) {
|
|
|
|
RESET_DMA(9);
|
|
|
|
WAIT_DMA(9);
|
|
|
|
} else {
|
|
|
|
RESET_DMA(5);
|
|
|
|
WAIT_DMA(5);
|
|
|
|
}
|
|
|
|
start_dma_in(port);
|
|
|
|
}
|
|
|
|
local_irq_restore(flags);
|
|
|
|
return return_val;
|
|
|
|
}
|
|
|
|
|
2010-07-30 17:04:37 +00:00
|
|
|
static long sync_serial_ioctl(struct file *file,
|
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
long ret;
|
|
|
|
|
2010-09-11 16:18:22 +00:00
|
|
|
mutex_lock(&sync_serial_mutex);
|
2010-07-30 17:04:37 +00:00
|
|
|
ret = sync_serial_ioctl_unlocked(file, cmd, arg);
|
2010-09-11 16:18:22 +00:00
|
|
|
mutex_unlock(&sync_serial_mutex);
|
2010-07-30 17:04:37 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-01-21 10:44:11 +00:00
|
|
|
|
|
|
|
static ssize_t sync_serial_write(struct file *file, const char *buf,
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
{
|
2013-01-23 22:07:38 +00:00
|
|
|
int dev = MINOR(file_inode(file)->i_rdev);
|
2008-01-21 10:44:11 +00:00
|
|
|
DECLARE_WAITQUEUE(wait, current);
|
|
|
|
struct sync_port *port;
|
|
|
|
unsigned long flags;
|
|
|
|
unsigned long c, c1;
|
|
|
|
unsigned long free_outp;
|
|
|
|
unsigned long outp;
|
|
|
|
unsigned long out_buffer;
|
|
|
|
|
|
|
|
if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) {
|
|
|
|
DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev));
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
port = &ports[dev];
|
|
|
|
|
|
|
|
DEBUGWRITE(printk(KERN_DEBUG "W d%d c %lu (%d/%d)\n",
|
|
|
|
port->port_nbr, count, port->out_count, OUT_BUFFER_SIZE));
|
|
|
|
/* Space to end of buffer */
|
|
|
|
/*
|
|
|
|
* out_buffer <c1>012345<- c ->OUT_BUFFER_SIZE
|
|
|
|
* outp^ +out_count
|
|
|
|
* ^free_outp
|
|
|
|
* out_buffer 45<- c ->0123OUT_BUFFER_SIZE
|
|
|
|
* +out_count outp^
|
|
|
|
* free_outp
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Read variables that may be updated by interrupts */
|
|
|
|
local_irq_save(flags);
|
|
|
|
if (count > OUT_BUFFER_SIZE - port->out_count)
|
|
|
|
count = OUT_BUFFER_SIZE - port->out_count;
|
|
|
|
|
|
|
|
outp = (unsigned long)port->outp;
|
|
|
|
free_outp = outp + port->out_count;
|
|
|
|
local_irq_restore(flags);
|
|
|
|
out_buffer = (unsigned long)port->out_buffer;
|
|
|
|
|
|
|
|
/* Find out where and how much to write */
|
|
|
|
if (free_outp >= out_buffer + OUT_BUFFER_SIZE)
|
|
|
|
free_outp -= OUT_BUFFER_SIZE;
|
|
|
|
if (free_outp >= outp)
|
|
|
|
c = out_buffer + OUT_BUFFER_SIZE - free_outp;
|
|
|
|
else
|
|
|
|
c = outp - free_outp;
|
|
|
|
if (c > count)
|
|
|
|
c = count;
|
|
|
|
|
|
|
|
DEBUGWRITE(printk(KERN_DEBUG "w op %08lX fop %08lX c %lu\n",
|
|
|
|
outp, free_outp, c));
|
|
|
|
if (copy_from_user((void *)free_outp, buf, c))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (c != count) {
|
|
|
|
buf += c;
|
|
|
|
c1 = count - c;
|
|
|
|
DEBUGWRITE(printk(KERN_DEBUG "w2 fi %lu c %lu c1 %lu\n",
|
|
|
|
free_outp-out_buffer, c, c1));
|
|
|
|
if (copy_from_user((void *)out_buffer, buf, c1))
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
local_irq_save(flags);
|
|
|
|
port->out_count += count;
|
|
|
|
local_irq_restore(flags);
|
|
|
|
|
|
|
|
/* Make sure transmitter/receiver is running */
|
|
|
|
if (!port->started) {
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt,
|
|
|
|
running);
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable,
|
|
|
|
enable);
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable,
|
|
|
|
enable);
|
|
|
|
port->started = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*port->ctrl_data = port->ctrl_data_shadow;
|
|
|
|
|
|
|
|
if (file->f_flags & O_NONBLOCK) {
|
|
|
|
local_irq_save(flags);
|
|
|
|
if (!port->tr_running) {
|
|
|
|
if (!port->use_dma) {
|
|
|
|
/* Start sender by writing data */
|
|
|
|
send_word(port);
|
|
|
|
/* and enable transmitter ready IRQ */
|
|
|
|
*R_IRQ_MASK1_SET = 1 <<
|
|
|
|
port->transmitter_ready_bit;
|
|
|
|
} else
|
|
|
|
start_dma(port,
|
|
|
|
(unsigned char *volatile)port->outp, c);
|
|
|
|
}
|
|
|
|
local_irq_restore(flags);
|
|
|
|
DEBUGWRITE(printk(KERN_DEBUG "w d%d c %lu NB\n",
|
|
|
|
port->port_nbr, count));
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sleep until all sent */
|
|
|
|
add_wait_queue(&port->out_wait_q, &wait);
|
|
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
|
|
|
local_irq_save(flags);
|
|
|
|
if (!port->tr_running) {
|
|
|
|
if (!port->use_dma) {
|
|
|
|
/* Start sender by writing data */
|
|
|
|
send_word(port);
|
|
|
|
/* and enable transmitter ready IRQ */
|
|
|
|
*R_IRQ_MASK1_SET = 1 << port->transmitter_ready_bit;
|
|
|
|
} else
|
|
|
|
start_dma(port, port->outp, c);
|
|
|
|
}
|
|
|
|
local_irq_restore(flags);
|
|
|
|
schedule();
|
|
|
|
set_current_state(TASK_RUNNING);
|
|
|
|
remove_wait_queue(&port->out_wait_q, &wait);
|
|
|
|
if (signal_pending(current))
|
|
|
|
return -EINTR;
|
|
|
|
|
|
|
|
DEBUGWRITE(printk(KERN_DEBUG "w d%d c %lu\n", port->port_nbr, count));
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t sync_serial_read(struct file *file, char *buf,
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
{
|
2013-01-23 22:07:38 +00:00
|
|
|
int dev = MINOR(file_inode(file)->i_rdev);
|
2008-01-21 10:44:11 +00:00
|
|
|
int avail;
|
|
|
|
struct sync_port *port;
|
|
|
|
unsigned char *start;
|
|
|
|
unsigned char *end;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) {
|
|
|
|
DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev));
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
port = &ports[dev];
|
|
|
|
|
|
|
|
DEBUGREAD(printk(KERN_DEBUG "R%d c %d ri %lu wi %lu /%lu\n",
|
|
|
|
dev, count, port->readp - port->flip,
|
|
|
|
port->writep - port->flip, port->in_buffer_size));
|
|
|
|
|
|
|
|
if (!port->started) {
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt,
|
|
|
|
running);
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable,
|
|
|
|
enable);
|
|
|
|
SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable,
|
|
|
|
enable);
|
|
|
|
port->started = 1;
|
|
|
|
}
|
|
|
|
*port->ctrl_data = port->ctrl_data_shadow;
|
|
|
|
|
|
|
|
/* Calculate number of available bytes */
|
|
|
|
/* Save pointers to avoid that they are modified by interrupt */
|
|
|
|
local_irq_save(flags);
|
|
|
|
start = (unsigned char *)port->readp; /* cast away volatile */
|
|
|
|
end = (unsigned char *)port->writep; /* cast away volatile */
|
|
|
|
local_irq_restore(flags);
|
|
|
|
while (start == end && !port->full) {
|
|
|
|
/* No data */
|
|
|
|
if (file->f_flags & O_NONBLOCK)
|
|
|
|
return -EAGAIN;
|
|
|
|
|
|
|
|
interruptible_sleep_on(&port->in_wait_q);
|
|
|
|
if (signal_pending(current))
|
|
|
|
return -EINTR;
|
|
|
|
|
|
|
|
local_irq_save(flags);
|
|
|
|
start = (unsigned char *)port->readp; /* cast away volatile */
|
|
|
|
end = (unsigned char *)port->writep; /* cast away volatile */
|
|
|
|
local_irq_restore(flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Lazy read, never return wrapped data. */
|
|
|
|
if (port->full)
|
|
|
|
avail = port->in_buffer_size;
|
|
|
|
else if (end > start)
|
|
|
|
avail = end - start;
|
|
|
|
else
|
|
|
|
avail = port->flip + port->in_buffer_size - start;
|
|
|
|
|
|
|
|
count = count > avail ? avail : count;
|
|
|
|
if (copy_to_user(buf, start, count))
|
|
|
|
return -EFAULT;
|
|
|
|
/* Disable interrupts while updating readp */
|
|
|
|
local_irq_save(flags);
|
|
|
|
port->readp += count;
|
|
|
|
if (port->readp >= port->flip + port->in_buffer_size) /* Wrap? */
|
|
|
|
port->readp = port->flip;
|
|
|
|
port->full = 0;
|
|
|
|
local_irq_restore(flags);
|
|
|
|
DEBUGREAD(printk(KERN_DEBUG "r %d\n", count));
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void send_word(struct sync_port *port)
|
|
|
|
{
|
|
|
|
switch (IO_EXTRACT(R_SYNC_SERIAL1_CTRL, wordsize,
|
|
|
|
port->ctrl_data_shadow)) {
|
|
|
|
case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit):
|
|
|
|
port->out_count--;
|
|
|
|
*port->data_out = *port->outp++;
|
|
|
|
if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
|
|
|
|
port->outp = port->out_buffer;
|
|
|
|
break;
|
|
|
|
case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size12bit):
|
|
|
|
{
|
|
|
|
int data = (*port->outp++) << 8;
|
|
|
|
data |= *port->outp++;
|
|
|
|
port->out_count -= 2;
|
|
|
|
*port->data_out = data;
|
|
|
|
if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
|
|
|
|
port->outp = port->out_buffer;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size16bit):
|
|
|
|
port->out_count -= 2;
|
|
|
|
*port->data_out = *(unsigned short *)port->outp;
|
|
|
|
port->outp += 2;
|
|
|
|
if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
|
|
|
|
port->outp = port->out_buffer;
|
|
|
|
break;
|
|
|
|
case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size24bit):
|
|
|
|
port->out_count -= 3;
|
|
|
|
*port->data_out = *(unsigned int *)port->outp;
|
|
|
|
port->outp += 3;
|
|
|
|
if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
|
|
|
|
port->outp = port->out_buffer;
|
|
|
|
break;
|
|
|
|
case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size32bit):
|
|
|
|
port->out_count -= 4;
|
|
|
|
*port->data_out = *(unsigned int *)port->outp;
|
|
|
|
port->outp += 4;
|
|
|
|
if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
|
|
|
|
port->outp = port->out_buffer;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void start_dma(struct sync_port *port, const char *data, int count)
|
|
|
|
{
|
|
|
|
port->tr_running = 1;
|
|
|
|
port->out_descr.hw_len = 0;
|
|
|
|
port->out_descr.next = 0;
|
|
|
|
port->out_descr.ctrl = d_eol | d_eop; /* No d_wait to avoid glitches */
|
|
|
|
port->out_descr.sw_len = count;
|
|
|
|
port->out_descr.buf = virt_to_phys(data);
|
|
|
|
port->out_descr.status = 0;
|
|
|
|
|
|
|
|
*port->output_dma_first = virt_to_phys(&port->out_descr);
|
|
|
|
*port->output_dma_cmd = IO_STATE(R_DMA_CH0_CMD, cmd, start);
|
|
|
|
DEBUGTXINT(printk(KERN_DEBUG "dma %08lX c %d\n",
|
|
|
|
(unsigned long)data, count));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void start_dma_in(struct sync_port *port)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
unsigned long buf;
|
|
|
|
port->writep = port->flip;
|
|
|
|
|
|
|
|
if (port->writep > port->flip + port->in_buffer_size) {
|
|
|
|
panic("Offset too large in sync serial driver\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
buf = virt_to_phys(port->in_buffer);
|
|
|
|
for (i = 0; i < NUM_IN_DESCR; i++) {
|
|
|
|
port->in_descr[i].sw_len = port->inbufchunk;
|
|
|
|
port->in_descr[i].ctrl = d_int;
|
|
|
|
port->in_descr[i].next = virt_to_phys(&port->in_descr[i+1]);
|
|
|
|
port->in_descr[i].buf = buf;
|
|
|
|
port->in_descr[i].hw_len = 0;
|
|
|
|
port->in_descr[i].status = 0;
|
|
|
|
port->in_descr[i].fifo_len = 0;
|
|
|
|
buf += port->inbufchunk;
|
|
|
|
prepare_rx_descriptor(&port->in_descr[i]);
|
|
|
|
}
|
|
|
|
/* Link the last descriptor to the first */
|
|
|
|
port->in_descr[i-1].next = virt_to_phys(&port->in_descr[0]);
|
|
|
|
port->in_descr[i-1].ctrl |= d_eol;
|
|
|
|
port->next_rx_desc = &port->in_descr[0];
|
|
|
|
port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR - 1];
|
|
|
|
*port->input_dma_first = virt_to_phys(port->next_rx_desc);
|
|
|
|
*port->input_dma_cmd = IO_STATE(R_DMA_CH0_CMD, cmd, start);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef SYNC_SER_DMA
|
|
|
|
static irqreturn_t tr_interrupt(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
unsigned long ireg = *R_IRQ_MASK2_RD;
|
|
|
|
struct etrax_dma_descr *descr;
|
|
|
|
unsigned int sentl;
|
|
|
|
int handled = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < NUMBER_OF_PORTS; i++) {
|
|
|
|
struct sync_port *port = &ports[i];
|
|
|
|
if (!port->enabled || !port->use_dma)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* IRQ active for the port? */
|
|
|
|
if (!(ireg & (1 << port->output_dma_bit)))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
handled = 1;
|
|
|
|
|
|
|
|
/* Clear IRQ */
|
|
|
|
*port->output_dma_clr_irq =
|
|
|
|
IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do) |
|
|
|
|
IO_STATE(R_DMA_CH0_CLR_INTR, clr_descr, do);
|
|
|
|
|
|
|
|
descr = &port->out_descr;
|
|
|
|
if (!(descr->status & d_stop))
|
|
|
|
sentl = descr->sw_len;
|
|
|
|
else
|
|
|
|
/* Otherwise find amount of data sent here */
|
|
|
|
sentl = descr->hw_len;
|
|
|
|
|
|
|
|
port->out_count -= sentl;
|
|
|
|
port->outp += sentl;
|
|
|
|
if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
|
|
|
|
port->outp = port->out_buffer;
|
|
|
|
if (port->out_count) {
|
|
|
|
int c = port->out_buffer + OUT_BUFFER_SIZE - port->outp;
|
|
|
|
if (c > port->out_count)
|
|
|
|
c = port->out_count;
|
|
|
|
DEBUGTXINT(printk(KERN_DEBUG
|
|
|
|
"tx_int DMAWRITE %i %i\n", sentl, c));
|
|
|
|
start_dma(port, port->outp, c);
|
|
|
|
} else {
|
|
|
|
DEBUGTXINT(printk(KERN_DEBUG
|
|
|
|
"tx_int DMA stop %i\n", sentl));
|
|
|
|
port->tr_running = 0;
|
|
|
|
}
|
|
|
|
/* wake up the waiting process */
|
|
|
|
wake_up_interruptible(&port->out_wait_q);
|
|
|
|
}
|
|
|
|
return IRQ_RETVAL(handled);
|
|
|
|
} /* tr_interrupt */
|
|
|
|
|
|
|
|
static irqreturn_t rx_interrupt(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
unsigned long ireg = *R_IRQ_MASK2_RD;
|
|
|
|
int i;
|
|
|
|
int handled = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < NUMBER_OF_PORTS; i++) {
|
|
|
|
struct sync_port *port = &ports[i];
|
|
|
|
|
|
|
|
if (!port->enabled || !port->use_dma)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!(ireg & (1 << port->input_dma_descr_bit)))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Descriptor interrupt */
|
|
|
|
handled = 1;
|
|
|
|
while (*port->input_dma_descr !=
|
|
|
|
virt_to_phys(port->next_rx_desc)) {
|
|
|
|
if (port->writep + port->inbufchunk > port->flip +
|
|
|
|
port->in_buffer_size) {
|
|
|
|
int first_size = port->flip +
|
|
|
|
port->in_buffer_size - port->writep;
|
|
|
|
memcpy(port->writep,
|
|
|
|
phys_to_virt(port->next_rx_desc->buf),
|
|
|
|
first_size);
|
|
|
|
memcpy(port->flip,
|
|
|
|
phys_to_virt(port->next_rx_desc->buf +
|
|
|
|
first_size),
|
|
|
|
port->inbufchunk - first_size);
|
|
|
|
port->writep = port->flip +
|
|
|
|
port->inbufchunk - first_size;
|
|
|
|
} else {
|
|
|
|
memcpy(port->writep,
|
|
|
|
phys_to_virt(port->next_rx_desc->buf),
|
|
|
|
port->inbufchunk);
|
|
|
|
port->writep += port->inbufchunk;
|
|
|
|
if (port->writep >= port->flip
|
|
|
|
+ port->in_buffer_size)
|
|
|
|
port->writep = port->flip;
|
|
|
|
}
|
|
|
|
if (port->writep == port->readp)
|
|
|
|
port->full = 1;
|
|
|
|
prepare_rx_descriptor(port->next_rx_desc);
|
|
|
|
port->next_rx_desc->ctrl |= d_eol;
|
|
|
|
port->prev_rx_desc->ctrl &= ~d_eol;
|
|
|
|
port->prev_rx_desc = phys_to_virt((unsigned)
|
|
|
|
port->next_rx_desc);
|
|
|
|
port->next_rx_desc = phys_to_virt((unsigned)
|
|
|
|
port->next_rx_desc->next);
|
|
|
|
/* Wake up the waiting process */
|
|
|
|
wake_up_interruptible(&port->in_wait_q);
|
|
|
|
*port->input_dma_cmd = IO_STATE(R_DMA_CH1_CMD,
|
|
|
|
cmd, restart);
|
|
|
|
/* DMA has reached end of descriptor */
|
|
|
|
*port->input_dma_clr_irq = IO_STATE(R_DMA_CH0_CLR_INTR,
|
|
|
|
clr_descr, do);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return IRQ_RETVAL(handled);
|
|
|
|
} /* rx_interrupt */
|
|
|
|
#endif /* SYNC_SER_DMA */
|
|
|
|
|
|
|
|
#ifdef SYNC_SER_MANUAL
|
|
|
|
static irqreturn_t manual_interrupt(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int handled = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < NUMBER_OF_PORTS; i++) {
|
|
|
|
struct sync_port *port = &ports[i];
|
|
|
|
|
|
|
|
if (!port->enabled || port->use_dma)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Data received? */
|
|
|
|
if (*R_IRQ_MASK1_RD & (1 << port->data_avail_bit)) {
|
|
|
|
handled = 1;
|
|
|
|
/* Read data */
|
|
|
|
switch (port->ctrl_data_shadow &
|
|
|
|
IO_MASK(R_SYNC_SERIAL1_CTRL, wordsize)) {
|
|
|
|
case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit):
|
|
|
|
*port->writep++ =
|
|
|
|
*(volatile char *)port->data_in;
|
|
|
|
break;
|
|
|
|
case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size12bit):
|
|
|
|
{
|
|
|
|
int data = *(unsigned short *)port->data_in;
|
|
|
|
*port->writep = (data & 0x0ff0) >> 4;
|
|
|
|
*(port->writep + 1) = data & 0x0f;
|
|
|
|
port->writep += 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size16bit):
|
|
|
|
*(unsigned short *)port->writep =
|
|
|
|
*(volatile unsigned short *)port->data_in;
|
|
|
|
port->writep += 2;
|
|
|
|
break;
|
|
|
|
case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size24bit):
|
|
|
|
*(unsigned int *)port->writep = *port->data_in;
|
|
|
|
port->writep += 3;
|
|
|
|
break;
|
|
|
|
case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size32bit):
|
|
|
|
*(unsigned int *)port->writep = *port->data_in;
|
|
|
|
port->writep += 4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Wrap? */
|
|
|
|
if (port->writep >= port->flip + port->in_buffer_size)
|
|
|
|
port->writep = port->flip;
|
|
|
|
if (port->writep == port->readp) {
|
|
|
|
/* Receive buffer overrun, discard oldest */
|
|
|
|
port->readp++;
|
|
|
|
/* Wrap? */
|
|
|
|
if (port->readp >= port->flip +
|
|
|
|
port->in_buffer_size)
|
|
|
|
port->readp = port->flip;
|
|
|
|
}
|
|
|
|
if (sync_data_avail(port) >= port->inbufchunk) {
|
|
|
|
/* Wake up application */
|
|
|
|
wake_up_interruptible(&port->in_wait_q);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Transmitter ready? */
|
|
|
|
if (*R_IRQ_MASK1_RD & (1 << port->transmitter_ready_bit)) {
|
|
|
|
if (port->out_count > 0) {
|
|
|
|
/* More data to send */
|
|
|
|
send_word(port);
|
|
|
|
} else {
|
|
|
|
/* Transmission finished */
|
|
|
|
/* Turn off IRQ */
|
|
|
|
*R_IRQ_MASK1_CLR = 1 <<
|
|
|
|
port->transmitter_ready_bit;
|
|
|
|
/* Wake up application */
|
|
|
|
wake_up_interruptible(&port->out_wait_q);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return IRQ_RETVAL(handled);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
module_init(etrax_sync_serial_init);
|