diff options
Diffstat (limited to 'drivers/tty')
37 files changed, 1049 insertions, 484 deletions
diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index 6f2c9809f1fb..e371753ba921 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig @@ -19,6 +19,11 @@ config HVC_CONSOLE console. This driver allows each pSeries partition to have a console which is accessed via the HMC. +config HVC_OLD_HVSI + bool "Old driver for pSeries serial port (/dev/hvsi*)" + depends on HVC_CONSOLE + default n + config HVC_ISERIES bool "iSeries Hypervisor Virtual Console support" depends on PPC_ISERIES diff --git a/drivers/tty/hvc/Makefile b/drivers/tty/hvc/Makefile index 40a25d93fe52..e29205316376 100644 --- a/drivers/tty/hvc/Makefile +++ b/drivers/tty/hvc/Makefile @@ -1,4 +1,5 @@ -obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi.o +obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi_lib.o +obj-$(CONFIG_HVC_OLD_HVSI) += hvsi.o obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o obj-$(CONFIG_HVC_TILE) += hvc_tile.o diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index e9cba13ee800..e1aaf4f309b3 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -39,6 +39,7 @@ #include <linux/delay.h> #include <linux/freezer.h> #include <linux/slab.h> +#include <linux/serial_core.h> #include <asm/uaccess.h> @@ -163,8 +164,10 @@ static void hvc_console_print(struct console *co, const char *b, } else { r = cons_ops[index]->put_chars(vtermnos[index], c, i); if (r <= 0) { - /* throw away chars on error */ - i = 0; + /* throw away characters on error + * but spin in case of -EAGAIN */ + if (r != -EAGAIN) + i = 0; } else if (r > 0) { i -= r; if (i > 0) @@ -184,7 +187,7 @@ static struct tty_driver *hvc_console_device(struct console *c, int *index) } static int __init hvc_console_setup(struct console *co, char *options) -{ +{ if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES) return -ENODEV; @@ -448,7 +451,7 @@ static int hvc_push(struct hvc_struct *hp) n = hp->ops->put_chars(hp->vtermno, hp->outbuf, hp->n_outbuf); if (n <= 0) { - if (n == 0) { + if (n == 0 || n == -EAGAIN) { hp->do_wakeup = 1; return 0; } @@ -745,6 +748,58 @@ static int khvcd(void *unused) return 0; } +static int hvc_tiocmget(struct tty_struct *tty) +{ + struct hvc_struct *hp = tty->driver_data; + + if (!hp || !hp->ops->tiocmget) + return -EINVAL; + return hp->ops->tiocmget(hp); +} + +static int hvc_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct hvc_struct *hp = tty->driver_data; + + if (!hp || !hp->ops->tiocmset) + return -EINVAL; + return hp->ops->tiocmset(hp, set, clear); +} + +#ifdef CONFIG_CONSOLE_POLL +int hvc_poll_init(struct tty_driver *driver, int line, char *options) +{ + return 0; +} + +static int hvc_poll_get_char(struct tty_driver *driver, int line) +{ + struct tty_struct *tty = driver->ttys[0]; + struct hvc_struct *hp = tty->driver_data; + int n; + char ch; + + n = hp->ops->get_chars(hp->vtermno, &ch, 1); + + if (n == 0) + return NO_POLL_CHAR; + + return ch; +} + +static void hvc_poll_put_char(struct tty_driver *driver, int line, char ch) +{ + struct tty_struct *tty = driver->ttys[0]; + struct hvc_struct *hp = tty->driver_data; + int n; + + do { + n = hp->ops->put_chars(hp->vtermno, &ch, 1); + } while (n <= 0); +} +#endif + static const struct tty_operations hvc_ops = { .open = hvc_open, .close = hvc_close, @@ -753,6 +808,13 @@ static const struct tty_operations hvc_ops = { .unthrottle = hvc_unthrottle, .write_room = hvc_write_room, .chars_in_buffer = hvc_chars_in_buffer, + .tiocmget = hvc_tiocmget, + .tiocmset = hvc_tiocmset, +#ifdef CONFIG_CONSOLE_POLL + .poll_init = hvc_poll_init, + .poll_get_char = hvc_poll_get_char, + .poll_put_char = hvc_poll_put_char, +#endif }; struct hvc_struct *hvc_alloc(uint32_t vtermno, int data, diff --git a/drivers/tty/hvc/hvc_console.h b/drivers/tty/hvc/hvc_console.h index 54381eba4e4a..c335a1492a54 100644 --- a/drivers/tty/hvc/hvc_console.h +++ b/drivers/tty/hvc/hvc_console.h @@ -73,6 +73,10 @@ struct hv_ops { int (*notifier_add)(struct hvc_struct *hp, int irq); void (*notifier_del)(struct hvc_struct *hp, int irq); void (*notifier_hangup)(struct hvc_struct *hp, int irq); + + /* tiocmget/set implementation */ + int (*tiocmget)(struct hvc_struct *hp); + int (*tiocmset)(struct hvc_struct *hp, unsigned int set, unsigned int clear); }; /* Register a vterm and a slot index for use as a console (console_init) */ diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c index e6eea1485244..130aace67f31 100644 --- a/drivers/tty/hvc/hvc_vio.c +++ b/drivers/tty/hvc/hvc_vio.c @@ -27,15 +27,27 @@ * 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 + * + * TODO: + * + * - handle error in sending hvsi protocol packets + * - retry nego on subsequent sends ? */ +#undef DEBUG + #include <linux/types.h> #include <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/console.h> #include <asm/hvconsole.h> #include <asm/vio.h> #include <asm/prom.h> #include <asm/firmware.h> +#include <asm/hvsi.h> +#include <asm/udbg.h> #include "hvc_console.h" @@ -43,59 +55,236 @@ static const char hvc_driver_name[] = "hvc_console"; static struct vio_device_id hvc_driver_table[] __devinitdata = { {"serial", "hvterm1"}, +#ifndef HVC_OLD_HVSI + {"serial", "hvterm-protocol"}, +#endif { "", "" } }; MODULE_DEVICE_TABLE(vio, hvc_driver_table); -static int filtered_get_chars(uint32_t vtermno, char *buf, int count) +typedef enum hv_protocol { + HV_PROTOCOL_RAW, + HV_PROTOCOL_HVSI +} hv_protocol_t; + +struct hvterm_priv { + u32 termno; /* HV term number */ + hv_protocol_t proto; /* Raw data or HVSI packets */ + struct hvsi_priv hvsi; /* HVSI specific data */ + spinlock_t buf_lock; + char buf[SIZE_VIO_GET_CHARS]; + int left; + int offset; +}; +static struct hvterm_priv *hvterm_privs[MAX_NR_HVC_CONSOLES]; +/* For early boot console */ +static struct hvterm_priv hvterm_priv0; + +static int hvterm_raw_get_chars(uint32_t vtermno, char *buf, int count) { - unsigned long got; - int i; + struct hvterm_priv *pv = hvterm_privs[vtermno]; + unsigned long i; + unsigned long flags; + int got; - /* - * Vio firmware will read up to SIZE_VIO_GET_CHARS at its own discretion - * so we play safe and avoid the situation where got > count which could - * overload the flip buffer. - */ - if (count < SIZE_VIO_GET_CHARS) - return -EAGAIN; + if (WARN_ON(!pv)) + return 0; - got = hvc_get_chars(vtermno, buf, count); + spin_lock_irqsave(&pv->buf_lock, flags); - /* - * Work around a HV bug where it gives us a null - * after every \r. -- paulus - */ - for (i = 1; i < got; ++i) { - if (buf[i] == 0 && buf[i-1] == '\r') { - --got; - if (i < got) - memmove(&buf[i], &buf[i+1], - got - i); + if (pv->left == 0) { + pv->offset = 0; + pv->left = hvc_get_chars(pv->termno, pv->buf, count); + + /* + * Work around a HV bug where it gives us a null + * after every \r. -- paulus + */ + for (i = 1; i < pv->left; ++i) { + if (pv->buf[i] == 0 && pv->buf[i-1] == '\r') { + --pv->left; + if (i < pv->left) { + memmove(&pv->buf[i], &pv->buf[i+1], + pv->left - i); + } + } } } + + got = min(count, pv->left); + memcpy(buf, &pv->buf[pv->offset], got); + pv->offset += got; + pv->left -= got; + + spin_unlock_irqrestore(&pv->buf_lock, flags); + return got; } -static const struct hv_ops hvc_get_put_ops = { - .get_chars = filtered_get_chars, - .put_chars = hvc_put_chars, +static int hvterm_raw_put_chars(uint32_t vtermno, const char *buf, int count) +{ + struct hvterm_priv *pv = hvterm_privs[vtermno]; + + if (WARN_ON(!pv)) + return 0; + + return hvc_put_chars(pv->termno, buf, count); +} + +static const struct hv_ops hvterm_raw_ops = { + .get_chars = hvterm_raw_get_chars, + .put_chars = hvterm_raw_put_chars, .notifier_add = notifier_add_irq, .notifier_del = notifier_del_irq, .notifier_hangup = notifier_hangup_irq, }; +static int hvterm_hvsi_get_chars(uint32_t vtermno, char *buf, int count) +{ + struct hvterm_priv *pv = hvterm_privs[vtermno]; + + if (WARN_ON(!pv)) + return 0; + + return hvsilib_get_chars(&pv->hvsi, buf, count); +} + +static int hvterm_hvsi_put_chars(uint32_t vtermno, const char *buf, int count) +{ + struct hvterm_priv *pv = hvterm_privs[vtermno]; + + if (WARN_ON(!pv)) + return 0; + + return hvsilib_put_chars(&pv->hvsi, buf, count); +} + +static int hvterm_hvsi_open(struct hvc_struct *hp, int data) +{ + struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; + int rc; + + pr_devel("HVSI@%x: open !\n", pv->termno); + + rc = notifier_add_irq(hp, data); + if (rc) + return rc; + + return hvsilib_open(&pv->hvsi, hp); +} + +static void hvterm_hvsi_close(struct hvc_struct *hp, int data) +{ + struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; + + pr_devel("HVSI@%x: do close !\n", pv->termno); + + hvsilib_close(&pv->hvsi, hp); + + notifier_del_irq(hp, data); +} + +void hvterm_hvsi_hangup(struct hvc_struct *hp, int data) +{ + struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; + + pr_devel("HVSI@%x: do hangup !\n", pv->termno); + + hvsilib_close(&pv->hvsi, hp); + + notifier_hangup_irq(hp, data); +} + +static int hvterm_hvsi_tiocmget(struct hvc_struct *hp) +{ + struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; + + if (!pv) + return -EINVAL; + return pv->hvsi.mctrl; +} + +static int hvterm_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set, + unsigned int clear) +{ + struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; + + pr_devel("HVSI@%x: Set modem control, set=%x,clr=%x\n", + pv->termno, set, clear); + + if (set & TIOCM_DTR) + hvsilib_write_mctrl(&pv->hvsi, 1); + else if (clear & TIOCM_DTR) + hvsilib_write_mctrl(&pv->hvsi, 0); + + return 0; +} + +static const struct hv_ops hvterm_hvsi_ops = { + .get_chars = hvterm_hvsi_get_chars, + .put_chars = hvterm_hvsi_put_chars, + .notifier_add = hvterm_hvsi_open, + .notifier_del = hvterm_hvsi_close, + .notifier_hangup = hvterm_hvsi_hangup, + .tiocmget = hvterm_hvsi_tiocmget, + .tiocmset = hvterm_hvsi_tiocmset, +}; + static int __devinit hvc_vio_probe(struct vio_dev *vdev, - const struct vio_device_id *id) + const struct vio_device_id *id) { + const struct hv_ops *ops; struct hvc_struct *hp; + struct hvterm_priv *pv; + hv_protocol_t proto; + int i, termno = -1; /* probed with invalid parameters. */ if (!vdev || !id) return -EPERM; - hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops, - MAX_VIO_PUT_CHARS); + if (of_device_is_compatible(vdev->dev.of_node, "hvterm1")) { + proto = HV_PROTOCOL_RAW; + ops = &hvterm_raw_ops; + } else if (of_device_is_compatible(vdev->dev.of_node, "hvterm-protocol")) { + proto = HV_PROTOCOL_HVSI; + ops = &hvterm_hvsi_ops; + } else { + pr_err("hvc_vio: Unkown protocol for %s\n", vdev->dev.of_node->full_name); + return -ENXIO; + } + + pr_devel("hvc_vio_probe() device %s, using %s protocol\n", + vdev->dev.of_node->full_name, + proto == HV_PROTOCOL_RAW ? "raw" : "hvsi"); + + /* Is it our boot one ? */ + if (hvterm_privs[0] == &hvterm_priv0 && + vdev->unit_address == hvterm_priv0.termno) { + pv = hvterm_privs[0]; + termno = 0; + pr_devel("->boot console, using termno 0\n"); + } + /* nope, allocate a new one */ + else { + for (i = 0; i < MAX_NR_HVC_CONSOLES && termno < 0; i++) + if (!hvterm_privs[i]) + termno = i; + pr_devel("->non-boot console, using termno %d\n", termno); + if (termno < 0) + return -ENODEV; + pv = kzalloc(sizeof(struct hvterm_priv), GFP_KERNEL); + if (!pv) + return -ENOMEM; + pv->termno = vdev->unit_address; + pv->proto = proto; + spin_lock_init(&pv->buf_lock); + hvterm_privs[termno] = pv; + hvsilib_init(&pv->hvsi, hvc_get_chars, hvc_put_chars, + pv->termno, 0); + } + + hp = hvc_alloc(termno, vdev->irq, ops, MAX_VIO_PUT_CHARS); if (IS_ERR(hp)) return PTR_ERR(hp); dev_set_drvdata(&vdev->dev, hp); @@ -106,8 +295,16 @@ static int __devinit hvc_vio_probe(struct vio_dev *vdev, static int __devexit hvc_vio_remove(struct vio_dev *vdev) { struct hvc_struct *hp = dev_get_drvdata(&vdev->dev); + int rc, termno; - return hvc_remove(hp); + termno = hp->vtermno; + rc = hvc_remove(hp); + if (rc == 0) { + if (hvterm_privs[termno] != &hvterm_priv0) + kfree(hvterm_privs[termno]); + hvterm_privs[termno] = NULL; + } + return rc; } static struct vio_driver hvc_vio_driver = { @@ -140,34 +337,149 @@ static void __exit hvc_vio_exit(void) } module_exit(hvc_vio_exit); -/* the device tree order defines our numbering */ -static int hvc_find_vtys(void) +static void udbg_hvc_putc(char c) { - struct device_node *vty; - int num_found = 0; + int count = -1; - for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; - vty = of_find_node_by_name(vty, "vty")) { - const uint32_t *vtermno; + if (c == '\n') + udbg_hvc_putc('\r'); - /* We have statically defined space for only a certain number - * of console adapters. - */ - if (num_found >= MAX_NR_HVC_CONSOLES) { - of_node_put(vty); + do { + switch(hvterm_priv0.proto) { + case HV_PROTOCOL_RAW: + count = hvterm_raw_put_chars(0, &c, 1); + break; + case HV_PROTOCOL_HVSI: + count = hvterm_hvsi_put_chars(0, &c, 1); break; } + } while(count == 0); +} + +static int udbg_hvc_getc_poll(void) +{ + int rc = 0; + char c; - vtermno = of_get_property(vty, "reg", NULL); - if (!vtermno) - continue; + switch(hvterm_priv0.proto) { + case HV_PROTOCOL_RAW: + rc = hvterm_raw_get_chars(0, &c, 1); + break; + case HV_PROTOCOL_HVSI: + rc = hvterm_hvsi_get_chars(0, &c, 1); + break; + } + if (!rc) + return -1; + return c; +} - if (of_device_is_compatible(vty, "hvterm1")) { - hvc_instantiate(*vtermno, num_found, &hvc_get_put_ops); - ++num_found; +static int udbg_hvc_getc(void) +{ + int ch; + for (;;) { + ch = udbg_hvc_getc_poll(); + if (ch == -1) { + /* This shouldn't be needed...but... */ + volatile unsigned long delay; + for (delay=0; delay < 2000000; delay++) + ; + } else { + return ch; } } +} + +void __init hvc_vio_init_early(void) +{ + struct device_node *stdout_node; + const u32 *termno; + const char *name; + const struct hv_ops *ops; + + /* find the boot console from /chosen/stdout */ + if (!of_chosen) + return; + name = of_get_property(of_chosen, "linux,stdout-path", NULL); + if (name == NULL) + return; + stdout_node = of_find_node_by_path(name); + if (!stdout_node) + return; + name = of_get_property(stdout_node, "name", NULL); + if (!name) { + printk(KERN_WARNING "stdout node missing 'name' property!\n"); + goto out; + } + + /* Check if it's a virtual terminal */ + if (strncmp(name, "vty", 3) != 0) + goto out; + termno = of_get_property(stdout_node, "reg", NULL); + if (termno == NULL) + goto out; + hvterm_priv0.termno = *termno; + spin_lock_init(&hvterm_priv0.buf_lock); + hvterm_privs[0] = &hvterm_priv0; + + /* Check the protocol */ + if (of_device_is_compatible(stdout_node, "hvterm1")) { + hvterm_priv0.proto = HV_PROTOCOL_RAW; + ops = &hvterm_raw_ops; + } + else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) { + hvterm_priv0.proto = HV_PROTOCOL_HVSI; + ops = &hvterm_hvsi_ops; + hvsilib_init(&hvterm_priv0.hvsi, hvc_get_chars, hvc_put_chars, + hvterm_priv0.termno, 1); + /* HVSI, perform the handshake now */ + hvsilib_establish(&hvterm_priv0.hvsi); + } else + goto out; + udbg_putc = udbg_hvc_putc; + udbg_getc = udbg_hvc_getc; + udbg_getc_poll = udbg_hvc_getc_poll; +#ifdef HVC_OLD_HVSI + /* When using the old HVSI driver don't register the HVC + * backend for HVSI, only do udbg + */ + if (hvterm_priv0.proto == HV_PROTOCOL_HVSI) + goto out; +#endif + add_preferred_console("hvc", 0, NULL); + hvc_instantiate(0, 0, ops); +out: + of_node_put(stdout_node); +} - return num_found; +/* call this from early_init() for a working debug console on + * vterm capable LPAR machines + */ +#ifdef CONFIG_PPC_EARLY_DEBUG_LPAR +void __init udbg_init_debug_lpar(void) +{ + hvterm_privs[0] = &hvterm_priv0; + hvterm_priv0.termno = 0; + hvterm_priv0.proto = HV_PROTOCOL_RAW; + spin_lock_init(&hvterm_priv0.buf_lock); + udbg_putc = udbg_hvc_putc; + udbg_getc = udbg_hvc_getc; + udbg_getc_poll = udbg_hvc_getc_poll; +} +#endif /* CONFIG_PPC_EARLY_DEBUG_LPAR */ + +#ifdef CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI +void __init udbg_init_debug_lpar_hvsi(void) +{ + hvterm_privs[0] = &hvterm_priv0; + hvterm_priv0.termno = CONFIG_PPC_EARLY_DEBUG_HVSI_VTERMNO; + hvterm_priv0.proto = HV_PROTOCOL_HVSI; + spin_lock_init(&hvterm_priv0.buf_lock); + udbg_putc = udbg_hvc_putc; + udbg_getc = udbg_hvc_getc; + udbg_getc_poll = udbg_hvc_getc_poll; + hvsilib_init(&hvterm_priv0.hvsi, hvc_get_chars, hvc_put_chars, + hvterm_priv0.termno, 1); + hvsilib_establish(&hvterm_priv0.hvsi); } -console_initcall(hvc_find_vtys); +#endif /* CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI */ diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index 8a8d6373f164..c94e2f5853d8 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -49,6 +49,7 @@ #include <asm/uaccess.h> #include <asm/vio.h> #include <asm/param.h> +#include <asm/hvsi.h> #define HVSI_MAJOR 229 #define HVSI_MINOR 128 @@ -109,68 +110,6 @@ enum HVSI_PROTOCOL_STATE { }; #define HVSI_CONSOLE 0x1 -#define VS_DATA_PACKET_HEADER 0xff -#define VS_CONTROL_PACKET_HEADER 0xfe -#define VS_QUERY_PACKET_HEADER 0xfd -#define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc - -/* control verbs */ -#define VSV_SET_MODEM_CTL 1 /* to service processor only */ -#define VSV_MODEM_CTL_UPDATE 2 /* from service processor only */ -#define VSV_CLOSE_PROTOCOL 3 - -/* query verbs */ -#define VSV_SEND_VERSION_NUMBER 1 -#define VSV_SEND_MODEM_CTL_STATUS 2 - -/* yes, these masks are not consecutive. */ -#define HVSI_TSDTR 0x01 -#define HVSI_TSCD 0x20 - -struct hvsi_header { - uint8_t type; - uint8_t len; - uint16_t seqno; -} __attribute__((packed)); - -struct hvsi_data { - uint8_t type; - uint8_t len; - uint16_t seqno; - uint8_t data[HVSI_MAX_OUTGOING_DATA]; -} __attribute__((packed)); - -struct hvsi_control { - uint8_t type; - uint8_t len; - uint16_t seqno; - uint16_t verb; - /* optional depending on verb: */ - uint32_t word; - uint32_t mask; -} __attribute__((packed)); - -struct hvsi_query { - uint8_t type; - uint8_t len; - uint16_t seqno; - uint16_t verb; -} __attribute__((packed)); - -struct hvsi_query_response { - uint8_t type; - uint8_t len; - uint16_t seqno; - uint16_t verb; - uint16_t query_seqno; - union { - uint8_t version; - uint32_t mctrl_word; - } u; -} __attribute__((packed)); - - - static inline int is_console(struct hvsi_struct *hp) { return hp->flags & HVSI_CONSOLE; @@ -356,18 +295,18 @@ static int hvsi_version_respond(struct hvsi_struct *hp, uint16_t query_seqno) struct hvsi_query_response packet __ALIGNED__; int wrote; - packet.type = VS_QUERY_RESPONSE_PACKET_HEADER; - packet.len = sizeof(struct hvsi_query_response); - packet.seqno = atomic_inc_return(&hp->seqno); + packet.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER; + packet.hdr.len = sizeof(struct hvsi_query_response); + packet.hdr.seqno = atomic_inc_return(&hp->seqno); packet.verb = VSV_SEND_VERSION_NUMBER; packet.u.version = HVSI_VERSION; packet.query_seqno = query_seqno+1; - pr_debug("%s: sending %i bytes\n", __func__, packet.len); - dbg_dump_hex((uint8_t*)&packet, packet.len); + pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); + dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); - wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); - if (wrote != packet.len) { + wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); + if (wrote != packet.hdr.len) { printk(KERN_ERR "hvsi%i: couldn't send query response!\n", hp->index); return -EIO; @@ -382,7 +321,7 @@ static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet) switch (hp->state) { case HVSI_WAIT_FOR_VER_QUERY: - hvsi_version_respond(hp, query->seqno); + hvsi_version_respond(hp, query->hdr.seqno); __set_state(hp, HVSI_OPEN); break; default: @@ -640,16 +579,16 @@ static int hvsi_query(struct hvsi_struct *hp, uint16_t verb) struct hvsi_query packet __ALIGNED__; int wrote; - packet.type = VS_QUERY_PACKET_HEADER; - packet.len = sizeof(struct hvsi_query); - packet.seqno = atomic_inc_return(&hp->seqno); + packet.hdr.type = VS_QUERY_PACKET_HEADER; + packet.hdr.len = sizeof(struct hvsi_query); + packet.hdr.seqno = atomic_inc_return(&hp->seqno); packet.verb = verb; - pr_debug("%s: sending %i bytes\n", __func__, packet.len); - dbg_dump_hex((uint8_t*)&packet, packet.len); + pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); + dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); - wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); - if (wrote != packet.len) { + wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); + if (wrote != packet.hdr.len) { printk(KERN_ERR "hvsi%i: couldn't send query (%i)!\n", hp->index, wrote); return -EIO; @@ -683,20 +622,20 @@ static int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl) struct hvsi_control packet __ALIGNED__; int wrote; - packet.type = VS_CONTROL_PACKET_HEADER, - packet.seqno = atomic_inc_return(&hp->seqno); - packet.len = sizeof(struct hvsi_control); + packet.hdr.type = VS_CONTROL_PACKET_HEADER, + packet.hdr.seqno = atomic_inc_return(&hp->seqno); + packet.hdr.len = sizeof(struct hvsi_control); packet.verb = VSV_SET_MODEM_CTL; packet.mask = HVSI_TSDTR; if (mctrl & TIOCM_DTR) packet.word = HVSI_TSDTR; - pr_debug("%s: sending %i bytes\n", __func__, packet.len); - dbg_dump_hex((uint8_t*)&packet, packet.len); + pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); + dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); - wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); - if (wrote != packet.len) { + wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); + if (wrote != packet.hdr.len) { printk(KERN_ERR "hvsi%i: couldn't set DTR!\n", hp->index); return -EIO; } @@ -766,13 +705,13 @@ static int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count) BUG_ON(count > HVSI_MAX_OUTGOING_DATA); - packet.type = VS_DATA_PACKET_HEADER; - packet.seqno = atomic_inc_return(&hp->seqno); - packet.len = count + sizeof(struct hvsi_header); + packet.hdr.type = VS_DATA_PACKET_HEADER; + packet.hdr.seqno = atomic_inc_return(&hp->seqno); + packet.hdr.len = count + sizeof(struct hvsi_header); memcpy(&packet.data, buf, count); - ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); - if (ret == packet.len) { + ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); + if (ret == packet.hdr.len) { /* return the number of chars written, not the packet length */ return count; } @@ -783,15 +722,15 @@ static void hvsi_close_protocol(struct hvsi_struct *hp) { struct hvsi_control packet __ALIGNED__; - packet.type = VS_CONTROL_PACKET_HEADER; - packet.seqno = atomic_inc_return(&hp->seqno); - packet.len = 6; + packet.hdr.type = VS_CONTROL_PACKET_HEADER; + packet.hdr.seqno = atomic_inc_return(&hp->seqno); + packet.hdr.len = 6; packet.verb = VSV_CLOSE_PROTOCOL; - pr_debug("%s: sending %i bytes\n", __func__, packet.len); - dbg_dump_hex((uint8_t*)&packet, packet.len); + pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); + dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); - hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); + hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); } static int hvsi_open(struct tty_struct *tty, struct file *filp) diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c new file mode 100644 index 000000000000..bd9b09827b24 --- /dev/null +++ b/drivers/tty/hvc/hvsi_lib.c @@ -0,0 +1,426 @@ +#include <linux/types.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/console.h> +#include <asm/hvsi.h> + +#include "hvc_console.h" + +static int hvsi_send_packet(struct hvsi_priv *pv, struct hvsi_header *packet) +{ + packet->seqno = atomic_inc_return(&pv->seqno); + + /* Assumes that always succeeds, works in practice */ + return pv->put_chars(pv->termno, (char *)packet, packet->len); +} + +static void hvsi_start_handshake(struct hvsi_priv *pv) +{ + struct hvsi_query q; + + /* Reset state */ + pv->established = 0; + atomic_set(&pv->seqno, 0); + + pr_devel("HVSI@%x: Handshaking started\n", pv->termno); + + /* Send version query */ + q.hdr.type = VS_QUERY_PACKET_HEADER; + q.hdr.len = sizeof(struct hvsi_query); + q.verb = VSV_SEND_VERSION_NUMBER; + hvsi_send_packet(pv, &q.hdr); +} + +static int hvsi_send_close(struct hvsi_priv *pv) +{ + struct hvsi_control ctrl; + + pv->established = 0; + + ctrl.hdr.type = VS_CONTROL_PACKET_HEADER; + ctrl.hdr.len = sizeof(struct hvsi_control); + ctrl.verb = VSV_CLOSE_PROTOCOL; + return hvsi_send_packet(pv, &ctrl.hdr); +} + +static void hvsi_cd_change(struct hvsi_priv *pv, int cd) +{ + if (cd) + pv->mctrl |= TIOCM_CD; + else { + pv->mctrl &= ~TIOCM_CD; + + /* We copy the existing hvsi driver semantics + * here which are to trigger a hangup when + * we get a carrier loss. + * Closing our connection to the server will + * do just that. + */ + if (!pv->is_console && pv->opened) { + pr_devel("HVSI@%x Carrier lost, hanging up !\n", + pv->termno); + hvsi_send_close(pv); + } + } +} + +static void hvsi_got_control(struct hvsi_priv *pv) +{ + struct hvsi_control *pkt = (struct hvsi_control *)pv->inbuf; + + switch (pkt->verb) { + case VSV_CLOSE_PROTOCOL: + /* We restart the handshaking */ + hvsi_start_handshake(pv); + break; + case VSV_MODEM_CTL_UPDATE: + /* Transition of carrier detect */ + hvsi_cd_change(pv, pkt->word & HVSI_TSCD); + break; + } +} + +static void hvsi_got_query(struct hvsi_priv *pv) +{ + struct hvsi_query *pkt = (struct hvsi_query *)pv->inbuf; + struct hvsi_query_response r; + + /* We only handle version queries */ + if (pkt->verb != VSV_SEND_VERSION_NUMBER) + return; + + pr_devel("HVSI@%x: Got version query, sending response...\n", + pv->termno); + + /* Send version response */ + r.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER; + r.hdr.len = sizeof(struct hvsi_query_response); + r.verb = VSV_SEND_VERSION_NUMBER; + r.u.version = HVSI_VERSION; + r.query_seqno = pkt->hdr.seqno; + hvsi_send_packet(pv, &r.hdr); + + /* Assume protocol is open now */ + pv->established = 1; +} + +static void hvsi_got_response(struct hvsi_priv *pv) +{ + struct hvsi_query_response *r = + (struct hvsi_query_response *)pv->inbuf; + + switch(r->verb) { + case VSV_SEND_MODEM_CTL_STATUS: + hvsi_cd_change(pv, r->u.mctrl_word & HVSI_TSCD); + pv->mctrl_update = 1; + break; + } +} + +static int hvsi_check_packet(struct hvsi_priv *pv) +{ + u8 len, type; + + /* Check header validity. If it's invalid, we ditch + * the whole buffer and hope we eventually resync + */ + if (pv->inbuf[0] < 0xfc) { + pv->inbuf_len = pv->inbuf_pktlen = 0; + return 0; + } + type = pv->inbuf[0]; + len = pv->inbuf[1]; + + /* Packet incomplete ? */ + if (pv->inbuf_len < len) + return 0; + + pr_devel("HVSI@%x: Got packet type %x len %d bytes:\n", + pv->termno, type, len); + + /* We have a packet, yay ! Handle it */ + switch(type) { + case VS_DATA_PACKET_HEADER: + pv->inbuf_pktlen = len - 4; + pv->inbuf_cur = 4; + return 1; + case VS_CONTROL_PACKET_HEADER: + hvsi_got_control(pv); + break; + case VS_QUERY_PACKET_HEADER: + hvsi_got_query(pv); + break; + case VS_QUERY_RESPONSE_PACKET_HEADER: + hvsi_got_response(pv); + break; + } + + /* Swallow packet and retry */ + pv->inbuf_len -= len; + memmove(pv->inbuf, &pv->inbuf[len], pv->inbuf_len); + return 1; +} + +static int hvsi_get_packet(struct hvsi_priv *pv) +{ + /* If we have room in the buffer, ask HV for more */ + if (pv->inbuf_len < HVSI_INBUF_SIZE) + pv->inbuf_len += pv->get_chars(pv->termno, + &pv->inbuf[pv->inbuf_len], + HVSI_INBUF_SIZE - pv->inbuf_len); + /* + * If we have at least 4 bytes in the buffer, check for + * a full packet and retry + */ + if (pv->inbuf_len >= 4) + return hvsi_check_packet(pv); + return 0; +} + +int hvsilib_get_chars(struct hvsi_priv *pv, char *buf, int count) +{ + unsigned int tries, read = 0; + + if (WARN_ON(!pv)) + return 0; + + /* If we aren't open, don't do anything in order to avoid races + * with connection establishment. The hvc core will call this + * before we have returned from notifier_add(), and we need to + * avoid multiple users playing with the receive buffer + */ + if (!pv->opened) + return 0; + + /* We try twice, once with what data we have and once more + * after we try to fetch some more from the hypervisor + */ + for (tries = 1; count && tries < 2; tries++) { + /* Consume existing data packet */ + if (pv->inbuf_pktlen) { + unsigned int l = min(count, (int)pv->inbuf_pktlen); + memcpy(&buf[read], &pv->inbuf[pv->inbuf_cur], l); + pv->inbuf_cur += l; + pv->inbuf_pktlen -= l; + count -= l; + read += l; + } + if (count == 0) + break; + + /* Data packet fully consumed, move down remaning data */ + if (pv->inbuf_cur) { + pv->inbuf_len -= pv->inbuf_cur; + memmove(pv->inbuf, &pv->inbuf[pv->inbuf_cur], + pv->inbuf_len); + pv->inbuf_cur = 0; + } + + /* Try to get another packet */ + if (hvsi_get_packet(pv)) + tries--; + } + if (!pv->established) { + pr_devel("HVSI@%x: returning -EPIPE\n", pv->termno); + return -EPIPE; + } + return read; +} + +int hvsilib_put_chars(struct hvsi_priv *pv, const char *buf, int count) +{ + struct hvsi_data dp; + int rc, adjcount = min(count, HVSI_MAX_OUTGOING_DATA); + + if (WARN_ON(!pv)) + return 0; + + dp.hdr.type = VS_DATA_PACKET_HEADER; + dp.hdr.len = adjcount + sizeof(struct hvsi_header); + memcpy(dp.data, buf, adjcount); + rc = hvsi_send_packet(pv, &dp.hdr); + if (rc <= 0) + return rc; + return adjcount; +} + +static void maybe_msleep(unsigned long ms) +{ + /* During early boot, IRQs are disabled, use mdelay */ + if (irqs_disabled()) + mdelay(ms); + else + msleep(ms); +} + +int hvsilib_read_mctrl(struct hvsi_priv *pv) +{ + struct hvsi_query q; + int rc, timeout; + + pr_devel("HVSI@%x: Querying modem control status...\n", + pv->termno); + + pv->mctrl_update = 0; + q.hdr.type = VS_QUERY_PACKET_HEADER; + q.hdr.len = sizeof(struct hvsi_query); + q.hdr.seqno = atomic_inc_return(&pv->seqno); + q.verb = VSV_SEND_MODEM_CTL_STATUS; + rc = hvsi_send_packet(pv, &q.hdr); + if (rc <= 0) { + pr_devel("HVSI@%x: Error %d...\n", pv->termno, rc); + return rc; + } + + /* Try for up to 200ms */ + for (timeout = 0; timeout < 20; timeout++) { + if (!pv->established) + return -ENXIO; + if (pv->mctrl_update) + return 0; + if (!hvsi_get_packet(pv)) + maybe_msleep(10); + } + return -EIO; +} + +int hvsilib_write_mctrl(struct hvsi_priv *pv, int dtr) +{ + struct hvsi_control ctrl; + unsigned short mctrl; + + mctrl = pv->mctrl; + if (dtr) + mctrl |= TIOCM_DTR; + else + mctrl &= ~TIOCM_DTR; + if (mctrl == pv->mctrl) + return 0; + pv->mctrl = mctrl; + + pr_devel("HVSI@%x: %s DTR...\n", pv->termno, + dtr ? "Setting" : "Clearing"); + + ctrl.hdr.type = VS_CONTROL_PACKET_HEADER, + ctrl.hdr.len = sizeof(struct hvsi_control); + ctrl.verb = VSV_SET_MODEM_CTL; + ctrl.mask = HVSI_TSDTR; + ctrl.word = dtr ? HVSI_TSDTR : 0; + return hvsi_send_packet(pv, &ctrl.hdr); +} + +void hvsilib_establish(struct hvsi_priv *pv) +{ + int timeout; + + pr_devel("HVSI@%x: Establishing...\n", pv->termno); + + /* Try for up to 200ms, there can be a packet to + * start the process waiting for us... + */ + for (timeout = 0; timeout < 20; timeout++) { + if (pv->established) + goto established; + if (!hvsi_get_packet(pv)) + maybe_msleep(10); + } + + /* Failed, send a close connection packet just + * in case + */ + pr_devel("HVSI@%x: ... sending close\n", pv->termno); + + hvsi_send_close(pv); + + /* Then restart handshake */ + + pr_devel("HVSI@%x: ... restarting handshake\n", pv->termno); + + hvsi_start_handshake(pv); + + pr_devel("HVSI@%x: ... waiting handshake\n", pv->termno); + + /* Try for up to 200s */ + for (timeout = 0; timeout < 20; timeout++) { + if (pv->established) + goto established; + if (!hvsi_get_packet(pv)) + maybe_msleep(10); + } + + if (!pv->established) { + pr_devel("HVSI@%x: Timeout handshaking, giving up !\n", + pv->termno); + return; + } + established: + /* Query modem control lines */ + + pr_devel("HVSI@%x: ... established, reading mctrl\n", pv->termno); + + hvsilib_read_mctrl(pv); + + /* Set our own DTR */ + + pr_devel("HVSI@%x: ... setting mctrl\n", pv->termno); + + hvsilib_write_mctrl(pv, 1); + + /* Set the opened flag so reads are allowed */ + wmb(); + pv->opened = 1; +} + +int hvsilib_open(struct hvsi_priv *pv, struct hvc_struct *hp) +{ + pr_devel("HVSI@%x: open !\n", pv->termno); + + /* Keep track of the tty data structure */ + pv->tty = tty_kref_get(hp->tty); + + hvsilib_establish(pv); + + return 0; +} + +void hvsilib_close(struct hvsi_priv *pv, struct hvc_struct *hp) +{ + unsigned long flags; + + pr_devel("HVSI@%x: close !\n", pv->termno); + + if (!pv->is_console) { + pr_devel("HVSI@%x: Not a console, tearing down\n", + pv->termno); + + /* Clear opened, synchronize with khvcd */ + spin_lock_irqsave(&hp->lock, flags); + pv->opened = 0; + spin_unlock_irqrestore(&hp->lock, flags); + + /* Clear our own DTR */ + if (!pv->tty || (pv->tty->termios->c_cflag & HUPCL)) + hvsilib_write_mctrl(pv, 0); + + /* Tear down the connection */ + hvsi_send_close(pv); + } + + if (pv->tty) + tty_kref_put(pv->tty); + pv->tty = NULL; +} + +void hvsilib_init(struct hvsi_priv *pv, + int (*get_chars)(uint32_t termno, char *buf, int count), + int (*put_chars)(uint32_t termno, const char *buf, + int count), + int termno, int is_console) +{ + memset(pv, 0, sizeof(*pv)); + pv->get_chars = get_chars; + pv->put_chars = put_chars; + pv->termno = termno; + pv->is_console = is_console; +} diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 14522ee8a91f..8a50e4eebf18 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -904,7 +904,8 @@ static int gsm_dlci_data_output_framed(struct gsm_mux *gsm, *dp++ = last << 7 | first << 6 | 1; /* EA */ len--; } - memcpy(dp, skb_pull(dlci->skb, len), len); + memcpy(dp, dlci->skb->data, len); + skb_pull(dlci->skb, len); __gsm_data_queue(dlci, msg); if (last) { kfree_skb(dlci->skb); @@ -1018,10 +1019,22 @@ static void gsm_control_reply(struct gsm_mux *gsm, int cmd, u8 *data, */ static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci, - u32 modem) + u32 modem, int clen) { int mlines = 0; - u8 brk = modem >> 6; + u8 brk = 0; + + /* The modem status command can either contain one octet (v.24 signals) + or two octets (v.24 signals + break signals). The length field will + either be 2 or 3 respectively. This is specified in section + 5.4.6.3.7 of the 27.010 mux spec. */ + + if (clen == 2) + modem = modem & 0x7f; + else { + brk = modem & 0x7f; + modem = (modem >> 7) & 0x7f; + }; /* Flow control/ready to communicate */ if (modem & MDM_FC) { @@ -1095,7 +1108,7 @@ static void gsm_control_modem(struct gsm_mux *gsm, u8 *data, int clen) return; } tty = tty_port_tty_get(&dlci->port); - gsm_process_modem(tty, dlci, modem); + gsm_process_modem(tty, dlci, modem, clen); if (tty) { tty_wakeup(tty); tty_kref_put(tty); @@ -1516,12 +1529,13 @@ static void gsm_dlci_begin_close(struct gsm_dlci *dlci) * open we shovel the bits down it, if not we drop them. */ -static void gsm_dlci_data(struct gsm_dlci *dlci, u8 *data, int len) +static void gsm_dlci_data(struct gsm_dlci *dlci, u8 *data, int clen) { /* krefs .. */ struct tty_port *port = &dlci->port; struct tty_struct *tty = tty_port_tty_get(port); unsigned int modem = 0; + int len = clen; if (debug & 16) pr_debug("%d bytes for tty %p\n", len, tty); @@ -1541,7 +1555,7 @@ static void gsm_dlci_data(struct gsm_dlci *dlci, u8 *data, int len) if (len == 0) return; } - gsm_process_modem(tty, dlci, modem); + gsm_process_modem(tty, dlci, modem, clen); /* Line state will go via DLCI 0 controls only */ case 1: default: diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 1ddc5d7c9592..39d6ab6551e0 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1815,6 +1815,7 @@ 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); diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index f1422a646767..f2dfec82faf8 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -3318,6 +3318,7 @@ void serial8250_unregister_port(int line) uart->port.flags &= ~UPF_BOOT_AUTOCONF; uart->port.type = PORT_UNKNOWN; uart->port.dev = &serial8250_isa_devs->dev; + uart->capabilities = uart_config[uart->port.type].flags; uart_add_one_port(&serial8250_reg, &uart->port); } else { uart->port.dev = NULL; diff --git a/drivers/tty/serial/8250_pci.c b/drivers/tty/serial/8250_pci.c index ae2188ce0670..6b887d90a205 100644 --- a/drivers/tty/serial/8250_pci.c +++ b/drivers/tty/serial/8250_pci.c @@ -769,7 +769,7 @@ pci_ni8430_setup(struct serial_private *priv, len = pci_resource_len(priv->dev, bar); p = ioremap_nocache(base, len); - /* enable the transciever */ + /* enable the transceiver */ writeb(readb(p + offset + NI8430_PORTCON) | NI8430_PORTCON_TXVR_ENABLE, p + offset + NI8430_PORTCON); @@ -1071,7 +1071,7 @@ ce4100_serial_setup(struct serial_private *priv, static int pci_omegapci_setup(struct serial_private *priv, - struct pciserial_board *board, + const struct pciserial_board *board, struct uart_port *port, int idx) { return setup_port(priv, port, 2, idx * 8, 0); @@ -1092,6 +1092,15 @@ static int skip_tx_en_setup(struct serial_private *priv, return pci_default_setup(priv, board, port, idx); } +static int pci_eg20t_init(struct pci_dev *dev) +{ +#if defined(CONFIG_SERIAL_PCH_UART) || defined(CONFIG_SERIAL_PCH_UART_MODULE) + return -ENODEV; +#else + return 0; +#endif +} + /* This should be in linux/pci_ids.h */ #define PCI_VENDOR_ID_SBSMODULARIO 0x124B #define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B @@ -1545,6 +1554,56 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .init = pci_oxsemi_tornado_init, .setup = pci_default_setup, }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x8811, + .init = pci_eg20t_init, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x8812, + .init = pci_eg20t_init, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x8813, + .init = pci_eg20t_init, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x8814, + .init = pci_eg20t_init, + }, + { + .vendor = 0x10DB, + .device = 0x8027, + .init = pci_eg20t_init, + }, + { + .vendor = 0x10DB, + .device = 0x8028, + .init = pci_eg20t_init, + }, + { + .vendor = 0x10DB, + .device = 0x8029, + .init = pci_eg20t_init, + }, + { + .vendor = 0x10DB, + .device = 0x800C, + .init = pci_eg20t_init, + }, + { + .vendor = 0x10DB, + .device = 0x800D, + .init = pci_eg20t_init, + }, + { + .vendor = 0x10DB, + .device = 0x800D, + .init = pci_eg20t_init, + }, /* * Cronyx Omega PCI (PLX-chip based) */ diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index b3692e6e3c13..cb40b82daf36 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -457,7 +457,6 @@ config SERIAL_SAMSUNG_UARTS_4 config SERIAL_SAMSUNG_UARTS int depends on ARM && PLAT_SAMSUNG - default 2 if ARCH_S3C2400 default 6 if ARCH_S5P6450 default 4 if SERIAL_SAMSUNG_UARTS_4 default 3 @@ -489,13 +488,6 @@ config SERIAL_SAMSUNG_CONSOLE your boot loader about how to pass options to the kernel at boot time.) -config SERIAL_S3C2400 - tristate "Samsung S3C2410 Serial port support" - depends on ARM && SERIAL_SAMSUNG && CPU_S3C2400 - default y if CPU_S3C2400 - help - Serial port support for the Samsung S3C2400 SoC - config SERIAL_S3C2410 tristate "Samsung S3C2410 Serial port support" depends on SERIAL_SAMSUNG && CPU_S3C2410 @@ -519,13 +511,6 @@ config SERIAL_S3C2440 help Serial port support for the Samsung S3C2440, S3C2416 and S3C2442 SoC -config SERIAL_S3C24A0 - tristate "Samsung S3C24A0 Serial port support" - depends on SERIAL_SAMSUNG && CPU_S3C24A0 - default y if CPU_S3C24A0 - help - Serial port support for the Samsung S3C24A0 SoC - config SERIAL_S3C6400 tristate "Samsung S3C6400/S3C6410/S5P6440/S5P6450/S5PC100 Serial port support" depends on SERIAL_SAMSUNG && (CPU_S3C6400 || CPU_S3C6410 || CPU_S5P6440 || CPU_S5P6450 || CPU_S5PC100) diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index cb2628fee4c7..83b4da6a1062 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -38,11 +38,9 @@ obj-$(CONFIG_SERIAL_BCM63XX) += bcm63xx_uart.o obj-$(CONFIG_SERIAL_BFIN) += bfin_5xx.o obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o -obj-$(CONFIG_SERIAL_S3C2400) += s3c2400.o obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o obj-$(CONFIG_SERIAL_S3C2412) += s3c2412.o obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o -obj-$(CONFIG_SERIAL_S3C24A0) += s3c24a0.o obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o obj-$(CONFIG_SERIAL_S5PV210) += s5pv210.o obj-$(CONFIG_SERIAL_MAX3100) += max3100.o diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 6d5d6e679fc7..af9b7814965a 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1709,12 +1709,13 @@ static int atmel_serial_resume(struct platform_device *pdev) static int __devinit atmel_serial_probe(struct platform_device *pdev) { struct atmel_uart_port *port; + struct atmel_uart_data *pdata = pdev->dev.platform_data; void *data; int ret; BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1)); - port = &atmel_ports[pdev->id]; + port = &atmel_ports[pdata->num]; port->backup_imr = 0; atmel_init_port(port, pdev); diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index a1a0e55d0807..c0b68b9cad91 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -250,6 +250,20 @@ static void bcm_uart_do_rx(struct uart_port *port) /* get overrun/fifo empty information from ier * register */ iestat = bcm_uart_readl(port, UART_IR_REG); + + if (unlikely(iestat & UART_IR_STAT(UART_IR_RXOVER))) { + unsigned int val; + + /* fifo reset is required to clear + * interrupt */ + val = bcm_uart_readl(port, UART_CTL_REG); + val |= UART_CTL_RSTRXFIFO_MASK; + bcm_uart_writel(port, val, UART_CTL_REG); + + port->icount.overrun++; + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + if (!(iestat & UART_IR_STAT(UART_IR_RXNOTEMPTY))) break; @@ -284,10 +298,6 @@ static void bcm_uart_do_rx(struct uart_port *port) if (uart_handle_sysrq_char(port, c)) continue; - if (unlikely(iestat & UART_IR_STAT(UART_IR_RXOVER))) { - port->icount.overrun++; - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } if ((cstat & port->ignore_status_mask) == 0) tty_insert_flip_char(tty, c, flag); diff --git a/drivers/tty/serial/bfin_5xx.c b/drivers/tty/serial/bfin_5xx.c index 957135a19743..ff6979181ac5 100644 --- a/drivers/tty/serial/bfin_5xx.c +++ b/drivers/tty/serial/bfin_5xx.c @@ -1482,7 +1482,7 @@ static int bfin_earlyprintk_probe(struct platform_device *pdev) } bfin_earlyprintk_port.port.membase = ioremap(res->start, - res->end - res->start); + resource_size(res)); if (!bfin_earlyprintk_port.port.membase) { dev_err(&pdev->dev, "Cannot map uart IO\n"); ret = -ENXIO; diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 5315525220fb..426434e5eb7c 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -36,6 +36,7 @@ * you need to use this driver for another platform. * *****************************************************************************/ +#include <linux/dma-mapping.h> #include <linux/module.h> #include <linux/termios.h> #include <linux/tty.h> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index a54473123e0a..22fe801cce31 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -954,7 +954,7 @@ static void imx_release_port(struct uart_port *port) struct resource *mmres; mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(mmres->start, mmres->end - mmres->start + 1); + release_mem_region(mmres->start, resource_size(mmres)); } /* @@ -970,8 +970,7 @@ static int imx_request_port(struct uart_port *port) if (!mmres) return -ENODEV; - ret = request_mem_region(mmres->start, mmres->end - mmres->start + 1, - "imx-uart"); + ret = request_mem_region(mmres->start, resource_size(mmres), "imx-uart"); return ret ? 0 : -EBUSY; } diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c index 18f548449c63..96da17868cf3 100644 --- a/drivers/tty/serial/jsm/jsm_driver.c +++ b/drivers/tty/serial/jsm/jsm_driver.c @@ -125,7 +125,7 @@ static int __devinit jsm_probe_one(struct pci_dev *pdev, const struct pci_device brd->bd_uart_offset = 0x200; brd->bd_dividend = 921600; - brd->re_map_membase = ioremap(brd->membase, 0x1000); + brd->re_map_membase = ioremap(brd->membase, pci_resource_len(pdev, 0)); if (!brd->re_map_membase) { dev_err(&pdev->dev, "card has no PCI Memory resources, " diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c index 84db7321cce8..8e07517f8acd 100644 --- a/drivers/tty/serial/m32r_sio.c +++ b/drivers/tty/serial/m32r_sio.c @@ -892,7 +892,7 @@ static int m32r_sio_request_port(struct uart_port *port) * If we have a mapbase, then request that as well. */ if (ret == 0 && up->port.flags & UPF_IOREMAP) { - int size = res->end - res->start + 1; + int size = resource_size(res); up->port.membase = ioremap(up->port.mapbase, size); if (!up->port.membase) diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index c911b2419abb..e58cece6f443 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -32,17 +32,17 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, { struct resource resource; struct device_node *np = ofdev->dev.of_node; - const __be32 *clk, *spd; - const __be32 *prop; - int ret, prop_size; + u32 clk, spd, prop; + int ret; memset(port, 0, sizeof *port); - spd = of_get_property(np, "current-speed", NULL); - clk = of_get_property(np, "clock-frequency", NULL); - if (!clk) { + if (of_property_read_u32(np, "clock-frequency", &clk)) { dev_warn(&ofdev->dev, "no clock-frequency property set\n"); return -ENODEV; } + /* If current-speed was set, then try not to change it. */ + if (of_property_read_u32(np, "current-speed", &spd) == 0) + port->custom_divisor = clk / (16 * spd); ret = of_address_to_resource(np, 0, &resource); if (ret) { @@ -54,25 +54,35 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, port->mapbase = resource.start; /* Check for shifted address mapping */ - prop = of_get_property(np, "reg-offset", &prop_size); - if (prop && (prop_size == sizeof(u32))) - port->mapbase += be32_to_cpup(prop); + if (of_property_read_u32(np, "reg-offset", &prop) == 0) + port->mapbase += prop; /* Check for registers offset within the devices address range */ - prop = of_get_property(np, "reg-shift", &prop_size); - if (prop && (prop_size == sizeof(u32))) - port->regshift = be32_to_cpup(prop); + if (of_property_read_u32(np, "reg-shift", &prop) == 0) + port->regshift = prop; port->irq = irq_of_parse_and_map(np, 0); port->iotype = UPIO_MEM; + if (of_property_read_u32(np, "reg-io-width", &prop) == 0) { + switch (prop) { + case 1: + port->iotype = UPIO_MEM; + break; + case 4: + port->iotype = UPIO_MEM32; + break; + default: + dev_warn(&ofdev->dev, "unsupported reg-io-width (%d)\n", + prop); + return -EINVAL; + } + } + port->type = type; - port->uartclk = be32_to_cpup(clk); + port->uartclk = clk; port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_FIXED_PORT | UPF_FIXED_TYPE; port->dev = &ofdev->dev; - /* If current-speed was set, then try not to change it. */ - if (spd) - port->custom_divisor = be32_to_cpup(clk) / (16 * (be32_to_cpup(spd))); return 0; } @@ -171,6 +181,7 @@ static struct of_device_id __devinitdata of_platform_serial_table[] = { { .compatible = "ns16550", .data = (void *)PORT_16550, }, { .compatible = "ns16750", .data = (void *)PORT_16750, }, { .compatible = "ns16850", .data = (void *)PORT_16850, }, + { .compatible = "nvidia,tegra20-uart", .data = (void *)PORT_TEGRA, }, #ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL { .compatible = "ibm,qpace-nwp-serial", .data = (void *)PORT_NWPSERIAL, }, diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 47cadf474149..c37df8d0fa28 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1241,8 +1241,8 @@ static int serial_omap_probe(struct platform_device *pdev) return -ENODEV; } - if (!request_mem_region(mem->start, (mem->end - mem->start) + 1, - pdev->dev.driver->name)) { + if (!request_mem_region(mem->start, resource_size(mem), + pdev->dev.driver->name)) { dev_err(&pdev->dev, "memory region already claimed\n"); return -EBUSY; } @@ -1308,7 +1308,7 @@ err: dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n", pdev->id, __func__, ret); do_release_region: - release_mem_region(mem->start, (mem->end - mem->start) + 1); + release_mem_region(mem->start, resource_size(mem)); return ret; } diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 037368285301..846dfcd3ce0d 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -1397,6 +1397,7 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, int fifosize, base_baud; int port_type; struct pch_uart_driver_data *board; + const char *board_name; board = &drv_dat[id->driver_data]; port_type = board->port_type; @@ -1412,7 +1413,8 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, base_baud = 1843200; /* 1.8432MHz */ /* quirk for CM-iTC board */ - if (strstr(dmi_get_system_info(DMI_BOARD_NAME), "CM-iTC")) + board_name = dmi_get_system_info(DMI_BOARD_NAME); + if (board_name && strstr(board_name, "CM-iTC")) base_baud = 192000000; /* 192.0MHz */ switch (port_type) { diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index 4302e6e3768e..531931c1b250 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -803,7 +803,7 @@ static int serial_pxa_probe(struct platform_device *dev) break; } - sport->port.membase = ioremap(mmres->start, mmres->end - mmres->start + 1); + sport->port.membase = ioremap(mmres->start, resource_size(mmres)); if (!sport->port.membase) { ret = -ENOMEM; goto err_clk; diff --git a/drivers/tty/serial/s3c2400.c b/drivers/tty/serial/s3c2400.c deleted file mode 100644 index d13051b3df87..000000000000 --- a/drivers/tty/serial/s3c2400.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Driver for Samsung SoC onboard UARTs. - * - * Ben Dooks, Copyright (c) 2003-2005 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * - * 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. -*/ - -#include <linux/module.h> -#include <linux/ioport.h> -#include <linux/io.h> -#include <linux/platform_device.h> - -#include <asm/irq.h> - -#include <mach/hardware.h> - -#include <plat/regs-serial.h> -#include <mach/regs-gpio.h> - -#include "samsung.h" - -static int s3c2400_serial_getsource(struct uart_port *port, - struct s3c24xx_uart_clksrc *clk) -{ - clk->divisor = 1; - clk->name = "pclk"; - - return 0; -} - -static int s3c2400_serial_setsource(struct uart_port *port, - struct s3c24xx_uart_clksrc *clk) -{ - return 0; -} - -static int s3c2400_serial_resetport(struct uart_port *port, - struct s3c2410_uartcfg *cfg) -{ - dbg("s3c2400_serial_resetport: port=%p (%08lx), cfg=%p\n", - port, port->mapbase, cfg); - - wr_regl(port, S3C2410_UCON, cfg->ucon); - wr_regl(port, S3C2410_ULCON, cfg->ulcon); - - /* reset both fifos */ - - wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); - wr_regl(port, S3C2410_UFCON, cfg->ufcon); - - return 0; -} - -static struct s3c24xx_uart_info s3c2400_uart_inf = { - .name = "Samsung S3C2400 UART", - .type = PORT_S3C2400, - .fifosize = 16, - .rx_fifomask = S3C2410_UFSTAT_RXMASK, - .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT, - .rx_fifofull = S3C2410_UFSTAT_RXFULL, - .tx_fifofull = S3C2410_UFSTAT_TXFULL, - .tx_fifomask = S3C2410_UFSTAT_TXMASK, - .tx_fifoshift = S3C2410_UFSTAT_TXSHIFT, - .get_clksrc = s3c2400_serial_getsource, - .set_clksrc = s3c2400_serial_setsource, - .reset_port = s3c2400_serial_resetport, -}; - -static int s3c2400_serial_probe(struct platform_device *dev) -{ - return s3c24xx_serial_probe(dev, &s3c2400_uart_inf); -} - -static struct platform_driver s3c2400_serial_driver = { - .probe = s3c2400_serial_probe, - .remove = __devexit_p(s3c24xx_serial_remove), - .driver = { - .name = "s3c2400-uart", - .owner = THIS_MODULE, - }, -}; - -s3c24xx_console_init(&s3c2400_serial_driver, &s3c2400_uart_inf); - -static inline int s3c2400_serial_init(void) -{ - return s3c24xx_serial_init(&s3c2400_serial_driver, &s3c2400_uart_inf); -} - -static inline void s3c2400_serial_exit(void) -{ - platform_driver_unregister(&s3c2400_serial_driver); -} - -module_init(s3c2400_serial_init); -module_exit(s3c2400_serial_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); -MODULE_DESCRIPTION("Samsung S3C2400 SoC Serial port driver"); -MODULE_ALIAS("platform:s3c2400-uart"); diff --git a/drivers/tty/serial/s3c2410.c b/drivers/tty/serial/s3c2410.c index bffe6ff9b158..b1d7e7c1849d 100644 --- a/drivers/tty/serial/s3c2410.c +++ b/drivers/tty/serial/s3c2410.c @@ -96,8 +96,6 @@ static struct platform_driver s3c2410_serial_driver = { }, }; -s3c24xx_console_init(&s3c2410_serial_driver, &s3c2410_uart_inf); - static int __init s3c2410_serial_init(void) { return s3c24xx_serial_init(&s3c2410_serial_driver, &s3c2410_uart_inf); diff --git a/drivers/tty/serial/s3c2412.c b/drivers/tty/serial/s3c2412.c index 7e2b9504a687..2234bf9ced45 100644 --- a/drivers/tty/serial/s3c2412.c +++ b/drivers/tty/serial/s3c2412.c @@ -130,8 +130,6 @@ static struct platform_driver s3c2412_serial_driver = { }, }; -s3c24xx_console_init(&s3c2412_serial_driver, &s3c2412_uart_inf); - static inline int s3c2412_serial_init(void) { return s3c24xx_serial_init(&s3c2412_serial_driver, &s3c2412_uart_inf); diff --git a/drivers/tty/serial/s3c2440.c b/drivers/tty/serial/s3c2440.c index 9e10d415d5fd..1d0c324b813f 100644 --- a/drivers/tty/serial/s3c2440.c +++ b/drivers/tty/serial/s3c2440.c @@ -159,8 +159,6 @@ static struct platform_driver s3c2440_serial_driver = { }, }; -s3c24xx_console_init(&s3c2440_serial_driver, &s3c2440_uart_inf); - static int __init s3c2440_serial_init(void) { return s3c24xx_serial_init(&s3c2440_serial_driver, &s3c2440_uart_inf); diff --git a/drivers/tty/serial/s3c24a0.c b/drivers/tty/serial/s3c24a0.c deleted file mode 100644 index 914eff22e499..000000000000 --- a/drivers/tty/serial/s3c24a0.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Driver for Samsung S3C24A0 SoC onboard UARTs. - * - * Based on drivers/serial/s3c2410.c - * - * Author: Sandeep Patil <sandeep.patil@azingo.com> - * - * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * - * 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. -*/ - -#include <linux/module.h> -#include <linux/ioport.h> -#include <linux/platform_device.h> -#include <linux/init.h> -#include <linux/serial_core.h> -#include <linux/serial.h> -#include <linux/io.h> -#include <linux/irq.h> - -#include <mach/hardware.h> - -#include <plat/regs-serial.h> -#include <mach/regs-gpio.h> - -#include "samsung.h" - -static int s3c24a0_serial_setsource(struct uart_port *port, - struct s3c24xx_uart_clksrc *clk) -{ - unsigned long ucon = rd_regl(port, S3C2410_UCON); - - if (strcmp(clk->name, "uclk") == 0) - ucon |= S3C2410_UCON_UCLK; - else - ucon &= ~S3C2410_UCON_UCLK; - - wr_regl(port, S3C2410_UCON, ucon); - return 0; -} - -static int s3c24a0_serial_getsource(struct uart_port *port, - struct s3c24xx_uart_clksrc *clk) -{ - unsigned long ucon = rd_regl(port, S3C2410_UCON); - - clk->divisor = 1; - clk->name = (ucon & S3C2410_UCON_UCLK) ? "uclk" : "pclk"; - - return 0; -} - -static int s3c24a0_serial_resetport(struct uart_port *port, - struct s3c2410_uartcfg *cfg) -{ - dbg("s3c24a0_serial_resetport: port=%p (%08lx), cfg=%p\n", - port, port->mapbase, cfg); - - wr_regl(port, S3C2410_UCON, cfg->ucon); - wr_regl(port, S3C2410_ULCON, cfg->ulcon); - - /* reset both fifos */ - - wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); - wr_regl(port, S3C2410_UFCON, cfg->ufcon); - - return 0; -} - -static struct s3c24xx_uart_info s3c24a0_uart_inf = { - .name = "Samsung S3C24A0 UART", - .type = PORT_S3C2410, - .fifosize = 16, - .rx_fifomask = S3C24A0_UFSTAT_RXMASK, - .rx_fifoshift = S3C24A0_UFSTAT_RXSHIFT, - .rx_fifofull = S3C24A0_UFSTAT_RXFULL, - .tx_fifofull = S3C24A0_UFSTAT_TXFULL, - .tx_fifomask = S3C24A0_UFSTAT_TXMASK, - .tx_fifoshift = S3C24A0_UFSTAT_TXSHIFT, - .get_clksrc = s3c24a0_serial_getsource, - .set_clksrc = s3c24a0_serial_setsource, - .reset_port = s3c24a0_serial_resetport, -}; - -static int s3c24a0_serial_probe(struct platform_device *dev) -{ - return s3c24xx_serial_probe(dev, &s3c24a0_uart_inf); -} - -static struct platform_driver s3c24a0_serial_driver = { - .probe = s3c24a0_serial_probe, - .remove = __devexit_p(s3c24xx_serial_remove), - .driver = { - .name = "s3c24a0-uart", - .owner = THIS_MODULE, - }, -}; - -s3c24xx_console_init(&s3c24a0_serial_driver, &s3c24a0_uart_inf); - -static int __init s3c24a0_serial_init(void) -{ - return s3c24xx_serial_init(&s3c24a0_serial_driver, &s3c24a0_uart_inf); -} - -static void __exit s3c24a0_serial_exit(void) -{ - platform_driver_unregister(&s3c24a0_serial_driver); -} - -module_init(s3c24a0_serial_init); -module_exit(s3c24a0_serial_exit); - diff --git a/drivers/tty/serial/s3c6400.c b/drivers/tty/serial/s3c6400.c index ded26c42ff37..e2f6913d84d5 100644 --- a/drivers/tty/serial/s3c6400.c +++ b/drivers/tty/serial/s3c6400.c @@ -130,8 +130,6 @@ static struct platform_driver s3c6400_serial_driver = { }, }; -s3c24xx_console_init(&s3c6400_serial_driver, &s3c6400_uart_inf); - static int __init s3c6400_serial_init(void) { return s3c24xx_serial_init(&s3c6400_serial_driver, &s3c6400_uart_inf); diff --git a/drivers/tty/serial/s5pv210.c b/drivers/tty/serial/s5pv210.c index dce6cb3dbad1..8b0b888a1b76 100644 --- a/drivers/tty/serial/s5pv210.c +++ b/drivers/tty/serial/s5pv210.c @@ -31,7 +31,7 @@ static int s5pv210_serial_setsource(struct uart_port *port, struct s3c2410_uartcfg *cfg = port->dev->platform_data; unsigned long ucon = rd_regl(port, S3C2410_UCON); - if ((cfg->clocks_size) == 1) + if (cfg->flags & NO_NEED_CHECK_CLKSRC) return 0; if (strcmp(clk->name, "pclk") == 0) @@ -56,7 +56,7 @@ static int s5pv210_serial_getsource(struct uart_port *port, clk->divisor = 1; - if ((cfg->clocks_size) == 1) + if (cfg->flags & NO_NEED_CHECK_CLKSRC) return 0; switch (ucon & S5PV210_UCON_CLKMASK) { @@ -139,13 +139,6 @@ static struct platform_driver s5p_serial_driver = { }, }; -static int __init s5pv210_serial_console_init(void) -{ - return s3c24xx_serial_initconsole(&s5p_serial_driver, s5p_uart_inf); -} - -console_initcall(s5pv210_serial_console_init); - static int __init s5p_serial_init(void) { return s3c24xx_serial_init(&s5p_serial_driver, *s5p_uart_inf); diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 34b8afe5f470..afc629423152 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1417,10 +1417,8 @@ s3c24xx_serial_console_setup(struct console *co, char *options) /* is the port configured? */ - if (port->mapbase == 0x0) { - co->index = 0; - port = &s3c24xx_serial_ports[co->index].port; - } + if (port->mapbase == 0x0) + return -ENODEV; cons_uart = port; @@ -1452,7 +1450,8 @@ static struct console s3c24xx_serial_console = { .flags = CON_PRINTBUFFER, .index = -1, .write = s3c24xx_serial_console_write, - .setup = s3c24xx_serial_console_setup + .setup = s3c24xx_serial_console_setup, + .data = &s3c24xx_uart_drv, }; int s3c24xx_serial_initconsole(struct platform_driver *drv, diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h index 5b098cd76040..a69d9a54be94 100644 --- a/drivers/tty/serial/samsung.h +++ b/drivers/tty/serial/samsung.h @@ -79,25 +79,6 @@ extern int s3c24xx_serial_initconsole(struct platform_driver *drv, extern int s3c24xx_serial_init(struct platform_driver *drv, struct s3c24xx_uart_info *info); -#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE - -#define s3c24xx_console_init(__drv, __inf) \ -static int __init s3c_serial_console_init(void) \ -{ \ - struct s3c24xx_uart_info *uinfo[CONFIG_SERIAL_SAMSUNG_UARTS]; \ - int i; \ - \ - for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++) \ - uinfo[i] = __inf; \ - return s3c24xx_serial_initconsole(__drv, uinfo); \ -} \ - \ -console_initcall(s3c_serial_console_init) - -#else -#define s3c24xx_console_init(drv, inf) extern void no_console(void) -#endif - #ifdef CONFIG_SERIAL_SAMSUNG_DEBUG extern void printascii(const char *); diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index 92aa54550e84..ad0f8f5f6ea1 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -1435,7 +1435,7 @@ static int __devinit su_probe(struct platform_device *op) rp = &op->resource[0]; up->port.mapbase = rp->start; - up->reg_size = (rp->end - rp->start) + 1; + up->reg_size = resource_size(rp); up->port.membase = of_ioremap(rp, 0, up->reg_size, "su"); if (!up->port.membase) { if (type != SU_PORT_PORT) diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 37fc4e3d487c..026cb9ea5cd1 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -573,8 +573,7 @@ static int __init vt8500_serial_probe(struct platform_device *pdev) snprintf(vt8500_port->name, sizeof(vt8500_port->name), "VT8500 UART%d", pdev->id); - vt8500_port->uart.membase = ioremap(mmres->start, - mmres->end - mmres->start + 1); + vt8500_port->uart.membase = ioremap(mmres->start, resource_size(mmres)); if (!vt8500_port->uart.membase) { ret = -ENOMEM; goto err; diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index f1a7918d71aa..6c9b7cd6778a 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -413,8 +413,7 @@ static void flush_to_ldisc(struct work_struct *work) spin_lock_irqsave(&tty->buf.lock, flags); if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) { - struct tty_buffer *head, *tail = tty->buf.tail; - int seen_tail = 0; + struct tty_buffer *head; while ((head = tty->buf.head) != NULL) { int count; char *char_buf; @@ -424,15 +423,6 @@ static void flush_to_ldisc(struct work_struct *work) if (!count) { if (head->next == NULL) break; - /* - There's a possibility tty might get new buffer - added during the unlock window below. We could - end up spinning in here forever hogging the CPU - completely. To avoid this let's have a rest each - time we processed the tail buffer. - */ - if (tail == head) - seen_tail = 1; tty->buf.head = head->next; tty_buffer_free(tty, head); continue; @@ -442,7 +432,7 @@ static void flush_to_ldisc(struct work_struct *work) line discipline as we want to empty the queue */ if (test_bit(TTY_FLUSHPENDING, &tty->flags)) break; - if (!tty->receive_room || seen_tail) + if (!tty->receive_room) break; if (count > tty->receive_room) count = tty->receive_room; diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 5d01d32e2cf0..ef925d581713 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -555,7 +555,7 @@ static void tty_ldisc_flush_works(struct tty_struct *tty) static int tty_ldisc_wait_idle(struct tty_struct *tty) { int ret; - ret = wait_event_interruptible_timeout(tty_ldisc_idle, + ret = wait_event_timeout(tty_ldisc_idle, atomic_read(&tty->ldisc->users) == 1, 5 * HZ); if (ret < 0) return ret; @@ -763,6 +763,8 @@ static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc) if (IS_ERR(ld)) return -1; + WARN_ON_ONCE(tty_ldisc_wait_idle(tty)); + tty_ldisc_close(tty, tty->ldisc); tty_ldisc_put(tty->ldisc); tty->ldisc = NULL; |