patch-2.2.14 linux/drivers/macintosh/macserial.c
Next file: linux/drivers/macintosh/macserial.h
Previous file: linux/drivers/isdn/pcbit/pcbit.h
Back to the patch index
Back to the overall index
- Lines: 920
- Date:
Tue Jan 4 10:12:17 2000
- Orig file:
v2.2.13/linux/drivers/macintosh/macserial.c
- Orig date:
Tue Jan 4 11:10:35 2000
diff -u --recursive --new-file v2.2.13/linux/drivers/macintosh/macserial.c linux/drivers/macintosh/macserial.c
@@ -6,7 +6,9 @@
* Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
*
- * $Id: macserial.c,v 1.24.2.3 1999/09/10 02:05:58 paulus Exp $
+ * Receive DMA code by Takashi Oe <toe@unlserve.unl.edu>.
+ *
+ * $Id: macserial.c,v 1.24.2.4 1999/10/19 04:36:42 paulus Exp $
*/
#include <linux/config.h>
@@ -28,6 +30,7 @@
#ifdef CONFIG_SERIAL_CONSOLE
#include <linux/console.h>
#endif
+#include <linux/slab.h>
#include <asm/io.h>
#include <asm/pgtable.h>
@@ -42,6 +45,7 @@
#ifdef CONFIG_KGDB
#include <asm/kgdb.h>
#endif
+#include <asm/dbdma.h>
#include "macserial.h"
@@ -53,6 +57,8 @@
};
#endif
+#define SUPPORT_SERIAL_DMA
+
/*
* It would be nice to dynamically allocate everything that
* depends on NUM_SERIAL, so we could support any number of
@@ -128,6 +134,13 @@
static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
static int set_scc_power(struct mac_serial * info, int state);
static int setup_scc(struct mac_serial * info);
+static void dbdma_reset(volatile struct dbdma_regs *dma);
+static void dbdma_flush(volatile struct dbdma_regs *dma);
+static void rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs);
+static void rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs);
+static void dma_init(struct mac_serial * info);
+static void rxdma_start(struct mac_serial * info, int current);
+static void rxdma_to_tty(struct mac_serial * info);
static struct tty_struct *serial_table[NUM_CHANNELS];
static struct termios *serial_termios[NUM_CHANNELS];
@@ -153,7 +166,7 @@
__openfirmware
#endif /* MODULE */
static inline int serial_paranoia_check(struct mac_serial *info,
- dev_t device, const char *routine)
+ dev_t device, const char *routine)
{
#ifdef SERIAL_PARANOIA_CHECK
static const char *badmagic =
@@ -177,7 +190,7 @@
* Reading and writing Z8530 registers.
*/
static inline unsigned char read_zsreg(struct mac_zschannel *channel,
- unsigned char reg)
+ unsigned char reg)
{
unsigned char retval;
unsigned long flags;
@@ -197,7 +210,7 @@
}
static inline void write_zsreg(struct mac_zschannel *channel,
- unsigned char reg, unsigned char value)
+ unsigned char reg, unsigned char value)
{
unsigned long flags;
@@ -290,6 +303,39 @@
}
/*
+ * Reset a Descriptor-Based DMA channel.
+ */
+static void dbdma_reset(volatile struct dbdma_regs *dma)
+{
+ int i;
+
+ out_le32(&dma->control, (WAKE|FLUSH|PAUSE|RUN) << 16);
+
+ /*
+ * Yes this looks peculiar, but apparently it needs to be this
+ * way on some machines. (We need to make sure the DBDMA
+ * engine has actually got the write above and responded
+ * to it. - paulus)
+ */
+ for (i = 200; i > 0; --i)
+ if (ld_le32(&dma->control) & RUN)
+ udelay(1);
+}
+
+/*
+ * Tells a DBDMA channel to stop and write any buffered data
+ * it might have to memory.
+ */
+static _INLINE_ void dbdma_flush(volatile struct dbdma_regs *dma)
+{
+ int i = 0;
+
+ out_le32(&dma->control, (FLUSH << 16) | FLUSH);
+ while (((in_le32(&dma->status) & FLUSH) != 0) && (i++ < 100))
+ udelay(1);
+}
+
+/*
* ----------------------------------------------------------------------
*
* Here starts the interrupt handling routines. All of the following
@@ -312,6 +358,22 @@
mark_bh(MACSERIAL_BH);
}
+/* Work out the flag value for a z8530 status value. */
+static _INLINE_ int stat_to_flag(int stat)
+{
+ int flag;
+
+ if (stat & Rx_OVR) {
+ flag = TTY_OVERRUN;
+ } else if (stat & FRM_ERR) {
+ flag = TTY_FRAME;
+ } else if (stat & PAR_ERR) {
+ flag = TTY_PARITY;
+ } else
+ flag = 0;
+ return flag;
+}
+
static _INLINE_ void receive_chars(struct mac_serial *info,
struct pt_regs *regs)
{
@@ -349,14 +411,7 @@
if (flip_max_cnt < tty->flip.count)
flip_max_cnt = tty->flip.count;
}
- if (stat & Rx_OVR) {
- flag = TTY_OVERRUN;
- } else if (stat & FRM_ERR) {
- flag = TTY_FRAME;
- } else if (stat & PAR_ERR) {
- flag = TTY_PARITY;
- } else
- flag = 0;
+ flag = stat_to_flag(stat);
if (flag)
/* reset the error indication */
write_zsreg(info->zs_channel, 0, ERR_RES);
@@ -452,6 +507,32 @@
info->read_reg_zero = status;
}
+static _INLINE_ void receive_special_dma(struct mac_serial *info)
+{
+ unsigned char stat, flag;
+ volatile struct dbdma_regs *rd = &info->rx->dma;
+ int where = RX_BUF_SIZE;
+
+ spin_lock(&info->rx_dma_lock);
+ if ((ld_le32(&rd->status) & ACTIVE) != 0)
+ dbdma_flush(rd);
+ if (in_le32(&rd->cmdptr)
+ == virt_to_bus(info->rx_cmds[info->rx_cbuf] + 1))
+ where -= in_le16(&info->rx->res_count);
+ where--;
+
+ stat = read_zsreg(info->zs_channel, R1);
+
+ flag = stat_to_flag(stat);
+ if (flag) {
+ info->rx_flag_buf[info->rx_cbuf][where] = flag;
+ /* reset the error indication */
+ write_zsreg(info->zs_channel, 0, ERR_RES);
+ }
+
+ spin_unlock(&info->rx_dma_lock);
+}
+
/*
* This is the serial driver's generic interrupt routine
*/
@@ -461,6 +542,12 @@
unsigned char zs_intreg;
int shift;
+ if (!(info->flags & ZILOG_INITIALIZED)) {
+ printk("rs_interrupt: irq %d, port not initialized\n", irq);
+ disable_irq(irq);
+ return;
+ }
+
/* NOTE: The read register 3, which holds the irq status,
* does so for both channels on each chip. Although
* the status value itself must be read from the A
@@ -477,19 +564,21 @@
for (;;) {
zs_intreg = read_zsreg(info->zs_chan_a, 3) >> shift;
#ifdef SERIAL_DEBUG_INTR
- printk("rs_interrupt: irq %d, zs_intreg 0x%x\n", irq, (int)zs_intreg);
+ printk("rs_interrupt: irq %d, zs_intreg 0x%x\n",
+ irq, (int)zs_intreg);
#endif
if ((zs_intreg & CHAN_IRQMASK) == 0)
break;
- if (!(info->flags & ZILOG_INITIALIZED)) {
- printk("rs_interrupt: irq %d, port not initialized\n", irq);
- break;
+ if (zs_intreg & CHBRxIP) {
+ /* If we are doing DMA, we only ask for interrupts
+ on characters with errors or special conditions. */
+ if (info->dma_initted)
+ receive_special_dma(info);
+ else
+ receive_chars(info, regs);
}
-
- if (zs_intreg & CHBRxIP)
- receive_chars(info, regs);
if (zs_intreg & CHBTxIP)
transmit_chars(info);
if (zs_intreg & CHBEXT)
@@ -497,6 +586,39 @@
}
}
+/* Transmit DMA interrupt - not used at present */
+static void rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+}
+
+/*
+ * Receive DMA interrupt.
+ */
+static void rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct mac_serial *info = (struct mac_serial *) dev_id;
+ volatile struct dbdma_cmd *cd;
+
+ if (!info->dma_initted)
+ return;
+ spin_lock(&info->rx_dma_lock);
+ /* First, confirm that this interrupt is, indeed, coming */
+ /* from Rx DMA */
+ cd = info->rx_cmds[info->rx_cbuf] + 2;
+ if ((in_le16(&cd->xfer_status) & (RUN | ACTIVE)) != (RUN | ACTIVE)) {
+ spin_unlock(&info->rx_dma_lock);
+ return;
+ }
+ if (info->rx_fbuf != RX_NO_FBUF) {
+ info->rx_cbuf = info->rx_fbuf;
+ if (++info->rx_fbuf == info->rx_nbuf)
+ info->rx_fbuf = 0;
+ if (info->rx_fbuf == info->rx_ubuf)
+ info->rx_fbuf = RX_NO_FBUF;
+ }
+ spin_unlock(&info->rx_dma_lock);
+}
+
/*
* -------------------------------------------------------------------
* Here ends the serial interrupt routines.
@@ -592,10 +714,6 @@
}
}
-static void rs_timer(void)
-{
-}
-
static int startup(struct mac_serial * info, int can_sleep)
{
int delay;
@@ -631,6 +749,10 @@
info->flags |= ZILOG_INITIALIZED;
enable_irq(info->irq);
+ if (info->dma_initted) {
+// enable_irq(info->tx_dma_irq);
+ enable_irq(info->rx_dma_irq);
+ }
if (delay) {
if (can_sleep) {
@@ -644,6 +766,187 @@
return 0;
}
+static _INLINE_ void rxdma_start(struct mac_serial * info, int current)
+{
+ volatile struct dbdma_regs *rd = &info->rx->dma;
+ volatile struct dbdma_cmd *cd = info->rx_cmds[current];
+
+//printk(KERN_DEBUG "SCC: rxdma_start\n");
+
+ st_le32(&rd->cmdptr, virt_to_bus(cd));
+ out_le32(&rd->control, (RUN << 16) | RUN);
+}
+
+static void rxdma_to_tty(struct mac_serial *info)
+{
+ struct tty_struct *tty = info->tty;
+ volatile struct dbdma_regs *rd = &info->rx->dma;
+ unsigned long flags;
+ int residue, available, space, do_queue;
+
+ if (!tty)
+ return;
+
+ do_queue = 0;
+ spin_lock_irqsave(&info->rx_dma_lock, flags);
+more:
+ space = TTY_FLIPBUF_SIZE - tty->flip.count;
+ if (!space) {
+ do_queue++;
+ goto out;
+ }
+ residue = 0;
+ if (info->rx_ubuf == info->rx_cbuf) {
+ if ((ld_le32(&rd->status) & ACTIVE) != 0) {
+ dbdma_flush(rd);
+ if (in_le32(&rd->cmdptr)
+ == virt_to_bus(info->rx_cmds[info->rx_cbuf]+1))
+ residue = in_le16(&info->rx->res_count);
+ }
+ }
+ available = RX_BUF_SIZE - residue - info->rx_done_bytes;
+ if (available > space)
+ available = space;
+ if (available) {
+ memcpy(tty->flip.char_buf_ptr,
+ info->rx_char_buf[info->rx_ubuf] + info->rx_done_bytes,
+ available);
+ memcpy(tty->flip.flag_buf_ptr,
+ info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes,
+ available);
+ tty->flip.char_buf_ptr += available;
+ tty->flip.count += available;
+ tty->flip.flag_buf_ptr += available;
+ memset(info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes,
+ 0, available);
+ info->rx_done_bytes += available;
+ do_queue++;
+ }
+ if (info->rx_done_bytes == RX_BUF_SIZE) {
+ volatile struct dbdma_cmd *cd = info->rx_cmds[info->rx_ubuf];
+
+ if (info->rx_ubuf == info->rx_cbuf)
+ goto out;
+ /* mark rx_char_buf[rx_ubuf] free */
+ st_le16(&cd->command, DBDMA_NOP);
+ cd++;
+ st_le32(&cd->cmd_dep, 0);
+ st_le32((unsigned int *)&cd->res_count, 0);
+ cd++;
+ st_le16(&cd->xfer_status, 0);
+
+ if (info->rx_fbuf == RX_NO_FBUF) {
+ info->rx_fbuf = info->rx_ubuf;
+ if (!(ld_le32(&rd->status) & ACTIVE)) {
+ dbdma_reset(&info->rx->dma);
+ rxdma_start(info, info->rx_ubuf);
+ info->rx_cbuf = info->rx_ubuf;
+ }
+ }
+ info->rx_done_bytes = 0;
+ if (++info->rx_ubuf == info->rx_nbuf)
+ info->rx_ubuf = 0;
+ if (info->rx_fbuf == info->rx_ubuf)
+ info->rx_fbuf = RX_NO_FBUF;
+ goto more;
+ }
+out:
+ spin_unlock_irqrestore(&info->rx_dma_lock, flags);
+ if (do_queue)
+ queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+static void poll_rxdma(void *private_)
+{
+ struct mac_serial *info = (struct mac_serial *) private_;
+ unsigned long flags;
+
+ rxdma_to_tty(info);
+ spin_lock_irqsave(&info->rx_dma_lock, flags);
+ mod_timer(&info->poll_dma_timer, RX_DMA_TIMER);
+ spin_unlock_irqrestore(&info->rx_dma_lock, flags);
+}
+
+static void dma_init(struct mac_serial * info)
+{
+ int i, size;
+ volatile struct dbdma_cmd *cd;
+ unsigned char *p;
+
+//printk(KERN_DEBUG "SCC: dma_init\n");
+
+ info->rx_nbuf = 8;
+
+ /* various mem set up */
+ size = sizeof(struct dbdma_cmd) * (3 * info->rx_nbuf + 2)
+ + (RX_BUF_SIZE * 2 + sizeof(*info->rx_cmds)
+ + sizeof(*info->rx_char_buf) + sizeof(*info->rx_flag_buf))
+ * info->rx_nbuf;
+ info->dma_priv = kmalloc(size, GFP_KERNEL | GFP_DMA);
+ if (info->dma_priv == NULL)
+ return;
+ memset(info->dma_priv, 0, size);
+
+ info->rx_cmds = (volatile struct dbdma_cmd **)info->dma_priv;
+ info->rx_char_buf = (unsigned char **) (info->rx_cmds + info->rx_nbuf);
+ info->rx_flag_buf = info->rx_char_buf + info->rx_nbuf;
+ p = (unsigned char *) (info->rx_flag_buf + info->rx_nbuf);
+ for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE)
+ info->rx_char_buf[i] = p;
+ for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE)
+ info->rx_flag_buf[i] = p;
+
+ /* a bit of DMA programming */
+ cd = info->rx_cmds[0] = (volatile struct dbdma_cmd *) DBDMA_ALIGN(p);
+ st_le16(&cd->command, DBDMA_NOP);
+ cd++;
+ st_le16(&cd->req_count, RX_BUF_SIZE);
+ st_le16(&cd->command, INPUT_MORE);
+ st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[0]));
+ cd++;
+ st_le16(&cd->req_count, 4);
+ st_le16(&cd->command, STORE_WORD | INTR_ALWAYS);
+ st_le32(&cd->phy_addr, virt_to_bus(cd-2));
+ st_le32(&cd->cmd_dep, DBDMA_STOP);
+ for (i = 1; i < info->rx_nbuf; i++) {
+ info->rx_cmds[i] = ++cd;
+ st_le16(&cd->command, DBDMA_NOP);
+ cd++;
+ st_le16(&cd->req_count, RX_BUF_SIZE);
+ st_le16(&cd->command, INPUT_MORE);
+ st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[i]));
+ cd++;
+ st_le16(&cd->req_count, 4);
+ st_le16(&cd->command, STORE_WORD | INTR_ALWAYS);
+ st_le32(&cd->phy_addr, virt_to_bus(cd-2));
+ st_le32(&cd->cmd_dep, DBDMA_STOP);
+ }
+ cd++;
+ st_le16(&cd->command, DBDMA_NOP | BR_ALWAYS);
+ st_le32(&cd->cmd_dep, virt_to_bus(info->rx_cmds[0]));
+
+ /* setup DMA to our liking */
+ dbdma_reset(&info->rx->dma);
+ st_le32(&info->rx->dma.intr_sel, 0x10001);
+ st_le32(&info->rx->dma.br_sel, 0x10001);
+ out_le32(&info->rx->dma.wait_sel, 0x10001);
+
+ /* set various flags */
+ info->rx_ubuf = 0;
+ info->rx_cbuf = 0;
+ info->rx_fbuf = info->rx_ubuf + 1;
+ if (info->rx_fbuf == info->rx_nbuf)
+ info->rx_fbuf = RX_NO_FBUF;
+ info->rx_done_bytes = 0;
+
+ /* setup polling */
+ init_timer(&info->poll_dma_timer);
+ info->poll_dma_timer.function = (void *)&poll_rxdma;
+ info->poll_dma_timer.data = (unsigned long)info;
+
+ info->dma_initted = 1;
+}
+
static int setup_scc(struct mac_serial * info)
{
unsigned long flags;
@@ -669,6 +972,12 @@
info->xmit_fifo_size = 1;
/*
+ * Reset DMAs
+ */
+ if (info->has_dma)
+ dma_init(info);
+
+ /*
* Clear the interrupt registers.
*/
write_zsreg(info->zs_channel, 0, ERR_RES);
@@ -682,7 +991,23 @@
/*
* Finally, enable sequencing and interrupts
*/
- info->curregs[1] = (info->curregs[1] & ~0x18) | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB);
+ if (!info->dma_initted) {
+ /* interrupt on ext/status changes, all received chars,
+ transmit ready */
+ info->curregs[1] = (info->curregs[1] & ~0x18)
+ | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB);
+ } else {
+ /* interrupt on ext/status changes, W/Req pin is
+ receive DMA request */
+ info->curregs[1] = (info->curregs[1] & ~(0x18 | TxINT_ENAB))
+ | (EXT_INT_ENAB | WT_RDY_RT | WT_FN_RDYFN);
+ write_zsreg(info->zs_channel, 1, info->curregs[1]);
+ /* enable W/Req pin */
+ info->curregs[1] |= WT_RDY_ENAB;
+ write_zsreg(info->zs_channel, 1, info->curregs[1]);
+ /* enable interrupts on transmit ready and receive errors */
+ info->curregs[1] |= INT_ERR_Rx | TxINT_ENAB;
+ }
info->pendregs[1] = info->curregs[1];
info->curregs[3] |= (RxENABLE | Rx8);
info->pendregs[3] = info->curregs[3];
@@ -708,6 +1033,14 @@
restore_flags(flags);
+ if (info->dma_initted) {
+ spin_lock_irqsave(&info->rx_dma_lock, flags);
+ rxdma_start(info, 0);
+ info->poll_dma_timer.expires = RX_DMA_TIMER;
+ add_timer(&info->poll_dma_timer);
+ spin_unlock_irqrestore(&info->rx_dma_lock, flags);
+ }
+
return 0;
}
@@ -729,7 +1062,14 @@
return;
}
-
+
+ if (info->has_dma) {
+ del_timer(&info->poll_dma_timer);
+ dbdma_reset(info->tx_dma);
+ dbdma_reset(&info->rx->dma);
+ disable_irq(info->tx_dma_irq);
+ disable_irq(info->rx_dma_irq);
+ }
disable_irq(info->irq);
info->pendregs[1] = info->curregs[1] = 0;
@@ -755,6 +1095,12 @@
info->xmit_buf = 0;
}
+ if (info->has_dma && info->dma_priv) {
+ kfree(info->dma_priv);
+ info->dma_priv = NULL;
+ info->dma_initted = 0;
+ }
+
memset(info->curregs, 0, sizeof(info->curregs));
memset(info->curregs, 0, sizeof(info->pendregs));
@@ -1052,7 +1398,6 @@
if (!tty || !info->xmit_buf || !tmp_buf)
return 0;
- save_flags(flags);
if (from_user) {
down(&tmp_buf_sem);
while (1) {
@@ -1068,6 +1413,7 @@
ret = -EFAULT;
break;
}
+ save_flags(flags);
cli();
c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
@@ -1083,6 +1429,7 @@
up(&tmp_buf_sem);
} else {
while (1) {
+ save_flags(flags);
cli();
c = MIN(count,
MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
@@ -1104,7 +1451,6 @@
if (info->xmit_cnt && !tty->stopped && !info->tx_stopped
&& !info->tx_active)
transmit_chars(info);
- restore_flags(flags);
return ret;
}
@@ -1133,12 +1479,13 @@
static void rs_flush_buffer(struct tty_struct *tty)
{
struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+ unsigned long flags;
if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
return;
- cli();
+ save_flags(flags); cli();
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
- sti();
+ restore_flags(flags);
wake_up_interruptible(&tty->write_wait);
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup)
@@ -1158,7 +1505,6 @@
struct mac_serial *info = (struct mac_serial *)tty->driver_data;
unsigned long flags;
#ifdef SERIAL_DEBUG_THROTTLE
- char buf[64];
printk("throttle %ld....\n",tty->ldisc.chars_in_buffer(tty));
#endif
@@ -1195,7 +1541,6 @@
struct mac_serial *info = (struct mac_serial *)tty->driver_data;
unsigned long flags;
#ifdef SERIAL_DEBUG_THROTTLE
- char buf[64];
printk("unthrottle %s: %d....\n",tty->ldisc.chars_in_buffer(tty));
#endif
@@ -1473,6 +1818,7 @@
save_flags(flags); cli();
if (tty_hung_up_p(filp)) {
+ MOD_DEC_USE_COUNT;
restore_flags(flags);
return;
}
@@ -1498,6 +1844,7 @@
info->count = 0;
}
if (info->count) {
+ MOD_DEC_USE_COUNT;
restore_flags(flags);
return;
}
@@ -1518,8 +1865,12 @@
printk("waiting end of Tx... (timeout:%d)\n", info->closing_wait);
#endif
tty->closing = 1;
- if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE)
+ if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE) {
+ restore_flags(flags);
tty_wait_until_sent(tty, info->closing_wait);
+ save_flags(flags); cli();
+ }
+
/*
* At this point we stop accepting input. To do this, we
* disable the receiver and receive interrupts.
@@ -1539,7 +1890,9 @@
#ifdef SERIAL_DEBUG_OPEN
printk("waiting end of Rx...\n");
#endif
+ restore_flags(flags);
rs_wait_until_sent(tty, info->timeout);
+ save_flags(flags); cli();
}
shutdown(info);
@@ -1565,6 +1918,7 @@
info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE|
ZILOG_CLOSING);
wake_up_interruptible(&info->close_wait);
+ MOD_DEC_USE_COUNT;
}
/*
@@ -1603,7 +1957,6 @@
char_time = MIN(char_time, timeout);
while ((read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) {
current->state = TASK_INTERRUPTIBLE;
- current->counter = 0; /* make us low-priority */
schedule_timeout(char_time);
if (signal_pending(current))
break;
@@ -1775,14 +2128,19 @@
int retval, line;
unsigned long page;
+ MOD_INC_USE_COUNT;
line = MINOR(tty->device) - tty->driver.minor_start;
- if ((line < 0) || (line >= zs_channels_found))
+ if ((line < 0) || (line >= zs_channels_found)) {
+ MOD_DEC_USE_COUNT;
return -ENODEV;
+ }
info = zs_soft + line;
#ifdef CONFIG_KGDB
- if (info->kgdb_channel)
+ if (info->kgdb_channel) {
+ MOD_DEC_USE_COUNT;
return -ENODEV;
+ }
#endif
if (serial_paranoia_check(info, tty->device, "rs_open"))
return -ENODEV;
@@ -1865,7 +2223,58 @@
static void show_serial_version(void)
{
- printk("PowerMac Z8530 serial driver version 1.01\n");
+ printk("PowerMac Z8530 serial driver version 2.0\n");
+}
+
+/*
+ * Initialize one channel, both the mac_serial and mac_zschannel
+ * structs. We use the dev_node field of the mac_serial struct.
+ */
+static void
+chan_init(struct mac_serial *zss, struct mac_zschannel *zs_chan,
+ struct mac_zschannel *zs_chan_a)
+{
+ struct device_node *ch = zss->dev_node;
+ char *conn;
+ int len;
+
+ zss->irq = ch->intrs[0].line;
+ zss->has_dma = 0;
+#if !defined(CONFIG_KGDB) && defined(SUPPORT_SERIAL_DMA)
+ if (ch->n_addrs == 3 && ch->n_intrs == 3)
+ zss->has_dma = 1;
+#endif
+ zss->dma_initted = 0;
+
+ zs_chan->control = (volatile unsigned char *)
+ ioremap(ch->addrs[0].address, 0x1000);
+ zs_chan->data = zs_chan->control + 0x10;
+ spin_lock_init(&zs_chan->lock);
+ zs_chan->parent = zss;
+ zss->zs_channel = zs_chan;
+ zss->zs_chan_a = zs_chan_a;
+
+ /* setup misc varariables */
+ zss->kgdb_channel = 0;
+ zss->is_cobalt_modem = device_is_compatible(ch, "cobalt");
+
+ /* XXX tested only with wallstreet PowerBook,
+ should do no harm anyway */
+ conn = get_property(ch, "AAPL,connector", &len);
+ zss->is_pwbk_ir = conn && (strcmp(conn, "infrared") == 0);
+
+ if (zss->has_dma) {
+ zss->dma_priv = NULL;
+ /* it seems that the last two addresses are the
+ DMA controllers */
+ zss->tx_dma = (volatile struct dbdma_regs *)
+ ioremap(ch->addrs[ch->n_addrs - 2].address, 0x100);
+ zss->rx = (volatile struct mac_dma *)
+ ioremap(ch->addrs[ch->n_addrs - 1].address, 0x100);
+ zss->tx_dma_irq = ch->intrs[1].line;
+ zss->rx_dma_irq = ch->intrs[2].line;
+ spin_lock_init(&zss->rx_dma_lock);
+ }
}
/* Ask the PROM how many Z8530s we have and initialize their zs_channels */
@@ -1874,51 +2283,63 @@
{
struct device_node *dev, *ch;
struct mac_serial **pp;
- int n, lenp;
- char *conn;
+ int n, chip, nchan;
+ struct mac_zschannel *zs_chan;
+ int chan_a_index;
n = 0;
pp = &zs_chain;
+ zs_chan = zs_channels;
for (dev = find_devices("escc"); dev != 0; dev = dev->next) {
+ nchan = 0;
+ chip = n;
if (n >= NUM_CHANNELS) {
printk("Sorry, can't use %s: no more channels\n",
dev->full_name);
continue;
}
+ chan_a_index = 0;
for (ch = dev->child; ch != 0; ch = ch->sibling) {
+ if (nchan >= 2) {
+ printk(KERN_WARNING "SCC: Only 2 channels per "
+ "chip are supported\n");
+ break;
+ }
if (ch->n_addrs < 1 || (ch ->n_intrs < 1)) {
printk("Can't use %s: %d addrs %d intrs\n",
ch->full_name, ch->n_addrs, ch->n_intrs);
continue;
}
- zs_channels[n].control = (volatile unsigned char *)
- ioremap(ch->addrs[0].address, 0x1000);
- zs_channels[n].data = zs_channels[n].control + 0x10;
- spin_lock_init(&zs_channels[n].lock);
- zs_soft[n].zs_channel = &zs_channels[n];
- zs_soft[n].dev_node = ch;
- zs_soft[n].irq = ch->intrs[0].line;
- zs_soft[n].zs_channel->parent = &zs_soft[n];
- zs_soft[n].is_cobalt_modem = device_is_compatible(ch, "cobalt");
-
- /* XXX tested only with wallstreet PowerBook,
- should do no harm anyway */
- conn = get_property(ch, "AAPL,connector", &lenp);
- zs_soft[n].is_pwbk_ir =
- conn && (strcmp(conn, "infrared") == 0);
-
- /* XXX this assumes the prom puts chan A before B */
- if (n & 1)
- zs_soft[n].zs_chan_a = &zs_channels[n-1];
- else
- zs_soft[n].zs_chan_a = &zs_channels[n];
+ /* The channel with the higher address
+ will be the A side. */
+ if (nchan > 0 &&
+ ch->addrs[0].address
+ > zs_soft[n-1].dev_node->addrs[0].address)
+ chan_a_index = 1;
+
+ /* minimal initialization for now */
+ zs_soft[n].dev_node = ch;
*pp = &zs_soft[n];
pp = &zs_soft[n].zs_next;
+ ++nchan;
++n;
}
+ if (nchan == 0)
+ continue;
+
+ /* set up A side */
+ chan_init(&zs_soft[chip + chan_a_index], zs_chan, zs_chan);
+ ++zs_chan;
+
+ /* set up B side, if it exists */
+ if (nchan > 1)
+ chan_init(&zs_soft[chip + 1 - chan_a_index],
+ zs_chan, zs_chan - 1);
+ ++zs_chan;
}
*pp = 0;
+
zs_channels_found = n;
#ifdef CONFIG_PMAC_PBOOK
if (n)
@@ -1935,8 +2356,6 @@
/* Setup base handler, and timer table. */
init_bh(MACSERIAL_BH, do_serial_bh);
- timer_table[RS_TIMER].fn = rs_timer;
- timer_table[RS_TIMER].expires = 0;
/* Find out how many Z8530 SCCs we have */
if (zs_chain == 0)
@@ -1948,6 +2367,18 @@
/* Register the interrupt handler for each one */
save_flags(flags); cli();
for (i = 0; i < zs_channels_found; ++i) {
+ if (zs_soft[i].has_dma) {
+ if (request_irq(zs_soft[i].tx_dma_irq, rs_txdma_irq, 0,
+ "SCC-txdma", &zs_soft[i]))
+ printk(KERN_ERR "macserial: can't get irq %d\n",
+ zs_soft[i].tx_dma_irq);
+ disable_irq(zs_soft[i].tx_dma_irq);
+ if (request_irq(zs_soft[i].rx_dma_irq, rs_rxdma_irq, 0,
+ "SCC-rxdma", &zs_soft[i]))
+ printk(KERN_ERR "macserial: can't get irq %d\n",
+ zs_soft[i].rx_dma_irq);
+ disable_irq(zs_soft[i].rx_dma_irq);
+ }
if (request_irq(zs_soft[i].irq, rs_interrupt, 0,
"SCC", &zs_soft[i]))
printk(KERN_ERR "macserial: can't get irq %d\n",
@@ -2107,8 +2538,13 @@
for (info = zs_chain, i = 0; info; info = info->zs_next, i++)
set_scc_power(info, 0);
save_flags(flags); cli();
- for (i = 0; i < zs_channels_found; ++i)
+ for (i = 0; i < zs_channels_found; ++i) {
free_irq(zs_soft[i].irq, &zs_soft[i]);
+ if (zs_soft[i].has_dma) {
+ free_irq(zs_soft[i].tx_dma_irq, &zs_soft[i]);
+ free_irq(zs_soft[i].rx_dma_irq, &zs_soft[i]);
+ }
+ }
restore_flags(flags);
tty_unregister_driver(&callout_driver);
tty_unregister_driver(&serial_driver);
@@ -2238,6 +2674,8 @@
if (zs_chain == 0)
return -1;
+ set_scc_power(info, 1);
+
/* Reset the channel */
write_zsreg(info->zs_channel, R9, CHRA);
@@ -2481,14 +2919,13 @@
if (zs_chain == 0)
probe_sccs();
- set_scc_power(&zs_soft[n], 1);
+ set_scc_power(&zs_soft[tty_num], 1);
zs_kgdbchan = zs_soft[tty_num].zs_channel;
zs_soft[tty_num].change_needed = 0;
zs_soft[tty_num].clk_divisor = 16;
zs_soft[tty_num].zs_baud = 38400;
zs_soft[tty_num].kgdb_channel = 1; /* This runs kgdb */
- zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */
/* Turn on transmitter/receiver at 8-bits/char */
kgdb_chaninit(zs_soft[tty_num].zs_channel, 1, 38400);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)