diff options
Diffstat (limited to 'drivers')
90 files changed, 7149 insertions, 1380 deletions
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index c8abce3..ed0fade 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -270,15 +270,10 @@ static int hci_uart_send_frame(struct sk_buff *skb) */ static int hci_uart_tty_open(struct tty_struct *tty) { - struct hci_uart *hu = (void *) tty->disc_data; + struct hci_uart *hu; BT_DBG("tty %p", tty); - /* FIXME: This btw is bogus, nothing requires the old ldisc to clear - the pointer */ - if (hu) - return -EEXIST; - /* Error if the tty has no write op instead of leaving an exploitable hole */ if (tty->ops->write == NULL) diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 21721d2..b66eaa0 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -549,8 +549,10 @@ static int mgslpc_probe(struct pcmcia_device *link) /* Initialize the struct pcmcia_device structure */ ret = mgslpc_config(link); - if (ret) + if (ret) { + tty_port_destroy(&info->port); return ret; + } mgslpc_add_device(info); @@ -2757,6 +2759,7 @@ static void mgslpc_remove_device(MGSLPC_INFO *remove_info) hdlcdev_exit(info); #endif release_resources(info); + tty_port_destroy(&info->port); kfree(info); mgslpc_device_count--; return; diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c index af98f6d..4945bd3 100644 --- a/drivers/char/ttyprintk.c +++ b/drivers/char/ttyprintk.c @@ -179,7 +179,6 @@ static int __init ttyprintk_init(void) { int ret = -ENOMEM; - tty_port_init(&tpk_port.port); tpk_port.port.ops = &null_ops; mutex_init(&tpk_port.port_write_mutex); @@ -190,6 +189,8 @@ static int __init ttyprintk_init(void) if (IS_ERR(ttyprintk_driver)) return PTR_ERR(ttyprintk_driver); + tty_port_init(&tpk_port.port); + ttyprintk_driver->driver_name = "ttyprintk"; ttyprintk_driver->name = "ttyprintk"; ttyprintk_driver->major = TTYAUX_MAJOR; @@ -211,6 +212,7 @@ static int __init ttyprintk_init(void) error: tty_unregister_driver(ttyprintk_driver); put_tty_driver(ttyprintk_driver); + tty_port_destroy(&tpk_port.port); ttyprintk_driver = NULL; return ret; } diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index c679867..89562a8 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -77,8 +77,6 @@ struct ackqueue_entry { }; struct capiminor { - struct kref kref; - unsigned int minor; struct capi20_appl *ap; @@ -190,7 +188,20 @@ static void capiminor_del_all_ack(struct capiminor *mp) /* -------- struct capiminor ---------------------------------------- */ -static const struct tty_port_operations capiminor_port_ops; /* we have none */ +static void capiminor_destroy(struct tty_port *port) +{ + struct capiminor *mp = container_of(port, struct capiminor, port); + + kfree_skb(mp->outskb); + skb_queue_purge(&mp->inqueue); + skb_queue_purge(&mp->outqueue); + capiminor_del_all_ack(mp); + kfree(mp); +} + +static const struct tty_port_operations capiminor_port_ops = { + .destruct = capiminor_destroy, +}; static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci) { @@ -204,8 +215,6 @@ static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci) return NULL; } - kref_init(&mp->kref); - mp->ap = ap; mp->ncci = ncci; INIT_LIST_HEAD(&mp->ackqueue); @@ -247,21 +256,10 @@ err_out2: spin_unlock(&capiminors_lock); err_out1: - kfree(mp); + tty_port_put(&mp->port); return NULL; } -static void capiminor_destroy(struct kref *kref) -{ - struct capiminor *mp = container_of(kref, struct capiminor, kref); - - kfree_skb(mp->outskb); - skb_queue_purge(&mp->inqueue); - skb_queue_purge(&mp->outqueue); - capiminor_del_all_ack(mp); - kfree(mp); -} - static struct capiminor *capiminor_get(unsigned int minor) { struct capiminor *mp; @@ -269,7 +267,7 @@ static struct capiminor *capiminor_get(unsigned int minor) spin_lock(&capiminors_lock); mp = capiminors[minor]; if (mp) - kref_get(&mp->kref); + tty_port_get(&mp->port); spin_unlock(&capiminors_lock); return mp; @@ -277,7 +275,7 @@ static struct capiminor *capiminor_get(unsigned int minor) static inline void capiminor_put(struct capiminor *mp) { - kref_put(&mp->kref, capiminor_destroy); + tty_port_put(&mp->port); } static void capiminor_free(struct capiminor *mp) diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c index 30a6b17..bc9d89a 100644 --- a/drivers/isdn/gigaset/common.c +++ b/drivers/isdn/gigaset/common.c @@ -518,6 +518,7 @@ f_bcs: gig_dbg(DEBUG_INIT, "freeing bcs[]"); kfree(cs->bcs); f_cs: gig_dbg(DEBUG_INIT, "freeing cs"); mutex_unlock(&cs->mutex); + tty_port_destroy(&cs->port); free_cs(cs); } EXPORT_SYMBOL_GPL(gigaset_freecs); @@ -751,14 +752,14 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, gig_dbg(DEBUG_INIT, "setting up iif"); if (gigaset_isdn_regdev(cs, modulename) < 0) { pr_err("error registering ISDN device\n"); - goto error; + goto error_port; } make_valid(cs, VALID_ID); ++cs->cs_init; gig_dbg(DEBUG_INIT, "setting up hw"); if (cs->ops->initcshw(cs) < 0) - goto error; + goto error_port; ++cs->cs_init; @@ -773,7 +774,7 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, gig_dbg(DEBUG_INIT, "setting up bcs[%d]", i); if (gigaset_initbcs(cs->bcs + i, cs, i) < 0) { pr_err("could not allocate channel %d data\n", i); - goto error; + goto error_port; } } @@ -786,7 +787,8 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, gig_dbg(DEBUG_INIT, "cs initialized"); return cs; - +error_port: + tty_port_destroy(&cs->port); error: gig_dbg(DEBUG_INIT, "failed"); gigaset_freecs(cs); diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index b817809..e09dc8a 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1849,6 +1849,8 @@ err_unregister: kfree(info->fax); #endif kfree(info->port.xmit_buf - 4); + info->port.xmit_buf = NULL; + tty_port_destroy(&info->port); } tty_unregister_driver(m->tty_modem); err: @@ -1870,6 +1872,8 @@ isdn_tty_exit(void) kfree(info->fax); #endif kfree(info->port.xmit_buf - 4); + info->port.xmit_buf = NULL; + tty_port_destroy(&info->port); } tty_unregister_driver(dev->mdm.tty_modem); put_tty_driver(dev->mdm.tty_modem); diff --git a/drivers/misc/pti.c b/drivers/misc/pti.c index 4999b34..a1f0d17 100644 --- a/drivers/misc/pti.c +++ b/drivers/misc/pti.c @@ -882,11 +882,14 @@ err: static void __devexit pti_pci_remove(struct pci_dev *pdev) { struct pti_dev *drv_data = pci_get_drvdata(pdev); + unsigned int a; unregister_console(&pti_console); - tty_unregister_device(pti_tty_driver, 0); - tty_unregister_device(pti_tty_driver, 1); + for (a = 0; a < PTITTY_MINOR_NUM; a++) { + tty_unregister_device(pti_tty_driver, a); + tty_port_destroy(&drv_data->port[a]); + } iounmap(drv_data->pti_ioaddr); pci_set_drvdata(pdev, NULL); diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index d2339ea..bd57a11 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -66,8 +66,6 @@ struct uart_icount { struct sdio_uart_port { struct tty_port port; - struct kref kref; - struct tty_struct *tty; unsigned int index; struct sdio_func *func; struct mutex func_lock; @@ -93,7 +91,6 @@ static int sdio_uart_add_port(struct sdio_uart_port *port) { int index, ret = -EBUSY; - kref_init(&port->kref); mutex_init(&port->func_lock); spin_lock_init(&port->write_lock); if (kfifo_alloc(&port->xmit_fifo, FIFO_SIZE, GFP_KERNEL)) @@ -123,23 +120,15 @@ static struct sdio_uart_port *sdio_uart_port_get(unsigned index) spin_lock(&sdio_uart_table_lock); port = sdio_uart_table[index]; if (port) - kref_get(&port->kref); + tty_port_get(&port->port); spin_unlock(&sdio_uart_table_lock); return port; } -static void sdio_uart_port_destroy(struct kref *kref) -{ - struct sdio_uart_port *port = - container_of(kref, struct sdio_uart_port, kref); - kfifo_free(&port->xmit_fifo); - kfree(port); -} - static void sdio_uart_port_put(struct sdio_uart_port *port) { - kref_put(&port->kref, sdio_uart_port_destroy); + tty_port_put(&port->port); } static void sdio_uart_port_remove(struct sdio_uart_port *port) @@ -737,6 +726,14 @@ static void sdio_uart_shutdown(struct tty_port *tport) sdio_uart_release_func(port); } +static void sdio_uart_port_destroy(struct tty_port *tport) +{ + struct sdio_uart_port *port = + container_of(tport, struct sdio_uart_port, port); + kfifo_free(&port->xmit_fifo); + kfree(port); +} + /** * sdio_uart_install - install method * @driver: the driver in use (sdio_uart in our case) @@ -1045,6 +1042,7 @@ static const struct tty_port_operations sdio_uart_port_ops = { .carrier_raised = uart_carrier_raised, .shutdown = sdio_uart_shutdown, .activate = sdio_uart_activate, + .destruct = sdio_uart_port_destroy, }; static const struct tty_operations sdio_uart_ops = { diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 605a4ba..cd8ccb2 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -2274,6 +2274,7 @@ static void hso_serial_common_free(struct hso_serial *serial) /* unlink and free TX URB */ usb_free_urb(serial->tx_urb); kfree(serial->tx_data); + tty_port_destroy(&serial->port); } static int hso_serial_common_create(struct hso_serial *serial, int num_urbs, @@ -2283,12 +2284,12 @@ static int hso_serial_common_create(struct hso_serial *serial, int num_urbs, int minor; int i; + tty_port_init(&serial->port); + minor = get_free_serial_index(); if (minor < 0) goto exit; - tty_port_init(&serial->port); - /* register our minor number */ serial->parent->dev = tty_port_register_device(&serial->port, tty_drv, minor, &serial->parent->interface->dev); diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 4ed343e..4008450 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -677,6 +677,7 @@ static void raw3215_free_info(struct raw3215_info *raw) { kfree(raw->inbuf); kfree(raw->buffer); + tty_port_destroy(&raw->port); kfree(raw); } diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index 30ec09e..877fbc3 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -547,7 +547,6 @@ sclp_tty_init(void) sclp_tty_tolower = 1; } sclp_tty_chars_count = 0; - tty_port_init(&sclp_port); rc = sclp_register(&sclp_input_event); if (rc) { @@ -555,6 +554,8 @@ sclp_tty_init(void) return rc; } + tty_port_init(&sclp_port); + driver->driver_name = "sclp_line"; driver->name = "sclp_line"; driver->major = TTY_MAJOR; @@ -571,6 +572,7 @@ sclp_tty_init(void) rc = tty_register_driver(driver); if (rc) { put_tty_driver(driver); + tty_port_destroy(&sclp_port); return rc; } sclp_tty_driver = driver; diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 7e60f3d..effcc87 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -615,6 +615,7 @@ static void __init __sclp_vt220_cleanup(void) return; sclp_unregister(&sclp_vt220_register); __sclp_vt220_free_pages(); + tty_port_destroy(&sclp_vt220_port); } /* Allocate buffer pages and register with sclp core. Controlled by init @@ -650,6 +651,7 @@ out: if (rc) { __sclp_vt220_free_pages(); sclp_vt220_init_count--; + tty_port_destroy(&sclp_vt220_port); } return rc; } diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 482ee02..43ea059 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -722,6 +722,7 @@ out_pages: while (pages--) free_pages((unsigned long) tp->freemem_pages[pages], 0); kfree(tp->freemem_pages); + tty_port_destroy(&tp->port); out_tp: kfree(tp); out_err: @@ -744,6 +745,7 @@ tty3270_free_view(struct tty3270 *tp) for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) free_pages((unsigned long) tp->freemem_pages[pages], 0); kfree(tp->freemem_pages); + tty_port_destroy(&tp->port); kfree(tp); } diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index d805eef..f245fd3 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -144,4 +144,6 @@ source "drivers/staging/imx-drm/Kconfig" source "drivers/staging/dgrp/Kconfig" +source "drivers/staging/sb105x/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 76e2ebd..94cc3fa 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -64,3 +64,4 @@ obj-$(CONFIG_NET_VENDOR_SILICOM) += silicom/ obj-$(CONFIG_CED1401) += ced1401/ obj-$(CONFIG_DRM_IMX) += imx-drm/ obj-$(CONFIG_DGRP) += dgrp/ +obj-$(CONFIG_SB105X) += sb105x/ diff --git a/drivers/staging/ccg/u_serial.c b/drivers/staging/ccg/u_serial.c index 5b3f5ff..373c406 100644 --- a/drivers/staging/ccg/u_serial.c +++ b/drivers/staging/ccg/u_serial.c @@ -1140,8 +1140,10 @@ int gserial_setup(struct usb_gadget *g, unsigned count) return status; fail: - while (count--) + while (count--) { + tty_port_destroy(&ports[count].port->port); kfree(ports[count].port); + } put_tty_driver(gs_tty_driver); gs_tty_driver = NULL; return status; @@ -1195,6 +1197,7 @@ void gserial_cleanup(void) WARN_ON(port->port_usb != NULL); + tty_port_destroy(&port->port); kfree(port); } n_ports = 0; diff --git a/drivers/staging/dgrp/dgrp_common.h b/drivers/staging/dgrp/dgrp_common.h index 05ff338..0583fe9 100644 --- a/drivers/staging/dgrp/dgrp_common.h +++ b/drivers/staging/dgrp/dgrp_common.h @@ -31,7 +31,6 @@ * All global storage allocation. ************************************************************************/ -extern int dgrp_rawreadok; /* Allow raw writing of input */ extern int dgrp_register_cudevices; /* enable legacy cu devices */ extern int dgrp_register_prdevices; /* enable transparent print devices */ extern int dgrp_poll_tick; /* Poll interval - in ms */ diff --git a/drivers/staging/dgrp/dgrp_driver.c b/drivers/staging/dgrp/dgrp_driver.c index 6e4a0eb..aa26258 100644 --- a/drivers/staging/dgrp/dgrp_driver.c +++ b/drivers/staging/dgrp/dgrp_driver.c @@ -39,14 +39,10 @@ MODULE_VERSION(DIGI_VERSION); struct list_head nd_struct_list; struct dgrp_poll_data dgrp_poll_data; -int dgrp_rawreadok = 1; /* Bypass flipbuf on input */ int dgrp_register_cudevices = 1;/* Turn on/off registering legacy cu devices */ int dgrp_register_prdevices = 1;/* Turn on/off registering transparent print */ int dgrp_poll_tick = 20; /* Poll interval - in ms */ -module_param_named(rawreadok, dgrp_rawreadok, int, 0644); -MODULE_PARM_DESC(rawreadok, "Bypass flip buffers on input"); - module_param_named(register_cudevices, dgrp_register_cudevices, int, 0644); MODULE_PARM_DESC(register_cudevices, "Turn on/off registering legacy cu devices"); diff --git a/drivers/staging/dgrp/dgrp_net_ops.c b/drivers/staging/dgrp/dgrp_net_ops.c index ab839ea..0788357 100644 --- a/drivers/staging/dgrp/dgrp_net_ops.c +++ b/drivers/staging/dgrp/dgrp_net_ops.c @@ -151,20 +151,15 @@ static void dgrp_read_data_block(struct ch_struct *ch, u8 *flipbuf, * Copys the rbuf to the flipbuf and sends to line discipline. * Sends input buffer data to the line discipline. * - * There are several modes to consider here: - * rawreadok, tty->real_raw, and IF_PARMRK */ static void dgrp_input(struct ch_struct *ch) { struct nd_struct *nd; struct tty_struct *tty; - int remain; int data_len; int len; - int flip_len; int tty_count; ulong lock_flags; - struct tty_ldisc *ld; u8 *myflipbuf; u8 *myflipflagbuf; @@ -212,37 +207,11 @@ static void dgrp_input(struct ch_struct *ch) spin_unlock_irqrestore(&nd->nd_lock, lock_flags); - /* Decide how much data we can send into the tty layer */ - if (dgrp_rawreadok && tty->real_raw) - flip_len = MYFLIPLEN; - else - flip_len = TTY_FLIPBUF_SIZE; - /* data_len should be the number of chars that we read in */ data_len = (ch->ch_rin - ch->ch_rout) & RBUF_MASK; - remain = data_len; /* len is the amount of data we are going to transfer here */ - len = min(data_len, flip_len); - - /* take into consideration length of ldisc */ - len = min(len, (N_TTY_BUF_SIZE - 1) - tty->read_cnt); - - ld = tty_ldisc_ref(tty); - - /* - * If we were unable to get a reference to the ld, - * don't flush our buffer, and act like the ld doesn't - * have any space to put the data right now. - */ - if (!ld) { - len = 0; - } else if (!ld->ops->receive_buf) { - spin_lock_irqsave(&nd->nd_lock, lock_flags); - ch->ch_rout = ch->ch_rin; - spin_unlock_irqrestore(&nd->nd_lock, lock_flags); - len = 0; - } + len = tty_buffer_request_room(tty, data_len); /* Check DPA flow control */ if ((nd->nd_dpa_debug) && @@ -254,42 +223,22 @@ static void dgrp_input(struct ch_struct *ch) dgrp_read_data_block(ch, myflipbuf, len); - /* - * In high performance mode, we don't have to update - * flag_buf or any of the counts or pointers into flip buf. - */ - if (!dgrp_rawreadok || !tty->real_raw) { - if (I_PARMRK(tty) || I_BRKINT(tty) || I_INPCK(tty)) - parity_scan(ch, myflipbuf, myflipflagbuf, &len); - else - memset(myflipflagbuf, TTY_NORMAL, len); - } + if (I_PARMRK(tty) || I_BRKINT(tty) || I_INPCK(tty)) + parity_scan(ch, myflipbuf, myflipflagbuf, &len); + else + memset(myflipflagbuf, TTY_NORMAL, len); if ((nd->nd_dpa_debug) && (nd->nd_dpa_port == PORT_NUM(MINOR(tty_devnum(tty))))) dgrp_dpa_data(nd, 1, myflipbuf, len); - /* - * If we're doing raw reads, jam it right into the - * line disc bypassing the flip buffers. - */ - if (dgrp_rawreadok && tty->real_raw) - ld->ops->receive_buf(tty, myflipbuf, NULL, len); - else { - len = tty_buffer_request_room(tty, len); - tty_insert_flip_string_flags(tty, myflipbuf, - myflipflagbuf, len); - - /* Tell the tty layer its okay to "eat" the data now */ - tty_flip_buffer_push(tty); - } + tty_insert_flip_string_flags(tty, myflipbuf, + myflipflagbuf, len); + tty_flip_buffer_push(tty); ch->ch_rxcount += len; } - if (ld) - tty_ldisc_deref(ld); - /* * Wake up any sleepers (maybe dgrp close) that might be waiting * for a channel flag state change. diff --git a/drivers/staging/dgrp/dgrp_specproc.c b/drivers/staging/dgrp/dgrp_specproc.c index 24327c3..c214078 100644 --- a/drivers/staging/dgrp/dgrp_specproc.c +++ b/drivers/staging/dgrp/dgrp_specproc.c @@ -629,8 +629,6 @@ static int info_proc_show(struct seq_file *m, void *v) { seq_printf(m, "version: %s\n", DIGI_VERSION); seq_puts(m, "register_with_sysfs: 1\n"); - seq_printf(m, "rawreadok: 0x%08x\t(%d)\n", - dgrp_rawreadok, dgrp_rawreadok); seq_printf(m, "pollrate: 0x%08x\t(%d)\n", dgrp_poll_tick, dgrp_poll_tick); @@ -754,6 +752,8 @@ static int dgrp_add_id(long id) return 0; + /* FIXME this guy should free the tty driver stored in nd and destroy + * all channel ports */ error_out: kfree(nd); return ret; diff --git a/drivers/staging/dgrp/dgrp_sysfs.c b/drivers/staging/dgrp/dgrp_sysfs.c index e5a3c88..8b513e9 100644 --- a/drivers/staging/dgrp/dgrp_sysfs.c +++ b/drivers/staging/dgrp/dgrp_sysfs.c @@ -55,23 +55,6 @@ static DEVICE_ATTR(register_with_sysfs, 0400, dgrp_class_register_with_sysfs_show, NULL); -static ssize_t dgrp_class_rawreadok_show(struct device *c, - struct device_attribute *attr, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", dgrp_rawreadok); -} -static ssize_t dgrp_class_rawreadok_store(struct device *c, - struct device_attribute *attr, - const char *buf, size_t count) -{ - sscanf(buf, "0x%x\n", &dgrp_rawreadok); - return count; -} -static DEVICE_ATTR(rawreadok, 0600, dgrp_class_rawreadok_show, - dgrp_class_rawreadok_store); - - static ssize_t dgrp_class_pollrate_show(struct device *c, struct device_attribute *attr, char *buf) @@ -91,7 +74,6 @@ static DEVICE_ATTR(pollrate, 0600, dgrp_class_pollrate_show, static struct attribute *dgrp_sysfs_global_settings_entries[] = { &dev_attr_pollrate.attr, - &dev_attr_rawreadok.attr, &dev_attr_register_with_sysfs.attr, NULL }; diff --git a/drivers/staging/dgrp/dgrp_tty.c b/drivers/staging/dgrp/dgrp_tty.c index e125b03..0db4c05 100644 --- a/drivers/staging/dgrp/dgrp_tty.c +++ b/drivers/staging/dgrp/dgrp_tty.c @@ -3119,6 +3119,7 @@ static void dgrp_tty_hangup(struct tty_struct *tty) void dgrp_tty_uninit(struct nd_struct *nd) { + unsigned int i; char id[3]; ID_TO_CHAR(nd->nd_ID, id); @@ -3152,6 +3153,8 @@ dgrp_tty_uninit(struct nd_struct *nd) put_tty_driver(nd->nd_xprint_ttdriver); nd->nd_ttdriver_flags &= ~XPRINT_TTDRV_REG; } + for (i = 0; i < CHAN_MAX; i++) + tty_port_destroy(&nd->nd_chan[i].port); } @@ -3335,7 +3338,6 @@ dgrp_tty_init(struct nd_struct *nd) init_waitqueue_head(&(ch->ch_pun.un_open_wait)); init_waitqueue_head(&(ch->ch_pun.un_close_wait)); tty_port_init(&ch->port); - tty_port_init(&ch->port); } return 0; } diff --git a/drivers/staging/ipack/devices/ipoctal.c b/drivers/staging/ipack/devices/ipoctal.c index d751edf..729cb64 100644 --- a/drivers/staging/ipack/devices/ipoctal.c +++ b/drivers/staging/ipack/devices/ipoctal.c @@ -446,6 +446,7 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, tty_dev = tty_port_register_device(&channel->tty_port, tty, i, NULL); if (IS_ERR(tty_dev)) { dev_err(&ipoctal->dev->dev, "Failed to register tty device.\n"); + tty_port_destroy(&channel->tty_port); continue; } dev_set_drvdata(tty_dev, channel); @@ -741,6 +742,7 @@ static void __ipoctal_remove(struct ipoctal *ipoctal) struct ipoctal_channel *channel = &ipoctal->channel[i]; tty_unregister_device(ipoctal->tty_drv, i); tty_port_free_xmit_buf(&channel->tty_port); + tty_port_destroy(&channel->tty_port); } tty_unregister_driver(ipoctal->tty_drv); diff --git a/drivers/staging/sb105x/Kconfig b/drivers/staging/sb105x/Kconfig new file mode 100644 index 0000000..ac87c5e --- /dev/null +++ b/drivers/staging/sb105x/Kconfig @@ -0,0 +1,9 @@ +config SB105X + tristate "SystemBase PCI Multiport UART" + select SERIAL_CORE + depends on PCI + help + A driver for the SystemBase Multi-2/PCI serial card + + To compile this driver a module, choose M here: the module + will be called "sb105x". diff --git a/drivers/staging/sb105x/Makefile b/drivers/staging/sb105x/Makefile new file mode 100644 index 0000000..b1bf377 --- /dev/null +++ b/drivers/staging/sb105x/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_SB105X) += sb105x.o + +sb105x-y := sb_pci_mp.o diff --git a/drivers/staging/sb105x/sb_mp_register.h b/drivers/staging/sb105x/sb_mp_register.h new file mode 100644 index 0000000..5480ae1 --- /dev/null +++ b/drivers/staging/sb105x/sb_mp_register.h @@ -0,0 +1,295 @@ + +/* + * SB105X_UART.h + * + * Copyright (C) 2008 systembase + * + * UART registers. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef UART_SB105X_H +#define UART_SB105X_H + +/* + * option register + */ + +/* Device Infomation Register */ +#define MP_OPTR_DIR0 0x04 /* port0 ~ port8 */ +#define MP_OPTR_DIR1 0x05 /* port8 ~ port15 */ +#define MP_OPTR_DIR2 0x06 /* port16 ~ port23 */ +#define MP_OPTR_DIR3 0x07 /* port24 ~ port31 */ + +#define DIR_UART_16C550 0 +#define DIR_UART_16C1050 1 +#define DIR_UART_16C1050A 2 + +#define DIR_CLK_1843200 0x0 /* input clock 1843200 Hz */ +#define DIR_CLK_3686400 0x1 /* input clock 3686400 Hz */ +#define DIR_CLK_7372800 0x2 /* input clock 7372800 Hz */ +#define DIR_CLK_14745600 0x3 /* input clock 14745600 Hz */ +#define DIR_CLK_29491200 0x4 /* input clock 29491200 Hz */ +#define DIR_CLK_58985400 0x5 /* input clock 58985400 Hz */ + +/* Interface Information Register */ +#define MP_OPTR_IIR0 0x08 /* port0 ~ port8 */ +#define MP_OPTR_IIR1 0x09 /* port8 ~ port15 */ +#define MP_OPTR_IIR2 0x0A /* port16 ~ port23 */ +#define MP_OPTR_IIR3 0x0B /* port24 ~ port31 */ + +#define IIR_RS232 0x00 /* RS232 type */ +#define IIR_RS422 0x10 /* RS422 type */ +#define IIR_RS485 0x20 /* RS485 type */ +#define IIR_UNKNOWN 0x30 /* unknown type */ + +/* Interrrupt Mask Register */ +#define MP_OPTR_IMR0 0x0C /* port0 ~ port8 */ +#define MP_OPTR_IMR1 0x0D /* port8 ~ port15 */ +#define MP_OPTR_IMR2 0x0E /* port16 ~ port23 */ +#define MP_OPTR_IMR3 0x0F /* port24 ~ port31 */ + +/* Interrupt Poll Register */ +#define MP_OPTR_IPR0 0x10 /* port0 ~ port8 */ +#define MP_OPTR_IPR1 0x11 /* port8 ~ port15 */ +#define MP_OPTR_IPR2 0x12 /* port16 ~ port23 */ +#define MP_OPTR_IPR3 0x13 /* port24 ~ port31 */ + +/* General Purpose Output Control Register */ +#define MP_OPTR_GPOCR 0x20 + +/* General Purpose Output Data Register */ +#define MP_OPTR_GPODR 0x21 + +/* Parallel Additional Function Register */ +#define MP_OPTR_PAFR 0x23 + +/* + * systembase 16c105x UART register + */ + +#define PAGE_0 0 +#define PAGE_1 1 +#define PAGE_2 2 +#define PAGE_3 3 +#define PAGE_4 4 + +/* + * ****************************************************************** + * * DLAB=0 =============== Page 0 Registers * + * ****************************************************************** + */ + +#define SB105X_RX 0 /* In: Receive buffer */ +#define SB105X_TX 0 /* Out: Transmit buffer */ + +#define SB105X_IER 1 /* Out: Interrupt Enable Register */ + +#define SB105X_IER_CTSI 0x80 /* CTS# Interrupt Enable (Requires EFR[4] = 1) */ +#define SB105X_IER_RTSI 0x40 /* RTS# Interrupt Enable (Requires EFR[4] = 1) */ +#define SB105X_IER_XOI 0x20 /* Xoff Interrupt Enable (Requires EFR[4] = 1) */ +#define SB105X_IER_SME 0x10 /* Sleep Mode Enable (Requires EFR[4] = 1) */ +#define SB105X_IER_MSI 0x08 /* Enable Modem status interrupt */ +#define SB105X_IER_RLSI 0x04 /* Enable receiver line status interrupt */ +#define SB105X_IER_THRI 0x02 /* Enable Transmitter holding register int. */ +#define SB105X_IER_RDI 0x01 /* Enable receiver data interrupt */ + +#define SB105X_ISR 2 /* In: Interrupt ID Register */ + +#define SB105X_ISR_NOINT 0x01 /* No interrupts pending */ +#define SB105X_ISR_RLSI 0x06 /* Receiver line status interrupt (Priority = 1)*/ +#define SB105X_ISR_RDAI 0x0c /* Receive Data Available interrupt */ +#define SB105X_ISR_CTII 0x04 /* Character Timeout Indication interrupt */ +#define SB105X_ISR_THRI 0x02 /* Transmitter holding register empty */ +#define SB105X_ISR_MSI 0x00 /* Modem status interrupt */ +#define SB105X_ISR_RXCI 0x10 /* Receive Xoff or Special Character interrupt */ +#define SB105X_ISR_RCSI 0x20 /* RTS#, CTS# status interrupt during Auto RTS/CTS flow control */ + +#define SB105X_FCR 2 /* Out: FIFO Control Register */ + +#define SB105X_FCR_FEN 0x01 /* FIFO Enable */ +#define SB105X_FCR_RXFR 0x02 /* RX FIFO Reset */ +#define SB105X_FCR_TXFR 0x04 /* TX FIFO Reset */ +#define SB105X_FCR_DMS 0x08 /* DMA Mode Select */ + +#define SB105X_FCR_RTR08 0x00 /* Receice Trigger Level set at 8 */ +#define SB105X_FCR_RTR16 0x40 /* Receice Trigger Level set at 16 */ +#define SB105X_FCR_RTR56 0x80 /* Receice Trigger Level set at 56 */ +#define SB105X_FCR_RTR60 0xc0 /* Receice Trigger Level set at 60 */ +#define SB105X_FCR_TTR08 0x00 /* Transmit Trigger Level set at 8 */ +#define SB105X_FCR_TTR16 0x10 /* Transmit Trigger Level set at 16 */ +#define SB105X_FCR_TTR32 0x20 /* Transmit Trigger Level set at 32 */ +#define SB105X_FCR_TTR56 0x30 /* Transmit Trigger Level set at 56 */ + +#define SB105X_LCR 3 /* Out: Line Control Register */ +/* + * * Note: if the word length is 5 bits (SB105X_LCR_WLEN5), then setting + * * SB105X_LCR_STOP will select 1.5 stop bits, not 2 stop bits. + */ +#define SB105X_LCR_DLAB 0x80 /* Divisor Latch Enable */ +#define SB105X_LCR_SBC 0x40 /* Break Enable*/ +#define SB105X_LCR_SPAR 0x20 /* Set Stick parity */ +#define SB105X_LCR_EPAR 0x10 /* Even parity select */ +#define SB105X_LCR_PAREN 0x08 /* Parity Enable */ +#define SB105X_LCR_STOP 0x04 /* Stop bits: 0->1 bit, 1->2 bits, 1 and SB105X_LCR_WLEN5 -> 1.5 bit */ +#define SB105X_LCR_WLEN5 0x00 /* Wordlength: 5 bits */ +#define SB105X_LCR_WLEN6 0x01 /* Wordlength: 6 bits */ +#define SB105X_LCR_WLEN7 0x02 /* Wordlength: 7 bits */ +#define SB105X_LCR_WLEN8 0x03 /* Wordlength: 8 bits */ + +#define SB105X_LCR_BF 0xBF + +#define SB105X_MCR 4 /* Out: Modem Control Register */ +#define SB105X_MCR_CPS 0x80 /* Clock Prescaler Select */ +#define SB105X_MCR_P2S 0x40 /* Page 2 Select /Xoff Re-Transmit Access Enable */ +#define SB105X_MCR_XOA 0x20 /* Xon Any Enable */ +#define SB105X_MCR_ILB 0x10 /* Internal Loopback Enable */ +#define SB105X_MCR_OUT2 0x08 /* Out2/Interrupt Output Enable*/ +#define SB105X_MCR_OUT1 0x04 /* Out1/Interrupt Output Enable */ +#define SB105X_MCR_RTS 0x02 /* RTS# Output */ +#define SB105X_MCR_DTR 0x01 /* DTR# Output */ + +#define SB105X_LSR 5 /* In: Line Status Register */ +#define SB105X_LSR_RFEI 0x80 /* Receive FIFO data error Indicator */ +#define SB105X_LSR_TEMI 0x40 /* THR and TSR Empty Indicator */ +#define SB105X_LSR_THRE 0x20 /* THR Empty Indicator */ +#define SB105X_LSR_BII 0x10 /* Break interrupt indicator */ +#define SB105X_LSR_FEI 0x08 /* Frame error indicator */ +#define SB105X_LSR_PEI 0x04 /* Parity error indicator */ +#define SB105X_LSR_OEI 0x02 /* Overrun error indicator */ +#define SB105X_LSR_RDRI 0x01 /* Receive data ready Indicator*/ + +#define SB105X_MSR 6 /* In: Modem Status Register */ +#define SB105X_MSR_DCD 0x80 /* Data Carrier Detect */ +#define SB105X_MSR_RI 0x40 /* Ring Indicator */ +#define SB105X_MSR_DSR 0x20 /* Data Set Ready */ +#define SB105X_MSR_CTS 0x10 /* Clear to Send */ +#define SB105X_MSR_DDCD 0x08 /* Delta DCD */ +#define SB105X_MSR_DRI 0x04 /* Delta ring indicator */ +#define SB105X_MSR_DDSR 0x02 /* Delta DSR */ +#define SB105X_MSR_DCTS 0x01 /* Delta CTS */ + +#define SB105XA_MDR 6 /* Out: Multi Drop mode Register */ +#define SB105XA_MDR_NPS 0x08 /* 9th Bit Polarity Select */ +#define SB105XA_MDR_AME 0x02 /* Auto Multi-drop Enable */ +#define SB105XA_MDR_MDE 0x01 /* Multi Drop Enable */ + +#define SB105X_SPR 7 /* I/O: Scratch Register */ + +/* + * DLAB=1 + */ +#define SB105X_DLL 0 /* Out: Divisor Latch Low */ +#define SB105X_DLM 1 /* Out: Divisor Latch High */ + +/* + * ****************************************************************** + * * DLAB(LCR[7]) = 0 , MCR[6] = 1 ============= Page 2 Registers * + * ****************************************************************** + */ +#define SB105X_GICR 1 /* Global Interrupt Control Register */ +#define SB105X_GICR_GIM 0x01 /* Global Interrupt Mask */ + +#define SB105X_GISR 2 /* Global Interrupt Status Register */ +#define SB105X_GISR_MGICR0 0x80 /* Mirror the content of GICR[0] */ +#define SB105X_GISR_CS3IS 0x08 /* SB105X of CS3# Interrupt Status */ +#define SB105X_GISR_CS2IS 0x04 /* SB105X of CS2# Interrupt Status */ +#define SB105X_GISR_CS1IS 0x02 /* SB105X of CS1# Interrupt Status */ +#define SB105X_GISR_CS0IS 0x01 /* SB105X of CS0# Interrupt Status */ + +#define SB105X_TFCR 5 /* Transmit FIFO Count Register */ + +#define SB105X_RFCR 6 /* Receive FIFO Count Register */ + +#define SB105X_FSR 7 /* Flow Control Status Register */ +#define SB105X_FSR_THFS 0x20 /* Transmit Hardware Flow Control Status */ +#define SB105X_FSR_TSFS 0x10 /* Transmit Software Flow Control Status */ +#define SB105X_FSR_RHFS 0x02 /* Receive Hardware Flow Control Status */ +#define SB105X_FSR_RSFS 0x01 /* Receive Software Flow Control Status */ + +/* + * ****************************************************************** + * * LCR = 0xBF, PSR[0] = 0 ============= Page 3 Registers * + * ****************************************************************** + */ + +#define SB105X_PSR 0 /* Page Select Register */ +#define SB105X_PSR_P3KEY 0xA4 /* Page 3 Select Key */ +#define SB105X_PSR_P4KEY 0xA5 /* Page 5 Select Key */ + +#define SB105X_ATR 1 /* Auto Toggle Control Register */ +#define SB105X_ATR_RPS 0x80 /* RXEN Polarity Select */ +#define SB105X_ATR_RCMS 0x40 /* RXEN Control Mode Select */ +#define SB105X_ATR_TPS 0x20 /* TXEN Polarity Select */ +#define SB105X_ATR_TCMS 0x10 /* TXEN Control Mode Select */ +#define SB105X_ATR_ATDIS 0x00 /* Auto Toggle is disabled */ +#define SB105X_ATR_ART 0x01 /* RTS#/TXEN pin operates as TXEN */ +#define SB105X_ATR_ADT 0x02 /* DTR#/TXEN pin operates as TXEN */ +#define SB105X_ATR_A80 0x03 /* only in 80 pin use */ + +#define SB105X_EFR 2 /* (Auto) Enhanced Feature Register */ +#define SB105X_EFR_ACTS 0x80 /* Auto-CTS Flow Control Enable */ +#define SB105X_EFR_ARTS 0x40 /* Auto-RTS Flow Control Enable */ +#define SB105X_EFR_SCD 0x20 /* Special Character Detect */ +#define SB105X_EFR_EFBEN 0x10 /* Enhanced Function Bits Enable */ + +#define SB105X_XON1 4 /* Xon1 Character Register */ +#define SB105X_XON2 5 /* Xon2 Character Register */ +#define SB105X_XOFF1 6 /* Xoff1 Character Register */ +#define SB105X_XOFF2 7 /* Xoff2 Character Register */ + +/* + * ****************************************************************** + * * LCR = 0xBF, PSR[0] = 1 ============ Page 4 Registers * + * ****************************************************************** + */ + +#define SB105X_AFR 1 /* Additional Feature Register */ +#define SB105X_AFR_GIPS 0x20 /* Global Interrupt Polarity Select */ +#define SB105X_AFR_GIEN 0x10 /* Global Interrupt Enable */ +#define SB105X_AFR_AFEN 0x01 /* 256-byte FIFO Enable */ + +#define SB105X_XRCR 2 /* Xoff Re-transmit Count Register */ +#define SB105X_XRCR_NRC1 0x00 /* Transmits Xoff Character whenever the number of received data is 1 during XOFF status */ +#define SB105X_XRCR_NRC4 0x01 /* Transmits Xoff Character whenever the number of received data is 4 during XOFF status */ +#define SB105X_XRCR_NRC8 0x02 /* Transmits Xoff Character whenever the number of received data is 8 during XOFF status */ +#define SB105X_XRCR_NRC16 0x03 /* Transmits Xoff Character whenever the number of received data is 16 during XOFF status */ + +#define SB105X_TTR 4 /* Transmit FIFO Trigger Level Register */ +#define SB105X_RTR 5 /* Receive FIFO Trigger Level Register */ +#define SB105X_FUR 6 /* Flow Control Upper Threshold Register */ +#define SB105X_FLR 7 /* Flow Control Lower Threshold Register */ + + +/* page 0 */ + +#define SB105X_GET_CHAR(port) inb((port)->iobase + SB105X_RX) +#define SB105X_GET_IER(port) inb((port)->iobase + SB105X_IER) +#define SB105X_GET_ISR(port) inb((port)->iobase + SB105X_ISR) +#define SB105X_GET_LCR(port) inb((port)->iobase + SB105X_LCR) +#define SB105X_GET_MCR(port) inb((port)->iobase + SB105X_MCR) +#define SB105X_GET_LSR(port) inb((port)->iobase + SB105X_LSR) +#define SB105X_GET_MSR(port) inb((port)->iobase + SB105X_MSR) +#define SB105X_GET_SPR(port) inb((port)->iobase + SB105X_SPR) + +#define SB105X_PUT_CHAR(port,v) outb((v),(port)->iobase + SB105X_TX ) +#define SB105X_PUT_IER(port,v) outb((v),(port)->iobase + SB105X_IER ) +#define SB105X_PUT_FCR(port,v) outb((v),(port)->iobase + SB105X_FCR ) +#define SB105X_PUT_LCR(port,v) outb((v),(port)->iobase + SB105X_LCR ) +#define SB105X_PUT_MCR(port,v) outb((v),(port)->iobase + SB105X_MCR ) +#define SB105X_PUT_SPR(port,v) outb((v),(port)->iobase + SB105X_SPR ) + + +/* page 1 */ +#define SB105X_GET_REG(port,reg) inb((port)->iobase + (reg)) +#define SB105X_PUT_REG(port,reg,v) outb((v),(port)->iobase + (reg)) + +/* page 2 */ + +#define SB105X_PUT_PSR(port,v) outb((v),(port)->iobase + SB105X_PSR ) + +#endif diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c new file mode 100644 index 0000000..fbebf88 --- /dev/null +++ b/drivers/staging/sb105x/sb_pci_mp.c @@ -0,0 +1,3195 @@ +#include "sb_pci_mp.h" +#include <linux/module.h> +#include <linux/parport.h> + +extern struct parport *parport_pc_probe_port(unsigned long base_lo, + unsigned long base_hi, + int irq, int dma, + struct device *dev, + int irqflags); + +static struct mp_device_t mp_devs[MAX_MP_DEV]; +static int mp_nrpcibrds = sizeof(mp_pciboards)/sizeof(mppcibrd_t); +static int NR_BOARD=0; +static int NR_PORTS=0; +static struct mp_port multi_ports[MAX_MP_PORT]; +static struct irq_info irq_lists[NR_IRQS]; + +static _INLINE_ unsigned int serial_in(struct mp_port *mtpt, int offset); +static _INLINE_ void serial_out(struct mp_port *mtpt, int offset, int value); +static _INLINE_ unsigned int read_option_register(struct mp_port *mtpt, int offset); +static int sb1054_get_register(struct sb_uart_port * port, int page, int reg); +static int sb1054_set_register(struct sb_uart_port * port, int page, int reg, int value); +static void SendATCommand(struct mp_port * mtpt); +static int set_deep_fifo(struct sb_uart_port * port, int status); +static int get_deep_fifo(struct sb_uart_port * port); +static int get_device_type(int arg); +static int set_auto_rts(struct sb_uart_port *port, int status); +static void mp_stop(struct tty_struct *tty); +static void __mp_start(struct tty_struct *tty); +static void mp_start(struct tty_struct *tty); +static void mp_tasklet_action(unsigned long data); +static inline void mp_update_mctrl(struct sb_uart_port *port, unsigned int set, unsigned int clear); +static int mp_startup(struct sb_uart_state *state, int init_hw); +static void mp_shutdown(struct sb_uart_state *state); +static void mp_change_speed(struct sb_uart_state *state, struct MP_TERMIOS *old_termios); + +static inline int __mp_put_char(struct sb_uart_port *port, struct circ_buf *circ, unsigned char c); +static int mp_put_char(struct tty_struct *tty, unsigned char ch); + +static void mp_put_chars(struct tty_struct *tty); +static int mp_write(struct tty_struct *tty, const unsigned char * buf, int count); +static int mp_write_room(struct tty_struct *tty); +static int mp_chars_in_buffer(struct tty_struct *tty); +static void mp_flush_buffer(struct tty_struct *tty); +static void mp_send_xchar(struct tty_struct *tty, char ch); +static void mp_throttle(struct tty_struct *tty); +static void mp_unthrottle(struct tty_struct *tty); +static int mp_get_info(struct sb_uart_state *state, struct serial_struct *retinfo); +static int mp_set_info(struct sb_uart_state *state, struct serial_struct *newinfo); +static int mp_get_lsr_info(struct sb_uart_state *state, unsigned int *value); + +static int mp_tiocmget(struct tty_struct *tty); +static int mp_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); +static int mp_break_ctl(struct tty_struct *tty, int break_state); +static int mp_do_autoconfig(struct sb_uart_state *state); +static int mp_wait_modem_status(struct sb_uart_state *state, unsigned long arg); +static int mp_get_count(struct sb_uart_state *state, struct serial_icounter_struct *icnt); +static int mp_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); +static void mp_set_termios(struct tty_struct *tty, struct MP_TERMIOS *old_termios); +static void mp_close(struct tty_struct *tty, struct file *filp); +static void mp_wait_until_sent(struct tty_struct *tty, int timeout); +static void mp_hangup(struct tty_struct *tty); +static void mp_update_termios(struct sb_uart_state *state); +static int mp_block_til_ready(struct file *filp, struct sb_uart_state *state); +static struct sb_uart_state *uart_get(struct uart_driver *drv, int line); +static int mp_open(struct tty_struct *tty, struct file *filp); +static const char *mp_type(struct sb_uart_port *port); +static void mp_change_pm(struct sb_uart_state *state, int pm_state); +static inline void mp_report_port(struct uart_driver *drv, struct sb_uart_port *port); +static void mp_configure_port(struct uart_driver *drv, struct sb_uart_state *state, struct sb_uart_port *port); +static void mp_unconfigure_port(struct uart_driver *drv, struct sb_uart_state *state); +static int mp_register_driver(struct uart_driver *drv); +static void mp_unregister_driver(struct uart_driver *drv); +static int mp_add_one_port(struct uart_driver *drv, struct sb_uart_port *port); +static int mp_remove_one_port(struct uart_driver *drv, struct sb_uart_port *port); +static void autoconfig(struct mp_port *mtpt, unsigned int probeflags); +static void autoconfig_irq(struct mp_port *mtpt); +static void multi_stop_tx(struct sb_uart_port *port); +static void multi_start_tx(struct sb_uart_port *port); +static void multi_stop_rx(struct sb_uart_port *port); +static void multi_enable_ms(struct sb_uart_port *port); +static _INLINE_ void receive_chars(struct mp_port *mtpt, int *status ); +static _INLINE_ void transmit_chars(struct mp_port *mtpt); +static _INLINE_ void check_modem_status(struct mp_port *mtpt); +static inline void multi_handle_port(struct mp_port *mtpt); +static irqreturn_t multi_interrupt(int irq, void *dev_id); +static void serial_do_unlink(struct irq_info *i, struct mp_port *mtpt); +static int serial_link_irq_chain(struct mp_port *mtpt); +static void serial_unlink_irq_chain(struct mp_port *mtpt); +static void multi_timeout(unsigned long data); +static unsigned int multi_tx_empty(struct sb_uart_port *port); +static unsigned int multi_get_mctrl(struct sb_uart_port *port); +static void multi_set_mctrl(struct sb_uart_port *port, unsigned int mctrl); +static void multi_break_ctl(struct sb_uart_port *port, int break_state); +static int multi_startup(struct sb_uart_port *port); +static void multi_shutdown(struct sb_uart_port *port); +static unsigned int multi_get_divisor(struct sb_uart_port *port, unsigned int baud); +static void multi_set_termios(struct sb_uart_port *port, struct MP_TERMIOS *termios, struct MP_TERMIOS *old); +static void multi_pm(struct sb_uart_port *port, unsigned int state, unsigned int oldstate); +static void multi_release_std_resource(struct mp_port *mtpt); +static void multi_release_port(struct sb_uart_port *port); +static int multi_request_port(struct sb_uart_port *port); +static void multi_config_port(struct sb_uart_port *port, int flags); +static int multi_verify_port(struct sb_uart_port *port, struct serial_struct *ser); +static const char * multi_type(struct sb_uart_port *port); +static void __init multi_init_ports(void); +static void __init multi_register_ports(struct uart_driver *drv); +static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd); + +static int deep[256]; +static int deep_count; +static int fcr_arr[256]; +static int fcr_count; +static int ttr[256]; +static int ttr_count; +static int rtr[256]; +static int rtr_count; + +module_param_array(deep,int,&deep_count,0); +module_param_array(fcr_arr,int,&fcr_count,0); +module_param_array(ttr,int,&ttr_count,0); +module_param_array(rtr,int,&rtr_count,0); + +static _INLINE_ unsigned int serial_in(struct mp_port *mtpt, int offset) +{ + return inb(mtpt->port.iobase + offset); +} + +static _INLINE_ void serial_out(struct mp_port *mtpt, int offset, int value) +{ + outb(value, mtpt->port.iobase + offset); +} + +static _INLINE_ unsigned int read_option_register(struct mp_port *mtpt, int offset) +{ + return inb(mtpt->option_base_addr + offset); +} + +static int sb1053a_get_interface(struct mp_port *mtpt, int port_num) +{ + unsigned long option_base_addr = mtpt->option_base_addr; + unsigned int interface = 0; + + switch (port_num) + { + case 0: + case 1: + /* set GPO[1:0] = 00 */ + outb(0x00, option_base_addr + MP_OPTR_GPODR); + break; + case 2: + case 3: + /* set GPO[1:0] = 01 */ + outb(0x01, option_base_addr + MP_OPTR_GPODR); + break; + case 4: + case 5: + /* set GPO[1:0] = 10 */ + outb(0x02, option_base_addr + MP_OPTR_GPODR); + break; + default: + break; + } + + port_num &= 0x1; + + /* get interface */ + interface = inb(option_base_addr + MP_OPTR_IIR0 + port_num); + + /* set GPO[1:0] = 11 */ + outb(0x03, option_base_addr + MP_OPTR_GPODR); + + return (interface); +} + +static int sb1054_get_register(struct sb_uart_port * port, int page, int reg) +{ + int ret = 0; + unsigned int lcr = 0; + unsigned int mcr = 0; + unsigned int tmp = 0; + + if( page <= 0) + { + printk(" page 0 can not use this fuction\n"); + return -1; + } + + switch(page) + { + case 1: + lcr = SB105X_GET_LCR(port); + tmp = lcr | SB105X_LCR_DLAB; + SB105X_PUT_LCR(port, tmp); + + tmp = SB105X_GET_LCR(port); + + ret = SB105X_GET_REG(port,reg); + SB105X_PUT_LCR(port,lcr); + break; + case 2: + mcr = SB105X_GET_MCR(port); + tmp = mcr | SB105X_MCR_P2S; + SB105X_PUT_MCR(port,tmp); + + ret = SB105X_GET_REG(port,reg); + + SB105X_PUT_MCR(port,mcr); + break; + case 3: + lcr = SB105X_GET_LCR(port); + tmp = lcr | SB105X_LCR_BF; + SB105X_PUT_LCR(port,tmp); + SB105X_PUT_REG(port,SB105X_PSR,SB105X_PSR_P3KEY); + + ret = SB105X_GET_REG(port,reg); + + SB105X_PUT_LCR(port,lcr); + break; + case 4: + lcr = SB105X_GET_LCR(port); + tmp = lcr | SB105X_LCR_BF; + SB105X_PUT_LCR(port,tmp); + SB105X_PUT_REG(port,SB105X_PSR,SB105X_PSR_P4KEY); + + ret = SB105X_GET_REG(port,reg); + + SB105X_PUT_LCR(port,lcr); + break; + default: + printk(" error invalid page number \n"); + return -1; + } + + return ret; +} + +static int sb1054_set_register(struct sb_uart_port * port, int page, int reg, int value) +{ + int lcr = 0; + int mcr = 0; + int ret = 0; + + if( page <= 0) + { + printk(" page 0 can not use this fuction\n"); + return -1; + } + switch(page) + { + case 1: + lcr = SB105X_GET_LCR(port); + SB105X_PUT_LCR(port, lcr | SB105X_LCR_DLAB); + + SB105X_PUT_REG(port,reg,value); + + SB105X_PUT_LCR(port, lcr); + ret = 1; + break; + case 2: + mcr = SB105X_GET_MCR(port); + SB105X_PUT_MCR(port, mcr | SB105X_MCR_P2S); + + SB105X_PUT_REG(port,reg,value); + + SB105X_PUT_MCR(port, mcr); + ret = 1; + break; + case 3: + lcr = SB105X_GET_LCR(port); + SB105X_PUT_LCR(port, lcr | SB105X_LCR_BF); + SB105X_PUT_PSR(port, SB105X_PSR_P3KEY); + + SB105X_PUT_REG(port,reg,value); + + SB105X_PUT_LCR(port, lcr); + ret = 1; + break; + case 4: + lcr = SB105X_GET_LCR(port); + SB105X_PUT_LCR(port, lcr | SB105X_LCR_BF); + SB105X_PUT_PSR(port, SB105X_PSR_P4KEY); + + SB105X_PUT_REG(port,reg,value); + + SB105X_PUT_LCR(port, lcr); + ret = 1; + break; + default: + printk(" error invalid page number \n"); + return -1; + } + + return ret; +} + +static int set_multidrop_mode(struct sb_uart_port *port, unsigned int mode) +{ + int mdr = SB105XA_MDR_NPS; + + if (mode & MDMODE_ENABLE) + { + mdr |= SB105XA_MDR_MDE; + } + + if (1) //(mode & MDMODE_AUTO) + { + int efr = 0; + mdr |= SB105XA_MDR_AME; + efr = sb1054_get_register(port, PAGE_3, SB105X_EFR); + efr |= SB105X_EFR_SCD; + sb1054_set_register(port, PAGE_3, SB105X_EFR, efr); + } + + sb1054_set_register(port, PAGE_1, SB105XA_MDR, mdr); + port->mdmode &= ~0x6; + port->mdmode |= mode; + printk("[%d] multidrop init: %x\n", port->line, port->mdmode); + + return 0; +} + +static int get_multidrop_addr(struct sb_uart_port *port) +{ + return sb1054_get_register(port, PAGE_3, SB105X_XOFF2); +} + +static int set_multidrop_addr(struct sb_uart_port *port, unsigned int addr) +{ + sb1054_set_register(port, PAGE_3, SB105X_XOFF2, addr); + + return 0; +} + +static void SendATCommand(struct mp_port * mtpt) +{ + // a t cr lf + unsigned char ch[] = {0x61,0x74,0x0d,0x0a,0x0}; + unsigned char lineControl; + unsigned char i=0; + unsigned char Divisor = 0xc; + + lineControl = serial_inp(mtpt,UART_LCR); + serial_outp(mtpt,UART_LCR,(lineControl | UART_LCR_DLAB)); + serial_outp(mtpt,UART_DLL,(Divisor & 0xff)); + serial_outp(mtpt,UART_DLM,(Divisor & 0xff00)>>8); //baudrate is 4800 + + + serial_outp(mtpt,UART_LCR,lineControl); + serial_outp(mtpt,UART_LCR,0x03); // N-8-1 + serial_outp(mtpt,UART_FCR,7); + serial_outp(mtpt,UART_MCR,0x3); + while(ch[i]){ + while((serial_inp(mtpt,UART_LSR) & 0x60) !=0x60){ + ; + } + serial_outp(mtpt,0,ch[i++]); + } + + +}// end of SendATCommand() + +static int set_deep_fifo(struct sb_uart_port * port, int status) +{ + int afr_status = 0; + afr_status = sb1054_get_register(port, PAGE_4, SB105X_AFR); + + if(status == ENABLE) + { + afr_status |= SB105X_AFR_AFEN; + } + else + { + afr_status &= ~SB105X_AFR_AFEN; + } + + sb1054_set_register(port,PAGE_4,SB105X_AFR,afr_status); + sb1054_set_register(port,PAGE_4,SB105X_TTR,ttr[port->line]); + sb1054_set_register(port,PAGE_4,SB105X_RTR,rtr[port->line]); + afr_status = sb1054_get_register(port, PAGE_4, SB105X_AFR); + + return afr_status; +} + +static int get_device_type(int arg) +{ + int ret; + ret = inb(mp_devs[arg].option_reg_addr+MP_OPTR_DIR0); + ret = (ret & 0xf0) >> 4; + switch (ret) + { + case DIR_UART_16C550: + return PORT_16C55X; + case DIR_UART_16C1050: + return PORT_16C105X; + case DIR_UART_16C1050A: + /* + if (mtpt->port.line < 2) + { + return PORT_16C105XA; + } + else + { + if (mtpt->device->device_id & 0x50) + { + return PORT_16C55X; + } + else + { + return PORT_16C105X; + } + }*/ + return PORT_16C105XA; + default: + return PORT_UNKNOWN; + } + +} +static int get_deep_fifo(struct sb_uart_port * port) +{ + int afr_status = 0; + afr_status = sb1054_get_register(port, PAGE_4, SB105X_AFR); + return afr_status; +} + +static int set_auto_rts(struct sb_uart_port *port, int status) +{ + int atr_status = 0; + +#if 0 + int efr_status = 0; + + efr_status = sb1054_get_register(port, PAGE_3, SB105X_EFR); + if(status == ENABLE) + efr_status |= SB105X_EFR_ARTS; + else + efr_status &= ~SB105X_EFR_ARTS; + sb1054_set_register(port,PAGE_3,SB105X_EFR,efr_status); + efr_status = sb1054_get_register(port, PAGE_3, SB105X_EFR); +#endif + +//ATR + atr_status = sb1054_get_register(port, PAGE_3, SB105X_ATR); + switch(status) + { + case RS422PTP: + atr_status = (SB105X_ATR_TPS) | (SB105X_ATR_A80); + break; + case RS422MD: + atr_status = (SB105X_ATR_TPS) | (SB105X_ATR_TCMS) | (SB105X_ATR_A80); + break; + case RS485NE: + atr_status = (SB105X_ATR_RCMS) | (SB105X_ATR_TPS) | (SB105X_ATR_TCMS) | (SB105X_ATR_A80); + break; + case RS485ECHO: + atr_status = (SB105X_ATR_TPS) | (SB105X_ATR_TCMS) | (SB105X_ATR_A80); + break; + } + + sb1054_set_register(port,PAGE_3,SB105X_ATR,atr_status); + atr_status = sb1054_get_register(port, PAGE_3, SB105X_ATR); + + return atr_status; +} + +static void mp_stop(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + port->ops->stop_tx(port); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void __mp_start(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + + if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf && + !tty->stopped && !tty->hw_stopped) + port->ops->start_tx(port); +} + +static void mp_start(struct tty_struct *tty) +{ + __mp_start(tty); +} + +static void mp_tasklet_action(unsigned long data) +{ + struct sb_uart_state *state = (struct sb_uart_state *)data; + struct tty_struct *tty; + + printk("tasklet is called!\n"); + tty = state->info->tty; + tty_wakeup(tty); +} + +static inline void mp_update_mctrl(struct sb_uart_port *port, unsigned int set, unsigned int clear) +{ + unsigned int old; + + old = port->mctrl; + port->mctrl = (old & ~clear) | set; + if (old != port->mctrl) + port->ops->set_mctrl(port, port->mctrl); +} + +#define uart_set_mctrl(port,set) mp_update_mctrl(port,set,0) +#define uart_clear_mctrl(port,clear) mp_update_mctrl(port,0,clear) + +static int mp_startup(struct sb_uart_state *state, int init_hw) +{ + struct sb_uart_info *info = state->info; + struct sb_uart_port *port = state->port; + unsigned long page; + int retval = 0; + + if (info->flags & UIF_INITIALIZED) + return 0; + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + if (port->type == PORT_UNKNOWN) + return 0; + + if (!info->xmit.buf) { + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + info->xmit.buf = (unsigned char *) page; + + uart_circ_clear(&info->xmit); + } + + retval = port->ops->startup(port); + if (retval == 0) { + if (init_hw) { + mp_change_speed(state, NULL); + + if (info->tty->termios.c_cflag & CBAUD) + uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); + } + + info->flags |= UIF_INITIALIZED; + + + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + + if (retval && capable(CAP_SYS_ADMIN)) + retval = 0; + + return retval; +} + +static void mp_shutdown(struct sb_uart_state *state) +{ + struct sb_uart_info *info = state->info; + struct sb_uart_port *port = state->port; + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + if (info->flags & UIF_INITIALIZED) { + info->flags &= ~UIF_INITIALIZED; + + if (!info->tty || (info->tty->termios.c_cflag & HUPCL)) + uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); + + wake_up_interruptible(&info->delta_msr_wait); + + port->ops->shutdown(port); + + synchronize_irq(port->irq); + } + tasklet_kill(&info->tlet); + + if (info->xmit.buf) { + free_page((unsigned long)info->xmit.buf); + info->xmit.buf = NULL; + } +} + +static void mp_change_speed(struct sb_uart_state *state, struct MP_TERMIOS *old_termios) +{ + struct tty_struct *tty = state->info->tty; + struct sb_uart_port *port = state->port; + + if (!tty || port->type == PORT_UNKNOWN) + return; + + if (tty->termios.c_cflag & CRTSCTS) + state->info->flags |= UIF_CTS_FLOW; + else + state->info->flags &= ~UIF_CTS_FLOW; + + if (tty->termios.c_cflag & CLOCAL) + state->info->flags &= ~UIF_CHECK_CD; + else + state->info->flags |= UIF_CHECK_CD; + + port->ops->set_termios(port, &tty->termios, old_termios); +} + +static inline int __mp_put_char(struct sb_uart_port *port, struct circ_buf *circ, unsigned char c) +{ + unsigned long flags; + int ret = 0; + + if (!circ->buf) + return 0; + + spin_lock_irqsave(&port->lock, flags); + if (uart_circ_chars_free(circ) != 0) { + circ->buf[circ->head] = c; + circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); + ret = 1; + } + spin_unlock_irqrestore(&port->lock, flags); + return ret; +} + +static int mp_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct sb_uart_state *state = tty->driver_data; + + return __mp_put_char(state->port, &state->info->xmit, ch); +} + +static void mp_put_chars(struct tty_struct *tty) +{ + mp_start(tty); +} + +static int mp_write(struct tty_struct *tty, const unsigned char * buf, int count) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port; + struct circ_buf *circ; + int c, ret = 0; + + if (!state || !state->info) { + return -EL3HLT; + } + + port = state->port; + circ = &state->info->xmit; + + if (!circ->buf) + return 0; + + while (1) { + c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + memcpy(circ->buf + circ->head, buf, c); + + circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); + buf += c; + count -= c; + ret += c; + } + mp_start(tty); + return ret; +} + +static int mp_write_room(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + + return uart_circ_chars_free(&state->info->xmit); +} + +static int mp_chars_in_buffer(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + + return uart_circ_chars_pending(&state->info->xmit); +} + +static void mp_flush_buffer(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + unsigned long flags; + + if (!state || !state->info) { + return; + } + + spin_lock_irqsave(&port->lock, flags); + uart_circ_clear(&state->info->xmit); + spin_unlock_irqrestore(&port->lock, flags); + wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); +} + +static void mp_send_xchar(struct tty_struct *tty, char ch) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + unsigned long flags; + + if (port->ops->send_xchar) + port->ops->send_xchar(port, ch); + else { + port->x_char = ch; + if (ch) { + spin_lock_irqsave(&port->lock, flags); + port->ops->start_tx(port); + spin_unlock_irqrestore(&port->lock, flags); + } + } +} + +static void mp_throttle(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + + if (I_IXOFF(tty)) + mp_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios.c_cflag & CRTSCTS) + uart_clear_mctrl(state->port, TIOCM_RTS); +} + +static void mp_unthrottle(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + + if (I_IXOFF(tty)) { + if (port->x_char) + port->x_char = 0; + else + mp_send_xchar(tty, START_CHAR(tty)); + } + + if (tty->termios.c_cflag & CRTSCTS) + uart_set_mctrl(port, TIOCM_RTS); +} + +static int mp_get_info(struct sb_uart_state *state, struct serial_struct *retinfo) +{ + struct sb_uart_port *port = state->port; + struct serial_struct tmp; + + memset(&tmp, 0, sizeof(tmp)); + tmp.type = port->type; + tmp.line = port->line; + tmp.port = port->iobase; + if (HIGH_BITS_OFFSET) + tmp.port_high = (long) port->iobase >> HIGH_BITS_OFFSET; + tmp.irq = port->irq; + tmp.flags = port->flags; + tmp.xmit_fifo_size = port->fifosize; + tmp.baud_base = port->uartclk / 16; + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait == USF_CLOSING_WAIT_NONE ? + ASYNC_CLOSING_WAIT_NONE : + state->closing_wait; + tmp.custom_divisor = port->custom_divisor; + tmp.hub6 = port->hub6; + tmp.io_type = port->iotype; + tmp.iomem_reg_shift = port->regshift; + tmp.iomem_base = (void *)port->mapbase; + + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int mp_set_info(struct sb_uart_state *state, struct serial_struct *newinfo) +{ + struct serial_struct new_serial; + struct sb_uart_port *port = state->port; + unsigned long new_port; + unsigned int change_irq, change_port, closing_wait; + unsigned int old_custom_divisor; + unsigned int old_flags, new_flags; + int retval = 0; + + if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) + return -EFAULT; + + new_port = new_serial.port; + if (HIGH_BITS_OFFSET) + new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; + + new_serial.irq = irq_canonicalize(new_serial.irq); + + closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? + USF_CLOSING_WAIT_NONE : new_serial.closing_wait; + MP_STATE_LOCK(state); + + change_irq = new_serial.irq != port->irq; + change_port = new_port != port->iobase || + (unsigned long)new_serial.iomem_base != port->mapbase || + new_serial.hub6 != port->hub6 || + new_serial.io_type != port->iotype || + new_serial.iomem_reg_shift != port->regshift || + new_serial.type != port->type; + old_flags = port->flags; + new_flags = new_serial.flags; + old_custom_divisor = port->custom_divisor; + + if (!capable(CAP_SYS_ADMIN)) { + retval = -EPERM; + if (change_irq || change_port || + (new_serial.baud_base != port->uartclk / 16) || + (new_serial.close_delay != state->close_delay) || + (closing_wait != state->closing_wait) || + (new_serial.xmit_fifo_size != port->fifosize) || + (((new_flags ^ old_flags) & ~UPF_USR_MASK) != 0)) + goto exit; + port->flags = ((port->flags & ~UPF_USR_MASK) | + (new_flags & UPF_USR_MASK)); + port->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + if (port->ops->verify_port) + retval = port->ops->verify_port(port, &new_serial); + + if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || + (new_serial.baud_base < 9600)) + retval = -EINVAL; + + if (retval) + goto exit; + + if (change_port || change_irq) { + retval = -EBUSY; + + if (uart_users(state) > 1) + goto exit; + + mp_shutdown(state); + } + + if (change_port) { + unsigned long old_iobase, old_mapbase; + unsigned int old_type, old_iotype, old_hub6, old_shift; + + old_iobase = port->iobase; + old_mapbase = port->mapbase; + old_type = port->type; + old_hub6 = port->hub6; + old_iotype = port->iotype; + old_shift = port->regshift; + + if (old_type != PORT_UNKNOWN) + port->ops->release_port(port); + + port->iobase = new_port; + port->type = new_serial.type; + port->hub6 = new_serial.hub6; + port->iotype = new_serial.io_type; + port->regshift = new_serial.iomem_reg_shift; + port->mapbase = (unsigned long)new_serial.iomem_base; + + if (port->type != PORT_UNKNOWN) { + retval = port->ops->request_port(port); + } else { + retval = 0; + } + + if (retval && old_type != PORT_UNKNOWN) { + port->iobase = old_iobase; + port->type = old_type; + port->hub6 = old_hub6; + port->iotype = old_iotype; + port->regshift = old_shift; + port->mapbase = old_mapbase; + retval = port->ops->request_port(port); + if (retval) + port->type = PORT_UNKNOWN; + + retval = -EBUSY; + } + } + + port->irq = new_serial.irq; + port->uartclk = new_serial.baud_base * 16; + port->flags = (port->flags & ~UPF_CHANGE_MASK) | + (new_flags & UPF_CHANGE_MASK); + port->custom_divisor = new_serial.custom_divisor; + state->close_delay = new_serial.close_delay; + state->closing_wait = closing_wait; + port->fifosize = new_serial.xmit_fifo_size; + if (state->info->tty) + state->info->tty->low_latency = + (port->flags & UPF_LOW_LATENCY) ? 1 : 0; + +check_and_exit: + retval = 0; + if (port->type == PORT_UNKNOWN) + goto exit; + if (state->info->flags & UIF_INITIALIZED) { + if (((old_flags ^ port->flags) & UPF_SPD_MASK) || + old_custom_divisor != port->custom_divisor) { + if (port->flags & UPF_SPD_MASK) { + printk(KERN_NOTICE + "%s sets custom speed on ttyMP%d. This " + "is deprecated.\n", current->comm, + port->line); + } + mp_change_speed(state, NULL); + } + } else + retval = mp_startup(state, 1); +exit: + MP_STATE_UNLOCK(state); + return retval; +} + + +static int mp_get_lsr_info(struct sb_uart_state *state, unsigned int *value) +{ + struct sb_uart_port *port = state->port; + unsigned int result; + + result = port->ops->tx_empty(port); + + if (port->x_char || + ((uart_circ_chars_pending(&state->info->xmit) > 0) && + !state->info->tty->stopped && !state->info->tty->hw_stopped)) + result &= ~TIOCSER_TEMT; + + return put_user(result, value); +} + +static int mp_tiocmget(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + int result = -EIO; + + MP_STATE_LOCK(state); + if (!(tty->flags & (1 << TTY_IO_ERROR))) { + result = port->mctrl; + spin_lock_irq(&port->lock); + result |= port->ops->get_mctrl(port); + spin_unlock_irq(&port->lock); + } + MP_STATE_UNLOCK(state); + return result; +} + +static int mp_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + int ret = -EIO; + + + MP_STATE_LOCK(state); + if (!(tty->flags & (1 << TTY_IO_ERROR))) { + mp_update_mctrl(port, set, clear); + ret = 0; + } + MP_STATE_UNLOCK(state); + + return ret; +} + +static int mp_break_ctl(struct tty_struct *tty, int break_state) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + + MP_STATE_LOCK(state); + + if (port->type != PORT_UNKNOWN) + port->ops->break_ctl(port, break_state); + + MP_STATE_UNLOCK(state); + return 0; +} + +static int mp_do_autoconfig(struct sb_uart_state *state) +{ + struct sb_uart_port *port = state->port; + int flags, ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (mutex_lock_interruptible(&state->mutex)) + return -ERESTARTSYS; + ret = -EBUSY; + if (uart_users(state) == 1) { + mp_shutdown(state); + + if (port->type != PORT_UNKNOWN) + port->ops->release_port(port); + + flags = UART_CONFIG_TYPE; + if (port->flags & UPF_AUTO_IRQ) + flags |= UART_CONFIG_IRQ; + + port->ops->config_port(port, flags); + + ret = mp_startup(state, 1); + } + MP_STATE_UNLOCK(state); + return ret; +} + +static int mp_wait_modem_status(struct sb_uart_state *state, unsigned long arg) +{ + struct sb_uart_port *port = state->port; + DECLARE_WAITQUEUE(wait, current); + struct sb_uart_icount cprev, cnow; + int ret; + + spin_lock_irq(&port->lock); + memcpy(&cprev, &port->icount, sizeof(struct sb_uart_icount)); + + port->ops->enable_ms(port); + spin_unlock_irq(&port->lock); + + add_wait_queue(&state->info->delta_msr_wait, &wait); + for (;;) { + spin_lock_irq(&port->lock); + memcpy(&cnow, &port->icount, sizeof(struct sb_uart_icount)); + spin_unlock_irq(&port->lock); + + set_current_state(TASK_INTERRUPTIBLE); + + if (((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))) { + ret = 0; + break; + } + + schedule(); + + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + cprev = cnow; + } + + current->state = TASK_RUNNING; + remove_wait_queue(&state->info->delta_msr_wait, &wait); + + return ret; +} + +static int mp_get_count(struct sb_uart_state *state, struct serial_icounter_struct *icnt) +{ + struct serial_icounter_struct icount; + struct sb_uart_icount cnow; + struct sb_uart_port *port = state->port; + + spin_lock_irq(&port->lock); + memcpy(&cnow, &port->icount, sizeof(struct sb_uart_icount)); + spin_unlock_irq(&port->lock); + + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0; +} + +static int mp_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) +{ + struct sb_uart_state *state = tty->driver_data; + struct mp_port *info = (struct mp_port *)state->port; + int ret = -ENOIOCTLCMD; + + + switch (cmd) { + case TIOCSMULTIDROP: + /* set multi-drop mode enable or disable, and default operation mode is H/W mode */ + if (info->port.type == PORT_16C105XA) + { + //arg &= ~0x6; + //state->port->mdmode = 0; + return set_multidrop_mode((struct sb_uart_port *)info, (unsigned int)arg); + } + ret = -ENOTSUPP; + break; + case GETDEEPFIFO: + ret = get_deep_fifo(state->port); + return ret; + case SETDEEPFIFO: + ret = set_deep_fifo(state->port,arg); + deep[state->port->line] = arg; + return ret; + case SETTTR: + if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ + ret = sb1054_set_register(state->port,PAGE_4,SB105X_TTR,arg); + ttr[state->port->line] = arg; + } + return ret; + case SETRTR: + if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ + ret = sb1054_set_register(state->port,PAGE_4,SB105X_RTR,arg); + rtr[state->port->line] = arg; + } + return ret; + case GETTTR: + if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ + ret = sb1054_get_register(state->port,PAGE_4,SB105X_TTR); + } + return ret; + case GETRTR: + if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ + ret = sb1054_get_register(state->port,PAGE_4,SB105X_RTR); + } + return ret; + + case SETFCR: + if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ + ret = sb1054_set_register(state->port,PAGE_1,SB105X_FCR,arg); + } + else{ + serial_out(info,2,arg); + } + + return ret; + case TIOCSMDADDR: + /* set multi-drop address */ + if (info->port.type == PORT_16C105XA) + { + state->port->mdmode |= MDMODE_ADDR; + return set_multidrop_addr((struct sb_uart_port *)info, (unsigned int)arg); + } + ret = -ENOTSUPP; + break; + + case TIOCGMDADDR: + /* set multi-drop address */ + if ((info->port.type == PORT_16C105XA) && (state->port->mdmode & MDMODE_ADDR)) + { + return get_multidrop_addr((struct sb_uart_port *)info); + } + ret = -ENOTSUPP; + break; + + case TIOCSENDADDR: + /* send address in multi-drop mode */ + if ((info->port.type == PORT_16C105XA) + && (state->port->mdmode & (MDMODE_ENABLE))) + { + if (mp_chars_in_buffer(tty) > 0) + { + tty_wait_until_sent(tty, 0); + } + //while ((serial_in(info, UART_LSR) & 0x60) != 0x60); + //while (sb1054_get_register(state->port, PAGE_2, SB105X_TFCR) != 0); + while ((serial_in(info, UART_LSR) & 0x60) != 0x60); + serial_out(info, UART_SCR, (int)arg); + } + break; + + case TIOCGSERIAL: + ret = mp_get_info(state, (struct serial_struct *)arg); + break; + + case TIOCSSERIAL: + ret = mp_set_info(state, (struct serial_struct *)arg); + break; + + case TIOCSERCONFIG: + ret = mp_do_autoconfig(state); + break; + + case TIOCSERGWILD: /* obsolete */ + case TIOCSERSWILD: /* obsolete */ + ret = 0; + break; + /* for Multiport */ + case TIOCGNUMOFPORT: /* Get number of ports */ + return NR_PORTS; + case TIOCGGETDEVID: + return mp_devs[arg].device_id; + case TIOCGGETREV: + return mp_devs[arg].revision; + case TIOCGGETNRPORTS: + return mp_devs[arg].nr_ports; + case TIOCGGETBDNO: + return NR_BOARD; + case TIOCGGETINTERFACE: + if (mp_devs[arg].revision == 0xc0) + { + /* for SB16C1053APCI */ + return (sb1053a_get_interface(info, info->port.line)); + } + else + { + return (inb(mp_devs[arg].option_reg_addr+MP_OPTR_IIR0+(state->port->line/8))); + } + case TIOCGGETPORTTYPE: + ret = get_device_type(arg);; + return ret; + case TIOCSMULTIECHO: /* set to multi-drop mode(RS422) or echo mode(RS485)*/ + outb( ( inb(info->interface_config_addr) & ~0x03 ) | 0x01 , + info->interface_config_addr); + return 0; + case TIOCSPTPNOECHO: /* set to multi-drop mode(RS422) or echo mode(RS485) */ + outb( ( inb(info->interface_config_addr) & ~0x03 ) , + info->interface_config_addr); + return 0; + } + + if (ret != -ENOIOCTLCMD) + goto out; + + if (tty->flags & (1 << TTY_IO_ERROR)) { + ret = -EIO; + goto out; + } + + switch (cmd) { + case TIOCMIWAIT: + ret = mp_wait_modem_status(state, arg); + break; + + case TIOCGICOUNT: + ret = mp_get_count(state, (struct serial_icounter_struct *)arg); + break; + } + + if (ret != -ENOIOCTLCMD) + goto out; + + MP_STATE_LOCK(state); + switch (cmd) { + case TIOCSERGETLSR: /* Get line status register */ + ret = mp_get_lsr_info(state, (unsigned int *)arg); + break; + + default: { + struct sb_uart_port *port = state->port; + if (port->ops->ioctl) + ret = port->ops->ioctl(port, cmd, arg); + break; + } + } + + MP_STATE_UNLOCK(state); +out: + return ret; +} + +static void mp_set_termios(struct tty_struct *tty, struct MP_TERMIOS *old_termios) +{ + struct sb_uart_state *state = tty->driver_data; + unsigned long flags; + unsigned int cflag = tty->termios.c_cflag; + +#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + + if ((cflag ^ old_termios->c_cflag) == 0 && + RELEVANT_IFLAG(tty->termios.c_iflag ^ old_termios->c_iflag) == 0) + return; + + mp_change_speed(state, old_termios); + + if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) + uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR); + + if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { + unsigned int mask = TIOCM_DTR; + if (!(cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) + mask |= TIOCM_RTS; + uart_set_mctrl(state->port, mask); + } + + if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { + spin_lock_irqsave(&state->port->lock, flags); + tty->hw_stopped = 0; + __mp_start(tty); + spin_unlock_irqrestore(&state->port->lock, flags); + } + + if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) { + spin_lock_irqsave(&state->port->lock, flags); + if (!(state->port->ops->get_mctrl(state->port) & TIOCM_CTS)) { + tty->hw_stopped = 1; + state->port->ops->stop_tx(state->port); + } + spin_unlock_irqrestore(&state->port->lock, flags); + } +} + +static void mp_close(struct tty_struct *tty, struct file *filp) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port; + + printk("mp_close!\n"); + if (!state || !state->port) + return; + + port = state->port; + + printk("close1 %d\n", __LINE__); + MP_STATE_LOCK(state); + + printk("close2 %d\n", __LINE__); + if (tty_hung_up_p(filp)) + goto done; + + printk("close3 %d\n", __LINE__); + if ((tty->count == 1) && (state->count != 1)) { + printk("mp_close: bad serial port count; tty->count is 1, " + "state->count is %d\n", state->count); + state->count = 1; + } + printk("close4 %d\n", __LINE__); + if (--state->count < 0) { + printk("rs_close: bad serial port count for ttyMP%d: %d\n", + port->line, state->count); + state->count = 0; + } + if (state->count) + goto done; + + tty->closing = 1; + + printk("close5 %d\n", __LINE__); + if (state->closing_wait != USF_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, state->closing_wait); + + printk("close6 %d\n", __LINE__); + if (state->info->flags & UIF_INITIALIZED) { + unsigned long flags; + spin_lock_irqsave(&port->lock, flags); + port->ops->stop_rx(port); + spin_unlock_irqrestore(&port->lock, flags); + mp_wait_until_sent(tty, port->timeout); + } + printk("close7 %d\n", __LINE__); + + mp_shutdown(state); + printk("close8 %d\n", __LINE__); + mp_flush_buffer(tty); + tty_ldisc_flush(tty); + tty->closing = 0; + state->info->tty = NULL; + if (state->info->blocked_open) + { + if (state->close_delay) + { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(state->close_delay); + } + } + else + { + mp_change_pm(state, 3); + } + printk("close8 %d\n", __LINE__); + + state->info->flags &= ~UIF_NORMAL_ACTIVE; + wake_up_interruptible(&state->info->open_wait); + +done: + printk("close done\n"); + MP_STATE_UNLOCK(state); + module_put(THIS_MODULE); +} + +static void mp_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + unsigned long char_time, expire; + + if (port->type == PORT_UNKNOWN || port->fifosize == 0) + return; + + char_time = (port->timeout - HZ/50) / port->fifosize; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout && timeout < char_time) + char_time = timeout; + + if (timeout == 0 || timeout > 2 * port->timeout) + timeout = 2 * port->timeout; + + expire = jiffies + timeout; + + while (!port->ops->tx_empty(port)) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (time_after(jiffies, expire)) + break; + } + set_current_state(TASK_RUNNING); /* might not be needed */ +} + +static void mp_hangup(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + + MP_STATE_LOCK(state); + if (state->info && state->info->flags & UIF_NORMAL_ACTIVE) { + mp_flush_buffer(tty); + mp_shutdown(state); + state->count = 0; + state->info->flags &= ~UIF_NORMAL_ACTIVE; + state->info->tty = NULL; + wake_up_interruptible(&state->info->open_wait); + wake_up_interruptible(&state->info->delta_msr_wait); + } + MP_STATE_UNLOCK(state); +} + +static void mp_update_termios(struct sb_uart_state *state) +{ + struct tty_struct *tty = state->info->tty; + struct sb_uart_port *port = state->port; + + if (!(tty->flags & (1 << TTY_IO_ERROR))) { + mp_change_speed(state, NULL); + + if (tty->termios.c_cflag & CBAUD) + uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS); + } +} + +static int mp_block_til_ready(struct file *filp, struct sb_uart_state *state) +{ + DECLARE_WAITQUEUE(wait, current); + struct sb_uart_info *info = state->info; + struct sb_uart_port *port = state->port; + unsigned int mctrl; + + info->blocked_open++; + state->count--; + + add_wait_queue(&info->open_wait, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + + if (tty_hung_up_p(filp) || info->tty == NULL) + break; + + if (!(info->flags & UIF_INITIALIZED)) + break; + + if ((filp->f_flags & O_NONBLOCK) || + (info->tty->termios.c_cflag & CLOCAL) || + (info->tty->flags & (1 << TTY_IO_ERROR))) { + break; + } + + if (info->tty->termios.c_cflag & CBAUD) + uart_set_mctrl(port, TIOCM_DTR); + + spin_lock_irq(&port->lock); + port->ops->enable_ms(port); + mctrl = port->ops->get_mctrl(port); + spin_unlock_irq(&port->lock); + if (mctrl & TIOCM_CAR) + break; + + MP_STATE_UNLOCK(state); + schedule(); + MP_STATE_LOCK(state); + + if (signal_pending(current)) + break; + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + + state->count++; + info->blocked_open--; + + if (signal_pending(current)) + return -ERESTARTSYS; + + if (!info->tty || tty_hung_up_p(filp)) + return -EAGAIN; + + return 0; +} + +static struct sb_uart_state *uart_get(struct uart_driver *drv, int line) +{ + struct sb_uart_state *state; + + MP_MUTEX_LOCK(mp_mutex); + state = drv->state + line; + if (mutex_lock_interruptible(&state->mutex)) { + state = ERR_PTR(-ERESTARTSYS); + goto out; + } + state->count++; + if (!state->port) { + state->count--; + MP_STATE_UNLOCK(state); + state = ERR_PTR(-ENXIO); + goto out; + } + + if (!state->info) { + state->info = kmalloc(sizeof(struct sb_uart_info), GFP_KERNEL); + if (state->info) { + memset(state->info, 0, sizeof(struct sb_uart_info)); + init_waitqueue_head(&state->info->open_wait); + init_waitqueue_head(&state->info->delta_msr_wait); + + state->port->info = state->info; + + tasklet_init(&state->info->tlet, mp_tasklet_action, + (unsigned long)state); + } else { + state->count--; + MP_STATE_UNLOCK(state); + state = ERR_PTR(-ENOMEM); + } + } + +out: + MP_MUTEX_UNLOCK(mp_mutex); + return state; +} + +static int mp_open(struct tty_struct *tty, struct file *filp) +{ + struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state; + struct sb_uart_state *state; + int retval; + int line = tty->index; + struct mp_port *mtpt; + + retval = -ENODEV; + if (line >= tty->driver->num) + goto fail; + + state = uart_get(drv, line); + + mtpt = (struct mp_port *)state->port; + + if (IS_ERR(state)) { + retval = PTR_ERR(state); + goto fail; + } + + tty->driver_data = state; + tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; + tty->alt_speed = 0; + state->info->tty = tty; + + if (tty_hung_up_p(filp)) { + retval = -EAGAIN; + state->count--; + MP_STATE_UNLOCK(state); + goto fail; + } + + if (state->count == 1) + mp_change_pm(state, 0); + + retval = mp_startup(state, 0); + + if (retval == 0) + retval = mp_block_til_ready(filp, state); + MP_STATE_UNLOCK(state); + + if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) { + state->info->flags |= UIF_NORMAL_ACTIVE; + + mp_update_termios(state); + } + + uart_clear_mctrl(state->port, TIOCM_RTS); + try_module_get(THIS_MODULE); +fail: + return retval; +} + + +static const char *mp_type(struct sb_uart_port *port) +{ + const char *str = NULL; + + if (port->ops->type) + str = port->ops->type(port); + + if (!str) + str = "unknown"; + + return str; +} + +static void mp_change_pm(struct sb_uart_state *state, int pm_state) +{ + struct sb_uart_port *port = state->port; + if (port->ops->pm) + port->ops->pm(port, pm_state, state->pm_state); + state->pm_state = pm_state; +} + +static inline void mp_report_port(struct uart_driver *drv, struct sb_uart_port *port) +{ + char address[64]; + + switch (port->iotype) { + case UPIO_PORT: + snprintf(address, sizeof(address),"I/O 0x%x", port->iobase); + break; + case UPIO_HUB6: + snprintf(address, sizeof(address),"I/O 0x%x offset 0x%x", port->iobase, port->hub6); + break; + case UPIO_MEM: + snprintf(address, sizeof(address),"MMIO 0x%lx", port->mapbase); + break; + default: + snprintf(address, sizeof(address),"*unknown*" ); + strlcpy(address, "*unknown*", sizeof(address)); + break; + } + + printk( "%s%d at %s (irq = %d) is a %s\n", + drv->dev_name, port->line, address, port->irq, mp_type(port)); + +} + +static void mp_configure_port(struct uart_driver *drv, struct sb_uart_state *state, struct sb_uart_port *port) +{ + unsigned int flags; + + + if (!port->iobase && !port->mapbase && !port->membase) + { + DPRINTK("%s error \n",__FUNCTION__); + return; + } + flags = UART_CONFIG_TYPE; + if (port->flags & UPF_AUTO_IRQ) + flags |= UART_CONFIG_IRQ; + if (port->flags & UPF_BOOT_AUTOCONF) { + port->type = PORT_UNKNOWN; + port->ops->config_port(port, flags); + } + + if (port->type != PORT_UNKNOWN) { + unsigned long flags; + + mp_report_port(drv, port); + + spin_lock_irqsave(&port->lock, flags); + port->ops->set_mctrl(port, 0); + spin_unlock_irqrestore(&port->lock, flags); + + mp_change_pm(state, 3); + } +} + +static void mp_unconfigure_port(struct uart_driver *drv, struct sb_uart_state *state) +{ + struct sb_uart_port *port = state->port; + struct sb_uart_info *info = state->info; + + if (info && info->tty) + tty_hangup(info->tty); + + MP_STATE_LOCK(state); + + state->info = NULL; + + if (port->type != PORT_UNKNOWN) + port->ops->release_port(port); + + port->type = PORT_UNKNOWN; + + if (info) { + tasklet_kill(&info->tlet); + kfree(info); + } + + MP_STATE_UNLOCK(state); +} +static struct tty_operations mp_ops = { + .open = mp_open, + .close = mp_close, + .write = mp_write, + .put_char = mp_put_char, + .flush_chars = mp_put_chars, + .write_room = mp_write_room, + .chars_in_buffer= mp_chars_in_buffer, + .flush_buffer = mp_flush_buffer, + .ioctl = mp_ioctl, + .throttle = mp_throttle, + .unthrottle = mp_unthrottle, + .send_xchar = mp_send_xchar, + .set_termios = mp_set_termios, + .stop = mp_stop, + .start = mp_start, + .hangup = mp_hangup, + .break_ctl = mp_break_ctl, + .wait_until_sent= mp_wait_until_sent, +#ifdef CONFIG_PROC_FS + .proc_fops = NULL, +#endif + .tiocmget = mp_tiocmget, + .tiocmset = mp_tiocmset, +}; + +static int mp_register_driver(struct uart_driver *drv) +{ + struct tty_driver *normal = NULL; + int i, retval; + + drv->state = kmalloc(sizeof(struct sb_uart_state) * drv->nr, GFP_KERNEL); + retval = -ENOMEM; + if (!drv->state) + { + printk("SB PCI Error: Kernel memory allocation error!\n"); + goto out; + } + memset(drv->state, 0, sizeof(struct sb_uart_state) * drv->nr); + + normal = alloc_tty_driver(drv->nr); + if (!normal) + { + printk("SB PCI Error: tty allocation error!\n"); + goto out; + } + + drv->tty_driver = normal; + + normal->owner = drv->owner; + normal->magic = TTY_DRIVER_MAGIC; + normal->driver_name = drv->driver_name; + normal->name = drv->dev_name; + normal->major = drv->major; + normal->minor_start = drv->minor; + + normal->num = MAX_MP_PORT ; + + normal->type = TTY_DRIVER_TYPE_SERIAL; + normal->subtype = SERIAL_TYPE_NORMAL; + normal->init_termios = tty_std_termios; + normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + normal->driver_state = drv; + + tty_set_operations(normal, &mp_ops); + +for (i = 0; i < drv->nr; i++) { + struct sb_uart_state *state = drv->state + i; + + state->close_delay = 500; + state->closing_wait = 30000; + + mutex_init(&state->mutex); + } + + retval = tty_register_driver(normal); +out: + if (retval < 0) { + printk("Register tty driver Fail!\n"); + put_tty_driver(normal); + kfree(drv->state); + } + + return retval; +} + +void mp_unregister_driver(struct uart_driver *drv) +{ + struct tty_driver *normal = NULL; + + normal = drv->tty_driver; + + if (!normal) + { + return; + } + + tty_unregister_driver(normal); + put_tty_driver(normal); + drv->tty_driver = NULL; + + + if (drv->state) + { + kfree(drv->state); + } + +} + +static int mp_add_one_port(struct uart_driver *drv, struct sb_uart_port *port) +{ + struct sb_uart_state *state; + int ret = 0; + + + if (port->line >= drv->nr) + return -EINVAL; + + state = drv->state + port->line; + + MP_MUTEX_LOCK(mp_mutex); + if (state->port) { + ret = -EINVAL; + goto out; + } + + state->port = port; + + spin_lock_init(&port->lock); + port->cons = drv->cons; + port->info = state->info; + + mp_configure_port(drv, state, port); + + tty_register_device(drv->tty_driver, port->line, port->dev); + +out: + MP_MUTEX_UNLOCK(mp_mutex); + + + return ret; +} + +static int mp_remove_one_port(struct uart_driver *drv, struct sb_uart_port *port) +{ + struct sb_uart_state *state = drv->state + port->line; + + if (state->port != port) + printk(KERN_ALERT "Removing wrong port: %p != %p\n", + state->port, port); + + MP_MUTEX_LOCK(mp_mutex); + + tty_unregister_device(drv->tty_driver, port->line); + + mp_unconfigure_port(drv, state); + state->port = NULL; + MP_MUTEX_UNLOCK(mp_mutex); + + return 0; +} + +static void autoconfig(struct mp_port *mtpt, unsigned int probeflags) +{ + unsigned char status1, scratch, scratch2, scratch3; + unsigned char save_lcr, save_mcr; + unsigned long flags; + + unsigned char u_type; + unsigned char b_ret = 0; + + if (!mtpt->port.iobase && !mtpt->port.mapbase && !mtpt->port.membase) + return; + + DEBUG_AUTOCONF("ttyMP%d: autoconf (0x%04x, 0x%p): ", + mtpt->port.line, mtpt->port.iobase, mtpt->port.membase); + + spin_lock_irqsave(&mtpt->port.lock, flags); + + if (!(mtpt->port.flags & UPF_BUGGY_UART)) { + scratch = serial_inp(mtpt, UART_IER); + serial_outp(mtpt, UART_IER, 0); +#ifdef __i386__ + outb(0xff, 0x080); +#endif + scratch2 = serial_inp(mtpt, UART_IER) & 0x0f; + serial_outp(mtpt, UART_IER, 0x0F); +#ifdef __i386__ + outb(0, 0x080); +#endif + scratch3 = serial_inp(mtpt, UART_IER) & 0x0F; + serial_outp(mtpt, UART_IER, scratch); + if (scratch2 != 0 || scratch3 != 0x0F) { + DEBUG_AUTOCONF("IER test failed (%02x, %02x) ", + scratch2, scratch3); + goto out; + } + } + + save_mcr = serial_in(mtpt, UART_MCR); + save_lcr = serial_in(mtpt, UART_LCR); + + if (!(mtpt->port.flags & UPF_SKIP_TEST)) { + serial_outp(mtpt, UART_MCR, UART_MCR_LOOP | 0x0A); + status1 = serial_inp(mtpt, UART_MSR) & 0xF0; + serial_outp(mtpt, UART_MCR, save_mcr); + if (status1 != 0x90) { + DEBUG_AUTOCONF("LOOP test failed (%02x) ", + status1); + goto out; + } + } + + serial_outp(mtpt, UART_LCR, 0xBF); + serial_outp(mtpt, UART_EFR, 0); + serial_outp(mtpt, UART_LCR, 0); + + serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = serial_in(mtpt, UART_IIR) >> 6; + + DEBUG_AUTOCONF("iir=%d ", scratch); + if(mtpt->device->nr_ports >= 8) + b_ret = read_option_register(mtpt,(MP_OPTR_DIR0 + ((mtpt->port.line)/8))); + else + b_ret = read_option_register(mtpt,MP_OPTR_DIR0); + u_type = (b_ret & 0xf0) >> 4; + if(mtpt->port.type == PORT_UNKNOWN ) + { + switch (u_type) + { + case DIR_UART_16C550: + mtpt->port.type = PORT_16C55X; + break; + case DIR_UART_16C1050: + mtpt->port.type = PORT_16C105X; + break; + case DIR_UART_16C1050A: + if (mtpt->port.line < 2) + { + mtpt->port.type = PORT_16C105XA; + } + else + { + if (mtpt->device->device_id & 0x50) + { + mtpt->port.type = PORT_16C55X; + } + else + { + mtpt->port.type = PORT_16C105X; + } + } + break; + default: + mtpt->port.type = PORT_UNKNOWN; + break; + } + } + + if(mtpt->port.type == PORT_UNKNOWN ) + { +printk("unknow2\n"); + switch (scratch) { + case 0: + case 1: + mtpt->port.type = PORT_UNKNOWN; + break; + case 2: + case 3: + mtpt->port.type = PORT_16C55X; + break; + } + } + + serial_outp(mtpt, UART_LCR, save_lcr); + + mtpt->port.fifosize = uart_config[mtpt->port.type].dfl_xmit_fifo_size; + mtpt->capabilities = uart_config[mtpt->port.type].flags; + + if (mtpt->port.type == PORT_UNKNOWN) + goto out; + serial_outp(mtpt, UART_MCR, save_mcr); + serial_outp(mtpt, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(mtpt, UART_FCR, 0); + (void)serial_in(mtpt, UART_RX); + serial_outp(mtpt, UART_IER, 0); + +out: + spin_unlock_irqrestore(&mtpt->port.lock, flags); + DEBUG_AUTOCONF("type=%s\n", uart_config[mtpt->port.type].name); +} + +static void autoconfig_irq(struct mp_port *mtpt) +{ + unsigned char save_mcr, save_ier; + unsigned long irqs; + int irq; + + /* forget possible initially masked and pending IRQ */ + probe_irq_off(probe_irq_on()); + save_mcr = serial_inp(mtpt, UART_MCR); + save_ier = serial_inp(mtpt, UART_IER); + serial_outp(mtpt, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); + + irqs = probe_irq_on(); + serial_outp(mtpt, UART_MCR, 0); + serial_outp(mtpt, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + + serial_outp(mtpt, UART_IER, 0x0f); /* enable all intrs */ + (void)serial_inp(mtpt, UART_LSR); + (void)serial_inp(mtpt, UART_RX); + (void)serial_inp(mtpt, UART_IIR); + (void)serial_inp(mtpt, UART_MSR); + serial_outp(mtpt, UART_TX, 0xFF); + irq = probe_irq_off(irqs); + + serial_outp(mtpt, UART_MCR, save_mcr); + serial_outp(mtpt, UART_IER, save_ier); + + mtpt->port.irq = (irq > 0) ? irq : 0; +} + +static void multi_stop_tx(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + + if (mtpt->ier & UART_IER_THRI) { + mtpt->ier &= ~UART_IER_THRI; + serial_out(mtpt, UART_IER, mtpt->ier); + } + + tasklet_schedule(&port->info->tlet); +} + +static void multi_start_tx(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + + if (!(mtpt->ier & UART_IER_THRI)) { + mtpt->ier |= UART_IER_THRI; + serial_out(mtpt, UART_IER, mtpt->ier); + } +} + +static void multi_stop_rx(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + + mtpt->ier &= ~UART_IER_RLSI; + mtpt->port.read_status_mask &= ~UART_LSR_DR; + serial_out(mtpt, UART_IER, mtpt->ier); +} + +static void multi_enable_ms(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + + mtpt->ier |= UART_IER_MSI; + serial_out(mtpt, UART_IER, mtpt->ier); +} + + +static _INLINE_ void receive_chars(struct mp_port *mtpt, int *status ) +{ + struct tty_struct *tty = mtpt->port.info->tty; + unsigned char lsr = *status; + int max_count = 256; + unsigned char ch; + char flag; + + //lsr &= mtpt->port.read_status_mask; + + do { + if ((lsr & UART_LSR_PE) && (mtpt->port.mdmode & MDMODE_ENABLE)) + { + ch = serial_inp(mtpt, UART_RX); + } + else if (lsr & UART_LSR_SPECIAL) + { + flag = 0; + ch = serial_inp(mtpt, UART_RX); + + if (lsr & UART_LSR_BI) + { + + mtpt->port.icount.brk++; + flag = TTY_BREAK; + + if (sb_uart_handle_break(&mtpt->port)) + goto ignore_char; + } + if (lsr & UART_LSR_PE) + { + mtpt->port.icount.parity++; + flag = TTY_PARITY; + } + if (lsr & UART_LSR_FE) + { + mtpt->port.icount.frame++; + flag = TTY_FRAME; + } + if (lsr & UART_LSR_OE) + { + mtpt->port.icount.overrun++; + flag = TTY_OVERRUN; + } + tty_insert_flip_char(tty, ch, flag); + } + else + { + ch = serial_inp(mtpt, UART_RX); + tty_insert_flip_char(tty, ch, 0); + } +ignore_char: + lsr = serial_inp(mtpt, UART_LSR); + } while ((lsr & UART_LSR_DR) && (max_count-- > 0)); + + tty_flip_buffer_push(tty); +} + + + + +static _INLINE_ void transmit_chars(struct mp_port *mtpt) +{ + struct circ_buf *xmit = &mtpt->port.info->xmit; + int count; + + if (mtpt->port.x_char) { + serial_outp(mtpt, UART_TX, mtpt->port.x_char); + mtpt->port.icount.tx++; + mtpt->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&mtpt->port)) { + multi_stop_tx(&mtpt->port); + return; + } + + count = uart_circ_chars_pending(xmit); + + if(count > mtpt->port.fifosize) + { + count = mtpt->port.fifosize; + } + + printk("[%d] mdmode: %x\n", mtpt->port.line, mtpt->port.mdmode); + do { +#if 0 + /* check multi-drop mode */ + if ((mtpt->port.mdmode & (MDMODE_ENABLE | MDMODE_ADDR)) == (MDMODE_ENABLE | MDMODE_ADDR)) + { + printk("send address\n"); + /* send multi-drop address */ + serial_out(mtpt, UART_SCR, xmit->buf[xmit->tail]); + } + else +#endif + { + serial_out(mtpt, UART_TX, xmit->buf[xmit->tail]); + } + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + mtpt->port.icount.tx++; + } while (--count > 0); +} + + + +static _INLINE_ void check_modem_status(struct mp_port *mtpt) +{ + int status; + + status = serial_in(mtpt, UART_MSR); + + if ((status & UART_MSR_ANY_DELTA) == 0) + return; + + if (status & UART_MSR_TERI) + mtpt->port.icount.rng++; + if (status & UART_MSR_DDSR) + mtpt->port.icount.dsr++; + if (status & UART_MSR_DDCD) + sb_uart_handle_dcd_change(&mtpt->port, status & UART_MSR_DCD); + if (status & UART_MSR_DCTS) + sb_uart_handle_cts_change(&mtpt->port, status & UART_MSR_CTS); + + wake_up_interruptible(&mtpt->port.info->delta_msr_wait); +} + +static inline void multi_handle_port(struct mp_port *mtpt) +{ + unsigned int status = serial_inp(mtpt, UART_LSR); + + //printk("lsr: %x\n", status); + + if ((status & UART_LSR_DR) || (status & UART_LSR_SPECIAL)) + receive_chars(mtpt, &status); + check_modem_status(mtpt); + if (status & UART_LSR_THRE) + { + if ((mtpt->port.type == PORT_16C105X) + || (mtpt->port.type == PORT_16C105XA)) + transmit_chars(mtpt); + else + { + if (mtpt->interface >= RS485NE) + uart_set_mctrl(&mtpt->port, TIOCM_RTS); + + transmit_chars(mtpt); + + + if (mtpt->interface >= RS485NE) + { + while((status=serial_in(mtpt,UART_LSR) &0x60)!=0x60); + uart_clear_mctrl(&mtpt->port, TIOCM_RTS); + } + } + } +} + + + +static irqreturn_t multi_interrupt(int irq, void *dev_id) +{ + struct irq_info *iinfo = dev_id; + struct list_head *lhead, *end = NULL; + int pass_counter = 0; + + + spin_lock(&iinfo->lock); + + lhead = iinfo->head; + do { + struct mp_port *mtpt; + unsigned int iir; + + mtpt = list_entry(lhead, struct mp_port, list); + + iir = serial_in(mtpt, UART_IIR); + printk("intrrupt! port %d, iir 0x%x\n", mtpt->port.line, iir); //wlee + if (!(iir & UART_IIR_NO_INT)) + { + printk("interrupt handle\n"); + spin_lock(&mtpt->port.lock); + multi_handle_port(mtpt); + spin_unlock(&mtpt->port.lock); + + end = NULL; + } else if (end == NULL) + end = lhead; + + lhead = lhead->next; + if (lhead == iinfo->head && pass_counter++ > PASS_LIMIT) + { + printk(KERN_ERR "multi: too much work for " + "irq%d\n", irq); + printk( "multi: too much work for " + "irq%d\n", irq); + break; + } + } while (lhead != end); + + spin_unlock(&iinfo->lock); + + + return IRQ_HANDLED; +} + +static void serial_do_unlink(struct irq_info *i, struct mp_port *mtpt) +{ + spin_lock_irq(&i->lock); + + if (!list_empty(i->head)) { + if (i->head == &mtpt->list) + i->head = i->head->next; + list_del(&mtpt->list); + } else { + i->head = NULL; + } + + spin_unlock_irq(&i->lock); +} + +static int serial_link_irq_chain(struct mp_port *mtpt) +{ + struct irq_info *i = irq_lists + mtpt->port.irq; + int ret, irq_flags = mtpt->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0; + spin_lock_irq(&i->lock); + + if (i->head) { + list_add(&mtpt->list, i->head); + spin_unlock_irq(&i->lock); + + ret = 0; + } else { + INIT_LIST_HEAD(&mtpt->list); + i->head = &mtpt->list; + spin_unlock_irq(&i->lock); + + ret = request_irq(mtpt->port.irq, multi_interrupt, + irq_flags, "serial", i); + if (ret < 0) + serial_do_unlink(i, mtpt); + } + + return ret; +} + + + + +static void serial_unlink_irq_chain(struct mp_port *mtpt) +{ + struct irq_info *i = irq_lists + mtpt->port.irq; + + if (list_empty(i->head)) + { + free_irq(mtpt->port.irq, i); + } + serial_do_unlink(i, mtpt); +} + +static void multi_timeout(unsigned long data) +{ + struct mp_port *mtpt = (struct mp_port *)data; + + + spin_lock(&mtpt->port.lock); + multi_handle_port(mtpt); + spin_unlock(&mtpt->port.lock); + + mod_timer(&mtpt->timer, jiffies+1 ); +} + +static unsigned int multi_tx_empty(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&mtpt->port.lock, flags); + ret = serial_in(mtpt, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&mtpt->port.lock, flags); + + return ret; +} + + +static unsigned int multi_get_mctrl(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned char status; + unsigned int ret; + + status = serial_in(mtpt, UART_MSR); + + ret = 0; + if (status & UART_MSR_DCD) + ret |= TIOCM_CAR; + if (status & UART_MSR_RI) + ret |= TIOCM_RNG; + if (status & UART_MSR_DSR) + ret |= TIOCM_DSR; + if (status & UART_MSR_CTS) + ret |= TIOCM_CTS; + return ret; +} + +static void multi_set_mctrl(struct sb_uart_port *port, unsigned int mctrl) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned char mcr = 0; + + mctrl &= 0xff; + + if (mctrl & TIOCM_RTS) + mcr |= UART_MCR_RTS; + if (mctrl & TIOCM_DTR) + mcr |= UART_MCR_DTR; + if (mctrl & TIOCM_OUT1) + mcr |= UART_MCR_OUT1; + if (mctrl & TIOCM_OUT2) + mcr |= UART_MCR_OUT2; + if (mctrl & TIOCM_LOOP) + mcr |= UART_MCR_LOOP; + + + serial_out(mtpt, UART_MCR, mcr); +} + + +static void multi_break_ctl(struct sb_uart_port *port, int break_state) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned long flags; + + spin_lock_irqsave(&mtpt->port.lock, flags); + if (break_state == -1) + mtpt->lcr |= UART_LCR_SBC; + else + mtpt->lcr &= ~UART_LCR_SBC; + serial_out(mtpt, UART_LCR, mtpt->lcr); + spin_unlock_irqrestore(&mtpt->port.lock, flags); +} + + + +static int multi_startup(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned long flags; + int retval; + + mtpt->capabilities = uart_config[mtpt->port.type].flags; + mtpt->mcr = 0; + + if (mtpt->capabilities & UART_CLEAR_FIFO) { + serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(mtpt, UART_FCR, 0); + } + + (void) serial_inp(mtpt, UART_LSR); + (void) serial_inp(mtpt, UART_RX); + (void) serial_inp(mtpt, UART_IIR); + (void) serial_inp(mtpt, UART_MSR); + //test-wlee 9-bit disable + serial_outp(mtpt, UART_MSR, 0); + + + if (!(mtpt->port.flags & UPF_BUGGY_UART) && + (serial_inp(mtpt, UART_LSR) == 0xff)) { + printk("ttyS%d: LSR safety check engaged!\n", mtpt->port.line); + //return -ENODEV; + } + + if ((!is_real_interrupt(mtpt->port.irq)) || (mtpt->poll_type==TYPE_POLL)) { + unsigned int timeout = mtpt->port.timeout; + + timeout = timeout > 6 ? (timeout / 2 - 2) : 1; + + mtpt->timer.data = (unsigned long)mtpt; + mod_timer(&mtpt->timer, jiffies + timeout); + } + else + { + retval = serial_link_irq_chain(mtpt); + if (retval) + return retval; + } + + serial_outp(mtpt, UART_LCR, UART_LCR_WLEN8); + + spin_lock_irqsave(&mtpt->port.lock, flags); + if ((is_real_interrupt(mtpt->port.irq))||(mtpt->poll_type==TYPE_INTERRUPT)) + mtpt->port.mctrl |= TIOCM_OUT2; + + multi_set_mctrl(&mtpt->port, mtpt->port.mctrl); + spin_unlock_irqrestore(&mtpt->port.lock, flags); + + + mtpt->ier = UART_IER_RLSI | UART_IER_RDI; + serial_outp(mtpt, UART_IER, mtpt->ier); + + (void) serial_inp(mtpt, UART_LSR); + (void) serial_inp(mtpt, UART_RX); + (void) serial_inp(mtpt, UART_IIR); + (void) serial_inp(mtpt, UART_MSR); + + return 0; +} + + + +static void multi_shutdown(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned long flags; + + + mtpt->ier = 0; + serial_outp(mtpt, UART_IER, 0); + + spin_lock_irqsave(&mtpt->port.lock, flags); + mtpt->port.mctrl &= ~TIOCM_OUT2; + + multi_set_mctrl(&mtpt->port, mtpt->port.mctrl); + spin_unlock_irqrestore(&mtpt->port.lock, flags); + + serial_out(mtpt, UART_LCR, serial_inp(mtpt, UART_LCR) & ~UART_LCR_SBC); + serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + serial_outp(mtpt, UART_FCR, 0); + + + (void) serial_in(mtpt, UART_RX); + + if ((!is_real_interrupt(mtpt->port.irq))||(mtpt->poll_type==TYPE_POLL)) + { + del_timer_sync(&mtpt->timer); + } + else + { + serial_unlink_irq_chain(mtpt); + } +} + + + +static unsigned int multi_get_divisor(struct sb_uart_port *port, unsigned int baud) +{ + unsigned int quot; + + if ((port->flags & UPF_MAGIC_MULTIPLIER) && + baud == (port->uartclk/4)) + quot = 0x8001; + else if ((port->flags & UPF_MAGIC_MULTIPLIER) && + baud == (port->uartclk/8)) + quot = 0x8002; + else + quot = sb_uart_get_divisor(port, baud); + + return quot; +} + + + + +static void multi_set_termios(struct sb_uart_port *port, struct MP_TERMIOS *termios, struct MP_TERMIOS *old) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned char cval, fcr = 0; + unsigned long flags; + unsigned int baud, quot; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = 0x00; + break; + case CS6: + cval = 0x01; + break; + case CS7: + cval = 0x02; + break; + default: + case CS8: + cval = 0x03; + break; + } + + if (termios->c_cflag & CSTOPB) + cval |= 0x04; + if (termios->c_cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(termios->c_cflag & PARODD)) + cval |= UART_LCR_EPAR; + +#ifdef CMSPAR + if (termios->c_cflag & CMSPAR) + cval |= UART_LCR_SPAR; +#endif + + baud = sb_uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = multi_get_divisor(port, baud); + + if (mtpt->capabilities & UART_USE_FIFO) { + //if (baud < 2400) + // fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; + //else + // fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; + + // fcr = UART_FCR_ENABLE_FIFO | 0x90; + fcr = fcr_arr[mtpt->port.line]; + } + + spin_lock_irqsave(&mtpt->port.lock, flags); + + sb_uart_update_timeout(port, termios->c_cflag, baud); + + mtpt->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + mtpt->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + mtpt->port.read_status_mask |= UART_LSR_BI; + + mtpt->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + mtpt->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + mtpt->port.ignore_status_mask |= UART_LSR_BI; + if (termios->c_iflag & IGNPAR) + mtpt->port.ignore_status_mask |= UART_LSR_OE; + } + + if ((termios->c_cflag & CREAD) == 0) + mtpt->port.ignore_status_mask |= UART_LSR_DR; + + mtpt->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&mtpt->port, termios->c_cflag)) + mtpt->ier |= UART_IER_MSI; + + serial_out(mtpt, UART_IER, mtpt->ier); + + if (mtpt->capabilities & UART_STARTECH) { + serial_outp(mtpt, UART_LCR, 0xBF); + serial_outp(mtpt, UART_EFR, + termios->c_cflag & CRTSCTS ? UART_EFR_CTS :0); + } + + serial_outp(mtpt, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ + + serial_outp(mtpt, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_outp(mtpt, UART_DLM, quot >> 8); /* MS of divisor */ + + serial_outp(mtpt, UART_LCR, cval); /* reset DLAB */ + mtpt->lcr = cval; /* Save LCR */ + + if (fcr & UART_FCR_ENABLE_FIFO) { + /* emulated UARTs (Lucent Venus 167x) need two steps */ + serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO); + } + + serial_outp(mtpt, UART_FCR, fcr); /* set fcr */ + + + if ((mtpt->port.type == PORT_16C105X) + || (mtpt->port.type == PORT_16C105XA)) + { + if(deep[mtpt->port.line]!=0) + set_deep_fifo(port, ENABLE); + + if (mtpt->interface != RS232) + set_auto_rts(port,mtpt->interface); + + } + else + { + if (mtpt->interface >= RS485NE) + { + uart_clear_mctrl(&mtpt->port, TIOCM_RTS); + } + } + + if(mtpt->device->device_id == PCI_DEVICE_ID_MP4M) + { + SendATCommand(mtpt); + printk("SendATCommand\n"); + } + multi_set_mctrl(&mtpt->port, mtpt->port.mctrl); + spin_unlock_irqrestore(&mtpt->port.lock, flags); +} + +static void multi_pm(struct sb_uart_port *port, unsigned int state, unsigned int oldstate) +{ + struct mp_port *mtpt = (struct mp_port *)port; + if (state) { + if (mtpt->capabilities & UART_STARTECH) { + serial_outp(mtpt, UART_LCR, 0xBF); + serial_outp(mtpt, UART_EFR, UART_EFR_ECB); + serial_outp(mtpt, UART_LCR, 0); + serial_outp(mtpt, UART_IER, UART_IERX_SLEEP); + serial_outp(mtpt, UART_LCR, 0xBF); + serial_outp(mtpt, UART_EFR, 0); + serial_outp(mtpt, UART_LCR, 0); + } + + if (mtpt->pm) + mtpt->pm(port, state, oldstate); + } + else + { + if (mtpt->capabilities & UART_STARTECH) { + serial_outp(mtpt, UART_LCR, 0xBF); + serial_outp(mtpt, UART_EFR, UART_EFR_ECB); + serial_outp(mtpt, UART_LCR, 0); + serial_outp(mtpt, UART_IER, 0); + serial_outp(mtpt, UART_LCR, 0xBF); + serial_outp(mtpt, UART_EFR, 0); + serial_outp(mtpt, UART_LCR, 0); + } + + if (mtpt->pm) + mtpt->pm(port, state, oldstate); + } +} + +static void multi_release_std_resource(struct mp_port *mtpt) +{ + unsigned int size = 8 << mtpt->port.regshift; + + switch (mtpt->port.iotype) { + case UPIO_MEM: + if (!mtpt->port.mapbase) + break; + + if (mtpt->port.flags & UPF_IOREMAP) { + iounmap(mtpt->port.membase); + mtpt->port.membase = NULL; + } + + release_mem_region(mtpt->port.mapbase, size); + break; + + case UPIO_HUB6: + case UPIO_PORT: + release_region(mtpt->port.iobase,size); + break; + } +} + +static void multi_release_port(struct sb_uart_port *port) +{ +} + +static int multi_request_port(struct sb_uart_port *port) +{ + return 0; +} + +static void multi_config_port(struct sb_uart_port *port, int flags) +{ + struct mp_port *mtpt = (struct mp_port *)port; + int probeflags = PROBE_ANY; + + if (flags & UART_CONFIG_TYPE) + autoconfig(mtpt, probeflags); + if (mtpt->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) + autoconfig_irq(mtpt); + + if (mtpt->port.type == PORT_UNKNOWN) + multi_release_std_resource(mtpt); +} + +static int multi_verify_port(struct sb_uart_port *port, struct serial_struct *ser) +{ + if (ser->irq >= NR_IRQS || ser->irq < 0 || + ser->baud_base < 9600 || ser->type < PORT_UNKNOWN || + ser->type == PORT_STARTECH) + return -EINVAL; + return 0; +} + +static const char * multi_type(struct sb_uart_port *port) +{ + int type = port->type; + + if (type >= ARRAY_SIZE(uart_config)) + type = 0; + return uart_config[type].name; +} + +static struct sb_uart_ops multi_pops = { + .tx_empty = multi_tx_empty, + .set_mctrl = multi_set_mctrl, + .get_mctrl = multi_get_mctrl, + .stop_tx = multi_stop_tx, + .start_tx = multi_start_tx, + .stop_rx = multi_stop_rx, + .enable_ms = multi_enable_ms, + .break_ctl = multi_break_ctl, + .startup = multi_startup, + .shutdown = multi_shutdown, + .set_termios = multi_set_termios, + .pm = multi_pm, + .type = multi_type, + .release_port = multi_release_port, + .request_port = multi_request_port, + .config_port = multi_config_port, + .verify_port = multi_verify_port, +}; + +static struct uart_driver multi_reg = { + .owner = THIS_MODULE, + .driver_name = "goldel_tulip", + .dev_name = "ttyMP", + .major = SB_TTY_MP_MAJOR, + .minor = 0, + .nr = MAX_MP_PORT, + .cons = NULL, +}; + +static void __init multi_init_ports(void) +{ + struct mp_port *mtpt; + static int first = 1; + int i,j,k; + unsigned char osc; + unsigned char b_ret = 0; + static struct mp_device_t * sbdev; + + if (!first) + return; + first = 0; + + mtpt = multi_ports; + + for (k=0;k<NR_BOARD;k++) + { + sbdev = &mp_devs[k]; + + for (i = 0; i < sbdev->nr_ports; i++, mtpt++) + { + mtpt->device = sbdev; + mtpt->port.iobase = sbdev->uart_access_addr + 8*i; + mtpt->port.irq = sbdev->irq; + if ( ((sbdev->device_id == PCI_DEVICE_ID_MP4)&&(sbdev->revision==0x91))) + mtpt->interface_config_addr = sbdev->option_reg_addr + 0x08 + i; + else if (sbdev->revision == 0xc0) + mtpt->interface_config_addr = sbdev->option_reg_addr + 0x08 + (i & 0x1); + else + mtpt->interface_config_addr = sbdev->option_reg_addr + 0x08 + i/8; + + mtpt->option_base_addr = sbdev->option_reg_addr; + + mtpt->poll_type = sbdev->poll_type; + + mtpt->port.uartclk = BASE_BAUD * 16; + + /* get input clock infomation */ + osc = inb(sbdev->option_reg_addr + MP_OPTR_DIR0 + i/8) & 0x0F; + if (osc==0x0f) + osc = 0; + for(j=0;j<osc;j++) + mtpt->port.uartclk *= 2; + mtpt->port.flags |= STD_COM_FLAGS | UPF_SHARE_IRQ ; + mtpt->port.iotype = UPIO_PORT; + mtpt->port.ops = &multi_pops; + + if (sbdev->revision == 0xc0) + { + /* for SB16C1053APCI */ + b_ret = sb1053a_get_interface(mtpt, i); + } + else + { + b_ret = read_option_register(mtpt,(MP_OPTR_IIR0 + i/8)); + printk("IIR_RET = %x\n",b_ret); + } + + if(IIR_RS232 == (b_ret & IIR_RS232)) + { + mtpt->interface = RS232; + } + if(IIR_RS422 == (b_ret & IIR_RS422)) + { + mtpt->interface = RS422PTP; + } + if(IIR_RS485 == (b_ret & IIR_RS485)) + { + mtpt->interface = RS485NE; + } + } + } +} + +static void __init multi_register_ports(struct uart_driver *drv) +{ + int i; + + multi_init_ports(); + + for (i = 0; i < NR_PORTS; i++) { + struct mp_port *mtpt = &multi_ports[i]; + + mtpt->port.line = i; + mtpt->port.ops = &multi_pops; + init_timer(&mtpt->timer); + mtpt->timer.function = multi_timeout; + mp_add_one_port(drv, &mtpt->port); + } +} + +/** + * pci_remap_base - remap BAR value of pci device + * + * PARAMETERS + * pcidev - pci_dev structure address + * offset - BAR offset PCI_BASE_ADDRESS_0 ~ PCI_BASE_ADDRESS_4 + * address - address to be changed BAR value + * size - size of address space + * + * RETURNS + * If this function performs successful, it returns 0. Otherwise, It returns -1. + */ +static int pci_remap_base(struct pci_dev *pcidev, unsigned int offset, + unsigned int address, unsigned int size) +{ +#if 0 + struct resource *root; + unsigned index = (offset - 0x10) >> 2; +#endif + + pci_write_config_dword(pcidev, offset, address); +#if 0 + root = pcidev->resource[index].parent; + release_resource(&pcidev->resource[index]); + address &= ~0x1; + pcidev->resource[index].start = address; + pcidev->resource[index].end = address + size - 1; + + if (request_resource(root, &pcidev->resource[index]) != NULL) + { + printk(KERN_ERR "pci remap conflict!! 0x%x\n", address); + return (-1); + } +#endif + + return (0); +} + +static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd) +{ + static struct mp_device_t * sbdev = mp_devs; + unsigned long addr = 0; + int j; + struct resource * ret = NULL; + + sbdev->device_id = brd.device_id; + pci_read_config_byte(pcidev, PCI_CLASS_REVISION, &(sbdev->revision)); + sbdev->name = brd.name; + sbdev->uart_access_addr = pcidev->resource[0].start & PCI_BASE_ADDRESS_IO_MASK; + + /* check revision. The SB16C1053APCI's option i/o address is BAR4 */ + if (sbdev->revision == 0xc0) + { + /* SB16C1053APCI */ + sbdev->option_reg_addr = pcidev->resource[4].start & PCI_BASE_ADDRESS_IO_MASK; + } + else + { + sbdev->option_reg_addr = pcidev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK; + } +#if 1 + if (sbdev->revision == 0xc0) + { + outb(0x00, sbdev->option_reg_addr + MP_OPTR_GPOCR); + inb(sbdev->option_reg_addr + MP_OPTR_GPOCR); + outb(0x83, sbdev->option_reg_addr + MP_OPTR_GPOCR); + } +#endif + + sbdev->irq = pcidev->irq; + + if ((brd.device_id & 0x0800) || !(brd.device_id &0xff00)) + { + sbdev->poll_type = TYPE_INTERRUPT; + } + else + { + sbdev->poll_type = TYPE_POLL; + } + + /* codes which is specific to each board*/ + switch(brd.device_id){ + case PCI_DEVICE_ID_MP1 : + case PCIE_DEVICE_ID_MP1 : + case PCIE_DEVICE_ID_MP1E : + case PCIE_DEVICE_ID_GT_MP1 : + sbdev->nr_ports = 1; + break; + case PCI_DEVICE_ID_MP2 : + case PCIE_DEVICE_ID_MP2 : + case PCIE_DEVICE_ID_GT_MP2 : + case PCIE_DEVICE_ID_MP2B : + case PCIE_DEVICE_ID_MP2E : + sbdev->nr_ports = 2; + + /* serial base address remap */ + if (sbdev->revision == 0xc0) + { + int prev_port_addr = 0; + + pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8); + } + break; + case PCI_DEVICE_ID_MP4 : + case PCI_DEVICE_ID_MP4A : + case PCIE_DEVICE_ID_MP4 : + case PCI_DEVICE_ID_GT_MP4 : + case PCI_DEVICE_ID_GT_MP4A : + case PCIE_DEVICE_ID_GT_MP4 : + case PCI_DEVICE_ID_MP4M : + case PCIE_DEVICE_ID_MP4B : + sbdev->nr_ports = 4; + + if(sbdev->revision == 0x91){ + sbdev->reserved_addr[0] = pcidev->resource[0].start & PCI_BASE_ADDRESS_IO_MASK; + outb(0x03 , sbdev->reserved_addr[0] + 0x01); + outb(0x03 , sbdev->reserved_addr[0] + 0x02); + outb(0x01 , sbdev->reserved_addr[0] + 0x20); + outb(0x00 , sbdev->reserved_addr[0] + 0x21); + request_region(sbdev->reserved_addr[0], 32, sbdev->name); + sbdev->uart_access_addr = pcidev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK; + sbdev->option_reg_addr = pcidev->resource[2].start & PCI_BASE_ADDRESS_IO_MASK; + } + + /* SB16C1053APCI */ + if (sbdev->revision == 0xc0) + { + int prev_port_addr = 0; + + pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_2, prev_port_addr + 16, 8); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_3, prev_port_addr + 24, 8); + } + break; + case PCI_DEVICE_ID_MP6 : + case PCI_DEVICE_ID_MP6A : + case PCI_DEVICE_ID_GT_MP6 : + case PCI_DEVICE_ID_GT_MP6A : + sbdev->nr_ports = 6; + + /* SB16C1053APCI */ + if (sbdev->revision == 0xc0) + { + int prev_port_addr = 0; + + pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_2, prev_port_addr + 16, 16); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_3, prev_port_addr + 32, 16); + } + break; + case PCI_DEVICE_ID_MP8 : + case PCIE_DEVICE_ID_MP8 : + case PCI_DEVICE_ID_GT_MP8 : + case PCIE_DEVICE_ID_GT_MP8 : + case PCIE_DEVICE_ID_MP8B : + sbdev->nr_ports = 8; + break; + case PCI_DEVICE_ID_MP32 : + case PCIE_DEVICE_ID_MP32 : + case PCI_DEVICE_ID_GT_MP32 : + case PCIE_DEVICE_ID_GT_MP32 : + { + int portnum_hex=0; + portnum_hex = inb(sbdev->option_reg_addr); + sbdev->nr_ports = ((portnum_hex/16)*10) + (portnum_hex % 16); + } + break; + case PCI_DEVICE_ID_MP2S1P : + sbdev->nr_ports = 2; + + /* SB16C1053APCI */ + if (sbdev->revision == 0xc0) + { + int prev_port_addr = 0; + + pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8); + } + + /* add PC compatible parallel port */ + parport_pc_probe_port(pcidev->resource[2].start, pcidev->resource[3].start, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &pcidev->dev, 0); + break; + case PCI_DEVICE_ID_MP1P : + /* add PC compatible parallel port */ + parport_pc_probe_port(pcidev->resource[2].start, pcidev->resource[3].start, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &pcidev->dev, 0); + break; + } + + ret = request_region(sbdev->uart_access_addr, (8*sbdev->nr_ports), sbdev->name); + + if (sbdev->revision == 0xc0) + { + ret = request_region(sbdev->option_reg_addr, 0x40, sbdev->name); + } + else + { + ret = request_region(sbdev->option_reg_addr, 0x20, sbdev->name); + } + + + NR_BOARD++; + NR_PORTS += sbdev->nr_ports; + + /* Enable PCI interrupt */ + addr = sbdev->option_reg_addr + MP_OPTR_IMR0; + for(j=0; j < (sbdev->nr_ports/8)+1; j++) + { + if (sbdev->poll_type == TYPE_INTERRUPT) + { + outb(0xff,addr +j); + } + } + sbdev++; + + return 0; +} + +static int __init multi_init(void) +{ + int ret, i; + struct pci_dev *dev = NULL; + + if(fcr_count==0) + { + for(i=0;i<256;i++) + { + fcr_arr[i] = 0x01; + + } + } + if(deep_count==0) + { + for(i=0;i<256;i++) + { + deep[i] = 1; + + } + } + if(rtr_count==0) + { + for(i=0;i<256;i++) + { + rtr[i] = 0x10; + } + } + if(ttr_count==0) + { + for(i=0;i<256;i++) + { + ttr[i] = 0x38; + } + } + + +printk("MULTI INIT\n"); + for( i=0; i< mp_nrpcibrds; i++) + { + + while( (dev = pci_get_device(mp_pciboards[i].vendor_id, mp_pciboards[i].device_id, dev) ) ) + + { +printk("FOUND~~~\n"); +// Cent OS bug fix +// if (mp_pciboards[i].device_id & 0x0800) + { + int status; + pci_disable_device(dev); + status = pci_enable_device(dev); + + if (status != 0) + { + printk("Multiport Board Enable Fail !\n\n"); + status = -ENXIO; + return status; + } + } + + init_mp_dev(dev, mp_pciboards[i]); + } + } + + for (i = 0; i < NR_IRQS; i++) + spin_lock_init(&irq_lists[i].lock); + + ret = mp_register_driver(&multi_reg); + + if (ret >= 0) + multi_register_ports(&multi_reg); + + return ret; +} + +static void __exit multi_exit(void) +{ + int i; + + for (i = 0; i < NR_PORTS; i++) + mp_remove_one_port(&multi_reg, &multi_ports[i].port); + + mp_unregister_driver(&multi_reg); +} + +module_init(multi_init); +module_exit(multi_exit); + +MODULE_DESCRIPTION("SystemBase Multiport PCI/PCIe CORE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/sb105x/sb_pci_mp.h b/drivers/staging/sb105x/sb_pci_mp.h new file mode 100644 index 0000000..f33efde --- /dev/null +++ b/drivers/staging/sb105x/sb_pci_mp.h @@ -0,0 +1,293 @@ +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/serial_reg.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/sched.h> + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/wait.h> +#include <linux/tty_driver.h> +#include <linux/pci.h> +#include <linux/circ_buf.h> + +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/segment.h> +#include <asm/serial.h> +#include <linux/interrupt.h> + + +#include <linux/parport.h> +#include <linux/ctype.h> +#include <linux/poll.h> + + +#define MP_TERMIOS ktermios + +#include "sb_mp_register.h" +#include "sb_ser_core.h" + +#define DRIVER_VERSION "1.1" +#define DRIVER_DATE "2012/01/05" +#define DRIVER_AUTHOR "SYSTEMBASE<tech@sysbas.com>" +#define DRIVER_DESC "SystemBase PCI/PCIe Multiport Core" + +#define SB_TTY_MP_MAJOR 54 +#define PCI_VENDOR_ID_MULTIPORT 0x14A1 + +#define PCI_DEVICE_ID_MP1 0x4d01 +#define PCI_DEVICE_ID_MP2 0x4d02 +#define PCI_DEVICE_ID_MP4 0x4d04 +#define PCI_DEVICE_ID_MP4A 0x4d54 +#define PCI_DEVICE_ID_MP6 0x4d06 +#define PCI_DEVICE_ID_MP6A 0x4d56 +#define PCI_DEVICE_ID_MP8 0x4d08 +#define PCI_DEVICE_ID_MP32 0x4d32 +/* Parallel port */ +#define PCI_DEVICE_ID_MP1P 0x4301 +#define PCI_DEVICE_ID_MP2S1P 0x4303 + +#define PCIE_DEVICE_ID_MP1 0x4501 +#define PCIE_DEVICE_ID_MP2 0x4502 +#define PCIE_DEVICE_ID_MP4 0x4504 +#define PCIE_DEVICE_ID_MP8 0x4508 +#define PCIE_DEVICE_ID_MP32 0x4532 + +#define PCIE_DEVICE_ID_MP1E 0x4e01 +#define PCIE_DEVICE_ID_MP2E 0x4e02 +#define PCIE_DEVICE_ID_MP2B 0x4b02 +#define PCIE_DEVICE_ID_MP4B 0x4b04 +#define PCIE_DEVICE_ID_MP8B 0x4b08 + +#define PCI_DEVICE_ID_GT_MP4 0x0004 +#define PCI_DEVICE_ID_GT_MP4A 0x0054 +#define PCI_DEVICE_ID_GT_MP6 0x0006 +#define PCI_DEVICE_ID_GT_MP6A 0x0056 +#define PCI_DEVICE_ID_GT_MP8 0x0008 +#define PCI_DEVICE_ID_GT_MP32 0x0032 + +#define PCIE_DEVICE_ID_GT_MP1 0x1501 +#define PCIE_DEVICE_ID_GT_MP2 0x1502 +#define PCIE_DEVICE_ID_GT_MP4 0x1504 +#define PCIE_DEVICE_ID_GT_MP8 0x1508 +#define PCIE_DEVICE_ID_GT_MP32 0x1532 + +#define PCI_DEVICE_ID_MP4M 0x4604 //modem + +#define MAX_MP_DEV 8 +#define BD_MAX_PORT 32 /* Max serial port in one board */ +#define MAX_MP_PORT 256 /* Max serial port in one PC */ + +#define PORT_16C105XA 3 +#define PORT_16C105X 2 +#define PORT_16C55X 1 + +#define ENABLE 1 +#define DISABLE 0 + +/* ioctls */ +#define TIOCGNUMOFPORT 0x545F +#define TIOCSMULTIECHO 0x5440 +#define TIOCSPTPNOECHO 0x5441 + +#define TIOCGOPTIONREG 0x5461 +#define TIOCGDISABLEIRQ 0x5462 +#define TIOCGENABLEIRQ 0x5463 +#define TIOCGSOFTRESET 0x5464 +#define TIOCGSOFTRESETR 0x5465 +#define TIOCGREGINFO 0x5466 +#define TIOCGGETLSR 0x5467 +#define TIOCGGETDEVID 0x5468 +#define TIOCGGETBDNO 0x5469 +#define TIOCGGETINTERFACE 0x546A +#define TIOCGGETREV 0x546B +#define TIOCGGETNRPORTS 0x546C +#define TIOCGGETPORTTYPE 0x546D +#define GETDEEPFIFO 0x54AA +#define SETDEEPFIFO 0x54AB +#define SETFCR 0x54BA +#define SETTTR 0x54B1 +#define SETRTR 0x54B2 +#define GETTTR 0x54B3 +#define GETRTR 0x54B4 + +/* multi-drop mode related ioctl commands */ +#define TIOCSMULTIDROP 0x5470 +#define TIOCSMDADDR 0x5471 +#define TIOCGMDADDR 0x5472 +#define TIOCSENDADDR 0x5473 + + +/* serial interface */ +#define RS232 1 +#define RS422PTP 2 +#define RS422MD 3 +#define RS485NE 4 +#define RS485ECHO 5 + +#define serial_inp(up, offset) serial_in(up, offset) +#define serial_outp(up, offset, value) serial_out(up, offset, value) + +#define PASS_LIMIT 256 +#define is_real_interrupt(irq) ((irq) != 0) + +#define PROBE_ANY (~0) + +static DEFINE_MUTEX(mp_mutex); +#define MP_MUTEX_LOCK(x) mutex_lock(&(x)) +#define MP_MUTEX_UNLOCK(x) mutex_unlock(&(x)) +#define MP_STATE_LOCK(x) mutex_lock(&((x)->mutex)) +#define MP_STATE_UNLOCK(x) mutex_unlock(&((x)->mutex)) + + +#define UART_LSR_SPECIAL 0x1E + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) +#define uart_users(state) ((state)->count + ((state)->info ? (state)->info->blocked_open : 0)) + + +//#define MP_DEBUG 1 +#undef MP_DEBUG + +#ifdef MP_DEBUG +#define DPRINTK(x...) printk(x) +#else +#define DPRINTK(x...) do { } while (0) +#endif + +#ifdef MP_DEBUG +#define DEBUG_AUTOCONF(fmt...) printk(fmt) +#else +#define DEBUG_AUTOCONF(fmt...) do { } while (0) +#endif + +#ifdef MP_DEBUG +#define DEBUG_INTR(fmt...) printk(fmt) +#else +#define DEBUG_INTR(fmt...) do { } while (0) +#endif + +#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) +#define SERIAL_INLINE +#endif +#ifdef SERIAL_INLINE +#define _INLINE_ inline +#else +#define _INLINE_ +#endif + +#define TYPE_POLL 1 +#define TYPE_INTERRUPT 2 + + +struct mp_device_t { + unsigned short device_id; + unsigned char revision; + char *name; + unsigned long uart_access_addr; + unsigned long option_reg_addr; + unsigned long reserved_addr[4]; + int irq; + int nr_ports; + int poll_type; +}; + +typedef struct mppcibrd { + char *name; + unsigned short vendor_id; + unsigned short device_id; +} mppcibrd_t; + +static mppcibrd_t mp_pciboards[] = { + + { "Multi-1 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP1} , + { "Multi-2 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP2} , + { "Multi-4 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP4} , + { "Multi-4 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP4A} , + { "Multi-6 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP6} , + { "Multi-6 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP6A} , + { "Multi-8 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP8} , + { "Multi-32 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP32} , + + { "Multi-1P PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP1P} , + { "Multi-2S1P PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP2S1P} , + + { "Multi-4(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP4} , + { "Multi-4(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP4A} , + { "Multi-6(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP6} , + { "Multi-6(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP6A} , + { "Multi-8(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP8} , + { "Multi-32(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP32} , + + { "Multi-1 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP1} , + { "Multi-2 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP2} , + { "Multi-4 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP4} , + { "Multi-8 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP8} , + { "Multi-32 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP32} , + + { "Multi-1 PCIe E", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP1E} , + { "Multi-2 PCIe E", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP2E} , + { "Multi-2 PCIe B", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP2B} , + { "Multi-4 PCIe B", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP4B} , + { "Multi-8 PCIe B", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP8B} , + + { "Multi-1(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP1} , + { "Multi-2(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP2} , + { "Multi-4(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP4} , + { "Multi-8(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP8} , + { "Multi-32(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP32} , + + { "Multi-4M PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP4M} , +}; + +struct mp_port { + struct sb_uart_port port; + + struct timer_list timer; /* "no irq" timer */ + struct list_head list; /* ports on this IRQ */ + unsigned int capabilities; /* port capabilities */ + unsigned short rev; + unsigned char acr; + unsigned char ier; + unsigned char lcr; + unsigned char mcr; + unsigned char mcr_mask; /* mask of user bits */ + unsigned char mcr_force; /* mask of forced bits */ + unsigned char lsr_break_flag; + + void (*pm)(struct sb_uart_port *port, + unsigned int state, unsigned int old); + struct mp_device_t *device; + unsigned long interface_config_addr; + unsigned long option_base_addr; + unsigned char interface; + unsigned char poll_type; +}; + +struct irq_info { + spinlock_t lock; + struct list_head *head; +}; + +struct sb105x_uart_config { + char *name; + int dfl_xmit_fifo_size; + int flags; +}; + +static const struct sb105x_uart_config uart_config[] = { + { "unknown", 1, 0 }, + { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "SB16C1050", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, + { "SB16C1050A", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, +}; + + + diff --git a/drivers/staging/sb105x/sb_ser_core.h b/drivers/staging/sb105x/sb_ser_core.h new file mode 100644 index 0000000..c8fb991 --- /dev/null +++ b/drivers/staging/sb105x/sb_ser_core.h @@ -0,0 +1,368 @@ +#include <linux/wait.h> + +#define UART_CONFIG_TYPE (1 << 0) +#define UART_CONFIG_IRQ (1 << 1) +#define UPIO_PORT (0) +#define UPIO_HUB6 (1) +#define UPIO_MEM (2) +#define UPIO_MEM32 (3) +#define UPIO_AU (4) /* Au1x00 type IO */ +#define UPIO_TSI (5) /* Tsi108/109 type IO */ +#define UPF_FOURPORT (1 << 1) +#define UPF_SAK (1 << 2) +#define UPF_SPD_MASK (0x1030) +#define UPF_SPD_HI (0x0010) +#define UPF_SPD_VHI (0x0020) +#define UPF_SPD_CUST (0x0030) +#define UPF_SPD_SHI (0x1000) +#define UPF_SPD_WARP (0x1010) +#define UPF_SKIP_TEST (1 << 6) +#define UPF_AUTO_IRQ (1 << 7) +#define UPF_HARDPPS_CD (1 << 11) +#define UPF_LOW_LATENCY (1 << 13) +#define UPF_BUGGY_UART (1 << 14) +#define UPF_MAGIC_MULTIPLIER (1 << 16) +#define UPF_CONS_FLOW (1 << 23) +#define UPF_SHARE_IRQ (1 << 24) +#define UPF_BOOT_AUTOCONF (1 << 28) +#define UPF_DEAD (1 << 30) +#define UPF_IOREMAP (1 << 31) +#define UPF_CHANGE_MASK (0x17fff) +#define UPF_USR_MASK (UPF_SPD_MASK|UPF_LOW_LATENCY) +#define USF_CLOSING_WAIT_INF (0) +#define USF_CLOSING_WAIT_NONE (~0U) + +#define UART_XMIT_SIZE PAGE_SIZE + +#define UIF_CHECK_CD (1 << 25) +#define UIF_CTS_FLOW (1 << 26) +#define UIF_NORMAL_ACTIVE (1 << 29) +#define UIF_INITIALIZED (1 << 31) +#define UIF_SUSPENDED (1 << 30) + +#define WAKEUP_CHARS 256 + +#define uart_circ_empty(circ) ((circ)->head == (circ)->tail) +#define uart_circ_clear(circ) ((circ)->head = (circ)->tail = 0) + +#define uart_circ_chars_pending(circ) \ + (CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE)) + +#define uart_circ_chars_free(circ) \ + (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE)) + +#define uart_tx_stopped(port) \ + ((port)->info->tty->stopped || (port)->info->tty->hw_stopped) + +#define UART_ENABLE_MS(port,cflag) ((port)->flags & UPF_HARDPPS_CD || \ + (cflag) & CRTSCTS || \ + !((cflag) & CLOCAL)) + + +struct sb_uart_port; +struct sb_uart_info; +struct serial_struct; +struct device; + +struct sb_uart_ops { + unsigned int (*tx_empty)(struct sb_uart_port *); + void (*set_mctrl)(struct sb_uart_port *, unsigned int mctrl); + unsigned int (*get_mctrl)(struct sb_uart_port *); + void (*stop_tx)(struct sb_uart_port *); + void (*start_tx)(struct sb_uart_port *); + void (*send_xchar)(struct sb_uart_port *, char ch); + void (*stop_rx)(struct sb_uart_port *); + void (*enable_ms)(struct sb_uart_port *); + void (*break_ctl)(struct sb_uart_port *, int ctl); + int (*startup)(struct sb_uart_port *); + void (*shutdown)(struct sb_uart_port *); + void (*set_termios)(struct sb_uart_port *, struct MP_TERMIOS *new, + struct MP_TERMIOS *old); + void (*pm)(struct sb_uart_port *, unsigned int state, + unsigned int oldstate); + int (*set_wake)(struct sb_uart_port *, unsigned int state); + + const char *(*type)(struct sb_uart_port *); + + void (*release_port)(struct sb_uart_port *); + + int (*request_port)(struct sb_uart_port *); + void (*config_port)(struct sb_uart_port *, int); + int (*verify_port)(struct sb_uart_port *, struct serial_struct *); + int (*ioctl)(struct sb_uart_port *, unsigned int, unsigned long); +}; + + +struct sb_uart_icount { + __u32 cts; + __u32 dsr; + __u32 rng; + __u32 dcd; + __u32 rx; + __u32 tx; + __u32 frame; + __u32 overrun; + __u32 parity; + __u32 brk; + __u32 buf_overrun; +}; +typedef unsigned int upf_t; + +struct sb_uart_port { + spinlock_t lock; /* port lock */ + unsigned int iobase; /* in/out[bwl] */ + unsigned char __iomem *membase; /* read/write[bwl] */ + unsigned int irq; /* irq number */ + unsigned int uartclk; /* base uart clock */ + unsigned int fifosize; /* tx fifo size */ + unsigned char x_char; /* xon/xoff char */ + unsigned char regshift; /* reg offset shift */ + unsigned char iotype; /* io access style */ + unsigned char unused1; + + + unsigned int read_status_mask; /* driver specific */ + unsigned int ignore_status_mask; /* driver specific */ + struct sb_uart_info *info; /* pointer to parent info */ + struct sb_uart_icount icount; /* statistics */ + + struct console *cons; /* struct console, if any */ +#ifdef CONFIG_SERIAL_CORE_CONSOLE + unsigned long sysrq; /* sysrq timeout */ +#endif + + upf_t flags; + + unsigned int mctrl; /* current modem ctrl settings */ + unsigned int timeout; /* character-based timeout */ + unsigned int type; /* port type */ + const struct sb_uart_ops *ops; + unsigned int custom_divisor; + unsigned int line; /* port index */ + unsigned long mapbase; /* for ioremap */ + struct device *dev; /* parent device */ + unsigned char hub6; /* this should be in the 8250 driver */ + unsigned char unused[3]; +}; + +#define mdmode unused[2] +#define MDMODE_ADDR 0x1 +#define MDMODE_ENABLE 0x2 +#define MDMODE_AUTO 0x4 +#define MDMODE_ADDRSEND 0x8 + +struct sb_uart_state { + unsigned int close_delay; /* msec */ + unsigned int closing_wait; /* msec */ + + + int count; + int pm_state; + struct sb_uart_info *info; + struct sb_uart_port *port; + + struct mutex mutex; +}; + +typedef unsigned int uif_t; + +struct sb_uart_info { + struct tty_struct *tty; + struct circ_buf xmit; + uif_t flags; + + int blocked_open; + + struct tasklet_struct tlet; + + wait_queue_head_t open_wait; + wait_queue_head_t delta_msr_wait; +}; + + +struct module; +struct tty_driver; + +struct uart_driver { + struct module *owner; + const char *driver_name; + const char *dev_name; + int major; + int minor; + int nr; + struct console *cons; + + struct sb_uart_state *state; + struct tty_driver *tty_driver; +}; + +void sb_uart_write_wakeup(struct sb_uart_port *port) +{ + struct sb_uart_info *info = port->info; + tasklet_schedule(&info->tlet); +} + +void sb_uart_update_timeout(struct sb_uart_port *port, unsigned int cflag, + unsigned int baud) +{ + unsigned int bits; + + switch (cflag & CSIZE) + { + case CS5: + bits = 7; + break; + + case CS6: + bits = 8; + break; + + case CS7: + bits = 9; + break; + + default: + bits = 10; + break; + } + + if (cflag & CSTOPB) + { + bits++; + } + + if (cflag & PARENB) + { + bits++; + } + + bits = bits * port->fifosize; + + port->timeout = (HZ * bits) / baud + HZ/50; +} +unsigned int sb_uart_get_baud_rate(struct sb_uart_port *port, struct MP_TERMIOS *termios, + struct MP_TERMIOS *old, unsigned int min, + unsigned int max) +{ + unsigned int try, baud, altbaud = 38400; + upf_t flags = port->flags & UPF_SPD_MASK; + + if (flags == UPF_SPD_HI) + altbaud = 57600; + if (flags == UPF_SPD_VHI) + altbaud = 115200; + if (flags == UPF_SPD_SHI) + altbaud = 230400; + if (flags == UPF_SPD_WARP) + altbaud = 460800; + + for (try = 0; try < 2; try++) { + + switch (termios->c_cflag & (CBAUD | CBAUDEX)) + { + case B921600 : baud = 921600; break; + case B460800 : baud = 460800; break; + case B230400 : baud = 230400; break; + case B115200 : baud = 115200; break; + case B57600 : baud = 57600; break; + case B38400 : baud = 38400; break; + case B19200 : baud = 19200; break; + case B9600 : baud = 9600; break; + case B4800 : baud = 4800; break; + case B2400 : baud = 2400; break; + case B1800 : baud = 1800; break; + case B1200 : baud = 1200; break; + case B600 : baud = 600; break; + case B300 : baud = 300; break; + case B200 : baud = 200; break; + case B150 : baud = 150; break; + case B134 : baud = 134; break; + case B110 : baud = 110; break; + case B75 : baud = 75; break; + case B50 : baud = 50; break; + default : baud = 9600; break; + } + + if (baud == 38400) + baud = altbaud; + + if (baud == 0) + baud = 9600; + + if (baud >= min && baud <= max) + return baud; + + termios->c_cflag &= ~CBAUD; + if (old) { + termios->c_cflag |= old->c_cflag & CBAUD; + old = NULL; + continue; + } + + termios->c_cflag |= B9600; + } + + return 0; +} +unsigned int sb_uart_get_divisor(struct sb_uart_port *port, unsigned int baud) +{ + unsigned int quot; + + if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) + quot = port->custom_divisor; + else + quot = (port->uartclk + (8 * baud)) / (16 * baud); + + return quot; +} + + + +static inline int sb_uart_handle_break(struct sb_uart_port *port) +{ + struct sb_uart_info *info = port->info; + + if (port->flags & UPF_SAK) + do_SAK(info->tty); + return 0; +} + +static inline void sb_uart_handle_dcd_change(struct sb_uart_port *port, unsigned int status) +{ + struct sb_uart_info *info = port->info; + + port->icount.dcd++; + + if (info->flags & UIF_CHECK_CD) { + if (status) + wake_up_interruptible(&info->open_wait); + else if (info->tty) + tty_hangup(info->tty); + } +} + +static inline void sb_uart_handle_cts_change(struct sb_uart_port *port, unsigned int status) +{ + struct sb_uart_info *info = port->info; + struct tty_struct *tty = info->tty; + + port->icount.cts++; + + if (info->flags & UIF_CTS_FLOW) { + if (tty->hw_stopped) { + if (status) { + tty->hw_stopped = 0; + port->ops->start_tx(port); + sb_uart_write_wakeup(port); + } + } else { + if (!status) { + tty->hw_stopped = 1; + port->ops->stop_tx(port); + } + } + } +} + + + diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 42d0a25..9d7d00c 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1771,6 +1771,7 @@ fail_free_irq: fail_unregister: tty_unregister_driver(serial_driver); fail_put_tty_driver: + tty_port_destroy(&state->tport); put_tty_driver(serial_driver); return error; } @@ -1785,6 +1786,7 @@ static int __exit amiga_serial_remove(struct platform_device *pdev) printk("SERIAL: failed to unregister serial driver (%d)\n", error); put_tty_driver(serial_driver); + tty_port_destroy(&state->tport); free_irq(IRQ_AMIGA_TBE, state); free_irq(IRQ_AMIGA_RBF, state); diff --git a/drivers/tty/bfin_jtag_comm.c b/drivers/tty/bfin_jtag_comm.c index 02b7d3a..1cfcdbf 100644 --- a/drivers/tty/bfin_jtag_comm.c +++ b/drivers/tty/bfin_jtag_comm.c @@ -240,8 +240,6 @@ static int __init bfin_jc_init(void) { int ret; - tty_port_init(&port); - bfin_jc_kthread = kthread_create(bfin_jc_emudat_manager, NULL, DRV_NAME); if (IS_ERR(bfin_jc_kthread)) return PTR_ERR(bfin_jc_kthread); @@ -257,6 +255,8 @@ static int __init bfin_jc_init(void) if (!bfin_jc_driver) goto err_driver; + tty_port_init(&port); + bfin_jc_driver->driver_name = DRV_NAME; bfin_jc_driver->name = DEV_NAME; bfin_jc_driver->type = TTY_DRIVER_TYPE_SERIAL; @@ -274,6 +274,7 @@ static int __init bfin_jc_init(void) return 0; err: + tty_port_destroy(&port); put_tty_driver(bfin_jc_driver); err_driver: kfree(bfin_jc_write_buf.buf); @@ -289,6 +290,7 @@ static void __exit bfin_jc_exit(void) kfree(bfin_jc_write_buf.buf); tty_unregister_driver(bfin_jc_driver); put_tty_driver(bfin_jc_driver); + tty_port_destroy(&port); } module_exit(bfin_jc_exit); diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index 0a6a0bc..de25774 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -3934,7 +3934,7 @@ err: static void __devexit cy_pci_remove(struct pci_dev *pdev) { struct cyclades_card *cinfo = pci_get_drvdata(pdev); - unsigned int i; + unsigned int i, channel; /* non-Z with old PLX */ if (!cy_is_Z(cinfo) && (readb(cinfo->base_addr + CyPLX_VER) & 0x0f) == @@ -3960,9 +3960,11 @@ static void __devexit cy_pci_remove(struct pci_dev *pdev) pci_release_regions(pdev); cinfo->base_addr = NULL; - for (i = cinfo->first_line; i < cinfo->first_line + - cinfo->nports; i++) + 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); } diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index 4ab936b..4193afb 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -757,6 +757,7 @@ static int __devinit ehv_bc_tty_probe(struct platform_device *pdev) return 0; error: + tty_port_destroy(&bc->port); irq_dispose_mapping(bc->tx_irq); irq_dispose_mapping(bc->rx_irq); @@ -770,6 +771,7 @@ static int ehv_bc_tty_remove(struct platform_device *pdev) tty_unregister_device(ehv_bc_driver, bc - bcs); + tty_port_destroy(&bc->port); irq_dispose_mapping(bc->tx_irq); irq_dispose_mapping(bc->rx_irq); diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index cab5c7a..744c3b8 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -1496,8 +1496,10 @@ static int __devinit hvcs_initialize(void) num_ttys_to_alloc = hvcs_parm_num_devs; hvcs_tty_driver = alloc_tty_driver(num_ttys_to_alloc); - if (!hvcs_tty_driver) + if (!hvcs_tty_driver) { + mutex_unlock(&hvcs_init_mutex); return -ENOMEM; + } if (hvcs_alloc_index_list(num_ttys_to_alloc)) { rc = -ENOMEM; diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index 5b95b4f..68357a6 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -1218,6 +1218,7 @@ static int __init hvsi_console_init(void) if (hp->virq == 0) { printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n", __func__, irq[0]); + tty_port_destroy(&hp->port); continue; } diff --git a/drivers/tty/ipwireless/network.c b/drivers/tty/ipwireless/network.c index 57102e6..c0dfb64 100644 --- a/drivers/tty/ipwireless/network.c +++ b/drivers/tty/ipwireless/network.c @@ -352,6 +352,8 @@ static struct sk_buff *ipw_packet_received_skb(unsigned char *data, } skb = dev_alloc_skb(length + 4); + if (skb == NULL) + return NULL; skb_reserve(skb, 2); memcpy(skb_put(skb, length), data, length); @@ -397,7 +399,8 @@ void ipwireless_network_packet_received(struct ipw_network *network, /* Send the data to the ppp_generic module. */ skb = ipw_packet_received_skb(data, length); - ppp_input(network->ppp_channel, skb); + if (skb) + ppp_input(network->ppp_channel, skb); } else spin_unlock_irqrestore(&network->lock, flags); diff --git a/drivers/tty/ipwireless/tty.c b/drivers/tty/ipwireless/tty.c index 160f0ad..2cde13d 100644 --- a/drivers/tty/ipwireless/tty.c +++ b/drivers/tty/ipwireless/tty.c @@ -566,6 +566,7 @@ void ipwireless_tty_free(struct ipw_tty *tty) ipwireless_disassociate_network_ttys(network, ttyj->channel_idx); tty_unregister_device(ipw_tty_driver, j); + tty_port_destroy(&ttyj->port); ttys[j] = NULL; mutex_unlock(&ttyj->ipw_tty_mutex); kfree(ttyj); diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c index d7492e1..67f288d 100644 --- a/drivers/tty/isicom.c +++ b/drivers/tty/isicom.c @@ -603,7 +603,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) if (tty_port_cts_enabled(&port->port)) { if (tty->hw_stopped) { if (header & ISI_CTS) { - port->port.tty->hw_stopped = 0; + tty->hw_stopped = 0; /* start tx ing */ port->status |= (ISI_TXOK | ISI_CTS); @@ -1610,10 +1610,15 @@ static int __devinit isicom_probe(struct pci_dev *pdev, if (retval < 0) goto errunri; - for (index = 0; index < board->port_count; index++) - tty_port_register_device(&board->ports[index].port, - isicom_normal, board->index * 16 + index, - &pdev->dev); + 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; @@ -1635,8 +1640,10 @@ static void __devexit 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++) + 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); @@ -1655,13 +1662,9 @@ static int __init isicom_init(void) isi_card[idx].ports = port; spin_lock_init(&isi_card[idx].card_lock); for (channel = 0; channel < 16; channel++, port++) { - tty_port_init(&port->port); - port->port.ops = &isicom_port_ops; port->magic = ISICOM_MAGIC; port->card = &isi_card[idx]; port->channel = channel; - port->port.close_delay = 50 * HZ/100; - port->port.closing_wait = 3000 * HZ/100; port->status = 0; /* . . . */ } diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index 56e616b..d628176 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -895,6 +895,8 @@ static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev) return 0; err_free: + for (i = 0; i < MAX_PORTS_PER_BOARD; i++) + tty_port_destroy(&brd->ports[i].port); kfree(brd->ports); err: return ret; @@ -919,6 +921,8 @@ static void moxa_board_deinit(struct moxa_board_conf *brd) tty_kref_put(tty); } } + for (a = 0; a < MAX_PORTS_PER_BOARD; a++) + tty_port_destroy(&brd->ports[a].port); while (1) { opened = 0; for (a = 0; a < brd->numPorts; a++) @@ -1370,7 +1374,7 @@ static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd) p->DCDState = dcd; spin_unlock_irqrestore(&p->port.lock, flags); tty = tty_port_tty_get(&p->port); - if (tty && C_CLOCAL(tty) && !dcd) + if (tty && !C_CLOCAL(tty) && !dcd) tty_hangup(tty); tty_kref_put(tty); } diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index cfda47d..802a248 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -2411,14 +2411,27 @@ static int __devinit mxser_initbrd(struct mxser_board *brd, retval = request_irq(brd->irq, mxser_interrupt, IRQF_SHARED, "mxser", brd); - if (retval) + if (retval) { + for (i = 0; i < brd->info->nports; i++) + tty_port_destroy(&brd->ports[i].port); printk(KERN_ERR "Board %s: Request irq failed, IRQ (%d) may " "conflict with another device.\n", brd->info->name, brd->irq); + } return retval; } +static void mxser_board_remove(struct mxser_board *brd) +{ + unsigned int i; + + for (i = 0; i < brd->info->nports; i++) { + tty_unregister_device(mxvar_sdriver, brd->idx + i); + tty_port_destroy(&brd->ports[i].port); + } +} + static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd) { int id, i, bits, ret; @@ -2649,10 +2662,8 @@ static void __devexit mxser_remove(struct pci_dev *pdev) { #ifdef CONFIG_PCI struct mxser_board *brd = pci_get_drvdata(pdev); - unsigned int i; - for (i = 0; i < brd->info->nports; i++) - tty_unregister_device(mxvar_sdriver, brd->idx + i); + mxser_board_remove(brd); free_irq(pdev->irq, brd); pci_release_region(pdev, 2); @@ -2748,15 +2759,13 @@ err_put: static void __exit mxser_module_exit(void) { - unsigned int i, j; + unsigned int i; pci_unregister_driver(&mxser_driver); for (i = 0; i < MXSER_BOARDS; i++) /* ISA remains */ if (mxser_boards[i].info != NULL) - for (j = 0; j < mxser_boards[i].info->nports; j++) - tty_unregister_device(mxvar_sdriver, - mxser_boards[i].idx + j); + mxser_board_remove(&mxser_boards[i]); tty_unregister_driver(mxvar_sdriver); put_tty_driver(mxvar_sdriver); diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 1e8e8ce..dcc0430 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -134,7 +134,6 @@ struct gsm_dlci { #define DLCI_OPENING 1 /* Sending SABM not seen UA */ #define DLCI_OPEN 2 /* SABM/UA complete */ #define DLCI_CLOSING 3 /* Sending DISC not seen UA/DM */ - struct kref ref; /* freed from port or mux close */ struct mutex mutex; /* Link layer */ @@ -1635,7 +1634,6 @@ static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr) if (dlci == NULL) return NULL; spin_lock_init(&dlci->lock); - kref_init(&dlci->ref); mutex_init(&dlci->mutex); dlci->fifo = &dlci->_fifo; if (kfifo_alloc(&dlci->_fifo, 4096, GFP_KERNEL) < 0) { @@ -1669,9 +1667,9 @@ static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr) * * Can sleep. */ -static void gsm_dlci_free(struct kref *ref) +static void gsm_dlci_free(struct tty_port *port) { - struct gsm_dlci *dlci = container_of(ref, struct gsm_dlci, ref); + struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port); del_timer_sync(&dlci->t1); dlci->gsm->dlci[dlci->addr] = NULL; @@ -1683,12 +1681,12 @@ static void gsm_dlci_free(struct kref *ref) static inline void dlci_get(struct gsm_dlci *dlci) { - kref_get(&dlci->ref); + tty_port_get(&dlci->port); } static inline void dlci_put(struct gsm_dlci *dlci) { - kref_put(&dlci->ref, gsm_dlci_free); + tty_port_put(&dlci->port); } /** @@ -2874,6 +2872,7 @@ static void gsm_dtr_rts(struct tty_port *port, int onoff) static const struct tty_port_operations gsm_port_ops = { .carrier_raised = gsm_carrier_raised, .dtr_rts = gsm_dtr_rts, + .destruct = gsm_dlci_free, }; static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 8c0b7b4..19083ef 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -73,10 +73,42 @@ #define ECHO_OP_SET_CANON_COL 0x81 #define ECHO_OP_ERASE_TAB 0x82 +struct n_tty_data { + unsigned int column; + unsigned long overrun_time; + int num_overrun; + + unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; + unsigned char echo_overrun:1; + + DECLARE_BITMAP(process_char_map, 256); + DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE); + + char *read_buf; + int read_head; + int read_tail; + int read_cnt; + + unsigned char *echo_buf; + unsigned int echo_pos; + unsigned int echo_cnt; + + int canon_data; + unsigned long canon_head; + unsigned int canon_column; + + struct mutex atomic_read_lock; + struct mutex output_lock; + struct mutex echo_lock; + spinlock_t read_lock; +}; + static inline int tty_put_user(struct tty_struct *tty, unsigned char x, unsigned char __user *ptr) { - tty_audit_add_data(tty, &x, 1); + struct n_tty_data *ldata = tty->disc_data; + + tty_audit_add_data(tty, &x, 1, ldata->icanon); return put_user(x, ptr); } @@ -92,17 +124,18 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, static void n_tty_set_room(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; int left; int old_left; - /* tty->read_cnt is not read locked ? */ + /* ldata->read_cnt is not read locked ? */ if (I_PARMRK(tty)) { /* Multiply read_cnt by 3, since each byte might take up to * three times as many spaces when PARMRK is set (depending on * its flags, e.g. parity error). */ - left = N_TTY_BUF_SIZE - tty->read_cnt * 3 - 1; + left = N_TTY_BUF_SIZE - ldata->read_cnt * 3 - 1; } else - left = N_TTY_BUF_SIZE - tty->read_cnt - 1; + left = N_TTY_BUF_SIZE - ldata->read_cnt - 1; /* * If we are doing input canonicalization, and there are no @@ -111,44 +144,47 @@ static void n_tty_set_room(struct tty_struct *tty) * characters will be beeped. */ if (left <= 0) - left = tty->icanon && !tty->canon_data; + left = ldata->icanon && !ldata->canon_data; old_left = tty->receive_room; tty->receive_room = left; /* Did this open up the receive buffer? We may need to flip */ - if (left && !old_left) - schedule_work(&tty->buf.work); + if (left && !old_left) { + WARN_RATELIMIT(tty->port->itty == NULL, + "scheduling with invalid itty\n"); + schedule_work(&tty->port->buf.work); + } } -static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty) +static void put_tty_queue_nolock(unsigned char c, struct n_tty_data *ldata) { - if (tty->read_cnt < N_TTY_BUF_SIZE) { - tty->read_buf[tty->read_head] = c; - tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1); - tty->read_cnt++; + if (ldata->read_cnt < N_TTY_BUF_SIZE) { + ldata->read_buf[ldata->read_head] = c; + ldata->read_head = (ldata->read_head + 1) & (N_TTY_BUF_SIZE-1); + ldata->read_cnt++; } } /** * put_tty_queue - add character to tty * @c: character - * @tty: tty device + * @ldata: n_tty data * * Add a character to the tty read_buf queue. This is done under the * read_lock to serialize character addition and also to protect us * against parallel reads or flushes */ -static void put_tty_queue(unsigned char c, struct tty_struct *tty) +static void put_tty_queue(unsigned char c, struct n_tty_data *ldata) { unsigned long flags; /* * The problem of stomping on the buffers ends here. * Why didn't anyone see this one coming? --AJK */ - spin_lock_irqsave(&tty->read_lock, flags); - put_tty_queue_nolock(c, tty); - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); + put_tty_queue_nolock(c, ldata); + spin_unlock_irqrestore(&ldata->read_lock, flags); } /** @@ -179,18 +215,19 @@ static void check_unthrottle(struct tty_struct *tty) static void reset_buffer_flags(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; unsigned long flags; - spin_lock_irqsave(&tty->read_lock, flags); - tty->read_head = tty->read_tail = tty->read_cnt = 0; - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); + ldata->read_head = ldata->read_tail = ldata->read_cnt = 0; + spin_unlock_irqrestore(&ldata->read_lock, flags); - mutex_lock(&tty->echo_lock); - tty->echo_pos = tty->echo_cnt = tty->echo_overrun = 0; - mutex_unlock(&tty->echo_lock); + mutex_lock(&ldata->echo_lock); + ldata->echo_pos = ldata->echo_cnt = ldata->echo_overrun = 0; + mutex_unlock(&ldata->echo_lock); - tty->canon_head = tty->canon_data = tty->erasing = 0; - memset(&tty->read_flags, 0, sizeof tty->read_flags); + ldata->canon_head = ldata->canon_data = ldata->erasing = 0; + bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); n_tty_set_room(tty); } @@ -235,18 +272,19 @@ static void n_tty_flush_buffer(struct tty_struct *tty) static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; unsigned long flags; ssize_t n = 0; - spin_lock_irqsave(&tty->read_lock, flags); - if (!tty->icanon) { - n = tty->read_cnt; - } else if (tty->canon_data) { - n = (tty->canon_head > tty->read_tail) ? - tty->canon_head - tty->read_tail : - tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail); + spin_lock_irqsave(&ldata->read_lock, flags); + if (!ldata->icanon) { + n = ldata->read_cnt; + } else if (ldata->canon_data) { + n = (ldata->canon_head > ldata->read_tail) ? + ldata->canon_head - ldata->read_tail : + ldata->canon_head + (N_TTY_BUF_SIZE - ldata->read_tail); } - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); return n; } @@ -301,6 +339,7 @@ static inline int is_continuation(unsigned char c, struct tty_struct *tty) static int do_output_char(unsigned char c, struct tty_struct *tty, int space) { + struct n_tty_data *ldata = tty->disc_data; int spaces; if (!space) @@ -309,48 +348,48 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space) switch (c) { case '\n': if (O_ONLRET(tty)) - tty->column = 0; + ldata->column = 0; if (O_ONLCR(tty)) { if (space < 2) return -1; - tty->canon_column = tty->column = 0; + ldata->canon_column = ldata->column = 0; tty->ops->write(tty, "\r\n", 2); return 2; } - tty->canon_column = tty->column; + ldata->canon_column = ldata->column; break; case '\r': - if (O_ONOCR(tty) && tty->column == 0) + if (O_ONOCR(tty) && ldata->column == 0) return 0; if (O_OCRNL(tty)) { c = '\n'; if (O_ONLRET(tty)) - tty->canon_column = tty->column = 0; + ldata->canon_column = ldata->column = 0; break; } - tty->canon_column = tty->column = 0; + ldata->canon_column = ldata->column = 0; break; case '\t': - spaces = 8 - (tty->column & 7); + spaces = 8 - (ldata->column & 7); if (O_TABDLY(tty) == XTABS) { if (space < spaces) return -1; - tty->column += spaces; + ldata->column += spaces; tty->ops->write(tty, " ", spaces); return spaces; } - tty->column += spaces; + ldata->column += spaces; break; case '\b': - if (tty->column > 0) - tty->column--; + if (ldata->column > 0) + ldata->column--; break; default: if (!iscntrl(c)) { if (O_OLCUC(tty)) c = toupper(c); if (!is_continuation(c, tty)) - tty->column++; + ldata->column++; } break; } @@ -375,14 +414,15 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space) static int process_output(unsigned char c, struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; int space, retval; - mutex_lock(&tty->output_lock); + mutex_lock(&ldata->output_lock); space = tty_write_room(tty); retval = do_output_char(c, tty, space); - mutex_unlock(&tty->output_lock); + mutex_unlock(&ldata->output_lock); if (retval < 0) return -1; else @@ -411,15 +451,16 @@ static int process_output(unsigned char c, struct tty_struct *tty) static ssize_t process_output_block(struct tty_struct *tty, const unsigned char *buf, unsigned int nr) { + struct n_tty_data *ldata = tty->disc_data; int space; int i; const unsigned char *cp; - mutex_lock(&tty->output_lock); + mutex_lock(&ldata->output_lock); space = tty_write_room(tty); if (!space) { - mutex_unlock(&tty->output_lock); + mutex_unlock(&ldata->output_lock); return 0; } if (nr > space) @@ -431,30 +472,30 @@ static ssize_t process_output_block(struct tty_struct *tty, switch (c) { case '\n': if (O_ONLRET(tty)) - tty->column = 0; + ldata->column = 0; if (O_ONLCR(tty)) goto break_out; - tty->canon_column = tty->column; + ldata->canon_column = ldata->column; break; case '\r': - if (O_ONOCR(tty) && tty->column == 0) + if (O_ONOCR(tty) && ldata->column == 0) goto break_out; if (O_OCRNL(tty)) goto break_out; - tty->canon_column = tty->column = 0; + ldata->canon_column = ldata->column = 0; break; case '\t': goto break_out; case '\b': - if (tty->column > 0) - tty->column--; + if (ldata->column > 0) + ldata->column--; break; default: if (!iscntrl(c)) { if (O_OLCUC(tty)) goto break_out; if (!is_continuation(c, tty)) - tty->column++; + ldata->column++; } break; } @@ -462,7 +503,7 @@ static ssize_t process_output_block(struct tty_struct *tty, break_out: i = tty->ops->write(tty, buf, i); - mutex_unlock(&tty->output_lock); + mutex_unlock(&ldata->output_lock); return i; } @@ -494,21 +535,22 @@ break_out: static void process_echoes(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; int space, nr; unsigned char c; unsigned char *cp, *buf_end; - if (!tty->echo_cnt) + if (!ldata->echo_cnt) return; - mutex_lock(&tty->output_lock); - mutex_lock(&tty->echo_lock); + mutex_lock(&ldata->output_lock); + mutex_lock(&ldata->echo_lock); space = tty_write_room(tty); - buf_end = tty->echo_buf + N_TTY_BUF_SIZE; - cp = tty->echo_buf + tty->echo_pos; - nr = tty->echo_cnt; + buf_end = ldata->echo_buf + N_TTY_BUF_SIZE; + cp = ldata->echo_buf + ldata->echo_pos; + nr = ldata->echo_cnt; while (nr > 0) { c = *cp; if (c == ECHO_OP_START) { @@ -545,7 +587,7 @@ static void process_echoes(struct tty_struct *tty) * Otherwise, tab spacing is normal. */ if (!(num_chars & 0x80)) - num_chars += tty->canon_column; + num_chars += ldata->canon_column; num_bs = 8 - (num_chars & 7); if (num_bs > space) { @@ -555,22 +597,22 @@ static void process_echoes(struct tty_struct *tty) space -= num_bs; while (num_bs--) { tty_put_char(tty, '\b'); - if (tty->column > 0) - tty->column--; + if (ldata->column > 0) + ldata->column--; } cp += 3; nr -= 3; break; case ECHO_OP_SET_CANON_COL: - tty->canon_column = tty->column; + ldata->canon_column = ldata->column; cp += 2; nr -= 2; break; case ECHO_OP_MOVE_BACK_COL: - if (tty->column > 0) - tty->column--; + if (ldata->column > 0) + ldata->column--; cp += 2; nr -= 2; break; @@ -582,7 +624,7 @@ static void process_echoes(struct tty_struct *tty) break; } tty_put_char(tty, ECHO_OP_START); - tty->column++; + ldata->column++; space--; cp += 2; nr -= 2; @@ -604,7 +646,7 @@ static void process_echoes(struct tty_struct *tty) } tty_put_char(tty, '^'); tty_put_char(tty, op ^ 0100); - tty->column += 2; + ldata->column += 2; space -= 2; cp += 2; nr -= 2; @@ -635,20 +677,20 @@ static void process_echoes(struct tty_struct *tty) } if (nr == 0) { - tty->echo_pos = 0; - tty->echo_cnt = 0; - tty->echo_overrun = 0; + ldata->echo_pos = 0; + ldata->echo_cnt = 0; + ldata->echo_overrun = 0; } else { - int num_processed = tty->echo_cnt - nr; - tty->echo_pos += num_processed; - tty->echo_pos &= N_TTY_BUF_SIZE - 1; - tty->echo_cnt = nr; + int num_processed = ldata->echo_cnt - nr; + ldata->echo_pos += num_processed; + ldata->echo_pos &= N_TTY_BUF_SIZE - 1; + ldata->echo_cnt = nr; if (num_processed > 0) - tty->echo_overrun = 0; + ldata->echo_overrun = 0; } - mutex_unlock(&tty->echo_lock); - mutex_unlock(&tty->output_lock); + mutex_unlock(&ldata->echo_lock); + mutex_unlock(&ldata->output_lock); if (tty->ops->flush_chars) tty->ops->flush_chars(tty); @@ -657,72 +699,70 @@ static void process_echoes(struct tty_struct *tty) /** * add_echo_byte - add a byte to the echo buffer * @c: unicode byte to echo - * @tty: terminal device + * @ldata: n_tty data * * Add a character or operation byte to the echo buffer. * * Should be called under the echo lock to protect the echo buffer. */ -static void add_echo_byte(unsigned char c, struct tty_struct *tty) +static void add_echo_byte(unsigned char c, struct n_tty_data *ldata) { int new_byte_pos; - if (tty->echo_cnt == N_TTY_BUF_SIZE) { + if (ldata->echo_cnt == N_TTY_BUF_SIZE) { /* Circular buffer is already at capacity */ - new_byte_pos = tty->echo_pos; + new_byte_pos = ldata->echo_pos; /* * Since the buffer start position needs to be advanced, * be sure to step by a whole operation byte group. */ - if (tty->echo_buf[tty->echo_pos] == ECHO_OP_START) { - if (tty->echo_buf[(tty->echo_pos + 1) & + if (ldata->echo_buf[ldata->echo_pos] == ECHO_OP_START) { + if (ldata->echo_buf[(ldata->echo_pos + 1) & (N_TTY_BUF_SIZE - 1)] == ECHO_OP_ERASE_TAB) { - tty->echo_pos += 3; - tty->echo_cnt -= 2; + ldata->echo_pos += 3; + ldata->echo_cnt -= 2; } else { - tty->echo_pos += 2; - tty->echo_cnt -= 1; + ldata->echo_pos += 2; + ldata->echo_cnt -= 1; } } else { - tty->echo_pos++; + ldata->echo_pos++; } - tty->echo_pos &= N_TTY_BUF_SIZE - 1; + ldata->echo_pos &= N_TTY_BUF_SIZE - 1; - tty->echo_overrun = 1; + ldata->echo_overrun = 1; } else { - new_byte_pos = tty->echo_pos + tty->echo_cnt; + new_byte_pos = ldata->echo_pos + ldata->echo_cnt; new_byte_pos &= N_TTY_BUF_SIZE - 1; - tty->echo_cnt++; + ldata->echo_cnt++; } - tty->echo_buf[new_byte_pos] = c; + ldata->echo_buf[new_byte_pos] = c; } /** * echo_move_back_col - add operation to move back a column - * @tty: terminal device + * @ldata: n_tty data * * Add an operation to the echo buffer to move back one column. * * Locking: echo_lock to protect the echo buffer */ -static void echo_move_back_col(struct tty_struct *tty) +static void echo_move_back_col(struct n_tty_data *ldata) { - mutex_lock(&tty->echo_lock); - - add_echo_byte(ECHO_OP_START, tty); - add_echo_byte(ECHO_OP_MOVE_BACK_COL, tty); - - mutex_unlock(&tty->echo_lock); + mutex_lock(&ldata->echo_lock); + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_MOVE_BACK_COL, ldata); + mutex_unlock(&ldata->echo_lock); } /** * echo_set_canon_col - add operation to set the canon column - * @tty: terminal device + * @ldata: n_tty data * * Add an operation to the echo buffer to set the canon column * to the current column. @@ -730,21 +770,19 @@ static void echo_move_back_col(struct tty_struct *tty) * Locking: echo_lock to protect the echo buffer */ -static void echo_set_canon_col(struct tty_struct *tty) +static void echo_set_canon_col(struct n_tty_data *ldata) { - mutex_lock(&tty->echo_lock); - - add_echo_byte(ECHO_OP_START, tty); - add_echo_byte(ECHO_OP_SET_CANON_COL, tty); - - mutex_unlock(&tty->echo_lock); + mutex_lock(&ldata->echo_lock); + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_SET_CANON_COL, ldata); + mutex_unlock(&ldata->echo_lock); } /** * echo_erase_tab - add operation to erase a tab * @num_chars: number of character columns already used * @after_tab: true if num_chars starts after a previous tab - * @tty: terminal device + * @ldata: n_tty data * * Add an operation to the echo buffer to erase a tab. * @@ -758,12 +796,12 @@ static void echo_set_canon_col(struct tty_struct *tty) */ static void echo_erase_tab(unsigned int num_chars, int after_tab, - struct tty_struct *tty) + struct n_tty_data *ldata) { - mutex_lock(&tty->echo_lock); + mutex_lock(&ldata->echo_lock); - add_echo_byte(ECHO_OP_START, tty); - add_echo_byte(ECHO_OP_ERASE_TAB, tty); + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_ERASE_TAB, ldata); /* We only need to know this modulo 8 (tab spacing) */ num_chars &= 7; @@ -772,9 +810,9 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab, if (after_tab) num_chars |= 0x80; - add_echo_byte(num_chars, tty); + add_echo_byte(num_chars, ldata); - mutex_unlock(&tty->echo_lock); + mutex_unlock(&ldata->echo_lock); } /** @@ -790,18 +828,16 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab, * Locking: echo_lock to protect the echo buffer */ -static void echo_char_raw(unsigned char c, struct tty_struct *tty) +static void echo_char_raw(unsigned char c, struct n_tty_data *ldata) { - mutex_lock(&tty->echo_lock); - + mutex_lock(&ldata->echo_lock); if (c == ECHO_OP_START) { - add_echo_byte(ECHO_OP_START, tty); - add_echo_byte(ECHO_OP_START, tty); + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_START, ldata); } else { - add_echo_byte(c, tty); + add_echo_byte(c, ldata); } - - mutex_unlock(&tty->echo_lock); + mutex_unlock(&ldata->echo_lock); } /** @@ -820,30 +856,32 @@ static void echo_char_raw(unsigned char c, struct tty_struct *tty) static void echo_char(unsigned char c, struct tty_struct *tty) { - mutex_lock(&tty->echo_lock); + struct n_tty_data *ldata = tty->disc_data; + + mutex_lock(&ldata->echo_lock); if (c == ECHO_OP_START) { - add_echo_byte(ECHO_OP_START, tty); - add_echo_byte(ECHO_OP_START, tty); + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_START, ldata); } else { if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') - add_echo_byte(ECHO_OP_START, tty); - add_echo_byte(c, tty); + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(c, ldata); } - mutex_unlock(&tty->echo_lock); + mutex_unlock(&ldata->echo_lock); } /** * finish_erasing - complete erase - * @tty: tty doing the erase + * @ldata: n_tty data */ -static inline void finish_erasing(struct tty_struct *tty) +static inline void finish_erasing(struct n_tty_data *ldata) { - if (tty->erasing) { - echo_char_raw('/', tty); - tty->erasing = 0; + if (ldata->erasing) { + echo_char_raw('/', ldata); + ldata->erasing = 0; } } @@ -861,12 +899,13 @@ static inline void finish_erasing(struct tty_struct *tty) static void eraser(unsigned char c, struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; enum { ERASE, WERASE, KILL } kill_type; int head, seen_alnums, cnt; unsigned long flags; /* FIXME: locking needed ? */ - if (tty->read_head == tty->canon_head) { + if (ldata->read_head == ldata->canon_head) { /* process_output('\a', tty); */ /* what do you think? */ return; } @@ -876,24 +915,24 @@ static void eraser(unsigned char c, struct tty_struct *tty) kill_type = WERASE; else { if (!L_ECHO(tty)) { - spin_lock_irqsave(&tty->read_lock, flags); - tty->read_cnt -= ((tty->read_head - tty->canon_head) & + spin_lock_irqsave(&ldata->read_lock, flags); + ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) & (N_TTY_BUF_SIZE - 1)); - tty->read_head = tty->canon_head; - spin_unlock_irqrestore(&tty->read_lock, flags); + ldata->read_head = ldata->canon_head; + spin_unlock_irqrestore(&ldata->read_lock, flags); return; } if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) { - spin_lock_irqsave(&tty->read_lock, flags); - tty->read_cnt -= ((tty->read_head - tty->canon_head) & + spin_lock_irqsave(&ldata->read_lock, flags); + ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) & (N_TTY_BUF_SIZE - 1)); - tty->read_head = tty->canon_head; - spin_unlock_irqrestore(&tty->read_lock, flags); - finish_erasing(tty); + ldata->read_head = ldata->canon_head; + spin_unlock_irqrestore(&ldata->read_lock, flags); + finish_erasing(ldata); echo_char(KILL_CHAR(tty), tty); /* Add a newline if ECHOK is on and ECHOKE is off. */ if (L_ECHOK(tty)) - echo_char_raw('\n', tty); + echo_char_raw('\n', ldata); return; } kill_type = KILL; @@ -901,14 +940,14 @@ static void eraser(unsigned char c, struct tty_struct *tty) seen_alnums = 0; /* FIXME: Locking ?? */ - while (tty->read_head != tty->canon_head) { - head = tty->read_head; + while (ldata->read_head != ldata->canon_head) { + head = ldata->read_head; /* erase a single possibly multibyte character */ do { head = (head - 1) & (N_TTY_BUF_SIZE-1); - c = tty->read_buf[head]; - } while (is_continuation(c, tty) && head != tty->canon_head); + c = ldata->read_buf[head]; + } while (is_continuation(c, tty) && head != ldata->canon_head); /* do not partially erase */ if (is_continuation(c, tty)) @@ -921,30 +960,31 @@ static void eraser(unsigned char c, struct tty_struct *tty) else if (seen_alnums) break; } - cnt = (tty->read_head - head) & (N_TTY_BUF_SIZE-1); - spin_lock_irqsave(&tty->read_lock, flags); - tty->read_head = head; - tty->read_cnt -= cnt; - spin_unlock_irqrestore(&tty->read_lock, flags); + cnt = (ldata->read_head - head) & (N_TTY_BUF_SIZE-1); + spin_lock_irqsave(&ldata->read_lock, flags); + ldata->read_head = head; + ldata->read_cnt -= cnt; + spin_unlock_irqrestore(&ldata->read_lock, flags); if (L_ECHO(tty)) { if (L_ECHOPRT(tty)) { - if (!tty->erasing) { - echo_char_raw('\\', tty); - tty->erasing = 1; + if (!ldata->erasing) { + echo_char_raw('\\', ldata); + ldata->erasing = 1; } /* if cnt > 1, output a multi-byte character */ echo_char(c, tty); while (--cnt > 0) { head = (head+1) & (N_TTY_BUF_SIZE-1); - echo_char_raw(tty->read_buf[head], tty); - echo_move_back_col(tty); + echo_char_raw(ldata->read_buf[head], + ldata); + echo_move_back_col(ldata); } } else if (kill_type == ERASE && !L_ECHOE(tty)) { echo_char(ERASE_CHAR(tty), tty); } else if (c == '\t') { unsigned int num_chars = 0; int after_tab = 0; - unsigned long tail = tty->read_head; + unsigned long tail = ldata->read_head; /* * Count the columns used for characters @@ -953,9 +993,9 @@ static void eraser(unsigned char c, struct tty_struct *tty) * This info is used to go back the correct * number of columns. */ - while (tail != tty->canon_head) { + while (tail != ldata->canon_head) { tail = (tail-1) & (N_TTY_BUF_SIZE-1); - c = tty->read_buf[tail]; + c = ldata->read_buf[tail]; if (c == '\t') { after_tab = 1; break; @@ -966,25 +1006,25 @@ static void eraser(unsigned char c, struct tty_struct *tty) num_chars++; } } - echo_erase_tab(num_chars, after_tab, tty); + echo_erase_tab(num_chars, after_tab, ldata); } else { if (iscntrl(c) && L_ECHOCTL(tty)) { - echo_char_raw('\b', tty); - echo_char_raw(' ', tty); - echo_char_raw('\b', tty); + echo_char_raw('\b', ldata); + echo_char_raw(' ', ldata); + echo_char_raw('\b', ldata); } if (!iscntrl(c) || L_ECHOCTL(tty)) { - echo_char_raw('\b', tty); - echo_char_raw(' ', tty); - echo_char_raw('\b', tty); + echo_char_raw('\b', ldata); + echo_char_raw(' ', ldata); + echo_char_raw('\b', ldata); } } } if (kill_type == ERASE) break; } - if (tty->read_head == tty->canon_head && L_ECHO(tty)) - finish_erasing(tty); + if (ldata->read_head == ldata->canon_head && L_ECHO(tty)) + finish_erasing(ldata); } /** @@ -1023,6 +1063,8 @@ static inline void isig(int sig, struct tty_struct *tty, int flush) static inline void n_tty_receive_break(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; + if (I_IGNBRK(tty)) return; if (I_BRKINT(tty)) { @@ -1030,10 +1072,10 @@ static inline void n_tty_receive_break(struct tty_struct *tty) return; } if (I_PARMRK(tty)) { - put_tty_queue('\377', tty); - put_tty_queue('\0', tty); + put_tty_queue('\377', ldata); + put_tty_queue('\0', ldata); } - put_tty_queue('\0', tty); + put_tty_queue('\0', ldata); wake_up_interruptible(&tty->read_wait); } @@ -1052,16 +1094,17 @@ static inline void n_tty_receive_break(struct tty_struct *tty) static inline void n_tty_receive_overrun(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; char buf[64]; - tty->num_overrun++; - if (time_before(tty->overrun_time, jiffies - HZ) || - time_after(tty->overrun_time, jiffies)) { + ldata->num_overrun++; + if (time_after(jiffies, ldata->overrun_time + HZ) || + time_after(ldata->overrun_time, jiffies)) { printk(KERN_WARNING "%s: %d input overrun(s)\n", tty_name(tty, buf), - tty->num_overrun); - tty->overrun_time = jiffies; - tty->num_overrun = 0; + ldata->num_overrun); + ldata->overrun_time = jiffies; + ldata->num_overrun = 0; } } @@ -1076,16 +1119,18 @@ static inline void n_tty_receive_overrun(struct tty_struct *tty) static inline void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c) { + struct n_tty_data *ldata = tty->disc_data; + if (I_IGNPAR(tty)) return; if (I_PARMRK(tty)) { - put_tty_queue('\377', tty); - put_tty_queue('\0', tty); - put_tty_queue(c, tty); + put_tty_queue('\377', ldata); + put_tty_queue('\0', ldata); + put_tty_queue(c, ldata); } else if (I_INPCK(tty)) - put_tty_queue('\0', tty); + put_tty_queue('\0', ldata); else - put_tty_queue(c, tty); + put_tty_queue(c, ldata); wake_up_interruptible(&tty->read_wait); } @@ -1101,11 +1146,12 @@ static inline void n_tty_receive_parity_error(struct tty_struct *tty, static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) { + struct n_tty_data *ldata = tty->disc_data; unsigned long flags; int parmrk; - if (tty->raw) { - put_tty_queue(c, tty); + if (ldata->raw) { + put_tty_queue(c, ldata); return; } @@ -1115,7 +1161,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) c = tolower(c); if (L_EXTPROC(tty)) { - put_tty_queue(c, tty); + put_tty_queue(c, ldata); return; } @@ -1143,26 +1189,26 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) * handle specially, do shortcut processing to speed things * up. */ - if (!test_bit(c, tty->process_char_map) || tty->lnext) { - tty->lnext = 0; + if (!test_bit(c, ldata->process_char_map) || ldata->lnext) { + ldata->lnext = 0; parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; - if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { + if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { /* beep if no space */ if (L_ECHO(tty)) process_output('\a', tty); return; } if (L_ECHO(tty)) { - finish_erasing(tty); + finish_erasing(ldata); /* Record the column of first canon char. */ - if (tty->canon_head == tty->read_head) - echo_set_canon_col(tty); + if (ldata->canon_head == ldata->read_head) + echo_set_canon_col(ldata); echo_char(c, tty); process_echoes(tty); } if (parmrk) - put_tty_queue(c, tty); - put_tty_queue(c, tty); + put_tty_queue(c, ldata); + put_tty_queue(c, ldata); return; } @@ -1218,7 +1264,7 @@ send_signal: } else if (c == '\n' && I_INLCR(tty)) c = '\r'; - if (tty->icanon) { + if (ldata->icanon) { if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) { eraser(c, tty); @@ -1226,12 +1272,12 @@ send_signal: return; } if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) { - tty->lnext = 1; + ldata->lnext = 1; if (L_ECHO(tty)) { - finish_erasing(tty); + finish_erasing(ldata); if (L_ECHOCTL(tty)) { - echo_char_raw('^', tty); - echo_char_raw('\b', tty); + echo_char_raw('^', ldata); + echo_char_raw('\b', ldata); process_echoes(tty); } } @@ -1239,34 +1285,34 @@ send_signal: } if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) { - unsigned long tail = tty->canon_head; + unsigned long tail = ldata->canon_head; - finish_erasing(tty); + finish_erasing(ldata); echo_char(c, tty); - echo_char_raw('\n', tty); - while (tail != tty->read_head) { - echo_char(tty->read_buf[tail], tty); + echo_char_raw('\n', ldata); + while (tail != ldata->read_head) { + echo_char(ldata->read_buf[tail], tty); tail = (tail+1) & (N_TTY_BUF_SIZE-1); } process_echoes(tty); return; } if (c == '\n') { - if (tty->read_cnt >= N_TTY_BUF_SIZE) { + if (ldata->read_cnt >= N_TTY_BUF_SIZE) { if (L_ECHO(tty)) process_output('\a', tty); return; } if (L_ECHO(tty) || L_ECHONL(tty)) { - echo_char_raw('\n', tty); + echo_char_raw('\n', ldata); process_echoes(tty); } goto handle_newline; } if (c == EOF_CHAR(tty)) { - if (tty->read_cnt >= N_TTY_BUF_SIZE) + if (ldata->read_cnt >= N_TTY_BUF_SIZE) return; - if (tty->canon_head != tty->read_head) + if (ldata->canon_head != ldata->read_head) set_bit(TTY_PUSH, &tty->flags); c = __DISABLED_CHAR; goto handle_newline; @@ -1275,7 +1321,7 @@ send_signal: (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) { parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; - if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk)) { + if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk)) { if (L_ECHO(tty)) process_output('\a', tty); return; @@ -1285,8 +1331,8 @@ send_signal: */ if (L_ECHO(tty)) { /* Record the column of first canon char. */ - if (tty->canon_head == tty->read_head) - echo_set_canon_col(tty); + if (ldata->canon_head == ldata->read_head) + echo_set_canon_col(ldata); echo_char(c, tty); process_echoes(tty); } @@ -1295,15 +1341,15 @@ send_signal: * EOL_CHAR and EOL2_CHAR? */ if (parmrk) - put_tty_queue(c, tty); + put_tty_queue(c, ldata); handle_newline: - spin_lock_irqsave(&tty->read_lock, flags); - set_bit(tty->read_head, tty->read_flags); - put_tty_queue_nolock(c, tty); - tty->canon_head = tty->read_head; - tty->canon_data++; - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); + set_bit(ldata->read_head, ldata->read_flags); + put_tty_queue_nolock(c, ldata); + ldata->canon_head = ldata->read_head; + ldata->canon_data++; + spin_unlock_irqrestore(&ldata->read_lock, flags); kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) wake_up_interruptible(&tty->read_wait); @@ -1312,29 +1358,29 @@ handle_newline: } parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; - if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { + if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { /* beep if no space */ if (L_ECHO(tty)) process_output('\a', tty); return; } if (L_ECHO(tty)) { - finish_erasing(tty); + finish_erasing(ldata); if (c == '\n') - echo_char_raw('\n', tty); + echo_char_raw('\n', ldata); else { /* Record the column of first canon char. */ - if (tty->canon_head == tty->read_head) - echo_set_canon_col(tty); + if (ldata->canon_head == ldata->read_head) + echo_set_canon_col(ldata); echo_char(c, tty); } process_echoes(tty); } if (parmrk) - put_tty_queue(c, tty); + put_tty_queue(c, ldata); - put_tty_queue(c, tty); + put_tty_queue(c, ldata); } @@ -1369,33 +1415,31 @@ static void n_tty_write_wakeup(struct tty_struct *tty) static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { + struct n_tty_data *ldata = tty->disc_data; const unsigned char *p; char *f, flags = TTY_NORMAL; int i; char buf[64]; unsigned long cpuflags; - if (!tty->read_buf) - return; - - if (tty->real_raw) { - spin_lock_irqsave(&tty->read_lock, cpuflags); - i = min(N_TTY_BUF_SIZE - tty->read_cnt, - N_TTY_BUF_SIZE - tty->read_head); + if (ldata->real_raw) { + spin_lock_irqsave(&ldata->read_lock, cpuflags); + i = min(N_TTY_BUF_SIZE - ldata->read_cnt, + N_TTY_BUF_SIZE - ldata->read_head); i = min(count, i); - memcpy(tty->read_buf + tty->read_head, cp, i); - tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); - tty->read_cnt += i; + memcpy(ldata->read_buf + ldata->read_head, cp, i); + ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1); + ldata->read_cnt += i; cp += i; count -= i; - i = min(N_TTY_BUF_SIZE - tty->read_cnt, - N_TTY_BUF_SIZE - tty->read_head); + i = min(N_TTY_BUF_SIZE - ldata->read_cnt, + N_TTY_BUF_SIZE - ldata->read_head); i = min(count, i); - memcpy(tty->read_buf + tty->read_head, cp, i); - tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); - tty->read_cnt += i; - spin_unlock_irqrestore(&tty->read_lock, cpuflags); + memcpy(ldata->read_buf + ldata->read_head, cp, i); + ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1); + ldata->read_cnt += i; + spin_unlock_irqrestore(&ldata->read_lock, cpuflags); } else { for (i = count, p = cp, f = fp; i; i--, p++) { if (f) @@ -1426,7 +1470,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, n_tty_set_room(tty); - if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) || + if ((!ldata->icanon && (ldata->read_cnt >= tty->minimum_to_wake)) || L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) @@ -1470,25 +1514,25 @@ int is_ignored(int sig) static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) { + struct n_tty_data *ldata = tty->disc_data; int canon_change = 1; - BUG_ON(!tty); if (old) canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON; if (canon_change) { - memset(&tty->read_flags, 0, sizeof tty->read_flags); - tty->canon_head = tty->read_tail; - tty->canon_data = 0; - tty->erasing = 0; + bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); + ldata->canon_head = ldata->read_tail; + ldata->canon_data = 0; + ldata->erasing = 0; } - if (canon_change && !L_ICANON(tty) && tty->read_cnt) + if (canon_change && !L_ICANON(tty) && ldata->read_cnt) wake_up_interruptible(&tty->read_wait); - tty->icanon = (L_ICANON(tty) != 0); + ldata->icanon = (L_ICANON(tty) != 0); if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { - tty->raw = 1; - tty->real_raw = 1; + ldata->raw = 1; + ldata->real_raw = 1; n_tty_set_room(tty); return; } @@ -1496,51 +1540,51 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) || I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) || I_PARMRK(tty)) { - memset(tty->process_char_map, 0, 256/8); + bitmap_zero(ldata->process_char_map, 256); if (I_IGNCR(tty) || I_ICRNL(tty)) - set_bit('\r', tty->process_char_map); + set_bit('\r', ldata->process_char_map); if (I_INLCR(tty)) - set_bit('\n', tty->process_char_map); + set_bit('\n', ldata->process_char_map); if (L_ICANON(tty)) { - set_bit(ERASE_CHAR(tty), tty->process_char_map); - set_bit(KILL_CHAR(tty), tty->process_char_map); - set_bit(EOF_CHAR(tty), tty->process_char_map); - set_bit('\n', tty->process_char_map); - set_bit(EOL_CHAR(tty), tty->process_char_map); + set_bit(ERASE_CHAR(tty), ldata->process_char_map); + set_bit(KILL_CHAR(tty), ldata->process_char_map); + set_bit(EOF_CHAR(tty), ldata->process_char_map); + set_bit('\n', ldata->process_char_map); + set_bit(EOL_CHAR(tty), ldata->process_char_map); if (L_IEXTEN(tty)) { set_bit(WERASE_CHAR(tty), - tty->process_char_map); + ldata->process_char_map); set_bit(LNEXT_CHAR(tty), - tty->process_char_map); + ldata->process_char_map); set_bit(EOL2_CHAR(tty), - tty->process_char_map); + ldata->process_char_map); if (L_ECHO(tty)) set_bit(REPRINT_CHAR(tty), - tty->process_char_map); + ldata->process_char_map); } } if (I_IXON(tty)) { - set_bit(START_CHAR(tty), tty->process_char_map); - set_bit(STOP_CHAR(tty), tty->process_char_map); + set_bit(START_CHAR(tty), ldata->process_char_map); + set_bit(STOP_CHAR(tty), ldata->process_char_map); } if (L_ISIG(tty)) { - set_bit(INTR_CHAR(tty), tty->process_char_map); - set_bit(QUIT_CHAR(tty), tty->process_char_map); - set_bit(SUSP_CHAR(tty), tty->process_char_map); + set_bit(INTR_CHAR(tty), ldata->process_char_map); + set_bit(QUIT_CHAR(tty), ldata->process_char_map); + set_bit(SUSP_CHAR(tty), ldata->process_char_map); } - clear_bit(__DISABLED_CHAR, tty->process_char_map); - tty->raw = 0; - tty->real_raw = 0; + clear_bit(__DISABLED_CHAR, ldata->process_char_map); + ldata->raw = 0; + ldata->real_raw = 0; } else { - tty->raw = 1; + ldata->raw = 1; if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) && (I_IGNPAR(tty) || !I_INPCK(tty)) && (tty->driver->flags & TTY_DRIVER_REAL_RAW)) - tty->real_raw = 1; + ldata->real_raw = 1; else - tty->real_raw = 0; + ldata->real_raw = 0; } n_tty_set_room(tty); /* The termios change make the tty ready for I/O */ @@ -1560,15 +1604,13 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) static void n_tty_close(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; + n_tty_flush_buffer(tty); - if (tty->read_buf) { - kfree(tty->read_buf); - tty->read_buf = NULL; - } - if (tty->echo_buf) { - kfree(tty->echo_buf); - tty->echo_buf = NULL; - } + kfree(ldata->read_buf); + kfree(ldata->echo_buf); + kfree(ldata); + tty->disc_data = NULL; } /** @@ -1583,37 +1625,50 @@ static void n_tty_close(struct tty_struct *tty) static int n_tty_open(struct tty_struct *tty) { - if (!tty) - return -EINVAL; + struct n_tty_data *ldata; + + ldata = kzalloc(sizeof(*ldata), GFP_KERNEL); + if (!ldata) + goto err; + + ldata->overrun_time = jiffies; + mutex_init(&ldata->atomic_read_lock); + mutex_init(&ldata->output_lock); + mutex_init(&ldata->echo_lock); + spin_lock_init(&ldata->read_lock); /* These are ugly. Currently a malloc failure here can panic */ - if (!tty->read_buf) { - tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); - if (!tty->read_buf) - return -ENOMEM; - } - if (!tty->echo_buf) { - tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); + ldata->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); + ldata->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); + if (!ldata->read_buf || !ldata->echo_buf) + goto err_free_bufs; - if (!tty->echo_buf) - return -ENOMEM; - } + tty->disc_data = ldata; reset_buffer_flags(tty); tty_unthrottle(tty); - tty->column = 0; + ldata->column = 0; n_tty_set_termios(tty, NULL); tty->minimum_to_wake = 1; tty->closing = 0; + return 0; +err_free_bufs: + kfree(ldata->read_buf); + kfree(ldata->echo_buf); + kfree(ldata); +err: + return -ENOMEM; } static inline int input_available_p(struct tty_struct *tty, int amt) { + struct n_tty_data *ldata = tty->disc_data; + tty_flush_to_ldisc(tty); - if (tty->icanon && !L_EXTPROC(tty)) { - if (tty->canon_data) + if (ldata->icanon && !L_EXTPROC(tty)) { + if (ldata->canon_data) return 1; - } else if (tty->read_cnt >= (amt ? amt : 1)) + } else if (ldata->read_cnt >= (amt ? amt : 1)) return 1; return 0; @@ -1632,7 +1687,7 @@ static inline int input_available_p(struct tty_struct *tty, int amt) * buffer, and once to drain the space from the (physical) beginning of * the buffer to head pointer. * - * Called under the tty->atomic_read_lock sem + * Called under the ldata->atomic_read_lock sem * */ @@ -1641,29 +1696,31 @@ static int copy_from_read_buf(struct tty_struct *tty, size_t *nr) { + struct n_tty_data *ldata = tty->disc_data; int retval; size_t n; unsigned long flags; bool is_eof; retval = 0; - spin_lock_irqsave(&tty->read_lock, flags); - n = min(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail); + spin_lock_irqsave(&ldata->read_lock, flags); + n = min(ldata->read_cnt, N_TTY_BUF_SIZE - ldata->read_tail); n = min(*nr, n); - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); if (n) { - retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n); + retval = copy_to_user(*b, &ldata->read_buf[ldata->read_tail], n); n -= retval; is_eof = n == 1 && - tty->read_buf[tty->read_tail] == EOF_CHAR(tty); - tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n); - spin_lock_irqsave(&tty->read_lock, flags); - tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1); - tty->read_cnt -= n; + ldata->read_buf[ldata->read_tail] == EOF_CHAR(tty); + tty_audit_add_data(tty, &ldata->read_buf[ldata->read_tail], n, + ldata->icanon); + spin_lock_irqsave(&ldata->read_lock, flags); + ldata->read_tail = (ldata->read_tail + n) & (N_TTY_BUF_SIZE-1); + ldata->read_cnt -= n; /* Turn single EOF into zero-length read */ - if (L_EXTPROC(tty) && tty->icanon && is_eof && !tty->read_cnt) + if (L_EXTPROC(tty) && ldata->icanon && is_eof && !ldata->read_cnt) n = 0; - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); *b += n; *nr -= n; } @@ -1730,6 +1787,7 @@ static int job_control(struct tty_struct *tty, struct file *file) static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, unsigned char __user *buf, size_t nr) { + struct n_tty_data *ldata = tty->disc_data; unsigned char __user *b = buf; DECLARE_WAITQUEUE(wait, current); int c; @@ -1741,17 +1799,13 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, int packet; do_it_again: - - if (WARN_ON(!tty->read_buf)) - return -EAGAIN; - c = job_control(tty, file); if (c < 0) return c; minimum = time = 0; timeout = MAX_SCHEDULE_TIMEOUT; - if (!tty->icanon) { + if (!ldata->icanon) { time = (HZ / 10) * TIME_CHAR(tty); minimum = MIN_CHAR(tty); if (minimum) { @@ -1774,10 +1828,10 @@ do_it_again: * Internal serialization of reads. */ if (file->f_flags & O_NONBLOCK) { - if (!mutex_trylock(&tty->atomic_read_lock)) + if (!mutex_trylock(&ldata->atomic_read_lock)) return -EAGAIN; } else { - if (mutex_lock_interruptible(&tty->atomic_read_lock)) + if (mutex_lock_interruptible(&ldata->atomic_read_lock)) return -ERESTARTSYS; } packet = tty->packet; @@ -1830,7 +1884,6 @@ do_it_again: /* FIXME: does n_tty_set_room need locking ? */ n_tty_set_room(tty); timeout = schedule_timeout(timeout); - BUG_ON(!tty->read_buf); continue; } __set_current_state(TASK_RUNNING); @@ -1845,45 +1898,45 @@ do_it_again: nr--; } - if (tty->icanon && !L_EXTPROC(tty)) { + if (ldata->icanon && !L_EXTPROC(tty)) { /* N.B. avoid overrun if nr == 0 */ - spin_lock_irqsave(&tty->read_lock, flags); - while (nr && tty->read_cnt) { + spin_lock_irqsave(&ldata->read_lock, flags); + while (nr && ldata->read_cnt) { int eol; - eol = test_and_clear_bit(tty->read_tail, - tty->read_flags); - c = tty->read_buf[tty->read_tail]; - tty->read_tail = ((tty->read_tail+1) & + eol = test_and_clear_bit(ldata->read_tail, + ldata->read_flags); + c = ldata->read_buf[ldata->read_tail]; + ldata->read_tail = ((ldata->read_tail+1) & (N_TTY_BUF_SIZE-1)); - tty->read_cnt--; + ldata->read_cnt--; if (eol) { /* this test should be redundant: * we shouldn't be reading data if * canon_data is 0 */ - if (--tty->canon_data < 0) - tty->canon_data = 0; + if (--ldata->canon_data < 0) + ldata->canon_data = 0; } - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); if (!eol || (c != __DISABLED_CHAR)) { if (tty_put_user(tty, c, b++)) { retval = -EFAULT; b--; - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); break; } nr--; } if (eol) { tty_audit_push(tty); - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); break; } - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); } - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); if (retval) break; } else { @@ -1915,7 +1968,7 @@ do_it_again: if (time) timeout = time; } - mutex_unlock(&tty->atomic_read_lock); + mutex_unlock(&ldata->atomic_read_lock); remove_wait_queue(&tty->read_wait, &wait); if (!waitqueue_active(&tty->read_wait)) @@ -2076,19 +2129,19 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, return mask; } -static unsigned long inq_canon(struct tty_struct *tty) +static unsigned long inq_canon(struct n_tty_data *ldata) { int nr, head, tail; - if (!tty->canon_data) + if (!ldata->canon_data) return 0; - head = tty->canon_head; - tail = tty->read_tail; + head = ldata->canon_head; + tail = ldata->read_tail; nr = (head - tail) & (N_TTY_BUF_SIZE-1); /* Skip EOF-chars.. */ while (head != tail) { - if (test_bit(tail, tty->read_flags) && - tty->read_buf[tail] == __DISABLED_CHAR) + if (test_bit(tail, ldata->read_flags) && + ldata->read_buf[tail] == __DISABLED_CHAR) nr--; tail = (tail+1) & (N_TTY_BUF_SIZE-1); } @@ -2098,6 +2151,7 @@ static unsigned long inq_canon(struct tty_struct *tty) static int n_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct n_tty_data *ldata = tty->disc_data; int retval; switch (cmd) { @@ -2105,9 +2159,9 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file, return put_user(tty_chars_in_buffer(tty), (int __user *) arg); case TIOCINQ: /* FIXME: Locking */ - retval = tty->read_cnt; + retval = ldata->read_cnt; if (L_ICANON(tty)) - retval = inq_canon(tty); + retval = inq_canon(ldata); return put_user(retval, (unsigned int __user *) arg); default: return n_tty_ioctl_helper(tty, file, cmd, arg); diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index b917c94..cb764d2 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -1479,6 +1479,7 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev, if (IS_ERR(tty_dev)) { ret = PTR_ERR(tty_dev); dev_err(&pdev->dev, "Could not allocate tty?\n"); + tty_port_destroy(&port->port); goto err_free_tty; } } @@ -1486,8 +1487,10 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev, return 0; err_free_tty: - for (i = dc->index_start; i < dc->index_start + MAX_PORT; ++i) - tty_unregister_device(ntty_driver, i); + for (i = 0; i < MAX_PORT; ++i) { + tty_unregister_device(ntty_driver, dc->index_start + i); + tty_port_destroy(&dc->port[i].port); + } err_free_kfifo: for (i = 0; i < MAX_PORT; i++) kfifo_free(&dc->port[i].fifo_ul); @@ -1520,8 +1523,10 @@ static void __devexit tty_exit(struct nozomi *dc) complete off a hangup method ? */ while (dc->open_ttys) msleep(1); - for (i = dc->index_start; i < dc->index_start + MAX_PORT; ++i) - tty_unregister_device(ntty_driver, i); + for (i = 0; i < MAX_PORT; ++i) { + tty_unregister_device(ntty_driver, dc->index_start + i); + tty_port_destroy(&dc->port[i].port); + } } /* Deallocate memory for one device */ diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index a82b399..a541ec8 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -4,9 +4,6 @@ * Added support for a Unix98-style ptmx device. * -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998 * - * When reading this code see also fs/devpts. In particular note that the - * driver_data field is used by the devpts side as a binding to the devpts - * inode. */ #include <linux/module.h> @@ -59,7 +56,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp) #ifdef CONFIG_UNIX98_PTYS if (tty->driver == ptm_driver) { mutex_lock(&devpts_mutex); - devpts_pty_kill(tty->link); + devpts_pty_kill(tty->link->driver_data); mutex_unlock(&devpts_mutex); } #endif @@ -96,7 +93,7 @@ static void pty_unthrottle(struct tty_struct *tty) static int pty_space(struct tty_struct *to) { - int n = 8192 - to->buf.memory_used; + int n = 8192 - to->port->buf.memory_used; if (n < 0) return 0; return n; @@ -174,6 +171,41 @@ static int pty_set_lock(struct tty_struct *tty, int __user *arg) return 0; } +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); +} + +/* Set the packet mode on a pty */ +static int pty_set_pktmode(struct tty_struct *tty, int __user *arg) +{ + unsigned long flags; + int pktmode; + + if (get_user(pktmode, arg)) + return -EFAULT; + + spin_lock_irqsave(&tty->ctrl_lock, flags); + if (pktmode) { + if (!tty->packet) { + tty->packet = 1; + tty->link->ctrl_status = 0; + } + } else + tty->packet = 0; + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + + return 0; +} + +/* Get the packet mode of a pty */ +static int pty_get_pktmode(struct tty_struct *tty, int __user *arg) +{ + int pktmode = tty->packet; + return put_user(pktmode, arg); +} + /* Send a signal to the slave */ static int pty_signal(struct tty_struct *tty, int sig) { @@ -348,6 +380,7 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty, tty_port_init(ports[1]); o_tty->port = ports[0]; tty->port = ports[1]; + o_tty->port->itty = o_tty; tty_driver_kref_get(driver); tty->count++; @@ -366,9 +399,16 @@ err: return retval; } +/* this is called once with whichever end is closed last */ +static void pty_unix98_shutdown(struct tty_struct *tty) +{ + devpts_kill_index(tty->driver_data, tty->index); +} + static void pty_cleanup(struct tty_struct *tty) { - kfree(tty->port); + tty->port->itty = NULL; + tty_port_put(tty->port); } /* Traditional BSD devices */ @@ -393,6 +433,12 @@ static int pty_bsd_ioctl(struct tty_struct *tty, switch (cmd) { case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */ return pty_set_lock(tty, (int __user *) arg); + case TIOCGPTLCK: /* Get PT Lock status */ + return pty_get_lock(tty, (int __user *)arg); + case TIOCPKT: /* Set PT packet mode */ + return pty_set_pktmode(tty, (int __user *)arg); + case TIOCGPKT: /* Get PT packet mode */ + return pty_get_pktmode(tty, (int __user *)arg); case TIOCSIG: /* Send signal to other side of pty */ return pty_signal(tty, (int) arg); } @@ -507,6 +553,12 @@ static int pty_unix98_ioctl(struct tty_struct *tty, switch (cmd) { case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */ return pty_set_lock(tty, (int __user *)arg); + case TIOCGPTLCK: /* Get PT Lock status */ + return pty_get_lock(tty, (int __user *)arg); + case TIOCPKT: /* Set PT packet mode */ + return pty_set_pktmode(tty, (int __user *)arg); + case TIOCGPKT: /* Get PT packet mode */ + return pty_get_pktmode(tty, (int __user *)arg); case TIOCGPTN: /* Get PT Number */ return put_user(tty->index, (unsigned int __user *)arg); case TIOCSIG: /* Send signal to other side of pty */ @@ -547,7 +599,7 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver, struct tty_struct *tty; mutex_lock(&devpts_mutex); - tty = devpts_get_tty(pts_inode, idx); + tty = devpts_get_priv(pts_inode); mutex_unlock(&devpts_mutex); /* Master must be open before slave */ if (!tty) @@ -581,6 +633,7 @@ static const struct tty_operations ptm_unix98_ops = { .set_termios = pty_set_termios, .ioctl = pty_unix98_ioctl, .resize = pty_resize, + .shutdown = pty_unix98_shutdown, .cleanup = pty_cleanup }; @@ -596,6 +649,7 @@ static const struct tty_operations pty_unix98_ops = { .chars_in_buffer = pty_chars_in_buffer, .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, + .shutdown = pty_unix98_shutdown, .cleanup = pty_cleanup, }; @@ -614,6 +668,7 @@ static const struct tty_operations pty_unix98_ops = { static int ptmx_open(struct inode *inode, struct file *filp) { struct tty_struct *tty; + struct inode *slave_inode; int retval; int index; @@ -650,15 +705,21 @@ static int ptmx_open(struct inode *inode, struct file *filp) tty_add_file(tty, filp); - retval = devpts_pty_new(inode, tty->link); - if (retval) + slave_inode = devpts_pty_new(inode, + MKDEV(UNIX98_PTY_SLAVE_MAJOR, index), index, + tty->link); + if (IS_ERR(slave_inode)) { + retval = PTR_ERR(slave_inode); goto err_release; + } retval = ptm_driver->ops->open(tty, filp); if (retval) goto err_release; tty_unlock(tty); + tty->driver_data = inode; + tty->link->driver_data = slave_inode; return 0; err_release: tty_unlock(tty); diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index 9700d34..d9056da 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -673,6 +673,7 @@ static void init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev) 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; } @@ -2357,6 +2358,7 @@ static void rp_cleanup_module(void) 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]); } diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c index 66c38a3..f99a845 100644 --- a/drivers/tty/serial/68328serial.c +++ b/drivers/tty/serial/68328serial.c @@ -1225,6 +1225,8 @@ rs68328_init(void) if (tty_register_driver(serial_driver)) { put_tty_driver(serial_driver); + for (i = 0; i < NR_PORTS; i++) + tty_port_destroy(&m68k_soft[i].tport); printk(KERN_ERR "Couldn't register serial driver\n"); return -ENOMEM; } diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 3ba4234..5ccbd90 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -2349,16 +2349,14 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, serial_port_out(port, UART_EFR, efr); } -#ifdef CONFIG_ARCH_OMAP1 /* Workaround to enable 115200 baud on OMAP1510 internal ports */ - if (cpu_is_omap1510() && is_omap_port(up)) { + if (is_omap1510_8250(up)) { if (baud == 115200) { quot = 1; serial_port_out(port, UART_OMAP_OSC_12M_SEL, 1); } else serial_port_out(port, UART_OMAP_OSC_12M_SEL, 0); } -#endif /* * For NatSemi, switch to bank 2 not bank 1, to avoid resetting EXCR2, @@ -2439,10 +2437,9 @@ static unsigned int serial8250_port_size(struct uart_8250_port *pt) { if (pt->port.iotype == UPIO_AU) return 0x1000; -#ifdef CONFIG_ARCH_OMAP1 - if (is_omap_port(pt)) + if (is_omap1_8250(pt)) return 0x16 << pt->port.regshift; -#endif + return 8 << pt->port.regshift; } diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 5a76f9c..3b4ea84 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -106,3 +106,39 @@ static inline int serial8250_pnp_init(void) { return 0; } static inline void serial8250_pnp_exit(void) { } #endif +#ifdef CONFIG_ARCH_OMAP1 +static inline int is_omap1_8250(struct uart_8250_port *pt) +{ + int res; + + switch (pt->port.mapbase) { + case OMAP1_UART1_BASE: + case OMAP1_UART2_BASE: + case OMAP1_UART3_BASE: + res = 1; + break; + default: + res = 0; + break; + } + + return res; +} + +static inline int is_omap1510_8250(struct uart_8250_port *pt) +{ + if (!cpu_is_omap1510()) + return 0; + + return is_omap1_8250(pt); +} +#else +static inline int is_omap1_8250(struct uart_8250_port *pt) +{ + return 0; +} +static inline int is_omap1510_8250(struct uart_8250_port *pt) +{ + return 0; +} +#endif diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index c3b2ec0..b19b8c5 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -161,6 +161,29 @@ static int __devexit dw8250_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int dw8250_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct dw8250_data *data = platform_get_drvdata(pdev); + + serial8250_suspend_port(data->line); + + return 0; +} + +static int dw8250_resume(struct platform_device *pdev) +{ + struct dw8250_data *data = platform_get_drvdata(pdev); + + serial8250_resume_port(data->line); + + return 0; +} +#else +#define dw8250_suspend NULL +#define dw8250_resume NULL +#endif /* CONFIG_PM */ + static const struct of_device_id dw8250_match[] = { { .compatible = "snps,dw-apb-uart" }, { /* Sentinel */ } @@ -175,6 +198,8 @@ static struct platform_driver dw8250_platform_driver = { }, .probe = dw8250_probe, .remove = __devexit_p(dw8250_remove), + .suspend = dw8250_suspend, + .resume = dw8250_resume, }; module_platform_driver(dw8250_platform_driver); diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index eaafb98..f53a7db 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -48,7 +48,7 @@ struct early_serial8250_device { static struct early_serial8250_device early_device; -static unsigned int __init serial_in(struct uart_port *port, int offset) +unsigned int __weak __init serial8250_early_in(struct uart_port *port, int offset) { switch (port->iotype) { case UPIO_MEM: @@ -62,7 +62,7 @@ static unsigned int __init serial_in(struct uart_port *port, int offset) } } -static void __init serial_out(struct uart_port *port, int offset, int value) +void __weak __init serial8250_early_out(struct uart_port *port, int offset, int value) { switch (port->iotype) { case UPIO_MEM: @@ -84,7 +84,7 @@ static void __init wait_for_xmitr(struct uart_port *port) unsigned int status; for (;;) { - status = serial_in(port, UART_LSR); + status = serial8250_early_in(port, UART_LSR); if ((status & BOTH_EMPTY) == BOTH_EMPTY) return; cpu_relax(); @@ -94,7 +94,7 @@ static void __init wait_for_xmitr(struct uart_port *port) static void __init serial_putc(struct uart_port *port, int c) { wait_for_xmitr(port); - serial_out(port, UART_TX, c); + serial8250_early_out(port, UART_TX, c); } static void __init early_serial8250_write(struct console *console, @@ -104,14 +104,14 @@ static void __init early_serial8250_write(struct console *console, unsigned int ier; /* Save the IER and disable interrupts */ - ier = serial_in(port, UART_IER); - serial_out(port, UART_IER, 0); + ier = serial8250_early_in(port, UART_IER); + serial8250_early_out(port, UART_IER, 0); uart_console_write(port, s, count, serial_putc); /* Wait for transmitter to become empty and restore the IER */ wait_for_xmitr(port); - serial_out(port, UART_IER, ier); + serial8250_early_out(port, UART_IER, ier); } static unsigned int __init probe_baud(struct uart_port *port) @@ -119,11 +119,11 @@ static unsigned int __init probe_baud(struct uart_port *port) unsigned char lcr, dll, dlm; unsigned int quot; - lcr = serial_in(port, UART_LCR); - serial_out(port, UART_LCR, lcr | UART_LCR_DLAB); - dll = serial_in(port, UART_DLL); - dlm = serial_in(port, UART_DLM); - serial_out(port, UART_LCR, lcr); + lcr = serial8250_early_in(port, UART_LCR); + serial8250_early_out(port, UART_LCR, lcr | UART_LCR_DLAB); + dll = serial8250_early_in(port, UART_DLL); + dlm = serial8250_early_in(port, UART_DLM); + serial8250_early_out(port, UART_LCR, lcr); quot = (dlm << 8) | dll; return (port->uartclk / 16) / quot; @@ -135,17 +135,17 @@ static void __init init_port(struct early_serial8250_device *device) unsigned int divisor; unsigned char c; - serial_out(port, UART_LCR, 0x3); /* 8n1 */ - serial_out(port, UART_IER, 0); /* no interrupt */ - serial_out(port, UART_FCR, 0); /* no fifo */ - serial_out(port, UART_MCR, 0x3); /* DTR + RTS */ - - divisor = port->uartclk / (16 * device->baud); - c = serial_in(port, UART_LCR); - serial_out(port, UART_LCR, c | UART_LCR_DLAB); - serial_out(port, UART_DLL, divisor & 0xff); - serial_out(port, UART_DLM, (divisor >> 8) & 0xff); - serial_out(port, UART_LCR, c & ~UART_LCR_DLAB); + serial8250_early_out(port, UART_LCR, 0x3); /* 8n1 */ + serial8250_early_out(port, UART_IER, 0); /* no interrupt */ + serial8250_early_out(port, UART_FCR, 0); /* no fifo */ + serial8250_early_out(port, UART_MCR, 0x3); /* DTR + RTS */ + + divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud); + c = serial8250_early_in(port, UART_LCR); + serial8250_early_out(port, UART_LCR, c | UART_LCR_DLAB); + serial8250_early_out(port, UART_DLL, divisor & 0xff); + serial8250_early_out(port, UART_DLM, (divisor >> 8) & 0xff); + serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB); } static int __init parse_options(struct early_serial8250_device *device, diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 17b7d26..508063b 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1068,7 +1068,7 @@ ce4100_serial_setup(struct serial_private *priv, { int ret; - ret = setup_port(priv, port, 0, 0, board->reg_shift); + ret = setup_port(priv, port, idx, 0, board->reg_shift); port->port.iotype = UPIO_MEM32; port->port.type = PORT_XSCALE; port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE); @@ -2658,8 +2658,8 @@ static struct pciserial_board pci_boards[] __devinitdata = { .first_offset = 0x1000, }, [pbn_ce4100_1_115200] = { - .flags = FL_BASE0, - .num_ports = 1, + .flags = FL_BASE_BARS, + .num_ports = 2, .base_baud = 921600, .reg_shift = 2, }, @@ -4332,18 +4332,7 @@ static struct pci_driver serial_pci_driver = { .err_handler = &serial8250_err_handler, }; -static int __init serial8250_pci_init(void) -{ - return pci_register_driver(&serial_pci_driver); -} - -static void __exit serial8250_pci_exit(void) -{ - pci_unregister_driver(&serial_pci_driver); -} - -module_init(serial8250_pci_init); -module_exit(serial8250_pci_exit); +module_pci_driver(serial_pci_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module"); diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 2a53be5..59c23d0 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -93,7 +93,7 @@ config SERIAL_SB1250_DUART_CONSOLE config SERIAL_ATMEL bool "AT91 / AT32 on-chip serial port support" - depends on (ARM && ARCH_AT91) || AVR32 + depends on ARCH_AT91 || AVR32 select SERIAL_CORE help This enables the driver for the on-chip UARTs of the Atmel @@ -198,7 +198,7 @@ config SERIAL_CLPS711X_CONSOLE config SERIAL_SAMSUNG tristate "Samsung SoC serial support" - depends on ARM && PLAT_SAMSUNG + depends on PLAT_SAMSUNG select SERIAL_CORE help Support for the on-chip UARTs on the Samsung S3C24XX series CPUs, @@ -208,14 +208,14 @@ config SERIAL_SAMSUNG config SERIAL_SAMSUNG_UARTS_4 bool - depends on ARM && PLAT_SAMSUNG + depends on PLAT_SAMSUNG default y if !(CPU_S3C2410 || SERIAL_S3C2412 || CPU_S3C2440 || CPU_S3C2442) help Internal node for the common case of 4 Samsung compatible UARTs config SERIAL_SAMSUNG_UARTS int - depends on ARM && PLAT_SAMSUNG + depends on PLAT_SAMSUNG default 6 if ARCH_S5P6450 default 4 if SERIAL_SAMSUNG_UARTS_4 || CPU_S3C2416 default 3 @@ -249,7 +249,7 @@ config SERIAL_SAMSUNG_CONSOLE config SERIAL_SIRFSOC tristate "SiRF SoC Platform Serial port support" - depends on ARM && ARCH_PRIMA2 + depends on ARCH_PRIMA2 select SERIAL_CORE help Support for the on-chip UART on the CSR SiRFprimaII series, @@ -347,7 +347,7 @@ config SERIAL_ZS_CONSOLE config SERIAL_21285 tristate "DC21285 serial port support" - depends on ARM && FOOTBRIDGE + depends on FOOTBRIDGE select SERIAL_CORE help If you have a machine based on a 21285 (Footbridge) StrongARM(R)/ @@ -371,7 +371,7 @@ config SERIAL_21285_CONSOLE config SERIAL_MPSC bool "Marvell MPSC serial port support" - depends on PPC32 && MV64X60 + depends on MV64X60 select SERIAL_CORE help Say Y here if you want to use the Marvell MPSC serial controller. @@ -408,7 +408,7 @@ config SERIAL_PXA_CONSOLE config SERIAL_SA1100 bool "SA1100 serial port support" - depends on ARM && ARCH_SA1100 + depends on ARCH_SA1100 select SERIAL_CORE help If you have a machine based on a SA1100/SA1110 StrongARM(R) CPU you @@ -716,7 +716,7 @@ config SERIAL_SH_SCI_DMA config SERIAL_PNX8XXX bool "Enable PNX8XXX SoCs' UART Support" - depends on MIPS && (SOC_PNX8550 || SOC_PNX833X) + depends on SOC_PNX8550 || SOC_PNX833X select SERIAL_CORE help If you have a MIPS-based Philips SoC such as PNX8550 or PNX8330 @@ -1013,7 +1013,7 @@ config SERIAL_SGI_IOC3 config SERIAL_MSM bool "MSM on-chip serial port support" - depends on ARM && ARCH_MSM + depends on ARCH_MSM select SERIAL_CORE config SERIAL_MSM_CONSOLE @@ -1035,7 +1035,7 @@ config SERIAL_MSM_HS config SERIAL_VT8500 bool "VIA VT8500 on-chip serial port support" - depends on ARM && ARCH_VT8500 + depends on ARCH_VT8500 select SERIAL_CORE config SERIAL_VT8500_CONSOLE @@ -1045,7 +1045,7 @@ config SERIAL_VT8500_CONSOLE config SERIAL_NETX tristate "NetX serial port support" - depends on ARM && ARCH_NETX + depends on ARCH_NETX select SERIAL_CORE help If you have a machine based on a Hilscher NetX SoC you @@ -1376,6 +1376,7 @@ config SERIAL_MXS_AUART_CONSOLE config SERIAL_XILINX_PS_UART tristate "Xilinx PS UART support" + depends on OF select SERIAL_CORE help This driver supports the Xilinx PS UART port. @@ -1423,4 +1424,27 @@ config SERIAL_EFM32_UART_CONSOLE depends on SERIAL_EFM32_UART=y select SERIAL_CORE_CONSOLE +config SERIAL_ARC + tristate "ARC UART driver support" + select SERIAL_CORE + help + Driver for on-chip UART for ARC(Synopsys) for the legacy + FPGA Boards (ML50x/ARCAngel4) + +config SERIAL_ARC_CONSOLE + bool "Console on ARC UART" + depends on SERIAL_ARC=y + select SERIAL_CORE_CONSOLE + help + Enable system Console on ARC UART + +config SERIAL_ARC_NR_PORTS + int "Number of ARC UART ports" + depends on SERIAL_ARC + range 1 3 + default "1" + help + Set this to the number of serial ports you want the driver + to support. + endmenu diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 4f694da..df1b998 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -82,3 +82,4 @@ obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o +obj-$(CONFIG_SERIAL_ARC) += arc_uart.o diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index d7e1ede..7fca402 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -56,8 +56,7 @@ #include <linux/of_device.h> #include <linux/pinctrl/consumer.h> #include <linux/sizes.h> - -#include <asm/io.h> +#include <linux/io.h> #define UART_NR 14 @@ -1973,7 +1972,8 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) goto out; } - uap = kzalloc(sizeof(struct uart_amba_port), GFP_KERNEL); + uap = devm_kzalloc(&dev->dev, sizeof(struct uart_amba_port), + GFP_KERNEL); if (uap == NULL) { ret = -ENOMEM; goto out; @@ -1981,16 +1981,17 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) i = pl011_probe_dt_alias(i, &dev->dev); - base = ioremap(dev->res.start, resource_size(&dev->res)); + base = devm_ioremap(&dev->dev, dev->res.start, + resource_size(&dev->res)); if (!base) { ret = -ENOMEM; - goto free; + goto out; } uap->pinctrl = devm_pinctrl_get(&dev->dev); if (IS_ERR(uap->pinctrl)) { ret = PTR_ERR(uap->pinctrl); - goto unmap; + goto out; } uap->pins_default = pinctrl_lookup_state(uap->pinctrl, PINCTRL_STATE_DEFAULT); @@ -2002,10 +2003,10 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) if (IS_ERR(uap->pins_sleep)) dev_dbg(&dev->dev, "could not get sleep pinstate\n"); - uap->clk = clk_get(&dev->dev, NULL); + uap->clk = devm_clk_get(&dev->dev, NULL); if (IS_ERR(uap->clk)) { ret = PTR_ERR(uap->clk); - goto unmap; + goto out; } uap->vendor = vendor; @@ -2038,11 +2039,6 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) amba_set_drvdata(dev, NULL); amba_ports[i] = NULL; pl011_dma_remove(uap); - clk_put(uap->clk); - unmap: - iounmap(base); - free: - kfree(uap); } out: return ret; @@ -2062,9 +2058,6 @@ static int pl011_remove(struct amba_device *dev) amba_ports[i] = NULL; pl011_dma_remove(uap); - iounmap(uap->port.membase); - clk_put(uap->clk); - kfree(uap); return 0; } diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index e4f60e2..0f5b28a 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -25,11 +25,19 @@ #include <linux/io.h> #include <linux/irq.h> +#include <asm/div64.h> + #include <asm/mach-ath79/ar933x_uart.h> #include <asm/mach-ath79/ar933x_uart_platform.h> #define DRIVER_NAME "ar933x-uart" +#define AR933X_UART_MAX_SCALE 0xff +#define AR933X_UART_MAX_STEP 0xffff + +#define AR933X_UART_MIN_BAUD 300 +#define AR933X_UART_MAX_BAUD 3000000 + #define AR933X_DUMMY_STATUS_RD 0x01 static struct uart_driver ar933x_uart_driver; @@ -37,6 +45,8 @@ static struct uart_driver ar933x_uart_driver; struct ar933x_uart_port { struct uart_port port; unsigned int ier; /* shadow Interrupt Enable Register */ + unsigned int min_baud; + unsigned int max_baud; }; static inline unsigned int ar933x_uart_read(struct ar933x_uart_port *up, @@ -162,6 +172,57 @@ static void ar933x_uart_enable_ms(struct uart_port *port) { } +/* + * baudrate = (clk / (scale + 1)) * (step * (1 / 2^17)) + */ +static unsigned long ar933x_uart_get_baud(unsigned int clk, + unsigned int scale, + unsigned int step) +{ + u64 t; + u32 div; + + div = (2 << 16) * (scale + 1); + t = clk; + t *= step; + t += (div / 2); + do_div(t, div); + + return t; +} + +static void ar933x_uart_get_scale_step(unsigned int clk, + unsigned int baud, + unsigned int *scale, + unsigned int *step) +{ + unsigned int tscale; + long min_diff; + + *scale = 0; + *step = 0; + + min_diff = baud; + for (tscale = 0; tscale < AR933X_UART_MAX_SCALE; tscale++) { + u64 tstep; + int diff; + + tstep = baud * (tscale + 1); + tstep *= (2 << 16); + do_div(tstep, clk); + + if (tstep > AR933X_UART_MAX_STEP) + break; + + diff = abs(ar933x_uart_get_baud(clk, tscale, tstep) - baud); + if (diff < min_diff) { + min_diff = diff; + *scale = tscale; + *step = tstep; + } + } +} + static void ar933x_uart_set_termios(struct uart_port *port, struct ktermios *new, struct ktermios *old) @@ -169,7 +230,7 @@ static void ar933x_uart_set_termios(struct uart_port *port, struct ar933x_uart_port *up = (struct ar933x_uart_port *) port; unsigned int cs; unsigned long flags; - unsigned int baud, scale; + unsigned int baud, scale, step; /* Only CS8 is supported */ new->c_cflag &= ~CSIZE; @@ -191,8 +252,8 @@ static void ar933x_uart_set_termios(struct uart_port *port, /* Mark/space parity is not supported */ new->c_cflag &= ~CMSPAR; - baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16); - scale = (port->uartclk / (16 * baud)) - 1; + baud = uart_get_baud_rate(port, new, old, up->min_baud, up->max_baud); + ar933x_uart_get_scale_step(port->uartclk, baud, &scale, &step); /* * Ok, we're now changing the port state. Do it with @@ -200,6 +261,10 @@ static void ar933x_uart_set_termios(struct uart_port *port, */ spin_lock_irqsave(&up->port.lock, flags); + /* disable the UART */ + ar933x_uart_rmw_clear(up, AR933X_UART_CS_REG, + AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S); + /* Update the per-port timeout. */ uart_update_timeout(port, new->c_cflag, baud); @@ -210,7 +275,7 @@ static void ar933x_uart_set_termios(struct uart_port *port, up->port.ignore_status_mask |= AR933X_DUMMY_STATUS_RD; ar933x_uart_write(up, AR933X_UART_CLOCK_REG, - scale << AR933X_UART_CLOCK_SCALE_S | 8192); + scale << AR933X_UART_CLOCK_SCALE_S | step); /* setup configuration register */ ar933x_uart_rmw(up, AR933X_UART_CS_REG, AR933X_UART_CS_PARITY_M, cs); @@ -219,6 +284,11 @@ static void ar933x_uart_set_termios(struct uart_port *port, ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, AR933X_UART_CS_HOST_INT_EN); + /* reenable the UART */ + ar933x_uart_rmw(up, AR933X_UART_CS_REG, + AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S, + AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S); + spin_unlock_irqrestore(&up->port.lock, flags); if (tty_termios_baud_rate(new)) @@ -401,6 +471,8 @@ static void ar933x_uart_config_port(struct uart_port *port, int flags) static int ar933x_uart_verify_port(struct uart_port *port, struct serial_struct *ser) { + struct ar933x_uart_port *up = (struct ar933x_uart_port *) port; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_AR933X) return -EINVAL; @@ -408,7 +480,8 @@ static int ar933x_uart_verify_port(struct uart_port *port, if (ser->irq < 0 || ser->irq >= NR_IRQS) return -EINVAL; - if (ser->baud_base < 28800) + if (ser->baud_base < up->min_baud || + ser->baud_base > up->max_baud) return -EINVAL; return 0; @@ -561,6 +634,7 @@ static int __devinit ar933x_uart_probe(struct platform_device *pdev) struct uart_port *port; struct resource *mem_res; struct resource *irq_res; + unsigned int baud; int id; int ret; @@ -611,6 +685,12 @@ static int __devinit ar933x_uart_probe(struct platform_device *pdev) port->fifosize = AR933X_UART_FIFO_SIZE; port->ops = &ar933x_uart_ops; + baud = ar933x_uart_get_baud(port->uartclk, AR933X_UART_MAX_SCALE, 1); + up->min_baud = max_t(unsigned int, baud, AR933X_UART_MIN_BAUD); + + baud = ar933x_uart_get_baud(port->uartclk, 0, AR933X_UART_MAX_STEP); + up->max_baud = min_t(unsigned int, baud, AR933X_UART_MAX_BAUD); + ar933x_uart_add_console_port(up); ret = uart_add_one_port(&ar933x_uart_driver, &up->port); diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c new file mode 100644 index 0000000..e9c61d1 --- /dev/null +++ b/drivers/tty/serial/arc_uart.c @@ -0,0 +1,746 @@ +/* + * ARC On-Chip(fpga) UART Driver + * + * Copyright (C) 2010-2012 Synopsys, Inc. (www.synopsys.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * vineetg: July 10th 2012 + * -Decoupled the driver from arch/arc + * +Using platform_get_resource() for irq/membase (thx to bfin_uart.c) + * +Using early_platform_xxx() for early console (thx to mach-shmobile/xxx) + * + * Vineetg: Aug 21st 2010 + * -Is uart_tx_stopped() not done in tty write path as it has already been + * taken care of, in serial core + * + * Vineetg: Aug 18th 2010 + * -New Serial Core based ARC UART driver + * -Derived largely from blackfin driver albiet with some major tweaks + * + * TODO: + * -check if sysreq works + */ + +#if defined(CONFIG_SERIAL_ARC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/module.h> +#include <linux/serial.h> +#include <linux/console.h> +#include <linux/sysrq.h> +#include <linux/platform_device.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial_core.h> +#include <linux/io.h> + +/************************************* + * ARC UART Hardware Specs + ************************************/ +#define ARC_UART_TX_FIFO_SIZE 1 + +/* + * UART Register set (this is not a Standards Compliant IP) + * Also each reg is Word aligned, but only 8 bits wide + */ +#define R_ID0 0 +#define R_ID1 4 +#define R_ID2 8 +#define R_ID3 12 +#define R_DATA 16 +#define R_STS 20 +#define R_BAUDL 24 +#define R_BAUDH 28 + +/* Bits for UART Status Reg (R/W) */ +#define RXIENB 0x04 /* Receive Interrupt Enable */ +#define TXIENB 0x40 /* Transmit Interrupt Enable */ + +#define RXEMPTY 0x20 /* Receive FIFO Empty: No char receivede */ +#define TXEMPTY 0x80 /* Transmit FIFO Empty, thus char can be written into */ + +#define RXFULL 0x08 /* Receive FIFO full */ +#define RXFULL1 0x10 /* Receive FIFO has space for 1 char (tot space=4) */ + +#define RXFERR 0x01 /* Frame Error: Stop Bit not detected */ +#define RXOERR 0x02 /* OverFlow Err: Char recv but RXFULL still set */ + +/* Uart bit fiddling helpers: lowest level */ +#define RBASE(uart, reg) (uart->port.membase + reg) +#define UART_REG_SET(u, r, v) writeb((v), RBASE(u, r)) +#define UART_REG_GET(u, r) readb(RBASE(u, r)) + +#define UART_REG_OR(u, r, v) UART_REG_SET(u, r, UART_REG_GET(u, r) | (v)) +#define UART_REG_CLR(u, r, v) UART_REG_SET(u, r, UART_REG_GET(u, r) & ~(v)) + +/* Uart bit fiddling helpers: API level */ +#define UART_SET_DATA(uart, val) UART_REG_SET(uart, R_DATA, val) +#define UART_GET_DATA(uart) UART_REG_GET(uart, R_DATA) + +#define UART_SET_BAUDH(uart, val) UART_REG_SET(uart, R_BAUDH, val) +#define UART_SET_BAUDL(uart, val) UART_REG_SET(uart, R_BAUDL, val) + +#define UART_CLR_STATUS(uart, val) UART_REG_CLR(uart, R_STS, val) +#define UART_GET_STATUS(uart) UART_REG_GET(uart, R_STS) + +#define UART_ALL_IRQ_DISABLE(uart) UART_REG_CLR(uart, R_STS, RXIENB|TXIENB) +#define UART_RX_IRQ_DISABLE(uart) UART_REG_CLR(uart, R_STS, RXIENB) +#define UART_TX_IRQ_DISABLE(uart) UART_REG_CLR(uart, R_STS, TXIENB) + +#define UART_ALL_IRQ_ENABLE(uart) UART_REG_OR(uart, R_STS, RXIENB|TXIENB) +#define UART_RX_IRQ_ENABLE(uart) UART_REG_OR(uart, R_STS, RXIENB) +#define UART_TX_IRQ_ENABLE(uart) UART_REG_OR(uart, R_STS, TXIENB) + +#define ARC_SERIAL_DEV_NAME "ttyARC" + +struct arc_uart_port { + struct uart_port port; + unsigned long baud; + int is_emulated; /* H/w vs. Instruction Set Simulator */ +}; + +#define to_arc_port(uport) container_of(uport, struct arc_uart_port, port) + +static struct arc_uart_port arc_uart_ports[CONFIG_SERIAL_ARC_NR_PORTS]; + +#ifdef CONFIG_SERIAL_ARC_CONSOLE +static struct console arc_console; +#endif + +#define DRIVER_NAME "arc-uart" + +static struct uart_driver arc_uart_driver = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = ARC_SERIAL_DEV_NAME, + .major = 0, + .minor = 0, + .nr = CONFIG_SERIAL_ARC_NR_PORTS, +#ifdef CONFIG_SERIAL_ARC_CONSOLE + .cons = &arc_console, +#endif +}; + +static void arc_serial_stop_rx(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + + UART_RX_IRQ_DISABLE(uart); +} + +static void arc_serial_stop_tx(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + + while (!(UART_GET_STATUS(uart) & TXEMPTY)) + cpu_relax(); + + UART_TX_IRQ_DISABLE(uart); +} + +/* + * Return TIOCSER_TEMT when transmitter is not busy. + */ +static unsigned int arc_serial_tx_empty(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + unsigned int stat; + + stat = UART_GET_STATUS(uart); + if (stat & TXEMPTY) + return TIOCSER_TEMT; + + return 0; +} + +/* + * Driver internal routine, used by both tty(serial core) as well as tx-isr + * -Called under spinlock in either cases + * -also tty->stopped / tty->hw_stopped has already been checked + * = by uart_start( ) before calling us + * = tx_ist checks that too before calling + */ +static void arc_serial_tx_chars(struct arc_uart_port *uart) +{ + struct circ_buf *xmit = &uart->port.state->xmit; + int sent = 0; + unsigned char ch; + + if (unlikely(uart->port.x_char)) { + UART_SET_DATA(uart, uart->port.x_char); + uart->port.icount.tx++; + uart->port.x_char = 0; + sent = 1; + } else if (xmit->tail != xmit->head) { /* TODO: uart_circ_empty */ + ch = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + uart->port.icount.tx++; + while (!(UART_GET_STATUS(uart) & TXEMPTY)) + cpu_relax(); + UART_SET_DATA(uart, ch); + sent = 1; + } + + /* + * If num chars in xmit buffer are too few, ask tty layer for more. + * By Hard ISR to schedule processing in software interrupt part + */ + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&uart->port); + + if (sent) + UART_TX_IRQ_ENABLE(uart); +} + +/* + * port is locked and interrupts are disabled + * uart_start( ) calls us under the port spinlock irqsave + */ +static void arc_serial_start_tx(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + + arc_serial_tx_chars(uart); +} + +static void arc_serial_rx_chars(struct arc_uart_port *uart) +{ + struct tty_struct *tty = tty_port_tty_get(&uart->port.state->port); + unsigned int status, ch, flg = 0; + + if (!tty) + return; + + /* + * UART has 4 deep RX-FIFO. Driver's recongnition of this fact + * is very subtle. Here's how ... + * Upon getting a RX-Intr, such that RX-EMPTY=0, meaning data available, + * driver reads the DATA Reg and keeps doing that in a loop, until + * RX-EMPTY=1. Multiple chars being avail, with a single Interrupt, + * before RX-EMPTY=0, implies some sort of buffering going on in the + * controller, which is indeed the Rx-FIFO. + */ + while (!((status = UART_GET_STATUS(uart)) & RXEMPTY)) { + + ch = UART_GET_DATA(uart); + uart->port.icount.rx++; + + if (unlikely(status & (RXOERR | RXFERR))) { + if (status & RXOERR) { + uart->port.icount.overrun++; + flg = TTY_OVERRUN; + UART_CLR_STATUS(uart, RXOERR); + } + + if (status & RXFERR) { + uart->port.icount.frame++; + flg = TTY_FRAME; + UART_CLR_STATUS(uart, RXFERR); + } + } else + flg = TTY_NORMAL; + + if (unlikely(uart_handle_sysrq_char(&uart->port, ch))) + goto done; + + uart_insert_char(&uart->port, status, RXOERR, ch, flg); + +done: + tty_flip_buffer_push(tty); + } + + tty_kref_put(tty); +} + +/* + * A note on the Interrupt handling state machine of this driver + * + * kernel printk writes funnel thru the console driver framework and in order + * to keep things simple as well as efficient, it writes to UART in polled + * mode, in one shot, and exits. + * + * OTOH, Userland output (via tty layer), uses interrupt based writes as there + * can be undeterministic delay between char writes. + * + * Thus Rx-interrupts are always enabled, while tx-interrupts are by default + * disabled. + * + * When tty has some data to send out, serial core calls driver's start_tx + * which + * -checks-if-tty-buffer-has-char-to-send + * -writes-data-to-uart + * -enable-tx-intr + * + * Once data bits are pushed out, controller raises the Tx-room-avail-Interrupt. + * The first thing Tx ISR does is disable further Tx interrupts (as this could + * be the last char to send, before settling down into the quiet polled mode). + * It then calls the exact routine used by tty layer write to send out any + * more char in tty buffer. In case of sending, it re-enables Tx-intr. In case + * of no data, it remains disabled. + * This is how the transmit state machine is dynamically switched on/off + */ + +static irqreturn_t arc_serial_isr(int irq, void *dev_id) +{ + struct arc_uart_port *uart = dev_id; + unsigned int status; + + status = UART_GET_STATUS(uart); + + /* + * Single IRQ for both Rx (data available) Tx (room available) Interrupt + * notifications from the UART Controller. + * To demultiplex between the two, we check the relevant bits + */ + if ((status & RXIENB) && !(status & RXEMPTY)) { + + /* already in ISR, no need of xx_irqsave */ + spin_lock(&uart->port.lock); + arc_serial_rx_chars(uart); + spin_unlock(&uart->port.lock); + } + + if ((status & TXIENB) && (status & TXEMPTY)) { + + /* Unconditionally disable further Tx-Interrupts. + * will be enabled by tx_chars() if needed. + */ + UART_TX_IRQ_DISABLE(uart); + + spin_lock(&uart->port.lock); + + if (!uart_tx_stopped(&uart->port)) + arc_serial_tx_chars(uart); + + spin_unlock(&uart->port.lock); + } + + return IRQ_HANDLED; +} + +static unsigned int arc_serial_get_mctrl(struct uart_port *port) +{ + /* + * Pretend we have a Modem status reg and following bits are + * always set, to satify the serial core state machine + * (DSR) Data Set Ready + * (CTS) Clear To Send + * (CAR) Carrier Detect + */ + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; +} + +static void arc_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* MCR not present */ +} + +/* Enable Modem Status Interrupts */ + +static void arc_serial_enable_ms(struct uart_port *port) +{ + /* MSR not present */ +} + +static void arc_serial_break_ctl(struct uart_port *port, int break_state) +{ + /* ARC UART doesn't support sending Break signal */ +} + +static int arc_serial_startup(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + + /* Before we hook up the ISR, Disable all UART Interrupts */ + UART_ALL_IRQ_DISABLE(uart); + + if (request_irq(uart->port.irq, arc_serial_isr, 0, "arc uart rx-tx", + uart)) { + dev_warn(uart->port.dev, "Unable to attach ARC UART intr\n"); + return -EBUSY; + } + + UART_RX_IRQ_ENABLE(uart); /* Only Rx IRQ enabled to begin with */ + + return 0; +} + +/* This is not really needed */ +static void arc_serial_shutdown(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + free_irq(uart->port.irq, uart); +} + +static void +arc_serial_set_termios(struct uart_port *port, struct ktermios *new, + struct ktermios *old) +{ + struct arc_uart_port *uart = to_arc_port(port); + unsigned int baud, uartl, uarth, hw_val; + unsigned long flags; + + /* + * Use the generic handler so that any specially encoded baud rates + * such as SPD_xx flags or "%B0" can be handled + * Max Baud I suppose will not be more than current 115K * 4 + * Formula for ARC UART is: hw-val = ((CLK/(BAUD*4)) -1) + * spread over two 8-bit registers + */ + baud = uart_get_baud_rate(port, new, old, 0, 460800); + + hw_val = port->uartclk / (uart->baud * 4) - 1; + uartl = hw_val & 0xFF; + uarth = (hw_val >> 8) & 0xFF; + + /* + * UART ISS(Instruction Set simulator) emulation has a subtle bug: + * A existing value of Baudh = 0 is used as a indication to startup + * it's internal state machine. + * Thus if baudh is set to 0, 2 times, it chokes. + * This happens with BAUD=115200 and the formaula above + * Until that is fixed, when running on ISS, we will set baudh to !0 + */ + if (uart->is_emulated) + uarth = 1; + + spin_lock_irqsave(&port->lock, flags); + + UART_ALL_IRQ_DISABLE(uart); + + UART_SET_BAUDL(uart, uartl); + UART_SET_BAUDH(uart, uarth); + + UART_RX_IRQ_ENABLE(uart); + + /* + * UART doesn't support Parity/Hardware Flow Control; + * Only supports 8N1 character size + */ + new->c_cflag &= ~(CMSPAR|CRTSCTS|CSIZE); + new->c_cflag |= CS8; + + if (old) + tty_termios_copy_hw(new, old); + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(new)) + tty_termios_encode_baud_rate(new, baud, baud); + + uart_update_timeout(port, new->c_cflag, baud); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *arc_serial_type(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + + return uart->port.type == PORT_ARC ? DRIVER_NAME : NULL; +} + +static void arc_serial_release_port(struct uart_port *port) +{ +} + +static int arc_serial_request_port(struct uart_port *port) +{ + return 0; +} + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + */ +static int +arc_serial_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if (port->type != PORT_UNKNOWN && ser->type != PORT_ARC) + return -EINVAL; + + return 0; +} + +/* + * Configure/autoconfigure the port. + */ +static void arc_serial_config_port(struct uart_port *port, int flags) +{ + struct arc_uart_port *uart = to_arc_port(port); + + if (flags & UART_CONFIG_TYPE) + uart->port.type = PORT_ARC; +} + +#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_ARC_CONSOLE) + +static void arc_serial_poll_putchar(struct uart_port *port, unsigned char chr) +{ + struct arc_uart_port *uart = to_arc_port(port); + + while (!(UART_GET_STATUS(uart) & TXEMPTY)) + cpu_relax(); + + UART_SET_DATA(uart, chr); +} +#endif + +#ifdef CONFIG_CONSOLE_POLL +static int arc_serial_poll_getchar(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + unsigned char chr; + + while (!(UART_GET_STATUS(uart) & RXEMPTY)) + cpu_relax(); + + chr = UART_GET_DATA(uart); + return chr; +} +#endif + +static struct uart_ops arc_serial_pops = { + .tx_empty = arc_serial_tx_empty, + .set_mctrl = arc_serial_set_mctrl, + .get_mctrl = arc_serial_get_mctrl, + .stop_tx = arc_serial_stop_tx, + .start_tx = arc_serial_start_tx, + .stop_rx = arc_serial_stop_rx, + .enable_ms = arc_serial_enable_ms, + .break_ctl = arc_serial_break_ctl, + .startup = arc_serial_startup, + .shutdown = arc_serial_shutdown, + .set_termios = arc_serial_set_termios, + .type = arc_serial_type, + .release_port = arc_serial_release_port, + .request_port = arc_serial_request_port, + .config_port = arc_serial_config_port, + .verify_port = arc_serial_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_put_char = arc_serial_poll_putchar, + .poll_get_char = arc_serial_poll_getchar, +#endif +}; + +static int __devinit +arc_uart_init_one(struct platform_device *pdev, struct arc_uart_port *uart) +{ + struct resource *res, *res2; + unsigned long *plat_data; + + if (pdev->id < 0 || pdev->id >= CONFIG_SERIAL_ARC_NR_PORTS) { + dev_err(&pdev->dev, "Wrong uart platform device id.\n"); + return -ENOENT; + } + + plat_data = ((unsigned long *)(pdev->dev.platform_data)); + uart->baud = plat_data[0]; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res2) + return -ENODEV; + + uart->port.mapbase = res->start; + uart->port.membase = ioremap_nocache(res->start, resource_size(res)); + if (!uart->port.membase) + /* No point of dev_err since UART itself is hosed here */ + return -ENXIO; + + uart->port.irq = res2->start; + uart->port.dev = &pdev->dev; + uart->port.iotype = UPIO_MEM; + uart->port.flags = UPF_BOOT_AUTOCONF; + uart->port.line = pdev->id; + uart->port.ops = &arc_serial_pops; + + uart->port.uartclk = plat_data[1]; + uart->port.fifosize = ARC_UART_TX_FIFO_SIZE; + + /* + * uart_insert_char( ) uses it in decideding whether to ignore a + * char or not. Explicitly setting it here, removes the subtelty + */ + uart->port.ignore_status_mask = 0; + + /* Real Hardware vs. emulated to work around a bug */ + uart->is_emulated = !!plat_data[2]; + + return 0; +} + +#ifdef CONFIG_SERIAL_ARC_CONSOLE + +static int __devinit arc_serial_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= CONFIG_SERIAL_ARC_NR_PORTS) + return -ENODEV; + + /* + * The uart port backing the console (e.g. ttyARC1) might not have been + * init yet. If so, defer the console setup to after the port. + */ + port = &arc_uart_ports[co->index].port; + if (!port->membase) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + /* + * Serial core will call port->ops->set_termios( ) + * which will set the baud reg + */ + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static void arc_serial_console_putchar(struct uart_port *port, int ch) +{ + arc_serial_poll_putchar(port, (unsigned char)ch); +} + +/* + * Interrupts are disabled on entering + */ +static void arc_serial_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port = &arc_uart_ports[co->index].port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + uart_console_write(port, s, count, arc_serial_console_putchar); + spin_unlock_irqrestore(&port->lock, flags); +} + +static struct console arc_console = { + .name = ARC_SERIAL_DEV_NAME, + .write = arc_serial_console_write, + .device = uart_console_device, + .setup = arc_serial_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &arc_uart_driver +}; + +static __init void early_serial_write(struct console *con, const char *s, + unsigned int n) +{ + struct uart_port *port = &arc_uart_ports[con->index].port; + unsigned int i; + + for (i = 0; i < n; i++, s++) { + if (*s == '\n') + arc_serial_poll_putchar(port, '\r'); + arc_serial_poll_putchar(port, *s); + } +} + +static struct __initdata console arc_early_serial_console = { + .name = "early_ARCuart", + .write = early_serial_write, + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1 +}; + +static int __devinit arc_serial_probe_earlyprintk(struct platform_device *pdev) +{ + arc_early_serial_console.index = pdev->id; + + arc_uart_init_one(pdev, &arc_uart_ports[pdev->id]); + + arc_serial_console_setup(&arc_early_serial_console, NULL); + + register_console(&arc_early_serial_console); + return 0; +} +#else +static int __devinit arc_serial_probe_earlyprintk(struct platform_device *pdev) +{ + return -ENODEV; +} +#endif /* CONFIG_SERIAL_ARC_CONSOLE */ + +static int __devinit arc_serial_probe(struct platform_device *pdev) +{ + struct arc_uart_port *uart; + int rc; + + if (is_early_platform_device(pdev)) + return arc_serial_probe_earlyprintk(pdev); + + uart = &arc_uart_ports[pdev->id]; + rc = arc_uart_init_one(pdev, uart); + if (rc) + return rc; + + return uart_add_one_port(&arc_uart_driver, &uart->port); +} + +static int __devexit arc_serial_remove(struct platform_device *pdev) +{ + /* This will never be called */ + return 0; +} + +static struct platform_driver arc_platform_driver = { + .probe = arc_serial_probe, + .remove = __devexit_p(arc_serial_remove), + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +#ifdef CONFIG_SERIAL_ARC_CONSOLE +/* + * Register an early platform driver of "earlyprintk" class. + * ARCH platform code installs the driver and probes the early devices + * The installation could rely on user specifying earlyprintk=xyx in cmd line + * or it could be done independently, for all "earlyprintk" class drivers. + * [see arch/arc/plat-arcfpga/platform.c] + */ +early_platform_init("earlyprintk", &arc_platform_driver); + +#endif /* CONFIG_SERIAL_ARC_CONSOLE */ + +static int __init arc_serial_init(void) +{ + int ret; + + ret = uart_register_driver(&arc_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&arc_platform_driver); + if (ret) + uart_unregister_driver(&arc_uart_driver); + + return ret; +} + +static void __exit arc_serial_exit(void) +{ + platform_driver_unregister(&arc_platform_driver); + uart_unregister_driver(&arc_uart_driver); +} + +module_init(arc_serial_init); +module_exit(arc_serial_exit); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("plat-arcfpga/uart"); +MODULE_AUTHOR("Vineet Gupta"); +MODULE_DESCRIPTION("ARC(Synopsys) On-Chip(fpga) serial driver"); diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index 9242d56..ca64c4a 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -477,9 +477,9 @@ static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart) void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart) { int x_pos, pos; + unsigned long flags; - dma_disable_irq_nosync(uart->rx_dma_channel); - spin_lock_bh(&uart->rx_lock); + spin_lock_irqsave(&uart->rx_lock, flags); /* 2D DMA RX buffer ring is used. Because curr_y_count and * curr_x_count can't be read as an atomic operation, @@ -510,8 +510,7 @@ void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart) uart->rx_dma_buf.tail = uart->rx_dma_buf.head; } - spin_unlock_bh(&uart->rx_lock); - dma_enable_irq(uart->rx_dma_channel); + spin_unlock_irqrestore(&uart->rx_lock, flags); mod_timer(&(uart->rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES); } diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index d0f719f..a0a6db5 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -10,15 +10,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #if defined(CONFIG_SERIAL_CLPS711X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) @@ -26,172 +17,169 @@ #endif #include <linux/module.h> -#include <linux/ioport.h> -#include <linux/init.h> -#include <linux/console.h> -#include <linux/sysrq.h> -#include <linux/spinlock.h> #include <linux/device.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> +#include <linux/console.h> #include <linux/serial_core.h> #include <linux/serial.h> #include <linux/io.h> +#include <linux/clk.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/ioport.h> +#include <linux/platform_device.h> #include <mach/hardware.h> -#include <asm/irq.h> - -#define UART_NR 2 - -#define SERIAL_CLPS711X_MAJOR 204 -#define SERIAL_CLPS711X_MINOR 40 -#define SERIAL_CLPS711X_NR UART_NR - -/* - * We use the relevant SYSCON register as a base address for these ports. - */ -#define UBRLCR(port) ((port)->iobase + UBRLCR1 - SYSCON1) -#define UARTDR(port) ((port)->iobase + UARTDR1 - SYSCON1) -#define SYSFLG(port) ((port)->iobase + SYSFLG1 - SYSCON1) -#define SYSCON(port) ((port)->iobase + SYSCON1 - SYSCON1) - -#define TX_IRQ(port) ((port)->irq) -#define RX_IRQ(port) ((port)->irq + 1) -#define UART_ANY_ERR (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR) - -#define tx_enabled(port) ((port)->unused[0]) +#define UART_CLPS711X_NAME "uart-clps711x" +#define UART_CLPS711X_NR 2 +#define UART_CLPS711X_MAJOR 204 +#define UART_CLPS711X_MINOR 40 + +#define UBRLCR(port) ((port)->line ? UBRLCR2 : UBRLCR1) +#define UARTDR(port) ((port)->line ? UARTDR2 : UARTDR1) +#define SYSFLG(port) ((port)->line ? SYSFLG2 : SYSFLG1) +#define SYSCON(port) ((port)->line ? SYSCON2 : SYSCON1) +#define TX_IRQ(port) ((port)->line ? IRQ_UTXINT2 : IRQ_UTXINT1) +#define RX_IRQ(port) ((port)->line ? IRQ_URXINT2 : IRQ_URXINT1) + +struct clps711x_port { + struct uart_driver uart; + struct clk *uart_clk; + struct uart_port port[UART_CLPS711X_NR]; + int tx_enabled[UART_CLPS711X_NR]; +#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE + struct console console; +#endif +}; -static void clps711xuart_stop_tx(struct uart_port *port) +static void uart_clps711x_stop_tx(struct uart_port *port) { - if (tx_enabled(port)) { + struct clps711x_port *s = dev_get_drvdata(port->dev); + + if (s->tx_enabled[port->line]) { disable_irq(TX_IRQ(port)); - tx_enabled(port) = 0; + s->tx_enabled[port->line] = 0; } } -static void clps711xuart_start_tx(struct uart_port *port) +static void uart_clps711x_start_tx(struct uart_port *port) { - if (!tx_enabled(port)) { + struct clps711x_port *s = dev_get_drvdata(port->dev); + + if (!s->tx_enabled[port->line]) { enable_irq(TX_IRQ(port)); - tx_enabled(port) = 1; + s->tx_enabled[port->line] = 1; } } -static void clps711xuart_stop_rx(struct uart_port *port) +static void uart_clps711x_stop_rx(struct uart_port *port) { disable_irq(RX_IRQ(port)); } -static void clps711xuart_enable_ms(struct uart_port *port) +static void uart_clps711x_enable_ms(struct uart_port *port) { + /* Do nothing */ } -static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id) +static irqreturn_t uart_clps711x_int_rx(int irq, void *dev_id) { struct uart_port *port = dev_id; - struct tty_struct *tty = port->state->port.tty; + struct tty_struct *tty = tty_port_tty_get(&port->state->port); unsigned int status, ch, flg; - status = clps_readl(SYSFLG(port)); - while (!(status & SYSFLG_URXFE)) { - ch = clps_readl(UARTDR(port)); + if (!tty) + return IRQ_HANDLED; - port->icount.rx++; + for (;;) { + status = clps_readl(SYSFLG(port)); + if (status & SYSFLG_URXFE) + break; + + ch = clps_readw(UARTDR(port)); + status = ch & (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR); + ch &= 0xff; + port->icount.rx++; flg = TTY_NORMAL; - /* - * Note that the error handling code is - * out of the main execution path - */ - if (unlikely(ch & UART_ANY_ERR)) { - if (ch & UARTDR_PARERR) + if (unlikely(status)) { + if (status & UARTDR_PARERR) port->icount.parity++; - else if (ch & UARTDR_FRMERR) + else if (status & UARTDR_FRMERR) port->icount.frame++; - if (ch & UARTDR_OVERR) + else if (status & UARTDR_OVERR) port->icount.overrun++; - ch &= port->read_status_mask; + status &= port->read_status_mask; - if (ch & UARTDR_PARERR) + if (status & UARTDR_PARERR) flg = TTY_PARITY; - else if (ch & UARTDR_FRMERR) + else if (status & UARTDR_FRMERR) flg = TTY_FRAME; - -#ifdef SUPPORT_SYSRQ - port->sysrq = 0; -#endif + else if (status & UARTDR_OVERR) + flg = TTY_OVERRUN; } if (uart_handle_sysrq_char(port, ch)) - goto ignore_char; + continue; - /* - * CHECK: does overrun affect the current character? - * ASSUMPTION: it does not. - */ - uart_insert_char(port, ch, UARTDR_OVERR, ch, flg); + if (status & port->ignore_status_mask) + continue; - ignore_char: - status = clps_readl(SYSFLG(port)); + uart_insert_char(port, status, UARTDR_OVERR, ch, flg); } + tty_flip_buffer_push(tty); + + tty_kref_put(tty); + return IRQ_HANDLED; } -static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id) +static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id) { struct uart_port *port = dev_id; + struct clps711x_port *s = dev_get_drvdata(port->dev); struct circ_buf *xmit = &port->state->xmit; - int count; if (port->x_char) { - clps_writel(port->x_char, UARTDR(port)); + clps_writew(port->x_char, UARTDR(port)); port->icount.tx++; port->x_char = 0; return IRQ_HANDLED; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) - goto disable_tx_irq; + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + disable_irq_nosync(TX_IRQ(port)); + s->tx_enabled[port->line] = 0; + return IRQ_HANDLED; + } - count = port->fifosize >> 1; - do { - clps_writel(xmit->buf[xmit->tail], UARTDR(port)); + while (!uart_circ_empty(xmit)) { + clps_writew(xmit->buf[xmit->tail], UARTDR(port)); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; - if (uart_circ_empty(xmit)) + if (clps_readl(SYSFLG(port) & SYSFLG_UTXFF)) break; - } while (--count > 0); + } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) { - disable_tx_irq: - disable_irq_nosync(TX_IRQ(port)); - tx_enabled(port) = 0; - } - return IRQ_HANDLED; } -static unsigned int clps711xuart_tx_empty(struct uart_port *port) +static unsigned int uart_clps711x_tx_empty(struct uart_port *port) { - unsigned int status = clps_readl(SYSFLG(port)); - return status & SYSFLG_UBUSY ? 0 : TIOCSER_TEMT; + return (clps_readl(SYSFLG(port) & SYSFLG_UBUSY)) ? 0 : TIOCSER_TEMT; } -static unsigned int clps711xuart_get_mctrl(struct uart_port *port) +static unsigned int uart_clps711x_get_mctrl(struct uart_port *port) { - unsigned int port_addr; - unsigned int result = 0; - unsigned int status; + unsigned int status, result = 0; - port_addr = SYSFLG(port); - if (port_addr == SYSFLG1) { + if (port->line == 0) { status = clps_readl(SYSFLG1); if (status & SYSFLG1_DCD) result |= TIOCM_CAR; @@ -199,104 +187,86 @@ static unsigned int clps711xuart_get_mctrl(struct uart_port *port) result |= TIOCM_DSR; if (status & SYSFLG1_CTS) result |= TIOCM_CTS; - } + } else + result = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; return result; } -static void -clps711xuart_set_mctrl_null(struct uart_port *port, unsigned int mctrl) +static void uart_clps711x_set_mctrl(struct uart_port *port, unsigned int mctrl) { + /* Do nothing */ } -static void clps711xuart_break_ctl(struct uart_port *port, int break_state) +static void uart_clps711x_break_ctl(struct uart_port *port, int break_state) { unsigned long flags; unsigned int ubrlcr; spin_lock_irqsave(&port->lock, flags); + ubrlcr = clps_readl(UBRLCR(port)); - if (break_state == -1) + if (break_state) ubrlcr |= UBRLCR_BREAK; else ubrlcr &= ~UBRLCR_BREAK; clps_writel(ubrlcr, UBRLCR(port)); + spin_unlock_irqrestore(&port->lock, flags); } -static int clps711xuart_startup(struct uart_port *port) +static int uart_clps711x_startup(struct uart_port *port) { - unsigned int syscon; - int retval; - - tx_enabled(port) = 1; - - /* - * Allocate the IRQs - */ - retval = request_irq(TX_IRQ(port), clps711xuart_int_tx, 0, - "clps711xuart_tx", port); - if (retval) - return retval; - - retval = request_irq(RX_IRQ(port), clps711xuart_int_rx, 0, - "clps711xuart_rx", port); - if (retval) { - free_irq(TX_IRQ(port), port); - return retval; + struct clps711x_port *s = dev_get_drvdata(port->dev); + int ret; + + s->tx_enabled[port->line] = 1; + /* Allocate the IRQs */ + ret = devm_request_irq(port->dev, TX_IRQ(port), uart_clps711x_int_tx, + 0, UART_CLPS711X_NAME " TX", port); + if (ret) + return ret; + + ret = devm_request_irq(port->dev, RX_IRQ(port), uart_clps711x_int_rx, + 0, UART_CLPS711X_NAME " RX", port); + if (ret) { + devm_free_irq(port->dev, TX_IRQ(port), port); + return ret; } - /* - * enable the port - */ - syscon = clps_readl(SYSCON(port)); - syscon |= SYSCON_UARTEN; - clps_writel(syscon, SYSCON(port)); + /* Disable break */ + clps_writel(clps_readl(UBRLCR(port)) & ~UBRLCR_BREAK, UBRLCR(port)); + + /* Enable the port */ + clps_writel(clps_readl(SYSCON(port)) | SYSCON_UARTEN, SYSCON(port)); return 0; } -static void clps711xuart_shutdown(struct uart_port *port) +static void uart_clps711x_shutdown(struct uart_port *port) { - unsigned int ubrlcr, syscon; + /* Free the interrupts */ + devm_free_irq(port->dev, TX_IRQ(port), port); + devm_free_irq(port->dev, RX_IRQ(port), port); - /* - * Free the interrupt - */ - free_irq(TX_IRQ(port), port); /* TX interrupt */ - free_irq(RX_IRQ(port), port); /* RX interrupt */ - - /* - * disable the port - */ - syscon = clps_readl(SYSCON(port)); - syscon &= ~SYSCON_UARTEN; - clps_writel(syscon, SYSCON(port)); - - /* - * disable break condition and fifos - */ - ubrlcr = clps_readl(UBRLCR(port)); - ubrlcr &= ~(UBRLCR_FIFOEN | UBRLCR_BREAK); - clps_writel(ubrlcr, UBRLCR(port)); + /* Disable the port */ + clps_writel(clps_readl(SYSCON(port)) & ~SYSCON_UARTEN, SYSCON(port)); } -static void -clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) +static void uart_clps711x_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) { unsigned int ubrlcr, baud, quot; unsigned long flags; - /* - * We don't implement CREAD. - */ - termios->c_cflag |= CREAD; + /* Mask termios capabilities we don't support */ + termios->c_cflag &= ~CMSPAR; + termios->c_iflag &= ~(BRKINT | IGNBRK); - /* - * Ask the core to calculate the divisor for us. - */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + /* Ask the core to calculate the divisor for us */ + baud = uart_get_baud_rate(port, termios, old, port->uartclk / 4096, + port->uartclk / 16); quot = uart_get_divisor(port, baud); switch (termios->c_cflag & CSIZE) { @@ -309,160 +279,117 @@ clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios, case CS7: ubrlcr = UBRLCR_WRDLEN7; break; - default: // CS8 + case CS8: + default: ubrlcr = UBRLCR_WRDLEN8; break; } + if (termios->c_cflag & CSTOPB) ubrlcr |= UBRLCR_XSTOP; + if (termios->c_cflag & PARENB) { ubrlcr |= UBRLCR_PRTEN; if (!(termios->c_cflag & PARODD)) ubrlcr |= UBRLCR_EVENPRT; } - if (port->fifosize > 1) - ubrlcr |= UBRLCR_FIFOEN; - spin_lock_irqsave(&port->lock, flags); + /* Enable FIFO */ + ubrlcr |= UBRLCR_FIFOEN; - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); + spin_lock_irqsave(&port->lock, flags); + /* Set read status mask */ port->read_status_mask = UARTDR_OVERR; if (termios->c_iflag & INPCK) port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR; - /* - * Characters to ignore - */ + /* Set status ignore mask */ port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= UARTDR_FRMERR | UARTDR_PARERR; - if (termios->c_iflag & IGNBRK) { - /* - * If we're ignoring parity and break indicators, - * ignore overruns to (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= UARTDR_OVERR; - } + if (!(termios->c_cflag & CREAD)) + port->ignore_status_mask |= UARTDR_OVERR | UARTDR_PARERR | + UARTDR_FRMERR; - quot -= 1; + uart_update_timeout(port, termios->c_cflag, baud); - clps_writel(ubrlcr | quot, UBRLCR(port)); + clps_writel(ubrlcr | (quot - 1), UBRLCR(port)); spin_unlock_irqrestore(&port->lock, flags); } -static const char *clps711xuart_type(struct uart_port *port) +static const char *uart_clps711x_type(struct uart_port *port) { - return port->type == PORT_CLPS711X ? "CLPS711x" : NULL; + return (port->type == PORT_CLPS711X) ? "CLPS711X" : NULL; } -/* - * Configure/autoconfigure the port. - */ -static void clps711xuart_config_port(struct uart_port *port, int flags) +static void uart_clps711x_config_port(struct uart_port *port, int flags) { if (flags & UART_CONFIG_TYPE) port->type = PORT_CLPS711X; } -static void clps711xuart_release_port(struct uart_port *port) +static void uart_clps711x_release_port(struct uart_port *port) { + /* Do nothing */ } -static int clps711xuart_request_port(struct uart_port *port) +static int uart_clps711x_request_port(struct uart_port *port) { + /* Do nothing */ return 0; } -static struct uart_ops clps711x_pops = { - .tx_empty = clps711xuart_tx_empty, - .set_mctrl = clps711xuart_set_mctrl_null, - .get_mctrl = clps711xuart_get_mctrl, - .stop_tx = clps711xuart_stop_tx, - .start_tx = clps711xuart_start_tx, - .stop_rx = clps711xuart_stop_rx, - .enable_ms = clps711xuart_enable_ms, - .break_ctl = clps711xuart_break_ctl, - .startup = clps711xuart_startup, - .shutdown = clps711xuart_shutdown, - .set_termios = clps711xuart_set_termios, - .type = clps711xuart_type, - .config_port = clps711xuart_config_port, - .release_port = clps711xuart_release_port, - .request_port = clps711xuart_request_port, -}; - -static struct uart_port clps711x_ports[UART_NR] = { - { - .iobase = SYSCON1, - .irq = IRQ_UTXINT1, /* IRQ_URXINT1, IRQ_UMSINT */ - .uartclk = 3686400, - .fifosize = 16, - .ops = &clps711x_pops, - .line = 0, - .flags = UPF_BOOT_AUTOCONF, - }, - { - .iobase = SYSCON2, - .irq = IRQ_UTXINT2, /* IRQ_URXINT2 */ - .uartclk = 3686400, - .fifosize = 16, - .ops = &clps711x_pops, - .line = 1, - .flags = UPF_BOOT_AUTOCONF, - } +static const struct uart_ops uart_clps711x_ops = { + .tx_empty = uart_clps711x_tx_empty, + .set_mctrl = uart_clps711x_set_mctrl, + .get_mctrl = uart_clps711x_get_mctrl, + .stop_tx = uart_clps711x_stop_tx, + .start_tx = uart_clps711x_start_tx, + .stop_rx = uart_clps711x_stop_rx, + .enable_ms = uart_clps711x_enable_ms, + .break_ctl = uart_clps711x_break_ctl, + .startup = uart_clps711x_startup, + .shutdown = uart_clps711x_shutdown, + .set_termios = uart_clps711x_set_termios, + .type = uart_clps711x_type, + .config_port = uart_clps711x_config_port, + .release_port = uart_clps711x_release_port, + .request_port = uart_clps711x_request_port, }; #ifdef CONFIG_SERIAL_CLPS711X_CONSOLE -static void clps711xuart_console_putchar(struct uart_port *port, int ch) +static void uart_clps711x_console_putchar(struct uart_port *port, int ch) { while (clps_readl(SYSFLG(port)) & SYSFLG_UTXFF) barrier(); - clps_writel(ch, UARTDR(port)); + + clps_writew(ch, UARTDR(port)); } -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - * - * The console_lock must be held when we get here. - * - * Note that this is called with interrupts already disabled - */ -static void -clps711xuart_console_write(struct console *co, const char *s, - unsigned int count) +static void uart_clps711x_console_write(struct console *co, const char *c, + unsigned n) { - struct uart_port *port = clps711x_ports + co->index; - unsigned int status, syscon; + struct clps711x_port *s = (struct clps711x_port *)co->data; + struct uart_port *port = &s->port[co->index]; + u32 syscon; - /* - * Ensure that the port is enabled. - */ + /* Ensure that the port is enabled */ syscon = clps_readl(SYSCON(port)); clps_writel(syscon | SYSCON_UARTEN, SYSCON(port)); - uart_console_write(port, s, count, clps711xuart_console_putchar); + uart_console_write(port, c, n, uart_clps711x_console_putchar); - /* - * Finally, wait for transmitter to become empty - * and restore the uart state. - */ - do { - status = clps_readl(SYSFLG(port)); - } while (status & SYSFLG_UBUSY); + /* Wait for transmitter to become empty */ + while (clps_readl(SYSFLG(port)) & SYSFLG_UBUSY) + barrier(); + /* Restore the uart state */ clps_writel(syscon, SYSCON(port)); } -static void __init -clps711xuart_console_get_options(struct uart_port *port, int *baud, - int *parity, int *bits) +static void uart_clps711x_console_get_options(struct uart_port *port, + int *baud, int *parity, + int *bits) { if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) { unsigned int ubrlcr, quot; @@ -487,92 +414,124 @@ clps711xuart_console_get_options(struct uart_port *port, int *baud, } } -static int __init clps711xuart_console_setup(struct console *co, char *options) +static int uart_clps711x_console_setup(struct console *co, char *options) { - struct uart_port *port; - int baud = 38400; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - /* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - port = uart_get_console(clps711x_ports, UART_NR, co); + int baud = 38400, bits = 8, parity = 'n', flow = 'n'; + struct clps711x_port *s = (struct clps711x_port *)co->data; + struct uart_port *port = &s->port[(co->index > 0) ? co->index : 0]; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else - clps711xuart_console_get_options(port, &baud, &parity, &bits); + uart_clps711x_console_get_options(port, &baud, &parity, &bits); return uart_set_options(port, co, baud, parity, bits, flow); } +#endif -static struct uart_driver clps711x_reg; -static struct console clps711x_console = { - .name = "ttyCL", - .write = clps711xuart_console_write, - .device = uart_console_device, - .setup = clps711xuart_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &clps711x_reg, -}; - -static int __init clps711xuart_console_init(void) +static int __devinit uart_clps711x_probe(struct platform_device *pdev) { - register_console(&clps711x_console); - return 0; -} -console_initcall(clps711xuart_console_init); + struct clps711x_port *s; + int ret, i; + + s = devm_kzalloc(&pdev->dev, sizeof(struct clps711x_port), GFP_KERNEL); + if (!s) { + dev_err(&pdev->dev, "Error allocating port structure\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, s); -#define CLPS711X_CONSOLE &clps711x_console -#else -#define CLPS711X_CONSOLE NULL + s->uart_clk = devm_clk_get(&pdev->dev, "uart"); + if (IS_ERR(s->uart_clk)) { + dev_err(&pdev->dev, "Can't get UART clocks\n"); + ret = PTR_ERR(s->uart_clk); + goto err_out; + } + + s->uart.owner = THIS_MODULE; + s->uart.dev_name = "ttyCL"; + s->uart.major = UART_CLPS711X_MAJOR; + s->uart.minor = UART_CLPS711X_MINOR; + s->uart.nr = UART_CLPS711X_NR; +#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE + s->uart.cons = &s->console; + s->uart.cons->device = uart_console_device; + s->uart.cons->write = uart_clps711x_console_write; + s->uart.cons->setup = uart_clps711x_console_setup; + s->uart.cons->flags = CON_PRINTBUFFER; + s->uart.cons->index = -1; + s->uart.cons->data = s; + strcpy(s->uart.cons->name, "ttyCL"); #endif + ret = uart_register_driver(&s->uart); + if (ret) { + dev_err(&pdev->dev, "Registering UART driver failed\n"); + devm_clk_put(&pdev->dev, s->uart_clk); + goto err_out; + } -static struct uart_driver clps711x_reg = { - .driver_name = "ttyCL", - .dev_name = "ttyCL", - .major = SERIAL_CLPS711X_MAJOR, - .minor = SERIAL_CLPS711X_MINOR, - .nr = UART_NR, + for (i = 0; i < UART_CLPS711X_NR; i++) { + s->port[i].line = i; + s->port[i].dev = &pdev->dev; + s->port[i].irq = TX_IRQ(&s->port[i]); + s->port[i].iobase = SYSCON(&s->port[i]); + s->port[i].type = PORT_CLPS711X; + s->port[i].fifosize = 16; + s->port[i].flags = UPF_SKIP_TEST | UPF_FIXED_TYPE; + s->port[i].uartclk = clk_get_rate(s->uart_clk); + s->port[i].ops = &uart_clps711x_ops; + WARN_ON(uart_add_one_port(&s->uart, &s->port[i])); + } - .cons = CLPS711X_CONSOLE, -}; + return 0; -static int __init clps711xuart_init(void) -{ - int ret, i; +err_out: + platform_set_drvdata(pdev, NULL); - printk(KERN_INFO "Serial: CLPS711x driver\n"); + return ret; +} - ret = uart_register_driver(&clps711x_reg); - if (ret) - return ret; +static int __devexit uart_clps711x_remove(struct platform_device *pdev) +{ + struct clps711x_port *s = platform_get_drvdata(pdev); + int i; - for (i = 0; i < UART_NR; i++) - uart_add_one_port(&clps711x_reg, &clps711x_ports[i]); + for (i = 0; i < UART_CLPS711X_NR; i++) + uart_remove_one_port(&s->uart, &s->port[i]); + + devm_clk_put(&pdev->dev, s->uart_clk); + uart_unregister_driver(&s->uart); + platform_set_drvdata(pdev, NULL); return 0; } -static void __exit clps711xuart_exit(void) -{ - int i; +static struct platform_driver clps711x_uart_driver = { + .driver = { + .name = UART_CLPS711X_NAME, + .owner = THIS_MODULE, + }, + .probe = uart_clps711x_probe, + .remove = __devexit_p(uart_clps711x_remove), +}; +module_platform_driver(clps711x_uart_driver); - for (i = 0; i < UART_NR; i++) - uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]); +static struct platform_device clps711x_uart_device = { + .name = UART_CLPS711X_NAME, +}; - uart_unregister_driver(&clps711x_reg); +static int __init uart_clps711x_init(void) +{ + return platform_device_register(&clps711x_uart_device); } +module_init(uart_clps711x_init); -module_init(clps711xuart_init); -module_exit(clps711xuart_exit); +static void __exit uart_clps711x_exit(void) +{ + platform_device_unregister(&clps711x_uart_device); +} +module_exit(uart_clps711x_exit); MODULE_AUTHOR("Deep Blue Solutions Ltd"); -MODULE_DESCRIPTION("CLPS-711x generic serial driver"); +MODULE_DESCRIPTION("CLPS711X serial driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR); diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 5b9bc19..91125bb 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -64,8 +64,8 @@ #include "ifx6x60.h" #define IFX_SPI_MORE_MASK 0x10 -#define IFX_SPI_MORE_BIT 12 /* bit position in u16 */ -#define IFX_SPI_CTS_BIT 13 /* bit position in u16 */ +#define IFX_SPI_MORE_BIT 4 /* bit position in u8 */ +#define IFX_SPI_CTS_BIT 6 /* bit position in u8 */ #define IFX_SPI_MODE SPI_MODE_1 #define IFX_SPI_TTY_ID 0 #define IFX_SPI_TIMEOUT_SEC 2 @@ -152,26 +152,67 @@ ifx_spi_power_state_clear(struct ifx_spi_device *ifx_dev, unsigned char val) } /** - * swap_buf + * swap_buf_8 * @buf: our buffer * @len : number of bytes (not words) in the buffer * @end: end of buffer * * Swap the contents of a buffer into big endian format */ -static inline void swap_buf(u16 *buf, int len, void *end) +static inline void swap_buf_8(unsigned char *buf, int len, void *end) +{ + /* don't swap buffer if SPI word width is 8 bits */ + return; +} + +/** + * swap_buf_16 + * @buf: our buffer + * @len : number of bytes (not words) in the buffer + * @end: end of buffer + * + * Swap the contents of a buffer into big endian format + */ +static inline void swap_buf_16(unsigned char *buf, int len, void *end) { int n; + u16 *buf_16 = (u16 *)buf; len = ((len + 1) >> 1); - if ((void *)&buf[len] > end) { - pr_err("swap_buf: swap exceeds boundary (%p > %p)!", - &buf[len], end); + if ((void *)&buf_16[len] > end) { + pr_err("swap_buf_16: swap exceeds boundary (%p > %p)!", + &buf_16[len], end); return; } for (n = 0; n < len; n++) { - *buf = cpu_to_be16(*buf); - buf++; + *buf_16 = cpu_to_be16(*buf_16); + buf_16++; + } +} + +/** + * swap_buf_32 + * @buf: our buffer + * @len : number of bytes (not words) in the buffer + * @end: end of buffer + * + * Swap the contents of a buffer into big endian format + */ +static inline void swap_buf_32(unsigned char *buf, int len, void *end) +{ + int n; + + u32 *buf_32 = (u32 *)buf; + len = (len + 3) >> 2; + + if ((void *)&buf_32[len] > end) { + pr_err("swap_buf_32: swap exceeds boundary (%p > %p)!\n", + &buf_32[len], end); + return; + } + for (n = 0; n < len; n++) { + *buf_32 = cpu_to_be32(*buf_32); + buf_32++; } } @@ -190,9 +231,7 @@ static void mrdy_assert(struct ifx_spi_device *ifx_dev) if (!val) { if (!test_and_set_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags)) { - ifx_dev->spi_timer.expires = - jiffies + IFX_SPI_TIMEOUT_SEC*HZ; - add_timer(&ifx_dev->spi_timer); + mod_timer(&ifx_dev->spi_timer,jiffies + IFX_SPI_TIMEOUT_SEC*HZ); } } @@ -449,7 +488,7 @@ static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev) tx_count-IFX_SPI_HEADER_OVERHEAD, ifx_dev->spi_more); /* swap actual data in the buffer */ - swap_buf((u16 *)(ifx_dev->tx_buffer), tx_count, + ifx_dev->swap_buf((ifx_dev->tx_buffer), tx_count, &ifx_dev->tx_buffer[IFX_SPI_TRANSFER_SIZE]); return tx_count; } @@ -469,9 +508,16 @@ static int ifx_spi_write(struct tty_struct *tty, const unsigned char *buf, { struct ifx_spi_device *ifx_dev = tty->driver_data; unsigned char *tmp_buf = (unsigned char *)buf; - int tx_count = kfifo_in_locked(&ifx_dev->tx_fifo, tmp_buf, count, - &ifx_dev->fifo_lock); - mrdy_assert(ifx_dev); + unsigned long flags; + bool is_fifo_empty; + + spin_lock_irqsave(&ifx_dev->fifo_lock, flags); + is_fifo_empty = kfifo_is_empty(&ifx_dev->tx_fifo); + int tx_count = kfifo_in(&ifx_dev->tx_fifo, tmp_buf, count); + spin_unlock_irqrestore(&ifx_dev->fifo_lock, flags); + if (is_fifo_empty) + mrdy_assert(ifx_dev); + return tx_count; } @@ -530,12 +576,19 @@ static int ifx_port_activate(struct tty_port *port, struct tty_struct *tty) /* clear any old data; can't do this in 'close' */ kfifo_reset(&ifx_dev->tx_fifo); + /* clear any flag which may be set in port shutdown procedure */ + clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags); + clear_bit(IFX_SPI_STATE_IO_READY, &ifx_dev->flags); + /* put port data into this tty */ tty->driver_data = ifx_dev; /* allows flip string push from int context */ tty->low_latency = 1; + /* set flag to allows data transfer */ + set_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags); + return 0; } @@ -551,6 +604,7 @@ static void ifx_port_shutdown(struct tty_port *port) struct ifx_spi_device *ifx_dev = container_of(port, struct ifx_spi_device, tty_port); + clear_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags); mrdy_set_low(ifx_dev); clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); tasklet_kill(&ifx_dev->io_work_tasklet); @@ -617,7 +671,7 @@ static void ifx_spi_complete(void *ctx) if (!ifx_dev->spi_msg.status) { /* check header validity, get comm flags */ - swap_buf((u16 *)ifx_dev->rx_buffer, IFX_SPI_HEADER_OVERHEAD, + ifx_dev->swap_buf(ifx_dev->rx_buffer, IFX_SPI_HEADER_OVERHEAD, &ifx_dev->rx_buffer[IFX_SPI_HEADER_OVERHEAD]); decode_result = ifx_spi_decode_spi_header(ifx_dev->rx_buffer, &length, &more, &cts); @@ -636,7 +690,8 @@ static void ifx_spi_complete(void *ctx) actual_length = min((unsigned int)length, ifx_dev->spi_msg.actual_length); - swap_buf((u16 *)(ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD), + ifx_dev->swap_buf( + (ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD), actual_length, &ifx_dev->rx_buffer[IFX_SPI_TRANSFER_SIZE]); ifx_spi_insert_flip_string( @@ -705,7 +760,8 @@ static void ifx_spi_io(unsigned long data) int retval; struct ifx_spi_device *ifx_dev = (struct ifx_spi_device *) data; - if (!test_and_set_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags)) { + if (!test_and_set_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags) && + test_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags)) { if (ifx_dev->gpio.unack_srdy_int_nb > 0) ifx_dev->gpio.unack_srdy_int_nb--; @@ -773,6 +829,7 @@ static void ifx_spi_free_port(struct ifx_spi_device *ifx_dev) { if (ifx_dev->tty_dev) tty_unregister_device(tty_drv, ifx_dev->minor); + tty_port_destroy(&ifx_dev->tty_port); kfifo_free(&ifx_dev->tx_fifo); } @@ -806,10 +863,12 @@ static int ifx_spi_create_port(struct ifx_spi_device *ifx_dev) dev_dbg(&ifx_dev->spi_dev->dev, "%s: registering tty device failed", __func__); ret = PTR_ERR(ifx_dev->tty_dev); - goto error_ret; + goto error_port; } return 0; +error_port: + tty_port_destroy(pport); error_ret: ifx_spi_free_port(ifx_dev); return ret; @@ -826,7 +885,7 @@ error_ret: static void ifx_spi_handle_srdy(struct ifx_spi_device *ifx_dev) { if (test_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags)) { - del_timer_sync(&ifx_dev->spi_timer); + del_timer(&ifx_dev->spi_timer); clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); } @@ -1001,6 +1060,14 @@ static int ifx_spi_spi_probe(struct spi_device *spi) return -ENODEV; } + /* init swap_buf function according to word width configuration */ + if (spi->bits_per_word == 32) + ifx_dev->swap_buf = swap_buf_32; + else if (spi->bits_per_word == 16) + ifx_dev->swap_buf = swap_buf_16; + else + ifx_dev->swap_buf = swap_buf_8; + /* ensure SPI protocol flags are initialized to enable transfer */ ifx_dev->spi_more = 0; ifx_dev->spi_slave_cts = 0; diff --git a/drivers/tty/serial/ifx6x60.h b/drivers/tty/serial/ifx6x60.h index e8464ba..4fbddc2 100644 --- a/drivers/tty/serial/ifx6x60.h +++ b/drivers/tty/serial/ifx6x60.h @@ -41,6 +41,7 @@ #define IFX_SPI_STATE_IO_IN_PROGRESS 1 #define IFX_SPI_STATE_IO_READY 2 #define IFX_SPI_STATE_TIMER_PENDING 3 +#define IFX_SPI_STATE_IO_AVAILABLE 4 /* flow control bitfields */ #define IFX_SPI_DCD 0 @@ -124,6 +125,7 @@ struct ifx_spi_device { #define MR_INPROGRESS 1 #define MR_COMPLETE 2 wait_queue_head_t mdm_reset_wait; + void (*swap_buf)(unsigned char *buf, int len, void *end); }; #endif /* _IFX6X60_H */ diff --git a/drivers/tty/serial/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h index 529bec6..844d5e4 100644 --- a/drivers/tty/serial/jsm/jsm.h +++ b/drivers/tty/serial/jsm/jsm.h @@ -57,9 +57,11 @@ enum { DBG_CARR = 0x10000, }; -#define jsm_printk(nlevel, klevel, pdev, fmt, args...) \ - if ((DBG_##nlevel & jsm_debug)) \ - dev_printk(KERN_##klevel, pdev->dev, fmt, ## args) +#define jsm_dbg(nlevel, pdev, fmt, ...) \ +do { \ + if (DBG_##nlevel & jsm_debug) \ + dev_dbg(pdev->dev, fmt, ##__VA_ARGS__); \ +} while (0) #define MAXLINES 256 #define MAXPORTS 8 diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c index 5ab3c3b..8e05ce9 100644 --- a/drivers/tty/serial/jsm/jsm_driver.c +++ b/drivers/tty/serial/jsm/jsm_driver.c @@ -107,8 +107,7 @@ static int __devinit jsm_probe_one(struct pci_dev *pdev, const struct pci_device brd->irq = pdev->irq; - jsm_printk(INIT, INFO, &brd->pci_dev, - "jsm_found_board - NEO adapter\n"); + jsm_dbg(INIT, &brd->pci_dev, "jsm_found_board - NEO adapter\n"); /* get the PCI Base Address Registers */ brd->membase = pci_resource_start(pdev, 0); diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c index 81dfafa..dfaf488 100644 --- a/drivers/tty/serial/jsm/jsm_neo.c +++ b/drivers/tty/serial/jsm/jsm_neo.c @@ -52,7 +52,7 @@ static void neo_set_cts_flow_control(struct jsm_channel *ch) ier = readb(&ch->ch_neo_uart->ier); efr = readb(&ch->ch_neo_uart->efr); - jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting CTSFLOW\n"); + jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Setting CTSFLOW\n"); /* Turn on auto CTS flow control */ ier |= (UART_17158_IER_CTSDSR); @@ -83,7 +83,7 @@ static void neo_set_rts_flow_control(struct jsm_channel *ch) ier = readb(&ch->ch_neo_uart->ier); efr = readb(&ch->ch_neo_uart->efr); - jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting RTSFLOW\n"); + jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Setting RTSFLOW\n"); /* Turn on auto RTS flow control */ ier |= (UART_17158_IER_RTSDTR); @@ -123,7 +123,7 @@ static void neo_set_ixon_flow_control(struct jsm_channel *ch) ier = readb(&ch->ch_neo_uart->ier); efr = readb(&ch->ch_neo_uart->efr); - jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting IXON FLOW\n"); + jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Setting IXON FLOW\n"); /* Turn off auto CTS flow control */ ier &= ~(UART_17158_IER_CTSDSR); @@ -160,7 +160,7 @@ static void neo_set_ixoff_flow_control(struct jsm_channel *ch) ier = readb(&ch->ch_neo_uart->ier); efr = readb(&ch->ch_neo_uart->efr); - jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting IXOFF FLOW\n"); + jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Setting IXOFF FLOW\n"); /* Turn off auto RTS flow control */ ier &= ~(UART_17158_IER_RTSDTR); @@ -198,7 +198,7 @@ static void neo_set_no_input_flow_control(struct jsm_channel *ch) ier = readb(&ch->ch_neo_uart->ier); efr = readb(&ch->ch_neo_uart->efr); - jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Unsetting Input FLOW\n"); + jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Unsetting Input FLOW\n"); /* Turn off auto RTS flow control */ ier &= ~(UART_17158_IER_RTSDTR); @@ -237,7 +237,7 @@ static void neo_set_no_output_flow_control(struct jsm_channel *ch) ier = readb(&ch->ch_neo_uart->ier); efr = readb(&ch->ch_neo_uart->efr); - jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Unsetting Output FLOW\n"); + jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Unsetting Output FLOW\n"); /* Turn off auto CTS flow control */ ier &= ~(UART_17158_IER_CTSDSR); @@ -276,7 +276,7 @@ static inline void neo_set_new_start_stop_chars(struct jsm_channel *ch) if (ch->ch_c_cflag & CRTSCTS) return; - jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "start\n"); + jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "start\n"); /* Tell UART what start/stop chars it should be looking for */ writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1); @@ -455,7 +455,7 @@ static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch) * I hope thats okay with everyone? Yes? Good. */ while (qleft < 1) { - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, + jsm_dbg(READ, &ch->ch_bd->pci_dev, "Queue full, dropping DATA:%x LSR:%x\n", ch->ch_rqueue[tail], ch->ch_equeue[tail]); @@ -467,8 +467,8 @@ static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch) memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, 1); ch->ch_equeue[head] = (u8) linestatus; - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, - "DATA/LSR pair: %x %x\n", ch->ch_rqueue[head], ch->ch_equeue[head]); + jsm_dbg(READ, &ch->ch_bd->pci_dev, "DATA/LSR pair: %x %x\n", + ch->ch_rqueue[head], ch->ch_equeue[head]); /* Ditch any remaining linestatus value. */ linestatus = 0; @@ -521,8 +521,8 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch) ch->ch_cached_lsr &= ~(UART_LSR_THRE); writeb(circ->buf[circ->tail], &ch->ch_neo_uart->txrx); - jsm_printk(WRITE, INFO, &ch->ch_bd->pci_dev, - "Tx data: %x\n", circ->buf[circ->tail]); + jsm_dbg(WRITE, &ch->ch_bd->pci_dev, + "Tx data: %x\n", circ->buf[circ->tail]); circ->tail = (circ->tail + 1) & (UART_XMIT_SIZE - 1); ch->ch_txcount++; } @@ -575,8 +575,9 @@ static void neo_parse_modem(struct jsm_channel *ch, u8 signals) { u8 msignals = signals; - jsm_printk(MSIGS, INFO, &ch->ch_bd->pci_dev, - "neo_parse_modem: port: %d msignals: %x\n", ch->ch_portnum, msignals); + jsm_dbg(MSIGS, &ch->ch_bd->pci_dev, + "neo_parse_modem: port: %d msignals: %x\n", + ch->ch_portnum, msignals); /* Scrub off lower bits. They signify delta's, which I don't care about */ /* Keep DDCD and DDSR though */ @@ -606,8 +607,8 @@ static void neo_parse_modem(struct jsm_channel *ch, u8 signals) else ch->ch_mistat &= ~UART_MSR_CTS; - jsm_printk(MSIGS, INFO, &ch->ch_bd->pci_dev, - "Port: %d DTR: %d RTS: %d CTS: %d DSR: %d " "RI: %d CD: %d\n", + jsm_dbg(MSIGS, &ch->ch_bd->pci_dev, + "Port: %d DTR: %d RTS: %d CTS: %d DSR: %d " "RI: %d CD: %d\n", ch->ch_portnum, !!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_DTR), !!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_RTS), @@ -649,8 +650,8 @@ static void neo_flush_uart_write(struct jsm_channel *ch) /* Check to see if the UART feels it completely flushed the FIFO. */ tmp = readb(&ch->ch_neo_uart->isr_fcr); if (tmp & 4) { - jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, - "Still flushing TX UART... i: %d\n", i); + jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, + "Still flushing TX UART... i: %d\n", i); udelay(10); } else @@ -681,8 +682,8 @@ static void neo_flush_uart_read(struct jsm_channel *ch) /* Check to see if the UART feels it completely flushed the FIFO. */ tmp = readb(&ch->ch_neo_uart->isr_fcr); if (tmp & 2) { - jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, - "Still flushing RX UART... i: %d\n", i); + jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, + "Still flushing RX UART... i: %d\n", i); udelay(10); } else @@ -705,8 +706,9 @@ static void neo_clear_break(struct jsm_channel *ch, int force) writeb((temp & ~UART_LCR_SBC), &ch->ch_neo_uart->lcr); ch->ch_flags &= ~(CH_BREAK_SENDING); - jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, - "clear break Finishing UART_LCR_SBC! finished: %lx\n", jiffies); + jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, + "clear break Finishing UART_LCR_SBC! finished: %lx\n", + jiffies); /* flush write operation */ neo_pci_posting_flush(ch->ch_bd); @@ -748,8 +750,8 @@ static inline void neo_parse_isr(struct jsm_board *brd, u32 port) */ isr &= ~(UART_17158_IIR_FIFO_ENABLED); - jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, - "%s:%d isr: %x\n", __FILE__, __LINE__, isr); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d isr: %x\n", + __FILE__, __LINE__, isr); if (isr & (UART_17158_IIR_RDI_TIMEOUT | UART_IIR_RDI)) { /* Read data from uart -> queue */ @@ -772,8 +774,9 @@ static inline void neo_parse_isr(struct jsm_board *brd, u32 port) if (isr & UART_17158_IIR_XONXOFF) { cause = readb(&ch->ch_neo_uart->xoffchar1); - jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, - "Port %d. Got ISR_XONXOFF: cause:%x\n", port, cause); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, + "Port %d. Got ISR_XONXOFF: cause:%x\n", + port, cause); /* * Since the UART detected either an XON or @@ -786,17 +789,19 @@ static inline void neo_parse_isr(struct jsm_board *brd, u32 port) if (brd->channels[port]->ch_flags & CH_STOP) { ch->ch_flags &= ~(CH_STOP); } - jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, - "Port %d. XON detected in incoming data\n", port); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, + "Port %d. XON detected in incoming data\n", + port); } else if (cause == UART_17158_XOFF_DETECT) { if (!(brd->channels[port]->ch_flags & CH_STOP)) { ch->ch_flags |= CH_STOP; - jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, - "Setting CH_STOP\n"); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, + "Setting CH_STOP\n"); } - jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, - "Port: %d. XOFF detected in incoming data\n", port); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, + "Port: %d. XOFF detected in incoming data\n", + port); } spin_unlock_irqrestore(&ch->ch_lock, lock_flags); } @@ -825,8 +830,8 @@ static inline void neo_parse_isr(struct jsm_board *brd, u32 port) } /* Parse any modem signal changes */ - jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, - "MOD_STAT: sending to parse_modem_sigs\n"); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, + "MOD_STAT: sending to parse_modem_sigs\n"); neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr)); } } @@ -849,8 +854,8 @@ static inline void neo_parse_lsr(struct jsm_board *brd, u32 port) linestatus = readb(&ch->ch_neo_uart->lsr); - jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, - "%s:%d port: %d linestatus: %x\n", __FILE__, __LINE__, port, linestatus); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d port: %d linestatus: %x\n", + __FILE__, __LINE__, port, linestatus); ch->ch_cached_lsr |= linestatus; @@ -869,7 +874,7 @@ static inline void neo_parse_lsr(struct jsm_board *brd, u32 port) *to do the special RX+LSR read for this FIFO load. */ if (linestatus & UART_17158_RX_FIFO_DATA_ERROR) - jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, + jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d Port: %d Got an RX error, need to parse LSR\n", __FILE__, __LINE__, port); @@ -880,20 +885,21 @@ static inline void neo_parse_lsr(struct jsm_board *brd, u32 port) if (linestatus & UART_LSR_PE) { ch->ch_err_parity++; - jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, - "%s:%d Port: %d. PAR ERR!\n", __FILE__, __LINE__, port); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d Port: %d. PAR ERR!\n", + __FILE__, __LINE__, port); } if (linestatus & UART_LSR_FE) { ch->ch_err_frame++; - jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, - "%s:%d Port: %d. FRM ERR!\n", __FILE__, __LINE__, port); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d Port: %d. FRM ERR!\n", + __FILE__, __LINE__, port); } if (linestatus & UART_LSR_BI) { ch->ch_err_break++; - jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, - "%s:%d Port: %d. BRK INTR!\n", __FILE__, __LINE__, port); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, + "%s:%d Port: %d. BRK INTR!\n", + __FILE__, __LINE__, port); } if (linestatus & UART_LSR_OE) { @@ -904,8 +910,9 @@ static inline void neo_parse_lsr(struct jsm_board *brd, u32 port) * Probably we should eventually have an orun stat in our driver... */ ch->ch_err_overrun++; - jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, - "%s:%d Port: %d. Rx Overrun!\n", __FILE__, __LINE__, port); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, + "%s:%d Port: %d. Rx Overrun!\n", + __FILE__, __LINE__, port); } if (linestatus & UART_LSR_THRE) { @@ -1128,11 +1135,11 @@ static irqreturn_t neo_intr(int irq, void *voidbrd) */ uart_poll = readl(brd->re_map_membase + UART_17158_POLL_ADDR_OFFSET); - jsm_printk(INTR, INFO, &brd->pci_dev, - "%s:%d uart_poll: %x\n", __FILE__, __LINE__, uart_poll); + jsm_dbg(INTR, &brd->pci_dev, "%s:%d uart_poll: %x\n", + __FILE__, __LINE__, uart_poll); if (!uart_poll) { - jsm_printk(INTR, INFO, &brd->pci_dev, + jsm_dbg(INTR, &brd->pci_dev, "Kernel interrupted to me, but no pending interrupts...\n"); spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags); return IRQ_NONE; @@ -1158,15 +1165,15 @@ static irqreturn_t neo_intr(int irq, void *voidbrd) continue; } - jsm_printk(INTR, INFO, &brd->pci_dev, - "%s:%d port: %x type: %x\n", __FILE__, __LINE__, port, type); + jsm_dbg(INTR, &brd->pci_dev, "%s:%d port: %x type: %x\n", + __FILE__, __LINE__, port, type); /* Remove this port + type from uart_poll */ uart_poll &= ~(jsm_offset_table[port]); if (!type) { /* If no type, just ignore it, and move onto next port */ - jsm_printk(INTR, ERR, &brd->pci_dev, + jsm_dbg(INTR, &brd->pci_dev, "Interrupt with no type! port: %d\n", port); continue; } @@ -1231,15 +1238,16 @@ static irqreturn_t neo_intr(int irq, void *voidbrd) * these once and awhile. * Its harmless, just ignore it and move on. */ - jsm_printk(INTR, ERR, &brd->pci_dev, - "%s:%d Unknown Interrupt type: %x\n", __FILE__, __LINE__, type); + jsm_dbg(INTR, &brd->pci_dev, + "%s:%d Unknown Interrupt type: %x\n", + __FILE__, __LINE__, type); continue; } } spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags); - jsm_printk(INTR, INFO, &brd->pci_dev, "finish.\n"); + jsm_dbg(INTR, &brd->pci_dev, "finish\n"); return IRQ_HANDLED; } diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index 7139796..7d2c1f3 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -43,7 +43,7 @@ static inline int jsm_get_mstat(struct jsm_channel *ch) unsigned char mstat; unsigned result; - jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "start\n"); + jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "start\n"); mstat = (ch->ch_mostat | ch->ch_mistat); @@ -62,7 +62,7 @@ static inline int jsm_get_mstat(struct jsm_channel *ch) if (mstat & UART_MSR_DCD) result |= TIOCM_CD; - jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n"); + jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "finish\n"); return result; } @@ -79,14 +79,14 @@ static unsigned int jsm_tty_get_mctrl(struct uart_port *port) int result; struct jsm_channel *channel = (struct jsm_channel *)port; - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n"); + jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); result = jsm_get_mstat(channel); if (result < 0) return -ENXIO; - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n"); + jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n"); return result; } @@ -100,7 +100,7 @@ static void jsm_tty_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct jsm_channel *channel = (struct jsm_channel *)port; - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n"); + jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); if (mctrl & TIOCM_RTS) channel->ch_mostat |= UART_MCR_RTS; @@ -114,7 +114,7 @@ static void jsm_tty_set_mctrl(struct uart_port *port, unsigned int mctrl) channel->ch_bd->bd_ops->assert_modem_signals(channel); - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n"); + jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n"); udelay(10); } @@ -135,23 +135,23 @@ static void jsm_tty_start_tx(struct uart_port *port) { struct jsm_channel *channel = (struct jsm_channel *)port; - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n"); + jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); channel->ch_flags &= ~(CH_STOP); jsm_tty_write(port); - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n"); + jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n"); } static void jsm_tty_stop_tx(struct uart_port *port) { struct jsm_channel *channel = (struct jsm_channel *)port; - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n"); + jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); channel->ch_flags |= (CH_STOP); - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n"); + jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n"); } static void jsm_tty_send_xchar(struct uart_port *port, char ch) @@ -216,16 +216,16 @@ static int jsm_tty_open(struct uart_port *port) if (!channel->ch_rqueue) { channel->ch_rqueue = kzalloc(RQUEUESIZE, GFP_KERNEL); if (!channel->ch_rqueue) { - jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev, - "unable to allocate read queue buf"); + jsm_dbg(INIT, &channel->ch_bd->pci_dev, + "unable to allocate read queue buf\n"); return -ENOMEM; } } if (!channel->ch_equeue) { channel->ch_equeue = kzalloc(EQUEUESIZE, GFP_KERNEL); if (!channel->ch_equeue) { - jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev, - "unable to allocate error queue buf"); + jsm_dbg(INIT, &channel->ch_bd->pci_dev, + "unable to allocate error queue buf\n"); return -ENOMEM; } } @@ -234,7 +234,7 @@ static int jsm_tty_open(struct uart_port *port) /* * Initialize if neither terminal is open. */ - jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev, + jsm_dbg(OPEN, &channel->ch_bd->pci_dev, "jsm_open: initializing channel in open...\n"); /* @@ -270,7 +270,7 @@ static int jsm_tty_open(struct uart_port *port) channel->ch_open_count++; - jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev, "finish\n"); + jsm_dbg(OPEN, &channel->ch_bd->pci_dev, "finish\n"); return 0; } @@ -280,7 +280,7 @@ static void jsm_tty_close(struct uart_port *port) struct ktermios *ts; struct jsm_channel *channel = (struct jsm_channel *)port; - jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "start\n"); + jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, "start\n"); bd = channel->ch_bd; ts = &port->state->port.tty->termios; @@ -293,7 +293,7 @@ static void jsm_tty_close(struct uart_port *port) * If we have HUPCL set, lower DTR and RTS */ if (channel->ch_c_cflag & HUPCL) { - jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, + jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, "Close. HUPCL set, dropping DTR/RTS\n"); /* Drop RTS/DTR */ @@ -304,7 +304,7 @@ static void jsm_tty_close(struct uart_port *port) /* Turn off UART interrupts for this port */ channel->ch_bd->bd_ops->uart_off(channel); - jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "finish\n"); + jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, "finish\n"); } static void jsm_tty_set_termios(struct uart_port *port, @@ -380,7 +380,7 @@ int __devinit jsm_tty_init(struct jsm_board *brd) if (!brd) return -ENXIO; - jsm_printk(INIT, INFO, &brd->pci_dev, "start\n"); + jsm_dbg(INIT, &brd->pci_dev, "start\n"); /* * Initialize board structure elements. @@ -401,9 +401,9 @@ int __devinit jsm_tty_init(struct jsm_board *brd) */ brd->channels[i] = kzalloc(sizeof(struct jsm_channel), GFP_KERNEL); if (!brd->channels[i]) { - jsm_printk(CORE, ERR, &brd->pci_dev, + jsm_dbg(CORE, &brd->pci_dev, "%s:%d Unable to allocate memory for channel struct\n", - __FILE__, __LINE__); + __FILE__, __LINE__); } } } @@ -431,7 +431,7 @@ int __devinit jsm_tty_init(struct jsm_board *brd) init_waitqueue_head(&ch->ch_flags_wait); } - jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n"); + jsm_dbg(INIT, &brd->pci_dev, "finish\n"); return 0; } @@ -444,7 +444,7 @@ int jsm_uart_port_init(struct jsm_board *brd) if (!brd) return -ENXIO; - jsm_printk(INIT, INFO, &brd->pci_dev, "start\n"); + jsm_dbg(INIT, &brd->pci_dev, "start\n"); /* * Initialize board structure elements. @@ -481,7 +481,7 @@ int jsm_uart_port_init(struct jsm_board *brd) printk(KERN_INFO "jsm: Port %d added\n", i); } - jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n"); + jsm_dbg(INIT, &brd->pci_dev, "finish\n"); return 0; } @@ -493,7 +493,7 @@ int jsm_remove_uart_port(struct jsm_board *brd) if (!brd) return -ENXIO; - jsm_printk(INIT, INFO, &brd->pci_dev, "start\n"); + jsm_dbg(INIT, &brd->pci_dev, "start\n"); /* * Initialize board structure elements. @@ -513,7 +513,7 @@ int jsm_remove_uart_port(struct jsm_board *brd) uart_remove_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port); } - jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n"); + jsm_dbg(INIT, &brd->pci_dev, "finish\n"); return 0; } @@ -531,7 +531,7 @@ void jsm_input(struct jsm_channel *ch) int s = 0; int i = 0; - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n"); + jsm_dbg(READ, &ch->ch_bd->pci_dev, "start\n"); if (!ch) return; @@ -560,7 +560,7 @@ void jsm_input(struct jsm_channel *ch) return; } - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n"); + jsm_dbg(READ, &ch->ch_bd->pci_dev, "start\n"); /* *If the device is not open, or CREAD is off, flush @@ -569,8 +569,9 @@ void jsm_input(struct jsm_channel *ch) if (!tp || !(tp->termios.c_cflag & CREAD) ) { - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, - "input. dropping %d bytes on port %d...\n", data_len, ch->ch_portnum); + jsm_dbg(READ, &ch->ch_bd->pci_dev, + "input. dropping %d bytes on port %d...\n", + data_len, ch->ch_portnum); ch->ch_r_head = tail; /* Force queue flow control to be released, if needed */ @@ -585,17 +586,17 @@ void jsm_input(struct jsm_channel *ch) */ if (ch->ch_flags & CH_STOPI) { spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, + jsm_dbg(READ, &ch->ch_bd->pci_dev, "Port %d throttled, not reading any data. head: %x tail: %x\n", ch->ch_portnum, head, tail); return; } - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start 2\n"); + jsm_dbg(READ, &ch->ch_bd->pci_dev, "start 2\n"); if (data_len <= 0) { spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "jsm_input 1\n"); + jsm_dbg(READ, &ch->ch_bd->pci_dev, "jsm_input 1\n"); return; } @@ -653,7 +654,7 @@ void jsm_input(struct jsm_channel *ch) /* Tell the tty layer its okay to "eat" the data now */ tty_flip_buffer_push(tp); - jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n"); + jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "finish\n"); } static void jsm_carrier(struct jsm_channel *ch) @@ -663,7 +664,7 @@ static void jsm_carrier(struct jsm_channel *ch) int virt_carrier = 0; int phys_carrier = 0; - jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, "start\n"); + jsm_dbg(CARR, &ch->ch_bd->pci_dev, "start\n"); if (!ch) return; @@ -673,16 +674,16 @@ static void jsm_carrier(struct jsm_channel *ch) return; if (ch->ch_mistat & UART_MSR_DCD) { - jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, - "mistat: %x D_CD: %x\n", ch->ch_mistat, ch->ch_mistat & UART_MSR_DCD); + jsm_dbg(CARR, &ch->ch_bd->pci_dev, "mistat: %x D_CD: %x\n", + ch->ch_mistat, ch->ch_mistat & UART_MSR_DCD); phys_carrier = 1; } if (ch->ch_c_cflag & CLOCAL) virt_carrier = 1; - jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, - "DCD: physical: %d virt: %d\n", phys_carrier, virt_carrier); + jsm_dbg(CARR, &ch->ch_bd->pci_dev, "DCD: physical: %d virt: %d\n", + phys_carrier, virt_carrier); /* * Test for a VIRTUAL carrier transition to HIGH. @@ -694,8 +695,7 @@ static void jsm_carrier(struct jsm_channel *ch) * for carrier in the open routine. */ - jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, - "carrier: virt DCD rose\n"); + jsm_dbg(CARR, &ch->ch_bd->pci_dev, "carrier: virt DCD rose\n"); if (waitqueue_active(&(ch->ch_flags_wait))) wake_up_interruptible(&ch->ch_flags_wait); @@ -711,7 +711,7 @@ static void jsm_carrier(struct jsm_channel *ch) * for carrier in the open routine. */ - jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, + jsm_dbg(CARR, &ch->ch_bd->pci_dev, "carrier: physical DCD rose\n"); if (waitqueue_active(&(ch->ch_flags_wait))) @@ -790,8 +790,8 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch) if(!(ch->ch_flags & CH_RECEIVER_OFF)) { bd_ops->disable_receiver(ch); ch->ch_flags |= (CH_RECEIVER_OFF); - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, - "Internal queue hit hilevel mark (%d)! Turning off interrupts.\n", + jsm_dbg(READ, &ch->ch_bd->pci_dev, + "Internal queue hit hilevel mark (%d)! Turning off interrupts\n", qleft); } } @@ -800,8 +800,9 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch) if (ch->ch_stops_sent <= MAX_STOPS_SENT) { bd_ops->send_stop_character(ch); ch->ch_stops_sent++; - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, - "Sending stop char! Times sent: %x\n", ch->ch_stops_sent); + jsm_dbg(READ, &ch->ch_bd->pci_dev, + "Sending stop char! Times sent: %x\n", + ch->ch_stops_sent); } } } @@ -827,8 +828,8 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch) if (ch->ch_flags & CH_RECEIVER_OFF) { bd_ops->enable_receiver(ch); ch->ch_flags &= ~(CH_RECEIVER_OFF); - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, - "Internal queue hit lowlevel mark (%d)! Turning on interrupts.\n", + jsm_dbg(READ, &ch->ch_bd->pci_dev, + "Internal queue hit lowlevel mark (%d)! Turning on interrupts\n", qleft); } } @@ -836,7 +837,8 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch) else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) { ch->ch_stops_sent = 0; bd_ops->send_start_character(ch); - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "Sending start char!\n"); + jsm_dbg(READ, &ch->ch_bd->pci_dev, + "Sending start char!\n"); } } } diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c index d185247..6ac2b79 100644 --- a/drivers/tty/serial/kgdb_nmi.c +++ b/drivers/tty/serial/kgdb_nmi.c @@ -266,6 +266,7 @@ static int kgdb_nmi_tty_install(struct tty_driver *drv, struct tty_struct *tty) } return 0; err: + tty_port_destroy(&priv->port); kfree(priv); return ret; } @@ -275,6 +276,7 @@ static void kgdb_nmi_tty_cleanup(struct tty_struct *tty) struct kgdb_nmi_tty_priv *priv = tty->driver_data; tty->driver_data = NULL; + tty_port_destroy(&priv->port); kfree(priv); } diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c index c4b50af..79fe59b 100644 --- a/drivers/tty/serial/mfd.c +++ b/drivers/tty/serial/mfd.c @@ -36,6 +36,7 @@ #include <linux/serial_mfd.h> #include <linux/dma-mapping.h> #include <linux/pci.h> +#include <linux/nmi.h> #include <linux/io.h> #include <linux/debugfs.h> #include <linux/pm_runtime.h> @@ -1113,6 +1114,8 @@ serial_hsu_console_write(struct console *co, const char *s, unsigned int count) unsigned int ier; int locked = 1; + touch_nmi_watchdog(); + local_irq_save(flags); if (up->port.sysrq) locked = 0; diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 6db3baa..d5b9e30 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -34,6 +34,8 @@ #include <linux/io.h> #include <linux/pinctrl/consumer.h> #include <linux/of_device.h> +#include <linux/dma-mapping.h> +#include <linux/fsl/mxs-dma.h> #include <asm/cacheflush.h> @@ -71,6 +73,15 @@ #define AUART_CTRL0_SFTRST (1 << 31) #define AUART_CTRL0_CLKGATE (1 << 30) +#define AUART_CTRL0_RXTO_ENABLE (1 << 27) +#define AUART_CTRL0_RXTIMEOUT(v) (((v) & 0x7ff) << 16) +#define AUART_CTRL0_XFER_COUNT(v) ((v) & 0xffff) + +#define AUART_CTRL1_XFER_COUNT(v) ((v) & 0xffff) + +#define AUART_CTRL2_DMAONERR (1 << 26) +#define AUART_CTRL2_TXDMAE (1 << 25) +#define AUART_CTRL2_RXDMAE (1 << 24) #define AUART_CTRL2_CTSEN (1 << 15) #define AUART_CTRL2_RTSEN (1 << 14) @@ -111,29 +122,170 @@ #define AUART_STAT_BERR (1 << 18) #define AUART_STAT_PERR (1 << 17) #define AUART_STAT_FERR (1 << 16) +#define AUART_STAT_RXCOUNT_MASK 0xffff static struct uart_driver auart_driver; +enum mxs_auart_type { + IMX23_AUART, + IMX28_AUART, +}; + struct mxs_auart_port { struct uart_port port; - unsigned int flags; +#define MXS_AUART_DMA_CONFIG 0x1 +#define MXS_AUART_DMA_ENABLED 0x2 +#define MXS_AUART_DMA_TX_SYNC 2 /* bit 2 */ +#define MXS_AUART_DMA_RX_READY 3 /* bit 3 */ + unsigned long flags; unsigned int ctrl; + enum mxs_auart_type devtype; unsigned int irq; struct clk *clk; struct device *dev; + + /* for DMA */ + struct mxs_dma_data dma_data; + int dma_channel_rx, dma_channel_tx; + int dma_irq_rx, dma_irq_tx; + int dma_channel; + + struct scatterlist tx_sgl; + struct dma_chan *tx_dma_chan; + void *tx_dma_buf; + + struct scatterlist rx_sgl; + struct dma_chan *rx_dma_chan; + void *rx_dma_buf; }; +static struct platform_device_id mxs_auart_devtype[] = { + { .name = "mxs-auart-imx23", .driver_data = IMX23_AUART }, + { .name = "mxs-auart-imx28", .driver_data = IMX28_AUART }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, mxs_auart_devtype); + +static struct of_device_id mxs_auart_dt_ids[] = { + { + .compatible = "fsl,imx28-auart", + .data = &mxs_auart_devtype[IMX28_AUART] + }, { + .compatible = "fsl,imx23-auart", + .data = &mxs_auart_devtype[IMX23_AUART] + }, { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_auart_dt_ids); + +static inline int is_imx28_auart(struct mxs_auart_port *s) +{ + return s->devtype == IMX28_AUART; +} + +static inline bool auart_dma_enabled(struct mxs_auart_port *s) +{ + return s->flags & MXS_AUART_DMA_ENABLED; +} + static void mxs_auart_stop_tx(struct uart_port *u); #define to_auart_port(u) container_of(u, struct mxs_auart_port, port) -static inline void mxs_auart_tx_chars(struct mxs_auart_port *s) +static void mxs_auart_tx_chars(struct mxs_auart_port *s); + +static void dma_tx_callback(void *param) +{ + struct mxs_auart_port *s = param; + struct circ_buf *xmit = &s->port.state->xmit; + + dma_unmap_sg(s->dev, &s->tx_sgl, 1, DMA_TO_DEVICE); + + /* clear the bit used to serialize the DMA tx. */ + clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags); + smp_mb__after_clear_bit(); + + /* wake up the possible processes. */ + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&s->port); + + mxs_auart_tx_chars(s); +} + +static int mxs_auart_dma_tx(struct mxs_auart_port *s, int size) +{ + struct dma_async_tx_descriptor *desc; + struct scatterlist *sgl = &s->tx_sgl; + struct dma_chan *channel = s->tx_dma_chan; + u32 pio; + + /* [1] : send PIO. Note, the first pio word is CTRL1. */ + pio = AUART_CTRL1_XFER_COUNT(size); + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)&pio, + 1, DMA_TRANS_NONE, 0); + if (!desc) { + dev_err(s->dev, "step 1 error\n"); + return -EINVAL; + } + + /* [2] : set DMA buffer. */ + sg_init_one(sgl, s->tx_dma_buf, size); + dma_map_sg(s->dev, sgl, 1, DMA_TO_DEVICE); + desc = dmaengine_prep_slave_sg(channel, sgl, + 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(s->dev, "step 2 error\n"); + return -EINVAL; + } + + /* [3] : submit the DMA */ + desc->callback = dma_tx_callback; + desc->callback_param = s; + dmaengine_submit(desc); + dma_async_issue_pending(channel); + return 0; +} + +static void mxs_auart_tx_chars(struct mxs_auart_port *s) { struct circ_buf *xmit = &s->port.state->xmit; + if (auart_dma_enabled(s)) { + int i = 0; + int size; + void *buffer = s->tx_dma_buf; + + if (test_and_set_bit(MXS_AUART_DMA_TX_SYNC, &s->flags)) + return; + + while (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) { + size = min_t(u32, UART_XMIT_SIZE - i, + CIRC_CNT_TO_END(xmit->head, + xmit->tail, + UART_XMIT_SIZE)); + memcpy(buffer + i, xmit->buf + xmit->tail, size); + xmit->tail = (xmit->tail + size) & (UART_XMIT_SIZE - 1); + + i += size; + if (i >= UART_XMIT_SIZE) + break; + } + + if (uart_tx_stopped(&s->port)) + mxs_auart_stop_tx(&s->port); + + if (i) { + mxs_auart_dma_tx(s, i); + } else { + clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags); + smp_mb__after_clear_bit(); + } + return; + } + + while (!(readl(s->port.membase + AUART_STAT) & AUART_STAT_TXFF)) { if (s->port.x_char) { @@ -287,10 +439,157 @@ static u32 mxs_auart_get_mctrl(struct uart_port *u) return mctrl; } +static bool mxs_auart_dma_filter(struct dma_chan *chan, void *param) +{ + struct mxs_auart_port *s = param; + + if (!mxs_dma_is_apbx(chan)) + return false; + + if (s->dma_channel == chan->chan_id) { + chan->private = &s->dma_data; + return true; + } + return false; +} + +static int mxs_auart_dma_prep_rx(struct mxs_auart_port *s); +static void dma_rx_callback(void *arg) +{ + struct mxs_auart_port *s = (struct mxs_auart_port *) arg; + struct tty_struct *tty = s->port.state->port.tty; + int count; + u32 stat; + + stat = readl(s->port.membase + AUART_STAT); + stat &= ~(AUART_STAT_OERR | AUART_STAT_BERR | + AUART_STAT_PERR | AUART_STAT_FERR); + + count = stat & AUART_STAT_RXCOUNT_MASK; + tty_insert_flip_string(tty, s->rx_dma_buf, count); + + writel(stat, s->port.membase + AUART_STAT); + tty_flip_buffer_push(tty); + + /* start the next DMA for RX. */ + mxs_auart_dma_prep_rx(s); +} + +static int mxs_auart_dma_prep_rx(struct mxs_auart_port *s) +{ + struct dma_async_tx_descriptor *desc; + struct scatterlist *sgl = &s->rx_sgl; + struct dma_chan *channel = s->rx_dma_chan; + u32 pio[1]; + + /* [1] : send PIO */ + pio[0] = AUART_CTRL0_RXTO_ENABLE + | AUART_CTRL0_RXTIMEOUT(0x80) + | AUART_CTRL0_XFER_COUNT(UART_XMIT_SIZE); + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, + 1, DMA_TRANS_NONE, 0); + if (!desc) { + dev_err(s->dev, "step 1 error\n"); + return -EINVAL; + } + + /* [2] : send DMA request */ + sg_init_one(sgl, s->rx_dma_buf, UART_XMIT_SIZE); + dma_map_sg(s->dev, sgl, 1, DMA_FROM_DEVICE); + desc = dmaengine_prep_slave_sg(channel, sgl, 1, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(s->dev, "step 2 error\n"); + return -1; + } + + /* [3] : submit the DMA, but do not issue it. */ + desc->callback = dma_rx_callback; + desc->callback_param = s; + dmaengine_submit(desc); + dma_async_issue_pending(channel); + return 0; +} + +static void mxs_auart_dma_exit_channel(struct mxs_auart_port *s) +{ + if (s->tx_dma_chan) { + dma_release_channel(s->tx_dma_chan); + s->tx_dma_chan = NULL; + } + if (s->rx_dma_chan) { + dma_release_channel(s->rx_dma_chan); + s->rx_dma_chan = NULL; + } + + kfree(s->tx_dma_buf); + kfree(s->rx_dma_buf); + s->tx_dma_buf = NULL; + s->rx_dma_buf = NULL; +} + +static void mxs_auart_dma_exit(struct mxs_auart_port *s) +{ + + writel(AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE | AUART_CTRL2_DMAONERR, + s->port.membase + AUART_CTRL2_CLR); + + mxs_auart_dma_exit_channel(s); + s->flags &= ~MXS_AUART_DMA_ENABLED; + clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags); + clear_bit(MXS_AUART_DMA_RX_READY, &s->flags); +} + +static int mxs_auart_dma_init(struct mxs_auart_port *s) +{ + dma_cap_mask_t mask; + + if (auart_dma_enabled(s)) + return 0; + + /* We do not get the right DMA channels. */ + if (s->dma_channel_rx == -1 || s->dma_channel_rx == -1) + return -EINVAL; + + /* init for RX */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + s->dma_channel = s->dma_channel_rx; + s->dma_data.chan_irq = s->dma_irq_rx; + s->rx_dma_chan = dma_request_channel(mask, mxs_auart_dma_filter, s); + if (!s->rx_dma_chan) + goto err_out; + s->rx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA); + if (!s->rx_dma_buf) + goto err_out; + + /* init for TX */ + s->dma_channel = s->dma_channel_tx; + s->dma_data.chan_irq = s->dma_irq_tx; + s->tx_dma_chan = dma_request_channel(mask, mxs_auart_dma_filter, s); + if (!s->tx_dma_chan) + goto err_out; + s->tx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA); + if (!s->tx_dma_buf) + goto err_out; + + /* set the flags */ + s->flags |= MXS_AUART_DMA_ENABLED; + dev_dbg(s->dev, "enabled the DMA support."); + + return 0; + +err_out: + mxs_auart_dma_exit_channel(s); + return -EINVAL; + +} + static void mxs_auart_settermios(struct uart_port *u, struct ktermios *termios, struct ktermios *old) { + struct mxs_auart_port *s = to_auart_port(u); u32 bm, ctrl, ctrl2, div; unsigned int cflag, baud; @@ -362,10 +661,23 @@ static void mxs_auart_settermios(struct uart_port *u, ctrl |= AUART_LINECTRL_STP2; /* figure out the hardware flow control settings */ - if (cflag & CRTSCTS) + if (cflag & CRTSCTS) { + /* + * The DMA has a bug(see errata:2836) in mx23. + * So we can not implement the DMA for auart in mx23, + * we can only implement the DMA support for auart + * in mx28. + */ + if (is_imx28_auart(s) && (s->flags & MXS_AUART_DMA_CONFIG)) { + if (!mxs_auart_dma_init(s)) + /* enable DMA tranfer */ + ctrl2 |= AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE + | AUART_CTRL2_DMAONERR; + } ctrl2 |= AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN; - else + } else { ctrl2 &= ~(AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN); + } /* set baud rate */ baud = uart_get_baud_rate(u, termios, old, 0, u->uartclk); @@ -377,6 +689,18 @@ static void mxs_auart_settermios(struct uart_port *u, writel(ctrl2, u->membase + AUART_CTRL2); uart_update_timeout(u, termios->c_cflag, baud); + + /* prepare for the DMA RX. */ + if (auart_dma_enabled(s) && + !test_and_set_bit(MXS_AUART_DMA_RX_READY, &s->flags)) { + if (!mxs_auart_dma_prep_rx(s)) { + /* Disable the normal RX interrupt. */ + writel(AUART_INTR_RXIEN, u->membase + AUART_INTR_CLR); + } else { + mxs_auart_dma_exit(s); + dev_err(s->dev, "We can not start up the DMA.\n"); + } + } } static irqreturn_t mxs_auart_irq_handle(int irq, void *context) @@ -455,6 +779,9 @@ static void mxs_auart_shutdown(struct uart_port *u) { struct mxs_auart_port *s = to_auart_port(u); + if (auart_dma_enabled(s)) + mxs_auart_dma_exit(s); + writel(AUART_CTRL2_UARTEN, u->membase + AUART_CTRL2_CLR); writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN, @@ -688,6 +1015,7 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + u32 dma_channel[2]; int ret; if (!np) @@ -701,11 +1029,27 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, } s->port.line = ret; + s->dma_irq_rx = platform_get_irq(pdev, 1); + s->dma_irq_tx = platform_get_irq(pdev, 2); + + ret = of_property_read_u32_array(np, "fsl,auart-dma-channel", + dma_channel, 2); + if (ret == 0) { + s->dma_channel_rx = dma_channel[0]; + s->dma_channel_tx = dma_channel[1]; + + s->flags |= MXS_AUART_DMA_CONFIG; + } else { + s->dma_channel_rx = -1; + s->dma_channel_tx = -1; + } return 0; } static int __devinit mxs_auart_probe(struct platform_device *pdev) { + const struct of_device_id *of_id = + of_match_device(mxs_auart_dt_ids, &pdev->dev); struct mxs_auart_port *s; u32 version; int ret = 0; @@ -730,6 +1074,11 @@ static int __devinit mxs_auart_probe(struct platform_device *pdev) goto out_free; } + if (of_id) { + pdev->id_entry = of_id->data; + s->devtype = pdev->id_entry->driver_data; + } + s->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(s->clk)) { ret = PTR_ERR(s->clk); @@ -751,7 +1100,6 @@ static int __devinit mxs_auart_probe(struct platform_device *pdev) s->port.type = PORT_IMX; s->port.dev = s->dev = get_device(&pdev->dev); - s->flags = 0; s->ctrl = 0; s->irq = platform_get_irq(pdev, 0); @@ -805,12 +1153,6 @@ static int __devexit mxs_auart_remove(struct platform_device *pdev) return 0; } -static struct of_device_id mxs_auart_dt_ids[] = { - { .compatible = "fsl,imx23-auart", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, mxs_auart_dt_ids); - static struct platform_driver mxs_auart_driver = { .probe = mxs_auart_probe, .remove = __devexit_p(mxs_auart_remove), diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index df443b9..b9fdccb 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -21,8 +21,10 @@ #include <linux/of_serial.h> #include <linux/of_platform.h> #include <linux/nwpserial.h> +#include <linux/clk.h> struct of_serial_info { + struct clk *clk; int type; int line; }; @@ -51,7 +53,8 @@ EXPORT_SYMBOL_GPL(tegra_serial_handle_break); * Fill a struct uart_port for a given device node */ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, - int type, struct uart_port *port) + int type, struct uart_port *port, + struct of_serial_info *info) { struct resource resource; struct device_node *np = ofdev->dev.of_node; @@ -60,8 +63,17 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, memset(port, 0, sizeof *port); if (of_property_read_u32(np, "clock-frequency", &clk)) { - dev_warn(&ofdev->dev, "no clock-frequency property set\n"); - return -ENODEV; + + /* Get clk rate through clk driver if present */ + info->clk = clk_get(&ofdev->dev, NULL); + if (IS_ERR(info->clk)) { + dev_warn(&ofdev->dev, + "clk or clock-frequency not defined\n"); + return PTR_ERR(info->clk); + } + + clk_prepare_enable(info->clk); + clk = clk_get_rate(info->clk); } /* If current-speed was set, then try not to change it. */ if (of_property_read_u32(np, "current-speed", &spd) == 0) @@ -70,7 +82,7 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, ret = of_address_to_resource(np, 0, &resource); if (ret) { dev_warn(&ofdev->dev, "invalid address\n"); - return ret; + goto out; } spin_lock_init(&port->lock); @@ -97,7 +109,8 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, default: dev_warn(&ofdev->dev, "unsupported reg-io-width (%d)\n", prop); - return -EINVAL; + ret = -EINVAL; + goto out; } } @@ -115,6 +128,10 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, port->handle_break = tegra_serial_handle_break; return 0; +out: + if (info->clk) + clk_disable_unprepare(info->clk); + return ret; } /* @@ -141,7 +158,7 @@ static int __devinit of_platform_serial_probe(struct platform_device *ofdev) return -ENOMEM; port_type = (unsigned long)match->data; - ret = of_platform_serial_setup(ofdev, port_type, &port); + ret = of_platform_serial_setup(ofdev, port_type, &port, info); if (ret) goto out; @@ -204,6 +221,9 @@ static int of_platform_serial_remove(struct platform_device *ofdev) /* need to add code for these */ break; } + + if (info->clk) + clk_disable_unprepare(info->clk); kfree(info); return 0; } diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 6d3d26a..c34735c 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -96,7 +96,7 @@ struct uart_omap_port { unsigned char msr_saved_flags; char name[20]; unsigned long port_activity; - u32 context_loss_cnt; + int context_loss_cnt; u32 errata; u8 wakeups_enabled; unsigned int irq_pending:1; @@ -702,11 +702,7 @@ serial_omap_configure_xonxoff serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); - /* Enable special char function UARTi.EFR_REG[5] and - * load the new software flow control mode IXON or IXOFF - * and restore the UARTi.EFR_REG[4] ENHANCED_EN value. - */ - serial_out(up, UART_EFR, up->efr | UART_EFR_SCD); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); serial_out(up, UART_MCR, up->mcr & ~UART_MCR_TCRTLR); @@ -1081,7 +1077,7 @@ out: #ifdef CONFIG_SERIAL_OMAP_CONSOLE -static struct uart_omap_port *serial_omap_console_ports[4]; +static struct uart_omap_port *serial_omap_console_ports[OMAP_MAX_HSUART_PORTS]; static struct uart_driver serial_omap_reg; @@ -1556,11 +1552,15 @@ static int serial_omap_runtime_resume(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); - u32 loss_cnt = serial_omap_get_context_loss_count(up); + int loss_cnt = serial_omap_get_context_loss_count(up); - if (up->context_loss_cnt != loss_cnt) + if (loss_cnt < 0) { + dev_err(dev, "serial_omap_get_context_loss_count failed : %d\n", + loss_cnt); serial_omap_restore_context(up); - + } else if (up->context_loss_cnt != loss_cnt) { + serial_omap_restore_context(up); + } up->latency = up->calc_latency; schedule_work(&up->qos_work); diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index 9033fc6..2764828 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -705,6 +705,57 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count) clk_disable_unprepare(up->clk); } +#ifdef CONFIG_CONSOLE_POLL +/* + * Console polling routines for writing and reading from the uart while + * in an interrupt or debug context. + */ + +static int serial_pxa_get_poll_char(struct uart_port *port) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + unsigned char lsr = serial_in(up, UART_LSR); + + while (!(lsr & UART_LSR_DR)) + lsr = serial_in(up, UART_LSR); + + return serial_in(up, UART_RX); +} + + +static void serial_pxa_put_poll_char(struct uart_port *port, + unsigned char c) +{ + unsigned int ier; + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + + /* + * First save the IER then disable the interrupts + */ + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, UART_IER_UUE); + + wait_for_xmitr(up); + /* + * Send the character out. + * If a LF, also do CR... + */ + serial_out(up, UART_TX, c); + if (c == 10) { + wait_for_xmitr(up); + serial_out(up, UART_TX, 13); + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + serial_out(up, UART_IER, ier); +} + +#endif /* CONFIG_CONSOLE_POLL */ + static int __init serial_pxa_console_setup(struct console *co, char *options) { @@ -759,6 +810,10 @@ struct uart_ops serial_pxa_pops = { .request_port = serial_pxa_request_port, .config_port = serial_pxa_config_port, .verify_port = serial_pxa_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = serial_pxa_get_poll_char, + .poll_put_char = serial_pxa_put_poll_char, +#endif }; static struct uart_driver serial_pxa_reg = { diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 7f04717..740458c 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -530,16 +530,16 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, switch (level) { case 3: if (!IS_ERR(ourport->baudclk)) - clk_disable(ourport->baudclk); + clk_disable_unprepare(ourport->baudclk); - clk_disable(ourport->clk); + clk_disable_unprepare(ourport->clk); break; case 0: - clk_enable(ourport->clk); + clk_prepare_enable(ourport->clk); if (!IS_ERR(ourport->baudclk)) - clk_enable(ourport->baudclk); + clk_prepare_enable(ourport->baudclk); break; default: @@ -713,11 +713,11 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, s3c24xx_serial_setsource(port, clk_sel); if (!IS_ERR(ourport->baudclk)) { - clk_disable(ourport->baudclk); + clk_disable_unprepare(ourport->baudclk); ourport->baudclk = ERR_PTR(-EINVAL); } - clk_enable(clk); + clk_prepare_enable(clk); ourport->baudclk = clk; ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0; @@ -1287,9 +1287,9 @@ static int s3c24xx_serial_resume(struct device *dev) struct s3c24xx_uart_port *ourport = to_ourport(port); if (port) { - clk_enable(ourport->clk); + clk_prepare_enable(ourport->clk); s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port)); - clk_disable(ourport->clk); + clk_disable_unprepare(ourport->clk); uart_resume_port(&s3c24xx_uart_drv, port); } diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 0fcfd98..61ba240 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -634,10 +634,10 @@ static void uart_unthrottle(struct tty_struct *tty) uart_set_mctrl(port, TIOCM_RTS); } -static void uart_get_info(struct tty_port *port, - struct uart_state *state, +static void do_uart_get_info(struct tty_port *port, struct serial_struct *retinfo) { + struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport = state->uart_port; memset(retinfo, 0, sizeof(*retinfo)); @@ -662,17 +662,21 @@ static void uart_get_info(struct tty_port *port, retinfo->iomem_base = (void *)(unsigned long)uport->mapbase; } -static int uart_get_info_user(struct uart_state *state, - struct serial_struct __user *retinfo) +static void uart_get_info(struct tty_port *port, + struct serial_struct *retinfo) { - struct tty_port *port = &state->port; - struct serial_struct tmp; - /* Ensure the state we copy is consistent and no hardware changes occur as we go */ mutex_lock(&port->mutex); - uart_get_info(port, state, &tmp); + do_uart_get_info(port, retinfo); mutex_unlock(&port->mutex); +} + +static int uart_get_info_user(struct tty_port *port, + struct serial_struct __user *retinfo) +{ + struct serial_struct tmp; + uart_get_info(port, &tmp); if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; @@ -1131,7 +1135,7 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, */ switch (cmd) { case TIOCGSERIAL: - ret = uart_get_info_user(state, uarg); + ret = uart_get_info_user(port, uarg); break; case TIOCSSERIAL: @@ -2293,6 +2297,8 @@ int uart_register_driver(struct uart_driver *drv) if (retval >= 0) return retval; + for (i = 0; i < drv->nr; i++) + tty_port_destroy(&drv->state[i].port); put_tty_driver(normal); out_kfree: kfree(drv->state); @@ -2312,8 +2318,12 @@ out: void uart_unregister_driver(struct uart_driver *drv) { struct tty_driver *p = drv->tty_driver; + unsigned int i; + tty_unregister_driver(p); put_tty_driver(p); + for (i = 0; i < drv->nr; i++) + tty_port_destroy(&drv->state[i].port); kfree(drv->state); drv->state = NULL; drv->tty_driver = NULL; @@ -2329,21 +2339,162 @@ struct tty_driver *uart_console_device(struct console *co, int *index) static ssize_t uart_get_attr_uartclk(struct device *dev, struct device_attribute *attr, char *buf) { - int ret; + struct serial_struct tmp; struct tty_port *port = dev_get_drvdata(dev); - struct uart_state *state = container_of(port, struct uart_state, port); - mutex_lock(&state->port.mutex); - ret = snprintf(buf, PAGE_SIZE, "%d\n", state->uart_port->uartclk); - mutex_unlock(&state->port.mutex); + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.baud_base * 16); +} - return ret; +static ssize_t uart_get_attr_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.type); +} +static ssize_t uart_get_attr_line(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.line); +} + +static ssize_t uart_get_attr_port(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "0x%lX\n", (unsigned long)(tmp.port | (((unsigned long)tmp.port_high) << HIGH_BITS_OFFSET))); +} + +static ssize_t uart_get_attr_irq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.irq); +} + +static ssize_t uart_get_attr_flags(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "0x%X\n", tmp.flags); +} + +static ssize_t uart_get_attr_xmit_fifo_size(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.xmit_fifo_size); } + +static ssize_t uart_get_attr_close_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.close_delay); +} + + +static ssize_t uart_get_attr_closing_wait(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.closing_wait); +} + +static ssize_t uart_get_attr_custom_divisor(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.custom_divisor); +} + +static ssize_t uart_get_attr_io_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.io_type); +} + +static ssize_t uart_get_attr_iomem_base(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "0x%lX\n", (unsigned long)tmp.iomem_base); +} + +static ssize_t uart_get_attr_iomem_reg_shift(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.iomem_reg_shift); +} + +static DEVICE_ATTR(type, S_IRUSR | S_IRGRP, uart_get_attr_type, NULL); +static DEVICE_ATTR(line, S_IRUSR | S_IRGRP, uart_get_attr_line, NULL); +static DEVICE_ATTR(port, S_IRUSR | S_IRGRP, uart_get_attr_port, NULL); +static DEVICE_ATTR(irq, S_IRUSR | S_IRGRP, uart_get_attr_irq, NULL); +static DEVICE_ATTR(flags, S_IRUSR | S_IRGRP, uart_get_attr_flags, NULL); +static DEVICE_ATTR(xmit_fifo_size, S_IRUSR | S_IRGRP, uart_get_attr_xmit_fifo_size, NULL); static DEVICE_ATTR(uartclk, S_IRUSR | S_IRGRP, uart_get_attr_uartclk, NULL); +static DEVICE_ATTR(close_delay, S_IRUSR | S_IRGRP, uart_get_attr_close_delay, NULL); +static DEVICE_ATTR(closing_wait, S_IRUSR | S_IRGRP, uart_get_attr_closing_wait, NULL); +static DEVICE_ATTR(custom_divisor, S_IRUSR | S_IRGRP, uart_get_attr_custom_divisor, NULL); +static DEVICE_ATTR(io_type, S_IRUSR | S_IRGRP, uart_get_attr_io_type, NULL); +static DEVICE_ATTR(iomem_base, S_IRUSR | S_IRGRP, uart_get_attr_iomem_base, NULL); +static DEVICE_ATTR(iomem_reg_shift, S_IRUSR | S_IRGRP, uart_get_attr_iomem_reg_shift, NULL); static struct attribute *tty_dev_attrs[] = { + &dev_attr_type.attr, + &dev_attr_line.attr, + &dev_attr_port.attr, + &dev_attr_irq.attr, + &dev_attr_flags.attr, + &dev_attr_xmit_fifo_size.attr, &dev_attr_uartclk.attr, + &dev_attr_close_delay.attr, + &dev_attr_closing_wait.attr, + &dev_attr_custom_divisor.attr, + &dev_attr_io_type.attr, + &dev_attr_iomem_base.attr, + &dev_attr_iomem_reg_shift.attr, NULL, }; @@ -2356,6 +2507,7 @@ static const struct attribute_group *tty_dev_attr_groups[] = { NULL }; + /** * uart_add_one_port - attach a driver-defined port structure * @drv: pointer to the uart low level driver structure for this port diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 6ee5900..d38c0f5 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -99,12 +99,6 @@ struct sci_port { #endif struct notifier_block freq_transition; - -#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE - unsigned short saved_smr; - unsigned short saved_fcr; - unsigned char saved_brr; -#endif }; /* Function prototypes */ @@ -202,9 +196,9 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCxSR] = { 0x14, 16 }, [SCxRDR] = { 0x60, 8 }, [SCFCR] = { 0x18, 16 }, - [SCFDR] = { 0x1c, 16 }, - [SCTFDR] = sci_reg_invalid, - [SCRFDR] = sci_reg_invalid, + [SCFDR] = sci_reg_invalid, + [SCTFDR] = { 0x38, 16 }, + [SCRFDR] = { 0x3c, 16 }, [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, }, @@ -491,7 +485,7 @@ static int sci_txfill(struct uart_port *port) reg = sci_getreg(port, SCTFDR); if (reg->size) - return serial_port_in(port, SCTFDR) & 0xff; + return serial_port_in(port, SCTFDR) & ((port->fifosize << 1) - 1); reg = sci_getreg(port, SCFDR); if (reg->size) @@ -511,7 +505,7 @@ static int sci_rxfill(struct uart_port *port) reg = sci_getreg(port, SCRFDR); if (reg->size) - return serial_port_in(port, SCRFDR) & 0xff; + return serial_port_in(port, SCRFDR) & ((port->fifosize << 1) - 1); reg = sci_getreg(port, SCFDR); if (reg->size) @@ -1749,22 +1743,21 @@ static inline void sci_free_dma(struct uart_port *port) static int sci_startup(struct uart_port *port) { struct sci_port *s = to_sci_port(port); + unsigned long flags; int ret; dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); - pm_runtime_put_noidle(port->dev); - - sci_port_enable(s); - ret = sci_request_irq(s); if (unlikely(ret < 0)) return ret; sci_request_dma(port); + spin_lock_irqsave(&port->lock, flags); sci_start_tx(port); sci_start_rx(port); + spin_unlock_irqrestore(&port->lock, flags); return 0; } @@ -1772,18 +1765,17 @@ static int sci_startup(struct uart_port *port) static void sci_shutdown(struct uart_port *port) { struct sci_port *s = to_sci_port(port); + unsigned long flags; dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + spin_lock_irqsave(&port->lock, flags); sci_stop_rx(port); sci_stop_tx(port); + spin_unlock_irqrestore(&port->lock, flags); sci_free_dma(port); sci_free_irq(s); - - sci_port_disable(s); - - pm_runtime_get_noresume(port->dev); } static unsigned int sci_scbrr_calc(unsigned int algo_id, unsigned int bps, @@ -1829,7 +1821,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, { struct sci_port *s = to_sci_port(port); struct plat_sci_reg *reg; - unsigned int baud, smr_val, max_baud; + unsigned int baud, smr_val, max_baud, cks; int t = -1; /* @@ -1863,21 +1855,18 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, uart_update_timeout(port, termios->c_cflag, baud); - serial_port_out(port, SCSMR, smr_val); - - dev_dbg(port->dev, "%s: SMR %x, t %x, SCSCR %x\n", __func__, smr_val, t, - s->cfg->scscr); + for (cks = 0; t >= 256 && cks <= 3; cks++) + t >>= 2; - if (t > 0) { - if (t >= 256) { - serial_port_out(port, SCSMR, (serial_port_in(port, SCSMR) & ~3) | 1); - t >>= 2; - } else - serial_port_out(port, SCSMR, serial_port_in(port, SCSMR) & ~3); + dev_dbg(port->dev, "%s: SMR %x, cks %x, t %x, SCSCR %x\n", + __func__, smr_val, cks, t, s->cfg->scscr); + if (t >= 0) { + serial_port_out(port, SCSMR, (smr_val & ~3) | cks); serial_port_out(port, SCBRR, t); udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */ - } + } else + serial_port_out(port, SCSMR, smr_val); sci_init_pins(port, termios->c_cflag); @@ -1932,6 +1921,21 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, sci_port_disable(s); } +static void sci_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct sci_port *sci_port = to_sci_port(port); + + switch (state) { + case 3: + sci_port_disable(sci_port); + break; + default: + sci_port_enable(sci_port); + break; + } +} + static const char *sci_type(struct uart_port *port) { switch (port->type) { @@ -2053,6 +2057,7 @@ static struct uart_ops sci_uart_ops = { .startup = sci_startup, .shutdown = sci_shutdown, .set_termios = sci_set_termios, + .pm = sci_pm, .type = sci_type, .release_port = sci_release_port, .request_port = sci_request_port, @@ -2121,8 +2126,6 @@ static int __devinit sci_init_single(struct platform_device *dev, sci_init_gpios(sci_port); - pm_runtime_irq_safe(&dev->dev); - pm_runtime_get_noresume(&dev->dev); pm_runtime_enable(&dev->dev); } @@ -2206,9 +2209,21 @@ static void serial_console_write(struct console *co, const char *s, { struct sci_port *sci_port = &sci_ports[co->index]; struct uart_port *port = &sci_port->port; - unsigned short bits; + unsigned short bits, ctrl; + unsigned long flags; + int locked = 1; + + local_irq_save(flags); + if (port->sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&port->lock); + else + spin_lock(&port->lock); - sci_port_enable(sci_port); + /* first save the SCSCR then disable the interrupts */ + ctrl = serial_port_in(port, SCSCR); + serial_port_out(port, SCSCR, sci_port->cfg->scscr); uart_console_write(port, s, count, serial_console_putchar); @@ -2217,7 +2232,12 @@ static void serial_console_write(struct console *co, const char *s, while ((serial_port_in(port, SCxSR) & bits) != bits) cpu_relax(); - sci_port_disable(sci_port); + /* restore the SCSCR */ + serial_port_out(port, SCSCR, ctrl); + + if (locked) + spin_unlock(&port->lock); + local_irq_restore(flags); } static int __devinit serial_console_setup(struct console *co, char *options) @@ -2249,13 +2269,9 @@ static int __devinit serial_console_setup(struct console *co, char *options) if (unlikely(ret != 0)) return ret; - sci_port_enable(sci_port); - if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); - sci_port_disable(sci_port); - return uart_set_options(port, co, baud, parity, bits, flow); } @@ -2298,46 +2314,6 @@ static int __devinit sci_probe_earlyprintk(struct platform_device *pdev) return 0; } -#define uart_console(port) ((port)->cons->index == (port)->line) - -static int sci_runtime_suspend(struct device *dev) -{ - struct sci_port *sci_port = dev_get_drvdata(dev); - struct uart_port *port = &sci_port->port; - - if (uart_console(port)) { - struct plat_sci_reg *reg; - - sci_port->saved_smr = serial_port_in(port, SCSMR); - sci_port->saved_brr = serial_port_in(port, SCBRR); - - reg = sci_getreg(port, SCFCR); - if (reg->size) - sci_port->saved_fcr = serial_port_in(port, SCFCR); - else - sci_port->saved_fcr = 0; - } - return 0; -} - -static int sci_runtime_resume(struct device *dev) -{ - struct sci_port *sci_port = dev_get_drvdata(dev); - struct uart_port *port = &sci_port->port; - - if (uart_console(port)) { - sci_reset(port); - serial_port_out(port, SCSMR, sci_port->saved_smr); - serial_port_out(port, SCBRR, sci_port->saved_brr); - - if (sci_port->saved_fcr) - serial_port_out(port, SCFCR, sci_port->saved_fcr); - - serial_port_out(port, SCSCR, sci_port->cfg->scscr); - } - return 0; -} - #define SCI_CONSOLE (&serial_console) #else @@ -2347,8 +2323,6 @@ static inline int __devinit sci_probe_earlyprintk(struct platform_device *pdev) } #define SCI_CONSOLE NULL -#define sci_runtime_suspend NULL -#define sci_runtime_resume NULL #endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */ @@ -2466,8 +2440,6 @@ static int sci_resume(struct device *dev) } static const struct dev_pm_ops sci_dev_pm_ops = { - .runtime_suspend = sci_runtime_suspend, - .runtime_resume = sci_runtime_resume, .suspend = sci_suspend, .resume = sci_resume, }; diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 205d4cf..4354fe5 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -567,10 +567,6 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev) if (!mmres || !irqres) return -ENODEV; - vt8500_port = kzalloc(sizeof(struct vt8500_port), GFP_KERNEL); - if (!vt8500_port) - return -ENOMEM; - if (np) port = of_alias_get_id(np, "serial"); if (port > VT8500_MAX_PORTS) @@ -593,6 +589,10 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev) return -EBUSY; } + vt8500_port = kzalloc(sizeof(struct vt8500_port), GFP_KERNEL); + if (!vt8500_port) + return -ENOMEM; + vt8500_port->uart.type = PORT_VT8500; vt8500_port->uart.iotype = UPIO_MEM; vt8500_port->uart.mapbase = mmres->start; diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index b627363..23efe17 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -946,15 +946,11 @@ static int __devinit xuartps_probe(struct platform_device *pdev) struct resource *res, *res2; int clk = 0; -#ifdef CONFIG_OF const unsigned int *prop; prop = of_get_property(pdev->dev.of_node, "clock", NULL); if (prop) clk = be32_to_cpup(prop); -#else - clk = *((unsigned int *)(pdev->dev.platform_data)); -#endif if (!clk) { dev_err(&pdev->dev, "no clock specified\n"); return -ENODEV; @@ -1044,16 +1040,11 @@ static int xuartps_resume(struct platform_device *pdev) } /* Match table for of_platform binding */ - -#ifdef CONFIG_OF static struct of_device_id xuartps_of_match[] __devinitdata = { { .compatible = "xlnx,xuartps", }, {} }; MODULE_DEVICE_TABLE(of, xuartps_of_match); -#else -#define xuartps_of_match NULL -#endif static struct platform_driver xuartps_platform_driver = { .probe = xuartps_probe, /* Probe method */ diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 70e3a52..e4b5c39 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -4425,6 +4425,7 @@ static void synclink_cleanup(void) mgsl_release_resources(info); tmp = info; info = info->next_device; + tty_port_destroy(&tmp->port); kfree(tmp); } diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index b38e954..6e4c340 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -3645,8 +3645,10 @@ static void device_init(int adapter_num, struct pci_dev *pdev) for (i=0; i < port_count; ++i) { port_array[i] = alloc_dev(adapter_num, i, pdev); if (port_array[i] == NULL) { - for (--i; i >= 0; --i) + for (--i; i >= 0; --i) { + tty_port_destroy(&port_array[i]->port); kfree(port_array[i]); + } return; } } @@ -3773,6 +3775,7 @@ static void slgt_cleanup(void) release_resources(info); tmp = info; info = info->next_device; + tty_port_destroy(&tmp->port); kfree(tmp); } diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index f17d9f3..40745be 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -3843,8 +3843,10 @@ static void device_init(int adapter_num, struct pci_dev *pdev) for ( port = 0; port < SCA_MAX_PORTS; ++port ) { port_array[port] = alloc_dev(adapter_num,port,pdev); if( port_array[port] == NULL ) { - for ( --port; port >= 0; --port ) + for (--port; port >= 0; --port) { + tty_port_destroy(&port_array[port]->port); kfree(port_array[port]); + } return; } } @@ -3953,6 +3955,7 @@ static void synclinkmp_cleanup(void) } tmp = info; info = info->next_device; + tty_port_destroy(&tmp->port); kfree(tmp); } diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 16ee6ce..b3c4a25 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -346,7 +346,8 @@ static struct sysrq_key_op sysrq_term_op = { static void moom_callback(struct work_struct *ignored) { - out_of_memory(node_zonelist(0, GFP_KERNEL), GFP_KERNEL, 0, NULL, true); + out_of_memory(node_zonelist(first_online_node, GFP_KERNEL), GFP_KERNEL, + 0, NULL, true); } static DECLARE_WORK(moom_work, moom_callback); diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c index b0b39b8..6953dc8 100644 --- a/drivers/tty/tty_audit.c +++ b/drivers/tty/tty_audit.c @@ -23,7 +23,7 @@ struct tty_audit_buf { }; static struct tty_audit_buf *tty_audit_buf_alloc(int major, int minor, - int icanon) + unsigned icanon) { struct tty_audit_buf *buf; @@ -239,7 +239,8 @@ int tty_audit_push_task(struct task_struct *tsk, kuid_t loginuid, u32 sessionid) * if TTY auditing is disabled or out of memory. Otherwise, return a new * reference to the buffer. */ -static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty) +static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, + unsigned icanon) { struct tty_audit_buf *buf, *buf2; @@ -257,7 +258,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty) buf2 = tty_audit_buf_alloc(tty->driver->major, tty->driver->minor_start + tty->index, - tty->icanon); + icanon); if (buf2 == NULL) { audit_log_lost("out of memory in TTY auditing"); return NULL; @@ -287,7 +288,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty) * Audit @data of @size from @tty, if necessary. */ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, - size_t size) + size_t size, unsigned icanon) { struct tty_audit_buf *buf; int major, minor; @@ -299,7 +300,7 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, && tty->driver->subtype == PTY_TYPE_MASTER) return; - buf = tty_audit_buf_get(tty); + buf = tty_audit_buf_get(tty, icanon); if (!buf) return; @@ -307,11 +308,11 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, major = tty->driver->major; minor = tty->driver->minor_start + tty->index; if (buf->major != major || buf->minor != minor - || buf->icanon != tty->icanon) { + || buf->icanon != icanon) { tty_audit_buf_push_current(buf); buf->major = major; buf->minor = minor; - buf->icanon = tty->icanon; + buf->icanon = icanon; } do { size_t run; diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 91e326f..45d9161 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -27,19 +27,21 @@ * Locking: none */ -void tty_buffer_free_all(struct tty_struct *tty) +void tty_buffer_free_all(struct tty_port *port) { + struct tty_bufhead *buf = &port->buf; struct tty_buffer *thead; - while ((thead = tty->buf.head) != NULL) { - tty->buf.head = thead->next; + + while ((thead = buf->head) != NULL) { + buf->head = thead->next; kfree(thead); } - while ((thead = tty->buf.free) != NULL) { - tty->buf.free = thead->next; + while ((thead = buf->free) != NULL) { + buf->free = thead->next; kfree(thead); } - tty->buf.tail = NULL; - tty->buf.memory_used = 0; + buf->tail = NULL; + buf->memory_used = 0; } /** @@ -54,11 +56,11 @@ void tty_buffer_free_all(struct tty_struct *tty) * Locking: Caller must hold tty->buf.lock */ -static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) +static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) { struct tty_buffer *p; - if (tty->buf.memory_used + size > 65536) + if (port->buf.memory_used + size > 65536) return NULL; p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); if (p == NULL) @@ -70,7 +72,7 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) p->read = 0; p->char_buf_ptr = (char *)(p->data); p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; - tty->buf.memory_used += size; + port->buf.memory_used += size; return p; } @@ -85,17 +87,19 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) * Locking: Caller must hold tty->buf.lock */ -static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b) +static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) { + struct tty_bufhead *buf = &port->buf; + /* Dumb strategy for now - should keep some stats */ - tty->buf.memory_used -= b->size; - WARN_ON(tty->buf.memory_used < 0); + buf->memory_used -= b->size; + WARN_ON(buf->memory_used < 0); if (b->size >= 512) kfree(b); else { - b->next = tty->buf.free; - tty->buf.free = b; + b->next = buf->free; + buf->free = b; } } @@ -110,15 +114,16 @@ static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b) * Locking: Caller must hold tty->buf.lock */ -static void __tty_buffer_flush(struct tty_struct *tty) +static void __tty_buffer_flush(struct tty_port *port) { + struct tty_bufhead *buf = &port->buf; struct tty_buffer *thead; - while ((thead = tty->buf.head) != NULL) { - tty->buf.head = thead->next; - tty_buffer_free(tty, thead); + while ((thead = buf->head) != NULL) { + buf->head = thead->next; + tty_buffer_free(port, thead); } - tty->buf.tail = NULL; + buf->tail = NULL; } /** @@ -134,21 +139,24 @@ static void __tty_buffer_flush(struct tty_struct *tty) void tty_buffer_flush(struct tty_struct *tty) { + struct tty_port *port = tty->port; + struct tty_bufhead *buf = &port->buf; unsigned long flags; - spin_lock_irqsave(&tty->buf.lock, flags); + + spin_lock_irqsave(&buf->lock, flags); /* If the data is being pushed to the tty layer then we can't process it here. Instead set a flag and the flush_to_ldisc path will process the flush request before it exits */ - if (test_bit(TTY_FLUSHING, &tty->flags)) { - set_bit(TTY_FLUSHPENDING, &tty->flags); - spin_unlock_irqrestore(&tty->buf.lock, flags); + if (test_bit(TTYP_FLUSHING, &port->iflags)) { + set_bit(TTYP_FLUSHPENDING, &port->iflags); + spin_unlock_irqrestore(&buf->lock, flags); wait_event(tty->read_wait, - test_bit(TTY_FLUSHPENDING, &tty->flags) == 0); + test_bit(TTYP_FLUSHPENDING, &port->iflags) == 0); return; } else - __tty_buffer_flush(tty); - spin_unlock_irqrestore(&tty->buf.lock, flags); + __tty_buffer_flush(port); + spin_unlock_irqrestore(&buf->lock, flags); } /** @@ -163,9 +171,9 @@ void tty_buffer_flush(struct tty_struct *tty) * Locking: Caller must hold tty->buf.lock */ -static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) +static struct tty_buffer *tty_buffer_find(struct tty_port *port, size_t size) { - struct tty_buffer **tbh = &tty->buf.free; + struct tty_buffer **tbh = &port->buf.free; while ((*tbh) != NULL) { struct tty_buffer *t = *tbh; if (t->size >= size) { @@ -174,14 +182,14 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) t->used = 0; t->commit = 0; t->read = 0; - tty->buf.memory_used += t->size; + port->buf.memory_used += t->size; return t; } tbh = &((*tbh)->next); } /* Round the buffer size out */ size = (size + 0xFF) & ~0xFF; - return tty_buffer_alloc(tty, size); + return tty_buffer_alloc(port, size); /* Should possibly check if this fails for the largest buffer we have queued and recycle that ? */ } @@ -192,29 +200,31 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) * * Make at least size bytes of linear space available for the tty * buffer. If we fail return the size we managed to find. - * Locking: Caller must hold tty->buf.lock + * Locking: Caller must hold port->buf.lock */ -static int __tty_buffer_request_room(struct tty_struct *tty, size_t size) +static int __tty_buffer_request_room(struct tty_port *port, size_t size) { + struct tty_bufhead *buf = &port->buf; struct tty_buffer *b, *n; int left; /* OPTIMISATION: We could keep a per tty "zero" sized buffer to remove this conditional if its worth it. This would be invisible to the callers */ - if ((b = tty->buf.tail) != NULL) + b = buf->tail; + if (b != NULL) left = b->size - b->used; else left = 0; if (left < size) { /* This is the slow path - looking for new buffers to use */ - if ((n = tty_buffer_find(tty, size)) != NULL) { + if ((n = tty_buffer_find(port, size)) != NULL) { if (b != NULL) { b->next = n; b->commit = b->used; } else - tty->buf.head = n; - tty->buf.tail = n; + buf->head = n; + buf->tail = n; } else size = left; } @@ -231,16 +241,17 @@ static int __tty_buffer_request_room(struct tty_struct *tty, size_t size) * Make at least size bytes of linear space available for the tty * buffer. If we fail return the size we managed to find. * - * Locking: Takes tty->buf.lock + * Locking: Takes port->buf.lock */ int tty_buffer_request_room(struct tty_struct *tty, size_t size) { + struct tty_port *port = tty->port; unsigned long flags; int length; - spin_lock_irqsave(&tty->buf.lock, flags); - length = __tty_buffer_request_room(tty, size); - spin_unlock_irqrestore(&tty->buf.lock, flags); + spin_lock_irqsave(&port->buf.lock, flags); + length = __tty_buffer_request_room(port, size); + spin_unlock_irqrestore(&port->buf.lock, flags); return length; } EXPORT_SYMBOL_GPL(tty_buffer_request_room); @@ -255,12 +266,13 @@ EXPORT_SYMBOL_GPL(tty_buffer_request_room); * Queue a series of bytes to the tty buffering. All the characters * passed are marked with the supplied flag. Returns the number added. * - * Locking: Called functions may take tty->buf.lock + * Locking: Called functions may take port->buf.lock */ int tty_insert_flip_string_fixed_flag(struct tty_struct *tty, const unsigned char *chars, char flag, size_t size) { + struct tty_bufhead *buf = &tty->port->buf; int copied = 0; do { int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); @@ -268,18 +280,18 @@ int tty_insert_flip_string_fixed_flag(struct tty_struct *tty, unsigned long flags; struct tty_buffer *tb; - spin_lock_irqsave(&tty->buf.lock, flags); - space = __tty_buffer_request_room(tty, goal); - tb = tty->buf.tail; + spin_lock_irqsave(&buf->lock, flags); + space = __tty_buffer_request_room(tty->port, goal); + tb = buf->tail; /* If there is no space then tb may be NULL */ if (unlikely(space == 0)) { - spin_unlock_irqrestore(&tty->buf.lock, flags); + spin_unlock_irqrestore(&buf->lock, flags); break; } memcpy(tb->char_buf_ptr + tb->used, chars, space); memset(tb->flag_buf_ptr + tb->used, flag, space); tb->used += space; - spin_unlock_irqrestore(&tty->buf.lock, flags); + spin_unlock_irqrestore(&buf->lock, flags); copied += space; chars += space; /* There is a small chance that we need to split the data over @@ -300,12 +312,13 @@ EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag); * the flags array indicates the status of the character. Returns the * number added. * - * Locking: Called functions may take tty->buf.lock + * Locking: Called functions may take port->buf.lock */ int tty_insert_flip_string_flags(struct tty_struct *tty, const unsigned char *chars, const char *flags, size_t size) { + struct tty_bufhead *buf = &tty->port->buf; int copied = 0; do { int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); @@ -313,18 +326,18 @@ int tty_insert_flip_string_flags(struct tty_struct *tty, unsigned long __flags; struct tty_buffer *tb; - spin_lock_irqsave(&tty->buf.lock, __flags); - space = __tty_buffer_request_room(tty, goal); - tb = tty->buf.tail; + spin_lock_irqsave(&buf->lock, __flags); + space = __tty_buffer_request_room(tty->port, goal); + tb = buf->tail; /* If there is no space then tb may be NULL */ if (unlikely(space == 0)) { - spin_unlock_irqrestore(&tty->buf.lock, __flags); + spin_unlock_irqrestore(&buf->lock, __flags); break; } memcpy(tb->char_buf_ptr + tb->used, chars, space); memcpy(tb->flag_buf_ptr + tb->used, flags, space); tb->used += space; - spin_unlock_irqrestore(&tty->buf.lock, __flags); + spin_unlock_irqrestore(&buf->lock, __flags); copied += space; chars += space; flags += space; @@ -342,18 +355,23 @@ EXPORT_SYMBOL(tty_insert_flip_string_flags); * Takes any pending buffers and transfers their ownership to the * ldisc side of the queue. It then schedules those characters for * processing by the line discipline. + * Note that this function can only be used when the low_latency flag + * is unset. Otherwise the workqueue won't be flushed. * - * Locking: Takes tty->buf.lock + * Locking: Takes port->buf.lock */ void tty_schedule_flip(struct tty_struct *tty) { + struct tty_bufhead *buf = &tty->port->buf; unsigned long flags; - spin_lock_irqsave(&tty->buf.lock, flags); - if (tty->buf.tail != NULL) - tty->buf.tail->commit = tty->buf.tail->used; - spin_unlock_irqrestore(&tty->buf.lock, flags); - schedule_work(&tty->buf.work); + WARN_ON(tty->low_latency); + + spin_lock_irqsave(&buf->lock, flags); + if (buf->tail != NULL) + buf->tail->commit = buf->tail->used; + spin_unlock_irqrestore(&buf->lock, flags); + schedule_work(&buf->work); } EXPORT_SYMBOL(tty_schedule_flip); @@ -369,26 +387,27 @@ EXPORT_SYMBOL(tty_schedule_flip); * that need their own block copy routines into the buffer. There is no * guarantee the buffer is a DMA target! * - * Locking: May call functions taking tty->buf.lock + * Locking: May call functions taking port->buf.lock */ int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, - size_t size) + size_t size) { + struct tty_bufhead *buf = &tty->port->buf; int space; unsigned long flags; struct tty_buffer *tb; - spin_lock_irqsave(&tty->buf.lock, flags); - space = __tty_buffer_request_room(tty, size); + spin_lock_irqsave(&buf->lock, flags); + space = __tty_buffer_request_room(tty->port, size); - tb = tty->buf.tail; + tb = buf->tail; if (likely(space)) { *chars = tb->char_buf_ptr + tb->used; memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); tb->used += space; } - spin_unlock_irqrestore(&tty->buf.lock, flags); + spin_unlock_irqrestore(&buf->lock, flags); return space; } EXPORT_SYMBOL_GPL(tty_prepare_flip_string); @@ -406,26 +425,27 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string); * that need their own block copy routines into the buffer. There is no * guarantee the buffer is a DMA target! * - * Locking: May call functions taking tty->buf.lock + * Locking: May call functions taking port->buf.lock */ int tty_prepare_flip_string_flags(struct tty_struct *tty, unsigned char **chars, char **flags, size_t size) { + struct tty_bufhead *buf = &tty->port->buf; int space; unsigned long __flags; struct tty_buffer *tb; - spin_lock_irqsave(&tty->buf.lock, __flags); - space = __tty_buffer_request_room(tty, size); + spin_lock_irqsave(&buf->lock, __flags); + space = __tty_buffer_request_room(tty->port, size); - tb = tty->buf.tail; + tb = buf->tail; if (likely(space)) { *chars = tb->char_buf_ptr + tb->used; *flags = tb->flag_buf_ptr + tb->used; tb->used += space; } - spin_unlock_irqrestore(&tty->buf.lock, __flags); + spin_unlock_irqrestore(&buf->lock, __flags); return space; } EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); @@ -446,20 +466,25 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); static void flush_to_ldisc(struct work_struct *work) { - struct tty_struct *tty = - container_of(work, struct tty_struct, buf.work); + struct tty_port *port = container_of(work, struct tty_port, buf.work); + struct tty_bufhead *buf = &port->buf; + struct tty_struct *tty; unsigned long flags; struct tty_ldisc *disc; + tty = port->itty; + if (WARN_RATELIMIT(tty == NULL, "tty is NULL\n")) + return; + disc = tty_ldisc_ref(tty); if (disc == NULL) /* !TTY_LDISC */ return; - spin_lock_irqsave(&tty->buf.lock, flags); + spin_lock_irqsave(&buf->lock, flags); - if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) { + if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) { struct tty_buffer *head; - while ((head = tty->buf.head) != NULL) { + while ((head = buf->head) != NULL) { int count; char *char_buf; unsigned char *flag_buf; @@ -468,14 +493,14 @@ static void flush_to_ldisc(struct work_struct *work) if (!count) { if (head->next == NULL) break; - tty->buf.head = head->next; - tty_buffer_free(tty, head); + buf->head = head->next; + tty_buffer_free(port, head); continue; } /* Ldisc or user is trying to flush the buffers we are feeding to the ldisc, stop feeding the line discipline as we want to empty the queue */ - if (test_bit(TTY_FLUSHPENDING, &tty->flags)) + if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) break; if (!tty->receive_room) break; @@ -484,22 +509,22 @@ static void flush_to_ldisc(struct work_struct *work) char_buf = head->char_buf_ptr + head->read; flag_buf = head->flag_buf_ptr + head->read; head->read += count; - spin_unlock_irqrestore(&tty->buf.lock, flags); + spin_unlock_irqrestore(&buf->lock, flags); disc->ops->receive_buf(tty, char_buf, flag_buf, count); - spin_lock_irqsave(&tty->buf.lock, flags); + spin_lock_irqsave(&buf->lock, flags); } - clear_bit(TTY_FLUSHING, &tty->flags); + clear_bit(TTYP_FLUSHING, &port->iflags); } /* We may have a deferred request to flush the input buffer, if so pull the chain under the lock and empty the queue */ - if (test_bit(TTY_FLUSHPENDING, &tty->flags)) { - __tty_buffer_flush(tty); - clear_bit(TTY_FLUSHPENDING, &tty->flags); + if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) { + __tty_buffer_flush(port); + clear_bit(TTYP_FLUSHPENDING, &port->iflags); wake_up(&tty->read_wait); } - spin_unlock_irqrestore(&tty->buf.lock, flags); + spin_unlock_irqrestore(&buf->lock, flags); tty_ldisc_deref(disc); } @@ -514,7 +539,8 @@ static void flush_to_ldisc(struct work_struct *work) */ void tty_flush_to_ldisc(struct tty_struct *tty) { - flush_work(&tty->buf.work); + if (!tty->low_latency) + flush_work(&tty->port->buf.work); } /** @@ -532,16 +558,18 @@ void tty_flush_to_ldisc(struct tty_struct *tty) void tty_flip_buffer_push(struct tty_struct *tty) { + struct tty_bufhead *buf = &tty->port->buf; unsigned long flags; - spin_lock_irqsave(&tty->buf.lock, flags); - if (tty->buf.tail != NULL) - tty->buf.tail->commit = tty->buf.tail->used; - spin_unlock_irqrestore(&tty->buf.lock, flags); + + spin_lock_irqsave(&buf->lock, flags); + if (buf->tail != NULL) + buf->tail->commit = buf->tail->used; + spin_unlock_irqrestore(&buf->lock, flags); if (tty->low_latency) - flush_to_ldisc(&tty->buf.work); + flush_to_ldisc(&buf->work); else - schedule_work(&tty->buf.work); + schedule_work(&buf->work); } EXPORT_SYMBOL(tty_flip_buffer_push); @@ -555,13 +583,15 @@ EXPORT_SYMBOL(tty_flip_buffer_push); * Locking: none */ -void tty_buffer_init(struct tty_struct *tty) +void tty_buffer_init(struct tty_port *port) { - spin_lock_init(&tty->buf.lock); - tty->buf.head = NULL; - tty->buf.tail = NULL; - tty->buf.free = NULL; - tty->buf.memory_used = 0; - INIT_WORK(&tty->buf.work, flush_to_ldisc); + struct tty_bufhead *buf = &port->buf; + + spin_lock_init(&buf->lock); + buf->head = NULL; + buf->tail = NULL; + buf->free = NULL; + buf->memory_used = 0; + INIT_WORK(&buf->work, flush_to_ldisc); } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 2ea176b..739ea86 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -186,7 +186,6 @@ void free_tty_struct(struct tty_struct *tty) if (tty->dev) put_device(tty->dev); kfree(tty->write_buf); - tty_buffer_free_all(tty); tty->magic = 0xDEADDEAD; kfree(tty); } @@ -1417,6 +1416,8 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) "%s: %s driver does not set tty->port. This will crash the kernel later. Fix the driver!\n", __func__, tty->driver->name); + tty->port->itty = tty; + /* * Structures all installed ... call the ldisc open routines. * If we fail here just call release_tty to clean up. No need @@ -1552,6 +1553,7 @@ static void release_tty(struct tty_struct *tty, int idx) tty->ops->shutdown(tty); tty_free_termios(tty); tty_driver_remove_tty(tty->driver, tty); + tty->port->itty = NULL; if (tty->link) tty_kref_put(tty->link); @@ -1625,7 +1627,6 @@ int tty_release(struct inode *inode, struct file *filp) struct tty_struct *tty = file_tty(filp); struct tty_struct *o_tty; int pty_master, tty_closing, o_tty_closing, do_sleep; - int devpts; int idx; char buf[64]; @@ -1640,7 +1641,6 @@ int tty_release(struct inode *inode, struct file *filp) idx = tty->index; pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER); - devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0; /* Review: parallel close */ o_tty = tty->link; @@ -1799,9 +1799,6 @@ int tty_release(struct inode *inode, struct file *filp) release_tty(tty, idx); mutex_unlock(&tty_mutex); - /* Make this pty number available for reallocation */ - if (devpts) - devpts_kill_index(inode, idx); return 0; } @@ -2690,6 +2687,11 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case TIOCNXCL: clear_bit(TTY_EXCLUSIVE, &tty->flags); return 0; + case TIOCGEXCL: + { + int excl = test_bit(TTY_EXCLUSIVE, &tty->flags); + return put_user(excl, (int __user *)p); + } case TIOCNOTTY: if (current->signal->tty != tty) return -ENOTTY; @@ -2937,19 +2939,13 @@ void initialize_tty_struct(struct tty_struct *tty, tty_ldisc_init(tty); tty->session = NULL; tty->pgrp = NULL; - tty->overrun_time = jiffies; - tty_buffer_init(tty); mutex_init(&tty->legacy_mutex); mutex_init(&tty->termios_mutex); mutex_init(&tty->ldisc_mutex); init_waitqueue_head(&tty->write_wait); init_waitqueue_head(&tty->read_wait); INIT_WORK(&tty->hangup_work, do_tty_hangup); - mutex_init(&tty->atomic_read_lock); mutex_init(&tty->atomic_write_lock); - mutex_init(&tty->output_lock); - mutex_init(&tty->echo_lock); - spin_lock_init(&tty->read_lock); spin_lock_init(&tty->ctrl_lock); INIT_LIST_HEAD(&tty->tty_files); INIT_WORK(&tty->SAK_work, do_SAK_work); diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 12b1fa0..8481b29 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -1118,7 +1118,6 @@ EXPORT_SYMBOL_GPL(tty_perform_flush); int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { - unsigned long flags; int retval; switch (cmd) { @@ -1153,26 +1152,6 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, return 0; case TCFLSH: return tty_perform_flush(tty, arg); - case TIOCPKT: - { - int pktmode; - - if (tty->driver->type != TTY_DRIVER_TYPE_PTY || - tty->driver->subtype != PTY_TYPE_MASTER) - return -ENOTTY; - if (get_user(pktmode, (int __user *) arg)) - return -EFAULT; - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (pktmode) { - if (!tty->packet) { - tty->packet = 1; - tty->link->ctrl_status = 0; - } - } else - tty->packet = 0; - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - return 0; - } default: /* Try the mode commands */ return tty_mode_ioctl(tty, file, cmd, arg); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 0f2a2c5..c578229 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -26,7 +26,7 @@ * callers who will do ldisc lookups and cannot sleep. */ -static DEFINE_SPINLOCK(tty_ldisc_lock); +static DEFINE_RAW_SPINLOCK(tty_ldisc_lock); static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); /* Line disc dispatch table */ static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; @@ -49,21 +49,21 @@ static void put_ldisc(struct tty_ldisc *ld) * If this is the last user, free the ldisc, and * release the ldisc ops. * - * We really want an "atomic_dec_and_lock_irqsave()", + * We really want an "atomic_dec_and_raw_lock_irqsave()", * but we don't have it, so this does it by hand. */ - local_irq_save(flags); - if (atomic_dec_and_lock(&ld->users, &tty_ldisc_lock)) { + raw_spin_lock_irqsave(&tty_ldisc_lock, flags); + if (atomic_dec_and_test(&ld->users)) { struct tty_ldisc_ops *ldo = ld->ops; ldo->refcount--; module_put(ldo->owner); - spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); kfree(ld); return; } - local_irq_restore(flags); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); wake_up(&ld->wq_idle); } @@ -88,11 +88,11 @@ int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) if (disc < N_TTY || disc >= NR_LDISCS) return -EINVAL; - spin_lock_irqsave(&tty_ldisc_lock, flags); + raw_spin_lock_irqsave(&tty_ldisc_lock, flags); tty_ldiscs[disc] = new_ldisc; new_ldisc->num = disc; new_ldisc->refcount = 0; - spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); return ret; } @@ -118,12 +118,12 @@ int tty_unregister_ldisc(int disc) if (disc < N_TTY || disc >= NR_LDISCS) return -EINVAL; - spin_lock_irqsave(&tty_ldisc_lock, flags); + raw_spin_lock_irqsave(&tty_ldisc_lock, flags); if (tty_ldiscs[disc]->refcount) ret = -EBUSY; else tty_ldiscs[disc] = NULL; - spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); return ret; } @@ -134,7 +134,7 @@ static struct tty_ldisc_ops *get_ldops(int disc) unsigned long flags; struct tty_ldisc_ops *ldops, *ret; - spin_lock_irqsave(&tty_ldisc_lock, flags); + raw_spin_lock_irqsave(&tty_ldisc_lock, flags); ret = ERR_PTR(-EINVAL); ldops = tty_ldiscs[disc]; if (ldops) { @@ -144,7 +144,7 @@ static struct tty_ldisc_ops *get_ldops(int disc) ret = ldops; } } - spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); return ret; } @@ -152,10 +152,10 @@ static void put_ldops(struct tty_ldisc_ops *ldops) { unsigned long flags; - spin_lock_irqsave(&tty_ldisc_lock, flags); + raw_spin_lock_irqsave(&tty_ldisc_lock, flags); ldops->refcount--; module_put(ldops->owner); - spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); } /** @@ -287,11 +287,11 @@ static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty) unsigned long flags; struct tty_ldisc *ld; - spin_lock_irqsave(&tty_ldisc_lock, flags); + raw_spin_lock_irqsave(&tty_ldisc_lock, flags); ld = NULL; if (test_bit(TTY_LDISC, &tty->flags)) ld = get_ldisc(tty->ldisc); - spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); return ld; } @@ -512,7 +512,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) static int tty_ldisc_halt(struct tty_struct *tty) { clear_bit(TTY_LDISC, &tty->flags); - return cancel_work_sync(&tty->buf.work); + return cancel_work_sync(&tty->port->buf.work); } /** @@ -525,7 +525,7 @@ static void tty_ldisc_flush_works(struct tty_struct *tty) { flush_work(&tty->hangup_work); flush_work(&tty->SAK_work); - flush_work(&tty->buf.work); + flush_work(&tty->port->buf.work); } /** @@ -704,9 +704,9 @@ enable: /* Restart the work queue in case no characters kick it off. Safe if already running */ if (work) - schedule_work(&tty->buf.work); + schedule_work(&tty->port->buf.work); if (o_work) - schedule_work(&o_tty->buf.work); + schedule_work(&o_tty->port->buf.work); mutex_unlock(&tty->ldisc_mutex); tty_unlock(tty); return retval; @@ -817,7 +817,7 @@ void tty_ldisc_hangup(struct tty_struct *tty) */ clear_bit(TTY_LDISC, &tty->flags); tty_unlock(tty); - cancel_work_sync(&tty->buf.work); + cancel_work_sync(&tty->port->buf.work); mutex_unlock(&tty->ldisc_mutex); retry: tty_lock(tty); @@ -897,6 +897,11 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) static void tty_ldisc_kill(struct tty_struct *tty) { + /* There cannot be users from userspace now. But there still might be + * drivers holding a reference via tty_ldisc_ref. Do not steal them the + * ldisc until they are done. */ + tty_ldisc_wait_idle(tty, MAX_SCHEDULE_TIMEOUT); + mutex_lock(&tty->ldisc_mutex); /* * Now kill off the ldisc diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c index 67feac9..2e41abe 100644 --- a/drivers/tty/tty_mutex.c +++ b/drivers/tty/tty_mutex.c @@ -19,7 +19,7 @@ static void __lockfunc tty_lock_nested(struct tty_struct *tty, unsigned int subclass) { if (tty->magic != TTY_MAGIC) { - printk(KERN_ERR "L Bad %p\n", tty); + pr_err("L Bad %p\n", tty); WARN_ON(1); return; } @@ -36,7 +36,7 @@ EXPORT_SYMBOL(tty_lock); void __lockfunc tty_unlock(struct tty_struct *tty) { if (tty->magic != TTY_MAGIC) { - printk(KERN_ERR "U Bad %p\n", tty); + pr_err("U Bad %p\n", tty); WARN_ON(1); return; } diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index d7bdd8d..b7ff59d 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -21,6 +21,7 @@ void tty_port_init(struct tty_port *port) { memset(port, 0, sizeof(*port)); + tty_buffer_init(port); init_waitqueue_head(&port->open_wait); init_waitqueue_head(&port->close_wait); init_waitqueue_head(&port->delta_msr_wait); @@ -121,12 +122,27 @@ void tty_port_free_xmit_buf(struct tty_port *port) } EXPORT_SYMBOL(tty_port_free_xmit_buf); +/** + * tty_port_destroy -- destroy inited port + * @port: tty port to be doestroyed + * + * When a port was initialized using tty_port_init, one has to destroy the + * port by this function. Either indirectly by using tty_port refcounting + * (tty_port_put) or directly if refcounting is not used. + */ +void tty_port_destroy(struct tty_port *port) +{ + tty_buffer_free_all(port); +} +EXPORT_SYMBOL(tty_port_destroy); + static void tty_port_destructor(struct kref *kref) { struct tty_port *port = container_of(kref, struct tty_port, kref); if (port->xmit_buf) free_page((unsigned long)port->xmit_buf); - if (port->ops->destruct) + tty_port_destroy(port); + if (port->ops && port->ops->destruct) port->ops->destruct(port); else kfree(port); diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 8e9b4be..60b7b69 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -341,15 +341,11 @@ int paste_selection(struct tty_struct *tty) struct tty_ldisc *ld; DECLARE_WAITQUEUE(wait, current); - console_lock(); poke_blanked_console(); console_unlock(); - /* FIXME: wtf is this supposed to achieve ? */ - ld = tty_ldisc_ref(tty); - if (!ld) - ld = tty_ldisc_ref_wait(tty); + ld = tty_ldisc_ref_wait(tty); /* FIXME: this is completely unsafe */ add_wait_queue(&vc->paste_wait, &wait); @@ -361,8 +357,7 @@ int paste_selection(struct tty_struct *tty) } count = sel_buffer_lth - pasted; count = min(count, tty->receive_room); - tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted, - NULL, count); + ld->ops->receive_buf(tty, sel_buffer + pasted, NULL, count); pasted += count; } remove_wait_queue(&vc->paste_wait, &wait); diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index f87d7e8..607636b 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -779,6 +779,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ con_set_default_unimap(vc); vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); if (!vc->vc_screenbuf) { + tty_port_destroy(&vc->port); kfree(vc); vc_cons[currcons].d = NULL; return -ENOMEM; @@ -999,8 +1000,10 @@ void vc_deallocate(unsigned int currcons) put_pid(vc->vt_pid); module_put(vc->vc_sw->owner); kfree(vc->vc_screenbuf); - if (currcons >= MIN_NR_CONSOLES) + if (currcons >= MIN_NR_CONSOLES) { + tty_port_destroy(&vc->port); kfree(vc); + } vc_cons[currcons].d = NULL; } } diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index f173952..d0f9548 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -1145,8 +1145,10 @@ int gserial_setup(struct usb_gadget *g, unsigned count) return status; fail: - while (count--) + while (count--) { + tty_port_destroy(&ports[count].port->port); kfree(ports[count].port); + } put_tty_driver(gs_tty_driver); gs_tty_driver = NULL; return status; @@ -1200,6 +1202,7 @@ void gserial_cleanup(void) WARN_ON(port->port_usb != NULL); + tty_port_destroy(&port->port); kfree(port); } n_ports = 0; diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 73b8e05..64bda13 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -597,6 +597,7 @@ static void port_release(struct device *dev) kfifo_free(&port->write_fifo); kfree(port->interrupt_in_buffer); kfree(port->interrupt_out_buffer); + tty_port_destroy(&port->port); kfree(port); } |