diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-04-26 11:20:10 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-04-26 11:20:10 -0700 |
commit | d08410d8c9908058a2f69b55e24edfb0d19da7a1 (patch) | |
tree | 8e7a00baaa0d3b5198e5a5b4501991dd18740d51 /drivers | |
parent | 8900d92fd666d936a7bfb4c567ac26736a414fb4 (diff) | |
parent | 8720037d55dbfa3011b8795ca2187b00bb05ee03 (diff) | |
download | linux-d08410d8c9908058a2f69b55e24edfb0d19da7a1.tar.bz2 |
Merge tag 'tty-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty and serial driver updates from Greg KH:
"Here is the big set of tty and serial driver updates for 5.13-rc1.
Actually busy this release, with a number of cleanups happening:
- much needed core tty cleanups by Jiri Slaby
- removal of unused and orphaned old-style serial drivers. If anyone
shows up with this hardware, it is trivial to restore these but we
really do not think they are in use anymore.
- fixes and cleanups from Johan Hovold on a number of termios setting
corner cases that loads of drivers got wrong as well as removing
unneeded code due to tty core changes from long ago that were never
propagated out to the drivers
- loads of platform-specific serial port driver updates and fixes
- coding style cleanups and other small fixes and updates all over
the tty/serial tree.
All of these have been in linux-next for a while now with no reported
issues"
* tag 'tty-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (186 commits)
serial: extend compile-test coverage
serial: stm32: add FIFO threshold configuration
dt-bindings: serial: 8250: update TX FIFO trigger level
dt-bindings: serial: stm32: override FIFO threshold properties
dt-bindings: serial: add RX and TX FIFO properties
serial: xilinx_uartps: drop low-latency workaround
serial: vt8500: drop low-latency workaround
serial: timbuart: drop low-latency workaround
serial: sunsu: drop low-latency workaround
serial: sifive: drop low-latency workaround
serial: txx9: drop low-latency workaround
serial: sa1100: drop low-latency workaround
serial: rp2: drop low-latency workaround
serial: rda: drop low-latency workaround
serial: owl: drop low-latency workaround
serial: msm_serial: drop low-latency workaround
serial: mpc52xx_uart: drop low-latency workaround
serial: meson: drop low-latency workaround
serial: mcf: drop low-latency workaround
serial: lpc32xx_hs: drop low-latency workaround
...
Diffstat (limited to 'drivers')
105 files changed, 2405 insertions, 11248 deletions
diff --git a/drivers/accessibility/speakup/spk_ttyio.c b/drivers/accessibility/speakup/spk_ttyio.c index 9af1d4c124d3..2e39fcf492d8 100644 --- a/drivers/accessibility/speakup/spk_ttyio.c +++ b/drivers/accessibility/speakup/spk_ttyio.c @@ -104,7 +104,6 @@ static int spk_ttyio_receive_buf2(struct tty_struct *tty, static struct tty_ldisc_ops spk_ttyio_ldisc_ops = { .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, .name = "speakup_ldisc", .open = spk_ttyio_ldisc_open, .close = spk_ttyio_ldisc_close, diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 637c5b8c2aa1..71a4ca505e09 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -821,7 +821,6 @@ static __poll_t hci_uart_tty_poll(struct tty_struct *tty, static struct tty_ldisc_ops hci_uart_ldisc = { .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, .name = "n_hci", .open = hci_uart_tty_open, .close = hci_uart_tty_close, diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 2be8d9a8eec5..3287a7627ed0 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -530,8 +530,6 @@ static int mgslpc_probe(struct pcmcia_device *link) info->port.ops = &mgslpc_port_ops; INIT_WORK(&info->task, bh_handler); info->max_frame_size = 4096; - info->port.close_delay = 5*HZ/10; - info->port.closing_wait = 30*HZ; init_waitqueue_head(&info->status_event_wait_q); init_waitqueue_head(&info->event_wait_q); spin_lock_init(&info->lock); diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c index 6a0059e508e3..93f5d11c830b 100644 --- a/drivers/char/ttyprintk.c +++ b/drivers/char/ttyprintk.c @@ -158,12 +158,23 @@ static int tpk_ioctl(struct tty_struct *tty, return 0; } +/* + * TTY operations hangup function. + */ +static void tpk_hangup(struct tty_struct *tty) +{ + struct ttyprintk_port *tpkp = tty->driver_data; + + tty_port_hangup(&tpkp->port); +} + static const struct tty_operations ttyprintk_ops = { .open = tpk_open, .close = tpk_close, .write = tpk_write, .write_room = tpk_write_room, .ioctl = tpk_ioctl, + .hangup = tpk_hangup, }; static const struct tty_port_operations null_ops = { }; diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index 5a0a5fc3d3ab..071844b58073 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -845,7 +845,6 @@ static void st_tty_flush_buffer(struct tty_struct *tty) } static struct tty_ldisc_ops st_ldisc_ops = { - .magic = TTY_LDISC_MAGIC, .name = "n_st", .open = st_tty_open, .close = st_tty_close, diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c index 8215cd77301f..da6fffb4d5a8 100644 --- a/drivers/net/caif/caif_serial.c +++ b/drivers/net/caif/caif_serial.c @@ -382,7 +382,6 @@ static void ldisc_close(struct tty_struct *tty) /* The line discipline structure. */ static struct tty_ldisc_ops caif_ldisc = { .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, .name = "n_caif", .open = ldisc_open, .close = ldisc_close, @@ -390,18 +389,6 @@ static struct tty_ldisc_ops caif_ldisc = { .write_wakeup = ldisc_tx_wakeup }; -static int register_ldisc(void) -{ - int result; - - result = tty_register_ldisc(N_CAIF, &caif_ldisc); - if (result < 0) { - pr_err("cannot register CAIF ldisc=%d err=%d\n", N_CAIF, - result); - return result; - } - return result; -} static const struct net_device_ops netdev_ops = { .ndo_open = caif_net_open, .ndo_stop = caif_net_close, @@ -444,7 +431,10 @@ static int __init caif_ser_init(void) { int ret; - ret = register_ldisc(); + ret = tty_register_ldisc(N_CAIF, &caif_ldisc); + if (ret < 0) + pr_err("cannot register CAIF ldisc=%d err=%d\n", N_CAIF, ret); + debugfsdir = debugfs_create_dir("caif_serial", NULL); return ret; } diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c index 30c8d53c9745..31ba6664503d 100644 --- a/drivers/net/can/slcan.c +++ b/drivers/net/can/slcan.c @@ -697,7 +697,6 @@ static int slcan_ioctl(struct tty_struct *tty, struct file *file, static struct tty_ldisc_ops slc_ldisc = { .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, .name = "slcan", .open = slcan_open, .close = slcan_close, diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 9f5b5614a150..80f41945709f 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -744,7 +744,6 @@ static int sixpack_ioctl(struct tty_struct *tty, struct file *file, static struct tty_ldisc_ops sp_ldisc = { .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, .name = "6pack", .open = sixpack_open, .close = sixpack_close, diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index 17be2bb2985c..65154224d5b8 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -933,7 +933,6 @@ out: static struct tty_ldisc_ops ax_ldisc = { .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, .name = "mkiss", .open = mkiss_open, .close = mkiss_close, diff --git a/drivers/net/ppp/ppp_async.c b/drivers/net/ppp/ppp_async.c index 2a91caa4f37b..8b41aa3fb64e 100644 --- a/drivers/net/ppp/ppp_async.c +++ b/drivers/net/ppp/ppp_async.c @@ -372,7 +372,6 @@ ppp_asynctty_wakeup(struct tty_struct *tty) static struct tty_ldisc_ops ppp_ldisc = { .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, .name = "ppp", .open = ppp_asynctty_open, .close = ppp_asynctty_close, diff --git a/drivers/net/ppp/ppp_synctty.c b/drivers/net/ppp/ppp_synctty.c index d8890923a9e3..576b6a93bf23 100644 --- a/drivers/net/ppp/ppp_synctty.c +++ b/drivers/net/ppp/ppp_synctty.c @@ -365,7 +365,6 @@ ppp_sync_wakeup(struct tty_struct *tty) static struct tty_ldisc_ops ppp_sync_ldisc = { .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, .name = "pppsync", .open = ppp_sync_open, .close = ppp_sync_close, diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c index f81fb0b13a94..1ab124eba8eb 100644 --- a/drivers/net/slip/slip.c +++ b/drivers/net/slip/slip.c @@ -1263,7 +1263,6 @@ static int sl_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) static struct tty_ldisc_ops sl_ldisc = { .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, .name = "slip", .open = slip_open, .close = slip_close, diff --git a/drivers/pps/clients/pps-ldisc.c b/drivers/pps/clients/pps-ldisc.c index 4fd0cbf7f931..bf26cc56b863 100644 --- a/drivers/pps/clients/pps-ldisc.c +++ b/drivers/pps/clients/pps-ldisc.c @@ -13,8 +13,6 @@ #include <linux/pps_kernel.h> #include <linux/bug.h> -#define PPS_TTY_MAGIC 0x0001 - static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status) { struct pps_device *pps; @@ -114,7 +112,6 @@ static int __init pps_tty_init(void) /* Init PPS_TTY data */ pps_ldisc_ops.owner = THIS_MODULE; - pps_ldisc_ops.magic = PPS_TTY_MAGIC; pps_ldisc_ops.name = "pps_tty"; pps_ldisc_ops.dcd_change = pps_tty_dcd_change; pps_ldisc_ops.open = pps_tty_open; diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 671efee612af..d26947d743bc 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -85,7 +85,6 @@ struct raw3215_info { int written; /* number of bytes in write requests */ struct raw3215_req *queued_read; /* pointer to queued read requests */ struct raw3215_req *queued_write;/* pointer to queued write requests */ - struct tasklet_struct tlet; /* tasklet to invoke tty_wakeup */ wait_queue_head_t empty_wait; /* wait queue for flushing */ struct timer_list timer; /* timer for delayed output */ int line_pos; /* position on the line (for tabs) */ @@ -330,21 +329,6 @@ static inline void raw3215_try_io(struct raw3215_info *raw) } /* - * Call tty_wakeup from tasklet context - */ -static void raw3215_wakeup(unsigned long data) -{ - struct raw3215_info *raw = (struct raw3215_info *) data; - struct tty_struct *tty; - - tty = tty_port_tty_get(&raw->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } -} - -/* * Try to start the next IO and wake up processes waiting on the tty. */ static void raw3215_next_io(struct raw3215_info *raw, struct tty_struct *tty) @@ -352,7 +336,7 @@ static void raw3215_next_io(struct raw3215_info *raw, struct tty_struct *tty) raw3215_mk_write_req(raw); raw3215_try_io(raw); if (tty && RAW3215_BUFFER_SIZE - raw->count >= RAW3215_MIN_SPACE) - tasklet_schedule(&raw->tlet); + tty_wakeup(tty); } /* @@ -644,7 +628,6 @@ static struct raw3215_info *raw3215_alloc_info(void) timer_setup(&info->timer, raw3215_timeout, 0); init_waitqueue_head(&info->empty_wait); - tasklet_init(&info->tlet, raw3215_wakeup, (unsigned long)info); tty_port_init(&info->port); return info; @@ -928,15 +911,13 @@ static int tty3215_open(struct tty_struct *tty, struct file * filp) */ static void tty3215_close(struct tty_struct *tty, struct file * filp) { - struct raw3215_info *raw; + struct raw3215_info *raw = tty->driver_data; - raw = (struct raw3215_info *) tty->driver_data; if (raw == NULL || tty->count > 1) return; tty->closing = 1; /* Shutdown the terminal */ raw3215_shutdown(raw); - tasklet_kill(&raw->tlet); tty->closing = 0; tty_port_tty_set(&raw->port, NULL); } @@ -946,9 +927,7 @@ static void tty3215_close(struct tty_struct *tty, struct file * filp) */ static int tty3215_write_room(struct tty_struct *tty) { - struct raw3215_info *raw; - - raw = (struct raw3215_info *) tty->driver_data; + struct raw3215_info *raw = tty->driver_data; /* Subtract TAB_STOP_SIZE to allow for a tab, 8 <<< 64K */ if ((RAW3215_BUFFER_SIZE - raw->count - TAB_STOP_SIZE) >= 0) @@ -963,12 +942,9 @@ static int tty3215_write_room(struct tty_struct *tty) static int tty3215_write(struct tty_struct * tty, const unsigned char *buf, int count) { - struct raw3215_info *raw; + struct raw3215_info *raw = tty->driver_data; int i, written; - if (!tty) - return 0; - raw = (struct raw3215_info *) tty->driver_data; written = count; while (count > 0) { for (i = 0; i < count; i++) @@ -991,12 +967,10 @@ static int tty3215_write(struct tty_struct * tty, */ static int tty3215_put_char(struct tty_struct *tty, unsigned char ch) { - struct raw3215_info *raw; + struct raw3215_info *raw = tty->driver_data; - if (!tty) - return 0; - raw = (struct raw3215_info *) tty->driver_data; raw3215_putchar(raw, ch); + return 1; } @@ -1009,17 +983,15 @@ static void tty3215_flush_chars(struct tty_struct *tty) */ static int tty3215_chars_in_buffer(struct tty_struct *tty) { - struct raw3215_info *raw; + struct raw3215_info *raw = tty->driver_data; - raw = (struct raw3215_info *) tty->driver_data; return raw->count; } static void tty3215_flush_buffer(struct tty_struct *tty) { - struct raw3215_info *raw; + struct raw3215_info *raw = tty->driver_data; - raw = (struct raw3215_info *) tty->driver_data; raw3215_flush_buffer(raw); tty_wakeup(tty); } @@ -1029,9 +1001,8 @@ static void tty3215_flush_buffer(struct tty_struct *tty) */ static void tty3215_throttle(struct tty_struct * tty) { - struct raw3215_info *raw; + struct raw3215_info *raw = tty->driver_data; - raw = (struct raw3215_info *) tty->driver_data; raw->flags |= RAW3215_THROTTLED; } @@ -1040,10 +1011,9 @@ static void tty3215_throttle(struct tty_struct * tty) */ static void tty3215_unthrottle(struct tty_struct * tty) { - struct raw3215_info *raw; + struct raw3215_info *raw = tty->driver_data; unsigned long flags; - raw = (struct raw3215_info *) tty->driver_data; if (raw->flags & RAW3215_THROTTLED) { spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); raw->flags &= ~RAW3215_THROTTLED; @@ -1057,9 +1027,8 @@ static void tty3215_unthrottle(struct tty_struct * tty) */ static void tty3215_stop(struct tty_struct *tty) { - struct raw3215_info *raw; + struct raw3215_info *raw = tty->driver_data; - raw = (struct raw3215_info *) tty->driver_data; raw->flags |= RAW3215_STOPPED; } @@ -1068,10 +1037,9 @@ static void tty3215_stop(struct tty_struct *tty) */ static void tty3215_start(struct tty_struct *tty) { - struct raw3215_info *raw; + struct raw3215_info *raw = tty->driver_data; unsigned long flags; - raw = (struct raw3215_info *) tty->driver_data; if (raw->flags & RAW3215_STOPPED) { spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); raw->flags &= ~RAW3215_STOPPED; diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c index 2cc461b26897..1ee6382cafc4 100644 --- a/drivers/staging/fwserial/fwserial.c +++ b/drivers/staging/fwserial/fwserial.c @@ -1218,13 +1218,12 @@ static int get_serial_info(struct tty_struct *tty, struct fwtty_port *port = tty->driver_data; mutex_lock(&port->port.mutex); - ss->type = PORT_UNKNOWN; - ss->line = port->port.tty->index; - ss->flags = port->port.flags; - ss->xmit_fifo_size = FWTTY_PORT_TXFIFO_LEN; + ss->line = port->index; ss->baud_base = 400000000; - ss->close_delay = port->port.close_delay; + ss->close_delay = jiffies_to_msecs(port->port.close_delay) / 10; + ss->closing_wait = 3000; mutex_unlock(&port->port.mutex); + return 0; } @@ -1232,20 +1231,20 @@ static int set_serial_info(struct tty_struct *tty, struct serial_struct *ss) { struct fwtty_port *port = tty->driver_data; + unsigned int cdelay; - if (ss->irq != 0 || ss->port != 0 || ss->custom_divisor != 0 || - ss->baud_base != 400000000) - return -EPERM; + cdelay = msecs_to_jiffies(ss->close_delay * 10); mutex_lock(&port->port.mutex); if (!capable(CAP_SYS_ADMIN)) { - if (((ss->flags & ~ASYNC_USR_MASK) != + if (cdelay != port->port.close_delay || + ((ss->flags & ~ASYNC_USR_MASK) != (port->port.flags & ~ASYNC_USR_MASK))) { mutex_unlock(&port->port.mutex); return -EPERM; } } - port->port.close_delay = ss->close_delay * HZ / 100; + port->port.close_delay = cdelay; mutex_unlock(&port->port.mutex); return 0; diff --git a/drivers/staging/gdm724x/gdm_tty.c b/drivers/staging/gdm724x/gdm_tty.c index 6e813693a766..0ccc8c24e754 100644 --- a/drivers/staging/gdm724x/gdm_tty.c +++ b/drivers/staging/gdm724x/gdm_tty.c @@ -188,7 +188,7 @@ static int gdm_tty_write_room(struct tty_struct *tty) struct gdm *gdm = tty->driver_data; if (!GDM_TTY_READY(gdm)) - return -ENODEV; + return 0; return WRITE_SIZE; } diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 607378bfebb7..b1e63f7798b0 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -610,14 +610,13 @@ static int get_serial_info(struct tty_struct *tty, { struct gb_tty *gb_tty = tty->driver_data; - ss->type = PORT_16550A; ss->line = gb_tty->minor; - ss->xmit_fifo_size = 16; - ss->baud_base = 9600; - ss->close_delay = gb_tty->port.close_delay / 10; + ss->close_delay = jiffies_to_msecs(gb_tty->port.close_delay) / 10; ss->closing_wait = gb_tty->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? - ASYNC_CLOSING_WAIT_NONE : gb_tty->port.closing_wait / 10; + ASYNC_CLOSING_WAIT_NONE : + jiffies_to_msecs(gb_tty->port.closing_wait) / 10; + return 0; } @@ -629,17 +628,16 @@ static int set_serial_info(struct tty_struct *tty, unsigned int close_delay; int retval = 0; - close_delay = ss->close_delay * 10; + close_delay = msecs_to_jiffies(ss->close_delay * 10); closing_wait = ss->closing_wait == ASYNC_CLOSING_WAIT_NONE ? - ASYNC_CLOSING_WAIT_NONE : ss->closing_wait * 10; + ASYNC_CLOSING_WAIT_NONE : + msecs_to_jiffies(ss->closing_wait * 10); mutex_lock(&gb_tty->port.mutex); if (!capable(CAP_SYS_ADMIN)) { if ((close_delay != gb_tty->port.close_delay) || (closing_wait != gb_tty->port.closing_wait)) retval = -EPERM; - else - retval = -EOPNOTSUPP; } else { gb_tty->port.close_delay = close_delay; gb_tty->port.closing_wait = closing_wait; diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index e15cd6b5bb99..f6a7fd6d3bb6 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -181,7 +181,7 @@ config SERIAL_NONSTANDARD help Say Y here if you have any non-standard serial boards -- boards which aren't supported using the standard "dumb" serial driver. - This includes intelligent serial boards such as Cyclades, + This includes intelligent serial boards such as Digiboards, etc. These are usually used for systems that need many serial ports because they serve many terminals or dial-in connections. @@ -192,50 +192,6 @@ config SERIAL_NONSTANDARD Most people can say N here. -config ROCKETPORT - tristate "Comtrol RocketPort support" - depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) - help - This driver supports Comtrol RocketPort and RocketModem PCI boards. - These boards provide 2, 4, 8, 16, or 32 high-speed serial ports or - modems. For information about the RocketPort/RocketModem boards - and this driver read <file:Documentation/driver-api/serial/rocket.rst>. - - To compile this driver as a module, choose M here: the - module will be called rocket. - - If you want to compile this driver into the kernel, say Y here. If - you don't have a Comtrol RocketPort/RocketModem card installed, say N. - -config CYCLADES - tristate "Cyclades async mux support" - depends on SERIAL_NONSTANDARD && (PCI || ISA) - select FW_LOADER - help - This driver supports Cyclades Z and Y multiserial boards. - You would need something like this to connect more than two modems to - your Linux box, for instance in order to become a dial-in server. - - For information about the Cyclades-Z card, read - <file:Documentation/driver-api/serial/cyclades_z.rst>. - - To compile this driver as a module, choose M here: the - module will be called cyclades. - - If you haven't heard about it, it's safe to say N. - -config CYZ_INTR - bool "Cyclades-Z interrupt mode operation" - depends on CYCLADES && PCI - help - The Cyclades-Z family of multiport cards allows 2 (two) driver op - modes: polling and interrupt. In polling mode, the driver will check - the status of the Cyclades-Z ports every certain amount of time - (which is called polling cycle and is configurable). In interrupt - mode, it will use an interrupt line (IRQ) in order to check the - status of the Cyclades-Z ports. The default op mode is polling. If - unsure, say N. - config MOXA_INTELLIO tristate "Moxa Intellio support" depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) @@ -267,16 +223,6 @@ config SYNCLINK_GT synchronous and asynchronous serial adapters manufactured by Microgate Systems, Ltd. (www.microgate.com) -config ISI - tristate "Multi-Tech multiport card support" - depends on SERIAL_NONSTANDARD && PCI - select FW_LOADER - help - This is a driver for the Multi-Tech cards which provide several - serial ports. The driver is experimental and can currently only be - built as a module. The module will be called isicom. - If you want to do that, choose M here. - config N_HDLC tristate "HDLC line discipline support" depends on SERIAL_NONSTANDARD @@ -415,36 +361,6 @@ config NULL_TTY If unsure, say N. -config TRACE_ROUTER - tristate "Trace data router for MIPI P1149.7 cJTAG standard" - depends on TRACE_SINK - help - The trace router uses the Linux tty line discipline framework to - route trace data coming from a tty port (say UART for example) to - the trace sink line discipline driver and to another tty port (say - USB). This is part of a solution for the MIPI P1149.7, compact JTAG, - standard, which is for debugging mobile devices. The PTI driver in - drivers/misc/pti.c defines the majority of this MIPI solution. - - You should select this driver if the target kernel is meant for - a mobile device containing a modem. Then you will need to select - "Trace data sink for MIPI P1149.7 cJTAG standard" line discipline - driver. - -config TRACE_SINK - tristate "Trace data sink for MIPI P1149.7 cJTAG standard" - help - The trace sink uses the Linux line discipline framework to receive - trace data coming from the trace router line discipline driver - to a user-defined tty port target, like USB. - This is to provide a way to extract modem trace data on - devices that do not have a PTI HW module, or just need modem - trace data to come out of a different HW output port. - This is part of a solution for the P1149.7, compact JTAG, standard. - - If you select this option, you need to select - "Trace data router for MIPI P1149.7 cJTAG standard". - config VCC tristate "Sun Virtual Console Concentrator" depends on SUN_LDOMS diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 730de6bf048b..c7054f5117c3 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -18,13 +18,10 @@ obj-$(CONFIG_SERIAL_DEV_BUS) += serdev/ # tty drivers obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o -obj-$(CONFIG_CYCLADES) += cyclades.o -obj-$(CONFIG_ISI) += isicom.o obj-$(CONFIG_MOXA_INTELLIO) += moxa.o obj-$(CONFIG_MOXA_SMARTIO) += mxser.o obj-$(CONFIG_NOZOMI) += nozomi.o obj-$(CONFIG_NULL_TTY) += ttynull.o -obj-$(CONFIG_ROCKETPORT) += rocket.o obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 18b78ea110ef..ca48ce5a0862 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -937,15 +937,21 @@ static void rs_unthrottle(struct tty_struct * tty) static int get_serial_info(struct tty_struct *tty, struct serial_struct *ss) { struct serial_state *state = tty->driver_data; + unsigned int close_delay, closing_wait; tty_lock(tty); + close_delay = jiffies_to_msecs(state->tport.close_delay) / 10; + closing_wait = state->tport.closing_wait; + if (closing_wait != ASYNC_CLOSING_WAIT_NONE) + closing_wait = jiffies_to_msecs(closing_wait) / 10; + ss->line = tty->index; ss->port = state->port; ss->flags = state->tport.flags; ss->xmit_fifo_size = state->xmit_fifo_size; ss->baud_base = state->baud_base; - ss->close_delay = state->tport.close_delay; - ss->closing_wait = state->tport.closing_wait; + ss->close_delay = close_delay; + ss->closing_wait = closing_wait; ss->custom_divisor = state->custom_divisor; tty_unlock(tty); return 0; @@ -957,6 +963,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_struct *ss) struct tty_port *port = &state->tport; bool change_spd; int retval = 0; + unsigned int close_delay, closing_wait; tty_lock(tty); change_spd = ((ss->flags ^ port->flags) & ASYNC_SPD_MASK) || @@ -966,10 +973,16 @@ static int set_serial_info(struct tty_struct *tty, struct serial_struct *ss) tty_unlock(tty); return -EINVAL; } - + + close_delay = msecs_to_jiffies(ss->close_delay * 10); + closing_wait = ss->closing_wait; + if (closing_wait != ASYNC_CLOSING_WAIT_NONE) + closing_wait = msecs_to_jiffies(closing_wait * 10); + if (!serial_isroot()) { if ((ss->baud_base != state->baud_base) || - (ss->close_delay != port->close_delay) || + (close_delay != port->close_delay) || + (closing_wait != port->closing_wait) || (ss->xmit_fifo_size != state->xmit_fifo_size) || ((ss->flags & ~ASYNC_USR_MASK) != (port->flags & ~ASYNC_USR_MASK))) { @@ -996,8 +1009,8 @@ static int set_serial_info(struct tty_struct *tty, struct serial_struct *ss) port->flags = ((port->flags & ~ASYNC_FLAGS) | (ss->flags & ASYNC_FLAGS)); state->custom_divisor = ss->custom_divisor; - port->close_delay = ss->close_delay * HZ/100; - port->closing_wait = ss->closing_wait * HZ/100; + port->close_delay = close_delay; + port->closing_wait = closing_wait; check_and_exit: if (tty_port_initialized(port)) { @@ -1622,21 +1635,17 @@ fail_put_tty_driver: static int __exit amiga_serial_remove(struct platform_device *pdev) { - int error; struct serial_state *state = platform_get_drvdata(pdev); /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ - error = tty_unregister_driver(serial_driver); - if (error) - printk("SERIAL: failed to unregister serial driver (%d)\n", - error); + tty_unregister_driver(serial_driver); put_tty_driver(serial_driver); tty_port_destroy(&state->tport); free_irq(IRQ_AMIGA_TBE, state); free_irq(IRQ_AMIGA_RBF, state); - return error; + return 0; } static struct platform_driver amiga_serial_driver = { diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c deleted file mode 100644 index 097266342e5e..000000000000 --- a/drivers/tty/cyclades.c +++ /dev/null @@ -1,4119 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#undef BLOCKMOVE -#define Z_WAKE -#undef Z_EXT_CHARS_IN_BUFFER - -/* - * This file contains the driver for the Cyclades async multiport - * serial boards. - * - * Initially written by Randolph Bentson <bentson@grieg.seaslug.org>. - * Modified and maintained by Marcio Saito <marcio@cyclades.com>. - * - * Copyright (C) 2007-2009 Jiri Slaby <jirislaby@gmail.com> - * - * Much of the design and some of the code came from serial.c - * which was copyright (C) 1991, 1992 Linus Torvalds. It was - * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92, - * and then fixed as suggested by Michael K. Johnson 12/12/92. - * Converted to pci probing and cleaned up by Jiri Slaby. - * - */ - -#define CY_VERSION "2.6" - -/* If you need to install more boards than NR_CARDS, change the constant - in the definition below. No other change is necessary to support up to - eight boards. Beyond that you'll have to extend cy_isa_addresses. */ - -#define NR_CARDS 4 - -/* - If the total number of ports is larger than NR_PORTS, change this - constant in the definition below. No other change is necessary to - support more boards/ports. */ - -#define NR_PORTS 256 - -#define ZO_V1 0 -#define ZO_V2 1 -#define ZE_V1 2 - -#define SERIAL_PARANOIA_CHECK -#undef CY_DEBUG_OPEN -#undef CY_DEBUG_THROTTLE -#undef CY_DEBUG_OTHER -#undef CY_DEBUG_IO -#undef CY_DEBUG_COUNT -#undef CY_DEBUG_DTR -#undef CY_DEBUG_INTERRUPTS -#undef CY_16Y_HACK -#undef CY_ENABLE_MONITORING -#undef CY_PCI_DEBUG - -/* - * Include section - */ -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/serial.h> -#include <linux/major.h> -#include <linux/string.h> -#include <linux/fcntl.h> -#include <linux/ptrace.h> -#include <linux/cyclades.h> -#include <linux/mm.h> -#include <linux/ioport.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/spinlock.h> -#include <linux/bitops.h> -#include <linux/firmware.h> -#include <linux/device.h> -#include <linux/slab.h> - -#include <linux/io.h> -#include <linux/uaccess.h> - -#include <linux/kernel.h> -#include <linux/pci.h> - -#include <linux/stat.h> -#include <linux/proc_fs.h> -#include <linux/seq_file.h> - -static void cy_send_xchar(struct tty_struct *tty, char ch); - -#ifndef SERIAL_XMIT_SIZE -#define SERIAL_XMIT_SIZE (min(PAGE_SIZE, 4096)) -#endif - -/* firmware stuff */ -#define ZL_MAX_BLOCKS 16 -#define DRIVER_VERSION 0x02010203 -#define RAM_SIZE 0x80000 - -enum zblock_type { - ZBLOCK_PRG = 0, - ZBLOCK_FPGA = 1 -}; - -struct zfile_header { - char name[64]; - char date[32]; - char aux[32]; - u32 n_config; - u32 config_offset; - u32 n_blocks; - u32 block_offset; - u32 reserved[9]; -} __attribute__ ((packed)); - -struct zfile_config { - char name[64]; - u32 mailbox; - u32 function; - u32 n_blocks; - u32 block_list[ZL_MAX_BLOCKS]; -} __attribute__ ((packed)); - -struct zfile_block { - u32 type; - u32 file_offset; - u32 ram_offset; - u32 size; -} __attribute__ ((packed)); - -static struct tty_driver *cy_serial_driver; - -#ifdef CONFIG_ISA -/* This is the address lookup table. The driver will probe for - Cyclom-Y/ISA boards at all addresses in here. If you want the - driver to probe addresses at a different address, add it to - this table. If the driver is probing some other board and - causing problems, remove the offending address from this table. -*/ - -static unsigned int cy_isa_addresses[] = { - 0xD0000, - 0xD2000, - 0xD4000, - 0xD6000, - 0xD8000, - 0xDA000, - 0xDC000, - 0xDE000, - 0, 0, 0, 0, 0, 0, 0, 0 -}; - -#define NR_ISA_ADDRS ARRAY_SIZE(cy_isa_addresses) - -static long maddr[NR_CARDS]; -static int irq[NR_CARDS]; - -module_param_hw_array(maddr, long, iomem, NULL, 0); -module_param_hw_array(irq, int, irq, NULL, 0); - -#endif /* CONFIG_ISA */ - -/* This is the per-card data structure containing address, irq, number of - channels, etc. This driver supports a maximum of NR_CARDS cards. -*/ -static struct cyclades_card cy_card[NR_CARDS]; - -static int cy_next_channel; /* next minor available */ - -/* - * This is used to look up the divisor speeds and the timeouts - * We're normally limited to 15 distinct baud rates. The extra - * are accessed via settings in info->port.flags. - * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - * HI VHI - * 20 - */ -static const int baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, - 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 150000, - 230400, 0 -}; - -static const char baud_co_25[] = { /* 25 MHz clock option table */ - /* value => 00 01 02 03 04 */ - /* divide by 8 32 128 512 2048 */ - 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, - 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -static const char baud_bpr_25[] = { /* 25 MHz baud rate period table */ - 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, - 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15 -}; - -static const char baud_co_60[] = { /* 60 MHz clock option table (CD1400 J) */ - /* value => 00 01 02 03 04 */ - /* divide by 8 32 128 512 2048 */ - 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, - 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00 -}; - -static const char baud_bpr_60[] = { /* 60 MHz baud rate period table (CD1400 J) */ - 0x00, 0x82, 0x21, 0xff, 0xdb, 0xc3, 0x92, 0x62, 0xc3, 0x62, - 0x41, 0xc3, 0x62, 0xc3, 0x62, 0xc3, 0x82, 0x62, 0x41, 0x32, - 0x21 -}; - -static const char baud_cor3[] = { /* receive threshold */ - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07, - 0x07 -}; - -/* - * The Cyclades driver implements HW flow control as any serial driver. - * The cyclades_port structure member rflow and the vector rflow_thr - * allows us to take advantage of a special feature in the CD1400 to avoid - * data loss even when the system interrupt latency is too high. These flags - * are to be used only with very special applications. Setting these flags - * requires the use of a special cable (DTR and RTS reversed). In the new - * CD1400-based boards (rev. 6.00 or later), there is no need for special - * cables. - */ - -static const char rflow_thr[] = { /* rflow threshold */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a -}; - -/* The Cyclom-Ye has placed the sequential chips in non-sequential - * address order. This look-up table overcomes that problem. - */ -static const unsigned int cy_chip_offset[] = { 0x0000, - 0x0400, - 0x0800, - 0x0C00, - 0x0200, - 0x0600, - 0x0A00, - 0x0E00 -}; - -/* PCI related definitions */ - -#ifdef CONFIG_PCI -static const struct pci_device_id cy_pci_dev_id[] = { - /* PCI < 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Lo) }, - /* PCI > 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Hi) }, - /* 4Y PCI < 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Lo) }, - /* 4Y PCI > 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Hi) }, - /* 8Y PCI < 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Lo) }, - /* 8Y PCI > 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Hi) }, - /* Z PCI < 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Lo) }, - /* Z PCI > 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Hi) }, - { } /* end of table */ -}; -MODULE_DEVICE_TABLE(pci, cy_pci_dev_id); -#endif - -static void cy_start(struct tty_struct *); -static void cy_set_line_char(struct cyclades_port *, struct tty_struct *); -static int cyz_issue_cmd(struct cyclades_card *, __u32, __u8, __u32); -#ifdef CONFIG_ISA -static unsigned detect_isa_irq(void __iomem *); -#endif /* CONFIG_ISA */ - -#ifndef CONFIG_CYZ_INTR -static void cyz_poll(struct timer_list *); - -/* The Cyclades-Z polling cycle is defined by this variable */ -static long cyz_polling_cycle = CZ_DEF_POLL; - -static DEFINE_TIMER(cyz_timerlist, cyz_poll); - -#else /* CONFIG_CYZ_INTR */ -static void cyz_rx_restart(struct timer_list *); -#endif /* CONFIG_CYZ_INTR */ - -static void cyy_writeb(struct cyclades_port *port, u32 reg, u8 val) -{ - struct cyclades_card *card = port->card; - - cy_writeb(port->u.cyy.base_addr + (reg << card->bus_index), val); -} - -static u8 cyy_readb(struct cyclades_port *port, u32 reg) -{ - struct cyclades_card *card = port->card; - - return readb(port->u.cyy.base_addr + (reg << card->bus_index)); -} - -static inline bool cy_is_Z(struct cyclades_card *card) -{ - return card->num_chips == (unsigned int)-1; -} - -static inline bool __cyz_fpga_loaded(struct RUNTIME_9060 __iomem *ctl_addr) -{ - return readl(&ctl_addr->init_ctrl) & (1 << 17); -} - -static inline bool cyz_fpga_loaded(struct cyclades_card *card) -{ - return __cyz_fpga_loaded(card->ctl_addr.p9060); -} - -static bool cyz_is_loaded(struct cyclades_card *card) -{ - struct FIRM_ID __iomem *fw_id = card->base_addr + ID_ADDRESS; - - return (card->hw_ver == ZO_V1 || cyz_fpga_loaded(card)) && - readl(&fw_id->signature) == ZFIRM_ID; -} - -static int serial_paranoia_check(struct cyclades_port *info, - const char *name, const char *routine) -{ -#ifdef SERIAL_PARANOIA_CHECK - if (!info) { - printk(KERN_WARNING "cyc Warning: null cyclades_port for (%s) " - "in %s\n", name, routine); - return 1; - } - - if (info->magic != CYCLADES_MAGIC) { - printk(KERN_WARNING "cyc Warning: bad magic number for serial " - "struct (%s) in %s\n", name, routine); - return 1; - } -#endif - return 0; -} - -/***********************************************************/ -/********* Start of block of Cyclom-Y specific code ********/ - -/* This routine waits up to 1000 micro-seconds for the previous - command to the Cirrus chip to complete and then issues the - new command. An error is returned if the previous command - didn't finish within the time limit. - - This function is only called from inside spinlock-protected code. - */ -static int __cyy_issue_cmd(void __iomem *base_addr, u8 cmd, int index) -{ - void __iomem *ccr = base_addr + (CyCCR << index); - unsigned int i; - - /* Check to see that the previous command has completed */ - for (i = 0; i < 100; i++) { - if (readb(ccr) == 0) - break; - udelay(10L); - } - /* if the CCR never cleared, the previous command - didn't finish within the "reasonable time" */ - if (i == 100) - return -1; - - /* Issue the new command */ - cy_writeb(ccr, cmd); - - return 0; -} - -static inline int cyy_issue_cmd(struct cyclades_port *port, u8 cmd) -{ - return __cyy_issue_cmd(port->u.cyy.base_addr, cmd, - port->card->bus_index); -} - -#ifdef CONFIG_ISA -/* ISA interrupt detection code */ -static unsigned detect_isa_irq(void __iomem *address) -{ - int irq; - unsigned long irqs, flags; - int save_xir, save_car; - int index = 0; /* IRQ probing is only for ISA */ - - /* forget possible initially masked and pending IRQ */ - irq = probe_irq_off(probe_irq_on()); - - /* Clear interrupts on the board first */ - cy_writeb(address + (Cy_ClrIntr << index), 0); - /* Cy_ClrIntr is 0x1800 */ - - irqs = probe_irq_on(); - /* Wait ... */ - msleep(5); - - /* Enable the Tx interrupts on the CD1400 */ - local_irq_save(flags); - cy_writeb(address + (CyCAR << index), 0); - __cyy_issue_cmd(address, CyCHAN_CTL | CyENB_XMTR, index); - - cy_writeb(address + (CyCAR << index), 0); - cy_writeb(address + (CySRER << index), - readb(address + (CySRER << index)) | CyTxRdy); - local_irq_restore(flags); - - /* Wait ... */ - msleep(5); - - /* Check which interrupt is in use */ - irq = probe_irq_off(irqs); - - /* Clean up */ - save_xir = (u_char) readb(address + (CyTIR << index)); - save_car = readb(address + (CyCAR << index)); - cy_writeb(address + (CyCAR << index), (save_xir & 0x3)); - cy_writeb(address + (CySRER << index), - readb(address + (CySRER << index)) & ~CyTxRdy); - cy_writeb(address + (CyTIR << index), (save_xir & 0x3f)); - cy_writeb(address + (CyCAR << index), (save_car)); - cy_writeb(address + (Cy_ClrIntr << index), 0); - /* Cy_ClrIntr is 0x1800 */ - - return (irq > 0) ? irq : 0; -} -#endif /* CONFIG_ISA */ - -static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, - void __iomem *base_addr) -{ - struct cyclades_port *info; - struct tty_port *port; - int len, index = cinfo->bus_index; - u8 ivr, save_xir, channel, save_car, data, char_count; - -#ifdef CY_DEBUG_INTERRUPTS - printk(KERN_DEBUG "cyy_interrupt: rcvd intr, chip %d\n", chip); -#endif - /* determine the channel & change to that context */ - save_xir = readb(base_addr + (CyRIR << index)); - channel = save_xir & CyIRChannel; - info = &cinfo->ports[channel + chip * 4]; - port = &info->port; - save_car = cyy_readb(info, CyCAR); - cyy_writeb(info, CyCAR, save_xir); - ivr = cyy_readb(info, CyRIVR) & CyIVRMask; - - /* there is an open port for this data */ - if (ivr == CyIVRRxEx) { /* exception */ - data = cyy_readb(info, CyRDSR); - - /* For statistics only */ - if (data & CyBREAK) - info->icount.brk++; - else if (data & CyFRAME) - info->icount.frame++; - else if (data & CyPARITY) - info->icount.parity++; - else if (data & CyOVERRUN) - info->icount.overrun++; - - if (data & info->ignore_status_mask) { - info->icount.rx++; - return; - } - if (tty_buffer_request_room(port, 1)) { - if (data & info->read_status_mask) { - if (data & CyBREAK) { - tty_insert_flip_char(port, - cyy_readb(info, CyRDSR), - TTY_BREAK); - info->icount.rx++; - if (port->flags & ASYNC_SAK) { - struct tty_struct *tty = - tty_port_tty_get(port); - if (tty) { - do_SAK(tty); - tty_kref_put(tty); - } - } - } else if (data & CyFRAME) { - tty_insert_flip_char(port, - cyy_readb(info, CyRDSR), - TTY_FRAME); - info->icount.rx++; - info->idle_stats.frame_errs++; - } else if (data & CyPARITY) { - /* Pieces of seven... */ - tty_insert_flip_char(port, - cyy_readb(info, CyRDSR), - TTY_PARITY); - info->icount.rx++; - info->idle_stats.parity_errs++; - } else if (data & CyOVERRUN) { - tty_insert_flip_char(port, 0, - TTY_OVERRUN); - info->icount.rx++; - /* If the flip buffer itself is - overflowing, we still lose - the next incoming character. - */ - tty_insert_flip_char(port, - cyy_readb(info, CyRDSR), - TTY_FRAME); - info->icount.rx++; - info->idle_stats.overruns++; - /* These two conditions may imply */ - /* a normal read should be done. */ - /* } else if(data & CyTIMEOUT) { */ - /* } else if(data & CySPECHAR) { */ - } else { - tty_insert_flip_char(port, 0, - TTY_NORMAL); - info->icount.rx++; - } - } else { - tty_insert_flip_char(port, 0, TTY_NORMAL); - info->icount.rx++; - } - } else { - /* there was a software buffer overrun and nothing - * could be done about it!!! */ - info->icount.buf_overrun++; - info->idle_stats.overruns++; - } - } else { /* normal character reception */ - /* load # chars available from the chip */ - char_count = cyy_readb(info, CyRDCR); - -#ifdef CY_ENABLE_MONITORING - ++info->mon.int_count; - info->mon.char_count += char_count; - if (char_count > info->mon.char_max) - info->mon.char_max = char_count; - info->mon.char_last = char_count; -#endif - len = tty_buffer_request_room(port, char_count); - while (len--) { - data = cyy_readb(info, CyRDSR); - tty_insert_flip_char(port, data, TTY_NORMAL); - info->idle_stats.recv_bytes++; - info->icount.rx++; -#ifdef CY_16Y_HACK - udelay(10L); -#endif - } - info->idle_stats.recv_idle = jiffies; - } - tty_schedule_flip(port); - - /* end of service */ - cyy_writeb(info, CyRIR, save_xir & 0x3f); - cyy_writeb(info, CyCAR, save_car); -} - -static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, - void __iomem *base_addr) -{ - struct cyclades_port *info; - struct tty_struct *tty; - int char_count, index = cinfo->bus_index; - u8 save_xir, channel, save_car, outch; - - /* Since we only get here when the transmit buffer - is empty, we know we can always stuff a dozen - characters. */ -#ifdef CY_DEBUG_INTERRUPTS - printk(KERN_DEBUG "cyy_interrupt: xmit intr, chip %d\n", chip); -#endif - - /* determine the channel & change to that context */ - save_xir = readb(base_addr + (CyTIR << index)); - channel = save_xir & CyIRChannel; - save_car = readb(base_addr + (CyCAR << index)); - cy_writeb(base_addr + (CyCAR << index), save_xir); - - info = &cinfo->ports[channel + chip * 4]; - tty = tty_port_tty_get(&info->port); - if (tty == NULL) { - cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy); - goto end; - } - - /* load the on-chip space for outbound data */ - char_count = info->xmit_fifo_size; - - if (info->x_char) { /* send special char */ - outch = info->x_char; - cyy_writeb(info, CyTDR, outch); - char_count--; - info->icount.tx++; - info->x_char = 0; - } - - if (info->breakon || info->breakoff) { - if (info->breakon) { - cyy_writeb(info, CyTDR, 0); - cyy_writeb(info, CyTDR, 0x81); - info->breakon = 0; - char_count -= 2; - } - if (info->breakoff) { - cyy_writeb(info, CyTDR, 0); - cyy_writeb(info, CyTDR, 0x83); - info->breakoff = 0; - char_count -= 2; - } - } - - while (char_count-- > 0) { - if (!info->xmit_cnt) { - if (cyy_readb(info, CySRER) & CyTxMpty) { - cyy_writeb(info, CySRER, - cyy_readb(info, CySRER) & ~CyTxMpty); - } else { - cyy_writeb(info, CySRER, CyTxMpty | - (cyy_readb(info, CySRER) & ~CyTxRdy)); - } - goto done; - } - if (info->port.xmit_buf == NULL) { - cyy_writeb(info, CySRER, - cyy_readb(info, CySRER) & ~CyTxRdy); - goto done; - } - if (tty->stopped || tty->hw_stopped) { - cyy_writeb(info, CySRER, - cyy_readb(info, CySRER) & ~CyTxRdy); - goto done; - } - /* Because the Embedded Transmit Commands have been enabled, - * we must check to see if the escape character, NULL, is being - * sent. If it is, we must ensure that there is room for it to - * be doubled in the output stream. Therefore we no longer - * advance the pointer when the character is fetched, but - * rather wait until after the check for a NULL output - * character. This is necessary because there may not be room - * for the two chars needed to send a NULL.) - */ - outch = info->port.xmit_buf[info->xmit_tail]; - if (outch) { - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) & - (SERIAL_XMIT_SIZE - 1); - cyy_writeb(info, CyTDR, outch); - info->icount.tx++; - } else { - if (char_count > 1) { - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) & - (SERIAL_XMIT_SIZE - 1); - cyy_writeb(info, CyTDR, outch); - cyy_writeb(info, CyTDR, 0); - info->icount.tx++; - char_count--; - } - } - } - -done: - tty_wakeup(tty); - tty_kref_put(tty); -end: - /* end of service */ - cyy_writeb(info, CyTIR, save_xir & 0x3f); - cyy_writeb(info, CyCAR, save_car); -} - -static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, - void __iomem *base_addr) -{ - struct cyclades_port *info; - struct tty_struct *tty; - int index = cinfo->bus_index; - u8 save_xir, channel, save_car, mdm_change, mdm_status; - - /* determine the channel & change to that context */ - save_xir = readb(base_addr + (CyMIR << index)); - channel = save_xir & CyIRChannel; - info = &cinfo->ports[channel + chip * 4]; - save_car = cyy_readb(info, CyCAR); - cyy_writeb(info, CyCAR, save_xir); - - mdm_change = cyy_readb(info, CyMISR); - mdm_status = cyy_readb(info, CyMSVR1); - - tty = tty_port_tty_get(&info->port); - if (!tty) - goto end; - - if (mdm_change & CyANY_DELTA) { - /* For statistics only */ - if (mdm_change & CyDCD) - info->icount.dcd++; - if (mdm_change & CyCTS) - info->icount.cts++; - if (mdm_change & CyDSR) - info->icount.dsr++; - if (mdm_change & CyRI) - info->icount.rng++; - - wake_up_interruptible(&info->port.delta_msr_wait); - } - - if ((mdm_change & CyDCD) && tty_port_check_carrier(&info->port)) { - if (mdm_status & CyDCD) - wake_up_interruptible(&info->port.open_wait); - else - tty_hangup(tty); - } - if ((mdm_change & CyCTS) && tty_port_cts_enabled(&info->port)) { - if (tty->hw_stopped) { - if (mdm_status & CyCTS) { - /* cy_start isn't used - because... !!! */ - tty->hw_stopped = 0; - cyy_writeb(info, CySRER, - cyy_readb(info, CySRER) | CyTxRdy); - tty_wakeup(tty); - } - } else { - if (!(mdm_status & CyCTS)) { - /* cy_stop isn't used - because ... !!! */ - tty->hw_stopped = 1; - cyy_writeb(info, CySRER, - cyy_readb(info, CySRER) & ~CyTxRdy); - } - } - } -/* if (mdm_change & CyDSR) { - } - if (mdm_change & CyRI) { - }*/ - tty_kref_put(tty); -end: - /* end of service */ - cyy_writeb(info, CyMIR, save_xir & 0x3f); - cyy_writeb(info, CyCAR, save_car); -} - -/* The real interrupt service routine is called - whenever the card wants its hand held--chars - received, out buffer empty, modem change, etc. - */ -static irqreturn_t cyy_interrupt(int irq, void *dev_id) -{ - int status; - struct cyclades_card *cinfo = dev_id; - void __iomem *base_addr, *card_base_addr; - unsigned int chip, too_many, had_work; - int index; - - if (unlikely(cinfo == NULL)) { -#ifdef CY_DEBUG_INTERRUPTS - printk(KERN_DEBUG "cyy_interrupt: spurious interrupt %d\n", - irq); -#endif - return IRQ_NONE; /* spurious interrupt */ - } - - card_base_addr = cinfo->base_addr; - index = cinfo->bus_index; - - /* card was not initialized yet (e.g. DEBUG_SHIRQ) */ - if (unlikely(card_base_addr == NULL)) - return IRQ_HANDLED; - - /* This loop checks all chips in the card. Make a note whenever - _any_ chip had some work to do, as this is considered an - indication that there will be more to do. Only when no chip - has any work does this outermost loop exit. - */ - do { - had_work = 0; - for (chip = 0; chip < cinfo->num_chips; chip++) { - base_addr = cinfo->base_addr + - (cy_chip_offset[chip] << index); - too_many = 0; - while ((status = readb(base_addr + - (CySVRR << index))) != 0x00) { - had_work++; - /* The purpose of the following test is to ensure that - no chip can monopolize the driver. This forces the - chips to be checked in a round-robin fashion (after - draining each of a bunch (1000) of characters). - */ - if (1000 < too_many++) - break; - spin_lock(&cinfo->card_lock); - if (status & CySRReceive) /* rx intr */ - cyy_chip_rx(cinfo, chip, base_addr); - if (status & CySRTransmit) /* tx intr */ - cyy_chip_tx(cinfo, chip, base_addr); - if (status & CySRModem) /* modem intr */ - cyy_chip_modem(cinfo, chip, base_addr); - spin_unlock(&cinfo->card_lock); - } - } - } while (had_work); - - /* clear interrupts */ - spin_lock(&cinfo->card_lock); - cy_writeb(card_base_addr + (Cy_ClrIntr << index), 0); - /* Cy_ClrIntr is 0x1800 */ - spin_unlock(&cinfo->card_lock); - return IRQ_HANDLED; -} /* cyy_interrupt */ - -static void cyy_change_rts_dtr(struct cyclades_port *info, unsigned int set, - unsigned int clear) -{ - struct cyclades_card *card = info->card; - int channel = info->line - card->first_line; - u32 rts, dtr, msvrr, msvrd; - - channel &= 0x03; - - if (info->rtsdtr_inv) { - msvrr = CyMSVR2; - msvrd = CyMSVR1; - rts = CyDTR; - dtr = CyRTS; - } else { - msvrr = CyMSVR1; - msvrd = CyMSVR2; - rts = CyRTS; - dtr = CyDTR; - } - if (set & TIOCM_RTS) { - cyy_writeb(info, CyCAR, channel); - cyy_writeb(info, msvrr, rts); - } - if (clear & TIOCM_RTS) { - cyy_writeb(info, CyCAR, channel); - cyy_writeb(info, msvrr, ~rts); - } - if (set & TIOCM_DTR) { - cyy_writeb(info, CyCAR, channel); - cyy_writeb(info, msvrd, dtr); -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_modem_info raising DTR\n"); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - cyy_readb(info, CyMSVR1), - cyy_readb(info, CyMSVR2)); -#endif - } - if (clear & TIOCM_DTR) { - cyy_writeb(info, CyCAR, channel); - cyy_writeb(info, msvrd, ~dtr); -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_modem_info dropping DTR\n"); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - cyy_readb(info, CyMSVR1), - cyy_readb(info, CyMSVR2)); -#endif - } -} - -/***********************************************************/ -/********* End of block of Cyclom-Y specific code **********/ -/******** Start of block of Cyclades-Z specific code *******/ -/***********************************************************/ - -static int -cyz_fetch_msg(struct cyclades_card *cinfo, - __u32 *channel, __u8 *cmd, __u32 *param) -{ - struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; - unsigned long loc_doorbell; - - loc_doorbell = readl(&cinfo->ctl_addr.p9060->loc_doorbell); - if (loc_doorbell) { - *cmd = (char)(0xff & loc_doorbell); - *channel = readl(&board_ctrl->fwcmd_channel); - *param = (__u32) readl(&board_ctrl->fwcmd_param); - cy_writel(&cinfo->ctl_addr.p9060->loc_doorbell, 0xffffffff); - return 1; - } - return 0; -} /* cyz_fetch_msg */ - -static int -cyz_issue_cmd(struct cyclades_card *cinfo, - __u32 channel, __u8 cmd, __u32 param) -{ - struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; - __u32 __iomem *pci_doorbell; - unsigned int index; - - if (!cyz_is_loaded(cinfo)) - return -1; - - index = 0; - pci_doorbell = &cinfo->ctl_addr.p9060->pci_doorbell; - while ((readl(pci_doorbell) & 0xff) != 0) { - if (index++ == 1000) - return (int)(readl(pci_doorbell) & 0xff); - udelay(50L); - } - cy_writel(&board_ctrl->hcmd_channel, channel); - cy_writel(&board_ctrl->hcmd_param, param); - cy_writel(pci_doorbell, (long)cmd); - - return 0; -} /* cyz_issue_cmd */ - -static void cyz_handle_rx(struct cyclades_port *info) -{ - struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; - struct cyclades_card *cinfo = info->card; - struct tty_port *port = &info->port; - unsigned int char_count; - int len; -#ifdef BLOCKMOVE - unsigned char *buf; -#else - char data; -#endif - __u32 rx_put, rx_get, new_rx_get, rx_bufsize, rx_bufaddr; - - rx_get = new_rx_get = readl(&buf_ctrl->rx_get); - rx_put = readl(&buf_ctrl->rx_put); - rx_bufsize = readl(&buf_ctrl->rx_bufsize); - rx_bufaddr = readl(&buf_ctrl->rx_bufaddr); - if (rx_put >= rx_get) - char_count = rx_put - rx_get; - else - char_count = rx_put - rx_get + rx_bufsize; - - if (!char_count) - return; - -#ifdef CY_ENABLE_MONITORING - info->mon.int_count++; - info->mon.char_count += char_count; - if (char_count > info->mon.char_max) - info->mon.char_max = char_count; - info->mon.char_last = char_count; -#endif - -#ifdef BLOCKMOVE - /* we'd like to use memcpy(t, f, n) and memset(s, c, count) - for performance, but because of buffer boundaries, there - may be several steps to the operation */ - while (1) { - len = tty_prepare_flip_string(port, &buf, - char_count); - if (!len) - break; - - len = min_t(unsigned int, min(len, char_count), - rx_bufsize - new_rx_get); - - memcpy_fromio(buf, cinfo->base_addr + - rx_bufaddr + new_rx_get, len); - - new_rx_get = (new_rx_get + len) & - (rx_bufsize - 1); - char_count -= len; - info->icount.rx += len; - info->idle_stats.recv_bytes += len; - } -#else - len = tty_buffer_request_room(port, char_count); - while (len--) { - data = readb(cinfo->base_addr + rx_bufaddr + - new_rx_get); - new_rx_get = (new_rx_get + 1) & - (rx_bufsize - 1); - tty_insert_flip_char(port, data, TTY_NORMAL); - info->idle_stats.recv_bytes++; - info->icount.rx++; - } -#endif -#ifdef CONFIG_CYZ_INTR - /* Recalculate the number of chars in the RX buffer and issue - a cmd in case it's higher than the RX high water mark */ - rx_put = readl(&buf_ctrl->rx_put); - if (rx_put >= rx_get) - char_count = rx_put - rx_get; - else - char_count = rx_put - rx_get + rx_bufsize; - if (char_count >= readl(&buf_ctrl->rx_threshold) && - !timer_pending(&info->rx_full_timer)) - mod_timer(&info->rx_full_timer, jiffies + 1); -#endif - info->idle_stats.recv_idle = jiffies; - tty_schedule_flip(&info->port); - - /* Update rx_get */ - cy_writel(&buf_ctrl->rx_get, new_rx_get); -} - -static void cyz_handle_tx(struct cyclades_port *info) -{ - struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; - struct cyclades_card *cinfo = info->card; - struct tty_struct *tty; - u8 data; - unsigned int char_count; -#ifdef BLOCKMOVE - int small_count; -#endif - __u32 tx_put, tx_get, tx_bufsize, tx_bufaddr; - - if (info->xmit_cnt <= 0) /* Nothing to transmit */ - return; - - tx_get = readl(&buf_ctrl->tx_get); - tx_put = readl(&buf_ctrl->tx_put); - tx_bufsize = readl(&buf_ctrl->tx_bufsize); - tx_bufaddr = readl(&buf_ctrl->tx_bufaddr); - if (tx_put >= tx_get) - char_count = tx_get - tx_put - 1 + tx_bufsize; - else - char_count = tx_get - tx_put - 1; - - if (!char_count) - return; - - tty = tty_port_tty_get(&info->port); - if (tty == NULL) - goto ztxdone; - - if (info->x_char) { /* send special char */ - data = info->x_char; - - cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data); - tx_put = (tx_put + 1) & (tx_bufsize - 1); - info->x_char = 0; - char_count--; - info->icount.tx++; - } -#ifdef BLOCKMOVE - while (0 < (small_count = min_t(unsigned int, - tx_bufsize - tx_put, min_t(unsigned int, - (SERIAL_XMIT_SIZE - info->xmit_tail), - min_t(unsigned int, info->xmit_cnt, - char_count))))) { - - memcpy_toio((char *)(cinfo->base_addr + tx_bufaddr + tx_put), - &info->port.xmit_buf[info->xmit_tail], - small_count); - - tx_put = (tx_put + small_count) & (tx_bufsize - 1); - char_count -= small_count; - info->icount.tx += small_count; - info->xmit_cnt -= small_count; - info->xmit_tail = (info->xmit_tail + small_count) & - (SERIAL_XMIT_SIZE - 1); - } -#else - while (info->xmit_cnt && char_count) { - data = info->port.xmit_buf[info->xmit_tail]; - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) & - (SERIAL_XMIT_SIZE - 1); - - cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data); - tx_put = (tx_put + 1) & (tx_bufsize - 1); - char_count--; - info->icount.tx++; - } -#endif - tty_wakeup(tty); - tty_kref_put(tty); -ztxdone: - /* Update tx_put */ - cy_writel(&buf_ctrl->tx_put, tx_put); -} - -static void cyz_handle_cmd(struct cyclades_card *cinfo) -{ - struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; - struct cyclades_port *info; - __u32 channel, param, fw_ver; - __u8 cmd; - int special_count; - int delta_count; - - fw_ver = readl(&board_ctrl->fw_version); - - while (cyz_fetch_msg(cinfo, &channel, &cmd, ¶m) == 1) { - special_count = 0; - delta_count = 0; - info = &cinfo->ports[channel]; - - switch (cmd) { - case C_CM_PR_ERROR: - tty_insert_flip_char(&info->port, 0, TTY_PARITY); - info->icount.rx++; - special_count++; - break; - case C_CM_FR_ERROR: - tty_insert_flip_char(&info->port, 0, TTY_FRAME); - info->icount.rx++; - special_count++; - break; - case C_CM_RXBRK: - tty_insert_flip_char(&info->port, 0, TTY_BREAK); - info->icount.rx++; - special_count++; - break; - case C_CM_MDCD: - info->icount.dcd++; - delta_count++; - if (tty_port_check_carrier(&info->port)) { - u32 dcd = fw_ver > 241 ? param : - readl(&info->u.cyz.ch_ctrl->rs_status); - if (dcd & C_RS_DCD) - wake_up_interruptible(&info->port.open_wait); - else - tty_port_tty_hangup(&info->port, false); - } - break; - case C_CM_MCTS: - info->icount.cts++; - delta_count++; - break; - case C_CM_MRI: - info->icount.rng++; - delta_count++; - break; - case C_CM_MDSR: - info->icount.dsr++; - delta_count++; - break; -#ifdef Z_WAKE - case C_CM_IOCTLW: - complete(&info->shutdown_wait); - break; -#endif -#ifdef CONFIG_CYZ_INTR - case C_CM_RXHIWM: - case C_CM_RXNNDT: - case C_CM_INTBACK2: - /* Reception Interrupt */ -#ifdef CY_DEBUG_INTERRUPTS - printk(KERN_DEBUG "cyz_interrupt: rcvd intr, card %d, " - "port %ld\n", info->card, channel); -#endif - cyz_handle_rx(info); - break; - case C_CM_TXBEMPTY: - case C_CM_TXLOWWM: - case C_CM_INTBACK: - /* Transmission Interrupt */ -#ifdef CY_DEBUG_INTERRUPTS - printk(KERN_DEBUG "cyz_interrupt: xmit intr, card %d, " - "port %ld\n", info->card, channel); -#endif - cyz_handle_tx(info); - break; -#endif /* CONFIG_CYZ_INTR */ - case C_CM_FATAL: - /* should do something with this !!! */ - break; - default: - break; - } - if (delta_count) - wake_up_interruptible(&info->port.delta_msr_wait); - if (special_count) - tty_schedule_flip(&info->port); - } -} - -#ifdef CONFIG_CYZ_INTR -static irqreturn_t cyz_interrupt(int irq, void *dev_id) -{ - struct cyclades_card *cinfo = dev_id; - - if (unlikely(!cyz_is_loaded(cinfo))) { -#ifdef CY_DEBUG_INTERRUPTS - printk(KERN_DEBUG "cyz_interrupt: board not yet loaded " - "(IRQ%d).\n", irq); -#endif - return IRQ_NONE; - } - - /* Handle the interrupts */ - cyz_handle_cmd(cinfo); - - return IRQ_HANDLED; -} /* cyz_interrupt */ - -static void cyz_rx_restart(struct timer_list *t) -{ - struct cyclades_port *info = from_timer(info, t, rx_full_timer); - struct cyclades_card *card = info->card; - int retval; - __u32 channel = info->line - card->first_line; - unsigned long flags; - - spin_lock_irqsave(&card->card_lock, flags); - retval = cyz_issue_cmd(card, channel, C_CM_INTBACK2, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:cyz_rx_restart retval on ttyC%d was %x\n", - info->line, retval); - } - spin_unlock_irqrestore(&card->card_lock, flags); -} - -#else /* CONFIG_CYZ_INTR */ - -static void cyz_poll(struct timer_list *unused) -{ - struct cyclades_card *cinfo; - struct cyclades_port *info; - unsigned long expires = jiffies + HZ; - unsigned int port, card; - - for (card = 0; card < NR_CARDS; card++) { - cinfo = &cy_card[card]; - - if (!cy_is_Z(cinfo)) - continue; - if (!cyz_is_loaded(cinfo)) - continue; - - /* Skip first polling cycle to avoid racing conditions with the FW */ - if (!cinfo->intr_enabled) { - cinfo->intr_enabled = 1; - continue; - } - - cyz_handle_cmd(cinfo); - - for (port = 0; port < cinfo->nports; port++) { - info = &cinfo->ports[port]; - - if (!info->throttle) - cyz_handle_rx(info); - cyz_handle_tx(info); - } - /* poll every 'cyz_polling_cycle' period */ - expires = jiffies + cyz_polling_cycle; - } - mod_timer(&cyz_timerlist, expires); -} /* cyz_poll */ - -#endif /* CONFIG_CYZ_INTR */ - -/********** End of block of Cyclades-Z specific code *********/ -/***********************************************************/ - -/* This is called whenever a port becomes active; - interrupts are enabled and DTR & RTS are turned on. - */ -static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) -{ - struct cyclades_card *card; - unsigned long flags; - int retval = 0; - int channel; - unsigned long page; - - card = info->card; - channel = info->line - card->first_line; - - page = get_zeroed_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - spin_lock_irqsave(&card->card_lock, flags); - - if (tty_port_initialized(&info->port)) - goto errout; - - if (!info->type) { - set_bit(TTY_IO_ERROR, &tty->flags); - goto errout; - } - - if (info->port.xmit_buf) - free_page(page); - else - info->port.xmit_buf = (unsigned char *)page; - - spin_unlock_irqrestore(&card->card_lock, flags); - - cy_set_line_char(info, tty); - - if (!cy_is_Z(card)) { - channel &= 0x03; - - spin_lock_irqsave(&card->card_lock, flags); - - cyy_writeb(info, CyCAR, channel); - - cyy_writeb(info, CyRTPR, - (info->default_timeout ? info->default_timeout : 0x02)); - /* 10ms rx timeout */ - - cyy_issue_cmd(info, CyCHAN_CTL | CyENB_RCVR | CyENB_XMTR); - - cyy_change_rts_dtr(info, TIOCM_RTS | TIOCM_DTR, 0); - - cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyRxData); - } else { - struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; - - if (!cyz_is_loaded(card)) - return -ENODEV; - -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc startup Z card %d, channel %d, " - "base_addr %p\n", card, channel, card->base_addr); -#endif - spin_lock_irqsave(&card->card_lock, flags); - - cy_writel(&ch_ctrl->op_mode, C_CH_ENABLE); -#ifdef Z_WAKE -#ifdef CONFIG_CYZ_INTR - cy_writel(&ch_ctrl->intr_enable, - C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM | - C_IN_RXNNDT | C_IN_IOCTLW | C_IN_MDCD); -#else - cy_writel(&ch_ctrl->intr_enable, - C_IN_IOCTLW | C_IN_MDCD); -#endif /* CONFIG_CYZ_INTR */ -#else -#ifdef CONFIG_CYZ_INTR - cy_writel(&ch_ctrl->intr_enable, - C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM | - C_IN_RXNNDT | C_IN_MDCD); -#else - cy_writel(&ch_ctrl->intr_enable, C_IN_MDCD); -#endif /* CONFIG_CYZ_INTR */ -#endif /* Z_WAKE */ - - retval = cyz_issue_cmd(card, channel, C_CM_IOCTL, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:startup(1) retval on ttyC%d was " - "%x\n", info->line, retval); - } - - /* Flush RX buffers before raising DTR and RTS */ - retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_RX, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:startup(2) retval on ttyC%d was " - "%x\n", info->line, retval); - } - - /* set timeout !!! */ - /* set RTS and DTR !!! */ - tty_port_raise_dtr_rts(&info->port); - - /* enable send, recv, modem !!! */ - } - - tty_port_set_initialized(&info->port, 1); - - clear_bit(TTY_IO_ERROR, &tty->flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - info->breakon = info->breakoff = 0; - memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); - info->idle_stats.in_use = - info->idle_stats.recv_idle = - info->idle_stats.xmit_idle = jiffies; - - spin_unlock_irqrestore(&card->card_lock, flags); - -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc startup done\n"); -#endif - return 0; - -errout: - spin_unlock_irqrestore(&card->card_lock, flags); - free_page(page); - return retval; -} /* startup */ - -static void start_xmit(struct cyclades_port *info) -{ - struct cyclades_card *card = info->card; - unsigned long flags; - int channel = info->line - card->first_line; - - if (!cy_is_Z(card)) { - spin_lock_irqsave(&card->card_lock, flags); - cyy_writeb(info, CyCAR, channel & 0x03); - cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy); - spin_unlock_irqrestore(&card->card_lock, flags); - } else { -#ifdef CONFIG_CYZ_INTR - int retval; - - spin_lock_irqsave(&card->card_lock, flags); - retval = cyz_issue_cmd(card, channel, C_CM_INTBACK, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:start_xmit retval on ttyC%d was " - "%x\n", info->line, retval); - } - spin_unlock_irqrestore(&card->card_lock, flags); -#else /* CONFIG_CYZ_INTR */ - /* Don't have to do anything at this time */ -#endif /* CONFIG_CYZ_INTR */ - } -} /* start_xmit */ - -/* - * This routine shuts down a serial port; interrupts are disabled, - * and DTR is dropped if the hangup on close termio flag is on. - */ -static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) -{ - struct cyclades_card *card; - unsigned long flags; - - if (!tty_port_initialized(&info->port)) - return; - - card = info->card; - if (!cy_is_Z(card)) { - spin_lock_irqsave(&card->card_lock, flags); - - /* Clear delta_msr_wait queue to avoid mem leaks. */ - wake_up_interruptible(&info->port.delta_msr_wait); - - if (info->port.xmit_buf) { - unsigned char *temp; - temp = info->port.xmit_buf; - info->port.xmit_buf = NULL; - free_page((unsigned long)temp); - } - if (C_HUPCL(tty)) - cyy_change_rts_dtr(info, 0, TIOCM_RTS | TIOCM_DTR); - - cyy_issue_cmd(info, CyCHAN_CTL | CyDIS_RCVR); - /* it may be appropriate to clear _XMIT at - some later date (after testing)!!! */ - - set_bit(TTY_IO_ERROR, &tty->flags); - tty_port_set_initialized(&info->port, 0); - spin_unlock_irqrestore(&card->card_lock, flags); - } else { -#ifdef CY_DEBUG_OPEN - int channel = info->line - card->first_line; - printk(KERN_DEBUG "cyc shutdown Z card %d, channel %d, " - "base_addr %p\n", card, channel, card->base_addr); -#endif - - if (!cyz_is_loaded(card)) - return; - - spin_lock_irqsave(&card->card_lock, flags); - - if (info->port.xmit_buf) { - unsigned char *temp; - temp = info->port.xmit_buf; - info->port.xmit_buf = NULL; - free_page((unsigned long)temp); - } - - if (C_HUPCL(tty)) - tty_port_lower_dtr_rts(&info->port); - - set_bit(TTY_IO_ERROR, &tty->flags); - tty_port_set_initialized(&info->port, 0); - - spin_unlock_irqrestore(&card->card_lock, flags); - } - -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc shutdown done\n"); -#endif -} /* shutdown */ - -/* - * ------------------------------------------------------------ - * cy_open() and friends - * ------------------------------------------------------------ - */ - -/* - * This routine is called whenever a serial port is opened. It - * performs the serial-specific initialization for the tty structure. - */ -static int cy_open(struct tty_struct *tty, struct file *filp) -{ - struct cyclades_port *info; - unsigned int i, line = tty->index; - int retval; - - for (i = 0; i < NR_CARDS; i++) - if (line < cy_card[i].first_line + cy_card[i].nports && - line >= cy_card[i].first_line) - break; - if (i >= NR_CARDS) - return -ENODEV; - info = &cy_card[i].ports[line - cy_card[i].first_line]; - if (info->line < 0) - return -ENODEV; - - /* If the card's firmware hasn't been loaded, - treat it as absent from the system. This - will make the user pay attention. - */ - if (cy_is_Z(info->card)) { - struct cyclades_card *cinfo = info->card; - struct FIRM_ID __iomem *firm_id = cinfo->base_addr + ID_ADDRESS; - - if (!cyz_is_loaded(cinfo)) { - if (cinfo->hw_ver == ZE_V1 && cyz_fpga_loaded(cinfo) && - readl(&firm_id->signature) == - ZFIRM_HLT) { - printk(KERN_ERR "cyc:Cyclades-Z Error: you " - "need an external power supply for " - "this number of ports.\nFirmware " - "halted.\n"); - } else { - printk(KERN_ERR "cyc:Cyclades-Z firmware not " - "yet loaded\n"); - } - return -ENODEV; - } -#ifdef CONFIG_CYZ_INTR - else { - /* In case this Z board is operating in interrupt mode, its - interrupts should be enabled as soon as the first open - happens to one of its ports. */ - if (!cinfo->intr_enabled) { - u16 intr; - - /* Enable interrupts on the PLX chip */ - intr = readw(&cinfo->ctl_addr.p9060-> - intr_ctrl_stat) | 0x0900; - cy_writew(&cinfo->ctl_addr.p9060-> - intr_ctrl_stat, intr); - /* Enable interrupts on the FW */ - retval = cyz_issue_cmd(cinfo, 0, - C_CM_IRQ_ENBL, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:IRQ enable retval " - "was %x\n", retval); - } - cinfo->intr_enabled = 1; - } - } -#endif /* CONFIG_CYZ_INTR */ - /* Make sure this Z port really exists in hardware */ - if (info->line > (cinfo->first_line + cinfo->nports - 1)) - return -ENODEV; - } -#ifdef CY_DEBUG_OTHER - printk(KERN_DEBUG "cyc:cy_open ttyC%d\n", info->line); -#endif - tty->driver_data = info; - if (serial_paranoia_check(info, tty->name, "cy_open")) - return -ENODEV; - -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc:cy_open ttyC%d, count = %d\n", info->line, - info->port.count); -#endif - info->port.count++; -#ifdef CY_DEBUG_COUNT - printk(KERN_DEBUG "cyc:cy_open (%d): incrementing count to %d\n", - current->pid, info->port.count); -#endif - - /* - * Start up serial port - */ - retval = cy_startup(info, tty); - if (retval) - return retval; - - retval = tty_port_block_til_ready(&info->port, tty, filp); - if (retval) { -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc:cy_open returning after block_til_ready " - "with %d\n", retval); -#endif - return retval; - } - - info->throttle = 0; - tty_port_tty_set(&info->port, tty); - -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc:cy_open done\n"); -#endif - return 0; -} /* cy_open */ - -/* - * cy_wait_until_sent() --- wait until the transmitter is empty - */ -static void cy_wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct cyclades_card *card; - struct cyclades_port *info = tty->driver_data; - unsigned long orig_jiffies; - int char_time; - - if (serial_paranoia_check(info, tty->name, "cy_wait_until_sent")) - return; - - if (info->xmit_fifo_size == 0) - return; /* Just in case.... */ - - orig_jiffies = jiffies; - /* - * Set the check interval to be 1/5 of the estimated time to - * send a single character, and make it at least 1. The check - * interval should also be less than the timeout. - * - * Note: we have to use pretty tight timings here to satisfy - * the NIST-PCTS. - */ - char_time = (info->timeout - HZ / 50) / info->xmit_fifo_size; - char_time = char_time / 5; - if (char_time <= 0) - char_time = 1; - if (timeout < 0) - timeout = 0; - if (timeout) - char_time = min(char_time, timeout); - /* - * If the transmitter hasn't cleared in twice the approximate - * amount of time to send the entire FIFO, it probably won't - * ever clear. This assumes the UART isn't doing flow - * control, which is currently the case. Hence, if it ever - * takes longer than info->timeout, this is probably due to a - * UART bug of some kind. So, we clamp the timeout parameter at - * 2*info->timeout. - */ - if (!timeout || timeout > 2 * info->timeout) - timeout = 2 * info->timeout; - - card = info->card; - if (!cy_is_Z(card)) { - while (cyy_readb(info, CySRER) & CyTxRdy) { - if (msleep_interruptible(jiffies_to_msecs(char_time))) - break; - if (timeout && time_after(jiffies, orig_jiffies + - timeout)) - break; - } - } - /* Run one more char cycle */ - msleep_interruptible(jiffies_to_msecs(char_time * 5)); -} - -static void cy_flush_buffer(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_card *card; - int channel, retval; - unsigned long flags; - -#ifdef CY_DEBUG_IO - printk(KERN_DEBUG "cyc:cy_flush_buffer ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_flush_buffer")) - return; - - card = info->card; - channel = info->line - card->first_line; - - spin_lock_irqsave(&card->card_lock, flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - spin_unlock_irqrestore(&card->card_lock, flags); - - if (cy_is_Z(card)) { /* If it is a Z card, flush the on-board - buffers as well */ - spin_lock_irqsave(&card->card_lock, flags); - retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_TX, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc: flush_buffer retval on ttyC%d " - "was %x\n", info->line, retval); - } - spin_unlock_irqrestore(&card->card_lock, flags); - } - tty_wakeup(tty); -} /* cy_flush_buffer */ - - -static void cy_do_close(struct tty_port *port) -{ - struct cyclades_port *info = container_of(port, struct cyclades_port, - port); - struct cyclades_card *card; - unsigned long flags; - int channel; - - card = info->card; - channel = info->line - card->first_line; - spin_lock_irqsave(&card->card_lock, flags); - - if (!cy_is_Z(card)) { - /* Stop accepting input */ - cyy_writeb(info, CyCAR, channel & 0x03); - cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyRxData); - if (tty_port_initialized(&info->port)) { - /* Waiting for on-board buffers to be empty before - closing the port */ - spin_unlock_irqrestore(&card->card_lock, flags); - cy_wait_until_sent(port->tty, info->timeout); - spin_lock_irqsave(&card->card_lock, flags); - } - } else { -#ifdef Z_WAKE - /* Waiting for on-board buffers to be empty before closing - the port */ - struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; - int retval; - - if (readl(&ch_ctrl->flow_status) != C_FS_TXIDLE) { - retval = cyz_issue_cmd(card, channel, C_CM_IOCTLW, 0L); - if (retval != 0) { - printk(KERN_DEBUG "cyc:cy_close retval on " - "ttyC%d was %x\n", info->line, retval); - } - spin_unlock_irqrestore(&card->card_lock, flags); - wait_for_completion_interruptible(&info->shutdown_wait); - spin_lock_irqsave(&card->card_lock, flags); - } -#endif - } - spin_unlock_irqrestore(&card->card_lock, flags); - cy_shutdown(info, port->tty); -} - -/* - * This routine is called when a particular tty device is closed. - */ -static void cy_close(struct tty_struct *tty, struct file *filp) -{ - struct cyclades_port *info = tty->driver_data; - if (!info || serial_paranoia_check(info, tty->name, "cy_close")) - return; - tty_port_close(&info->port, tty, filp); -} /* cy_close */ - -/* This routine gets called when tty_write has put something into - * the write_queue. The characters may come from user space or - * kernel space. - * - * This routine will return the number of characters actually - * accepted for writing. - * - * If the port is not already transmitting stuff, start it off by - * enabling interrupts. The interrupt service routine will then - * ensure that the characters are sent. - * If the port is already active, there is no need to kick it. - * - */ -static int cy_write(struct tty_struct *tty, const unsigned char *buf, int count) -{ - struct cyclades_port *info = tty->driver_data; - unsigned long flags; - int c, ret = 0; - -#ifdef CY_DEBUG_IO - printk(KERN_DEBUG "cyc:cy_write ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_write")) - return 0; - - if (!info->port.xmit_buf) - return 0; - - spin_lock_irqsave(&info->card->card_lock, flags); - while (1) { - c = min(count, (int)(SERIAL_XMIT_SIZE - info->xmit_cnt - 1)); - c = min(c, (int)(SERIAL_XMIT_SIZE - info->xmit_head)); - - if (c <= 0) - break; - - memcpy(info->port.xmit_buf + info->xmit_head, buf, c); - info->xmit_head = (info->xmit_head + c) & - (SERIAL_XMIT_SIZE - 1); - info->xmit_cnt += c; - buf += c; - count -= c; - ret += c; - } - spin_unlock_irqrestore(&info->card->card_lock, flags); - - info->idle_stats.xmit_bytes += ret; - info->idle_stats.xmit_idle = jiffies; - - if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) - start_xmit(info); - - return ret; -} /* cy_write */ - -/* - * This routine is called by the kernel to write a single - * character to the tty device. If the kernel uses this routine, - * it must call the flush_chars() routine (if defined) when it is - * done stuffing characters into the driver. If there is no room - * in the queue, the character is ignored. - */ -static int cy_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct cyclades_port *info = tty->driver_data; - unsigned long flags; - -#ifdef CY_DEBUG_IO - printk(KERN_DEBUG "cyc:cy_put_char ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_put_char")) - return 0; - - if (!info->port.xmit_buf) - return 0; - - spin_lock_irqsave(&info->card->card_lock, flags); - if (info->xmit_cnt >= (int)(SERIAL_XMIT_SIZE - 1)) { - spin_unlock_irqrestore(&info->card->card_lock, flags); - return 0; - } - - info->port.xmit_buf[info->xmit_head++] = ch; - info->xmit_head &= SERIAL_XMIT_SIZE - 1; - info->xmit_cnt++; - info->idle_stats.xmit_bytes++; - info->idle_stats.xmit_idle = jiffies; - spin_unlock_irqrestore(&info->card->card_lock, flags); - return 1; -} /* cy_put_char */ - -/* - * This routine is called by the kernel after it has written a - * series of characters to the tty device using put_char(). - */ -static void cy_flush_chars(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - -#ifdef CY_DEBUG_IO - printk(KERN_DEBUG "cyc:cy_flush_chars ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_flush_chars")) - return; - - if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !info->port.xmit_buf) - return; - - start_xmit(info); -} /* cy_flush_chars */ - -/* - * This routine returns the numbers of characters the tty driver - * will accept for queuing to be written. This number is subject - * to change as output buffers get emptied, or if the output flow - * control is activated. - */ -static int cy_write_room(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - int ret; - -#ifdef CY_DEBUG_IO - printk(KERN_DEBUG "cyc:cy_write_room ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_write_room")) - return 0; - ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; - if (ret < 0) - ret = 0; - return ret; -} /* cy_write_room */ - -static int cy_chars_in_buffer(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "cy_chars_in_buffer")) - return 0; - -#ifdef Z_EXT_CHARS_IN_BUFFER - if (!cy_is_Z(info->card)) { -#endif /* Z_EXT_CHARS_IN_BUFFER */ -#ifdef CY_DEBUG_IO - printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n", - info->line, info->xmit_cnt); -#endif - return info->xmit_cnt; -#ifdef Z_EXT_CHARS_IN_BUFFER - } else { - struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; - int char_count; - __u32 tx_put, tx_get, tx_bufsize; - - tx_get = readl(&buf_ctrl->tx_get); - tx_put = readl(&buf_ctrl->tx_put); - tx_bufsize = readl(&buf_ctrl->tx_bufsize); - if (tx_put >= tx_get) - char_count = tx_put - tx_get; - else - char_count = tx_put - tx_get + tx_bufsize; -#ifdef CY_DEBUG_IO - printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n", - info->line, info->xmit_cnt + char_count); -#endif - return info->xmit_cnt + char_count; - } -#endif /* Z_EXT_CHARS_IN_BUFFER */ -} /* cy_chars_in_buffer */ - -/* - * ------------------------------------------------------------ - * cy_ioctl() and friends - * ------------------------------------------------------------ - */ - -static void cyy_baud_calc(struct cyclades_port *info, __u32 baud) -{ - int co, co_val, bpr; - __u32 cy_clock = ((info->chip_rev >= CD1400_REV_J) ? 60000000 : - 25000000); - - if (baud == 0) { - info->tbpr = info->tco = info->rbpr = info->rco = 0; - return; - } - - /* determine which prescaler to use */ - for (co = 4, co_val = 2048; co; co--, co_val >>= 2) { - if (cy_clock / co_val / baud > 63) - break; - } - - bpr = (cy_clock / co_val * 2 / baud + 1) / 2; - if (bpr > 255) - bpr = 255; - - info->tbpr = info->rbpr = bpr; - info->tco = info->rco = co; -} - -/* - * This routine finds or computes the various line characteristics. - * It used to be called config_setup - */ -static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) -{ - struct cyclades_card *card; - unsigned long flags; - int channel; - unsigned cflag, iflag; - int baud, baud_rate = 0; - int i; - - if (info->line == -1) - return; - - cflag = tty->termios.c_cflag; - iflag = tty->termios.c_iflag; - - card = info->card; - channel = info->line - card->first_line; - - if (!cy_is_Z(card)) { - u32 cflags; - - /* baud rate */ - baud = tty_get_baud_rate(tty); - if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == - ASYNC_SPD_CUST) { - if (info->custom_divisor) - baud_rate = info->baud / info->custom_divisor; - else - baud_rate = info->baud; - } else if (baud > CD1400_MAX_SPEED) { - baud = CD1400_MAX_SPEED; - } - /* find the baud index */ - for (i = 0; i < 20; i++) { - if (baud == baud_table[i]) - break; - } - if (i == 20) - i = 19; /* CD1400_MAX_SPEED */ - - if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == - ASYNC_SPD_CUST) { - cyy_baud_calc(info, baud_rate); - } else { - if (info->chip_rev >= CD1400_REV_J) { - /* It is a CD1400 rev. J or later */ - info->tbpr = baud_bpr_60[i]; /* Tx BPR */ - info->tco = baud_co_60[i]; /* Tx CO */ - info->rbpr = baud_bpr_60[i]; /* Rx BPR */ - info->rco = baud_co_60[i]; /* Rx CO */ - } else { - info->tbpr = baud_bpr_25[i]; /* Tx BPR */ - info->tco = baud_co_25[i]; /* Tx CO */ - info->rbpr = baud_bpr_25[i]; /* Rx BPR */ - info->rco = baud_co_25[i]; /* Rx CO */ - } - } - if (baud_table[i] == 134) { - /* get it right for 134.5 baud */ - info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) + - 2; - } else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == - ASYNC_SPD_CUST) { - info->timeout = (info->xmit_fifo_size * HZ * 15 / - baud_rate) + 2; - } else if (baud_table[i]) { - info->timeout = (info->xmit_fifo_size * HZ * 15 / - baud_table[i]) + 2; - /* this needs to be propagated into the card info */ - } else { - info->timeout = 0; - } - /* By tradition (is it a standard?) a baud rate of zero - implies the line should be/has been closed. A bit - later in this routine such a test is performed. */ - - /* byte size and parity */ - info->cor5 = 0; - info->cor4 = 0; - /* receive threshold */ - info->cor3 = (info->default_threshold ? - info->default_threshold : baud_cor3[i]); - info->cor2 = CyETC; - switch (cflag & CSIZE) { - case CS5: - info->cor1 = Cy_5_BITS; - break; - case CS6: - info->cor1 = Cy_6_BITS; - break; - case CS7: - info->cor1 = Cy_7_BITS; - break; - case CS8: - info->cor1 = Cy_8_BITS; - break; - } - if (cflag & CSTOPB) - info->cor1 |= Cy_2_STOP; - - if (cflag & PARENB) { - if (cflag & PARODD) - info->cor1 |= CyPARITY_O; - else - info->cor1 |= CyPARITY_E; - } else - info->cor1 |= CyPARITY_NONE; - - /* CTS flow control flag */ - tty_port_set_cts_flow(&info->port, cflag & CRTSCTS); - if (cflag & CRTSCTS) - info->cor2 |= CyCtsAE; - else - info->cor2 &= ~CyCtsAE; - tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL); - - /*********************************************** - The hardware option, CyRtsAO, presents RTS when - the chip has characters to send. Since most modems - use RTS as reverse (inbound) flow control, this - option is not used. If inbound flow control is - necessary, DTR can be programmed to provide the - appropriate signals for use with a non-standard - cable. Contact Marcio Saito for details. - ***********************************************/ - - channel &= 0x03; - - spin_lock_irqsave(&card->card_lock, flags); - cyy_writeb(info, CyCAR, channel); - - /* tx and rx baud rate */ - - cyy_writeb(info, CyTCOR, info->tco); - cyy_writeb(info, CyTBPR, info->tbpr); - cyy_writeb(info, CyRCOR, info->rco); - cyy_writeb(info, CyRBPR, info->rbpr); - - /* set line characteristics according configuration */ - - cyy_writeb(info, CySCHR1, START_CHAR(tty)); - cyy_writeb(info, CySCHR2, STOP_CHAR(tty)); - cyy_writeb(info, CyCOR1, info->cor1); - cyy_writeb(info, CyCOR2, info->cor2); - cyy_writeb(info, CyCOR3, info->cor3); - cyy_writeb(info, CyCOR4, info->cor4); - cyy_writeb(info, CyCOR5, info->cor5); - - cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR1ch | CyCOR2ch | - CyCOR3ch); - - /* !!! Is this needed? */ - cyy_writeb(info, CyCAR, channel); - cyy_writeb(info, CyRTPR, - (info->default_timeout ? info->default_timeout : 0x02)); - /* 10ms rx timeout */ - - cflags = CyCTS; - if (!C_CLOCAL(tty)) - cflags |= CyDSR | CyRI | CyDCD; - /* without modem intr */ - cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyMdmCh); - /* act on 1->0 modem transitions */ - if ((cflag & CRTSCTS) && info->rflow) - cyy_writeb(info, CyMCOR1, cflags | rflow_thr[i]); - else - cyy_writeb(info, CyMCOR1, cflags); - /* act on 0->1 modem transitions */ - cyy_writeb(info, CyMCOR2, cflags); - - if (i == 0) /* baud rate is zero, turn off line */ - cyy_change_rts_dtr(info, 0, TIOCM_DTR); - else - cyy_change_rts_dtr(info, TIOCM_DTR, 0); - - clear_bit(TTY_IO_ERROR, &tty->flags); - spin_unlock_irqrestore(&card->card_lock, flags); - - } else { - struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; - __u32 sw_flow; - int retval; - - if (!cyz_is_loaded(card)) - return; - - /* baud rate */ - baud = tty_get_baud_rate(tty); - if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == - ASYNC_SPD_CUST) { - if (info->custom_divisor) - baud_rate = info->baud / info->custom_divisor; - else - baud_rate = info->baud; - } else if (baud > CYZ_MAX_SPEED) { - baud = CYZ_MAX_SPEED; - } - cy_writel(&ch_ctrl->comm_baud, baud); - - if (baud == 134) { - /* get it right for 134.5 baud */ - info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) + - 2; - } else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == - ASYNC_SPD_CUST) { - info->timeout = (info->xmit_fifo_size * HZ * 15 / - baud_rate) + 2; - } else if (baud) { - info->timeout = (info->xmit_fifo_size * HZ * 15 / - baud) + 2; - /* this needs to be propagated into the card info */ - } else { - info->timeout = 0; - } - - /* byte size and parity */ - switch (cflag & CSIZE) { - case CS5: - cy_writel(&ch_ctrl->comm_data_l, C_DL_CS5); - break; - case CS6: - cy_writel(&ch_ctrl->comm_data_l, C_DL_CS6); - break; - case CS7: - cy_writel(&ch_ctrl->comm_data_l, C_DL_CS7); - break; - case CS8: - cy_writel(&ch_ctrl->comm_data_l, C_DL_CS8); - break; - } - if (cflag & CSTOPB) { - cy_writel(&ch_ctrl->comm_data_l, - readl(&ch_ctrl->comm_data_l) | C_DL_2STOP); - } else { - cy_writel(&ch_ctrl->comm_data_l, - readl(&ch_ctrl->comm_data_l) | C_DL_1STOP); - } - if (cflag & PARENB) { - if (cflag & PARODD) - cy_writel(&ch_ctrl->comm_parity, C_PR_ODD); - else - cy_writel(&ch_ctrl->comm_parity, C_PR_EVEN); - } else - cy_writel(&ch_ctrl->comm_parity, C_PR_NONE); - - /* CTS flow control flag */ - if (cflag & CRTSCTS) { - cy_writel(&ch_ctrl->hw_flow, - readl(&ch_ctrl->hw_flow) | C_RS_CTS | C_RS_RTS); - } else { - cy_writel(&ch_ctrl->hw_flow, readl(&ch_ctrl->hw_flow) & - ~(C_RS_CTS | C_RS_RTS)); - } - /* As the HW flow control is done in firmware, the driver - doesn't need to care about it */ - tty_port_set_cts_flow(&info->port, 0); - - /* XON/XOFF/XANY flow control flags */ - sw_flow = 0; - if (iflag & IXON) { - sw_flow |= C_FL_OXX; - if (iflag & IXANY) - sw_flow |= C_FL_OIXANY; - } - cy_writel(&ch_ctrl->sw_flow, sw_flow); - - retval = cyz_issue_cmd(card, channel, C_CM_IOCTL, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:set_line_char retval on ttyC%d " - "was %x\n", info->line, retval); - } - - /* CD sensitivity */ - tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL); - - if (baud == 0) { /* baud rate is zero, turn off line */ - cy_writel(&ch_ctrl->rs_control, - readl(&ch_ctrl->rs_control) & ~C_RS_DTR); -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_line_char dropping Z DTR\n"); -#endif - } else { - cy_writel(&ch_ctrl->rs_control, - readl(&ch_ctrl->rs_control) | C_RS_DTR); -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_line_char raising Z DTR\n"); -#endif - } - - retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:set_line_char(2) retval on ttyC%d " - "was %x\n", info->line, retval); - } - - clear_bit(TTY_IO_ERROR, &tty->flags); - } -} /* set_line_char */ - -static int cy_get_serial_info(struct tty_struct *tty, - struct serial_struct *ss) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_card *cinfo = info->card; - - if (serial_paranoia_check(info, tty->name, "cy_ioctl")) - return -ENODEV; - ss->type = info->type; - ss->line = info->line; - ss->port = (info->card - cy_card) * 0x100 + info->line - - cinfo->first_line; - ss->irq = cinfo->irq; - ss->flags = info->port.flags; - ss->close_delay = info->port.close_delay; - ss->closing_wait = info->port.closing_wait; - ss->baud_base = info->baud; - ss->custom_divisor = info->custom_divisor; - return 0; -} - -static int cy_set_serial_info(struct tty_struct *tty, - struct serial_struct *ss) -{ - struct cyclades_port *info = tty->driver_data; - int old_flags; - int ret; - - if (serial_paranoia_check(info, tty->name, "cy_ioctl")) - return -ENODEV; - - mutex_lock(&info->port.mutex); - - old_flags = info->port.flags; - - if (!capable(CAP_SYS_ADMIN)) { - if (ss->close_delay != info->port.close_delay || - ss->baud_base != info->baud || - (ss->flags & ASYNC_FLAGS & - ~ASYNC_USR_MASK) != - (info->port.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)) - { - mutex_unlock(&info->port.mutex); - return -EPERM; - } - info->port.flags = (info->port.flags & ~ASYNC_USR_MASK) | - (ss->flags & ASYNC_USR_MASK); - info->baud = ss->baud_base; - info->custom_divisor = ss->custom_divisor; - goto check_and_exit; - } - - /* - * OK, past this point, all the error checking has been done. - * At this point, we start making changes..... - */ - - info->baud = ss->baud_base; - info->custom_divisor = ss->custom_divisor; - info->port.flags = (info->port.flags & ~ASYNC_FLAGS) | - (ss->flags & ASYNC_FLAGS); - info->port.close_delay = ss->close_delay * HZ / 100; - info->port.closing_wait = ss->closing_wait * HZ / 100; - -check_and_exit: - if (tty_port_initialized(&info->port)) { - if ((ss->flags ^ old_flags) & ASYNC_SPD_MASK) { - /* warn about deprecation unless clearing */ - if (ss->flags & ASYNC_SPD_MASK) - dev_warn_ratelimited(tty->dev, "use of SPD flags is deprecated\n"); - } - cy_set_line_char(info, tty); - ret = 0; - } else { - ret = cy_startup(info, tty); - } - mutex_unlock(&info->port.mutex); - return ret; -} /* set_serial_info */ - -/* - * get_lsr_info - get line status register info - * - * Purpose: Let user call ioctl() to get info when the UART physically - * is emptied. On bus types like RS485, the transmitter must - * release the bus after transmitting. This must be done when - * the transmit shift register is empty, not be done when the - * transmit holding register is empty. This functionality - * allows an RS485 driver to be written in user space. - */ -static int get_lsr_info(struct cyclades_port *info, unsigned int __user *value) -{ - struct cyclades_card *card = info->card; - unsigned int result; - unsigned long flags; - u8 status; - - if (!cy_is_Z(card)) { - spin_lock_irqsave(&card->card_lock, flags); - status = cyy_readb(info, CySRER) & (CyTxRdy | CyTxMpty); - spin_unlock_irqrestore(&card->card_lock, flags); - result = (status ? 0 : TIOCSER_TEMT); - } else { - /* Not supported yet */ - return -EINVAL; - } - return put_user(result, value); -} - -static int cy_tiocmget(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_card *card; - int result; - - if (serial_paranoia_check(info, tty->name, __func__)) - return -ENODEV; - - card = info->card; - - if (!cy_is_Z(card)) { - unsigned long flags; - int channel = info->line - card->first_line; - u8 status; - - spin_lock_irqsave(&card->card_lock, flags); - cyy_writeb(info, CyCAR, channel & 0x03); - status = cyy_readb(info, CyMSVR1); - status |= cyy_readb(info, CyMSVR2); - spin_unlock_irqrestore(&card->card_lock, flags); - - if (info->rtsdtr_inv) { - result = ((status & CyRTS) ? TIOCM_DTR : 0) | - ((status & CyDTR) ? TIOCM_RTS : 0); - } else { - result = ((status & CyRTS) ? TIOCM_RTS : 0) | - ((status & CyDTR) ? TIOCM_DTR : 0); - } - result |= ((status & CyDCD) ? TIOCM_CAR : 0) | - ((status & CyRI) ? TIOCM_RNG : 0) | - ((status & CyDSR) ? TIOCM_DSR : 0) | - ((status & CyCTS) ? TIOCM_CTS : 0); - } else { - u32 lstatus; - - if (!cyz_is_loaded(card)) { - result = -ENODEV; - goto end; - } - - lstatus = readl(&info->u.cyz.ch_ctrl->rs_status); - result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0) | - ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0) | - ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0) | - ((lstatus & C_RS_RI) ? TIOCM_RNG : 0) | - ((lstatus & C_RS_DSR) ? TIOCM_DSR : 0) | - ((lstatus & C_RS_CTS) ? TIOCM_CTS : 0); - } -end: - return result; -} /* cy_tiomget */ - -static int -cy_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_card *card; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, __func__)) - return -ENODEV; - - card = info->card; - if (!cy_is_Z(card)) { - spin_lock_irqsave(&card->card_lock, flags); - cyy_change_rts_dtr(info, set, clear); - spin_unlock_irqrestore(&card->card_lock, flags); - } else { - struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; - int retval, channel = info->line - card->first_line; - u32 rs; - - if (!cyz_is_loaded(card)) - return -ENODEV; - - spin_lock_irqsave(&card->card_lock, flags); - rs = readl(&ch_ctrl->rs_control); - if (set & TIOCM_RTS) - rs |= C_RS_RTS; - if (clear & TIOCM_RTS) - rs &= ~C_RS_RTS; - if (set & TIOCM_DTR) { - rs |= C_RS_DTR; -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_modem_info raising Z DTR\n"); -#endif - } - if (clear & TIOCM_DTR) { - rs &= ~C_RS_DTR; -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_modem_info clearing " - "Z DTR\n"); -#endif - } - cy_writel(&ch_ctrl->rs_control, rs); - retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L); - spin_unlock_irqrestore(&card->card_lock, flags); - if (retval != 0) { - printk(KERN_ERR "cyc:set_modem_info retval on ttyC%d " - "was %x\n", info->line, retval); - } - } - return 0; -} - -/* - * cy_break() --- routine which turns the break handling on or off - */ -static int cy_break(struct tty_struct *tty, int break_state) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_card *card; - unsigned long flags; - int retval = 0; - - if (serial_paranoia_check(info, tty->name, "cy_break")) - return -EINVAL; - - card = info->card; - - spin_lock_irqsave(&card->card_lock, flags); - if (!cy_is_Z(card)) { - /* Let the transmit ISR take care of this (since it - requires stuffing characters into the output stream). - */ - if (break_state == -1) { - if (!info->breakon) { - info->breakon = 1; - if (!info->xmit_cnt) { - spin_unlock_irqrestore(&card->card_lock, flags); - start_xmit(info); - spin_lock_irqsave(&card->card_lock, flags); - } - } - } else { - if (!info->breakoff) { - info->breakoff = 1; - if (!info->xmit_cnt) { - spin_unlock_irqrestore(&card->card_lock, flags); - start_xmit(info); - spin_lock_irqsave(&card->card_lock, flags); - } - } - } - } else { - if (break_state == -1) { - retval = cyz_issue_cmd(card, - info->line - card->first_line, - C_CM_SET_BREAK, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:cy_break (set) retval on " - "ttyC%d was %x\n", info->line, retval); - } - } else { - retval = cyz_issue_cmd(card, - info->line - card->first_line, - C_CM_CLR_BREAK, 0L); - if (retval != 0) { - printk(KERN_DEBUG "cyc:cy_break (clr) retval " - "on ttyC%d was %x\n", info->line, - retval); - } - } - } - spin_unlock_irqrestore(&card->card_lock, flags); - return retval; -} /* cy_break */ - -static int set_threshold(struct cyclades_port *info, unsigned long value) -{ - struct cyclades_card *card = info->card; - unsigned long flags; - - if (!cy_is_Z(card)) { - info->cor3 &= ~CyREC_FIFO; - info->cor3 |= value & CyREC_FIFO; - - spin_lock_irqsave(&card->card_lock, flags); - cyy_writeb(info, CyCOR3, info->cor3); - cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR3ch); - spin_unlock_irqrestore(&card->card_lock, flags); - } - return 0; -} /* set_threshold */ - -static int get_threshold(struct cyclades_port *info, - unsigned long __user *value) -{ - struct cyclades_card *card = info->card; - - if (!cy_is_Z(card)) { - u8 tmp = cyy_readb(info, CyCOR3) & CyREC_FIFO; - return put_user(tmp, value); - } - return 0; -} /* get_threshold */ - -static int set_timeout(struct cyclades_port *info, unsigned long value) -{ - struct cyclades_card *card = info->card; - unsigned long flags; - - if (!cy_is_Z(card)) { - spin_lock_irqsave(&card->card_lock, flags); - cyy_writeb(info, CyRTPR, value & 0xff); - spin_unlock_irqrestore(&card->card_lock, flags); - } - return 0; -} /* set_timeout */ - -static int get_timeout(struct cyclades_port *info, - unsigned long __user *value) -{ - struct cyclades_card *card = info->card; - - if (!cy_is_Z(card)) { - u8 tmp = cyy_readb(info, CyRTPR); - return put_user(tmp, value); - } - return 0; -} /* get_timeout */ - -static int cy_cflags_changed(struct cyclades_port *info, unsigned long arg, - struct cyclades_icount *cprev) -{ - struct cyclades_icount cnow; - unsigned long flags; - int ret; - - spin_lock_irqsave(&info->card->card_lock, flags); - cnow = info->icount; /* atomic copy */ - spin_unlock_irqrestore(&info->card->card_lock, flags); - - ret = ((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev->dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev->cts)); - - *cprev = cnow; - - return ret; -} - -/* - * This routine allows the tty driver to implement device- - * specific ioctl's. If the ioctl number passed in cmd is - * not recognized by the driver, it should return ENOIOCTLCMD. - */ -static int -cy_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_icount cnow; /* kernel counter temps */ - int ret_val = 0; - unsigned long flags; - void __user *argp = (void __user *)arg; - - if (serial_paranoia_check(info, tty->name, "cy_ioctl")) - return -ENODEV; - -#ifdef CY_DEBUG_OTHER - printk(KERN_DEBUG "cyc:cy_ioctl ttyC%d, cmd = %x arg = %lx\n", - info->line, cmd, arg); -#endif - - switch (cmd) { - case CYGETMON: - if (copy_to_user(argp, &info->mon, sizeof(info->mon))) { - ret_val = -EFAULT; - break; - } - memset(&info->mon, 0, sizeof(info->mon)); - break; - case CYGETTHRESH: - ret_val = get_threshold(info, argp); - break; - case CYSETTHRESH: - ret_val = set_threshold(info, arg); - break; - case CYGETDEFTHRESH: - ret_val = put_user(info->default_threshold, - (unsigned long __user *)argp); - break; - case CYSETDEFTHRESH: - info->default_threshold = arg & 0x0f; - break; - case CYGETTIMEOUT: - ret_val = get_timeout(info, argp); - break; - case CYSETTIMEOUT: - ret_val = set_timeout(info, arg); - break; - case CYGETDEFTIMEOUT: - ret_val = put_user(info->default_timeout, - (unsigned long __user *)argp); - break; - case CYSETDEFTIMEOUT: - info->default_timeout = arg & 0xff; - break; - case CYSETRFLOW: - info->rflow = (int)arg; - break; - case CYGETRFLOW: - ret_val = info->rflow; - break; - case CYSETRTSDTR_INV: - info->rtsdtr_inv = (int)arg; - break; - case CYGETRTSDTR_INV: - ret_val = info->rtsdtr_inv; - break; - case CYGETCD1400VER: - ret_val = info->chip_rev; - break; -#ifndef CONFIG_CYZ_INTR - case CYZSETPOLLCYCLE: - if (arg > LONG_MAX / HZ) - return -ENODEV; - cyz_polling_cycle = (arg * HZ) / 1000; - break; - case CYZGETPOLLCYCLE: - ret_val = (cyz_polling_cycle * 1000) / HZ; - break; -#endif /* CONFIG_CYZ_INTR */ - case CYSETWAIT: - info->port.closing_wait = (unsigned short)arg * HZ / 100; - break; - case CYGETWAIT: - ret_val = info->port.closing_wait / (HZ / 100); - break; - case TIOCSERGETLSR: /* Get line status register */ - ret_val = get_lsr_info(info, argp); - break; - /* - * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - * - mask passed in arg for lines of interest - * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) - * Caller should use TIOCGICOUNT to see which one it was - */ - case TIOCMIWAIT: - spin_lock_irqsave(&info->card->card_lock, flags); - /* note the counters on entry */ - cnow = info->icount; - spin_unlock_irqrestore(&info->card->card_lock, flags); - ret_val = wait_event_interruptible(info->port.delta_msr_wait, - cy_cflags_changed(info, arg, &cnow)); - break; - - /* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ - default: - ret_val = -ENOIOCTLCMD; - } - -#ifdef CY_DEBUG_OTHER - printk(KERN_DEBUG "cyc:cy_ioctl done\n"); -#endif - return ret_val; -} /* cy_ioctl */ - -static int cy_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *sic) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_icount cnow; /* Used to snapshot */ - unsigned long flags; - - spin_lock_irqsave(&info->card->card_lock, flags); - cnow = info->icount; - spin_unlock_irqrestore(&info->card->card_lock, flags); - - sic->cts = cnow.cts; - sic->dsr = cnow.dsr; - sic->rng = cnow.rng; - sic->dcd = cnow.dcd; - sic->rx = cnow.rx; - sic->tx = cnow.tx; - sic->frame = cnow.frame; - sic->overrun = cnow.overrun; - sic->parity = cnow.parity; - sic->brk = cnow.brk; - sic->buf_overrun = cnow.buf_overrun; - return 0; -} - -/* - * This routine allows the tty driver to be notified when - * device's termios settings have changed. Note that a - * well-designed tty driver should be prepared to accept the case - * where old == NULL, and try to do something rational. - */ -static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - struct cyclades_port *info = tty->driver_data; - -#ifdef CY_DEBUG_OTHER - printk(KERN_DEBUG "cyc:cy_set_termios ttyC%d\n", info->line); -#endif - - cy_set_line_char(info, tty); - - if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) { - tty->hw_stopped = 0; - cy_start(tty); - } -#if 0 - /* - * No need to wake up processes in open wait, since they - * sample the CLOCAL flag once, and don't recheck it. - * XXX It's not clear whether the current behavior is correct - * or not. Hence, this may change..... - */ - if (!(old_termios->c_cflag & CLOCAL) && C_CLOCAL(tty)) - wake_up_interruptible(&info->port.open_wait); -#endif -} /* cy_set_termios */ - -/* This function is used to send a high-priority XON/XOFF character to - the device. -*/ -static void cy_send_xchar(struct tty_struct *tty, char ch) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_card *card; - int channel; - - if (serial_paranoia_check(info, tty->name, "cy_send_xchar")) - return; - - info->x_char = ch; - - if (ch) - cy_start(tty); - - card = info->card; - channel = info->line - card->first_line; - - if (cy_is_Z(card)) { - if (ch == STOP_CHAR(tty)) - cyz_issue_cmd(card, channel, C_CM_SENDXOFF, 0L); - else if (ch == START_CHAR(tty)) - cyz_issue_cmd(card, channel, C_CM_SENDXON, 0L); - } -} - -/* This routine is called by the upper-layer tty layer to signal - that incoming characters should be throttled because the input - buffers are close to full. - */ -static void cy_throttle(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_card *card; - unsigned long flags; - -#ifdef CY_DEBUG_THROTTLE - printk(KERN_DEBUG "cyc:throttle %s ...ttyC%d\n", tty_name(tty), - info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_throttle")) - return; - - card = info->card; - - if (I_IXOFF(tty)) { - if (!cy_is_Z(card)) - cy_send_xchar(tty, STOP_CHAR(tty)); - else - info->throttle = 1; - } - - if (C_CRTSCTS(tty)) { - if (!cy_is_Z(card)) { - spin_lock_irqsave(&card->card_lock, flags); - cyy_change_rts_dtr(info, 0, TIOCM_RTS); - spin_unlock_irqrestore(&card->card_lock, flags); - } else { - info->throttle = 1; - } - } -} /* cy_throttle */ - -/* - * This routine notifies the tty driver that it should signal - * that characters can now be sent to the tty without fear of - * overrunning the input buffers of the line disciplines. - */ -static void cy_unthrottle(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_card *card; - unsigned long flags; - -#ifdef CY_DEBUG_THROTTLE - printk(KERN_DEBUG "cyc:unthrottle %s ...ttyC%d\n", - tty_name(tty), info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_unthrottle")) - return; - - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - cy_send_xchar(tty, START_CHAR(tty)); - } - - if (C_CRTSCTS(tty)) { - card = info->card; - if (!cy_is_Z(card)) { - spin_lock_irqsave(&card->card_lock, flags); - cyy_change_rts_dtr(info, TIOCM_RTS, 0); - spin_unlock_irqrestore(&card->card_lock, flags); - } else { - info->throttle = 0; - } - } -} /* cy_unthrottle */ - -/* cy_start and cy_stop provide software output flow control as a - function of XON/XOFF, software CTS, and other such stuff. -*/ -static void cy_stop(struct tty_struct *tty) -{ - struct cyclades_card *cinfo; - struct cyclades_port *info = tty->driver_data; - int channel; - unsigned long flags; - -#ifdef CY_DEBUG_OTHER - printk(KERN_DEBUG "cyc:cy_stop ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_stop")) - return; - - cinfo = info->card; - channel = info->line - cinfo->first_line; - if (!cy_is_Z(cinfo)) { - spin_lock_irqsave(&cinfo->card_lock, flags); - cyy_writeb(info, CyCAR, channel & 0x03); - cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy); - spin_unlock_irqrestore(&cinfo->card_lock, flags); - } -} /* cy_stop */ - -static void cy_start(struct tty_struct *tty) -{ - struct cyclades_card *cinfo; - struct cyclades_port *info = tty->driver_data; - int channel; - unsigned long flags; - -#ifdef CY_DEBUG_OTHER - printk(KERN_DEBUG "cyc:cy_start ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_start")) - return; - - cinfo = info->card; - channel = info->line - cinfo->first_line; - if (!cy_is_Z(cinfo)) { - spin_lock_irqsave(&cinfo->card_lock, flags); - cyy_writeb(info, CyCAR, channel & 0x03); - cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy); - spin_unlock_irqrestore(&cinfo->card_lock, flags); - } -} /* cy_start */ - -/* - * cy_hangup() --- called by tty_hangup() when a hangup is signaled. - */ -static void cy_hangup(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - -#ifdef CY_DEBUG_OTHER - printk(KERN_DEBUG "cyc:cy_hangup ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_hangup")) - return; - - cy_flush_buffer(tty); - cy_shutdown(info, tty); - tty_port_hangup(&info->port); -} /* cy_hangup */ - -static int cyy_carrier_raised(struct tty_port *port) -{ - struct cyclades_port *info = container_of(port, struct cyclades_port, - port); - struct cyclades_card *cinfo = info->card; - unsigned long flags; - int channel = info->line - cinfo->first_line; - u32 cd; - - spin_lock_irqsave(&cinfo->card_lock, flags); - cyy_writeb(info, CyCAR, channel & 0x03); - cd = cyy_readb(info, CyMSVR1) & CyDCD; - spin_unlock_irqrestore(&cinfo->card_lock, flags); - - return cd; -} - -static void cyy_dtr_rts(struct tty_port *port, int raise) -{ - struct cyclades_port *info = container_of(port, struct cyclades_port, - port); - struct cyclades_card *cinfo = info->card; - unsigned long flags; - - spin_lock_irqsave(&cinfo->card_lock, flags); - cyy_change_rts_dtr(info, raise ? TIOCM_RTS | TIOCM_DTR : 0, - raise ? 0 : TIOCM_RTS | TIOCM_DTR); - spin_unlock_irqrestore(&cinfo->card_lock, flags); -} - -static int cyz_carrier_raised(struct tty_port *port) -{ - struct cyclades_port *info = container_of(port, struct cyclades_port, - port); - - return readl(&info->u.cyz.ch_ctrl->rs_status) & C_RS_DCD; -} - -static void cyz_dtr_rts(struct tty_port *port, int raise) -{ - struct cyclades_port *info = container_of(port, struct cyclades_port, - port); - struct cyclades_card *cinfo = info->card; - struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; - int ret, channel = info->line - cinfo->first_line; - u32 rs; - - rs = readl(&ch_ctrl->rs_control); - if (raise) - rs |= C_RS_RTS | C_RS_DTR; - else - rs &= ~(C_RS_RTS | C_RS_DTR); - cy_writel(&ch_ctrl->rs_control, rs); - ret = cyz_issue_cmd(cinfo, channel, C_CM_IOCTLM, 0L); - if (ret != 0) - printk(KERN_ERR "%s: retval on ttyC%d was %x\n", - __func__, info->line, ret); -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "%s: raising Z DTR\n", __func__); -#endif -} - -static const struct tty_port_operations cyy_port_ops = { - .carrier_raised = cyy_carrier_raised, - .dtr_rts = cyy_dtr_rts, - .shutdown = cy_do_close, -}; - -static const struct tty_port_operations cyz_port_ops = { - .carrier_raised = cyz_carrier_raised, - .dtr_rts = cyz_dtr_rts, - .shutdown = cy_do_close, -}; - -/* - * --------------------------------------------------------------------- - * cy_init() and friends - * - * cy_init() is called at boot-time to initialize the serial driver. - * --------------------------------------------------------------------- - */ - -static int cy_init_card(struct cyclades_card *cinfo) -{ - struct cyclades_port *info; - unsigned int channel, port; - - spin_lock_init(&cinfo->card_lock); - cinfo->intr_enabled = 0; - - cinfo->ports = kcalloc(cinfo->nports, sizeof(*cinfo->ports), - GFP_KERNEL); - if (cinfo->ports == NULL) { - printk(KERN_ERR "Cyclades: cannot allocate ports\n"); - return -ENOMEM; - } - - for (channel = 0, port = cinfo->first_line; channel < cinfo->nports; - channel++, port++) { - info = &cinfo->ports[channel]; - tty_port_init(&info->port); - info->magic = CYCLADES_MAGIC; - info->card = cinfo; - info->line = port; - - info->port.closing_wait = CLOSING_WAIT_DELAY; - info->port.close_delay = 5 * HZ / 10; - init_completion(&info->shutdown_wait); - - if (cy_is_Z(cinfo)) { - struct FIRM_ID *firm_id = cinfo->base_addr + ID_ADDRESS; - struct ZFW_CTRL *zfw_ctrl; - - info->port.ops = &cyz_port_ops; - info->type = PORT_STARTECH; - - zfw_ctrl = cinfo->base_addr + - (readl(&firm_id->zfwctrl_addr) & 0xfffff); - info->u.cyz.ch_ctrl = &zfw_ctrl->ch_ctrl[channel]; - info->u.cyz.buf_ctrl = &zfw_ctrl->buf_ctrl[channel]; - - if (cinfo->hw_ver == ZO_V1) - info->xmit_fifo_size = CYZ_FIFO_SIZE; - else - info->xmit_fifo_size = 4 * CYZ_FIFO_SIZE; -#ifdef CONFIG_CYZ_INTR - timer_setup(&info->rx_full_timer, cyz_rx_restart, 0); -#endif - } else { - unsigned short chip_number; - int index = cinfo->bus_index; - - info->port.ops = &cyy_port_ops; - info->type = PORT_CIRRUS; - info->xmit_fifo_size = CyMAX_CHAR_FIFO; - info->cor1 = CyPARITY_NONE | Cy_1_STOP | Cy_8_BITS; - info->cor2 = CyETC; - info->cor3 = 0x08; /* _very_ small rcv threshold */ - - chip_number = channel / CyPORTS_PER_CHIP; - info->u.cyy.base_addr = cinfo->base_addr + - (cy_chip_offset[chip_number] << index); - info->chip_rev = cyy_readb(info, CyGFRCR); - - if (info->chip_rev >= CD1400_REV_J) { - /* It is a CD1400 rev. J or later */ - info->tbpr = baud_bpr_60[13]; /* Tx BPR */ - info->tco = baud_co_60[13]; /* Tx CO */ - info->rbpr = baud_bpr_60[13]; /* Rx BPR */ - info->rco = baud_co_60[13]; /* Rx CO */ - info->rtsdtr_inv = 1; - } else { - info->tbpr = baud_bpr_25[13]; /* Tx BPR */ - info->tco = baud_co_25[13]; /* Tx CO */ - info->rbpr = baud_bpr_25[13]; /* Rx BPR */ - info->rco = baud_co_25[13]; /* Rx CO */ - info->rtsdtr_inv = 0; - } - info->read_status_mask = CyTIMEOUT | CySPECHAR | - CyBREAK | CyPARITY | CyFRAME | CyOVERRUN; - } - - } - -#ifndef CONFIG_CYZ_INTR - if (cy_is_Z(cinfo) && !timer_pending(&cyz_timerlist)) { - mod_timer(&cyz_timerlist, jiffies + 1); -#ifdef CY_PCI_DEBUG - printk(KERN_DEBUG "Cyclades-Z polling initialized\n"); -#endif - } -#endif - return 0; -} - -/* initialize chips on Cyclom-Y card -- return number of valid - chips (which is number of ports/4) */ -static unsigned short cyy_init_card(void __iomem *true_base_addr, - int index) -{ - unsigned int chip_number; - void __iomem *base_addr; - - cy_writeb(true_base_addr + (Cy_HwReset << index), 0); - /* Cy_HwReset is 0x1400 */ - cy_writeb(true_base_addr + (Cy_ClrIntr << index), 0); - /* Cy_ClrIntr is 0x1800 */ - udelay(500L); - - for (chip_number = 0; chip_number < CyMAX_CHIPS_PER_CARD; - chip_number++) { - base_addr = - true_base_addr + (cy_chip_offset[chip_number] << index); - mdelay(1); - if (readb(base_addr + (CyCCR << index)) != 0x00) { - /************* - printk(" chip #%d at %#6lx is never idle (CCR != 0)\n", - chip_number, (unsigned long)base_addr); - *************/ - return chip_number; - } - - cy_writeb(base_addr + (CyGFRCR << index), 0); - udelay(10L); - - /* The Cyclom-16Y does not decode address bit 9 and therefore - cannot distinguish between references to chip 0 and a non- - existent chip 4. If the preceding clearing of the supposed - chip 4 GFRCR register appears at chip 0, there is no chip 4 - and this must be a Cyclom-16Y, not a Cyclom-32Ye. - */ - if (chip_number == 4 && readb(true_base_addr + - (cy_chip_offset[0] << index) + - (CyGFRCR << index)) == 0) { - return chip_number; - } - - cy_writeb(base_addr + (CyCCR << index), CyCHIP_RESET); - mdelay(1); - - if (readb(base_addr + (CyGFRCR << index)) == 0x00) { - /* - printk(" chip #%d at %#6lx is not responding ", - chip_number, (unsigned long)base_addr); - printk("(GFRCR stayed 0)\n", - */ - return chip_number; - } - if ((0xf0 & (readb(base_addr + (CyGFRCR << index)))) != - 0x40) { - /* - printk(" chip #%d at %#6lx is not valid (GFRCR == " - "%#2x)\n", - chip_number, (unsigned long)base_addr, - base_addr[CyGFRCR<<index]); - */ - return chip_number; - } - cy_writeb(base_addr + (CyGCR << index), CyCH0_SERIAL); - if (readb(base_addr + (CyGFRCR << index)) >= CD1400_REV_J) { - /* It is a CD1400 rev. J or later */ - /* Impossible to reach 5ms with this chip. - Changed to 2ms instead (f = 500 Hz). */ - cy_writeb(base_addr + (CyPPR << index), CyCLOCK_60_2MS); - } else { - /* f = 200 Hz */ - cy_writeb(base_addr + (CyPPR << index), CyCLOCK_25_5MS); - } - - /* - printk(" chip #%d at %#6lx is rev 0x%2x\n", - chip_number, (unsigned long)base_addr, - readb(base_addr+(CyGFRCR<<index))); - */ - } - return chip_number; -} /* cyy_init_card */ - -/* - * --------------------------------------------------------------------- - * cy_detect_isa() - Probe for Cyclom-Y/ISA boards. - * sets global variables and return the number of ISA boards found. - * --------------------------------------------------------------------- - */ -static int __init cy_detect_isa(void) -{ -#ifdef CONFIG_ISA - struct cyclades_card *card; - unsigned short cy_isa_irq, nboard; - void __iomem *cy_isa_address; - unsigned short i, j, k, cy_isa_nchan; - int isparam = 0; - - nboard = 0; - - /* Check for module parameters */ - for (i = 0; i < NR_CARDS; i++) { - if (maddr[i] || i) { - isparam = 1; - cy_isa_addresses[i] = maddr[i]; - } - if (!maddr[i]) - break; - } - - /* scan the address table probing for Cyclom-Y/ISA boards */ - for (i = 0; i < NR_ISA_ADDRS; i++) { - unsigned int isa_address = cy_isa_addresses[i]; - if (isa_address == 0x0000) - return nboard; - - /* probe for CD1400... */ - cy_isa_address = ioremap(isa_address, CyISA_Ywin); - if (cy_isa_address == NULL) { - printk(KERN_ERR "Cyclom-Y/ISA: can't remap base " - "address\n"); - continue; - } - cy_isa_nchan = CyPORTS_PER_CHIP * - cyy_init_card(cy_isa_address, 0); - if (cy_isa_nchan == 0) { - iounmap(cy_isa_address); - continue; - } - - if (isparam && i < NR_CARDS && irq[i]) - cy_isa_irq = irq[i]; - else - /* find out the board's irq by probing */ - cy_isa_irq = detect_isa_irq(cy_isa_address); - if (cy_isa_irq == 0) { - printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but the " - "IRQ could not be detected.\n", - (unsigned long)cy_isa_address); - iounmap(cy_isa_address); - continue; - } - - if ((cy_next_channel + cy_isa_nchan) > NR_PORTS) { - printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but no " - "more channels are available. Change NR_PORTS " - "in cyclades.c and recompile kernel.\n", - (unsigned long)cy_isa_address); - iounmap(cy_isa_address); - return nboard; - } - /* fill the next cy_card structure available */ - for (j = 0; j < NR_CARDS; j++) { - card = &cy_card[j]; - if (card->base_addr == NULL) - break; - } - if (j == NR_CARDS) { /* no more cy_cards available */ - printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but no " - "more cards can be used. Change NR_CARDS in " - "cyclades.c and recompile kernel.\n", - (unsigned long)cy_isa_address); - iounmap(cy_isa_address); - return nboard; - } - - /* allocate IRQ */ - if (request_irq(cy_isa_irq, cyy_interrupt, - 0, "Cyclom-Y", card)) { - printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but " - "could not allocate IRQ#%d.\n", - (unsigned long)cy_isa_address, cy_isa_irq); - iounmap(cy_isa_address); - return nboard; - } - - /* set cy_card */ - card->base_addr = cy_isa_address; - card->ctl_addr.p9050 = NULL; - card->irq = (int)cy_isa_irq; - card->bus_index = 0; - card->first_line = cy_next_channel; - card->num_chips = cy_isa_nchan / CyPORTS_PER_CHIP; - card->nports = cy_isa_nchan; - if (cy_init_card(card)) { - card->base_addr = NULL; - free_irq(cy_isa_irq, card); - iounmap(cy_isa_address); - continue; - } - nboard++; - - printk(KERN_INFO "Cyclom-Y/ISA #%d: 0x%lx-0x%lx, IRQ%d found: " - "%d channels starting from port %d\n", - j + 1, (unsigned long)cy_isa_address, - (unsigned long)(cy_isa_address + (CyISA_Ywin - 1)), - cy_isa_irq, cy_isa_nchan, cy_next_channel); - - for (k = 0, j = cy_next_channel; - j < cy_next_channel + cy_isa_nchan; j++, k++) - tty_port_register_device(&card->ports[k].port, - cy_serial_driver, j, NULL); - cy_next_channel += cy_isa_nchan; - } - return nboard; -#else - return 0; -#endif /* CONFIG_ISA */ -} /* cy_detect_isa */ - -#ifdef CONFIG_PCI -static inline int cyc_isfwstr(const char *str, unsigned int size) -{ - unsigned int a; - - for (a = 0; a < size && *str; a++, str++) - if (*str & 0x80) - return -EINVAL; - - for (; a < size; a++, str++) - if (*str) - return -EINVAL; - - return 0; -} - -static inline void cyz_fpga_copy(void __iomem *fpga, const u8 *data, - unsigned int size) -{ - for (; size > 0; size--) { - cy_writel(fpga, *data++); - udelay(10); - } -} - -static void plx_init(struct pci_dev *pdev, int irq, - struct RUNTIME_9060 __iomem *addr) -{ - /* Reset PLX */ - cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x40000000); - udelay(100L); - cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x40000000); - - /* Reload Config. Registers from EEPROM */ - cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x20000000); - udelay(100L); - cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x20000000); - - /* For some yet unknown reason, once the PLX9060 reloads the EEPROM, - * the IRQ is lost and, thus, we have to re-write it to the PCI config. - * registers. This will remain here until we find a permanent fix. - */ - pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, irq); -} - -static int __cyz_load_fw(const struct firmware *fw, - const char *name, const u32 mailbox, void __iomem *base, - void __iomem *fpga) -{ - const void *ptr = fw->data; - const struct zfile_header *h = ptr; - const struct zfile_config *c, *cs; - const struct zfile_block *b, *bs; - unsigned int a, tmp, len = fw->size; -#define BAD_FW KERN_ERR "Bad firmware: " - if (len < sizeof(*h)) { - printk(BAD_FW "too short: %u<%zu\n", len, sizeof(*h)); - return -EINVAL; - } - - cs = ptr + h->config_offset; - bs = ptr + h->block_offset; - - if ((void *)(cs + h->n_config) > ptr + len || - (void *)(bs + h->n_blocks) > ptr + len) { - printk(BAD_FW "too short"); - return -EINVAL; - } - - if (cyc_isfwstr(h->name, sizeof(h->name)) || - cyc_isfwstr(h->date, sizeof(h->date))) { - printk(BAD_FW "bad formatted header string\n"); - return -EINVAL; - } - - if (strncmp(name, h->name, sizeof(h->name))) { - printk(BAD_FW "bad name '%s' (expected '%s')\n", h->name, name); - return -EINVAL; - } - - tmp = 0; - for (c = cs; c < cs + h->n_config; c++) { - for (a = 0; a < c->n_blocks; a++) - if (c->block_list[a] > h->n_blocks) { - printk(BAD_FW "bad block ref number in cfgs\n"); - return -EINVAL; - } - if (c->mailbox == mailbox && c->function == 0) /* 0 is normal */ - tmp++; - } - if (!tmp) { - printk(BAD_FW "nothing appropriate\n"); - return -EINVAL; - } - - for (b = bs; b < bs + h->n_blocks; b++) - if (b->file_offset + b->size > len) { - printk(BAD_FW "bad block data offset\n"); - return -EINVAL; - } - - /* everything is OK, let's seek'n'load it */ - for (c = cs; c < cs + h->n_config; c++) - if (c->mailbox == mailbox && c->function == 0) - break; - - for (a = 0; a < c->n_blocks; a++) { - b = &bs[c->block_list[a]]; - if (b->type == ZBLOCK_FPGA) { - if (fpga != NULL) - cyz_fpga_copy(fpga, ptr + b->file_offset, - b->size); - } else { - if (base != NULL) - memcpy_toio(base + b->ram_offset, - ptr + b->file_offset, b->size); - } - } -#undef BAD_FW - return 0; -} - -static int cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr, - struct RUNTIME_9060 __iomem *ctl_addr, int irq) -{ - const struct firmware *fw; - struct FIRM_ID __iomem *fid = base_addr + ID_ADDRESS; - struct CUSTOM_REG __iomem *cust = base_addr; - struct ZFW_CTRL __iomem *pt_zfwctrl; - void __iomem *tmp; - u32 mailbox, status, nchan; - unsigned int i; - int retval; - - retval = request_firmware(&fw, "cyzfirm.bin", &pdev->dev); - if (retval) { - dev_err(&pdev->dev, "can't get firmware\n"); - goto err; - } - - /* Check whether the firmware is already loaded and running. If - positive, skip this board */ - if (__cyz_fpga_loaded(ctl_addr) && readl(&fid->signature) == ZFIRM_ID) { - u32 cntval = readl(base_addr + 0x190); - - udelay(100); - if (cntval != readl(base_addr + 0x190)) { - /* FW counter is working, FW is running */ - dev_dbg(&pdev->dev, "Cyclades-Z FW already loaded. " - "Skipping board.\n"); - retval = 0; - goto err_rel; - } - } - - /* start boot */ - cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) & - ~0x00030800UL); - - mailbox = readl(&ctl_addr->mail_box_0); - - if (mailbox == 0 || __cyz_fpga_loaded(ctl_addr)) { - /* stops CPU and set window to beginning of RAM */ - cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); - cy_writel(&cust->cpu_stop, 0); - cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); - udelay(100); - } - - plx_init(pdev, irq, ctl_addr); - - if (mailbox != 0) { - /* load FPGA */ - retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, NULL, - base_addr); - if (retval) - goto err_rel; - if (!__cyz_fpga_loaded(ctl_addr)) { - dev_err(&pdev->dev, "fw upload successful, but fw is " - "not loaded\n"); - goto err_rel; - } - } - - /* stops CPU and set window to beginning of RAM */ - cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); - cy_writel(&cust->cpu_stop, 0); - cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); - udelay(100); - - /* clear memory */ - for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++) - cy_writeb(tmp, 255); - if (mailbox != 0) { - /* set window to last 512K of RAM */ - cy_writel(&ctl_addr->loc_addr_base, WIN_RAM + RAM_SIZE); - for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++) - cy_writeb(tmp, 255); - /* set window to beginning of RAM */ - cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); - } - - retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, base_addr, NULL); - release_firmware(fw); - if (retval) - goto err; - - /* finish boot and start boards */ - cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); - cy_writel(&cust->cpu_start, 0); - cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); - i = 0; - while ((status = readl(&fid->signature)) != ZFIRM_ID && i++ < 40) - msleep(100); - if (status != ZFIRM_ID) { - if (status == ZFIRM_HLT) { - dev_err(&pdev->dev, "you need an external power supply " - "for this number of ports. Firmware halted and " - "board reset.\n"); - retval = -EIO; - goto err; - } - dev_warn(&pdev->dev, "fid->signature = 0x%x... Waiting " - "some more time\n", status); - while ((status = readl(&fid->signature)) != ZFIRM_ID && - i++ < 200) - msleep(100); - if (status != ZFIRM_ID) { - dev_err(&pdev->dev, "Board not started in 20 seconds! " - "Giving up. (fid->signature = 0x%x)\n", - status); - dev_info(&pdev->dev, "*** Warning ***: if you are " - "upgrading the FW, please power cycle the " - "system before loading the new FW to the " - "Cyclades-Z.\n"); - - if (__cyz_fpga_loaded(ctl_addr)) - plx_init(pdev, irq, ctl_addr); - - retval = -EIO; - goto err; - } - dev_dbg(&pdev->dev, "Firmware started after %d seconds.\n", - i / 10); - } - pt_zfwctrl = base_addr + readl(&fid->zfwctrl_addr); - - dev_dbg(&pdev->dev, "fid=> %p, zfwctrl_addr=> %x, npt_zfwctrl=> %p\n", - base_addr + ID_ADDRESS, readl(&fid->zfwctrl_addr), - base_addr + readl(&fid->zfwctrl_addr)); - - nchan = readl(&pt_zfwctrl->board_ctrl.n_channel); - dev_info(&pdev->dev, "Cyclades-Z FW loaded: version = %x, ports = %u\n", - readl(&pt_zfwctrl->board_ctrl.fw_version), nchan); - - if (nchan == 0) { - dev_warn(&pdev->dev, "no Cyclades-Z ports were found. Please " - "check the connection between the Z host card and the " - "serial expanders.\n"); - - if (__cyz_fpga_loaded(ctl_addr)) - plx_init(pdev, irq, ctl_addr); - - dev_info(&pdev->dev, "Null number of ports detected. Board " - "reset.\n"); - retval = 0; - goto err; - } - - cy_writel(&pt_zfwctrl->board_ctrl.op_system, C_OS_LINUX); - cy_writel(&pt_zfwctrl->board_ctrl.dr_version, DRIVER_VERSION); - - /* - Early firmware failed to start looking for commands. - This enables firmware interrupts for those commands. - */ - cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) | - (1 << 17)); - cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) | - 0x00030800UL); - - return nchan; -err_rel: - release_firmware(fw); -err: - return retval; -} - -static int cy_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct cyclades_card *card; - void __iomem *addr0 = NULL, *addr2 = NULL; - char *card_name = NULL; - u32 mailbox; - unsigned int device_id, nchan = 0, card_no, i, j; - unsigned char plx_ver; - int retval, irq; - - retval = pci_enable_device(pdev); - if (retval) { - dev_err(&pdev->dev, "cannot enable device\n"); - goto err; - } - - /* read PCI configuration area */ - irq = pdev->irq; - device_id = pdev->device & ~PCI_DEVICE_ID_MASK; - -#if defined(__alpha__) - if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */ - dev_err(&pdev->dev, "Cyclom-Y/PCI not supported for low " - "addresses on Alpha systems.\n"); - retval = -EIO; - goto err_dis; - } -#endif - if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo) { - dev_err(&pdev->dev, "Cyclades-Z/PCI not supported for low " - "addresses\n"); - retval = -EIO; - goto err_dis; - } - - if (pci_resource_flags(pdev, 2) & IORESOURCE_IO) { - dev_warn(&pdev->dev, "PCI I/O bit incorrectly set. Ignoring " - "it...\n"); - pdev->resource[2].flags &= ~IORESOURCE_IO; - } - - retval = pci_request_regions(pdev, "cyclades"); - if (retval) { - dev_err(&pdev->dev, "failed to reserve resources\n"); - goto err_dis; - } - - retval = -EIO; - if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || - device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { - card_name = "Cyclom-Y"; - - addr0 = ioremap(pci_resource_start(pdev, 0), - CyPCI_Yctl); - if (addr0 == NULL) { - dev_err(&pdev->dev, "can't remap ctl region\n"); - goto err_reg; - } - addr2 = ioremap(pci_resource_start(pdev, 2), - CyPCI_Ywin); - if (addr2 == NULL) { - dev_err(&pdev->dev, "can't remap base region\n"); - goto err_unmap; - } - - nchan = CyPORTS_PER_CHIP * cyy_init_card(addr2, 1); - if (nchan == 0) { - dev_err(&pdev->dev, "Cyclom-Y PCI host card with no " - "Serial-Modules\n"); - goto err_unmap; - } - } else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi) { - struct RUNTIME_9060 __iomem *ctl_addr; - - ctl_addr = addr0 = ioremap(pci_resource_start(pdev, 0), - CyPCI_Zctl); - if (addr0 == NULL) { - dev_err(&pdev->dev, "can't remap ctl region\n"); - goto err_reg; - } - - /* Disable interrupts on the PLX before resetting it */ - cy_writew(&ctl_addr->intr_ctrl_stat, - readw(&ctl_addr->intr_ctrl_stat) & ~0x0900); - - plx_init(pdev, irq, addr0); - - mailbox = readl(&ctl_addr->mail_box_0); - - addr2 = ioremap(pci_resource_start(pdev, 2), - mailbox == ZE_V1 ? CyPCI_Ze_win : CyPCI_Zwin); - if (addr2 == NULL) { - dev_err(&pdev->dev, "can't remap base region\n"); - goto err_unmap; - } - - if (mailbox == ZE_V1) { - card_name = "Cyclades-Ze"; - } else { - card_name = "Cyclades-8Zo"; -#ifdef CY_PCI_DEBUG - if (mailbox == ZO_V1) { - cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); - dev_info(&pdev->dev, "Cyclades-8Zo/PCI: FPGA " - "id %lx, ver %lx\n", (ulong)(0xff & - readl(&((struct CUSTOM_REG *)addr2)-> - fpga_id)), (ulong)(0xff & - readl(&((struct CUSTOM_REG *)addr2)-> - fpga_version))); - cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); - } else { - dev_info(&pdev->dev, "Cyclades-Z/PCI: New " - "Cyclades-Z board. FPGA not loaded\n"); - } -#endif - /* The following clears the firmware id word. This - ensures that the driver will not attempt to talk to - the board until it has been properly initialized. - */ - if ((mailbox == ZO_V1) || (mailbox == ZO_V2)) - cy_writel(addr2 + ID_ADDRESS, 0L); - } - - retval = cyz_load_fw(pdev, addr2, addr0, irq); - if (retval <= 0) - goto err_unmap; - nchan = retval; - } - - if ((cy_next_channel + nchan) > NR_PORTS) { - dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no " - "channels are available. Change NR_PORTS in " - "cyclades.c and recompile kernel.\n"); - goto err_unmap; - } - /* fill the next cy_card structure available */ - for (card_no = 0; card_no < NR_CARDS; card_no++) { - card = &cy_card[card_no]; - if (card->base_addr == NULL) - break; - } - if (card_no == NR_CARDS) { /* no more cy_cards available */ - dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no " - "more cards can be used. Change NR_CARDS in " - "cyclades.c and recompile kernel.\n"); - goto err_unmap; - } - - if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || - device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { - /* allocate IRQ */ - retval = request_irq(irq, cyy_interrupt, - IRQF_SHARED, "Cyclom-Y", card); - if (retval) { - dev_err(&pdev->dev, "could not allocate IRQ\n"); - goto err_unmap; - } - card->num_chips = nchan / CyPORTS_PER_CHIP; - } else { - struct FIRM_ID __iomem *firm_id = addr2 + ID_ADDRESS; - struct ZFW_CTRL __iomem *zfw_ctrl; - - zfw_ctrl = addr2 + (readl(&firm_id->zfwctrl_addr) & 0xfffff); - - card->hw_ver = mailbox; - card->num_chips = (unsigned int)-1; - card->board_ctrl = &zfw_ctrl->board_ctrl; -#ifdef CONFIG_CYZ_INTR - /* allocate IRQ only if board has an IRQ */ - if (irq != 0 && irq != 255) { - retval = request_irq(irq, cyz_interrupt, - IRQF_SHARED, "Cyclades-Z", card); - if (retval) { - dev_err(&pdev->dev, "could not allocate IRQ\n"); - goto err_unmap; - } - } -#endif /* CONFIG_CYZ_INTR */ - } - - /* set cy_card */ - card->base_addr = addr2; - card->ctl_addr.p9050 = addr0; - card->irq = irq; - card->bus_index = 1; - card->first_line = cy_next_channel; - card->nports = nchan; - retval = cy_init_card(card); - if (retval) - goto err_null; - - pci_set_drvdata(pdev, card); - - if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || - device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { - /* enable interrupts in the PCI interface */ - plx_ver = readb(addr2 + CyPLX_VER) & 0x0f; - switch (plx_ver) { - case PLX_9050: - cy_writeb(addr0 + 0x4c, 0x43); - break; - - case PLX_9060: - case PLX_9080: - default: /* Old boards, use PLX_9060 */ - { - struct RUNTIME_9060 __iomem *ctl_addr = addr0; - plx_init(pdev, irq, ctl_addr); - cy_writew(&ctl_addr->intr_ctrl_stat, - readw(&ctl_addr->intr_ctrl_stat) | 0x0900); - break; - } - } - } - - dev_info(&pdev->dev, "%s/PCI #%d found: %d channels starting from " - "port %d.\n", card_name, card_no + 1, nchan, cy_next_channel); - for (j = 0, i = cy_next_channel; i < cy_next_channel + nchan; i++, j++) - tty_port_register_device(&card->ports[j].port, - cy_serial_driver, i, &pdev->dev); - cy_next_channel += nchan; - - return 0; -err_null: - card->base_addr = NULL; - free_irq(irq, card); -err_unmap: - iounmap(addr0); - if (addr2) - iounmap(addr2); -err_reg: - pci_release_regions(pdev); -err_dis: - pci_disable_device(pdev); -err: - return retval; -} - -static void cy_pci_remove(struct pci_dev *pdev) -{ - struct cyclades_card *cinfo = pci_get_drvdata(pdev); - unsigned int i, channel; - - /* non-Z with old PLX */ - if (!cy_is_Z(cinfo) && (readb(cinfo->base_addr + CyPLX_VER) & 0x0f) == - PLX_9050) - cy_writeb(cinfo->ctl_addr.p9050 + 0x4c, 0); - else -#ifndef CONFIG_CYZ_INTR - if (!cy_is_Z(cinfo)) -#endif - cy_writew(&cinfo->ctl_addr.p9060->intr_ctrl_stat, - readw(&cinfo->ctl_addr.p9060->intr_ctrl_stat) & - ~0x0900); - - iounmap(cinfo->base_addr); - if (cinfo->ctl_addr.p9050) - iounmap(cinfo->ctl_addr.p9050); - if (cinfo->irq -#ifndef CONFIG_CYZ_INTR - && !cy_is_Z(cinfo) -#endif /* CONFIG_CYZ_INTR */ - ) - free_irq(cinfo->irq, cinfo); - pci_release_regions(pdev); - - cinfo->base_addr = NULL; - for (channel = 0, i = cinfo->first_line; i < cinfo->first_line + - cinfo->nports; i++, channel++) { - tty_unregister_device(cy_serial_driver, i); - tty_port_destroy(&cinfo->ports[channel].port); - } - cinfo->nports = 0; - kfree(cinfo->ports); -} - -static struct pci_driver cy_pci_driver = { - .name = "cyclades", - .id_table = cy_pci_dev_id, - .probe = cy_pci_probe, - .remove = cy_pci_remove -}; -#endif - -static int cyclades_proc_show(struct seq_file *m, void *v) -{ - struct cyclades_port *info; - unsigned int i, j; - __u32 cur_jifs = jiffies; - - seq_puts(m, "Dev TimeOpen BytesOut IdleOut BytesIn " - "IdleIn Overruns Ldisc\n"); - - /* Output one line for each known port */ - for (i = 0; i < NR_CARDS; i++) - for (j = 0; j < cy_card[i].nports; j++) { - info = &cy_card[i].ports[j]; - - if (info->port.count) { - /* XXX is the ldisc num worth this? */ - struct tty_struct *tty; - struct tty_ldisc *ld; - int num = 0; - tty = tty_port_tty_get(&info->port); - if (tty) { - ld = tty_ldisc_ref(tty); - if (ld) { - num = ld->ops->num; - tty_ldisc_deref(ld); - } - tty_kref_put(tty); - } - seq_printf(m, "%3d %8lu %10lu %8lu " - "%10lu %8lu %9lu %6d\n", info->line, - (cur_jifs - info->idle_stats.in_use) / - HZ, info->idle_stats.xmit_bytes, - (cur_jifs - info->idle_stats.xmit_idle)/ - HZ, info->idle_stats.recv_bytes, - (cur_jifs - info->idle_stats.recv_idle)/ - HZ, info->idle_stats.overruns, - num); - } else - seq_printf(m, "%3d %8lu %10lu %8lu " - "%10lu %8lu %9lu %6ld\n", - info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L); - } - return 0; -} - -/* The serial driver boot-time initialization code! - Hardware I/O ports are mapped to character special devices on a - first found, first allocated manner. That is, this code searches - for Cyclom cards in the system. As each is found, it is probed - to discover how many chips (and thus how many ports) are present. - These ports are mapped to the tty ports 32 and upward in monotonic - fashion. If an 8-port card is replaced with a 16-port card, the - port mapping on a following card will shift. - - This approach is different from what is used in the other serial - device driver because the Cyclom is more properly a multiplexer, - not just an aggregation of serial ports on one card. - - If there are more cards with more ports than have been - statically allocated above, a warning is printed and the - extra ports are ignored. - */ - -static const struct tty_operations cy_ops = { - .open = cy_open, - .close = cy_close, - .write = cy_write, - .put_char = cy_put_char, - .flush_chars = cy_flush_chars, - .write_room = cy_write_room, - .chars_in_buffer = cy_chars_in_buffer, - .flush_buffer = cy_flush_buffer, - .ioctl = cy_ioctl, - .throttle = cy_throttle, - .unthrottle = cy_unthrottle, - .set_termios = cy_set_termios, - .stop = cy_stop, - .start = cy_start, - .hangup = cy_hangup, - .break_ctl = cy_break, - .wait_until_sent = cy_wait_until_sent, - .tiocmget = cy_tiocmget, - .tiocmset = cy_tiocmset, - .get_icount = cy_get_icount, - .set_serial = cy_set_serial_info, - .get_serial = cy_get_serial_info, - .proc_show = cyclades_proc_show, -}; - -static int __init cy_init(void) -{ - unsigned int nboards; - int retval = -ENOMEM; - - cy_serial_driver = alloc_tty_driver(NR_PORTS); - if (!cy_serial_driver) - goto err; - - printk(KERN_INFO "Cyclades driver " CY_VERSION "\n"); - - /* Initialize the tty_driver structure */ - - cy_serial_driver->driver_name = "cyclades"; - cy_serial_driver->name = "ttyC"; - cy_serial_driver->major = CYCLADES_MAJOR; - cy_serial_driver->minor_start = 0; - cy_serial_driver->type = TTY_DRIVER_TYPE_SERIAL; - cy_serial_driver->subtype = SERIAL_TYPE_NORMAL; - cy_serial_driver->init_termios = tty_std_termios; - cy_serial_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - cy_serial_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - tty_set_operations(cy_serial_driver, &cy_ops); - - retval = tty_register_driver(cy_serial_driver); - if (retval) { - printk(KERN_ERR "Couldn't register Cyclades serial driver\n"); - goto err_frtty; - } - - /* the code below is responsible to find the boards. Each different - type of board has its own detection routine. If a board is found, - the next cy_card structure available is set by the detection - routine. These functions are responsible for checking the - availability of cy_card and cy_port data structures and updating - the cy_next_channel. */ - - /* look for isa boards */ - nboards = cy_detect_isa(); - -#ifdef CONFIG_PCI - /* look for pci boards */ - retval = pci_register_driver(&cy_pci_driver); - if (retval && !nboards) { - tty_unregister_driver(cy_serial_driver); - goto err_frtty; - } -#endif - - return 0; -err_frtty: - put_tty_driver(cy_serial_driver); -err: - return retval; -} /* cy_init */ - -static void __exit cy_cleanup_module(void) -{ - struct cyclades_card *card; - unsigned int i, e1; - -#ifndef CONFIG_CYZ_INTR - del_timer_sync(&cyz_timerlist); -#endif /* CONFIG_CYZ_INTR */ - - e1 = tty_unregister_driver(cy_serial_driver); - if (e1) - printk(KERN_ERR "failed to unregister Cyclades serial " - "driver(%d)\n", e1); - -#ifdef CONFIG_PCI - pci_unregister_driver(&cy_pci_driver); -#endif - - for (i = 0; i < NR_CARDS; i++) { - card = &cy_card[i]; - if (card->base_addr) { - /* clear interrupt */ - cy_writeb(card->base_addr + Cy_ClrIntr, 0); - iounmap(card->base_addr); - if (card->ctl_addr.p9050) - iounmap(card->ctl_addr.p9050); - if (card->irq -#ifndef CONFIG_CYZ_INTR - && !cy_is_Z(card) -#endif /* CONFIG_CYZ_INTR */ - ) - free_irq(card->irq, card); - for (e1 = card->first_line; e1 < card->first_line + - card->nports; e1++) - tty_unregister_device(cy_serial_driver, e1); - kfree(card->ports); - } - } - - put_tty_driver(cy_serial_driver); -} /* cy_cleanup_module */ - -module_init(cy_init); -module_exit(cy_cleanup_module); - -MODULE_LICENSE("GPL"); -MODULE_VERSION(CY_VERSION); -MODULE_ALIAS_CHARDEV_MAJOR(CYCLADES_MAJOR); -MODULE_FIRMWARE("cyzfirm.bin"); diff --git a/drivers/tty/hvc/hvc_udbg.c b/drivers/tty/hvc/hvc_udbg.c index a4c9913f76a0..ff0dcc56413c 100644 --- a/drivers/tty/hvc/hvc_udbg.c +++ b/drivers/tty/hvc/hvc_udbg.c @@ -17,7 +17,7 @@ #include "hvc_console.h" -struct hvc_struct *hvc_udbg_dev; +static struct hvc_struct *hvc_udbg_dev; static int hvc_udbg_put(uint32_t vtermno, const char *buf, int count) { diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 9afa1dcef2c2..197988c55e0c 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -290,35 +290,11 @@ static LIST_HEAD(hvcs_structs); static DEFINE_SPINLOCK(hvcs_structs_lock); static DEFINE_MUTEX(hvcs_init_mutex); -static void hvcs_unthrottle(struct tty_struct *tty); -static void hvcs_throttle(struct tty_struct *tty); -static irqreturn_t hvcs_handle_interrupt(int irq, void *dev_instance); - -static int hvcs_write(struct tty_struct *tty, - const unsigned char *buf, int count); -static int hvcs_write_room(struct tty_struct *tty); -static int hvcs_chars_in_buffer(struct tty_struct *tty); - -static int hvcs_has_pi(struct hvcs_struct *hvcsd); -static void hvcs_set_pi(struct hvcs_partner_info *pi, - struct hvcs_struct *hvcsd); static int hvcs_get_pi(struct hvcs_struct *hvcsd); static int hvcs_rescan_devices_list(void); -static int hvcs_partner_connect(struct hvcs_struct *hvcsd); static void hvcs_partner_free(struct hvcs_struct *hvcsd); -static int hvcs_enable_device(struct hvcs_struct *hvcsd, - uint32_t unit_address, unsigned int irq, struct vio_dev *dev); - -static int hvcs_open(struct tty_struct *tty, struct file *filp); -static void hvcs_close(struct tty_struct *tty, struct file *filp); -static void hvcs_hangup(struct tty_struct * tty); - -static int hvcs_probe(struct vio_dev *dev, - const struct vio_device_id *id); -static int __init hvcs_module_init(void); -static void __exit hvcs_module_exit(void); static int hvcs_initialize(void); #define HVCS_SCHED_READ 0x00000001 diff --git a/drivers/tty/ipwireless/tty.c b/drivers/tty/ipwireless/tty.c index 6dacbc5e286c..99bb2f149ff5 100644 --- a/drivers/tty/ipwireless/tty.c +++ b/drivers/tty/ipwireless/tty.c @@ -235,10 +235,10 @@ static int ipw_write_room(struct tty_struct *linux_tty) /* FIXME: Exactly how is the tty object locked here .. */ if (!tty) - return -ENODEV; + return 0; if (!tty->port.count) - return -EINVAL; + return 0; room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued; if (room < 0) @@ -596,13 +596,8 @@ int ipwireless_tty_init(void) void ipwireless_tty_release(void) { - int ret; - - ret = tty_unregister_driver(ipw_tty_driver); + tty_unregister_driver(ipw_tty_driver); put_tty_driver(ipw_tty_driver); - if (ret != 0) - printk(KERN_ERR IPWIRELESS_PCCARD_NAME - ": tty_unregister_driver failed with code %d\n", ret); } int ipwireless_tty_is_modem(struct ipw_tty *tty) diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c deleted file mode 100644 index 3b2f9fb01aa0..000000000000 --- a/drivers/tty/isicom.c +++ /dev/null @@ -1,1699 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Original driver code supplied by Multi-Tech - * - * Changes - * 1/9/98 alan@lxorguk.ukuu.org.uk - * Merge to 2.0.x kernel tree - * Obtain and use official major/minors - * Loader switched to a misc device - * (fixed range check bug as a side effect) - * Printk clean up - * 9/12/98 alan@lxorguk.ukuu.org.uk - * Rough port to 2.1.x - * - * 10/6/99 sameer Merged the ISA and PCI drivers to - * a new unified driver. - * - * 3/9/99 sameer Added support for ISI4616 cards. - * - * 16/9/99 sameer We do not force RTS low anymore. - * This is to prevent the firmware - * from getting confused. - * - * 26/10/99 sameer Cosmetic changes:The driver now - * dumps the Port Count information - * along with I/O address and IRQ. - * - * 13/12/99 sameer Fixed the problem with IRQ sharing. - * - * 10/5/00 sameer Fixed isicom_shutdown_board() - * to not lower DTR on all the ports - * when the last port on the card is - * closed. - * - * 10/5/00 sameer Signal mask setup command added - * to isicom_setup_port and - * isicom_shutdown_port. - * - * 24/5/00 sameer The driver is now SMP aware. - * - * - * 27/11/00 Vinayak P Risbud Fixed the Driver Crash Problem - * - * - * 03/01/01 anil .s Added support for resetting the - * internal modems on ISI cards. - * - * 08/02/01 anil .s Upgraded the driver for kernel - * 2.4.x - * - * 11/04/01 Kevin Fixed firmware load problem with - * ISIHP-4X card - * - * 30/04/01 anil .s Fixed the remote login through - * ISI port problem. Now the link - * does not go down before password - * prompt. - * - * 03/05/01 anil .s Fixed the problem with IRQ sharing - * among ISI-PCI cards. - * - * 03/05/01 anil .s Added support to display the version - * info during insmod as well as module - * listing by lsmod. - * - * 10/05/01 anil .s Done the modifications to the source - * file and Install script so that the - * same installation can be used for - * 2.2.x and 2.4.x kernel. - * - * 06/06/01 anil .s Now we drop both dtr and rts during - * shutdown_port as well as raise them - * during isicom_config_port. - * - * 09/06/01 acme@conectiva.com.br use capable, not suser, do - * restore_flags on failure in - * isicom_send_break, verify put_user - * result - * - * 11/02/03 ranjeeth Added support for 230 Kbps and 460 Kbps - * Baud index extended to 21 - * - * 20/03/03 ranjeeth Made to work for Linux Advanced server. - * Taken care of license warning. - * - * 10/12/03 Ravindra Made to work for Fedora Core 1 of - * Red Hat Distribution - * - * 06/01/05 Alan Cox Merged the ISI and base kernel strands - * into a single 2.6 driver - * - * *********************************************************** - * - * To use this driver you also need the support package. You - * can find this in RPM format on - * ftp://ftp.linux.org.uk/pub/linux/alan - * - * You can find the original tools for this direct from Multitech - * ftp://ftp.multitech.com/ISI-Cards/ - * - * Having installed the cards the module options (/etc/modprobe.d/) - * - * options isicom io=card1,card2,card3,card4 irq=card1,card2,card3,card4 - * - * Omit those entries for boards you don't have installed. - * - * TODO - * Merge testing - * 64-bit verification - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/module.h> -#include <linux/firmware.h> -#include <linux/kernel.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/termios.h> -#include <linux/fs.h> -#include <linux/sched.h> -#include <linux/serial.h> -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/timer.h> -#include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/slab.h> - -#include <linux/uaccess.h> -#include <linux/io.h> - -#include <linux/pci.h> - -#include <linux/isicom.h> - -#define InterruptTheCard(base) outw(0, (base) + 0xc) -#define ClearInterrupt(base) inw((base) + 0x0a) - -#ifdef DEBUG -#define isicom_paranoia_check(a, b, c) __isicom_paranoia_check((a), (b), (c)) -#else -#define isicom_paranoia_check(a, b, c) 0 -#endif - -static int isicom_probe(struct pci_dev *, const struct pci_device_id *); -static void isicom_remove(struct pci_dev *); - -static const struct pci_device_id isicom_pci_tbl[] = { - { PCI_DEVICE(VENDOR_ID, 0x2028) }, - { PCI_DEVICE(VENDOR_ID, 0x2051) }, - { PCI_DEVICE(VENDOR_ID, 0x2052) }, - { PCI_DEVICE(VENDOR_ID, 0x2053) }, - { PCI_DEVICE(VENDOR_ID, 0x2054) }, - { PCI_DEVICE(VENDOR_ID, 0x2055) }, - { PCI_DEVICE(VENDOR_ID, 0x2056) }, - { PCI_DEVICE(VENDOR_ID, 0x2057) }, - { PCI_DEVICE(VENDOR_ID, 0x2058) }, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, isicom_pci_tbl); - -static struct pci_driver isicom_driver = { - .name = "isicom", - .id_table = isicom_pci_tbl, - .probe = isicom_probe, - .remove = isicom_remove -}; - -static int prev_card = 3; /* start servicing isi_card[0] */ -static struct tty_driver *isicom_normal; - -static void isicom_tx(struct timer_list *unused); -static void isicom_start(struct tty_struct *tty); - -static DEFINE_TIMER(tx, isicom_tx); - -/* baud index mappings from linux defns to isi */ - -static signed char linuxb_to_isib[] = { - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17, 18, 19, 20, 21 -}; - -struct isi_board { - unsigned long base; - int irq; - unsigned char port_count; - unsigned short status; - unsigned short port_status; /* each bit for each port */ - unsigned short shift_count; - struct isi_port *ports; - signed char count; - spinlock_t card_lock; /* Card wide lock 11/5/00 -sameer */ - unsigned long flags; - unsigned int index; -}; - -struct isi_port { - unsigned short magic; - struct tty_port port; - u16 channel; - u16 status; - struct isi_board *card; - unsigned char *xmit_buf; - int xmit_head; - int xmit_tail; - int xmit_cnt; -}; - -static struct isi_board isi_card[BOARD_COUNT]; -static struct isi_port isi_ports[PORT_COUNT]; - -/* - * Locking functions for card level locking. We need to own both - * the kernel lock for the card and have the card in a position that - * it wants to talk. - */ - -static int WaitTillCardIsFree(unsigned long base) -{ - unsigned int count = 0; - - while (!(inw(base + 0xe) & 0x1) && count++ < 100) - mdelay(1); - - return !(inw(base + 0xe) & 0x1); -} - -static int lock_card(struct isi_board *card) -{ - unsigned long base = card->base; - unsigned int retries, a; - - for (retries = 0; retries < 10; retries++) { - spin_lock_irqsave(&card->card_lock, card->flags); - for (a = 0; a < 10; a++) { - if (inw(base + 0xe) & 0x1) - return 1; - udelay(10); - } - spin_unlock_irqrestore(&card->card_lock, card->flags); - msleep(10); - } - pr_warn("Failed to lock Card (0x%lx)\n", card->base); - - return 0; /* Failed to acquire the card! */ -} - -static void unlock_card(struct isi_board *card) -{ - spin_unlock_irqrestore(&card->card_lock, card->flags); -} - -/* - * ISI Card specific ops ... - */ - -/* card->lock HAS to be held */ -static void raise_dtr(struct isi_port *port) -{ - struct isi_board *card = port->card; - unsigned long base = card->base; - u16 channel = port->channel; - - if (WaitTillCardIsFree(base)) - return; - - outw(0x8000 | (channel << card->shift_count) | 0x02, base); - outw(0x0504, base); - InterruptTheCard(base); - port->status |= ISI_DTR; -} - -/* card->lock HAS to be held */ -static void drop_dtr(struct isi_port *port) -{ - struct isi_board *card = port->card; - unsigned long base = card->base; - u16 channel = port->channel; - - if (WaitTillCardIsFree(base)) - return; - - outw(0x8000 | (channel << card->shift_count) | 0x02, base); - outw(0x0404, base); - InterruptTheCard(base); - port->status &= ~ISI_DTR; -} - -/* card->lock HAS to be held */ -static inline void raise_rts(struct isi_port *port) -{ - struct isi_board *card = port->card; - unsigned long base = card->base; - u16 channel = port->channel; - - if (WaitTillCardIsFree(base)) - return; - - outw(0x8000 | (channel << card->shift_count) | 0x02, base); - outw(0x0a04, base); - InterruptTheCard(base); - port->status |= ISI_RTS; -} - -/* card->lock HAS to be held */ -static inline void drop_rts(struct isi_port *port) -{ - struct isi_board *card = port->card; - unsigned long base = card->base; - u16 channel = port->channel; - - if (WaitTillCardIsFree(base)) - return; - - outw(0x8000 | (channel << card->shift_count) | 0x02, base); - outw(0x0804, base); - InterruptTheCard(base); - port->status &= ~ISI_RTS; -} - -/* card->lock MUST NOT be held */ - -static void isicom_dtr_rts(struct tty_port *port, int on) -{ - struct isi_port *ip = container_of(port, struct isi_port, port); - struct isi_board *card = ip->card; - unsigned long base = card->base; - u16 channel = ip->channel; - - if (!lock_card(card)) - return; - - if (on) { - outw(0x8000 | (channel << card->shift_count) | 0x02, base); - outw(0x0f04, base); - InterruptTheCard(base); - ip->status |= (ISI_DTR | ISI_RTS); - } else { - outw(0x8000 | (channel << card->shift_count) | 0x02, base); - outw(0x0C04, base); - InterruptTheCard(base); - ip->status &= ~(ISI_DTR | ISI_RTS); - } - unlock_card(card); -} - -/* card->lock HAS to be held */ -static void drop_dtr_rts(struct isi_port *port) -{ - struct isi_board *card = port->card; - unsigned long base = card->base; - u16 channel = port->channel; - - if (WaitTillCardIsFree(base)) - return; - - outw(0x8000 | (channel << card->shift_count) | 0x02, base); - outw(0x0c04, base); - InterruptTheCard(base); - port->status &= ~(ISI_RTS | ISI_DTR); -} - -/* - * ISICOM Driver specific routines ... - * - */ - -static inline int __isicom_paranoia_check(struct isi_port const *port, - char *name, const char *routine) -{ - if (!port) { - pr_warn("Warning: bad isicom magic for dev %s in %s\n", - name, routine); - return 1; - } - if (port->magic != ISICOM_MAGIC) { - pr_warn("Warning: NULL isicom port for dev %s in %s\n", - name, routine); - return 1; - } - - return 0; -} - -/* - * Transmitter. - * - * We shovel data into the card buffers on a regular basis. The card - * will do the rest of the work for us. - */ - -static void isicom_tx(struct timer_list *unused) -{ - unsigned long flags, base; - unsigned int retries; - short count = (BOARD_COUNT-1), card; - short txcount, wrd, residue, word_count, cnt; - struct isi_port *port; - struct tty_struct *tty; - - /* find next active board */ - card = (prev_card + 1) & 0x0003; - while (count-- > 0) { - if (isi_card[card].status & BOARD_ACTIVE) - break; - card = (card + 1) & 0x0003; - } - if (!(isi_card[card].status & BOARD_ACTIVE)) - goto sched_again; - - prev_card = card; - - count = isi_card[card].port_count; - port = isi_card[card].ports; - base = isi_card[card].base; - - spin_lock_irqsave(&isi_card[card].card_lock, flags); - for (retries = 0; retries < 100; retries++) { - if (inw(base + 0xe) & 0x1) - break; - udelay(2); - } - if (retries >= 100) - goto unlock; - - tty = tty_port_tty_get(&port->port); - if (tty == NULL) - goto put_unlock; - - for (; count > 0; count--, port++) { - /* port not active or tx disabled to force flow control */ - if (!tty_port_initialized(&port->port) || - !(port->status & ISI_TXOK)) - continue; - - txcount = min_t(short, TX_SIZE, port->xmit_cnt); - if (txcount <= 0 || tty->stopped || tty->hw_stopped) - continue; - - if (!(inw(base + 0x02) & (1 << port->channel))) - continue; - - pr_debug("txing %d bytes, port%d.\n", - txcount, port->channel + 1); - outw((port->channel << isi_card[card].shift_count) | txcount, - base); - residue = NO; - wrd = 0; - while (1) { - cnt = min_t(int, txcount, (SERIAL_XMIT_SIZE - - port->xmit_tail)); - if (residue == YES) { - residue = NO; - if (cnt > 0) { - wrd |= (port->port.xmit_buf[port->xmit_tail] - << 8); - port->xmit_tail = (port->xmit_tail + 1) - & (SERIAL_XMIT_SIZE - 1); - port->xmit_cnt--; - txcount--; - cnt--; - outw(wrd, base); - } else { - outw(wrd, base); - break; - } - } - if (cnt <= 0) - break; - word_count = cnt >> 1; - outsw(base, port->port.xmit_buf+port->xmit_tail, word_count); - port->xmit_tail = (port->xmit_tail - + (word_count << 1)) & (SERIAL_XMIT_SIZE - 1); - txcount -= (word_count << 1); - port->xmit_cnt -= (word_count << 1); - if (cnt & 0x0001) { - residue = YES; - wrd = port->port.xmit_buf[port->xmit_tail]; - port->xmit_tail = (port->xmit_tail + 1) - & (SERIAL_XMIT_SIZE - 1); - port->xmit_cnt--; - txcount--; - } - } - - InterruptTheCard(base); - if (port->xmit_cnt <= 0) - port->status &= ~ISI_TXOK; - if (port->xmit_cnt <= WAKEUP_CHARS) - tty_wakeup(tty); - } - -put_unlock: - tty_kref_put(tty); -unlock: - spin_unlock_irqrestore(&isi_card[card].card_lock, flags); - /* schedule another tx for hopefully in about 10ms */ -sched_again: - mod_timer(&tx, jiffies + msecs_to_jiffies(10)); -} - -/* - * Main interrupt handler routine - */ - -static irqreturn_t isicom_interrupt(int irq, void *dev_id) -{ - struct isi_board *card = dev_id; - struct isi_port *port; - struct tty_struct *tty; - unsigned long base; - u16 header, word_count, count, channel; - short byte_count; - unsigned char *rp; - - if (!card || !(card->status & FIRMWARE_LOADED)) - return IRQ_NONE; - - base = card->base; - - /* did the card interrupt us? */ - if (!(inw(base + 0x0e) & 0x02)) - return IRQ_NONE; - - spin_lock(&card->card_lock); - - /* - * disable any interrupts from the PCI card and lower the - * interrupt line - */ - outw(0x8000, base+0x04); - ClearInterrupt(base); - - inw(base); /* get the dummy word out */ - header = inw(base); - channel = (header & 0x7800) >> card->shift_count; - byte_count = header & 0xff; - - if (channel + 1 > card->port_count) { - pr_warn("%s(0x%lx): %d(channel) > port_count\n", - __func__, base, channel + 1); - outw(0x0000, base+0x04); /* enable interrupts */ - spin_unlock(&card->card_lock); - return IRQ_HANDLED; - } - port = card->ports + channel; - if (!tty_port_initialized(&port->port)) { - outw(0x0000, base+0x04); /* enable interrupts */ - spin_unlock(&card->card_lock); - return IRQ_HANDLED; - } - - tty = tty_port_tty_get(&port->port); - if (tty == NULL) { - while (byte_count > 1) { - inw(base); - byte_count -= 2; - } - if (byte_count & 0x01) - inw(base); - outw(0x0000, base+0x04); /* enable interrupts */ - spin_unlock(&card->card_lock); - return IRQ_HANDLED; - } - - if (header & 0x8000) { /* Status Packet */ - header = inw(base); - switch (header & 0xff) { - case 0: /* Change in EIA signals */ - if (tty_port_check_carrier(&port->port)) { - if (port->status & ISI_DCD) { - if (!(header & ISI_DCD)) { - /* Carrier has been lost */ - pr_debug("%s: DCD->low.\n", - __func__); - port->status &= ~ISI_DCD; - tty_hangup(tty); - } - } else if (header & ISI_DCD) { - /* Carrier has been detected */ - pr_debug("%s: DCD->high.\n", - __func__); - port->status |= ISI_DCD; - wake_up_interruptible(&port->port.open_wait); - } - } else { - if (header & ISI_DCD) - port->status |= ISI_DCD; - else - port->status &= ~ISI_DCD; - } - - if (tty_port_cts_enabled(&port->port)) { - if (tty->hw_stopped) { - if (header & ISI_CTS) { - tty->hw_stopped = 0; - /* start tx ing */ - port->status |= (ISI_TXOK - | ISI_CTS); - tty_wakeup(tty); - } - } else if (!(header & ISI_CTS)) { - tty->hw_stopped = 1; - /* stop tx ing */ - port->status &= ~(ISI_TXOK | ISI_CTS); - } - } else { - if (header & ISI_CTS) - port->status |= ISI_CTS; - else - port->status &= ~ISI_CTS; - } - - if (header & ISI_DSR) - port->status |= ISI_DSR; - else - port->status &= ~ISI_DSR; - - if (header & ISI_RI) - port->status |= ISI_RI; - else - port->status &= ~ISI_RI; - - break; - - case 1: /* Received Break !!! */ - tty_insert_flip_char(&port->port, 0, TTY_BREAK); - if (port->port.flags & ASYNC_SAK) - do_SAK(tty); - tty_flip_buffer_push(&port->port); - break; - - case 2: /* Statistics */ - pr_debug("%s: stats!!!\n", __func__); - break; - - default: - pr_debug("%s: Unknown code in status packet.\n", - __func__); - break; - } - } else { /* Data Packet */ - count = tty_prepare_flip_string(&port->port, &rp, - byte_count & ~1); - pr_debug("%s: Can rx %d of %d bytes.\n", - __func__, count, byte_count); - word_count = count >> 1; - insw(base, rp, word_count); - byte_count -= (word_count << 1); - if (count & 0x0001) { - tty_insert_flip_char(&port->port, inw(base) & 0xff, - TTY_NORMAL); - byte_count -= 2; - } - if (byte_count > 0) { - pr_debug("%s(0x%lx:%d): Flip buffer overflow! dropping bytes...\n", - __func__, base, channel + 1); - /* drain out unread xtra data */ - while (byte_count > 0) { - inw(base); - byte_count -= 2; - } - } - tty_flip_buffer_push(&port->port); - } - outw(0x0000, base+0x04); /* enable interrupts */ - spin_unlock(&card->card_lock); - tty_kref_put(tty); - - return IRQ_HANDLED; -} - -static void isicom_config_port(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - struct isi_board *card = port->card; - unsigned long baud; - unsigned long base = card->base; - u16 channel_setup, channel = port->channel, - shift_count = card->shift_count; - unsigned char flow_ctrl; - - /* FIXME: Switch to new tty baud API */ - baud = C_BAUD(tty); - if (baud & CBAUDEX) { - baud &= ~CBAUDEX; - - /* if CBAUDEX bit is on and the baud is set to either 50 or 75 - * then the card is programmed for 57.6Kbps or 115Kbps - * respectively. - */ - - /* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */ - if (baud < 1 || baud > 4) - tty->termios.c_cflag &= ~CBAUDEX; - else - baud += 15; - } - if (baud == 15) { - - /* the ASYNC_SPD_HI and ASYNC_SPD_VHI options are set - * by the set_serial_info ioctl ... this is done by - * the 'setserial' utility. - */ - - if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - baud++; /* 57.6 Kbps */ - if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - baud += 2; /* 115 Kbps */ - if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - baud += 3; /* 230 kbps*/ - if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - baud += 4; /* 460 kbps*/ - } - if (linuxb_to_isib[baud] == -1) { - /* hang up */ - drop_dtr(port); - return; - } else - raise_dtr(port); - - if (WaitTillCardIsFree(base) == 0) { - outw(0x8000 | (channel << shift_count) | 0x03, base); - outw(linuxb_to_isib[baud] << 8 | 0x03, base); - channel_setup = 0; - switch (C_CSIZE(tty)) { - case CS5: - channel_setup |= ISICOM_CS5; - break; - case CS6: - channel_setup |= ISICOM_CS6; - break; - case CS7: - channel_setup |= ISICOM_CS7; - break; - case CS8: - channel_setup |= ISICOM_CS8; - break; - } - - if (C_CSTOPB(tty)) - channel_setup |= ISICOM_2SB; - if (C_PARENB(tty)) { - channel_setup |= ISICOM_EVPAR; - if (C_PARODD(tty)) - channel_setup |= ISICOM_ODPAR; - } - outw(channel_setup, base); - InterruptTheCard(base); - } - tty_port_set_check_carrier(&port->port, !C_CLOCAL(tty)); - - /* flow control settings ...*/ - flow_ctrl = 0; - tty_port_set_cts_flow(&port->port, C_CRTSCTS(tty)); - if (C_CRTSCTS(tty)) - flow_ctrl |= ISICOM_CTSRTS; - if (I_IXON(tty)) - flow_ctrl |= ISICOM_RESPOND_XONXOFF; - if (I_IXOFF(tty)) - flow_ctrl |= ISICOM_INITIATE_XONXOFF; - - if (WaitTillCardIsFree(base) == 0) { - outw(0x8000 | (channel << shift_count) | 0x04, base); - outw(flow_ctrl << 8 | 0x05, base); - outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base); - InterruptTheCard(base); - } - - /* rx enabled -> enable port for rx on the card */ - if (C_CREAD(tty)) { - card->port_status |= (1 << channel); - outw(card->port_status, base + 0x02); - } -} - -/* open et all */ - -static inline void isicom_setup_board(struct isi_board *bp) -{ - int channel; - struct isi_port *port; - - bp->count++; - if (!(bp->status & BOARD_INIT)) { - port = bp->ports; - for (channel = 0; channel < bp->port_count; channel++, port++) - drop_dtr_rts(port); - } - bp->status |= BOARD_ACTIVE | BOARD_INIT; -} - -/* Activate and thus setup board are protected from races against shutdown - by the tty_port mutex */ - -static int isicom_activate(struct tty_port *tport, struct tty_struct *tty) -{ - struct isi_port *port = container_of(tport, struct isi_port, port); - struct isi_board *card = port->card; - unsigned long flags; - - if (tty_port_alloc_xmit_buf(tport) < 0) - return -ENOMEM; - - spin_lock_irqsave(&card->card_lock, flags); - isicom_setup_board(card); - - port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - - /* discard any residual data */ - if (WaitTillCardIsFree(card->base) == 0) { - outw(0x8000 | (port->channel << card->shift_count) | 0x02, - card->base); - outw(((ISICOM_KILLTX | ISICOM_KILLRX) << 8) | 0x06, card->base); - InterruptTheCard(card->base); - } - isicom_config_port(tty); - spin_unlock_irqrestore(&card->card_lock, flags); - - return 0; -} - -static int isicom_carrier_raised(struct tty_port *port) -{ - struct isi_port *ip = container_of(port, struct isi_port, port); - return (ip->status & ISI_DCD)?1 : 0; -} - -static struct tty_port *isicom_find_port(struct tty_struct *tty) -{ - struct isi_port *port; - struct isi_board *card; - unsigned int board; - int line = tty->index; - - board = BOARD(line); - card = &isi_card[board]; - - if (!(card->status & FIRMWARE_LOADED)) - return NULL; - - /* open on a port greater than the port count for the card !!! */ - if (line > ((board * 16) + card->port_count - 1)) - return NULL; - - port = &isi_ports[line]; - if (isicom_paranoia_check(port, tty->name, "isicom_open")) - return NULL; - - return &port->port; -} - -static int isicom_open(struct tty_struct *tty, struct file *filp) -{ - struct isi_port *port; - struct tty_port *tport; - - tport = isicom_find_port(tty); - if (tport == NULL) - return -ENODEV; - port = container_of(tport, struct isi_port, port); - - tty->driver_data = port; - return tty_port_open(tport, tty, filp); -} - -/* close et all */ - -/* card->lock HAS to be held */ -static void isicom_shutdown_port(struct isi_port *port) -{ - struct isi_board *card = port->card; - - if (--card->count < 0) { - pr_debug("%s: bad board(0x%lx) count %d.\n", - __func__, card->base, card->count); - card->count = 0; - } - /* last port was closed, shutdown that board too */ - if (!card->count) - card->status &= BOARD_ACTIVE; -} - -static void isicom_flush_buffer(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - struct isi_board *card = port->card; - unsigned long flags; - - if (isicom_paranoia_check(port, tty->name, "isicom_flush_buffer")) - return; - - spin_lock_irqsave(&card->card_lock, flags); - port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - spin_unlock_irqrestore(&card->card_lock, flags); - - tty_wakeup(tty); -} - -static void isicom_shutdown(struct tty_port *port) -{ - struct isi_port *ip = container_of(port, struct isi_port, port); - struct isi_board *card = ip->card; - unsigned long flags; - - /* indicate to the card that no more data can be received - on this port */ - spin_lock_irqsave(&card->card_lock, flags); - card->port_status &= ~(1 << ip->channel); - outw(card->port_status, card->base + 0x02); - isicom_shutdown_port(ip); - spin_unlock_irqrestore(&card->card_lock, flags); - tty_port_free_xmit_buf(port); -} - -static void isicom_close(struct tty_struct *tty, struct file *filp) -{ - struct isi_port *ip = tty->driver_data; - struct tty_port *port; - - if (ip == NULL) - return; - - port = &ip->port; - if (isicom_paranoia_check(ip, tty->name, "isicom_close")) - return; - tty_port_close(port, tty, filp); -} - -/* write et all */ -static int isicom_write(struct tty_struct *tty, const unsigned char *buf, - int count) -{ - struct isi_port *port = tty->driver_data; - struct isi_board *card = port->card; - unsigned long flags; - int cnt, total = 0; - - if (isicom_paranoia_check(port, tty->name, "isicom_write")) - return 0; - - spin_lock_irqsave(&card->card_lock, flags); - - while (1) { - cnt = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - - 1, SERIAL_XMIT_SIZE - port->xmit_head)); - if (cnt <= 0) - break; - - memcpy(port->port.xmit_buf + port->xmit_head, buf, cnt); - port->xmit_head = (port->xmit_head + cnt) & (SERIAL_XMIT_SIZE - - 1); - port->xmit_cnt += cnt; - buf += cnt; - count -= cnt; - total += cnt; - } - if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped) - port->status |= ISI_TXOK; - spin_unlock_irqrestore(&card->card_lock, flags); - return total; -} - -/* put_char et all */ -static int isicom_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct isi_port *port = tty->driver_data; - struct isi_board *card = port->card; - unsigned long flags; - - if (isicom_paranoia_check(port, tty->name, "isicom_put_char")) - return 0; - - spin_lock_irqsave(&card->card_lock, flags); - if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { - spin_unlock_irqrestore(&card->card_lock, flags); - return 0; - } - - port->port.xmit_buf[port->xmit_head++] = ch; - port->xmit_head &= (SERIAL_XMIT_SIZE - 1); - port->xmit_cnt++; - spin_unlock_irqrestore(&card->card_lock, flags); - return 1; -} - -/* flush_chars et all */ -static void isicom_flush_chars(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - - if (isicom_paranoia_check(port, tty->name, "isicom_flush_chars")) - return; - - if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !port->port.xmit_buf) - return; - - /* this tells the transmitter to consider this port for - data output to the card ... that's the best we can do. */ - port->status |= ISI_TXOK; -} - -/* write_room et all */ -static int isicom_write_room(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - int free; - - if (isicom_paranoia_check(port, tty->name, "isicom_write_room")) - return 0; - - free = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; - if (free < 0) - free = 0; - return free; -} - -/* chars_in_buffer et all */ -static int isicom_chars_in_buffer(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - if (isicom_paranoia_check(port, tty->name, "isicom_chars_in_buffer")) - return 0; - return port->xmit_cnt; -} - -/* ioctl et all */ -static int isicom_send_break(struct tty_struct *tty, int length) -{ - struct isi_port *port = tty->driver_data; - struct isi_board *card = port->card; - unsigned long base = card->base; - - if (length == -1) - return -EOPNOTSUPP; - - if (!lock_card(card)) - return -EINVAL; - - outw(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base); - outw((length & 0xff) << 8 | 0x00, base); - outw((length & 0xff00u), base); - InterruptTheCard(base); - - unlock_card(card); - return 0; -} - -static int isicom_tiocmget(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - /* just send the port status */ - u16 status = port->status; - - if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) - return -ENODEV; - - return ((status & ISI_RTS) ? TIOCM_RTS : 0) | - ((status & ISI_DTR) ? TIOCM_DTR : 0) | - ((status & ISI_DCD) ? TIOCM_CAR : 0) | - ((status & ISI_DSR) ? TIOCM_DSR : 0) | - ((status & ISI_CTS) ? TIOCM_CTS : 0) | - ((status & ISI_RI ) ? TIOCM_RI : 0); -} - -static int isicom_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct isi_port *port = tty->driver_data; - unsigned long flags; - - if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) - return -ENODEV; - - spin_lock_irqsave(&port->card->card_lock, flags); - if (set & TIOCM_RTS) - raise_rts(port); - if (set & TIOCM_DTR) - raise_dtr(port); - - if (clear & TIOCM_RTS) - drop_rts(port); - if (clear & TIOCM_DTR) - drop_dtr(port); - spin_unlock_irqrestore(&port->card->card_lock, flags); - - return 0; -} - -static int isicom_set_serial_info(struct tty_struct *tty, - struct serial_struct *ss) -{ - struct isi_port *port = tty->driver_data; - int reconfig_port; - - if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) - return -ENODEV; - - mutex_lock(&port->port.mutex); - reconfig_port = ((port->port.flags & ASYNC_SPD_MASK) != - (ss->flags & ASYNC_SPD_MASK)); - - if (!capable(CAP_SYS_ADMIN)) { - if ((ss->close_delay != port->port.close_delay) || - (ss->closing_wait != port->port.closing_wait) || - ((ss->flags & ~ASYNC_USR_MASK) != - (port->port.flags & ~ASYNC_USR_MASK))) { - mutex_unlock(&port->port.mutex); - return -EPERM; - } - port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) | - (ss->flags & ASYNC_USR_MASK)); - } else { - port->port.close_delay = ss->close_delay; - port->port.closing_wait = ss->closing_wait; - port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) | - (ss->flags & ASYNC_FLAGS)); - } - if (reconfig_port) { - unsigned long flags; - spin_lock_irqsave(&port->card->card_lock, flags); - isicom_config_port(tty); - spin_unlock_irqrestore(&port->card->card_lock, flags); - } - mutex_unlock(&port->port.mutex); - return 0; -} - -static int isicom_get_serial_info(struct tty_struct *tty, - struct serial_struct *ss) -{ - struct isi_port *port = tty->driver_data; - - if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) - return -ENODEV; - - mutex_lock(&port->port.mutex); -/* ss->type = ? */ - ss->line = port - isi_ports; - ss->port = port->card->base; - ss->irq = port->card->irq; - ss->flags = port->port.flags; -/* ss->baud_base = ? */ - ss->close_delay = port->port.close_delay; - ss->closing_wait = port->port.closing_wait; - mutex_unlock(&port->port.mutex); - return 0; -} - -/* set_termios et all */ -static void isicom_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) -{ - struct isi_port *port = tty->driver_data; - unsigned long flags; - - if (isicom_paranoia_check(port, tty->name, "isicom_set_termios")) - return; - - if (tty->termios.c_cflag == old_termios->c_cflag && - tty->termios.c_iflag == old_termios->c_iflag) - return; - - spin_lock_irqsave(&port->card->card_lock, flags); - isicom_config_port(tty); - spin_unlock_irqrestore(&port->card->card_lock, flags); - - if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) { - tty->hw_stopped = 0; - isicom_start(tty); - } -} - -/* throttle et all */ -static void isicom_throttle(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - struct isi_board *card = port->card; - - if (isicom_paranoia_check(port, tty->name, "isicom_throttle")) - return; - - /* tell the card that this port cannot handle any more data for now */ - card->port_status &= ~(1 << port->channel); - outw(card->port_status, card->base + 0x02); -} - -/* unthrottle et all */ -static void isicom_unthrottle(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - struct isi_board *card = port->card; - - if (isicom_paranoia_check(port, tty->name, "isicom_unthrottle")) - return; - - /* tell the card that this port is ready to accept more data */ - card->port_status |= (1 << port->channel); - outw(card->port_status, card->base + 0x02); -} - -/* stop et all */ -static void isicom_stop(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - - if (isicom_paranoia_check(port, tty->name, "isicom_stop")) - return; - - /* this tells the transmitter not to consider this port for - data output to the card. */ - port->status &= ~ISI_TXOK; -} - -/* start et all */ -static void isicom_start(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - - if (isicom_paranoia_check(port, tty->name, "isicom_start")) - return; - - /* this tells the transmitter to consider this port for - data output to the card. */ - port->status |= ISI_TXOK; -} - -static void isicom_hangup(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - - if (isicom_paranoia_check(port, tty->name, "isicom_hangup")) - return; - tty_port_hangup(&port->port); -} - - -/* - * Driver init and deinit functions - */ - -static const struct tty_operations isicom_ops = { - .open = isicom_open, - .close = isicom_close, - .write = isicom_write, - .put_char = isicom_put_char, - .flush_chars = isicom_flush_chars, - .write_room = isicom_write_room, - .chars_in_buffer = isicom_chars_in_buffer, - .set_termios = isicom_set_termios, - .throttle = isicom_throttle, - .unthrottle = isicom_unthrottle, - .stop = isicom_stop, - .start = isicom_start, - .hangup = isicom_hangup, - .flush_buffer = isicom_flush_buffer, - .tiocmget = isicom_tiocmget, - .tiocmset = isicom_tiocmset, - .break_ctl = isicom_send_break, - .get_serial = isicom_get_serial_info, - .set_serial = isicom_set_serial_info, -}; - -static const struct tty_port_operations isicom_port_ops = { - .carrier_raised = isicom_carrier_raised, - .dtr_rts = isicom_dtr_rts, - .activate = isicom_activate, - .shutdown = isicom_shutdown, -}; - -static int reset_card(struct pci_dev *pdev, - const unsigned int card, unsigned int *signature) -{ - struct isi_board *board = pci_get_drvdata(pdev); - unsigned long base = board->base; - unsigned int sig, portcount = 0; - int retval = 0; - - dev_dbg(&pdev->dev, "ISILoad:Resetting Card%d at 0x%lx\n", card + 1, - base); - - inw(base + 0x8); - - msleep(10); - - outw(0, base + 0x8); /* Reset */ - - msleep(1000); - - sig = inw(base + 0x4) & 0xff; - - if (sig != 0xa5 && sig != 0xbb && sig != 0xcc && sig != 0xdd && - sig != 0xee) { - dev_warn(&pdev->dev, "ISILoad:Card%u reset failure (Possible " - "bad I/O Port Address 0x%lx).\n", card + 1, base); - dev_dbg(&pdev->dev, "Sig=0x%x\n", sig); - retval = -EIO; - goto end; - } - - msleep(10); - - portcount = inw(base + 0x2); - if (!(inw(base + 0xe) & 0x1) || (portcount != 0 && portcount != 4 && - portcount != 8 && portcount != 16)) { - dev_err(&pdev->dev, "ISILoad:PCI Card%d reset failure.\n", - card + 1); - retval = -EIO; - goto end; - } - - switch (sig) { - case 0xa5: - case 0xbb: - case 0xdd: - board->port_count = (portcount == 4) ? 4 : 8; - board->shift_count = 12; - break; - case 0xcc: - case 0xee: - board->port_count = 16; - board->shift_count = 11; - break; - } - dev_info(&pdev->dev, "-Done\n"); - *signature = sig; - -end: - return retval; -} - -static int load_firmware(struct pci_dev *pdev, - const unsigned int index, const unsigned int signature) -{ - struct isi_board *board = pci_get_drvdata(pdev); - const struct firmware *fw; - unsigned long base = board->base; - unsigned int a; - u16 word_count, status; - int retval = -EIO; - char *name; - u8 *data; - - struct stframe { - u16 addr; - u16 count; - u8 data[0]; - } *frame; - - switch (signature) { - case 0xa5: - name = "isi608.bin"; - break; - case 0xbb: - name = "isi608em.bin"; - break; - case 0xcc: - name = "isi616em.bin"; - break; - case 0xdd: - name = "isi4608.bin"; - break; - case 0xee: - name = "isi4616.bin"; - break; - default: - dev_err(&pdev->dev, "Unknown signature.\n"); - goto end; - } - - retval = request_firmware(&fw, name, &pdev->dev); - if (retval) - goto end; - - retval = -EIO; - - for (frame = (struct stframe *)fw->data; - frame < (struct stframe *)(fw->data + fw->size); - frame = (struct stframe *)((u8 *)(frame + 1) + - frame->count)) { - if (WaitTillCardIsFree(base)) - goto errrelfw; - - outw(0xf0, base); /* start upload sequence */ - outw(0x00, base); - outw(frame->addr, base); /* lsb of address */ - - word_count = frame->count / 2 + frame->count % 2; - outw(word_count, base); - InterruptTheCard(base); - - udelay(100); /* 0x2f */ - - if (WaitTillCardIsFree(base)) - goto errrelfw; - - status = inw(base + 0x4); - if (status != 0) { - dev_warn(&pdev->dev, "Card%d rejected load header:\n" - "Address:0x%x\n" - "Count:0x%x\n" - "Status:0x%x\n", - index + 1, frame->addr, frame->count, status); - goto errrelfw; - } - outsw(base, frame->data, word_count); - - InterruptTheCard(base); - - udelay(50); /* 0x0f */ - - if (WaitTillCardIsFree(base)) - goto errrelfw; - - status = inw(base + 0x4); - if (status != 0) { - dev_err(&pdev->dev, "Card%d got out of sync.Card " - "Status:0x%x\n", index + 1, status); - goto errrelfw; - } - } - -/* XXX: should we test it by reading it back and comparing with original like - * in load firmware package? */ - for (frame = (struct stframe *)fw->data; - frame < (struct stframe *)(fw->data + fw->size); - frame = (struct stframe *)((u8 *)(frame + 1) + - frame->count)) { - if (WaitTillCardIsFree(base)) - goto errrelfw; - - outw(0xf1, base); /* start download sequence */ - outw(0x00, base); - outw(frame->addr, base); /* lsb of address */ - - word_count = (frame->count >> 1) + frame->count % 2; - outw(word_count + 1, base); - InterruptTheCard(base); - - udelay(50); /* 0xf */ - - if (WaitTillCardIsFree(base)) - goto errrelfw; - - status = inw(base + 0x4); - if (status != 0) { - dev_warn(&pdev->dev, "Card%d rejected verify header:\n" - "Address:0x%x\n" - "Count:0x%x\n" - "Status: 0x%x\n", - index + 1, frame->addr, frame->count, status); - goto errrelfw; - } - - data = kmalloc_array(word_count, 2, GFP_KERNEL); - if (data == NULL) { - dev_err(&pdev->dev, "Card%d, firmware upload " - "failed, not enough memory\n", index + 1); - goto errrelfw; - } - inw(base); - insw(base, data, word_count); - InterruptTheCard(base); - - for (a = 0; a < frame->count; a++) - if (data[a] != frame->data[a]) { - kfree(data); - dev_err(&pdev->dev, "Card%d, firmware upload " - "failed\n", index + 1); - goto errrelfw; - } - kfree(data); - - udelay(50); /* 0xf */ - - if (WaitTillCardIsFree(base)) - goto errrelfw; - - status = inw(base + 0x4); - if (status != 0) { - dev_err(&pdev->dev, "Card%d verify got out of sync. " - "Card Status:0x%x\n", index + 1, status); - goto errrelfw; - } - } - - /* xfer ctrl */ - if (WaitTillCardIsFree(base)) - goto errrelfw; - - outw(0xf2, base); - outw(0x800, base); - outw(0x0, base); - outw(0x0, base); - InterruptTheCard(base); - outw(0x0, base + 0x4); /* for ISI4608 cards */ - - board->status |= FIRMWARE_LOADED; - retval = 0; - -errrelfw: - release_firmware(fw); -end: - return retval; -} - -/* - * Insmod can set static symbols so keep these static - */ -static unsigned int card_count; - -static int isicom_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - unsigned int signature, index; - int retval = -EPERM; - struct isi_board *board = NULL; - - if (card_count >= BOARD_COUNT) - goto err; - - retval = pci_enable_device(pdev); - if (retval) { - dev_err(&pdev->dev, "failed to enable\n"); - goto err; - } - - dev_info(&pdev->dev, "ISI PCI Card(Device ID 0x%x)\n", ent->device); - - /* allot the first empty slot in the array */ - for (index = 0; index < BOARD_COUNT; index++) { - if (isi_card[index].base == 0) { - board = &isi_card[index]; - break; - } - } - if (index == BOARD_COUNT) { - retval = -ENODEV; - goto err_disable; - } - - board->index = index; - board->base = pci_resource_start(pdev, 3); - board->irq = pdev->irq; - card_count++; - - pci_set_drvdata(pdev, board); - - retval = pci_request_region(pdev, 3, ISICOM_NAME); - if (retval) { - dev_err(&pdev->dev, "I/O Region 0x%lx-0x%lx is busy. Card%d " - "will be disabled.\n", board->base, board->base + 15, - index + 1); - retval = -EBUSY; - goto errdec; - } - - retval = request_irq(board->irq, isicom_interrupt, - IRQF_SHARED, ISICOM_NAME, board); - if (retval < 0) { - dev_err(&pdev->dev, "Could not install handler at Irq %d. " - "Card%d will be disabled.\n", board->irq, index + 1); - goto errunrr; - } - - retval = reset_card(pdev, index, &signature); - if (retval < 0) - goto errunri; - - retval = load_firmware(pdev, index, signature); - if (retval < 0) - goto errunri; - - for (index = 0; index < board->port_count; index++) { - struct tty_port *tport = &board->ports[index].port; - tty_port_init(tport); - tport->ops = &isicom_port_ops; - tport->close_delay = 50 * HZ/100; - tport->closing_wait = 3000 * HZ/100; - tty_port_register_device(tport, isicom_normal, - board->index * 16 + index, &pdev->dev); - } - - return 0; - -errunri: - free_irq(board->irq, board); -errunrr: - pci_release_region(pdev, 3); -errdec: - board->base = 0; - card_count--; -err_disable: - pci_disable_device(pdev); -err: - return retval; -} - -static void isicom_remove(struct pci_dev *pdev) -{ - struct isi_board *board = pci_get_drvdata(pdev); - unsigned int i; - - for (i = 0; i < board->port_count; i++) { - tty_unregister_device(isicom_normal, board->index * 16 + i); - tty_port_destroy(&board->ports[i].port); - } - - free_irq(board->irq, board); - pci_release_region(pdev, 3); - board->base = 0; - card_count--; - pci_disable_device(pdev); -} - -static int __init isicom_init(void) -{ - int retval, idx, channel; - struct isi_port *port; - - for (idx = 0; idx < BOARD_COUNT; idx++) { - port = &isi_ports[idx * 16]; - isi_card[idx].ports = port; - spin_lock_init(&isi_card[idx].card_lock); - for (channel = 0; channel < 16; channel++, port++) { - port->magic = ISICOM_MAGIC; - port->card = &isi_card[idx]; - port->channel = channel; - port->status = 0; - /* . . . */ - } - isi_card[idx].base = 0; - isi_card[idx].irq = 0; - } - - /* tty driver structure initialization */ - isicom_normal = alloc_tty_driver(PORT_COUNT); - if (!isicom_normal) { - retval = -ENOMEM; - goto error; - } - - isicom_normal->name = "ttyM"; - isicom_normal->major = ISICOM_NMAJOR; - isicom_normal->minor_start = 0; - isicom_normal->type = TTY_DRIVER_TYPE_SERIAL; - isicom_normal->subtype = SERIAL_TYPE_NORMAL; - isicom_normal->init_termios = tty_std_termios; - isicom_normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | - CLOCAL; - isicom_normal->flags = TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK; - tty_set_operations(isicom_normal, &isicom_ops); - - retval = tty_register_driver(isicom_normal); - if (retval) { - pr_debug("Couldn't register the dialin driver\n"); - goto err_puttty; - } - - retval = pci_register_driver(&isicom_driver); - if (retval < 0) { - pr_err("Unable to register pci driver.\n"); - goto err_unrtty; - } - - mod_timer(&tx, jiffies + 1); - - return 0; -err_unrtty: - tty_unregister_driver(isicom_normal); -err_puttty: - put_tty_driver(isicom_normal); -error: - return retval; -} - -static void __exit isicom_exit(void) -{ - del_timer_sync(&tx); - - pci_unregister_driver(&isicom_driver); - tty_unregister_driver(isicom_normal); - put_tty_driver(isicom_normal); -} - -module_init(isicom_init); -module_exit(isicom_exit); - -MODULE_AUTHOR("MultiTech"); -MODULE_DESCRIPTION("Driver for the ISI series of cards by MultiTech"); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE("isi608.bin"); -MODULE_FIRMWARE("isi608em.bin"); -MODULE_FIRMWARE("isi616em.bin"); -MODULE_FIRMWARE("isi4608.bin"); -MODULE_FIRMWARE("isi4616.bin"); diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index 9f13f7d49dd7..4d4f15b5cd29 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -1118,9 +1118,7 @@ static void __exit moxa_exit(void) del_timer_sync(&moxaTimer); - if (tty_unregister_driver(moxaDriver)) - printk(KERN_ERR "Couldn't unregister MOXA Intellio family " - "serial driver\n"); + tty_unregister_driver(moxaDriver); put_tty_driver(moxaDriver); } @@ -2040,7 +2038,7 @@ static int moxa_get_serial_info(struct tty_struct *tty, ss->line = info->port.tty->index, ss->flags = info->port.flags, ss->baud_base = 921600, - ss->close_delay = info->port.close_delay; + ss->close_delay = jiffies_to_msecs(info->port.close_delay) / 10; mutex_unlock(&info->port.mutex); return 0; } @@ -2050,30 +2048,31 @@ static int moxa_set_serial_info(struct tty_struct *tty, struct serial_struct *ss) { struct moxa_port *info = tty->driver_data; + unsigned int close_delay; if (tty->index == MAX_PORTS) return -EINVAL; if (!info) return -ENODEV; - if (ss->irq != 0 || ss->port != 0 || - ss->custom_divisor != 0 || - ss->baud_base != 921600) - return -EPERM; + close_delay = msecs_to_jiffies(ss->close_delay * 10); mutex_lock(&info->port.mutex); if (!capable(CAP_SYS_ADMIN)) { - if (((ss->flags & ~ASYNC_USR_MASK) != + if (close_delay != info->port.close_delay || + ss->type != info->type || + ((ss->flags & ~ASYNC_USR_MASK) != (info->port.flags & ~ASYNC_USR_MASK))) { mutex_unlock(&info->port.mutex); return -EPERM; } - } - info->port.close_delay = ss->close_delay * HZ / 100; + } else { + info->port.close_delay = close_delay; - MoxaSetFifo(info, ss->type == PORT_16550A); + MoxaSetFifo(info, ss->type == PORT_16550A); - info->type = ss->type; + info->type = ss->type; + } mutex_unlock(&info->port.mutex); return 0; } diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 4203b64bccdb..16a852ecbe8a 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -1208,19 +1208,26 @@ static int mxser_get_serial_info(struct tty_struct *tty, { struct mxser_port *info = tty->driver_data; struct tty_port *port = &info->port; + unsigned int closing_wait, close_delay; if (tty->index == MXSER_PORTS) return -ENOTTY; mutex_lock(&port->mutex); + + close_delay = jiffies_to_msecs(info->port.close_delay) / 10; + closing_wait = info->port.closing_wait; + if (closing_wait != ASYNC_CLOSING_WAIT_NONE) + closing_wait = jiffies_to_msecs(closing_wait) / 10; + ss->type = info->type, ss->line = tty->index, ss->port = info->ioaddr, ss->irq = info->board->irq, ss->flags = info->port.flags, ss->baud_base = info->baud_base, - ss->close_delay = info->port.close_delay, - ss->closing_wait = info->port.closing_wait, + ss->close_delay = close_delay; + ss->closing_wait = closing_wait; ss->custom_divisor = info->custom_divisor, mutex_unlock(&port->mutex); return 0; @@ -1233,7 +1240,7 @@ static int mxser_set_serial_info(struct tty_struct *tty, struct tty_port *port = &info->port; speed_t baud; unsigned long sl_flags; - unsigned int flags; + unsigned int flags, close_delay, closing_wait; int retval = 0; if (tty->index == MXSER_PORTS) @@ -1255,9 +1262,15 @@ static int mxser_set_serial_info(struct tty_struct *tty, flags = port->flags & ASYNC_SPD_MASK; + close_delay = msecs_to_jiffies(ss->close_delay * 10); + closing_wait = ss->closing_wait; + if (closing_wait != ASYNC_CLOSING_WAIT_NONE) + closing_wait = msecs_to_jiffies(closing_wait * 10); + if (!capable(CAP_SYS_ADMIN)) { if ((ss->baud_base != info->baud_base) || - (ss->close_delay != info->port.close_delay) || + (close_delay != info->port.close_delay) || + (closing_wait != info->port.closing_wait) || ((ss->flags & ~ASYNC_USR_MASK) != (info->port.flags & ~ASYNC_USR_MASK))) { mutex_unlock(&port->mutex); return -EPERM; @@ -1271,8 +1284,8 @@ static int mxser_set_serial_info(struct tty_struct *tty, */ port->flags = ((port->flags & ~ASYNC_FLAGS) | (ss->flags & ASYNC_FLAGS)); - port->close_delay = ss->close_delay * HZ / 100; - port->closing_wait = ss->closing_wait * HZ / 100; + port->close_delay = close_delay; + port->closing_wait = closing_wait; if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST && (ss->baud_base != info->baud_base || ss->custom_divisor != @@ -1284,11 +1297,11 @@ static int mxser_set_serial_info(struct tty_struct *tty, baud = ss->baud_base / ss->custom_divisor; tty_encode_baud_rate(tty, baud, baud); } - } - info->type = ss->type; + info->type = ss->type; - process_txrx_fifo(info); + process_txrx_fifo(info); + } if (tty_port_initialized(port)) { if (flags != (port->flags & ASYNC_SPD_MASK)) { @@ -2142,14 +2155,7 @@ end_intr: port->mon_data.rxcnt += cnt; port->mon_data.up_rxcnt += cnt; - /* - * We are called from an interrupt context with &port->slock - * being held. Drop it temporarily in order to prevent - * recursive locking. - */ - spin_unlock(&port->slock); tty_flip_buffer_push(&port->port); - spin_lock(&port->slock); } static void mxser_transmit_chars(struct tty_struct *tty, struct mxser_port *port) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 51dafc06f541..5fea02cfb0cc 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -50,6 +50,7 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/gsmmux.h> +#include "tty.h" static int debug; module_param(debug, int, 0600); @@ -266,7 +267,7 @@ struct gsm_mux { #define MAX_MUX 4 /* 256 minors */ static struct gsm_mux *gsm_mux[MAX_MUX]; /* GSM muxes */ -static spinlock_t gsm_mux_lock; +static DEFINE_SPINLOCK(gsm_mux_lock); static struct tty_driver *gsm_tty_driver; @@ -2384,8 +2385,18 @@ static int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) /* Don't register device 0 - this is the control channel and not a usable tty interface */ base = mux_num_to_base(gsm); /* Base for this MUX */ - for (i = 1; i < NUM_DLCI; i++) - tty_register_device(gsm_tty_driver, base + i, NULL); + for (i = 1; i < NUM_DLCI; i++) { + struct device *dev; + + dev = tty_register_device(gsm_tty_driver, + base + i, NULL); + if (IS_ERR(dev)) { + for (i--; i >= 1; i--) + tty_unregister_device(gsm_tty_driver, + base + i); + return PTR_ERR(dev); + } + } } return ret; } @@ -2416,27 +2427,24 @@ static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { struct gsm_mux *gsm = tty->disc_data; - const unsigned char *dp; - char *f; - int i; char flags = TTY_NORMAL; if (debug & 4) print_hex_dump_bytes("gsmld_receive: ", DUMP_PREFIX_OFFSET, cp, count); - for (i = count, dp = cp, f = fp; i; i--, dp++) { - if (f) - flags = *f++; + for (; count; count--, cp++) { + if (fp) + flags = *fp++; switch (flags) { case TTY_NORMAL: - gsm->receive(gsm, *dp); + gsm->receive(gsm, *cp); break; case TTY_OVERRUN: case TTY_BREAK: case TTY_PARITY: case TTY_FRAME: - gsm_error(gsm, *dp, flags); + gsm_error(gsm, *cp, flags); break; default: WARN_ONCE(1, "%s: unknown flag %d\n", @@ -2849,7 +2857,6 @@ static int gsm_create_network(struct gsm_dlci *dlci, struct gsm_netconfig *nc) /* Line discipline for real tty */ static struct tty_ldisc_ops tty_ldisc_packet = { .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, .name = "n_gsm", .open = gsmld_open, .close = gsmld_close, @@ -3052,7 +3059,7 @@ static int gsmtty_write_room(struct tty_struct *tty) { struct gsm_dlci *dlci = tty->driver_data; if (dlci->state == DLCI_CLOSED) - return -EINVAL; + return 0; return TX_SIZE - kfifo_len(&dlci->fifo); } @@ -3060,7 +3067,7 @@ static int gsmtty_chars_in_buffer(struct tty_struct *tty) { struct gsm_dlci *dlci = tty->driver_data; if (dlci->state == DLCI_CLOSED) - return -EINVAL; + return 0; return kfifo_len(&dlci->fifo); } @@ -3261,8 +3268,6 @@ static int __init gsm_init(void) gsm_tty_driver->init_termios.c_lflag &= ~ECHO; tty_set_operations(gsm_tty_driver, &gsmtty_ops); - spin_lock_init(&gsm_mux_lock); - if (tty_register_driver(gsm_tty_driver)) { put_tty_driver(gsm_tty_driver); tty_unregister_ldisc(N_GSM0710); diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index 1363e659dc1d..b0f33e8ac819 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -100,6 +100,7 @@ #include <asm/termios.h> #include <linux/uaccess.h> +#include "tty.h" /* * Buffers for individual HDLC frames @@ -787,7 +788,6 @@ static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *buf_list) static struct tty_ldisc_ops n_hdlc_ldisc = { .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, .name = "hdlc", .open = n_hdlc_tty_open, .close = n_hdlc_tty_close, diff --git a/drivers/tty/n_null.c b/drivers/tty/n_null.c index ce03ae78f5c6..b8f67b5f1ef8 100644 --- a/drivers/tty/n_null.c +++ b/drivers/tty/n_null.c @@ -40,7 +40,6 @@ static void n_null_receivebuf(struct tty_struct *tty, static struct tty_ldisc_ops null_ldisc = { .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, .name = "n_null", .open = n_null_open, .close = n_null_close, diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 3161f0a535e3..2eb76ea1d88d 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -146,7 +146,6 @@ static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, static struct tty_ldisc_ops tty_ldisc_N_R3964 = { .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, .name = "R3964", .open = r3964_open, .close = r3964_close, diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 87ec15dbe10d..9686c5d10571 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -49,6 +49,7 @@ #include <linux/module.h> #include <linux/ratelimit.h> #include <linux/vmalloc.h> +#include "tty.h" /* * Until this number of characters is queued in the xmit buffer, select will @@ -2488,7 +2489,7 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file, } static struct tty_ldisc_ops n_tty_ops = { - .magic = TTY_LDISC_MAGIC, + .owner = THIS_MODULE, .name = "n_tty", .open = n_tty_open, .close = n_tty_close, diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index 861e95043191..9a2d78ace49b 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -47,9 +47,6 @@ #include <linux/delay.h> - -#define VERSION_STRING DRIVER_DESC " 2.1d" - /* Default debug printout level */ #define NOZOMI_DEBUG_LEVEL 0x00 static int debug = NOZOMI_DEBUG_LEVEL; @@ -89,7 +86,6 @@ do { \ /* Defines */ #define NOZOMI_NAME "nozomi" #define NOZOMI_NAME_TTY "nozomi_tty" -#define DRIVER_DESC "Nozomi driver" #define NTTY_TTY_MAXMINORS 256 #define NTTY_FIFO_BUFFER_SIZE 8192 @@ -359,12 +355,6 @@ struct nozomi { u32 open_ttys; }; -/* This is a data packet that is read or written to/from card */ -struct buffer { - u32 size; /* size is the length of the data buffer */ - u8 *data; -} __attribute__ ((packed)); - /* Global variables */ static const struct pci_device_id nozomi_pci_tbl[] = { {PCI_DEVICE(0x1931, 0x000c)}, /* Nozomi HSDPA */ @@ -787,7 +777,6 @@ static int receive_data(enum port_type index, struct nozomi *dc) int i, ret; size = __le32_to_cpu(readl(addr)); - /* DBG1( "%d bytes port: %d", size, index); */ if (tty && tty_throttled(tty)) { DBG1("No room in tty, don't read data, don't ack interrupt, " @@ -1318,8 +1307,6 @@ static int nozomi_card_init(struct pci_dev *pdev, int ndev_idx; int i; - dev_dbg(&pdev->dev, "Init, new card found\n"); - for (ndev_idx = 0; ndev_idx < ARRAY_SIZE(ndevs); ndev_idx++) if (!ndevs[ndev_idx]) break; @@ -1453,8 +1440,6 @@ static void tty_exit(struct nozomi *dc) { unsigned int i; - DBG1(" "); - for (i = 0; i < MAX_PORT; ++i) tty_port_tty_hangup(&dc->port[i].port, false); @@ -1619,8 +1604,6 @@ static int ntty_write(struct tty_struct *tty, const unsigned char *buffer, struct port *port = tty->driver_data; unsigned long flags; - /* DBG1( "WRITEx: %d, index = %d", count, index); */ - if (!dc || !port) return -ENODEV; @@ -1746,8 +1729,6 @@ static int ntty_ioctl(struct tty_struct *tty, struct port *port = tty->driver_data; int rval = -ENOIOCTLCMD; - DBG1("******** IOCTL, cmd: %d", cmd); - switch (cmd) { case TIOCMIWAIT: { struct async_icount cprev = port->tty_icount; @@ -1773,7 +1754,6 @@ static void ntty_unthrottle(struct tty_struct *tty) struct nozomi *dc = get_dc_by_tty(tty); unsigned long flags; - DBG1("UNTHROTTLE"); spin_lock_irqsave(&dc->spin_mutex, flags); enable_transmit_dl(tty->index % MAX_PORT, dc); set_rts(tty, 1); @@ -1790,7 +1770,6 @@ static void ntty_throttle(struct tty_struct *tty) struct nozomi *dc = get_dc_by_tty(tty); unsigned long flags; - DBG1("THROTTLE"); spin_lock_irqsave(&dc->spin_mutex, flags); set_rts(tty, 0); spin_unlock_irqrestore(&dc->spin_mutex, flags); @@ -1847,8 +1826,6 @@ static __init int nozomi_init(void) { int ret; - printk(KERN_INFO "Initializing %s\n", VERSION_STRING); - ntty_driver = alloc_tty_driver(NTTY_TTY_MAXMINORS); if (!ntty_driver) return -ENOMEM; @@ -1888,7 +1865,6 @@ free_tty: static __exit void nozomi_exit(void) { - printk(KERN_INFO "Unloading %s\n", DRIVER_DESC); pci_unregister_driver(&nozomi_driver); tty_unregister_driver(ntty_driver); put_tty_driver(ntty_driver); @@ -1898,4 +1874,4 @@ module_init(nozomi_init); module_exit(nozomi_exit); MODULE_LICENSE("Dual BSD/GPL"); -MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_DESCRIPTION("Nozomi driver"); diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 5e2374580e27..9b5d4ae5d8f2 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -29,6 +29,7 @@ #include <linux/file.h> #include <linux/ioctl.h> #include <linux/compat.h> +#include "tty.h" #undef TTY_DEBUG_HANGUP #ifdef TTY_DEBUG_HANGUP @@ -159,6 +160,7 @@ static int pty_chars_in_buffer(struct tty_struct *tty) static int pty_set_lock(struct tty_struct *tty, int __user *arg) { int val; + if (get_user(val, arg)) return -EFAULT; if (val) @@ -171,6 +173,7 @@ static int pty_set_lock(struct tty_struct *tty, int __user *arg) static int pty_get_lock(struct tty_struct *tty, int __user *arg) { int locked = test_bit(TTY_PTY_LOCK, &tty->flags); + return put_user(locked, arg); } @@ -200,6 +203,7 @@ static int pty_set_pktmode(struct tty_struct *tty, int __user *arg) static int pty_get_pktmode(struct tty_struct *tty, int __user *arg) { int pktmode = tty->packet; + return put_user(pktmode, arg); } @@ -463,6 +467,7 @@ static int pty_install(struct tty_driver *driver, struct tty_struct *tty) static void pty_remove(struct tty_driver *driver, struct tty_struct *tty) { struct tty_struct *pair = tty->link; + driver->ttys[tty->index] = NULL; if (pair) pair->driver->ttys[pair->index] = NULL; diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c deleted file mode 100644 index 2540b2e4c8e8..000000000000 --- a/drivers/tty/rocket.c +++ /dev/null @@ -1,3127 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) -/* - * RocketPort device driver for Linux - * - * Written by Theodore Ts'o, 1995, 1996, 1997, 1998, 1999, 2000. - * - * Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2003 by Comtrol, Inc. - */ - -/* - * Kernel Synchronization: - * - * This driver has 2 kernel control paths - exception handlers (calls into the driver - * from user mode) and the timer bottom half (tasklet). This is a polled driver, interrupts - * are not used. - * - * Critical data: - * - rp_table[], accessed through passed "info" pointers, is a global (static) array of - * serial port state information and the xmit_buf circular buffer. Protected by - * a per port spinlock. - * - xmit_flags[], an array of ints indexed by line (port) number, indicating that there - * is data to be transmitted. Protected by atomic bit operations. - * - rp_num_ports, int indicating number of open ports, protected by atomic operations. - * - * rp_write() and rp_write_char() functions use a per port semaphore to protect against - * simultaneous access to the same port by more than one process. - */ - -/****** Defines ******/ -#define ROCKET_PARANOIA_CHECK -#define ROCKET_DISABLE_SIMUSAGE - -#undef ROCKET_SOFT_FLOW -#undef ROCKET_DEBUG_OPEN -#undef ROCKET_DEBUG_INTR -#undef ROCKET_DEBUG_WRITE -#undef ROCKET_DEBUG_FLOW -#undef ROCKET_DEBUG_THROTTLE -#undef ROCKET_DEBUG_WAIT_UNTIL_SENT -#undef ROCKET_DEBUG_RECEIVE -#undef ROCKET_DEBUG_HANGUP -#undef REV_PCI_ORDER -#undef ROCKET_DEBUG_IO - -#define POLL_PERIOD (HZ/100) /* Polling period .01 seconds (10ms) */ - -/****** Kernel includes ******/ - -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/major.h> -#include <linux/kernel.h> -#include <linux/signal.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/tty.h> -#include <linux/tty_driver.h> -#include <linux/tty_flip.h> -#include <linux/serial.h> -#include <linux/string.h> -#include <linux/fcntl.h> -#include <linux/ptrace.h> -#include <linux/mutex.h> -#include <linux/ioport.h> -#include <linux/delay.h> -#include <linux/completion.h> -#include <linux/wait.h> -#include <linux/pci.h> -#include <linux/uaccess.h> -#include <linux/atomic.h> -#include <asm/unaligned.h> -#include <linux/bitops.h> -#include <linux/spinlock.h> -#include <linux/init.h> - -/****** RocketPort includes ******/ - -#include "rocket_int.h" -#include "rocket.h" - -#define ROCKET_VERSION "2.09" -#define ROCKET_DATE "12-June-2003" - -/****** RocketPort Local Variables ******/ - -static void rp_do_poll(struct timer_list *unused); - -static struct tty_driver *rocket_driver; - -static struct rocket_version driver_version = { - ROCKET_VERSION, ROCKET_DATE -}; - -static struct r_port *rp_table[MAX_RP_PORTS]; /* The main repository of serial port state information. */ -static unsigned int xmit_flags[NUM_BOARDS]; /* Bit significant, indicates port had data to transmit. */ - /* eg. Bit 0 indicates port 0 has xmit data, ... */ -static atomic_t rp_num_ports_open; /* Number of serial ports open */ -static DEFINE_TIMER(rocket_timer, rp_do_poll); - -static unsigned long board1; /* ISA addresses, retrieved from rocketport.conf */ -static unsigned long board2; -static unsigned long board3; -static unsigned long board4; -static unsigned long controller; -static bool support_low_speed; -static unsigned long modem1; -static unsigned long modem2; -static unsigned long modem3; -static unsigned long modem4; -static unsigned long pc104_1[8]; -static unsigned long pc104_2[8]; -static unsigned long pc104_3[8]; -static unsigned long pc104_4[8]; -static unsigned long *pc104[4] = { pc104_1, pc104_2, pc104_3, pc104_4 }; - -static int rp_baud_base[NUM_BOARDS]; /* Board config info (Someday make a per-board structure) */ -static unsigned long rcktpt_io_addr[NUM_BOARDS]; -static int rcktpt_type[NUM_BOARDS]; -static int is_PCI[NUM_BOARDS]; -static rocketModel_t rocketModel[NUM_BOARDS]; -static int max_board; -static const struct tty_port_operations rocket_port_ops; - -/* - * The following arrays define the interrupt bits corresponding to each AIOP. - * These bits are different between the ISA and regular PCI boards and the - * Universal PCI boards. - */ - -static Word_t aiop_intr_bits[AIOP_CTL_SIZE] = { - AIOP_INTR_BIT_0, - AIOP_INTR_BIT_1, - AIOP_INTR_BIT_2, - AIOP_INTR_BIT_3 -}; - -#ifdef CONFIG_PCI -static Word_t upci_aiop_intr_bits[AIOP_CTL_SIZE] = { - UPCI_AIOP_INTR_BIT_0, - UPCI_AIOP_INTR_BIT_1, - UPCI_AIOP_INTR_BIT_2, - UPCI_AIOP_INTR_BIT_3 -}; -#endif - -static Byte_t RData[RDATASIZE] = { - 0x00, 0x09, 0xf6, 0x82, - 0x02, 0x09, 0x86, 0xfb, - 0x04, 0x09, 0x00, 0x0a, - 0x06, 0x09, 0x01, 0x0a, - 0x08, 0x09, 0x8a, 0x13, - 0x0a, 0x09, 0xc5, 0x11, - 0x0c, 0x09, 0x86, 0x85, - 0x0e, 0x09, 0x20, 0x0a, - 0x10, 0x09, 0x21, 0x0a, - 0x12, 0x09, 0x41, 0xff, - 0x14, 0x09, 0x82, 0x00, - 0x16, 0x09, 0x82, 0x7b, - 0x18, 0x09, 0x8a, 0x7d, - 0x1a, 0x09, 0x88, 0x81, - 0x1c, 0x09, 0x86, 0x7a, - 0x1e, 0x09, 0x84, 0x81, - 0x20, 0x09, 0x82, 0x7c, - 0x22, 0x09, 0x0a, 0x0a -}; - -static Byte_t RRegData[RREGDATASIZE] = { - 0x00, 0x09, 0xf6, 0x82, /* 00: Stop Rx processor */ - 0x08, 0x09, 0x8a, 0x13, /* 04: Tx software flow control */ - 0x0a, 0x09, 0xc5, 0x11, /* 08: XON char */ - 0x0c, 0x09, 0x86, 0x85, /* 0c: XANY */ - 0x12, 0x09, 0x41, 0xff, /* 10: Rx mask char */ - 0x14, 0x09, 0x82, 0x00, /* 14: Compare/Ignore #0 */ - 0x16, 0x09, 0x82, 0x7b, /* 18: Compare #1 */ - 0x18, 0x09, 0x8a, 0x7d, /* 1c: Compare #2 */ - 0x1a, 0x09, 0x88, 0x81, /* 20: Interrupt #1 */ - 0x1c, 0x09, 0x86, 0x7a, /* 24: Ignore/Replace #1 */ - 0x1e, 0x09, 0x84, 0x81, /* 28: Interrupt #2 */ - 0x20, 0x09, 0x82, 0x7c, /* 2c: Ignore/Replace #2 */ - 0x22, 0x09, 0x0a, 0x0a /* 30: Rx FIFO Enable */ -}; - -static CONTROLLER_T sController[CTL_SIZE] = { - {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, - {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}}, - {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, - {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}}, - {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, - {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}}, - {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, - {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}} -}; - -static Byte_t sBitMapClrTbl[8] = { - 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f -}; - -static Byte_t sBitMapSetTbl[8] = { - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 -}; - -static int sClockPrescale = 0x14; - -/* - * Line number is the ttySIx number (x), the Minor number. We - * assign them sequentially, starting at zero. The following - * array keeps track of the line number assigned to a given board/aiop/channel. - */ -static unsigned char lineNumbers[MAX_RP_PORTS]; -static unsigned long nextLineNumber; - -/***** RocketPort Static Prototypes *********/ -static int __init init_ISA(int i); -static void rp_wait_until_sent(struct tty_struct *tty, int timeout); -static void rp_flush_buffer(struct tty_struct *tty); -static unsigned char GetLineNumber(int ctrl, int aiop, int ch); -static unsigned char SetLineNumber(int ctrl, int aiop, int ch); -static void rp_start(struct tty_struct *tty); -static int sInitChan(CONTROLLER_T * CtlP, CHANNEL_T * ChP, int AiopNum, - int ChanNum); -static void sSetInterfaceMode(CHANNEL_T * ChP, Byte_t mode); -static void sFlushRxFIFO(CHANNEL_T * ChP); -static void sFlushTxFIFO(CHANNEL_T * ChP); -static void sEnInterrupts(CHANNEL_T * ChP, Word_t Flags); -static void sDisInterrupts(CHANNEL_T * ChP, Word_t Flags); -static void sModemReset(CONTROLLER_T * CtlP, int chan, int on); -static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on); -static int sWriteTxPrioByte(CHANNEL_T * ChP, Byte_t Data); -static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO, - ByteIO_t * AiopIOList, int AiopIOListSize, - int IRQNum, Byte_t Frequency, int PeriodicOnly); -static int sReadAiopID(ByteIO_t io); -static int sReadAiopNumChan(WordIO_t io); - -MODULE_AUTHOR("Theodore Ts'o"); -MODULE_DESCRIPTION("Comtrol RocketPort driver"); -module_param_hw(board1, ulong, ioport, 0); -MODULE_PARM_DESC(board1, "I/O port for (ISA) board #1"); -module_param_hw(board2, ulong, ioport, 0); -MODULE_PARM_DESC(board2, "I/O port for (ISA) board #2"); -module_param_hw(board3, ulong, ioport, 0); -MODULE_PARM_DESC(board3, "I/O port for (ISA) board #3"); -module_param_hw(board4, ulong, ioport, 0); -MODULE_PARM_DESC(board4, "I/O port for (ISA) board #4"); -module_param_hw(controller, ulong, ioport, 0); -MODULE_PARM_DESC(controller, "I/O port for (ISA) rocketport controller"); -module_param(support_low_speed, bool, 0); -MODULE_PARM_DESC(support_low_speed, "1 means support 50 baud, 0 means support 460400 baud"); -module_param(modem1, ulong, 0); -MODULE_PARM_DESC(modem1, "1 means (ISA) board #1 is a RocketModem"); -module_param(modem2, ulong, 0); -MODULE_PARM_DESC(modem2, "1 means (ISA) board #2 is a RocketModem"); -module_param(modem3, ulong, 0); -MODULE_PARM_DESC(modem3, "1 means (ISA) board #3 is a RocketModem"); -module_param(modem4, ulong, 0); -MODULE_PARM_DESC(modem4, "1 means (ISA) board #4 is a RocketModem"); -module_param_array(pc104_1, ulong, NULL, 0); -MODULE_PARM_DESC(pc104_1, "set interface types for ISA(PC104) board #1 (e.g. pc104_1=232,232,485,485,..."); -module_param_array(pc104_2, ulong, NULL, 0); -MODULE_PARM_DESC(pc104_2, "set interface types for ISA(PC104) board #2 (e.g. pc104_2=232,232,485,485,..."); -module_param_array(pc104_3, ulong, NULL, 0); -MODULE_PARM_DESC(pc104_3, "set interface types for ISA(PC104) board #3 (e.g. pc104_3=232,232,485,485,..."); -module_param_array(pc104_4, ulong, NULL, 0); -MODULE_PARM_DESC(pc104_4, "set interface types for ISA(PC104) board #4 (e.g. pc104_4=232,232,485,485,..."); - -static int __init rp_init(void); -static void rp_cleanup_module(void); - -module_init(rp_init); -module_exit(rp_cleanup_module); - - -MODULE_LICENSE("Dual BSD/GPL"); - -/*************************************************************************/ -/* Module code starts here */ - -static inline int rocket_paranoia_check(struct r_port *info, - const char *routine) -{ -#ifdef ROCKET_PARANOIA_CHECK - if (!info) - return 1; - if (info->magic != RPORT_MAGIC) { - printk(KERN_WARNING "Warning: bad magic number for rocketport " - "struct in %s\n", routine); - return 1; - } -#endif - return 0; -} - - -/* Serial port receive data function. Called (from timer poll) when an AIOPIC signals - * that receive data is present on a serial port. Pulls data from FIFO, moves it into the - * tty layer. - */ -static void rp_do_receive(struct r_port *info, CHANNEL_t *cp, - unsigned int ChanStatus) -{ - unsigned int CharNStat; - int ToRecv, wRecv, space; - unsigned char *cbuf; - - ToRecv = sGetRxCnt(cp); -#ifdef ROCKET_DEBUG_INTR - printk(KERN_INFO "rp_do_receive(%d)...\n", ToRecv); -#endif - if (ToRecv == 0) - return; - - /* - * if status indicates there are errored characters in the - * FIFO, then enter status mode (a word in FIFO holds - * character and status). - */ - if (ChanStatus & (RXFOVERFL | RXBREAK | RXFRAME | RXPARITY)) { - if (!(ChanStatus & STATMODE)) { -#ifdef ROCKET_DEBUG_RECEIVE - printk(KERN_INFO "Entering STATMODE...\n"); -#endif - ChanStatus |= STATMODE; - sEnRxStatusMode(cp); - } - } - - /* - * if we previously entered status mode, then read down the - * FIFO one word at a time, pulling apart the character and - * the status. Update error counters depending on status - */ - if (ChanStatus & STATMODE) { -#ifdef ROCKET_DEBUG_RECEIVE - printk(KERN_INFO "Ignore %x, read %x...\n", - info->ignore_status_mask, info->read_status_mask); -#endif - while (ToRecv) { - char flag; - - CharNStat = sInW(sGetTxRxDataIO(cp)); -#ifdef ROCKET_DEBUG_RECEIVE - printk(KERN_INFO "%x...\n", CharNStat); -#endif - if (CharNStat & STMBREAKH) - CharNStat &= ~(STMFRAMEH | STMPARITYH); - if (CharNStat & info->ignore_status_mask) { - ToRecv--; - continue; - } - CharNStat &= info->read_status_mask; - if (CharNStat & STMBREAKH) - flag = TTY_BREAK; - else if (CharNStat & STMPARITYH) - flag = TTY_PARITY; - else if (CharNStat & STMFRAMEH) - flag = TTY_FRAME; - else if (CharNStat & STMRCVROVRH) - flag = TTY_OVERRUN; - else - flag = TTY_NORMAL; - tty_insert_flip_char(&info->port, CharNStat & 0xff, - flag); - ToRecv--; - } - - /* - * after we've emptied the FIFO in status mode, turn - * status mode back off - */ - if (sGetRxCnt(cp) == 0) { -#ifdef ROCKET_DEBUG_RECEIVE - printk(KERN_INFO "Status mode off.\n"); -#endif - sDisRxStatusMode(cp); - } - } else { - /* - * we aren't in status mode, so read down the FIFO two - * characters at time by doing repeated word IO - * transfer. - */ - space = tty_prepare_flip_string(&info->port, &cbuf, ToRecv); - if (space < ToRecv) { -#ifdef ROCKET_DEBUG_RECEIVE - printk(KERN_INFO "rp_do_receive:insufficient space ToRecv=%d space=%d\n", ToRecv, space); -#endif - if (space <= 0) - return; - ToRecv = space; - } - wRecv = ToRecv >> 1; - if (wRecv) - sInStrW(sGetTxRxDataIO(cp), (unsigned short *) cbuf, wRecv); - if (ToRecv & 1) - cbuf[ToRecv - 1] = sInB(sGetTxRxDataIO(cp)); - } - /* Push the data up to the tty layer */ - tty_flip_buffer_push(&info->port); -} - -/* - * Serial port transmit data function. Called from the timer polling loop as a - * result of a bit set in xmit_flags[], indicating data (from the tty layer) is ready - * to be sent out the serial port. Data is buffered in rp_table[line].xmit_buf, it is - * moved to the port's xmit FIFO. *info is critical data, protected by spinlocks. - */ -static void rp_do_transmit(struct r_port *info) -{ - int c; - CHANNEL_t *cp = &info->channel; - struct tty_struct *tty; - unsigned long flags; - -#ifdef ROCKET_DEBUG_INTR - printk(KERN_DEBUG "%s\n", __func__); -#endif - if (!info) - return; - tty = tty_port_tty_get(&info->port); - - if (tty == NULL) { - printk(KERN_WARNING "rp: WARNING %s called with tty==NULL\n", __func__); - clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); - return; - } - - spin_lock_irqsave(&info->slock, flags); - info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); - - /* Loop sending data to FIFO until done or FIFO full */ - while (1) { - if (tty->stopped) - break; - c = min(info->xmit_fifo_room, info->xmit_cnt); - c = min(c, XMIT_BUF_SIZE - info->xmit_tail); - if (c <= 0 || info->xmit_fifo_room <= 0) - break; - sOutStrW(sGetTxRxDataIO(cp), (unsigned short *) (info->xmit_buf + info->xmit_tail), c / 2); - if (c & 1) - sOutB(sGetTxRxDataIO(cp), info->xmit_buf[info->xmit_tail + c - 1]); - info->xmit_tail += c; - info->xmit_tail &= XMIT_BUF_SIZE - 1; - info->xmit_cnt -= c; - info->xmit_fifo_room -= c; -#ifdef ROCKET_DEBUG_INTR - printk(KERN_INFO "tx %d chars...\n", c); -#endif - } - - if (info->xmit_cnt == 0) - clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); - - if (info->xmit_cnt < WAKEUP_CHARS) { - tty_wakeup(tty); -#ifdef ROCKETPORT_HAVE_POLL_WAIT - wake_up_interruptible(&tty->poll_wait); -#endif - } - - spin_unlock_irqrestore(&info->slock, flags); - tty_kref_put(tty); - -#ifdef ROCKET_DEBUG_INTR - printk(KERN_DEBUG "(%d,%d,%d,%d)...\n", info->xmit_cnt, info->xmit_head, - info->xmit_tail, info->xmit_fifo_room); -#endif -} - -/* - * Called when a serial port signals it has read data in it's RX FIFO. - * It checks what interrupts are pending and services them, including - * receiving serial data. - */ -static void rp_handle_port(struct r_port *info) -{ - CHANNEL_t *cp; - unsigned int IntMask, ChanStatus; - - if (!info) - return; - - if (!tty_port_initialized(&info->port)) { - printk(KERN_WARNING "rp: WARNING: rp_handle_port called with " - "info->flags & NOT_INIT\n"); - return; - } - - cp = &info->channel; - - IntMask = sGetChanIntID(cp) & info->intmask; -#ifdef ROCKET_DEBUG_INTR - printk(KERN_INFO "rp_interrupt %02x...\n", IntMask); -#endif - ChanStatus = sGetChanStatus(cp); - if (IntMask & RXF_TRIG) { /* Rx FIFO trigger level */ - rp_do_receive(info, cp, ChanStatus); - } - if (IntMask & DELTA_CD) { /* CD change */ -#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_INTR) || defined(ROCKET_DEBUG_HANGUP)) - printk(KERN_INFO "ttyR%d CD now %s...\n", info->line, - (ChanStatus & CD_ACT) ? "on" : "off"); -#endif - if (!(ChanStatus & CD_ACT) && info->cd_status) { -#ifdef ROCKET_DEBUG_HANGUP - printk(KERN_INFO "CD drop, calling hangup.\n"); -#endif - tty_port_tty_hangup(&info->port, false); - } - info->cd_status = (ChanStatus & CD_ACT) ? 1 : 0; - wake_up_interruptible(&info->port.open_wait); - } -#ifdef ROCKET_DEBUG_INTR - if (IntMask & DELTA_CTS) { /* CTS change */ - printk(KERN_INFO "CTS change...\n"); - } - if (IntMask & DELTA_DSR) { /* DSR change */ - printk(KERN_INFO "DSR change...\n"); - } -#endif -} - -/* - * The top level polling routine. Repeats every 1/100 HZ (10ms). - */ -static void rp_do_poll(struct timer_list *unused) -{ - CONTROLLER_t *ctlp; - int ctrl, aiop, ch, line; - unsigned int xmitmask, i; - unsigned int CtlMask; - unsigned char AiopMask; - Word_t bit; - - /* Walk through all the boards (ctrl's) */ - for (ctrl = 0; ctrl < max_board; ctrl++) { - if (rcktpt_io_addr[ctrl] <= 0) - continue; - - /* Get a ptr to the board's control struct */ - ctlp = sCtlNumToCtlPtr(ctrl); - - /* Get the interrupt status from the board */ -#ifdef CONFIG_PCI - if (ctlp->BusType == isPCI) - CtlMask = sPCIGetControllerIntStatus(ctlp); - else -#endif - CtlMask = sGetControllerIntStatus(ctlp); - - /* Check if any AIOP read bits are set */ - for (aiop = 0; CtlMask; aiop++) { - bit = ctlp->AiopIntrBits[aiop]; - if (CtlMask & bit) { - CtlMask &= ~bit; - AiopMask = sGetAiopIntStatus(ctlp, aiop); - - /* Check if any port read bits are set */ - for (ch = 0; AiopMask; AiopMask >>= 1, ch++) { - if (AiopMask & 1) { - - /* Get the line number (/dev/ttyRx number). */ - /* Read the data from the port. */ - line = GetLineNumber(ctrl, aiop, ch); - rp_handle_port(rp_table[line]); - } - } - } - } - - xmitmask = xmit_flags[ctrl]; - - /* - * xmit_flags contains bit-significant flags, indicating there is data - * to xmit on the port. Bit 0 is port 0 on this board, bit 1 is port - * 1, ... (32 total possible). The variable i has the aiop and ch - * numbers encoded in it (port 0-7 are aiop0, 8-15 are aiop1, etc). - */ - if (xmitmask) { - for (i = 0; i < rocketModel[ctrl].numPorts; i++) { - if (xmitmask & (1 << i)) { - aiop = (i & 0x18) >> 3; - ch = i & 0x07; - line = GetLineNumber(ctrl, aiop, ch); - rp_do_transmit(rp_table[line]); - } - } - } - } - - /* - * Reset the timer so we get called at the next clock tick (10ms). - */ - if (atomic_read(&rp_num_ports_open)) - mod_timer(&rocket_timer, jiffies + POLL_PERIOD); -} - -/* - * Initializes the r_port structure for a port, as well as enabling the port on - * the board. - * Inputs: board, aiop, chan numbers - */ -static void __init -init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev) -{ - unsigned rocketMode; - struct r_port *info; - int line; - CONTROLLER_T *ctlp; - - /* Get the next available line number */ - line = SetLineNumber(board, aiop, chan); - - ctlp = sCtlNumToCtlPtr(board); - - /* Get a r_port struct for the port, fill it in and save it globally, indexed by line number */ - info = kzalloc(sizeof (struct r_port), GFP_KERNEL); - if (!info) { - printk(KERN_ERR "Couldn't allocate info struct for line #%d\n", - line); - return; - } - - info->magic = RPORT_MAGIC; - info->line = line; - info->ctlp = ctlp; - info->board = board; - info->aiop = aiop; - info->chan = chan; - tty_port_init(&info->port); - info->port.ops = &rocket_port_ops; - info->flags &= ~ROCKET_MODE_MASK; - if (board < ARRAY_SIZE(pc104) && line < ARRAY_SIZE(pc104_1)) - switch (pc104[board][line]) { - case 422: - info->flags |= ROCKET_MODE_RS422; - break; - case 485: - info->flags |= ROCKET_MODE_RS485; - break; - case 232: - default: - info->flags |= ROCKET_MODE_RS232; - break; - } - else - info->flags |= ROCKET_MODE_RS232; - - info->intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR; - if (sInitChan(ctlp, &info->channel, aiop, chan) == 0) { - printk(KERN_ERR "RocketPort sInitChan(%d, %d, %d) failed!\n", - board, aiop, chan); - tty_port_destroy(&info->port); - kfree(info); - return; - } - - rocketMode = info->flags & ROCKET_MODE_MASK; - - if ((info->flags & ROCKET_RTS_TOGGLE) || (rocketMode == ROCKET_MODE_RS485)) - sEnRTSToggle(&info->channel); - else - sDisRTSToggle(&info->channel); - - if (ctlp->boardType == ROCKET_TYPE_PC104) { - switch (rocketMode) { - case ROCKET_MODE_RS485: - sSetInterfaceMode(&info->channel, InterfaceModeRS485); - break; - case ROCKET_MODE_RS422: - sSetInterfaceMode(&info->channel, InterfaceModeRS422); - break; - case ROCKET_MODE_RS232: - default: - if (info->flags & ROCKET_RTS_TOGGLE) - sSetInterfaceMode(&info->channel, InterfaceModeRS232T); - else - sSetInterfaceMode(&info->channel, InterfaceModeRS232); - break; - } - } - spin_lock_init(&info->slock); - mutex_init(&info->write_mtx); - rp_table[line] = info; - tty_port_register_device(&info->port, rocket_driver, line, - pci_dev ? &pci_dev->dev : NULL); -} - -/* - * Configures a rocketport port according to its termio settings. Called from - * user mode into the driver (exception handler). *info CD manipulation is spinlock protected. - */ -static void configure_r_port(struct tty_struct *tty, struct r_port *info, - struct ktermios *old_termios) -{ - unsigned cflag; - unsigned long flags; - unsigned rocketMode; - int bits, baud, divisor; - CHANNEL_t *cp; - struct ktermios *t = &tty->termios; - - cp = &info->channel; - cflag = t->c_cflag; - - /* Byte size and parity */ - if ((cflag & CSIZE) == CS8) { - sSetData8(cp); - bits = 10; - } else { - sSetData7(cp); - bits = 9; - } - if (cflag & CSTOPB) { - sSetStop2(cp); - bits++; - } else { - sSetStop1(cp); - } - - if (cflag & PARENB) { - sEnParity(cp); - bits++; - if (cflag & PARODD) { - sSetOddParity(cp); - } else { - sSetEvenParity(cp); - } - } else { - sDisParity(cp); - } - - /* baud rate */ - baud = tty_get_baud_rate(tty); - if (!baud) - baud = 9600; - divisor = ((rp_baud_base[info->board] + (baud >> 1)) / baud) - 1; - if ((divisor >= 8192 || divisor < 0) && old_termios) { - baud = tty_termios_baud_rate(old_termios); - if (!baud) - baud = 9600; - divisor = (rp_baud_base[info->board] / baud) - 1; - } - if (divisor >= 8192 || divisor < 0) { - baud = 9600; - divisor = (rp_baud_base[info->board] / baud) - 1; - } - info->cps = baud / bits; - sSetBaud(cp, divisor); - - /* FIXME: Should really back compute a baud rate from the divisor */ - tty_encode_baud_rate(tty, baud, baud); - - if (cflag & CRTSCTS) { - info->intmask |= DELTA_CTS; - sEnCTSFlowCtl(cp); - } else { - info->intmask &= ~DELTA_CTS; - sDisCTSFlowCtl(cp); - } - if (cflag & CLOCAL) { - info->intmask &= ~DELTA_CD; - } else { - spin_lock_irqsave(&info->slock, flags); - if (sGetChanStatus(cp) & CD_ACT) - info->cd_status = 1; - else - info->cd_status = 0; - info->intmask |= DELTA_CD; - spin_unlock_irqrestore(&info->slock, flags); - } - - /* - * Handle software flow control in the board - */ -#ifdef ROCKET_SOFT_FLOW - if (I_IXON(tty)) { - sEnTxSoftFlowCtl(cp); - if (I_IXANY(tty)) { - sEnIXANY(cp); - } else { - sDisIXANY(cp); - } - sSetTxXONChar(cp, START_CHAR(tty)); - sSetTxXOFFChar(cp, STOP_CHAR(tty)); - } else { - sDisTxSoftFlowCtl(cp); - sDisIXANY(cp); - sClrTxXOFF(cp); - } -#endif - - /* - * Set up ignore/read mask words - */ - info->read_status_mask = STMRCVROVRH | 0xFF; - if (I_INPCK(tty)) - info->read_status_mask |= STMFRAMEH | STMPARITYH; - if (I_BRKINT(tty) || I_PARMRK(tty)) - info->read_status_mask |= STMBREAKH; - - /* - * Characters to ignore - */ - info->ignore_status_mask = 0; - if (I_IGNPAR(tty)) - info->ignore_status_mask |= STMFRAMEH | STMPARITYH; - if (I_IGNBRK(tty)) { - info->ignore_status_mask |= STMBREAKH; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too. (For real raw support). - */ - if (I_IGNPAR(tty)) - info->ignore_status_mask |= STMRCVROVRH; - } - - rocketMode = info->flags & ROCKET_MODE_MASK; - - if ((info->flags & ROCKET_RTS_TOGGLE) - || (rocketMode == ROCKET_MODE_RS485)) - sEnRTSToggle(cp); - else - sDisRTSToggle(cp); - - sSetRTS(&info->channel); - - if (cp->CtlP->boardType == ROCKET_TYPE_PC104) { - switch (rocketMode) { - case ROCKET_MODE_RS485: - sSetInterfaceMode(cp, InterfaceModeRS485); - break; - case ROCKET_MODE_RS422: - sSetInterfaceMode(cp, InterfaceModeRS422); - break; - case ROCKET_MODE_RS232: - default: - if (info->flags & ROCKET_RTS_TOGGLE) - sSetInterfaceMode(cp, InterfaceModeRS232T); - else - sSetInterfaceMode(cp, InterfaceModeRS232); - break; - } - } -} - -static int carrier_raised(struct tty_port *port) -{ - struct r_port *info = container_of(port, struct r_port, port); - return (sGetChanStatusLo(&info->channel) & CD_ACT) ? 1 : 0; -} - -static void dtr_rts(struct tty_port *port, int on) -{ - struct r_port *info = container_of(port, struct r_port, port); - if (on) { - sSetDTR(&info->channel); - sSetRTS(&info->channel); - } else { - sClrDTR(&info->channel); - sClrRTS(&info->channel); - } -} - -/* - * Exception handler that opens a serial port. Creates xmit_buf storage, fills in - * port's r_port struct. Initializes the port hardware. - */ -static int rp_open(struct tty_struct *tty, struct file *filp) -{ - struct r_port *info; - struct tty_port *port; - int retval; - CHANNEL_t *cp; - unsigned long page; - - info = rp_table[tty->index]; - if (info == NULL) - return -ENXIO; - port = &info->port; - - page = __get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - /* - * We must not sleep from here until the port is marked fully in use. - */ - if (info->xmit_buf) - free_page(page); - else - info->xmit_buf = (unsigned char *) page; - - tty->driver_data = info; - tty_port_tty_set(port, tty); - - if (port->count++ == 0) { - atomic_inc(&rp_num_ports_open); - -#ifdef ROCKET_DEBUG_OPEN - printk(KERN_INFO "rocket mod++ = %d...\n", - atomic_read(&rp_num_ports_open)); -#endif - } -#ifdef ROCKET_DEBUG_OPEN - printk(KERN_INFO "rp_open ttyR%d, count=%d\n", info->line, info->port.count); -#endif - - /* - * Info->count is now 1; so it's safe to sleep now. - */ - if (!tty_port_initialized(port)) { - cp = &info->channel; - sSetRxTrigger(cp, TRIG_1); - if (sGetChanStatus(cp) & CD_ACT) - info->cd_status = 1; - else - info->cd_status = 0; - sDisRxStatusMode(cp); - sFlushRxFIFO(cp); - sFlushTxFIFO(cp); - - sEnInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN)); - sSetRxTrigger(cp, TRIG_1); - - sGetChanStatus(cp); - sDisRxStatusMode(cp); - sClrTxXOFF(cp); - - sDisCTSFlowCtl(cp); - sDisTxSoftFlowCtl(cp); - - sEnRxFIFO(cp); - sEnTransmit(cp); - - tty_port_set_initialized(&info->port, 1); - - configure_r_port(tty, info, NULL); - if (C_BAUD(tty)) { - sSetDTR(cp); - sSetRTS(cp); - } - } - /* Starts (or resets) the maint polling loop */ - mod_timer(&rocket_timer, jiffies + POLL_PERIOD); - - retval = tty_port_block_til_ready(port, tty, filp); - if (retval) { -#ifdef ROCKET_DEBUG_OPEN - printk(KERN_INFO "rp_open returning after block_til_ready with %d\n", retval); -#endif - return retval; - } - return 0; -} - -/* - * Exception handler that closes a serial port. info->port.count is considered critical. - */ -static void rp_close(struct tty_struct *tty, struct file *filp) -{ - struct r_port *info = tty->driver_data; - struct tty_port *port = &info->port; - int timeout; - CHANNEL_t *cp; - - if (rocket_paranoia_check(info, "rp_close")) - return; - -#ifdef ROCKET_DEBUG_OPEN - printk(KERN_INFO "rp_close ttyR%d, count = %d\n", info->line, info->port.count); -#endif - - if (tty_port_close_start(port, tty, filp) == 0) - return; - - mutex_lock(&port->mutex); - cp = &info->channel; - /* - * Before we drop DTR, make sure the UART transmitter - * has completely drained; this is especially - * important if there is a transmit FIFO! - */ - timeout = (sGetTxCnt(cp) + 1) * HZ / info->cps; - if (timeout == 0) - timeout = 1; - rp_wait_until_sent(tty, timeout); - clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); - - sDisTransmit(cp); - sDisInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN)); - sDisCTSFlowCtl(cp); - sDisTxSoftFlowCtl(cp); - sClrTxXOFF(cp); - sFlushRxFIFO(cp); - sFlushTxFIFO(cp); - sClrRTS(cp); - if (C_HUPCL(tty)) - sClrDTR(cp); - - rp_flush_buffer(tty); - - tty_ldisc_flush(tty); - - clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); - - /* We can't yet use tty_port_close_end as the buffer handling in this - driver is a bit different to the usual */ - - if (port->blocked_open) { - if (port->close_delay) { - msleep_interruptible(jiffies_to_msecs(port->close_delay)); - } - wake_up_interruptible(&port->open_wait); - } else { - if (info->xmit_buf) { - free_page((unsigned long) info->xmit_buf); - info->xmit_buf = NULL; - } - } - spin_lock_irq(&port->lock); - tty->closing = 0; - spin_unlock_irq(&port->lock); - tty_port_set_initialized(port, 0); - tty_port_set_active(port, 0); - mutex_unlock(&port->mutex); - tty_port_tty_set(port, NULL); - - atomic_dec(&rp_num_ports_open); - -#ifdef ROCKET_DEBUG_OPEN - printk(KERN_INFO "rocket mod-- = %d...\n", - atomic_read(&rp_num_ports_open)); - printk(KERN_INFO "rp_close ttyR%d complete shutdown\n", info->line); -#endif - -} - -static void rp_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - unsigned cflag; - - if (rocket_paranoia_check(info, "rp_set_termios")) - return; - - cflag = tty->termios.c_cflag; - - /* - * This driver doesn't support CS5 or CS6 - */ - if (((cflag & CSIZE) == CS5) || ((cflag & CSIZE) == CS6)) - tty->termios.c_cflag = - ((cflag & ~CSIZE) | (old_termios->c_cflag & CSIZE)); - /* Or CMSPAR */ - tty->termios.c_cflag &= ~CMSPAR; - - configure_r_port(tty, info, old_termios); - - cp = &info->channel; - - /* Handle transition to B0 status */ - if ((old_termios->c_cflag & CBAUD) && !C_BAUD(tty)) { - sClrDTR(cp); - sClrRTS(cp); - } - - /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && C_BAUD(tty)) { - sSetRTS(cp); - sSetDTR(cp); - } - - if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) - rp_start(tty); -} - -static int rp_break(struct tty_struct *tty, int break_state) -{ - struct r_port *info = tty->driver_data; - unsigned long flags; - - if (rocket_paranoia_check(info, "rp_break")) - return -EINVAL; - - spin_lock_irqsave(&info->slock, flags); - if (break_state == -1) - sSendBreak(&info->channel); - else - sClrBreak(&info->channel); - spin_unlock_irqrestore(&info->slock, flags); - return 0; -} - -/* - * sGetChanRI used to be a macro in rocket_int.h. When the functionality for - * the UPCI boards was added, it was decided to make this a function because - * the macro was getting too complicated. All cases except the first one - * (UPCIRingInd) are taken directly from the original macro. - */ -static int sGetChanRI(CHANNEL_T * ChP) -{ - CONTROLLER_t *CtlP = ChP->CtlP; - int ChanNum = ChP->ChanNum; - int RingInd = 0; - - if (CtlP->UPCIRingInd) - RingInd = !(sInB(CtlP->UPCIRingInd) & sBitMapSetTbl[ChanNum]); - else if (CtlP->AltChanRingIndicator) - RingInd = sInB((ByteIO_t) (ChP->ChanStat + 8)) & DSR_ACT; - else if (CtlP->boardType == ROCKET_TYPE_PC104) - RingInd = !(sInB(CtlP->AiopIO[3]) & sBitMapSetTbl[ChanNum]); - - return RingInd; -} - -/********************************************************************************************/ -/* Here are the routines used by rp_ioctl. These are all called from exception handlers. */ - -/* - * Returns the state of the serial modem control lines. These next 2 functions - * are the way kernel versions > 2.5 handle modem control lines rather than IOCTLs. - */ -static int rp_tiocmget(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - unsigned int control, result, ChanStatus; - - ChanStatus = sGetChanStatusLo(&info->channel); - control = info->channel.TxControl[3]; - result = ((control & SET_RTS) ? TIOCM_RTS : 0) | - ((control & SET_DTR) ? TIOCM_DTR : 0) | - ((ChanStatus & CD_ACT) ? TIOCM_CAR : 0) | - (sGetChanRI(&info->channel) ? TIOCM_RNG : 0) | - ((ChanStatus & DSR_ACT) ? TIOCM_DSR : 0) | - ((ChanStatus & CTS_ACT) ? TIOCM_CTS : 0); - - return result; -} - -/* - * Sets the modem control lines - */ -static int rp_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct r_port *info = tty->driver_data; - - if (set & TIOCM_RTS) - info->channel.TxControl[3] |= SET_RTS; - if (set & TIOCM_DTR) - info->channel.TxControl[3] |= SET_DTR; - if (clear & TIOCM_RTS) - info->channel.TxControl[3] &= ~SET_RTS; - if (clear & TIOCM_DTR) - info->channel.TxControl[3] &= ~SET_DTR; - - out32(info->channel.IndexAddr, info->channel.TxControl); - return 0; -} - -static int get_config(struct r_port *info, struct rocket_config __user *retinfo) -{ - struct rocket_config tmp; - - memset(&tmp, 0, sizeof (tmp)); - mutex_lock(&info->port.mutex); - tmp.line = info->line; - tmp.flags = info->flags; - tmp.close_delay = info->port.close_delay; - tmp.closing_wait = info->port.closing_wait; - tmp.port = rcktpt_io_addr[(info->line >> 5) & 3]; - mutex_unlock(&info->port.mutex); - - if (copy_to_user(retinfo, &tmp, sizeof (*retinfo))) - return -EFAULT; - return 0; -} - -static int set_config(struct tty_struct *tty, struct r_port *info, - struct rocket_config __user *new_info) -{ - struct rocket_config new_serial; - - if (copy_from_user(&new_serial, new_info, sizeof (new_serial))) - return -EFAULT; - - mutex_lock(&info->port.mutex); - if (!capable(CAP_SYS_ADMIN)) - { - if ((new_serial.flags & ~ROCKET_USR_MASK) != (info->flags & ~ROCKET_USR_MASK)) { - mutex_unlock(&info->port.mutex); - return -EPERM; - } - info->flags = ((info->flags & ~ROCKET_USR_MASK) | (new_serial.flags & ROCKET_USR_MASK)); - mutex_unlock(&info->port.mutex); - return 0; - } - - if ((new_serial.flags ^ info->flags) & ROCKET_SPD_MASK) { - /* warn about deprecation, unless clearing */ - if (new_serial.flags & ROCKET_SPD_MASK) - dev_warn_ratelimited(tty->dev, "use of SPD flags is deprecated\n"); - } - - info->flags = ((info->flags & ~ROCKET_FLAGS) | (new_serial.flags & ROCKET_FLAGS)); - info->port.close_delay = new_serial.close_delay; - info->port.closing_wait = new_serial.closing_wait; - - mutex_unlock(&info->port.mutex); - - configure_r_port(tty, info, NULL); - return 0; -} - -/* - * This function fills in a rocket_ports struct with information - * about what boards/ports are in the system. This info is passed - * to user space. See setrocket.c where the info is used to create - * the /dev/ttyRx ports. - */ -static int get_ports(struct r_port *info, struct rocket_ports __user *retports) -{ - struct rocket_ports *tmp; - int board, ret = 0; - - tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); - if (!tmp) - return -ENOMEM; - - tmp->tty_major = rocket_driver->major; - - for (board = 0; board < 4; board++) { - tmp->rocketModel[board].model = rocketModel[board].model; - strcpy(tmp->rocketModel[board].modelString, - rocketModel[board].modelString); - tmp->rocketModel[board].numPorts = rocketModel[board].numPorts; - tmp->rocketModel[board].loadrm2 = rocketModel[board].loadrm2; - tmp->rocketModel[board].startingPortNumber = - rocketModel[board].startingPortNumber; - } - if (copy_to_user(retports, tmp, sizeof(*retports))) - ret = -EFAULT; - kfree(tmp); - return ret; -} - -static int reset_rm2(struct r_port *info, void __user *arg) -{ - int reset; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - if (copy_from_user(&reset, arg, sizeof (int))) - return -EFAULT; - if (reset) - reset = 1; - - if (rcktpt_type[info->board] != ROCKET_TYPE_MODEMII && - rcktpt_type[info->board] != ROCKET_TYPE_MODEMIII) - return -EINVAL; - - if (info->ctlp->BusType == isISA) - sModemReset(info->ctlp, info->chan, reset); - else - sPCIModemReset(info->ctlp, info->chan, reset); - - return 0; -} - -static int get_version(struct r_port *info, struct rocket_version __user *retvers) -{ - if (copy_to_user(retvers, &driver_version, sizeof (*retvers))) - return -EFAULT; - return 0; -} - -/* IOCTL call handler into the driver */ -static int rp_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct r_port *info = tty->driver_data; - void __user *argp = (void __user *)arg; - int ret = 0; - - if (cmd != RCKP_GET_PORTS && rocket_paranoia_check(info, "rp_ioctl")) - return -ENXIO; - - switch (cmd) { - case RCKP_GET_CONFIG: - dev_warn_ratelimited(tty->dev, - "RCKP_GET_CONFIG option is deprecated\n"); - ret = get_config(info, argp); - break; - case RCKP_SET_CONFIG: - dev_warn_ratelimited(tty->dev, - "RCKP_SET_CONFIG option is deprecated\n"); - ret = set_config(tty, info, argp); - break; - case RCKP_GET_PORTS: - dev_warn_ratelimited(tty->dev, - "RCKP_GET_PORTS option is deprecated\n"); - ret = get_ports(info, argp); - break; - case RCKP_RESET_RM2: - dev_warn_ratelimited(tty->dev, - "RCKP_RESET_RM2 option is deprecated\n"); - ret = reset_rm2(info, argp); - break; - case RCKP_GET_VERSION: - dev_warn_ratelimited(tty->dev, - "RCKP_GET_VERSION option is deprecated\n"); - ret = get_version(info, argp); - break; - default: - ret = -ENOIOCTLCMD; - } - return ret; -} - -static void rp_send_xchar(struct tty_struct *tty, char ch) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - - if (rocket_paranoia_check(info, "rp_send_xchar")) - return; - - cp = &info->channel; - if (sGetTxCnt(cp)) - sWriteTxPrioByte(cp, ch); - else - sWriteTxByte(sGetTxRxDataIO(cp), ch); -} - -static void rp_throttle(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - -#ifdef ROCKET_DEBUG_THROTTLE - printk(KERN_INFO "throttle %s ....\n", tty->name); -#endif - - if (rocket_paranoia_check(info, "rp_throttle")) - return; - - if (I_IXOFF(tty)) - rp_send_xchar(tty, STOP_CHAR(tty)); - - sClrRTS(&info->channel); -} - -static void rp_unthrottle(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; -#ifdef ROCKET_DEBUG_THROTTLE - printk(KERN_INFO "unthrottle %s ....\n", tty->name); -#endif - - if (rocket_paranoia_check(info, "rp_unthrottle")) - return; - - if (I_IXOFF(tty)) - rp_send_xchar(tty, START_CHAR(tty)); - - sSetRTS(&info->channel); -} - -/* - * ------------------------------------------------------------ - * rp_stop() and rp_start() - * - * This routines are called before setting or resetting tty->stopped. - * They enable or disable transmitter interrupts, as necessary. - * ------------------------------------------------------------ - */ -static void rp_stop(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - -#ifdef ROCKET_DEBUG_FLOW - printk(KERN_INFO "stop %s: %d %d....\n", tty->name, - info->xmit_cnt, info->xmit_fifo_room); -#endif - - if (rocket_paranoia_check(info, "rp_stop")) - return; - - if (sGetTxCnt(&info->channel)) - sDisTransmit(&info->channel); -} - -static void rp_start(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - -#ifdef ROCKET_DEBUG_FLOW - printk(KERN_INFO "start %s: %d %d....\n", tty->name, - info->xmit_cnt, info->xmit_fifo_room); -#endif - - if (rocket_paranoia_check(info, "rp_stop")) - return; - - sEnTransmit(&info->channel); - set_bit((info->aiop * 8) + info->chan, - (void *) &xmit_flags[info->board]); -} - -/* - * rp_wait_until_sent() --- wait until the transmitter is empty - */ -static void rp_wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - unsigned long orig_jiffies; - int check_time, exit_time; - int txcnt; - - if (rocket_paranoia_check(info, "rp_wait_until_sent")) - return; - - cp = &info->channel; - - orig_jiffies = jiffies; -#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT - printk(KERN_INFO "In %s(%d) (jiff=%lu)...\n", __func__, timeout, - jiffies); - printk(KERN_INFO "cps=%d...\n", info->cps); -#endif - while (1) { - txcnt = sGetTxCnt(cp); - if (!txcnt) { - if (sGetChanStatusLo(cp) & TXSHRMT) - break; - check_time = (HZ / info->cps) / 5; - } else { - check_time = HZ * txcnt / info->cps; - } - if (timeout) { - exit_time = orig_jiffies + timeout - jiffies; - if (exit_time <= 0) - break; - if (exit_time < check_time) - check_time = exit_time; - } - if (check_time == 0) - check_time = 1; -#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT - printk(KERN_INFO "txcnt = %d (jiff=%lu,check=%d)...\n", txcnt, - jiffies, check_time); -#endif - msleep_interruptible(jiffies_to_msecs(check_time)); - if (signal_pending(current)) - break; - } - __set_current_state(TASK_RUNNING); -#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT - printk(KERN_INFO "txcnt = %d (jiff=%lu)...done\n", txcnt, jiffies); -#endif -} - -/* - * rp_hangup() --- called by tty_hangup() when a hangup is signaled. - */ -static void rp_hangup(struct tty_struct *tty) -{ - CHANNEL_t *cp; - struct r_port *info = tty->driver_data; - unsigned long flags; - - if (rocket_paranoia_check(info, "rp_hangup")) - return; - -#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_HANGUP)) - printk(KERN_INFO "rp_hangup of ttyR%d...\n", info->line); -#endif - rp_flush_buffer(tty); - spin_lock_irqsave(&info->port.lock, flags); - if (info->port.count) - atomic_dec(&rp_num_ports_open); - clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); - spin_unlock_irqrestore(&info->port.lock, flags); - - tty_port_hangup(&info->port); - - cp = &info->channel; - sDisRxFIFO(cp); - sDisTransmit(cp); - sDisInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN)); - sDisCTSFlowCtl(cp); - sDisTxSoftFlowCtl(cp); - sClrTxXOFF(cp); - tty_port_set_initialized(&info->port, 0); - - wake_up_interruptible(&info->port.open_wait); -} - -/* - * Exception handler - write char routine. The RocketPort driver uses a - * double-buffering strategy, with the twist that if the in-memory CPU - * buffer is empty, and there's space in the transmit FIFO, the - * writing routines will write directly to transmit FIFO. - * Write buffer and counters protected by spinlocks - */ -static int rp_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - unsigned long flags; - - if (rocket_paranoia_check(info, "rp_put_char")) - return 0; - - /* - * Grab the port write mutex, locking out other processes that try to - * write to this port - */ - mutex_lock(&info->write_mtx); - -#ifdef ROCKET_DEBUG_WRITE - printk(KERN_INFO "rp_put_char %c...\n", ch); -#endif - - spin_lock_irqsave(&info->slock, flags); - cp = &info->channel; - - if (!tty->stopped && info->xmit_fifo_room == 0) - info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); - - if (tty->stopped || info->xmit_fifo_room == 0 || info->xmit_cnt != 0) { - info->xmit_buf[info->xmit_head++] = ch; - info->xmit_head &= XMIT_BUF_SIZE - 1; - info->xmit_cnt++; - set_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); - } else { - sOutB(sGetTxRxDataIO(cp), ch); - info->xmit_fifo_room--; - } - spin_unlock_irqrestore(&info->slock, flags); - mutex_unlock(&info->write_mtx); - return 1; -} - -/* - * Exception handler - write routine, called when user app writes to the device. - * A per port write mutex is used to protect from another process writing to - * this port at the same time. This other process could be running on the other CPU - * or get control of the CPU if the copy_from_user() blocks due to a page fault (swapped out). - * Spinlocks protect the info xmit members. - */ -static int rp_write(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - const unsigned char *b; - int c, retval = 0; - unsigned long flags; - - if (count <= 0 || rocket_paranoia_check(info, "rp_write")) - return 0; - - if (mutex_lock_interruptible(&info->write_mtx)) - return -ERESTARTSYS; - -#ifdef ROCKET_DEBUG_WRITE - printk(KERN_INFO "rp_write %d chars...\n", count); -#endif - cp = &info->channel; - - if (!tty->stopped && info->xmit_fifo_room < count) - info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); - - /* - * If the write queue for the port is empty, and there is FIFO space, stuff bytes - * into FIFO. Use the write queue for temp storage. - */ - if (!tty->stopped && info->xmit_cnt == 0 && info->xmit_fifo_room > 0) { - c = min(count, info->xmit_fifo_room); - b = buf; - - /* Push data into FIFO, 2 bytes at a time */ - sOutStrW(sGetTxRxDataIO(cp), (unsigned short *) b, c / 2); - - /* If there is a byte remaining, write it */ - if (c & 1) - sOutB(sGetTxRxDataIO(cp), b[c - 1]); - - retval += c; - buf += c; - count -= c; - - spin_lock_irqsave(&info->slock, flags); - info->xmit_fifo_room -= c; - spin_unlock_irqrestore(&info->slock, flags); - } - - /* If count is zero, we wrote it all and are done */ - if (!count) - goto end; - - /* Write remaining data into the port's xmit_buf */ - while (1) { - /* Hung up ? */ - if (!tty_port_active(&info->port)) - goto end; - c = min(count, XMIT_BUF_SIZE - info->xmit_cnt - 1); - c = min(c, XMIT_BUF_SIZE - info->xmit_head); - if (c <= 0) - break; - - b = buf; - memcpy(info->xmit_buf + info->xmit_head, b, c); - - spin_lock_irqsave(&info->slock, flags); - info->xmit_head = - (info->xmit_head + c) & (XMIT_BUF_SIZE - 1); - info->xmit_cnt += c; - spin_unlock_irqrestore(&info->slock, flags); - - buf += c; - count -= c; - retval += c; - } - - if ((retval > 0) && !tty->stopped) - set_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); - -end: - if (info->xmit_cnt < WAKEUP_CHARS) { - tty_wakeup(tty); -#ifdef ROCKETPORT_HAVE_POLL_WAIT - wake_up_interruptible(&tty->poll_wait); -#endif - } - mutex_unlock(&info->write_mtx); - return retval; -} - -/* - * Return the number of characters that can be sent. We estimate - * only using the in-memory transmit buffer only, and ignore the - * potential space in the transmit FIFO. - */ -static int rp_write_room(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - int ret; - - if (rocket_paranoia_check(info, "rp_write_room")) - return 0; - - ret = XMIT_BUF_SIZE - info->xmit_cnt - 1; - if (ret < 0) - ret = 0; -#ifdef ROCKET_DEBUG_WRITE - printk(KERN_INFO "rp_write_room returns %d...\n", ret); -#endif - return ret; -} - -/* - * Return the number of characters in the buffer. Again, this only - * counts those characters in the in-memory transmit buffer. - */ -static int rp_chars_in_buffer(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - - if (rocket_paranoia_check(info, "rp_chars_in_buffer")) - return 0; - -#ifdef ROCKET_DEBUG_WRITE - printk(KERN_INFO "rp_chars_in_buffer returns %d...\n", info->xmit_cnt); -#endif - return info->xmit_cnt; -} - -/* - * Flushes the TX fifo for a port, deletes data in the xmit_buf stored in the - * r_port struct for the port. Note that spinlock are used to protect info members, - * do not call this function if the spinlock is already held. - */ -static void rp_flush_buffer(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - unsigned long flags; - - if (rocket_paranoia_check(info, "rp_flush_buffer")) - return; - - spin_lock_irqsave(&info->slock, flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - spin_unlock_irqrestore(&info->slock, flags); - -#ifdef ROCKETPORT_HAVE_POLL_WAIT - wake_up_interruptible(&tty->poll_wait); -#endif - tty_wakeup(tty); - - cp = &info->channel; - sFlushTxFIFO(cp); -} - -#ifdef CONFIG_PCI - -static const struct pci_device_id rocket_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP4QUAD) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8OCTA) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_URP8OCTA) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8INTF) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_URP8INTF) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8J) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP4J) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8SNI) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP16SNI) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP16INTF) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_URP16INTF) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_CRP16INTF) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP32INTF) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_URP32INTF) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RPP4) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RPP8) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP2_232) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP2_422) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP6M) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP4M) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_UPCI_RM3_8PORT) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_UPCI_RM3_4PORT) }, - { } -}; -MODULE_DEVICE_TABLE(pci, rocket_pci_ids); - -/* Resets the speaker controller on RocketModem II and III devices */ -static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model) -{ - ByteIO_t addr; - - /* RocketModem II speaker control is at the 8th port location of offset 0x40 */ - if ((model == MODEL_RP4M) || (model == MODEL_RP6M)) { - addr = CtlP->AiopIO[0] + 0x4F; - sOutB(addr, 0); - } - - /* RocketModem III speaker control is at the 1st port location of offset 0x80 */ - if ((model == MODEL_UPCI_RM3_8PORT) - || (model == MODEL_UPCI_RM3_4PORT)) { - addr = CtlP->AiopIO[0] + 0x88; - sOutB(addr, 0); - } -} - -/*************************************************************************** -Function: sPCIInitController -Purpose: Initialization of controller global registers and controller - structure. -Call: sPCIInitController(CtlP,CtlNum,AiopIOList,AiopIOListSize, - IRQNum,Frequency,PeriodicOnly) - CONTROLLER_T *CtlP; Ptr to controller structure - int CtlNum; Controller number - ByteIO_t *AiopIOList; List of I/O addresses for each AIOP. - This list must be in the order the AIOPs will be found on the - controller. Once an AIOP in the list is not found, it is - assumed that there are no more AIOPs on the controller. - int AiopIOListSize; Number of addresses in AiopIOList - int IRQNum; Interrupt Request number. Can be any of the following: - 0: Disable global interrupts - 3: IRQ 3 - 4: IRQ 4 - 5: IRQ 5 - 9: IRQ 9 - 10: IRQ 10 - 11: IRQ 11 - 12: IRQ 12 - 15: IRQ 15 - Byte_t Frequency: A flag identifying the frequency - of the periodic interrupt, can be any one of the following: - FREQ_DIS - periodic interrupt disabled - FREQ_137HZ - 137 Hertz - FREQ_69HZ - 69 Hertz - FREQ_34HZ - 34 Hertz - FREQ_17HZ - 17 Hertz - FREQ_9HZ - 9 Hertz - FREQ_4HZ - 4 Hertz - If IRQNum is set to 0 the Frequency parameter is - overidden, it is forced to a value of FREQ_DIS. - int PeriodicOnly: 1 if all interrupts except the periodic - interrupt are to be blocked. - 0 is both the periodic interrupt and - other channel interrupts are allowed. - If IRQNum is set to 0 the PeriodicOnly parameter is - overidden, it is forced to a value of 0. -Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller - initialization failed. - -Comments: - If periodic interrupts are to be disabled but AIOP interrupts - are allowed, set Frequency to FREQ_DIS and PeriodicOnly to 0. - - If interrupts are to be completely disabled set IRQNum to 0. - - Setting Frequency to FREQ_DIS and PeriodicOnly to 1 is an - invalid combination. - - This function performs initialization of global interrupt modes, - but it does not actually enable global interrupts. To enable - and disable global interrupts use functions sEnGlobalInt() and - sDisGlobalInt(). Enabling of global interrupts is normally not - done until all other initializations are complete. - - Even if interrupts are globally enabled, they must also be - individually enabled for each channel that is to generate - interrupts. - -Warnings: No range checking on any of the parameters is done. - - No context switches are allowed while executing this function. - - After this function all AIOPs on the controller are disabled, - they can be enabled with sEnAiop(). -*/ -static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum, - ByteIO_t * AiopIOList, int AiopIOListSize, - WordIO_t ConfigIO, int IRQNum, Byte_t Frequency, - int PeriodicOnly, int altChanRingIndicator, - int UPCIRingInd) -{ - int i; - ByteIO_t io; - - CtlP->AltChanRingIndicator = altChanRingIndicator; - CtlP->UPCIRingInd = UPCIRingInd; - CtlP->CtlNum = CtlNum; - CtlP->CtlID = CTLID_0001; /* controller release 1 */ - CtlP->BusType = isPCI; /* controller release 1 */ - - if (ConfigIO) { - CtlP->isUPCI = 1; - CtlP->PCIIO = ConfigIO + _PCI_9030_INT_CTRL; - CtlP->PCIIO2 = ConfigIO + _PCI_9030_GPIO_CTRL; - CtlP->AiopIntrBits = upci_aiop_intr_bits; - } else { - CtlP->isUPCI = 0; - CtlP->PCIIO = - (WordIO_t) ((ByteIO_t) AiopIOList[0] + _PCI_INT_FUNC); - CtlP->AiopIntrBits = aiop_intr_bits; - } - - sPCIControllerEOI(CtlP); /* clear EOI if warm init */ - /* Init AIOPs */ - CtlP->NumAiop = 0; - for (i = 0; i < AiopIOListSize; i++) { - io = AiopIOList[i]; - CtlP->AiopIO[i] = (WordIO_t) io; - CtlP->AiopIntChanIO[i] = io + _INT_CHAN; - - CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ - if (CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ - break; /* done looking for AIOPs */ - - CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t) io); /* num channels in AIOP */ - sOutW((WordIO_t) io + _INDX_ADDR, _CLK_PRE); /* clock prescaler */ - sOutB(io + _INDX_DATA, sClockPrescale); - CtlP->NumAiop++; /* bump count of AIOPs */ - } - - if (CtlP->NumAiop == 0) - return (-1); - else - return (CtlP->NumAiop); -} - -/* - * Called when a PCI card is found. Retrieves and stores model information, - * init's aiopic and serial port hardware. - * Inputs: i is the board number (0-n) - */ -static __init int register_PCI(int i, struct pci_dev *dev) -{ - int num_aiops, aiop, max_num_aiops, chan; - unsigned int aiopio[MAX_AIOPS_PER_BOARD]; - CONTROLLER_t *ctlp; - - int fast_clock = 0; - int altChanRingIndicator = 0; - int ports_per_aiop = 8; - WordIO_t ConfigIO = 0; - ByteIO_t UPCIRingInd = 0; - - if (!dev || !pci_match_id(rocket_pci_ids, dev) || - pci_enable_device(dev) || i >= NUM_BOARDS) - return 0; - - rcktpt_io_addr[i] = pci_resource_start(dev, 0); - - rcktpt_type[i] = ROCKET_TYPE_NORMAL; - rocketModel[i].loadrm2 = 0; - rocketModel[i].startingPortNumber = nextLineNumber; - - /* Depending on the model, set up some config variables */ - switch (dev->device) { - case PCI_DEVICE_ID_RP4QUAD: - max_num_aiops = 1; - ports_per_aiop = 4; - rocketModel[i].model = MODEL_RP4QUAD; - strcpy(rocketModel[i].modelString, "RocketPort 4 port w/quad cable"); - rocketModel[i].numPorts = 4; - break; - case PCI_DEVICE_ID_RP8OCTA: - max_num_aiops = 1; - rocketModel[i].model = MODEL_RP8OCTA; - strcpy(rocketModel[i].modelString, "RocketPort 8 port w/octa cable"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_URP8OCTA: - max_num_aiops = 1; - rocketModel[i].model = MODEL_UPCI_RP8OCTA; - strcpy(rocketModel[i].modelString, "RocketPort UPCI 8 port w/octa cable"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_RP8INTF: - max_num_aiops = 1; - rocketModel[i].model = MODEL_RP8INTF; - strcpy(rocketModel[i].modelString, "RocketPort 8 port w/external I/F"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_URP8INTF: - max_num_aiops = 1; - rocketModel[i].model = MODEL_UPCI_RP8INTF; - strcpy(rocketModel[i].modelString, "RocketPort UPCI 8 port w/external I/F"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_RP8J: - max_num_aiops = 1; - rocketModel[i].model = MODEL_RP8J; - strcpy(rocketModel[i].modelString, "RocketPort 8 port w/RJ11 connectors"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_RP4J: - max_num_aiops = 1; - ports_per_aiop = 4; - rocketModel[i].model = MODEL_RP4J; - strcpy(rocketModel[i].modelString, "RocketPort 4 port w/RJ45 connectors"); - rocketModel[i].numPorts = 4; - break; - case PCI_DEVICE_ID_RP8SNI: - max_num_aiops = 1; - rocketModel[i].model = MODEL_RP8SNI; - strcpy(rocketModel[i].modelString, "RocketPort 8 port w/ custom DB78"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_RP16SNI: - max_num_aiops = 2; - rocketModel[i].model = MODEL_RP16SNI; - strcpy(rocketModel[i].modelString, "RocketPort 16 port w/ custom DB78"); - rocketModel[i].numPorts = 16; - break; - case PCI_DEVICE_ID_RP16INTF: - max_num_aiops = 2; - rocketModel[i].model = MODEL_RP16INTF; - strcpy(rocketModel[i].modelString, "RocketPort 16 port w/external I/F"); - rocketModel[i].numPorts = 16; - break; - case PCI_DEVICE_ID_URP16INTF: - max_num_aiops = 2; - rocketModel[i].model = MODEL_UPCI_RP16INTF; - strcpy(rocketModel[i].modelString, "RocketPort UPCI 16 port w/external I/F"); - rocketModel[i].numPorts = 16; - break; - case PCI_DEVICE_ID_CRP16INTF: - max_num_aiops = 2; - rocketModel[i].model = MODEL_CPCI_RP16INTF; - strcpy(rocketModel[i].modelString, "RocketPort Compact PCI 16 port w/external I/F"); - rocketModel[i].numPorts = 16; - break; - case PCI_DEVICE_ID_RP32INTF: - max_num_aiops = 4; - rocketModel[i].model = MODEL_RP32INTF; - strcpy(rocketModel[i].modelString, "RocketPort 32 port w/external I/F"); - rocketModel[i].numPorts = 32; - break; - case PCI_DEVICE_ID_URP32INTF: - max_num_aiops = 4; - rocketModel[i].model = MODEL_UPCI_RP32INTF; - strcpy(rocketModel[i].modelString, "RocketPort UPCI 32 port w/external I/F"); - rocketModel[i].numPorts = 32; - break; - case PCI_DEVICE_ID_RPP4: - max_num_aiops = 1; - ports_per_aiop = 4; - altChanRingIndicator++; - fast_clock++; - rocketModel[i].model = MODEL_RPP4; - strcpy(rocketModel[i].modelString, "RocketPort Plus 4 port"); - rocketModel[i].numPorts = 4; - break; - case PCI_DEVICE_ID_RPP8: - max_num_aiops = 2; - ports_per_aiop = 4; - altChanRingIndicator++; - fast_clock++; - rocketModel[i].model = MODEL_RPP8; - strcpy(rocketModel[i].modelString, "RocketPort Plus 8 port"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_RP2_232: - max_num_aiops = 1; - ports_per_aiop = 2; - altChanRingIndicator++; - fast_clock++; - rocketModel[i].model = MODEL_RP2_232; - strcpy(rocketModel[i].modelString, "RocketPort Plus 2 port RS232"); - rocketModel[i].numPorts = 2; - break; - case PCI_DEVICE_ID_RP2_422: - max_num_aiops = 1; - ports_per_aiop = 2; - altChanRingIndicator++; - fast_clock++; - rocketModel[i].model = MODEL_RP2_422; - strcpy(rocketModel[i].modelString, "RocketPort Plus 2 port RS422"); - rocketModel[i].numPorts = 2; - break; - case PCI_DEVICE_ID_RP6M: - - max_num_aiops = 1; - ports_per_aiop = 6; - - /* If revision is 1, the rocketmodem flash must be loaded. - * If it is 2 it is a "socketed" version. */ - if (dev->revision == 1) { - rcktpt_type[i] = ROCKET_TYPE_MODEMII; - rocketModel[i].loadrm2 = 1; - } else { - rcktpt_type[i] = ROCKET_TYPE_MODEM; - } - - rocketModel[i].model = MODEL_RP6M; - strcpy(rocketModel[i].modelString, "RocketModem 6 port"); - rocketModel[i].numPorts = 6; - break; - case PCI_DEVICE_ID_RP4M: - max_num_aiops = 1; - ports_per_aiop = 4; - if (dev->revision == 1) { - rcktpt_type[i] = ROCKET_TYPE_MODEMII; - rocketModel[i].loadrm2 = 1; - } else { - rcktpt_type[i] = ROCKET_TYPE_MODEM; - } - - rocketModel[i].model = MODEL_RP4M; - strcpy(rocketModel[i].modelString, "RocketModem 4 port"); - rocketModel[i].numPorts = 4; - break; - default: - max_num_aiops = 0; - break; - } - - /* - * Check for UPCI boards. - */ - - switch (dev->device) { - case PCI_DEVICE_ID_URP32INTF: - case PCI_DEVICE_ID_URP8INTF: - case PCI_DEVICE_ID_URP16INTF: - case PCI_DEVICE_ID_CRP16INTF: - case PCI_DEVICE_ID_URP8OCTA: - rcktpt_io_addr[i] = pci_resource_start(dev, 2); - ConfigIO = pci_resource_start(dev, 1); - if (dev->device == PCI_DEVICE_ID_URP8OCTA) { - UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND; - - /* - * Check for octa or quad cable. - */ - if (! - (sInW(ConfigIO + _PCI_9030_GPIO_CTRL) & - PCI_GPIO_CTRL_8PORT)) { - ports_per_aiop = 4; - rocketModel[i].numPorts = 4; - } - } - break; - case PCI_DEVICE_ID_UPCI_RM3_8PORT: - max_num_aiops = 1; - rocketModel[i].model = MODEL_UPCI_RM3_8PORT; - strcpy(rocketModel[i].modelString, "RocketModem III 8 port"); - rocketModel[i].numPorts = 8; - rcktpt_io_addr[i] = pci_resource_start(dev, 2); - UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND; - ConfigIO = pci_resource_start(dev, 1); - rcktpt_type[i] = ROCKET_TYPE_MODEMIII; - break; - case PCI_DEVICE_ID_UPCI_RM3_4PORT: - max_num_aiops = 1; - rocketModel[i].model = MODEL_UPCI_RM3_4PORT; - strcpy(rocketModel[i].modelString, "RocketModem III 4 port"); - rocketModel[i].numPorts = 4; - rcktpt_io_addr[i] = pci_resource_start(dev, 2); - UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND; - ConfigIO = pci_resource_start(dev, 1); - rcktpt_type[i] = ROCKET_TYPE_MODEMIII; - break; - default: - break; - } - - if (fast_clock) { - sClockPrescale = 0x12; /* mod 2 (divide by 3) */ - rp_baud_base[i] = 921600; - } else { - /* - * If support_low_speed is set, use the slow clock - * prescale, which supports 50 bps - */ - if (support_low_speed) { - /* mod 9 (divide by 10) prescale */ - sClockPrescale = 0x19; - rp_baud_base[i] = 230400; - } else { - /* mod 4 (divide by 5) prescale */ - sClockPrescale = 0x14; - rp_baud_base[i] = 460800; - } - } - - for (aiop = 0; aiop < max_num_aiops; aiop++) - aiopio[aiop] = rcktpt_io_addr[i] + (aiop * 0x40); - ctlp = sCtlNumToCtlPtr(i); - num_aiops = sPCIInitController(ctlp, i, aiopio, max_num_aiops, ConfigIO, 0, FREQ_DIS, 0, altChanRingIndicator, UPCIRingInd); - for (aiop = 0; aiop < max_num_aiops; aiop++) - ctlp->AiopNumChan[aiop] = ports_per_aiop; - - dev_info(&dev->dev, "comtrol PCI controller #%d found at " - "address %04lx, %d AIOP(s) (%s), creating ttyR%d - %ld\n", - i, rcktpt_io_addr[i], num_aiops, rocketModel[i].modelString, - rocketModel[i].startingPortNumber, - rocketModel[i].startingPortNumber + rocketModel[i].numPorts-1); - - if (num_aiops <= 0) { - rcktpt_io_addr[i] = 0; - return (0); - } - is_PCI[i] = 1; - - /* Reset the AIOPIC, init the serial ports */ - for (aiop = 0; aiop < num_aiops; aiop++) { - sResetAiopByNum(ctlp, aiop); - for (chan = 0; chan < ports_per_aiop; chan++) - init_r_port(i, aiop, chan, dev); - } - - /* Rocket modems must be reset */ - if ((rcktpt_type[i] == ROCKET_TYPE_MODEM) || - (rcktpt_type[i] == ROCKET_TYPE_MODEMII) || - (rcktpt_type[i] == ROCKET_TYPE_MODEMIII)) { - for (chan = 0; chan < ports_per_aiop; chan++) - sPCIModemReset(ctlp, chan, 1); - msleep(500); - for (chan = 0; chan < ports_per_aiop; chan++) - sPCIModemReset(ctlp, chan, 0); - msleep(500); - rmSpeakerReset(ctlp, rocketModel[i].model); - } - return (1); -} - -/* - * Probes for PCI cards, inits them if found - * Input: board_found = number of ISA boards already found, or the - * starting board number - * Returns: Number of PCI boards found - */ -static int __init init_PCI(int boards_found) -{ - struct pci_dev *dev = NULL; - int count = 0; - - /* Work through the PCI device list, pulling out ours */ - while ((dev = pci_get_device(PCI_VENDOR_ID_RP, PCI_ANY_ID, dev))) { - if (register_PCI(count + boards_found, dev)) - count++; - } - return (count); -} - -#endif /* CONFIG_PCI */ - -/* - * Probes for ISA cards - * Input: i = the board number to look for - * Returns: 1 if board found, 0 else - */ -static int __init init_ISA(int i) -{ - int num_aiops, num_chan = 0, total_num_chan = 0; - int aiop, chan; - unsigned int aiopio[MAX_AIOPS_PER_BOARD]; - CONTROLLER_t *ctlp; - char *type_string; - - /* If io_addr is zero, no board configured */ - if (rcktpt_io_addr[i] == 0) - return (0); - - /* Reserve the IO region */ - if (!request_region(rcktpt_io_addr[i], 64, "Comtrol RocketPort")) { - printk(KERN_ERR "Unable to reserve IO region for configured " - "ISA RocketPort at address 0x%lx, board not " - "installed...\n", rcktpt_io_addr[i]); - rcktpt_io_addr[i] = 0; - return (0); - } - - ctlp = sCtlNumToCtlPtr(i); - - ctlp->boardType = rcktpt_type[i]; - - switch (rcktpt_type[i]) { - case ROCKET_TYPE_PC104: - type_string = "(PC104)"; - break; - case ROCKET_TYPE_MODEM: - type_string = "(RocketModem)"; - break; - case ROCKET_TYPE_MODEMII: - type_string = "(RocketModem II)"; - break; - default: - type_string = ""; - break; - } - - /* - * If support_low_speed is set, use the slow clock prescale, - * which supports 50 bps - */ - if (support_low_speed) { - sClockPrescale = 0x19; /* mod 9 (divide by 10) prescale */ - rp_baud_base[i] = 230400; - } else { - sClockPrescale = 0x14; /* mod 4 (divide by 5) prescale */ - rp_baud_base[i] = 460800; - } - - for (aiop = 0; aiop < MAX_AIOPS_PER_BOARD; aiop++) - aiopio[aiop] = rcktpt_io_addr[i] + (aiop * 0x400); - - num_aiops = sInitController(ctlp, i, controller + (i * 0x400), aiopio, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); - - if (ctlp->boardType == ROCKET_TYPE_PC104) { - sEnAiop(ctlp, 2); /* only one AIOPIC, but these */ - sEnAiop(ctlp, 3); /* CSels used for other stuff */ - } - - /* If something went wrong initing the AIOP's release the ISA IO memory */ - if (num_aiops <= 0) { - release_region(rcktpt_io_addr[i], 64); - rcktpt_io_addr[i] = 0; - return (0); - } - - rocketModel[i].startingPortNumber = nextLineNumber; - - for (aiop = 0; aiop < num_aiops; aiop++) { - sResetAiopByNum(ctlp, aiop); - sEnAiop(ctlp, aiop); - num_chan = sGetAiopNumChan(ctlp, aiop); - total_num_chan += num_chan; - for (chan = 0; chan < num_chan; chan++) - init_r_port(i, aiop, chan, NULL); - } - is_PCI[i] = 0; - if ((rcktpt_type[i] == ROCKET_TYPE_MODEM) || (rcktpt_type[i] == ROCKET_TYPE_MODEMII)) { - num_chan = sGetAiopNumChan(ctlp, 0); - total_num_chan = num_chan; - for (chan = 0; chan < num_chan; chan++) - sModemReset(ctlp, chan, 1); - msleep(500); - for (chan = 0; chan < num_chan; chan++) - sModemReset(ctlp, chan, 0); - msleep(500); - strcpy(rocketModel[i].modelString, "RocketModem ISA"); - } else { - strcpy(rocketModel[i].modelString, "RocketPort ISA"); - } - rocketModel[i].numPorts = total_num_chan; - rocketModel[i].model = MODEL_ISA; - - printk(KERN_INFO "RocketPort ISA card #%d found at 0x%lx - %d AIOPs %s\n", - i, rcktpt_io_addr[i], num_aiops, type_string); - - printk(KERN_INFO "Installing %s, creating /dev/ttyR%d - %ld\n", - rocketModel[i].modelString, - rocketModel[i].startingPortNumber, - rocketModel[i].startingPortNumber + - rocketModel[i].numPorts - 1); - - return (1); -} - -static const struct tty_operations rocket_ops = { - .open = rp_open, - .close = rp_close, - .write = rp_write, - .put_char = rp_put_char, - .write_room = rp_write_room, - .chars_in_buffer = rp_chars_in_buffer, - .flush_buffer = rp_flush_buffer, - .ioctl = rp_ioctl, - .throttle = rp_throttle, - .unthrottle = rp_unthrottle, - .set_termios = rp_set_termios, - .stop = rp_stop, - .start = rp_start, - .hangup = rp_hangup, - .break_ctl = rp_break, - .send_xchar = rp_send_xchar, - .wait_until_sent = rp_wait_until_sent, - .tiocmget = rp_tiocmget, - .tiocmset = rp_tiocmset, -}; - -static const struct tty_port_operations rocket_port_ops = { - .carrier_raised = carrier_raised, - .dtr_rts = dtr_rts, -}; - -/* - * The module "startup" routine; it's run when the module is loaded. - */ -static int __init rp_init(void) -{ - int ret = -ENOMEM, pci_boards_found, isa_boards_found, i; - - printk(KERN_INFO "RocketPort device driver module, version %s, %s\n", - ROCKET_VERSION, ROCKET_DATE); - - rocket_driver = alloc_tty_driver(MAX_RP_PORTS); - if (!rocket_driver) - goto err; - - /* - * If board 1 is non-zero, there is at least one ISA configured. If controller is - * zero, use the default controller IO address of board1 + 0x40. - */ - if (board1) { - if (controller == 0) - controller = board1 + 0x40; - } else { - controller = 0; /* Used as a flag, meaning no ISA boards */ - } - - /* If an ISA card is configured, reserve the 4 byte IO space for the Mudbac controller */ - if (controller && (!request_region(controller, 4, "Comtrol RocketPort"))) { - printk(KERN_ERR "Unable to reserve IO region for first " - "configured ISA RocketPort controller 0x%lx. " - "Driver exiting\n", controller); - ret = -EBUSY; - goto err_tty; - } - - /* Store ISA variable retrieved from command line or .conf file. */ - rcktpt_io_addr[0] = board1; - rcktpt_io_addr[1] = board2; - rcktpt_io_addr[2] = board3; - rcktpt_io_addr[3] = board4; - - rcktpt_type[0] = modem1 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; - rcktpt_type[0] = pc104_1[0] ? ROCKET_TYPE_PC104 : rcktpt_type[0]; - rcktpt_type[1] = modem2 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; - rcktpt_type[1] = pc104_2[0] ? ROCKET_TYPE_PC104 : rcktpt_type[1]; - rcktpt_type[2] = modem3 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; - rcktpt_type[2] = pc104_3[0] ? ROCKET_TYPE_PC104 : rcktpt_type[2]; - rcktpt_type[3] = modem4 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; - rcktpt_type[3] = pc104_4[0] ? ROCKET_TYPE_PC104 : rcktpt_type[3]; - - /* - * Set up the tty driver structure and then register this - * driver with the tty layer. - */ - - rocket_driver->flags = TTY_DRIVER_DYNAMIC_DEV; - rocket_driver->name = "ttyR"; - rocket_driver->driver_name = "Comtrol RocketPort"; - rocket_driver->major = TTY_ROCKET_MAJOR; - rocket_driver->minor_start = 0; - rocket_driver->type = TTY_DRIVER_TYPE_SERIAL; - rocket_driver->subtype = SERIAL_TYPE_NORMAL; - rocket_driver->init_termios = tty_std_termios; - rocket_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - rocket_driver->init_termios.c_ispeed = 9600; - rocket_driver->init_termios.c_ospeed = 9600; -#ifdef ROCKET_SOFT_FLOW - rocket_driver->flags |= TTY_DRIVER_REAL_RAW; -#endif - tty_set_operations(rocket_driver, &rocket_ops); - - ret = tty_register_driver(rocket_driver); - if (ret < 0) { - printk(KERN_ERR "Couldn't install tty RocketPort driver\n"); - goto err_controller; - } - -#ifdef ROCKET_DEBUG_OPEN - printk(KERN_INFO "RocketPort driver is major %d\n", rocket_driver.major); -#endif - - /* - * OK, let's probe each of the controllers looking for boards. Any boards found - * will be initialized here. - */ - isa_boards_found = 0; - pci_boards_found = 0; - - for (i = 0; i < NUM_BOARDS; i++) { - if (init_ISA(i)) - isa_boards_found++; - } - -#ifdef CONFIG_PCI - if (isa_boards_found < NUM_BOARDS) - pci_boards_found = init_PCI(isa_boards_found); -#endif - - max_board = pci_boards_found + isa_boards_found; - - if (max_board == 0) { - printk(KERN_ERR "No rocketport ports found; unloading driver\n"); - ret = -ENXIO; - goto err_ttyu; - } - - return 0; -err_ttyu: - tty_unregister_driver(rocket_driver); -err_controller: - if (controller) - release_region(controller, 4); -err_tty: - put_tty_driver(rocket_driver); -err: - return ret; -} - - -static void rp_cleanup_module(void) -{ - int retval; - int i; - - del_timer_sync(&rocket_timer); - - retval = tty_unregister_driver(rocket_driver); - if (retval) - printk(KERN_ERR "Error %d while trying to unregister " - "rocketport driver\n", -retval); - - for (i = 0; i < MAX_RP_PORTS; i++) - if (rp_table[i]) { - tty_unregister_device(rocket_driver, i); - tty_port_destroy(&rp_table[i]->port); - kfree(rp_table[i]); - } - - put_tty_driver(rocket_driver); - - for (i = 0; i < NUM_BOARDS; i++) { - if (rcktpt_io_addr[i] <= 0 || is_PCI[i]) - continue; - release_region(rcktpt_io_addr[i], 64); - } - if (controller) - release_region(controller, 4); -} - -/*************************************************************************** -Function: sInitController -Purpose: Initialization of controller global registers and controller - structure. -Call: sInitController(CtlP,CtlNum,MudbacIO,AiopIOList,AiopIOListSize, - IRQNum,Frequency,PeriodicOnly) - CONTROLLER_T *CtlP; Ptr to controller structure - int CtlNum; Controller number - ByteIO_t MudbacIO; Mudbac base I/O address. - ByteIO_t *AiopIOList; List of I/O addresses for each AIOP. - This list must be in the order the AIOPs will be found on the - controller. Once an AIOP in the list is not found, it is - assumed that there are no more AIOPs on the controller. - int AiopIOListSize; Number of addresses in AiopIOList - int IRQNum; Interrupt Request number. Can be any of the following: - 0: Disable global interrupts - 3: IRQ 3 - 4: IRQ 4 - 5: IRQ 5 - 9: IRQ 9 - 10: IRQ 10 - 11: IRQ 11 - 12: IRQ 12 - 15: IRQ 15 - Byte_t Frequency: A flag identifying the frequency - of the periodic interrupt, can be any one of the following: - FREQ_DIS - periodic interrupt disabled - FREQ_137HZ - 137 Hertz - FREQ_69HZ - 69 Hertz - FREQ_34HZ - 34 Hertz - FREQ_17HZ - 17 Hertz - FREQ_9HZ - 9 Hertz - FREQ_4HZ - 4 Hertz - If IRQNum is set to 0 the Frequency parameter is - overidden, it is forced to a value of FREQ_DIS. - int PeriodicOnly: 1 if all interrupts except the periodic - interrupt are to be blocked. - 0 is both the periodic interrupt and - other channel interrupts are allowed. - If IRQNum is set to 0 the PeriodicOnly parameter is - overidden, it is forced to a value of 0. -Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller - initialization failed. - -Comments: - If periodic interrupts are to be disabled but AIOP interrupts - are allowed, set Frequency to FREQ_DIS and PeriodicOnly to 0. - - If interrupts are to be completely disabled set IRQNum to 0. - - Setting Frequency to FREQ_DIS and PeriodicOnly to 1 is an - invalid combination. - - This function performs initialization of global interrupt modes, - but it does not actually enable global interrupts. To enable - and disable global interrupts use functions sEnGlobalInt() and - sDisGlobalInt(). Enabling of global interrupts is normally not - done until all other initializations are complete. - - Even if interrupts are globally enabled, they must also be - individually enabled for each channel that is to generate - interrupts. - -Warnings: No range checking on any of the parameters is done. - - No context switches are allowed while executing this function. - - After this function all AIOPs on the controller are disabled, - they can be enabled with sEnAiop(). -*/ -static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO, - ByteIO_t * AiopIOList, int AiopIOListSize, - int IRQNum, Byte_t Frequency, int PeriodicOnly) -{ - int i; - ByteIO_t io; - int done; - - CtlP->AiopIntrBits = aiop_intr_bits; - CtlP->AltChanRingIndicator = 0; - CtlP->CtlNum = CtlNum; - CtlP->CtlID = CTLID_0001; /* controller release 1 */ - CtlP->BusType = isISA; - CtlP->MBaseIO = MudbacIO; - CtlP->MReg1IO = MudbacIO + 1; - CtlP->MReg2IO = MudbacIO + 2; - CtlP->MReg3IO = MudbacIO + 3; -#if 1 - CtlP->MReg2 = 0; /* interrupt disable */ - CtlP->MReg3 = 0; /* no periodic interrupts */ -#else - if (sIRQMap[IRQNum] == 0) { /* interrupts globally disabled */ - CtlP->MReg2 = 0; /* interrupt disable */ - CtlP->MReg3 = 0; /* no periodic interrupts */ - } else { - CtlP->MReg2 = sIRQMap[IRQNum]; /* set IRQ number */ - CtlP->MReg3 = Frequency; /* set frequency */ - if (PeriodicOnly) { /* periodic interrupt only */ - CtlP->MReg3 |= PERIODIC_ONLY; - } - } -#endif - sOutB(CtlP->MReg2IO, CtlP->MReg2); - sOutB(CtlP->MReg3IO, CtlP->MReg3); - sControllerEOI(CtlP); /* clear EOI if warm init */ - /* Init AIOPs */ - CtlP->NumAiop = 0; - for (i = done = 0; i < AiopIOListSize; i++) { - io = AiopIOList[i]; - CtlP->AiopIO[i] = (WordIO_t) io; - CtlP->AiopIntChanIO[i] = io + _INT_CHAN; - sOutB(CtlP->MReg2IO, CtlP->MReg2 | (i & 0x03)); /* AIOP index */ - sOutB(MudbacIO, (Byte_t) (io >> 6)); /* set up AIOP I/O in MUDBAC */ - if (done) - continue; - sEnAiop(CtlP, i); /* enable the AIOP */ - CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ - if (CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ - done = 1; /* done looking for AIOPs */ - else { - CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t) io); /* num channels in AIOP */ - sOutW((WordIO_t) io + _INDX_ADDR, _CLK_PRE); /* clock prescaler */ - sOutB(io + _INDX_DATA, sClockPrescale); - CtlP->NumAiop++; /* bump count of AIOPs */ - } - sDisAiop(CtlP, i); /* disable AIOP */ - } - - if (CtlP->NumAiop == 0) - return (-1); - else - return (CtlP->NumAiop); -} - -/*************************************************************************** -Function: sReadAiopID -Purpose: Read the AIOP idenfication number directly from an AIOP. -Call: sReadAiopID(io) - ByteIO_t io: AIOP base I/O address -Return: int: Flag AIOPID_XXXX if a valid AIOP is found, where X - is replace by an identifying number. - Flag AIOPID_NULL if no valid AIOP is found -Warnings: No context switches are allowed while executing this function. - -*/ -static int sReadAiopID(ByteIO_t io) -{ - Byte_t AiopID; /* ID byte from AIOP */ - - sOutB(io + _CMD_REG, RESET_ALL); /* reset AIOP */ - sOutB(io + _CMD_REG, 0x0); - AiopID = sInW(io + _CHN_STAT0) & 0x07; - if (AiopID == 0x06) - return (1); - else /* AIOP does not exist */ - return (-1); -} - -/*************************************************************************** -Function: sReadAiopNumChan -Purpose: Read the number of channels available in an AIOP directly from - an AIOP. -Call: sReadAiopNumChan(io) - WordIO_t io: AIOP base I/O address -Return: int: The number of channels available -Comments: The number of channels is determined by write/reads from identical - offsets within the SRAM address spaces for channels 0 and 4. - If the channel 4 space is mirrored to channel 0 it is a 4 channel - AIOP, otherwise it is an 8 channel. -Warnings: No context switches are allowed while executing this function. -*/ -static int sReadAiopNumChan(WordIO_t io) -{ - Word_t x; - static Byte_t R[4] = { 0x00, 0x00, 0x34, 0x12 }; - - /* write to chan 0 SRAM */ - out32((DWordIO_t) io + _INDX_ADDR, R); - sOutW(io + _INDX_ADDR, 0); /* read from SRAM, chan 0 */ - x = sInW(io + _INDX_DATA); - sOutW(io + _INDX_ADDR, 0x4000); /* read from SRAM, chan 4 */ - if (x != sInW(io + _INDX_DATA)) /* if different must be 8 chan */ - return (8); - else - return (4); -} - -/*************************************************************************** -Function: sInitChan -Purpose: Initialization of a channel and channel structure -Call: sInitChan(CtlP,ChP,AiopNum,ChanNum) - CONTROLLER_T *CtlP; Ptr to controller structure - CHANNEL_T *ChP; Ptr to channel structure - int AiopNum; AIOP number within controller - int ChanNum; Channel number within AIOP -Return: int: 1 if initialization succeeded, 0 if it fails because channel - number exceeds number of channels available in AIOP. -Comments: This function must be called before a channel can be used. -Warnings: No range checking on any of the parameters is done. - - No context switches are allowed while executing this function. -*/ -static int sInitChan(CONTROLLER_T * CtlP, CHANNEL_T * ChP, int AiopNum, - int ChanNum) -{ - int i; - WordIO_t AiopIO; - WordIO_t ChIOOff; - Byte_t *ChR; - Word_t ChOff; - static Byte_t R[4]; - int brd9600; - - if (ChanNum >= CtlP->AiopNumChan[AiopNum]) - return 0; /* exceeds num chans in AIOP */ - - /* Channel, AIOP, and controller identifiers */ - ChP->CtlP = CtlP; - ChP->ChanID = CtlP->AiopID[AiopNum]; - ChP->AiopNum = AiopNum; - ChP->ChanNum = ChanNum; - - /* Global direct addresses */ - AiopIO = CtlP->AiopIO[AiopNum]; - ChP->Cmd = (ByteIO_t) AiopIO + _CMD_REG; - ChP->IntChan = (ByteIO_t) AiopIO + _INT_CHAN; - ChP->IntMask = (ByteIO_t) AiopIO + _INT_MASK; - ChP->IndexAddr = (DWordIO_t) AiopIO + _INDX_ADDR; - ChP->IndexData = AiopIO + _INDX_DATA; - - /* Channel direct addresses */ - ChIOOff = AiopIO + ChP->ChanNum * 2; - ChP->TxRxData = ChIOOff + _TD0; - ChP->ChanStat = ChIOOff + _CHN_STAT0; - ChP->TxRxCount = ChIOOff + _FIFO_CNT0; - ChP->IntID = (ByteIO_t) AiopIO + ChP->ChanNum + _INT_ID0; - - /* Initialize the channel from the RData array */ - for (i = 0; i < RDATASIZE; i += 4) { - R[0] = RData[i]; - R[1] = RData[i + 1] + 0x10 * ChanNum; - R[2] = RData[i + 2]; - R[3] = RData[i + 3]; - out32(ChP->IndexAddr, R); - } - - ChR = ChP->R; - for (i = 0; i < RREGDATASIZE; i += 4) { - ChR[i] = RRegData[i]; - ChR[i + 1] = RRegData[i + 1] + 0x10 * ChanNum; - ChR[i + 2] = RRegData[i + 2]; - ChR[i + 3] = RRegData[i + 3]; - } - - /* Indexed registers */ - ChOff = (Word_t) ChanNum *0x1000; - - if (sClockPrescale == 0x14) - brd9600 = 47; - else - brd9600 = 23; - - ChP->BaudDiv[0] = (Byte_t) (ChOff + _BAUD); - ChP->BaudDiv[1] = (Byte_t) ((ChOff + _BAUD) >> 8); - ChP->BaudDiv[2] = (Byte_t) brd9600; - ChP->BaudDiv[3] = (Byte_t) (brd9600 >> 8); - out32(ChP->IndexAddr, ChP->BaudDiv); - - ChP->TxControl[0] = (Byte_t) (ChOff + _TX_CTRL); - ChP->TxControl[1] = (Byte_t) ((ChOff + _TX_CTRL) >> 8); - ChP->TxControl[2] = 0; - ChP->TxControl[3] = 0; - out32(ChP->IndexAddr, ChP->TxControl); - - ChP->RxControl[0] = (Byte_t) (ChOff + _RX_CTRL); - ChP->RxControl[1] = (Byte_t) ((ChOff + _RX_CTRL) >> 8); - ChP->RxControl[2] = 0; - ChP->RxControl[3] = 0; - out32(ChP->IndexAddr, ChP->RxControl); - - ChP->TxEnables[0] = (Byte_t) (ChOff + _TX_ENBLS); - ChP->TxEnables[1] = (Byte_t) ((ChOff + _TX_ENBLS) >> 8); - ChP->TxEnables[2] = 0; - ChP->TxEnables[3] = 0; - out32(ChP->IndexAddr, ChP->TxEnables); - - ChP->TxCompare[0] = (Byte_t) (ChOff + _TXCMP1); - ChP->TxCompare[1] = (Byte_t) ((ChOff + _TXCMP1) >> 8); - ChP->TxCompare[2] = 0; - ChP->TxCompare[3] = 0; - out32(ChP->IndexAddr, ChP->TxCompare); - - ChP->TxReplace1[0] = (Byte_t) (ChOff + _TXREP1B1); - ChP->TxReplace1[1] = (Byte_t) ((ChOff + _TXREP1B1) >> 8); - ChP->TxReplace1[2] = 0; - ChP->TxReplace1[3] = 0; - out32(ChP->IndexAddr, ChP->TxReplace1); - - ChP->TxReplace2[0] = (Byte_t) (ChOff + _TXREP2); - ChP->TxReplace2[1] = (Byte_t) ((ChOff + _TXREP2) >> 8); - ChP->TxReplace2[2] = 0; - ChP->TxReplace2[3] = 0; - out32(ChP->IndexAddr, ChP->TxReplace2); - - ChP->TxFIFOPtrs = ChOff + _TXF_OUTP; - ChP->TxFIFO = ChOff + _TX_FIFO; - - sOutB(ChP->Cmd, (Byte_t) ChanNum | RESTXFCNT); /* apply reset Tx FIFO count */ - sOutB(ChP->Cmd, (Byte_t) ChanNum); /* remove reset Tx FIFO count */ - sOutW((WordIO_t) ChP->IndexAddr, ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ - sOutW(ChP->IndexData, 0); - ChP->RxFIFOPtrs = ChOff + _RXF_OUTP; - ChP->RxFIFO = ChOff + _RX_FIFO; - - sOutB(ChP->Cmd, (Byte_t) ChanNum | RESRXFCNT); /* apply reset Rx FIFO count */ - sOutB(ChP->Cmd, (Byte_t) ChanNum); /* remove reset Rx FIFO count */ - sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs); /* clear Rx out ptr */ - sOutW(ChP->IndexData, 0); - sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ - sOutW(ChP->IndexData, 0); - ChP->TxPrioCnt = ChOff + _TXP_CNT; - sOutW((WordIO_t) ChP->IndexAddr, ChP->TxPrioCnt); - sOutB(ChP->IndexData, 0); - ChP->TxPrioPtr = ChOff + _TXP_PNTR; - sOutW((WordIO_t) ChP->IndexAddr, ChP->TxPrioPtr); - sOutB(ChP->IndexData, 0); - ChP->TxPrioBuf = ChOff + _TXP_BUF; - sEnRxProcessor(ChP); /* start the Rx processor */ - - return 1; -} - -/*************************************************************************** -Function: sStopRxProcessor -Purpose: Stop the receive processor from processing a channel. -Call: sStopRxProcessor(ChP) - CHANNEL_T *ChP; Ptr to channel structure - -Comments: The receive processor can be started again with sStartRxProcessor(). - This function causes the receive processor to skip over the - stopped channel. It does not stop it from processing other channels. - -Warnings: No context switches are allowed while executing this function. - - Do not leave the receive processor stopped for more than one - character time. - - After calling this function a delay of 4 uS is required to ensure - that the receive processor is no longer processing this channel. -*/ -static void sStopRxProcessor(CHANNEL_T * ChP) -{ - Byte_t R[4]; - - R[0] = ChP->R[0]; - R[1] = ChP->R[1]; - R[2] = 0x0a; - R[3] = ChP->R[3]; - out32(ChP->IndexAddr, R); -} - -/*************************************************************************** -Function: sFlushRxFIFO -Purpose: Flush the Rx FIFO -Call: sFlushRxFIFO(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: void -Comments: To prevent data from being enqueued or dequeued in the Tx FIFO - while it is being flushed the receive processor is stopped - and the transmitter is disabled. After these operations a - 4 uS delay is done before clearing the pointers to allow - the receive processor to stop. These items are handled inside - this function. -Warnings: No context switches are allowed while executing this function. -*/ -static void sFlushRxFIFO(CHANNEL_T * ChP) -{ - int i; - Byte_t Ch; /* channel number within AIOP */ - int RxFIFOEnabled; /* 1 if Rx FIFO enabled */ - - if (sGetRxCnt(ChP) == 0) /* Rx FIFO empty */ - return; /* don't need to flush */ - - RxFIFOEnabled = 0; - if (ChP->R[0x32] == 0x08) { /* Rx FIFO is enabled */ - RxFIFOEnabled = 1; - sDisRxFIFO(ChP); /* disable it */ - for (i = 0; i < 2000 / 200; i++) /* delay 2 uS to allow proc to disable FIFO */ - sInB(ChP->IntChan); /* depends on bus i/o timing */ - } - sGetChanStatus(ChP); /* clear any pending Rx errors in chan stat */ - Ch = (Byte_t) sGetChanNum(ChP); - sOutB(ChP->Cmd, Ch | RESRXFCNT); /* apply reset Rx FIFO count */ - sOutB(ChP->Cmd, Ch); /* remove reset Rx FIFO count */ - sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs); /* clear Rx out ptr */ - sOutW(ChP->IndexData, 0); - sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ - sOutW(ChP->IndexData, 0); - if (RxFIFOEnabled) - sEnRxFIFO(ChP); /* enable Rx FIFO */ -} - -/*************************************************************************** -Function: sFlushTxFIFO -Purpose: Flush the Tx FIFO -Call: sFlushTxFIFO(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: void -Comments: To prevent data from being enqueued or dequeued in the Tx FIFO - while it is being flushed the receive processor is stopped - and the transmitter is disabled. After these operations a - 4 uS delay is done before clearing the pointers to allow - the receive processor to stop. These items are handled inside - this function. -Warnings: No context switches are allowed while executing this function. -*/ -static void sFlushTxFIFO(CHANNEL_T * ChP) -{ - int i; - Byte_t Ch; /* channel number within AIOP */ - int TxEnabled; /* 1 if transmitter enabled */ - - if (sGetTxCnt(ChP) == 0) /* Tx FIFO empty */ - return; /* don't need to flush */ - - TxEnabled = 0; - if (ChP->TxControl[3] & TX_ENABLE) { - TxEnabled = 1; - sDisTransmit(ChP); /* disable transmitter */ - } - sStopRxProcessor(ChP); /* stop Rx processor */ - for (i = 0; i < 4000 / 200; i++) /* delay 4 uS to allow proc to stop */ - sInB(ChP->IntChan); /* depends on bus i/o timing */ - Ch = (Byte_t) sGetChanNum(ChP); - sOutB(ChP->Cmd, Ch | RESTXFCNT); /* apply reset Tx FIFO count */ - sOutB(ChP->Cmd, Ch); /* remove reset Tx FIFO count */ - sOutW((WordIO_t) ChP->IndexAddr, ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ - sOutW(ChP->IndexData, 0); - if (TxEnabled) - sEnTransmit(ChP); /* enable transmitter */ - sStartRxProcessor(ChP); /* restart Rx processor */ -} - -/*************************************************************************** -Function: sWriteTxPrioByte -Purpose: Write a byte of priority transmit data to a channel -Call: sWriteTxPrioByte(ChP,Data) - CHANNEL_T *ChP; Ptr to channel structure - Byte_t Data; The transmit data byte - -Return: int: 1 if the bytes is successfully written, otherwise 0. - -Comments: The priority byte is transmitted before any data in the Tx FIFO. - -Warnings: No context switches are allowed while executing this function. -*/ -static int sWriteTxPrioByte(CHANNEL_T * ChP, Byte_t Data) -{ - Byte_t DWBuf[4]; /* buffer for double word writes */ - Word_t *WordPtr; /* must be far because Win SS != DS */ - register DWordIO_t IndexAddr; - - if (sGetTxCnt(ChP) > 1) { /* write it to Tx priority buffer */ - IndexAddr = ChP->IndexAddr; - sOutW((WordIO_t) IndexAddr, ChP->TxPrioCnt); /* get priority buffer status */ - if (sInB((ByteIO_t) ChP->IndexData) & PRI_PEND) /* priority buffer busy */ - return (0); /* nothing sent */ - - WordPtr = (Word_t *) (&DWBuf[0]); - *WordPtr = ChP->TxPrioBuf; /* data byte address */ - - DWBuf[2] = Data; /* data byte value */ - out32(IndexAddr, DWBuf); /* write it out */ - - *WordPtr = ChP->TxPrioCnt; /* Tx priority count address */ - - DWBuf[2] = PRI_PEND + 1; /* indicate 1 byte pending */ - DWBuf[3] = 0; /* priority buffer pointer */ - out32(IndexAddr, DWBuf); /* write it out */ - } else { /* write it to Tx FIFO */ - - sWriteTxByte(sGetTxRxDataIO(ChP), Data); - } - return (1); /* 1 byte sent */ -} - -/*************************************************************************** -Function: sEnInterrupts -Purpose: Enable one or more interrupts for a channel -Call: sEnInterrupts(ChP,Flags) - CHANNEL_T *ChP; Ptr to channel structure - Word_t Flags: Interrupt enable flags, can be any combination - of the following flags: - TXINT_EN: Interrupt on Tx FIFO empty - RXINT_EN: Interrupt on Rx FIFO at trigger level (see - sSetRxTrigger()) - SRCINT_EN: Interrupt on SRC (Special Rx Condition) - MCINT_EN: Interrupt on modem input change - CHANINT_EN: Allow channel interrupt signal to the AIOP's - Interrupt Channel Register. -Return: void -Comments: If an interrupt enable flag is set in Flags, that interrupt will be - enabled. If an interrupt enable flag is not set in Flags, that - interrupt will not be changed. Interrupts can be disabled with - function sDisInterrupts(). - - This function sets the appropriate bit for the channel in the AIOP's - Interrupt Mask Register if the CHANINT_EN flag is set. This allows - this channel's bit to be set in the AIOP's Interrupt Channel Register. - - Interrupts must also be globally enabled before channel interrupts - will be passed on to the host. This is done with function - sEnGlobalInt(). - - In some cases it may be desirable to disable interrupts globally but - enable channel interrupts. This would allow the global interrupt - status register to be used to determine which AIOPs need service. -*/ -static void sEnInterrupts(CHANNEL_T * ChP, Word_t Flags) -{ - Byte_t Mask; /* Interrupt Mask Register */ - - ChP->RxControl[2] |= - ((Byte_t) Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); - - out32(ChP->IndexAddr, ChP->RxControl); - - ChP->TxControl[2] |= ((Byte_t) Flags & TXINT_EN); - - out32(ChP->IndexAddr, ChP->TxControl); - - if (Flags & CHANINT_EN) { - Mask = sInB(ChP->IntMask) | sBitMapSetTbl[ChP->ChanNum]; - sOutB(ChP->IntMask, Mask); - } -} - -/*************************************************************************** -Function: sDisInterrupts -Purpose: Disable one or more interrupts for a channel -Call: sDisInterrupts(ChP,Flags) - CHANNEL_T *ChP; Ptr to channel structure - Word_t Flags: Interrupt flags, can be any combination - of the following flags: - TXINT_EN: Interrupt on Tx FIFO empty - RXINT_EN: Interrupt on Rx FIFO at trigger level (see - sSetRxTrigger()) - SRCINT_EN: Interrupt on SRC (Special Rx Condition) - MCINT_EN: Interrupt on modem input change - CHANINT_EN: Disable channel interrupt signal to the - AIOP's Interrupt Channel Register. -Return: void -Comments: If an interrupt flag is set in Flags, that interrupt will be - disabled. If an interrupt flag is not set in Flags, that - interrupt will not be changed. Interrupts can be enabled with - function sEnInterrupts(). - - This function clears the appropriate bit for the channel in the AIOP's - Interrupt Mask Register if the CHANINT_EN flag is set. This blocks - this channel's bit from being set in the AIOP's Interrupt Channel - Register. -*/ -static void sDisInterrupts(CHANNEL_T * ChP, Word_t Flags) -{ - Byte_t Mask; /* Interrupt Mask Register */ - - ChP->RxControl[2] &= - ~((Byte_t) Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); - out32(ChP->IndexAddr, ChP->RxControl); - ChP->TxControl[2] &= ~((Byte_t) Flags & TXINT_EN); - out32(ChP->IndexAddr, ChP->TxControl); - - if (Flags & CHANINT_EN) { - Mask = sInB(ChP->IntMask) & sBitMapClrTbl[ChP->ChanNum]; - sOutB(ChP->IntMask, Mask); - } -} - -static void sSetInterfaceMode(CHANNEL_T * ChP, Byte_t mode) -{ - sOutB(ChP->CtlP->AiopIO[2], (mode & 0x18) | ChP->ChanNum); -} - -/* - * Not an official SSCI function, but how to reset RocketModems. - * ISA bus version - */ -static void sModemReset(CONTROLLER_T * CtlP, int chan, int on) -{ - ByteIO_t addr; - Byte_t val; - - addr = CtlP->AiopIO[0] + 0x400; - val = sInB(CtlP->MReg3IO); - /* if AIOP[1] is not enabled, enable it */ - if ((val & 2) == 0) { - val = sInB(CtlP->MReg2IO); - sOutB(CtlP->MReg2IO, (val & 0xfc) | (1 & 0x03)); - sOutB(CtlP->MBaseIO, (unsigned char) (addr >> 6)); - } - - sEnAiop(CtlP, 1); - if (!on) - addr += 8; - sOutB(addr + chan, 0); /* apply or remove reset */ - sDisAiop(CtlP, 1); -} - -/* - * Not an official SSCI function, but how to reset RocketModems. - * PCI bus version - */ -static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on) -{ - ByteIO_t addr; - - addr = CtlP->AiopIO[0] + 0x40; /* 2nd AIOP */ - if (!on) - addr += 8; - sOutB(addr + chan, 0); /* apply or remove reset */ -} - -/* Returns the line number given the controller (board), aiop and channel number */ -static unsigned char GetLineNumber(int ctrl, int aiop, int ch) -{ - return lineNumbers[(ctrl << 5) | (aiop << 3) | ch]; -} - -/* - * Stores the line number associated with a given controller (board), aiop - * and channel number. - * Returns: The line number assigned - */ -static unsigned char SetLineNumber(int ctrl, int aiop, int ch) -{ - lineNumbers[(ctrl << 5) | (aiop << 3) | ch] = nextLineNumber++; - return (nextLineNumber - 1); -} diff --git a/drivers/tty/rocket.h b/drivers/tty/rocket.h deleted file mode 100644 index d62ed6587f32..000000000000 --- a/drivers/tty/rocket.h +++ /dev/null @@ -1,111 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * rocket.h --- the exported interface of the rocket driver to its configuration program. - * - * Written by Theodore Ts'o, Copyright 1997. - * Copyright 1997 Comtrol Corporation. - * - */ - -/* Model Information Struct */ -typedef struct { - unsigned long model; - char modelString[80]; - unsigned long numPorts; - int loadrm2; - int startingPortNumber; -} rocketModel_t; - -struct rocket_config { - int line; - int flags; - int closing_wait; - int close_delay; - int port; - int reserved[32]; -}; - -struct rocket_ports { - int tty_major; - int callout_major; - rocketModel_t rocketModel[8]; -}; - -struct rocket_version { - char rocket_version[32]; - char rocket_date[32]; - char reserved[64]; -}; - -/* - * Rocketport flags - */ -/*#define ROCKET_CALLOUT_NOHUP 0x00000001 */ -#define ROCKET_FORCE_CD 0x00000002 -#define ROCKET_HUP_NOTIFY 0x00000004 -#define ROCKET_SPLIT_TERMIOS 0x00000008 -#define ROCKET_SPD_MASK 0x00000070 -#define ROCKET_SPD_HI 0x00000010 /* Use 57600 instead of 38400 bps */ -#define ROCKET_SPD_VHI 0x00000020 /* Use 115200 instead of 38400 bps */ -#define ROCKET_SPD_SHI 0x00000030 /* Use 230400 instead of 38400 bps */ -#define ROCKET_SPD_WARP 0x00000040 /* Use 460800 instead of 38400 bps */ -#define ROCKET_SAK 0x00000080 -#define ROCKET_SESSION_LOCKOUT 0x00000100 -#define ROCKET_PGRP_LOCKOUT 0x00000200 -#define ROCKET_RTS_TOGGLE 0x00000400 -#define ROCKET_MODE_MASK 0x00003000 -#define ROCKET_MODE_RS232 0x00000000 -#define ROCKET_MODE_RS485 0x00001000 -#define ROCKET_MODE_RS422 0x00002000 -#define ROCKET_FLAGS 0x00003FFF - -#define ROCKET_USR_MASK 0x0071 /* Legal flags that non-privileged - * users can set or reset */ - -/* - * For closing_wait and closing_wait2 - */ -#define ROCKET_CLOSING_WAIT_NONE ASYNC_CLOSING_WAIT_NONE -#define ROCKET_CLOSING_WAIT_INF ASYNC_CLOSING_WAIT_INF - -/* - * Rocketport ioctls -- "RP" - */ -#define RCKP_GET_CONFIG 0x00525002 -#define RCKP_SET_CONFIG 0x00525003 -#define RCKP_GET_PORTS 0x00525004 -#define RCKP_RESET_RM2 0x00525005 -#define RCKP_GET_VERSION 0x00525006 - -/* Rocketport Models */ -#define MODEL_RP32INTF 0x0001 /* RP 32 port w/external I/F */ -#define MODEL_RP8INTF 0x0002 /* RP 8 port w/external I/F */ -#define MODEL_RP16INTF 0x0003 /* RP 16 port w/external I/F */ -#define MODEL_RP8OCTA 0x0005 /* RP 8 port w/octa cable */ -#define MODEL_RP4QUAD 0x0004 /* RP 4 port w/quad cable */ -#define MODEL_RP8J 0x0006 /* RP 8 port w/RJ11 connectors */ -#define MODEL_RP4J 0x0007 /* RP 4 port w/RJ45 connectors */ -#define MODEL_RP8SNI 0x0008 /* RP 8 port w/ DB78 SNI connector */ -#define MODEL_RP16SNI 0x0009 /* RP 16 port w/ DB78 SNI connector */ -#define MODEL_RPP4 0x000A /* RP Plus 4 port */ -#define MODEL_RPP8 0x000B /* RP Plus 8 port */ -#define MODEL_RP2_232 0x000E /* RP Plus 2 port RS232 */ -#define MODEL_RP2_422 0x000F /* RP Plus 2 port RS232 */ - -/* Rocketmodem II Models */ -#define MODEL_RP6M 0x000C /* RM 6 port */ -#define MODEL_RP4M 0x000D /* RM 4 port */ - -/* Universal PCI boards */ -#define MODEL_UPCI_RP32INTF 0x0801 /* RP UPCI 32 port w/external I/F */ -#define MODEL_UPCI_RP8INTF 0x0802 /* RP UPCI 8 port w/external I/F */ -#define MODEL_UPCI_RP16INTF 0x0803 /* RP UPCI 16 port w/external I/F */ -#define MODEL_UPCI_RP8OCTA 0x0805 /* RP UPCI 8 port w/octa cable */ -#define MODEL_UPCI_RM3_8PORT 0x080C /* RP UPCI Rocketmodem III 8 port */ -#define MODEL_UPCI_RM3_4PORT 0x080C /* RP UPCI Rocketmodem III 4 port */ - -/* Compact PCI 16 port */ -#define MODEL_CPCI_RP16INTF 0x0903 /* RP Compact PCI 16 port w/external I/F */ - -/* All ISA boards */ -#define MODEL_ISA 0x1000 diff --git a/drivers/tty/rocket_int.h b/drivers/tty/rocket_int.h deleted file mode 100644 index 727e50dbb92f..000000000000 --- a/drivers/tty/rocket_int.h +++ /dev/null @@ -1,1214 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * rocket_int.h --- internal header file for rocket.c - * - * Written by Theodore Ts'o, Copyright 1997. - * Copyright 1997 Comtrol Corporation. - * - */ - -/* - * Definition of the types in rcktpt_type - */ -#define ROCKET_TYPE_NORMAL 0 -#define ROCKET_TYPE_MODEM 1 -#define ROCKET_TYPE_MODEMII 2 -#define ROCKET_TYPE_MODEMIII 3 -#define ROCKET_TYPE_PC104 4 - -#include <linux/mutex.h> - -#include <asm/io.h> -#include <asm/byteorder.h> - -typedef unsigned char Byte_t; -typedef unsigned int ByteIO_t; - -typedef unsigned int Word_t; -typedef unsigned int WordIO_t; - -typedef unsigned int DWordIO_t; - -/* - * Note! Normally the Linux I/O macros already take care of - * byte-swapping the I/O instructions. However, all accesses using - * sOutDW aren't really 32-bit accesses, but should be handled in byte - * order. Hence the use of the cpu_to_le32() macro to byte-swap - * things to no-op the byte swapping done by the big-endian outl() - * instruction. - */ - -static inline void sOutB(unsigned short port, unsigned char value) -{ -#ifdef ROCKET_DEBUG_IO - printk(KERN_DEBUG "sOutB(%x, %x)...\n", port, value); -#endif - outb_p(value, port); -} - -static inline void sOutW(unsigned short port, unsigned short value) -{ -#ifdef ROCKET_DEBUG_IO - printk(KERN_DEBUG "sOutW(%x, %x)...\n", port, value); -#endif - outw_p(value, port); -} - -static inline void out32(unsigned short port, Byte_t *p) -{ - u32 value = get_unaligned_le32(p); -#ifdef ROCKET_DEBUG_IO - printk(KERN_DEBUG "out32(%x, %lx)...\n", port, value); -#endif - outl_p(value, port); -} - -static inline unsigned char sInB(unsigned short port) -{ - return inb_p(port); -} - -static inline unsigned short sInW(unsigned short port) -{ - return inw_p(port); -} - -/* This is used to move arrays of bytes so byte swapping isn't appropriate. */ -#define sOutStrW(port, addr, count) if (count) outsw(port, addr, count) -#define sInStrW(port, addr, count) if (count) insw(port, addr, count) - -#define CTL_SIZE 8 -#define AIOP_CTL_SIZE 4 -#define CHAN_AIOP_SIZE 8 -#define MAX_PORTS_PER_AIOP 8 -#define MAX_AIOPS_PER_BOARD 4 -#define MAX_PORTS_PER_BOARD 32 - -/* Bus type ID */ -#define isISA 0 -#define isPCI 1 -#define isMC 2 - -/* Controller ID numbers */ -#define CTLID_NULL -1 /* no controller exists */ -#define CTLID_0001 0x0001 /* controller release 1 */ - -/* AIOP ID numbers, identifies AIOP type implementing channel */ -#define AIOPID_NULL -1 /* no AIOP or channel exists */ -#define AIOPID_0001 0x0001 /* AIOP release 1 */ - -/************************************************************************ - Global Register Offsets - Direct Access - Fixed values -************************************************************************/ - -#define _CMD_REG 0x38 /* Command Register 8 Write */ -#define _INT_CHAN 0x39 /* Interrupt Channel Register 8 Read */ -#define _INT_MASK 0x3A /* Interrupt Mask Register 8 Read / Write */ -#define _UNUSED 0x3B /* Unused 8 */ -#define _INDX_ADDR 0x3C /* Index Register Address 16 Write */ -#define _INDX_DATA 0x3E /* Index Register Data 8/16 Read / Write */ - -/************************************************************************ - Channel Register Offsets for 1st channel in AIOP - Direct Access -************************************************************************/ -#define _TD0 0x00 /* Transmit Data 16 Write */ -#define _RD0 0x00 /* Receive Data 16 Read */ -#define _CHN_STAT0 0x20 /* Channel Status 8/16 Read / Write */ -#define _FIFO_CNT0 0x10 /* Transmit/Receive FIFO Count 16 Read */ -#define _INT_ID0 0x30 /* Interrupt Identification 8 Read */ - -/************************************************************************ - Tx Control Register Offsets - Indexed - External - Fixed -************************************************************************/ -#define _TX_ENBLS 0x980 /* Tx Processor Enables Register 8 Read / Write */ -#define _TXCMP1 0x988 /* Transmit Compare Value #1 8 Read / Write */ -#define _TXCMP2 0x989 /* Transmit Compare Value #2 8 Read / Write */ -#define _TXREP1B1 0x98A /* Tx Replace Value #1 - Byte 1 8 Read / Write */ -#define _TXREP1B2 0x98B /* Tx Replace Value #1 - Byte 2 8 Read / Write */ -#define _TXREP2 0x98C /* Transmit Replace Value #2 8 Read / Write */ - -/************************************************************************ -Memory Controller Register Offsets - Indexed - External - Fixed -************************************************************************/ -#define _RX_FIFO 0x000 /* Rx FIFO */ -#define _TX_FIFO 0x800 /* Tx FIFO */ -#define _RXF_OUTP 0x990 /* Rx FIFO OUT pointer 16 Read / Write */ -#define _RXF_INP 0x992 /* Rx FIFO IN pointer 16 Read / Write */ -#define _TXF_OUTP 0x994 /* Tx FIFO OUT pointer 8 Read / Write */ -#define _TXF_INP 0x995 /* Tx FIFO IN pointer 8 Read / Write */ -#define _TXP_CNT 0x996 /* Tx Priority Count 8 Read / Write */ -#define _TXP_PNTR 0x997 /* Tx Priority Pointer 8 Read / Write */ - -#define PRI_PEND 0x80 /* Priority data pending (bit7, Tx pri cnt) */ -#define TXFIFO_SIZE 255 /* size of Tx FIFO */ -#define RXFIFO_SIZE 1023 /* size of Rx FIFO */ - -/************************************************************************ -Tx Priority Buffer - Indexed - External - Fixed -************************************************************************/ -#define _TXP_BUF 0x9C0 /* Tx Priority Buffer 32 Bytes Read / Write */ -#define TXP_SIZE 0x20 /* 32 bytes */ - -/************************************************************************ -Channel Register Offsets - Indexed - Internal - Fixed -************************************************************************/ - -#define _TX_CTRL 0xFF0 /* Transmit Control 16 Write */ -#define _RX_CTRL 0xFF2 /* Receive Control 8 Write */ -#define _BAUD 0xFF4 /* Baud Rate 16 Write */ -#define _CLK_PRE 0xFF6 /* Clock Prescaler 8 Write */ - -#define STMBREAK 0x08 /* BREAK */ -#define STMFRAME 0x04 /* framing error */ -#define STMRCVROVR 0x02 /* receiver over run error */ -#define STMPARITY 0x01 /* parity error */ -#define STMERROR (STMBREAK | STMFRAME | STMPARITY) -#define STMBREAKH 0x800 /* BREAK */ -#define STMFRAMEH 0x400 /* framing error */ -#define STMRCVROVRH 0x200 /* receiver over run error */ -#define STMPARITYH 0x100 /* parity error */ -#define STMERRORH (STMBREAKH | STMFRAMEH | STMPARITYH) - -#define CTS_ACT 0x20 /* CTS input asserted */ -#define DSR_ACT 0x10 /* DSR input asserted */ -#define CD_ACT 0x08 /* CD input asserted */ -#define TXFIFOMT 0x04 /* Tx FIFO is empty */ -#define TXSHRMT 0x02 /* Tx shift register is empty */ -#define RDA 0x01 /* Rx data available */ -#define DRAINED (TXFIFOMT | TXSHRMT) /* indicates Tx is drained */ - -#define STATMODE 0x8000 /* status mode enable bit */ -#define RXFOVERFL 0x2000 /* receive FIFO overflow */ -#define RX2MATCH 0x1000 /* receive compare byte 2 match */ -#define RX1MATCH 0x0800 /* receive compare byte 1 match */ -#define RXBREAK 0x0400 /* received BREAK */ -#define RXFRAME 0x0200 /* received framing error */ -#define RXPARITY 0x0100 /* received parity error */ -#define STATERROR (RXBREAK | RXFRAME | RXPARITY) - -#define CTSFC_EN 0x80 /* CTS flow control enable bit */ -#define RTSTOG_EN 0x40 /* RTS toggle enable bit */ -#define TXINT_EN 0x10 /* transmit interrupt enable */ -#define STOP2 0x08 /* enable 2 stop bits (0 = 1 stop) */ -#define PARITY_EN 0x04 /* enable parity (0 = no parity) */ -#define EVEN_PAR 0x02 /* even parity (0 = odd parity) */ -#define DATA8BIT 0x01 /* 8 bit data (0 = 7 bit data) */ - -#define SETBREAK 0x10 /* send break condition (must clear) */ -#define LOCALLOOP 0x08 /* local loopback set for test */ -#define SET_DTR 0x04 /* assert DTR */ -#define SET_RTS 0x02 /* assert RTS */ -#define TX_ENABLE 0x01 /* enable transmitter */ - -#define RTSFC_EN 0x40 /* RTS flow control enable */ -#define RXPROC_EN 0x20 /* receive processor enable */ -#define TRIG_NO 0x00 /* Rx FIFO trigger level 0 (no trigger) */ -#define TRIG_1 0x08 /* trigger level 1 char */ -#define TRIG_1_2 0x10 /* trigger level 1/2 */ -#define TRIG_7_8 0x18 /* trigger level 7/8 */ -#define TRIG_MASK 0x18 /* trigger level mask */ -#define SRCINT_EN 0x04 /* special Rx condition interrupt enable */ -#define RXINT_EN 0x02 /* Rx interrupt enable */ -#define MCINT_EN 0x01 /* modem change interrupt enable */ - -#define RXF_TRIG 0x20 /* Rx FIFO trigger level interrupt */ -#define TXFIFO_MT 0x10 /* Tx FIFO empty interrupt */ -#define SRC_INT 0x08 /* special receive condition interrupt */ -#define DELTA_CD 0x04 /* CD change interrupt */ -#define DELTA_CTS 0x02 /* CTS change interrupt */ -#define DELTA_DSR 0x01 /* DSR change interrupt */ - -#define REP1W2_EN 0x10 /* replace byte 1 with 2 bytes enable */ -#define IGN2_EN 0x08 /* ignore byte 2 enable */ -#define IGN1_EN 0x04 /* ignore byte 1 enable */ -#define COMP2_EN 0x02 /* compare byte 2 enable */ -#define COMP1_EN 0x01 /* compare byte 1 enable */ - -#define RESET_ALL 0x80 /* reset AIOP (all channels) */ -#define TXOVERIDE 0x40 /* Transmit software off override */ -#define RESETUART 0x20 /* reset channel's UART */ -#define RESTXFCNT 0x10 /* reset channel's Tx FIFO count register */ -#define RESRXFCNT 0x08 /* reset channel's Rx FIFO count register */ - -#define INTSTAT0 0x01 /* AIOP 0 interrupt status */ -#define INTSTAT1 0x02 /* AIOP 1 interrupt status */ -#define INTSTAT2 0x04 /* AIOP 2 interrupt status */ -#define INTSTAT3 0x08 /* AIOP 3 interrupt status */ - -#define INTR_EN 0x08 /* allow interrupts to host */ -#define INT_STROB 0x04 /* strobe and clear interrupt line (EOI) */ - -/************************************************************************** - MUDBAC remapped for PCI -**************************************************************************/ - -#define _CFG_INT_PCI 0x40 -#define _PCI_INT_FUNC 0x3A - -#define PCI_STROB 0x2000 /* bit 13 of int aiop register */ -#define INTR_EN_PCI 0x0010 /* allow interrupts to host */ - -/* - * Definitions for Universal PCI board registers - */ -#define _PCI_9030_INT_CTRL 0x4c /* Offsets from BAR1 */ -#define _PCI_9030_GPIO_CTRL 0x54 -#define PCI_INT_CTRL_AIOP 0x0001 -#define PCI_GPIO_CTRL_8PORT 0x4000 -#define _PCI_9030_RING_IND 0xc0 /* Offsets from BAR1 */ - -#define CHAN3_EN 0x08 /* enable AIOP 3 */ -#define CHAN2_EN 0x04 /* enable AIOP 2 */ -#define CHAN1_EN 0x02 /* enable AIOP 1 */ -#define CHAN0_EN 0x01 /* enable AIOP 0 */ -#define FREQ_DIS 0x00 -#define FREQ_274HZ 0x60 -#define FREQ_137HZ 0x50 -#define FREQ_69HZ 0x40 -#define FREQ_34HZ 0x30 -#define FREQ_17HZ 0x20 -#define FREQ_9HZ 0x10 -#define PERIODIC_ONLY 0x80 /* only PERIODIC interrupt */ - -#define CHANINT_EN 0x0100 /* flags to enable/disable channel ints */ - -#define RDATASIZE 72 -#define RREGDATASIZE 52 - -/* - * AIOP interrupt bits for ISA/PCI boards and UPCI boards. - */ -#define AIOP_INTR_BIT_0 0x0001 -#define AIOP_INTR_BIT_1 0x0002 -#define AIOP_INTR_BIT_2 0x0004 -#define AIOP_INTR_BIT_3 0x0008 - -#define AIOP_INTR_BITS ( \ - AIOP_INTR_BIT_0 \ - | AIOP_INTR_BIT_1 \ - | AIOP_INTR_BIT_2 \ - | AIOP_INTR_BIT_3) - -#define UPCI_AIOP_INTR_BIT_0 0x0004 -#define UPCI_AIOP_INTR_BIT_1 0x0020 -#define UPCI_AIOP_INTR_BIT_2 0x0100 -#define UPCI_AIOP_INTR_BIT_3 0x0800 - -#define UPCI_AIOP_INTR_BITS ( \ - UPCI_AIOP_INTR_BIT_0 \ - | UPCI_AIOP_INTR_BIT_1 \ - | UPCI_AIOP_INTR_BIT_2 \ - | UPCI_AIOP_INTR_BIT_3) - -/* Controller level information structure */ -typedef struct { - int CtlID; - int CtlNum; - int BusType; - int boardType; - int isUPCI; - WordIO_t PCIIO; - WordIO_t PCIIO2; - ByteIO_t MBaseIO; - ByteIO_t MReg1IO; - ByteIO_t MReg2IO; - ByteIO_t MReg3IO; - Byte_t MReg2; - Byte_t MReg3; - int NumAiop; - int AltChanRingIndicator; - ByteIO_t UPCIRingInd; - WordIO_t AiopIO[AIOP_CTL_SIZE]; - ByteIO_t AiopIntChanIO[AIOP_CTL_SIZE]; - int AiopID[AIOP_CTL_SIZE]; - int AiopNumChan[AIOP_CTL_SIZE]; - Word_t *AiopIntrBits; -} CONTROLLER_T; - -typedef CONTROLLER_T CONTROLLER_t; - -/* Channel level information structure */ -typedef struct { - CONTROLLER_T *CtlP; - int AiopNum; - int ChanID; - int ChanNum; - int rtsToggle; - - ByteIO_t Cmd; - ByteIO_t IntChan; - ByteIO_t IntMask; - DWordIO_t IndexAddr; - WordIO_t IndexData; - - WordIO_t TxRxData; - WordIO_t ChanStat; - WordIO_t TxRxCount; - ByteIO_t IntID; - - Word_t TxFIFO; - Word_t TxFIFOPtrs; - Word_t RxFIFO; - Word_t RxFIFOPtrs; - Word_t TxPrioCnt; - Word_t TxPrioPtr; - Word_t TxPrioBuf; - - Byte_t R[RREGDATASIZE]; - - Byte_t BaudDiv[4]; - Byte_t TxControl[4]; - Byte_t RxControl[4]; - Byte_t TxEnables[4]; - Byte_t TxCompare[4]; - Byte_t TxReplace1[4]; - Byte_t TxReplace2[4]; -} CHANNEL_T; - -typedef CHANNEL_T CHANNEL_t; -typedef CHANNEL_T *CHANPTR_T; - -#define InterfaceModeRS232 0x00 -#define InterfaceModeRS422 0x08 -#define InterfaceModeRS485 0x10 -#define InterfaceModeRS232T 0x18 - -/*************************************************************************** -Function: sClrBreak -Purpose: Stop sending a transmit BREAK signal -Call: sClrBreak(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sClrBreak(ChP) \ -do { \ - (ChP)->TxControl[3] &= ~SETBREAK; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sClrDTR -Purpose: Clr the DTR output -Call: sClrDTR(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sClrDTR(ChP) \ -do { \ - (ChP)->TxControl[3] &= ~SET_DTR; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sClrRTS -Purpose: Clr the RTS output -Call: sClrRTS(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sClrRTS(ChP) \ -do { \ - if ((ChP)->rtsToggle) break; \ - (ChP)->TxControl[3] &= ~SET_RTS; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sClrTxXOFF -Purpose: Clear any existing transmit software flow control off condition -Call: sClrTxXOFF(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sClrTxXOFF(ChP) \ -do { \ - sOutB((ChP)->Cmd,TXOVERIDE | (Byte_t)(ChP)->ChanNum); \ - sOutB((ChP)->Cmd,(Byte_t)(ChP)->ChanNum); \ -} while (0) - -/*************************************************************************** -Function: sCtlNumToCtlPtr -Purpose: Convert a controller number to controller structure pointer -Call: sCtlNumToCtlPtr(CtlNum) - int CtlNum; Controller number -Return: CONTROLLER_T *: Ptr to controller structure -*/ -#define sCtlNumToCtlPtr(CTLNUM) &sController[CTLNUM] - -/*************************************************************************** -Function: sControllerEOI -Purpose: Strobe the MUDBAC's End Of Interrupt bit. -Call: sControllerEOI(CtlP) - CONTROLLER_T *CtlP; Ptr to controller structure -*/ -#define sControllerEOI(CTLP) sOutB((CTLP)->MReg2IO,(CTLP)->MReg2 | INT_STROB) - -/*************************************************************************** -Function: sPCIControllerEOI -Purpose: Strobe the PCI End Of Interrupt bit. - For the UPCI boards, toggle the AIOP interrupt enable bit - (this was taken from the Windows driver). -Call: sPCIControllerEOI(CtlP) - CONTROLLER_T *CtlP; Ptr to controller structure -*/ -#define sPCIControllerEOI(CTLP) \ -do { \ - if ((CTLP)->isUPCI) { \ - Word_t w = sInW((CTLP)->PCIIO); \ - sOutW((CTLP)->PCIIO, (w ^ PCI_INT_CTRL_AIOP)); \ - sOutW((CTLP)->PCIIO, w); \ - } \ - else { \ - sOutW((CTLP)->PCIIO, PCI_STROB); \ - } \ -} while (0) - -/*************************************************************************** -Function: sDisAiop -Purpose: Disable I/O access to an AIOP -Call: sDisAiop(CltP) - CONTROLLER_T *CtlP; Ptr to controller structure - int AiopNum; Number of AIOP on controller -*/ -#define sDisAiop(CTLP,AIOPNUM) \ -do { \ - (CTLP)->MReg3 &= sBitMapClrTbl[AIOPNUM]; \ - sOutB((CTLP)->MReg3IO,(CTLP)->MReg3); \ -} while (0) - -/*************************************************************************** -Function: sDisCTSFlowCtl -Purpose: Disable output flow control using CTS -Call: sDisCTSFlowCtl(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sDisCTSFlowCtl(ChP) \ -do { \ - (ChP)->TxControl[2] &= ~CTSFC_EN; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sDisIXANY -Purpose: Disable IXANY Software Flow Control -Call: sDisIXANY(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sDisIXANY(ChP) \ -do { \ - (ChP)->R[0x0e] = 0x86; \ - out32((ChP)->IndexAddr,&(ChP)->R[0x0c]); \ -} while (0) - -/*************************************************************************** -Function: DisParity -Purpose: Disable parity -Call: sDisParity(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Comments: Function sSetParity() can be used in place of functions sEnParity(), - sDisParity(), sSetOddParity(), and sSetEvenParity(). -*/ -#define sDisParity(ChP) \ -do { \ - (ChP)->TxControl[2] &= ~PARITY_EN; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sDisRTSToggle -Purpose: Disable RTS toggle -Call: sDisRTSToggle(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sDisRTSToggle(ChP) \ -do { \ - (ChP)->TxControl[2] &= ~RTSTOG_EN; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ - (ChP)->rtsToggle = 0; \ -} while (0) - -/*************************************************************************** -Function: sDisRxFIFO -Purpose: Disable Rx FIFO -Call: sDisRxFIFO(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sDisRxFIFO(ChP) \ -do { \ - (ChP)->R[0x32] = 0x0a; \ - out32((ChP)->IndexAddr,&(ChP)->R[0x30]); \ -} while (0) - -/*************************************************************************** -Function: sDisRxStatusMode -Purpose: Disable the Rx status mode -Call: sDisRxStatusMode(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Comments: This takes the channel out of the receive status mode. All - subsequent reads of receive data using sReadRxWord() will return - two data bytes. -*/ -#define sDisRxStatusMode(ChP) sOutW((ChP)->ChanStat,0) - -/*************************************************************************** -Function: sDisTransmit -Purpose: Disable transmit -Call: sDisTransmit(ChP) - CHANNEL_T *ChP; Ptr to channel structure - This disables movement of Tx data from the Tx FIFO into the 1 byte - Tx buffer. Therefore there could be up to a 2 byte latency - between the time sDisTransmit() is called and the transmit buffer - and transmit shift register going completely empty. -*/ -#define sDisTransmit(ChP) \ -do { \ - (ChP)->TxControl[3] &= ~TX_ENABLE; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sDisTxSoftFlowCtl -Purpose: Disable Tx Software Flow Control -Call: sDisTxSoftFlowCtl(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sDisTxSoftFlowCtl(ChP) \ -do { \ - (ChP)->R[0x06] = 0x8a; \ - out32((ChP)->IndexAddr,&(ChP)->R[0x04]); \ -} while (0) - -/*************************************************************************** -Function: sEnAiop -Purpose: Enable I/O access to an AIOP -Call: sEnAiop(CltP) - CONTROLLER_T *CtlP; Ptr to controller structure - int AiopNum; Number of AIOP on controller -*/ -#define sEnAiop(CTLP,AIOPNUM) \ -do { \ - (CTLP)->MReg3 |= sBitMapSetTbl[AIOPNUM]; \ - sOutB((CTLP)->MReg3IO,(CTLP)->MReg3); \ -} while (0) - -/*************************************************************************** -Function: sEnCTSFlowCtl -Purpose: Enable output flow control using CTS -Call: sEnCTSFlowCtl(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sEnCTSFlowCtl(ChP) \ -do { \ - (ChP)->TxControl[2] |= CTSFC_EN; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sEnIXANY -Purpose: Enable IXANY Software Flow Control -Call: sEnIXANY(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sEnIXANY(ChP) \ -do { \ - (ChP)->R[0x0e] = 0x21; \ - out32((ChP)->IndexAddr,&(ChP)->R[0x0c]); \ -} while (0) - -/*************************************************************************** -Function: EnParity -Purpose: Enable parity -Call: sEnParity(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Comments: Function sSetParity() can be used in place of functions sEnParity(), - sDisParity(), sSetOddParity(), and sSetEvenParity(). - -Warnings: Before enabling parity odd or even parity should be chosen using - functions sSetOddParity() or sSetEvenParity(). -*/ -#define sEnParity(ChP) \ -do { \ - (ChP)->TxControl[2] |= PARITY_EN; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sEnRTSToggle -Purpose: Enable RTS toggle -Call: sEnRTSToggle(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Comments: This function will disable RTS flow control and clear the RTS - line to allow operation of RTS toggle. -*/ -#define sEnRTSToggle(ChP) \ -do { \ - (ChP)->RxControl[2] &= ~RTSFC_EN; \ - out32((ChP)->IndexAddr,(ChP)->RxControl); \ - (ChP)->TxControl[2] |= RTSTOG_EN; \ - (ChP)->TxControl[3] &= ~SET_RTS; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ - (ChP)->rtsToggle = 1; \ -} while (0) - -/*************************************************************************** -Function: sEnRxFIFO -Purpose: Enable Rx FIFO -Call: sEnRxFIFO(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sEnRxFIFO(ChP) \ -do { \ - (ChP)->R[0x32] = 0x08; \ - out32((ChP)->IndexAddr,&(ChP)->R[0x30]); \ -} while (0) - -/*************************************************************************** -Function: sEnRxProcessor -Purpose: Enable the receive processor -Call: sEnRxProcessor(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Comments: This function is used to start the receive processor. When - the channel is in the reset state the receive processor is not - running. This is done to prevent the receive processor from - executing invalid microcode instructions prior to the - downloading of the microcode. - -Warnings: This function must be called after valid microcode has been - downloaded to the AIOP, and it must not be called before the - microcode has been downloaded. -*/ -#define sEnRxProcessor(ChP) \ -do { \ - (ChP)->RxControl[2] |= RXPROC_EN; \ - out32((ChP)->IndexAddr,(ChP)->RxControl); \ -} while (0) - -/*************************************************************************** -Function: sEnRxStatusMode -Purpose: Enable the Rx status mode -Call: sEnRxStatusMode(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Comments: This places the channel in the receive status mode. All subsequent - reads of receive data using sReadRxWord() will return a data byte - in the low word and a status byte in the high word. - -*/ -#define sEnRxStatusMode(ChP) sOutW((ChP)->ChanStat,STATMODE) - -/*************************************************************************** -Function: sEnTransmit -Purpose: Enable transmit -Call: sEnTransmit(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sEnTransmit(ChP) \ -do { \ - (ChP)->TxControl[3] |= TX_ENABLE; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sEnTxSoftFlowCtl -Purpose: Enable Tx Software Flow Control -Call: sEnTxSoftFlowCtl(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sEnTxSoftFlowCtl(ChP) \ -do { \ - (ChP)->R[0x06] = 0xc5; \ - out32((ChP)->IndexAddr,&(ChP)->R[0x04]); \ -} while (0) - -/*************************************************************************** -Function: sGetAiopIntStatus -Purpose: Get the AIOP interrupt status -Call: sGetAiopIntStatus(CtlP,AiopNum) - CONTROLLER_T *CtlP; Ptr to controller structure - int AiopNum; AIOP number -Return: Byte_t: The AIOP interrupt status. Bits 0 through 7 - represent channels 0 through 7 respectively. If a - bit is set that channel is interrupting. -*/ -#define sGetAiopIntStatus(CTLP,AIOPNUM) sInB((CTLP)->AiopIntChanIO[AIOPNUM]) - -/*************************************************************************** -Function: sGetAiopNumChan -Purpose: Get the number of channels supported by an AIOP -Call: sGetAiopNumChan(CtlP,AiopNum) - CONTROLLER_T *CtlP; Ptr to controller structure - int AiopNum; AIOP number -Return: int: The number of channels supported by the AIOP -*/ -#define sGetAiopNumChan(CTLP,AIOPNUM) (CTLP)->AiopNumChan[AIOPNUM] - -/*************************************************************************** -Function: sGetChanIntID -Purpose: Get a channel's interrupt identification byte -Call: sGetChanIntID(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: Byte_t: The channel interrupt ID. Can be any - combination of the following flags: - RXF_TRIG: Rx FIFO trigger level interrupt - TXFIFO_MT: Tx FIFO empty interrupt - SRC_INT: Special receive condition interrupt - DELTA_CD: CD change interrupt - DELTA_CTS: CTS change interrupt - DELTA_DSR: DSR change interrupt -*/ -#define sGetChanIntID(ChP) (sInB((ChP)->IntID) & (RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR)) - -/*************************************************************************** -Function: sGetChanNum -Purpose: Get the number of a channel within an AIOP -Call: sGetChanNum(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: int: Channel number within AIOP, or NULLCHAN if channel does - not exist. -*/ -#define sGetChanNum(ChP) (ChP)->ChanNum - -/*************************************************************************** -Function: sGetChanStatus -Purpose: Get the channel status -Call: sGetChanStatus(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: Word_t: The channel status. Can be any combination of - the following flags: - LOW BYTE FLAGS - CTS_ACT: CTS input asserted - DSR_ACT: DSR input asserted - CD_ACT: CD input asserted - TXFIFOMT: Tx FIFO is empty - TXSHRMT: Tx shift register is empty - RDA: Rx data available - - HIGH BYTE FLAGS - STATMODE: status mode enable bit - RXFOVERFL: receive FIFO overflow - RX2MATCH: receive compare byte 2 match - RX1MATCH: receive compare byte 1 match - RXBREAK: received BREAK - RXFRAME: received framing error - RXPARITY: received parity error -Warnings: This function will clear the high byte flags in the Channel - Status Register. -*/ -#define sGetChanStatus(ChP) sInW((ChP)->ChanStat) - -/*************************************************************************** -Function: sGetChanStatusLo -Purpose: Get the low byte only of the channel status -Call: sGetChanStatusLo(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: Byte_t: The channel status low byte. Can be any combination - of the following flags: - CTS_ACT: CTS input asserted - DSR_ACT: DSR input asserted - CD_ACT: CD input asserted - TXFIFOMT: Tx FIFO is empty - TXSHRMT: Tx shift register is empty - RDA: Rx data available -*/ -#define sGetChanStatusLo(ChP) sInB((ByteIO_t)(ChP)->ChanStat) - -/********************************************************************** - * Get RI status of channel - * Defined as a function in rocket.c -aes - */ -#if 0 -#define sGetChanRI(ChP) ((ChP)->CtlP->AltChanRingIndicator ? \ - (sInB((ByteIO_t)((ChP)->ChanStat+8)) & DSR_ACT) : \ - (((ChP)->CtlP->boardType == ROCKET_TYPE_PC104) ? \ - (!(sInB((ChP)->CtlP->AiopIO[3]) & sBitMapSetTbl[(ChP)->ChanNum])) : \ - 0)) -#endif - -/*************************************************************************** -Function: sGetControllerIntStatus -Purpose: Get the controller interrupt status -Call: sGetControllerIntStatus(CtlP) - CONTROLLER_T *CtlP; Ptr to controller structure -Return: Byte_t: The controller interrupt status in the lower 4 - bits. Bits 0 through 3 represent AIOP's 0 - through 3 respectively. If a bit is set that - AIOP is interrupting. Bits 4 through 7 will - always be cleared. -*/ -#define sGetControllerIntStatus(CTLP) (sInB((CTLP)->MReg1IO) & 0x0f) - -/*************************************************************************** -Function: sPCIGetControllerIntStatus -Purpose: Get the controller interrupt status -Call: sPCIGetControllerIntStatus(CtlP) - CONTROLLER_T *CtlP; Ptr to controller structure -Return: unsigned char: The controller interrupt status in the lower 4 - bits and bit 4. Bits 0 through 3 represent AIOP's 0 - through 3 respectively. Bit 4 is set if the int - was generated from periodic. If a bit is set the - AIOP is interrupting. -*/ -#define sPCIGetControllerIntStatus(CTLP) \ - ((CTLP)->isUPCI ? \ - (sInW((CTLP)->PCIIO2) & UPCI_AIOP_INTR_BITS) : \ - ((sInW((CTLP)->PCIIO) >> 8) & AIOP_INTR_BITS)) - -/*************************************************************************** - -Function: sGetRxCnt -Purpose: Get the number of data bytes in the Rx FIFO -Call: sGetRxCnt(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: int: The number of data bytes in the Rx FIFO. -Comments: Byte read of count register is required to obtain Rx count. - -*/ -#define sGetRxCnt(ChP) sInW((ChP)->TxRxCount) - -/*************************************************************************** -Function: sGetTxCnt -Purpose: Get the number of data bytes in the Tx FIFO -Call: sGetTxCnt(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: Byte_t: The number of data bytes in the Tx FIFO. -Comments: Byte read of count register is required to obtain Tx count. - -*/ -#define sGetTxCnt(ChP) sInB((ByteIO_t)(ChP)->TxRxCount) - -/***************************************************************************** -Function: sGetTxRxDataIO -Purpose: Get the I/O address of a channel's TxRx Data register -Call: sGetTxRxDataIO(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: WordIO_t: I/O address of a channel's TxRx Data register -*/ -#define sGetTxRxDataIO(ChP) (ChP)->TxRxData - -/*************************************************************************** -Function: sInitChanDefaults -Purpose: Initialize a channel structure to it's default state. -Call: sInitChanDefaults(ChP) - CHANNEL_T *ChP; Ptr to the channel structure -Comments: This function must be called once for every channel structure - that exists before any other SSCI calls can be made. - -*/ -#define sInitChanDefaults(ChP) \ -do { \ - (ChP)->CtlP = NULLCTLPTR; \ - (ChP)->AiopNum = NULLAIOP; \ - (ChP)->ChanID = AIOPID_NULL; \ - (ChP)->ChanNum = NULLCHAN; \ -} while (0) - -/*************************************************************************** -Function: sResetAiopByNum -Purpose: Reset the AIOP by number -Call: sResetAiopByNum(CTLP,AIOPNUM) - CONTROLLER_T CTLP; Ptr to controller structure - AIOPNUM; AIOP index -*/ -#define sResetAiopByNum(CTLP,AIOPNUM) \ -do { \ - sOutB((CTLP)->AiopIO[(AIOPNUM)]+_CMD_REG,RESET_ALL); \ - sOutB((CTLP)->AiopIO[(AIOPNUM)]+_CMD_REG,0x0); \ -} while (0) - -/*************************************************************************** -Function: sSendBreak -Purpose: Send a transmit BREAK signal -Call: sSendBreak(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sSendBreak(ChP) \ -do { \ - (ChP)->TxControl[3] |= SETBREAK; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetBaud -Purpose: Set baud rate -Call: sSetBaud(ChP,Divisor) - CHANNEL_T *ChP; Ptr to channel structure - Word_t Divisor; 16 bit baud rate divisor for channel -*/ -#define sSetBaud(ChP,DIVISOR) \ -do { \ - (ChP)->BaudDiv[2] = (Byte_t)(DIVISOR); \ - (ChP)->BaudDiv[3] = (Byte_t)((DIVISOR) >> 8); \ - out32((ChP)->IndexAddr,(ChP)->BaudDiv); \ -} while (0) - -/*************************************************************************** -Function: sSetData7 -Purpose: Set data bits to 7 -Call: sSetData7(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sSetData7(ChP) \ -do { \ - (ChP)->TxControl[2] &= ~DATA8BIT; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetData8 -Purpose: Set data bits to 8 -Call: sSetData8(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sSetData8(ChP) \ -do { \ - (ChP)->TxControl[2] |= DATA8BIT; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetDTR -Purpose: Set the DTR output -Call: sSetDTR(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sSetDTR(ChP) \ -do { \ - (ChP)->TxControl[3] |= SET_DTR; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetEvenParity -Purpose: Set even parity -Call: sSetEvenParity(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Comments: Function sSetParity() can be used in place of functions sEnParity(), - sDisParity(), sSetOddParity(), and sSetEvenParity(). - -Warnings: This function has no effect unless parity is enabled with function - sEnParity(). -*/ -#define sSetEvenParity(ChP) \ -do { \ - (ChP)->TxControl[2] |= EVEN_PAR; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetOddParity -Purpose: Set odd parity -Call: sSetOddParity(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Comments: Function sSetParity() can be used in place of functions sEnParity(), - sDisParity(), sSetOddParity(), and sSetEvenParity(). - -Warnings: This function has no effect unless parity is enabled with function - sEnParity(). -*/ -#define sSetOddParity(ChP) \ -do { \ - (ChP)->TxControl[2] &= ~EVEN_PAR; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetRTS -Purpose: Set the RTS output -Call: sSetRTS(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sSetRTS(ChP) \ -do { \ - if ((ChP)->rtsToggle) break; \ - (ChP)->TxControl[3] |= SET_RTS; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetRxTrigger -Purpose: Set the Rx FIFO trigger level -Call: sSetRxProcessor(ChP,Level) - CHANNEL_T *ChP; Ptr to channel structure - Byte_t Level; Number of characters in Rx FIFO at which the - interrupt will be generated. Can be any of the following flags: - - TRIG_NO: no trigger - TRIG_1: 1 character in FIFO - TRIG_1_2: FIFO 1/2 full - TRIG_7_8: FIFO 7/8 full -Comments: An interrupt will be generated when the trigger level is reached - only if function sEnInterrupt() has been called with flag - RXINT_EN set. The RXF_TRIG flag in the Interrupt Idenfification - register will be set whenever the trigger level is reached - regardless of the setting of RXINT_EN. - -*/ -#define sSetRxTrigger(ChP,LEVEL) \ -do { \ - (ChP)->RxControl[2] &= ~TRIG_MASK; \ - (ChP)->RxControl[2] |= LEVEL; \ - out32((ChP)->IndexAddr,(ChP)->RxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetStop1 -Purpose: Set stop bits to 1 -Call: sSetStop1(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sSetStop1(ChP) \ -do { \ - (ChP)->TxControl[2] &= ~STOP2; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetStop2 -Purpose: Set stop bits to 2 -Call: sSetStop2(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sSetStop2(ChP) \ -do { \ - (ChP)->TxControl[2] |= STOP2; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetTxXOFFChar -Purpose: Set the Tx XOFF flow control character -Call: sSetTxXOFFChar(ChP,Ch) - CHANNEL_T *ChP; Ptr to channel structure - Byte_t Ch; The value to set the Tx XOFF character to -*/ -#define sSetTxXOFFChar(ChP,CH) \ -do { \ - (ChP)->R[0x07] = (CH); \ - out32((ChP)->IndexAddr,&(ChP)->R[0x04]); \ -} while (0) - -/*************************************************************************** -Function: sSetTxXONChar -Purpose: Set the Tx XON flow control character -Call: sSetTxXONChar(ChP,Ch) - CHANNEL_T *ChP; Ptr to channel structure - Byte_t Ch; The value to set the Tx XON character to -*/ -#define sSetTxXONChar(ChP,CH) \ -do { \ - (ChP)->R[0x0b] = (CH); \ - out32((ChP)->IndexAddr,&(ChP)->R[0x08]); \ -} while (0) - -/*************************************************************************** -Function: sStartRxProcessor -Purpose: Start a channel's receive processor -Call: sStartRxProcessor(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Comments: This function is used to start a Rx processor after it was - stopped with sStopRxProcessor() or sStopSWInFlowCtl(). It - will restart both the Rx processor and software input flow control. - -*/ -#define sStartRxProcessor(ChP) out32((ChP)->IndexAddr,&(ChP)->R[0]) - -/*************************************************************************** -Function: sWriteTxByte -Purpose: Write a transmit data byte to a channel. - ByteIO_t io: Channel transmit register I/O address. This can - be obtained with sGetTxRxDataIO(). - Byte_t Data; The transmit data byte. -Warnings: This function writes the data byte without checking to see if - sMaxTxSize is exceeded in the Tx FIFO. -*/ -#define sWriteTxByte(IO,DATA) sOutB(IO,DATA) - -/* - * Begin Linux specific definitions for the Rocketport driver - * - * This code is Copyright Theodore Ts'o, 1995-1997 - */ - -struct r_port { - int magic; - struct tty_port port; - int line; - int flags; /* Don't yet match the ASY_ flags!! */ - unsigned int board:3; - unsigned int aiop:2; - unsigned int chan:3; - CONTROLLER_t *ctlp; - CHANNEL_t channel; - int intmask; - int xmit_fifo_room; /* room in xmit fifo */ - unsigned char *xmit_buf; - int xmit_head; - int xmit_tail; - int xmit_cnt; - int cd_status; - int ignore_status_mask; - int read_status_mask; - int cps; - - spinlock_t slock; - struct mutex write_mtx; -}; - -#define RPORT_MAGIC 0x525001 - -#define NUM_BOARDS 8 -#define MAX_RP_PORTS (32*NUM_BOARDS) - -/* - * The size of the xmit buffer is 1 page, or 4096 bytes - */ -#define XMIT_BUF_SIZE 4096 - -/* number of characters left in xmit buffer before we ask for more */ -#define WAKEUP_CHARS 256 - -/* - * Assigned major numbers for the Comtrol Rocketport - */ -#define TTY_ROCKET_MAJOR 46 -#define CUA_ROCKET_MAJOR 47 - -#ifdef PCI_VENDOR_ID_RP -#undef PCI_VENDOR_ID_RP -#undef PCI_DEVICE_ID_RP8OCTA -#undef PCI_DEVICE_ID_RP8INTF -#undef PCI_DEVICE_ID_RP16INTF -#undef PCI_DEVICE_ID_RP32INTF -#undef PCI_DEVICE_ID_URP8OCTA -#undef PCI_DEVICE_ID_URP8INTF -#undef PCI_DEVICE_ID_URP16INTF -#undef PCI_DEVICE_ID_CRP16INTF -#undef PCI_DEVICE_ID_URP32INTF -#endif - -/* Comtrol PCI Vendor ID */ -#define PCI_VENDOR_ID_RP 0x11fe - -/* Comtrol Device ID's */ -#define PCI_DEVICE_ID_RP32INTF 0x0001 /* Rocketport 32 port w/external I/F */ -#define PCI_DEVICE_ID_RP8INTF 0x0002 /* Rocketport 8 port w/external I/F */ -#define PCI_DEVICE_ID_RP16INTF 0x0003 /* Rocketport 16 port w/external I/F */ -#define PCI_DEVICE_ID_RP4QUAD 0x0004 /* Rocketport 4 port w/quad cable */ -#define PCI_DEVICE_ID_RP8OCTA 0x0005 /* Rocketport 8 port w/octa cable */ -#define PCI_DEVICE_ID_RP8J 0x0006 /* Rocketport 8 port w/RJ11 connectors */ -#define PCI_DEVICE_ID_RP4J 0x0007 /* Rocketport 4 port w/RJ11 connectors */ -#define PCI_DEVICE_ID_RP8SNI 0x0008 /* Rocketport 8 port w/ DB78 SNI (Siemens) connector */ -#define PCI_DEVICE_ID_RP16SNI 0x0009 /* Rocketport 16 port w/ DB78 SNI (Siemens) connector */ -#define PCI_DEVICE_ID_RPP4 0x000A /* Rocketport Plus 4 port */ -#define PCI_DEVICE_ID_RPP8 0x000B /* Rocketport Plus 8 port */ -#define PCI_DEVICE_ID_RP6M 0x000C /* RocketModem 6 port */ -#define PCI_DEVICE_ID_RP4M 0x000D /* RocketModem 4 port */ -#define PCI_DEVICE_ID_RP2_232 0x000E /* Rocketport Plus 2 port RS232 */ -#define PCI_DEVICE_ID_RP2_422 0x000F /* Rocketport Plus 2 port RS422 */ - -/* Universal PCI boards */ -#define PCI_DEVICE_ID_URP32INTF 0x0801 /* Rocketport UPCI 32 port w/external I/F */ -#define PCI_DEVICE_ID_URP8INTF 0x0802 /* Rocketport UPCI 8 port w/external I/F */ -#define PCI_DEVICE_ID_URP16INTF 0x0803 /* Rocketport UPCI 16 port w/external I/F */ -#define PCI_DEVICE_ID_URP8OCTA 0x0805 /* Rocketport UPCI 8 port w/octa cable */ -#define PCI_DEVICE_ID_UPCI_RM3_8PORT 0x080C /* Rocketmodem III 8 port */ -#define PCI_DEVICE_ID_UPCI_RM3_4PORT 0x080D /* Rocketmodem III 4 port */ - -/* Compact PCI device */ -#define PCI_DEVICE_ID_CRP16INTF 0x0903 /* Rocketport Compact PCI 16 port w/external I/F */ - diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c index c33e02cbde93..61550f24a2d3 100644 --- a/drivers/tty/serial/8250/8250_aspeed_vuart.c +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c @@ -28,6 +28,10 @@ #define ASPEED_VUART_ADDRL 0x28 #define ASPEED_VUART_ADDRH 0x2c +#define ASPEED_VUART_DEFAULT_LPC_ADDR 0x3f8 +#define ASPEED_VUART_DEFAULT_SIRQ 4 +#define ASPEED_VUART_DEFAULT_SIRQ_POLARITY IRQ_TYPE_LEVEL_LOW + struct aspeed_vuart { struct device *dev; void __iomem *regs; @@ -72,22 +76,31 @@ static ssize_t lpc_address_show(struct device *dev, return snprintf(buf, PAGE_SIZE - 1, "0x%x\n", addr); } +static int aspeed_vuart_set_lpc_address(struct aspeed_vuart *vuart, u32 addr) +{ + if (addr > U16_MAX) + return -EINVAL; + + writeb(addr >> 8, vuart->regs + ASPEED_VUART_ADDRH); + writeb(addr >> 0, vuart->regs + ASPEED_VUART_ADDRL); + + return 0; +} + static ssize_t lpc_address_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct aspeed_vuart *vuart = dev_get_drvdata(dev); - unsigned long val; + u32 val; int err; - err = kstrtoul(buf, 0, &val); + err = kstrtou32(buf, 0, &val); if (err) return err; - writeb(val >> 8, vuart->regs + ASPEED_VUART_ADDRH); - writeb(val >> 0, vuart->regs + ASPEED_VUART_ADDRL); - - return count; + err = aspeed_vuart_set_lpc_address(vuart, val); + return err ? : count; } static DEVICE_ATTR_RW(lpc_address); @@ -105,27 +118,37 @@ static ssize_t sirq_show(struct device *dev, return snprintf(buf, PAGE_SIZE - 1, "%u\n", reg); } +static int aspeed_vuart_set_sirq(struct aspeed_vuart *vuart, u32 sirq) +{ + u8 reg; + + if (sirq > (ASPEED_VUART_GCRB_HOST_SIRQ_MASK >> ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT)) + return -EINVAL; + + sirq <<= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT; + sirq &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK; + + reg = readb(vuart->regs + ASPEED_VUART_GCRB); + reg &= ~ASPEED_VUART_GCRB_HOST_SIRQ_MASK; + reg |= sirq; + writeb(reg, vuart->regs + ASPEED_VUART_GCRB); + + return 0; +} + static ssize_t sirq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct aspeed_vuart *vuart = dev_get_drvdata(dev); unsigned long val; int err; - u8 reg; err = kstrtoul(buf, 0, &val); if (err) return err; - val <<= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT; - val &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK; - - reg = readb(vuart->regs + ASPEED_VUART_GCRB); - reg &= ~ASPEED_VUART_GCRB_HOST_SIRQ_MASK; - reg |= val; - writeb(reg, vuart->regs + ASPEED_VUART_GCRB); - - return count; + err = aspeed_vuart_set_sirq(vuart, val); + return err ? : count; } static DEVICE_ATTR_RW(sirq); @@ -297,7 +320,6 @@ static int aspeed_vuart_handle_irq(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); unsigned int iir, lsr; - unsigned long flags; int space, count; iir = serial_port_in(port, UART_IIR); @@ -305,7 +327,7 @@ static int aspeed_vuart_handle_irq(struct uart_port *port) if (iir & UART_IIR_NO_INT) return 0; - spin_lock_irqsave(&port->lock, flags); + spin_lock(&port->lock); lsr = serial_port_in(port, UART_LSR); @@ -341,7 +363,7 @@ static int aspeed_vuart_handle_irq(struct uart_port *port) if (lsr & UART_LSR_THRE) serial8250_tx_chars(up); - uart_unlock_and_check_sysrq(port, flags); + uart_unlock_and_check_sysrq(port); return 1; } @@ -367,6 +389,18 @@ static void aspeed_vuart_auto_configure_sirq_polarity( aspeed_vuart_set_sirq_polarity(vuart, (value & reg_mask) == 0); } +static int aspeed_vuart_map_irq_polarity(u32 dt) +{ + switch (dt) { + case IRQ_TYPE_LEVEL_LOW: + return 0; + case IRQ_TYPE_LEVEL_HIGH: + return 1; + default: + return -EINVAL; + } +} + static int aspeed_vuart_probe(struct platform_device *pdev) { struct of_phandle_args sirq_polarity_sense_args; @@ -374,8 +408,8 @@ static int aspeed_vuart_probe(struct platform_device *pdev) struct aspeed_vuart *vuart; struct device_node *np; struct resource *res; - u32 clk, prop; - int rc; + u32 clk, prop, sirq[2]; + int rc, sirq_polarity; np = pdev->dev.of_node; @@ -482,6 +516,37 @@ static int aspeed_vuart_probe(struct platform_device *pdev) of_node_put(sirq_polarity_sense_args.np); } + rc = of_property_read_u32(np, "aspeed,lpc-io-reg", &prop); + if (rc < 0) + prop = ASPEED_VUART_DEFAULT_LPC_ADDR; + + rc = aspeed_vuart_set_lpc_address(vuart, prop); + if (rc < 0) { + dev_err(&pdev->dev, "invalid value in aspeed,lpc-io-reg property\n"); + goto err_clk_disable; + } + + rc = of_property_read_u32_array(np, "aspeed,lpc-interrupts", sirq, 2); + if (rc < 0) { + sirq[0] = ASPEED_VUART_DEFAULT_SIRQ; + sirq[1] = ASPEED_VUART_DEFAULT_SIRQ_POLARITY; + } + + rc = aspeed_vuart_set_sirq(vuart, sirq[0]); + if (rc < 0) { + dev_err(&pdev->dev, "invalid sirq number in aspeed,lpc-interrupts property\n"); + goto err_clk_disable; + } + + sirq_polarity = aspeed_vuart_map_irq_polarity(sirq[1]); + if (sirq_polarity < 0) { + dev_err(&pdev->dev, "invalid sirq polarity in aspeed,lpc-interrupts property\n"); + rc = sirq_polarity; + goto err_clk_disable; + } + + aspeed_vuart_set_sirq_polarity(vuart, sirq_polarity); + aspeed_vuart_set_enabled(vuart, true); aspeed_vuart_set_host_tx_discard(vuart, true); platform_set_drvdata(pdev, vuart); diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c new file mode 100644 index 000000000000..725a450058f8 --- /dev/null +++ b/drivers/tty/serial/8250/8250_bcm7271.c @@ -0,0 +1,1202 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2020, Broadcom */ +/* + * 8250-core based driver for Broadcom ns16550a UARTs + * + * This driver uses the standard 8250 driver core but adds additional + * optional features including the ability to use a baud rate clock + * mux for more accurate high speed baud rate selection and also + * an optional DMA engine. + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/tty.h> +#include <linux/errno.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/dma-mapping.h> +#include <linux/tty_flip.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/debugfs.h> + +#include "8250.h" + +/* Register definitions for UART DMA block. Version 1.1 or later. */ +#define UDMA_ARB_RX 0x00 +#define UDMA_ARB_TX 0x04 +#define UDMA_ARB_REQ 0x00000001 +#define UDMA_ARB_GRANT 0x00000002 + +#define UDMA_RX_REVISION 0x00 +#define UDMA_RX_REVISION_REQUIRED 0x00000101 +#define UDMA_RX_CTRL 0x04 +#define UDMA_RX_CTRL_BUF_CLOSE_MODE 0x00010000 +#define UDMA_RX_CTRL_MASK_WR_DONE 0x00008000 +#define UDMA_RX_CTRL_ENDIAN_OVERRIDE 0x00004000 +#define UDMA_RX_CTRL_ENDIAN 0x00002000 +#define UDMA_RX_CTRL_OE_IS_ERR 0x00001000 +#define UDMA_RX_CTRL_PE_IS_ERR 0x00000800 +#define UDMA_RX_CTRL_FE_IS_ERR 0x00000400 +#define UDMA_RX_CTRL_NUM_BUF_USED_MASK 0x000003c0 +#define UDMA_RX_CTRL_NUM_BUF_USED_SHIFT 6 +#define UDMA_RX_CTRL_BUF_CLOSE_CLK_SEL_SYS 0x00000020 +#define UDMA_RX_CTRL_BUF_CLOSE_ENA 0x00000010 +#define UDMA_RX_CTRL_TIMEOUT_CLK_SEL_SYS 0x00000008 +#define UDMA_RX_CTRL_TIMEOUT_ENA 0x00000004 +#define UDMA_RX_CTRL_ABORT 0x00000002 +#define UDMA_RX_CTRL_ENA 0x00000001 +#define UDMA_RX_STATUS 0x08 +#define UDMA_RX_STATUS_ACTIVE_BUF_MASK 0x0000000f +#define UDMA_RX_TRANSFER_LEN 0x0c +#define UDMA_RX_TRANSFER_TOTAL 0x10 +#define UDMA_RX_BUFFER_SIZE 0x14 +#define UDMA_RX_SRC_ADDR 0x18 +#define UDMA_RX_TIMEOUT 0x1c +#define UDMA_RX_BUFFER_CLOSE 0x20 +#define UDMA_RX_BLOCKOUT_COUNTER 0x24 +#define UDMA_RX_BUF0_PTR_LO 0x28 +#define UDMA_RX_BUF0_PTR_HI 0x2c +#define UDMA_RX_BUF0_STATUS 0x30 +#define UDMA_RX_BUFX_STATUS_OVERRUN_ERR 0x00000010 +#define UDMA_RX_BUFX_STATUS_FRAME_ERR 0x00000008 +#define UDMA_RX_BUFX_STATUS_PARITY_ERR 0x00000004 +#define UDMA_RX_BUFX_STATUS_CLOSE_EXPIRED 0x00000002 +#define UDMA_RX_BUFX_STATUS_DATA_RDY 0x00000001 +#define UDMA_RX_BUF0_DATA_LEN 0x34 +#define UDMA_RX_BUF1_PTR_LO 0x38 +#define UDMA_RX_BUF1_PTR_HI 0x3c +#define UDMA_RX_BUF1_STATUS 0x40 +#define UDMA_RX_BUF1_DATA_LEN 0x44 + +#define UDMA_TX_REVISION 0x00 +#define UDMA_TX_REVISION_REQUIRED 0x00000101 +#define UDMA_TX_CTRL 0x04 +#define UDMA_TX_CTRL_ENDIAN_OVERRIDE 0x00000080 +#define UDMA_TX_CTRL_ENDIAN 0x00000040 +#define UDMA_TX_CTRL_NUM_BUF_USED_MASK 0x00000030 +#define UDMA_TX_CTRL_NUM_BUF_USED_1 0x00000010 +#define UDMA_TX_CTRL_ABORT 0x00000002 +#define UDMA_TX_CTRL_ENA 0x00000001 +#define UDMA_TX_DST_ADDR 0x08 +#define UDMA_TX_BLOCKOUT_COUNTER 0x10 +#define UDMA_TX_TRANSFER_LEN 0x14 +#define UDMA_TX_TRANSFER_TOTAL 0x18 +#define UDMA_TX_STATUS 0x20 +#define UDMA_TX_BUF0_PTR_LO 0x24 +#define UDMA_TX_BUF0_PTR_HI 0x28 +#define UDMA_TX_BUF0_STATUS 0x2c +#define UDMA_TX_BUFX_LAST 0x00000002 +#define UDMA_TX_BUFX_EMPTY 0x00000001 +#define UDMA_TX_BUF0_DATA_LEN 0x30 +#define UDMA_TX_BUF0_DATA_SENT 0x34 +#define UDMA_TX_BUF1_PTR_LO 0x38 + +#define UDMA_INTR_STATUS 0x00 +#define UDMA_INTR_ARB_TX_GRANT 0x00040000 +#define UDMA_INTR_ARB_RX_GRANT 0x00020000 +#define UDMA_INTR_TX_ALL_EMPTY 0x00010000 +#define UDMA_INTR_TX_EMPTY_BUF1 0x00008000 +#define UDMA_INTR_TX_EMPTY_BUF0 0x00004000 +#define UDMA_INTR_TX_ABORT 0x00002000 +#define UDMA_INTR_TX_DONE 0x00001000 +#define UDMA_INTR_RX_ERROR 0x00000800 +#define UDMA_INTR_RX_TIMEOUT 0x00000400 +#define UDMA_INTR_RX_READY_BUF7 0x00000200 +#define UDMA_INTR_RX_READY_BUF6 0x00000100 +#define UDMA_INTR_RX_READY_BUF5 0x00000080 +#define UDMA_INTR_RX_READY_BUF4 0x00000040 +#define UDMA_INTR_RX_READY_BUF3 0x00000020 +#define UDMA_INTR_RX_READY_BUF2 0x00000010 +#define UDMA_INTR_RX_READY_BUF1 0x00000008 +#define UDMA_INTR_RX_READY_BUF0 0x00000004 +#define UDMA_INTR_RX_READY_MASK 0x000003fc +#define UDMA_INTR_RX_READY_SHIFT 2 +#define UDMA_INTR_RX_ABORT 0x00000002 +#define UDMA_INTR_RX_DONE 0x00000001 +#define UDMA_INTR_SET 0x04 +#define UDMA_INTR_CLEAR 0x08 +#define UDMA_INTR_MASK_STATUS 0x0c +#define UDMA_INTR_MASK_SET 0x10 +#define UDMA_INTR_MASK_CLEAR 0x14 + + +#define UDMA_RX_INTERRUPTS ( \ + UDMA_INTR_RX_ERROR | \ + UDMA_INTR_RX_TIMEOUT | \ + UDMA_INTR_RX_READY_BUF0 | \ + UDMA_INTR_RX_READY_BUF1 | \ + UDMA_INTR_RX_READY_BUF2 | \ + UDMA_INTR_RX_READY_BUF3 | \ + UDMA_INTR_RX_READY_BUF4 | \ + UDMA_INTR_RX_READY_BUF5 | \ + UDMA_INTR_RX_READY_BUF6 | \ + UDMA_INTR_RX_READY_BUF7 | \ + UDMA_INTR_RX_ABORT | \ + UDMA_INTR_RX_DONE) + +#define UDMA_RX_ERR_INTERRUPTS ( \ + UDMA_INTR_RX_ERROR | \ + UDMA_INTR_RX_TIMEOUT | \ + UDMA_INTR_RX_ABORT | \ + UDMA_INTR_RX_DONE) + +#define UDMA_TX_INTERRUPTS ( \ + UDMA_INTR_TX_ABORT | \ + UDMA_INTR_TX_DONE) + +#define UDMA_IS_RX_INTERRUPT(status) ((status) & UDMA_RX_INTERRUPTS) +#define UDMA_IS_TX_INTERRUPT(status) ((status) & UDMA_TX_INTERRUPTS) + + +/* Current devices have 8 sets of RX buffer registers */ +#define UDMA_RX_BUFS_COUNT 8 +#define UDMA_RX_BUFS_REG_OFFSET (UDMA_RX_BUF1_PTR_LO - UDMA_RX_BUF0_PTR_LO) +#define UDMA_RX_BUFx_PTR_LO(x) (UDMA_RX_BUF0_PTR_LO + \ + ((x) * UDMA_RX_BUFS_REG_OFFSET)) +#define UDMA_RX_BUFx_PTR_HI(x) (UDMA_RX_BUF0_PTR_HI + \ + ((x) * UDMA_RX_BUFS_REG_OFFSET)) +#define UDMA_RX_BUFx_STATUS(x) (UDMA_RX_BUF0_STATUS + \ + ((x) * UDMA_RX_BUFS_REG_OFFSET)) +#define UDMA_RX_BUFx_DATA_LEN(x) (UDMA_RX_BUF0_DATA_LEN + \ + ((x) * UDMA_RX_BUFS_REG_OFFSET)) + +/* Current devices have 2 sets of TX buffer registers */ +#define UDMA_TX_BUFS_COUNT 2 +#define UDMA_TX_BUFS_REG_OFFSET (UDMA_TX_BUF1_PTR_LO - UDMA_TX_BUF0_PTR_LO) +#define UDMA_TX_BUFx_PTR_LO(x) (UDMA_TX_BUF0_PTR_LO + \ + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define UDMA_TX_BUFx_PTR_HI(x) (UDMA_TX_BUF0_PTR_HI + \ + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define UDMA_TX_BUFx_STATUS(x) (UDMA_TX_BUF0_STATUS + \ + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define UDMA_TX_BUFx_DATA_LEN(x) (UDMA_TX_BUF0_DATA_LEN + \ + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define UDMA_TX_BUFx_DATA_SENT(x) (UDMA_TX_BUF0_DATA_SENT + \ + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define REGS_8250 0 +#define REGS_DMA_RX 1 +#define REGS_DMA_TX 2 +#define REGS_DMA_ISR 3 +#define REGS_DMA_ARB 4 +#define REGS_MAX 5 + +#define TX_BUF_SIZE 4096 +#define RX_BUF_SIZE 4096 +#define RX_BUFS_COUNT 2 +#define KHZ 1000 +#define MHZ(x) ((x) * KHZ * KHZ) + +static const u32 brcmstb_rate_table[] = { + MHZ(81), + MHZ(108), + MHZ(64), /* Actually 64285715 for some chips */ + MHZ(48), +}; + +static const u32 brcmstb_rate_table_7278[] = { + MHZ(81), + MHZ(108), + 0, + MHZ(48), +}; + +struct brcmuart_priv { + int line; + struct clk *baud_mux_clk; + unsigned long default_mux_rate; + u32 real_rates[ARRAY_SIZE(brcmstb_rate_table)]; + const u32 *rate_table; + ktime_t char_wait; + struct uart_port *up; + struct hrtimer hrt; + bool shutdown; + bool dma_enabled; + struct uart_8250_dma dma; + void __iomem *regs[REGS_MAX]; + dma_addr_t rx_addr; + void *rx_bufs; + size_t rx_size; + int rx_next_buf; + dma_addr_t tx_addr; + void *tx_buf; + size_t tx_size; + bool tx_running; + bool rx_running; + struct dentry *debugfs_dir; + + /* stats exposed through debugfs */ + u64 dma_rx_partial_buf; + u64 dma_rx_full_buf; + u32 rx_bad_timeout_late_char; + u32 rx_bad_timeout_no_char; + u32 rx_missing_close_timeout; + u32 rx_err; + u32 rx_timeout; + u32 rx_abort; +}; + +static struct dentry *brcmuart_debugfs_root; + +/* + * Register access routines + */ +static u32 udma_readl(struct brcmuart_priv *priv, + int reg_type, int offset) +{ + return readl(priv->regs[reg_type] + offset); +} + +static void udma_writel(struct brcmuart_priv *priv, + int reg_type, int offset, u32 value) +{ + writel(value, priv->regs[reg_type] + offset); +} + +static void udma_set(struct brcmuart_priv *priv, + int reg_type, int offset, u32 bits) +{ + void __iomem *reg = priv->regs[reg_type] + offset; + u32 value; + + value = readl(reg); + value |= bits; + writel(value, reg); +} + +static void udma_unset(struct brcmuart_priv *priv, + int reg_type, int offset, u32 bits) +{ + void __iomem *reg = priv->regs[reg_type] + offset; + u32 value; + + value = readl(reg); + value &= ~bits; + writel(value, reg); +} + +/* + * The UART DMA engine hardware can be used by multiple UARTS, but + * only one at a time. Sharing is not currently supported so + * the first UART to request the DMA engine will get it and any + * subsequent requests by other UARTS will fail. + */ +static int brcmuart_arbitration(struct brcmuart_priv *priv, bool acquire) +{ + u32 rx_grant; + u32 tx_grant; + int waits; + int ret = 0; + + if (acquire) { + udma_set(priv, REGS_DMA_ARB, UDMA_ARB_RX, UDMA_ARB_REQ); + udma_set(priv, REGS_DMA_ARB, UDMA_ARB_TX, UDMA_ARB_REQ); + + waits = 1; + while (1) { + rx_grant = udma_readl(priv, REGS_DMA_ARB, UDMA_ARB_RX); + tx_grant = udma_readl(priv, REGS_DMA_ARB, UDMA_ARB_TX); + if (rx_grant & tx_grant & UDMA_ARB_GRANT) + return 0; + if (waits-- == 0) + break; + msleep(1); + } + ret = 1; + } + + udma_unset(priv, REGS_DMA_ARB, UDMA_ARB_RX, UDMA_ARB_REQ); + udma_unset(priv, REGS_DMA_ARB, UDMA_ARB_TX, UDMA_ARB_REQ); + return ret; +} + +static void brcmuart_init_dma_hardware(struct brcmuart_priv *priv) +{ + u32 daddr; + u32 value; + int x; + + /* Start with all interrupts disabled */ + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_MASK_SET, 0xffffffff); + + udma_writel(priv, REGS_DMA_RX, UDMA_RX_BUFFER_SIZE, RX_BUF_SIZE); + + /* + * Setup buffer close to happen when 32 character times have + * elapsed since the last character was received. + */ + udma_writel(priv, REGS_DMA_RX, UDMA_RX_BUFFER_CLOSE, 16*10*32); + value = (RX_BUFS_COUNT << UDMA_RX_CTRL_NUM_BUF_USED_SHIFT) + | UDMA_RX_CTRL_BUF_CLOSE_MODE + | UDMA_RX_CTRL_BUF_CLOSE_ENA; + udma_writel(priv, REGS_DMA_RX, UDMA_RX_CTRL, value); + + udma_writel(priv, REGS_DMA_RX, UDMA_RX_BLOCKOUT_COUNTER, 0); + daddr = priv->rx_addr; + for (x = 0; x < RX_BUFS_COUNT; x++) { + + /* Set RX transfer length to 0 for unknown */ + udma_writel(priv, REGS_DMA_RX, UDMA_RX_TRANSFER_LEN, 0); + + udma_writel(priv, REGS_DMA_RX, UDMA_RX_BUFx_PTR_LO(x), + lower_32_bits(daddr)); + udma_writel(priv, REGS_DMA_RX, UDMA_RX_BUFx_PTR_HI(x), + upper_32_bits(daddr)); + daddr += RX_BUF_SIZE; + } + + daddr = priv->tx_addr; + udma_writel(priv, REGS_DMA_TX, UDMA_TX_BUFx_PTR_LO(0), + lower_32_bits(daddr)); + udma_writel(priv, REGS_DMA_TX, UDMA_TX_BUFx_PTR_HI(0), + upper_32_bits(daddr)); + udma_writel(priv, REGS_DMA_TX, UDMA_TX_CTRL, + UDMA_TX_CTRL_NUM_BUF_USED_1); + + /* clear all interrupts then enable them */ + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_CLEAR, 0xffffffff); + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_MASK_CLEAR, + UDMA_RX_INTERRUPTS | UDMA_TX_INTERRUPTS); + +} + +static void start_rx_dma(struct uart_8250_port *p) +{ + struct brcmuart_priv *priv = p->port.private_data; + int x; + + udma_unset(priv, REGS_DMA_RX, UDMA_RX_CTRL, UDMA_RX_CTRL_ENA); + + /* Clear the RX ready bit for all buffers */ + for (x = 0; x < RX_BUFS_COUNT; x++) + udma_unset(priv, REGS_DMA_RX, UDMA_RX_BUFx_STATUS(x), + UDMA_RX_BUFX_STATUS_DATA_RDY); + + /* always start with buffer 0 */ + udma_unset(priv, REGS_DMA_RX, UDMA_RX_STATUS, + UDMA_RX_STATUS_ACTIVE_BUF_MASK); + priv->rx_next_buf = 0; + + udma_set(priv, REGS_DMA_RX, UDMA_RX_CTRL, UDMA_RX_CTRL_ENA); + priv->rx_running = true; +} + +static void stop_rx_dma(struct uart_8250_port *p) +{ + struct brcmuart_priv *priv = p->port.private_data; + + /* If RX is running, set the RX ABORT */ + if (priv->rx_running) + udma_set(priv, REGS_DMA_RX, UDMA_RX_CTRL, UDMA_RX_CTRL_ABORT); +} + +static int stop_tx_dma(struct uart_8250_port *p) +{ + struct brcmuart_priv *priv = p->port.private_data; + u32 value; + + /* If TX is running, set the TX ABORT */ + value = udma_readl(priv, REGS_DMA_TX, UDMA_TX_CTRL); + if (value & UDMA_TX_CTRL_ENA) + udma_set(priv, REGS_DMA_TX, UDMA_TX_CTRL, UDMA_TX_CTRL_ABORT); + priv->tx_running = false; + return 0; +} + +/* + * NOTE: printk's in this routine will hang the system if this is + * the console tty + */ +static int brcmuart_tx_dma(struct uart_8250_port *p) +{ + struct brcmuart_priv *priv = p->port.private_data; + struct circ_buf *xmit = &p->port.state->xmit; + u32 tx_size; + + if (uart_tx_stopped(&p->port) || priv->tx_running || + uart_circ_empty(xmit)) { + return 0; + } + tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + + priv->dma.tx_err = 0; + memcpy(priv->tx_buf, &xmit->buf[xmit->tail], tx_size); + xmit->tail += tx_size; + xmit->tail &= UART_XMIT_SIZE - 1; + p->port.icount.tx += tx_size; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&p->port); + + udma_writel(priv, REGS_DMA_TX, UDMA_TX_TRANSFER_LEN, tx_size); + udma_writel(priv, REGS_DMA_TX, UDMA_TX_BUF0_DATA_LEN, tx_size); + udma_unset(priv, REGS_DMA_TX, UDMA_TX_BUF0_STATUS, UDMA_TX_BUFX_EMPTY); + udma_set(priv, REGS_DMA_TX, UDMA_TX_CTRL, UDMA_TX_CTRL_ENA); + priv->tx_running = true; + + return 0; +} + +static void brcmuart_rx_buf_done_isr(struct uart_port *up, int index) +{ + struct brcmuart_priv *priv = up->private_data; + struct tty_port *tty_port = &up->state->port; + u32 status; + u32 length; + u32 copied; + + /* Make sure we're still in sync with the hardware */ + status = udma_readl(priv, REGS_DMA_RX, UDMA_RX_BUFx_STATUS(index)); + length = udma_readl(priv, REGS_DMA_RX, UDMA_RX_BUFx_DATA_LEN(index)); + + if ((status & UDMA_RX_BUFX_STATUS_DATA_RDY) == 0) { + dev_err(up->dev, "RX done interrupt but DATA_RDY not found\n"); + return; + } + if (status & (UDMA_RX_BUFX_STATUS_OVERRUN_ERR | + UDMA_RX_BUFX_STATUS_FRAME_ERR | + UDMA_RX_BUFX_STATUS_PARITY_ERR)) { + if (status & UDMA_RX_BUFX_STATUS_OVERRUN_ERR) { + up->icount.overrun++; + dev_warn(up->dev, "RX OVERRUN Error\n"); + } + if (status & UDMA_RX_BUFX_STATUS_FRAME_ERR) { + up->icount.frame++; + dev_warn(up->dev, "RX FRAMING Error\n"); + } + if (status & UDMA_RX_BUFX_STATUS_PARITY_ERR) { + up->icount.parity++; + dev_warn(up->dev, "RX PARITY Error\n"); + } + } + copied = (u32)tty_insert_flip_string( + tty_port, + priv->rx_bufs + (index * RX_BUF_SIZE), + length); + if (copied != length) { + dev_warn(up->dev, "Flip buffer overrun of %d bytes\n", + length - copied); + up->icount.overrun += length - copied; + } + up->icount.rx += length; + if (status & UDMA_RX_BUFX_STATUS_CLOSE_EXPIRED) + priv->dma_rx_partial_buf++; + else if (length != RX_BUF_SIZE) + /* + * This is a bug in the controller that doesn't cause + * any problems but will be fixed in the future. + */ + priv->rx_missing_close_timeout++; + else + priv->dma_rx_full_buf++; + + tty_flip_buffer_push(tty_port); +} + +static void brcmuart_rx_isr(struct uart_port *up, u32 rx_isr) +{ + struct brcmuart_priv *priv = up->private_data; + struct device *dev = up->dev; + u32 rx_done_isr; + u32 check_isr; + + rx_done_isr = (rx_isr & UDMA_INTR_RX_READY_MASK); + while (rx_done_isr) { + check_isr = UDMA_INTR_RX_READY_BUF0 << priv->rx_next_buf; + if (check_isr & rx_done_isr) { + brcmuart_rx_buf_done_isr(up, priv->rx_next_buf); + } else { + dev_err(dev, + "RX buffer ready out of sequence, restarting RX DMA\n"); + start_rx_dma(up_to_u8250p(up)); + break; + } + if (rx_isr & UDMA_RX_ERR_INTERRUPTS) { + if (rx_isr & UDMA_INTR_RX_ERROR) + priv->rx_err++; + if (rx_isr & UDMA_INTR_RX_TIMEOUT) { + priv->rx_timeout++; + dev_err(dev, "RX TIMEOUT Error\n"); + } + if (rx_isr & UDMA_INTR_RX_ABORT) + priv->rx_abort++; + priv->rx_running = false; + } + /* If not ABORT, re-enable RX buffer */ + if (!(rx_isr & UDMA_INTR_RX_ABORT)) + udma_unset(priv, REGS_DMA_RX, + UDMA_RX_BUFx_STATUS(priv->rx_next_buf), + UDMA_RX_BUFX_STATUS_DATA_RDY); + rx_done_isr &= ~check_isr; + priv->rx_next_buf++; + if (priv->rx_next_buf == RX_BUFS_COUNT) + priv->rx_next_buf = 0; + } +} + +static void brcmuart_tx_isr(struct uart_port *up, u32 isr) +{ + struct brcmuart_priv *priv = up->private_data; + struct device *dev = up->dev; + struct uart_8250_port *port_8250 = up_to_u8250p(up); + struct circ_buf *xmit = &port_8250->port.state->xmit; + + if (isr & UDMA_INTR_TX_ABORT) { + if (priv->tx_running) + dev_err(dev, "Unexpected TX_ABORT interrupt\n"); + return; + } + priv->tx_running = false; + if (!uart_circ_empty(xmit) && !uart_tx_stopped(up)) + brcmuart_tx_dma(port_8250); +} + +static irqreturn_t brcmuart_isr(int irq, void *dev_id) +{ + struct uart_port *up = dev_id; + struct device *dev = up->dev; + struct brcmuart_priv *priv = up->private_data; + unsigned long flags; + u32 interrupts; + u32 rval; + u32 tval; + + interrupts = udma_readl(priv, REGS_DMA_ISR, UDMA_INTR_STATUS); + if (interrupts == 0) + return IRQ_NONE; + + spin_lock_irqsave(&up->lock, flags); + + /* Clear all interrupts */ + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_CLEAR, interrupts); + + rval = UDMA_IS_RX_INTERRUPT(interrupts); + if (rval) + brcmuart_rx_isr(up, rval); + tval = UDMA_IS_TX_INTERRUPT(interrupts); + if (tval) + brcmuart_tx_isr(up, tval); + if ((rval | tval) == 0) + dev_warn(dev, "Spurious interrupt: 0x%x\n", interrupts); + + spin_unlock_irqrestore(&up->lock, flags); + return IRQ_HANDLED; +} + +static int brcmuart_startup(struct uart_port *port) +{ + int res; + struct uart_8250_port *up = up_to_u8250p(port); + struct brcmuart_priv *priv = up->port.private_data; + + priv->shutdown = false; + + /* + * prevent serial8250_do_startup() from allocating non-existent + * DMA resources + */ + up->dma = NULL; + + res = serial8250_do_startup(port); + if (!priv->dma_enabled) + return res; + /* + * Disable the Receive Data Interrupt because the DMA engine + * will handle this. + */ + up->ier &= ~UART_IER_RDI; + serial_port_out(port, UART_IER, up->ier); + + priv->tx_running = false; + priv->dma.rx_dma = NULL; + priv->dma.tx_dma = brcmuart_tx_dma; + up->dma = &priv->dma; + + brcmuart_init_dma_hardware(priv); + start_rx_dma(up); + return res; +} + +static void brcmuart_shutdown(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct brcmuart_priv *priv = up->port.private_data; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + priv->shutdown = true; + if (priv->dma_enabled) { + stop_rx_dma(up); + stop_tx_dma(up); + /* disable all interrupts */ + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_MASK_SET, + UDMA_RX_INTERRUPTS | UDMA_TX_INTERRUPTS); + } + + /* + * prevent serial8250_do_shutdown() from trying to free + * DMA resources that we never alloc'd for this driver. + */ + up->dma = NULL; + + spin_unlock_irqrestore(&port->lock, flags); + serial8250_do_shutdown(port); +} + +/* + * Not all clocks run at the exact specified rate, so set each requested + * rate and then get the actual rate. + */ +static void init_real_clk_rates(struct device *dev, struct brcmuart_priv *priv) +{ + int x; + int rc; + + priv->default_mux_rate = clk_get_rate(priv->baud_mux_clk); + for (x = 0; x < ARRAY_SIZE(priv->real_rates); x++) { + if (priv->rate_table[x] == 0) { + priv->real_rates[x] = 0; + continue; + } + rc = clk_set_rate(priv->baud_mux_clk, priv->rate_table[x]); + if (rc) { + dev_err(dev, "Error selecting BAUD MUX clock for %u\n", + priv->rate_table[x]); + priv->real_rates[x] = priv->rate_table[x]; + } else { + priv->real_rates[x] = clk_get_rate(priv->baud_mux_clk); + } + } + clk_set_rate(priv->baud_mux_clk, priv->default_mux_rate); +} + +static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv, + u32 baud) +{ + u32 percent; + u32 best_percent = UINT_MAX; + u32 quot; + u32 best_quot = 1; + u32 rate; + int best_index = -1; + u64 hires_rate; + u64 hires_baud; + u64 hires_err; + int rc; + int i; + int real_baud; + + /* If the Baud Mux Clock was not specified, just return */ + if (priv->baud_mux_clk == NULL) + return; + + /* Find the closest match for specified baud */ + for (i = 0; i < ARRAY_SIZE(priv->real_rates); i++) { + if (priv->real_rates[i] == 0) + continue; + rate = priv->real_rates[i] / 16; + quot = DIV_ROUND_CLOSEST(rate, baud); + if (!quot) + continue; + + /* increase resolution to get xx.xx percent */ + hires_rate = (u64)rate * 10000; + hires_baud = (u64)baud * 10000; + + hires_err = div_u64(hires_rate, (u64)quot); + + /* get the delta */ + if (hires_err > hires_baud) + hires_err = (hires_err - hires_baud); + else + hires_err = (hires_baud - hires_err); + + percent = (unsigned long)DIV_ROUND_CLOSEST_ULL(hires_err, baud); + dev_dbg(up->dev, + "Baud rate: %u, MUX Clk: %u, Error: %u.%u%%\n", + baud, priv->real_rates[i], percent / 100, + percent % 100); + if (percent < best_percent) { + best_percent = percent; + best_index = i; + best_quot = quot; + } + } + if (best_index == -1) { + dev_err(up->dev, "Error, %d BAUD rate is too fast.\n", baud); + return; + } + rate = priv->real_rates[best_index]; + rc = clk_set_rate(priv->baud_mux_clk, rate); + if (rc) + dev_err(up->dev, "Error selecting BAUD MUX clock\n"); + + /* Error over 3 percent will cause data errors */ + if (best_percent > 300) + dev_err(up->dev, "Error, baud: %d has %u.%u%% error\n", + baud, percent / 100, percent % 100); + + real_baud = rate / 16 / best_quot; + dev_dbg(up->dev, "Selecting BAUD MUX rate: %u\n", rate); + dev_dbg(up->dev, "Requested baud: %u, Actual baud: %u\n", + baud, real_baud); + + /* calc nanoseconds for 1.5 characters time at the given baud rate */ + i = NSEC_PER_SEC / real_baud / 10; + i += (i / 2); + priv->char_wait = ns_to_ktime(i); + + up->uartclk = rate; +} + +static void brcmstb_set_termios(struct uart_port *up, + struct ktermios *termios, + struct ktermios *old) +{ + struct uart_8250_port *p8250 = up_to_u8250p(up); + struct brcmuart_priv *priv = up->private_data; + + if (priv->dma_enabled) + stop_rx_dma(p8250); + set_clock_mux(up, priv, tty_termios_baud_rate(termios)); + serial8250_do_set_termios(up, termios, old); + if (p8250->mcr & UART_MCR_AFE) + p8250->port.status |= UPSTAT_AUTOCTS; + if (priv->dma_enabled) + start_rx_dma(p8250); +} + +static int brcmuart_handle_irq(struct uart_port *p) +{ + unsigned int iir = serial_port_in(p, UART_IIR); + struct brcmuart_priv *priv = p->private_data; + struct uart_8250_port *up = up_to_u8250p(p); + unsigned int status; + unsigned long flags; + unsigned int ier; + unsigned int mcr; + int handled = 0; + + /* + * There's a bug in some 8250 cores where we get a timeout + * interrupt but there is no data ready. + */ + if (((iir & UART_IIR_ID) == UART_IIR_RX_TIMEOUT) && !(priv->shutdown)) { + spin_lock_irqsave(&p->lock, flags); + status = serial_port_in(p, UART_LSR); + if ((status & UART_LSR_DR) == 0) { + + ier = serial_port_in(p, UART_IER); + /* + * if Receive Data Interrupt is enabled and + * we're uing hardware flow control, deassert + * RTS and wait for any chars in the pipline to + * arrive and then check for DR again. + */ + if ((ier & UART_IER_RDI) && (up->mcr & UART_MCR_AFE)) { + ier &= ~(UART_IER_RLSI | UART_IER_RDI); + serial_port_out(p, UART_IER, ier); + mcr = serial_port_in(p, UART_MCR); + mcr &= ~UART_MCR_RTS; + serial_port_out(p, UART_MCR, mcr); + hrtimer_start(&priv->hrt, priv->char_wait, + HRTIMER_MODE_REL); + } else { + serial_port_in(p, UART_RX); + } + + handled = 1; + } + spin_unlock_irqrestore(&p->lock, flags); + if (handled) + return 1; + } + return serial8250_handle_irq(p, iir); +} + +static enum hrtimer_restart brcmuart_hrtimer_func(struct hrtimer *t) +{ + struct brcmuart_priv *priv = container_of(t, struct brcmuart_priv, hrt); + struct uart_port *p = priv->up; + struct uart_8250_port *up = up_to_u8250p(p); + unsigned int status; + unsigned long flags; + + if (priv->shutdown) + return HRTIMER_NORESTART; + + spin_lock_irqsave(&p->lock, flags); + status = serial_port_in(p, UART_LSR); + + /* + * If a character did not arrive after the timeout, clear the false + * receive timeout. + */ + if ((status & UART_LSR_DR) == 0) { + serial_port_in(p, UART_RX); + priv->rx_bad_timeout_no_char++; + } else { + priv->rx_bad_timeout_late_char++; + } + + /* re-enable receive unless upper layer has disabled it */ + if ((up->ier & (UART_IER_RLSI | UART_IER_RDI)) == + (UART_IER_RLSI | UART_IER_RDI)) { + status = serial_port_in(p, UART_IER); + status |= (UART_IER_RLSI | UART_IER_RDI); + serial_port_out(p, UART_IER, status); + status = serial_port_in(p, UART_MCR); + status |= UART_MCR_RTS; + serial_port_out(p, UART_MCR, status); + } + spin_unlock_irqrestore(&p->lock, flags); + return HRTIMER_NORESTART; +} + +static const struct of_device_id brcmuart_dt_ids[] = { + { + .compatible = "brcm,bcm7278-uart", + .data = brcmstb_rate_table_7278, + }, + { + .compatible = "brcm,bcm7271-uart", + .data = brcmstb_rate_table, + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, brcmuart_dt_ids); + +static void brcmuart_free_bufs(struct device *dev, struct brcmuart_priv *priv) +{ + if (priv->rx_bufs) + dma_free_coherent(dev, priv->rx_size, priv->rx_bufs, + priv->rx_addr); + if (priv->tx_buf) + dma_free_coherent(dev, priv->tx_size, priv->tx_buf, + priv->tx_addr); +} + +static void brcmuart_throttle(struct uart_port *port) +{ + struct brcmuart_priv *priv = port->private_data; + + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_MASK_SET, UDMA_RX_INTERRUPTS); +} + +static void brcmuart_unthrottle(struct uart_port *port) +{ + struct brcmuart_priv *priv = port->private_data; + + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_MASK_CLEAR, + UDMA_RX_INTERRUPTS); +} + +static int debugfs_stats_show(struct seq_file *s, void *unused) +{ + struct brcmuart_priv *priv = s->private; + + seq_printf(s, "rx_err:\t\t\t\t%u\n", + priv->rx_err); + seq_printf(s, "rx_timeout:\t\t\t%u\n", + priv->rx_timeout); + seq_printf(s, "rx_abort:\t\t\t%u\n", + priv->rx_abort); + seq_printf(s, "rx_bad_timeout_late_char:\t%u\n", + priv->rx_bad_timeout_late_char); + seq_printf(s, "rx_bad_timeout_no_char:\t\t%u\n", + priv->rx_bad_timeout_no_char); + seq_printf(s, "rx_missing_close_timeout:\t%u\n", + priv->rx_missing_close_timeout); + if (priv->dma_enabled) { + seq_printf(s, "dma_rx_partial_buf:\t\t%llu\n", + priv->dma_rx_partial_buf); + seq_printf(s, "dma_rx_full_buf:\t\t%llu\n", + priv->dma_rx_full_buf); + } + return 0; +} +DEFINE_SHOW_ATTRIBUTE(debugfs_stats); + +static void brcmuart_init_debugfs(struct brcmuart_priv *priv, + const char *device) +{ + priv->debugfs_dir = debugfs_create_dir(device, brcmuart_debugfs_root); + debugfs_create_file("stats", 0444, priv->debugfs_dir, priv, + &debugfs_stats_fops); +} + + +static int brcmuart_probe(struct platform_device *pdev) +{ + struct resource *regs; + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id = NULL; + struct uart_8250_port *new_port; + struct device *dev = &pdev->dev; + struct brcmuart_priv *priv; + struct clk *baud_mux_clk; + struct uart_8250_port up; + struct resource *irq; + void __iomem *membase = 0; + resource_size_t mapbase = 0; + u32 clk_rate = 0; + int ret; + int x; + int dma_irq; + static const char * const reg_names[REGS_MAX] = { + "uart", "dma_rx", "dma_tx", "dma_intr2", "dma_arb" + }; + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(dev, "missing irq\n"); + return -EINVAL; + } + priv = devm_kzalloc(dev, sizeof(struct brcmuart_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + of_id = of_match_node(brcmuart_dt_ids, np); + if (!of_id || !of_id->data) + priv->rate_table = brcmstb_rate_table; + else + priv->rate_table = of_id->data; + + for (x = 0; x < REGS_MAX; x++) { + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, + reg_names[x]); + if (!regs) + break; + priv->regs[x] = devm_ioremap(dev, regs->start, + resource_size(regs)); + if (!priv->regs[x]) + return -ENOMEM; + if (x == REGS_8250) { + mapbase = regs->start; + membase = priv->regs[x]; + } + } + + /* We should have just the uart base registers or all the registers */ + if (x != 1 && x != REGS_MAX) { + dev_warn(dev, "%s registers not specified\n", reg_names[x]); + return -EINVAL; + } + + /* if the DMA registers were specified, try to enable DMA */ + if (x > REGS_DMA_RX) { + if (brcmuart_arbitration(priv, 1) == 0) { + u32 txrev = 0; + u32 rxrev = 0; + + txrev = udma_readl(priv, REGS_DMA_RX, UDMA_RX_REVISION); + rxrev = udma_readl(priv, REGS_DMA_TX, UDMA_TX_REVISION); + if ((txrev >= UDMA_TX_REVISION_REQUIRED) && + (rxrev >= UDMA_RX_REVISION_REQUIRED)) { + + /* Enable the use of the DMA hardware */ + priv->dma_enabled = true; + } else { + brcmuart_arbitration(priv, 0); + dev_err(dev, + "Unsupported DMA Hardware Revision\n"); + } + } else { + dev_err(dev, + "Timeout arbitrating for UART DMA hardware\n"); + } + } + + of_property_read_u32(np, "clock-frequency", &clk_rate); + + /* See if a Baud clock has been specified */ + baud_mux_clk = of_clk_get_by_name(np, "sw_baud"); + if (IS_ERR(baud_mux_clk)) { + if (PTR_ERR(baud_mux_clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_dbg(dev, "BAUD MUX clock not specified\n"); + } else { + dev_dbg(dev, "BAUD MUX clock found\n"); + ret = clk_prepare_enable(baud_mux_clk); + if (ret) + return ret; + priv->baud_mux_clk = baud_mux_clk; + init_real_clk_rates(dev, priv); + clk_rate = priv->default_mux_rate; + } + + if (clk_rate == 0) { + dev_err(dev, "clock-frequency or clk not defined\n"); + return -EINVAL; + } + + dev_dbg(dev, "DMA is %senabled\n", priv->dma_enabled ? "" : "not "); + + memset(&up, 0, sizeof(up)); + up.port.type = PORT_16550A; + up.port.uartclk = clk_rate; + up.port.dev = dev; + up.port.mapbase = mapbase; + up.port.membase = membase; + up.port.irq = irq->start; + up.port.handle_irq = brcmuart_handle_irq; + up.port.regshift = 2; + up.port.iotype = of_device_is_big_endian(np) ? + UPIO_MEM32BE : UPIO_MEM32; + up.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF + | UPF_FIXED_PORT | UPF_FIXED_TYPE; + up.port.dev = dev; + up.port.private_data = priv; + up.capabilities = UART_CAP_FIFO | UART_CAP_AFE; + up.port.fifosize = 32; + + /* Check for a fixed line number */ + ret = of_alias_get_id(np, "serial"); + if (ret >= 0) + up.port.line = ret; + + /* setup HR timer */ + hrtimer_init(&priv->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + priv->hrt.function = brcmuart_hrtimer_func; + + up.port.shutdown = brcmuart_shutdown; + up.port.startup = brcmuart_startup; + up.port.throttle = brcmuart_throttle; + up.port.unthrottle = brcmuart_unthrottle; + up.port.set_termios = brcmstb_set_termios; + + if (priv->dma_enabled) { + priv->rx_size = RX_BUF_SIZE * RX_BUFS_COUNT; + priv->rx_bufs = dma_alloc_coherent(dev, + priv->rx_size, + &priv->rx_addr, GFP_KERNEL); + if (!priv->rx_bufs) + goto err; + priv->tx_size = UART_XMIT_SIZE; + priv->tx_buf = dma_alloc_coherent(dev, + priv->tx_size, + &priv->tx_addr, GFP_KERNEL); + if (!priv->tx_buf) + goto err; + } + + ret = serial8250_register_8250_port(&up); + if (ret < 0) { + dev_err(dev, "unable to register 8250 port\n"); + goto err; + } + priv->line = ret; + new_port = serial8250_get_port(ret); + priv->up = &new_port->port; + if (priv->dma_enabled) { + dma_irq = platform_get_irq_byname(pdev, "dma"); + if (dma_irq < 0) { + dev_err(dev, "no IRQ resource info\n"); + goto err1; + } + ret = devm_request_irq(dev, dma_irq, brcmuart_isr, + IRQF_SHARED, "uart DMA irq", &new_port->port); + if (ret) { + dev_err(dev, "unable to register IRQ handler\n"); + goto err1; + } + } + platform_set_drvdata(pdev, priv); + brcmuart_init_debugfs(priv, dev_name(&pdev->dev)); + return 0; + +err1: + serial8250_unregister_port(priv->line); +err: + brcmuart_free_bufs(dev, priv); + brcmuart_arbitration(priv, 0); + return -ENODEV; +} + +static int brcmuart_remove(struct platform_device *pdev) +{ + struct brcmuart_priv *priv = platform_get_drvdata(pdev); + + debugfs_remove_recursive(priv->debugfs_dir); + hrtimer_cancel(&priv->hrt); + serial8250_unregister_port(priv->line); + brcmuart_free_bufs(&pdev->dev, priv); + brcmuart_arbitration(priv, 0); + return 0; +} + +static int __maybe_unused brcmuart_suspend(struct device *dev) +{ + struct brcmuart_priv *priv = dev_get_drvdata(dev); + + serial8250_suspend_port(priv->line); + clk_disable_unprepare(priv->baud_mux_clk); + + return 0; +} + +static int __maybe_unused brcmuart_resume(struct device *dev) +{ + struct brcmuart_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(priv->baud_mux_clk); + if (ret) + dev_err(dev, "Error enabling BAUD MUX clock\n"); + + /* + * The hardware goes back to it's default after suspend + * so get the "clk" back in sync. + */ + ret = clk_set_rate(priv->baud_mux_clk, priv->default_mux_rate); + if (ret) + dev_err(dev, "Error restoring default BAUD MUX clock\n"); + if (priv->dma_enabled) { + if (brcmuart_arbitration(priv, 1)) { + dev_err(dev, "Timeout arbitrating for DMA hardware on resume\n"); + return(-EBUSY); + } + brcmuart_init_dma_hardware(priv); + start_rx_dma(serial8250_get_port(priv->line)); + } + serial8250_resume_port(priv->line); + return 0; +} + +static const struct dev_pm_ops brcmuart_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(brcmuart_suspend, brcmuart_resume) +}; + +static struct platform_driver brcmuart_platform_driver = { + .driver = { + .name = "bcm7271-uart", + .pm = &brcmuart_dev_pm_ops, + .of_match_table = brcmuart_dt_ids, + }, + .probe = brcmuart_probe, + .remove = brcmuart_remove, +}; + +static int __init brcmuart_init(void) +{ + brcmuart_debugfs_root = debugfs_create_dir( + brcmuart_platform_driver.driver.name, NULL); + return platform_driver_register(&brcmuart_platform_driver); +} +module_init(brcmuart_init); + +static void __exit brcmuart_deinit(void) +{ + platform_driver_unregister(&brcmuart_platform_driver); + debugfs_remove_recursive(brcmuart_debugfs_root); +} +module_exit(brcmuart_deinit); + +MODULE_AUTHOR("Al Cooper"); +MODULE_DESCRIPTION("Broadcom NS16550A compatible serial port driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 2d0e7c7e408d..2f49c580139b 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -354,7 +354,7 @@ static void setup_gpio(struct pci_dev *pcidev, u8 __iomem *p) static void * __xr17v35x_register_gpio(struct pci_dev *pcidev, - const struct property_entry *properties) + const struct software_node *node) { struct platform_device *pdev; @@ -365,7 +365,7 @@ __xr17v35x_register_gpio(struct pci_dev *pcidev, pdev->dev.parent = &pcidev->dev; ACPI_COMPANION_SET(&pdev->dev, ACPI_COMPANION(&pcidev->dev)); - if (platform_device_add_properties(pdev, properties) < 0 || + if (device_add_software_node(&pdev->dev, node) < 0 || platform_device_add(pdev) < 0) { platform_device_put(pdev); return NULL; @@ -380,12 +380,16 @@ static const struct property_entry exar_gpio_properties[] = { { } }; +static const struct software_node exar_gpio_node = { + .properties = exar_gpio_properties, +}; + static int xr17v35x_register_gpio(struct pci_dev *pcidev, struct uart_8250_port *port) { if (pcidev->vendor == PCI_VENDOR_ID_EXAR) port->port.private_data = - __xr17v35x_register_gpio(pcidev, exar_gpio_properties); + __xr17v35x_register_gpio(pcidev, &exar_gpio_node); return 0; } @@ -457,6 +461,10 @@ static const struct property_entry iot2040_gpio_properties[] = { { } }; +static const struct software_node iot2040_gpio_node = { + .properties = iot2040_gpio_properties, +}; + static int iot2040_register_gpio(struct pci_dev *pcidev, struct uart_8250_port *port) { @@ -468,7 +476,7 @@ static int iot2040_register_gpio(struct pci_dev *pcidev, writeb(IOT2040_UARTS_GPIO_HI_MODE, p + UART_EXAR_MPIOSEL_15_8); port->port.private_data = - __xr17v35x_register_gpio(pcidev, iot2040_gpio_properties); + __xr17v35x_register_gpio(pcidev, &iot2040_gpio_node); return 0; } @@ -547,6 +555,7 @@ static void pci_xr17v35x_exit(struct pci_dev *pcidev) struct uart_8250_port *port = serial8250_get_port(priv->line[0]); struct platform_device *pdev = port->port.private_data; + device_remove_software_node(&pdev->dev); platform_device_unregister(pdev); port->port.private_data = NULL; } diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c index fbcc90c31ca1..4e75d2e4f87c 100644 --- a/drivers/tty/serial/8250/8250_fsl.c +++ b/drivers/tty/serial/8250/8250_fsl.c @@ -30,15 +30,14 @@ struct fsl8250_data { int fsl8250_handle_irq(struct uart_port *port) { unsigned char lsr, orig_lsr; - unsigned long flags; unsigned int iir; struct uart_8250_port *up = up_to_u8250p(port); - spin_lock_irqsave(&up->port.lock, flags); + spin_lock(&up->port.lock); iir = port->serial_in(port, UART_IIR); if (iir & UART_IIR_NO_INT) { - spin_unlock_irqrestore(&up->port.lock, flags); + spin_unlock(&up->port.lock); return 0; } @@ -46,7 +45,7 @@ int fsl8250_handle_irq(struct uart_port *port) if (unlikely(up->lsr_saved_flags & UART_LSR_BI)) { up->lsr_saved_flags &= ~UART_LSR_BI; port->serial_in(port, UART_RX); - spin_unlock_irqrestore(&up->port.lock, flags); + spin_unlock(&up->port.lock); return 1; } @@ -82,7 +81,9 @@ int fsl8250_handle_irq(struct uart_port *port) serial8250_tx_chars(up); up->lsr_saved_flags = orig_lsr; - uart_unlock_and_check_sysrq(&up->port, flags); + + uart_unlock_and_check_sysrq(&up->port); + return 1; } EXPORT_SYMBOL_GPL(fsl8250_handle_irq); @@ -104,11 +105,8 @@ static int fsl8250_acpi_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - if (irq != -EPROBE_DEFER) - dev_err(dev, "cannot get irq\n"); + if (irq < 0) return irq; - } memset(&port8250, 0, sizeof(port8250)); diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 65e9045dafe6..0b077b45d6a9 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -318,6 +318,7 @@ static const struct of_device_id of_platform_serial_table[] = { { .compatible = "mrvl,mmp-uart", .data = (void *)PORT_XSCALE, }, { .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, }, + { .compatible = "nuvoton,wpcm450-uart", .data = (void *)PORT_NPCM, }, { .compatible = "nuvoton,npcm750-uart", .data = (void *)PORT_NPCM, }, { /* end of list */ }, }; diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 23e0decde33e..8ac11eaeca51 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1143,7 +1143,6 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) struct uart_8250_port *up = up_to_u8250p(port); struct omap8250_priv *priv = up->port.private_data; unsigned char status; - unsigned long flags; u8 iir; serial8250_rpm_get(up); @@ -1154,7 +1153,7 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) return IRQ_HANDLED; } - spin_lock_irqsave(&port->lock, flags); + spin_lock(&port->lock); status = serial_port_in(port, UART_LSR); @@ -1179,7 +1178,8 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) } } - uart_unlock_and_check_sysrq(port, flags); + uart_unlock_and_check_sysrq(port); + serial8250_rpm_put(up); return 1; } diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index b0af13074cd3..d45dab1ab316 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1466,13 +1466,11 @@ EXPORT_SYMBOL_GPL(serial8250_em485_stop_tx); static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t) { - struct uart_8250_em485 *em485; - struct uart_8250_port *p; + struct uart_8250_em485 *em485 = container_of(t, struct uart_8250_em485, + stop_tx_timer); + struct uart_8250_port *p = em485->port; unsigned long flags; - em485 = container_of(t, struct uart_8250_em485, stop_tx_timer); - p = em485->port; - serial8250_rpm_get(p); spin_lock_irqsave(&p->port.lock, flags); if (em485->active_timer == &em485->stop_tx_timer) { @@ -1482,16 +1480,13 @@ static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t) } spin_unlock_irqrestore(&p->port.lock, flags); serial8250_rpm_put(p); + return HRTIMER_NORESTART; } static void start_hrtimer_ms(struct hrtimer *hrt, unsigned long msec) { - long sec = msec / 1000; - long nsec = (msec % 1000) * 1000000; - ktime_t t = ktime_set(sec, nsec); - - hrtimer_start(hrt, t, HRTIMER_MODE_REL); + hrtimer_start(hrt, ms_to_ktime(msec), HRTIMER_MODE_REL); } static void __stop_tx_rs485(struct uart_8250_port *p) @@ -1633,19 +1628,18 @@ static inline void start_tx_rs485(struct uart_port *port) static enum hrtimer_restart serial8250_em485_handle_start_tx(struct hrtimer *t) { - struct uart_8250_em485 *em485; - struct uart_8250_port *p; + struct uart_8250_em485 *em485 = container_of(t, struct uart_8250_em485, + start_tx_timer); + struct uart_8250_port *p = em485->port; unsigned long flags; - em485 = container_of(t, struct uart_8250_em485, start_tx_timer); - p = em485->port; - spin_lock_irqsave(&p->port.lock, flags); if (em485->active_timer == &em485->start_tx_timer) { __start_tx(&p->port); em485->active_timer = NULL; } spin_unlock_irqrestore(&p->port.lock, flags); + return HRTIMER_NORESTART; } @@ -1885,14 +1879,13 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) int serial8250_handle_irq(struct uart_port *port, unsigned int iir) { unsigned char status; - unsigned long flags; struct uart_8250_port *up = up_to_u8250p(port); bool skip_rx = false; if (iir & UART_IIR_NO_INT) return 0; - spin_lock_irqsave(&port->lock, flags); + spin_lock(&port->lock); status = serial_port_in(port, UART_LSR); @@ -1918,7 +1911,8 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) (up->ier & UART_IER_THRI)) serial8250_tx_chars(up); - uart_unlock_and_check_sysrq(port, flags); + uart_unlock_and_check_sysrq(port); + return 1; } EXPORT_SYMBOL_GPL(serial8250_handle_irq); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 603137da4736..d1b3c2373fa4 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -15,8 +15,7 @@ config SERIAL_8250 here are those that are setting up dedicated Ethernet WWW/FTP servers, or users that have one of the various bus mice instead of a serial mouse and don't intend to use their machine's standard serial - port for anything. (Note that the Cyclades multi serial port driver - does not need this driver built in for it to work.) + port for anything. To compile this driver as a module, choose M here: the module will be called 8250. @@ -226,7 +225,7 @@ config SERIAL_8250_MANY_PORTS serial port hardware which acts similar to standard serial port hardware. If you only use the standard COM 1/2/3/4 ports, you can say N here to save some memory. You can also say Y if you have an - "intelligent" multiport card such as Cyclades, Digiboards, etc. + "intelligent" multiport card such as Digiboards, etc. # # Multi-port serial cards @@ -404,7 +403,8 @@ config SERIAL_8250_RT288X config SERIAL_8250_OMAP tristate "Support for OMAP internal UART (8250 based driver)" - depends on SERIAL_8250 && (ARCH_OMAP2PLUS || ARCH_K3) + depends on SERIAL_8250 + depends on ARCH_OMAP2PLUS || ARCH_K3 || COMPILE_TEST help If you have a machine based on an Texas Instruments OMAP CPU you can enable its onboard serial ports by enabling this option. @@ -440,7 +440,8 @@ config SERIAL_8250_LPC18XX config SERIAL_8250_MT6577 tristate "Mediatek serial port support" - depends on SERIAL_8250 && ARCH_MEDIATEK + depends on SERIAL_8250 + depends on ARCH_MEDIATEK || COMPILE_TEST help If you have a Mediatek based board and want to use the serial port, say Y to this option. If unsure, say N. @@ -510,6 +511,16 @@ config SERIAL_8250_TEGRA Select this option if you have machine with an NVIDIA Tegra SoC and wish to enable 8250 serial driver for the Tegra serial interfaces. +config SERIAL_8250_BCM7271 + tristate "Broadcom 8250 based serial port" + depends on SERIAL_8250 && (ARCH_BRCMSTB || COMPILE_TEST) + default ARCH_BRCMSTB + help + If you have a Broadcom STB based board and want to use the + enhanced features of the Broadcom 8250 based serial port, + including DMA support and high accuracy BAUD rates, say + Y to this option. If unsure, say N. + config SERIAL_OF_PLATFORM tristate "Devicetree based probing for 8250 ports" depends on SERIAL_8250 && OF diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index a8bfb654d490..b9bcd73c8997 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_SERIAL_8250_LPSS) += 8250_lpss.o obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o obj-$(CONFIG_SERIAL_8250_PXA) += 8250_pxa.o obj-$(CONFIG_SERIAL_8250_TEGRA) += 8250_tegra.o +obj-$(CONFIG_SERIAL_8250_BCM7271) += 8250_bcm7271.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += 8250_of.o CFLAGS_8250_ingenic.o += -I$(srctree)/scripts/dtc/libfdt diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c index 35ff6627c61b..63ea9c4da3d5 100644 --- a/drivers/tty/serial/8250/serial_cs.c +++ b/drivers/tty/serial/8250/serial_cs.c @@ -456,11 +456,11 @@ static int simple_config(struct pcmcia_device *link) * its base address, then try to grab any standard serial port * address, and finally try to get any free port. */ - if (!pcmcia_loop_config(link, simple_config_check_notpicky, NULL)) - goto found_port; - - dev_warn(&link->dev, "no usable port range found, giving up\n"); - return -1; + ret = pcmcia_loop_config(link, simple_config_check_notpicky, NULL); + if (ret) { + dev_warn(&link->dev, "no usable port range found, giving up\n"); + return ret; + } found_port: if (info->multi && (info->manfid == MANFID_3COM)) @@ -474,7 +474,7 @@ found_port: ret = pcmcia_enable_device(link); if (ret != 0) - return -1; + return ret; return setup_serial(link, info, link->resource[0]->start, link->irq); } diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 0c4cd4a348f4..682f9171c82c 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -20,7 +20,7 @@ comment "Non-8250 serial port support" config SERIAL_AMBA_PL010 tristate "ARM AMBA PL010 serial port support" - depends on ARM_AMBA + depends on ARM_AMBA || COMPILE_TEST select SERIAL_CORE help This selects the ARM(R) AMBA(R) PrimeCell PL010 UART. If you have @@ -198,7 +198,7 @@ config SERIAL_KGDB_NMI config SERIAL_MESON tristate "Meson serial port support" - depends on ARCH_MESON + depends on ARCH_MESON || COMPILE_TEST select SERIAL_CORE help This enables the driver for the on-chip UARTs of the Amlogic @@ -236,7 +236,7 @@ config SERIAL_CLPS711X_CONSOLE config SERIAL_SAMSUNG tristate "Samsung SoC serial support" - depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST + depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || ARCH_APPLE || COMPILE_TEST select SERIAL_CORE help Support for the on-chip UARTs on the Samsung S3C24XX series CPUs, @@ -278,7 +278,7 @@ config SERIAL_SAMSUNG_CONSOLE config SERIAL_TEGRA tristate "NVIDIA Tegra20/30 SoC serial controller" - depends on ARCH_TEGRA && TEGRA20_APB_DMA + depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST select SERIAL_CORE help Support for the on-chip UARTs on the NVIDIA Tegra series SOCs @@ -289,7 +289,8 @@ config SERIAL_TEGRA config SERIAL_TEGRA_TCU tristate "NVIDIA Tegra Combined UART" - depends on ARCH_TEGRA && TEGRA_HSP_MBOX + depends on MAILBOX + depends on (ARCH_TEGRA && TEGRA_HSP_MBOX) || COMPILE_TEST select SERIAL_CORE help Support for the mailbox-based TCU (Tegra Combined UART) serial port. @@ -498,6 +499,7 @@ config SERIAL_IMX_EARLYCON bool "Earlycon on IMX serial port" depends on ARCH_MXC || COMPILE_TEST depends on OF + select SERIAL_CORE select SERIAL_EARLYCON select SERIAL_CORE_CONSOLE default y if SERIAL_IMX_CONSOLE @@ -851,7 +853,8 @@ config SERIAL_MPC52xx_CONSOLE_BAUD config SERIAL_ICOM tristate "IBM Multiport Serial Adapter" - depends on PCI && PPC_PSERIES + depends on PCI + depends on PPC_PSERIES || COMPILE_TEST select SERIAL_CORE select FW_LOADER help @@ -920,7 +923,7 @@ config SERIAL_JSM config SERIAL_MSM tristate "MSM on-chip serial port support" - depends on ARCH_QCOM + depends on ARCH_QCOM || COMPILE_TEST select SERIAL_CORE config SERIAL_MSM_CONSOLE @@ -946,7 +949,7 @@ config SERIAL_QCOM_GENI_CONSOLE config SERIAL_VT8500 bool "VIA VT8500 on-chip serial port support" - depends on ARCH_VT8500 + depends on ARCH_VT8500 || COMPILE_TEST select SERIAL_CORE config SERIAL_VT8500_CONSOLE @@ -956,7 +959,7 @@ config SERIAL_VT8500_CONSOLE config SERIAL_OMAP tristate "OMAP serial port support" - depends on ARCH_OMAP2PLUS + depends on ARCH_OMAP2PLUS || COMPILE_TEST select SERIAL_CORE help If you have a machine based on an Texas Instruments OMAP CPU you diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index d0ca9cf29b62..23c4e0e79694 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -131,9 +131,7 @@ static void altera_jtaguart_rx_chars(struct altera_jtaguart *pp) uart_insert_char(port, 0, 0, ch, flag); } - spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); } static void altera_jtaguart_tx_chars(struct altera_jtaguart *pp) diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 0e487ce091ac..7c5f4e966b59 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -243,9 +243,7 @@ static void altera_uart_rx_chars(struct altera_uart *pp) flag); } - spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); } static void altera_uart_tx_chars(struct altera_uart *pp) diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c index 3f96edfe569c..e744b953ca34 100644 --- a/drivers/tty/serial/amba-pl010.c +++ b/drivers/tty/serial/amba-pl010.c @@ -159,9 +159,7 @@ static void pl010_rx_chars(struct uart_amba_port *uap) ignore_char: status = readb(uap->port.membase + UART01x_FR); } - spin_unlock(&uap->port.lock); tty_flip_buffer_push(&uap->port.state->port); - spin_lock(&uap->port.lock); } static void pl010_tx_chars(struct uart_amba_port *uap) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 4ead0c9048a8..78682c12156a 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -937,12 +937,10 @@ static void pl011_dma_rx_chars(struct uart_amba_port *uap, fifotaken = pl011_fifo_to_tty(uap); } - spin_unlock(&uap->port.lock); dev_vdbg(uap->port.dev, "Took %d chars from DMA buffer and %d chars from the FIFO\n", dma_count, fifotaken); tty_flip_buffer_push(port); - spin_lock(&uap->port.lock); } static void pl011_dma_rx_irq(struct uart_amba_port *uap) diff --git a/drivers/tty/serial/apbuart.c b/drivers/tty/serial/apbuart.c index e8d56e899ec7..d8c937bdf3f9 100644 --- a/drivers/tty/serial/apbuart.c +++ b/drivers/tty/serial/apbuart.c @@ -117,9 +117,7 @@ static void apbuart_rx_chars(struct uart_port *port) status = UART_GET_STATUS(port); } - spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); } static void apbuart_tx_chars(struct uart_port *port) diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index c2be7cf91399..4379ca4842ae 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -385,9 +385,7 @@ static void ar933x_uart_rx_chars(struct ar933x_uart_port *up) tty_insert_flip_char(port, ch, TTY_NORMAL); } while (max_count-- > 0); - spin_unlock(&up->port.lock); tty_flip_buffer_push(port); - spin_lock(&up->port.lock); } static void ar933x_uart_tx_chars(struct ar933x_uart_port *up) diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index 17c3fc398fc6..1a9444b6b57e 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -236,9 +236,7 @@ static void arc_serial_rx_chars(struct uart_port *port, unsigned int status) if (!(uart_handle_sysrq_char(port, ch))) uart_insert_char(port, status, RXOERR, ch, flg); - spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); } while (!((status = UART_GET_STATUS(port)) & RXEMPTY)); } diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index a24e5c2b30bc..058886d9045b 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1178,13 +1178,7 @@ static void atmel_rx_from_dma(struct uart_port *port) 1, DMA_FROM_DEVICE); - /* - * Drop the lock here since it might end up calling - * uart_start(), which takes the lock. - */ - spin_unlock(&port->lock); tty_flip_buffer_push(tport); - spin_lock(&port->lock); atmel_uart_writel(port, ATMEL_US_IER, ATMEL_US_TIMEOUT); } @@ -1576,13 +1570,7 @@ static void atmel_rx_from_ring(struct uart_port *port) uart_insert_char(port, status, ATMEL_US_OVRE, c.ch, flg); } - /* - * Drop the lock here since it might end up calling - * uart_start(), which takes the lock. - */ - spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); } static void atmel_release_rx_pdc(struct uart_port *port) @@ -1667,13 +1655,7 @@ static void atmel_rx_from_pdc(struct uart_port *port) } } while (head >= pdc->dma_size); - /* - * Drop the lock here since it might end up calling - * uart_start(), which takes the lock. - */ - spin_unlock(&port->lock); tty_flip_buffer_push(tport); - spin_lock(&port->lock); atmel_uart_writel(port, ATMEL_US_IER, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index 5674da2b76f0..5fb0e84f7fd1 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -294,9 +294,7 @@ static void bcm_uart_do_rx(struct uart_port *port) } while (--max_count); - spin_unlock(&port->lock); tty_flip_buffer_push(tty_port); - spin_lock(&port->lock); } /* diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index 94af7a5ea497..9e9abfc4824a 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -829,9 +829,7 @@ ignore_char: } icom_port->next_rcv = rcv_buff; - spin_unlock(&icom_port->uart_port.lock); tty_flip_buffer_push(port); - spin_lock(&icom_port->uart_port.lock); } static void process_interrupt(u16 port_int_reg, diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 8257597d034d..7d5a8dfa3e91 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -394,11 +394,7 @@ static void imx_uart_rts_inactive(struct imx_port *sport, u32 *ucr2) static void start_hrtimer_ms(struct hrtimer *hrt, unsigned long msec) { - long sec = msec / MSEC_PER_SEC; - long nsec = (msec % MSEC_PER_SEC) * 1000000; - ktime_t t = ktime_set(sec, nsec); - - hrtimer_start(hrt, t, HRTIMER_MODE_REL); + hrtimer_start(hrt, ms_to_ktime(msec), HRTIMER_MODE_REL); } /* called with port.lock taken and irqs off */ @@ -922,14 +918,8 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id) struct imx_port *sport = dev_id; unsigned int usr1, usr2, ucr1, ucr2, ucr3, ucr4; irqreturn_t ret = IRQ_NONE; - unsigned long flags = 0; - /* - * IRQs might not be disabled upon entering this interrupt handler, - * e.g. when interrupt handlers are forced to be threaded. To support - * this scenario as well, disable IRQs when acquiring the spinlock. - */ - spin_lock_irqsave(&sport->port.lock, flags); + spin_lock(&sport->port.lock); usr1 = imx_uart_readl(sport, USR1); usr2 = imx_uart_readl(sport, USR2); @@ -999,7 +989,7 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id) ret = IRQ_HANDLED; } - spin_unlock_irqrestore(&sport->port.lock, flags); + spin_unlock(&sport->port.lock); return ret; } diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index 512b77195e9f..8e42a7682c63 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -603,18 +603,22 @@ void jsm_input(struct jsm_channel *ch) if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) { for (i = 0; i < s; i++) { + u8 chr = ch->ch_rqueue[tail + i]; + u8 error = ch->ch_equeue[tail + i]; + char flag = TTY_NORMAL; + /* - * Give the Linux ld the flags in the - * format it likes. + * Give the Linux ld the flags in the format it + * likes. */ - if (*(ch->ch_equeue + tail + i) & UART_LSR_BI) - tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_BREAK); - else if (*(ch->ch_equeue +tail +i) & UART_LSR_PE) - tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_PARITY); - else if (*(ch->ch_equeue +tail +i) & UART_LSR_FE) - tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_FRAME); - else - tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_NORMAL); + if (error & UART_LSR_BI) + flag = TTY_BREAK; + else if (error & UART_LSR_PE) + flag = TTY_PARITY; + else if (error & UART_LSR_FE) + flag = TTY_FRAME; + + tty_insert_flip_char(port, chr, flag); } } else { tty_insert_flip_string(port, ch->ch_rqueue + tail, s); diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c index 6004c0c1d173..db059b66438e 100644 --- a/drivers/tty/serial/kgdb_nmi.c +++ b/drivers/tty/serial/kgdb_nmi.c @@ -373,9 +373,7 @@ int kgdb_unregister_nmi_console(void) if (ret) return ret; - ret = tty_unregister_driver(kgdb_nmi_tty_driver); - if (ret) - return ret; + tty_unregister_driver(kgdb_nmi_tty_driver); put_tty_driver(kgdb_nmi_tty_driver); return 0; diff --git a/drivers/tty/serial/liteuart.c b/drivers/tty/serial/liteuart.c index 64842f3539e1..0b06770642cb 100644 --- a/drivers/tty/serial/liteuart.c +++ b/drivers/tty/serial/liteuart.c @@ -270,8 +270,8 @@ static int liteuart_probe(struct platform_device *pdev) /* get membase */ port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); - if (!port->membase) - return -ENXIO; + if (IS_ERR(port->membase)) + return PTR_ERR(port->membase); /* values not from device tree */ port->dev = &pdev->dev; diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c index 1fa098d7aec4..b199d7859961 100644 --- a/drivers/tty/serial/lpc32xx_hs.c +++ b/drivers/tty/serial/lpc32xx_hs.c @@ -273,9 +273,7 @@ static void __serial_lpc32xx_rx(struct uart_port *port) tmp = readl(LPC32XX_HSUART_FIFO(port->membase)); } - spin_unlock(&port->lock); tty_flip_buffer_push(tport); - spin_lock(&port->lock); } static void __serial_lpc32xx_tx(struct uart_port *port) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 1b61d26bb7af..8534d6e45a1d 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -273,7 +273,7 @@ struct max310x_port { #ifdef CONFIG_GPIOLIB struct gpio_chip gpio; #endif - struct max310x_one p[0]; + struct max310x_one p[]; }; static struct uart_driver max310x_uart = { diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index 09c88c48fb7b..c7cec7d03620 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -319,9 +319,7 @@ static void mcf_rx_chars(struct mcf_uart *pp) uart_insert_char(port, status, MCFUART_USR_RXOVERRUN, ch, flag); } - spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); } /****************************************************************************/ diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c index 69eeef9edfa5..529cd0289056 100644 --- a/drivers/tty/serial/meson_uart.c +++ b/drivers/tty/serial/meson_uart.c @@ -226,9 +226,7 @@ static void meson_receive_chars(struct uart_port *port) } while (!(readl(port->membase + AML_UART_STATUS) & AML_UART_RX_EMPTY)); - spin_unlock(&port->lock); tty_flip_buffer_push(tport); - spin_lock(&port->lock); } static irqreturn_t meson_uart_interrupt(int irq, void *dev_id) diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index af1700445251..2704dc988e4a 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -1421,9 +1421,7 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) } } - spin_unlock(&port->lock); tty_flip_buffer_push(tport); - spin_lock(&port->lock); return psc_ops->raw_rx_rdy(port); } diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 770c182e2208..fcef7a961430 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -757,9 +757,7 @@ static void msm_handle_rx_dm(struct uart_port *port, unsigned int misr) count -= r_count; } - spin_unlock(&port->lock); tty_flip_buffer_push(tport); - spin_lock(&port->lock); if (misr & (UART_IMR_RXSTALE)) msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR); @@ -819,9 +817,7 @@ static void msm_handle_rx(struct uart_port *port) tty_insert_flip_char(tport, c, flag); } - spin_unlock(&port->lock); tty_flip_buffer_push(tport); - spin_lock(&port->lock); } static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count) diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 76b94d0ff586..84e8158088cd 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -159,6 +159,8 @@ struct uart_omap_port { u32 calc_latency; struct work_struct qos_work; bool is_suspending; + + unsigned int rs485_tx_filter_count; }; #define to_uart_omap_port(p) ((container_of((p), struct uart_omap_port, port))) @@ -302,7 +304,8 @@ static void serial_omap_stop_tx(struct uart_port *port) serial_out(up, UART_OMAP_SCR, up->scr); res = (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0; - if (gpiod_get_value(up->rts_gpiod) != res) { + if (up->rts_gpiod && + gpiod_get_value(up->rts_gpiod) != res) { if (port->rs485.delay_rts_after_send > 0) mdelay( port->rs485.delay_rts_after_send); @@ -328,19 +331,6 @@ static void serial_omap_stop_tx(struct uart_port *port) serial_out(up, UART_IER, up->ier); } - if ((port->rs485.flags & SER_RS485_ENABLED) && - !(port->rs485.flags & SER_RS485_RX_DURING_TX)) { - /* - * Empty the RX FIFO, we are not interested in anything - * received during the half-duplex transmission. - */ - serial_out(up, UART_FCR, up->fcr | UART_FCR_CLEAR_RCVR); - /* Re-enable RX interrupts */ - up->ier |= UART_IER_RLSI | UART_IER_RDI; - up->port.read_status_mask |= UART_LSR_DR; - serial_out(up, UART_IER, up->ier); - } - pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); } @@ -366,6 +356,10 @@ static void transmit_chars(struct uart_omap_port *up, unsigned int lsr) serial_out(up, UART_TX, up->port.x_char); up->port.icount.tx++; up->port.x_char = 0; + if ((up->port.rs485.flags & SER_RS485_ENABLED) && + !(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) + up->rs485_tx_filter_count++; + return; } if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { @@ -377,6 +371,10 @@ static void transmit_chars(struct uart_omap_port *up, unsigned int lsr) serial_out(up, UART_TX, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); up->port.icount.tx++; + if ((up->port.rs485.flags & SER_RS485_ENABLED) && + !(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) + up->rs485_tx_filter_count++; + if (uart_circ_empty(xmit)) break; } while (--count > 0); @@ -411,7 +409,7 @@ static void serial_omap_start_tx(struct uart_port *port) /* if rts not already enabled */ res = (port->rs485.flags & SER_RS485_RTS_ON_SEND) ? 1 : 0; - if (gpiod_get_value(up->rts_gpiod) != res) { + if (up->rts_gpiod && gpiod_get_value(up->rts_gpiod) != res) { gpiod_set_value(up->rts_gpiod, res); if (port->rs485.delay_rts_before_send > 0) mdelay(port->rs485.delay_rts_before_send); @@ -420,7 +418,7 @@ static void serial_omap_start_tx(struct uart_port *port) if ((port->rs485.flags & SER_RS485_ENABLED) && !(port->rs485.flags & SER_RS485_RX_DURING_TX)) - serial_omap_stop_rx(port); + up->rs485_tx_filter_count = 0; serial_omap_enable_ier_thri(up); pm_runtime_mark_last_busy(up->dev); @@ -491,8 +489,13 @@ static void serial_omap_rlsi(struct uart_omap_port *up, unsigned int lsr) * Read one data character out to avoid stalling the receiver according * to the table 23-246 of the omap4 TRM. */ - if (likely(lsr & UART_LSR_DR)) + if (likely(lsr & UART_LSR_DR)) { serial_in(up, UART_RX); + if ((up->port.rs485.flags & SER_RS485_ENABLED) && + !(up->port.rs485.flags & SER_RS485_RX_DURING_TX) && + up->rs485_tx_filter_count) + up->rs485_tx_filter_count--; + } up->port.icount.rx++; flag = TTY_NORMAL; @@ -543,6 +546,13 @@ static void serial_omap_rdi(struct uart_omap_port *up, unsigned int lsr) return; ch = serial_in(up, UART_RX); + if ((up->port.rs485.flags & SER_RS485_ENABLED) && + !(up->port.rs485.flags & SER_RS485_RX_DURING_TX) && + up->rs485_tx_filter_count) { + up->rs485_tx_filter_count--; + return; + } + flag = TTY_NORMAL; up->port.icount.rx++; @@ -1407,18 +1417,13 @@ serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) /* store new config */ port->rs485 = *rs485; - /* - * Just as a precaution, only allow rs485 - * to be enabled if the gpio pin is valid - */ if (up->rts_gpiod) { /* enable / disable rts */ val = (port->rs485.flags & SER_RS485_ENABLED) ? SER_RS485_RTS_AFTER_SEND : SER_RS485_RTS_ON_SEND; val = (port->rs485.flags & val) ? 1 : 0; gpiod_set_value(up->rts_gpiod, val); - } else - port->rs485.flags &= ~SER_RS485_ENABLED; + } /* Enable interrupts */ up->ier = mode; diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c index abc6042f0378..91f1eb0058d7 100644 --- a/drivers/tty/serial/owl-uart.c +++ b/drivers/tty/serial/owl-uart.c @@ -247,9 +247,7 @@ static void owl_uart_receive_chars(struct uart_port *port) stat = owl_uart_read(port, OWL_UART_STAT); } - spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); } static irqreturn_t owl_uart_irq(int irq, void *dev_id) diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index a7363bc66c11..f0351e6f0ef6 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -236,7 +236,6 @@ struct eg20t_port { void *rx_buf_virt; dma_addr_t rx_buf_dma; - struct dentry *debugfs; #define IRQ_NAME_SIZE 17 char irq_name[IRQ_NAME_SIZE]; @@ -292,8 +291,6 @@ static const int trigger_level_64[4] = { 1, 16, 32, 56 }; static const int trigger_level_16[4] = { 1, 4, 8, 14 }; static const int trigger_level_1[4] = { 1, 1, 1, 1 }; -#ifdef CONFIG_DEBUG_FS - #define PCH_REGS_BUFSIZE 1024 @@ -353,7 +350,6 @@ static const struct file_operations port_regs_ops = { .read = port_show_regs, .llseek = default_llseek, }; -#endif /* CONFIG_DEBUG_FS */ static const struct dmi_system_id pch_uart_dmi_table[] = { { @@ -1735,9 +1731,7 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, int fifosize; int port_type; struct pch_uart_driver_data *board; -#ifdef CONFIG_DEBUG_FS - char name[32]; /* for debugfs file name */ -#endif + char name[32]; board = &drv_dat[id->driver_data]; port_type = board->port_type; @@ -1813,11 +1807,9 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, if (ret < 0) goto init_port_hal_free; -#ifdef CONFIG_DEBUG_FS - snprintf(name, sizeof(name), "uart%d_regs", board->line_no); - priv->debugfs = debugfs_create_file(name, S_IFREG | S_IRUGO, - NULL, priv, &port_regs_ops); -#endif + snprintf(name, sizeof(name), "uart%d_regs", priv->port.line); + debugfs_create_file(name, S_IFREG | S_IRUGO, NULL, priv, + &port_regs_ops); return priv; @@ -1835,10 +1827,10 @@ init_port_alloc_err: static void pch_uart_exit_port(struct eg20t_port *priv) { + char name[32]; -#ifdef CONFIG_DEBUG_FS - debugfs_remove(priv->debugfs); -#endif + snprintf(name, sizeof(name), "uart%d_regs", priv->port.line); + debugfs_remove(debugfs_lookup(name, NULL)); uart_remove_one_port(&pch_uart_driver, &priv->port); free_page((unsigned long)priv->rxbuf.buf); } diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 0d85b55ea823..00bb88a71606 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -818,7 +818,6 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) u32 s_irq_status; u32 geni_status; struct uart_port *uport = dev; - unsigned long flags; bool drop_rx = false; struct tty_port *tport = &uport->state->port; struct qcom_geni_serial_port *port = to_dev_port(uport, uport); @@ -826,7 +825,8 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) if (uport->suspended) return IRQ_NONE; - spin_lock_irqsave(&uport->lock, flags); + spin_lock(&uport->lock); + m_irq_status = readl(uport->membase + SE_GENI_M_IRQ_STATUS); s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS); geni_status = readl(uport->membase + SE_GENI_STATUS); @@ -861,7 +861,7 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) qcom_geni_serial_handle_rx(uport, drop_rx); out_unlock: - uart_unlock_and_check_sysrq(uport, flags); + uart_unlock_and_check_sysrq(uport); return IRQ_HANDLED; } diff --git a/drivers/tty/serial/rda-uart.c b/drivers/tty/serial/rda-uart.c index 85366e059258..d550d8fa2fab 100644 --- a/drivers/tty/serial/rda-uart.c +++ b/drivers/tty/serial/rda-uart.c @@ -398,9 +398,7 @@ static void rda_uart_receive_chars(struct uart_port *port) status = rda_uart_read(port, RDA_UART_STATUS); } - spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); } static irqreturn_t rda_interrupt(int irq, void *dev_id) diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c index 5690c09cc041..d60abffab70e 100644 --- a/drivers/tty/serial/rp2.c +++ b/drivers/tty/serial/rp2.c @@ -424,9 +424,7 @@ static void rp2_rx_chars(struct rp2_uart_port *up) up->port.icount.rx++; } - spin_unlock(&up->port.lock); tty_flip_buffer_push(port); - spin_lock(&up->port.lock); } static void rp2_tx_chars(struct rp2_uart_port *up) diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c index f5fab1dd96bc..697b6a002a16 100644 --- a/drivers/tty/serial/sa1100.c +++ b/drivers/tty/serial/sa1100.c @@ -223,9 +223,7 @@ sa1100_rx_chars(struct sa1100_port *sport) UTSR0_TO_SM(UART_GET_UTSR0(sport)); } - spin_unlock(&sport->port.lock); tty_flip_buffer_push(&sport->port.state->port); - spin_lock(&sport->port.lock); } static void sa1100_tx_chars(struct sa1100_port *sport) diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c index 8ae3e03fbd8c..d9e4b67a12a0 100644 --- a/drivers/tty/serial/samsung_tty.c +++ b/drivers/tty/serial/samsung_tty.c @@ -56,9 +56,16 @@ /* flag to ignore all characters coming in */ #define RXSTAT_DUMMY_READ (0x10000000) +enum s3c24xx_port_type { + TYPE_S3C24XX, + TYPE_S3C6400, + TYPE_APPLE_S5L, +}; + struct s3c24xx_uart_info { char *name; - unsigned int type; + enum s3c24xx_port_type type; + unsigned int port_type; unsigned int fifosize; unsigned long rx_fifomask; unsigned long rx_fifoshift; @@ -70,6 +77,7 @@ struct s3c24xx_uart_info { unsigned long num_clks; unsigned long clksel_mask; unsigned long clksel_shift; + unsigned long ucon_mask; /* uart port features */ @@ -144,6 +152,8 @@ struct s3c24xx_uart_port { #endif }; +static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport); + /* conversion functions */ #define s3c24xx_dev_to_port(__dev) dev_get_drvdata(__dev) @@ -228,16 +238,6 @@ static int s3c24xx_serial_txempty_nofifo(struct uart_port *port) return rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE; } -/* - * s3c64xx and later SoC's include the interrupt mask and status registers in - * the controller itself, unlike the s3c24xx SoC's which have these registers - * in the interrupt controller. Check if the port type is s3c64xx or higher. - */ -static int s3c24xx_serial_has_interrupt_mask(struct uart_port *port) -{ - return to_ourport(port)->info->type == PORT_S3C6400; -} - static void s3c24xx_serial_rx_enable(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); @@ -289,10 +289,17 @@ static void s3c24xx_serial_stop_tx(struct uart_port *port) if (!ourport->tx_enabled) return; - if (s3c24xx_serial_has_interrupt_mask(port)) + switch (ourport->info->type) { + case TYPE_S3C6400: s3c24xx_set_bit(port, S3C64XX_UINTM_TXD, S3C64XX_UINTM); - else + break; + case TYPE_APPLE_S5L: + s3c24xx_clear_bit(port, APPLE_S5L_UCON_TXTHRESH_ENA, S3C2410_UCON); + break; + default: disable_irq_nosync(ourport->tx_irq); + break; + } if (dma && dma->tx_chan && ourport->tx_in_progress == S3C24XX_TX_DMA) { dmaengine_pause(dma->tx_chan); @@ -353,10 +360,17 @@ static void enable_tx_dma(struct s3c24xx_uart_port *ourport) u32 ucon; /* Mask Tx interrupt */ - if (s3c24xx_serial_has_interrupt_mask(port)) + switch (ourport->info->type) { + case TYPE_S3C6400: s3c24xx_set_bit(port, S3C64XX_UINTM_TXD, S3C64XX_UINTM); - else + break; + case TYPE_APPLE_S5L: + WARN_ON(1); // No DMA + break; + default: disable_irq_nosync(ourport->tx_irq); + break; + } /* Enable tx dma mode */ ucon = rd_regl(port, S3C2410_UCON); @@ -386,13 +400,28 @@ static void enable_tx_pio(struct s3c24xx_uart_port *ourport) wr_regl(port, S3C2410_UCON, ucon); /* Unmask Tx interrupt */ - if (s3c24xx_serial_has_interrupt_mask(port)) + switch (ourport->info->type) { + case TYPE_S3C6400: s3c24xx_clear_bit(port, S3C64XX_UINTM_TXD, S3C64XX_UINTM); - else + break; + case TYPE_APPLE_S5L: + ucon |= APPLE_S5L_UCON_TXTHRESH_ENA_MSK; + wr_regl(port, S3C2410_UCON, ucon); + break; + default: enable_irq(ourport->tx_irq); + break; + } ourport->tx_mode = S3C24XX_TX_PIO; + + /* + * The Apple version only has edge triggered TX IRQs, so we need + * to kick off the process by sending some characters here. + */ + if (ourport->info->type == TYPE_APPLE_S5L) + s3c24xx_serial_tx_chars(ourport); } static void s3c24xx_serial_start_tx_pio(struct s3c24xx_uart_port *ourport) @@ -513,11 +542,19 @@ static void s3c24xx_serial_stop_rx(struct uart_port *port) if (ourport->rx_enabled) { dev_dbg(port->dev, "stopping rx\n"); - if (s3c24xx_serial_has_interrupt_mask(port)) + switch (ourport->info->type) { + case TYPE_S3C6400: s3c24xx_set_bit(port, S3C64XX_UINTM_RXD, S3C64XX_UINTM); - else + break; + case TYPE_APPLE_S5L: + s3c24xx_clear_bit(port, APPLE_S5L_UCON_RXTHRESH_ENA, S3C2410_UCON); + s3c24xx_clear_bit(port, APPLE_S5L_UCON_RXTO_ENA, S3C2410_UCON); + break; + default: disable_irq_nosync(ourport->rx_irq); + break; + } ourport->rx_enabled = 0; } if (dma && dma->rx_chan) { @@ -651,14 +688,18 @@ static void enable_rx_pio(struct s3c24xx_uart_port *ourport) /* set Rx mode to DMA mode */ ucon = rd_regl(port, S3C2410_UCON); - ucon &= ~(S3C64XX_UCON_TIMEOUT_MASK | - S3C64XX_UCON_EMPTYINT_EN | - S3C64XX_UCON_DMASUS_EN | - S3C64XX_UCON_TIMEOUT_EN | - S3C64XX_UCON_RXMODE_MASK); - ucon |= 0xf << S3C64XX_UCON_TIMEOUT_SHIFT | - S3C64XX_UCON_TIMEOUT_EN | - S3C64XX_UCON_RXMODE_CPU; + ucon &= ~S3C64XX_UCON_RXMODE_MASK; + ucon |= S3C64XX_UCON_RXMODE_CPU; + + /* Apple types use these bits for IRQ masks */ + if (ourport->info->type != TYPE_APPLE_S5L) { + ucon &= ~(S3C64XX_UCON_TIMEOUT_MASK | + S3C64XX_UCON_EMPTYINT_EN | + S3C64XX_UCON_DMASUS_EN | + S3C64XX_UCON_TIMEOUT_EN); + ucon |= 0xf << S3C64XX_UCON_TIMEOUT_SHIFT | + S3C64XX_UCON_TIMEOUT_EN; + } wr_regl(port, S3C2410_UCON, ucon); ourport->rx_mode = S3C24XX_RX_PIO; @@ -674,13 +715,12 @@ static irqreturn_t s3c24xx_serial_rx_chars_dma(void *dev_id) struct s3c24xx_uart_dma *dma = ourport->dma; struct tty_struct *tty = tty_port_tty_get(&ourport->port.state->port); struct tty_port *t = &port->state->port; - unsigned long flags; struct dma_tx_state state; utrstat = rd_regl(port, S3C2410_UTRSTAT); rd_regl(port, S3C2410_UFSTAT); - spin_lock_irqsave(&port->lock, flags); + spin_lock(&port->lock); if (!(utrstat & S3C2410_UTRSTAT_TIMEOUT)) { s3c64xx_start_rx_dma(ourport); @@ -709,7 +749,7 @@ static irqreturn_t s3c24xx_serial_rx_chars_dma(void *dev_id) wr_regl(port, S3C2410_UTRSTAT, S3C2410_UTRSTAT_TIMEOUT); finish: - spin_unlock_irqrestore(&port->lock, flags); + spin_unlock(&port->lock); return IRQ_HANDLED; } @@ -805,16 +845,15 @@ static irqreturn_t s3c24xx_serial_rx_chars_pio(void *dev_id) { struct s3c24xx_uart_port *ourport = dev_id; struct uart_port *port = &ourport->port; - unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + spin_lock(&port->lock); s3c24xx_serial_rx_drain_fifo(ourport); - spin_unlock_irqrestore(&port->lock, flags); + spin_unlock(&port->lock); return IRQ_HANDLED; } -static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id) +static irqreturn_t s3c24xx_serial_rx_irq(int irq, void *dev_id) { struct s3c24xx_uart_port *ourport = dev_id; @@ -823,16 +862,12 @@ static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id) return s3c24xx_serial_rx_chars_pio(dev_id); } -static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) +static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport) { - struct s3c24xx_uart_port *ourport = id; struct uart_port *port = &ourport->port; struct circ_buf *xmit = &port->state->xmit; - unsigned long flags; int count, dma_count = 0; - spin_lock_irqsave(&port->lock, flags); - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); if (ourport->dma && ourport->dma->tx_chan && @@ -849,7 +884,7 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) wr_reg(port, S3C2410_UTXH, port->x_char); port->icount.tx++; port->x_char = 0; - goto out; + return; } /* if there isn't anything more to transmit, or the uart is now @@ -858,7 +893,7 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { s3c24xx_serial_stop_tx(port); - goto out; + return; } /* try and drain the buffer... */ @@ -880,7 +915,7 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) if (!count && dma_count) { s3c24xx_serial_start_tx_dma(ourport, dma_count); - goto out; + return; } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) { @@ -891,9 +926,18 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) if (uart_circ_empty(xmit)) s3c24xx_serial_stop_tx(port); +} -out: - spin_unlock_irqrestore(&port->lock, flags); +static irqreturn_t s3c24xx_serial_tx_irq(int irq, void *id) +{ + struct s3c24xx_uart_port *ourport = id; + struct uart_port *port = &ourport->port; + + spin_lock(&port->lock); + + s3c24xx_serial_tx_chars(ourport); + + spin_unlock(&port->lock); return IRQ_HANDLED; } @@ -906,16 +950,37 @@ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id) irqreturn_t ret = IRQ_HANDLED; if (pend & S3C64XX_UINTM_RXD_MSK) { - ret = s3c24xx_serial_rx_chars(irq, id); + ret = s3c24xx_serial_rx_irq(irq, id); wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_RXD_MSK); } if (pend & S3C64XX_UINTM_TXD_MSK) { - ret = s3c24xx_serial_tx_chars(irq, id); + ret = s3c24xx_serial_tx_irq(irq, id); wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_TXD_MSK); } return ret; } +/* interrupt handler for Apple SoC's.*/ +static irqreturn_t apple_serial_handle_irq(int irq, void *id) +{ + struct s3c24xx_uart_port *ourport = id; + struct uart_port *port = &ourport->port; + unsigned int pend = rd_regl(port, S3C2410_UTRSTAT); + irqreturn_t ret = IRQ_NONE; + + if (pend & (APPLE_S5L_UTRSTAT_RXTHRESH | APPLE_S5L_UTRSTAT_RXTO)) { + wr_regl(port, S3C2410_UTRSTAT, + APPLE_S5L_UTRSTAT_RXTHRESH | APPLE_S5L_UTRSTAT_RXTO); + ret = s3c24xx_serial_rx_irq(irq, id); + } + if (pend & APPLE_S5L_UTRSTAT_TXTHRESH) { + wr_regl(port, S3C2410_UTRSTAT, APPLE_S5L_UTRSTAT_TXTHRESH); + ret = s3c24xx_serial_tx_irq(irq, id); + } + + return ret; +} + static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port) { struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); @@ -1098,27 +1163,62 @@ static void s3c24xx_serial_shutdown(struct uart_port *port) struct s3c24xx_uart_port *ourport = to_ourport(port); if (ourport->tx_claimed) { - if (!s3c24xx_serial_has_interrupt_mask(port)) - free_irq(ourport->tx_irq, ourport); + free_irq(ourport->tx_irq, ourport); ourport->tx_enabled = 0; ourport->tx_claimed = 0; ourport->tx_mode = 0; } if (ourport->rx_claimed) { - if (!s3c24xx_serial_has_interrupt_mask(port)) - free_irq(ourport->rx_irq, ourport); + free_irq(ourport->rx_irq, ourport); ourport->rx_claimed = 0; ourport->rx_enabled = 0; } - /* Clear pending interrupts and mask all interrupts */ - if (s3c24xx_serial_has_interrupt_mask(port)) { - free_irq(port->irq, ourport); + if (ourport->dma) + s3c24xx_serial_release_dma(ourport); - wr_regl(port, S3C64XX_UINTP, 0xf); - wr_regl(port, S3C64XX_UINTM, 0xf); - } + ourport->tx_in_progress = 0; +} + +static void s3c64xx_serial_shutdown(struct uart_port *port) +{ + struct s3c24xx_uart_port *ourport = to_ourport(port); + + ourport->tx_enabled = 0; + ourport->tx_mode = 0; + ourport->rx_enabled = 0; + + free_irq(port->irq, ourport); + + wr_regl(port, S3C64XX_UINTP, 0xf); + wr_regl(port, S3C64XX_UINTM, 0xf); + + if (ourport->dma) + s3c24xx_serial_release_dma(ourport); + + ourport->tx_in_progress = 0; +} + +static void apple_s5l_serial_shutdown(struct uart_port *port) +{ + struct s3c24xx_uart_port *ourport = to_ourport(port); + + unsigned int ucon; + + ucon = rd_regl(port, S3C2410_UCON); + ucon &= ~(APPLE_S5L_UCON_TXTHRESH_ENA_MSK | + APPLE_S5L_UCON_RXTHRESH_ENA_MSK | + APPLE_S5L_UCON_RXTO_ENA_MSK); + wr_regl(port, S3C2410_UCON, ucon); + + wr_regl(port, S3C2410_UTRSTAT, APPLE_S5L_UTRSTAT_ALL_FLAGS); + + free_irq(port->irq, ourport); + + ourport->tx_enabled = 0; + ourport->tx_mode = 0; + ourport->rx_enabled = 0; if (ourport->dma) s3c24xx_serial_release_dma(ourport); @@ -1133,7 +1233,7 @@ static int s3c24xx_serial_startup(struct uart_port *port) ourport->rx_enabled = 1; - ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0, + ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_irq, 0, s3c24xx_serial_portname(port), ourport); if (ret != 0) { @@ -1147,7 +1247,7 @@ static int s3c24xx_serial_startup(struct uart_port *port) ourport->tx_enabled = 1; - ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0, + ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_irq, 0, s3c24xx_serial_portname(port), ourport); if (ret) { @@ -1193,9 +1293,7 @@ static int s3c64xx_serial_startup(struct uart_port *port) /* For compatibility with s3c24xx Soc's */ ourport->rx_enabled = 1; - ourport->rx_claimed = 1; ourport->tx_enabled = 0; - ourport->tx_claimed = 1; spin_lock_irqsave(&port->lock, flags); @@ -1215,6 +1313,45 @@ static int s3c64xx_serial_startup(struct uart_port *port) return ret; } +static int apple_s5l_serial_startup(struct uart_port *port) +{ + struct s3c24xx_uart_port *ourport = to_ourport(port); + unsigned long flags; + unsigned int ufcon; + int ret; + + wr_regl(port, S3C2410_UTRSTAT, APPLE_S5L_UTRSTAT_ALL_FLAGS); + + ret = request_irq(port->irq, apple_serial_handle_irq, 0, + s3c24xx_serial_portname(port), ourport); + if (ret) { + dev_err(port->dev, "cannot get irq %d\n", port->irq); + return ret; + } + + /* For compatibility with s3c24xx Soc's */ + ourport->rx_enabled = 1; + ourport->tx_enabled = 0; + + spin_lock_irqsave(&port->lock, flags); + + ufcon = rd_regl(port, S3C2410_UFCON); + ufcon |= S3C2410_UFCON_RESETRX | S5PV210_UFCON_RXTRIG8; + if (!uart_console(port)) + ufcon |= S3C2410_UFCON_RESETTX; + wr_regl(port, S3C2410_UFCON, ufcon); + + enable_rx_pio(ourport); + + spin_unlock_irqrestore(&port->lock, flags); + + /* Enable Rx Interrupt */ + s3c24xx_set_bit(port, APPLE_S5L_UCON_RXTHRESH_ENA, S3C2410_UCON); + s3c24xx_set_bit(port, APPLE_S5L_UCON_RXTO_ENA, S3C2410_UCON); + + return ret; +} + /* power power management control */ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, @@ -1535,41 +1672,26 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, static const char *s3c24xx_serial_type(struct uart_port *port) { - switch (port->type) { - case PORT_S3C2410: - return "S3C2410"; - case PORT_S3C2440: - return "S3C2440"; - case PORT_S3C2412: - return "S3C2412"; - case PORT_S3C6400: + struct s3c24xx_uart_port *ourport = to_ourport(port); + + switch (ourport->info->type) { + case TYPE_S3C24XX: + return "S3C24XX"; + case TYPE_S3C6400: return "S3C6400/10"; + case TYPE_APPLE_S5L: + return "APPLE S5L"; default: return NULL; } } -#define MAP_SIZE (0x100) - -static void s3c24xx_serial_release_port(struct uart_port *port) -{ - release_mem_region(port->mapbase, MAP_SIZE); -} - -static int s3c24xx_serial_request_port(struct uart_port *port) -{ - const char *name = s3c24xx_serial_portname(port); - - return request_mem_region(port->mapbase, MAP_SIZE, name) ? 0 : -EBUSY; -} - static void s3c24xx_serial_config_port(struct uart_port *port, int flags) { struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); - if (flags & UART_CONFIG_TYPE && - s3c24xx_serial_request_port(port) == 0) - port->type = info->type; + if (flags & UART_CONFIG_TYPE) + port->type = info->port_type; } /* @@ -1580,7 +1702,7 @@ s3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser) { struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); - if (ser->type != PORT_UNKNOWN && ser->type != info->type) + if (ser->type != PORT_UNKNOWN && ser->type != info->port_type) return -EINVAL; return 0; @@ -1608,7 +1730,7 @@ static void s3c24xx_serial_put_poll_char(struct uart_port *port, unsigned char c); #endif -static struct uart_ops s3c24xx_serial_ops = { +static const struct uart_ops s3c24xx_serial_ops = { .pm = s3c24xx_serial_pm, .tx_empty = s3c24xx_serial_tx_empty, .get_mctrl = s3c24xx_serial_get_mctrl, @@ -1621,8 +1743,48 @@ static struct uart_ops s3c24xx_serial_ops = { .shutdown = s3c24xx_serial_shutdown, .set_termios = s3c24xx_serial_set_termios, .type = s3c24xx_serial_type, - .release_port = s3c24xx_serial_release_port, - .request_port = s3c24xx_serial_request_port, + .config_port = s3c24xx_serial_config_port, + .verify_port = s3c24xx_serial_verify_port, +#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL) + .poll_get_char = s3c24xx_serial_get_poll_char, + .poll_put_char = s3c24xx_serial_put_poll_char, +#endif +}; + +static const struct uart_ops s3c64xx_serial_ops = { + .pm = s3c24xx_serial_pm, + .tx_empty = s3c24xx_serial_tx_empty, + .get_mctrl = s3c24xx_serial_get_mctrl, + .set_mctrl = s3c24xx_serial_set_mctrl, + .stop_tx = s3c24xx_serial_stop_tx, + .start_tx = s3c24xx_serial_start_tx, + .stop_rx = s3c24xx_serial_stop_rx, + .break_ctl = s3c24xx_serial_break_ctl, + .startup = s3c64xx_serial_startup, + .shutdown = s3c64xx_serial_shutdown, + .set_termios = s3c24xx_serial_set_termios, + .type = s3c24xx_serial_type, + .config_port = s3c24xx_serial_config_port, + .verify_port = s3c24xx_serial_verify_port, +#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL) + .poll_get_char = s3c24xx_serial_get_poll_char, + .poll_put_char = s3c24xx_serial_put_poll_char, +#endif +}; + +static const struct uart_ops apple_s5l_serial_ops = { + .pm = s3c24xx_serial_pm, + .tx_empty = s3c24xx_serial_tx_empty, + .get_mctrl = s3c24xx_serial_get_mctrl, + .set_mctrl = s3c24xx_serial_set_mctrl, + .stop_tx = s3c24xx_serial_stop_tx, + .start_tx = s3c24xx_serial_start_tx, + .stop_rx = s3c24xx_serial_stop_rx, + .break_ctl = s3c24xx_serial_break_ctl, + .startup = apple_s5l_serial_startup, + .shutdown = apple_s5l_serial_shutdown, + .set_termios = s3c24xx_serial_set_termios, + .type = s3c24xx_serial_type, .config_port = s3c24xx_serial_config_port, .verify_port = s3c24xx_serial_verify_port, #if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL) @@ -1706,14 +1868,9 @@ static void s3c24xx_serial_resetport(struct uart_port *port, { struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); unsigned long ucon = rd_regl(port, S3C2410_UCON); - unsigned int ucon_mask; - ucon_mask = info->clksel_mask; - if (info->type == PORT_S3C2440) - ucon_mask |= S3C2440_UCON0_DIVMASK; - - ucon &= ucon_mask; - wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); + ucon &= (info->clksel_mask | info->ucon_mask); + wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); /* reset both fifos */ wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); @@ -1868,10 +2025,6 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, /* setup info for port */ port->dev = &platdev->dev; - /* Startup sequence is different for s3c64xx and higher SoC's */ - if (s3c24xx_serial_has_interrupt_mask(port)) - s3c24xx_serial_ops.startup = s3c64xx_serial_startup; - port->uartclk = 1; if (cfg->uart_flags & UPF_CONS_FLOW) { @@ -1889,8 +2042,8 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, dev_dbg(port->dev, "resource %pR)\n", res); - port->membase = devm_ioremap(port->dev, res->start, resource_size(res)); - if (!port->membase) { + port->membase = devm_ioremap_resource(port->dev, res); + if (IS_ERR(port->membase)) { dev_err(port->dev, "failed to remap controller address\n"); return -EBUSY; } @@ -1905,11 +2058,16 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, ourport->tx_irq = ret + 1; } - if (!s3c24xx_serial_has_interrupt_mask(port)) { + switch (ourport->info->type) { + case TYPE_S3C24XX: ret = platform_get_irq(platdev, 1); if (ret > 0) ourport->tx_irq = ret; + break; + default: + break; } + /* * DMA is currently supported only on DT platforms, if DMA properties * are specified. @@ -1945,10 +2103,26 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, pr_warn("uart: failed to enable baudclk\n"); /* Keep all interrupts masked and cleared */ - if (s3c24xx_serial_has_interrupt_mask(port)) { + switch (ourport->info->type) { + case TYPE_S3C6400: wr_regl(port, S3C64XX_UINTM, 0xf); wr_regl(port, S3C64XX_UINTP, 0xf); wr_regl(port, S3C64XX_UINTSP, 0xf); + break; + case TYPE_APPLE_S5L: { + unsigned int ucon; + + ucon = rd_regl(port, S3C2410_UCON); + ucon &= ~(APPLE_S5L_UCON_TXTHRESH_ENA_MSK | + APPLE_S5L_UCON_RXTHRESH_ENA_MSK | + APPLE_S5L_UCON_RXTO_ENA_MSK); + wr_regl(port, S3C2410_UCON, ucon); + + wr_regl(port, S3C2410_UTRSTAT, APPLE_S5L_UTRSTAT_ALL_FLAGS); + break; + } + default: + break; } dev_dbg(port->dev, "port: map=%pa, mem=%p, irq=%d (%d,%d), clock=%u\n", @@ -2019,6 +2193,18 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) dev_get_platdata(&pdev->dev) : ourport->drv_data->def_cfg; + switch (ourport->info->type) { + case TYPE_S3C24XX: + ourport->port.ops = &s3c24xx_serial_ops; + break; + case TYPE_S3C6400: + ourport->port.ops = &s3c64xx_serial_ops; + break; + case TYPE_APPLE_S5L: + ourport->port.ops = &apple_s5l_serial_ops; + break; + } + if (np) { of_property_read_u32(np, "samsung,uart-fifosize", &ourport->port.fifosize); @@ -2142,7 +2328,8 @@ static int s3c24xx_serial_resume_noirq(struct device *dev) if (port) { /* restore IRQ mask */ - if (s3c24xx_serial_has_interrupt_mask(port)) { + switch (ourport->info->type) { + case TYPE_S3C6400: { unsigned int uintm = 0xf; if (ourport->tx_enabled) @@ -2156,6 +2343,47 @@ static int s3c24xx_serial_resume_noirq(struct device *dev) if (!IS_ERR(ourport->baudclk)) clk_disable_unprepare(ourport->baudclk); clk_disable_unprepare(ourport->clk); + break; + } + case TYPE_APPLE_S5L: { + unsigned int ucon; + int ret; + + ret = clk_prepare_enable(ourport->clk); + if (ret) { + dev_err(dev, "clk_enable clk failed: %d\n", ret); + return ret; + } + if (!IS_ERR(ourport->baudclk)) { + ret = clk_prepare_enable(ourport->baudclk); + if (ret) { + dev_err(dev, "clk_enable baudclk failed: %d\n", ret); + clk_disable_unprepare(ourport->clk); + return ret; + } + } + + ucon = rd_regl(port, S3C2410_UCON); + + ucon &= ~(APPLE_S5L_UCON_TXTHRESH_ENA_MSK | + APPLE_S5L_UCON_RXTHRESH_ENA_MSK | + APPLE_S5L_UCON_RXTO_ENA_MSK); + + if (ourport->tx_enabled) + ucon |= APPLE_S5L_UCON_TXTHRESH_ENA_MSK; + if (ourport->rx_enabled) + ucon |= APPLE_S5L_UCON_RXTHRESH_ENA_MSK | + APPLE_S5L_UCON_RXTO_ENA_MSK; + + wr_regl(port, S3C2410_UCON, ucon); + + if (!IS_ERR(ourport->baudclk)) + clk_disable_unprepare(ourport->baudclk); + clk_disable_unprepare(ourport->clk); + break; + } + default: + break; } } @@ -2380,7 +2608,8 @@ static struct console s3c24xx_serial_console = { static struct s3c24xx_serial_drv_data s3c2410_serial_drv_data = { .info = &(struct s3c24xx_uart_info) { .name = "Samsung S3C2410 UART", - .type = PORT_S3C2410, + .type = TYPE_S3C24XX, + .port_type = PORT_S3C2410, .fifosize = 16, .rx_fifomask = S3C2410_UFSTAT_RXMASK, .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT, @@ -2407,7 +2636,8 @@ static struct s3c24xx_serial_drv_data s3c2410_serial_drv_data = { static struct s3c24xx_serial_drv_data s3c2412_serial_drv_data = { .info = &(struct s3c24xx_uart_info) { .name = "Samsung S3C2412 UART", - .type = PORT_S3C2412, + .type = TYPE_S3C24XX, + .port_type = PORT_S3C2412, .fifosize = 64, .has_divslot = 1, .rx_fifomask = S3C2440_UFSTAT_RXMASK, @@ -2436,7 +2666,8 @@ static struct s3c24xx_serial_drv_data s3c2412_serial_drv_data = { static struct s3c24xx_serial_drv_data s3c2440_serial_drv_data = { .info = &(struct s3c24xx_uart_info) { .name = "Samsung S3C2440 UART", - .type = PORT_S3C2440, + .type = TYPE_S3C24XX, + .port_type = PORT_S3C2440, .fifosize = 64, .has_divslot = 1, .rx_fifomask = S3C2440_UFSTAT_RXMASK, @@ -2449,6 +2680,7 @@ static struct s3c24xx_serial_drv_data s3c2440_serial_drv_data = { .num_clks = 4, .clksel_mask = S3C2412_UCON_CLKMASK, .clksel_shift = S3C2412_UCON_CLKSHIFT, + .ucon_mask = S3C2440_UCON0_DIVMASK, }, .def_cfg = &(struct s3c2410_uartcfg) { .ucon = S3C2410_UCON_DEFAULT, @@ -2464,7 +2696,8 @@ static struct s3c24xx_serial_drv_data s3c2440_serial_drv_data = { static struct s3c24xx_serial_drv_data s3c6400_serial_drv_data = { .info = &(struct s3c24xx_uart_info) { .name = "Samsung S3C6400 UART", - .type = PORT_S3C6400, + .type = TYPE_S3C6400, + .port_type = PORT_S3C6400, .fifosize = 64, .has_divslot = 1, .rx_fifomask = S3C2440_UFSTAT_RXMASK, @@ -2492,7 +2725,8 @@ static struct s3c24xx_serial_drv_data s3c6400_serial_drv_data = { static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = { .info = &(struct s3c24xx_uart_info) { .name = "Samsung S5PV210 UART", - .type = PORT_S3C6400, + .type = TYPE_S3C6400, + .port_type = PORT_S3C6400, .has_divslot = 1, .rx_fifomask = S5PV210_UFSTAT_RXMASK, .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT, @@ -2520,7 +2754,8 @@ static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = { #define EXYNOS_COMMON_SERIAL_DRV_DATA \ .info = &(struct s3c24xx_uart_info) { \ .name = "Samsung Exynos UART", \ - .type = PORT_S3C6400, \ + .type = TYPE_S3C6400, \ + .port_type = PORT_S3C6400, \ .has_divslot = 1, \ .rx_fifomask = S5PV210_UFSTAT_RXMASK, \ .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT, \ @@ -2556,6 +2791,34 @@ static struct s3c24xx_serial_drv_data exynos5433_serial_drv_data = { #define EXYNOS5433_SERIAL_DRV_DATA (kernel_ulong_t)NULL #endif +#ifdef CONFIG_ARCH_APPLE +static struct s3c24xx_serial_drv_data s5l_serial_drv_data = { + .info = &(struct s3c24xx_uart_info) { + .name = "Apple S5L UART", + .type = TYPE_APPLE_S5L, + .port_type = PORT_8250, + .fifosize = 16, + .rx_fifomask = S3C2410_UFSTAT_RXMASK, + .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT, + .rx_fifofull = S3C2410_UFSTAT_RXFULL, + .tx_fifofull = S3C2410_UFSTAT_TXFULL, + .tx_fifomask = S3C2410_UFSTAT_TXMASK, + .tx_fifoshift = S3C2410_UFSTAT_TXSHIFT, + .def_clk_sel = S3C2410_UCON_CLKSEL0, + .num_clks = 1, + .clksel_mask = 0, + .clksel_shift = 0, + }, + .def_cfg = &(struct s3c2410_uartcfg) { + .ucon = APPLE_S5L_UCON_DEFAULT, + .ufcon = S3C2410_UFCON_DEFAULT, + }, +}; +#define S5L_SERIAL_DRV_DATA ((kernel_ulong_t)&s5l_serial_drv_data) +#else +#define S5L_SERIAL_DRV_DATA ((kernel_ulong_t)NULL) +#endif + static const struct platform_device_id s3c24xx_serial_driver_ids[] = { { .name = "s3c2410-uart", @@ -2578,6 +2841,9 @@ static const struct platform_device_id s3c24xx_serial_driver_ids[] = { }, { .name = "exynos5433-uart", .driver_data = EXYNOS5433_SERIAL_DRV_DATA, + }, { + .name = "s5l-uart", + .driver_data = S5L_SERIAL_DRV_DATA, }, { }, }; @@ -2599,6 +2865,8 @@ static const struct of_device_id s3c24xx_uart_dt_match[] = { .data = (void *)EXYNOS4210_SERIAL_DRV_DATA }, { .compatible = "samsung,exynos5433-uart", .data = (void *)EXYNOS5433_SERIAL_DRV_DATA }, + { .compatible = "apple,s5l-uart", + .data = (void *)S5L_SERIAL_DRV_DATA }, {}, }; MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match); @@ -2730,6 +2998,23 @@ OF_EARLYCON_DECLARE(s5pv210, "samsung,s5pv210-uart", s5pv210_early_console_setup); OF_EARLYCON_DECLARE(exynos4210, "samsung,exynos4210-uart", s5pv210_early_console_setup); + +/* Apple S5L */ +static int __init apple_s5l_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + /* Close enough to S3C2410 for earlycon... */ + device->port.private_data = &s3c2410_early_console_data; + +#ifdef CONFIG_ARM64 + /* ... but we need to override the existing fixmap entry as nGnRnE */ + __set_fixmap(FIX_EARLYCON_MEM_BASE, device->port.mapbase, + __pgprot(PROT_DEVICE_nGnRnE)); +#endif + return samsung_early_console_setup(device, opt); +} + +OF_EARLYCON_DECLARE(s5l, "apple,s5l-uart", apple_s5l_early_console_setup); #endif MODULE_ALIAS("platform:samsung-uart"); diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index f86ec2d2635b..9adb8362578c 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1196,7 +1196,7 @@ static int sc16is7xx_probe(struct device *dev, ret = regmap_read(regmap, SC16IS7XX_LSR_REG << SC16IS7XX_REG_SHIFT, &val); if (ret < 0) - return ret; + return -EPROBE_DEFER; /* Alloc port structure */ s = devm_kzalloc(dev, struct_size(s, p, devtype->nr_uart), GFP_KERNEL); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index ba31e97d3d96..87f7127b57e6 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -759,8 +759,6 @@ static int uart_get_info(struct tty_port *port, struct serial_struct *retinfo) struct uart_port *uport; int ret = -ENODEV; - memset(retinfo, 0, sizeof(*retinfo)); - /* * Ensure the state we copy is consistent and no hardware changes * occur as we go @@ -1305,7 +1303,7 @@ static int uart_set_rs485_config(struct uart_port *port, unsigned long flags; if (!port->rs485_config) - return -ENOIOCTLCMD; + return -ENOTTY; if (copy_from_user(&rs485, rs485_user, sizeof(*rs485_user))) return -EFAULT; @@ -1329,7 +1327,7 @@ static int uart_get_iso7816_config(struct uart_port *port, struct serial_iso7816 aux; if (!port->iso7816_config) - return -ENOIOCTLCMD; + return -ENOTTY; spin_lock_irqsave(&port->lock, flags); aux = port->iso7816; @@ -1349,7 +1347,7 @@ static int uart_set_iso7816_config(struct uart_port *port, unsigned long flags; if (!port->iso7816_config) - return -ENOIOCTLCMD; + return -ENOTTY; if (copy_from_user(&iso7816, iso7816_user, sizeof(*iso7816_user))) return -EFAULT; diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c index 7a07e7272de1..0a7e5b74bc1d 100644 --- a/drivers/tty/serial/serial_txx9.c +++ b/drivers/tty/serial/serial_txx9.c @@ -330,9 +330,9 @@ receive_chars(struct uart_txx9_port *up, unsigned int *status) up->port.ignore_status_mask = next_ignore_status_mask; disr = sio_in(up, TXX9_SIDISR); } while (!(disr & TXX9_SIDISR_UVALID) && (max_count-- > 0)); - spin_unlock(&up->port.lock); + tty_flip_buffer_push(&up->port.state->port); - spin_lock(&up->port.lock); + *status = disr; } diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index e1179e74a2b8..ef37fdf37612 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -2124,7 +2124,7 @@ static void sci_break_ctl(struct uart_port *port, int break_state) unsigned short scscr, scsptr; unsigned long flags; - /* check wheter the port has SCSPTR */ + /* check whether the port has SCSPTR */ if (!sci_getreg(port, SCSPTR)->size) { /* * Not supported by hardware. Most parts couple break and rx @@ -2609,21 +2609,10 @@ done: udelay(DIV_ROUND_UP(10 * 1000000, baud)); } - /* - * Calculate delay for 2 DMA buffers (4 FIFO). - * See serial_core.c::uart_update_timeout(). - * With 10 bits (CS8), 250Hz, 115200 baud and 64 bytes FIFO, the above - * function calculates 1 jiffie for the data plus 5 jiffies for the - * "slop(e)." Then below we calculate 5 jiffies (20ms) for 2 DMA - * buffers (4 FIFO sizes), but when performing a faster transfer, the - * value obtained by this formula is too small. Therefore, if the value - * is smaller than 20ms, use 20ms as the timeout value for DMA. - */ + /* Calculate delay for 2 DMA buffers (4 FIFO). */ s->rx_frame = (10000 * bits) / (baud / 100); #ifdef CONFIG_SERIAL_SH_SCI_DMA s->rx_timeout = s->buf_len_rx * 2 * s->rx_frame; - if (s->rx_timeout < 20) - s->rx_timeout = 20; #endif if ((termios->c_cflag & CREAD) != 0) diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c index 328d5a78792f..0ac0371f943b 100644 --- a/drivers/tty/serial/sifive.c +++ b/drivers/tty/serial/sifive.c @@ -448,9 +448,7 @@ static void __ssp_receive_chars(struct sifive_serial_port *ssp) uart_insert_char(&ssp->port, 0, 0, ch, TTY_NORMAL); } - spin_unlock(&ssp->port.lock); tty_flip_buffer_push(&ssp->port.state->port); - spin_lock(&ssp->port.lock); } /** diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index b3675cf25a69..c2ae7b392b86 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -218,8 +218,7 @@ static void stm32_usart_receive_chars(struct uart_port *port, bool threaded) u32 sr; char flag; - if (irqd_is_wakeup_set(irq_get_irq_data(port->irq))) - pm_wakeup_event(tport->tty->dev, 0); + spin_lock(&port->lock); while (stm32_usart_pending_rx(port, &sr, &stm32_port->last_res, threaded)) { @@ -271,14 +270,14 @@ static void stm32_usart_receive_chars(struct uart_port *port, bool threaded) } } - if (uart_handle_sysrq_char(port, c)) + if (uart_prepare_sysrq_char(port, c)) continue; uart_insert_char(port, sr, USART_SR_ORE, c, flag); } - spin_unlock(&port->lock); + uart_unlock_and_check_sysrq(port); + tty_flip_buffer_push(tport); - spin_lock(&port->lock); } static void stm32_usart_tx_dma_complete(void *arg) @@ -286,12 +285,16 @@ static void stm32_usart_tx_dma_complete(void *arg) struct uart_port *port = arg; struct stm32_port *stm32port = to_stm32_port(port); const struct stm32_usart_offsets *ofs = &stm32port->info->ofs; + unsigned long flags; + dmaengine_terminate_async(stm32port->tx_ch); stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT); stm32port->tx_dma_busy = false; /* Let's see if we have pending data to send */ + spin_lock_irqsave(&port->lock, flags); stm32_usart_transmit_chars(port); + spin_unlock_irqrestore(&port->lock, flags); } static void stm32_usart_tx_interrupt_enable(struct uart_port *port) @@ -303,7 +306,7 @@ static void stm32_usart_tx_interrupt_enable(struct uart_port *port) * Enables TX FIFO threashold irq when FIFO is enabled, * or TX empty irq when FIFO is disabled */ - if (stm32_port->fifoen) + if (stm32_port->fifoen && stm32_port->txftcfg >= 0) stm32_usart_set_bits(port, ofs->cr3, USART_CR3_TXFTIE); else stm32_usart_set_bits(port, ofs->cr1, USART_CR1_TXEIE); @@ -314,7 +317,7 @@ static void stm32_usart_tx_interrupt_disable(struct uart_port *port) struct stm32_port *stm32_port = to_stm32_port(port); const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - if (stm32_port->fifoen) + if (stm32_port->fifoen && stm32_port->txftcfg >= 0) stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_TXFTIE); else stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); @@ -455,29 +458,34 @@ static void stm32_usart_transmit_chars(struct uart_port *port) static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) { struct uart_port *port = ptr; + struct tty_port *tport = &port->state->port; struct stm32_port *stm32_port = to_stm32_port(port); const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; u32 sr; - spin_lock(&port->lock); - sr = readl_relaxed(port->membase + ofs->isr); if ((sr & USART_SR_RTOF) && ofs->icr != UNDEF_REG) writel_relaxed(USART_ICR_RTOCF, port->membase + ofs->icr); - if ((sr & USART_SR_WUF) && ofs->icr != UNDEF_REG) + if ((sr & USART_SR_WUF) && ofs->icr != UNDEF_REG) { + /* Clear wake up flag and disable wake up interrupt */ writel_relaxed(USART_ICR_WUCF, port->membase + ofs->icr); + stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_WUFIE); + if (irqd_is_wakeup_set(irq_get_irq_data(port->irq))) + pm_wakeup_event(tport->tty->dev, 0); + } if ((sr & USART_SR_RXNE) && !(stm32_port->rx_ch)) stm32_usart_receive_chars(port, false); - if ((sr & USART_SR_TXE) && !(stm32_port->tx_ch)) + if ((sr & USART_SR_TXE) && !(stm32_port->tx_ch)) { + spin_lock(&port->lock); stm32_usart_transmit_chars(port); - - spin_unlock(&port->lock); + spin_unlock(&port->lock); + } if (stm32_port->rx_ch) return IRQ_WAKE_THREAD; @@ -490,13 +498,9 @@ static irqreturn_t stm32_usart_threaded_interrupt(int irq, void *ptr) struct uart_port *port = ptr; struct stm32_port *stm32_port = to_stm32_port(port); - spin_lock(&port->lock); - if (stm32_port->rx_ch) stm32_usart_receive_chars(port, true); - spin_unlock(&port->lock); - return IRQ_HANDLED; } @@ -505,7 +509,10 @@ static unsigned int stm32_usart_tx_empty(struct uart_port *port) struct stm32_port *stm32_port = to_stm32_port(port); const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - return readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE; + if (readl_relaxed(port->membase + ofs->isr) & USART_SR_TC) + return TIOCSER_TEMT; + + return 0; } static void stm32_usart_set_mctrl(struct uart_port *port, unsigned int mctrl) @@ -584,6 +591,19 @@ static void stm32_usart_start_tx(struct uart_port *port) stm32_usart_transmit_chars(port); } +/* Flush the transmit buffer. */ +static void stm32_usart_flush_buffer(struct uart_port *port) +{ + struct stm32_port *stm32_port = to_stm32_port(port); + const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + + if (stm32_port->tx_ch) { + dmaengine_terminate_async(stm32_port->tx_ch); + stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT); + stm32_port->tx_dma_busy = false; + } +} + /* Throttle the remote when input buffer is about to overflow. */ static void stm32_usart_throttle(struct uart_port *port) { @@ -634,33 +654,30 @@ static int stm32_usart_startup(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + const struct stm32_usart_config *cfg = &stm32_port->info->cfg; const char *name = to_platform_device(port->dev)->name; u32 val; int ret; ret = request_threaded_irq(port->irq, stm32_usart_interrupt, stm32_usart_threaded_interrupt, - IRQF_NO_SUSPEND, name, port); + IRQF_ONESHOT | IRQF_NO_SUSPEND, + name, port); if (ret) return ret; + if (stm32_port->swap) { + val = readl_relaxed(port->membase + ofs->cr2); + val |= USART_CR2_SWAP; + writel_relaxed(val, port->membase + ofs->cr2); + } + /* RX FIFO Flush */ if (ofs->rqr != UNDEF_REG) - stm32_usart_set_bits(port, ofs->rqr, USART_RQR_RXFRQ); + writel_relaxed(USART_RQR_RXFRQ, port->membase + ofs->rqr); - /* Tx and RX FIFO configuration */ - if (stm32_port->fifoen) { - val = readl_relaxed(port->membase + ofs->cr3); - val &= ~(USART_CR3_TXFTCFG_MASK | USART_CR3_RXFTCFG_MASK); - val |= USART_CR3_TXFTCFG_HALF << USART_CR3_TXFTCFG_SHIFT; - val |= USART_CR3_RXFTCFG_HALF << USART_CR3_RXFTCFG_SHIFT; - writel_relaxed(val, port->membase + ofs->cr3); - } - - /* RX FIFO enabling */ - val = stm32_port->cr1_irq | USART_CR1_RE; - if (stm32_port->fifoen) - val |= USART_CR1_FIFOEN; + /* RX enabling */ + val = stm32_port->cr1_irq | USART_CR1_RE | BIT(cfg->uart_enable_bit); stm32_usart_set_bits(port, ofs->cr1, val); return 0; @@ -691,6 +708,11 @@ static void stm32_usart_shutdown(struct uart_port *port) if (ret) dev_err(port->dev, "Transmission is not complete\n"); + /* flush RX & TX FIFO */ + if (ofs->rqr != UNDEF_REG) + writel_relaxed(USART_RQR_TXFRQ | USART_RQR_RXFRQ, + port->membase + ofs->rqr); + stm32_usart_clr_bits(port, ofs->cr1, val); free_irq(port->irq, port); @@ -737,8 +759,9 @@ static void stm32_usart_set_termios(struct uart_port *port, unsigned int baud, bits; u32 usartdiv, mantissa, fraction, oversampling; tcflag_t cflag = termios->c_cflag; - u32 cr1, cr2, cr3; + u32 cr1, cr2, cr3, isr; unsigned long flags; + int ret; if (!stm32_port->hw_flow_control) cflag &= ~CRTSCTS; @@ -747,21 +770,37 @@ static void stm32_usart_set_termios(struct uart_port *port, spin_lock_irqsave(&port->lock, flags); + ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr, + isr, + (isr & USART_SR_TC), + 10, 100000); + + /* Send the TC error message only when ISR_TC is not set. */ + if (ret) + dev_err(port->dev, "Transmission is not complete\n"); + /* Stop serial port and reset value */ writel_relaxed(0, port->membase + ofs->cr1); /* flush RX & TX FIFO */ if (ofs->rqr != UNDEF_REG) - stm32_usart_set_bits(port, ofs->rqr, - USART_RQR_TXFRQ | USART_RQR_RXFRQ); + writel_relaxed(USART_RQR_TXFRQ | USART_RQR_RXFRQ, + port->membase + ofs->rqr); cr1 = USART_CR1_TE | USART_CR1_RE; if (stm32_port->fifoen) cr1 |= USART_CR1_FIFOEN; - cr2 = 0; + cr2 = stm32_port->swap ? USART_CR2_SWAP : 0; + + /* Tx and RX FIFO configuration */ cr3 = readl_relaxed(port->membase + ofs->cr3); - cr3 &= USART_CR3_TXFTIE | USART_CR3_RXFTCFG_MASK | USART_CR3_RXFTIE - | USART_CR3_TXFTCFG_MASK; + cr3 &= USART_CR3_TXFTIE | USART_CR3_RXFTIE; + if (stm32_port->fifoen) { + if (stm32_port->txftcfg >= 0) + cr3 |= stm32_port->txftcfg << USART_CR3_TXFTCFG_SHIFT; + if (stm32_port->rxftcfg >= 0) + cr3 |= stm32_port->rxftcfg << USART_CR3_RXFTCFG_SHIFT; + } if (cflag & CSTOPB) cr2 |= USART_CR2_STOP_2B; @@ -790,7 +829,8 @@ static void stm32_usart_set_termios(struct uart_port *port, , bits); if (ofs->rtor != UNDEF_REG && (stm32_port->rx_ch || - stm32_port->fifoen)) { + (stm32_port->fifoen && + stm32_port->rxftcfg >= 0))) { if (cflag & CSTOPB) bits = bits + 3; /* 1 start bit + 2 stop bits */ else @@ -817,12 +857,6 @@ static void stm32_usart_set_termios(struct uart_port *port, cr3 |= USART_CR3_CTSE | USART_CR3_RTSE; } - /* Handle modem control interrupts */ - if (UART_ENABLE_MS(port, termios->c_cflag)) - stm32_usart_enable_ms(port); - else - stm32_usart_disable_ms(port); - usartdiv = DIV_ROUND_CLOSEST(port->uartclk, baud); /* @@ -892,12 +926,24 @@ static void stm32_usart_set_termios(struct uart_port *port, cr1 &= ~(USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK); } + /* Configure wake up from low power on start bit detection */ + if (stm32_port->wakeup_src) { + cr3 &= ~USART_CR3_WUS_MASK; + cr3 |= USART_CR3_WUS_START_BIT; + } + writel_relaxed(cr3, port->membase + ofs->cr3); writel_relaxed(cr2, port->membase + ofs->cr2); writel_relaxed(cr1, port->membase + ofs->cr1); stm32_usart_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); spin_unlock_irqrestore(&port->lock, flags); + + /* Handle modem control interrupts */ + if (UART_ENABLE_MS(port, termios->c_cflag)) + stm32_usart_enable_ms(port); + else + stm32_usart_disable_ms(port); } static const char *stm32_usart_type(struct uart_port *port) @@ -962,6 +1008,7 @@ static const struct uart_ops stm32_uart_ops = { .break_ctl = stm32_usart_break_ctl, .startup = stm32_usart_startup, .shutdown = stm32_usart_shutdown, + .flush_buffer = stm32_usart_flush_buffer, .set_termios = stm32_usart_set_termios, .pm = stm32_usart_pm, .type = stm32_usart_type, @@ -971,6 +1018,39 @@ static const struct uart_ops stm32_uart_ops = { .verify_port = stm32_usart_verify_port, }; +/* + * STM32H7 RX & TX FIFO threshold configuration (CR3 RXFTCFG / TXFTCFG) + * Note: 1 isn't a valid value in RXFTCFG / TXFTCFG. In this case, + * RXNEIE / TXEIE can be used instead of threshold irqs: RXFTIE / TXFTIE. + * So, RXFTCFG / TXFTCFG bitfields values are encoded as array index + 1. + */ +static const u32 stm32h7_usart_fifo_thresh_cfg[] = { 1, 2, 4, 8, 12, 14, 16 }; + +static void stm32_usart_get_ftcfg(struct platform_device *pdev, const char *p, + int *ftcfg) +{ + u32 bytes, i; + + /* DT option to get RX & TX FIFO threshold (default to 8 bytes) */ + if (of_property_read_u32(pdev->dev.of_node, p, &bytes)) + bytes = 8; + + for (i = 0; i < ARRAY_SIZE(stm32h7_usart_fifo_thresh_cfg); i++) + if (stm32h7_usart_fifo_thresh_cfg[i] >= bytes) + break; + if (i >= ARRAY_SIZE(stm32h7_usart_fifo_thresh_cfg)) + i = ARRAY_SIZE(stm32h7_usart_fifo_thresh_cfg) - 1; + + dev_dbg(&pdev->dev, "%s set to %d bytes\n", p, + stm32h7_usart_fifo_thresh_cfg[i]); + + /* Provide FIFO threshold ftcfg (1 is invalid: threshold irq unused) */ + if (i) + *ftcfg = i - 1; + else + *ftcfg = -EINVAL; +} + static void stm32_usart_deinit_port(struct stm32_port *stm32port) { clk_disable_unprepare(stm32port->clk); @@ -1000,13 +1080,19 @@ static int stm32_usart_init_port(struct stm32_port *stm32port, if (ret) return ret; - if (stm32port->info->cfg.has_wakeup) { - stm32port->wakeirq = platform_get_irq_optional(pdev, 1); - if (stm32port->wakeirq <= 0 && stm32port->wakeirq != -ENXIO) - return stm32port->wakeirq ? : -ENODEV; - } + stm32port->wakeup_src = stm32port->info->cfg.has_wakeup && + of_property_read_bool(pdev->dev.of_node, "wakeup-source"); + + stm32port->swap = stm32port->info->cfg.has_swap && + of_property_read_bool(pdev->dev.of_node, "rx-tx-swap"); stm32port->fifoen = stm32port->info->cfg.has_fifo; + if (stm32port->fifoen) { + stm32_usart_get_ftcfg(pdev, "rx-threshold", + &stm32port->rxftcfg); + stm32_usart_get_ftcfg(pdev, "tx-threshold", + &stm32port->txftcfg); + } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); port->membase = devm_ioremap_resource(&pdev->dev, res); @@ -1106,6 +1192,13 @@ static int stm32_usart_of_dma_rx_probe(struct stm32_port *stm32port, struct dma_async_tx_descriptor *desc = NULL; int ret; + /* + * Using DMA and threaded handler for the console could lead to + * deadlocks. + */ + if (uart_console(port)) + return -ENODEV; + /* Request DMA RX channel */ stm32port->rx_ch = dma_request_slave_channel(dev, "rx"); if (!stm32port->rx_ch) { @@ -1239,23 +1332,13 @@ static int stm32_usart_serial_probe(struct platform_device *pdev) if (ret) return ret; - if (stm32port->wakeirq > 0) { - ret = device_init_wakeup(&pdev->dev, true); - if (ret) - goto err_uninit; - - ret = dev_pm_set_dedicated_wake_irq(&pdev->dev, - stm32port->wakeirq); + if (stm32port->wakeup_src) { + device_set_wakeup_capable(&pdev->dev, true); + ret = dev_pm_set_wake_irq(&pdev->dev, stm32port->port.irq); if (ret) goto err_nowup; - - device_set_wakeup_enable(&pdev->dev, false); } - ret = uart_add_one_port(&stm32_usart_driver, &stm32port->port); - if (ret) - goto err_wirq; - ret = stm32_usart_of_dma_rx_probe(stm32port, pdev); if (ret) dev_info(&pdev->dev, "interrupt mode used for rx (no dma)\n"); @@ -1269,19 +1352,47 @@ static int stm32_usart_serial_probe(struct platform_device *pdev) pm_runtime_get_noresume(&pdev->dev); pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); + + ret = uart_add_one_port(&stm32_usart_driver, &stm32port->port); + if (ret) + goto err_port; + pm_runtime_put_sync(&pdev->dev); return 0; -err_wirq: - if (stm32port->wakeirq > 0) +err_port: + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + + if (stm32port->rx_ch) { + dmaengine_terminate_async(stm32port->rx_ch); + dma_release_channel(stm32port->rx_ch); + } + + if (stm32port->rx_dma_buf) + dma_free_coherent(&pdev->dev, + RX_BUF_L, stm32port->rx_buf, + stm32port->rx_dma_buf); + + if (stm32port->tx_ch) { + dmaengine_terminate_async(stm32port->tx_ch); + dma_release_channel(stm32port->tx_ch); + } + + if (stm32port->tx_dma_buf) + dma_free_coherent(&pdev->dev, + TX_BUF_L, stm32port->tx_buf, + stm32port->tx_dma_buf); + + if (stm32port->wakeup_src) dev_pm_clear_wake_irq(&pdev->dev); err_nowup: - if (stm32port->wakeirq > 0) - device_init_wakeup(&pdev->dev, false); + if (stm32port->wakeup_src) + device_set_wakeup_capable(&pdev->dev, false); -err_uninit: stm32_usart_deinit_port(stm32port); return ret; @@ -1295,11 +1406,20 @@ static int stm32_usart_serial_remove(struct platform_device *pdev) int err; pm_runtime_get_sync(&pdev->dev); + err = uart_remove_one_port(&stm32_usart_driver, port); + if (err) + return(err); + + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAR); - if (stm32_port->rx_ch) + if (stm32_port->rx_ch) { + dmaengine_terminate_async(stm32_port->rx_ch); dma_release_channel(stm32_port->rx_ch); + } if (stm32_port->rx_dma_buf) dma_free_coherent(&pdev->dev, @@ -1308,27 +1428,24 @@ static int stm32_usart_serial_remove(struct platform_device *pdev) stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT); - if (stm32_port->tx_ch) + if (stm32_port->tx_ch) { + dmaengine_terminate_async(stm32_port->tx_ch); dma_release_channel(stm32_port->tx_ch); + } if (stm32_port->tx_dma_buf) dma_free_coherent(&pdev->dev, TX_BUF_L, stm32_port->tx_buf, stm32_port->tx_dma_buf); - if (stm32_port->wakeirq > 0) { + if (stm32_port->wakeup_src) { dev_pm_clear_wake_irq(&pdev->dev); device_init_wakeup(&pdev->dev, false); } stm32_usart_deinit_port(stm32_port); - err = uart_remove_one_port(&stm32_usart_driver, port); - - pm_runtime_disable(&pdev->dev); - pm_runtime_put_noidle(&pdev->dev); - - return err; + return 0; } #ifdef CONFIG_SERIAL_STM32_CONSOLE @@ -1354,13 +1471,10 @@ static void stm32_usart_console_write(struct console *co, const char *s, u32 old_cr1, new_cr1; int locked = 1; - local_irq_save(flags); - if (port->sysrq) - locked = 0; - else if (oops_in_progress) - locked = spin_trylock(&port->lock); + if (oops_in_progress) + locked = spin_trylock_irqsave(&port->lock, flags); else - spin_lock(&port->lock); + spin_lock_irqsave(&port->lock, flags); /* Save and disable interrupts, enable the transmitter */ old_cr1 = readl_relaxed(port->membase + ofs->cr1); @@ -1374,8 +1488,7 @@ static void stm32_usart_console_write(struct console *co, const char *s, writel_relaxed(old_cr1, port->membase + ofs->cr1); if (locked) - spin_unlock(&port->lock); - local_irq_restore(flags); + spin_unlock_irqrestore(&port->lock, flags); } static int stm32_usart_console_setup(struct console *co, char *options) @@ -1436,23 +1549,20 @@ static void __maybe_unused stm32_usart_serial_en_wakeup(struct uart_port *port, { struct stm32_port *stm32_port = to_stm32_port(port); const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - const struct stm32_usart_config *cfg = &stm32_port->info->cfg; - u32 val; - if (stm32_port->wakeirq <= 0) + if (!stm32_port->wakeup_src) return; + /* + * Enable low-power wake-up and wake-up irq if argument is set to + * "enable", disable low-power wake-up and wake-up irq otherwise + */ if (enable) { - stm32_usart_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); stm32_usart_set_bits(port, ofs->cr1, USART_CR1_UESM); - val = readl_relaxed(port->membase + ofs->cr3); - val &= ~USART_CR3_WUS_MASK; - /* Enable Wake up interrupt from low power on start bit */ - val |= USART_CR3_WUS_START_BIT | USART_CR3_WUFIE; - writel_relaxed(val, port->membase + ofs->cr3); - stm32_usart_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); + stm32_usart_set_bits(port, ofs->cr3, USART_CR3_WUFIE); } else { stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_UESM); + stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_WUFIE); } } @@ -1462,10 +1572,8 @@ static int __maybe_unused stm32_usart_serial_suspend(struct device *dev) uart_suspend_port(&stm32_usart_driver, port); - if (device_may_wakeup(dev)) + if (device_may_wakeup(dev) || device_wakeup_path(dev)) stm32_usart_serial_en_wakeup(port, true); - else - stm32_usart_serial_en_wakeup(port, false); /* * When "no_console_suspend" is enabled, keep the pinctrl default state @@ -1474,7 +1582,7 @@ static int __maybe_unused stm32_usart_serial_suspend(struct device *dev) * capabilities. */ if (console_suspend_enabled || !uart_console(port)) { - if (device_may_wakeup(dev)) + if (device_may_wakeup(dev) || device_wakeup_path(dev)) pinctrl_pm_select_idle_state(dev); else pinctrl_pm_select_sleep_state(dev); @@ -1489,7 +1597,7 @@ static int __maybe_unused stm32_usart_serial_resume(struct device *dev) pinctrl_pm_select_default_state(dev); - if (device_may_wakeup(dev)) + if (device_may_wakeup(dev) || device_wakeup_path(dev)) stm32_usart_serial_en_wakeup(port, false); return uart_resume_port(&stm32_usart_driver, port); diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h index cb4f327c46db..07ac291328cd 100644 --- a/drivers/tty/serial/stm32-usart.h +++ b/drivers/tty/serial/stm32-usart.h @@ -25,6 +25,7 @@ struct stm32_usart_offsets { struct stm32_usart_config { u8 uart_enable_bit; /* USART_CR1_UE */ bool has_7bits_data; + bool has_swap; bool has_wakeup; bool has_fifo; int fifosize; @@ -76,6 +77,7 @@ struct stm32_usart_info stm32f7_info = { .cfg = { .uart_enable_bit = 0, .has_7bits_data = true, + .has_swap = true, .fifosize = 1, } }; @@ -97,6 +99,7 @@ struct stm32_usart_info stm32h7_info = { .cfg = { .uart_enable_bit = 0, .has_7bits_data = true, + .has_swap = true, .has_wakeup = true, .has_fifo = true, .fifosize = 16, @@ -127,9 +130,6 @@ struct stm32_usart_info stm32h7_info = { /* Dummy bits */ #define USART_SR_DUMMY_RX BIT(16) -/* USART_ICR (F7) */ -#define USART_CR_TC BIT(6) - /* USART_DR */ #define USART_DR_MASK GENMASK(8, 0) @@ -216,12 +216,6 @@ struct stm32_usart_info stm32h7_info = { #define USART_CR3_TXFTCFG_MASK GENMASK(31, 29) /* H7 */ #define USART_CR3_TXFTCFG_SHIFT 29 /* H7 */ -/* TX FIFO threashold set to half of its depth */ -#define USART_CR3_TXFTCFG_HALF 0x2 - -/* RX FIFO threashold set to half of its depth */ -#define USART_CR3_RXFTCFG_HALF 0x2 - /* USART_GTPR */ #define USART_GTPR_PSC_MASK GENMASK(7, 0) #define USART_GTPR_GT_MASK GENMASK(15, 8) @@ -271,8 +265,11 @@ struct stm32_port { int last_res; bool tx_dma_busy; /* dma tx busy */ bool hw_flow_control; + bool swap; /* swap RX & TX pins */ bool fifoen; - int wakeirq; + int rxftcfg; /* RX FIFO threshold CFG */ + int txftcfg; /* TX FIFO threshold CFG */ + bool wakeup_src; int rdr_mask; /* receive data register mask */ struct mctrl_gpios *gpios; /* modem control gpios */ }; diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index 319e5ceb6130..12c2468f2b0e 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -466,12 +466,8 @@ static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id) if (status & UART_LSR_THRE) transmit_chars(up); - spin_unlock_irqrestore(&up->port.lock, flags); - tty_flip_buffer_push(&up->port.state->port); - spin_lock_irqsave(&up->port.lock, flags); - } while (!(serial_in(up, UART_IIR) & UART_IIR_NO_INT)); spin_unlock_irqrestore(&up->port.lock, flags); diff --git a/drivers/tty/serial/tegra-tcu.c b/drivers/tty/serial/tegra-tcu.c index aaf8748a6147..52687c65ad74 100644 --- a/drivers/tty/serial/tegra-tcu.c +++ b/drivers/tty/serial/tegra-tcu.c @@ -282,6 +282,7 @@ static const struct of_device_id tegra_tcu_match[] = { { .compatible = "nvidia,tegra194-tcu" }, { } }; +MODULE_DEVICE_TABLE(of, tegra_tcu_match); static struct platform_driver tegra_tcu_driver = { .driver = { diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c index 2126e6e6dfd1..08941eabe7b1 100644 --- a/drivers/tty/serial/timbuart.c +++ b/drivers/tty/serial/timbuart.c @@ -87,9 +87,7 @@ static void timbuart_rx_chars(struct uart_port *port) tty_insert_flip_char(tport, ch, TTY_NORMAL); } - spin_unlock(&port->lock); tty_flip_buffer_push(tport); - spin_lock(&port->lock); dev_dbg(port->dev, "%s - total read %d bytes\n", __func__, port->icount.rx); diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 764e992438b2..c5edd56ff830 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -184,9 +184,7 @@ static void handle_rx(struct uart_port *port) tty_insert_flip_char(tport, c, flag); } - spin_unlock(&port->lock); tty_flip_buffer_push(tport); - spin_lock(&port->lock); } static void handle_tx(struct uart_port *port) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index a14c5d996473..67a2db621e2b 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -301,9 +301,8 @@ static void cdns_uart_handle_rx(void *dev_id, unsigned int isrstatus) tty_insert_flip_char(&port->state->port, data, status); isrstatus = 0; } - spin_unlock(&port->lock); + tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); } /** diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index 644173786bf0..5523cf7bd1c2 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -137,37 +137,14 @@ MODULE_PARM_DESC(maxframe, "Maximum frame size used by device (4096 to 65535)"); */ static struct tty_driver *serial_driver; -static int open(struct tty_struct *tty, struct file * filp); -static void close(struct tty_struct *tty, struct file * filp); -static void hangup(struct tty_struct *tty); -static void set_termios(struct tty_struct *tty, struct ktermios *old_termios); - -static int write(struct tty_struct *tty, const unsigned char *buf, int count); -static int put_char(struct tty_struct *tty, unsigned char ch); -static void send_xchar(struct tty_struct *tty, char ch); static void wait_until_sent(struct tty_struct *tty, int timeout); -static int write_room(struct tty_struct *tty); -static void flush_chars(struct tty_struct *tty); static void flush_buffer(struct tty_struct *tty); -static void tx_hold(struct tty_struct *tty); static void tx_release(struct tty_struct *tty); -static int ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); -static int chars_in_buffer(struct tty_struct *tty); -static void throttle(struct tty_struct * tty); -static void unthrottle(struct tty_struct * tty); -static int set_break(struct tty_struct *tty, int break_state); - /* - * generic HDLC support and callbacks + * generic HDLC support */ -#if SYNCLINK_GENERIC_HDLC #define dev_to_port(D) (dev_to_hdlc(D)->priv) -static void hdlcdev_tx_done(struct slgt_info *info); -static void hdlcdev_rx(struct slgt_info *info, char *buf, int size); -static int hdlcdev_init(struct slgt_info *info); -static void hdlcdev_exit(struct slgt_info *info); -#endif /* @@ -186,9 +163,6 @@ struct cond_wait { wait_queue_entry_t wait; unsigned int data; }; -static void init_cond_wait(struct cond_wait *w, unsigned int data); -static void add_cond_wait(struct cond_wait **head, struct cond_wait *w); -static void remove_cond_wait(struct cond_wait **head, struct cond_wait *w); static void flush_cond_wait(struct cond_wait **head); /* @@ -443,12 +417,8 @@ static void shutdown(struct slgt_info *info); static void program_hw(struct slgt_info *info); static void change_params(struct slgt_info *info); -static int register_test(struct slgt_info *info); -static int irq_test(struct slgt_info *info); -static int loopback_test(struct slgt_info *info); static int adapter_test(struct slgt_info *info); -static void reset_adapter(struct slgt_info *info); static void reset_port(struct slgt_info *info); static void async_mode(struct slgt_info *info); static void sync_mode(struct slgt_info *info); @@ -457,14 +427,12 @@ static void rx_stop(struct slgt_info *info); static void rx_start(struct slgt_info *info); static void reset_rbufs(struct slgt_info *info); static void free_rbufs(struct slgt_info *info, unsigned int first, unsigned int last); -static void rdma_reset(struct slgt_info *info); static bool rx_get_frame(struct slgt_info *info); static bool rx_get_buf(struct slgt_info *info); static void tx_start(struct slgt_info *info); static void tx_stop(struct slgt_info *info); static void tx_set_idle(struct slgt_info *info); -static unsigned int free_tbuf_count(struct slgt_info *info); static unsigned int tbuf_bytes(struct slgt_info *info); static void reset_tbufs(struct slgt_info *info); static void tdma_reset(struct slgt_info *info); @@ -472,26 +440,10 @@ static bool tx_load(struct slgt_info *info, const char *buf, unsigned int count) static void get_signals(struct slgt_info *info); static void set_signals(struct slgt_info *info); -static void enable_loopback(struct slgt_info *info); static void set_rate(struct slgt_info *info, u32 data_rate); -static int bh_action(struct slgt_info *info); -static void bh_handler(struct work_struct *work); static void bh_transmit(struct slgt_info *info); -static void isr_serial(struct slgt_info *info); -static void isr_rdma(struct slgt_info *info); static void isr_txeom(struct slgt_info *info, unsigned short status); -static void isr_tdma(struct slgt_info *info); - -static int alloc_dma_bufs(struct slgt_info *info); -static void free_dma_bufs(struct slgt_info *info); -static int alloc_desc(struct slgt_info *info); -static void free_desc(struct slgt_info *info); -static int alloc_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count); -static void free_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count); - -static int alloc_tmp_rbuf(struct slgt_info *info); -static void free_tmp_rbuf(struct slgt_info *info); static void tx_timeout(struct timer_list *t); static void rx_timeout(struct timer_list *t); @@ -509,10 +461,6 @@ static int tx_abort(struct slgt_info *info); static int rx_enable(struct slgt_info *info, int enable); static int modem_input_wait(struct slgt_info *info,int arg); static int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr); -static int tiocmget(struct tty_struct *tty); -static int tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear); -static int set_break(struct tty_struct *tty, int break_state); static int get_interface(struct slgt_info *info, int __user *if_mode); static int set_interface(struct slgt_info *info, int if_mode); static int set_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); @@ -526,9 +474,6 @@ static int set_xctrl(struct slgt_info *info, int if_mode); /* * driver functions */ -static void add_device(struct slgt_info *info); -static void device_init(int adapter_num, struct pci_dev *pdev); -static int claim_resources(struct slgt_info *info); static void release_resources(struct slgt_info *info); /* @@ -3566,8 +3511,6 @@ static struct slgt_info *alloc_dev(int adapter_num, int port_num, struct pci_dev info->max_frame_size = 4096; info->base_clock = 14745600; info->rbuf_fill_level = DMABUFSIZE; - info->port.close_delay = 5*HZ/10; - info->port.closing_wait = 30*HZ; init_waitqueue_head(&info->status_event_wait_q); init_waitqueue_head(&info->event_wait_q); spin_lock_init(&info->netlock); @@ -3705,7 +3648,6 @@ static const struct tty_operations ops = { static void slgt_cleanup(void) { - int rc; struct slgt_info *info; struct slgt_info *tmp; @@ -3714,9 +3656,7 @@ static void slgt_cleanup(void) if (serial_driver) { for (info=slgt_device_list ; info != NULL ; info=info->next_device) tty_unregister_device(serial_driver, info->line); - rc = tty_unregister_driver(serial_driver); - if (rc) - DBGERR(("tty_unregister_driver error=%d\n", rc)); + tty_unregister_driver(serial_driver); put_tty_driver(serial_driver); } @@ -5018,7 +4958,7 @@ static int loopback_test(struct slgt_info *info) #define TESTFRAMESIZE 20 unsigned long timeout; - u16 count = TESTFRAMESIZE; + u16 count; unsigned char buf[TESTFRAMESIZE]; int rc = -ENODEV; unsigned long flags; diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 959f9e121cc6..6628792431dc 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -118,6 +118,7 @@ static const struct sysrq_key_op sysrq_loglevel_op = { static void sysrq_handle_SAK(int key) { struct work_struct *SAK_work = &vc_cons[fg_console].SAK_work; + schedule_work(SAK_work); } static const struct sysrq_key_op sysrq_SAK_op = { @@ -547,22 +548,22 @@ static int sysrq_key_table_key2index(int key) */ static const struct sysrq_key_op *__sysrq_get_key_op(int key) { - const struct sysrq_key_op *op_p = NULL; - int i; + const struct sysrq_key_op *op_p = NULL; + int i; i = sysrq_key_table_key2index(key); if (i != -1) - op_p = sysrq_key_table[i]; + op_p = sysrq_key_table[i]; - return op_p; + return op_p; } static void __sysrq_put_key_op(int key, const struct sysrq_key_op *op_p) { - int i = sysrq_key_table_key2index(key); + int i = sysrq_key_table_key2index(key); - if (i != -1) - sysrq_key_table[i] = op_p; + if (i != -1) + sysrq_key_table[i] = op_p; } void __handle_sysrq(int key, bool check_mask) @@ -586,8 +587,8 @@ void __handle_sysrq(int key, bool check_mask) orig_log_level = console_loglevel; console_loglevel = CONSOLE_LOGLEVEL_DEFAULT; - op_p = __sysrq_get_key_op(key); - if (op_p) { + op_p = __sysrq_get_key_op(key); + if (op_p) { /* * Should we check for enabled operations (/proc/sysrq-trigger * should not) and is the invoked operation enabled? @@ -636,13 +637,13 @@ static int sysrq_reset_downtime_ms; /* Simple translation table for the SysRq keys */ static const unsigned char sysrq_xlate[KEY_CNT] = - "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ - "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ - "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ - "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ - "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ - "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ - "\r\000/"; /* 0x60 - 0x6f */ + "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ + "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ + "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ + "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ + "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ + "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ + "\r\000/"; /* 0x60 - 0x6f */ struct sysrq_state { struct input_handle handle; @@ -1107,7 +1108,7 @@ int sysrq_toggle_support(int enable_mask) EXPORT_SYMBOL_GPL(sysrq_toggle_support); static int __sysrq_swap_key_ops(int key, const struct sysrq_key_op *insert_op_p, - const struct sysrq_key_op *remove_op_p) + const struct sysrq_key_op *remove_op_p) { int retval; diff --git a/drivers/tty/tty.h b/drivers/tty/tty.h new file mode 100644 index 000000000000..b710c5ef89ab --- /dev/null +++ b/drivers/tty/tty.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * TTY core internal functions + */ + +#ifndef _TTY_INTERNAL_H +#define _TTY_INTERNAL_H + +#define tty_msg(fn, tty, f, ...) \ + fn("%s %s: " f, tty_driver_name(tty), tty_name(tty), ##__VA_ARGS__) + +#define tty_debug(tty, f, ...) tty_msg(pr_debug, tty, f, ##__VA_ARGS__) +#define tty_notice(tty, f, ...) tty_msg(pr_notice, tty, f, ##__VA_ARGS__) +#define tty_warn(tty, f, ...) tty_msg(pr_warn, tty, f, ##__VA_ARGS__) +#define tty_err(tty, f, ...) tty_msg(pr_err, tty, f, ##__VA_ARGS__) + +#define tty_info_ratelimited(tty, f, ...) \ + tty_msg(pr_info_ratelimited, tty, f, ##__VA_ARGS__) + +/* + * Lock subclasses for tty locks + * + * TTY_LOCK_NORMAL is for normal ttys and master ptys. + * TTY_LOCK_SLAVE is for slave ptys only. + * + * Lock subclasses are necessary for handling nested locking with pty pairs. + * tty locks which use nested locking: + * + * legacy_mutex - Nested tty locks are necessary for releasing pty pairs. + * The stable lock order is master pty first, then slave pty. + * termios_rwsem - The stable lock order is tty_buffer lock->termios_rwsem. + * Subclassing this lock enables the slave pty to hold its + * termios_rwsem when claiming the master tty_buffer lock. + * tty_buffer lock - slave ptys can claim nested buffer lock when handling + * signal chars. The stable lock order is slave pty, then + * master. + */ +enum { + TTY_LOCK_NORMAL = 0, + TTY_LOCK_SLAVE, +}; + +/* Values for tty->flow_change */ +#define TTY_THROTTLE_SAFE 1 +#define TTY_UNTHROTTLE_SAFE 2 + +static inline void __tty_set_flow_change(struct tty_struct *tty, int val) +{ + tty->flow_change = val; +} + +static inline void tty_set_flow_change(struct tty_struct *tty, int val) +{ + tty->flow_change = val; + smp_mb(); +} + +int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout); +void tty_ldisc_unlock(struct tty_struct *tty); + +int __tty_check_change(struct tty_struct *tty, int sig); +int tty_check_change(struct tty_struct *tty); +void __stop_tty(struct tty_struct *tty); +void __start_tty(struct tty_struct *tty); +void tty_vhangup_session(struct tty_struct *tty); +void tty_open_proc_set_tty(struct file *filp, struct tty_struct *tty); +int tty_signal_session_leader(struct tty_struct *tty, int exit_session); +void session_clear_tty(struct pid *session); +void tty_buffer_free_all(struct tty_port *port); +void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld); +void tty_buffer_init(struct tty_port *port); +void tty_buffer_set_lock_subclass(struct tty_port *port); +bool tty_buffer_restart_work(struct tty_port *port); +bool tty_buffer_cancel_work(struct tty_port *port); +void tty_buffer_flush_work(struct tty_port *port); +speed_t tty_termios_input_baud_rate(struct ktermios *termios); +void tty_ldisc_hangup(struct tty_struct *tty, bool reset); +int tty_ldisc_reinit(struct tty_struct *tty, int disc); +long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +long tty_jobctrl_ioctl(struct tty_struct *tty, struct tty_struct *real_tty, + struct file *file, unsigned int cmd, unsigned long arg); +void tty_default_fops(struct file_operations *fops); +struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx); +int tty_alloc_file(struct file *file); +void tty_add_file(struct tty_struct *tty, struct file *file); +void tty_free_file(struct file *file); +int tty_release(struct inode *inode, struct file *filp); + +#define tty_is_writelocked(tty) (mutex_is_locked(&tty->atomic_write_lock)) + +int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty); +void tty_ldisc_release(struct tty_struct *tty); +int __must_check tty_ldisc_init(struct tty_struct *tty); +void tty_ldisc_deinit(struct tty_struct *tty); + +void tty_sysctl_init(void); + +/* tty_audit.c */ +#ifdef CONFIG_AUDIT +void tty_audit_add_data(struct tty_struct *tty, const void *data, size_t size); +void tty_audit_tiocsti(struct tty_struct *tty, char ch); +#else +static inline void tty_audit_add_data(struct tty_struct *tty, const void *data, + size_t size) +{ +} +static inline void tty_audit_tiocsti(struct tty_struct *tty, char ch) +{ +} +#endif + +ssize_t redirected_tty_write(struct kiocb *, struct iov_iter *); + +#endif diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c index 32898aabcd06..ca7afd7b2716 100644 --- a/drivers/tty/tty_audit.c +++ b/drivers/tty/tty_audit.c @@ -10,6 +10,7 @@ #include <linux/audit.h> #include <linux/slab.h> #include <linux/tty.h> +#include "tty.h" struct tty_audit_buf { struct mutex mutex; /* Protects all data below */ diff --git a/drivers/tty/tty_baudrate.c b/drivers/tty/tty_baudrate.c index 6551b188b736..48b5de659c77 100644 --- a/drivers/tty/tty_baudrate.c +++ b/drivers/tty/tty_baudrate.c @@ -8,6 +8,7 @@ #include <linux/termios.h> #include <linux/tty.h> #include <linux/export.h> +#include "tty.h" /* diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 6d4995a5f318..9733469a14b2 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -17,7 +17,7 @@ #include <linux/delay.h> #include <linux/module.h> #include <linux/ratelimit.h> - +#include "tty.h" #define MIN_TTYB_SIZE 256 #define TTYB_ALIGN_MASK 255 diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 391bada4cedb..5b5e99604989 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -108,6 +108,7 @@ #include <linux/kmod.h> #include <linux/nsproxy.h> +#include "tty.h" #undef TTY_DEBUG_HANGUP #ifdef TTY_DEBUG_HANGUP @@ -546,7 +547,7 @@ EXPORT_SYMBOL_GPL(tty_wakeup); * This is available to the pty code so if the master closes, if the * slave is a redirect it can release the redirect. */ -struct file *tty_release_redirect(struct tty_struct *tty) +static struct file *tty_release_redirect(struct tty_struct *tty) { struct file *f = NULL; @@ -559,7 +560,6 @@ struct file *tty_release_redirect(struct tty_struct *tty) return f; } -EXPORT_SYMBOL_GPL(tty_release_redirect); /** * __tty_hangup - actual handler for hangup events @@ -1195,8 +1195,6 @@ int tty_send_xchar(struct tty_struct *tty, char ch) return 0; } -static char ptychar[] = "pqrstuvwxyzabcde"; - /** * pty_line_name - generate name for a pty * @driver: the tty driver in use @@ -1210,6 +1208,7 @@ static char ptychar[] = "pqrstuvwxyzabcde"; */ static void pty_line_name(struct tty_driver *driver, int index, char *p) { + static const char ptychar[] = "pqrstuvwxyzabcde"; int i = index + driver->name_base; /* ->name is initialized to "ttyp", but "tty" is expected */ sprintf(p, "%s%c%x", @@ -2530,14 +2529,14 @@ out: * @p: pointer to result * * Obtain the modem status bits from the tty driver if the feature - * is supported. Return -EINVAL if it is not available. + * is supported. Return -ENOTTY if it is not available. * * Locking: none (up to the driver) */ static int tty_tiocmget(struct tty_struct *tty, int __user *p) { - int retval = -EINVAL; + int retval = -ENOTTY; if (tty->ops->tiocmget) { retval = tty->ops->tiocmget(tty); @@ -2555,7 +2554,7 @@ static int tty_tiocmget(struct tty_struct *tty, int __user *p) * @p: pointer to desired bits * * Set the modem status bits from the tty driver if the feature - * is supported. Return -EINVAL if it is not available. + * is supported. Return -ENOTTY if it is not available. * * Locking: none (up to the driver) */ @@ -2567,7 +2566,7 @@ static int tty_tiocmset(struct tty_struct *tty, unsigned int cmd, unsigned int set, clear, val; if (tty->ops->tiocmset == NULL) - return -EINVAL; + return -ENOTTY; retval = get_user(val, p); if (retval) @@ -2607,7 +2606,7 @@ int tty_get_icount(struct tty_struct *tty, if (tty->ops->get_icount) return tty->ops->get_icount(tty, icount); else - return -EINVAL; + return -ENOTTY; } EXPORT_SYMBOL_GPL(tty_get_icount); @@ -2625,26 +2624,31 @@ static int tty_tiocgicount(struct tty_struct *tty, void __user *arg) return 0; } -static int tty_tiocsserial(struct tty_struct *tty, struct serial_struct __user *ss) +static int tty_set_serial(struct tty_struct *tty, struct serial_struct *ss) { - static DEFINE_RATELIMIT_STATE(depr_flags, - DEFAULT_RATELIMIT_INTERVAL, - DEFAULT_RATELIMIT_BURST); char comm[TASK_COMM_LEN]; - struct serial_struct v; int flags; - if (copy_from_user(&v, ss, sizeof(*ss))) - return -EFAULT; + flags = ss->flags & ASYNC_DEPRECATED; - flags = v.flags & ASYNC_DEPRECATED; + if (flags) + pr_warn_ratelimited("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n", + __func__, get_task_comm(comm, current), flags); - if (flags && __ratelimit(&depr_flags)) - pr_warn("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n", - __func__, get_task_comm(comm, current), flags); if (!tty->ops->set_serial) return -ENOTTY; - return tty->ops->set_serial(tty, &v); + + return tty->ops->set_serial(tty, ss); +} + +static int tty_tiocsserial(struct tty_struct *tty, struct serial_struct __user *ss) +{ + struct serial_struct v; + + if (copy_from_user(&v, ss, sizeof(*ss))) + return -EFAULT; + + return tty_set_serial(tty, &v); } static int tty_tiocgserial(struct tty_struct *tty, struct serial_struct __user *ss) @@ -2842,13 +2846,8 @@ struct serial_struct32 { static int compat_tty_tiocsserial(struct tty_struct *tty, struct serial_struct32 __user *ss) { - static DEFINE_RATELIMIT_STATE(depr_flags, - DEFAULT_RATELIMIT_INTERVAL, - DEFAULT_RATELIMIT_BURST); - char comm[TASK_COMM_LEN]; struct serial_struct32 v32; struct serial_struct v; - int flags; if (copy_from_user(&v32, ss, sizeof(*ss))) return -EFAULT; @@ -2859,14 +2858,7 @@ static int compat_tty_tiocsserial(struct tty_struct *tty, v.port_high = v32.port_high; v.iomap_base = 0; - flags = v.flags & ASYNC_DEPRECATED; - - if (flags && __ratelimit(&depr_flags)) - pr_warn("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n", - __func__, get_task_comm(comm, current), flags); - if (!tty->ops->set_serial) - return -ENOTTY; - return tty->ops->set_serial(tty, &v); + return tty_set_serial(tty, &v); } static int compat_tty_tiocgserial(struct tty_struct *tty, @@ -3524,21 +3516,14 @@ EXPORT_SYMBOL(tty_register_driver); /* * Called by a tty driver to unregister itself. */ -int tty_unregister_driver(struct tty_driver *driver) +void tty_unregister_driver(struct tty_driver *driver) { -#if 0 - /* FIXME */ - if (driver->refcount) - return -EBUSY; -#endif unregister_chrdev_region(MKDEV(driver->major, driver->minor_start), driver->num); mutex_lock(&tty_mutex); list_del(&driver->tty_drivers); mutex_unlock(&tty_mutex); - return 0; } - EXPORT_SYMBOL(tty_unregister_driver); dev_t tty_devnum(struct tty_struct *tty) diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 4de1c6ddb8ff..41f7449d0464 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -21,6 +21,7 @@ #include <linux/bitops.h> #include <linux/mutex.h> #include <linux/compat.h> +#include "tty.h" #include <asm/io.h> #include <linux/uaccess.h> @@ -57,8 +58,7 @@ int tty_chars_in_buffer(struct tty_struct *tty) { if (tty->ops->chars_in_buffer) return tty->ops->chars_in_buffer(tty); - else - return 0; + return 0; } EXPORT_SYMBOL(tty_chars_in_buffer); @@ -774,8 +774,8 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, case TCSETX: case TCSETXW: case TCSETXF: - return -EINVAL; -#endif + return -ENOTTY; +#endif case TIOCGSOFTCAR: copy_termios(real_tty, &kterm); ret = put_user((kterm.c_cflag & CLOCAL) ? 1 : 0, diff --git a/drivers/tty/tty_jobctrl.c b/drivers/tty/tty_jobctrl.c index 4b751b9285ad..7813dc910a19 100644 --- a/drivers/tty/tty_jobctrl.c +++ b/drivers/tty/tty_jobctrl.c @@ -11,6 +11,7 @@ #include <linux/tty.h> #include <linux/fcntl.h> #include <linux/uaccess.h> +#include "tty.h" static int is_ignored(int sig) { @@ -75,6 +76,7 @@ void proc_clear_tty(struct task_struct *p) { unsigned long flags; struct tty_struct *tty; + spin_lock_irqsave(&p->sighand->siglock, flags); tty = p->signal->tty; p->signal->tty = NULL; @@ -173,6 +175,7 @@ EXPORT_SYMBOL_GPL(get_current_tty); void session_clear_tty(struct pid *session) { struct task_struct *p; + do_each_pid_task(session, PIDTYPE_SID, p) { proc_clear_tty(p); } while_each_pid_task(session, PIDTYPE_SID, p); @@ -202,8 +205,10 @@ int tty_signal_session_leader(struct tty_struct *tty, int exit_session) spin_lock_irq(&p->sighand->siglock); if (p->signal->tty == tty) { p->signal->tty = NULL; - /* We defer the dereferences outside fo - the tasklist lock */ + /* + * We defer the dereferences outside of + * the tasklist lock. + */ refs++; } if (!p->signal->leader) { @@ -240,10 +245,10 @@ int tty_signal_session_leader(struct tty_struct *tty, int exit_session) * it wants to disassociate itself from its controlling tty. * * It performs the following functions: - * (1) Sends a SIGHUP and SIGCONT to the foreground process group - * (2) Clears the tty from being controlling the session - * (3) Clears the controlling tty for all processes in the - * session group. + * (1) Sends a SIGHUP and SIGCONT to the foreground process group + * (2) Clears the tty from being controlling the session + * (3) Clears the controlling tty for all processes in the + * session group. * * The argument on_exit is set to 1 if called when a process is * exiting; it is 0 if called by the ioctl TIOCNOTTY. @@ -269,6 +274,7 @@ void disassociate_ctty(int on_exit) tty_vhangup_session(tty); } else { struct pid *tty_pgrp = tty_get_pgrp(tty); + if (tty_pgrp) { kill_pgrp(tty_pgrp, SIGHUP, on_exit); if (!on_exit) @@ -280,6 +286,7 @@ void disassociate_ctty(int on_exit) } else if (on_exit) { struct pid *old_pgrp; + spin_lock_irq(¤t->sighand->siglock); old_pgrp = current->signal->tty_old_pgrp; current->signal->tty_old_pgrp = NULL; @@ -324,10 +331,13 @@ void disassociate_ctty(int on_exit) */ void no_tty(void) { - /* FIXME: Review locking here. The tty_lock never covered any race - between a new association and proc_clear_tty but possible we need - to protect against this anyway */ + /* + * FIXME: Review locking here. The tty_lock never covered any race + * between a new association and proc_clear_tty but possibly we need + * to protect against this anyway. + */ struct task_struct *tsk = current; + disassociate_ctty(0); proc_clear_tty(tsk); } @@ -531,7 +541,7 @@ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t _ /* * (tty == real_tty) is a cheap way of * testing if the tty is NOT a master pty. - */ + */ if (tty == real_tty && current->signal->tty != real_tty) return -ENOTTY; diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 1ba74d6f5e5c..03f414172f34 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -19,6 +19,7 @@ #include <linux/seq_file.h> #include <linux/uaccess.h> #include <linux/ratelimit.h> +#include "tty.h" #undef LDISC_DEBUG_HANGUP @@ -147,7 +148,7 @@ static int tty_ldisc_autoload = IS_BUILTIN(CONFIG_LDISC_AUTOLOAD); * Returns: -EINVAL if the discipline index is not [N_TTY..NR_LDISCS] or * if the discipline is not registered * -EAGAIN if request_module() failed to load or register the - * the discipline + * discipline * -ENOMEM if allocation failure * * Otherwise, returns a pointer to the discipline and bumps the @@ -459,7 +460,7 @@ static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld) WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags)); if (ld->ops->open) { int ret; - /* BTM here locks versus a hangup event */ + /* BTM here locks versus a hangup event */ ret = ld->ops->open(tty); if (ret) clear_bit(TTY_LDISC_OPEN, &tty->flags); @@ -508,7 +509,8 @@ static int tty_ldisc_failto(struct tty_struct *tty, int ld) return PTR_ERR(disc); tty->ldisc = disc; tty_set_termios_ldisc(tty, ld); - if ((r = tty_ldisc_open(tty, disc)) < 0) + r = tty_ldisc_open(tty, disc); + if (r < 0) tty_ldisc_put(disc); return r; } @@ -529,9 +531,11 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) const char *name = tty_name(tty); pr_warn("Falling back ldisc for %s.\n", name); - /* The traditional behaviour is to fall back to N_TTY, we - want to avoid falling back to N_NULL unless we have no - choice to avoid the risk of breaking anything */ + /* + * The traditional behaviour is to fall back to N_TTY, we + * want to avoid falling back to N_NULL unless we have no + * choice to avoid the risk of breaking anything + */ if (tty_ldisc_failto(tty, N_TTY) < 0 && tty_ldisc_failto(tty, N_NULL) < 0) panic("Couldn't open N_NULL ldisc for %s.", name); @@ -600,17 +604,21 @@ int tty_set_ldisc(struct tty_struct *tty, int disc) up_read(&tty->termios_rwsem); } - /* At this point we hold a reference to the new ldisc and a - reference to the old ldisc, or we hold two references to - the old ldisc (if it was restored as part of error cleanup - above). In either case, releasing a single reference from - the old ldisc is correct. */ + /* + * At this point we hold a reference to the new ldisc and a + * reference to the old ldisc, or we hold two references to + * the old ldisc (if it was restored as part of error cleanup + * above). In either case, releasing a single reference from + * the old ldisc is correct. + */ new_ldisc = old_ldisc; out: tty_ldisc_unlock(tty); - /* Restart the work queue in case no characters kick it off. Safe if - already running */ + /* + * Restart the work queue in case no characters kick it off. Safe if + * already running + */ tty_buffer_restart_work(tty->port); err: tty_ldisc_put(new_ldisc); /* drop the extra reference */ @@ -771,6 +779,7 @@ void tty_ldisc_hangup(struct tty_struct *tty, bool reinit) int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) { int retval = tty_ldisc_open(tty, tty->ldisc); + if (retval) return retval; @@ -811,8 +820,10 @@ void tty_ldisc_release(struct tty_struct *tty) tty_ldisc_kill(o_tty); tty_ldisc_unlock_pair(tty, o_tty); - /* And the memory resources remaining (buffers, termios) will be - disposed of when the kref hits zero */ + /* + * And the memory resources remaining (buffers, termios) will be + * disposed of when the kref hits zero + */ tty_ldisc_debug(tty, "released\n"); } @@ -829,6 +840,7 @@ EXPORT_SYMBOL_GPL(tty_ldisc_release); int tty_ldisc_init(struct tty_struct *tty) { struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY); + if (IS_ERR(ld)) return PTR_ERR(ld); tty->ldisc = ld; diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c index 2640635ee177..393518a24cfe 100644 --- a/drivers/tty/tty_mutex.c +++ b/drivers/tty/tty_mutex.c @@ -4,6 +4,7 @@ #include <linux/kallsyms.h> #include <linux/semaphore.h> #include <linux/sched.h> +#include "tty.h" /* Legacy tty mutex glue */ diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 346d20f4a486..303c198fbf5c 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -18,6 +18,7 @@ #include <linux/delay.h> #include <linux/module.h> #include <linux/serdev.h> +#include "tty.h" static int tty_port_default_receive_buf(struct tty_port *port, const unsigned char *p, diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index e2d6205f83ce..0a3a71e14df4 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -14,16 +14,9 @@ #include <asm/vio.h> #include <asm/ldc.h> -#define DRV_MODULE_NAME "vcc" -#define DRV_MODULE_VERSION "1.1" -#define DRV_MODULE_RELDATE "July 1, 2017" - -static char version[] = - DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")"; - MODULE_DESCRIPTION("Sun LDOM virtual console concentrator driver"); MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_MODULE_VERSION); +MODULE_VERSION("1.1"); struct vcc_port { struct vio_driver_state vio; @@ -59,16 +52,14 @@ struct vcc_port { #define VCC_CTL_BREAK -1 #define VCC_CTL_HUP -2 -static const char vcc_driver_name[] = "vcc"; -static const char vcc_device_node[] = "vcc"; static struct tty_driver *vcc_tty_driver; static struct vcc_port *vcc_table[VCC_MAX_PORTS]; static DEFINE_SPINLOCK(vcc_table_lock); -int vcc_dbg; -int vcc_dbg_ldc; -int vcc_dbg_vio; +static unsigned int vcc_dbg; +static unsigned int vcc_dbg_ldc; +static unsigned int vcc_dbg_vio; module_param(vcc_dbg, uint, 0664); module_param(vcc_dbg_ldc, uint, 0664); @@ -735,11 +726,6 @@ static int vcc_open(struct tty_struct *tty, struct file *vcc_file) { struct vcc_port *port; - if (unlikely(!tty)) { - pr_err("VCC: open: Invalid TTY handle\n"); - return -ENXIO; - } - if (tty->count > 1) return -EBUSY; @@ -773,11 +759,6 @@ static int vcc_open(struct tty_struct *tty, struct file *vcc_file) static void vcc_close(struct tty_struct *tty, struct file *vcc_file) { - if (unlikely(!tty)) { - pr_err("VCC: close: Invalid TTY handle\n"); - return; - } - if (unlikely(tty->count > 1)) return; @@ -805,11 +786,6 @@ static void vcc_hangup(struct tty_struct *tty) { struct vcc_port *port; - if (unlikely(!tty)) { - pr_err("VCC: hangup: Invalid TTY handle\n"); - return; - } - port = vcc_get_ne(tty->index); if (unlikely(!port)) { pr_err("VCC: hangup: Failed to find VCC port\n"); @@ -839,11 +815,6 @@ static int vcc_write(struct tty_struct *tty, const unsigned char *buf, int tosend = 0; int rv = -EINVAL; - if (unlikely(!tty)) { - pr_err("VCC: write: Invalid TTY handle\n"); - return -ENXIO; - } - port = vcc_get_ne(tty->index); if (unlikely(!port)) { pr_err("VCC: write: Failed to find VCC port"); @@ -904,15 +875,10 @@ static int vcc_write_room(struct tty_struct *tty) struct vcc_port *port; u64 num; - if (unlikely(!tty)) { - pr_err("VCC: write_room: Invalid TTY handle\n"); - return -ENXIO; - } - port = vcc_get_ne(tty->index); if (unlikely(!port)) { pr_err("VCC: write_room: Failed to find VCC port\n"); - return -ENODEV; + return 0; } num = VCC_BUFF_LEN - port->chars_in_buffer; @@ -927,15 +893,10 @@ static int vcc_chars_in_buffer(struct tty_struct *tty) struct vcc_port *port; u64 num; - if (unlikely(!tty)) { - pr_err("VCC: chars_in_buffer: Invalid TTY handle\n"); - return -ENXIO; - } - port = vcc_get_ne(tty->index); if (unlikely(!port)) { pr_err("VCC: chars_in_buffer: Failed to find VCC port\n"); - return -ENODEV; + return 0; } num = port->chars_in_buffer; @@ -950,11 +911,6 @@ static int vcc_break_ctl(struct tty_struct *tty, int state) struct vcc_port *port; unsigned long flags; - if (unlikely(!tty)) { - pr_err("VCC: break_ctl: Invalid TTY handle\n"); - return -ENXIO; - } - port = vcc_get_ne(tty->index); if (unlikely(!port)) { pr_err("VCC: break_ctl: Failed to find VCC port\n"); @@ -985,11 +941,6 @@ static int vcc_install(struct tty_driver *driver, struct tty_struct *tty) struct tty_port *port_tty; int ret; - if (unlikely(!tty)) { - pr_err("VCC: install: Invalid TTY handle\n"); - return -ENXIO; - } - if (tty->index >= VCC_MAX_PORTS) return -EINVAL; @@ -1024,11 +975,6 @@ static void vcc_cleanup(struct tty_struct *tty) { struct vcc_port *port; - if (unlikely(!tty)) { - pr_err("VCC: cleanup: Invalid TTY handle\n"); - return; - } - port = vcc_get(tty->index, true); if (port) { port->tty = NULL; @@ -1066,16 +1012,14 @@ static int vcc_tty_init(void) { int rv; - pr_info("VCC: %s\n", version); - vcc_tty_driver = tty_alloc_driver(VCC_MAX_PORTS, VCC_TTY_FLAGS); if (IS_ERR(vcc_tty_driver)) { pr_err("VCC: TTY driver alloc failed\n"); return PTR_ERR(vcc_tty_driver); } - vcc_tty_driver->driver_name = vcc_driver_name; - vcc_tty_driver->name = vcc_device_node; + vcc_tty_driver->driver_name = "vcc"; + vcc_tty_driver->name = "vcc"; vcc_tty_driver->minor_start = VCC_MINOR_START; vcc_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM; diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 77638629c562..5d2309742718 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -2186,7 +2186,7 @@ void vt_reset_unicode(int console) } /** - * vt_get_shiftstate - shift bit state + * vt_get_shift_state - shift bit state * * Report the shift bits from the keyboard state. We have to export * this to support some oddities in the vt layer. diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 284b07224c55..01645e87b3d5 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1381,6 +1381,7 @@ struct vc_data *vc_deallocate(unsigned int currcons) atomic_notifier_call_chain(&vt_notifier_list, VT_DEALLOCATE, ¶m); vcs_remove_sysfs(currcons); visual_deinit(vc); + con_free_unimap(vc); put_pid(vc->vt_pid); vc_uniscr_set(vc, NULL); kfree(vc->vc_screenbuf); @@ -4448,7 +4449,7 @@ void poke_blanked_console(void) might_sleep(); /* This isn't perfectly race free, but a race here would be mostly harmless, - * at worse, we'll do a spurrious blank and it's unlikely + * at worst, we'll do a spurious blank and it's unlikely */ del_timer(&console_timer); blank_timer_expired = 0; diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 701dfb32b129..bb3d39307d93 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -1042,7 +1042,7 @@ static int mos7720_write_room(struct tty_struct *tty) mos7720_port = usb_get_serial_port_data(port); if (mos7720_port == NULL) - return -ENODEV; + return 0; /* FIXME: Locking */ for (i = 0; i < NUM_URBS; ++i) { diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 962c12be9774..39258f9d36a0 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -79,7 +79,6 @@ static struct uni_pagedir *vgacon_uni_pagedir; static int vgacon_refcount; /* Description of the hardware situation */ -static bool vga_init_done; static unsigned long vga_vram_base __read_mostly; /* Base of video memory */ static unsigned long vga_vram_end __read_mostly; /* End of video memory */ static unsigned int vga_vram_size __read_mostly; /* Size of video memory */ @@ -96,7 +95,7 @@ static bool vga_is_gfx; static bool vga_512_chars; static int vga_video_font_height; static int vga_scan_lines __read_mostly; -static unsigned int vga_rolled_over; +static unsigned int vga_rolled_over; /* last vc_origin offset before wrap */ static bool vgacon_text_mode_force; static bool vga_hardscroll_enabled; @@ -359,8 +358,6 @@ static const char *vgacon_startup(void) vgacon_xres = screen_info.orig_video_cols * VGA_FONTWIDTH; vgacon_yres = vga_scan_lines; - vga_init_done = true; - return display_desc; } |