patch-2.4.5 linux/arch/cris/drivers/sync_serial.c
Next file: linux/arch/cris/drivers/usb-host.c
Previous file: linux/arch/cris/drivers/serial.h
Back to the patch index
Back to the overall index
- Lines: 498
- Date:
Tue May 1 16:04:56 2001
- Orig file:
v2.4.4/linux/arch/cris/drivers/sync_serial.c
- Orig date:
Fri Apr 6 10:42:55 2001
diff -u --recursive --new-file v2.4.4/linux/arch/cris/drivers/sync_serial.c linux/arch/cris/drivers/sync_serial.c
@@ -2,8 +2,9 @@
* Simple synchronous serial port driver for ETRAX 100LX.
*
* Synchronous serial ports are used for continous streamed data like audio.
- * The deault 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
+ * 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 Axis Communications AB
*
@@ -29,37 +30,43 @@
#include <asm/sync_serial.h>
/* The receiver is a bit tricky beacuse of the continous stream of data. */
-/* */
-/* Two DMA descriptors are linked together. Each DMA descriptor is */
-/* responsible for one half of a common buffer. */
-/* */
-/* ------------------------------ */
-/* | ---------- ---------- | */
-/* --> | Descr1 |-->| Descr2 |--- */
-/* ---------- ---------- */
-/* | | */
-/* v v */
-/* ----------------------------- */
-/* | BUFFER | */
-/* ----------------------------- */
-/* | | */
-/* readp writep */
-/* */
+/* */
+/* Two DMA descriptors are linked together. Each DMA descriptor is */
+/* responsible for one half of a common buffer. */
+/* */
+/* ------------------------------ */
+/* | ---------- ---------- | */
+/* --> | Descr1 |-->| Descr2 |--- */
+/* ---------- ---------- */
+/* | | */
+/* v v */
+/* ----------------------------- */
+/* | BUFFER | */
+/* ----------------------------- */
+/* | | */
+/* 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. */
+/* 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 */
/* Descr1 when the DMA has filled in Descr2. Otherwise we will discard */
/* the rest of the data pointed out by Descr1 and set readp to the start */
-/* of Descr2 */
+/* of Descr2 */
#define SYNC_SERIAL_MAJOR 125
-#define IN_BUFFER_SIZE 8192
+/* 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 OUT_BUFFER_SIZE 4096
+#define DEFAULT_FRAME_RATE 0
+#define DEFAULT_WORD_RATE 7
+
#define DEBUG(x)
-/* Define some macros to access Etrax 100 registers */
+/* Define some macros to access ETRAX 100 registers */
#define SETF(var, reg, field, val) var = (var & ~IO_MASK(##reg##, field)) | \
IO_FIELD(##reg##, field, val)
#define SETS(var, reg, field, val) var = (var & ~IO_MASK(##reg##, field)) | \
@@ -81,10 +88,8 @@
char data_avail_bit; /* In R_IRQ_MASK1_RD */
char transmitter_ready_bit; /* In R_IRQ_MASK1_RD */
char ready_irq_bit; /* In R_IRQ_MASK1_SET and R_IRQ_MASK1_CLR */
- char input_dma_eop_bit; /* In R_IRQ_MASK2_RD */
char input_dma_descr_bit; /* In R_IRQ_MASK2_RD */
char output_dma_bit; /* In R_IRQ_MASK2_RD */
- char eop_bit; /* In R_SET_EOP */
int enabled; /* 1 if port is enabled */
int use_dma; /* 1 if port uses dma */
@@ -125,7 +130,6 @@
static void tr_interrupt(int irq, void *dev_id, struct pt_regs * regs);
static void rx_interrupt(int irq, void *dev_id, struct pt_regs * regs);
static void manual_interrupt(int irq, void *dev_id, struct pt_regs * regs);
-static void flush_handler(void);
/* The ports */
static struct sync_port ports[]=
@@ -133,40 +137,36 @@
{
R_SYNC_SERIAL1_STATUS, /* status */
R_SYNC_SERIAL1_CTRL, /* ctrl_data */
- R_DMA_CH8_FIRST, /* output_dma_first */
- R_DMA_CH8_CMD, /* output_dma_cmd */
- R_DMA_CH8_CLR_INTR, /* output_dma_clr_irq */
- R_DMA_CH9_FIRST, /* input_dma_first */
- R_DMA_CH9_CMD, /* input_dma_cmd */
- R_DMA_CH9_CLR_INTR, /* input_dma_clr_irq */
+ R_DMA_CH8_FIRST, /* output_dma_first */
+ R_DMA_CH8_CMD, /* output_dma_cmd */
+ R_DMA_CH8_CLR_INTR, /* output_dma_clr_irq */
+ R_DMA_CH9_FIRST, /* input_dma_first */
+ R_DMA_CH9_CMD, /* input_dma_cmd */
+ R_DMA_CH9_CLR_INTR, /* input_dma_clr_irq */
R_SYNC_SERIAL1_TR_DATA, /* data_out */
R_SYNC_SERIAL1_REC_DATA,/* data in */
IO_BITNR(R_IRQ_MASK1_RD, ser1_data), /* data_avail_bit */
IO_BITNR(R_IRQ_MASK1_RD, ser1_ready), /* transmitter_ready_bit */
IO_BITNR(R_IRQ_MASK1_SET, ser1_ready), /* ready_irq_bit */
- IO_BITNR(R_IRQ_MASK2_RD, dma9_eop), /* input_dma_eop_bit */
IO_BITNR(R_IRQ_MASK2_RD, dma9_descr), /* input_dma_descr_bit */
- IO_BITNR(R_IRQ_MASK2_RD, dma8_eop), /* output_dma_bit */
- IO_BITNR(R_SET_EOP, ch9_eop) /* eop_bit */
+ IO_BITNR(R_IRQ_MASK2_RD, dma8_eop), /* output_dma_bit */
},
{
R_SYNC_SERIAL3_STATUS, /* status */
R_SYNC_SERIAL3_CTRL, /* ctrl_data */
- R_DMA_CH4_FIRST, /* output_dma_first */
- R_DMA_CH4_CMD, /* output_dma_cmd */
- R_DMA_CH4_CLR_INTR, /* output_dma_clr_irq */
- R_DMA_CH5_FIRST, /* input_dma_first */
- R_DMA_CH5_CMD, /* input_dma_cmd */
- R_DMA_CH5_CLR_INTR, /* input_dma_clr_irq */
+ R_DMA_CH4_FIRST, /* output_dma_first */
+ R_DMA_CH4_CMD, /* output_dma_cmd */
+ R_DMA_CH4_CLR_INTR, /* output_dma_clr_irq */
+ R_DMA_CH5_FIRST, /* input_dma_first */
+ R_DMA_CH5_CMD, /* input_dma_cmd */
+ R_DMA_CH5_CLR_INTR, /* input_dma_clr_irq */
R_SYNC_SERIAL3_TR_DATA, /* data_out */
R_SYNC_SERIAL3_REC_DATA,/* data in */
IO_BITNR(R_IRQ_MASK1_RD, ser3_data), /* data_avail_bit */
IO_BITNR(R_IRQ_MASK1_RD, ser3_ready), /* transmitter_ready_bit */
IO_BITNR(R_IRQ_MASK1_SET, ser3_ready), /* ready_irq_bit */
- IO_BITNR(R_IRQ_MASK2_RD, dma5_eop), /* input_dma_eop_bit */
- IO_BITNR(R_IRQ_MASK2_RD, dma5_descr), /* input_dma_eop_bit */
- IO_BITNR(R_IRQ_MASK2_RD, dma4_eop), /* output_dma_bit */
- IO_BITNR(R_SET_EOP, ch5_eop) /* eop_bit */
+ IO_BITNR(R_IRQ_MASK2_RD, dma5_descr), /* input_dma_descr_bit */
+ IO_BITNR(R_IRQ_MASK2_RD, dma4_eop), /* output_dma_bit */
}
};
@@ -174,9 +174,6 @@
static unsigned sync_serial_prescale_shadow = 0;
static unsigned gen_config_ii_shadow = 0;
-/* Timer used to flush data from the DMA */
-static struct timer_list flush_timer;
-
#define NUMBER_OF_PORTS (sizeof(ports)/sizeof(sync_port))
static struct file_operations sync_serial_fops = {
@@ -213,24 +210,25 @@
#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)
ports[0].use_dma = 1;
initialize_port(0);
- if(request_irq(24, tr_interrupt, 0, "synchronous serial 1 dma tr", NULL))
+ if(request_irq(24, tr_interrupt, 0, "synchronous serial 1 dma tr", &ports[0]))
panic("Can't allocate sync serial port 1 IRQ");
- if(request_irq(25, rx_interrupt, 0, "synchronous serial 1 dma rx", NULL))
+ if(request_irq(25, rx_interrupt, 0, "synchronous serial 1 dma rx", &ports[0]))
panic("Can't allocate sync serial port 1 IRQ");
RESET_DMA(8); WAIT_DMA(8);
RESET_DMA(9); WAIT_DMA(9);
- *R_DMA_CH8_CLR_INTR = 3; /* Clear IRQ */
- *R_DMA_CH9_CLR_INTR = 3; /* Clear IRQ */
+ *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, dma8_descr, set) |
- IO_STATE(R_IRQ_MASK2_SET, dma9_eop, set) |
IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set);
start_dma_in(&ports[0]);
#else
ports[0].use_dma = 0;
initialize_port(0);
- if (request_irq(8, manual_interrupt, SA_SHIRQ, "synchronous serial manual irq", NULL))
+ if (request_irq(8, manual_interrupt, SA_SHIRQ, "synchronous serial manual irq", &ports[0]))
panic("Can't allocate sync serial manual irq");
*R_IRQ_MASK1_SET = IO_STATE(R_IRQ_MASK1_SET, ser1_data, set);
#endif
@@ -243,18 +241,19 @@
#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA)
ports[1].use_dma = 1;
initialize_port(1);
- if(request_irq(20, tr_interrupt, 0, "synchronous serial 3 dma tr", NULL))
+ if(request_irq(20, tr_interrupt, 0, "synchronous serial 3 dma tr", &ports[1]))
panic("Can't allocate sync serial port 1 IRQ");
- if(request_irq(21, rx_interrupt, 0, "synchronous serial 3 dma rx", NULL))
+ if(request_irq(21, rx_interrupt, 0, "synchronous serial 3 dma rx", &ports[1]))
panic("Can't allocate sync serial port 1 IRQ");
RESET_DMA(4); WAIT_DMA(4);
RESET_DMA(5); WAIT_DMA(5);
- *R_DMA_CH4_CLR_INTR = 3; /* Clear IRQ */
- *R_DMA_CH5_CLR_INTR = 3; /* Clear IRQ */
+ *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, dma4_descr, set) |
- IO_STATE(R_IRQ_MASK2_SET, dma5_eop, set) |
IO_STATE(R_IRQ_MASK2_SET, dma5_descr, set);
start_dma_in(&ports[1]);
#else
@@ -262,7 +261,7 @@
initialize_port(1);
if (port[0].use_dma) /* Port 0 uses dma, we must manual allocate IRQ */
{
- if (request_irq(8, manual_interrupt, SA_SHIRQ, "synchronous serial manual irq", NULL))
+ if (request_irq(8, manual_interrupt, SA_SHIRQ, "synchronous serial manual irq", &ports[1]))
panic("Can't allocate sync serial manual irq");
}
*R_IRQ_MASK1_SET = IO_STATE(R_IRQ_MASK1_SET, ser3_data, set);
@@ -278,22 +277,14 @@
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, 0) |
- IO_FIELD(R_SYNC_SERIAL_PRESCALE, word_rate, 7) |
+ 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;
- /*Initialize DMA flush timer if dma is used */
- if (ports[0].use_dma || ports[1].use_dma)
- {
- init_timer(&flush_timer);
- flush_timer.function = flush_handler;
- mod_timer(&flush_timer, jiffies + 10);
- }
-
- printk("Etrax100LX synchronous serial port driver\n");
+ printk("ETRAX 100LX synchronous serial port driver\n");
return 0;
}
@@ -321,9 +312,9 @@
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, running) |
+ 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, enable) |
+ 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) |
@@ -365,27 +356,43 @@
static int sync_serial_release(struct inode *inode, struct file *file)
{
- ports[MINOR(inode->i_rdev)].busy = 0;
+ int dev = MINOR(inode->i_rdev);
+ if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled)
+ {
+ DEBUG(printk("Invalid minor %d\n", dev));
+ return -ENODEV;
+ }
+ ports[dev].busy = 0;
return 0;
}
static int sync_serial_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
+ int return_val = 0;
int dev = MINOR(file->f_dentry->d_inode->i_rdev);
- sync_port* port = &ports[dev];
+ sync_port* port;
+
+ if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled)
+ {
+ DEBUG(printk("Invalid minor %d\n", dev));
+ return -1;
+ }
+ port = &ports[dev];
/* Disable port while changing config */
if (dev)
{
RESET_DMA(4); WAIT_DMA(4);
- *R_DMA_CH4_CLR_INTR = 3;
+ *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
{
RESET_DMA(8); WAIT_DMA(8);
- *R_DMA_CH8_CLR_INTR = 3;
+ *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;
@@ -516,7 +523,7 @@
}
break;
default:
- return -EINVAL;
+ return_val = -1;
}
/* Set config and enable port */
*port->ctrl_data = port->ctrl_data_shadow;
@@ -527,15 +534,23 @@
SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, sync);
*R_GEN_CONFIG_II = gen_config_ii_shadow;
- return 0;
+ return return_val;
}
static ssize_t sync_serial_manual_write(struct file * file, const char * buf,
size_t count, loff_t *ppos)
{
+ int dev = MINOR(file->f_dentry->d_inode->i_rdev);
DECLARE_WAITQUEUE(wait, current);
- sync_port* port = &ports[MINOR(file->f_dentry->d_inode->i_rdev)];
+ sync_port* port;
+ if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled)
+ {
+ DEBUG(printk("Invalid minor %d\n", dev));
+ return -ENODEV;
+ }
+
+ port = &ports[dev];
copy_from_user(port->out_buffer, buf, count);
port->outp = port->out_buffer;
port->out_count = count;
@@ -547,17 +562,36 @@
schedule();
set_current_state(TASK_RUNNING);
remove_wait_queue(&port->out_wait_q, &wait);
+ if (signal_pending(current))
+ {
+ return -EINTR;
+ }
return count;
}
static ssize_t sync_serial_write(struct file * file, const char * buf,
size_t count, loff_t *ppos)
{
- DECLARE_WAITQUEUE(wait, current);
- sync_port *port = &ports[MINOR(file->f_dentry->d_inode->i_rdev)];
+ int dev = MINOR(file->f_dentry->d_inode->i_rdev);
+ DECLARE_WAITQUEUE(wait, current);
+ sync_port *port;
+
+ if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled)
+ {
+ DEBUG(printk("Invalid minor %d\n", dev));
+ return -ENODEV;
+ }
+ port = &ports[dev];
DEBUG(printk("Write dev %d count %d\n", port->port_nbr, count));
+ count = count > OUT_BUFFER_SIZE ? OUT_BUFFER_SIZE : count;
+
+ /* Make sure transmitter is running */
+ SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, running);
+ SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable, enable);
+ *port->ctrl_data = port->ctrl_data_shadow;
+
if (!port->use_dma)
{
return sync_serial_manual_write(file, buf, count, ppos);
@@ -570,26 +604,47 @@
schedule();
set_current_state(TASK_RUNNING);
remove_wait_queue(&port->out_wait_q, &wait);
+ if (signal_pending(current))
+ {
+ return -EINTR;
+ }
return count;
}
static ssize_t sync_serial_read(struct file * file, char * buf,
size_t count, loff_t *ppos)
{
+ int dev = MINOR(file->f_dentry->d_inode->i_rdev);
int avail;
- sync_port *port = &ports[MINOR(file->f_dentry->d_inode->i_rdev)];
+ sync_port *port;
char* start;
char* end;
unsigned long flags;
- DEBUG(printk("Read dev %d count\n"));
+ if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled)
+ {
+ DEBUG(printk("Invalid minor %d\n", dev));
+ return -ENODEV;
+ }
+ port = &ports[dev];
+
+ DEBUG(printk("Read dev %d count %d\n", dev, count));
+
+ /* Make sure receiver is running */
+ SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, running);
+ SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable, enable);
+ *port->ctrl_data = port->ctrl_data_shadow;
/* Calculate number of available bytes */
while (port->readp == port->writep) /* No data */
{
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
- interruptible_sleep_on(&port->in_wait_q);
+ interruptible_sleep_on(&port->in_wait_q);
+ if (signal_pending(current))
+ {
+ return -EINTR;
+ }
}
/* Save pointers to avoid that they are modified by interrupt */
@@ -673,12 +728,12 @@
return;
}
port->in_descr1.hw_len = 0;
- port->in_descr1.ctrl = d_eop | d_int;
+ port->in_descr1.ctrl = d_int;
port->in_descr1.status = 0;
port->in_descr1.next = virt_to_phys(&port->in_descr2);
port->in_descr2.hw_len = 0;
port->in_descr2.next = virt_to_phys(&port->in_descr1);
- port->in_descr2.ctrl = d_eop | d_int;
+ port->in_descr2.ctrl = d_int;
port->in_descr2.status = 0;
/* Find out which descriptor to start */
@@ -766,22 +821,6 @@
start_dma_in(port);
wake_up_interruptible(&port->in_wait_q); /* wake up the waiting process */
}
- else if (ireg & (1 << port->input_dma_eop_bit)) /* EOP interrupt */
- {
- /* EOP interrupt means that DMA has not reached end of descriptor */
- *port->input_dma_clr_irq =
- IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do) |
- IO_STATE(R_DMA_CH0_CLR_INTR, clr_descr, do);
-
- /* Find out the current descriptor */
- if (port->writep >= port->in_buffer + IN_BUFFER_SIZE/2)
- port->writep += port->in_descr2.hw_len;
- else
- port->writep += port->in_descr1.hw_len;
-
- start_dma_in(port);
- wake_up_interruptible(&port->in_wait_q); /* wake up the waiting process */
- }
}
}
@@ -853,21 +892,6 @@
}
}
}
-}
-
-static void flush_handler(void)
-{
- int i;
-
- for (i = 0; i < NUMBER_OF_PORTS; i++)
- {
- if (ports[i].enabled)
- {
- *R_SET_EOP = 1 << ports[i].eop_bit;
- }
- }
- /* restart flush timer */
- mod_timer(&flush_timer, jiffies + 10);
}
module_init(etrax_sync_serial_init);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)