summaryrefslogtreecommitdiffstats
path: root/drivers/macintosh
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-01-27 01:05:20 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-01-27 01:05:20 +0000
commit546db14ee74118296f425f3b91634fb767d67290 (patch)
tree22b613a3da8d4bf663eec5e155af01b87fdf9094 /drivers/macintosh
parent1e25e41c4f5474e14452094492dbc169b800e4c8 (diff)
Merge with Linux 2.3.23. The new bootmem stuff has broken various
platforms. At this time I've only verified that IP22 support compiles and IP27 actually works.
Diffstat (limited to 'drivers/macintosh')
-rw-r--r--drivers/macintosh/macserial.c571
-rw-r--r--drivers/macintosh/macserial.h49
2 files changed, 557 insertions, 63 deletions
diff --git a/drivers/macintosh/macserial.c b/drivers/macintosh/macserial.c
index 53590a42a..720932cc9 100644
--- a/drivers/macintosh/macserial.c
+++ b/drivers/macintosh/macserial.c
@@ -5,6 +5,10 @@
*
* Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * Receive DMA code by Takashi Oe <toe@unlserve.unl.edu>.
+ *
+ * $Id$
*/
#include <linux/config.h>
@@ -26,6 +30,7 @@
#ifdef CONFIG_SERIAL_CONSOLE
#include <linux/console.h>
#endif
+#include <linux/slab.h>
#include <asm/init.h>
#include <asm/io.h>
@@ -41,6 +46,7 @@
#ifdef CONFIG_KGDB
#include <asm/kgdb.h>
#endif
+#include <asm/dbdma.h>
#include "macserial.h"
@@ -52,6 +58,8 @@ static struct pmu_sleep_notifier serial_sleep_notifier = {
};
#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
@@ -127,6 +135,13 @@ static void change_speed(struct mac_serial *info, struct termios *old);
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];
@@ -288,6 +303,39 @@ static inline void rs_recv_clear(struct mac_zschannel *zsc)
}
/*
+ * 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
@@ -310,6 +358,22 @@ static _INLINE_ void rs_sched_event(struct mac_serial *info,
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)
{
@@ -347,14 +411,7 @@ static _INLINE_ void receive_chars(struct mac_serial *info,
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);
@@ -450,6 +507,32 @@ static _INLINE_ void status_handle(struct mac_serial *info)
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
*/
@@ -459,6 +542,12 @@ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
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
@@ -475,19 +564,21 @@ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
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)
@@ -495,6 +586,39 @@ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
}
}
+/* 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.
@@ -590,10 +714,6 @@ static void do_softint(void *private_)
}
}
-static void rs_timer(void)
-{
-}
-
static int startup(struct mac_serial * info, int can_sleep)
{
int delay;
@@ -629,6 +749,10 @@ static int startup(struct mac_serial * info, int can_sleep)
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) {
@@ -642,6 +766,187 @@ static int startup(struct mac_serial * info, int can_sleep)
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;
@@ -667,6 +972,12 @@ static int setup_scc(struct mac_serial * info)
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);
@@ -680,7 +991,23 @@ static int setup_scc(struct mac_serial * info)
/*
* 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];
@@ -706,6 +1033,14 @@ static int setup_scc(struct mac_serial * info)
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;
}
@@ -727,7 +1062,14 @@ static void shutdown(struct mac_serial * info)
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;
@@ -753,6 +1095,12 @@ static void shutdown(struct mac_serial * info)
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));
@@ -795,7 +1143,7 @@ static int set_scc_power(struct mac_serial * info, int state)
feature_set(info->dev_node, FEATURE_Modem_Reset);
mdelay(5);
feature_clear(info->dev_node, FEATURE_Modem_Reset);
- delay = 1000; /* wait for 1s before using */
+ delay = 2500; /* wait for 2.5s before using */
}
#ifdef CONFIG_PMAC_PBOOK
if (info->is_pwbk_ir)
@@ -1050,7 +1398,6 @@ static int rs_write(struct tty_struct * tty, int from_user,
if (!tty || !info->xmit_buf || !tmp_buf)
return 0;
- save_flags(flags);
if (from_user) {
down(&tmp_buf_sem);
while (1) {
@@ -1066,6 +1413,7 @@ static int rs_write(struct tty_struct * tty, int from_user,
ret = -EFAULT;
break;
}
+ save_flags(flags);
cli();
c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
@@ -1081,6 +1429,7 @@ static int rs_write(struct tty_struct * tty, int from_user,
up(&tmp_buf_sem);
} else {
while (1) {
+ save_flags(flags);
cli();
c = MIN(count,
MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
@@ -1102,7 +1451,6 @@ static int rs_write(struct tty_struct * tty, int from_user,
if (info->xmit_cnt && !tty->stopped && !info->tx_stopped
&& !info->tx_active)
transmit_chars(info);
- restore_flags(flags);
return ret;
}
@@ -1131,12 +1479,13 @@ static int rs_chars_in_buffer(struct tty_struct *tty)
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)
@@ -1156,7 +1505,6 @@ static void rs_throttle(struct tty_struct * tty)
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
@@ -1193,7 +1541,6 @@ static void rs_unthrottle(struct tty_struct * tty)
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
@@ -1471,6 +1818,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
save_flags(flags); cli();
if (tty_hung_up_p(filp)) {
+ MOD_DEC_USE_COUNT;
restore_flags(flags);
return;
}
@@ -1496,6 +1844,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
info->count = 0;
}
if (info->count) {
+ MOD_DEC_USE_COUNT;
restore_flags(flags);
return;
}
@@ -1516,8 +1865,12 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
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.
@@ -1537,7 +1890,9 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
#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);
@@ -1563,6 +1918,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE|
ZILOG_CLOSING);
wake_up_interruptible(&info->close_wait);
+ MOD_DEC_USE_COUNT;
}
/*
@@ -1772,14 +2128,19 @@ static int rs_open(struct tty_struct *tty, struct file * filp)
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;
@@ -1862,7 +2223,58 @@ static int rs_open(struct tty_struct *tty, struct file * filp)
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 */
@@ -1871,51 +2283,63 @@ probe_sccs()
{
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)
@@ -1932,8 +2356,6 @@ int macserial_init(void)
/* 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)
@@ -1945,6 +2367,18 @@ int macserial_init(void)
/* 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",
@@ -2083,6 +2517,7 @@ int macserial_init(void)
/* By default, disable the port */
set_scc_power(info, 0);
}
+ tmp_buf = 0;
return 0;
}
@@ -2103,11 +2538,26 @@ void cleanup_module(void)
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);
+
+ if (tmp_buf) {
+ free_page((unsigned long) tmp_buf);
+ tmp_buf = 0;
+ }
+
+#ifdef CONFIG_PMAC_PBOOK
+ if (zs_channels_found)
+ pmu_unregister_sleep_notifier(&serial_sleep_notifier);
+#endif /* CONFIG_PMAC_PBOOK */
}
#endif /* MODULE */
@@ -2224,6 +2674,8 @@ static int __init serial_console_setup(struct console *co, char *options)
if (zs_chain == 0)
return -1;
+ set_scc_power(info, 1);
+
/* Reset the channel */
write_zsreg(info->zs_channel, R9, CHRA);
@@ -2467,14 +2919,13 @@ void __init zs_kgdb_hook(int tty_num)
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);
diff --git a/drivers/macintosh/macserial.h b/drivers/macintosh/macserial.h
index e2f137f73..3ba2e6062 100644
--- a/drivers/macintosh/macserial.h
+++ b/drivers/macintosh/macserial.h
@@ -92,6 +92,13 @@ struct mac_zschannel {
struct mac_serial* parent;
};
+struct mac_dma {
+ volatile struct dbdma_regs dma;
+ volatile unsigned short res_count;
+ volatile unsigned short command;
+ volatile unsigned int buf_addr;
+};
+
struct mac_serial {
struct mac_serial *zs_next; /* For IRQ servicing chain */
struct mac_zschannel *zs_channel; /* Channel registers */
@@ -156,6 +163,28 @@ struct mac_serial {
struct termios callout_termios;
wait_queue_head_t open_wait;
wait_queue_head_t close_wait;
+
+ volatile struct dbdma_regs *tx_dma;
+ int tx_dma_irq;
+ volatile struct dbdma_cmd *tx_cmds;
+ volatile struct mac_dma *rx;
+ int rx_dma_irq;
+ volatile struct dbdma_cmd **rx_cmds;
+ unsigned char **rx_char_buf;
+ unsigned char **rx_flag_buf;
+#define RX_BUF_SIZE 256
+ int rx_nbuf;
+ int rx_done_bytes;
+ int rx_ubuf;
+ int rx_fbuf;
+#define RX_NO_FBUF (-1)
+ int rx_cbuf;
+ spinlock_t rx_dma_lock;
+ int has_dma;
+ int dma_initted;
+ void *dma_priv;
+ struct timer_list poll_dma_timer;
+#define RX_DMA_TIMER (jiffies + 10*HZ/1000)
};
@@ -226,9 +255,9 @@ struct mac_serial {
#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */
#define INT_ERR_Rx 0x18 /* Int on error only */
-#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */
-#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */
-#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */
+#define WT_RDY_RT 0x20 /* W/Req reflects recv if 1, xmit if 0 */
+#define WT_FN_RDYFN 0x40 /* W/Req pin is DMA request if 1, wait if 0 */
+#define WT_RDY_ENAB 0x80 /* Enable W/Req pin */
/* Write Register #2 (Interrupt Vector) */
@@ -286,6 +315,9 @@ struct mac_serial {
/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
+/* Write Register 7' (Some enhanced feature control) */
+#define ENEXREAD 0x40 /* Enable read of some write registers */
+
/* Write Register 8 (transmit buffer) */
/* Write Register 9 (Master interrupt control) */
@@ -346,7 +378,9 @@ struct mac_serial {
#define SNRZI 0xe0 /* Set NRZI mode */
/* Write Register 15 (external/status interrupt control) */
+#define EN85C30 1 /* Enable some 85c30-enhanced registers */
#define ZCIE 2 /* Zero count IE */
+#define ENSTFIFO 4 /* Enable status FIFO (SDLC) */
#define DCDIE 8 /* DCD IE */
#define SYNCIE 0x10 /* Sync/hunt IE */
#define CTSIE 0x20 /* CTS IE */
@@ -382,6 +416,15 @@ struct mac_serial {
#define END_FR 0x80 /* End of Frame (SDLC) */
/* Read Register 2 (channel b only) - Interrupt vector */
+#define CHB_Tx_EMPTY 0x00
+#define CHB_EXT_STAT 0x02
+#define CHB_Rx_AVAIL 0x04
+#define CHB_SPECIAL 0x06
+#define CHA_Tx_EMPTY 0x08
+#define CHA_EXT_STAT 0x0a
+#define CHA_Rx_AVAIL 0x0c
+#define CHA_SPECIAL 0x0e
+#define STATUS_MASK 0x06
/* Read Register 3 (interrupt pending register) ch a only */
#define CHBEXT 0x1 /* Channel B Ext/Stat IP */