diff options
Diffstat (limited to 'drivers/tty')
100 files changed, 9612 insertions, 3427 deletions
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 6b78399bc7c9..58ad1c05b7f8 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_TTY) += tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \ - tty_buffer.o tty_port.o tty_mutex.o + tty_buffer.o tty_port.o tty_mutex.o tty_ldsem.o obj-$(CONFIG_LEGACY_PTYS) += pty.o obj-$(CONFIG_UNIX98_PTYS) += pty.o obj-$(CONFIG_AUDIT) += tty_audit.o diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 083710e02367..2b86f8e0fb58 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1785,8 +1785,6 @@ static int __exit amiga_serial_remove(struct platform_device *pdev) free_irq(IRQ_AMIGA_TBE, state); free_irq(IRQ_AMIGA_RBF, state); - platform_set_drvdata(pdev, NULL); - return error; } diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index eb255e807c06..9eba119bcdd3 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -361,7 +361,12 @@ static int hvc_open(struct tty_struct *tty, struct file * filp) tty->driver_data = NULL; tty_port_put(&hp->port); printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc); - } + } else + /* We are ready... raise DTR/RTS */ + if (C_BAUD(tty)) + if (hp->ops->dtr_rts) + hp->ops->dtr_rts(hp, 1); + /* Force wakeup of the polling thread */ hvc_kick(); @@ -393,6 +398,10 @@ static void hvc_close(struct tty_struct *tty, struct file * filp) /* We are done with the tty pointer now. */ tty_port_tty_set(&hp->port, NULL); + if (C_HUPCL(tty)) + if (hp->ops->dtr_rts) + hp->ops->dtr_rts(hp, 0); + if (hp->ops->notifier_del) hp->ops->notifier_del(hp, hp->data); diff --git a/drivers/tty/hvc/hvc_console.h b/drivers/tty/hvc/hvc_console.h index 674d23cb919a..913101980827 100644 --- a/drivers/tty/hvc/hvc_console.h +++ b/drivers/tty/hvc/hvc_console.h @@ -75,6 +75,9 @@ struct hv_ops { /* tiocmget/set implementation */ int (*tiocmget)(struct hvc_struct *hp); int (*tiocmset)(struct hvc_struct *hp, unsigned int set, unsigned int clear); + + /* Callbacks to handle tty ports */ + void (*dtr_rts)(struct hvc_struct *hp, int raise); }; /* Register a vterm and a slot index for use as a console (console_init) */ diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index b6f7d52f7c35..fd17a9b804b8 100644 --- a/drivers/tty/hvc/hvc_iucv.c +++ b/drivers/tty/hvc/hvc_iucv.c @@ -656,21 +656,64 @@ static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id) } /** + * hvc_iucv_dtr_rts() - HVC notifier for handling DTR/RTS + * @hp: Pointer the HVC device (struct hvc_struct) + * @raise: Non-zero to raise or zero to lower DTR/RTS lines + * + * This routine notifies the HVC back-end to raise or lower DTR/RTS + * lines. Raising DTR/RTS is ignored. Lowering DTR/RTS indicates to + * drop the IUCV connection (similar to hang up the modem). + */ +static void hvc_iucv_dtr_rts(struct hvc_struct *hp, int raise) +{ + struct hvc_iucv_private *priv; + struct iucv_path *path; + + /* Raising the DTR/RTS is ignored as IUCV connections can be + * established at any times. + */ + if (raise) + return; + + priv = hvc_iucv_get_private(hp->vtermno); + if (!priv) + return; + + /* Lowering the DTR/RTS lines disconnects an established IUCV + * connection. + */ + flush_sndbuf_sync(priv); + + spin_lock_bh(&priv->lock); + path = priv->path; /* save reference to IUCV path */ + priv->path = NULL; + priv->iucv_state = IUCV_DISCONN; + spin_unlock_bh(&priv->lock); + + /* Sever IUCV path outside of priv->lock due to lock ordering of: + * priv->lock <--> iucv_table_lock */ + if (path) { + iucv_path_sever(path, NULL); + iucv_path_free(path); + } +} + +/** * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time. * @hp: Pointer to the HVC device (struct hvc_struct) * @id: Additional data (originally passed to hvc_alloc): * the index of an struct hvc_iucv_private instance. * * This routine notifies the HVC back-end that the last tty device fd has been - * closed. The function calls hvc_iucv_cleanup() to clean up the struct - * hvc_iucv_private instance. + * closed. The function cleans up tty resources. The clean-up of the IUCV + * connection is done in hvc_iucv_dtr_rts() and depends on the HUPCL termios + * control setting. * * Locking: struct hvc_iucv_private->lock */ static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) { struct hvc_iucv_private *priv; - struct iucv_path *path; priv = hvc_iucv_get_private(id); if (!priv) @@ -679,17 +722,11 @@ static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) flush_sndbuf_sync(priv); spin_lock_bh(&priv->lock); - path = priv->path; /* save reference to IUCV path */ - priv->path = NULL; - hvc_iucv_cleanup(priv); + destroy_tty_buffer_list(&priv->tty_outqueue); + destroy_tty_buffer_list(&priv->tty_inqueue); + priv->tty_state = TTY_CLOSED; + priv->sndbuf_len = 0; spin_unlock_bh(&priv->lock); - - /* sever IUCV path outside of priv->lock due to lock ordering of: - * priv->lock <--> iucv_table_lock */ - if (path) { - iucv_path_sever(path, NULL); - iucv_path_free(path); - } } /** @@ -931,6 +968,7 @@ static const struct hv_ops hvc_iucv_ops = { .notifier_add = hvc_iucv_notifier_add, .notifier_del = hvc_iucv_notifier_del, .notifier_hangup = hvc_iucv_notifier_hangup, + .dtr_rts = hvc_iucv_dtr_rts, }; /* Suspend / resume device operations */ @@ -1328,7 +1366,7 @@ out_error: */ static int __init hvc_iucv_config(char *val) { - return strict_strtoul(val, 10, &hvc_iucv_devices); + return kstrtoul(val, 10, &hvc_iucv_devices); } diff --git a/drivers/tty/hvc/hvc_tile.c b/drivers/tty/hvc/hvc_tile.c index 7a84a0595477..af8cdaa1dcb9 100644 --- a/drivers/tty/hvc/hvc_tile.c +++ b/drivers/tty/hvc/hvc_tile.c @@ -18,16 +18,46 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> #include <linux/moduleparam.h> +#include <linux/platform_device.h> #include <linux/types.h> +#include <asm/setup.h> +#include <arch/sim_def.h> + #include <hv/hypervisor.h> #include "hvc_console.h" +static int use_sim_console; +static int __init sim_console(char *str) +{ + use_sim_console = 1; + return 0; +} +early_param("sim_console", sim_console); + +int tile_console_write(const char *buf, int count) +{ + if (unlikely(use_sim_console)) { + int i; + for (i = 0; i < count; ++i) + __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PUTC | + (buf[i] << _SIM_CONTROL_OPERATOR_BITS)); + __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PUTC | + (SIM_PUTC_FLUSH_BINARY << + _SIM_CONTROL_OPERATOR_BITS)); + return 0; + } else { + return hv_console_write((HV_VirtAddr)buf, count); + } +} + static int hvc_tile_put_chars(uint32_t vt, const char *buf, int count) { - return hv_console_write((HV_VirtAddr)buf, count); + return tile_console_write(buf, count); } static int hvc_tile_get_chars(uint32_t vt, char *buf, int count) @@ -44,25 +74,132 @@ static int hvc_tile_get_chars(uint32_t vt, char *buf, int count) return i; } +#ifdef __tilegx__ +/* + * IRQ based callbacks. + */ +static int hvc_tile_notifier_add_irq(struct hvc_struct *hp, int irq) +{ + int rc; + int cpu = raw_smp_processor_id(); /* Choose an arbitrary cpu */ + HV_Coord coord = { .x = cpu_x(cpu), .y = cpu_y(cpu) }; + + rc = notifier_add_irq(hp, irq); + if (rc) + return rc; + + /* + * Request that the hypervisor start sending us interrupts. + * If the hypervisor returns an error, we still return 0, so that + * we can fall back to polling. + */ + if (hv_console_set_ipi(KERNEL_PL, irq, coord) < 0) + notifier_del_irq(hp, irq); + + return 0; +} + +static void hvc_tile_notifier_del_irq(struct hvc_struct *hp, int irq) +{ + HV_Coord coord = { 0, 0 }; + + /* Tell the hypervisor to stop sending us interrupts. */ + hv_console_set_ipi(KERNEL_PL, -1, coord); + + notifier_del_irq(hp, irq); +} + +static void hvc_tile_notifier_hangup_irq(struct hvc_struct *hp, int irq) +{ + hvc_tile_notifier_del_irq(hp, irq); +} +#endif + static const struct hv_ops hvc_tile_get_put_ops = { .get_chars = hvc_tile_get_chars, .put_chars = hvc_tile_put_chars, +#ifdef __tilegx__ + .notifier_add = hvc_tile_notifier_add_irq, + .notifier_del = hvc_tile_notifier_del_irq, + .notifier_hangup = hvc_tile_notifier_hangup_irq, +#endif +}; + + +#ifdef __tilegx__ +static int hvc_tile_probe(struct platform_device *pdev) +{ + struct hvc_struct *hp; + int tile_hvc_irq; + + /* Create our IRQ and register it. */ + tile_hvc_irq = create_irq(); + if (tile_hvc_irq < 0) + return -ENXIO; + + tile_irq_activate(tile_hvc_irq, TILE_IRQ_PERCPU); + hp = hvc_alloc(0, tile_hvc_irq, &hvc_tile_get_put_ops, 128); + if (IS_ERR(hp)) { + destroy_irq(tile_hvc_irq); + return PTR_ERR(hp); + } + dev_set_drvdata(&pdev->dev, hp); + + return 0; +} + +static int hvc_tile_remove(struct platform_device *pdev) +{ + int rc; + struct hvc_struct *hp = dev_get_drvdata(&pdev->dev); + + rc = hvc_remove(hp); + if (rc == 0) + destroy_irq(hp->data); + + return rc; +} + +static void hvc_tile_shutdown(struct platform_device *pdev) +{ + struct hvc_struct *hp = dev_get_drvdata(&pdev->dev); + + hvc_tile_notifier_del_irq(hp, hp->data); +} + +static struct platform_device hvc_tile_pdev = { + .name = "hvc-tile", + .id = 0, +}; + +static struct platform_driver hvc_tile_driver = { + .probe = hvc_tile_probe, + .remove = hvc_tile_remove, + .shutdown = hvc_tile_shutdown, + .driver = { + .name = "hvc-tile", + .owner = THIS_MODULE, + } }; +#endif static int __init hvc_tile_console_init(void) { - extern void disable_early_printk(void); hvc_instantiate(0, 0, &hvc_tile_get_put_ops); add_preferred_console("hvc", 0, NULL); - disable_early_printk(); return 0; } console_initcall(hvc_tile_console_init); static int __init hvc_tile_init(void) { - struct hvc_struct *s; - s = hvc_alloc(0, 0, &hvc_tile_get_put_ops, 128); - return IS_ERR(s) ? PTR_ERR(s) : 0; +#ifndef __tilegx__ + struct hvc_struct *hp; + hp = hvc_alloc(0, 0, &hvc_tile_get_put_ops, 128); + return IS_ERR(hp) ? PTR_ERR(hp) : 0; +#else + platform_device_register(&hvc_tile_pdev); + return platform_driver_register(&hvc_tile_driver); +#endif } device_initcall(hvc_tile_init); diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c index 0c629807610e..c791b18cdd08 100644 --- a/drivers/tty/hvc/hvc_vio.c +++ b/drivers/tty/hvc/hvc_vio.c @@ -404,7 +404,7 @@ module_exit(hvc_vio_exit); void __init hvc_vio_init_early(void) { struct device_node *stdout_node; - const u32 *termno; + const __be32 *termno; const char *name; const struct hv_ops *ops; @@ -429,7 +429,7 @@ void __init hvc_vio_init_early(void) termno = of_get_property(stdout_node, "reg", NULL); if (termno == NULL) goto out; - hvterm_priv0.termno = *termno; + hvterm_priv0.termno = of_read_number(termno, 1); spin_lock_init(&hvterm_priv0.buf_lock); hvterm_privs[0] = &hvterm_priv0; diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c index 682210d778bd..c193af6a628f 100644 --- a/drivers/tty/hvc/hvc_xen.c +++ b/drivers/tty/hvc/hvc_xen.c @@ -208,7 +208,7 @@ static int xen_hvm_console_init(void) info = vtermno_to_xencons(HVC_COOKIE); if (!info) { - info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO); + info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL); if (!info) return -ENOMEM; } else if (info->intf != NULL) { @@ -257,7 +257,7 @@ static int xen_pv_console_init(void) info = vtermno_to_xencons(HVC_COOKIE); if (!info) { - info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO); + info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL); if (!info) return -ENOMEM; } else if (info->intf != NULL) { @@ -284,7 +284,7 @@ static int xen_initial_domain_console_init(void) info = vtermno_to_xencons(HVC_COOKIE); if (!info) { - info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO); + info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL); if (!info) return -ENOMEM; } @@ -636,6 +636,7 @@ struct console xenboot_console = { .name = "xenboot", .write = xenboot_write_console, .flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME, + .index = -1, }; #endif /* CONFIG_EARLY_PRINTK */ diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c index 3396eb9d57a3..ac2767100df5 100644 --- a/drivers/tty/hvc/hvsi_lib.c +++ b/drivers/tty/hvc/hvsi_lib.c @@ -341,8 +341,8 @@ void hvsilib_establish(struct hvsi_priv *pv) pr_devel("HVSI@%x: ... waiting handshake\n", pv->termno); - /* Try for up to 200s */ - for (timeout = 0; timeout < 20; timeout++) { + /* Try for up to 400ms */ + for (timeout = 0; timeout < 40; timeout++) { if (pv->established) goto established; if (!hvsi_get_packet(pv)) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 642239015b46..c0f76da55304 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -807,7 +807,7 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci) int h = dlci->adaption - 1; total_size = 0; - while(1) { + while (1) { len = kfifo_len(dlci->fifo); if (len == 0) return total_size; @@ -827,8 +827,8 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci) switch (dlci->adaption) { case 1: /* Unstructured */ break; - case 2: /* Unstructed with modem bits. Always one byte as we never - send inline break data */ + case 2: /* Unstructed with modem bits. + Always one byte as we never send inline break data */ *dp++ = gsm_encode_modem(dlci); break; } @@ -968,7 +968,7 @@ static void gsm_dlci_data_kick(struct gsm_dlci *dlci) unsigned long flags; int sweep; - if (dlci->constipated) + if (dlci->constipated) return; spin_lock_irqsave(&dlci->gsm->tx_lock, flags); @@ -981,7 +981,7 @@ static void gsm_dlci_data_kick(struct gsm_dlci *dlci) gsm_dlci_data_output(dlci->gsm, dlci); } if (sweep) - gsm_dlci_data_sweep(dlci->gsm); + gsm_dlci_data_sweep(dlci->gsm); spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags); } @@ -1138,7 +1138,7 @@ static void gsm_control_modem(struct gsm_mux *gsm, u8 *data, int clen) static void gsm_control_rls(struct gsm_mux *gsm, u8 *data, int clen) { struct tty_port *port; - unsigned int addr = 0 ; + unsigned int addr = 0; u8 bits; int len = clen; u8 *dp = data; @@ -1740,10 +1740,11 @@ static void gsm_queue(struct gsm_mux *gsm) if ((gsm->control & ~PF) == UI) gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len); - if (gsm->encoding == 0){ - /* WARNING: gsm->received_fcs is used for gsm->encoding = 0 only. - In this case it contain the last piece of data - required to generate final CRC */ + if (gsm->encoding == 0) { + /* WARNING: gsm->received_fcs is used for + gsm->encoding = 0 only. + In this case it contain the last piece of data + required to generate final CRC */ gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs); } if (gsm->fcs != GOOD_FCS) { @@ -2904,9 +2905,11 @@ static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty) gsm = gsm_mux[mux]; if (gsm->dead) return -EL2HLT; - /* If DLCI 0 is not yet fully open return an error. This is ok from a locking - perspective as we don't have to worry about this if DLCI0 is lost */ - if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) + /* If DLCI 0 is not yet fully open return an error. + This is ok from a locking + perspective as we don't have to worry about this + if DLCI0 is lost */ + if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) return -EL2NSYNC; dlci = gsm->dlci[line]; if (dlci == NULL) { diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 6c7fe90ad72d..7a744b69c3d1 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -50,6 +50,7 @@ #include <linux/uaccess.h> #include <linux/module.h> #include <linux/ratelimit.h> +#include <linux/vmalloc.h> /* number of characters left in xmit buffer before select has we have room */ @@ -74,36 +75,81 @@ #define ECHO_OP_SET_CANON_COL 0x81 #define ECHO_OP_ERASE_TAB 0x82 +#define ECHO_COMMIT_WATERMARK 256 +#define ECHO_BLOCK 256 +#define ECHO_DISCARD_WATERMARK N_TTY_BUF_SIZE - (ECHO_BLOCK + 32) + + +#undef N_TTY_TRACE +#ifdef N_TTY_TRACE +# define n_tty_trace(f, args...) trace_printk(f, ##args) +#else +# define n_tty_trace(f, args...) +#endif + struct n_tty_data { - unsigned int column; + /* producer-published */ + size_t read_head; + size_t canon_head; + size_t echo_head; + size_t echo_commit; + DECLARE_BITMAP(char_map, 256); + + /* private to n_tty_receive_overrun (single-threaded) */ unsigned long overrun_time; int num_overrun; + /* non-atomic */ + bool no_room; + + /* must hold exclusive termios_rwsem to reset these */ unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; - unsigned char echo_overrun:1; - DECLARE_BITMAP(process_char_map, 256); + /* shared by producer and consumer */ + char read_buf[N_TTY_BUF_SIZE]; DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE); + unsigned char echo_buf[N_TTY_BUF_SIZE]; - char *read_buf; - int read_head; - int read_tail; - int read_cnt; + int minimum_to_wake; - unsigned char *echo_buf; - unsigned int echo_pos; - unsigned int echo_cnt; + /* consumer-published */ + size_t read_tail; + size_t line_start; - int canon_data; - unsigned long canon_head; + /* protected by output lock */ + unsigned int column; unsigned int canon_column; + size_t echo_tail; struct mutex atomic_read_lock; struct mutex output_lock; - struct mutex echo_lock; - raw_spinlock_t read_lock; }; +static inline size_t read_cnt(struct n_tty_data *ldata) +{ + return ldata->read_head - ldata->read_tail; +} + +static inline unsigned char read_buf(struct n_tty_data *ldata, size_t i) +{ + return ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)]; +} + +static inline unsigned char *read_buf_addr(struct n_tty_data *ldata, size_t i) +{ + return &ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)]; +} + +static inline unsigned char echo_buf(struct n_tty_data *ldata, size_t i) +{ + return ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)]; +} + +static inline unsigned char *echo_buf_addr(struct n_tty_data *ldata, size_t i) +{ + return &ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)]; +} + static inline int tty_put_user(struct tty_struct *tty, unsigned char x, unsigned char __user *ptr) { @@ -113,30 +159,18 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, return put_user(x, ptr); } -/** - * n_tty_set__room - receive space - * @tty: terminal - * - * Called by the driver to find out how much data it is - * permitted to feed to the line discipline without any being lost - * and thus to manage flow control. Not serialized. Answers for the - * "instant". - */ - -static void n_tty_set_room(struct tty_struct *tty) +static int receive_room(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; int left; - int old_left; - /* 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 - ldata->read_cnt * 3 - 1; + left = N_TTY_BUF_SIZE - read_cnt(ldata) * 3 - 1; } else - left = N_TTY_BUF_SIZE - ldata->read_cnt - 1; + left = N_TTY_BUF_SIZE - read_cnt(ldata) - 1; /* * If we are doing input canonicalization, and there are no @@ -145,12 +179,31 @@ static void n_tty_set_room(struct tty_struct *tty) * characters will be beeped. */ if (left <= 0) - left = ldata->icanon && !ldata->canon_data; - old_left = tty->receive_room; - tty->receive_room = left; + left = ldata->icanon && ldata->canon_head == ldata->read_tail; + + return left; +} + +/** + * n_tty_set_room - receive space + * @tty: terminal + * + * Re-schedules the flip buffer work if space just became available. + * + * Caller holds exclusive termios_rwsem + * or + * n_tty_read()/consumer path: + * holds non-exclusive termios_rwsem + */ + +static void n_tty_set_room(struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; /* Did this open up the receive buffer? We may need to flip */ - if (left && !old_left) { + if (unlikely(ldata->no_room) && receive_room(tty)) { + ldata->no_room = 0; + WARN_RATELIMIT(tty->port->itty == NULL, "scheduling with invalid itty\n"); /* see if ldisc has been killed - if so, this means that @@ -159,17 +212,93 @@ static void n_tty_set_room(struct tty_struct *tty) */ WARN_RATELIMIT(test_bit(TTY_LDISC_HALTED, &tty->flags), "scheduling buffer work for halted ldisc\n"); - schedule_work(&tty->port->buf.work); + queue_work(system_unbound_wq, &tty->port->buf.work); } } -static void put_tty_queue_nolock(unsigned char c, struct n_tty_data *ldata) +static ssize_t chars_in_buffer(struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; + ssize_t n = 0; + + if (!ldata->icanon) + n = read_cnt(ldata); + else + n = ldata->canon_head - ldata->read_tail; + return n; +} + +/** + * n_tty_write_wakeup - asynchronous I/O notifier + * @tty: tty device + * + * Required for the ptys, serial driver etc. since processes + * that attach themselves to the master and rely on ASYNC + * IO must be woken up + */ + +static void n_tty_write_wakeup(struct tty_struct *tty) +{ + if (tty->fasync && test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) + kill_fasync(&tty->fasync, SIGIO, POLL_OUT); +} + +static void n_tty_check_throttle(struct tty_struct *tty) { - 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++; + if (tty->driver->type == TTY_DRIVER_TYPE_PTY) + return; + /* + * Check the remaining room for the input canonicalization + * mode. We don't want to throttle the driver if we're in + * canonical mode and don't have a newline yet! + */ + while (1) { + int throttled; + tty_set_flow_change(tty, TTY_THROTTLE_SAFE); + if (receive_room(tty) >= TTY_THRESHOLD_THROTTLE) + break; + throttled = tty_throttle_safe(tty); + if (!throttled) + break; } + __tty_set_flow_change(tty, 0); +} + +static void n_tty_check_unthrottle(struct tty_struct *tty) +{ + if (tty->driver->type == TTY_DRIVER_TYPE_PTY && + tty->link->ldisc->ops->write_wakeup == n_tty_write_wakeup) { + if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE) + return; + if (!tty->count) + return; + n_tty_set_room(tty); + n_tty_write_wakeup(tty->link); + wake_up_interruptible_poll(&tty->link->write_wait, POLLOUT); + return; + } + + /* If there is enough space in the read buffer now, let the + * low-level driver know. We use chars_in_buffer() to + * check the buffer, as it now knows about canonical mode. + * Otherwise, if the driver is throttled and the line is + * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, + * we won't get any more characters. + */ + + while (1) { + int unthrottled; + tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE); + if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE) + break; + if (!tty->count) + break; + n_tty_set_room(tty); + unthrottled = tty_unthrottle_safe(tty); + if (!unthrottled) + break; + } + __tty_set_flow_change(tty, 0); } /** @@ -177,21 +306,19 @@ static void put_tty_queue_nolock(unsigned char c, struct n_tty_data *ldata) * @c: character * @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 + * Add a character to the tty read_buf queue. + * + * n_tty_receive_buf()/producer path: + * caller holds non-exclusive termios_rwsem + * modifies read_head + * + * read_head is only considered 'published' if canonical mode is + * not active. */ -static void put_tty_queue(unsigned char c, struct n_tty_data *ldata) +static inline 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 - */ - raw_spin_lock_irqsave(&ldata->read_lock, flags); - put_tty_queue_nolock(c, ldata); - raw_spin_unlock_irqrestore(&ldata->read_lock, flags); + *read_buf_addr(ldata, ldata->read_head++) = c; } /** @@ -201,22 +328,17 @@ static void put_tty_queue(unsigned char c, struct n_tty_data *ldata) * Reset the read buffer counters and clear the flags. * Called from n_tty_open() and n_tty_flush_buffer(). * - * Locking: tty_read_lock for read fields. + * Locking: caller holds exclusive termios_rwsem + * (or locking is not required) */ static void reset_buffer_flags(struct n_tty_data *ldata) { - unsigned long flags; - - raw_spin_lock_irqsave(&ldata->read_lock, flags); - ldata->read_head = ldata->read_tail = ldata->read_cnt = 0; - raw_spin_unlock_irqrestore(&ldata->read_lock, flags); + ldata->read_head = ldata->canon_head = ldata->read_tail = 0; + ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0; + ldata->line_start = 0; - mutex_lock(&ldata->echo_lock); - ldata->echo_pos = ldata->echo_cnt = ldata->echo_overrun = 0; - mutex_unlock(&ldata->echo_lock); - - ldata->canon_head = ldata->canon_data = ldata->erasing = 0; + ldata->erasing = 0; bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); } @@ -240,16 +362,21 @@ static void n_tty_packet_mode_flush(struct tty_struct *tty) * buffer flushed (eg at hangup) or when the N_TTY line discipline * internally has to clean the pending queue (for example some signals). * - * Locking: ctrl_lock, read_lock. + * Holds termios_rwsem to exclude producer/consumer while + * buffer indices are reset. + * + * Locking: ctrl_lock, exclusive termios_rwsem */ static void n_tty_flush_buffer(struct tty_struct *tty) { + down_write(&tty->termios_rwsem); reset_buffer_flags(tty->disc_data); n_tty_set_room(tty); if (tty->link) n_tty_packet_mode_flush(tty); + up_write(&tty->termios_rwsem); } /** @@ -259,24 +386,18 @@ static void n_tty_flush_buffer(struct tty_struct *tty) * Report the number of characters buffered to be delivered to user * at this instant in time. * - * Locking: read_lock + * Locking: exclusive termios_rwsem */ 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; + ssize_t n; - raw_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); - } - raw_spin_unlock_irqrestore(&ldata->read_lock, flags); + WARN_ONCE(1, "%s is deprecated and scheduled for removal.", __func__); + + down_write(&tty->termios_rwsem); + n = chars_in_buffer(tty); + up_write(&tty->termios_rwsem); return n; } @@ -521,33 +642,23 @@ break_out: * are prioritized. Also, when control characters are echoed with a * prefixed "^", the pair is treated atomically and thus not separated. * - * Locking: output_lock to protect column state and space left, - * echo_lock to protect the echo buffer + * Locking: callers must hold output_lock */ -static void process_echoes(struct tty_struct *tty) +static size_t __process_echoes(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; - int space, nr; + int space, old_space; + size_t tail; unsigned char c; - unsigned char *cp, *buf_end; - - if (!ldata->echo_cnt) - return; - mutex_lock(&ldata->output_lock); - mutex_lock(&ldata->echo_lock); + old_space = space = tty_write_room(tty); - space = tty_write_room(tty); - - 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; + tail = ldata->echo_tail; + while (ldata->echo_commit != tail) { + c = echo_buf(ldata, tail); if (c == ECHO_OP_START) { unsigned char op; - unsigned char *opp; int no_space_left = 0; /* @@ -555,18 +666,13 @@ static void process_echoes(struct tty_struct *tty) * operation, get the next byte, which is either the * op code or a control character value. */ - opp = cp + 1; - if (opp == buf_end) - opp -= N_TTY_BUF_SIZE; - op = *opp; + op = echo_buf(ldata, tail + 1); switch (op) { unsigned int num_chars, num_bs; case ECHO_OP_ERASE_TAB: - if (++opp == buf_end) - opp -= N_TTY_BUF_SIZE; - num_chars = *opp; + num_chars = echo_buf(ldata, tail + 2); /* * Determine how many columns to go back @@ -592,21 +698,18 @@ static void process_echoes(struct tty_struct *tty) if (ldata->column > 0) ldata->column--; } - cp += 3; - nr -= 3; + tail += 3; break; case ECHO_OP_SET_CANON_COL: ldata->canon_column = ldata->column; - cp += 2; - nr -= 2; + tail += 2; break; case ECHO_OP_MOVE_BACK_COL: if (ldata->column > 0) ldata->column--; - cp += 2; - nr -= 2; + tail += 2; break; case ECHO_OP_START: @@ -618,8 +721,7 @@ static void process_echoes(struct tty_struct *tty) tty_put_char(tty, ECHO_OP_START); ldata->column++; space--; - cp += 2; - nr -= 2; + tail += 2; break; default: @@ -640,15 +742,13 @@ static void process_echoes(struct tty_struct *tty) tty_put_char(tty, op ^ 0100); ldata->column += 2; space -= 2; - cp += 2; - nr -= 2; + tail += 2; } if (no_space_left) break; } else { - if (O_OPOST(tty) && - !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { + if (O_OPOST(tty)) { int retval = do_output_char(c, tty, space); if (retval < 0) break; @@ -659,80 +759,92 @@ static void process_echoes(struct tty_struct *tty) tty_put_char(tty, c); space -= 1; } - cp += 1; - nr -= 1; + tail += 1; } - - /* When end of circular buffer reached, wrap around */ - if (cp >= buf_end) - cp -= N_TTY_BUF_SIZE; } - if (nr == 0) { - ldata->echo_pos = 0; - ldata->echo_cnt = 0; - ldata->echo_overrun = 0; - } else { - 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) - ldata->echo_overrun = 0; + /* If the echo buffer is nearly full (so that the possibility exists + * of echo overrun before the next commit), then discard enough + * data at the tail to prevent a subsequent overrun */ + while (ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) { + if (echo_buf(ldata, tail == ECHO_OP_START)) { + if (echo_buf(ldata, tail) == ECHO_OP_ERASE_TAB) + tail += 3; + else + tail += 2; + } else + tail++; } - mutex_unlock(&ldata->echo_lock); + ldata->echo_tail = tail; + return old_space - space; +} + +static void commit_echoes(struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; + size_t nr, old, echoed; + size_t head; + + head = ldata->echo_head; + old = ldata->echo_commit - ldata->echo_tail; + + /* Process committed echoes if the accumulated # of bytes + * is over the threshold (and try again each time another + * block is accumulated) */ + nr = head - ldata->echo_tail; + if (nr < ECHO_COMMIT_WATERMARK || (nr % ECHO_BLOCK > old % ECHO_BLOCK)) + return; + + mutex_lock(&ldata->output_lock); + ldata->echo_commit = head; + echoed = __process_echoes(tty); mutex_unlock(&ldata->output_lock); - if (tty->ops->flush_chars) + if (echoed && tty->ops->flush_chars) tty->ops->flush_chars(tty); } +static void process_echoes(struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; + size_t echoed; + + if (!L_ECHO(tty) || ldata->echo_commit == ldata->echo_tail) + return; + + mutex_lock(&ldata->output_lock); + echoed = __process_echoes(tty); + mutex_unlock(&ldata->output_lock); + + if (echoed && tty->ops->flush_chars) + tty->ops->flush_chars(tty); +} + +static void flush_echoes(struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; + + if (!L_ECHO(tty) || ldata->echo_commit == ldata->echo_head) + return; + + mutex_lock(&ldata->output_lock); + ldata->echo_commit = ldata->echo_head; + __process_echoes(tty); + mutex_unlock(&ldata->output_lock); +} + /** * add_echo_byte - add a byte to the echo buffer * @c: unicode byte to echo * @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 n_tty_data *ldata) +static inline void add_echo_byte(unsigned char c, struct n_tty_data *ldata) { - int new_byte_pos; - - if (ldata->echo_cnt == N_TTY_BUF_SIZE) { - /* Circular buffer is already at capacity */ - 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 (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) { - ldata->echo_pos += 3; - ldata->echo_cnt -= 2; - } else { - ldata->echo_pos += 2; - ldata->echo_cnt -= 1; - } - } else { - ldata->echo_pos++; - } - ldata->echo_pos &= N_TTY_BUF_SIZE - 1; - - ldata->echo_overrun = 1; - } else { - new_byte_pos = ldata->echo_pos + ldata->echo_cnt; - new_byte_pos &= N_TTY_BUF_SIZE - 1; - ldata->echo_cnt++; - } - - ldata->echo_buf[new_byte_pos] = c; + *echo_buf_addr(ldata, ldata->echo_head++) = c; } /** @@ -740,16 +852,12 @@ static void add_echo_byte(unsigned char c, struct n_tty_data *ldata) * @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 n_tty_data *ldata) { - 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); } /** @@ -758,16 +866,12 @@ static void echo_move_back_col(struct n_tty_data *ldata) * * Add an operation to the echo buffer to set the canon column * to the current column. - * - * Locking: echo_lock to protect the echo buffer */ static void echo_set_canon_col(struct n_tty_data *ldata) { - 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); } /** @@ -783,15 +887,11 @@ static void echo_set_canon_col(struct n_tty_data *ldata) * of input. This information will be used later, along with * canon column (if applicable), to go back the correct number * of columns. - * - * Locking: echo_lock to protect the echo buffer */ static void echo_erase_tab(unsigned int num_chars, int after_tab, struct n_tty_data *ldata) { - mutex_lock(&ldata->echo_lock); - add_echo_byte(ECHO_OP_START, ldata); add_echo_byte(ECHO_OP_ERASE_TAB, ldata); @@ -803,8 +903,6 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab, num_chars |= 0x80; add_echo_byte(num_chars, ldata); - - mutex_unlock(&ldata->echo_lock); } /** @@ -816,20 +914,16 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab, * L_ECHO(tty) is true. Called from the driver receive_buf path. * * This variant does not treat control characters specially. - * - * Locking: echo_lock to protect the echo buffer */ static void echo_char_raw(unsigned char c, struct n_tty_data *ldata) { - mutex_lock(&ldata->echo_lock); if (c == ECHO_OP_START) { add_echo_byte(ECHO_OP_START, ldata); add_echo_byte(ECHO_OP_START, ldata); } else { add_echo_byte(c, ldata); } - mutex_unlock(&ldata->echo_lock); } /** @@ -842,16 +936,12 @@ static void echo_char_raw(unsigned char c, struct n_tty_data *ldata) * * This variant tags control characters to be echoed as "^X" * (where X is the letter representing the control char). - * - * Locking: echo_lock to protect the echo buffer */ static void echo_char(unsigned char c, struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; - mutex_lock(&ldata->echo_lock); - if (c == ECHO_OP_START) { add_echo_byte(ECHO_OP_START, ldata); add_echo_byte(ECHO_OP_START, ldata); @@ -860,8 +950,6 @@ static void echo_char(unsigned char c, struct tty_struct *tty) add_echo_byte(ECHO_OP_START, ldata); add_echo_byte(c, ldata); } - - mutex_unlock(&ldata->echo_lock); } /** @@ -886,17 +974,22 @@ static inline void finish_erasing(struct n_tty_data *ldata) * present in the stream from the driver layer. Handles the complexities * of UTF-8 multibyte symbols. * - * Locking: read_lock for tty buffers + * n_tty_receive_buf()/producer path: + * caller holds non-exclusive termios_rwsem + * modifies read_head + * + * Modifying the read_head is not considered a publish in this context + * because canonical mode is active -- only canon_head publishes */ 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; + size_t head; + size_t cnt; + int seen_alnums; - /* FIXME: locking needed ? */ if (ldata->read_head == ldata->canon_head) { /* process_output('\a', tty); */ /* what do you think? */ return; @@ -907,19 +1000,11 @@ static void eraser(unsigned char c, struct tty_struct *tty) kill_type = WERASE; else { if (!L_ECHO(tty)) { - raw_spin_lock_irqsave(&ldata->read_lock, flags); - ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) & - (N_TTY_BUF_SIZE - 1)); ldata->read_head = ldata->canon_head; - raw_spin_unlock_irqrestore(&ldata->read_lock, flags); return; } if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) { - raw_spin_lock_irqsave(&ldata->read_lock, flags); - ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) & - (N_TTY_BUF_SIZE - 1)); ldata->read_head = ldata->canon_head; - raw_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. */ @@ -931,14 +1016,13 @@ static void eraser(unsigned char c, struct tty_struct *tty) } seen_alnums = 0; - /* FIXME: Locking ?? */ 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 = ldata->read_buf[head]; + head--; + c = read_buf(ldata, head); } while (is_continuation(c, tty) && head != ldata->canon_head); /* do not partially erase */ @@ -952,11 +1036,8 @@ static void eraser(unsigned char c, struct tty_struct *tty) else if (seen_alnums) break; } - cnt = (ldata->read_head - head) & (N_TTY_BUF_SIZE-1); - raw_spin_lock_irqsave(&ldata->read_lock, flags); + cnt = ldata->read_head - head; ldata->read_head = head; - ldata->read_cnt -= cnt; - raw_spin_unlock_irqrestore(&ldata->read_lock, flags); if (L_ECHO(tty)) { if (L_ECHOPRT(tty)) { if (!ldata->erasing) { @@ -966,9 +1047,8 @@ static void eraser(unsigned char c, struct tty_struct *tty) /* 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(ldata->read_buf[head], - ldata); + head++; + echo_char_raw(read_buf(ldata, head), ldata); echo_move_back_col(ldata); } } else if (kill_type == ERASE && !L_ECHOE(tty)) { @@ -976,7 +1056,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) } else if (c == '\t') { unsigned int num_chars = 0; int after_tab = 0; - unsigned long tail = ldata->read_head; + size_t tail = ldata->read_head; /* * Count the columns used for characters @@ -986,8 +1066,8 @@ static void eraser(unsigned char c, struct tty_struct *tty) * number of columns. */ while (tail != ldata->canon_head) { - tail = (tail-1) & (N_TTY_BUF_SIZE-1); - c = ldata->read_buf[tail]; + tail--; + c = read_buf(ldata, tail); if (c == '\t') { after_tab = 1; break; @@ -1030,7 +1110,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) * Locking: ctrl_lock */ -static inline void isig(int sig, struct tty_struct *tty) +static void isig(int sig, struct tty_struct *tty) { struct pid *tty_pgrp = tty_get_pgrp(tty); if (tty_pgrp) { @@ -1046,10 +1126,14 @@ static inline void isig(int sig, struct tty_struct *tty) * An RS232 break event has been hit in the incoming bitstream. This * can cause a variety of events depending upon the termios settings. * - * Called from the receive_buf path so single threaded. + * n_tty_receive_buf()/producer path: + * caller holds non-exclusive termios_rwsem + * publishes read_head via put_tty_queue() + * + * Note: may get exclusive termios_rwsem if flushing input buffer */ -static inline void n_tty_receive_break(struct tty_struct *tty) +static void n_tty_receive_break(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; @@ -1058,8 +1142,11 @@ static inline void n_tty_receive_break(struct tty_struct *tty) if (I_BRKINT(tty)) { isig(SIGINT, tty); if (!L_NOFLSH(tty)) { + /* flushing needs exclusive termios_rwsem */ + up_read(&tty->termios_rwsem); n_tty_flush_buffer(tty); tty_driver_flush_buffer(tty); + down_read(&tty->termios_rwsem); } return; } @@ -1084,7 +1171,7 @@ static inline void n_tty_receive_break(struct tty_struct *tty) * private. */ -static inline void n_tty_receive_overrun(struct tty_struct *tty) +static void n_tty_receive_overrun(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; char buf[64]; @@ -1106,10 +1193,13 @@ static inline void n_tty_receive_overrun(struct tty_struct *tty) * @c: character * * Process a parity error and queue the right data to indicate - * the error case if necessary. Locking as per n_tty_receive_buf. + * the error case if necessary. + * + * n_tty_receive_buf()/producer path: + * caller holds non-exclusive termios_rwsem + * publishes read_head via put_tty_queue() */ -static inline void n_tty_receive_parity_error(struct tty_struct *tty, - unsigned char c) +static void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c) { struct n_tty_data *ldata = tty->disc_data; @@ -1126,6 +1216,26 @@ static inline void n_tty_receive_parity_error(struct tty_struct *tty, wake_up_interruptible(&tty->read_wait); } +static void +n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c) +{ + if (!L_NOFLSH(tty)) { + /* flushing needs exclusive termios_rwsem */ + up_read(&tty->termios_rwsem); + n_tty_flush_buffer(tty); + tty_driver_flush_buffer(tty); + down_read(&tty->termios_rwsem); + } + if (I_IXON(tty)) + start_tty(tty); + if (L_ECHO(tty)) { + echo_char(c, tty); + commit_echoes(tty); + } + isig(signal, tty); + return; +} + /** * n_tty_receive_char - perform processing * @tty: terminal device @@ -1134,117 +1244,54 @@ static inline void n_tty_receive_parity_error(struct tty_struct *tty, * Process an individual character of input received from the driver. * This is serialized with respect to itself by the rules for the * driver above. + * + * n_tty_receive_buf()/producer path: + * caller holds non-exclusive termios_rwsem + * publishes canon_head if canonical mode is active + * otherwise, publishes read_head via put_tty_queue() + * + * Returns 1 if LNEXT was received, else returns 0 */ -static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) +static int +n_tty_receive_char_special(struct tty_struct *tty, unsigned char c) { struct n_tty_data *ldata = tty->disc_data; - unsigned long flags; int parmrk; - if (ldata->raw) { - put_tty_queue(c, ldata); - return; - } - - if (I_ISTRIP(tty)) - c &= 0x7f; - if (I_IUCLC(tty) && L_IEXTEN(tty)) - c = tolower(c); - - if (L_EXTPROC(tty)) { - put_tty_queue(c, ldata); - return; - } - - if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && - I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty) && - c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && c != SUSP_CHAR(tty)) { - start_tty(tty); - process_echoes(tty); - } - - if (tty->closing) { - if (I_IXON(tty)) { - if (c == START_CHAR(tty)) { - start_tty(tty); - process_echoes(tty); - } else if (c == STOP_CHAR(tty)) - stop_tty(tty); - } - return; - } - - /* - * If the previous character was LNEXT, or we know that this - * character is not one of the characters that we'll have to - * handle specially, do shortcut processing to speed things - * up. - */ - if (!test_bit(c, ldata->process_char_map) || ldata->lnext) { - ldata->lnext = 0; - parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; - 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(ldata); - /* Record the column of first canon char. */ - 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, ldata); - put_tty_queue(c, ldata); - return; - } - if (I_IXON(tty)) { if (c == START_CHAR(tty)) { start_tty(tty); - process_echoes(tty); - return; + commit_echoes(tty); + return 0; } if (c == STOP_CHAR(tty)) { stop_tty(tty); - return; + return 0; } } if (L_ISIG(tty)) { - int signal; - signal = SIGINT; - if (c == INTR_CHAR(tty)) - goto send_signal; - signal = SIGQUIT; - if (c == QUIT_CHAR(tty)) - goto send_signal; - signal = SIGTSTP; - if (c == SUSP_CHAR(tty)) { -send_signal: - if (!L_NOFLSH(tty)) { - n_tty_flush_buffer(tty); - tty_driver_flush_buffer(tty); - } - if (I_IXON(tty)) - start_tty(tty); - if (L_ECHO(tty)) { - echo_char(c, tty); - process_echoes(tty); - } - isig(signal, tty); - return; + if (c == INTR_CHAR(tty)) { + n_tty_receive_signal_char(tty, SIGINT, c); + return 0; + } else if (c == QUIT_CHAR(tty)) { + n_tty_receive_signal_char(tty, SIGQUIT, c); + return 0; + } else if (c == SUSP_CHAR(tty)) { + n_tty_receive_signal_char(tty, SIGTSTP, c); + return 0; } } + if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) { + start_tty(tty); + process_echoes(tty); + } + if (c == '\r') { if (I_IGNCR(tty)) - return; + return 0; if (I_ICRNL(tty)) c = '\n'; } else if (c == '\n' && I_INLCR(tty)) @@ -1254,8 +1301,8 @@ send_signal: if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) { eraser(c, tty); - process_echoes(tty); - return; + commit_echoes(tty); + return 0; } if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) { ldata->lnext = 1; @@ -1264,42 +1311,32 @@ send_signal: if (L_ECHOCTL(tty)) { echo_char_raw('^', ldata); echo_char_raw('\b', ldata); - process_echoes(tty); + commit_echoes(tty); } } - return; + return 1; } - if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && - L_IEXTEN(tty)) { - unsigned long tail = ldata->canon_head; + if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) { + size_t tail = ldata->canon_head; finish_erasing(ldata); echo_char(c, 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); + echo_char(read_buf(ldata, tail), tty); + tail++; } - process_echoes(tty); - return; + commit_echoes(tty); + return 0; } if (c == '\n') { - 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', ldata); - process_echoes(tty); + commit_echoes(tty); } goto handle_newline; } if (c == EOF_CHAR(tty)) { - if (ldata->read_cnt >= N_TTY_BUF_SIZE) - return; - if (ldata->canon_head != ldata->read_head) - set_bit(TTY_PUSH, &tty->flags); c = __DISABLED_CHAR; goto handle_newline; } @@ -1307,11 +1344,6 @@ send_signal: (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) { parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; - if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk)) { - if (L_ECHO(tty)) - process_output('\a', tty); - return; - } /* * XXX are EOL_CHAR and EOL2_CHAR echoed?!? */ @@ -1320,7 +1352,7 @@ send_signal: if (ldata->canon_head == ldata->read_head) echo_set_canon_col(ldata); echo_char(c, tty); - process_echoes(tty); + commit_echoes(tty); } /* * XXX does PARMRK doubling happen for @@ -1330,26 +1362,17 @@ send_signal: put_tty_queue(c, ldata); handle_newline: - raw_spin_lock_irqsave(&ldata->read_lock, flags); - set_bit(ldata->read_head, ldata->read_flags); - put_tty_queue_nolock(c, ldata); + set_bit(ldata->read_head & (N_TTY_BUF_SIZE - 1), ldata->read_flags); + put_tty_queue(c, ldata); ldata->canon_head = ldata->read_head; - ldata->canon_data++; - raw_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); - return; + return 0; } } parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; - 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(ldata); if (c == '\n') @@ -1360,29 +1383,123 @@ handle_newline: echo_set_canon_col(ldata); echo_char(c, tty); } - process_echoes(tty); + commit_echoes(tty); } if (parmrk) put_tty_queue(c, ldata); put_tty_queue(c, ldata); + return 0; } +static inline void +n_tty_receive_char_inline(struct tty_struct *tty, unsigned char c) +{ + struct n_tty_data *ldata = tty->disc_data; + int parmrk; -/** - * n_tty_write_wakeup - asynchronous I/O notifier - * @tty: tty device - * - * Required for the ptys, serial driver etc. since processes - * that attach themselves to the master and rely on ASYNC - * IO must be woken up - */ + if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) { + start_tty(tty); + process_echoes(tty); + } + if (L_ECHO(tty)) { + finish_erasing(ldata); + /* Record the column of first canon char. */ + if (ldata->canon_head == ldata->read_head) + echo_set_canon_col(ldata); + echo_char(c, tty); + commit_echoes(tty); + } + parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; + if (parmrk) + put_tty_queue(c, ldata); + put_tty_queue(c, ldata); +} -static void n_tty_write_wakeup(struct tty_struct *tty) +static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) { - if (tty->fasync && test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) - kill_fasync(&tty->fasync, SIGIO, POLL_OUT); + n_tty_receive_char_inline(tty, c); +} + +static inline void +n_tty_receive_char_fast(struct tty_struct *tty, unsigned char c) +{ + struct n_tty_data *ldata = tty->disc_data; + + if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) { + start_tty(tty); + process_echoes(tty); + } + if (L_ECHO(tty)) { + finish_erasing(ldata); + /* Record the column of first canon char. */ + if (ldata->canon_head == ldata->read_head) + echo_set_canon_col(ldata); + echo_char(c, tty); + commit_echoes(tty); + } + put_tty_queue(c, ldata); +} + +static inline void +n_tty_receive_char_closing(struct tty_struct *tty, unsigned char c) +{ + if (I_ISTRIP(tty)) + c &= 0x7f; + if (I_IUCLC(tty) && L_IEXTEN(tty)) + c = tolower(c); + + if (I_IXON(tty)) { + if (c == STOP_CHAR(tty)) + stop_tty(tty); + else if (c == START_CHAR(tty) || + (tty->stopped && !tty->flow_stopped && I_IXANY(tty) && + c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && + c != SUSP_CHAR(tty))) { + start_tty(tty); + process_echoes(tty); + } + } +} + +static void +n_tty_receive_char_flagged(struct tty_struct *tty, unsigned char c, char flag) +{ + char buf[64]; + + switch (flag) { + case TTY_BREAK: + n_tty_receive_break(tty); + break; + case TTY_PARITY: + case TTY_FRAME: + n_tty_receive_parity_error(tty, c); + break; + case TTY_OVERRUN: + n_tty_receive_overrun(tty); + break; + default: + printk(KERN_ERR "%s: unknown flag %d\n", + tty_name(tty, buf), flag); + break; + } +} + +static void +n_tty_receive_char_lnext(struct tty_struct *tty, unsigned char c, char flag) +{ + struct n_tty_data *ldata = tty->disc_data; + + ldata->lnext = 0; + if (likely(flag == TTY_NORMAL)) { + if (I_ISTRIP(tty)) + c &= 0x7f; + if (I_IUCLC(tty) && L_IEXTEN(tty)) + c = tolower(c); + n_tty_receive_char(tty, c); + } else + n_tty_receive_char_flagged(tty, c, flag); } /** @@ -1396,86 +1513,220 @@ static void n_tty_write_wakeup(struct tty_struct *tty) * been received. This function must be called from soft contexts * not from interrupt context. The driver is responsible for making * calls one at a time and in order (or using flush_to_ldisc) + * + * n_tty_receive_buf()/producer path: + * claims non-exclusive termios_rwsem + * publishes read_head and canon_head */ -static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static void +n_tty_receive_buf_real_raw(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 (ldata->real_raw) { - 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(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 - ldata->read_cnt, - N_TTY_BUF_SIZE - ldata->read_head); - i = min(count, 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; - raw_spin_unlock_irqrestore(&ldata->read_lock, cpuflags); - } else { - for (i = count, p = cp, f = fp; i; i--, p++) { - if (f) - flags = *f++; - switch (flags) { - case TTY_NORMAL: - n_tty_receive_char(tty, *p); - break; - case TTY_BREAK: - n_tty_receive_break(tty); - break; - case TTY_PARITY: - case TTY_FRAME: - n_tty_receive_parity_error(tty, *p); - break; - case TTY_OVERRUN: - n_tty_receive_overrun(tty); - break; - default: - printk(KERN_ERR "%s: unknown flag %d\n", - tty_name(tty, buf), flags); - break; + size_t n, head; + + head = ldata->read_head & (N_TTY_BUF_SIZE - 1); + n = N_TTY_BUF_SIZE - max(read_cnt(ldata), head); + n = min_t(size_t, count, n); + memcpy(read_buf_addr(ldata, head), cp, n); + ldata->read_head += n; + cp += n; + count -= n; + + head = ldata->read_head & (N_TTY_BUF_SIZE - 1); + n = N_TTY_BUF_SIZE - max(read_cnt(ldata), head); + n = min_t(size_t, count, n); + memcpy(read_buf_addr(ldata, head), cp, n); + ldata->read_head += n; +} + +static void +n_tty_receive_buf_raw(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct n_tty_data *ldata = tty->disc_data; + char flag = TTY_NORMAL; + + while (count--) { + if (fp) + flag = *fp++; + if (likely(flag == TTY_NORMAL)) + put_tty_queue(*cp++, ldata); + else + n_tty_receive_char_flagged(tty, *cp++, flag); + } +} + +static void +n_tty_receive_buf_closing(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + char flag = TTY_NORMAL; + + while (count--) { + if (fp) + flag = *fp++; + if (likely(flag == TTY_NORMAL)) + n_tty_receive_char_closing(tty, *cp++); + else + n_tty_receive_char_flagged(tty, *cp++, flag); + } +} + +static void +n_tty_receive_buf_standard(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct n_tty_data *ldata = tty->disc_data; + char flag = TTY_NORMAL; + + while (count--) { + if (fp) + flag = *fp++; + if (likely(flag == TTY_NORMAL)) { + unsigned char c = *cp++; + + if (I_ISTRIP(tty)) + c &= 0x7f; + if (I_IUCLC(tty) && L_IEXTEN(tty)) + c = tolower(c); + if (L_EXTPROC(tty)) { + put_tty_queue(c, ldata); + continue; + } + if (!test_bit(c, ldata->char_map)) + n_tty_receive_char_inline(tty, c); + else if (n_tty_receive_char_special(tty, c) && count) { + if (fp) + flag = *fp++; + n_tty_receive_char_lnext(tty, *cp++, flag); + count--; } + } else + n_tty_receive_char_flagged(tty, *cp++, flag); + } +} + +static void +n_tty_receive_buf_fast(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct n_tty_data *ldata = tty->disc_data; + char flag = TTY_NORMAL; + + while (count--) { + if (fp) + flag = *fp++; + if (likely(flag == TTY_NORMAL)) { + unsigned char c = *cp++; + + if (!test_bit(c, ldata->char_map)) + n_tty_receive_char_fast(tty, c); + else if (n_tty_receive_char_special(tty, c) && count) { + if (fp) + flag = *fp++; + n_tty_receive_char_lnext(tty, *cp++, flag); + count--; + } + } else + n_tty_receive_char_flagged(tty, *cp++, flag); + } +} + +static void __receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct n_tty_data *ldata = tty->disc_data; + bool preops = I_ISTRIP(tty) || (I_IUCLC(tty) && L_IEXTEN(tty)); + + if (ldata->real_raw) + n_tty_receive_buf_real_raw(tty, cp, fp, count); + else if (ldata->raw || (L_EXTPROC(tty) && !preops)) + n_tty_receive_buf_raw(tty, cp, fp, count); + else if (tty->closing && !L_EXTPROC(tty)) + n_tty_receive_buf_closing(tty, cp, fp, count); + else { + if (ldata->lnext) { + char flag = TTY_NORMAL; + + if (fp) + flag = *fp++; + n_tty_receive_char_lnext(tty, *cp++, flag); + count--; } + + if (!preops && !I_PARMRK(tty)) + n_tty_receive_buf_fast(tty, cp, fp, count); + else + n_tty_receive_buf_standard(tty, cp, fp, count); + + flush_echoes(tty); if (tty->ops->flush_chars) tty->ops->flush_chars(tty); } - n_tty_set_room(tty); - - if ((!ldata->icanon && (ldata->read_cnt >= tty->minimum_to_wake)) || + if ((!ldata->icanon && (read_cnt(ldata) >= ldata->minimum_to_wake)) || L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) wake_up_interruptible(&tty->read_wait); } +} + +static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + int room, n; + + down_read(&tty->termios_rwsem); - /* - * Check the remaining room for the input canonicalization - * mode. We don't want to throttle the driver if we're in - * canonical mode and don't have a newline yet! - */ while (1) { - tty_set_flow_change(tty, TTY_THROTTLE_SAFE); - if (tty->receive_room >= TTY_THRESHOLD_THROTTLE) + room = receive_room(tty); + n = min(count, room); + if (!n) break; - if (!tty_throttle_safe(tty)) + __receive_buf(tty, cp, fp, n); + cp += n; + if (fp) + fp += n; + count -= n; + } + + tty->receive_room = room; + n_tty_check_throttle(tty); + up_read(&tty->termios_rwsem); +} + +static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct n_tty_data *ldata = tty->disc_data; + int room, n, rcvd = 0; + + down_read(&tty->termios_rwsem); + + while (1) { + room = receive_room(tty); + n = min(count, room); + if (!n) { + if (!room) + ldata->no_room = 1; break; + } + __receive_buf(tty, cp, fp, n); + cp += n; + if (fp) + fp += n; + count -= n; + rcvd += n; } - __tty_set_flow_change(tty, 0); + + tty->receive_room = room; + n_tty_check_throttle(tty); + up_read(&tty->termios_rwsem); + + return rcvd; } int is_ignored(int sig) @@ -1495,7 +1746,7 @@ int is_ignored(int sig) * guaranteed that this function will not be re-entered or in progress * when the ldisc is closed. * - * Locking: Caller holds tty->termios_mutex + * Locking: Caller holds tty->termios_rwsem */ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) @@ -1507,60 +1758,52 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON; if (canon_change) { bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); - ldata->canon_head = ldata->read_tail; - ldata->canon_data = 0; + ldata->line_start = ldata->canon_head = ldata->read_tail; ldata->erasing = 0; + ldata->lnext = 0; } - if (canon_change && !L_ICANON(tty) && ldata->read_cnt) + if (canon_change && !L_ICANON(tty) && read_cnt(ldata)) wake_up_interruptible(&tty->read_wait); ldata->icanon = (L_ICANON(tty) != 0); - if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { - ldata->raw = 1; - ldata->real_raw = 1; - n_tty_set_room(tty); - return; - } + if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) || I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) || I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) || I_PARMRK(tty)) { - bitmap_zero(ldata->process_char_map, 256); + bitmap_zero(ldata->char_map, 256); if (I_IGNCR(tty) || I_ICRNL(tty)) - set_bit('\r', ldata->process_char_map); + set_bit('\r', ldata->char_map); if (I_INLCR(tty)) - set_bit('\n', ldata->process_char_map); + set_bit('\n', ldata->char_map); if (L_ICANON(tty)) { - 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); + set_bit(ERASE_CHAR(tty), ldata->char_map); + set_bit(KILL_CHAR(tty), ldata->char_map); + set_bit(EOF_CHAR(tty), ldata->char_map); + set_bit('\n', ldata->char_map); + set_bit(EOL_CHAR(tty), ldata->char_map); if (L_IEXTEN(tty)) { - set_bit(WERASE_CHAR(tty), - ldata->process_char_map); - set_bit(LNEXT_CHAR(tty), - ldata->process_char_map); - set_bit(EOL2_CHAR(tty), - ldata->process_char_map); + set_bit(WERASE_CHAR(tty), ldata->char_map); + set_bit(LNEXT_CHAR(tty), ldata->char_map); + set_bit(EOL2_CHAR(tty), ldata->char_map); if (L_ECHO(tty)) set_bit(REPRINT_CHAR(tty), - ldata->process_char_map); + ldata->char_map); } } if (I_IXON(tty)) { - set_bit(START_CHAR(tty), ldata->process_char_map); - set_bit(STOP_CHAR(tty), ldata->process_char_map); + set_bit(START_CHAR(tty), ldata->char_map); + set_bit(STOP_CHAR(tty), ldata->char_map); } if (L_ISIG(tty)) { - 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); + set_bit(INTR_CHAR(tty), ldata->char_map); + set_bit(QUIT_CHAR(tty), ldata->char_map); + set_bit(SUSP_CHAR(tty), ldata->char_map); } - clear_bit(__DISABLED_CHAR, ldata->process_char_map); + clear_bit(__DISABLED_CHAR, ldata->char_map); ldata->raw = 0; ldata->real_raw = 0; } else { @@ -1603,9 +1846,7 @@ static void n_tty_close(struct tty_struct *tty) if (tty->link) n_tty_packet_mode_flush(tty); - kfree(ldata->read_buf); - kfree(ldata->echo_buf); - kfree(ldata); + vfree(ldata); tty->disc_data = NULL; } @@ -1623,26 +1864,23 @@ static int n_tty_open(struct tty_struct *tty) { struct n_tty_data *ldata; - ldata = kzalloc(sizeof(*ldata), GFP_KERNEL); + /* Currently a malloc failure here can panic */ + ldata = vmalloc(sizeof(*ldata)); if (!ldata) goto err; ldata->overrun_time = jiffies; mutex_init(&ldata->atomic_read_lock); mutex_init(&ldata->output_lock); - mutex_init(&ldata->echo_lock); - raw_spin_lock_init(&ldata->read_lock); - - /* These are ugly. Currently a malloc failure here can panic */ - 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; tty->disc_data = ldata; reset_buffer_flags(tty->disc_data); ldata->column = 0; - tty->minimum_to_wake = 1; + ldata->canon_column = 0; + ldata->minimum_to_wake = 1; + ldata->num_overrun = 0; + ldata->no_room = 0; + ldata->lnext = 0; tty->closing = 0; /* indicate buffer work may resume */ clear_bit(TTY_LDISC_HALTED, &tty->flags); @@ -1650,10 +1888,6 @@ static int n_tty_open(struct tty_struct *tty) tty_unthrottle(tty); return 0; -err_free_bufs: - kfree(ldata->read_buf); - kfree(ldata->echo_buf); - kfree(ldata); err: return -ENOMEM; } @@ -1662,11 +1896,10 @@ 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 (ldata->icanon && !L_EXTPROC(tty)) { - if (ldata->canon_data) + if (ldata->canon_head != ldata->read_tail) return 1; - } else if (ldata->read_cnt >= (amt ? amt : 1)) + } else if (read_cnt(ldata) >= (amt ? amt : 1)) return 1; return 0; @@ -1687,6 +1920,9 @@ static inline int input_available_p(struct tty_struct *tty, int amt) * * Called under the ldata->atomic_read_lock sem * + * n_tty_read()/consumer path: + * caller holds non-exclusive termios_rwsem + * read_tail published */ static int copy_from_read_buf(struct tty_struct *tty, @@ -1697,34 +1933,114 @@ static int copy_from_read_buf(struct tty_struct *tty, struct n_tty_data *ldata = tty->disc_data; int retval; size_t n; - unsigned long flags; bool is_eof; + size_t tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1); retval = 0; - raw_spin_lock_irqsave(&ldata->read_lock, flags); - n = min(ldata->read_cnt, N_TTY_BUF_SIZE - ldata->read_tail); + n = min(read_cnt(ldata), N_TTY_BUF_SIZE - tail); n = min(*nr, n); - raw_spin_unlock_irqrestore(&ldata->read_lock, flags); if (n) { - retval = copy_to_user(*b, &ldata->read_buf[ldata->read_tail], n); + retval = copy_to_user(*b, read_buf_addr(ldata, tail), n); n -= retval; - is_eof = n == 1 && - ldata->read_buf[ldata->read_tail] == EOF_CHAR(tty); - tty_audit_add_data(tty, &ldata->read_buf[ldata->read_tail], n, + is_eof = n == 1 && read_buf(ldata, tail) == EOF_CHAR(tty); + tty_audit_add_data(tty, read_buf_addr(ldata, tail), n, ldata->icanon); - raw_spin_lock_irqsave(&ldata->read_lock, flags); - ldata->read_tail = (ldata->read_tail + n) & (N_TTY_BUF_SIZE-1); - ldata->read_cnt -= n; + ldata->read_tail += n; /* Turn single EOF into zero-length read */ - if (L_EXTPROC(tty) && ldata->icanon && is_eof && !ldata->read_cnt) + if (L_EXTPROC(tty) && ldata->icanon && is_eof && !read_cnt(ldata)) n = 0; - raw_spin_unlock_irqrestore(&ldata->read_lock, flags); *b += n; *nr -= n; } return retval; } +/** + * canon_copy_from_read_buf - copy read data in canonical mode + * @tty: terminal device + * @b: user data + * @nr: size of data + * + * Helper function for n_tty_read. It is only called when ICANON is on; + * it copies one line of input up to and including the line-delimiting + * character into the user-space buffer. + * + * Called under the atomic_read_lock mutex + * + * n_tty_read()/consumer path: + * caller holds non-exclusive termios_rwsem + * read_tail published + */ + +static int canon_copy_from_read_buf(struct tty_struct *tty, + unsigned char __user **b, + size_t *nr) +{ + struct n_tty_data *ldata = tty->disc_data; + size_t n, size, more, c; + size_t eol; + size_t tail; + int ret, found = 0; + bool eof_push = 0; + + /* N.B. avoid overrun if nr == 0 */ + n = min(*nr, read_cnt(ldata)); + if (!n) + return 0; + + tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1); + size = min_t(size_t, tail + n, N_TTY_BUF_SIZE); + + n_tty_trace("%s: nr:%zu tail:%zu n:%zu size:%zu\n", + __func__, *nr, tail, n, size); + + eol = find_next_bit(ldata->read_flags, size, tail); + more = n - (size - tail); + if (eol == N_TTY_BUF_SIZE && more) { + /* scan wrapped without finding set bit */ + eol = find_next_bit(ldata->read_flags, more, 0); + if (eol != more) + found = 1; + } else if (eol != size) + found = 1; + + size = N_TTY_BUF_SIZE - tail; + n = (found + eol + size) & (N_TTY_BUF_SIZE - 1); + c = n; + + if (found && read_buf(ldata, eol) == __DISABLED_CHAR) { + n--; + eof_push = !n && ldata->read_tail != ldata->line_start; + } + + n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu size:%zu more:%zu\n", + __func__, eol, found, n, c, size, more); + + if (n > size) { + ret = copy_to_user(*b, read_buf_addr(ldata, tail), size); + if (ret) + return -EFAULT; + ret = copy_to_user(*b + size, ldata->read_buf, n - size); + } else + ret = copy_to_user(*b, read_buf_addr(ldata, tail), n); + + if (ret) + return -EFAULT; + *b += n; + *nr -= n; + + if (found) + clear_bit(eol, ldata->read_flags); + smp_mb__after_clear_bit(); + ldata->read_tail += c; + + if (found) { + ldata->line_start = ldata->read_tail; + tty_audit_push(tty); + } + return eof_push ? -EAGAIN : 0; +} + extern ssize_t redirected_tty_write(struct file *, const char __user *, size_t, loff_t *); @@ -1782,6 +2098,10 @@ static int job_control(struct tty_struct *tty, struct file *file) * a hangup. Always called in user context, may sleep. * * This code must be sure never to sleep through a hangup. + * + * n_tty_read()/consumer path: + * claims non-exclusive termios_rwsem + * publishes read_tail */ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, @@ -1793,47 +2113,44 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, int c; int minimum, time; ssize_t retval = 0; - ssize_t size; long timeout; unsigned long flags; int packet; -do_it_again: c = job_control(tty, file); if (c < 0) return c; + /* + * Internal serialization of reads. + */ + if (file->f_flags & O_NONBLOCK) { + if (!mutex_trylock(&ldata->atomic_read_lock)) + return -EAGAIN; + } else { + if (mutex_lock_interruptible(&ldata->atomic_read_lock)) + return -ERESTARTSYS; + } + + down_read(&tty->termios_rwsem); + minimum = time = 0; timeout = MAX_SCHEDULE_TIMEOUT; if (!ldata->icanon) { - time = (HZ / 10) * TIME_CHAR(tty); minimum = MIN_CHAR(tty); if (minimum) { + time = (HZ / 10) * TIME_CHAR(tty); if (time) - tty->minimum_to_wake = 1; + ldata->minimum_to_wake = 1; else if (!waitqueue_active(&tty->read_wait) || - (tty->minimum_to_wake > minimum)) - tty->minimum_to_wake = minimum; + (ldata->minimum_to_wake > minimum)) + ldata->minimum_to_wake = minimum; } else { - timeout = 0; - if (time) { - timeout = time; - time = 0; - } - tty->minimum_to_wake = minimum = 1; + timeout = (HZ / 10) * TIME_CHAR(tty); + ldata->minimum_to_wake = minimum = 1; } } - /* - * Internal serialization of reads. - */ - if (file->f_flags & O_NONBLOCK) { - if (!mutex_trylock(&ldata->atomic_read_lock)) - return -EAGAIN; - } else { - if (mutex_lock_interruptible(&ldata->atomic_read_lock)) - return -ERESTARTSYS; - } packet = tty->packet; add_wait_queue(&tty->read_wait, &wait); @@ -1860,31 +2177,40 @@ do_it_again: TASK_RUNNING. */ set_current_state(TASK_INTERRUPTIBLE); - if (((minimum - (b - buf)) < tty->minimum_to_wake) && + if (((minimum - (b - buf)) < ldata->minimum_to_wake) && ((minimum - (b - buf)) >= 1)) - tty->minimum_to_wake = (minimum - (b - buf)); + ldata->minimum_to_wake = (minimum - (b - buf)); if (!input_available_p(tty, 0)) { if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { - retval = -EIO; - break; - } - if (tty_hung_up_p(file)) - break; - if (!timeout) - break; - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; + up_read(&tty->termios_rwsem); + tty_flush_to_ldisc(tty); + down_read(&tty->termios_rwsem); + if (!input_available_p(tty, 0)) { + retval = -EIO; + break; + } + } else { + if (tty_hung_up_p(file)) + break; + if (!timeout) + break; + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + n_tty_set_room(tty); + up_read(&tty->termios_rwsem); + + timeout = schedule_timeout(timeout); + + down_read(&tty->termios_rwsem); + continue; } - /* FIXME: does n_tty_set_room need locking ? */ - n_tty_set_room(tty); - timeout = schedule_timeout(timeout); - continue; } __set_current_state(TASK_RUNNING); @@ -1899,45 +2225,11 @@ do_it_again: } if (ldata->icanon && !L_EXTPROC(tty)) { - /* N.B. avoid overrun if nr == 0 */ - raw_spin_lock_irqsave(&ldata->read_lock, flags); - while (nr && ldata->read_cnt) { - int eol; - - 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)); - ldata->read_cnt--; - if (eol) { - /* this test should be redundant: - * we shouldn't be reading data if - * canon_data is 0 - */ - if (--ldata->canon_data < 0) - ldata->canon_data = 0; - } - raw_spin_unlock_irqrestore(&ldata->read_lock, flags); - - if (!eol || (c != __DISABLED_CHAR)) { - if (tty_put_user(tty, c, b++)) { - retval = -EFAULT; - b--; - raw_spin_lock_irqsave(&ldata->read_lock, flags); - break; - } - nr--; - } - if (eol) { - tty_audit_push(tty); - raw_spin_lock_irqsave(&ldata->read_lock, flags); - break; - } - raw_spin_lock_irqsave(&ldata->read_lock, flags); - } - raw_spin_unlock_irqrestore(&ldata->read_lock, flags); - if (retval) + retval = canon_copy_from_read_buf(tty, &b, &nr); + if (retval == -EAGAIN) { + retval = 0; + continue; + } else if (retval) break; } else { int uncopied; @@ -1951,24 +2243,7 @@ do_it_again: } } - /* If there is enough space in the read buffer now, let the - * low-level driver know. We use n_tty_chars_in_buffer() to - * check the buffer, as it now knows about canonical mode. - * Otherwise, if the driver is throttled and the line is - * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, - * we won't get any more characters. - */ - while (1) { - tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE); - if (n_tty_chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE) - break; - if (!tty->count) - break; - n_tty_set_room(tty); - if (!tty_unthrottle_safe(tty)) - break; - } - __tty_set_flow_change(tty, 0); + n_tty_check_unthrottle(tty); if (b - buf >= minimum) break; @@ -1979,18 +2254,14 @@ do_it_again: remove_wait_queue(&tty->read_wait, &wait); if (!waitqueue_active(&tty->read_wait)) - tty->minimum_to_wake = minimum; + ldata->minimum_to_wake = minimum; __set_current_state(TASK_RUNNING); - size = b - buf; - if (size) { - retval = size; - if (nr) - clear_bit(TTY_PUSH, &tty->flags); - } else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) - goto do_it_again; + if (b - buf) + retval = b - buf; n_tty_set_room(tty); + up_read(&tty->termios_rwsem); return retval; } @@ -2031,6 +2302,8 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, return retval; } + down_read(&tty->termios_rwsem); + /* Write out any echoed characters that are still pending */ process_echoes(tty); @@ -2045,7 +2318,7 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, retval = -EIO; break; } - if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { + if (O_OPOST(tty)) { while (nr > 0) { ssize_t num = process_output_block(tty, b, nr); if (num < 0) { @@ -2084,13 +2357,18 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, retval = -EAGAIN; break; } + up_read(&tty->termios_rwsem); + schedule(); + + down_read(&tty->termios_rwsem); } break_out: __set_current_state(TASK_RUNNING); remove_wait_queue(&tty->write_wait, &wait); if (b - buf != nr && tty->fasync) set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + up_read(&tty->termios_rwsem); return (b - buf) ? b - buf : retval; } @@ -2111,6 +2389,7 @@ break_out: static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, poll_table *wait) { + struct n_tty_data *ldata = tty->disc_data; unsigned int mask = 0; poll_wait(file, &tty->read_wait, wait); @@ -2125,9 +2404,9 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, mask |= POLLHUP; if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) { if (MIN_CHAR(tty) && !TIME_CHAR(tty)) - tty->minimum_to_wake = MIN_CHAR(tty); + ldata->minimum_to_wake = MIN_CHAR(tty); else - tty->minimum_to_wake = 1; + ldata->minimum_to_wake = 1; } if (tty->ops->write && !tty_is_writelocked(tty) && tty_chars_in_buffer(tty) < WAKEUP_CHARS && @@ -2138,19 +2417,19 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, static unsigned long inq_canon(struct n_tty_data *ldata) { - int nr, head, tail; + size_t nr, head, tail; - if (!ldata->canon_data) + if (ldata->canon_head == ldata->read_tail) return 0; head = ldata->canon_head; tail = ldata->read_tail; - nr = (head - tail) & (N_TTY_BUF_SIZE-1); + nr = head - tail; /* Skip EOF-chars.. */ while (head != tail) { - if (test_bit(tail, ldata->read_flags) && - ldata->read_buf[tail] == __DISABLED_CHAR) + if (test_bit(tail & (N_TTY_BUF_SIZE - 1), ldata->read_flags) && + read_buf(ldata, tail) == __DISABLED_CHAR) nr--; - tail = (tail+1) & (N_TTY_BUF_SIZE-1); + tail++; } return nr; } @@ -2165,16 +2444,30 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file, case TIOCOUTQ: return put_user(tty_chars_in_buffer(tty), (int __user *) arg); case TIOCINQ: - /* FIXME: Locking */ - retval = ldata->read_cnt; + down_write(&tty->termios_rwsem); if (L_ICANON(tty)) retval = inq_canon(ldata); + else + retval = read_cnt(ldata); + up_write(&tty->termios_rwsem); return put_user(retval, (unsigned int __user *) arg); default: return n_tty_ioctl_helper(tty, file, cmd, arg); } } +static void n_tty_fasync(struct tty_struct *tty, int on) +{ + struct n_tty_data *ldata = tty->disc_data; + + if (!waitqueue_active(&tty->read_wait)) { + if (on) + ldata->minimum_to_wake = 1; + else if (!tty->fasync) + ldata->minimum_to_wake = N_TTY_BUF_SIZE; + } +} + struct tty_ldisc_ops tty_ldisc_N_TTY = { .magic = TTY_LDISC_MAGIC, .name = "n_tty", @@ -2188,7 +2481,9 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = { .set_termios = n_tty_set_termios, .poll = n_tty_poll, .receive_buf = n_tty_receive_buf, - .write_wakeup = n_tty_write_wakeup + .write_wakeup = n_tty_write_wakeup, + .fasync = n_tty_fasync, + .receive_buf2 = n_tty_receive_buf2, }; /** diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 59bfaecc4e14..25c9bc783722 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -89,17 +89,13 @@ static void pty_unthrottle(struct tty_struct *tty) * pty_space - report space left for writing * @to: tty we are writing into * - * The tty buffers allow 64K but we sneak a peak and clip at 8K this - * allows a lot of overspill room for echo and other fun messes to - * be handled properly + * Limit the buffer space used by ptys to 8k. */ static int pty_space(struct tty_struct *to) { - int n = 8192 - to->port->buf.memory_used; - if (n < 0) - return 0; - return n; + int n = tty_buffer_space_avail(to->port); + return min(n, 8192); } /** @@ -125,10 +121,8 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c) /* Stuff the data into the input queue of the other end */ c = tty_insert_flip_string(to->port, buf, c); /* And shovel */ - if (c) { + if (c) tty_flip_buffer_push(to->port); - tty_wakeup(tty); - } } return c; } @@ -244,14 +238,9 @@ static void pty_flush_buffer(struct tty_struct *tty) static int pty_open(struct tty_struct *tty, struct file *filp) { - int retval = -ENODEV; - if (!tty || !tty->link) - goto out; - - set_bit(TTY_IO_ERROR, &tty->flags); + return -ENODEV; - retval = -EIO; if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) goto out; if (test_bit(TTY_PTY_LOCK, &tty->link->flags)) @@ -262,9 +251,11 @@ static int pty_open(struct tty_struct *tty, struct file *filp) clear_bit(TTY_IO_ERROR, &tty->flags); clear_bit(TTY_OTHER_CLOSED, &tty->link->flags); set_bit(TTY_THROTTLED, &tty->flags); - retval = 0; + return 0; + out: - return retval; + set_bit(TTY_IO_ERROR, &tty->flags); + return -EIO; } static void pty_set_termios(struct tty_struct *tty, @@ -290,7 +281,7 @@ static int pty_resize(struct tty_struct *tty, struct winsize *ws) struct tty_struct *pty = tty->link; /* For a PTY we need to lock the tty side */ - mutex_lock(&tty->termios_mutex); + mutex_lock(&tty->winsize_mutex); if (!memcmp(ws, &tty->winsize, sizeof(*ws))) goto done; @@ -317,7 +308,7 @@ static int pty_resize(struct tty_struct *tty, struct winsize *ws) tty->winsize = *ws; pty->winsize = *ws; /* Never used so will go away soon */ done: - mutex_unlock(&tty->termios_mutex); + mutex_unlock(&tty->winsize_mutex); return 0; } diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 86c00b1c5583..570df9d2a5d2 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -3062,7 +3062,7 @@ void serial8250_resume_port(int line) */ static int serial8250_probe(struct platform_device *dev) { - struct plat_serial8250_port *p = dev->dev.platform_data; + struct plat_serial8250_port *p = dev_get_platdata(&dev->dev); struct uart_8250_port uart; int ret, i, irqflag = 0; diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index d07b6af3a937..daf710f5c3fc 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -29,6 +29,8 @@ #include <linux/clk.h> #include <linux/pm_runtime.h> +#include <asm/byteorder.h> + #include "8250.h" /* Offsets for the DesignWare specific registers */ @@ -55,10 +57,25 @@ struct dw8250_data { int last_lcr; + int last_mcr; int line; struct clk *clk; + u8 usr_reg; }; +static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value) +{ + struct dw8250_data *d = p->private_data; + + /* If reading MSR, report CTS asserted when auto-CTS/RTS enabled */ + if (offset == UART_MSR && d->last_mcr & UART_MCR_AFE) { + value |= UART_MSR_CTS; + value &= ~UART_MSR_DCTS; + } + + return value; +} + static void dw8250_serial_out(struct uart_port *p, int offset, int value) { struct dw8250_data *d = p->private_data; @@ -66,15 +83,24 @@ static void dw8250_serial_out(struct uart_port *p, int offset, int value) if (offset == UART_LCR) d->last_lcr = value; - offset <<= p->regshift; - writeb(value, p->membase + offset); + if (offset == UART_MCR) + d->last_mcr = value; + + writeb(value, p->membase + (offset << p->regshift)); } static unsigned int dw8250_serial_in(struct uart_port *p, int offset) { - offset <<= p->regshift; + unsigned int value = readb(p->membase + (offset << p->regshift)); - return readb(p->membase + offset); + return dw8250_modify_msr(p, offset, value); +} + +/* Read Back (rb) version to ensure register access ording. */ +static void dw8250_serial_out_rb(struct uart_port *p, int offset, int value) +{ + dw8250_serial_out(p, offset, value); + dw8250_serial_in(p, UART_LCR); } static void dw8250_serial_out32(struct uart_port *p, int offset, int value) @@ -84,15 +110,17 @@ static void dw8250_serial_out32(struct uart_port *p, int offset, int value) if (offset == UART_LCR) d->last_lcr = value; - offset <<= p->regshift; - writel(value, p->membase + offset); + if (offset == UART_MCR) + d->last_mcr = value; + + writel(value, p->membase + (offset << p->regshift)); } static unsigned int dw8250_serial_in32(struct uart_port *p, int offset) { - offset <<= p->regshift; + unsigned int value = readl(p->membase + (offset << p->regshift)); - return readl(p->membase + offset); + return dw8250_modify_msr(p, offset, value); } static int dw8250_handle_irq(struct uart_port *p) @@ -104,7 +132,7 @@ static int dw8250_handle_irq(struct uart_port *p) return 1; } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { /* Clear the USR and write the LCR again. */ - (void)p->serial_in(p, DW_UART_USR); + (void)p->serial_in(p, d->usr_reg); p->serial_out(p, UART_LCR, d->last_lcr); return 1; @@ -125,12 +153,60 @@ dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) pm_runtime_put_sync_suspend(port->dev); } -static int dw8250_probe_of(struct uart_port *p) +static void dw8250_setup_port(struct uart_8250_port *up) +{ + struct uart_port *p = &up->port; + u32 reg = readl(p->membase + DW_UART_UCV); + + /* + * If the Component Version Register returns zero, we know that + * ADDITIONAL_FEATURES are not enabled. No need to go any further. + */ + if (!reg) + return; + + dev_dbg_ratelimited(p->dev, "Designware UART version %c.%c%c\n", + (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); + + reg = readl(p->membase + DW_UART_CPR); + if (!reg) + return; + + /* Select the type based on fifo */ + if (reg & DW_UART_CPR_FIFO_MODE) { + p->type = PORT_16550A; + p->flags |= UPF_FIXED_TYPE; + p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); + up->tx_loadsz = p->fifosize; + up->capabilities = UART_CAP_FIFO; + } + + if (reg & DW_UART_CPR_AFCE_MODE) + up->capabilities |= UART_CAP_AFE; +} + +static int dw8250_probe_of(struct uart_port *p, + struct dw8250_data *data) { struct device_node *np = p->dev->of_node; u32 val; - - if (!of_property_read_u32(np, "reg-io-width", &val)) { + bool has_ucv = true; + + if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) { +#ifdef __BIG_ENDIAN + /* + * Low order bits of these 64-bit registers, when + * accessed as a byte, are 7 bytes further down in the + * address space in big endian mode. + */ + p->membase += 7; +#endif + p->serial_out = dw8250_serial_out_rb; + p->flags = ASYNC_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; + p->type = PORT_OCTEON; + data->usr_reg = 0x27; + has_ucv = false; + } else if (!of_property_read_u32(np, "reg-io-width", &val)) { switch (val) { case 1: break; @@ -144,6 +220,8 @@ static int dw8250_probe_of(struct uart_port *p) return -EINVAL; } } + if (has_ucv) + dw8250_setup_port(container_of(p, struct uart_8250_port, port)); if (!of_property_read_u32(np, "reg-shift", &val)) p->regshift = val; @@ -168,6 +246,8 @@ static int dw8250_probe_acpi(struct uart_8250_port *up) const struct acpi_device_id *id; struct uart_port *p = &up->port; + dw8250_setup_port(up); + id = acpi_match_device(p->dev->driver->acpi_match_table, p->dev); if (!id) return -ENODEV; @@ -196,38 +276,6 @@ static inline int dw8250_probe_acpi(struct uart_8250_port *up) } #endif /* CONFIG_ACPI */ -static void dw8250_setup_port(struct uart_8250_port *up) -{ - struct uart_port *p = &up->port; - u32 reg = readl(p->membase + DW_UART_UCV); - - /* - * If the Component Version Register returns zero, we know that - * ADDITIONAL_FEATURES are not enabled. No need to go any further. - */ - if (!reg) - return; - - dev_dbg_ratelimited(p->dev, "Designware UART version %c.%c%c\n", - (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); - - reg = readl(p->membase + DW_UART_CPR); - if (!reg) - return; - - /* Select the type based on fifo */ - if (reg & DW_UART_CPR_FIFO_MODE) { - p->type = PORT_16550A; - p->flags |= UPF_FIXED_TYPE; - p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); - up->tx_loadsz = p->fifosize; - up->capabilities = UART_CAP_FIFO; - } - - if (reg & DW_UART_CPR_AFCE_MODE) - up->capabilities |= UART_CAP_AFE; -} - static int dw8250_probe(struct platform_device *pdev) { struct uart_8250_port uart = {}; @@ -259,6 +307,7 @@ static int dw8250_probe(struct platform_device *pdev) if (!data) return -ENOMEM; + data->usr_reg = DW_UART_USR; data->clk = devm_clk_get(&pdev->dev, NULL); if (!IS_ERR(data->clk)) { clk_prepare_enable(data->clk); @@ -270,10 +319,8 @@ static int dw8250_probe(struct platform_device *pdev) uart.port.serial_out = dw8250_serial_out; uart.port.private_data = data; - dw8250_setup_port(&uart); - if (pdev->dev.of_node) { - err = dw8250_probe_of(&uart.port); + err = dw8250_probe_of(&uart.port, data); if (err) return err; } else if (ACPI_HANDLE(&pdev->dev)) { @@ -362,6 +409,7 @@ static const struct dev_pm_ops dw8250_pm_ops = { static const struct of_device_id dw8250_of_match[] = { { .compatible = "snps,dw-apb-uart" }, + { .compatible = "cavium,octeon-3860-uart" }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, dw8250_of_match); diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index 721904f8efa9..c100d6343d50 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -193,7 +193,8 @@ static int __init parse_options(struct early_serial8250_device *device, if (options) { options++; device->baud = simple_strtoul(options, NULL, 0); - length = min(strcspn(options, " "), sizeof(device->options)); + length = min(strcspn(options, " ") + 1, + (size_t)(sizeof(device->options))); strlcpy(device->options, options, length); } else { device->baud = probe_baud(port); diff --git a/drivers/tty/serial/8250/8250_em.c b/drivers/tty/serial/8250/8250_em.c index 916cc19fbbda..5f3bba12c159 100644 --- a/drivers/tty/serial/8250/8250_em.c +++ b/drivers/tty/serial/8250/8250_em.c @@ -95,25 +95,23 @@ static int serial8250_em_probe(struct platform_device *pdev) struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); struct serial8250_em_priv *priv; struct uart_8250_port up; - int ret = -EINVAL; + int ret; if (!regs || !irq) { dev_err(&pdev->dev, "missing registers or irq\n"); - goto err0; + return -EINVAL; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { dev_err(&pdev->dev, "unable to allocate private data\n"); - ret = -ENOMEM; - goto err0; + return -ENOMEM; } - priv->sclk = clk_get(&pdev->dev, "sclk"); + priv->sclk = devm_clk_get(&pdev->dev, "sclk"); if (IS_ERR(priv->sclk)) { dev_err(&pdev->dev, "unable to get clock\n"); - ret = PTR_ERR(priv->sclk); - goto err1; + return PTR_ERR(priv->sclk); } memset(&up, 0, sizeof(up)); @@ -136,20 +134,13 @@ static int serial8250_em_probe(struct platform_device *pdev) ret = serial8250_register_8250_port(&up); if (ret < 0) { dev_err(&pdev->dev, "unable to register 8250 port\n"); - goto err2; + clk_disable(priv->sclk); + return ret; } priv->line = ret; platform_set_drvdata(pdev, priv); return 0; - - err2: - clk_disable(priv->sclk); - clk_put(priv->sclk); - err1: - kfree(priv); - err0: - return ret; } static int serial8250_em_remove(struct platform_device *pdev) @@ -158,8 +149,6 @@ static int serial8250_em_remove(struct platform_device *pdev) serial8250_unregister_port(priv->line); clk_disable(priv->sclk); - clk_put(priv->sclk); - kfree(priv); return 0; } diff --git a/drivers/tty/serial/8250/8250_gsc.c b/drivers/tty/serial/8250/8250_gsc.c index 097dff9c08ad..2e3ea1a70d7b 100644 --- a/drivers/tty/serial/8250/8250_gsc.c +++ b/drivers/tty/serial/8250/8250_gsc.c @@ -30,6 +30,11 @@ static int __init serial_init_chip(struct parisc_device *dev) unsigned long address; int err; +#ifdef CONFIG_64BIT + if (!dev->irq && (dev->id.sversion == 0xad)) + dev->irq = iosapic_serial_irq(dev); +#endif + if (!dev->irq) { /* We find some unattached serial ports by walking native * busses. These should be silently ignored. Otherwise, @@ -51,7 +56,8 @@ static int __init serial_init_chip(struct parisc_device *dev) memset(&uart, 0, sizeof(uart)); uart.port.iotype = UPIO_MEM; /* 7.272727MHz on Lasi. Assumed the same for Dino, Wax and Timi. */ - uart.port.uartclk = 7272727; + uart.port.uartclk = (dev->id.sversion != 0xad) ? + 7272727 : 1843200; uart.port.mapbase = address; uart.port.membase = ioremap_nocache(address, 16); uart.port.irq = dev->irq; @@ -73,6 +79,7 @@ static struct parisc_device_id serial_tbl[] = { { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00075 }, { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008c }, { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008d }, + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x000ad }, { 0 } }; diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 26e3a97ab157..c810da7c7a88 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1565,6 +1565,7 @@ pci_wch_ch353_setup(struct serial_private *priv, #define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021 #define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022 #define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a +#define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e #define PCI_VENDOR_ID_SUNIX 0x1fd4 #define PCI_DEVICE_ID_SUNIX_1999 0x1999 @@ -1587,8 +1588,8 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { * ADDI-DATA GmbH communication cards <info@addi-data.com> */ { - .vendor = PCI_VENDOR_ID_ADDIDATA_OLD, - .device = PCI_DEVICE_ID_ADDIDATA_APCI7800, + .vendor = PCI_VENDOR_ID_AMCC, + .device = PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .setup = addidata_apci7800_setup, @@ -4697,8 +4698,8 @@ static struct pci_device_id serial_pci_tbl[] = { 0, pbn_b0_1_115200 }, - { PCI_VENDOR_ID_ADDIDATA_OLD, - PCI_DEVICE_ID_ADDIDATA_APCI7800, + { PCI_VENDOR_ID_AMCC, + PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800, PCI_ANY_ID, PCI_ANY_ID, 0, @@ -4797,9 +4798,11 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_VENDOR_ID_IBM, 0x0299, 0, 0, pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, - 0x1000, 0x0012, - 0, 0, pbn_b0_bt_2_115200 }, + /* + * other NetMos 9835 devices are most likely handled by the + * parport_serial driver, check drivers/parport/parport_serial.c + * before adding them here. + */ { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901, 0xA000, 0x1000, diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 80fe91e64a52..f3b306efaa59 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -12,9 +12,8 @@ config SERIAL_8250 here are those that are setting up dedicated Ethernet WWW/FTP servers, or users that have one of the various bus mice instead of a serial mouse and don't intend to use their machine's standard serial - port for anything. (Note that the Cyclades and Stallion multi - serial port drivers do not need this driver built in for them to - work.) + port for anything. (Note that the Cyclades multi serial port driver + does not need this driver built in for it to work.) To compile this driver as a module, choose M here: the module will be called 8250. @@ -117,6 +116,8 @@ config SERIAL_8250_PCI This builds standard PCI serial support. You may be able to disable this feature if you only need legacy serial support. Saves about 9K. + Note that serial ports on NetMos 9835 Multi-I/O cards are handled + by the parport_serial driver, enabled with CONFIG_PARPORT_SERIAL. config SERIAL_8250_HP300 tristate diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 7e7006fd404e..febd45cd5027 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -5,7 +5,7 @@ if TTY menu "Serial drivers" - depends on HAS_IOMEM && GENERIC_HARDIRQS + depends on HAS_IOMEM source "drivers/tty/serial/8250/Kconfig" @@ -251,7 +251,7 @@ config SERIAL_SAMSUNG_CONSOLE config SERIAL_SIRFSOC tristate "SiRF SoC Platform Serial port support" - depends on ARCH_PRIMA2 + depends on ARCH_SIRF select SERIAL_CORE help Support for the on-chip UART on the CSR SiRFprimaII series, @@ -277,7 +277,7 @@ config SERIAL_TEGRA select SERIAL_CORE help Support for the on-chip UARTs on the NVIDIA Tegra series SOCs - providing /dev/ttyHS0, 1, 2, 3 and 4 (note, some machines may not + providing /dev/ttyTHS0, 1, 2, 3 and 4 (note, some machines may not provide all of these ports, depending on how the serial port are enabled). This driver uses the APB DMA to achieve higher baudrate and better performance. @@ -291,13 +291,13 @@ config SERIAL_MAX3100 config SERIAL_MAX310X bool "MAX310X support" - depends on SPI + depends on SPI_MASTER select SERIAL_CORE - select REGMAP_SPI if SPI + select REGMAP_SPI if SPI_MASTER default n help This selects support for an advanced UART from Maxim (Dallas). - Supported ICs are MAX3107, MAX3108. + Supported ICs are MAX3107, MAX3108, MAX3109, MAX14830. Each IC contains 128 words each of receive and transmit FIFO that can be controlled through I2C or high-speed SPI. @@ -551,7 +551,7 @@ config BFIN_UART3_CTSRTS Enable hardware flow control in the driver. config SERIAL_IMX - bool "IMX serial port support" + tristate "IMX serial port support" depends on ARCH_MXC select SERIAL_CORE select RATIONAL @@ -561,22 +561,21 @@ config SERIAL_IMX config SERIAL_IMX_CONSOLE bool "Console on IMX serial port" - depends on SERIAL_IMX + depends on SERIAL_IMX=y select SERIAL_CORE_CONSOLE help - If you have enabled the serial port on the Motorola IMX + If you have enabled the serial port on the Freescale IMX CPU you can make it the console by answering Y to this option. Even if you say Y here, the currently visible virtual console (/dev/tty0) will still be used as the system console by default, but you can alter that using a kernel command line option such as - "console=ttySA0". (Try "man bootparam" or see the documentation of - your boot loader (lilo or loadlin) about how to pass options to the - kernel at boot time.) + "console=ttymxc0". (Try "man bootparam" or see the documentation of + your bootloader about how to pass options to the kernel at boot time.) config SERIAL_UARTLITE tristate "Xilinx uartlite serial port support" - depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE + depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE || ARCH_ZYNQ select SERIAL_CORE help Say Y here if you want to use the Xilinx uartlite serial controller. @@ -1402,13 +1401,16 @@ config SERIAL_XILINX_PS_UART_CONSOLE Enable a Xilinx PS UART port to be the system console. config SERIAL_AR933X - bool "AR933X serial port support" - depends on SOC_AR933X + tristate "AR933X serial port support" + depends on HAVE_CLK && SOC_AR933X select SERIAL_CORE help If you have an Atheros AR933X SOC based board and want to use the built-in UART of the SoC, say Y to this option. + To compile this driver as a module, choose M here: the + module will be called ar933x_uart. + config SERIAL_AR933X_CONSOLE bool "Console on AR933X serial port" depends on SERIAL_AR933X=y @@ -1425,8 +1427,8 @@ config SERIAL_AR933X_NR_UARTS to support. config SERIAL_EFM32_UART - tristate "EFM32 UART/USART port." - depends on ARCH_EFM32 + tristate "EFM32 UART/USART port" + depends on ARM && (ARCH_EFM32 || COMPILE_TEST) select SERIAL_CORE help This driver support the USART and UART ports on @@ -1437,6 +1439,15 @@ config SERIAL_EFM32_UART_CONSOLE depends on SERIAL_EFM32_UART=y select SERIAL_CORE_CONSOLE +config SERIAL_TILEGX + tristate "TILE-Gx on-chip serial port support" + depends on TILEGX + select TILE_GXIO_UART + select SERIAL_CORE + ---help--- + This device provides access to the on-chip UARTs on the TILE-Gx + processor. + config SERIAL_ARC tristate "ARC UART driver support" select SERIAL_CORE @@ -1484,6 +1495,36 @@ config SERIAL_RP2_NR_UARTS If multiple cards are present, the default limit of 32 ports may need to be increased. +config SERIAL_FSL_LPUART + tristate "Freescale lpuart serial port support" + select SERIAL_CORE + help + Support for the on-chip lpuart on some Freescale SOCs. + +config SERIAL_FSL_LPUART_CONSOLE + bool "Console on Freescale lpuart serial port" + depends on SERIAL_FSL_LPUART=y + select SERIAL_CORE_CONSOLE + help + If you have enabled the lpuart serial port on the Freescale SoCs, + you can make it the console by answering Y to this option. + +config SERIAL_ST_ASC + tristate "ST ASC serial port support" + select SERIAL_CORE + help + This driver is for the on-chip Asychronous Serial Controller on + STMicroelectronics STi SoCs. + ASC is embedded in ST COMMS IP block. It supports Rx & Tx functionality. + It support all industry standard baud rates. + + If unsure, say N. + +config SERIAL_ST_ASC_CONSOLE + bool "Support for console on ST ASC" + depends on SERIAL_ST_ASC=y + select SERIAL_CORE_CONSOLE + endmenu endif # TTY diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index eedfec40e3dd..3068c7722087 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -65,6 +65,8 @@ obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o +obj-$(CONFIG_SERIAL_ST_ASC) += st-asc.o +obj-$(CONFIG_SERIAL_TILEGX) += tilegx.o obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o obj-$(CONFIG_SERIAL_QE) += ucc_uart.o obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o @@ -85,3 +87,4 @@ obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o obj-$(CONFIG_SERIAL_ARC) += arc_uart.o obj-$(CONFIG_SERIAL_RP2) += rp2.o +obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index c6bdb943726b..59b3da9bcc3f 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -139,7 +139,9 @@ static void altera_jtaguart_rx_chars(struct altera_jtaguart *pp) uart_insert_char(port, 0, 0, ch, flag); } + spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); + spin_lock(&port->lock); } static void altera_jtaguart_tx_chars(struct altera_jtaguart *pp) @@ -408,7 +410,8 @@ static struct uart_driver altera_jtaguart_driver = { static int altera_jtaguart_probe(struct platform_device *pdev) { - struct altera_jtaguart_platform_uart *platp = pdev->dev.platform_data; + struct altera_jtaguart_platform_uart *platp = + dev_get_platdata(&pdev->dev); struct uart_port *port; struct resource *res_irq, *res_mem; int i = pdev->id; @@ -470,6 +473,7 @@ static int altera_jtaguart_remove(struct platform_device *pdev) #ifdef CONFIG_OF static struct of_device_id altera_jtaguart_match[] = { { .compatible = "ALTR,juart-1.0", }, + { .compatible = "altr,juart-1.0", }, {}, }; MODULE_DEVICE_TABLE(of, altera_jtaguart_match); diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 13471dd95793..501667e3e3f5 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -231,7 +231,9 @@ static void altera_uart_rx_chars(struct altera_uart *pp) flag); } + spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); + spin_lock(&port->lock); } static void altera_uart_tx_chars(struct altera_uart *pp) @@ -534,7 +536,7 @@ static int altera_uart_get_of_uartclk(struct platform_device *pdev, static int altera_uart_probe(struct platform_device *pdev) { - struct altera_uart_platform_uart *platp = pdev->dev.platform_data; + struct altera_uart_platform_uart *platp = dev_get_platdata(&pdev->dev); struct uart_port *port; struct resource *res_mem; struct resource *res_irq; @@ -604,7 +606,6 @@ static int altera_uart_remove(struct platform_device *pdev) if (port) { uart_remove_one_port(&altera_uart_driver, port); - platform_set_drvdata(pdev, NULL); port->mapbase = 0; } @@ -614,6 +615,7 @@ static int altera_uart_remove(struct platform_device *pdev) #ifdef CONFIG_OF static struct of_device_id altera_uart_match[] = { { .compatible = "ALTR,uart-1.0", }, + { .compatible = "altr,uart-1.0", }, {}, }; MODULE_DEVICE_TABLE(of, altera_uart_match); diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c index c36840519527..8b90f0b6dfdf 100644 --- a/drivers/tty/serial/amba-pl010.c +++ b/drivers/tty/serial/amba-pl010.c @@ -721,7 +721,7 @@ static int pl010_probe(struct amba_device *dev, const struct amba_id *id) uap->port.flags = UPF_BOOT_AUTOCONF; uap->port.line = i; uap->dev = dev; - uap->data = dev->dev.platform_data; + uap->data = dev_get_platdata(&dev->dev); amba_ports[i] = uap; diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index e2774f9ecd59..aaa22867e656 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -79,13 +79,12 @@ struct vendor_data { bool dma_threshold; bool cts_event_workaround; - unsigned int (*get_fifosize)(unsigned int periphid); + unsigned int (*get_fifosize)(struct amba_device *dev); }; -static unsigned int get_fifosize_arm(unsigned int periphid) +static unsigned int get_fifosize_arm(struct amba_device *dev) { - unsigned int rev = (periphid >> 20) & 0xf; - return rev < 3 ? 16 : 32; + return amba_rev(dev) < 3 ? 16 : 32; } static struct vendor_data vendor_arm = { @@ -98,7 +97,7 @@ static struct vendor_data vendor_arm = { .get_fifosize = get_fifosize_arm, }; -static unsigned int get_fifosize_st(unsigned int periphid) +static unsigned int get_fifosize_st(struct amba_device *dev) { return 64; } @@ -151,10 +150,6 @@ struct pl011_dmatx_data { struct uart_amba_port { struct uart_port port; struct clk *clk; - /* Two optional pin states - default & sleep */ - struct pinctrl *pinctrl; - struct pinctrl_state *pins_default; - struct pinctrl_state *pins_sleep; const struct vendor_data *vendor; unsigned int dmacr; /* dma control reg */ unsigned int im; /* interrupt mask */ @@ -270,7 +265,7 @@ static void pl011_sgbuf_free(struct dma_chan *chan, struct pl011_sgbuf *sg, static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *uap) { /* DMA is the sole user of the platform data right now */ - struct amba_pl011_data *plat = uap->port.dev->platform_data; + struct amba_pl011_data *plat = dev_get_platdata(uap->port.dev); struct dma_slave_config tx_conf = { .dst_addr = uap->port.mapbase + UART01x_DR, .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, @@ -682,6 +677,8 @@ static inline bool pl011_dma_tx_start(struct uart_amba_port *uap) * Locking: called with port lock held and IRQs disabled. */ static void pl011_dma_flush_buffer(struct uart_port *port) +__releases(&uap->port.lock) +__acquires(&uap->port.lock) { struct uart_amba_port *uap = (struct uart_amba_port *)port; @@ -1203,6 +1200,8 @@ static void pl011_enable_ms(struct uart_port *port) } static void pl011_rx_chars(struct uart_amba_port *uap) +__releases(&uap->port.lock) +__acquires(&uap->port.lock) { pl011_fifo_to_tty(uap); @@ -1480,12 +1479,7 @@ static int pl011_hwinit(struct uart_port *port) int retval; /* Optionaly enable pins to be muxed in and configured */ - if (!IS_ERR(uap->pins_default)) { - retval = pinctrl_select_state(uap->pinctrl, uap->pins_default); - if (retval) - dev_err(port->dev, - "could not set default pins\n"); - } + pinctrl_pm_select_default_state(port->dev); /* * Try to enable the clock producer. @@ -1507,10 +1501,10 @@ static int pl011_hwinit(struct uart_port *port) uap->im = readw(uap->port.membase + UART011_IMSC); writew(UART011_RTIM | UART011_RXIM, uap->port.membase + UART011_IMSC); - if (uap->port.dev->platform_data) { + if (dev_get_platdata(uap->port.dev)) { struct amba_pl011_data *plat; - plat = uap->port.dev->platform_data; + plat = dev_get_platdata(uap->port.dev); if (plat->init) plat->init(); } @@ -1611,7 +1605,6 @@ static void pl011_shutdown(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; unsigned int cr; - int retval; /* * disable all interrupts @@ -1654,18 +1647,12 @@ static void pl011_shutdown(struct uart_port *port) */ clk_disable_unprepare(uap->clk); /* Optionally let pins go into sleep states */ - if (!IS_ERR(uap->pins_sleep)) { - retval = pinctrl_select_state(uap->pinctrl, uap->pins_sleep); - if (retval) - dev_err(port->dev, - "could not set pins to sleep state\n"); - } + pinctrl_pm_select_sleep_state(port->dev); - - if (uap->port.dev->platform_data) { + if (dev_get_platdata(uap->port.dev)) { struct amba_pl011_data *plat; - plat = uap->port.dev->platform_data; + plat = dev_get_platdata(uap->port.dev); if (plat->exit) plat->exit(); } @@ -2013,21 +2000,16 @@ static int __init pl011_console_setup(struct console *co, char *options) return -ENODEV; /* Allow pins to be muxed in and configured */ - if (!IS_ERR(uap->pins_default)) { - ret = pinctrl_select_state(uap->pinctrl, uap->pins_default); - if (ret) - dev_err(uap->port.dev, - "could not set default pins\n"); - } + pinctrl_pm_select_default_state(uap->port.dev); ret = clk_prepare(uap->clk); if (ret) return ret; - if (uap->port.dev->platform_data) { + if (dev_get_platdata(uap->port.dev)) { struct amba_pl011_data *plat; - plat = uap->port.dev->platform_data; + plat = dev_get_platdata(uap->port.dev); if (plat->init) plat->init(); } @@ -2132,21 +2114,6 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) goto out; } - uap->pinctrl = devm_pinctrl_get(&dev->dev); - if (IS_ERR(uap->pinctrl)) { - ret = PTR_ERR(uap->pinctrl); - goto out; - } - uap->pins_default = pinctrl_lookup_state(uap->pinctrl, - PINCTRL_STATE_DEFAULT); - if (IS_ERR(uap->pins_default)) - dev_err(&dev->dev, "could not get default pinstate\n"); - - uap->pins_sleep = pinctrl_lookup_state(uap->pinctrl, - PINCTRL_STATE_SLEEP); - if (IS_ERR(uap->pins_sleep)) - dev_dbg(&dev->dev, "could not get sleep pinstate\n"); - uap->clk = devm_clk_get(&dev->dev, NULL); if (IS_ERR(uap->clk)) { ret = PTR_ERR(uap->clk); @@ -2157,7 +2124,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) uap->lcrh_rx = vendor->lcrh_rx; uap->lcrh_tx = vendor->lcrh_tx; uap->old_cr = 0; - uap->fifosize = vendor->get_fifosize(dev->periphid); + uap->fifosize = vendor->get_fifosize(dev); uap->port.dev = &dev->dev; uap->port.mapbase = dev->res.start; uap->port.membase = base; diff --git a/drivers/tty/serial/apbuart.c b/drivers/tty/serial/apbuart.c index 6331464d9101..de11ab8ffd91 100644 --- a/drivers/tty/serial/apbuart.c +++ b/drivers/tty/serial/apbuart.c @@ -125,7 +125,9 @@ static void apbuart_rx_chars(struct uart_port *port) status = UART_GET_STATUS(port); } + spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); + spin_lock(&port->lock); } static void apbuart_tx_chars(struct uart_port *port) diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index 27f20c57abed..acd03af7cd52 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -17,6 +17,8 @@ #include <linux/sysrq.h> #include <linux/delay.h> #include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_platform.h> #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/serial_core.h> @@ -24,11 +26,11 @@ #include <linux/slab.h> #include <linux/io.h> #include <linux/irq.h> +#include <linux/clk.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" @@ -47,8 +49,14 @@ struct ar933x_uart_port { unsigned int ier; /* shadow Interrupt Enable Register */ unsigned int min_baud; unsigned int max_baud; + struct clk *clk; }; +static inline bool ar933x_uart_console_enabled(void) +{ + return config_enabled(CONFIG_SERIAL_AR933X_CONSOLE); +} + static inline unsigned int ar933x_uart_read(struct ar933x_uart_port *up, int offset) { @@ -322,7 +330,9 @@ static void ar933x_uart_rx_chars(struct ar933x_uart_port *up) tty_insert_flip_char(port, ch, TTY_NORMAL); } while (max_count-- > 0); + spin_unlock(&up->port.lock); tty_flip_buffer_push(port); + spin_lock(&up->port.lock); } static void ar933x_uart_tx_chars(struct ar933x_uart_port *up) @@ -497,8 +507,6 @@ static struct uart_ops ar933x_uart_ops = { .verify_port = ar933x_uart_verify_port, }; -#ifdef CONFIG_SERIAL_AR933X_CONSOLE - static struct ar933x_uart_port * ar933x_console_ports[CONFIG_SERIAL_AR933X_NR_UARTS]; @@ -597,80 +605,88 @@ static struct console ar933x_uart_console = { static void ar933x_uart_add_console_port(struct ar933x_uart_port *up) { + if (!ar933x_uart_console_enabled()) + return; + ar933x_console_ports[up->port.line] = up; } -#define AR933X_SERIAL_CONSOLE (&ar933x_uart_console) - -#else - -static inline void ar933x_uart_add_console_port(struct ar933x_uart_port *up) {} - -#define AR933X_SERIAL_CONSOLE NULL - -#endif /* CONFIG_SERIAL_AR933X_CONSOLE */ - static struct uart_driver ar933x_uart_driver = { .owner = THIS_MODULE, .driver_name = DRIVER_NAME, .dev_name = "ttyATH", .nr = CONFIG_SERIAL_AR933X_NR_UARTS, - .cons = AR933X_SERIAL_CONSOLE, + .cons = NULL, /* filled in runtime */ }; static int ar933x_uart_probe(struct platform_device *pdev) { - struct ar933x_uart_platform_data *pdata; struct ar933x_uart_port *up; struct uart_port *port; struct resource *mem_res; struct resource *irq_res; + struct device_node *np; unsigned int baud; int id; int ret; - pdata = pdev->dev.platform_data; - if (!pdata) - return -EINVAL; - - id = pdev->id; - if (id == -1) - id = 0; + np = pdev->dev.of_node; + if (config_enabled(CONFIG_OF) && np) { + id = of_alias_get_id(np, "serial"); + if (id < 0) { + dev_err(&pdev->dev, "unable to get alias id, err=%d\n", + id); + return id; + } + } else { + id = pdev->id; + if (id == -1) + id = 0; + } if (id > CONFIG_SERIAL_AR933X_NR_UARTS) return -EINVAL; - mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem_res) { - dev_err(&pdev->dev, "no MEM resource\n"); - return -EINVAL; - } - irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!irq_res) { dev_err(&pdev->dev, "no IRQ resource\n"); return -EINVAL; } - up = kzalloc(sizeof(struct ar933x_uart_port), GFP_KERNEL); + up = devm_kzalloc(&pdev->dev, sizeof(struct ar933x_uart_port), + GFP_KERNEL); if (!up) return -ENOMEM; + up->clk = devm_clk_get(&pdev->dev, "uart"); + if (IS_ERR(up->clk)) { + dev_err(&pdev->dev, "unable to get UART clock\n"); + return PTR_ERR(up->clk); + } + port = &up->port; - port->mapbase = mem_res->start; - port->membase = ioremap(mem_res->start, AR933X_UART_REGS_SIZE); - if (!port->membase) { - ret = -ENOMEM; - goto err_free_up; + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + port->membase = devm_ioremap_resource(&pdev->dev, mem_res); + if (IS_ERR(port->membase)) + return PTR_ERR(port->membase); + + ret = clk_prepare_enable(up->clk); + if (ret) + return ret; + + port->uartclk = clk_get_rate(up->clk); + if (!port->uartclk) { + ret = -EINVAL; + goto err_disable_clk; } + port->mapbase = mem_res->start; port->line = id; port->irq = irq_res->start; port->dev = &pdev->dev; port->type = PORT_AR933X; port->iotype = UPIO_MEM32; - port->uartclk = pdata->uartclk; port->regshift = 2; port->fifosize = AR933X_UART_FIFO_SIZE; @@ -686,15 +702,13 @@ static int ar933x_uart_probe(struct platform_device *pdev) ret = uart_add_one_port(&ar933x_uart_driver, &up->port); if (ret) - goto err_unmap; + goto err_disable_clk; platform_set_drvdata(pdev, up); return 0; -err_unmap: - iounmap(up->port.membase); -err_free_up: - kfree(up); +err_disable_clk: + clk_disable_unprepare(up->clk); return ret; } @@ -703,23 +717,30 @@ static int ar933x_uart_remove(struct platform_device *pdev) struct ar933x_uart_port *up; up = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); if (up) { uart_remove_one_port(&ar933x_uart_driver, &up->port); - iounmap(up->port.membase); - kfree(up); + clk_disable_unprepare(up->clk); } return 0; } +#ifdef CONFIG_OF +static const struct of_device_id ar933x_uart_of_ids[] = { + { .compatible = "qca,ar9330-uart" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ar933x_uart_of_ids); +#endif + static struct platform_driver ar933x_uart_platform_driver = { .probe = ar933x_uart_probe, .remove = ar933x_uart_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ar933x_uart_of_ids), }, }; @@ -727,7 +748,9 @@ static int __init ar933x_uart_init(void) { int ret; - ar933x_uart_driver.nr = CONFIG_SERIAL_AR933X_NR_UARTS; + if (ar933x_uart_console_enabled()) + ar933x_uart_driver.cons = &ar933x_uart_console; + ret = uart_register_driver(&ar933x_uart_driver); if (ret) goto err_out; diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index cbf1d155b7b2..569872f4c9b8 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -209,9 +209,9 @@ static void arc_serial_start_tx(struct uart_port *port) arc_serial_tx_chars(uart); } -static void arc_serial_rx_chars(struct arc_uart_port *uart) +static void arc_serial_rx_chars(struct arc_uart_port *uart, unsigned int status) { - unsigned int status, ch, flg = 0; + unsigned int ch, flg = 0; /* * UART has 4 deep RX-FIFO. Driver's recongnition of this fact @@ -222,11 +222,11 @@ static void arc_serial_rx_chars(struct arc_uart_port *uart) * 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++; - + do { + /* + * This could be an Rx Intr for err (no data), + * so check err and clear that Intr first + */ if (unlikely(status & (RXOERR | RXFERR))) { if (status & RXOERR) { uart->port.icount.overrun++; @@ -242,14 +242,19 @@ static void arc_serial_rx_chars(struct arc_uart_port *uart) } else flg = TTY_NORMAL; - if (unlikely(uart_handle_sysrq_char(&uart->port, ch))) - goto done; + if (status & RXEMPTY) + continue; - uart_insert_char(&uart->port, status, RXOERR, ch, flg); + ch = UART_GET_DATA(uart); + uart->port.icount.rx++; + + if (!(uart_handle_sysrq_char(&uart->port, ch))) + uart_insert_char(&uart->port, status, RXOERR, ch, flg); -done: + spin_unlock(&uart->port.lock); tty_flip_buffer_push(&uart->port.state->port); - } + spin_lock(&uart->port.lock); + } while (!((status = UART_GET_STATUS(uart)) & RXEMPTY)); } /* @@ -292,11 +297,11 @@ static irqreturn_t arc_serial_isr(int irq, void *dev_id) * notifications from the UART Controller. * To demultiplex between the two, we check the relevant bits */ - if ((status & RXIENB) && !(status & RXEMPTY)) { + if (status & RXIENB) { /* already in ISR, no need of xx_irqsave */ spin_lock(&uart->port.lock); - arc_serial_rx_chars(uart); + arc_serial_rx_chars(uart, status); spin_unlock(&uart->port.lock); } @@ -528,7 +533,7 @@ arc_uart_init_one(struct platform_device *pdev, int dev_id) unsigned long *plat_data; struct arc_uart_port *uart = &arc_uart_ports[dev_id]; - plat_data = ((unsigned long *)(pdev->dev.platform_data)); + plat_data = (unsigned long *)dev_get_platdata(&pdev->dev); if (!plat_data) return -ENODEV; @@ -773,6 +778,6 @@ module_init(arc_serial_init); module_exit(arc_serial_exit); MODULE_LICENSE("GPL"); -MODULE_ALIAS("plat-arcfpga/uart"); +MODULE_ALIAS("platform:" DRIVER_NAME); MODULE_AUTHOR("Vineet Gupta"); MODULE_DESCRIPTION("ARC(Synopsys) On-Chip(fpga) serial driver"); diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 3467462869ce..d067285a2d20 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -39,8 +39,8 @@ #include <linux/atmel_pdc.h> #include <linux/atmel_serial.h> #include <linux/uaccess.h> -#include <linux/pinctrl/consumer.h> #include <linux/platform_data/atmel.h> +#include <linux/timer.h> #include <asm/io.h> #include <asm/ioctls.h> @@ -98,6 +98,7 @@ static void atmel_stop_rx(struct uart_port *port); #define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR) #define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR) #define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR) +#define UART_GET_IP_NAME(port) __raw_readl((port)->membase + ATMEL_US_NAME) /* PDC registers */ #define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR) @@ -140,13 +141,25 @@ struct atmel_uart_port { u32 backup_imr; /* IMR saved during suspend */ int break_active; /* break being received */ - short use_dma_rx; /* enable PDC receiver */ + bool use_dma_rx; /* enable DMA receiver */ + bool use_pdc_rx; /* enable PDC receiver */ short pdc_rx_idx; /* current PDC RX buffer */ struct atmel_dma_buffer pdc_rx[2]; /* PDC receier */ - short use_dma_tx; /* enable PDC transmitter */ + bool use_dma_tx; /* enable DMA transmitter */ + bool use_pdc_tx; /* enable PDC transmitter */ struct atmel_dma_buffer pdc_tx; /* PDC transmitter */ + spinlock_t lock_tx; /* port lock */ + spinlock_t lock_rx; /* port lock */ + struct dma_chan *chan_tx; + struct dma_chan *chan_rx; + struct dma_async_tx_descriptor *desc_tx; + struct dma_async_tx_descriptor *desc_rx; + dma_cookie_t cookie_tx; + dma_cookie_t cookie_rx; + struct scatterlist sg_tx; + struct scatterlist sg_rx; struct tasklet_struct tasklet; unsigned int irq_status; unsigned int irq_status_prev; @@ -155,6 +168,14 @@ struct atmel_uart_port { struct serial_rs485 rs485; /* rs485 settings */ unsigned int tx_done_mask; + bool is_usart; /* usart or uart */ + struct timer_list uart_timer; /* uart timer */ + int (*prepare_rx)(struct uart_port *port); + int (*prepare_tx)(struct uart_port *port); + void (*schedule_rx)(struct uart_port *port); + void (*schedule_tx)(struct uart_port *port); + void (*release_rx)(struct uart_port *port); + void (*release_tx)(struct uart_port *port); }; static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART]; @@ -181,31 +202,45 @@ to_atmel_uart_port(struct uart_port *uart) } #ifdef CONFIG_SERIAL_ATMEL_PDC -static bool atmel_use_dma_rx(struct uart_port *port) +static bool atmel_use_pdc_rx(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - return atmel_port->use_dma_rx; + return atmel_port->use_pdc_rx; } -static bool atmel_use_dma_tx(struct uart_port *port) +static bool atmel_use_pdc_tx(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - return atmel_port->use_dma_tx; + return atmel_port->use_pdc_tx; } #else -static bool atmel_use_dma_rx(struct uart_port *port) +static bool atmel_use_pdc_rx(struct uart_port *port) { return false; } -static bool atmel_use_dma_tx(struct uart_port *port) +static bool atmel_use_pdc_tx(struct uart_port *port) { return false; } #endif +static bool atmel_use_dma_tx(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + return atmel_port->use_dma_tx; +} + +static bool atmel_use_dma_rx(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + return atmel_port->use_dma_rx; +} + /* Enable or disable the rs485 support */ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) { @@ -233,7 +268,7 @@ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) mode |= ATMEL_US_USMODE_RS485; } else { dev_dbg(port->dev, "Setting UART to RS232\n"); - if (atmel_use_dma_tx(port)) + if (atmel_use_pdc_tx(port)) atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE; else @@ -345,7 +380,7 @@ static void atmel_stop_tx(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - if (atmel_use_dma_tx(port)) { + if (atmel_use_pdc_tx(port)) { /* disable PDC transmit */ UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); } @@ -364,7 +399,7 @@ static void atmel_start_tx(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - if (atmel_use_dma_tx(port)) { + if (atmel_use_pdc_tx(port)) { if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN) /* The transmitter is already running. Yes, we really need this.*/ @@ -390,7 +425,7 @@ static void atmel_start_rx(struct uart_port *port) UART_PUT_CR(port, ATMEL_US_RXEN); - if (atmel_use_dma_rx(port)) { + if (atmel_use_pdc_rx(port)) { /* enable PDC controller */ UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | port->read_status_mask); @@ -407,7 +442,7 @@ static void atmel_stop_rx(struct uart_port *port) { UART_PUT_CR(port, ATMEL_US_RXDIS); - if (atmel_use_dma_rx(port)) { + if (atmel_use_pdc_rx(port)) { /* disable PDC receive */ UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS); UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | @@ -564,6 +599,372 @@ static void atmel_tx_chars(struct uart_port *port) UART_PUT_IER(port, atmel_port->tx_done_mask); } +static void atmel_complete_tx_dma(void *arg) +{ + struct atmel_uart_port *atmel_port = arg; + struct uart_port *port = &atmel_port->uart; + struct circ_buf *xmit = &port->state->xmit; + struct dma_chan *chan = atmel_port->chan_tx; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + if (chan) + dmaengine_terminate_all(chan); + xmit->tail += sg_dma_len(&atmel_port->sg_tx); + xmit->tail &= UART_XMIT_SIZE - 1; + + port->icount.tx += sg_dma_len(&atmel_port->sg_tx); + + spin_lock_irq(&atmel_port->lock_tx); + async_tx_ack(atmel_port->desc_tx); + atmel_port->cookie_tx = -EINVAL; + atmel_port->desc_tx = NULL; + spin_unlock_irq(&atmel_port->lock_tx); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + /* Do we really need this? */ + if (!uart_circ_empty(xmit)) + tasklet_schedule(&atmel_port->tasklet); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void atmel_release_tx_dma(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct dma_chan *chan = atmel_port->chan_tx; + + if (chan) { + dmaengine_terminate_all(chan); + dma_release_channel(chan); + dma_unmap_sg(port->dev, &atmel_port->sg_tx, 1, + DMA_MEM_TO_DEV); + } + + atmel_port->desc_tx = NULL; + atmel_port->chan_tx = NULL; + atmel_port->cookie_tx = -EINVAL; +} + +/* + * Called from tasklet with TXRDY interrupt is disabled. + */ +static void atmel_tx_dma(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct circ_buf *xmit = &port->state->xmit; + struct dma_chan *chan = atmel_port->chan_tx; + struct dma_async_tx_descriptor *desc; + struct scatterlist *sg = &atmel_port->sg_tx; + + /* Make sure we have an idle channel */ + if (atmel_port->desc_tx != NULL) + return; + + if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) { + /* + * DMA is idle now. + * Port xmit buffer is already mapped, + * and it is one page... Just adjust + * offsets and lengths. Since it is a circular buffer, + * we have to transmit till the end, and then the rest. + * Take the port lock to get a + * consistent xmit buffer state. + */ + sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); + sg_dma_address(sg) = (sg_dma_address(sg) & + ~(UART_XMIT_SIZE - 1)) + + sg->offset; + sg_dma_len(sg) = CIRC_CNT_TO_END(xmit->head, + xmit->tail, + UART_XMIT_SIZE); + BUG_ON(!sg_dma_len(sg)); + + desc = dmaengine_prep_slave_sg(chan, + sg, + 1, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + if (!desc) { + dev_err(port->dev, "Failed to send via dma!\n"); + return; + } + + dma_sync_sg_for_device(port->dev, sg, 1, DMA_MEM_TO_DEV); + + atmel_port->desc_tx = desc; + desc->callback = atmel_complete_tx_dma; + desc->callback_param = atmel_port; + atmel_port->cookie_tx = dmaengine_submit(desc); + + } else { + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + /* DMA done, stop TX, start RX for RS485 */ + atmel_start_rx(port); + } + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +static int atmel_prepare_tx_dma(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + dma_cap_mask_t mask; + struct dma_slave_config config; + int ret, nent; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + atmel_port->chan_tx = dma_request_slave_channel(port->dev, "tx"); + if (atmel_port->chan_tx == NULL) + goto chan_err; + dev_info(port->dev, "using %s for tx DMA transfers\n", + dma_chan_name(atmel_port->chan_tx)); + + spin_lock_init(&atmel_port->lock_tx); + sg_init_table(&atmel_port->sg_tx, 1); + /* UART circular tx buffer is an aligned page. */ + BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK); + sg_set_page(&atmel_port->sg_tx, + virt_to_page(port->state->xmit.buf), + UART_XMIT_SIZE, + (int)port->state->xmit.buf & ~PAGE_MASK); + nent = dma_map_sg(port->dev, + &atmel_port->sg_tx, + 1, + DMA_MEM_TO_DEV); + + if (!nent) { + dev_dbg(port->dev, "need to release resource of dma\n"); + goto chan_err; + } else { + dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__, + sg_dma_len(&atmel_port->sg_tx), + port->state->xmit.buf, + sg_dma_address(&atmel_port->sg_tx)); + } + + /* Configure the slave DMA */ + memset(&config, 0, sizeof(config)); + config.direction = DMA_MEM_TO_DEV; + config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + config.dst_addr = port->mapbase + ATMEL_US_THR; + + ret = dmaengine_device_control(atmel_port->chan_tx, + DMA_SLAVE_CONFIG, + (unsigned long)&config); + if (ret) { + dev_err(port->dev, "DMA tx slave configuration failed\n"); + goto chan_err; + } + + return 0; + +chan_err: + dev_err(port->dev, "TX channel not available, switch to pio\n"); + atmel_port->use_dma_tx = 0; + if (atmel_port->chan_tx) + atmel_release_tx_dma(port); + return -EINVAL; +} + +static void atmel_flip_buffer_rx_dma(struct uart_port *port, + char *buf, size_t count) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct tty_port *tport = &port->state->port; + + dma_sync_sg_for_cpu(port->dev, + &atmel_port->sg_rx, + 1, + DMA_DEV_TO_MEM); + + tty_insert_flip_string(tport, buf, count); + + dma_sync_sg_for_device(port->dev, + &atmel_port->sg_rx, + 1, + DMA_DEV_TO_MEM); + /* + * Drop the lock here since it might end up calling + * uart_start(), which takes the lock. + */ + spin_unlock(&port->lock); + tty_flip_buffer_push(tport); + spin_lock(&port->lock); +} + +static void atmel_complete_rx_dma(void *arg) +{ + struct uart_port *port = arg; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + tasklet_schedule(&atmel_port->tasklet); +} + +static void atmel_release_rx_dma(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct dma_chan *chan = atmel_port->chan_rx; + + if (chan) { + dmaengine_terminate_all(chan); + dma_release_channel(chan); + dma_unmap_sg(port->dev, &atmel_port->sg_rx, 1, + DMA_DEV_TO_MEM); + } + + atmel_port->desc_rx = NULL; + atmel_port->chan_rx = NULL; + atmel_port->cookie_rx = -EINVAL; + + if (!atmel_port->is_usart) + del_timer_sync(&atmel_port->uart_timer); +} + +static void atmel_rx_from_dma(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct circ_buf *ring = &atmel_port->rx_ring; + struct dma_chan *chan = atmel_port->chan_rx; + struct dma_tx_state state; + enum dma_status dmastat; + size_t pending, count; + + + /* Reset the UART timeout early so that we don't miss one */ + UART_PUT_CR(port, ATMEL_US_STTTO); + dmastat = dmaengine_tx_status(chan, + atmel_port->cookie_rx, + &state); + /* Restart a new tasklet if DMA status is error */ + if (dmastat == DMA_ERROR) { + dev_dbg(port->dev, "Get residue error, restart tasklet\n"); + UART_PUT_IER(port, ATMEL_US_TIMEOUT); + tasklet_schedule(&atmel_port->tasklet); + return; + } + /* current transfer size should no larger than dma buffer */ + pending = sg_dma_len(&atmel_port->sg_rx) - state.residue; + BUG_ON(pending > sg_dma_len(&atmel_port->sg_rx)); + + /* + * This will take the chars we have so far, + * ring->head will record the transfer size, only new bytes come + * will insert into the framework. + */ + if (pending > ring->head) { + count = pending - ring->head; + + atmel_flip_buffer_rx_dma(port, ring->buf + ring->head, count); + + ring->head += count; + if (ring->head == sg_dma_len(&atmel_port->sg_rx)) + ring->head = 0; + + port->icount.rx += count; + } + + UART_PUT_IER(port, ATMEL_US_TIMEOUT); +} + +static int atmel_prepare_rx_dma(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct dma_async_tx_descriptor *desc; + dma_cap_mask_t mask; + struct dma_slave_config config; + struct circ_buf *ring; + int ret, nent; + + ring = &atmel_port->rx_ring; + + dma_cap_zero(mask); + dma_cap_set(DMA_CYCLIC, mask); + + atmel_port->chan_rx = dma_request_slave_channel(port->dev, "rx"); + if (atmel_port->chan_rx == NULL) + goto chan_err; + dev_info(port->dev, "using %s for rx DMA transfers\n", + dma_chan_name(atmel_port->chan_rx)); + + spin_lock_init(&atmel_port->lock_rx); + sg_init_table(&atmel_port->sg_rx, 1); + /* UART circular rx buffer is an aligned page. */ + BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK); + sg_set_page(&atmel_port->sg_rx, + virt_to_page(ring->buf), + ATMEL_SERIAL_RINGSIZE, + (int)ring->buf & ~PAGE_MASK); + nent = dma_map_sg(port->dev, + &atmel_port->sg_rx, + 1, + DMA_DEV_TO_MEM); + + if (!nent) { + dev_dbg(port->dev, "need to release resource of dma\n"); + goto chan_err; + } else { + dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__, + sg_dma_len(&atmel_port->sg_rx), + ring->buf, + sg_dma_address(&atmel_port->sg_rx)); + } + + /* Configure the slave DMA */ + memset(&config, 0, sizeof(config)); + config.direction = DMA_DEV_TO_MEM; + config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + config.src_addr = port->mapbase + ATMEL_US_RHR; + + ret = dmaengine_device_control(atmel_port->chan_rx, + DMA_SLAVE_CONFIG, + (unsigned long)&config); + if (ret) { + dev_err(port->dev, "DMA rx slave configuration failed\n"); + goto chan_err; + } + /* + * Prepare a cyclic dma transfer, assign 2 descriptors, + * each one is half ring buffer size + */ + desc = dmaengine_prep_dma_cyclic(atmel_port->chan_rx, + sg_dma_address(&atmel_port->sg_rx), + sg_dma_len(&atmel_port->sg_rx), + sg_dma_len(&atmel_port->sg_rx)/2, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + desc->callback = atmel_complete_rx_dma; + desc->callback_param = port; + atmel_port->desc_rx = desc; + atmel_port->cookie_rx = dmaengine_submit(desc); + + return 0; + +chan_err: + dev_err(port->dev, "RX channel not available, switch to pio\n"); + atmel_port->use_dma_rx = 0; + if (atmel_port->chan_rx) + atmel_release_rx_dma(port); + return -EINVAL; +} + +static void atmel_uart_timer_callback(unsigned long data) +{ + struct uart_port *port = (void *)data; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + tasklet_schedule(&atmel_port->tasklet); + mod_timer(&atmel_port->uart_timer, jiffies + uart_poll_timeout(port)); +} + /* * receive interrupt handler. */ @@ -572,7 +973,7 @@ atmel_handle_receive(struct uart_port *port, unsigned int pending) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - if (atmel_use_dma_rx(port)) { + if (atmel_use_pdc_rx(port)) { /* * PDC receive. Just schedule the tasklet and let it * figure out the details. @@ -591,6 +992,13 @@ atmel_handle_receive(struct uart_port *port, unsigned int pending) atmel_pdc_rxerr(port, pending); } + if (atmel_use_dma_rx(port)) { + if (pending & ATMEL_US_TIMEOUT) { + UART_PUT_IDR(port, ATMEL_US_TIMEOUT); + tasklet_schedule(&atmel_port->tasklet); + } + } + /* Interrupt receive */ if (pending & ATMEL_US_RXRDY) atmel_rx_chars(port); @@ -658,10 +1066,21 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id) return pass_counter ? IRQ_HANDLED : IRQ_NONE; } +static void atmel_release_tx_pdc(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; + + dma_unmap_single(port->dev, + pdc->dma_addr, + pdc->dma_size, + DMA_TO_DEVICE); +} + /* * Called from tasklet with ENDTX and TXBUFE interrupts disabled. */ -static void atmel_tx_dma(struct uart_port *port) +static void atmel_tx_pdc(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); struct circ_buf *xmit = &port->state->xmit; @@ -710,6 +1129,23 @@ static void atmel_tx_dma(struct uart_port *port) uart_write_wakeup(port); } +static int atmel_prepare_tx_pdc(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; + struct circ_buf *xmit = &port->state->xmit; + + pdc->buf = xmit->buf; + pdc->dma_addr = dma_map_single(port->dev, + pdc->buf, + UART_XMIT_SIZE, + DMA_TO_DEVICE); + pdc->dma_size = UART_XMIT_SIZE; + pdc->ofs = 0; + + return 0; +} + static void atmel_rx_from_ring(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); @@ -778,7 +1214,26 @@ static void atmel_rx_from_ring(struct uart_port *port) spin_lock(&port->lock); } -static void atmel_rx_from_dma(struct uart_port *port) +static void atmel_release_rx_pdc(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + int i; + + for (i = 0; i < 2; i++) { + struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i]; + + dma_unmap_single(port->dev, + pdc->dma_addr, + pdc->dma_size, + DMA_FROM_DEVICE); + kfree(pdc->buf); + } + + if (!atmel_port->is_usart) + del_timer_sync(&atmel_port->uart_timer); +} + +static void atmel_rx_from_pdc(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); struct tty_port *tport = &port->state->port; @@ -855,6 +1310,45 @@ static void atmel_rx_from_dma(struct uart_port *port) UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); } +static int atmel_prepare_rx_pdc(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + int i; + + for (i = 0; i < 2; i++) { + struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i]; + + pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL); + if (pdc->buf == NULL) { + if (i != 0) { + dma_unmap_single(port->dev, + atmel_port->pdc_rx[0].dma_addr, + PDC_BUFFER_SIZE, + DMA_FROM_DEVICE); + kfree(atmel_port->pdc_rx[0].buf); + } + atmel_port->use_pdc_rx = 0; + return -ENOMEM; + } + pdc->dma_addr = dma_map_single(port->dev, + pdc->buf, + PDC_BUFFER_SIZE, + DMA_FROM_DEVICE); + pdc->dma_size = PDC_BUFFER_SIZE; + pdc->ofs = 0; + } + + atmel_port->pdc_rx_idx = 0; + + UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr); + UART_PUT_RCR(port, PDC_BUFFER_SIZE); + + UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr); + UART_PUT_RNCR(port, PDC_BUFFER_SIZE); + + return 0; +} + /* * tasklet handling tty stuff outside the interrupt handler. */ @@ -868,10 +1362,7 @@ static void atmel_tasklet_func(unsigned long data) /* The interrupt handler does not take the lock */ spin_lock(&port->lock); - if (atmel_use_dma_tx(port)) - atmel_tx_dma(port); - else - atmel_tx_chars(port); + atmel_port->schedule_tx(port); status = atmel_port->irq_status; status_change = status ^ atmel_port->irq_status_prev; @@ -893,19 +1384,152 @@ static void atmel_tasklet_func(unsigned long data) atmel_port->irq_status_prev = status; } - if (atmel_use_dma_rx(port)) - atmel_rx_from_dma(port); - else - atmel_rx_from_ring(port); + atmel_port->schedule_rx(port); spin_unlock(&port->lock); } +static int atmel_init_property(struct atmel_uart_port *atmel_port, + struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); + + if (np) { + /* DMA/PDC usage specification */ + if (of_get_property(np, "atmel,use-dma-rx", NULL)) { + if (of_get_property(np, "dmas", NULL)) { + atmel_port->use_dma_rx = true; + atmel_port->use_pdc_rx = false; + } else { + atmel_port->use_dma_rx = false; + atmel_port->use_pdc_rx = true; + } + } else { + atmel_port->use_dma_rx = false; + atmel_port->use_pdc_rx = false; + } + + if (of_get_property(np, "atmel,use-dma-tx", NULL)) { + if (of_get_property(np, "dmas", NULL)) { + atmel_port->use_dma_tx = true; + atmel_port->use_pdc_tx = false; + } else { + atmel_port->use_dma_tx = false; + atmel_port->use_pdc_tx = true; + } + } else { + atmel_port->use_dma_tx = false; + atmel_port->use_pdc_tx = false; + } + + } else { + atmel_port->use_pdc_rx = pdata->use_dma_rx; + atmel_port->use_pdc_tx = pdata->use_dma_tx; + atmel_port->use_dma_rx = false; + atmel_port->use_dma_tx = false; + } + + return 0; +} + +static void atmel_init_rs485(struct atmel_uart_port *atmel_port, + struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); + + if (np) { + u32 rs485_delay[2]; + /* rs485 properties */ + if (of_property_read_u32_array(np, "rs485-rts-delay", + rs485_delay, 2) == 0) { + struct serial_rs485 *rs485conf = &atmel_port->rs485; + + rs485conf->delay_rts_before_send = rs485_delay[0]; + rs485conf->delay_rts_after_send = rs485_delay[1]; + rs485conf->flags = 0; + + if (of_get_property(np, "rs485-rx-during-tx", NULL)) + rs485conf->flags |= SER_RS485_RX_DURING_TX; + + if (of_get_property(np, "linux,rs485-enabled-at-boot-time", + NULL)) + rs485conf->flags |= SER_RS485_ENABLED; + } + } else { + atmel_port->rs485 = pdata->rs485; + } + +} + +static void atmel_set_ops(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (atmel_use_dma_rx(port)) { + atmel_port->prepare_rx = &atmel_prepare_rx_dma; + atmel_port->schedule_rx = &atmel_rx_from_dma; + atmel_port->release_rx = &atmel_release_rx_dma; + } else if (atmel_use_pdc_rx(port)) { + atmel_port->prepare_rx = &atmel_prepare_rx_pdc; + atmel_port->schedule_rx = &atmel_rx_from_pdc; + atmel_port->release_rx = &atmel_release_rx_pdc; + } else { + atmel_port->prepare_rx = NULL; + atmel_port->schedule_rx = &atmel_rx_from_ring; + atmel_port->release_rx = NULL; + } + + if (atmel_use_dma_tx(port)) { + atmel_port->prepare_tx = &atmel_prepare_tx_dma; + atmel_port->schedule_tx = &atmel_tx_dma; + atmel_port->release_tx = &atmel_release_tx_dma; + } else if (atmel_use_pdc_tx(port)) { + atmel_port->prepare_tx = &atmel_prepare_tx_pdc; + atmel_port->schedule_tx = &atmel_tx_pdc; + atmel_port->release_tx = &atmel_release_tx_pdc; + } else { + atmel_port->prepare_tx = NULL; + atmel_port->schedule_tx = &atmel_tx_chars; + atmel_port->release_tx = NULL; + } +} + +/* + * Get ip name usart or uart + */ +static int atmel_get_ip_name(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + int name = UART_GET_IP_NAME(port); + int usart, uart; + /* usart and uart ascii */ + usart = 0x55534152; + uart = 0x44424755; + + atmel_port->is_usart = false; + + if (name == usart) { + dev_dbg(port->dev, "This is usart\n"); + atmel_port->is_usart = true; + } else if (name == uart) { + dev_dbg(port->dev, "This is uart\n"); + atmel_port->is_usart = false; + } else { + dev_err(port->dev, "Not supported ip name, set to uart\n"); + return -EINVAL; + } + + return 0; +} + /* * Perform initialization and enable port for reception */ static int atmel_startup(struct uart_port *port) { + struct platform_device *pdev = to_platform_device(port->dev); struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); struct tty_struct *tty = port->state->port.tty; int retval; @@ -930,53 +1554,19 @@ static int atmel_startup(struct uart_port *port) /* * Initialize DMA (if necessary) */ - if (atmel_use_dma_rx(port)) { - int i; - - for (i = 0; i < 2; i++) { - struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i]; + atmel_init_property(atmel_port, pdev); - pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL); - if (pdc->buf == NULL) { - if (i != 0) { - dma_unmap_single(port->dev, - atmel_port->pdc_rx[0].dma_addr, - PDC_BUFFER_SIZE, - DMA_FROM_DEVICE); - kfree(atmel_port->pdc_rx[0].buf); - } - free_irq(port->irq, port); - return -ENOMEM; - } - pdc->dma_addr = dma_map_single(port->dev, - pdc->buf, - PDC_BUFFER_SIZE, - DMA_FROM_DEVICE); - pdc->dma_size = PDC_BUFFER_SIZE; - pdc->ofs = 0; - } - - atmel_port->pdc_rx_idx = 0; - - UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr); - UART_PUT_RCR(port, PDC_BUFFER_SIZE); - - UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr); - UART_PUT_RNCR(port, PDC_BUFFER_SIZE); + if (atmel_port->prepare_rx) { + retval = atmel_port->prepare_rx(port); + if (retval < 0) + atmel_set_ops(port); } - if (atmel_use_dma_tx(port)) { - struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; - struct circ_buf *xmit = &port->state->xmit; - pdc->buf = xmit->buf; - pdc->dma_addr = dma_map_single(port->dev, - pdc->buf, - UART_XMIT_SIZE, - DMA_TO_DEVICE); - pdc->dma_size = UART_XMIT_SIZE; - pdc->ofs = 0; + if (atmel_port->prepare_tx) { + retval = atmel_port->prepare_tx(port); + if (retval < 0) + atmel_set_ops(port); } - /* * If there is a specific "open" function (to register * control line interrupts) @@ -1000,14 +1590,38 @@ static int atmel_startup(struct uart_port *port) /* enable xmit & rcvr */ UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); - if (atmel_use_dma_rx(port)) { + if (atmel_use_pdc_rx(port)) { /* set UART timeout */ - UART_PUT_RTOR(port, PDC_RX_TIMEOUT); - UART_PUT_CR(port, ATMEL_US_STTTO); - - UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); + if (!atmel_port->is_usart) { + setup_timer(&atmel_port->uart_timer, + atmel_uart_timer_callback, + (unsigned long)port); + mod_timer(&atmel_port->uart_timer, + jiffies + uart_poll_timeout(port)); + /* set USART timeout */ + } else { + UART_PUT_RTOR(port, PDC_RX_TIMEOUT); + UART_PUT_CR(port, ATMEL_US_STTTO); + + UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); + } /* enable PDC controller */ UART_PUT_PTCR(port, ATMEL_PDC_RXTEN); + } else if (atmel_use_dma_rx(port)) { + /* set UART timeout */ + if (!atmel_port->is_usart) { + setup_timer(&atmel_port->uart_timer, + atmel_uart_timer_callback, + (unsigned long)port); + mod_timer(&atmel_port->uart_timer, + jiffies + uart_poll_timeout(port)); + /* set USART timeout */ + } else { + UART_PUT_RTOR(port, PDC_RX_TIMEOUT); + UART_PUT_CR(port, ATMEL_US_STTTO); + + UART_PUT_IER(port, ATMEL_US_TIMEOUT); + } } else { /* enable receive only */ UART_PUT_IER(port, ATMEL_US_RXRDY); @@ -1031,27 +1645,10 @@ static void atmel_shutdown(struct uart_port *port) /* * Shut-down the DMA. */ - if (atmel_use_dma_rx(port)) { - int i; - - for (i = 0; i < 2; i++) { - struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i]; - - dma_unmap_single(port->dev, - pdc->dma_addr, - pdc->dma_size, - DMA_FROM_DEVICE); - kfree(pdc->buf); - } - } - if (atmel_use_dma_tx(port)) { - struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; - - dma_unmap_single(port->dev, - pdc->dma_addr, - pdc->dma_size, - DMA_TO_DEVICE); - } + if (atmel_port->release_rx) + atmel_port->release_rx(port); + if (atmel_port->release_tx) + atmel_port->release_tx(port); /* * Disable all interrupts, port and break condition. @@ -1080,7 +1677,7 @@ static void atmel_flush_buffer(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - if (atmel_use_dma_tx(port)) { + if (atmel_use_pdc_tx(port)) { UART_PUT_TCR(port, 0); atmel_port->pdc_tx.ofs = 0; } @@ -1100,7 +1697,7 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state, * Enable the peripheral clock for this serial port. * This is called on uart_open() or a resume event. */ - clk_enable(atmel_port->clk); + clk_prepare_enable(atmel_port->clk); /* re-enable interrupts if we disabled some on suspend */ UART_PUT_IER(port, atmel_port->backup_imr); @@ -1114,7 +1711,7 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state, * Disable the peripheral clock for this serial port. * This is called on uart_close() or a suspend event. */ - clk_disable(atmel_port->clk); + clk_disable_unprepare(atmel_port->clk); break; default: printk(KERN_ERR "atmel_serial: unknown pm %d\n", state); @@ -1193,7 +1790,7 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, if (termios->c_iflag & (BRKINT | PARMRK)) port->read_status_mask |= ATMEL_US_RXBRK; - if (atmel_use_dma_rx(port)) + if (atmel_use_pdc_rx(port)) /* need to enable error interrupts */ UART_PUT_IER(port, port->read_status_mask); @@ -1423,54 +2020,20 @@ static struct uart_ops atmel_pops = { #endif }; -static void atmel_of_init_port(struct atmel_uart_port *atmel_port, - struct device_node *np) -{ - u32 rs485_delay[2]; - - /* DMA/PDC usage specification */ - if (of_get_property(np, "atmel,use-dma-rx", NULL)) - atmel_port->use_dma_rx = 1; - else - atmel_port->use_dma_rx = 0; - if (of_get_property(np, "atmel,use-dma-tx", NULL)) - atmel_port->use_dma_tx = 1; - else - atmel_port->use_dma_tx = 0; - - /* rs485 properties */ - if (of_property_read_u32_array(np, "rs485-rts-delay", - rs485_delay, 2) == 0) { - struct serial_rs485 *rs485conf = &atmel_port->rs485; - - rs485conf->delay_rts_before_send = rs485_delay[0]; - rs485conf->delay_rts_after_send = rs485_delay[1]; - rs485conf->flags = 0; - - if (of_get_property(np, "rs485-rx-during-tx", NULL)) - rs485conf->flags |= SER_RS485_RX_DURING_TX; - - if (of_get_property(np, "linux,rs485-enabled-at-boot-time", NULL)) - rs485conf->flags |= SER_RS485_ENABLED; - } -} - /* * Configure the port from the platform device resource info. */ -static void atmel_init_port(struct atmel_uart_port *atmel_port, +static int atmel_init_port(struct atmel_uart_port *atmel_port, struct platform_device *pdev) { + int ret; struct uart_port *port = &atmel_port->uart; - struct atmel_uart_data *pdata = pdev->dev.platform_data; + struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); - if (pdev->dev.of_node) { - atmel_of_init_port(atmel_port, pdev->dev.of_node); - } else { - atmel_port->use_dma_rx = pdata->use_dma_rx; - atmel_port->use_dma_tx = pdata->use_dma_tx; - atmel_port->rs485 = pdata->rs485; - } + if (!atmel_init_property(atmel_port, pdev)) + atmel_set_ops(port); + + atmel_init_rs485(atmel_port, pdev); port->iotype = UPIO_MEM; port->flags = UPF_BOOT_AUTOCONF; @@ -1496,21 +2059,33 @@ static void atmel_init_port(struct atmel_uart_port *atmel_port, /* for console, the clock could already be configured */ if (!atmel_port->clk) { atmel_port->clk = clk_get(&pdev->dev, "usart"); - clk_enable(atmel_port->clk); + if (IS_ERR(atmel_port->clk)) { + ret = PTR_ERR(atmel_port->clk); + atmel_port->clk = NULL; + return ret; + } + ret = clk_prepare_enable(atmel_port->clk); + if (ret) { + clk_put(atmel_port->clk); + atmel_port->clk = NULL; + return ret; + } port->uartclk = clk_get_rate(atmel_port->clk); - clk_disable(atmel_port->clk); + clk_disable_unprepare(atmel_port->clk); /* only enable clock when USART is in use */ } /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */ if (atmel_port->rs485.flags & SER_RS485_ENABLED) atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; - else if (atmel_use_dma_tx(port)) { + else if (atmel_use_pdc_tx(port)) { port->fifosize = PDC_BUFFER_SIZE; atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE; } else { atmel_port->tx_done_mask = ATMEL_US_TXRDY; } + + return 0; } struct platform_device *atmel_default_console_device; /* the serial console device */ @@ -1601,6 +2176,7 @@ static void __init atmel_console_get_options(struct uart_port *port, int *baud, static int __init atmel_console_setup(struct console *co, char *options) { + int ret; struct uart_port *port = &atmel_ports[co->index].uart; int baud = 115200; int bits = 8; @@ -1612,7 +2188,9 @@ static int __init atmel_console_setup(struct console *co, char *options) return -ENODEV; } - clk_enable(atmel_ports[co->index].clk); + ret = clk_prepare_enable(atmel_ports[co->index].clk); + if (ret) + return ret; UART_PUT_IDR(port, -1); UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); @@ -1645,9 +2223,10 @@ static struct console atmel_console = { */ static int __init atmel_console_init(void) { + int ret; if (atmel_default_console_device) { struct atmel_uart_data *pdata = - atmel_default_console_device->dev.platform_data; + dev_get_platdata(&atmel_default_console_device->dev); int id = pdata->num; struct atmel_uart_port *port = &atmel_ports[id]; @@ -1655,7 +2234,9 @@ static int __init atmel_console_init(void) port->uart.line = id; add_preferred_console(ATMEL_DEVICENAME, id, NULL); - atmel_init_port(port, atmel_default_console_device); + ret = atmel_init_port(port, atmel_default_console_device); + if (ret) + return ret; register_console(&atmel_console); } @@ -1753,10 +2334,9 @@ static int atmel_serial_probe(struct platform_device *pdev) { struct atmel_uart_port *port; struct device_node *np = pdev->dev.of_node; - struct atmel_uart_data *pdata = pdev->dev.platform_data; + struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); void *data; int ret = -ENODEV; - struct pinctrl *pinctrl; BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1)); @@ -1786,15 +2366,11 @@ static int atmel_serial_probe(struct platform_device *pdev) port->backup_imr = 0; port->uart.line = ret; - atmel_init_port(port, pdev); - - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - ret = PTR_ERR(pinctrl); + ret = atmel_init_port(port, pdev); + if (ret) goto err; - } - if (!atmel_use_dma_rx(&port->uart)) { + if (!atmel_use_pdc_rx(&port->uart)) { ret = -ENOMEM; data = kmalloc(sizeof(struct atmel_uart_char) * ATMEL_SERIAL_RINGSIZE, GFP_KERNEL); @@ -1812,9 +2388,9 @@ static int atmel_serial_probe(struct platform_device *pdev) && ATMEL_CONSOLE_DEVICE->flags & CON_ENABLED) { /* * The serial core enabled the clock for us, so undo - * the clk_enable() in atmel_console_setup() + * the clk_prepare_enable() in atmel_console_setup() */ - clk_disable(port->clk); + clk_disable_unprepare(port->clk); } #endif @@ -1826,6 +2402,13 @@ static int atmel_serial_probe(struct platform_device *pdev) UART_PUT_CR(&port->uart, ATMEL_US_RTSEN); } + /* + * Get port name of usart or uart + */ + ret = atmel_get_ip_name(&port->uart); + if (ret < 0) + goto err_add_port; + return 0; err_add_port: @@ -1847,7 +2430,6 @@ static int atmel_serial_remove(struct platform_device *pdev) int ret = 0; device_init_wakeup(&pdev->dev, 0); - platform_set_drvdata(pdev, NULL); ret = uart_remove_one_port(&atmel_uart, port); diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index 6fa2ae77fffd..649d5129c4b4 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -302,7 +302,9 @@ static void bcm_uart_do_rx(struct uart_port *port) } while (--max_count); + spin_unlock(&port->lock); tty_flip_buffer_push(tty_port); + spin_lock(&port->lock); } /* @@ -852,7 +854,6 @@ static int bcm_uart_remove(struct platform_device *pdev) port = platform_get_drvdata(pdev); uart_remove_one_port(&bcm_uart_driver, port); - platform_set_drvdata(pdev, NULL); /* mark port as free */ ports[pdev->id].membase = 0; return 0; diff --git a/drivers/tty/serial/bfin_sport_uart.c b/drivers/tty/serial/bfin_sport_uart.c index 487c173b0f72..87636cc61a21 100644 --- a/drivers/tty/serial/bfin_sport_uart.c +++ b/drivers/tty/serial/bfin_sport_uart.c @@ -161,11 +161,12 @@ static irqreturn_t sport_uart_rx_irq(int irq, void *dev_id) if (!uart_handle_sysrq_char(&up->port, ch)) tty_insert_flip_char(port, ch, TTY_NORMAL); } - /* XXX this won't deadlock with lowlat? */ - tty_flip_buffer_push(port); spin_unlock(&up->port.lock); + /* XXX this won't deadlock with lowlat? */ + tty_flip_buffer_push(port); + return IRQ_HANDLED; } @@ -766,7 +767,8 @@ static int sport_uart_probe(struct platform_device *pdev) } ret = peripheral_request_list( - (unsigned short *)pdev->dev.platform_data, DRV_NAME); + (unsigned short *)dev_get_platdata(&pdev->dev), + DRV_NAME); if (ret) { dev_err(&pdev->dev, "Fail to request SPORT peripherals\n"); @@ -843,7 +845,7 @@ out_error_unmap: iounmap(sport->port.membase); out_error_free_peripherals: peripheral_free_list( - (unsigned short *)pdev->dev.platform_data); + (unsigned short *)dev_get_platdata(&pdev->dev)); out_error_free_mem: kfree(sport); bfin_sport_uart_ports[pdev->id] = NULL; @@ -863,7 +865,7 @@ static int sport_uart_remove(struct platform_device *pdev) uart_remove_one_port(&sport_uart_reg, &sport->port); iounmap(sport->port.membase); peripheral_free_list( - (unsigned short *)pdev->dev.platform_data); + (unsigned short *)dev_get_platdata(&pdev->dev)); kfree(sport); bfin_sport_uart_ports[pdev->id] = NULL; } @@ -883,7 +885,7 @@ static struct platform_driver sport_uart_driver = { }; #ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE -static __initdata struct early_platform_driver early_sport_uart_driver = { +static struct early_platform_driver early_sport_uart_driver __initdata = { .class_str = CLASS_BFIN_SPORT_CONSOLE, .pdrv = &sport_uart_driver, .requested_id = EARLY_PLATFORM_ID_UNSET, diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index 26a3be7ced7d..3c75e8e04028 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -41,10 +41,6 @@ # undef CONFIG_EARLY_PRINTK #endif -#ifdef CONFIG_SERIAL_BFIN_MODULE -# undef CONFIG_EARLY_PRINTK -#endif - /* UART name and device definitions */ #define BFIN_SERIAL_DEV_NAME "ttyBF" #define BFIN_SERIAL_MAJOR 204 @@ -1180,7 +1176,7 @@ bfin_earlyprintk_console_write(struct console *co, const char *s, unsigned int c * don't let the common infrastructure play with things. (see calls to setup * & earlysetup in ./kernel/printk.c:register_console() */ -static struct __initdata console bfin_early_serial_console = { +static struct console bfin_early_serial_console __initdata = { .name = "early_BFuart", .write = bfin_earlyprintk_console_write, .device = uart_console_device, @@ -1244,7 +1240,8 @@ static int bfin_serial_probe(struct platform_device *pdev) */ #endif ret = peripheral_request_list( - (unsigned short *)pdev->dev.platform_data, DRIVER_NAME); + (unsigned short *)dev_get_platdata(&pdev->dev), + DRIVER_NAME); if (ret) { dev_err(&pdev->dev, "fail to request bfin serial peripherals\n"); @@ -1362,7 +1359,7 @@ out_error_unmap: iounmap(uart->port.membase); out_error_free_peripherals: peripheral_free_list( - (unsigned short *)pdev->dev.platform_data); + (unsigned short *)dev_get_platdata(&pdev->dev)); out_error_free_mem: kfree(uart); bfin_serial_ports[pdev->id] = NULL; @@ -1381,7 +1378,7 @@ static int bfin_serial_remove(struct platform_device *pdev) uart_remove_one_port(&bfin_serial_reg, &uart->port); iounmap(uart->port.membase); peripheral_free_list( - (unsigned short *)pdev->dev.platform_data); + (unsigned short *)dev_get_platdata(&pdev->dev)); kfree(uart); bfin_serial_ports[pdev->id] = NULL; } @@ -1401,7 +1398,7 @@ static struct platform_driver bfin_serial_driver = { }; #if defined(CONFIG_SERIAL_BFIN_CONSOLE) -static __initdata struct early_platform_driver early_bfin_serial_driver = { +static struct early_platform_driver early_bfin_serial_driver __initdata = { .class_str = CLASS_BFIN_CONSOLE, .pdrv = &bfin_serial_driver, .requested_id = EARLY_PLATFORM_ID_UNSET, @@ -1436,7 +1433,7 @@ static int bfin_earlyprintk_probe(struct platform_device *pdev) } ret = peripheral_request_list( - (unsigned short *)pdev->dev.platform_data, DRIVER_NAME); + (unsigned short *)dev_get_platdata(&pdev->dev), DRIVER_NAME); if (ret) { dev_err(&pdev->dev, "fail to request bfin serial peripherals\n"); @@ -1467,7 +1464,7 @@ static int bfin_earlyprintk_probe(struct platform_device *pdev) out_error_free_peripherals: peripheral_free_list( - (unsigned short *)pdev->dev.platform_data); + (unsigned short *)dev_get_platdata(&pdev->dev)); return ret; } @@ -1480,7 +1477,7 @@ static struct platform_driver bfin_earlyprintk_driver = { }, }; -static __initdata struct early_platform_driver early_bfin_earlyprintk_driver = { +static struct early_platform_driver early_bfin_earlyprintk_driver __initdata = { .class_str = CLASS_BFIN_EARLYPRINTK, .pdrv = &bfin_earlyprintk_driver, .requested_id = EARLY_PLATFORM_ID_UNSET, diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index bfb17968c8db..7e4e4088471c 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -438,8 +438,7 @@ static int uart_clps711x_probe(struct platform_device *pdev) 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; + return PTR_ERR(s->uart_clk); } s->uart.owner = THIS_MODULE; @@ -461,7 +460,7 @@ static int uart_clps711x_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Registering UART driver failed\n"); devm_clk_put(&pdev->dev, s->uart_clk); - goto err_out; + return ret; } for (i = 0; i < UART_CLPS711X_NR; i++) { @@ -478,11 +477,6 @@ static int uart_clps711x_probe(struct platform_device *pdev) } return 0; - -err_out: - platform_set_drvdata(pdev, NULL); - - return ret; } static int uart_clps711x_remove(struct platform_device *pdev) @@ -495,7 +489,6 @@ static int uart_clps711x_remove(struct platform_device *pdev) devm_clk_put(&pdev->dev, s->uart_clk); uart_unregister_driver(&s->uart); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index 97f4e1858649..1a535f70dc41 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -1213,8 +1213,32 @@ static int cpm_uart_init_port(struct device_node *np, goto out_pram; } - for (i = 0; i < NUM_GPIOS; i++) - pinfo->gpios[i] = of_get_gpio(np, i); + for (i = 0; i < NUM_GPIOS; i++) { + int gpio; + + pinfo->gpios[i] = -1; + + gpio = of_get_gpio(np, i); + + if (gpio_is_valid(gpio)) { + ret = gpio_request(gpio, "cpm_uart"); + if (ret) { + pr_err("can't request gpio #%d: %d\n", i, ret); + continue; + } + if (i == GPIO_RTS || i == GPIO_DTR) + ret = gpio_direction_output(gpio, 0); + else + ret = gpio_direction_input(gpio); + if (ret) { + pr_err("can't set direction for gpio #%d: %d\n", + i, ret); + gpio_free(gpio); + continue; + } + pinfo->gpios[i] = gpio; + } + } #ifdef CONFIG_PPC_EARLY_DEBUG_CPM udbg_putc = NULL; @@ -1384,7 +1408,7 @@ static int cpm_uart_probe(struct platform_device *ofdev) if (index >= UART_NR) return -ENODEV; - dev_set_drvdata(&ofdev->dev, pinfo); + platform_set_drvdata(ofdev, pinfo); /* initialize the device pointer for the port */ pinfo->port.dev = &ofdev->dev; @@ -1398,7 +1422,7 @@ static int cpm_uart_probe(struct platform_device *ofdev) static int cpm_uart_remove(struct platform_device *ofdev) { - struct uart_cpm_port *pinfo = dev_get_drvdata(&ofdev->dev); + struct uart_cpm_port *pinfo = platform_get_drvdata(ofdev); return uart_remove_one_port(&cpm_reg, &pinfo->port); } diff --git a/drivers/tty/serial/efm32-uart.c b/drivers/tty/serial/efm32-uart.c index 7d199c8e1a75..0eb5b5673ede 100644 --- a/drivers/tty/serial/efm32-uart.c +++ b/drivers/tty/serial/efm32-uart.c @@ -268,10 +268,10 @@ static irqreturn_t efm32_uart_rxirq(int irq, void *data) handled = IRQ_HANDLED; } - tty_flip_buffer_push(tport); - spin_unlock(&port->lock); + tty_flip_buffer_push(tport); + return handled; } @@ -698,6 +698,7 @@ static int efm32_uart_probe(struct platform_device *pdev) { struct efm32_uart_port *efm_port; struct resource *res; + unsigned int line; int ret; efm_port = kzalloc(sizeof(*efm_port), GFP_KERNEL); @@ -750,18 +751,21 @@ static int efm32_uart_probe(struct platform_device *pdev) if (pdata) efm_port->pdata = *pdata; - } + } else if (ret < 0) + goto err_probe_dt; + + line = efm_port->port.line; - if (efm_port->port.line >= 0 && - efm_port->port.line < ARRAY_SIZE(efm32_uart_ports)) - efm32_uart_ports[efm_port->port.line] = efm_port; + if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports)) + efm32_uart_ports[line] = efm_port; ret = uart_add_one_port(&efm32_uart_reg, &efm_port->port); if (ret) { dev_dbg(&pdev->dev, "failed to add port: %d\n", ret); - if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports)) - efm32_uart_ports[pdev->id] = NULL; + if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports)) + efm32_uart_ports[line] = NULL; +err_probe_dt: err_get_rxirq: err_too_small: err_get_base: @@ -777,20 +781,19 @@ err_get_base: static int efm32_uart_remove(struct platform_device *pdev) { struct efm32_uart_port *efm_port = platform_get_drvdata(pdev); - - platform_set_drvdata(pdev, NULL); + unsigned int line = efm_port->port.line; uart_remove_one_port(&efm32_uart_reg, &efm_port->port); - if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports)) - efm32_uart_ports[pdev->id] = NULL; + if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports)) + efm32_uart_ports[line] = NULL; kfree(efm_port); return 0; } -static struct of_device_id efm32_uart_dt_ids[] = { +static const struct of_device_id efm32_uart_dt_ids[] = { { .compatible = "efm32,uart", }, { diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c new file mode 100644 index 000000000000..8978dc9a58b7 --- /dev/null +++ b/drivers/tty/serial/fsl_lpuart.c @@ -0,0 +1,879 @@ +/* + * Freescale lpuart serial port driver + * + * Copyright 2012-2013 Freescale Semiconductor, Inc. + * + * 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. + */ + +#if defined(CONFIG_SERIAL_FSL_LPUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/module.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/console.h> +#include <linux/serial_core.h> +#include <linux/tty_flip.h> + +/* All registers are 8-bit width */ +#define UARTBDH 0x00 +#define UARTBDL 0x01 +#define UARTCR1 0x02 +#define UARTCR2 0x03 +#define UARTSR1 0x04 +#define UARTCR3 0x06 +#define UARTDR 0x07 +#define UARTCR4 0x0a +#define UARTCR5 0x0b +#define UARTMODEM 0x0d +#define UARTPFIFO 0x10 +#define UARTCFIFO 0x11 +#define UARTSFIFO 0x12 +#define UARTTWFIFO 0x13 +#define UARTTCFIFO 0x14 +#define UARTRWFIFO 0x15 + +#define UARTBDH_LBKDIE 0x80 +#define UARTBDH_RXEDGIE 0x40 +#define UARTBDH_SBR_MASK 0x1f + +#define UARTCR1_LOOPS 0x80 +#define UARTCR1_RSRC 0x20 +#define UARTCR1_M 0x10 +#define UARTCR1_WAKE 0x08 +#define UARTCR1_ILT 0x04 +#define UARTCR1_PE 0x02 +#define UARTCR1_PT 0x01 + +#define UARTCR2_TIE 0x80 +#define UARTCR2_TCIE 0x40 +#define UARTCR2_RIE 0x20 +#define UARTCR2_ILIE 0x10 +#define UARTCR2_TE 0x08 +#define UARTCR2_RE 0x04 +#define UARTCR2_RWU 0x02 +#define UARTCR2_SBK 0x01 + +#define UARTSR1_TDRE 0x80 +#define UARTSR1_TC 0x40 +#define UARTSR1_RDRF 0x20 +#define UARTSR1_IDLE 0x10 +#define UARTSR1_OR 0x08 +#define UARTSR1_NF 0x04 +#define UARTSR1_FE 0x02 +#define UARTSR1_PE 0x01 + +#define UARTCR3_R8 0x80 +#define UARTCR3_T8 0x40 +#define UARTCR3_TXDIR 0x20 +#define UARTCR3_TXINV 0x10 +#define UARTCR3_ORIE 0x08 +#define UARTCR3_NEIE 0x04 +#define UARTCR3_FEIE 0x02 +#define UARTCR3_PEIE 0x01 + +#define UARTCR4_MAEN1 0x80 +#define UARTCR4_MAEN2 0x40 +#define UARTCR4_M10 0x20 +#define UARTCR4_BRFA_MASK 0x1f +#define UARTCR4_BRFA_OFF 0 + +#define UARTCR5_TDMAS 0x80 +#define UARTCR5_RDMAS 0x20 + +#define UARTMODEM_RXRTSE 0x08 +#define UARTMODEM_TXRTSPOL 0x04 +#define UARTMODEM_TXRTSE 0x02 +#define UARTMODEM_TXCTSE 0x01 + +#define UARTPFIFO_TXFE 0x80 +#define UARTPFIFO_FIFOSIZE_MASK 0x7 +#define UARTPFIFO_TXSIZE_OFF 4 +#define UARTPFIFO_RXFE 0x08 +#define UARTPFIFO_RXSIZE_OFF 0 + +#define UARTCFIFO_TXFLUSH 0x80 +#define UARTCFIFO_RXFLUSH 0x40 +#define UARTCFIFO_RXOFE 0x04 +#define UARTCFIFO_TXOFE 0x02 +#define UARTCFIFO_RXUFE 0x01 + +#define UARTSFIFO_TXEMPT 0x80 +#define UARTSFIFO_RXEMPT 0x40 +#define UARTSFIFO_RXOF 0x04 +#define UARTSFIFO_TXOF 0x02 +#define UARTSFIFO_RXUF 0x01 + +#define DRIVER_NAME "fsl-lpuart" +#define DEV_NAME "ttyLP" +#define UART_NR 6 + +struct lpuart_port { + struct uart_port port; + struct clk *clk; + unsigned int txfifo_size; + unsigned int rxfifo_size; +}; + +static struct of_device_id lpuart_dt_ids[] = { + { + .compatible = "fsl,vf610-lpuart", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, lpuart_dt_ids); + +static void lpuart_stop_tx(struct uart_port *port) +{ + unsigned char temp; + + temp = readb(port->membase + UARTCR2); + temp &= ~(UARTCR2_TIE | UARTCR2_TCIE); + writeb(temp, port->membase + UARTCR2); +} + +static void lpuart_stop_rx(struct uart_port *port) +{ + unsigned char temp; + + temp = readb(port->membase + UARTCR2); + writeb(temp & ~UARTCR2_RE, port->membase + UARTCR2); +} + +static void lpuart_enable_ms(struct uart_port *port) +{ +} + +static inline void lpuart_transmit_buffer(struct lpuart_port *sport) +{ + struct circ_buf *xmit = &sport->port.state->xmit; + + while (!uart_circ_empty(xmit) && + (readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size)) { + writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + + if (uart_circ_empty(xmit)) + lpuart_stop_tx(&sport->port); +} + +static void lpuart_start_tx(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + unsigned char temp; + + temp = readb(port->membase + UARTCR2); + writeb(temp | UARTCR2_TIE, port->membase + UARTCR2); + + if (readb(port->membase + UARTSR1) & UARTSR1_TDRE) + lpuart_transmit_buffer(sport); +} + +static irqreturn_t lpuart_txint(int irq, void *dev_id) +{ + struct lpuart_port *sport = dev_id; + struct circ_buf *xmit = &sport->port.state->xmit; + unsigned long flags; + + spin_lock_irqsave(&sport->port.lock, flags); + if (sport->port.x_char) { + writeb(sport->port.x_char, sport->port.membase + UARTDR); + goto out; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + lpuart_stop_tx(&sport->port); + goto out; + } + + lpuart_transmit_buffer(sport); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + +out: + spin_unlock_irqrestore(&sport->port.lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t lpuart_rxint(int irq, void *dev_id) +{ + struct lpuart_port *sport = dev_id; + unsigned int flg, ignored = 0; + struct tty_port *port = &sport->port.state->port; + unsigned long flags; + unsigned char rx, sr; + + spin_lock_irqsave(&sport->port.lock, flags); + + while (!(readb(sport->port.membase + UARTSFIFO) & UARTSFIFO_RXEMPT)) { + flg = TTY_NORMAL; + sport->port.icount.rx++; + /* + * to clear the FE, OR, NF, FE, PE flags, + * read SR1 then read DR + */ + sr = readb(sport->port.membase + UARTSR1); + rx = readb(sport->port.membase + UARTDR); + + if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) + continue; + + if (sr & (UARTSR1_PE | UARTSR1_OR | UARTSR1_FE)) { + if (sr & UARTSR1_PE) + sport->port.icount.parity++; + else if (sr & UARTSR1_FE) + sport->port.icount.frame++; + + if (sr & UARTSR1_OR) + sport->port.icount.overrun++; + + if (sr & sport->port.ignore_status_mask) { + if (++ignored > 100) + goto out; + continue; + } + + sr &= sport->port.read_status_mask; + + if (sr & UARTSR1_PE) + flg = TTY_PARITY; + else if (sr & UARTSR1_FE) + flg = TTY_FRAME; + + if (sr & UARTSR1_OR) + flg = TTY_OVERRUN; + +#ifdef SUPPORT_SYSRQ + sport->port.sysrq = 0; +#endif + } + + tty_insert_flip_char(port, rx, flg); + } + +out: + spin_unlock_irqrestore(&sport->port.lock, flags); + + tty_flip_buffer_push(port); + return IRQ_HANDLED; +} + +static irqreturn_t lpuart_int(int irq, void *dev_id) +{ + struct lpuart_port *sport = dev_id; + unsigned char sts; + + sts = readb(sport->port.membase + UARTSR1); + + if (sts & UARTSR1_RDRF) + lpuart_rxint(irq, dev_id); + + if (sts & UARTSR1_TDRE && + !(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS)) + lpuart_txint(irq, dev_id); + + return IRQ_HANDLED; +} + +/* return TIOCSER_TEMT when transmitter is not busy */ +static unsigned int lpuart_tx_empty(struct uart_port *port) +{ + return (readb(port->membase + UARTSR1) & UARTSR1_TC) ? + TIOCSER_TEMT : 0; +} + +static unsigned int lpuart_get_mctrl(struct uart_port *port) +{ + unsigned int temp = 0; + unsigned char reg; + + reg = readb(port->membase + UARTMODEM); + if (reg & UARTMODEM_TXCTSE) + temp |= TIOCM_CTS; + + if (reg & UARTMODEM_RXRTSE) + temp |= TIOCM_RTS; + + return temp; +} + +static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + unsigned char temp; + + temp = readb(port->membase + UARTMODEM) & + ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); + + if (mctrl & TIOCM_RTS) + temp |= UARTMODEM_RXRTSE; + + if (mctrl & TIOCM_CTS) + temp |= UARTMODEM_TXCTSE; + + writeb(temp, port->membase + UARTMODEM); +} + +static void lpuart_break_ctl(struct uart_port *port, int break_state) +{ + unsigned char temp; + + temp = readb(port->membase + UARTCR2) & ~UARTCR2_SBK; + + if (break_state != 0) + temp |= UARTCR2_SBK; + + writeb(temp, port->membase + UARTCR2); +} + +static void lpuart_setup_watermark(struct lpuart_port *sport) +{ + unsigned char val, cr2; + unsigned char cr2_saved; + + cr2 = readb(sport->port.membase + UARTCR2); + cr2_saved = cr2; + cr2 &= ~(UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_TE | + UARTCR2_RIE | UARTCR2_RE); + writeb(cr2, sport->port.membase + UARTCR2); + + /* determine FIFO size and enable FIFO mode */ + val = readb(sport->port.membase + UARTPFIFO); + + sport->txfifo_size = 0x1 << (((val >> UARTPFIFO_TXSIZE_OFF) & + UARTPFIFO_FIFOSIZE_MASK) + 1); + + sport->rxfifo_size = 0x1 << (((val >> UARTPFIFO_RXSIZE_OFF) & + UARTPFIFO_FIFOSIZE_MASK) + 1); + + writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE, + sport->port.membase + UARTPFIFO); + + /* flush Tx and Rx FIFO */ + writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH, + sport->port.membase + UARTCFIFO); + + writeb(2, sport->port.membase + UARTTWFIFO); + writeb(1, sport->port.membase + UARTRWFIFO); + + /* Restore cr2 */ + writeb(cr2_saved, sport->port.membase + UARTCR2); +} + +static int lpuart_startup(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + int ret; + unsigned long flags; + unsigned char temp; + + ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0, + DRIVER_NAME, sport); + if (ret) + return ret; + + spin_lock_irqsave(&sport->port.lock, flags); + + lpuart_setup_watermark(sport); + + temp = readb(sport->port.membase + UARTCR2); + temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE); + writeb(temp, sport->port.membase + UARTCR2); + + spin_unlock_irqrestore(&sport->port.lock, flags); + return 0; +} + +static void lpuart_shutdown(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + unsigned char temp; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* disable Rx/Tx and interrupts */ + temp = readb(port->membase + UARTCR2); + temp &= ~(UARTCR2_TE | UARTCR2_RE | + UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_RIE); + writeb(temp, port->membase + UARTCR2); + + spin_unlock_irqrestore(&port->lock, flags); + + devm_free_irq(port->dev, port->irq, sport); +} + +static void +lpuart_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + unsigned long flags; + unsigned char cr1, old_cr1, old_cr2, cr4, bdh, modem; + unsigned int baud; + unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; + unsigned int sbr, brfa; + + cr1 = old_cr1 = readb(sport->port.membase + UARTCR1); + old_cr2 = readb(sport->port.membase + UARTCR2); + cr4 = readb(sport->port.membase + UARTCR4); + bdh = readb(sport->port.membase + UARTBDH); + modem = readb(sport->port.membase + UARTMODEM); + /* + * only support CS8 and CS7, and for CS7 must enable PE. + * supported mode: + * - (7,e/o,1) + * - (8,n,1) + * - (8,m/s,1) + * - (8,e/o,1) + */ + while ((termios->c_cflag & CSIZE) != CS8 && + (termios->c_cflag & CSIZE) != CS7) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= old_csize; + old_csize = CS8; + } + + if ((termios->c_cflag & CSIZE) == CS8 || + (termios->c_cflag & CSIZE) == CS7) + cr1 = old_cr1 & ~UARTCR1_M; + + if (termios->c_cflag & CMSPAR) { + if ((termios->c_cflag & CSIZE) != CS8) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS8; + } + cr1 |= UARTCR1_M; + } + + if (termios->c_cflag & CRTSCTS) { + modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); + } else { + termios->c_cflag &= ~CRTSCTS; + modem &= ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); + } + + if (termios->c_cflag & CSTOPB) + termios->c_cflag &= ~CSTOPB; + + /* parity must be enabled when CS7 to match 8-bits format */ + if ((termios->c_cflag & CSIZE) == CS7) + termios->c_cflag |= PARENB; + + if ((termios->c_cflag & PARENB)) { + if (termios->c_cflag & CMSPAR) { + cr1 &= ~UARTCR1_PE; + cr1 |= UARTCR1_M; + } else { + cr1 |= UARTCR1_PE; + if ((termios->c_cflag & CSIZE) == CS8) + cr1 |= UARTCR1_M; + if (termios->c_cflag & PARODD) + cr1 |= UARTCR1_PT; + else + cr1 &= ~UARTCR1_PT; + } + } + + /* ask the core to calculate the divisor */ + baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); + + spin_lock_irqsave(&sport->port.lock, flags); + + sport->port.read_status_mask = 0; + if (termios->c_iflag & INPCK) + sport->port.read_status_mask |= (UARTSR1_FE | UARTSR1_PE); + if (termios->c_iflag & (BRKINT | PARMRK)) + sport->port.read_status_mask |= UARTSR1_FE; + + /* characters to ignore */ + sport->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= UARTSR1_PE; + if (termios->c_iflag & IGNBRK) { + sport->port.ignore_status_mask |= UARTSR1_FE; + /* + * if we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= UARTSR1_OR; + } + + /* update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* wait transmit engin complete */ + while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC)) + barrier(); + + /* disable transmit and receive */ + writeb(old_cr2 & ~(UARTCR2_TE | UARTCR2_RE), + sport->port.membase + UARTCR2); + + sbr = sport->port.uartclk / (16 * baud); + brfa = ((sport->port.uartclk - (16 * sbr * baud)) * 2) / baud; + bdh &= ~UARTBDH_SBR_MASK; + bdh |= (sbr >> 8) & 0x1F; + cr4 &= ~UARTCR4_BRFA_MASK; + brfa &= UARTCR4_BRFA_MASK; + writeb(cr4 | brfa, sport->port.membase + UARTCR4); + writeb(bdh, sport->port.membase + UARTBDH); + writeb(sbr & 0xFF, sport->port.membase + UARTBDL); + writeb(cr1, sport->port.membase + UARTCR1); + writeb(modem, sport->port.membase + UARTMODEM); + + /* restore control register */ + writeb(old_cr2, sport->port.membase + UARTCR2); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static const char *lpuart_type(struct uart_port *port) +{ + return "FSL_LPUART"; +} + +static void lpuart_release_port(struct uart_port *port) +{ + /* nothing to do */ +} + +static int lpuart_request_port(struct uart_port *port) +{ + return 0; +} + +/* configure/autoconfigure the port */ +static void lpuart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_LPUART; +} + +static int lpuart_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_LPUART) + ret = -EINVAL; + if (port->irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != UPIO_MEM) + ret = -EINVAL; + if (port->uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if (port->iobase != ser->port) + ret = -EINVAL; + if (ser->hub6 != 0) + ret = -EINVAL; + return ret; +} + +static struct uart_ops lpuart_pops = { + .tx_empty = lpuart_tx_empty, + .set_mctrl = lpuart_set_mctrl, + .get_mctrl = lpuart_get_mctrl, + .stop_tx = lpuart_stop_tx, + .start_tx = lpuart_start_tx, + .stop_rx = lpuart_stop_rx, + .enable_ms = lpuart_enable_ms, + .break_ctl = lpuart_break_ctl, + .startup = lpuart_startup, + .shutdown = lpuart_shutdown, + .set_termios = lpuart_set_termios, + .type = lpuart_type, + .request_port = lpuart_request_port, + .release_port = lpuart_release_port, + .config_port = lpuart_config_port, + .verify_port = lpuart_verify_port, +}; + +static struct lpuart_port *lpuart_ports[UART_NR]; + +#ifdef CONFIG_SERIAL_FSL_LPUART_CONSOLE +static void lpuart_console_putchar(struct uart_port *port, int ch) +{ + while (!(readb(port->membase + UARTSR1) & UARTSR1_TDRE)) + barrier(); + + writeb(ch, port->membase + UARTDR); +} + +static void +lpuart_console_write(struct console *co, const char *s, unsigned int count) +{ + struct lpuart_port *sport = lpuart_ports[co->index]; + unsigned char old_cr2, cr2; + + /* first save CR2 and then disable interrupts */ + cr2 = old_cr2 = readb(sport->port.membase + UARTCR2); + cr2 |= (UARTCR2_TE | UARTCR2_RE); + cr2 &= ~(UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_RIE); + writeb(cr2, sport->port.membase + UARTCR2); + + uart_console_write(&sport->port, s, count, lpuart_console_putchar); + + /* wait for transmitter finish complete and restore CR2 */ + while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC)) + barrier(); + + writeb(old_cr2, sport->port.membase + UARTCR2); +} + +/* + * if the port was already initialised (eg, by a boot loader), + * try to determine the current setup. + */ +static void __init +lpuart_console_get_options(struct lpuart_port *sport, int *baud, + int *parity, int *bits) +{ + unsigned char cr, bdh, bdl, brfa; + unsigned int sbr, uartclk, baud_raw; + + cr = readb(sport->port.membase + UARTCR2); + cr &= UARTCR2_TE | UARTCR2_RE; + if (!cr) + return; + + /* ok, the port was enabled */ + + cr = readb(sport->port.membase + UARTCR1); + + *parity = 'n'; + if (cr & UARTCR1_PE) { + if (cr & UARTCR1_PT) + *parity = 'o'; + else + *parity = 'e'; + } + + if (cr & UARTCR1_M) + *bits = 9; + else + *bits = 8; + + bdh = readb(sport->port.membase + UARTBDH); + bdh &= UARTBDH_SBR_MASK; + bdl = readb(sport->port.membase + UARTBDL); + sbr = bdh; + sbr <<= 8; + sbr |= bdl; + brfa = readb(sport->port.membase + UARTCR4); + brfa &= UARTCR4_BRFA_MASK; + + uartclk = clk_get_rate(sport->clk); + /* + * baud = mod_clk/(16*(sbr[13]+(brfa)/32) + */ + baud_raw = uartclk / (16 * (sbr + brfa / 32)); + + if (*baud != baud_raw) + printk(KERN_INFO "Serial: Console lpuart rounded baud rate" + "from %d to %d\n", baud_raw, *baud); +} + +static int __init lpuart_console_setup(struct console *co, char *options) +{ + struct lpuart_port *sport; + int baud = 115200; + 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. + */ + if (co->index == -1 || co->index >= ARRAY_SIZE(lpuart_ports)) + co->index = 0; + + sport = lpuart_ports[co->index]; + if (sport == NULL) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + lpuart_console_get_options(sport, &baud, &parity, &bits); + + lpuart_setup_watermark(sport); + + return uart_set_options(&sport->port, co, baud, parity, bits, flow); +} + +static struct uart_driver lpuart_reg; +static struct console lpuart_console = { + .name = DEV_NAME, + .write = lpuart_console_write, + .device = uart_console_device, + .setup = lpuart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &lpuart_reg, +}; + +#define LPUART_CONSOLE (&lpuart_console) +#else +#define LPUART_CONSOLE NULL +#endif + +static struct uart_driver lpuart_reg = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = DEV_NAME, + .nr = ARRAY_SIZE(lpuart_ports), + .cons = LPUART_CONSOLE, +}; + +static int lpuart_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct lpuart_port *sport; + struct resource *res; + int ret; + + sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); + if (!sport) + return -ENOMEM; + + pdev->dev.coherent_dma_mask = 0; + + ret = of_alias_get_id(np, "serial"); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); + return ret; + } + sport->port.line = ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + sport->port.mapbase = res->start; + sport->port.membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sport->port.membase)) + return PTR_ERR(sport->port.membase); + + sport->port.dev = &pdev->dev; + sport->port.type = PORT_LPUART; + sport->port.iotype = UPIO_MEM; + sport->port.irq = platform_get_irq(pdev, 0); + sport->port.ops = &lpuart_pops; + sport->port.flags = UPF_BOOT_AUTOCONF; + + sport->clk = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(sport->clk)) { + ret = PTR_ERR(sport->clk); + dev_err(&pdev->dev, "failed to get uart clk: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(sport->clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable uart clk: %d\n", ret); + return ret; + } + + sport->port.uartclk = clk_get_rate(sport->clk); + + lpuart_ports[sport->port.line] = sport; + + platform_set_drvdata(pdev, &sport->port); + + ret = uart_add_one_port(&lpuart_reg, &sport->port); + if (ret) { + clk_disable_unprepare(sport->clk); + return ret; + } + + return 0; +} + +static int lpuart_remove(struct platform_device *pdev) +{ + struct lpuart_port *sport = platform_get_drvdata(pdev); + + uart_remove_one_port(&lpuart_reg, &sport->port); + + clk_disable_unprepare(sport->clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int lpuart_suspend(struct device *dev) +{ + struct lpuart_port *sport = dev_get_drvdata(dev); + + uart_suspend_port(&lpuart_reg, &sport->port); + + return 0; +} + +static int lpuart_resume(struct device *dev) +{ + struct lpuart_port *sport = dev_get_drvdata(dev); + + uart_resume_port(&lpuart_reg, &sport->port); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(lpuart_pm_ops, lpuart_suspend, lpuart_resume); + +static struct platform_driver lpuart_driver = { + .probe = lpuart_probe, + .remove = lpuart_remove, + .driver = { + .name = "fsl-lpuart", + .owner = THIS_MODULE, + .of_match_table = lpuart_dt_ids, + .pm = &lpuart_pm_ops, + }, +}; + +static int __init lpuart_serial_init(void) +{ + int ret; + + pr_info("serial: Freescale lpuart driver\n"); + + ret = uart_register_driver(&lpuart_reg); + if (ret) + return ret; + + ret = platform_driver_register(&lpuart_driver); + if (ret) + uart_unregister_driver(&lpuart_reg); + + return ret; +} + +static void __exit lpuart_serial_exit(void) +{ + platform_driver_unregister(&lpuart_driver); + uart_unregister_driver(&lpuart_reg); +} + +module_init(lpuart_serial_init); +module_exit(lpuart_serial_exit); + +MODULE_DESCRIPTION("Freescale lpuart serial port driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index 18ed5aebb166..d98e43348970 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -105,7 +105,7 @@ static const struct pci_device_id icom_pci_table[] = { {} }; -struct lookup_proc_table start_proc[4] = { +static struct lookup_proc_table start_proc[4] = { {NULL, ICOM_CONTROL_START_A}, {NULL, ICOM_CONTROL_START_B}, {NULL, ICOM_CONTROL_START_C}, @@ -113,14 +113,14 @@ struct lookup_proc_table start_proc[4] = { }; -struct lookup_proc_table stop_proc[4] = { +static struct lookup_proc_table stop_proc[4] = { {NULL, ICOM_CONTROL_STOP_A}, {NULL, ICOM_CONTROL_STOP_B}, {NULL, ICOM_CONTROL_STOP_C}, {NULL, ICOM_CONTROL_STOP_D} }; -struct lookup_int_table int_mask_tbl[4] = { +static struct lookup_int_table int_mask_tbl[4] = { {NULL, ICOM_INT_MASK_PRC_A}, {NULL, ICOM_INT_MASK_PRC_B}, {NULL, ICOM_INT_MASK_PRC_C}, @@ -297,25 +297,25 @@ static void stop_processor(struct icom_port *icom_port) spin_lock_irqsave(&icom_lock, flags); port = icom_port->port; + if (port >= ARRAY_SIZE(stop_proc)) { + dev_err(&icom_port->adapter->pci_dev->dev, + "Invalid port assignment\n"); + goto unlock; + } + if (port == 0 || port == 1) stop_proc[port].global_control_reg = &icom_port->global_reg->control; else stop_proc[port].global_control_reg = &icom_port->global_reg->control_2; + temp = readl(stop_proc[port].global_control_reg); + temp = (temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id; + writel(temp, stop_proc[port].global_control_reg); - if (port < 4) { - temp = readl(stop_proc[port].global_control_reg); - temp = - (temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id; - writel(temp, stop_proc[port].global_control_reg); - - /* write flush */ - readl(stop_proc[port].global_control_reg); - } else { - dev_err(&icom_port->adapter->pci_dev->dev, - "Invalid port assignment\n"); - } + /* write flush */ + readl(stop_proc[port].global_control_reg); +unlock: spin_unlock_irqrestore(&icom_lock, flags); } @@ -328,23 +328,25 @@ static void start_processor(struct icom_port *icom_port) spin_lock_irqsave(&icom_lock, flags); port = icom_port->port; + if (port >= ARRAY_SIZE(start_proc)) { + dev_err(&icom_port->adapter->pci_dev->dev, + "Invalid port assignment\n"); + goto unlock; + } + if (port == 0 || port == 1) start_proc[port].global_control_reg = &icom_port->global_reg->control; else start_proc[port].global_control_reg = &icom_port->global_reg->control_2; - if (port < 4) { - temp = readl(start_proc[port].global_control_reg); - temp = - (temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id; - writel(temp, start_proc[port].global_control_reg); - /* write flush */ - readl(start_proc[port].global_control_reg); - } else { - dev_err(&icom_port->adapter->pci_dev->dev, - "Invalid port assignment\n"); - } + temp = readl(start_proc[port].global_control_reg); + temp = (temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id; + writel(temp, start_proc[port].global_control_reg); + + /* write flush */ + readl(start_proc[port].global_control_reg); +unlock: spin_unlock_irqrestore(&icom_lock, flags); } @@ -557,6 +559,12 @@ static int startup(struct icom_port *icom_port) */ spin_lock_irqsave(&icom_lock, flags); port = icom_port->port; + if (port >= ARRAY_SIZE(int_mask_tbl)) { + dev_err(&icom_port->adapter->pci_dev->dev, + "Invalid port assignment\n"); + goto unlock; + } + if (port == 0 || port == 1) int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask; else @@ -566,17 +574,14 @@ static int startup(struct icom_port *icom_port) writew(0x00FF, icom_port->int_reg); else writew(0x3F00, icom_port->int_reg); - if (port < 4) { - temp = readl(int_mask_tbl[port].global_int_mask); - writel(temp & ~int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask); - /* write flush */ - readl(int_mask_tbl[port].global_int_mask); - } else { - dev_err(&icom_port->adapter->pci_dev->dev, - "Invalid port assignment\n"); - } + temp = readl(int_mask_tbl[port].global_int_mask); + writel(temp & ~int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask); + /* write flush */ + readl(int_mask_tbl[port].global_int_mask); + +unlock: spin_unlock_irqrestore(&icom_lock, flags); return 0; } @@ -595,21 +600,23 @@ static void shutdown(struct icom_port *icom_port) * disable all interrupts */ port = icom_port->port; + if (port >= ARRAY_SIZE(int_mask_tbl)) { + dev_err(&icom_port->adapter->pci_dev->dev, + "Invalid port assignment\n"); + goto unlock; + } if (port == 0 || port == 1) int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask; else int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2; - if (port < 4) { - temp = readl(int_mask_tbl[port].global_int_mask); - writel(temp | int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask); + temp = readl(int_mask_tbl[port].global_int_mask); + writel(temp | int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask); - /* write flush */ - readl(int_mask_tbl[port].global_int_mask); - } else { - dev_err(&icom_port->adapter->pci_dev->dev, - "Invalid port assignment\n"); - } + /* write flush */ + readl(int_mask_tbl[port].global_int_mask); + +unlock: spin_unlock_irqrestore(&icom_lock, flags); /* @@ -834,7 +841,10 @@ ignore_char: status = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].flags); } icom_port->next_rcv = rcv_buff; + + spin_unlock(&icom_port->uart_port.lock); tty_flip_buffer_push(port); + spin_lock(&icom_port->uart_port.lock); } static void process_interrupt(u16 port_int_reg, @@ -1087,8 +1097,7 @@ static void icom_close(struct uart_port *port) /* stop receiver */ cmdReg = readb(&ICOM_PORT->dram->CmdReg); - writeb(cmdReg & (unsigned char) ~CMD_RCV_ENABLE, - &ICOM_PORT->dram->CmdReg); + writeb(cmdReg & ~CMD_RCV_ENABLE, &ICOM_PORT->dram->CmdReg); shutdown(ICOM_PORT); @@ -1567,7 +1576,7 @@ static int icom_probe(struct pci_dev *dev, icom_port->uart_port.type = PORT_ICOM; icom_port->uart_port.iotype = UPIO_MEM; icom_port->uart_port.membase = - (char *) icom_adapter->base_addr_pci; + (unsigned char __iomem *)icom_adapter->base_addr_pci; icom_port->uart_port.fifosize = 16; icom_port->uart_port.ops = &icom_ops; icom_port->uart_port.line = diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 8b1534c424af..af286e6713eb 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -1008,7 +1008,7 @@ static int ifx_spi_spi_probe(struct spi_device *spi) return -ENODEV; } - pl_data = (struct ifx_modem_platform_data *)spi->dev.platform_data; + pl_data = (struct ifx_modem_platform_data *)dev_get_platdata(&spi->dev); if (!pl_data) { dev_err(&spi->dev, "missing platform data!"); return -ENODEV; diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 8cdfbd365892..a0ebbc9ce5cd 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -47,11 +47,12 @@ #include <linux/slab.h> #include <linux/of.h> #include <linux/of_device.h> -#include <linux/pinctrl/consumer.h> #include <linux/io.h> +#include <linux/dma-mapping.h> #include <asm/irq.h> #include <linux/platform_data/serial-imx.h> +#include <linux/platform_data/dma-imx.h> /* Register definitions */ #define URXD0 0x0 /* Receiver Register */ @@ -83,6 +84,7 @@ #define UCR1_ADBR (1<<14) /* Auto detect baud rate */ #define UCR1_TRDYEN (1<<13) /* Transmitter ready interrupt enable */ #define UCR1_IDEN (1<<12) /* Idle condition interrupt */ +#define UCR1_ICD_REG(x) (((x) & 3) << 10) /* idle condition detect */ #define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */ #define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */ #define UCR1_IREN (1<<7) /* Infrared interface enable */ @@ -91,6 +93,7 @@ #define UCR1_SNDBRK (1<<4) /* Send break */ #define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */ #define IMX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, i.mx1 only */ +#define UCR1_ATDMAEN (1<<2) /* Aging DMA Timer Enable */ #define UCR1_DOZE (1<<1) /* Doze */ #define UCR1_UARTEN (1<<0) /* UART enabled */ #define UCR2_ESCI (1<<15) /* Escape seq interrupt enable */ @@ -126,6 +129,7 @@ #define UCR4_ENIRI (1<<8) /* Serial infrared interrupt enable */ #define UCR4_WKEN (1<<7) /* Wake interrupt enable */ #define UCR4_REF16 (1<<6) /* Ref freq 16 MHz */ +#define UCR4_IDDMAEN (1<<6) /* DMA IDLE Condition Detected */ #define UCR4_IRSC (1<<5) /* IR special case */ #define UCR4_TCEN (1<<3) /* Transmit complete interrupt enable */ #define UCR4_BKEN (1<<2) /* Break condition interrupt enable */ @@ -187,6 +191,7 @@ enum imx_uart_type { IMX1_UART, IMX21_UART, + IMX6Q_UART, }; /* device type dependent stuff */ @@ -201,6 +206,7 @@ struct imx_port { unsigned int old_status; int txirq, rxirq, rtsirq; unsigned int have_rtscts:1; + unsigned int dte_mode:1; unsigned int use_irda:1; unsigned int irda_inv_rx:1; unsigned int irda_inv_tx:1; @@ -208,6 +214,19 @@ struct imx_port { struct clk *clk_ipg; struct clk *clk_per; const struct imx_uart_data *devdata; + + /* DMA fields */ + unsigned int dma_is_inited:1; + unsigned int dma_is_enabled:1; + unsigned int dma_is_rxing:1; + unsigned int dma_is_txing:1; + struct dma_chan *dma_chan_rx, *dma_chan_tx; + struct scatterlist rx_sgl, tx_sgl[2]; + void *rx_buf; + unsigned int rx_bytes, tx_bytes; + struct work_struct tsk_dma_rx, tsk_dma_tx; + unsigned int dma_tx_nents; + wait_queue_head_t dma_wait; }; struct imx_port_ucrs { @@ -231,6 +250,10 @@ static struct imx_uart_data imx_uart_devdata[] = { .uts_reg = IMX21_UTS, .devtype = IMX21_UART, }, + [IMX6Q_UART] = { + .uts_reg = IMX21_UTS, + .devtype = IMX6Q_UART, + }, }; static struct platform_device_id imx_uart_devtype[] = { @@ -241,12 +264,16 @@ static struct platform_device_id imx_uart_devtype[] = { .name = "imx21-uart", .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX21_UART], }, { + .name = "imx6q-uart", + .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX6Q_UART], + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, imx_uart_devtype); static struct of_device_id imx_uart_dt_ids[] = { + { .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], }, { .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], }, { .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], }, { /* sentinel */ } @@ -268,9 +295,14 @@ static inline int is_imx21_uart(struct imx_port *sport) return sport->devdata->devtype == IMX21_UART; } +static inline int is_imx6q_uart(struct imx_port *sport) +{ + return sport->devdata->devtype == IMX6Q_UART; +} /* * Save and restore functions for UCR1, UCR2 and UCR3 registers */ +#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_IMX_CONSOLE) static void imx_port_ucrs_save(struct uart_port *port, struct imx_port_ucrs *ucr) { @@ -288,6 +320,7 @@ static void imx_port_ucrs_restore(struct uart_port *port, writel(ucr->ucr2, port->membase + UCR2); writel(ucr->ucr3, port->membase + UCR3); } +#endif /* * Handle any change of modem status signal since we were last called. @@ -384,6 +417,13 @@ static void imx_stop_tx(struct uart_port *port) return; } + /* + * We are maybe in the SMP context, so if the DMA TX thread is running + * on other cpu, we have to wait for it to finish. + */ + if (sport->dma_is_enabled && sport->dma_is_txing) + return; + temp = readl(sport->port.membase + UCR1); writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1); } @@ -396,6 +436,13 @@ static void imx_stop_rx(struct uart_port *port) struct imx_port *sport = (struct imx_port *)port; unsigned long temp; + /* + * We are maybe in the SMP context, so if the DMA TX thread is running + * on other cpu, we have to wait for it to finish. + */ + if (sport->dma_is_enabled && sport->dma_is_rxing) + return; + temp = readl(sport->port.membase + UCR2); writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2); } @@ -431,6 +478,95 @@ static inline void imx_transmit_buffer(struct imx_port *sport) imx_stop_tx(&sport->port); } +static void dma_tx_callback(void *data) +{ + struct imx_port *sport = data; + struct scatterlist *sgl = &sport->tx_sgl[0]; + struct circ_buf *xmit = &sport->port.state->xmit; + unsigned long flags; + + dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE); + + sport->dma_is_txing = 0; + + /* update the stat */ + spin_lock_irqsave(&sport->port.lock, flags); + xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx += sport->tx_bytes; + spin_unlock_irqrestore(&sport->port.lock, flags); + + dev_dbg(sport->port.dev, "we finish the TX DMA.\n"); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + + if (waitqueue_active(&sport->dma_wait)) { + wake_up(&sport->dma_wait); + dev_dbg(sport->port.dev, "exit in %s.\n", __func__); + return; + } + + schedule_work(&sport->tsk_dma_tx); +} + +static void dma_tx_work(struct work_struct *w) +{ + struct imx_port *sport = container_of(w, struct imx_port, tsk_dma_tx); + struct circ_buf *xmit = &sport->port.state->xmit; + struct scatterlist *sgl = sport->tx_sgl; + struct dma_async_tx_descriptor *desc; + struct dma_chan *chan = sport->dma_chan_tx; + struct device *dev = sport->port.dev; + enum dma_status status; + unsigned long flags; + int ret; + + status = chan->device->device_tx_status(chan, (dma_cookie_t)0, NULL); + if (DMA_IN_PROGRESS == status) + return; + + spin_lock_irqsave(&sport->port.lock, flags); + sport->tx_bytes = uart_circ_chars_pending(xmit); + if (sport->tx_bytes == 0) { + spin_unlock_irqrestore(&sport->port.lock, flags); + return; + } + + if (xmit->tail > xmit->head) { + sport->dma_tx_nents = 2; + sg_init_table(sgl, 2); + sg_set_buf(sgl, xmit->buf + xmit->tail, + UART_XMIT_SIZE - xmit->tail); + sg_set_buf(sgl + 1, xmit->buf, xmit->head); + } else { + sport->dma_tx_nents = 1; + sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes); + } + spin_unlock_irqrestore(&sport->port.lock, flags); + + ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE); + if (ret == 0) { + dev_err(dev, "DMA mapping error for TX.\n"); + return; + } + desc = dmaengine_prep_slave_sg(chan, sgl, sport->dma_tx_nents, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); + if (!desc) { + dev_err(dev, "We cannot prepare for the TX slave dma!\n"); + return; + } + desc->callback = dma_tx_callback; + desc->callback_param = sport; + + dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n", + uart_circ_chars_pending(xmit)); + /* fire it */ + sport->dma_is_txing = 1; + dmaengine_submit(desc); + dma_async_issue_pending(chan); + return; +} + /* * interrupts disabled on entry */ @@ -449,9 +585,18 @@ static void imx_start_tx(struct uart_port *port) temp &= ~(UCR1_RRDYEN); writel(temp, sport->port.membase + UCR1); } + /* Clear any pending ORE flag before enabling interrupt */ + temp = readl(sport->port.membase + USR2); + writel(temp | USR2_ORE, sport->port.membase + USR2); - temp = readl(sport->port.membase + UCR1); - writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1); + temp = readl(sport->port.membase + UCR4); + temp |= UCR4_OREN; + writel(temp, sport->port.membase + UCR4); + + if (!sport->dma_is_enabled) { + temp = readl(sport->port.membase + UCR1); + writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1); + } if (USE_IRDA(sport)) { temp = readl(sport->port.membase + UCR1); @@ -463,6 +608,15 @@ static void imx_start_tx(struct uart_port *port) writel(temp, sport->port.membase + UCR4); } + if (sport->dma_is_enabled) { + /* + * We may in the interrupt context, so arise a work_struct to + * do the real job. + */ + schedule_work(&sport->tsk_dma_tx); + return; + } + if (readl(sport->port.membase + uts_reg(sport)) & UTS_TXEMPTY) imx_transmit_buffer(sport); } @@ -578,15 +732,42 @@ out: return IRQ_HANDLED; } +/* + * If the RXFIFO is filled with some data, and then we + * arise a DMA operation to receive them. + */ +static void imx_dma_rxint(struct imx_port *sport) +{ + unsigned long temp; + + temp = readl(sport->port.membase + USR2); + if ((temp & USR2_RDR) && !sport->dma_is_rxing) { + sport->dma_is_rxing = 1; + + /* disable the `Recerver Ready Interrrupt` */ + temp = readl(sport->port.membase + UCR1); + temp &= ~(UCR1_RRDYEN); + writel(temp, sport->port.membase + UCR1); + + /* tell the DMA to receive the data. */ + schedule_work(&sport->tsk_dma_rx); + } +} + static irqreturn_t imx_int(int irq, void *dev_id) { struct imx_port *sport = dev_id; unsigned int sts; + unsigned int sts2; sts = readl(sport->port.membase + USR1); - if (sts & USR1_RRDY) - imx_rxint(irq, dev_id); + if (sts & USR1_RRDY) { + if (sport->dma_is_enabled) + imx_dma_rxint(sport); + else + imx_rxint(irq, dev_id); + } if (sts & USR1_TRDY && readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) @@ -598,6 +779,13 @@ static irqreturn_t imx_int(int irq, void *dev_id) if (sts & USR1_AWAKE) writel(USR1_AWAKE, sport->port.membase + USR1); + sts2 = readl(sport->port.membase + USR2); + if (sts2 & USR2_ORE) { + dev_err(sport->port.dev, "Rx FIFO overrun\n"); + sport->port.icount.overrun++; + writel(sts2 | USR2_ORE, sport->port.membase + USR2); + } + return IRQ_HANDLED; } @@ -636,7 +824,8 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl) temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS; if (mctrl & TIOCM_RTS) - temp |= UCR2_CTS; + if (!sport->dma_is_enabled) + temp |= UCR2_CTS; writel(temp, sport->port.membase + UCR2); } @@ -675,6 +864,226 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode) return 0; } +#define RX_BUF_SIZE (PAGE_SIZE) +static int start_rx_dma(struct imx_port *sport); +static void dma_rx_work(struct work_struct *w) +{ + struct imx_port *sport = container_of(w, struct imx_port, tsk_dma_rx); + struct tty_port *port = &sport->port.state->port; + + if (sport->rx_bytes) { + tty_insert_flip_string(port, sport->rx_buf, sport->rx_bytes); + tty_flip_buffer_push(port); + sport->rx_bytes = 0; + } + + if (sport->dma_is_rxing) + start_rx_dma(sport); +} + +static void imx_rx_dma_done(struct imx_port *sport) +{ + unsigned long temp; + + /* Enable this interrupt when the RXFIFO is empty. */ + temp = readl(sport->port.membase + UCR1); + temp |= UCR1_RRDYEN; + writel(temp, sport->port.membase + UCR1); + + sport->dma_is_rxing = 0; + + /* Is the shutdown waiting for us? */ + if (waitqueue_active(&sport->dma_wait)) + wake_up(&sport->dma_wait); +} + +/* + * There are three kinds of RX DMA interrupts(such as in the MX6Q): + * [1] the RX DMA buffer is full. + * [2] the Aging timer expires(wait for 8 bytes long) + * [3] the Idle Condition Detect(enabled the UCR4_IDDMAEN). + * + * The [2] is trigger when a character was been sitting in the FIFO + * meanwhile [3] can wait for 32 bytes long when the RX line is + * on IDLE state and RxFIFO is empty. + */ +static void dma_rx_callback(void *data) +{ + struct imx_port *sport = data; + struct dma_chan *chan = sport->dma_chan_rx; + struct scatterlist *sgl = &sport->rx_sgl; + struct dma_tx_state state; + enum dma_status status; + unsigned int count; + + /* unmap it first */ + dma_unmap_sg(sport->port.dev, sgl, 1, DMA_FROM_DEVICE); + + status = chan->device->device_tx_status(chan, (dma_cookie_t)0, &state); + count = RX_BUF_SIZE - state.residue; + dev_dbg(sport->port.dev, "We get %d bytes.\n", count); + + if (count) { + sport->rx_bytes = count; + schedule_work(&sport->tsk_dma_rx); + } else + imx_rx_dma_done(sport); +} + +static int start_rx_dma(struct imx_port *sport) +{ + struct scatterlist *sgl = &sport->rx_sgl; + struct dma_chan *chan = sport->dma_chan_rx; + struct device *dev = sport->port.dev; + struct dma_async_tx_descriptor *desc; + int ret; + + sg_init_one(sgl, sport->rx_buf, RX_BUF_SIZE); + ret = dma_map_sg(dev, sgl, 1, DMA_FROM_DEVICE); + if (ret == 0) { + dev_err(dev, "DMA mapping error for RX.\n"); + return -EINVAL; + } + desc = dmaengine_prep_slave_sg(chan, sgl, 1, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!desc) { + dev_err(dev, "We cannot prepare for the RX slave dma!\n"); + return -EINVAL; + } + desc->callback = dma_rx_callback; + desc->callback_param = sport; + + dev_dbg(dev, "RX: prepare for the DMA.\n"); + dmaengine_submit(desc); + dma_async_issue_pending(chan); + return 0; +} + +static void imx_uart_dma_exit(struct imx_port *sport) +{ + if (sport->dma_chan_rx) { + dma_release_channel(sport->dma_chan_rx); + sport->dma_chan_rx = NULL; + + kfree(sport->rx_buf); + sport->rx_buf = NULL; + } + + if (sport->dma_chan_tx) { + dma_release_channel(sport->dma_chan_tx); + sport->dma_chan_tx = NULL; + } + + sport->dma_is_inited = 0; +} + +static int imx_uart_dma_init(struct imx_port *sport) +{ + struct dma_slave_config slave_config = {}; + struct device *dev = sport->port.dev; + int ret; + + /* Prepare for RX : */ + sport->dma_chan_rx = dma_request_slave_channel(dev, "rx"); + if (!sport->dma_chan_rx) { + dev_dbg(dev, "cannot get the DMA channel.\n"); + ret = -EINVAL; + goto err; + } + + slave_config.direction = DMA_DEV_TO_MEM; + slave_config.src_addr = sport->port.mapbase + URXD0; + slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + slave_config.src_maxburst = RXTL; + ret = dmaengine_slave_config(sport->dma_chan_rx, &slave_config); + if (ret) { + dev_err(dev, "error in RX dma configuration.\n"); + goto err; + } + + sport->rx_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!sport->rx_buf) { + dev_err(dev, "cannot alloc DMA buffer.\n"); + ret = -ENOMEM; + goto err; + } + sport->rx_bytes = 0; + + /* Prepare for TX : */ + sport->dma_chan_tx = dma_request_slave_channel(dev, "tx"); + if (!sport->dma_chan_tx) { + dev_err(dev, "cannot get the TX DMA channel!\n"); + ret = -EINVAL; + goto err; + } + + slave_config.direction = DMA_MEM_TO_DEV; + slave_config.dst_addr = sport->port.mapbase + URTX0; + slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + slave_config.dst_maxburst = TXTL; + ret = dmaengine_slave_config(sport->dma_chan_tx, &slave_config); + if (ret) { + dev_err(dev, "error in TX dma configuration."); + goto err; + } + + sport->dma_is_inited = 1; + + return 0; +err: + imx_uart_dma_exit(sport); + return ret; +} + +static void imx_enable_dma(struct imx_port *sport) +{ + unsigned long temp; + struct tty_port *port = &sport->port.state->port; + + port->low_latency = 1; + INIT_WORK(&sport->tsk_dma_tx, dma_tx_work); + INIT_WORK(&sport->tsk_dma_rx, dma_rx_work); + init_waitqueue_head(&sport->dma_wait); + + /* set UCR1 */ + temp = readl(sport->port.membase + UCR1); + temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN | + /* wait for 32 idle frames for IDDMA interrupt */ + UCR1_ICD_REG(3); + writel(temp, sport->port.membase + UCR1); + + /* set UCR4 */ + temp = readl(sport->port.membase + UCR4); + temp |= UCR4_IDDMAEN; + writel(temp, sport->port.membase + UCR4); + + sport->dma_is_enabled = 1; +} + +static void imx_disable_dma(struct imx_port *sport) +{ + unsigned long temp; + struct tty_port *port = &sport->port.state->port; + + /* clear UCR1 */ + temp = readl(sport->port.membase + UCR1); + temp &= ~(UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN); + writel(temp, sport->port.membase + UCR1); + + /* clear UCR2 */ + temp = readl(sport->port.membase + UCR2); + temp &= ~(UCR2_CTSC | UCR2_CTS); + writel(temp, sport->port.membase + UCR2); + + /* clear UCR4 */ + temp = readl(sport->port.membase + UCR4); + temp &= ~UCR4_IDDMAEN; + writel(temp, sport->port.membase + UCR4); + + sport->dma_is_enabled = 0; + port->low_latency = 0; +} + /* half the RX buffer size */ #define CTSTL 16 @@ -684,6 +1093,15 @@ static int imx_startup(struct uart_port *port) int retval; unsigned long flags, temp; + retval = clk_prepare_enable(sport->clk_per); + if (retval) + goto error_out1; + retval = clk_prepare_enable(sport->clk_ipg); + if (retval) { + clk_disable_unprepare(sport->clk_per); + goto error_out1; + } + imx_setup_ufcr(sport, 0); /* disable the DREN bit (Data Ready interrupt enable) before @@ -774,7 +1192,7 @@ static int imx_startup(struct uart_port *port) } } - if (is_imx21_uart(sport)) { + if (!is_imx1_uart(sport)) { temp = readl(sport->port.membase + UCR3); temp |= IMX21_UCR3_RXDMUXSEL; writel(temp, sport->port.membase + UCR3); @@ -804,7 +1222,7 @@ static int imx_startup(struct uart_port *port) if (USE_IRDA(sport)) { struct imxuart_platform_data *pdata; - pdata = sport->port.dev->platform_data; + pdata = dev_get_platdata(sport->port.dev); sport->irda_inv_rx = pdata->irda_inv_rx; sport->irda_inv_tx = pdata->irda_inv_tx; sport->trcv_delay = pdata->transceiver_delay; @@ -830,6 +1248,15 @@ static void imx_shutdown(struct uart_port *port) unsigned long temp; unsigned long flags; + if (sport->dma_is_enabled) { + /* We have to wait for the DMA to finish. */ + wait_event(sport->dma_wait, + !sport->dma_is_rxing && !sport->dma_is_txing); + imx_stop_rx(port); + imx_disable_dma(sport); + imx_uart_dma_exit(sport); + } + spin_lock_irqsave(&sport->port.lock, flags); temp = readl(sport->port.membase + UCR2); temp &= ~(UCR2_TXEN); @@ -838,7 +1265,7 @@ static void imx_shutdown(struct uart_port *port) if (USE_IRDA(sport)) { struct imxuart_platform_data *pdata; - pdata = sport->port.dev->platform_data; + pdata = dev_get_platdata(sport->port.dev); if (pdata->irda_enable) pdata->irda_enable(0); } @@ -871,6 +1298,9 @@ static void imx_shutdown(struct uart_port *port) writel(temp, sport->port.membase + UCR1); spin_unlock_irqrestore(&sport->port.lock, flags); + + clk_disable_unprepare(sport->clk_per); + clk_disable_unprepare(sport->clk_ipg); } static void @@ -913,6 +1343,11 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, if (sport->have_rtscts) { ucr2 &= ~UCR2_IRTS; ucr2 |= UCR2_CTSC; + + /* Can we enable the DMA support? */ + if (is_imx6q_uart(sport) && !uart_console(port) + && !sport->dma_is_inited) + imx_uart_dma_init(sport); } else { termios->c_cflag &= ~CRTSCTS; } @@ -986,6 +1421,11 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, */ div = 1; } else { + /* custom-baudrate handling */ + div = sport->port.uartclk / (baud * 16); + if (baud == 38400 && quot != div) + baud = sport->port.uartclk / (quot * 16); + div = sport->port.uartclk / (baud * 16); if (div > 7) div = 7; @@ -1007,12 +1447,14 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, ufcr = readl(sport->port.membase + UFCR); ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div); + if (sport->dte_mode) + ufcr |= UFCR_DCEDTE; writel(ufcr, sport->port.membase + UFCR); writel(num, sport->port.membase + UBIR); writel(denom, sport->port.membase + UBMR); - if (is_imx21_uart(sport)) + if (!is_imx1_uart(sport)) writel(sport->port.uartclk / div / 1000, sport->port.membase + IMX21_ONEMS); @@ -1024,6 +1466,8 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) imx_enable_ms(&sport->port); + if (sport->dma_is_inited && !sport->dma_is_enabled) + imx_enable_dma(sport); spin_unlock_irqrestore(&sport->port.lock, flags); } @@ -1215,6 +1659,16 @@ imx_console_write(struct console *co, const char *s, unsigned int count) unsigned int ucr1; unsigned long flags = 0; int locked = 1; + int retval; + + retval = clk_enable(sport->clk_per); + if (retval) + return; + retval = clk_enable(sport->clk_ipg); + if (retval) { + clk_disable(sport->clk_per); + return; + } if (sport->port.sysrq) locked = 0; @@ -1250,6 +1704,9 @@ imx_console_write(struct console *co, const char *s, unsigned int count) if (locked) spin_unlock_irqrestore(&sport->port.lock, flags); + + clk_disable(sport->clk_ipg); + clk_disable(sport->clk_per); } /* @@ -1323,6 +1780,7 @@ imx_console_setup(struct console *co, char *options) int bits = 8; int parity = 'n'; int flow = 'n'; + int retval; /* * Check whether an invalid uart number has been specified, and @@ -1335,6 +1793,11 @@ imx_console_setup(struct console *co, char *options) if (sport == NULL) return -ENODEV; + /* For setting the registers, we only need to enable the ipg clock. */ + retval = clk_prepare_enable(sport->clk_ipg); + if (retval) + goto error_console; + if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else @@ -1342,7 +1805,20 @@ imx_console_setup(struct console *co, char *options) imx_setup_ufcr(sport, 0); - return uart_set_options(&sport->port, co, baud, parity, bits, flow); + retval = uart_set_options(&sport->port, co, baud, parity, bits, flow); + + clk_disable(sport->clk_ipg); + if (retval) { + clk_unprepare(sport->clk_ipg); + goto error_console; + } + + retval = clk_prepare(sport->clk_per); + if (retval) + clk_disable_unprepare(sport->clk_ipg); + +error_console: + return retval; } static struct uart_driver imx_reg; @@ -1431,8 +1907,14 @@ static int serial_imx_probe_dt(struct imx_port *sport, if (of_get_property(np, "fsl,irda-mode", NULL)) sport->use_irda = 1; + if (of_get_property(np, "fsl,dte-mode", NULL)) + sport->dte_mode = 1; + sport->devdata = of_id->data; + if (of_device_is_stdout_path(np)) + add_preferred_console(imx_reg.cons->name, sport->port.line, 0); + return 0; } #else @@ -1446,7 +1928,7 @@ static inline int serial_imx_probe_dt(struct imx_port *sport, static void serial_imx_probe_pdata(struct imx_port *sport, struct platform_device *pdev) { - struct imxuart_platform_data *pdata = pdev->dev.platform_data; + struct imxuart_platform_data *pdata = dev_get_platdata(&pdev->dev); sport->port.line = pdev->id; sport->devdata = (struct imx_uart_data *) pdev->id_entry->driver_data; @@ -1468,7 +1950,6 @@ static int serial_imx_probe(struct platform_device *pdev) void __iomem *base; int ret = 0; struct resource *res; - struct pinctrl *pinctrl; sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); if (!sport) @@ -1504,13 +1985,6 @@ static int serial_imx_probe(struct platform_device *pdev) sport->timer.function = imx_timeout; sport->timer.data = (unsigned long)sport; - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - ret = PTR_ERR(pinctrl); - dev_err(&pdev->dev, "failed to get default pinctrl: %d\n", ret); - return ret; - } - sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(sport->clk_ipg)) { ret = PTR_ERR(sport->clk_ipg); @@ -1525,18 +1999,15 @@ static int serial_imx_probe(struct platform_device *pdev) return ret; } - clk_prepare_enable(sport->clk_per); - clk_prepare_enable(sport->clk_ipg); - sport->port.uartclk = clk_get_rate(sport->clk_per); imx_ports[sport->port.line] = sport; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (pdata && pdata->init) { ret = pdata->init(pdev); if (ret) - goto clkput; + return ret; } ret = uart_add_one_port(&imx_reg, &sport->port); @@ -1548,9 +2019,6 @@ static int serial_imx_probe(struct platform_device *pdev) deinit: if (pdata && pdata->exit) pdata->exit(pdev); -clkput: - clk_disable_unprepare(sport->clk_per); - clk_disable_unprepare(sport->clk_ipg); return ret; } @@ -1559,15 +2027,10 @@ static int serial_imx_remove(struct platform_device *pdev) struct imxuart_platform_data *pdata; struct imx_port *sport = platform_get_drvdata(pdev); - pdata = pdev->dev.platform_data; - - platform_set_drvdata(pdev, NULL); + pdata = dev_get_platdata(&pdev->dev); uart_remove_one_port(&imx_reg, &sport->port); - clk_disable_unprepare(sport->clk_per); - clk_disable_unprepare(sport->clk_ipg); - if (pdata && pdata->exit) pdata->exit(pdev); diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c index e2520abcb1c4..1274499850fc 100644 --- a/drivers/tty/serial/ioc4_serial.c +++ b/drivers/tty/serial/ioc4_serial.c @@ -297,7 +297,7 @@ struct ioc4_serial { struct ioc4_uartregs uart_1; struct ioc4_uartregs uart_2; struct ioc4_uartregs uart_3; -} ioc4_serial; +}; /* UART clock speed */ #define IOC4_SER_XIN_CLK_66 66666667 @@ -2767,7 +2767,7 @@ ioc4_serial_core_attach(struct pci_dev *pdev, int port_type) * called per card found from IOC4 master module. * @idd: Master module data for this IOC4 */ -int +static int ioc4_serial_attach_one(struct ioc4_driver_data *idd) { unsigned long tmp_addr1; diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index 10020547c60b..a260cde743e2 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -45,7 +45,7 @@ static int kgdboc_reset_connect(struct input_handler *handler, { input_reset_device(dev); - /* Retrun an error - we do not want to bind, just to reset */ + /* Return an error - we do not want to bind, just to reset */ return -ENODEV; } diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c index 15733da757c6..88d01e0bb0c8 100644 --- a/drivers/tty/serial/lantiq.c +++ b/drivers/tty/serial/lantiq.c @@ -318,7 +318,7 @@ lqasc_startup(struct uart_port *port) struct ltq_uart_port *ltq_port = to_ltq_uart_port(port); int retval; - if (ltq_port->clk) + if (!IS_ERR(ltq_port->clk)) clk_enable(ltq_port->clk); port->uartclk = clk_get_rate(ltq_port->fpiclk); @@ -386,7 +386,7 @@ lqasc_shutdown(struct uart_port *port) port->membase + LTQ_ASC_RXFCON); ltq_w32_mask(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU, port->membase + LTQ_ASC_TXFCON); - if (ltq_port->clk) + if (!IS_ERR(ltq_port->clk)) clk_disable(ltq_port->clk); } @@ -636,6 +636,9 @@ lqasc_console_setup(struct console *co, char *options) port = <q_port->port; + if (!IS_ERR(ltq_port->clk)) + clk_enable(ltq_port->clk); + port->uartclk = clk_get_rate(ltq_port->fpiclk); if (options) diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c index dffea6b2cd7d..701644f06820 100644 --- a/drivers/tty/serial/lpc32xx_hs.c +++ b/drivers/tty/serial/lpc32xx_hs.c @@ -279,7 +279,10 @@ static void __serial_lpc32xx_rx(struct uart_port *port) tmp = readl(LPC32XX_HSUART_FIFO(port->membase)); } + + spin_unlock(&port->lock); tty_flip_buffer_push(tport); + spin_lock(&port->lock); } static void __serial_lpc32xx_tx(struct uart_port *port) @@ -351,10 +354,8 @@ static irqreturn_t serial_lpc32xx_interrupt(int irq, void *dev_id) } /* Data received? */ - if (status & (LPC32XX_HSU_RX_TIMEOUT_INT | LPC32XX_HSU_RX_TRIG_INT)) { + if (status & (LPC32XX_HSU_RX_TIMEOUT_INT | LPC32XX_HSU_RX_TRIG_INT)) __serial_lpc32xx_rx(port); - tty_flip_buffer_push(tport); - } /* Transmit data request? */ if ((status & LPC32XX_HSU_TX_INT) && (!uart_tx_stopped(port))) { diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c index bb1afa0922e1..9cd9b4eba9fc 100644 --- a/drivers/tty/serial/m32r_sio.c +++ b/drivers/tty/serial/m32r_sio.c @@ -368,7 +368,10 @@ static void receive_chars(struct uart_sio_port *up, int *status) ignore_char: *status = serial_in(up, UART_LSR); } while ((*status & UART_LSR_DR) && (max_count-- > 0)); + + spin_unlock(&up->port.lock); tty_flip_buffer_push(port); + spin_lock(&up->port.lock); } static void transmit_chars(struct uart_sio_port *up) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 35866d5872ad..79f9a9eff545 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -779,7 +779,7 @@ static int max3100_probe(struct spi_device *spi) max3100s[i]->irq = spi->irq; spin_lock_init(&max3100s[i]->conf_lock); spi_set_drvdata(spi, max3100s[i]); - pdata = spi->dev.platform_data; + pdata = dev_get_platdata(&spi->dev); max3100s[i]->crystal = pdata->crystal; max3100s[i]->loopback = pdata->loopback; max3100s[i]->poll_time = pdata->poll_time * HZ / 1000; diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 8941e6418942..b2e707aa603a 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1,7 +1,7 @@ /* - * Maxim (Dallas) MAX3107/8 serial driver + * Maxim (Dallas) MAX3107/8/9, MAX14830 serial driver * - * Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru> + * Copyright (C) 2012-2013 Alexander Shiyan <shc_work@mail.ru> * * Based on max3100.c, by Christian Pellegrin <chripell@evolware.org> * Based on max3110.c, by Feng Tang <feng.tang@intel.com> @@ -13,11 +13,10 @@ * (at your option) any later version. */ -/* TODO: MAX3109 support (Dual) */ -/* TODO: MAX14830 support (Quad) */ - #include <linux/module.h> +#include <linux/delay.h> #include <linux/device.h> +#include <linux/bitops.h> #include <linux/serial_core.h> #include <linux/serial.h> #include <linux/tty.h> @@ -25,8 +24,10 @@ #include <linux/regmap.h> #include <linux/gpio.h> #include <linux/spi/spi.h> + #include <linux/platform_data/max310x.h> +#define MAX310X_NAME "max310x" #define MAX310X_MAJOR 204 #define MAX310X_MINOR 209 @@ -37,7 +38,8 @@ #define MAX310X_IRQSTS_REG (0x02) /* IRQ status */ #define MAX310X_LSR_IRQEN_REG (0x03) /* LSR IRQ enable */ #define MAX310X_LSR_IRQSTS_REG (0x04) /* LSR IRQ status */ -#define MAX310X_SPCHR_IRQEN_REG (0x05) /* Special char IRQ enable */ +#define MAX310X_REG_05 (0x05) +#define MAX310X_SPCHR_IRQEN_REG MAX310X_REG_05 /* Special char IRQ en */ #define MAX310X_SPCHR_IRQSTS_REG (0x06) /* Special char IRQ status */ #define MAX310X_STS_IRQEN_REG (0x07) /* Status IRQ enable */ #define MAX310X_STS_IRQSTS_REG (0x08) /* Status IRQ status */ @@ -63,8 +65,15 @@ #define MAX310X_BRGDIVLSB_REG (0x1c) /* Baud rate divisor LSB */ #define MAX310X_BRGDIVMSB_REG (0x1d) /* Baud rate divisor MSB */ #define MAX310X_CLKSRC_REG (0x1e) /* Clock source */ -/* Only present in MAX3107 */ -#define MAX3107_REVID_REG (0x1f) /* Revision identification */ +#define MAX310X_REG_1F (0x1f) + +#define MAX310X_REVID_REG MAX310X_REG_1F /* Revision ID */ + +#define MAX310X_GLOBALIRQ_REG MAX310X_REG_1F /* Global IRQ (RO) */ +#define MAX310X_GLOBALCMD_REG MAX310X_REG_1F /* Global Command (WO) */ + +/* Extended registers */ +#define MAX310X_REVID_EXTREG MAX310X_REG_05 /* Revision ID */ /* IRQ register bits */ #define MAX310X_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */ @@ -246,58 +255,210 @@ #define MAX310X_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */ #define MAX310X_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */ +/* Global commands */ +#define MAX310X_EXTREG_ENBL (0xce) +#define MAX310X_EXTREG_DSBL (0xcd) + /* Misc definitions */ #define MAX310X_FIFO_SIZE (128) +#define MAX310x_REV_MASK (0xfc) /* MAX3107 specific */ #define MAX3107_REV_ID (0xa0) -#define MAX3107_REV_MASK (0xfe) - -/* IRQ status bits definitions */ -#define MAX310X_IRQ_TX (MAX310X_IRQ_TXFIFO_BIT | \ - MAX310X_IRQ_TXEMPTY_BIT) -#define MAX310X_IRQ_RX (MAX310X_IRQ_RXFIFO_BIT | \ - MAX310X_IRQ_RXEMPTY_BIT) - -/* Supported chip types */ -enum { - MAX310X_TYPE_MAX3107 = 3107, - MAX310X_TYPE_MAX3108 = 3108, + +/* MAX3109 specific */ +#define MAX3109_REV_ID (0xc0) + +/* MAX14830 specific */ +#define MAX14830_BRGCFG_CLKDIS_BIT (1 << 6) /* Clock Disable */ +#define MAX14830_REV_ID (0xb0) + +struct max310x_devtype { + char name[9]; + int nr; + int (*detect)(struct device *); + void (*power)(struct uart_port *, int); }; -struct max310x_port { - struct uart_driver uart; +struct max310x_one { struct uart_port port; + struct work_struct tx_work; +}; - const char *name; - int uartclk; - - unsigned int nr_gpio; +struct max310x_port { + struct uart_driver uart; + struct max310x_devtype *devtype; + struct regmap *regmap; + struct regmap_config regcfg; + struct mutex mutex; + struct max310x_pdata *pdata; + int gpio_used; #ifdef CONFIG_GPIOLIB struct gpio_chip gpio; #endif + struct max310x_one p[0]; +}; - struct regmap *regmap; - struct regmap_config regcfg; +static u8 max310x_port_read(struct uart_port *port, u8 reg) +{ + struct max310x_port *s = dev_get_drvdata(port->dev); + unsigned int val = 0; - struct workqueue_struct *wq; - struct work_struct tx_work; + regmap_read(s->regmap, port->iobase + reg, &val); - struct mutex max310x_mutex; + return val; +} - struct max310x_pdata *pdata; +static void max310x_port_write(struct uart_port *port, u8 reg, u8 val) +{ + struct max310x_port *s = dev_get_drvdata(port->dev); + + regmap_write(s->regmap, port->iobase + reg, val); +} + +static void max310x_port_update(struct uart_port *port, u8 reg, u8 mask, u8 val) +{ + struct max310x_port *s = dev_get_drvdata(port->dev); + + regmap_update_bits(s->regmap, port->iobase + reg, mask, val); +} + +static int max3107_detect(struct device *dev) +{ + struct max310x_port *s = dev_get_drvdata(dev); + unsigned int val = 0; + int ret; + + ret = regmap_read(s->regmap, MAX310X_REVID_REG, &val); + if (ret) + return ret; + + if (((val & MAX310x_REV_MASK) != MAX3107_REV_ID)) { + dev_err(dev, + "%s ID 0x%02x does not match\n", s->devtype->name, val); + return -ENODEV; + } + + return 0; +} + +static int max3108_detect(struct device *dev) +{ + struct max310x_port *s = dev_get_drvdata(dev); + unsigned int val = 0; + int ret; + + /* MAX3108 have not REV ID register, we just check default value + * from clocksource register to make sure everything works. + */ + ret = regmap_read(s->regmap, MAX310X_CLKSRC_REG, &val); + if (ret) + return ret; + + if (val != (MAX310X_CLKSRC_EXTCLK_BIT | MAX310X_CLKSRC_PLLBYP_BIT)) { + dev_err(dev, "%s not present\n", s->devtype->name); + return -ENODEV; + } + + return 0; +} + +static int max3109_detect(struct device *dev) +{ + struct max310x_port *s = dev_get_drvdata(dev); + unsigned int val = 0; + int ret; + + ret = regmap_read(s->regmap, MAX310X_REVID_REG, &val); + if (ret) + return ret; + + if (((val & MAX310x_REV_MASK) != MAX3109_REV_ID)) { + dev_err(dev, + "%s ID 0x%02x does not match\n", s->devtype->name, val); + return -ENODEV; + } + + return 0; +} + +static void max310x_power(struct uart_port *port, int on) +{ + max310x_port_update(port, MAX310X_MODE1_REG, + MAX310X_MODE1_FORCESLEEP_BIT, + on ? 0 : MAX310X_MODE1_FORCESLEEP_BIT); + if (on) + msleep(50); +} + +static int max14830_detect(struct device *dev) +{ + struct max310x_port *s = dev_get_drvdata(dev); + unsigned int val = 0; + int ret; + + ret = regmap_write(s->regmap, MAX310X_GLOBALCMD_REG, + MAX310X_EXTREG_ENBL); + if (ret) + return ret; + + regmap_read(s->regmap, MAX310X_REVID_EXTREG, &val); + regmap_write(s->regmap, MAX310X_GLOBALCMD_REG, MAX310X_EXTREG_DSBL); + if (((val & MAX310x_REV_MASK) != MAX14830_REV_ID)) { + dev_err(dev, + "%s ID 0x%02x does not match\n", s->devtype->name, val); + return -ENODEV; + } + + return 0; +} + +static void max14830_power(struct uart_port *port, int on) +{ + max310x_port_update(port, MAX310X_BRGCFG_REG, + MAX14830_BRGCFG_CLKDIS_BIT, + on ? 0 : MAX14830_BRGCFG_CLKDIS_BIT); + if (on) + msleep(50); +} + +static const struct max310x_devtype max3107_devtype = { + .name = "MAX3107", + .nr = 1, + .detect = max3107_detect, + .power = max310x_power, +}; + +static const struct max310x_devtype max3108_devtype = { + .name = "MAX3108", + .nr = 1, + .detect = max3108_detect, + .power = max310x_power, +}; + +static const struct max310x_devtype max3109_devtype = { + .name = "MAX3109", + .nr = 2, + .detect = max3109_detect, + .power = max310x_power, +}; + +static const struct max310x_devtype max14830_devtype = { + .name = "MAX14830", + .nr = 4, + .detect = max14830_detect, + .power = max14830_power, }; -static bool max3107_8_reg_writeable(struct device *dev, unsigned int reg) +static bool max310x_reg_writeable(struct device *dev, unsigned int reg) { - switch (reg) { + switch (reg & 0x1f) { case MAX310X_IRQSTS_REG: case MAX310X_LSR_IRQSTS_REG: case MAX310X_SPCHR_IRQSTS_REG: case MAX310X_STS_IRQSTS_REG: case MAX310X_TXFIFOLVL_REG: case MAX310X_RXFIFOLVL_REG: - case MAX3107_REVID_REG: /* Only available on MAX3107 */ return false; default: break; @@ -308,7 +469,7 @@ static bool max3107_8_reg_writeable(struct device *dev, unsigned int reg) static bool max310x_reg_volatile(struct device *dev, unsigned int reg) { - switch (reg) { + switch (reg & 0x1f) { case MAX310X_RHR_REG: case MAX310X_IRQSTS_REG: case MAX310X_LSR_IRQSTS_REG: @@ -317,6 +478,9 @@ static bool max310x_reg_volatile(struct device *dev, unsigned int reg) case MAX310X_TXFIFOLVL_REG: case MAX310X_RXFIFOLVL_REG: case MAX310X_GPIODATA_REG: + case MAX310X_BRGDIVLSB_REG: + case MAX310X_REG_05: + case MAX310X_REG_1F: return true; default: break; @@ -327,7 +491,7 @@ static bool max310x_reg_volatile(struct device *dev, unsigned int reg) static bool max310x_reg_precious(struct device *dev, unsigned int reg) { - switch (reg) { + switch (reg & 0x1f) { case MAX310X_RHR_REG: case MAX310X_IRQSTS_REG: case MAX310X_SPCHR_IRQSTS_REG: @@ -340,42 +504,25 @@ static bool max310x_reg_precious(struct device *dev, unsigned int reg) return false; } -static void max310x_set_baud(struct max310x_port *s, int baud) +static void max310x_set_baud(struct uart_port *port, int baud) { - unsigned int mode = 0, div = s->uartclk / baud; + unsigned int mode = 0, div = port->uartclk / baud; if (!(div / 16)) { /* Mode x2 */ mode = MAX310X_BRGCFG_2XMODE_BIT; - div = (s->uartclk * 2) / baud; + div = (port->uartclk * 2) / baud; } if (!(div / 16)) { /* Mode x4 */ mode = MAX310X_BRGCFG_4XMODE_BIT; - div = (s->uartclk * 4) / baud; + div = (port->uartclk * 4) / baud; } - regmap_write(s->regmap, MAX310X_BRGDIVMSB_REG, - ((div / 16) >> 8) & 0xff); - regmap_write(s->regmap, MAX310X_BRGDIVLSB_REG, (div / 16) & 0xff); - regmap_write(s->regmap, MAX310X_BRGCFG_REG, (div % 16) | mode); -} - -static void max310x_wait_pll(struct max310x_port *s) -{ - int tryes = 1000; - - /* Wait for PLL only if crystal is used */ - if (!(s->pdata->driver_flags & MAX310X_EXT_CLK)) { - unsigned int sts = 0; - - while (tryes--) { - regmap_read(s->regmap, MAX310X_STS_IRQSTS_REG, &sts); - if (sts & MAX310X_STS_CLKREADY_BIT) - break; - } - } + max310x_port_write(port, MAX310X_BRGDIVMSB_REG, (div / 16) >> 8); + max310x_port_write(port, MAX310X_BRGDIVLSB_REG, div / 16); + max310x_port_write(port, MAX310X_BRGCFG_REG, (div % 16) | mode); } static int max310x_update_best_err(unsigned long f, long *besterr) @@ -449,49 +596,49 @@ static int max310x_set_ref_clk(struct max310x_port *s) regmap_write(s->regmap, MAX310X_CLKSRC_REG, clksrc); - if (pllcfg) - max310x_wait_pll(s); - - dev_dbg(s->port.dev, "Reference clock set to %lu Hz\n", bestfreq); + /* Wait for crystal */ + if (pllcfg && !(s->pdata->driver_flags & MAX310X_EXT_CLK)) + msleep(10); return (int)bestfreq; } -static void max310x_handle_rx(struct max310x_port *s, unsigned int rxlen) +static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen) { - unsigned int sts = 0, ch = 0, flag; + unsigned int sts, ch, flag; - if (unlikely(rxlen >= MAX310X_FIFO_SIZE)) { - dev_warn(s->port.dev, "Possible RX FIFO overrun %d\n", rxlen); + if (unlikely(rxlen >= port->fifosize)) { + dev_warn_ratelimited(port->dev, + "Port %i: Possible RX FIFO overrun\n", + port->line); + port->icount.buf_overrun++; /* Ensure sanity of RX level */ - rxlen = MAX310X_FIFO_SIZE; + rxlen = port->fifosize; } - dev_dbg(s->port.dev, "RX Len = %u\n", rxlen); - while (rxlen--) { - regmap_read(s->regmap, MAX310X_RHR_REG, &ch); - regmap_read(s->regmap, MAX310X_LSR_IRQSTS_REG, &sts); + ch = max310x_port_read(port, MAX310X_RHR_REG); + sts = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG); sts &= MAX310X_LSR_RXPAR_BIT | MAX310X_LSR_FRERR_BIT | MAX310X_LSR_RXOVR_BIT | MAX310X_LSR_RXBRK_BIT; - s->port.icount.rx++; + port->icount.rx++; flag = TTY_NORMAL; if (unlikely(sts)) { if (sts & MAX310X_LSR_RXBRK_BIT) { - s->port.icount.brk++; - if (uart_handle_break(&s->port)) + port->icount.brk++; + if (uart_handle_break(port)) continue; } else if (sts & MAX310X_LSR_RXPAR_BIT) - s->port.icount.parity++; + port->icount.parity++; else if (sts & MAX310X_LSR_FRERR_BIT) - s->port.icount.frame++; + port->icount.frame++; else if (sts & MAX310X_LSR_RXOVR_BIT) - s->port.icount.overrun++; + port->icount.overrun++; - sts &= s->port.read_status_mask; + sts &= port->read_status_mask; if (sts & MAX310X_LSR_RXBRK_BIT) flag = TTY_BREAK; else if (sts & MAX310X_LSR_RXPAR_BIT) @@ -502,129 +649,129 @@ static void max310x_handle_rx(struct max310x_port *s, unsigned int rxlen) flag = TTY_OVERRUN; } - if (uart_handle_sysrq_char(s->port, ch)) + if (uart_handle_sysrq_char(port, ch)) continue; - if (sts & s->port.ignore_status_mask) + if (sts & port->ignore_status_mask) continue; - uart_insert_char(&s->port, sts, MAX310X_LSR_RXOVR_BIT, - ch, flag); + uart_insert_char(port, sts, MAX310X_LSR_RXOVR_BIT, ch, flag); } - tty_flip_buffer_push(&s->port.state->port); + tty_flip_buffer_push(&port->state->port); } -static void max310x_handle_tx(struct max310x_port *s) +static void max310x_handle_tx(struct uart_port *port) { - struct circ_buf *xmit = &s->port.state->xmit; - unsigned int txlen = 0, to_send; + struct circ_buf *xmit = &port->state->xmit; + unsigned int txlen, to_send; - if (unlikely(s->port.x_char)) { - regmap_write(s->regmap, MAX310X_THR_REG, s->port.x_char); - s->port.icount.tx++; - s->port.x_char = 0; + if (unlikely(port->x_char)) { + max310x_port_write(port, MAX310X_THR_REG, port->x_char); + port->icount.tx++; + port->x_char = 0; return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port)) + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) return; /* Get length of data pending in circular buffer */ to_send = uart_circ_chars_pending(xmit); if (likely(to_send)) { /* Limit to size of TX FIFO */ - regmap_read(s->regmap, MAX310X_TXFIFOLVL_REG, &txlen); - txlen = MAX310X_FIFO_SIZE - txlen; + txlen = max310x_port_read(port, MAX310X_TXFIFOLVL_REG); + txlen = port->fifosize - txlen; to_send = (to_send > txlen) ? txlen : to_send; - dev_dbg(s->port.dev, "TX Len = %u\n", to_send); - /* Add data to send */ - s->port.icount.tx += to_send; + port->icount.tx += to_send; while (to_send--) { - regmap_write(s->regmap, MAX310X_THR_REG, - xmit->buf[xmit->tail]); + max310x_port_write(port, MAX310X_THR_REG, + xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); }; } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&s->port); + uart_write_wakeup(port); } -static irqreturn_t max310x_ist(int irq, void *dev_id) +static void max310x_port_irq(struct max310x_port *s, int portno) { - struct max310x_port *s = (struct max310x_port *)dev_id; - unsigned int ists = 0, lsr = 0, rxlen = 0; + struct uart_port *port = &s->p[portno].port; - mutex_lock(&s->max310x_mutex); + do { + unsigned int ists, lsr, rxlen; - for (;;) { /* Read IRQ status & RX FIFO level */ - regmap_read(s->regmap, MAX310X_IRQSTS_REG, &ists); - regmap_read(s->regmap, MAX310X_LSR_IRQSTS_REG, &lsr); - regmap_read(s->regmap, MAX310X_RXFIFOLVL_REG, &rxlen); - if (!ists && !(lsr & MAX310X_LSR_RXTO_BIT) && !rxlen) + ists = max310x_port_read(port, MAX310X_IRQSTS_REG); + rxlen = max310x_port_read(port, MAX310X_RXFIFOLVL_REG); + if (!ists && !rxlen) break; - dev_dbg(s->port.dev, "IRQ status: 0x%02x\n", ists); - - if (rxlen) - max310x_handle_rx(s, rxlen); - if (ists & MAX310X_IRQ_TX) - max310x_handle_tx(s); - if (ists & MAX310X_IRQ_CTS_BIT) - uart_handle_cts_change(&s->port, + if (ists & MAX310X_IRQ_CTS_BIT) { + lsr = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG); + uart_handle_cts_change(port, !!(lsr & MAX310X_LSR_CTS_BIT)); - } + } + if (rxlen) + max310x_handle_rx(port, rxlen); + if (ists & MAX310X_IRQ_TXEMPTY_BIT) { + mutex_lock(&s->mutex); + max310x_handle_tx(port); + mutex_unlock(&s->mutex); + } + } while (1); +} + +static irqreturn_t max310x_ist(int irq, void *dev_id) +{ + struct max310x_port *s = (struct max310x_port *)dev_id; - mutex_unlock(&s->max310x_mutex); + if (s->uart.nr > 1) { + do { + unsigned int val = ~0; + + WARN_ON_ONCE(regmap_read(s->regmap, + MAX310X_GLOBALIRQ_REG, &val)); + val = ((1 << s->uart.nr) - 1) & ~val; + if (!val) + break; + max310x_port_irq(s, fls(val) - 1); + } while (1); + } else + max310x_port_irq(s, 0); return IRQ_HANDLED; } static void max310x_wq_proc(struct work_struct *ws) { - struct max310x_port *s = container_of(ws, struct max310x_port, tx_work); + struct max310x_one *one = container_of(ws, struct max310x_one, tx_work); + struct max310x_port *s = dev_get_drvdata(one->port.dev); - mutex_lock(&s->max310x_mutex); - max310x_handle_tx(s); - mutex_unlock(&s->max310x_mutex); + mutex_lock(&s->mutex); + max310x_handle_tx(&one->port); + mutex_unlock(&s->mutex); } static void max310x_start_tx(struct uart_port *port) { - struct max310x_port *s = container_of(port, struct max310x_port, port); - - queue_work(s->wq, &s->tx_work); -} - -static void max310x_stop_tx(struct uart_port *port) -{ - /* Do nothing */ -} + struct max310x_one *one = container_of(port, struct max310x_one, port); -static void max310x_stop_rx(struct uart_port *port) -{ - /* Do nothing */ + if (!work_pending(&one->tx_work)) + schedule_work(&one->tx_work); } static unsigned int max310x_tx_empty(struct uart_port *port) { - unsigned int val = 0; - struct max310x_port *s = container_of(port, struct max310x_port, port); + unsigned int lvl, sts; - mutex_lock(&s->max310x_mutex); - regmap_read(s->regmap, MAX310X_TXFIFOLVL_REG, &val); - mutex_unlock(&s->max310x_mutex); + lvl = max310x_port_read(port, MAX310X_TXFIFOLVL_REG); + sts = max310x_port_read(port, MAX310X_IRQSTS_REG); - return val ? 0 : TIOCSER_TEMT; -} - -static void max310x_enable_ms(struct uart_port *port) -{ - /* Modem status not supported */ + return ((sts & MAX310X_IRQ_TXEMPTY_BIT) && !lvl) ? TIOCSER_TEMT : 0; } static unsigned int max310x_get_mctrl(struct uart_port *port) @@ -644,28 +791,20 @@ static void max310x_set_mctrl(struct uart_port *port, unsigned int mctrl) static void max310x_break_ctl(struct uart_port *port, int break_state) { - struct max310x_port *s = container_of(port, struct max310x_port, port); - - mutex_lock(&s->max310x_mutex); - regmap_update_bits(s->regmap, MAX310X_LCR_REG, - MAX310X_LCR_TXBREAK_BIT, - break_state ? MAX310X_LCR_TXBREAK_BIT : 0); - mutex_unlock(&s->max310x_mutex); + max310x_port_update(port, MAX310X_LCR_REG, + MAX310X_LCR_TXBREAK_BIT, + break_state ? MAX310X_LCR_TXBREAK_BIT : 0); } static void max310x_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - struct max310x_port *s = container_of(port, struct max310x_port, port); unsigned int lcr, flow = 0; int baud; - mutex_lock(&s->max310x_mutex); - /* Mask termios capabilities we don't support */ termios->c_cflag &= ~CMSPAR; - termios->c_iflag &= ~IXANY; /* Word size */ switch (termios->c_cflag & CSIZE) { @@ -696,7 +835,7 @@ static void max310x_set_termios(struct uart_port *port, lcr |= MAX310X_LCR_STOPLEN_BIT; /* 2 stops */ /* Update LCR register */ - regmap_write(s->regmap, MAX310X_LCR_REG, lcr); + max310x_port_write(port, MAX310X_LCR_REG, lcr); /* Set read status mask */ port->read_status_mask = MAX310X_LSR_RXOVR_BIT; @@ -717,8 +856,8 @@ static void max310x_set_termios(struct uart_port *port, MAX310X_LSR_RXBRK_BIT; /* Configure flow control */ - regmap_write(s->regmap, MAX310X_XON1_REG, termios->c_cc[VSTART]); - regmap_write(s->regmap, MAX310X_XOFF1_REG, termios->c_cc[VSTOP]); + max310x_port_write(port, MAX310X_XON1_REG, termios->c_cc[VSTART]); + max310x_port_write(port, MAX310X_XOFF1_REG, termios->c_cc[VSTOP]); if (termios->c_cflag & CRTSCTS) flow |= MAX310X_FLOWCTRL_AUTOCTS_BIT | MAX310X_FLOWCTRL_AUTORTS_BIT; @@ -728,7 +867,7 @@ static void max310x_set_termios(struct uart_port *port, if (termios->c_iflag & IXOFF) flow |= MAX310X_FLOWCTRL_SWFLOW1_BIT | MAX310X_FLOWCTRL_SWFLOWEN_BIT; - regmap_write(s->regmap, MAX310X_FLOWCTRL_REG, flow); + max310x_port_write(port, MAX310X_FLOWCTRL_REG, flow); /* Get baud rate generator configuration */ baud = uart_get_baud_rate(port, termios, old, @@ -736,36 +875,30 @@ static void max310x_set_termios(struct uart_port *port, port->uartclk / 4); /* Setup baudrate generator */ - max310x_set_baud(s, baud); + max310x_set_baud(port, baud); /* Update timeout according to new baud rate */ uart_update_timeout(port, termios->c_cflag, baud); - - mutex_unlock(&s->max310x_mutex); } static int max310x_startup(struct uart_port *port) { unsigned int val, line = port->line; - struct max310x_port *s = container_of(port, struct max310x_port, port); - - if (s->pdata->suspend) - s->pdata->suspend(0); + struct max310x_port *s = dev_get_drvdata(port->dev); - mutex_lock(&s->max310x_mutex); + s->devtype->power(port, 1); /* Configure baud rate, 9600 as default */ - max310x_set_baud(s, 9600); + max310x_set_baud(port, 9600); /* Configure LCR register, 8N1 mode by default */ - val = MAX310X_LCR_WORD_LEN_8; - regmap_write(s->regmap, MAX310X_LCR_REG, val); + max310x_port_write(port, MAX310X_LCR_REG, MAX310X_LCR_WORD_LEN_8); /* Configure MODE1 register */ - regmap_update_bits(s->regmap, MAX310X_MODE1_REG, - MAX310X_MODE1_TRNSCVCTRL_BIT, - (s->pdata->uart_flags[line] & MAX310X_AUTO_DIR_CTRL) - ? MAX310X_MODE1_TRNSCVCTRL_BIT : 0); + max310x_port_update(port, MAX310X_MODE1_REG, + MAX310X_MODE1_TRNSCVCTRL_BIT, + (s->pdata->uart_flags[line] & MAX310X_AUTO_DIR_CTRL) + ? MAX310X_MODE1_TRNSCVCTRL_BIT : 0); /* Configure MODE2 register */ val = MAX310X_MODE2_RXEMPTINV_BIT; @@ -776,63 +909,40 @@ static int max310x_startup(struct uart_port *port) /* Reset FIFOs */ val |= MAX310X_MODE2_FIFORST_BIT; - regmap_write(s->regmap, MAX310X_MODE2_REG, val); - - /* Configure FIFO trigger level register */ - /* RX FIFO trigger for 16 words, TX FIFO trigger for 64 words */ - val = MAX310X_FIFOTRIGLVL_RX(16) | MAX310X_FIFOTRIGLVL_TX(64); - regmap_write(s->regmap, MAX310X_FIFOTRIGLVL_REG, val); + max310x_port_write(port, MAX310X_MODE2_REG, val); + max310x_port_update(port, MAX310X_MODE2_REG, + MAX310X_MODE2_FIFORST_BIT, 0); /* Configure flow control levels */ /* Flow control halt level 96, resume level 48 */ - val = MAX310X_FLOWLVL_RES(48) | MAX310X_FLOWLVL_HALT(96); - regmap_write(s->regmap, MAX310X_FLOWLVL_REG, val); - - /* Clear timeout register */ - regmap_write(s->regmap, MAX310X_RXTO_REG, 0); + max310x_port_write(port, MAX310X_FLOWLVL_REG, + MAX310X_FLOWLVL_RES(48) | MAX310X_FLOWLVL_HALT(96)); - /* Configure LSR interrupt enable register */ - /* Enable RX timeout interrupt */ - val = MAX310X_LSR_RXTO_BIT; - regmap_write(s->regmap, MAX310X_LSR_IRQEN_REG, val); + /* Clear IRQ status register */ + max310x_port_read(port, MAX310X_IRQSTS_REG); - /* Clear FIFO reset */ - regmap_update_bits(s->regmap, MAX310X_MODE2_REG, - MAX310X_MODE2_FIFORST_BIT, 0); - - /* Clear IRQ status register by reading it */ - regmap_read(s->regmap, MAX310X_IRQSTS_REG, &val); - - /* Configure interrupt enable register */ - /* Enable CTS change interrupt */ - val = MAX310X_IRQ_CTS_BIT; - /* Enable RX, TX interrupts */ - val |= MAX310X_IRQ_RX | MAX310X_IRQ_TX; - regmap_write(s->regmap, MAX310X_IRQEN_REG, val); - - mutex_unlock(&s->max310x_mutex); + /* Enable RX, TX, CTS change interrupts */ + val = MAX310X_IRQ_RXEMPTY_BIT | MAX310X_IRQ_TXEMPTY_BIT; + max310x_port_write(port, MAX310X_IRQEN_REG, val | MAX310X_IRQ_CTS_BIT); return 0; } static void max310x_shutdown(struct uart_port *port) { - struct max310x_port *s = container_of(port, struct max310x_port, port); + struct max310x_port *s = dev_get_drvdata(port->dev); /* Disable all interrupts */ - mutex_lock(&s->max310x_mutex); - regmap_write(s->regmap, MAX310X_IRQEN_REG, 0); - mutex_unlock(&s->max310x_mutex); + max310x_port_write(port, MAX310X_IRQEN_REG, 0); - if (s->pdata->suspend) - s->pdata->suspend(1); + s->devtype->power(port, 0); } static const char *max310x_type(struct uart_port *port) { - struct max310x_port *s = container_of(port, struct max310x_port, port); + struct max310x_port *s = dev_get_drvdata(port->dev); - return (port->type == PORT_MAX310X) ? s->name : NULL; + return (port->type == PORT_MAX310X) ? s->devtype->name : NULL; } static int max310x_request_port(struct uart_port *port) @@ -841,134 +951,99 @@ static int max310x_request_port(struct uart_port *port) return 0; } -static void max310x_release_port(struct uart_port *port) -{ - /* Do nothing */ -} - static void max310x_config_port(struct uart_port *port, int flags) { if (flags & UART_CONFIG_TYPE) port->type = PORT_MAX310X; } -static int max310x_verify_port(struct uart_port *port, struct serial_struct *ser) +static int max310x_verify_port(struct uart_port *port, struct serial_struct *s) { - if ((ser->type == PORT_UNKNOWN) || (ser->type == PORT_MAX310X)) - return 0; - if (ser->irq == port->irq) - return 0; + if ((s->type != PORT_UNKNOWN) && (s->type != PORT_MAX310X)) + return -EINVAL; + if (s->irq != port->irq) + return -EINVAL; - return -EINVAL; + return 0; } -static struct uart_ops max310x_ops = { +static void max310x_null_void(struct uart_port *port) +{ + /* Do nothing */ +} + +static const struct uart_ops max310x_ops = { .tx_empty = max310x_tx_empty, .set_mctrl = max310x_set_mctrl, .get_mctrl = max310x_get_mctrl, - .stop_tx = max310x_stop_tx, + .stop_tx = max310x_null_void, .start_tx = max310x_start_tx, - .stop_rx = max310x_stop_rx, - .enable_ms = max310x_enable_ms, + .stop_rx = max310x_null_void, + .enable_ms = max310x_null_void, .break_ctl = max310x_break_ctl, .startup = max310x_startup, .shutdown = max310x_shutdown, .set_termios = max310x_set_termios, .type = max310x_type, .request_port = max310x_request_port, - .release_port = max310x_release_port, + .release_port = max310x_null_void, .config_port = max310x_config_port, .verify_port = max310x_verify_port, }; -#ifdef CONFIG_PM_SLEEP - -static int max310x_suspend(struct device *dev) +static int __maybe_unused max310x_suspend(struct device *dev) { - int ret; struct max310x_port *s = dev_get_drvdata(dev); + int i; - dev_dbg(dev, "Suspend\n"); - - ret = uart_suspend_port(&s->uart, &s->port); - - mutex_lock(&s->max310x_mutex); - - /* Enable sleep mode */ - regmap_update_bits(s->regmap, MAX310X_MODE1_REG, - MAX310X_MODE1_FORCESLEEP_BIT, - MAX310X_MODE1_FORCESLEEP_BIT); - - mutex_unlock(&s->max310x_mutex); - - if (s->pdata->suspend) - s->pdata->suspend(1); + for (i = 0; i < s->uart.nr; i++) { + uart_suspend_port(&s->uart, &s->p[i].port); + s->devtype->power(&s->p[i].port, 0); + } - return ret; + return 0; } -static int max310x_resume(struct device *dev) +static int __maybe_unused max310x_resume(struct device *dev) { struct max310x_port *s = dev_get_drvdata(dev); + int i; - dev_dbg(dev, "Resume\n"); - - if (s->pdata->suspend) - s->pdata->suspend(0); - - mutex_lock(&s->max310x_mutex); - - /* Disable sleep mode */ - regmap_update_bits(s->regmap, MAX310X_MODE1_REG, - MAX310X_MODE1_FORCESLEEP_BIT, - 0); - - max310x_wait_pll(s); - - mutex_unlock(&s->max310x_mutex); + for (i = 0; i < s->uart.nr; i++) { + s->devtype->power(&s->p[i].port, 1); + uart_resume_port(&s->uart, &s->p[i].port); + } - return uart_resume_port(&s->uart, &s->port); + return 0; } -static SIMPLE_DEV_PM_OPS(max310x_pm_ops, max310x_suspend, max310x_resume); -#define MAX310X_PM_OPS (&max310x_pm_ops) - -#else -#define MAX310X_PM_OPS NULL -#endif - #ifdef CONFIG_GPIOLIB static int max310x_gpio_get(struct gpio_chip *chip, unsigned offset) { - unsigned int val = 0; + unsigned int val; struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + struct uart_port *port = &s->p[offset / 4].port; - mutex_lock(&s->max310x_mutex); - regmap_read(s->regmap, MAX310X_GPIODATA_REG, &val); - mutex_unlock(&s->max310x_mutex); + val = max310x_port_read(port, MAX310X_GPIODATA_REG); - return !!((val >> 4) & (1 << offset)); + return !!((val >> 4) & (1 << (offset % 4))); } static void max310x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + struct uart_port *port = &s->p[offset / 4].port; - mutex_lock(&s->max310x_mutex); - regmap_update_bits(s->regmap, MAX310X_GPIODATA_REG, 1 << offset, value ? - 1 << offset : 0); - mutex_unlock(&s->max310x_mutex); + max310x_port_update(port, MAX310X_GPIODATA_REG, 1 << (offset % 4), + value ? 1 << (offset % 4) : 0); } static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + struct uart_port *port = &s->p[offset / 4].port; - mutex_lock(&s->max310x_mutex); - - regmap_update_bits(s->regmap, MAX310X_GPIOCFG_REG, 1 << offset, 0); - - mutex_unlock(&s->max310x_mutex); + max310x_port_update(port, MAX310X_GPIOCFG_REG, 1 << (offset % 4), 0); return 0; } @@ -977,74 +1052,42 @@ static int max310x_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + struct uart_port *port = &s->p[offset / 4].port; - mutex_lock(&s->max310x_mutex); - - regmap_update_bits(s->regmap, MAX310X_GPIOCFG_REG, 1 << offset, - 1 << offset); - regmap_update_bits(s->regmap, MAX310X_GPIODATA_REG, 1 << offset, value ? - 1 << offset : 0); - - mutex_unlock(&s->max310x_mutex); + max310x_port_update(port, MAX310X_GPIODATA_REG, 1 << (offset % 4), + value ? 1 << (offset % 4) : 0); + max310x_port_update(port, MAX310X_GPIOCFG_REG, 1 << (offset % 4), + 1 << (offset % 4)); return 0; } #endif -/* Generic platform data */ -static struct max310x_pdata generic_plat_data = { - .driver_flags = MAX310X_EXT_CLK, - .uart_flags[0] = MAX310X_ECHO_SUPRESS, - .frequency = 26000000, -}; - -static int max310x_probe(struct spi_device *spi) +static int max310x_probe(struct device *dev, int is_spi, + struct max310x_devtype *devtype, int irq) { struct max310x_port *s; - struct device *dev = &spi->dev; - int chiptype = spi_get_device_id(spi)->driver_data; - struct max310x_pdata *pdata = dev->platform_data; - unsigned int val = 0; - int ret; + struct max310x_pdata *pdata = dev_get_platdata(dev); + int i, ret, uartclk; /* Check for IRQ */ - if (spi->irq <= 0) { + if (irq <= 0) { dev_err(dev, "No IRQ specified\n"); return -ENOTSUPP; } + if (!pdata) { + dev_err(dev, "No platform data supplied\n"); + return -EINVAL; + } + /* Alloc port structure */ - s = devm_kzalloc(dev, sizeof(struct max310x_port), GFP_KERNEL); + s = devm_kzalloc(dev, sizeof(*s) + + sizeof(struct max310x_one) * devtype->nr, GFP_KERNEL); if (!s) { dev_err(dev, "Error allocating port structure\n"); return -ENOMEM; } - dev_set_drvdata(dev, s); - - if (!pdata) { - dev_warn(dev, "No platform data supplied, using defaults\n"); - pdata = &generic_plat_data; - } - s->pdata = pdata; - - /* Individual chip settings */ - switch (chiptype) { - case MAX310X_TYPE_MAX3107: - s->name = "MAX3107"; - s->nr_gpio = 4; - s->uart.nr = 1; - s->regcfg.max_register = 0x1f; - break; - case MAX310X_TYPE_MAX3108: - s->name = "MAX3108"; - s->nr_gpio = 4; - s->uart.nr = 1; - s->regcfg.max_register = 0x1e; - break; - default: - dev_err(dev, "Unsupported chip type %i\n", chiptype); - return -ENOTSUPP; - } /* Check input frequency */ if ((pdata->driver_flags & MAX310X_EXT_CLK) && @@ -1055,13 +1098,11 @@ static int max310x_probe(struct spi_device *spi) ((pdata->frequency < 1000000) || (pdata->frequency > 4000000))) goto err_freq; - mutex_init(&s->max310x_mutex); + s->pdata = pdata; + s->devtype = devtype; + dev_set_drvdata(dev, s); - /* Setup SPI bus */ - spi->mode = SPI_MODE_0; - spi->bits_per_word = 8; - spi->max_speed_hz = 26000000; - spi_setup(spi); + mutex_init(&s->mutex); /* Setup regmap */ s->regcfg.reg_bits = 8; @@ -1069,109 +1110,100 @@ static int max310x_probe(struct spi_device *spi) s->regcfg.read_flag_mask = 0x00; s->regcfg.write_flag_mask = 0x80; s->regcfg.cache_type = REGCACHE_RBTREE; - s->regcfg.writeable_reg = max3107_8_reg_writeable; + s->regcfg.writeable_reg = max310x_reg_writeable; s->regcfg.volatile_reg = max310x_reg_volatile; s->regcfg.precious_reg = max310x_reg_precious; - s->regmap = devm_regmap_init_spi(spi, &s->regcfg); + s->regcfg.max_register = devtype->nr * 0x20 - 1; + + if (IS_ENABLED(CONFIG_SPI_MASTER) && is_spi) { + struct spi_device *spi = to_spi_device(dev); + + s->regmap = devm_regmap_init_spi(spi, &s->regcfg); + } else + return -ENOTSUPP; + if (IS_ERR(s->regmap)) { - ret = PTR_ERR(s->regmap); dev_err(dev, "Failed to initialize register map\n"); - goto err_out; - } - - /* Reset chip & check SPI function */ - ret = regmap_write(s->regmap, MAX310X_MODE2_REG, MAX310X_MODE2_RST_BIT); - if (ret) { - dev_err(dev, "SPI transfer failed\n"); - goto err_out; - } - /* Clear chip reset */ - regmap_write(s->regmap, MAX310X_MODE2_REG, 0); - - switch (chiptype) { - case MAX310X_TYPE_MAX3107: - /* Check REV ID to ensure we are talking to what we expect */ - regmap_read(s->regmap, MAX3107_REVID_REG, &val); - if (((val & MAX3107_REV_MASK) != MAX3107_REV_ID)) { - dev_err(dev, "%s ID 0x%02x does not match\n", - s->name, val); - ret = -ENODEV; - goto err_out; - } - break; - case MAX310X_TYPE_MAX3108: - /* MAX3108 have not REV ID register, we just check default value - * from clocksource register to make sure everything works. - */ - regmap_read(s->regmap, MAX310X_CLKSRC_REG, &val); - if (val != (MAX310X_CLKSRC_EXTCLK_BIT | - MAX310X_CLKSRC_PLLBYP_BIT)) { - dev_err(dev, "%s not present\n", s->name); - ret = -ENODEV; - goto err_out; - } - break; + return PTR_ERR(s->regmap); } /* Board specific configure */ - if (pdata->init) - pdata->init(); - if (pdata->suspend) - pdata->suspend(0); - - /* Calculate referecne clock */ - s->uartclk = max310x_set_ref_clk(s); - - /* Disable all interrupts */ - regmap_write(s->regmap, MAX310X_IRQEN_REG, 0); - - /* Setup MODE1 register */ - val = MAX310X_MODE1_IRQSEL_BIT; /* Enable IRQ pin */ - if (pdata->driver_flags & MAX310X_AUTOSLEEP) - val = MAX310X_MODE1_AUTOSLEEP_BIT; - regmap_write(s->regmap, MAX310X_MODE1_REG, val); - - /* Setup interrupt */ - ret = devm_request_threaded_irq(dev, spi->irq, NULL, max310x_ist, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(dev), s); - if (ret) { - dev_err(dev, "Unable to reguest IRQ %i\n", spi->irq); - goto err_out; + if (s->pdata->init) + s->pdata->init(); + + /* Check device to ensure we are talking to what we expect */ + ret = devtype->detect(dev); + if (ret) + return ret; + + for (i = 0; i < devtype->nr; i++) { + unsigned int offs = i << 5; + + /* Reset port */ + regmap_write(s->regmap, MAX310X_MODE2_REG + offs, + MAX310X_MODE2_RST_BIT); + /* Clear port reset */ + regmap_write(s->regmap, MAX310X_MODE2_REG + offs, 0); + + /* Wait for port startup */ + do { + regmap_read(s->regmap, + MAX310X_BRGDIVLSB_REG + offs, &ret); + } while (ret != 0x01); + + regmap_update_bits(s->regmap, MAX310X_MODE1_REG + offs, + MAX310X_MODE1_AUTOSLEEP_BIT, + MAX310X_MODE1_AUTOSLEEP_BIT); } + uartclk = max310x_set_ref_clk(s); + dev_dbg(dev, "Reference clock set to %i Hz\n", uartclk); + /* Register UART driver */ s->uart.owner = THIS_MODULE; - s->uart.driver_name = dev_name(dev); s->uart.dev_name = "ttyMAX"; s->uart.major = MAX310X_MAJOR; s->uart.minor = MAX310X_MINOR; + s->uart.nr = devtype->nr; ret = uart_register_driver(&s->uart); if (ret) { dev_err(dev, "Registering UART driver failed\n"); - goto err_out; + return ret; } - /* Initialize workqueue for start TX */ - s->wq = create_freezable_workqueue(dev_name(dev)); - INIT_WORK(&s->tx_work, max310x_wq_proc); - - /* Initialize UART port data */ - s->port.line = 0; - s->port.dev = dev; - s->port.irq = spi->irq; - s->port.type = PORT_MAX310X; - s->port.fifosize = MAX310X_FIFO_SIZE; - s->port.flags = UPF_SKIP_TEST | UPF_FIXED_TYPE; - s->port.iotype = UPIO_PORT; - s->port.membase = (void __iomem *)0xffffffff; /* Bogus value */ - s->port.uartclk = s->uartclk; - s->port.ops = &max310x_ops; - uart_add_one_port(&s->uart, &s->port); + for (i = 0; i < devtype->nr; i++) { + /* Initialize port data */ + s->p[i].port.line = i; + s->p[i].port.dev = dev; + s->p[i].port.irq = irq; + s->p[i].port.type = PORT_MAX310X; + s->p[i].port.fifosize = MAX310X_FIFO_SIZE; + s->p[i].port.flags = UPF_SKIP_TEST | UPF_FIXED_TYPE | + UPF_LOW_LATENCY; + s->p[i].port.iotype = UPIO_PORT; + s->p[i].port.iobase = i * 0x20; + s->p[i].port.membase = (void __iomem *)~0; + s->p[i].port.uartclk = uartclk; + s->p[i].port.ops = &max310x_ops; + /* Disable all interrupts */ + max310x_port_write(&s->p[i].port, MAX310X_IRQEN_REG, 0); + /* Clear IRQ status register */ + max310x_port_read(&s->p[i].port, MAX310X_IRQSTS_REG); + /* Enable IRQ pin */ + max310x_port_update(&s->p[i].port, MAX310X_MODE1_REG, + MAX310X_MODE1_IRQSEL_BIT, + MAX310X_MODE1_IRQSEL_BIT); + /* Initialize queue for start TX */ + INIT_WORK(&s->p[i].tx_work, max310x_wq_proc); + /* Register port */ + uart_add_one_port(&s->uart, &s->p[i].port); + /* Go to suspend mode */ + devtype->power(&s->p[i].port, 0); + } #ifdef CONFIG_GPIOLIB /* Setup GPIO cotroller */ - if (pdata->gpio_base) { + if (s->pdata->gpio_base) { s->gpio.owner = THIS_MODULE; s->gpio.dev = dev; s->gpio.label = dev_name(dev); @@ -1179,86 +1211,107 @@ static int max310x_probe(struct spi_device *spi) s->gpio.get = max310x_gpio_get; s->gpio.direction_output= max310x_gpio_direction_output; s->gpio.set = max310x_gpio_set; - s->gpio.base = pdata->gpio_base; - s->gpio.ngpio = s->nr_gpio; + s->gpio.base = s->pdata->gpio_base; + s->gpio.ngpio = devtype->nr * 4; s->gpio.can_sleep = 1; - if (gpiochip_add(&s->gpio)) { - /* Indicate that we should not call gpiochip_remove */ - s->gpio.base = 0; - } + if (!gpiochip_add(&s->gpio)) + s->gpio_used = 1; } else dev_info(dev, "GPIO support not enabled\n"); #endif - /* Go to suspend mode */ - if (pdata->suspend) - pdata->suspend(1); + /* Setup interrupt */ + ret = devm_request_threaded_irq(dev, irq, NULL, max310x_ist, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(dev), s); + if (ret) { + dev_err(dev, "Unable to reguest IRQ %i\n", irq); +#ifdef CONFIG_GPIOLIB + if (s->gpio_used) + WARN_ON(gpiochip_remove(&s->gpio)); +#endif + } - return 0; + return ret; err_freq: dev_err(dev, "Frequency parameter incorrect\n"); - ret = -EINVAL; - -err_out: - dev_set_drvdata(dev, NULL); - - return ret; + return -EINVAL; } -static int max310x_remove(struct spi_device *spi) +static int max310x_remove(struct device *dev) { - struct device *dev = &spi->dev; struct max310x_port *s = dev_get_drvdata(dev); - int ret = 0; - - dev_dbg(dev, "Removing port\n"); - - devm_free_irq(dev, s->port.irq, s); - - destroy_workqueue(s->wq); + int i, ret = 0; - uart_remove_one_port(&s->uart, &s->port); + for (i = 0; i < s->uart.nr; i++) { + cancel_work_sync(&s->p[i].tx_work); + uart_remove_one_port(&s->uart, &s->p[i].port); + s->devtype->power(&s->p[i].port, 0); + } uart_unregister_driver(&s->uart); #ifdef CONFIG_GPIOLIB - if (s->pdata->gpio_base) { + if (s->gpio_used) ret = gpiochip_remove(&s->gpio); - if (ret) - dev_err(dev, "Failed to remove gpio chip: %d\n", ret); - } #endif - dev_set_drvdata(dev, NULL); - - if (s->pdata->suspend) - s->pdata->suspend(1); if (s->pdata->exit) s->pdata->exit(); return ret; } +#ifdef CONFIG_SPI_MASTER +static int max310x_spi_probe(struct spi_device *spi) +{ + struct max310x_devtype *devtype = + (struct max310x_devtype *)spi_get_device_id(spi)->driver_data; + int ret; + + /* Setup SPI bus */ + spi->bits_per_word = 8; + spi->mode = spi->mode ? : SPI_MODE_0; + spi->max_speed_hz = spi->max_speed_hz ? : 26000000; + ret = spi_setup(spi); + if (ret) { + dev_err(&spi->dev, "SPI setup failed\n"); + return ret; + } + + return max310x_probe(&spi->dev, 1, devtype, spi->irq); +} + +static int max310x_spi_remove(struct spi_device *spi) +{ + return max310x_remove(&spi->dev); +} + +static SIMPLE_DEV_PM_OPS(max310x_pm_ops, max310x_suspend, max310x_resume); + static const struct spi_device_id max310x_id_table[] = { - { "max3107", MAX310X_TYPE_MAX3107 }, - { "max3108", MAX310X_TYPE_MAX3108 }, + { "max3107", (kernel_ulong_t)&max3107_devtype, }, + { "max3108", (kernel_ulong_t)&max3108_devtype, }, + { "max3109", (kernel_ulong_t)&max3109_devtype, }, + { "max14830", (kernel_ulong_t)&max14830_devtype, }, { } }; MODULE_DEVICE_TABLE(spi, max310x_id_table); -static struct spi_driver max310x_driver = { +static struct spi_driver max310x_uart_driver = { .driver = { - .name = "max310x", + .name = MAX310X_NAME, .owner = THIS_MODULE, - .pm = MAX310X_PM_OPS, + .pm = &max310x_pm_ops, }, - .probe = max310x_probe, - .remove = max310x_remove, + .probe = max310x_spi_probe, + .remove = max310x_spi_remove, .id_table = max310x_id_table, }; -module_spi_driver(max310x_driver); +module_spi_driver(max310x_uart_driver); +#endif -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); MODULE_DESCRIPTION("MAX310X serial driver"); diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index 65be0c00c4bf..0edfaf8cd269 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -24,6 +24,7 @@ #include <linux/serial_core.h> #include <linux/io.h> #include <linux/uaccess.h> +#include <linux/platform_device.h> #include <asm/coldfire.h> #include <asm/mcfsim.h> #include <asm/mcfuart.h> @@ -324,7 +325,9 @@ static void mcf_rx_chars(struct mcf_uart *pp) uart_insert_char(port, status, MCFUART_USR_RXOVERRUN, ch, flag); } + spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); + spin_lock(&port->lock); } /****************************************************************************/ @@ -644,7 +647,7 @@ static struct uart_driver mcf_driver = { static int mcf_probe(struct platform_device *pdev) { - struct mcf_platform_uart *platp = pdev->dev.platform_data; + struct mcf_platform_uart *platp = dev_get_platdata(&pdev->dev); struct uart_port *port; int i; diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c index 5f4765a7a5c5..d3db042f649e 100644 --- a/drivers/tty/serial/mfd.c +++ b/drivers/tty/serial/mfd.c @@ -21,6 +21,10 @@ * be triggered */ +#if defined(CONFIG_SERIAL_MFD_HSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + #include <linux/module.h> #include <linux/init.h> #include <linux/console.h> @@ -382,7 +386,7 @@ static void serial_hsu_stop_tx(struct uart_port *port) /* This is always called in spinlock protected mode, so * modify timeout timer is safe here */ -void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts) +void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts, unsigned long *flags) { struct hsu_dma_buffer *dbuf = &up->rxbuf; struct hsu_dma_chan *chan = up->rxc; @@ -434,7 +438,9 @@ void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts) | (0x1 << 16) | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ ); + spin_unlock_irqrestore(&up->port.lock, *flags); tty_flip_buffer_push(tport); + spin_lock_irqsave(&up->port.lock, *flags); chan_writel(chan, HSU_CH_CR, 0x3); @@ -455,7 +461,8 @@ static void serial_hsu_stop_rx(struct uart_port *port) } } -static inline void receive_chars(struct uart_hsu_port *up, int *status) +static inline void receive_chars(struct uart_hsu_port *up, int *status, + unsigned long *flags) { unsigned int ch, flag; unsigned int max_count = 256; @@ -515,7 +522,10 @@ static inline void receive_chars(struct uart_hsu_port *up, int *status) ignore_char: *status = serial_in(up, UART_LSR); } while ((*status & UART_LSR_DR) && max_count--); + + spin_unlock_irqrestore(&up->port.lock, *flags); tty_flip_buffer_push(&up->port.state->port); + spin_lock_irqsave(&up->port.lock, *flags); } static void transmit_chars(struct uart_hsu_port *up) @@ -609,7 +619,7 @@ static irqreturn_t port_irq(int irq, void *dev_id) lsr = serial_in(up, UART_LSR); if (lsr & UART_LSR_DR) - receive_chars(up, &lsr); + receive_chars(up, &lsr, &flags); check_modem_status(up); /* lsr will be renewed during the receive_chars */ @@ -639,7 +649,7 @@ static inline void dma_chan_irq(struct hsu_dma_chan *chan) /* Rx channel */ if (chan->dirt == DMA_FROM_DEVICE) - hsu_dma_rx(up, int_sts); + hsu_dma_rx(up, int_sts, &flags); /* Tx channel */ if (chan->dirt == DMA_TO_DEVICE) { @@ -1248,13 +1258,8 @@ static int serial_hsu_resume(struct pci_dev *pdev) #ifdef CONFIG_PM_RUNTIME static int serial_hsu_runtime_idle(struct device *dev) { - int err; - - err = pm_schedule_suspend(dev, 500); - if (err) - return -EBUSY; - - return 0; + pm_schedule_suspend(dev, 500); + return -EBUSY; } static int serial_hsu_runtime_suspend(struct device *dev) diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index f51b280f3bf2..5be1df39f9f5 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -84,16 +84,6 @@ static void mpc52xx_uart_of_enumerate(void); static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id); static irqreturn_t mpc5xxx_uart_process_int(struct uart_port *port); - -/* Simple macro to test if a port is console or not. This one is taken - * for serial_core.c and maybe should be moved to serial_core.h ? */ -#ifdef CONFIG_SERIAL_CORE_CONSOLE -#define uart_console(port) \ - ((port)->cons && (port)->cons->index == (port)->line) -#else -#define uart_console(port) (0) -#endif - /* ======================================================================== */ /* PSC fifo operations for isolating differences between 52xx and 512x */ /* ======================================================================== */ @@ -117,11 +107,22 @@ struct psc_ops { unsigned int (*set_baudrate)(struct uart_port *port, struct ktermios *new, struct ktermios *old); + int (*clock_alloc)(struct uart_port *port); + void (*clock_relse)(struct uart_port *port); int (*clock)(struct uart_port *port, int enable); int (*fifoc_init)(void); void (*fifoc_uninit)(void); void (*get_irq)(struct uart_port *, struct device_node *); irqreturn_t (*handle_irq)(struct uart_port *port); + u16 (*get_status)(struct uart_port *port); + u8 (*get_ipcr)(struct uart_port *port); + void (*command)(struct uart_port *port, u8 cmd); + void (*set_mode)(struct uart_port *port, u8 mr1, u8 mr2); + void (*set_rts)(struct uart_port *port, int state); + void (*enable_ms)(struct uart_port *port); + void (*set_sicr)(struct uart_port *port, u32 val); + void (*set_imr)(struct uart_port *port, u16 val); + u8 (*get_mr1)(struct uart_port *port); }; /* setting the prescaler and divisor reg is common for all chips */ @@ -134,6 +135,65 @@ static inline void mpc52xx_set_divisor(struct mpc52xx_psc __iomem *psc, out_8(&psc->ctlr, divisor & 0xff); } +static u16 mpc52xx_psc_get_status(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_status); +} + +static u8 mpc52xx_psc_get_ipcr(struct uart_port *port) +{ + return in_8(&PSC(port)->mpc52xx_psc_ipcr); +} + +static void mpc52xx_psc_command(struct uart_port *port, u8 cmd) +{ + out_8(&PSC(port)->command, cmd); +} + +static void mpc52xx_psc_set_mode(struct uart_port *port, u8 mr1, u8 mr2) +{ + out_8(&PSC(port)->command, MPC52xx_PSC_SEL_MODE_REG_1); + out_8(&PSC(port)->mode, mr1); + out_8(&PSC(port)->mode, mr2); +} + +static void mpc52xx_psc_set_rts(struct uart_port *port, int state) +{ + if (state) + out_8(&PSC(port)->op1, MPC52xx_PSC_OP_RTS); + else + out_8(&PSC(port)->op0, MPC52xx_PSC_OP_RTS); +} + +static void mpc52xx_psc_enable_ms(struct uart_port *port) +{ + struct mpc52xx_psc __iomem *psc = PSC(port); + + /* clear D_*-bits by reading them */ + in_8(&psc->mpc52xx_psc_ipcr); + /* enable CTS and DCD as IPC interrupts */ + out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD); + + port->read_status_mask |= MPC52xx_PSC_IMR_IPC; + out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); +} + +static void mpc52xx_psc_set_sicr(struct uart_port *port, u32 val) +{ + out_be32(&PSC(port)->sicr, val); +} + +static void mpc52xx_psc_set_imr(struct uart_port *port, u16 val) +{ + out_be16(&PSC(port)->mpc52xx_psc_imr, val); +} + +static u8 mpc52xx_psc_get_mr1(struct uart_port *port) +{ + out_8(&PSC(port)->command, MPC52xx_PSC_SEL_MODE_REG_1); + return in_8(&PSC(port)->mode); +} + #ifdef CONFIG_PPC_MPC52xx #define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1)) static void mpc52xx_psc_fifo_init(struct uart_port *port) @@ -304,6 +364,15 @@ static struct psc_ops mpc52xx_psc_ops = { .set_baudrate = mpc5200_psc_set_baudrate, .get_irq = mpc52xx_psc_get_irq, .handle_irq = mpc52xx_psc_handle_irq, + .get_status = mpc52xx_psc_get_status, + .get_ipcr = mpc52xx_psc_get_ipcr, + .command = mpc52xx_psc_command, + .set_mode = mpc52xx_psc_set_mode, + .set_rts = mpc52xx_psc_set_rts, + .enable_ms = mpc52xx_psc_enable_ms, + .set_sicr = mpc52xx_psc_set_sicr, + .set_imr = mpc52xx_psc_set_imr, + .get_mr1 = mpc52xx_psc_get_mr1, }; static struct psc_ops mpc5200b_psc_ops = { @@ -325,6 +394,15 @@ static struct psc_ops mpc5200b_psc_ops = { .set_baudrate = mpc5200b_psc_set_baudrate, .get_irq = mpc52xx_psc_get_irq, .handle_irq = mpc52xx_psc_handle_irq, + .get_status = mpc52xx_psc_get_status, + .get_ipcr = mpc52xx_psc_get_ipcr, + .command = mpc52xx_psc_command, + .set_mode = mpc52xx_psc_set_mode, + .set_rts = mpc52xx_psc_set_rts, + .enable_ms = mpc52xx_psc_enable_ms, + .set_sicr = mpc52xx_psc_set_sicr, + .set_imr = mpc52xx_psc_set_imr, + .get_mr1 = mpc52xx_psc_get_mr1, }; #endif /* CONFIG_MPC52xx */ @@ -540,31 +618,73 @@ static irqreturn_t mpc512x_psc_handle_irq(struct uart_port *port) return IRQ_NONE; } -static int mpc512x_psc_clock(struct uart_port *port, int enable) +static struct clk *psc_mclk_clk[MPC52xx_PSC_MAXNUM]; + +/* called from within the .request_port() callback (allocation) */ +static int mpc512x_psc_alloc_clock(struct uart_port *port) +{ + int psc_num; + char clk_name[16]; + struct clk *clk; + int err; + + psc_num = (port->mapbase & 0xf00) >> 8; + snprintf(clk_name, sizeof(clk_name), "psc%d_mclk", psc_num); + clk = devm_clk_get(port->dev, clk_name); + if (IS_ERR(clk)) { + dev_err(port->dev, "Failed to get MCLK!\n"); + return PTR_ERR(clk); + } + err = clk_prepare_enable(clk); + if (err) { + dev_err(port->dev, "Failed to enable MCLK!\n"); + return err; + } + psc_mclk_clk[psc_num] = clk; + return 0; +} + +/* called from within the .release_port() callback (release) */ +static void mpc512x_psc_relse_clock(struct uart_port *port) +{ + int psc_num; + struct clk *clk; + + psc_num = (port->mapbase & 0xf00) >> 8; + clk = psc_mclk_clk[psc_num]; + if (clk) { + clk_disable_unprepare(clk); + psc_mclk_clk[psc_num] = NULL; + } +} + +/* implementation of the .clock() callback (enable/disable) */ +static int mpc512x_psc_endis_clock(struct uart_port *port, int enable) { - struct clk *psc_clk; int psc_num; - char clk_name[10]; + struct clk *psc_clk; + int ret; if (uart_console(port)) return 0; psc_num = (port->mapbase & 0xf00) >> 8; - snprintf(clk_name, sizeof(clk_name), "psc%d_mclk", psc_num); - psc_clk = clk_get(port->dev, clk_name); - if (IS_ERR(psc_clk)) { + psc_clk = psc_mclk_clk[psc_num]; + if (!psc_clk) { dev_err(port->dev, "Failed to get PSC clock entry!\n"); return -ENODEV; } - dev_dbg(port->dev, "%s %sable\n", clk_name, enable ? "en" : "dis"); - - if (enable) - clk_enable(psc_clk); - else + dev_dbg(port->dev, "mclk %sable\n", enable ? "en" : "dis"); + if (enable) { + ret = clk_enable(psc_clk); + if (ret) + dev_err(port->dev, "Failed to enable MCLK!\n"); + return ret; + } else { clk_disable(psc_clk); - - return 0; + return 0; + } } static void mpc512x_psc_get_irq(struct uart_port *port, struct device_node *np) @@ -572,6 +692,248 @@ static void mpc512x_psc_get_irq(struct uart_port *port, struct device_node *np) port->irqflags = IRQF_SHARED; port->irq = psc_fifoc_irq; } +#endif + +#ifdef CONFIG_PPC_MPC512x + +#define PSC_5125(port) ((struct mpc5125_psc __iomem *)((port)->membase)) +#define FIFO_5125(port) ((struct mpc512x_psc_fifo __iomem *)(PSC_5125(port)+1)) + +static void mpc5125_psc_fifo_init(struct uart_port *port) +{ + /* /32 prescaler */ + out_8(&PSC_5125(port)->mpc52xx_psc_clock_select, 0xdd); + + out_be32(&FIFO_5125(port)->txcmd, MPC512x_PSC_FIFO_RESET_SLICE); + out_be32(&FIFO_5125(port)->txcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); + out_be32(&FIFO_5125(port)->txalarm, 1); + out_be32(&FIFO_5125(port)->tximr, 0); + + out_be32(&FIFO_5125(port)->rxcmd, MPC512x_PSC_FIFO_RESET_SLICE); + out_be32(&FIFO_5125(port)->rxcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); + out_be32(&FIFO_5125(port)->rxalarm, 1); + out_be32(&FIFO_5125(port)->rximr, 0); + + out_be32(&FIFO_5125(port)->tximr, MPC512x_PSC_FIFO_ALARM); + out_be32(&FIFO_5125(port)->rximr, MPC512x_PSC_FIFO_ALARM); +} + +static int mpc5125_psc_raw_rx_rdy(struct uart_port *port) +{ + return !(in_be32(&FIFO_5125(port)->rxsr) & MPC512x_PSC_FIFO_EMPTY); +} + +static int mpc5125_psc_raw_tx_rdy(struct uart_port *port) +{ + return !(in_be32(&FIFO_5125(port)->txsr) & MPC512x_PSC_FIFO_FULL); +} + +static int mpc5125_psc_rx_rdy(struct uart_port *port) +{ + return in_be32(&FIFO_5125(port)->rxsr) & + in_be32(&FIFO_5125(port)->rximr) & MPC512x_PSC_FIFO_ALARM; +} + +static int mpc5125_psc_tx_rdy(struct uart_port *port) +{ + return in_be32(&FIFO_5125(port)->txsr) & + in_be32(&FIFO_5125(port)->tximr) & MPC512x_PSC_FIFO_ALARM; +} + +static int mpc5125_psc_tx_empty(struct uart_port *port) +{ + return in_be32(&FIFO_5125(port)->txsr) & MPC512x_PSC_FIFO_EMPTY; +} + +static void mpc5125_psc_stop_rx(struct uart_port *port) +{ + unsigned long rx_fifo_imr; + + rx_fifo_imr = in_be32(&FIFO_5125(port)->rximr); + rx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_5125(port)->rximr, rx_fifo_imr); +} + +static void mpc5125_psc_start_tx(struct uart_port *port) +{ + unsigned long tx_fifo_imr; + + tx_fifo_imr = in_be32(&FIFO_5125(port)->tximr); + tx_fifo_imr |= MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_5125(port)->tximr, tx_fifo_imr); +} + +static void mpc5125_psc_stop_tx(struct uart_port *port) +{ + unsigned long tx_fifo_imr; + + tx_fifo_imr = in_be32(&FIFO_5125(port)->tximr); + tx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_5125(port)->tximr, tx_fifo_imr); +} + +static void mpc5125_psc_rx_clr_irq(struct uart_port *port) +{ + out_be32(&FIFO_5125(port)->rxisr, in_be32(&FIFO_5125(port)->rxisr)); +} + +static void mpc5125_psc_tx_clr_irq(struct uart_port *port) +{ + out_be32(&FIFO_5125(port)->txisr, in_be32(&FIFO_5125(port)->txisr)); +} + +static void mpc5125_psc_write_char(struct uart_port *port, unsigned char c) +{ + out_8(&FIFO_5125(port)->txdata_8, c); +} + +static unsigned char mpc5125_psc_read_char(struct uart_port *port) +{ + return in_8(&FIFO_5125(port)->rxdata_8); +} + +static void mpc5125_psc_cw_disable_ints(struct uart_port *port) +{ + port->read_status_mask = + in_be32(&FIFO_5125(port)->tximr) << 16 | + in_be32(&FIFO_5125(port)->rximr); + out_be32(&FIFO_5125(port)->tximr, 0); + out_be32(&FIFO_5125(port)->rximr, 0); +} + +static void mpc5125_psc_cw_restore_ints(struct uart_port *port) +{ + out_be32(&FIFO_5125(port)->tximr, + (port->read_status_mask >> 16) & 0x7f); + out_be32(&FIFO_5125(port)->rximr, port->read_status_mask & 0x7f); +} + +static inline void mpc5125_set_divisor(struct mpc5125_psc __iomem *psc, + u8 prescaler, unsigned int divisor) +{ + /* select prescaler */ + out_8(&psc->mpc52xx_psc_clock_select, prescaler); + out_8(&psc->ctur, divisor >> 8); + out_8(&psc->ctlr, divisor & 0xff); +} + +static unsigned int mpc5125_psc_set_baudrate(struct uart_port *port, + struct ktermios *new, + struct ktermios *old) +{ + unsigned int baud; + unsigned int divisor; + + /* + * Calculate with a /16 prescaler here. + */ + + /* uartclk contains the ips freq */ + baud = uart_get_baud_rate(port, new, old, + port->uartclk / (16 * 0xffff) + 1, + port->uartclk / 16); + divisor = (port->uartclk + 8 * baud) / (16 * baud); + + /* enable the /16 prescaler and set the divisor */ + mpc5125_set_divisor(PSC_5125(port), 0xdd, divisor); + return baud; +} + +/* + * MPC5125 have compatible PSC FIFO Controller. + * Special init not needed. + */ +static u16 mpc5125_psc_get_status(struct uart_port *port) +{ + return in_be16(&PSC_5125(port)->mpc52xx_psc_status); +} + +static u8 mpc5125_psc_get_ipcr(struct uart_port *port) +{ + return in_8(&PSC_5125(port)->mpc52xx_psc_ipcr); +} + +static void mpc5125_psc_command(struct uart_port *port, u8 cmd) +{ + out_8(&PSC_5125(port)->command, cmd); +} + +static void mpc5125_psc_set_mode(struct uart_port *port, u8 mr1, u8 mr2) +{ + out_8(&PSC_5125(port)->mr1, mr1); + out_8(&PSC_5125(port)->mr2, mr2); +} + +static void mpc5125_psc_set_rts(struct uart_port *port, int state) +{ + if (state & TIOCM_RTS) + out_8(&PSC_5125(port)->op1, MPC52xx_PSC_OP_RTS); + else + out_8(&PSC_5125(port)->op0, MPC52xx_PSC_OP_RTS); +} + +static void mpc5125_psc_enable_ms(struct uart_port *port) +{ + struct mpc5125_psc __iomem *psc = PSC_5125(port); + + /* clear D_*-bits by reading them */ + in_8(&psc->mpc52xx_psc_ipcr); + /* enable CTS and DCD as IPC interrupts */ + out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD); + + port->read_status_mask |= MPC52xx_PSC_IMR_IPC; + out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); +} + +static void mpc5125_psc_set_sicr(struct uart_port *port, u32 val) +{ + out_be32(&PSC_5125(port)->sicr, val); +} + +static void mpc5125_psc_set_imr(struct uart_port *port, u16 val) +{ + out_be16(&PSC_5125(port)->mpc52xx_psc_imr, val); +} + +static u8 mpc5125_psc_get_mr1(struct uart_port *port) +{ + return in_8(&PSC_5125(port)->mr1); +} + +static struct psc_ops mpc5125_psc_ops = { + .fifo_init = mpc5125_psc_fifo_init, + .raw_rx_rdy = mpc5125_psc_raw_rx_rdy, + .raw_tx_rdy = mpc5125_psc_raw_tx_rdy, + .rx_rdy = mpc5125_psc_rx_rdy, + .tx_rdy = mpc5125_psc_tx_rdy, + .tx_empty = mpc5125_psc_tx_empty, + .stop_rx = mpc5125_psc_stop_rx, + .start_tx = mpc5125_psc_start_tx, + .stop_tx = mpc5125_psc_stop_tx, + .rx_clr_irq = mpc5125_psc_rx_clr_irq, + .tx_clr_irq = mpc5125_psc_tx_clr_irq, + .write_char = mpc5125_psc_write_char, + .read_char = mpc5125_psc_read_char, + .cw_disable_ints = mpc5125_psc_cw_disable_ints, + .cw_restore_ints = mpc5125_psc_cw_restore_ints, + .set_baudrate = mpc5125_psc_set_baudrate, + .clock_alloc = mpc512x_psc_alloc_clock, + .clock_relse = mpc512x_psc_relse_clock, + .clock = mpc512x_psc_endis_clock, + .fifoc_init = mpc512x_psc_fifoc_init, + .fifoc_uninit = mpc512x_psc_fifoc_uninit, + .get_irq = mpc512x_psc_get_irq, + .handle_irq = mpc512x_psc_handle_irq, + .get_status = mpc5125_psc_get_status, + .get_ipcr = mpc5125_psc_get_ipcr, + .command = mpc5125_psc_command, + .set_mode = mpc5125_psc_set_mode, + .set_rts = mpc5125_psc_set_rts, + .enable_ms = mpc5125_psc_enable_ms, + .set_sicr = mpc5125_psc_set_sicr, + .set_imr = mpc5125_psc_set_imr, + .get_mr1 = mpc5125_psc_get_mr1, +}; static struct psc_ops mpc512x_psc_ops = { .fifo_init = mpc512x_psc_fifo_init, @@ -590,13 +952,25 @@ static struct psc_ops mpc512x_psc_ops = { .cw_disable_ints = mpc512x_psc_cw_disable_ints, .cw_restore_ints = mpc512x_psc_cw_restore_ints, .set_baudrate = mpc512x_psc_set_baudrate, - .clock = mpc512x_psc_clock, + .clock_alloc = mpc512x_psc_alloc_clock, + .clock_relse = mpc512x_psc_relse_clock, + .clock = mpc512x_psc_endis_clock, .fifoc_init = mpc512x_psc_fifoc_init, .fifoc_uninit = mpc512x_psc_fifoc_uninit, .get_irq = mpc512x_psc_get_irq, .handle_irq = mpc512x_psc_handle_irq, + .get_status = mpc52xx_psc_get_status, + .get_ipcr = mpc52xx_psc_get_ipcr, + .command = mpc52xx_psc_command, + .set_mode = mpc52xx_psc_set_mode, + .set_rts = mpc52xx_psc_set_rts, + .enable_ms = mpc52xx_psc_enable_ms, + .set_sicr = mpc52xx_psc_set_sicr, + .set_imr = mpc52xx_psc_set_imr, + .get_mr1 = mpc52xx_psc_get_mr1, }; -#endif +#endif /* CONFIG_PPC_MPC512x */ + static const struct psc_ops *psc_ops; @@ -613,17 +987,14 @@ mpc52xx_uart_tx_empty(struct uart_port *port) static void mpc52xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { - if (mctrl & TIOCM_RTS) - out_8(&PSC(port)->op1, MPC52xx_PSC_OP_RTS); - else - out_8(&PSC(port)->op0, MPC52xx_PSC_OP_RTS); + psc_ops->set_rts(port, mctrl & TIOCM_RTS); } static unsigned int mpc52xx_uart_get_mctrl(struct uart_port *port) { unsigned int ret = TIOCM_DSR; - u8 status = in_8(&PSC(port)->mpc52xx_psc_ipcr); + u8 status = psc_ops->get_ipcr(port); if (!(status & MPC52xx_PSC_CTS)) ret |= TIOCM_CTS; @@ -673,15 +1044,7 @@ mpc52xx_uart_stop_rx(struct uart_port *port) static void mpc52xx_uart_enable_ms(struct uart_port *port) { - struct mpc52xx_psc __iomem *psc = PSC(port); - - /* clear D_*-bits by reading them */ - in_8(&psc->mpc52xx_psc_ipcr); - /* enable CTS and DCD as IPC interrupts */ - out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD); - - port->read_status_mask |= MPC52xx_PSC_IMR_IPC; - out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->enable_ms(port); } static void @@ -691,9 +1054,9 @@ mpc52xx_uart_break_ctl(struct uart_port *port, int ctl) spin_lock_irqsave(&port->lock, flags); if (ctl == -1) - out_8(&PSC(port)->command, MPC52xx_PSC_START_BRK); + psc_ops->command(port, MPC52xx_PSC_START_BRK); else - out_8(&PSC(port)->command, MPC52xx_PSC_STOP_BRK); + psc_ops->command(port, MPC52xx_PSC_STOP_BRK); spin_unlock_irqrestore(&port->lock, flags); } @@ -701,7 +1064,6 @@ mpc52xx_uart_break_ctl(struct uart_port *port, int ctl) static int mpc52xx_uart_startup(struct uart_port *port) { - struct mpc52xx_psc __iomem *psc = PSC(port); int ret; if (psc_ops->clock) { @@ -717,15 +1079,15 @@ mpc52xx_uart_startup(struct uart_port *port) return ret; /* Reset/activate the port, clear and enable interrupts */ - out_8(&psc->command, MPC52xx_PSC_RST_RX); - out_8(&psc->command, MPC52xx_PSC_RST_TX); + psc_ops->command(port, MPC52xx_PSC_RST_RX); + psc_ops->command(port, MPC52xx_PSC_RST_TX); - out_be32(&psc->sicr, 0); /* UART mode DCD ignored */ + psc_ops->set_sicr(port, 0); /* UART mode DCD ignored */ psc_ops->fifo_init(port); - out_8(&psc->command, MPC52xx_PSC_TX_ENABLE); - out_8(&psc->command, MPC52xx_PSC_RX_ENABLE); + psc_ops->command(port, MPC52xx_PSC_TX_ENABLE); + psc_ops->command(port, MPC52xx_PSC_RX_ENABLE); return 0; } @@ -733,19 +1095,20 @@ mpc52xx_uart_startup(struct uart_port *port) static void mpc52xx_uart_shutdown(struct uart_port *port) { - struct mpc52xx_psc __iomem *psc = PSC(port); - /* Shut down the port. Leave TX active if on a console port */ - out_8(&psc->command, MPC52xx_PSC_RST_RX); + psc_ops->command(port, MPC52xx_PSC_RST_RX); if (!uart_console(port)) - out_8(&psc->command, MPC52xx_PSC_RST_TX); + psc_ops->command(port, MPC52xx_PSC_RST_TX); port->read_status_mask = 0; - out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->set_imr(port, port->read_status_mask); if (psc_ops->clock) psc_ops->clock(port, 0); + /* Disable interrupt */ + psc_ops->cw_disable_ints(port); + /* Release interrupt */ free_irq(port->irq, port); } @@ -754,7 +1117,6 @@ static void mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, struct ktermios *old) { - struct mpc52xx_psc __iomem *psc = PSC(port); unsigned long flags; unsigned char mr1, mr2; unsigned int j; @@ -818,13 +1180,11 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, "Some chars may have been lost.\n"); /* Reset the TX & RX */ - out_8(&psc->command, MPC52xx_PSC_RST_RX); - out_8(&psc->command, MPC52xx_PSC_RST_TX); + psc_ops->command(port, MPC52xx_PSC_RST_RX); + psc_ops->command(port, MPC52xx_PSC_RST_TX); /* Send new mode settings */ - out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); - out_8(&psc->mode, mr1); - out_8(&psc->mode, mr2); + psc_ops->set_mode(port, mr1, mr2); baud = psc_ops->set_baudrate(port, new, old); /* Update the per-port timeout */ @@ -834,8 +1194,8 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, mpc52xx_uart_enable_ms(port); /* Reenable TX & RX */ - out_8(&psc->command, MPC52xx_PSC_TX_ENABLE); - out_8(&psc->command, MPC52xx_PSC_RX_ENABLE); + psc_ops->command(port, MPC52xx_PSC_TX_ENABLE); + psc_ops->command(port, MPC52xx_PSC_RX_ENABLE); /* We're all set, release the lock */ spin_unlock_irqrestore(&port->lock, flags); @@ -854,6 +1214,9 @@ mpc52xx_uart_type(struct uart_port *port) static void mpc52xx_uart_release_port(struct uart_port *port) { + if (psc_ops->clock_relse) + psc_ops->clock_relse(port); + /* remapped by us ? */ if (port->flags & UPF_IOREMAP) { iounmap(port->membase); @@ -878,11 +1241,24 @@ mpc52xx_uart_request_port(struct uart_port *port) err = request_mem_region(port->mapbase, sizeof(struct mpc52xx_psc), "mpc52xx_psc_uart") != NULL ? 0 : -EBUSY; - if (err && (port->flags & UPF_IOREMAP)) { + if (err) + goto out_membase; + + if (psc_ops->clock_alloc) { + err = psc_ops->clock_alloc(port); + if (err) + goto out_mapregion; + } + + return 0; + +out_mapregion: + release_mem_region(port->mapbase, sizeof(struct mpc52xx_psc)); +out_membase: + if (port->flags & UPF_IOREMAP) { iounmap(port->membase); port->membase = NULL; } - return err; } @@ -963,7 +1339,7 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) flag = TTY_NORMAL; port->icount.rx++; - status = in_be16(&PSC(port)->mpc52xx_psc_status); + status = psc_ops->get_status(port); if (status & (MPC52xx_PSC_SR_PE | MPC52xx_PSC_SR_FE | @@ -983,7 +1359,7 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) } /* Clear error condition */ - out_8(&PSC(port)->command, MPC52xx_PSC_RST_ERR_STAT); + psc_ops->command(port, MPC52xx_PSC_RST_ERR_STAT); } tty_insert_flip_char(tport, ch, flag); @@ -1066,7 +1442,7 @@ mpc5xxx_uart_process_int(struct uart_port *port) if (psc_ops->tx_rdy(port)) keepgoing |= mpc52xx_uart_int_tx_chars(port); - status = in_8(&PSC(port)->mpc52xx_psc_ipcr); + status = psc_ops->get_ipcr(port); if (status & MPC52xx_PSC_D_DCD) uart_handle_dcd_change(port, !(status & MPC52xx_PSC_DCD)); @@ -1107,14 +1483,12 @@ static void __init mpc52xx_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits, int *flow) { - struct mpc52xx_psc __iomem *psc = PSC(port); unsigned char mr1; pr_debug("mpc52xx_console_get_options(port=%p)\n", port); /* Read the mode registers */ - out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); - mr1 = in_8(&psc->mode); + mr1 = psc_ops->get_mr1(port); /* CT{U,L}R are write-only ! */ *baud = CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD; @@ -1304,6 +1678,7 @@ static struct of_device_id mpc52xx_uart_of_match[] = { #endif #ifdef CONFIG_PPC_MPC512x { .compatible = "fsl,mpc5121-psc-uart", .data = &mpc512x_psc_ops, }, + { .compatible = "fsl,mpc5125-psc-uart", .data = &mpc5125_psc_ops, }, #endif {}, }; @@ -1372,15 +1747,14 @@ static int mpc52xx_uart_of_probe(struct platform_device *op) if (ret) return ret; - dev_set_drvdata(&op->dev, (void *)port); + platform_set_drvdata(op, (void *)port); return 0; } static int mpc52xx_uart_of_remove(struct platform_device *op) { - struct uart_port *port = dev_get_drvdata(&op->dev); - dev_set_drvdata(&op->dev, NULL); + struct uart_port *port = platform_get_drvdata(op); if (port) uart_remove_one_port(&mpc52xx_uart_driver, port); @@ -1392,7 +1766,7 @@ mpc52xx_uart_of_remove(struct platform_device *op) static int mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state) { - struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev); + struct uart_port *port = (struct uart_port *) platform_get_drvdata(op); if (port) uart_suspend_port(&mpc52xx_uart_driver, port); @@ -1403,7 +1777,7 @@ mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state) static int mpc52xx_uart_of_resume(struct platform_device *op) { - struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev); + struct uart_port *port = (struct uart_port *) platform_get_drvdata(op); if (port) uart_resume_port(&mpc52xx_uart_driver, port); diff --git a/drivers/tty/serial/mpsc.c b/drivers/tty/serial/mpsc.c index bc24f4931670..8d702677acc5 100644 --- a/drivers/tty/serial/mpsc.c +++ b/drivers/tty/serial/mpsc.c @@ -934,7 +934,7 @@ static int serial_polled; ****************************************************************************** */ -static int mpsc_rx_intr(struct mpsc_port_info *pi) +static int mpsc_rx_intr(struct mpsc_port_info *pi, unsigned long *flags) { struct mpsc_rx_desc *rxre; struct tty_port *port = &pi->port.state->port; @@ -969,8 +969,11 @@ static int mpsc_rx_intr(struct mpsc_port_info *pi) #endif /* Following use of tty struct directly is deprecated */ if (tty_buffer_request_room(port, bytes_in) < bytes_in) { - if (port->low_latency) + if (port->low_latency) { + spin_unlock_irqrestore(&pi->port.lock, *flags); tty_flip_buffer_push(port); + spin_lock_irqsave(&pi->port.lock, *flags); + } /* * If this failed then we will throw away the bytes * but must do so to clear interrupts. @@ -1080,7 +1083,9 @@ next_frame: if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0) mpsc_start_rx(pi); + spin_unlock_irqrestore(&pi->port.lock, *flags); tty_flip_buffer_push(port); + spin_lock_irqsave(&pi->port.lock, *flags); return rc; } @@ -1222,7 +1227,7 @@ static irqreturn_t mpsc_sdma_intr(int irq, void *dev_id) spin_lock_irqsave(&pi->port.lock, iflags); mpsc_sdma_intr_ack(pi); - if (mpsc_rx_intr(pi)) + if (mpsc_rx_intr(pi, &iflags)) rc = IRQ_HANDLED; if (mpsc_tx_intr(pi)) rc = IRQ_HANDLED; @@ -1884,7 +1889,7 @@ static int mpsc_shared_drv_probe(struct platform_device *dev) if (dev->id == 0) { if (!(rc = mpsc_shared_map_regs(dev))) { pdata = (struct mpsc_shared_pdata *) - dev->dev.platform_data; + dev_get_platdata(&dev->dev); mpsc_shared_regs.MPSC_MRR_m = pdata->mrr_val; mpsc_shared_regs.MPSC_RCRR_m= pdata->rcrr_val; @@ -2025,7 +2030,7 @@ static void mpsc_drv_get_platform_data(struct mpsc_port_info *pi, { struct mpsc_pdata *pdata; - pdata = (struct mpsc_pdata *)pd->dev.platform_data; + pdata = (struct mpsc_pdata *)dev_get_platdata(&pd->dev); pi->port.uartclk = pdata->brg_clk_freq; pi->port.iotype = UPIO_MEM; diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index 9b6ef20420c0..a67e7081f001 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -713,7 +713,7 @@ static void serial_m3110_enable_ms(struct uart_port *port) { } -struct uart_ops serial_m3110_ops = { +static struct uart_ops serial_m3110_ops = { .tx_empty = serial_m3110_tx_empty, .set_mctrl = serial_m3110_set_mctrl, .get_mctrl = serial_m3110_get_mctrl, @@ -844,7 +844,7 @@ static int serial_m3110_probe(struct spi_device *spi) pmax = max; /* Give membase a psudo value to pass serial_core's check */ - max->port.membase = (void *)0xff110000; + max->port.membase = (unsigned char __iomem *)0xff110000; uart_add_one_port(&serial_m3110_reg, &max->port); return 0; diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index b11e99797fd8..b5d779cd3c2b 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -45,16 +45,19 @@ struct msm_port { struct clk *clk; struct clk *pclk; unsigned int imr; - unsigned int *gsbi_base; + void __iomem *gsbi_base; int is_uartdm; unsigned int old_snap_state; }; -static inline void wait_for_xmitr(struct uart_port *port, int bits) +static inline void wait_for_xmitr(struct uart_port *port) { - if (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY)) - while ((msm_read(port, UART_ISR) & bits) != bits) - cpu_relax(); + while (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY)) { + if (msm_read(port, UART_ISR) & UART_ISR_TX_READY) + break; + udelay(1); + } + msm_write(port, UART_CR_CMD_RESET_TX_READY, UART_CR); } static void msm_stop_tx(struct uart_port *port) @@ -137,7 +140,10 @@ static void handle_rx_dm(struct uart_port *port, unsigned int misr) count -= 4; } + spin_unlock(&port->lock); tty_flip_buffer_push(tport); + spin_lock(&port->lock); + if (misr & (UART_IMR_RXSTALE)) msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR); msm_write(port, 0xFFFFFF, UARTDM_DMRX); @@ -189,52 +195,69 @@ static void handle_rx(struct uart_port *port) tty_insert_flip_char(tport, c, flag); } + spin_unlock(&port->lock); tty_flip_buffer_push(tport); + spin_lock(&port->lock); } -static void reset_dm_count(struct uart_port *port) +static void reset_dm_count(struct uart_port *port, int count) { - wait_for_xmitr(port, UART_ISR_TX_READY); - msm_write(port, 1, UARTDM_NCF_TX); + wait_for_xmitr(port); + msm_write(port, count, UARTDM_NCF_TX); + msm_read(port, UARTDM_NCF_TX); } static void handle_tx(struct uart_port *port) { struct circ_buf *xmit = &port->state->xmit; struct msm_port *msm_port = UART_TO_MSM(port); - int sent_tx; + unsigned int tx_count, num_chars; + unsigned int tf_pointer = 0; + + tx_count = uart_circ_chars_pending(xmit); + tx_count = min3(tx_count, (unsigned int)UART_XMIT_SIZE - xmit->tail, + port->fifosize); if (port->x_char) { if (msm_port->is_uartdm) - reset_dm_count(port); + reset_dm_count(port, tx_count + 1); msm_write(port, port->x_char, msm_port->is_uartdm ? UARTDM_TF : UART_TF); port->icount.tx++; port->x_char = 0; + } else if (tx_count && msm_port->is_uartdm) { + reset_dm_count(port, tx_count); } - if (msm_port->is_uartdm) - reset_dm_count(port); + while (tf_pointer < tx_count) { + int i; + char buf[4] = { 0 }; + unsigned int *bf = (unsigned int *)&buf; - while (msm_read(port, UART_SR) & UART_SR_TX_READY) { - if (uart_circ_empty(xmit)) { - /* disable tx interrupts */ - msm_port->imr &= ~UART_IMR_TXLEV; - msm_write(port, msm_port->imr, UART_IMR); + if (!(msm_read(port, UART_SR) & UART_SR_TX_READY)) break; - } - msm_write(port, xmit->buf[xmit->tail], - msm_port->is_uartdm ? UARTDM_TF : UART_TF); if (msm_port->is_uartdm) - reset_dm_count(port); + num_chars = min(tx_count - tf_pointer, + (unsigned int)sizeof(buf)); + else + num_chars = 1; - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - sent_tx = 1; + for (i = 0; i < num_chars; i++) { + buf[i] = xmit->buf[xmit->tail + i]; + port->icount.tx++; + } + + msm_write(port, *bf, msm_port->is_uartdm ? UARTDM_TF : UART_TF); + xmit->tail = (xmit->tail + num_chars) & (UART_XMIT_SIZE - 1); + tf_pointer += num_chars; } + /* disable tx interrupts if nothing more to send */ + if (uart_circ_empty(xmit)) + msm_stop_tx(port); + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); } @@ -295,7 +318,7 @@ static void msm_reset(struct uart_port *port) msm_write(port, UART_CR_CMD_SET_RFR, UART_CR); } -void msm_set_mctrl(struct uart_port *port, unsigned int mctrl) +static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl) { unsigned int mr; mr = msm_read(port, UART_MR1); @@ -318,70 +341,60 @@ static void msm_break_ctl(struct uart_port *port, int break_ctl) msm_write(port, UART_CR_CMD_STOP_BREAK, UART_CR); } +struct msm_baud_map { + u16 divisor; + u8 code; + u8 rxstale; +}; + +static const struct msm_baud_map * +msm_find_best_baud(struct uart_port *port, unsigned int baud) +{ + unsigned int i, divisor; + const struct msm_baud_map *entry; + static const struct msm_baud_map table[] = { + { 1536, 0x00, 1 }, + { 768, 0x11, 1 }, + { 384, 0x22, 1 }, + { 192, 0x33, 1 }, + { 96, 0x44, 1 }, + { 48, 0x55, 1 }, + { 32, 0x66, 1 }, + { 24, 0x77, 1 }, + { 16, 0x88, 1 }, + { 12, 0x99, 6 }, + { 8, 0xaa, 6 }, + { 6, 0xbb, 6 }, + { 4, 0xcc, 6 }, + { 3, 0xdd, 8 }, + { 2, 0xee, 16 }, + { 1, 0xff, 31 }, + }; + + divisor = uart_get_divisor(port, baud); + + for (i = 0, entry = table; i < ARRAY_SIZE(table); i++, entry++) + if (entry->divisor <= divisor) + break; + + return entry; /* Default to smallest divider */ +} + static int msm_set_baud_rate(struct uart_port *port, unsigned int baud) { - unsigned int baud_code, rxstale, watermark; + unsigned int rxstale, watermark; struct msm_port *msm_port = UART_TO_MSM(port); + const struct msm_baud_map *entry; - switch (baud) { - case 300: - baud_code = UART_CSR_300; - rxstale = 1; - break; - case 600: - baud_code = UART_CSR_600; - rxstale = 1; - break; - case 1200: - baud_code = UART_CSR_1200; - rxstale = 1; - break; - case 2400: - baud_code = UART_CSR_2400; - rxstale = 1; - break; - case 4800: - baud_code = UART_CSR_4800; - rxstale = 1; - break; - case 9600: - baud_code = UART_CSR_9600; - rxstale = 2; - break; - case 14400: - baud_code = UART_CSR_14400; - rxstale = 3; - break; - case 19200: - baud_code = UART_CSR_19200; - rxstale = 4; - break; - case 28800: - baud_code = UART_CSR_28800; - rxstale = 6; - break; - case 38400: - baud_code = UART_CSR_38400; - rxstale = 8; - break; - case 57600: - baud_code = UART_CSR_57600; - rxstale = 16; - break; - case 115200: - default: - baud_code = UART_CSR_115200; - baud = 115200; - rxstale = 31; - break; - } + entry = msm_find_best_baud(port, baud); if (msm_port->is_uartdm) msm_write(port, UART_CR_CMD_RESET_RX, UART_CR); - msm_write(port, baud_code, UART_CSR); + msm_write(port, entry->code, UART_CSR); /* RX stale watermark */ + rxstale = entry->rxstale; watermark = UART_IPR_STALE_LSB & rxstale; watermark |= UART_IPR_RXSTALE_LAST; watermark |= UART_IPR_STALE_TIMEOUT_MSB & (rxstale << 2); @@ -408,9 +421,8 @@ static void msm_init_clock(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); - clk_enable(msm_port->clk); - if (!IS_ERR(msm_port->pclk)) - clk_enable(msm_port->pclk); + clk_prepare_enable(msm_port->clk); + clk_prepare_enable(msm_port->pclk); msm_serial_set_mnd_regs(port); } @@ -486,7 +498,7 @@ static void msm_shutdown(struct uart_port *port) msm_port->imr = 0; msm_write(port, 0, UART_IMR); /* disable interrupts */ - clk_disable(msm_port->clk); + clk_disable_unprepare(msm_port->clk); free_irq(port->irq, port); } @@ -589,12 +601,10 @@ static void msm_release_port(struct uart_port *port) port->membase = NULL; if (msm_port->gsbi_base) { - iowrite32(GSBI_PROTOCOL_IDLE, msm_port->gsbi_base + - GSBI_CONTROL); - - gsbi_resource = platform_get_resource(pdev, - IORESOURCE_MEM, 1); + writel_relaxed(GSBI_PROTOCOL_IDLE, + msm_port->gsbi_base + GSBI_CONTROL); + gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (unlikely(!gsbi_resource)) return; @@ -637,7 +647,7 @@ static int msm_request_port(struct uart_port *port) if (!request_mem_region(gsbi_resource->start, size, "msm_serial")) { ret = -EBUSY; - goto fail_release_port; + goto fail_release_port_membase; } msm_port->gsbi_base = ioremap(gsbi_resource->start, size); @@ -651,6 +661,8 @@ static int msm_request_port(struct uart_port *port) fail_release_gsbi: release_mem_region(gsbi_resource->start, size); +fail_release_port_membase: + iounmap(port->membase); fail_release_port: release_mem_region(port->mapbase, size); return ret; @@ -666,10 +678,9 @@ static void msm_config_port(struct uart_port *port, int flags) if (ret) return; } - - if (msm_port->is_uartdm) - iowrite32(GSBI_PROTOCOL_UART, msm_port->gsbi_base + - GSBI_CONTROL); + if (msm_port->gsbi_base) + writel_relaxed(GSBI_PROTOCOL_UART, + msm_port->gsbi_base + GSBI_CONTROL); } static int msm_verify_port(struct uart_port *port, struct serial_struct *ser) @@ -688,14 +699,12 @@ static void msm_power(struct uart_port *port, unsigned int state, switch (state) { case 0: - clk_enable(msm_port->clk); - if (!IS_ERR(msm_port->pclk)) - clk_enable(msm_port->pclk); + clk_prepare_enable(msm_port->clk); + clk_prepare_enable(msm_port->pclk); break; case 3: - clk_disable(msm_port->clk); - if (!IS_ERR(msm_port->pclk)) - clk_disable(msm_port->pclk); + clk_disable_unprepare(msm_port->clk); + clk_disable_unprepare(msm_port->pclk); break; default: printk(KERN_ERR "msm_serial: Unknown PM state %d\n", state); @@ -760,32 +769,63 @@ static inline struct uart_port *get_port_from_line(unsigned int line) } #ifdef CONFIG_SERIAL_MSM_CONSOLE - -static void msm_console_putchar(struct uart_port *port, int c) -{ - struct msm_port *msm_port = UART_TO_MSM(port); - - if (msm_port->is_uartdm) - reset_dm_count(port); - - while (!(msm_read(port, UART_SR) & UART_SR_TX_READY)) - ; - msm_write(port, c, msm_port->is_uartdm ? UARTDM_TF : UART_TF); -} - static void msm_console_write(struct console *co, const char *s, unsigned int count) { + int i; struct uart_port *port; struct msm_port *msm_port; + int num_newlines = 0; + bool replaced = false; BUG_ON(co->index < 0 || co->index >= UART_NR); port = get_port_from_line(co->index); msm_port = UART_TO_MSM(port); + /* Account for newlines that will get a carriage return added */ + for (i = 0; i < count; i++) + if (s[i] == '\n') + num_newlines++; + count += num_newlines; + spin_lock(&port->lock); - uart_console_write(port, s, count, msm_console_putchar); + if (msm_port->is_uartdm) + reset_dm_count(port, count); + + i = 0; + while (i < count) { + int j; + unsigned int num_chars; + char buf[4] = { 0 }; + unsigned int *bf = (unsigned int *)&buf; + + if (msm_port->is_uartdm) + num_chars = min(count - i, (unsigned int)sizeof(buf)); + else + num_chars = 1; + + for (j = 0; j < num_chars; j++) { + char c = *s; + + if (c == '\n' && !replaced) { + buf[j] = '\r'; + j++; + replaced = true; + } + if (j < num_chars) { + buf[j] = c; + s++; + replaced = false; + } + } + + while (!(msm_read(port, UART_SR) & UART_SR_TX_READY)) + cpu_relax(); + + msm_write(port, *bf, msm_port->is_uartdm ? UARTDM_TF : UART_TF); + i += num_chars; + } spin_unlock(&port->lock); } @@ -859,6 +899,11 @@ static struct uart_driver msm_uart_driver = { static atomic_t msm_uart_next_id = ATOMIC_INIT(0); +static const struct of_device_id msm_uartdm_table[] = { + { .compatible = "qcom,msm-uartdm" }, + { } +}; + static int __init msm_serial_probe(struct platform_device *pdev) { struct msm_port *msm_port; @@ -878,25 +923,22 @@ static int __init msm_serial_probe(struct platform_device *pdev) port->dev = &pdev->dev; msm_port = UART_TO_MSM(port); - if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) + if (of_match_device(msm_uartdm_table, &pdev->dev)) msm_port->is_uartdm = 1; else msm_port->is_uartdm = 0; - if (msm_port->is_uartdm) { - msm_port->clk = clk_get(&pdev->dev, "gsbi_uart_clk"); - msm_port->pclk = clk_get(&pdev->dev, "gsbi_pclk"); - } else { - msm_port->clk = clk_get(&pdev->dev, "uart_clk"); - msm_port->pclk = ERR_PTR(-ENOENT); - } + msm_port->clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(msm_port->clk)) + return PTR_ERR(msm_port->clk); - if (unlikely(IS_ERR(msm_port->clk) || (IS_ERR(msm_port->pclk) && - msm_port->is_uartdm))) - return PTR_ERR(msm_port->clk); + if (msm_port->is_uartdm) { + msm_port->pclk = devm_clk_get(&pdev->dev, "iface"); + if (IS_ERR(msm_port->pclk)) + return PTR_ERR(msm_port->pclk); - if (msm_port->is_uartdm) clk_set_rate(msm_port->clk, 1843200); + } port->uartclk = clk_get_rate(msm_port->clk); printk(KERN_INFO "uartclk = %d\n", port->uartclk); @@ -919,15 +961,16 @@ static int __init msm_serial_probe(struct platform_device *pdev) static int msm_serial_remove(struct platform_device *pdev) { - struct msm_port *msm_port = platform_get_drvdata(pdev); + struct uart_port *port = platform_get_drvdata(pdev); - clk_put(msm_port->clk); + uart_remove_one_port(&msm_uart_driver, port); return 0; } static struct of_device_id msm_match_table[] = { { .compatible = "qcom,msm-uart" }, + { .compatible = "qcom,msm-uartdm" }, {} }; diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h index e4acef5de77e..469fda50ac63 100644 --- a/drivers/tty/serial/msm_serial.h +++ b/drivers/tty/serial/msm_serial.h @@ -38,19 +38,7 @@ #define UART_MR2_PARITY_MODE_SPACE 0x3 #define UART_MR2_PARITY_MODE 0x3 -#define UART_CSR 0x0008 -#define UART_CSR_115200 0xFF -#define UART_CSR_57600 0xEE -#define UART_CSR_38400 0xDD -#define UART_CSR_28800 0xCC -#define UART_CSR_19200 0xBB -#define UART_CSR_14400 0xAA -#define UART_CSR_9600 0x99 -#define UART_CSR_4800 0x77 -#define UART_CSR_2400 0x55 -#define UART_CSR_1200 0x44 -#define UART_CSR_600 0x33 -#define UART_CSR_300 0x22 +#define UART_CSR 0x0008 #define UART_TF 0x000C #define UARTDM_TF 0x0070 @@ -71,6 +59,7 @@ #define UART_CR_CMD_RESET_RFR (14 << 4) #define UART_CR_CMD_PROTECTION_EN (16 << 4) #define UART_CR_CMD_STALE_EVENT_ENABLE (80 << 4) +#define UART_CR_CMD_RESET_TX_READY (3 << 8) #define UART_CR_TX_DISABLE (1 << 3) #define UART_CR_TX_ENABLE (1 << 2) #define UART_CR_RX_DISABLE (1 << 1) @@ -151,6 +140,7 @@ static inline void msm_serial_set_mnd_regs_tcxo(struct uart_port *port) msm_write(port, 0xF1, UART_NREG); msm_write(port, 0x0F, UART_DREG); msm_write(port, 0x1A, UART_MNDREG); + port->uartclk = 1843200; } /* @@ -162,6 +152,7 @@ static inline void msm_serial_set_mnd_regs_tcxoby4(struct uart_port *port) msm_write(port, 0xF6, UART_NREG); msm_write(port, 0x0F, UART_DREG); msm_write(port, 0x0A, UART_MNDREG); + port->uartclk = 1843200; } static inline @@ -169,7 +160,7 @@ void msm_serial_set_mnd_regs_from_uartclk(struct uart_port *port) { if (port->uartclk == 19200000) msm_serial_set_mnd_regs_tcxo(port); - else + else if (port->uartclk == 4800000) msm_serial_set_mnd_regs_tcxoby4(port); } diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 4ca2f64861e6..48e94961a9e5 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -1618,7 +1618,7 @@ static int msm_hs_probe(struct platform_device *pdev) struct msm_hs_port *msm_uport; struct resource *resource; const struct msm_serial_hs_platform_data *pdata = - pdev->dev.platform_data; + dev_get_platdata(&pdev->dev); if (pdev->id < 0 || pdev->id >= UARTDM_NR) { printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id); diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 4f5f161896a1..10e9d70b5c40 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -32,7 +32,6 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/io.h> -#include <linux/pinctrl/consumer.h> #include <linux/of_device.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> @@ -134,10 +133,10 @@ enum mxs_auart_type { struct mxs_auart_port { struct uart_port port; -#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 */ +#define MXS_AUART_RTSCTS 4 /* bit 4 */ unsigned long flags; unsigned int ctrl; enum mxs_auart_type devtype; @@ -640,7 +639,8 @@ static void mxs_auart_settermios(struct uart_port *u, * we can only implement the DMA support for auart * in mx28. */ - if (is_imx28_auart(s) && (s->flags & MXS_AUART_DMA_CONFIG)) { + if (is_imx28_auart(s) + && test_bit(MXS_AUART_RTSCTS, &s->flags)) { if (!mxs_auart_dma_init(s)) /* enable DMA tranfer */ ctrl2 |= AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE @@ -678,11 +678,18 @@ static void mxs_auart_settermios(struct uart_port *u, static irqreturn_t mxs_auart_irq_handle(int irq, void *context) { - u32 istatus, istat; + u32 istat; struct mxs_auart_port *s = context; u32 stat = readl(s->port.membase + AUART_STAT); - istatus = istat = readl(s->port.membase + AUART_INTR); + istat = readl(s->port.membase + AUART_INTR); + + /* ack irq */ + writel(istat & (AUART_INTR_RTIS + | AUART_INTR_TXIS + | AUART_INTR_RXIS + | AUART_INTR_CTSMIS), + s->port.membase + AUART_INTR_CLR); if (istat & AUART_INTR_CTSMIS) { uart_handle_cts_change(&s->port, stat & AUART_STAT_CTS); @@ -702,12 +709,6 @@ static irqreturn_t mxs_auart_irq_handle(int irq, void *context) istat &= ~AUART_INTR_TXIS; } - writel(istatus & (AUART_INTR_RTIS - | AUART_INTR_TXIS - | AUART_INTR_RXIS - | AUART_INTR_CTSMIS), - s->port.membase + AUART_INTR_CLR); - return IRQ_HANDLED; } @@ -850,7 +851,7 @@ auart_console_write(struct console *co, const char *str, unsigned int count) struct mxs_auart_port *s; struct uart_port *port; unsigned int old_ctrl0, old_ctrl2; - unsigned int to = 1000; + unsigned int to = 20000; if (co->index >= MXS_AUART_PORTS || co->index < 0) return; @@ -871,18 +872,23 @@ auart_console_write(struct console *co, const char *str, unsigned int count) uart_console_write(port, str, count, mxs_auart_console_putchar); - /* - * Finally, wait for transmitter to become empty - * and restore the TCR - */ + /* Finally, wait for transmitter to become empty ... */ while (readl(port->membase + AUART_STAT) & AUART_STAT_BUSY) { + udelay(1); if (!to--) break; - udelay(1); } - writel(old_ctrl0, port->membase + AUART_CTRL0); - writel(old_ctrl2, port->membase + AUART_CTRL2); + /* + * ... and restore the TCR if we waited long enough for the transmitter + * to be idle. This might keep the transmitter enabled although it is + * unused, but that is better than to disable it while it is still + * transmitting. + */ + if (!(readl(port->membase + AUART_STAT) & AUART_STAT_BUSY)) { + writel(old_ctrl0, port->membase + AUART_CTRL0); + writel(old_ctrl2, port->membase + AUART_CTRL2); + } clk_disable(s->clk); } @@ -1002,7 +1008,8 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, } s->port.line = ret; - s->flags |= MXS_AUART_DMA_CONFIG; + if (of_get_property(np, "fsl,uart-has-rtscts", NULL)) + set_bit(MXS_AUART_RTSCTS, &s->flags); return 0; } @@ -1015,7 +1022,6 @@ static int mxs_auart_probe(struct platform_device *pdev) u32 version; int ret = 0; struct resource *r; - struct pinctrl *pinctrl; s = kzalloc(sizeof(struct mxs_auart_port), GFP_KERNEL); if (!s) { @@ -1029,12 +1035,6 @@ static int mxs_auart_probe(struct platform_device *pdev) else if (ret < 0) goto out_free; - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - ret = PTR_ERR(pinctrl); - goto out_free; - } - if (of_id) { pdev->id_entry = of_id->data; s->devtype = pdev->id_entry->driver_data; diff --git a/drivers/tty/serial/netx-serial.c b/drivers/tty/serial/netx-serial.c index b9a40ed70be2..0a4dd70d29eb 100644 --- a/drivers/tty/serial/netx-serial.c +++ b/drivers/tty/serial/netx-serial.c @@ -196,7 +196,7 @@ static void netx_txint(struct uart_port *port) uart_write_wakeup(port); } -static void netx_rxint(struct uart_port *port) +static void netx_rxint(struct uart_port *port, unsigned long *flags) { unsigned char rx, flg, status; @@ -236,7 +236,9 @@ static void netx_rxint(struct uart_port *port) uart_insert_char(port, status, SR_OE, rx, flg); } + spin_unlock_irqrestore(&port->lock, *flags); tty_flip_buffer_push(&port->state->port); + spin_lock_irqsave(&port->lock, *flags); } static irqreturn_t netx_int(int irq, void *dev_id) @@ -250,7 +252,7 @@ static irqreturn_t netx_int(int irq, void *dev_id) status = readl(port->membase + UART_IIR) & IIR_MASK; while (status) { if (status & IIR_RIS) - netx_rxint(port); + netx_rxint(port, &flags); if (status & IIR_TIS) netx_txint(port); if (status & IIR_MIS) { @@ -693,8 +695,6 @@ static int serial_netx_remove(struct platform_device *pdev) { struct netx_port *sport = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - if (sport) uart_remove_one_port(&netx_reg, &sport->port); diff --git a/drivers/tty/serial/nwpserial.c b/drivers/tty/serial/nwpserial.c index 549c70a2a63e..693bc6c2561e 100644 --- a/drivers/tty/serial/nwpserial.c +++ b/drivers/tty/serial/nwpserial.c @@ -149,7 +149,10 @@ static irqreturn_t nwpserial_interrupt(int irq, void *dev_id) tty_insert_flip_char(port, ch, TTY_NORMAL); } while (dcr_read(up->dcr_host, UART_LSR) & UART_LSR_DR); + spin_unlock(&up->port.lock); tty_flip_buffer_push(port); + spin_lock(&up->port.lock); + ret = IRQ_HANDLED; /* clear interrupt */ diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 39c7ea4cb14f..2caf9c6f6149 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -204,7 +204,7 @@ static int of_platform_serial_probe(struct platform_device *ofdev) info->type = port_type; info->line = ret; - dev_set_drvdata(&ofdev->dev, info); + platform_set_drvdata(ofdev, info); return 0; out: kfree(info); @@ -217,7 +217,7 @@ out: */ static int of_platform_serial_remove(struct platform_device *ofdev) { - struct of_serial_info *info = dev_get_drvdata(&ofdev->dev); + struct of_serial_info *info = platform_get_drvdata(ofdev); switch (info->type) { #ifdef CONFIG_SERIAL_8250 case PORT_8250 ... PORT_MAX_8250: diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index f0b9f6b52b32..816d1a23f9d0 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -40,9 +40,11 @@ #include <linux/pm_runtime.h> #include <linux/of.h> #include <linux/gpio.h> -#include <linux/pinctrl/consumer.h> +#include <linux/of_gpio.h> #include <linux/platform_data/serial-omap.h> +#include <dt-bindings/gpio/gpio.h> + #define OMAP_MAX_HSUART_PORTS 6 #define UART_BUILD_REVISION(x, y) (((x) << 8) | (y)) @@ -52,6 +54,11 @@ #define OMAP_UART_REV_52 0x0502 #define OMAP_UART_REV_63 0x0603 +#define OMAP_UART_TX_WAKEUP_EN BIT(7) + +/* Feature flags */ +#define OMAP_UART_WER_HAS_TX_WAKEUP BIT(0) + #define UART_ERRATA_i202_MDR1_ACCESS BIT(0) #define UART_ERRATA_i291_DMA_FORCEIDLE BIT(1) @@ -137,6 +144,7 @@ struct uart_omap_port { unsigned char dlh; unsigned char mdr1; unsigned char scr; + unsigned char wer; int use_dma; /* @@ -151,16 +159,20 @@ struct uart_omap_port { int context_loss_cnt; u32 errata; u8 wakeups_enabled; + u32 features; int DTR_gpio; int DTR_inverted; int DTR_active; + struct serial_rs485 rs485; + int rts_gpio; + struct pm_qos_request pm_qos_request; u32 latency; u32 calc_latency; struct work_struct qos_work; - struct pinctrl *pins; + bool is_suspending; }; #define to_uart_omap_port(p) ((container_of((p), struct uart_omap_port, port))) @@ -194,17 +206,17 @@ static inline void serial_omap_clear_fifos(struct uart_omap_port *up) static int serial_omap_get_context_loss_count(struct uart_omap_port *up) { - struct omap_uart_port_info *pdata = up->dev->platform_data; + struct omap_uart_port_info *pdata = dev_get_platdata(up->dev); if (!pdata || !pdata->get_context_loss_count) - return 0; + return -EINVAL; return pdata->get_context_loss_count(up->dev); } static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable) { - struct omap_uart_port_info *pdata = up->dev->platform_data; + struct omap_uart_port_info *pdata = dev_get_platdata(up->dev); if (!pdata || !pdata->enable_wakeup) return; @@ -271,13 +283,42 @@ static void serial_omap_enable_ms(struct uart_port *port) static void serial_omap_stop_tx(struct uart_port *port) { struct uart_omap_port *up = to_uart_omap_port(port); + struct circ_buf *xmit = &up->port.state->xmit; + int res; pm_runtime_get_sync(up->dev); + + /* handle rs485 */ + if (up->rs485.flags & SER_RS485_ENABLED) { + /* do nothing if current tx not yet completed */ + res = serial_in(up, UART_LSR) & UART_LSR_TEMT; + if (!res) + return; + + /* if there's no more data to send, turn off rts */ + if (uart_circ_empty(xmit)) { + /* if rts not already disabled */ + res = (up->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0; + if (gpio_get_value(up->rts_gpio) != res) { + if (up->rs485.delay_rts_after_send > 0) { + mdelay(up->rs485.delay_rts_after_send); + } + gpio_set_value(up->rts_gpio, res); + } + } + } + if (up->ier & UART_IER_THRI) { up->ier &= ~UART_IER_THRI; serial_out(up, UART_IER, up->ier); } + if ((up->rs485.flags & SER_RS485_ENABLED) && + !(up->rs485.flags & SER_RS485_RX_DURING_TX)) { + up->ier = UART_IER_RLSI | UART_IER_RDI; + serial_out(up, UART_IER, up->ier); + } + pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); } @@ -339,8 +380,26 @@ static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up) static void serial_omap_start_tx(struct uart_port *port) { struct uart_omap_port *up = to_uart_omap_port(port); + int res; pm_runtime_get_sync(up->dev); + + /* handle rs485 */ + if (up->rs485.flags & SER_RS485_ENABLED) { + /* if rts not already enabled */ + res = (up->rs485.flags & SER_RS485_RTS_ON_SEND) ? 1 : 0; + if (gpio_get_value(up->rts_gpio) != res) { + gpio_set_value(up->rts_gpio, res); + if (up->rs485.delay_rts_before_send > 0) { + mdelay(up->rs485.delay_rts_before_send); + } + } + } + + if ((up->rs485.flags & SER_RS485_ENABLED) && + !(up->rs485.flags & SER_RS485_RX_DURING_TX)) + serial_omap_stop_rx(port); + serial_omap_enable_ier_thri(up); pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); @@ -682,7 +741,11 @@ static int serial_omap_startup(struct uart_port *port) serial_out(up, UART_IER, up->ier); /* Enable module level wake up */ - serial_out(up, UART_OMAP_WER, OMAP_UART_WER_MOD_WKUP); + up->wer = OMAP_UART_WER_MOD_WKUP; + if (up->features & OMAP_UART_WER_HAS_TX_WAKEUP) + up->wer |= OMAP_UART_TX_WAKEUP_EN; + + serial_out(up, UART_OMAP_WER, up->wer); pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); @@ -1253,6 +1316,76 @@ static inline void serial_omap_add_console_port(struct uart_omap_port *up) #endif +/* Enable or disable the rs485 support */ +static void +serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) +{ + struct uart_omap_port *up = to_uart_omap_port(port); + unsigned long flags; + unsigned int mode; + int val; + + pm_runtime_get_sync(up->dev); + spin_lock_irqsave(&up->port.lock, flags); + + /* Disable interrupts from this port */ + mode = up->ier; + up->ier = 0; + serial_out(up, UART_IER, 0); + + /* store new config */ + up->rs485 = *rs485conf; + + /* + * Just as a precaution, only allow rs485 + * to be enabled if the gpio pin is valid + */ + if (gpio_is_valid(up->rts_gpio)) { + /* enable / disable rts */ + val = (up->rs485.flags & SER_RS485_ENABLED) ? + SER_RS485_RTS_AFTER_SEND : SER_RS485_RTS_ON_SEND; + val = (up->rs485.flags & val) ? 1 : 0; + gpio_set_value(up->rts_gpio, val); + } else + up->rs485.flags &= ~SER_RS485_ENABLED; + + /* Enable interrupts */ + up->ier = mode; + serial_out(up, UART_IER, up->ier); + + spin_unlock_irqrestore(&up->port.lock, flags); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); +} + +static int +serial_omap_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) +{ + struct serial_rs485 rs485conf; + + switch (cmd) { + case TIOCSRS485: + if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg, + sizeof(rs485conf))) + return -EFAULT; + + serial_omap_config_rs485(port, &rs485conf); + break; + + case TIOCGRS485: + if (copy_to_user((struct serial_rs485 *) arg, + &(to_uart_omap_port(port)->rs485), + sizeof(rs485conf))) + return -EFAULT; + break; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + + static struct uart_ops serial_omap_pops = { .tx_empty = serial_omap_tx_empty, .set_mctrl = serial_omap_set_mctrl, @@ -1274,6 +1407,7 @@ static struct uart_ops serial_omap_pops = { .request_port = serial_omap_request_port, .config_port = serial_omap_config_port, .verify_port = serial_omap_verify_port, + .ioctl = serial_omap_ioctl, #ifdef CONFIG_CONSOLE_POLL .poll_put_char = serial_omap_poll_put_char, .poll_get_char = serial_omap_poll_get_char, @@ -1289,6 +1423,22 @@ static struct uart_driver serial_omap_reg = { }; #ifdef CONFIG_PM_SLEEP +static int serial_omap_prepare(struct device *dev) +{ + struct uart_omap_port *up = dev_get_drvdata(dev); + + up->is_suspending = true; + + return 0; +} + +static void serial_omap_complete(struct device *dev) +{ + struct uart_omap_port *up = dev_get_drvdata(dev); + + up->is_suspending = false; +} + static int serial_omap_suspend(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); @@ -1307,14 +1457,17 @@ static int serial_omap_resume(struct device *dev) return 0; } -#endif +#else +#define serial_omap_prepare NULL +#define serial_omap_complete NULL +#endif /* CONFIG_PM_SLEEP */ static void omap_serial_fill_features_erratas(struct uart_omap_port *up) { u32 mvr, scheme; u16 revision, major, minor; - mvr = serial_in(up, UART_OMAP_MVER); + mvr = readl(up->port.membase + (UART_OMAP_MVER << up->port.regshift)); /* Check revision register scheme */ scheme = mvr >> OMAP_UART_MVR_SCHEME_SHIFT; @@ -1353,9 +1506,11 @@ static void omap_serial_fill_features_erratas(struct uart_omap_port *up) case OMAP_UART_REV_52: up->errata |= (UART_ERRATA_i202_MDR1_ACCESS | UART_ERRATA_i291_DMA_FORCEIDLE); + up->features |= OMAP_UART_WER_HAS_TX_WAKEUP; break; case OMAP_UART_REV_63: up->errata |= UART_ERRATA_i202_MDR1_ACCESS; + up->features |= OMAP_UART_WER_HAS_TX_WAKEUP; break; default: break; @@ -1375,15 +1530,64 @@ static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev) return omap_up_info; } +static int serial_omap_probe_rs485(struct uart_omap_port *up, + struct device_node *np) +{ + struct serial_rs485 *rs485conf = &up->rs485; + u32 rs485_delay[2]; + enum of_gpio_flags flags; + int ret; + + rs485conf->flags = 0; + up->rts_gpio = -EINVAL; + + if (!np) + return 0; + + if (of_property_read_bool(np, "rs485-rts-active-high")) + rs485conf->flags |= SER_RS485_RTS_ON_SEND; + else + rs485conf->flags |= SER_RS485_RTS_AFTER_SEND; + + /* check for tx enable gpio */ + up->rts_gpio = of_get_named_gpio_flags(np, "rts-gpio", 0, &flags); + if (gpio_is_valid(up->rts_gpio)) { + ret = gpio_request(up->rts_gpio, "omap-serial"); + if (ret < 0) + return ret; + ret = gpio_direction_output(up->rts_gpio, + flags & SER_RS485_RTS_AFTER_SEND); + if (ret < 0) + return ret; + } else + up->rts_gpio = -EINVAL; + + if (of_property_read_u32_array(np, "rs485-rts-delay", + rs485_delay, 2) == 0) { + rs485conf->delay_rts_before_send = rs485_delay[0]; + rs485conf->delay_rts_after_send = rs485_delay[1]; + } + + if (of_property_read_bool(np, "rs485-rx-during-tx")) + rs485conf->flags |= SER_RS485_RX_DURING_TX; + + if (of_property_read_bool(np, "linux,rs485-enabled-at-boot-time")) + rs485conf->flags |= SER_RS485_ENABLED; + + return 0; +} + static int serial_omap_probe(struct platform_device *pdev) { struct uart_omap_port *up; struct resource *mem, *irq; - struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data; + struct omap_uart_port_info *omap_up_info = dev_get_platdata(&pdev->dev); int ret; - if (pdev->dev.of_node) + if (pdev->dev.of_node) { omap_up_info = of_get_uart_port_info(&pdev->dev); + pdev->dev.platform_data = omap_up_info; + } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) { @@ -1448,12 +1652,9 @@ static int serial_omap_probe(struct platform_device *pdev) goto err_port_line; } - up->pins = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(up->pins)) { - dev_warn(&pdev->dev, "did not get pins for uart%i error: %li\n", - up->port.line, PTR_ERR(up->pins)); - up->pins = NULL; - } + ret = serial_omap_probe_rs485(up, pdev->dev.of_node); + if (ret < 0) + goto err_rs485; sprintf(up->name, "OMAP UART%d", up->port.line); up->port.mapbase = mem->start; @@ -1481,12 +1682,16 @@ static int serial_omap_probe(struct platform_device *pdev) INIT_WORK(&up->qos_work, serial_omap_uart_qos_work); platform_set_drvdata(pdev, up); - pm_runtime_enable(&pdev->dev); + if (omap_up_info->autosuspend_timeout == 0) + omap_up_info->autosuspend_timeout = -1; + device_init_wakeup(up->dev, true); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, omap_up_info->autosuspend_timeout); pm_runtime_irq_safe(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); omap_serial_fill_features_erratas(up); @@ -1506,6 +1711,7 @@ err_add_port: pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); err_ioremap: +err_rs485: err_port_line: dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n", pdev->id, __func__, ret); @@ -1586,18 +1792,25 @@ static void serial_omap_restore_context(struct uart_omap_port *up) serial_omap_mdr1_errataset(up, up->mdr1); else serial_out(up, UART_OMAP_MDR1, up->mdr1); + serial_out(up, UART_OMAP_WER, up->wer); } static int serial_omap_runtime_suspend(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); - struct omap_uart_port_info *pdata = dev->platform_data; if (!up) return -EINVAL; - if (!pdata) - return 0; + /* + * When using 'no_console_suspend', the console UART must not be + * suspended. Since driver suspend is managed by runtime suspend, + * preventing runtime suspend (by returning error) will keep device + * active during suspend. + */ + if (up->is_suspending && !console_suspend_enabled && + uart_console(&up->port)) + return -EBUSY; up->context_loss_cnt = serial_omap_get_context_loss_count(up); @@ -1626,7 +1839,7 @@ static int serial_omap_runtime_resume(struct device *dev) int loss_cnt = serial_omap_get_context_loss_count(up); if (loss_cnt < 0) { - dev_err(dev, "serial_omap_get_context_loss_count failed : %d\n", + dev_dbg(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) { @@ -1643,6 +1856,8 @@ static const struct dev_pm_ops serial_omap_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(serial_omap_suspend, serial_omap_resume) SET_RUNTIME_PM_OPS(serial_omap_runtime_suspend, serial_omap_runtime_resume, NULL) + .prepare = serial_omap_prepare, + .complete = serial_omap_complete, }; #if defined(CONFIG_OF) diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 21a7e179edf3..44077c0b7670 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -217,6 +217,7 @@ enum { #define FRI2_64_UARTCLK 64000000 /* 64.0000 MHz */ #define FRI2_48_UARTCLK 48000000 /* 48.0000 MHz */ #define NTC1_UARTCLK 64000000 /* 64.0000 MHz */ +#define MINNOW_UARTCLK 50000000 /* 50.0000 MHz */ struct pch_uart_buffer { unsigned char *buf; @@ -231,7 +232,7 @@ struct eg20t_port { unsigned int iobase; struct pci_dev *pdev; int fifo_size; - int uartclk; + unsigned int uartclk; int start_tx; int start_rx; int tx_empty; @@ -372,31 +373,62 @@ static const struct file_operations port_regs_ops = { }; #endif /* CONFIG_DEBUG_FS */ +static struct dmi_system_id pch_uart_dmi_table[] = { + { + .ident = "CM-iTC", + { + DMI_MATCH(DMI_BOARD_NAME, "CM-iTC"), + }, + (void *)CMITC_UARTCLK, + }, + { + .ident = "FRI2", + { + DMI_MATCH(DMI_BIOS_VERSION, "FRI2"), + }, + (void *)FRI2_64_UARTCLK, + }, + { + .ident = "Fish River Island II", + { + DMI_MATCH(DMI_PRODUCT_NAME, "Fish River Island II"), + }, + (void *)FRI2_48_UARTCLK, + }, + { + .ident = "COMe-mTT", + { + DMI_MATCH(DMI_BOARD_NAME, "COMe-mTT"), + }, + (void *)NTC1_UARTCLK, + }, + { + .ident = "nanoETXexpress-TT", + { + DMI_MATCH(DMI_BOARD_NAME, "nanoETXexpress-TT"), + }, + (void *)NTC1_UARTCLK, + }, + { + .ident = "MinnowBoard", + { + DMI_MATCH(DMI_BOARD_NAME, "MinnowBoard"), + }, + (void *)MINNOW_UARTCLK, + }, +}; + /* Return UART clock, checking for board specific clocks. */ -static int pch_uart_get_uartclk(void) +static unsigned int pch_uart_get_uartclk(void) { - const char *cmp; + const struct dmi_system_id *d; if (user_uartclk) return user_uartclk; - cmp = dmi_get_system_info(DMI_BOARD_NAME); - if (cmp && strstr(cmp, "CM-iTC")) - return CMITC_UARTCLK; - - cmp = dmi_get_system_info(DMI_BIOS_VERSION); - if (cmp && strnstr(cmp, "FRI2", 4)) - return FRI2_64_UARTCLK; - - cmp = dmi_get_system_info(DMI_PRODUCT_NAME); - if (cmp && strstr(cmp, "Fish River Island II")) - return FRI2_48_UARTCLK; - - /* Kontron COMe-mTT10 (nanoETXexpress-TT) */ - cmp = dmi_get_system_info(DMI_BOARD_NAME); - if (cmp && (strstr(cmp, "COMe-mTT") || - strstr(cmp, "nanoETXexpress-TT"))) - return NTC1_UARTCLK; + d = dmi_first_match(pch_uart_dmi_table); + if (d) + return (unsigned long)d->driver_data; return DEFAULT_UARTCLK; } @@ -417,7 +449,7 @@ static void pch_uart_hal_disable_interrupt(struct eg20t_port *priv, iowrite8(ier, priv->membase + UART_IER); } -static int pch_uart_hal_set_line(struct eg20t_port *priv, int baud, +static int pch_uart_hal_set_line(struct eg20t_port *priv, unsigned int baud, unsigned int parity, unsigned int bits, unsigned int stb) { @@ -452,7 +484,7 @@ static int pch_uart_hal_set_line(struct eg20t_port *priv, int baud, lcr |= bits; lcr |= stb; - dev_dbg(priv->port.dev, "%s:baud = %d, div = %04x, lcr = %02x (%lu)\n", + dev_dbg(priv->port.dev, "%s:baud = %u, div = %04x, lcr = %02x (%lu)\n", __func__, baud, div, lcr, jiffies); iowrite8(PCH_UART_LCR_DLAB, priv->membase + UART_LCR); iowrite8(dll, priv->membase + PCH_UART_DLL); @@ -635,30 +667,21 @@ static int pop_tx_x(struct eg20t_port *priv, unsigned char *buf) static int dma_push_rx(struct eg20t_port *priv, int size) { - struct tty_struct *tty; int room; struct uart_port *port = &priv->port; struct tty_port *tport = &port->state->port; - port = &priv->port; - tty = tty_port_tty_get(tport); - if (!tty) { - dev_dbg(priv->port.dev, "%s:tty is busy now", __func__); - return 0; - } - room = tty_buffer_request_room(tport, size); if (room < size) dev_warn(port->dev, "Rx overrun: dropping %u bytes\n", size - room); if (!room) - return room; + return 0; tty_insert_flip_string(tport, sg_virt(&priv->sg_rx), size); port->icount.rx += room; - tty_kref_put(tty); return room; } @@ -1066,6 +1089,8 @@ static void pch_uart_err_ir(struct eg20t_port *priv, unsigned int lsr) if (tty == NULL) { for (i = 0; error_msg[i] != NULL; i++) dev_err(&priv->pdev->dev, error_msg[i]); + } else { + tty_kref_put(tty); } } @@ -1358,9 +1383,8 @@ static void pch_uart_shutdown(struct uart_port *port) static void pch_uart_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - int baud; int rtn; - unsigned int parity, bits, stb; + unsigned int baud, parity, bits, stb; struct eg20t_port *priv; unsigned long flags; @@ -1493,6 +1517,7 @@ static int pch_uart_verify_port(struct uart_port *port, return 0; } +#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_PCH_UART_CONSOLE) /* * Wait for transmitter & holding register to empty */ @@ -1523,6 +1548,7 @@ static void wait_for_xmitr(struct eg20t_port *up, int bits) } } } +#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_PCH_UART_CONSOLE */ #ifdef CONFIG_CONSOLE_POLL /* diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index b1785f58b6e3..f87f1a0c8c6e 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -1798,7 +1798,6 @@ static int __exit pmz_detach(struct platform_device *pdev) uart_remove_one_port(&pmz_uart_reg, &uap->port); - platform_set_drvdata(pdev, NULL); uap->port.dev = NULL; return 0; diff --git a/drivers/tty/serial/pnx8xxx_uart.c b/drivers/tty/serial/pnx8xxx_uart.c index 7e277a5384a7..de6c05c63683 100644 --- a/drivers/tty/serial/pnx8xxx_uart.c +++ b/drivers/tty/serial/pnx8xxx_uart.c @@ -237,7 +237,10 @@ static void pnx8xxx_rx_chars(struct pnx8xxx_port *sport) status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) | ISTAT_TO_SM(serial_in(sport, PNX8XXX_ISTAT)); } + + spin_unlock(&sport->port.lock); tty_flip_buffer_push(&sport->port.state->port); + spin_lock(&sport->port.lock); } static void pnx8xxx_tx_chars(struct pnx8xxx_port *sport) @@ -801,8 +804,6 @@ static int pnx8xxx_serial_remove(struct platform_device *pdev) { struct pnx8xxx_port *sport = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - if (sport) uart_remove_one_port(&pnx8xxx_reg, &sport->port); diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index 05f504e0c271..f9f20f383760 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -332,31 +332,6 @@ static void serial_pxa_break_ctl(struct uart_port *port, int break_state) spin_unlock_irqrestore(&up->port.lock, flags); } -#if 0 -static void serial_pxa_dma_init(struct pxa_uart *up) -{ - up->rxdma = - pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_receive_dma, up); - if (up->rxdma < 0) - goto out; - up->txdma = - pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_transmit_dma, up); - if (up->txdma < 0) - goto err_txdma; - up->dmadesc = kmalloc(4 * sizeof(pxa_dma_desc), GFP_KERNEL); - if (!up->dmadesc) - goto err_alloc; - - /* ... */ -err_alloc: - pxa_free_dma(up->txdma); -err_rxdma: - pxa_free_dma(up->rxdma); -out: - return; -} -#endif - static int serial_pxa_startup(struct uart_port *port) { struct uart_pxa_port *up = (struct uart_pxa_port *)port; @@ -790,7 +765,7 @@ static struct console serial_pxa_console = { #define PXA_CONSOLE NULL #endif -struct uart_ops serial_pxa_pops = { +static struct uart_ops serial_pxa_pops = { .tx_empty = serial_pxa_tx_empty, .set_mctrl = serial_pxa_set_mctrl, .get_mctrl = serial_pxa_get_mctrl, @@ -945,8 +920,6 @@ static int serial_pxa_remove(struct platform_device *dev) { struct uart_pxa_port *sport = platform_get_drvdata(dev); - platform_set_drvdata(dev, NULL); - uart_remove_one_port(&serial_pxa_reg, &sport->port); clk_unprepare(sport->clk); @@ -970,7 +943,7 @@ static struct platform_driver serial_pxa_driver = { }, }; -int __init serial_pxa_init(void) +static int __init serial_pxa_init(void) { int ret; @@ -985,7 +958,7 @@ int __init serial_pxa_init(void) return ret; } -void __exit serial_pxa_exit(void) +static void __exit serial_pxa_exit(void) { platform_driver_unregister(&serial_pxa_driver); uart_unregister_driver(&serial_pxa_reg); diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c index a314a943f124..328d6deb6b08 100644 --- a/drivers/tty/serial/rp2.c +++ b/drivers/tty/serial/rp2.c @@ -427,7 +427,9 @@ static void rp2_rx_chars(struct rp2_uart_port *up) up->port.icount.rx++; } + spin_unlock(&up->port.lock); tty_flip_buffer_push(port); + spin_lock(&up->port.lock); } static void rp2_tx_chars(struct rp2_uart_port *up) diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c index af6b3e3ad24d..ba25722a7131 100644 --- a/drivers/tty/serial/sa1100.c +++ b/drivers/tty/serial/sa1100.c @@ -232,7 +232,10 @@ sa1100_rx_chars(struct sa1100_port *sport) status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) | UTSR0_TO_SM(UART_GET_UTSR0(sport)); } + + spin_unlock(&sport->port.lock); tty_flip_buffer_push(&sport->port.state->port); + spin_lock(&sport->port.lock); } static void sa1100_tx_chars(struct sa1100_port *sport) @@ -864,8 +867,6 @@ static int sa1100_serial_remove(struct platform_device *pdev) { struct sa1100_port *sport = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - if (sport) uart_remove_one_port(&sa1100_reg, &sport->port); diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 0c8a9fa2be6c..f3dfa19a1cb8 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -249,6 +249,8 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id) ufcon |= S3C2410_UFCON_RESETRX; wr_regl(port, S3C2410_UFCON, ufcon); rx_enabled(port) = 1; + spin_unlock_irqrestore(&port->lock, + flags); goto out; } continue; @@ -297,10 +299,11 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id) ignore_char: continue; } + + spin_unlock_irqrestore(&port->lock, flags); tty_flip_buffer_push(&port->state->port); out: - spin_unlock_irqrestore(&port->lock, flags); return IRQ_HANDLED; } @@ -1250,8 +1253,8 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) ourport->baudclk = ERR_PTR(-EINVAL); ourport->info = ourport->drv_data->info; - ourport->cfg = (pdev->dev.platform_data) ? - (struct s3c2410_uartcfg *)pdev->dev.platform_data : + ourport->cfg = (dev_get_platdata(&pdev->dev)) ? + (struct s3c2410_uartcfg *)dev_get_platdata(&pdev->dev) : ourport->drv_data->def_cfg; ourport->port.fifosize = (ourport->info->fifosize) ? @@ -1713,9 +1716,7 @@ static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = { #define S5PV210_SERIAL_DRV_DATA (kernel_ulong_t)NULL #endif -#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212) || \ - defined(CONFIG_SOC_EXYNOS4412) || defined(CONFIG_SOC_EXYNOS5250) || \ - defined(CONFIG_SOC_EXYNOS5440) +#if defined(CONFIG_ARCH_EXYNOS) static struct s3c24xx_serial_drv_data exynos4210_serial_drv_data = { .info = &(struct s3c24xx_uart_info) { .name = "Samsung Exynos4 UART", @@ -1811,7 +1812,13 @@ static int __init s3c24xx_serial_modinit(void) return ret; } - return platform_driver_register(&samsung_serial_driver); + ret = platform_driver_register(&samsung_serial_driver); + if (ret < 0) { + pr_err("Failed to register platform driver\n"); + uart_unregister_driver(&s3c24xx_uart_drv); + } + + return ret; } static void __exit s3c24xx_serial_modexit(void) diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h index 00a499ecd385..aaa617a6c499 100644 --- a/drivers/tty/serial/samsung.h +++ b/drivers/tty/serial/samsung.h @@ -68,7 +68,8 @@ struct s3c24xx_uart_port { /* register access controls */ #define portaddr(port, reg) ((port)->membase + (reg)) -#define portaddrl(port, reg) ((unsigned long *)((port)->membase + (reg))) +#define portaddrl(port, reg) \ + ((unsigned long *)(unsigned long)((port)->membase + (reg))) #define rd_regb(port, reg) (__raw_readb(portaddr(port, reg))) #define rd_regl(port, reg) (__raw_readl(portaddr(port, reg))) diff --git a/drivers/tty/serial/sc26xx.c b/drivers/tty/serial/sc26xx.c index c9735680762d..887b4f770749 100644 --- a/drivers/tty/serial/sc26xx.c +++ b/drivers/tty/serial/sc26xx.c @@ -637,7 +637,7 @@ static int sc26xx_probe(struct platform_device *dev) { struct resource *res; struct uart_sc26xx_port *up; - unsigned int *sc26xx_data = dev->dev.platform_data; + unsigned int *sc26xx_data = dev_get_platdata(&dev->dev); int err; res = platform_get_resource(dev, IORESOURCE_MEM, 0); @@ -696,7 +696,7 @@ static int sc26xx_probe(struct platform_device *dev) if (err) goto out_remove_ports; - dev_set_drvdata(&dev->dev, up); + platform_set_drvdata(dev, up); return 0; out_remove_ports: @@ -716,7 +716,7 @@ out_free_port: static int __exit sc26xx_driver_remove(struct platform_device *dev) { - struct uart_sc26xx_port *up = dev_get_drvdata(&dev->dev); + struct uart_sc26xx_port *up = platform_get_drvdata(dev); free_irq(up->port[0].irq, up); @@ -728,7 +728,6 @@ static int __exit sc26xx_driver_remove(struct platform_device *dev) kfree(up); sc26xx_port = NULL; - dev_set_drvdata(&dev->dev, NULL); return 0; } diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index c77304155410..49e9bbfe6cab 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -15,6 +15,7 @@ #define SUPPORT_SYSRQ #endif +#include <linux/clk.h> #include <linux/err.h> #include <linux/module.h> #include <linux/device.h> @@ -94,16 +95,17 @@ #define MCTRL_IBIT(cfg, sig) ((((cfg) >> (sig)) & 0xf) - LINE_IP0) #define MCTRL_OBIT(cfg, sig) ((((cfg) >> (sig)) & 0xf) - LINE_OP0) -/* Supported chip types */ -enum { - SCCNXP_TYPE_SC2681 = 2681, - SCCNXP_TYPE_SC2691 = 2691, - SCCNXP_TYPE_SC2692 = 2692, - SCCNXP_TYPE_SC2891 = 2891, - SCCNXP_TYPE_SC2892 = 2892, - SCCNXP_TYPE_SC28202 = 28202, - SCCNXP_TYPE_SC68681 = 68681, - SCCNXP_TYPE_SC68692 = 68692, +#define SCCNXP_HAVE_IO 0x00000001 +#define SCCNXP_HAVE_MR0 0x00000002 + +struct sccnxp_chip { + const char *name; + unsigned int nr; + unsigned long freq_min; + unsigned long freq_std; + unsigned long freq_max; + unsigned int flags; + unsigned int fifosize; }; struct sccnxp_port { @@ -111,16 +113,10 @@ struct sccnxp_port { struct uart_port port[SCCNXP_MAX_UARTS]; bool opened[SCCNXP_MAX_UARTS]; - const char *name; int irq; - u8 imr; - u8 addr_mask; - int freq_std; - int flags; -#define SCCNXP_HAVE_IO 0x00000001 -#define SCCNXP_HAVE_MR0 0x00000002 + struct sccnxp_chip *chip; #ifdef CONFIG_SERIAL_SCCNXP_CONSOLE struct console console; @@ -136,29 +132,94 @@ struct sccnxp_port { struct regulator *regulator; }; -static inline u8 sccnxp_raw_read(void __iomem *base, u8 reg, u8 shift) -{ - return readb(base + (reg << shift)); -} +static const struct sccnxp_chip sc2681 = { + .name = "SC2681", + .nr = 2, + .freq_min = 1000000, + .freq_std = 3686400, + .freq_max = 4000000, + .flags = SCCNXP_HAVE_IO, + .fifosize = 3, +}; -static inline void sccnxp_raw_write(void __iomem *base, u8 reg, u8 shift, u8 v) -{ - writeb(v, base + (reg << shift)); -} +static const struct sccnxp_chip sc2691 = { + .name = "SC2691", + .nr = 1, + .freq_min = 1000000, + .freq_std = 3686400, + .freq_max = 4000000, + .flags = 0, + .fifosize = 3, +}; + +static const struct sccnxp_chip sc2692 = { + .name = "SC2692", + .nr = 2, + .freq_min = 1000000, + .freq_std = 3686400, + .freq_max = 4000000, + .flags = SCCNXP_HAVE_IO, + .fifosize = 3, +}; + +static const struct sccnxp_chip sc2891 = { + .name = "SC2891", + .nr = 1, + .freq_min = 100000, + .freq_std = 3686400, + .freq_max = 8000000, + .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0, + .fifosize = 16, +}; + +static const struct sccnxp_chip sc2892 = { + .name = "SC2892", + .nr = 2, + .freq_min = 100000, + .freq_std = 3686400, + .freq_max = 8000000, + .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0, + .fifosize = 16, +}; + +static const struct sccnxp_chip sc28202 = { + .name = "SC28202", + .nr = 2, + .freq_min = 1000000, + .freq_std = 14745600, + .freq_max = 50000000, + .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0, + .fifosize = 256, +}; + +static const struct sccnxp_chip sc68681 = { + .name = "SC68681", + .nr = 2, + .freq_min = 1000000, + .freq_std = 3686400, + .freq_max = 4000000, + .flags = SCCNXP_HAVE_IO, + .fifosize = 3, +}; + +static const struct sccnxp_chip sc68692 = { + .name = "SC68692", + .nr = 2, + .freq_min = 1000000, + .freq_std = 3686400, + .freq_max = 4000000, + .flags = SCCNXP_HAVE_IO, + .fifosize = 3, +}; static inline u8 sccnxp_read(struct uart_port *port, u8 reg) { - struct sccnxp_port *s = dev_get_drvdata(port->dev); - - return sccnxp_raw_read(port->membase, reg & s->addr_mask, - port->regshift); + return readb(port->membase + (reg << port->regshift)); } static inline void sccnxp_write(struct uart_port *port, u8 reg, u8 v) { - struct sccnxp_port *s = dev_get_drvdata(port->dev); - - sccnxp_raw_write(port->membase, reg & s->addr_mask, port->regshift, v); + writeb(v, port->membase + (reg << port->regshift)); } static inline u8 sccnxp_port_read(struct uart_port *port, u8 reg) @@ -224,13 +285,14 @@ static int sccnxp_set_baud(struct uart_port *port, int baud) { struct sccnxp_port *s = dev_get_drvdata(port->dev); int div_std, tmp_baud, bestbaud = baud, besterr = -1; + struct sccnxp_chip *chip = s->chip; u8 i, acr = 0, csr = 0, mr0 = 0; /* Find best baud from table */ for (i = 0; baud_std[i].baud && besterr; i++) { - if (baud_std[i].mr0 && !(s->flags & SCCNXP_HAVE_MR0)) + if (baud_std[i].mr0 && !(chip->flags & SCCNXP_HAVE_MR0)) continue; - div_std = DIV_ROUND_CLOSEST(s->freq_std, baud_std[i].baud); + div_std = DIV_ROUND_CLOSEST(chip->freq_std, baud_std[i].baud); tmp_baud = DIV_ROUND_CLOSEST(port->uartclk, div_std); if (!sccnxp_update_best_err(baud, tmp_baud, &besterr)) { acr = baud_std[i].acr; @@ -240,7 +302,7 @@ static int sccnxp_set_baud(struct uart_port *port, int baud) } } - if (s->flags & SCCNXP_HAVE_MR0) { + if (chip->flags & SCCNXP_HAVE_MR0) { /* Enable FIFO, set half level for TX */ mr0 |= MR0_FIFO | MR0_TXLVL; /* Update MR0 */ @@ -363,7 +425,7 @@ static void sccnxp_handle_tx(struct uart_port *port) sccnxp_disable_irq(port, IMR_TXRDY); /* Set direction to input */ - if (s->flags & SCCNXP_HAVE_IO) + if (s->chip->flags & SCCNXP_HAVE_IO) sccnxp_set_bit(port, DIR_OP, 0); } return; @@ -437,7 +499,7 @@ static void sccnxp_start_tx(struct uart_port *port) spin_lock_irqsave(&s->lock, flags); /* Set direction to output */ - if (s->flags & SCCNXP_HAVE_IO) + if (s->chip->flags & SCCNXP_HAVE_IO) sccnxp_set_bit(port, DIR_OP, 1); sccnxp_enable_irq(port, IMR_TXRDY); @@ -483,7 +545,7 @@ static void sccnxp_set_mctrl(struct uart_port *port, unsigned int mctrl) struct sccnxp_port *s = dev_get_drvdata(port->dev); unsigned long flags; - if (!(s->flags & SCCNXP_HAVE_IO)) + if (!(s->chip->flags & SCCNXP_HAVE_IO)) return; spin_lock_irqsave(&s->lock, flags); @@ -501,7 +563,7 @@ static unsigned int sccnxp_get_mctrl(struct uart_port *port) struct sccnxp_port *s = dev_get_drvdata(port->dev); unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; - if (!(s->flags & SCCNXP_HAVE_IO)) + if (!(s->chip->flags & SCCNXP_HAVE_IO)) return mctrl; spin_lock_irqsave(&s->lock, flags); @@ -617,7 +679,7 @@ static void sccnxp_set_termios(struct uart_port *port, /* Setup baudrate */ baud = uart_get_baud_rate(port, termios, old, 50, - (s->flags & SCCNXP_HAVE_MR0) ? + (s->chip->flags & SCCNXP_HAVE_MR0) ? 230400 : 38400); baud = sccnxp_set_baud(port, baud); @@ -641,7 +703,7 @@ static int sccnxp_startup(struct uart_port *port) spin_lock_irqsave(&s->lock, flags); - if (s->flags & SCCNXP_HAVE_IO) { + if (s->chip->flags & SCCNXP_HAVE_IO) { /* Outputs are controlled manually */ sccnxp_write(port, SCCNXP_OPCR_REG, 0); } @@ -681,7 +743,7 @@ static void sccnxp_shutdown(struct uart_port *port) sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE | CR_TX_DISABLE); /* Leave direction to input */ - if (s->flags & SCCNXP_HAVE_IO) + if (s->chip->flags & SCCNXP_HAVE_IO) sccnxp_set_bit(port, DIR_OP, 0); spin_unlock_irqrestore(&s->lock, flags); @@ -691,7 +753,7 @@ static const char *sccnxp_type(struct uart_port *port) { struct sccnxp_port *s = dev_get_drvdata(port->dev); - return (port->type == PORT_SC26XX) ? s->name : NULL; + return (port->type == PORT_SC26XX) ? s->chip->name : NULL; } static void sccnxp_release_port(struct uart_port *port) @@ -778,19 +840,31 @@ static int sccnxp_console_setup(struct console *co, char *options) } #endif +static const struct platform_device_id sccnxp_id_table[] = { + { .name = "sc2681", .driver_data = (kernel_ulong_t)&sc2681, }, + { .name = "sc2691", .driver_data = (kernel_ulong_t)&sc2691, }, + { .name = "sc2692", .driver_data = (kernel_ulong_t)&sc2692, }, + { .name = "sc2891", .driver_data = (kernel_ulong_t)&sc2891, }, + { .name = "sc2892", .driver_data = (kernel_ulong_t)&sc2892, }, + { .name = "sc28202", .driver_data = (kernel_ulong_t)&sc28202, }, + { .name = "sc68681", .driver_data = (kernel_ulong_t)&sc68681, }, + { .name = "sc68692", .driver_data = (kernel_ulong_t)&sc68692, }, + { } +}; +MODULE_DEVICE_TABLE(platform, sccnxp_id_table); + static int sccnxp_probe(struct platform_device *pdev) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - int chiptype = pdev->id_entry->driver_data; struct sccnxp_pdata *pdata = dev_get_platdata(&pdev->dev); - int i, ret, fifosize, freq_min, freq_max; + int i, ret, uartclk; struct sccnxp_port *s; void __iomem *membase; + struct clk *clk; - if (!res) { - dev_err(&pdev->dev, "Missing memory resource data\n"); - return -EADDRNOTAVAIL; - } + membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(membase)) + return PTR_ERR(membase); s = devm_kzalloc(&pdev->dev, sizeof(struct sccnxp_port), GFP_KERNEL); if (!s) { @@ -801,99 +875,38 @@ static int sccnxp_probe(struct platform_device *pdev) spin_lock_init(&s->lock); - /* Individual chip settings */ - switch (chiptype) { - case SCCNXP_TYPE_SC2681: - s->name = "SC2681"; - s->uart.nr = 2; - s->freq_std = 3686400; - s->addr_mask = 0x0f; - s->flags = SCCNXP_HAVE_IO; - fifosize = 3; - freq_min = 1000000; - freq_max = 4000000; - break; - case SCCNXP_TYPE_SC2691: - s->name = "SC2691"; - s->uart.nr = 1; - s->freq_std = 3686400; - s->addr_mask = 0x07; - s->flags = 0; - fifosize = 3; - freq_min = 1000000; - freq_max = 4000000; - break; - case SCCNXP_TYPE_SC2692: - s->name = "SC2692"; - s->uart.nr = 2; - s->freq_std = 3686400; - s->addr_mask = 0x0f; - s->flags = SCCNXP_HAVE_IO; - fifosize = 3; - freq_min = 1000000; - freq_max = 4000000; - break; - case SCCNXP_TYPE_SC2891: - s->name = "SC2891"; - s->uart.nr = 1; - s->freq_std = 3686400; - s->addr_mask = 0x0f; - s->flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0; - fifosize = 16; - freq_min = 100000; - freq_max = 8000000; - break; - case SCCNXP_TYPE_SC2892: - s->name = "SC2892"; - s->uart.nr = 2; - s->freq_std = 3686400; - s->addr_mask = 0x0f; - s->flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0; - fifosize = 16; - freq_min = 100000; - freq_max = 8000000; - break; - case SCCNXP_TYPE_SC28202: - s->name = "SC28202"; - s->uart.nr = 2; - s->freq_std = 14745600; - s->addr_mask = 0x7f; - s->flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0; - fifosize = 256; - freq_min = 1000000; - freq_max = 50000000; - break; - case SCCNXP_TYPE_SC68681: - s->name = "SC68681"; - s->uart.nr = 2; - s->freq_std = 3686400; - s->addr_mask = 0x0f; - s->flags = SCCNXP_HAVE_IO; - fifosize = 3; - freq_min = 1000000; - freq_max = 4000000; - break; - case SCCNXP_TYPE_SC68692: - s->name = "SC68692"; - s->uart.nr = 2; - s->freq_std = 3686400; - s->addr_mask = 0x0f; - s->flags = SCCNXP_HAVE_IO; - fifosize = 3; - freq_min = 1000000; - freq_max = 4000000; - break; - default: - dev_err(&pdev->dev, "Unsupported chip type %i\n", chiptype); - ret = -ENOTSUPP; + s->chip = (struct sccnxp_chip *)pdev->id_entry->driver_data; + + s->regulator = devm_regulator_get(&pdev->dev, "vcc"); + if (!IS_ERR(s->regulator)) { + ret = regulator_enable(s->regulator); + if (ret) { + dev_err(&pdev->dev, + "Failed to enable regulator: %i\n", ret); + return ret; + } + } else if (PTR_ERR(s->regulator) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_out; + } + dev_notice(&pdev->dev, "Using default clock frequency\n"); + uartclk = s->chip->freq_std; + } else + uartclk = clk_get_rate(clk); + + /* Check input frequency */ + if ((uartclk < s->chip->freq_min) || (uartclk > s->chip->freq_max)) { + dev_err(&pdev->dev, "Frequency out of bounds\n"); + ret = -EINVAL; goto err_out; } - if (!pdata) { - dev_warn(&pdev->dev, - "No platform data supplied, using defaults\n"); - s->pdata.frequency = s->freq_std; - } else + if (pdata) memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata)); if (s->pdata.poll_time_us) { @@ -911,34 +924,11 @@ static int sccnxp_probe(struct platform_device *pdev) } } - /* Check input frequency */ - if ((s->pdata.frequency < freq_min) || - (s->pdata.frequency > freq_max)) { - dev_err(&pdev->dev, "Frequency out of bounds\n"); - ret = -EINVAL; - goto err_out; - } - - s->regulator = devm_regulator_get(&pdev->dev, "VCC"); - if (!IS_ERR(s->regulator)) { - ret = regulator_enable(s->regulator); - if (ret) { - dev_err(&pdev->dev, - "Failed to enable regulator: %i\n", ret); - return ret; - } - } - - membase = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(membase)) { - ret = PTR_ERR(membase); - goto err_out; - } - s->uart.owner = THIS_MODULE; s->uart.dev_name = "ttySC"; s->uart.major = SCCNXP_MAJOR; s->uart.minor = SCCNXP_MINOR; + s->uart.nr = s->chip->nr; #ifdef CONFIG_SERIAL_SCCNXP_CONSOLE s->uart.cons = &s->console; s->uart.cons->device = uart_console_device; @@ -960,17 +950,17 @@ static int sccnxp_probe(struct platform_device *pdev) s->port[i].dev = &pdev->dev; s->port[i].irq = s->irq; s->port[i].type = PORT_SC26XX; - s->port[i].fifosize = fifosize; + s->port[i].fifosize = s->chip->fifosize; s->port[i].flags = UPF_SKIP_TEST | UPF_FIXED_TYPE; s->port[i].iotype = UPIO_MEM; s->port[i].mapbase = res->start; s->port[i].membase = membase; s->port[i].regshift = s->pdata.reg_shift; - s->port[i].uartclk = s->pdata.frequency; + s->port[i].uartclk = uartclk; s->port[i].ops = &sccnxp_ops; uart_add_one_port(&s->uart, &s->port[i]); /* Set direction to input */ - if (s->flags & SCCNXP_HAVE_IO) + if (s->chip->flags & SCCNXP_HAVE_IO) sccnxp_set_bit(&s->port[i], DIR_OP, 0); } @@ -997,7 +987,8 @@ static int sccnxp_probe(struct platform_device *pdev) } err_out: - platform_set_drvdata(pdev, NULL); + if (!IS_ERR(s->regulator)) + return regulator_disable(s->regulator); return ret; } @@ -1016,7 +1007,6 @@ static int sccnxp_remove(struct platform_device *pdev) uart_remove_one_port(&s->uart, &s->port[i]); uart_unregister_driver(&s->uart); - platform_set_drvdata(pdev, NULL); if (!IS_ERR(s->regulator)) return regulator_disable(s->regulator); @@ -1024,19 +1014,6 @@ static int sccnxp_remove(struct platform_device *pdev) return 0; } -static const struct platform_device_id sccnxp_id_table[] = { - { "sc2681", SCCNXP_TYPE_SC2681 }, - { "sc2691", SCCNXP_TYPE_SC2691 }, - { "sc2692", SCCNXP_TYPE_SC2692 }, - { "sc2891", SCCNXP_TYPE_SC2891 }, - { "sc2892", SCCNXP_TYPE_SC2892 }, - { "sc28202", SCCNXP_TYPE_SC28202 }, - { "sc68681", SCCNXP_TYPE_SC68681 }, - { "sc68692", SCCNXP_TYPE_SC68692 }, - { }, -}; -MODULE_DEVICE_TABLE(platform, sccnxp_id_table); - static struct platform_driver sccnxp_uart_driver = { .driver = { .name = SCCNXP_NAME, diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 9799d043a9bd..0489a2bdcdf9 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -571,7 +571,9 @@ static void tegra_uart_rx_dma_complete(void *args) tegra_uart_handle_rx_pio(tup, port); if (tty) { + spin_unlock_irqrestore(&u->lock, flags); tty_flip_buffer_push(port); + spin_lock_irqsave(&u->lock, flags); tty_kref_put(tty); } tegra_uart_start_rx_dma(tup); @@ -583,11 +585,13 @@ static void tegra_uart_rx_dma_complete(void *args) spin_unlock_irqrestore(&u->lock, flags); } -static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup) +static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup, + unsigned long *flags) { struct dma_tx_state state; struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port); struct tty_port *port = &tup->uport.state->port; + struct uart_port *u = &tup->uport; int count; /* Deactivate flow control to stop sender */ @@ -604,7 +608,9 @@ static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup) tegra_uart_handle_rx_pio(tup, port); if (tty) { + spin_unlock_irqrestore(&u->lock, *flags); tty_flip_buffer_push(port); + spin_lock_irqsave(&u->lock, *flags); tty_kref_put(tty); } tegra_uart_start_rx_dma(tup); @@ -671,7 +677,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) iir = tegra_uart_read(tup, UART_IIR); if (iir & UART_IIR_NO_INT) { if (is_rx_int) { - tegra_uart_handle_rx_dma(tup); + tegra_uart_handle_rx_dma(tup, &flags); if (tup->rx_in_progress) { ier = tup->ier_shadow; ier |= (UART_IER_RLSI | UART_IER_RTOIE | @@ -726,7 +732,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) static void tegra_uart_stop_rx(struct uart_port *u) { struct tegra_uart_port *tup = to_tegra_uport(u); - struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port); + struct tty_struct *tty; struct tty_port *port = &u->state->port; struct dma_tx_state state; unsigned long ier; @@ -738,6 +744,8 @@ static void tegra_uart_stop_rx(struct uart_port *u) if (!tup->rx_in_progress) return; + tty = tty_port_tty_get(&tup->uport.state->port); + tegra_uart_wait_sym_time(tup, 1); /* wait a character interval */ ier = tup->ier_shadow; @@ -1206,7 +1214,7 @@ static struct uart_driver tegra_uart_driver = { .owner = THIS_MODULE, .driver_name = "tegra_hsuart", .dev_name = "ttyTHS", - .cons = 0, + .cons = NULL, .nr = TEGRA_UART_MAXIMUM, }; @@ -1237,13 +1245,13 @@ static int tegra_uart_parse_dt(struct platform_device *pdev, return 0; } -struct tegra_uart_chip_data tegra20_uart_chip_data = { +static struct tegra_uart_chip_data tegra20_uart_chip_data = { .tx_fifo_full_status = false, .allow_txfifo_reset_fifo_mode = true, .support_clk_src_div = false, }; -struct tegra_uart_chip_data tegra30_uart_chip_data = { +static struct tegra_uart_chip_data tegra30_uart_chip_data = { .tx_fifo_full_status = true, .allow_txfifo_reset_fifo_mode = false, .support_clk_src_div = true, @@ -1377,7 +1385,7 @@ static int __init tegra_uart_init(void) ret = platform_driver_register(&tegra_uart_platform_driver); if (ret < 0) { - pr_err("Uart platfrom driver register failed, e = %d\n", ret); + pr_err("Uart platform driver register failed, e = %d\n", ret); uart_unregister_driver(&tegra_uart_driver); return ret; } diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index f87dbfd32770..0f02351c9239 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -50,12 +50,6 @@ static struct lock_class_key port_lock_key; #define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) -#ifdef CONFIG_SERIAL_CORE_CONSOLE -#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) -#else -#define uart_console(port) (0) -#endif - static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, struct ktermios *old_termios); static void uart_wait_until_sent(struct tty_struct *tty, int timeout); @@ -2101,12 +2095,12 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port) break; } - printk(KERN_INFO "%s%s%s%d at %s (irq = %d) is a %s\n", + printk(KERN_INFO "%s%s%s%d at %s (irq = %d, base_baud = %d) is a %s\n", port->dev ? dev_name(port->dev) : "", port->dev ? ": " : "", drv->dev_name, drv->tty_driver->name_base + port->line, - address, port->irq, uart_type(port)); + address, port->irq, port->uartclk / 16, uart_type(port)); } static void diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c index fe48a0c2b4ca..440a962412da 100644 --- a/drivers/tty/serial/serial_txx9.c +++ b/drivers/tty/serial/serial_txx9.c @@ -1097,7 +1097,7 @@ static void serial_txx9_unregister_port(int line) */ static int serial_txx9_probe(struct platform_device *dev) { - struct uart_port *p = dev->dev.platform_data; + struct uart_port *p = dev_get_platdata(&dev->dev); struct uart_port port; int ret, i; diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 156418619949..537750261aaa 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -146,6 +146,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = sci_reg_invalid, [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, + [HSSRR] = sci_reg_invalid, }, /* @@ -165,6 +166,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = sci_reg_invalid, [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, + [HSSRR] = sci_reg_invalid, }, /* @@ -183,6 +185,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = sci_reg_invalid, [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, + [HSSRR] = sci_reg_invalid, }, /* @@ -201,6 +204,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = { 0x3c, 16 }, [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, + [HSSRR] = sci_reg_invalid, }, /* @@ -220,6 +224,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = sci_reg_invalid, [SCSPTR] = { 0x20, 16 }, [SCLSR] = { 0x24, 16 }, + [HSSRR] = sci_reg_invalid, }, /* @@ -238,6 +243,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = sci_reg_invalid, [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, + [HSSRR] = sci_reg_invalid, }, /* @@ -256,6 +262,26 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = sci_reg_invalid, [SCSPTR] = { 0x20, 16 }, [SCLSR] = { 0x24, 16 }, + [HSSRR] = sci_reg_invalid, + }, + + /* + * Common HSCIF definitions. + */ + [SCIx_HSCIF_REGTYPE] = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x0c, 8 }, + [SCxSR] = { 0x10, 16 }, + [SCxRDR] = { 0x14, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = { 0x20, 16 }, + [SCLSR] = { 0x24, 16 }, + [HSSRR] = { 0x40, 16 }, }, /* @@ -275,6 +301,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = sci_reg_invalid, [SCSPTR] = sci_reg_invalid, [SCLSR] = { 0x24, 16 }, + [HSSRR] = sci_reg_invalid, }, /* @@ -294,6 +321,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = { 0x20, 16 }, [SCSPTR] = { 0x24, 16 }, [SCLSR] = { 0x28, 16 }, + [HSSRR] = sci_reg_invalid, }, /* @@ -313,6 +341,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = sci_reg_invalid, [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, + [HSSRR] = sci_reg_invalid, }, }; @@ -374,6 +403,9 @@ static int sci_probe_regmap(struct plat_sci_port *cfg) */ cfg->regtype = SCIx_SH4_SCIF_REGTYPE; break; + case PORT_HSCIF: + cfg->regtype = SCIx_HSCIF_REGTYPE; + break; default: printk(KERN_ERR "Can't probe register map for given port\n"); return -EINVAL; @@ -1798,6 +1830,42 @@ static unsigned int sci_scbrr_calc(unsigned int algo_id, unsigned int bps, return ((freq + 16 * bps) / (32 * bps) - 1); } +/* calculate sample rate, BRR, and clock select for HSCIF */ +static void sci_baud_calc_hscif(unsigned int bps, unsigned long freq, + int *brr, unsigned int *srr, + unsigned int *cks) +{ + int sr, c, br, err; + int min_err = 1000; /* 100% */ + + /* Find the combination of sample rate and clock select with the + smallest deviation from the desired baud rate. */ + for (sr = 8; sr <= 32; sr++) { + for (c = 0; c <= 3; c++) { + /* integerized formulas from HSCIF documentation */ + br = freq / (sr * (1 << (2 * c + 1)) * bps) - 1; + if (br < 0 || br > 255) + continue; + err = freq / ((br + 1) * bps * sr * + (1 << (2 * c + 1)) / 1000) - 1000; + if (min_err > err) { + min_err = err; + *brr = br; + *srr = sr - 1; + *cks = c; + } + } + } + + if (min_err == 1000) { + WARN_ON(1); + /* use defaults */ + *brr = 255; + *srr = 15; + *cks = 0; + } +} + static void sci_reset(struct uart_port *port) { struct plat_sci_reg *reg; @@ -1819,8 +1887,9 @@ 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, cks; + unsigned int baud, smr_val, max_baud, cks = 0; int t = -1; + unsigned int srr = 15; /* * earlyprintk comes here early on with port->uartclk set to zero. @@ -1833,8 +1902,17 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, max_baud = port->uartclk ? port->uartclk / 16 : 115200; baud = uart_get_baud_rate(port, termios, old, 0, max_baud); - if (likely(baud && port->uartclk)) - t = sci_scbrr_calc(s->cfg->scbrr_algo_id, baud, port->uartclk); + if (likely(baud && port->uartclk)) { + if (s->cfg->scbrr_algo_id == SCBRR_ALGO_6) { + sci_baud_calc_hscif(baud, port->uartclk, &t, &srr, + &cks); + } else { + t = sci_scbrr_calc(s->cfg->scbrr_algo_id, baud, + port->uartclk); + for (cks = 0; t >= 256 && cks <= 3; cks++) + t >>= 2; + } + } sci_port_enable(s); @@ -1853,15 +1931,15 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, uart_update_timeout(port, termios->c_cflag, baud); - for (cks = 0; t >= 256 && cks <= 3; cks++) - t >>= 2; - 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); + reg = sci_getreg(port, HSSRR); + if (reg->size) + serial_port_out(port, HSSRR, srr | HSCIF_SRE); udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */ } else serial_port_out(port, SCSMR, smr_val); @@ -1947,6 +2025,8 @@ static const char *sci_type(struct uart_port *port) return "scifa"; case PORT_SCIFB: return "scifb"; + case PORT_HSCIF: + return "hscif"; } return NULL; @@ -1960,7 +2040,10 @@ static inline unsigned long sci_port_size(struct uart_port *port) * from platform resource data at such a time that ports begin to * behave more erratically. */ - return 64; + if (port->type == PORT_HSCIF) + return 96; + else + return 64; } static int sci_remap_port(struct uart_port *port) @@ -2085,6 +2168,9 @@ static int sci_init_single(struct platform_device *dev, case PORT_SCIFB: port->fifosize = 256; break; + case PORT_HSCIF: + port->fifosize = 128; + break; case PORT_SCIFA: port->fifosize = 64; break; @@ -2294,7 +2380,7 @@ static char early_serial_buf[32]; static int sci_probe_earlyprintk(struct platform_device *pdev) { - struct plat_sci_port *cfg = pdev->dev.platform_data; + struct plat_sci_port *cfg = dev_get_platdata(&pdev->dev); if (early_serial_console.data) return -EEXIST; @@ -2325,7 +2411,7 @@ static inline int sci_probe_earlyprintk(struct platform_device *pdev) #endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */ static char banner[] __initdata = - KERN_INFO "SuperH SCI(F) driver initialized\n"; + KERN_INFO "SuperH (H)SCI(F) driver initialized\n"; static struct uart_driver sci_uart_driver = { .owner = THIS_MODULE, @@ -2383,7 +2469,7 @@ static int sci_probe_single(struct platform_device *dev, static int sci_probe(struct platform_device *dev) { - struct plat_sci_port *p = dev->dev.platform_data; + struct plat_sci_port *p = dev_get_platdata(&dev->dev); struct sci_port *sp = &sci_ports[dev->id]; int ret; @@ -2484,4 +2570,4 @@ module_exit(sci_exit); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:sh-sci"); MODULE_AUTHOR("Paul Mundt"); -MODULE_DESCRIPTION("SuperH SCI(F) serial driver"); +MODULE_DESCRIPTION("SuperH (H)SCI(F) serial driver"); diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 03465b673945..61c1ad03db5b 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -20,9 +20,13 @@ #include <linux/of.h> #include <linux/slab.h> #include <linux/io.h> +#include <linux/of_gpio.h> +#include <linux/dmaengine.h> +#include <linux/dma-direction.h> +#include <linux/dma-mapping.h> +#include <linux/sirfsoc_dma.h> #include <asm/irq.h> #include <asm/mach/irq.h> -#include <linux/pinctrl/consumer.h> #include "sirfsoc_uart.h" @@ -32,6 +36,9 @@ static unsigned int sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count); static struct uart_driver sirfsoc_uart_drv; +static void sirfsoc_uart_tx_dma_complete_callback(void *param); +static void sirfsoc_uart_start_next_rx_dma(struct uart_port *port); +static void sirfsoc_uart_rx_dma_complete_callback(void *param); static const struct sirfsoc_baudrate_to_regv baudrate_to_regv[] = { {4000000, 2359296}, {3500000, 1310721}, @@ -89,6 +96,13 @@ static struct sirfsoc_uart_port sirfsoc_uart_ports[SIRFSOC_UART_NR] = { .line = 4, }, }, + [5] = { + .port = { + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF, + .line = 5, + }, + }, }; static inline struct sirfsoc_uart_port *to_sirfport(struct uart_port *port) @@ -99,21 +113,28 @@ static inline struct sirfsoc_uart_port *to_sirfport(struct uart_port *port) static inline unsigned int sirfsoc_uart_tx_empty(struct uart_port *port) { unsigned long reg; - reg = rd_regl(port, SIRFUART_TX_FIFO_STATUS); - if (reg & SIRFUART_FIFOEMPTY_MASK(port)) - return TIOCSER_TEMT; - else - return 0; + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status; + reg = rd_regl(port, ureg->sirfsoc_tx_fifo_status); + + return (reg & ufifo_st->ff_empty(port->line)) ? TIOCSER_TEMT : 0; } static unsigned int sirfsoc_uart_get_mctrl(struct uart_port *port) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); - if (!(sirfport->ms_enabled)) { + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + if (!sirfport->hw_flow_ctrl || !sirfport->ms_enabled) goto cts_asserted; - } else if (sirfport->hw_flow_ctrl) { - if (!(rd_regl(port, SIRFUART_AFC_CTRL) & - SIRFUART_CTS_IN_STATUS)) + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { + if (!(rd_regl(port, ureg->sirfsoc_afc_ctrl) & + SIRFUART_AFC_CTS_STATUS)) + goto cts_asserted; + else + goto cts_deasserted; + } else { + if (!gpio_get_value(sirfport->cts_gpio)) goto cts_asserted; else goto cts_deasserted; @@ -127,89 +148,276 @@ cts_asserted: static void sirfsoc_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; unsigned int assert = mctrl & TIOCM_RTS; unsigned int val = assert ? SIRFUART_AFC_CTRL_RX_THD : 0x0; unsigned int current_val; - if (sirfport->hw_flow_ctrl) { - current_val = rd_regl(port, SIRFUART_AFC_CTRL) & ~0xFF; + + if (!sirfport->hw_flow_ctrl || !sirfport->ms_enabled) + return; + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { + current_val = rd_regl(port, ureg->sirfsoc_afc_ctrl) & ~0xFF; val |= current_val; - wr_regl(port, SIRFUART_AFC_CTRL, val); + wr_regl(port, ureg->sirfsoc_afc_ctrl, val); + } else { + if (!val) + gpio_set_value(sirfport->rts_gpio, 1); + else + gpio_set_value(sirfport->rts_gpio, 0); } } static void sirfsoc_uart_stop_tx(struct uart_port *port) { - unsigned int regv; - regv = rd_regl(port, SIRFUART_INT_EN); - wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_TX_INT_EN); + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + + if (IS_DMA_CHAN_VALID(sirfport->tx_dma_no)) { + if (sirfport->tx_dma_state == TX_DMA_RUNNING) { + dmaengine_pause(sirfport->tx_dma_chan); + sirfport->tx_dma_state = TX_DMA_PAUSE; + } else { + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) & + ~uint_en->sirfsoc_txfifo_empty_en); + else + wr_regl(port, SIRFUART_INT_EN_CLR, + uint_en->sirfsoc_txfifo_empty_en); + } + } else { + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) & + ~uint_en->sirfsoc_txfifo_empty_en); + else + wr_regl(port, SIRFUART_INT_EN_CLR, + uint_en->sirfsoc_txfifo_empty_en); + } } -void sirfsoc_uart_start_tx(struct uart_port *port) +static void sirfsoc_uart_tx_with_dma(struct sirfsoc_uart_port *sirfport) +{ + struct uart_port *port = &sirfport->port; + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + struct circ_buf *xmit = &port->state->xmit; + unsigned long tran_size; + unsigned long tran_start; + unsigned long pio_tx_size; + + tran_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + tran_start = (unsigned long)(xmit->buf + xmit->tail); + if (uart_circ_empty(xmit) || uart_tx_stopped(port) || + !tran_size) + return; + if (sirfport->tx_dma_state == TX_DMA_PAUSE) { + dmaengine_resume(sirfport->tx_dma_chan); + return; + } + if (sirfport->tx_dma_state == TX_DMA_RUNNING) + return; + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg)& + ~(uint_en->sirfsoc_txfifo_empty_en)); + else + wr_regl(port, SIRFUART_INT_EN_CLR, + uint_en->sirfsoc_txfifo_empty_en); + /* + * DMA requires buffer address and buffer length are both aligned with + * 4 bytes, so we use PIO for + * 1. if address is not aligned with 4bytes, use PIO for the first 1~3 + * bytes, and move to DMA for the left part aligned with 4bytes + * 2. if buffer length is not aligned with 4bytes, use DMA for aligned + * part first, move to PIO for the left 1~3 bytes + */ + if (tran_size < 4 || BYTES_TO_ALIGN(tran_start)) { + wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_STOP); + wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, + rd_regl(port, ureg->sirfsoc_tx_dma_io_ctrl)| + SIRFUART_IO_MODE); + if (BYTES_TO_ALIGN(tran_start)) { + pio_tx_size = sirfsoc_uart_pio_tx_chars(sirfport, + BYTES_TO_ALIGN(tran_start)); + tran_size -= pio_tx_size; + } + if (tran_size < 4) + sirfsoc_uart_pio_tx_chars(sirfport, tran_size); + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg)| + uint_en->sirfsoc_txfifo_empty_en); + else + wr_regl(port, ureg->sirfsoc_int_en_reg, + uint_en->sirfsoc_txfifo_empty_en); + wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START); + } else { + /* tx transfer mode switch into dma mode */ + wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_STOP); + wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, + rd_regl(port, ureg->sirfsoc_tx_dma_io_ctrl)& + ~SIRFUART_IO_MODE); + wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START); + tran_size &= ~(0x3); + + sirfport->tx_dma_addr = dma_map_single(port->dev, + xmit->buf + xmit->tail, + tran_size, DMA_TO_DEVICE); + sirfport->tx_dma_desc = dmaengine_prep_slave_single( + sirfport->tx_dma_chan, sirfport->tx_dma_addr, + tran_size, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); + if (!sirfport->tx_dma_desc) { + dev_err(port->dev, "DMA prep slave single fail\n"); + return; + } + sirfport->tx_dma_desc->callback = + sirfsoc_uart_tx_dma_complete_callback; + sirfport->tx_dma_desc->callback_param = (void *)sirfport; + sirfport->transfer_size = tran_size; + + dmaengine_submit(sirfport->tx_dma_desc); + dma_async_issue_pending(sirfport->tx_dma_chan); + sirfport->tx_dma_state = TX_DMA_RUNNING; + } +} + +static void sirfsoc_uart_start_tx(struct uart_port *port) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); - unsigned long regv; - sirfsoc_uart_pio_tx_chars(sirfport, 1); - wr_regl(port, SIRFUART_TX_FIFO_OP, SIRFUART_TX_FIFO_START); - regv = rd_regl(port, SIRFUART_INT_EN); - wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_TX_INT_EN); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + if (IS_DMA_CHAN_VALID(sirfport->tx_dma_no)) + sirfsoc_uart_tx_with_dma(sirfport); + else { + sirfsoc_uart_pio_tx_chars(sirfport, 1); + wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START); + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg)| + uint_en->sirfsoc_txfifo_empty_en); + else + wr_regl(port, ureg->sirfsoc_int_en_reg, + uint_en->sirfsoc_txfifo_empty_en); + } } static void sirfsoc_uart_stop_rx(struct uart_port *port) { - unsigned long regv; - wr_regl(port, SIRFUART_RX_FIFO_OP, 0); - regv = rd_regl(port, SIRFUART_INT_EN); - wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_RX_IO_INT_EN); + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + + wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0); + if (IS_DMA_CHAN_VALID(sirfport->rx_dma_no)) { + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) & + ~(SIRFUART_RX_DMA_INT_EN(port, uint_en) | + uint_en->sirfsoc_rx_done_en)); + else + wr_regl(port, SIRFUART_INT_EN_CLR, + SIRFUART_RX_DMA_INT_EN(port, uint_en)| + uint_en->sirfsoc_rx_done_en); + dmaengine_terminate_all(sirfport->rx_dma_chan); + } else { + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg)& + ~(SIRFUART_RX_IO_INT_EN(port, uint_en))); + else + wr_regl(port, SIRFUART_INT_EN_CLR, + SIRFUART_RX_IO_INT_EN(port, uint_en)); + } } static void sirfsoc_uart_disable_ms(struct uart_port *port) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); - unsigned long reg; - sirfport->ms_enabled = 0; + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + if (!sirfport->hw_flow_ctrl) return; - reg = rd_regl(port, SIRFUART_AFC_CTRL); - wr_regl(port, SIRFUART_AFC_CTRL, reg & ~0x3FF); - reg = rd_regl(port, SIRFUART_INT_EN); - wr_regl(port, SIRFUART_INT_EN, reg & ~SIRFUART_CTS_INT_EN); + sirfport->ms_enabled = false; + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { + wr_regl(port, ureg->sirfsoc_afc_ctrl, + rd_regl(port, ureg->sirfsoc_afc_ctrl) & ~0x3FF); + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg)& + ~uint_en->sirfsoc_cts_en); + else + wr_regl(port, SIRFUART_INT_EN_CLR, + uint_en->sirfsoc_cts_en); + } else + disable_irq(gpio_to_irq(sirfport->cts_gpio)); +} + +static irqreturn_t sirfsoc_uart_usp_cts_handler(int irq, void *dev_id) +{ + struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)dev_id; + struct uart_port *port = &sirfport->port; + if (gpio_is_valid(sirfport->cts_gpio) && sirfport->ms_enabled) + uart_handle_cts_change(port, + !gpio_get_value(sirfport->cts_gpio)); + return IRQ_HANDLED; } static void sirfsoc_uart_enable_ms(struct uart_port *port) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); - unsigned long reg; - unsigned long flg; + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + if (!sirfport->hw_flow_ctrl) return; - flg = SIRFUART_AFC_RX_EN | SIRFUART_AFC_TX_EN; - reg = rd_regl(port, SIRFUART_AFC_CTRL); - wr_regl(port, SIRFUART_AFC_CTRL, reg | flg); - reg = rd_regl(port, SIRFUART_INT_EN); - wr_regl(port, SIRFUART_INT_EN, reg | SIRFUART_CTS_INT_EN); - uart_handle_cts_change(port, - !(rd_regl(port, SIRFUART_AFC_CTRL) & SIRFUART_CTS_IN_STATUS)); - sirfport->ms_enabled = 1; + sirfport->ms_enabled = true; + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { + wr_regl(port, ureg->sirfsoc_afc_ctrl, + rd_regl(port, ureg->sirfsoc_afc_ctrl) | + SIRFUART_AFC_TX_EN | SIRFUART_AFC_RX_EN); + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) + | uint_en->sirfsoc_cts_en); + else + wr_regl(port, ureg->sirfsoc_int_en_reg, + uint_en->sirfsoc_cts_en); + } else + enable_irq(gpio_to_irq(sirfport->cts_gpio)); } static void sirfsoc_uart_break_ctl(struct uart_port *port, int break_state) { - unsigned long ulcon = rd_regl(port, SIRFUART_LINE_CTRL); - if (break_state) - ulcon |= SIRFUART_SET_BREAK; - else - ulcon &= ~SIRFUART_SET_BREAK; - wr_regl(port, SIRFUART_LINE_CTRL, ulcon); + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { + unsigned long ulcon = rd_regl(port, ureg->sirfsoc_line_ctrl); + if (break_state) + ulcon |= SIRFUART_SET_BREAK; + else + ulcon &= ~SIRFUART_SET_BREAK; + wr_regl(port, ureg->sirfsoc_line_ctrl, ulcon); + } } static unsigned int sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count) { + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status; unsigned int ch, rx_count = 0; - - while (!(rd_regl(port, SIRFUART_RX_FIFO_STATUS) & - SIRFUART_FIFOEMPTY_MASK(port))) { - ch = rd_regl(port, SIRFUART_RX_FIFO_DATA) | SIRFUART_DUMMY_READ; + struct tty_struct *tty; + tty = tty_port_tty_get(&port->state->port); + if (!tty) + return -ENODEV; + while (!(rd_regl(port, ureg->sirfsoc_rx_fifo_status) & + ufifo_st->ff_empty(port->line))) { + ch = rd_regl(port, ureg->sirfsoc_rx_fifo_data) | + SIRFUART_DUMMY_READ; if (unlikely(uart_handle_sysrq_char(port, ch))) continue; uart_insert_char(port, 0, 0, ch, TTY_NORMAL); @@ -218,8 +426,12 @@ sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count) break; } + sirfport->rx_io_count += rx_count; port->icount.rx += rx_count; + + spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); + spin_lock(&port->lock); return rx_count; } @@ -228,13 +440,16 @@ static unsigned int sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count) { struct uart_port *port = &sirfport->port; + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status; struct circ_buf *xmit = &port->state->xmit; unsigned int num_tx = 0; while (!uart_circ_empty(xmit) && - !(rd_regl(port, SIRFUART_TX_FIFO_STATUS) & - SIRFUART_FIFOFULL_MASK(port)) && + !(rd_regl(port, ureg->sirfsoc_tx_fifo_status) & + ufifo_st->ff_full(port->line)) && count--) { - wr_regl(port, SIRFUART_TX_FIFO_DATA, xmit->buf[xmit->tail]); + wr_regl(port, ureg->sirfsoc_tx_fifo_data, + xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; num_tx++; @@ -244,6 +459,166 @@ sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count) return num_tx; } +static void sirfsoc_uart_tx_dma_complete_callback(void *param) +{ + struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param; + struct uart_port *port = &sirfport->port; + struct circ_buf *xmit = &port->state->xmit; + unsigned long flags; + + xmit->tail = (xmit->tail + sirfport->transfer_size) & + (UART_XMIT_SIZE - 1); + port->icount.tx += sirfport->transfer_size; + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + if (sirfport->tx_dma_addr) + dma_unmap_single(port->dev, sirfport->tx_dma_addr, + sirfport->transfer_size, DMA_TO_DEVICE); + spin_lock_irqsave(&sirfport->tx_lock, flags); + sirfport->tx_dma_state = TX_DMA_IDLE; + sirfsoc_uart_tx_with_dma(sirfport); + spin_unlock_irqrestore(&sirfport->tx_lock, flags); +} + +static void sirfsoc_uart_insert_rx_buf_to_tty( + struct sirfsoc_uart_port *sirfport, int count) +{ + struct uart_port *port = &sirfport->port; + struct tty_port *tport = &port->state->port; + int inserted; + + inserted = tty_insert_flip_string(tport, + sirfport->rx_dma_items[sirfport->rx_completed].xmit.buf, count); + port->icount.rx += inserted; + tty_flip_buffer_push(tport); +} + +static void sirfsoc_rx_submit_one_dma_desc(struct uart_port *port, int index) +{ + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + + sirfport->rx_dma_items[index].xmit.tail = + sirfport->rx_dma_items[index].xmit.head = 0; + sirfport->rx_dma_items[index].desc = + dmaengine_prep_slave_single(sirfport->rx_dma_chan, + sirfport->rx_dma_items[index].dma_addr, SIRFSOC_RX_DMA_BUF_SIZE, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + if (!sirfport->rx_dma_items[index].desc) { + dev_err(port->dev, "DMA slave single fail\n"); + return; + } + sirfport->rx_dma_items[index].desc->callback = + sirfsoc_uart_rx_dma_complete_callback; + sirfport->rx_dma_items[index].desc->callback_param = sirfport; + sirfport->rx_dma_items[index].cookie = + dmaengine_submit(sirfport->rx_dma_items[index].desc); + dma_async_issue_pending(sirfport->rx_dma_chan); +} + +static void sirfsoc_rx_tmo_process_tl(unsigned long param) +{ + struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param; + struct uart_port *port = &sirfport->port; + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + struct sirfsoc_int_status *uint_st = &sirfport->uart_reg->uart_int_st; + unsigned int count; + unsigned long flags; + + spin_lock_irqsave(&sirfport->rx_lock, flags); + while (sirfport->rx_completed != sirfport->rx_issued) { + sirfsoc_uart_insert_rx_buf_to_tty(sirfport, + SIRFSOC_RX_DMA_BUF_SIZE); + sirfsoc_rx_submit_one_dma_desc(port, sirfport->rx_completed++); + sirfport->rx_completed %= SIRFSOC_RX_LOOP_BUF_CNT; + } + count = CIRC_CNT(sirfport->rx_dma_items[sirfport->rx_issued].xmit.head, + sirfport->rx_dma_items[sirfport->rx_issued].xmit.tail, + SIRFSOC_RX_DMA_BUF_SIZE); + if (count > 0) + sirfsoc_uart_insert_rx_buf_to_tty(sirfport, count); + wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, + rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) | + SIRFUART_IO_MODE); + sirfsoc_uart_pio_rx_chars(port, 4 - sirfport->rx_io_count); + spin_unlock_irqrestore(&sirfport->rx_lock, flags); + if (sirfport->rx_io_count == 4) { + spin_lock_irqsave(&sirfport->rx_lock, flags); + sirfport->rx_io_count = 0; + wr_regl(port, ureg->sirfsoc_int_st_reg, + uint_st->sirfsoc_rx_done); + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) & + ~(uint_en->sirfsoc_rx_done_en)); + else + wr_regl(port, SIRFUART_INT_EN_CLR, + uint_en->sirfsoc_rx_done_en); + spin_unlock_irqrestore(&sirfport->rx_lock, flags); + + sirfsoc_uart_start_next_rx_dma(port); + } else { + spin_lock_irqsave(&sirfport->rx_lock, flags); + wr_regl(port, ureg->sirfsoc_int_st_reg, + uint_st->sirfsoc_rx_done); + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) | + (uint_en->sirfsoc_rx_done_en)); + else + wr_regl(port, ureg->sirfsoc_int_en_reg, + uint_en->sirfsoc_rx_done_en); + spin_unlock_irqrestore(&sirfport->rx_lock, flags); + } +} + +static void sirfsoc_uart_handle_rx_tmo(struct sirfsoc_uart_port *sirfport) +{ + struct uart_port *port = &sirfport->port; + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + struct dma_tx_state tx_state; + spin_lock(&sirfport->rx_lock); + + dmaengine_tx_status(sirfport->rx_dma_chan, + sirfport->rx_dma_items[sirfport->rx_issued].cookie, &tx_state); + dmaengine_terminate_all(sirfport->rx_dma_chan); + sirfport->rx_dma_items[sirfport->rx_issued].xmit.head = + SIRFSOC_RX_DMA_BUF_SIZE - tx_state.residue; + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) & + ~(uint_en->sirfsoc_rx_timeout_en)); + else + wr_regl(port, SIRFUART_INT_EN_CLR, + uint_en->sirfsoc_rx_timeout_en); + spin_unlock(&sirfport->rx_lock); + tasklet_schedule(&sirfport->rx_tmo_process_tasklet); +} + +static void sirfsoc_uart_handle_rx_done(struct sirfsoc_uart_port *sirfport) +{ + struct uart_port *port = &sirfport->port; + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + struct sirfsoc_int_status *uint_st = &sirfport->uart_reg->uart_int_st; + + sirfsoc_uart_pio_rx_chars(port, 4 - sirfport->rx_io_count); + if (sirfport->rx_io_count == 4) { + sirfport->rx_io_count = 0; + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) & + ~(uint_en->sirfsoc_rx_done_en)); + else + wr_regl(port, SIRFUART_INT_EN_CLR, + uint_en->sirfsoc_rx_done_en); + wr_regl(port, ureg->sirfsoc_int_st_reg, + uint_st->sirfsoc_rx_timeout); + sirfsoc_uart_start_next_rx_dma(port); + } +} + static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id) { unsigned long intr_status; @@ -251,79 +626,191 @@ static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id) unsigned long flag = TTY_NORMAL; struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)dev_id; struct uart_port *port = &sirfport->port; + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status; + struct sirfsoc_int_status *uint_st = &sirfport->uart_reg->uart_int_st; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; struct uart_state *state = port->state; struct circ_buf *xmit = &port->state->xmit; spin_lock(&port->lock); - intr_status = rd_regl(port, SIRFUART_INT_STATUS); - wr_regl(port, SIRFUART_INT_STATUS, intr_status); - intr_status &= rd_regl(port, SIRFUART_INT_EN); - if (unlikely(intr_status & (SIRFUART_ERR_INT_STAT))) { - if (intr_status & SIRFUART_RXD_BREAK) { + intr_status = rd_regl(port, ureg->sirfsoc_int_st_reg); + wr_regl(port, ureg->sirfsoc_int_st_reg, intr_status); + intr_status &= rd_regl(port, ureg->sirfsoc_int_en_reg); + if (unlikely(intr_status & (SIRFUART_ERR_INT_STAT(port, uint_st)))) { + if (intr_status & uint_st->sirfsoc_rxd_brk) { + port->icount.brk++; if (uart_handle_break(port)) goto recv_char; - uart_insert_char(port, intr_status, - SIRFUART_RX_OFLOW, 0, TTY_BREAK); - spin_unlock(&port->lock); - return IRQ_HANDLED; } - if (intr_status & SIRFUART_RX_OFLOW) + if (intr_status & uint_st->sirfsoc_rx_oflow) port->icount.overrun++; - if (intr_status & SIRFUART_FRM_ERR) { + if (intr_status & uint_st->sirfsoc_frm_err) { port->icount.frame++; flag = TTY_FRAME; } - if (intr_status & SIRFUART_PARITY_ERR) + if (intr_status & uint_st->sirfsoc_parity_err) flag = TTY_PARITY; - wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET); - wr_regl(port, SIRFUART_RX_FIFO_OP, 0); - wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_START); + wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET); + wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0); + wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_START); intr_status &= port->read_status_mask; uart_insert_char(port, intr_status, - SIRFUART_RX_OFLOW_INT, 0, flag); + uint_en->sirfsoc_rx_oflow_en, 0, flag); + tty_flip_buffer_push(&state->port); } recv_char: - if (intr_status & SIRFUART_CTS_INT_EN) { - cts_status = !(rd_regl(port, SIRFUART_AFC_CTRL) & - SIRFUART_CTS_IN_STATUS); - if (cts_status != 0) { - uart_handle_cts_change(port, 1); - } else { - uart_handle_cts_change(port, 0); - wake_up_interruptible(&state->port.delta_msr_wait); - } + if ((sirfport->uart_reg->uart_type == SIRF_REAL_UART) && + (intr_status & SIRFUART_CTS_INT_ST(uint_st)) && + !sirfport->tx_dma_state) { + cts_status = rd_regl(port, ureg->sirfsoc_afc_ctrl) & + SIRFUART_AFC_CTS_STATUS; + if (cts_status != 0) + cts_status = 0; + else + cts_status = 1; + uart_handle_cts_change(port, cts_status); + wake_up_interruptible(&state->port.delta_msr_wait); } - if (intr_status & SIRFUART_RX_IO_INT_EN) - sirfsoc_uart_pio_rx_chars(port, SIRFSOC_UART_IO_RX_MAX_CNT); - if (intr_status & SIRFUART_TX_INT_EN) { - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - spin_unlock(&port->lock); - return IRQ_HANDLED; - } else { - sirfsoc_uart_pio_tx_chars(sirfport, + if (IS_DMA_CHAN_VALID(sirfport->rx_dma_no)) { + if (intr_status & uint_st->sirfsoc_rx_timeout) + sirfsoc_uart_handle_rx_tmo(sirfport); + if (intr_status & uint_st->sirfsoc_rx_done) + sirfsoc_uart_handle_rx_done(sirfport); + } else { + if (intr_status & SIRFUART_RX_IO_INT_ST(uint_st)) + sirfsoc_uart_pio_rx_chars(port, + SIRFSOC_UART_IO_RX_MAX_CNT); + } + if (intr_status & uint_st->sirfsoc_txfifo_empty) { + if (IS_DMA_CHAN_VALID(sirfport->tx_dma_no)) + sirfsoc_uart_tx_with_dma(sirfport); + else { + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + spin_unlock(&port->lock); + return IRQ_HANDLED; + } else { + sirfsoc_uart_pio_tx_chars(sirfport, SIRFSOC_UART_IO_TX_REASONABLE_CNT); - if ((uart_circ_empty(xmit)) && - (rd_regl(port, SIRFUART_TX_FIFO_STATUS) & - SIRFUART_FIFOEMPTY_MASK(port))) - sirfsoc_uart_stop_tx(port); + if ((uart_circ_empty(xmit)) && + (rd_regl(port, ureg->sirfsoc_tx_fifo_status) & + ufifo_st->ff_empty(port->line))) + sirfsoc_uart_stop_tx(port); + } } } spin_unlock(&port->lock); return IRQ_HANDLED; } +static void sirfsoc_uart_rx_dma_complete_tl(unsigned long param) +{ + struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param; + struct uart_port *port = &sirfport->port; + unsigned long flags; + spin_lock_irqsave(&sirfport->rx_lock, flags); + while (sirfport->rx_completed != sirfport->rx_issued) { + sirfsoc_uart_insert_rx_buf_to_tty(sirfport, + SIRFSOC_RX_DMA_BUF_SIZE); + sirfsoc_rx_submit_one_dma_desc(port, sirfport->rx_completed++); + sirfport->rx_completed %= SIRFSOC_RX_LOOP_BUF_CNT; + } + spin_unlock_irqrestore(&sirfport->rx_lock, flags); +} + +static void sirfsoc_uart_rx_dma_complete_callback(void *param) +{ + struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param; + spin_lock(&sirfport->rx_lock); + sirfport->rx_issued++; + sirfport->rx_issued %= SIRFSOC_RX_LOOP_BUF_CNT; + spin_unlock(&sirfport->rx_lock); + tasklet_schedule(&sirfport->rx_dma_complete_tasklet); +} + +/* submit rx dma task into dmaengine */ +static void sirfsoc_uart_start_next_rx_dma(struct uart_port *port) +{ + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + unsigned long flags; + int i; + spin_lock_irqsave(&sirfport->rx_lock, flags); + sirfport->rx_io_count = 0; + wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, + rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) & + ~SIRFUART_IO_MODE); + spin_unlock_irqrestore(&sirfport->rx_lock, flags); + for (i = 0; i < SIRFSOC_RX_LOOP_BUF_CNT; i++) + sirfsoc_rx_submit_one_dma_desc(port, i); + sirfport->rx_completed = sirfport->rx_issued = 0; + spin_lock_irqsave(&sirfport->rx_lock, flags); + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) | + SIRFUART_RX_DMA_INT_EN(port, uint_en)); + else + wr_regl(port, ureg->sirfsoc_int_en_reg, + SIRFUART_RX_DMA_INT_EN(port, uint_en)); + spin_unlock_irqrestore(&sirfport->rx_lock, flags); +} + static void sirfsoc_uart_start_rx(struct uart_port *port) { - unsigned long regv; - regv = rd_regl(port, SIRFUART_INT_EN); - wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_RX_IO_INT_EN); - wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET); - wr_regl(port, SIRFUART_RX_FIFO_OP, 0); - wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_START); + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + + sirfport->rx_io_count = 0; + wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET); + wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0); + wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_START); + if (IS_DMA_CHAN_VALID(sirfport->rx_dma_no)) + sirfsoc_uart_start_next_rx_dma(port); + else { + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) | + SIRFUART_RX_IO_INT_EN(port, uint_en)); + else + wr_regl(port, ureg->sirfsoc_int_en_reg, + SIRFUART_RX_IO_INT_EN(port, uint_en)); + } +} + +static unsigned int +sirfsoc_usp_calc_sample_div(unsigned long set_rate, + unsigned long ioclk_rate, unsigned long *sample_reg) +{ + unsigned long min_delta = ~0UL; + unsigned short sample_div; + unsigned long ioclk_div = 0; + unsigned long temp_delta; + + for (sample_div = SIRF_MIN_SAMPLE_DIV; + sample_div <= SIRF_MAX_SAMPLE_DIV; sample_div++) { + temp_delta = ioclk_rate - + (ioclk_rate + (set_rate * sample_div) / 2) + / (set_rate * sample_div) * set_rate * sample_div; + + temp_delta = (temp_delta > 0) ? temp_delta : -temp_delta; + if (temp_delta < min_delta) { + ioclk_div = (2 * ioclk_rate / + (set_rate * sample_div) + 1) / 2 - 1; + if (ioclk_div > SIRF_IOCLK_DIV_MAX) + continue; + min_delta = temp_delta; + *sample_reg = sample_div; + if (!temp_delta) + break; + } + } + return ioclk_div; } static unsigned int -sirfsoc_calc_sample_div(unsigned long baud_rate, - unsigned long ioclk_rate, unsigned long *setted_baud) +sirfsoc_uart_calc_sample_div(unsigned long baud_rate, + unsigned long ioclk_rate, unsigned long *set_baud) { unsigned long min_delta = ~0UL; unsigned short sample_div; @@ -346,7 +833,7 @@ sirfsoc_calc_sample_div(unsigned long baud_rate, regv = regv & (~SIRF_SAMPLE_DIV_MASK); regv = regv | (sample_div << SIRF_SAMPLE_DIV_SHIFT); min_delta = temp_delta; - *setted_baud = baud_tmp; + *set_baud = baud_tmp; } } return regv; @@ -357,63 +844,93 @@ static void sirfsoc_uart_set_termios(struct uart_port *port, struct ktermios *old) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; unsigned long config_reg = 0; unsigned long baud_rate; - unsigned long setted_baud; + unsigned long set_baud; unsigned long flags; unsigned long ic; unsigned int clk_div_reg = 0; - unsigned long temp_reg_val; + unsigned long txfifo_op_reg, ioclk_rate; unsigned long rx_time_out; int threshold_div; - int temp; + u32 data_bit_len, stop_bit_len, len_val; + unsigned long sample_div_reg = 0xf; + ioclk_rate = port->uartclk; switch (termios->c_cflag & CSIZE) { default: case CS8: + data_bit_len = 8; config_reg |= SIRFUART_DATA_BIT_LEN_8; break; case CS7: + data_bit_len = 7; config_reg |= SIRFUART_DATA_BIT_LEN_7; break; case CS6: + data_bit_len = 6; config_reg |= SIRFUART_DATA_BIT_LEN_6; break; case CS5: + data_bit_len = 5; config_reg |= SIRFUART_DATA_BIT_LEN_5; break; } - if (termios->c_cflag & CSTOPB) + if (termios->c_cflag & CSTOPB) { config_reg |= SIRFUART_STOP_BIT_LEN_2; - baud_rate = uart_get_baud_rate(port, termios, old, 0, 4000000); + stop_bit_len = 2; + } else + stop_bit_len = 1; + spin_lock_irqsave(&port->lock, flags); - port->read_status_mask = SIRFUART_RX_OFLOW_INT; + port->read_status_mask = uint_en->sirfsoc_rx_oflow_en; port->ignore_status_mask = 0; - /* read flags */ - if (termios->c_iflag & INPCK) - port->read_status_mask |= - SIRFUART_FRM_ERR_INT | SIRFUART_PARITY_ERR_INT; + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { + if (termios->c_iflag & INPCK) + port->read_status_mask |= uint_en->sirfsoc_frm_err_en | + uint_en->sirfsoc_parity_err_en; + } else { + if (termios->c_iflag & INPCK) + port->read_status_mask |= uint_en->sirfsoc_frm_err_en; + } if (termios->c_iflag & (BRKINT | PARMRK)) - port->read_status_mask |= SIRFUART_RXD_BREAK_INT; - /* ignore flags */ - if (termios->c_iflag & IGNPAR) + port->read_status_mask |= uint_en->sirfsoc_rxd_brk_en; + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= + uint_en->sirfsoc_frm_err_en | + uint_en->sirfsoc_parity_err_en; + if (termios->c_cflag & PARENB) { + if (termios->c_cflag & CMSPAR) { + if (termios->c_cflag & PARODD) + config_reg |= SIRFUART_STICK_BIT_MARK; + else + config_reg |= SIRFUART_STICK_BIT_SPACE; + } else if (termios->c_cflag & PARODD) { + config_reg |= SIRFUART_STICK_BIT_ODD; + } else { + config_reg |= SIRFUART_STICK_BIT_EVEN; + } + } + } else { + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= + uint_en->sirfsoc_frm_err_en; + if (termios->c_cflag & PARENB) + dev_warn(port->dev, + "USP-UART not support parity err\n"); + } + if (termios->c_iflag & IGNBRK) { port->ignore_status_mask |= - SIRFUART_FRM_ERR_INT | SIRFUART_PARITY_ERR_INT; + uint_en->sirfsoc_rxd_brk_en; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= + uint_en->sirfsoc_rx_oflow_en; + } if ((termios->c_cflag & CREAD) == 0) port->ignore_status_mask |= SIRFUART_DUMMY_READ; - /* enable parity if PARENB is set*/ - if (termios->c_cflag & PARENB) { - if (termios->c_cflag & CMSPAR) { - if (termios->c_cflag & PARODD) - config_reg |= SIRFUART_STICK_BIT_MARK; - else - config_reg |= SIRFUART_STICK_BIT_SPACE; - } else if (termios->c_cflag & PARODD) { - config_reg |= SIRFUART_STICK_BIT_ODD; - } else { - config_reg |= SIRFUART_STICK_BIT_EVEN; - } - } /* Hardware Flow Control Settings */ if (UART_ENABLE_MS(port, termios->c_cflag)) { if (!sirfport->ms_enabled) @@ -422,75 +939,184 @@ static void sirfsoc_uart_set_termios(struct uart_port *port, if (sirfport->ms_enabled) sirfsoc_uart_disable_ms(port); } - - if (port->uartclk == 150000000) { - /* common rate: fast calculation */ + baud_rate = uart_get_baud_rate(port, termios, old, 0, 4000000); + if (ioclk_rate == 150000000) { for (ic = 0; ic < SIRF_BAUD_RATE_SUPPORT_NR; ic++) if (baud_rate == baudrate_to_regv[ic].baud_rate) clk_div_reg = baudrate_to_regv[ic].reg_val; } - - setted_baud = baud_rate; - /* arbitary rate setting */ - if (unlikely(clk_div_reg == 0)) - clk_div_reg = sirfsoc_calc_sample_div(baud_rate, port->uartclk, - &setted_baud); - wr_regl(port, SIRFUART_DIVISOR, clk_div_reg); - + set_baud = baud_rate; + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { + if (unlikely(clk_div_reg == 0)) + clk_div_reg = sirfsoc_uart_calc_sample_div(baud_rate, + ioclk_rate, &set_baud); + wr_regl(port, ureg->sirfsoc_divisor, clk_div_reg); + } else { + clk_div_reg = sirfsoc_usp_calc_sample_div(baud_rate, + ioclk_rate, &sample_div_reg); + sample_div_reg--; + set_baud = ((ioclk_rate / (clk_div_reg+1) - 1) / + (sample_div_reg + 1)); + /* setting usp mode 2 */ + len_val = ((1 << SIRFSOC_USP_MODE2_RXD_DELAY_OFFSET) | + (1 << SIRFSOC_USP_MODE2_TXD_DELAY_OFFSET)); + len_val |= ((clk_div_reg & SIRFSOC_USP_MODE2_CLK_DIVISOR_MASK) + << SIRFSOC_USP_MODE2_CLK_DIVISOR_OFFSET); + wr_regl(port, ureg->sirfsoc_mode2, len_val); + } if (tty_termios_baud_rate(termios)) - tty_termios_encode_baud_rate(termios, setted_baud, setted_baud); - - /* set receive timeout */ - rx_time_out = SIRFSOC_UART_RX_TIMEOUT(baud_rate, 20000); - rx_time_out = (rx_time_out > 0xFFFF) ? 0xFFFF : rx_time_out; - config_reg |= SIRFUART_RECV_TIMEOUT(rx_time_out); - temp_reg_val = rd_regl(port, SIRFUART_TX_FIFO_OP); - wr_regl(port, SIRFUART_RX_FIFO_OP, 0); - wr_regl(port, SIRFUART_TX_FIFO_OP, - temp_reg_val & ~SIRFUART_TX_FIFO_START); - wr_regl(port, SIRFUART_TX_DMA_IO_CTRL, SIRFUART_TX_MODE_IO); - wr_regl(port, SIRFUART_RX_DMA_IO_CTRL, SIRFUART_RX_MODE_IO); - wr_regl(port, SIRFUART_LINE_CTRL, config_reg); - + tty_termios_encode_baud_rate(termios, set_baud, set_baud); + /* set receive timeout && data bits len */ + rx_time_out = SIRFSOC_UART_RX_TIMEOUT(set_baud, 20000); + rx_time_out = SIRFUART_RECV_TIMEOUT_VALUE(rx_time_out); + txfifo_op_reg = rd_regl(port, ureg->sirfsoc_tx_fifo_op); + wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_STOP); + wr_regl(port, ureg->sirfsoc_tx_fifo_op, + (txfifo_op_reg & ~SIRFUART_FIFO_START)); + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { + config_reg |= SIRFUART_RECV_TIMEOUT(port, rx_time_out); + wr_regl(port, ureg->sirfsoc_line_ctrl, config_reg); + } else { + /*tx frame ctrl*/ + len_val = (data_bit_len - 1) << SIRFSOC_USP_TX_DATA_LEN_OFFSET; + len_val |= (data_bit_len + 1 + stop_bit_len - 1) << + SIRFSOC_USP_TX_FRAME_LEN_OFFSET; + len_val |= ((data_bit_len - 1) << + SIRFSOC_USP_TX_SHIFTER_LEN_OFFSET); + len_val |= (((clk_div_reg & 0xc00) >> 10) << + SIRFSOC_USP_TX_CLK_DIVISOR_OFFSET); + wr_regl(port, ureg->sirfsoc_tx_frame_ctrl, len_val); + /*rx frame ctrl*/ + len_val = (data_bit_len - 1) << SIRFSOC_USP_RX_DATA_LEN_OFFSET; + len_val |= (data_bit_len + 1 + stop_bit_len - 1) << + SIRFSOC_USP_RX_FRAME_LEN_OFFSET; + len_val |= (data_bit_len - 1) << + SIRFSOC_USP_RX_SHIFTER_LEN_OFFSET; + len_val |= (((clk_div_reg & 0xf000) >> 12) << + SIRFSOC_USP_RX_CLK_DIVISOR_OFFSET); + wr_regl(port, ureg->sirfsoc_rx_frame_ctrl, len_val); + /*async param*/ + wr_regl(port, ureg->sirfsoc_async_param_reg, + (SIRFUART_RECV_TIMEOUT(port, rx_time_out)) | + (sample_div_reg & SIRFSOC_USP_ASYNC_DIV2_MASK) << + SIRFSOC_USP_ASYNC_DIV2_OFFSET); + } + if (IS_DMA_CHAN_VALID(sirfport->tx_dma_no)) + wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, SIRFUART_DMA_MODE); + else + wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, SIRFUART_IO_MODE); + if (IS_DMA_CHAN_VALID(sirfport->rx_dma_no)) + wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, SIRFUART_DMA_MODE); + else + wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, SIRFUART_IO_MODE); /* Reset Rx/Tx FIFO Threshold level for proper baudrate */ - if (baud_rate < 1000000) + if (set_baud < 1000000) threshold_div = 1; else threshold_div = 2; - temp = port->line == 1 ? 16 : 64; - wr_regl(port, SIRFUART_TX_FIFO_CTRL, temp / threshold_div); - wr_regl(port, SIRFUART_RX_FIFO_CTRL, temp / threshold_div); - temp_reg_val |= SIRFUART_TX_FIFO_START; - wr_regl(port, SIRFUART_TX_FIFO_OP, temp_reg_val); - uart_update_timeout(port, termios->c_cflag, baud_rate); + wr_regl(port, ureg->sirfsoc_tx_fifo_ctrl, + SIRFUART_FIFO_THD(port) / threshold_div); + wr_regl(port, ureg->sirfsoc_rx_fifo_ctrl, + SIRFUART_FIFO_THD(port) / threshold_div); + txfifo_op_reg |= SIRFUART_FIFO_START; + wr_regl(port, ureg->sirfsoc_tx_fifo_op, txfifo_op_reg); + uart_update_timeout(port, termios->c_cflag, set_baud); sirfsoc_uart_start_rx(port); - wr_regl(port, SIRFUART_TX_RX_EN, SIRFUART_TX_EN | SIRFUART_RX_EN); + wr_regl(port, ureg->sirfsoc_tx_rx_en, SIRFUART_TX_EN | SIRFUART_RX_EN); spin_unlock_irqrestore(&port->lock, flags); } -static void startup_uart_controller(struct uart_port *port) +static unsigned int sirfsoc_uart_init_tx_dma(struct uart_port *port) { - unsigned long temp_regv; - int temp; - temp_regv = rd_regl(port, SIRFUART_TX_DMA_IO_CTRL); - wr_regl(port, SIRFUART_TX_DMA_IO_CTRL, temp_regv | SIRFUART_TX_MODE_IO); - temp_regv = rd_regl(port, SIRFUART_RX_DMA_IO_CTRL); - wr_regl(port, SIRFUART_RX_DMA_IO_CTRL, temp_regv | SIRFUART_RX_MODE_IO); - wr_regl(port, SIRFUART_TX_DMA_IO_LEN, 0); - wr_regl(port, SIRFUART_RX_DMA_IO_LEN, 0); - wr_regl(port, SIRFUART_TX_RX_EN, SIRFUART_RX_EN | SIRFUART_TX_EN); - wr_regl(port, SIRFUART_TX_FIFO_OP, SIRFUART_TX_FIFO_RESET); - wr_regl(port, SIRFUART_TX_FIFO_OP, 0); - wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET); - wr_regl(port, SIRFUART_RX_FIFO_OP, 0); - temp = port->line == 1 ? 16 : 64; - wr_regl(port, SIRFUART_TX_FIFO_CTRL, temp); - wr_regl(port, SIRFUART_RX_FIFO_CTRL, temp); + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + dma_cap_mask_t dma_mask; + struct dma_slave_config tx_slv_cfg = { + .dst_maxburst = 2, + }; + + dma_cap_zero(dma_mask); + dma_cap_set(DMA_SLAVE, dma_mask); + sirfport->tx_dma_chan = dma_request_channel(dma_mask, + (dma_filter_fn)sirfsoc_dma_filter_id, + (void *)sirfport->tx_dma_no); + if (!sirfport->tx_dma_chan) { + dev_err(port->dev, "Uart Request Dma Channel Fail %d\n", + sirfport->tx_dma_no); + return -EPROBE_DEFER; + } + dmaengine_slave_config(sirfport->tx_dma_chan, &tx_slv_cfg); + + return 0; +} + +static unsigned int sirfsoc_uart_init_rx_dma(struct uart_port *port) +{ + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + dma_cap_mask_t dma_mask; + int ret; + int i, j; + struct dma_slave_config slv_cfg = { + .src_maxburst = 2, + }; + + dma_cap_zero(dma_mask); + dma_cap_set(DMA_SLAVE, dma_mask); + sirfport->rx_dma_chan = dma_request_channel(dma_mask, + (dma_filter_fn)sirfsoc_dma_filter_id, + (void *)sirfport->rx_dma_no); + if (!sirfport->rx_dma_chan) { + dev_err(port->dev, "Uart Request Dma Channel Fail %d\n", + sirfport->rx_dma_no); + ret = -EPROBE_DEFER; + goto request_err; + } + for (i = 0; i < SIRFSOC_RX_LOOP_BUF_CNT; i++) { + sirfport->rx_dma_items[i].xmit.buf = + dma_alloc_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE, + &sirfport->rx_dma_items[i].dma_addr, GFP_KERNEL); + if (!sirfport->rx_dma_items[i].xmit.buf) { + dev_err(port->dev, "Uart alloc bufa failed\n"); + ret = -ENOMEM; + goto alloc_coherent_err; + } + sirfport->rx_dma_items[i].xmit.head = + sirfport->rx_dma_items[i].xmit.tail = 0; + } + dmaengine_slave_config(sirfport->rx_dma_chan, &slv_cfg); + + return 0; +alloc_coherent_err: + for (j = 0; j < i; j++) + dma_free_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE, + sirfport->rx_dma_items[j].xmit.buf, + sirfport->rx_dma_items[j].dma_addr); + dma_release_channel(sirfport->rx_dma_chan); +request_err: + return ret; +} + +static void sirfsoc_uart_uninit_tx_dma(struct sirfsoc_uart_port *sirfport) +{ + dmaengine_terminate_all(sirfport->tx_dma_chan); + dma_release_channel(sirfport->tx_dma_chan); +} + +static void sirfsoc_uart_uninit_rx_dma(struct sirfsoc_uart_port *sirfport) +{ + int i; + struct uart_port *port = &sirfport->port; + dmaengine_terminate_all(sirfport->rx_dma_chan); + dma_release_channel(sirfport->rx_dma_chan); + for (i = 0; i < SIRFSOC_RX_LOOP_BUF_CNT; i++) + dma_free_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE, + sirfport->rx_dma_items[i].xmit.buf, + sirfport->rx_dma_items[i].dma_addr); } static int sirfsoc_uart_startup(struct uart_port *port) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; unsigned int index = port->line; int ret; set_irq_flags(port->irq, IRQF_VALID | IRQF_NOAUTOEN); @@ -504,8 +1130,64 @@ static int sirfsoc_uart_startup(struct uart_port *port) index, port->irq); goto irq_err; } - startup_uart_controller(port); + + /* initial hardware settings */ + wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, + rd_regl(port, ureg->sirfsoc_tx_dma_io_ctrl) | + SIRFUART_IO_MODE); + wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, + rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) | + SIRFUART_IO_MODE); + wr_regl(port, ureg->sirfsoc_tx_dma_io_len, 0); + wr_regl(port, ureg->sirfsoc_rx_dma_io_len, 0); + wr_regl(port, ureg->sirfsoc_tx_rx_en, SIRFUART_RX_EN | SIRFUART_TX_EN); + if (sirfport->uart_reg->uart_type == SIRF_USP_UART) + wr_regl(port, ureg->sirfsoc_mode1, + SIRFSOC_USP_ENDIAN_CTRL_LSBF | + SIRFSOC_USP_EN); + wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_RESET); + wr_regl(port, ureg->sirfsoc_tx_fifo_op, 0); + wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET); + wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0); + wr_regl(port, ureg->sirfsoc_tx_fifo_ctrl, SIRFUART_FIFO_THD(port)); + wr_regl(port, ureg->sirfsoc_rx_fifo_ctrl, SIRFUART_FIFO_THD(port)); + + if (IS_DMA_CHAN_VALID(sirfport->rx_dma_no)) { + ret = sirfsoc_uart_init_rx_dma(port); + if (ret) + goto init_rx_err; + wr_regl(port, ureg->sirfsoc_rx_fifo_level_chk, + SIRFUART_RX_FIFO_CHK_SC(port->line, 0x4) | + SIRFUART_RX_FIFO_CHK_LC(port->line, 0xe) | + SIRFUART_RX_FIFO_CHK_HC(port->line, 0x1b)); + } + if (IS_DMA_CHAN_VALID(sirfport->tx_dma_no)) { + sirfsoc_uart_init_tx_dma(port); + sirfport->tx_dma_state = TX_DMA_IDLE; + wr_regl(port, ureg->sirfsoc_tx_fifo_level_chk, + SIRFUART_TX_FIFO_CHK_SC(port->line, 0x1b) | + SIRFUART_TX_FIFO_CHK_LC(port->line, 0xe) | + SIRFUART_TX_FIFO_CHK_HC(port->line, 0x4)); + } + sirfport->ms_enabled = false; + if (sirfport->uart_reg->uart_type == SIRF_USP_UART && + sirfport->hw_flow_ctrl) { + set_irq_flags(gpio_to_irq(sirfport->cts_gpio), + IRQF_VALID | IRQF_NOAUTOEN); + ret = request_irq(gpio_to_irq(sirfport->cts_gpio), + sirfsoc_uart_usp_cts_handler, IRQF_TRIGGER_FALLING | + IRQF_TRIGGER_RISING, "usp_cts_irq", sirfport); + if (ret != 0) { + dev_err(port->dev, "UART-USP:request gpio irq fail\n"); + goto init_rx_err; + } + } + enable_irq(port->irq); + + return 0; +init_rx_err: + free_irq(port->irq, sirfport); irq_err: return ret; } @@ -513,11 +1195,25 @@ irq_err: static void sirfsoc_uart_shutdown(struct uart_port *port) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); - wr_regl(port, SIRFUART_INT_EN, 0); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, 0); + else + wr_regl(port, SIRFUART_INT_EN_CLR, ~0UL); + free_irq(port->irq, sirfport); - if (sirfport->ms_enabled) { + if (sirfport->ms_enabled) sirfsoc_uart_disable_ms(port); - sirfport->ms_enabled = 0; + if (sirfport->uart_reg->uart_type == SIRF_USP_UART && + sirfport->hw_flow_ctrl) { + gpio_set_value(sirfport->rts_gpio, 1); + free_irq(gpio_to_irq(sirfport->cts_gpio), sirfport); + } + if (IS_DMA_CHAN_VALID(sirfport->rx_dma_no)) + sirfsoc_uart_uninit_rx_dma(sirfport); + if (IS_DMA_CHAN_VALID(sirfport->tx_dma_no)) { + sirfsoc_uart_uninit_tx_dma(sirfport); + sirfport->tx_dma_state = TX_DMA_IDLE; } } @@ -528,9 +1224,11 @@ static const char *sirfsoc_uart_type(struct uart_port *port) static int sirfsoc_uart_request_port(struct uart_port *port) { + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_uart_param *uart_param = &sirfport->uart_reg->uart_param; void *ret; ret = request_mem_region(port->mapbase, - SIRFUART_MAP_SIZE, SIRFUART_PORT_NAME); + SIRFUART_MAP_SIZE, uart_param->port_name); return ret ? 0 : -EBUSY; } @@ -566,32 +1264,45 @@ static struct uart_ops sirfsoc_uart_ops = { }; #ifdef CONFIG_SERIAL_SIRFSOC_CONSOLE -static int __init sirfsoc_uart_console_setup(struct console *co, char *options) +static int __init +sirfsoc_uart_console_setup(struct console *co, char *options) { unsigned int baud = 115200; unsigned int bits = 8; unsigned int parity = 'n'; unsigned int flow = 'n'; struct uart_port *port = &sirfsoc_uart_ports[co->index].port; - + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; if (co->index < 0 || co->index >= SIRFSOC_UART_NR) return -EINVAL; if (!port->mapbase) return -ENODEV; + /* enable usp in mode1 register */ + if (sirfport->uart_reg->uart_type == SIRF_USP_UART) + wr_regl(port, ureg->sirfsoc_mode1, SIRFSOC_USP_EN | + SIRFSOC_USP_ENDIAN_CTRL_LSBF); if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); port->cons = co; + + /* default console tx/rx transfer using io mode */ + sirfport->rx_dma_no = UNVALID_DMA_CHAN; + sirfport->tx_dma_no = UNVALID_DMA_CHAN; return uart_set_options(port, co, baud, parity, bits, flow); } static void sirfsoc_uart_console_putchar(struct uart_port *port, int ch) { + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status; while (rd_regl(port, - SIRFUART_TX_FIFO_STATUS) & SIRFUART_FIFOFULL_MASK(port)) + ureg->sirfsoc_tx_fifo_status) & ufifo_st->ff_full(port->line)) cpu_relax(); - wr_regb(port, SIRFUART_TX_FIFO_DATA, ch); + wr_regb(port, ureg->sirfsoc_tx_fifo_data, ch); } static void sirfsoc_uart_console_write(struct console *co, const char *s, @@ -633,27 +1344,99 @@ static struct uart_driver sirfsoc_uart_drv = { #endif }; -int sirfsoc_uart_probe(struct platform_device *pdev) +static struct of_device_id sirfsoc_uart_ids[] = { + { .compatible = "sirf,prima2-uart", .data = &sirfsoc_uart,}, + { .compatible = "sirf,marco-uart", .data = &sirfsoc_uart}, + { .compatible = "sirf,prima2-usp-uart", .data = &sirfsoc_usp}, + {} +}; +MODULE_DEVICE_TABLE(of, sirfsoc_uart_ids); + +static int sirfsoc_uart_probe(struct platform_device *pdev) { struct sirfsoc_uart_port *sirfport; struct uart_port *port; struct resource *res; int ret; + const struct of_device_id *match; + match = of_match_node(sirfsoc_uart_ids, pdev->dev.of_node); if (of_property_read_u32(pdev->dev.of_node, "cell-index", &pdev->id)) { dev_err(&pdev->dev, "Unable to find cell-index in uart node.\n"); ret = -EFAULT; goto err; } - + if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-usp-uart")) + pdev->id += ((struct sirfsoc_uart_register *) + match->data)->uart_param.register_uart_nr; sirfport = &sirfsoc_uart_ports[pdev->id]; port = &sirfport->port; port->dev = &pdev->dev; port->private_data = sirfport; + sirfport->uart_reg = (struct sirfsoc_uart_register *)match->data; + + sirfport->hw_flow_ctrl = of_property_read_bool(pdev->dev.of_node, + "sirf,uart-has-rtscts"); + if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-uart")) { + sirfport->uart_reg->uart_type = SIRF_REAL_UART; + if (of_property_read_u32(pdev->dev.of_node, + "sirf,uart-dma-rx-channel", + &sirfport->rx_dma_no)) + sirfport->rx_dma_no = UNVALID_DMA_CHAN; + if (of_property_read_u32(pdev->dev.of_node, + "sirf,uart-dma-tx-channel", + &sirfport->tx_dma_no)) + sirfport->tx_dma_no = UNVALID_DMA_CHAN; + } + if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-usp-uart")) { + sirfport->uart_reg->uart_type = SIRF_USP_UART; + if (of_property_read_u32(pdev->dev.of_node, + "sirf,usp-dma-rx-channel", + &sirfport->rx_dma_no)) + sirfport->rx_dma_no = UNVALID_DMA_CHAN; + if (of_property_read_u32(pdev->dev.of_node, + "sirf,usp-dma-tx-channel", + &sirfport->tx_dma_no)) + sirfport->tx_dma_no = UNVALID_DMA_CHAN; + if (!sirfport->hw_flow_ctrl) + goto usp_no_flow_control; + if (of_find_property(pdev->dev.of_node, "cts-gpios", NULL)) + sirfport->cts_gpio = of_get_named_gpio( + pdev->dev.of_node, "cts-gpios", 0); + else + sirfport->cts_gpio = -1; + if (of_find_property(pdev->dev.of_node, "rts-gpios", NULL)) + sirfport->rts_gpio = of_get_named_gpio( + pdev->dev.of_node, "rts-gpios", 0); + else + sirfport->rts_gpio = -1; - if (of_find_property(pdev->dev.of_node, "hw_flow_ctrl", NULL)) - sirfport->hw_flow_ctrl = 1; + if ((!gpio_is_valid(sirfport->cts_gpio) || + !gpio_is_valid(sirfport->rts_gpio))) { + ret = -EINVAL; + dev_err(&pdev->dev, + "Usp flow control must have cts and rts gpio"); + goto err; + } + ret = devm_gpio_request(&pdev->dev, sirfport->cts_gpio, + "usp-cts-gpio"); + if (ret) { + dev_err(&pdev->dev, "Unable request cts gpio"); + goto err; + } + gpio_direction_input(sirfport->cts_gpio); + ret = devm_gpio_request(&pdev->dev, sirfport->rts_gpio, + "usp-rts-gpio"); + if (ret) { + dev_err(&pdev->dev, "Unable request rts gpio"); + goto err; + } + gpio_direction_output(sirfport->rts_gpio, 1); + } +usp_no_flow_control: + if (of_device_is_compatible(pdev->dev.of_node, "sirf,marco-uart")) + sirfport->is_marco = true; if (of_property_read_u32(pdev->dev.of_node, "fifosize", @@ -670,6 +1453,12 @@ int sirfsoc_uart_probe(struct platform_device *pdev) ret = -EFAULT; goto err; } + spin_lock_init(&sirfport->rx_lock); + spin_lock_init(&sirfport->tx_lock); + tasklet_init(&sirfport->rx_dma_complete_tasklet, + sirfsoc_uart_rx_dma_complete_tl, (unsigned long)sirfport); + tasklet_init(&sirfport->rx_tmo_process_tasklet, + sirfsoc_rx_tmo_process_tl, (unsigned long)sirfport); port->mapbase = res->start; port->membase = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!port->membase) { @@ -685,17 +1474,10 @@ int sirfsoc_uart_probe(struct platform_device *pdev) } port->irq = res->start; - if (sirfport->hw_flow_ctrl) { - sirfport->p = pinctrl_get_select_default(&pdev->dev); - ret = IS_ERR(sirfport->p); - if (ret) - goto err; - } - sirfport->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(sirfport->clk)) { ret = PTR_ERR(sirfport->clk); - goto clk_err; + goto err; } clk_prepare_enable(sirfport->clk); port->uartclk = clk_get_rate(sirfport->clk); @@ -715,10 +1497,6 @@ int sirfsoc_uart_probe(struct platform_device *pdev) port_err: clk_disable_unprepare(sirfport->clk); clk_put(sirfport->clk); -clk_err: - platform_set_drvdata(pdev, NULL); - if (sirfport->hw_flow_ctrl) - pinctrl_put(sirfport->p); err: return ret; } @@ -727,9 +1505,6 @@ static int sirfsoc_uart_remove(struct platform_device *pdev) { struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev); struct uart_port *port = &sirfport->port; - platform_set_drvdata(pdev, NULL); - if (sirfport->hw_flow_ctrl) - pinctrl_put(sirfport->p); clk_disable_unprepare(sirfport->clk); clk_put(sirfport->clk); uart_remove_one_port(&sirfsoc_uart_drv, port); @@ -753,13 +1528,6 @@ static int sirfsoc_uart_resume(struct platform_device *pdev) return 0; } -static struct of_device_id sirfsoc_uart_ids[] = { - { .compatible = "sirf,prima2-uart", }, - { .compatible = "sirf,marco-uart", }, - {} -}; -MODULE_DEVICE_TABLE(of, sirfsoc_uart_ids); - static struct platform_driver sirfsoc_uart_driver = { .probe = sirfsoc_uart_probe, .remove = sirfsoc_uart_remove, diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h index 85328ba0c4e3..fb8d0a002607 100644 --- a/drivers/tty/serial/sirfsoc_uart.h +++ b/drivers/tty/serial/sirfsoc_uart.h @@ -6,31 +6,260 @@ * Licensed under GPLv2 or later. */ #include <linux/bitops.h> +struct sirfsoc_uart_param { + const char *uart_name; + const char *port_name; + u32 uart_nr; + u32 register_uart_nr; +}; + +struct sirfsoc_register { + /* hardware uart specific */ + u32 sirfsoc_line_ctrl; + u32 sirfsoc_divisor; + /* uart - usp common */ + u32 sirfsoc_tx_rx_en; + u32 sirfsoc_int_en_reg; + u32 sirfsoc_int_st_reg; + u32 sirfsoc_tx_dma_io_ctrl; + u32 sirfsoc_tx_dma_io_len; + u32 sirfsoc_tx_fifo_ctrl; + u32 sirfsoc_tx_fifo_level_chk; + u32 sirfsoc_tx_fifo_op; + u32 sirfsoc_tx_fifo_status; + u32 sirfsoc_tx_fifo_data; + u32 sirfsoc_rx_dma_io_ctrl; + u32 sirfsoc_rx_dma_io_len; + u32 sirfsoc_rx_fifo_ctrl; + u32 sirfsoc_rx_fifo_level_chk; + u32 sirfsoc_rx_fifo_op; + u32 sirfsoc_rx_fifo_status; + u32 sirfsoc_rx_fifo_data; + u32 sirfsoc_afc_ctrl; + u32 sirfsoc_swh_dma_io; + /* hardware usp specific */ + u32 sirfsoc_mode1; + u32 sirfsoc_mode2; + u32 sirfsoc_tx_frame_ctrl; + u32 sirfsoc_rx_frame_ctrl; + u32 sirfsoc_async_param_reg; +}; + +typedef u32 (*fifo_full_mask)(int line); +typedef u32 (*fifo_empty_mask)(int line); + +struct sirfsoc_fifo_status { + fifo_full_mask ff_full; + fifo_empty_mask ff_empty; +}; -/* UART Register Offset Define */ -#define SIRFUART_LINE_CTRL 0x0040 -#define SIRFUART_TX_RX_EN 0x004c -#define SIRFUART_DIVISOR 0x0050 -#define SIRFUART_INT_EN 0x0054 -#define SIRFUART_INT_STATUS 0x0058 -#define SIRFUART_TX_DMA_IO_CTRL 0x0100 -#define SIRFUART_TX_DMA_IO_LEN 0x0104 -#define SIRFUART_TX_FIFO_CTRL 0x0108 -#define SIRFUART_TX_FIFO_LEVEL_CHK 0x010C -#define SIRFUART_TX_FIFO_OP 0x0110 -#define SIRFUART_TX_FIFO_STATUS 0x0114 -#define SIRFUART_TX_FIFO_DATA 0x0118 -#define SIRFUART_RX_DMA_IO_CTRL 0x0120 -#define SIRFUART_RX_DMA_IO_LEN 0x0124 -#define SIRFUART_RX_FIFO_CTRL 0x0128 -#define SIRFUART_RX_FIFO_LEVEL_CHK 0x012C -#define SIRFUART_RX_FIFO_OP 0x0130 -#define SIRFUART_RX_FIFO_STATUS 0x0134 -#define SIRFUART_RX_FIFO_DATA 0x0138 -#define SIRFUART_AFC_CTRL 0x0140 -#define SIRFUART_SWH_DMA_IO 0x0148 - -/* UART Line Control Register */ +struct sirfsoc_int_en { + u32 sirfsoc_rx_done_en; + u32 sirfsoc_tx_done_en; + u32 sirfsoc_rx_oflow_en; + u32 sirfsoc_tx_allout_en; + u32 sirfsoc_rx_io_dma_en; + u32 sirfsoc_tx_io_dma_en; + u32 sirfsoc_rxfifo_full_en; + u32 sirfsoc_txfifo_empty_en; + u32 sirfsoc_rxfifo_thd_en; + u32 sirfsoc_txfifo_thd_en; + u32 sirfsoc_frm_err_en; + u32 sirfsoc_rxd_brk_en; + u32 sirfsoc_rx_timeout_en; + u32 sirfsoc_parity_err_en; + u32 sirfsoc_cts_en; + u32 sirfsoc_rts_en; +}; + +struct sirfsoc_int_status { + u32 sirfsoc_rx_done; + u32 sirfsoc_tx_done; + u32 sirfsoc_rx_oflow; + u32 sirfsoc_tx_allout; + u32 sirfsoc_rx_io_dma; + u32 sirfsoc_tx_io_dma; + u32 sirfsoc_rxfifo_full; + u32 sirfsoc_txfifo_empty; + u32 sirfsoc_rxfifo_thd; + u32 sirfsoc_txfifo_thd; + u32 sirfsoc_frm_err; + u32 sirfsoc_rxd_brk; + u32 sirfsoc_rx_timeout; + u32 sirfsoc_parity_err; + u32 sirfsoc_cts; + u32 sirfsoc_rts; +}; + +enum sirfsoc_uart_type { + SIRF_REAL_UART, + SIRF_USP_UART, +}; + +struct sirfsoc_uart_register { + struct sirfsoc_register uart_reg; + struct sirfsoc_int_en uart_int_en; + struct sirfsoc_int_status uart_int_st; + struct sirfsoc_fifo_status fifo_status; + struct sirfsoc_uart_param uart_param; + enum sirfsoc_uart_type uart_type; +}; + +u32 usp_ff_full(int line) +{ + return 0x80; +} +u32 usp_ff_empty(int line) +{ + return 0x100; +} +u32 uart_ff_full(int line) +{ + return (line == 1) ? (0x20) : (0x80); +} +u32 uart_ff_empty(int line) +{ + return (line == 1) ? (0x40) : (0x100); +} +struct sirfsoc_uart_register sirfsoc_usp = { + .uart_reg = { + .sirfsoc_mode1 = 0x0000, + .sirfsoc_mode2 = 0x0004, + .sirfsoc_tx_frame_ctrl = 0x0008, + .sirfsoc_rx_frame_ctrl = 0x000c, + .sirfsoc_tx_rx_en = 0x0010, + .sirfsoc_int_en_reg = 0x0014, + .sirfsoc_int_st_reg = 0x0018, + .sirfsoc_async_param_reg = 0x0024, + .sirfsoc_tx_dma_io_ctrl = 0x0100, + .sirfsoc_tx_dma_io_len = 0x0104, + .sirfsoc_tx_fifo_ctrl = 0x0108, + .sirfsoc_tx_fifo_level_chk = 0x010c, + .sirfsoc_tx_fifo_op = 0x0110, + .sirfsoc_tx_fifo_status = 0x0114, + .sirfsoc_tx_fifo_data = 0x0118, + .sirfsoc_rx_dma_io_ctrl = 0x0120, + .sirfsoc_rx_dma_io_len = 0x0124, + .sirfsoc_rx_fifo_ctrl = 0x0128, + .sirfsoc_rx_fifo_level_chk = 0x012c, + .sirfsoc_rx_fifo_op = 0x0130, + .sirfsoc_rx_fifo_status = 0x0134, + .sirfsoc_rx_fifo_data = 0x0138, + }, + .uart_int_en = { + .sirfsoc_rx_done_en = BIT(0), + .sirfsoc_tx_done_en = BIT(1), + .sirfsoc_rx_oflow_en = BIT(2), + .sirfsoc_tx_allout_en = BIT(3), + .sirfsoc_rx_io_dma_en = BIT(4), + .sirfsoc_tx_io_dma_en = BIT(5), + .sirfsoc_rxfifo_full_en = BIT(6), + .sirfsoc_txfifo_empty_en = BIT(7), + .sirfsoc_rxfifo_thd_en = BIT(8), + .sirfsoc_txfifo_thd_en = BIT(9), + .sirfsoc_frm_err_en = BIT(10), + .sirfsoc_rx_timeout_en = BIT(11), + .sirfsoc_rxd_brk_en = BIT(15), + }, + .uart_int_st = { + .sirfsoc_rx_done = BIT(0), + .sirfsoc_tx_done = BIT(1), + .sirfsoc_rx_oflow = BIT(2), + .sirfsoc_tx_allout = BIT(3), + .sirfsoc_rx_io_dma = BIT(4), + .sirfsoc_tx_io_dma = BIT(5), + .sirfsoc_rxfifo_full = BIT(6), + .sirfsoc_txfifo_empty = BIT(7), + .sirfsoc_rxfifo_thd = BIT(8), + .sirfsoc_txfifo_thd = BIT(9), + .sirfsoc_frm_err = BIT(10), + .sirfsoc_rx_timeout = BIT(11), + .sirfsoc_rxd_brk = BIT(15), + }, + .fifo_status = { + .ff_full = usp_ff_full, + .ff_empty = usp_ff_empty, + }, + .uart_param = { + .uart_name = "ttySiRF", + .port_name = "sirfsoc-uart", + .uart_nr = 2, + .register_uart_nr = 3, + }, +}; + +struct sirfsoc_uart_register sirfsoc_uart = { + .uart_reg = { + .sirfsoc_line_ctrl = 0x0040, + .sirfsoc_tx_rx_en = 0x004c, + .sirfsoc_divisor = 0x0050, + .sirfsoc_int_en_reg = 0x0054, + .sirfsoc_int_st_reg = 0x0058, + .sirfsoc_tx_dma_io_ctrl = 0x0100, + .sirfsoc_tx_dma_io_len = 0x0104, + .sirfsoc_tx_fifo_ctrl = 0x0108, + .sirfsoc_tx_fifo_level_chk = 0x010c, + .sirfsoc_tx_fifo_op = 0x0110, + .sirfsoc_tx_fifo_status = 0x0114, + .sirfsoc_tx_fifo_data = 0x0118, + .sirfsoc_rx_dma_io_ctrl = 0x0120, + .sirfsoc_rx_dma_io_len = 0x0124, + .sirfsoc_rx_fifo_ctrl = 0x0128, + .sirfsoc_rx_fifo_level_chk = 0x012c, + .sirfsoc_rx_fifo_op = 0x0130, + .sirfsoc_rx_fifo_status = 0x0134, + .sirfsoc_rx_fifo_data = 0x0138, + .sirfsoc_afc_ctrl = 0x0140, + .sirfsoc_swh_dma_io = 0x0148, + }, + .uart_int_en = { + .sirfsoc_rx_done_en = BIT(0), + .sirfsoc_tx_done_en = BIT(1), + .sirfsoc_rx_oflow_en = BIT(2), + .sirfsoc_tx_allout_en = BIT(3), + .sirfsoc_rx_io_dma_en = BIT(4), + .sirfsoc_tx_io_dma_en = BIT(5), + .sirfsoc_rxfifo_full_en = BIT(6), + .sirfsoc_txfifo_empty_en = BIT(7), + .sirfsoc_rxfifo_thd_en = BIT(8), + .sirfsoc_txfifo_thd_en = BIT(9), + .sirfsoc_frm_err_en = BIT(10), + .sirfsoc_rxd_brk_en = BIT(11), + .sirfsoc_rx_timeout_en = BIT(12), + .sirfsoc_parity_err_en = BIT(13), + .sirfsoc_cts_en = BIT(14), + .sirfsoc_rts_en = BIT(15), + }, + .uart_int_st = { + .sirfsoc_rx_done = BIT(0), + .sirfsoc_tx_done = BIT(1), + .sirfsoc_rx_oflow = BIT(2), + .sirfsoc_tx_allout = BIT(3), + .sirfsoc_rx_io_dma = BIT(4), + .sirfsoc_tx_io_dma = BIT(5), + .sirfsoc_rxfifo_full = BIT(6), + .sirfsoc_txfifo_empty = BIT(7), + .sirfsoc_rxfifo_thd = BIT(8), + .sirfsoc_txfifo_thd = BIT(9), + .sirfsoc_frm_err = BIT(10), + .sirfsoc_rxd_brk = BIT(11), + .sirfsoc_rx_timeout = BIT(12), + .sirfsoc_parity_err = BIT(13), + .sirfsoc_cts = BIT(14), + .sirfsoc_rts = BIT(15), + }, + .fifo_status = { + .ff_full = uart_ff_full, + .ff_empty = uart_ff_empty, + }, + .uart_param = { + .uart_name = "ttySiRF", + .port_name = "sirfsoc_uart", + .uart_nr = 3, + .register_uart_nr = 0, + }, +}; +/* uart io ctrl */ #define SIRFUART_DATA_BIT_LEN_MASK 0x3 #define SIRFUART_DATA_BIT_LEN_5 BIT(0) #define SIRFUART_DATA_BIT_LEN_6 1 @@ -50,96 +279,93 @@ #define SIRFUART_LOOP_BACK BIT(7) #define SIRFUART_PARITY_MASK (7 << 3) #define SIRFUART_DUMMY_READ BIT(16) - -#define SIRFSOC_UART_RX_TIMEOUT(br, to) (((br) * (((to) + 999) / 1000)) / 1000) -#define SIRFUART_RECV_TIMEOUT_MASK (0xFFFF << 16) -#define SIRFUART_RECV_TIMEOUT(x) (((x) & 0xFFFF) << 16) - -/* UART Auto Flow Control */ -#define SIRFUART_AFC_RX_THD_MASK 0x000000FF +#define SIRFUART_AFC_CTRL_RX_THD 0x70 #define SIRFUART_AFC_RX_EN BIT(8) #define SIRFUART_AFC_TX_EN BIT(9) -#define SIRFUART_CTS_CTRL BIT(10) -#define SIRFUART_RTS_CTRL BIT(11) -#define SIRFUART_CTS_IN_STATUS BIT(12) -#define SIRFUART_RTS_OUT_STATUS BIT(13) - -/* UART Interrupt Enable Register */ -#define SIRFUART_RX_DONE_INT BIT(0) -#define SIRFUART_TX_DONE_INT BIT(1) -#define SIRFUART_RX_OFLOW_INT BIT(2) -#define SIRFUART_TX_ALLOUT_INT BIT(3) -#define SIRFUART_RX_IO_DMA_INT BIT(4) -#define SIRFUART_TX_IO_DMA_INT BIT(5) -#define SIRFUART_RXFIFO_FULL_INT BIT(6) -#define SIRFUART_TXFIFO_EMPTY_INT BIT(7) -#define SIRFUART_RXFIFO_THD_INT BIT(8) -#define SIRFUART_TXFIFO_THD_INT BIT(9) -#define SIRFUART_FRM_ERR_INT BIT(10) -#define SIRFUART_RXD_BREAK_INT BIT(11) -#define SIRFUART_RX_TIMEOUT_INT BIT(12) -#define SIRFUART_PARITY_ERR_INT BIT(13) -#define SIRFUART_CTS_INT_EN BIT(14) -#define SIRFUART_RTS_INT_EN BIT(15) - -/* UART Interrupt Status Register */ -#define SIRFUART_RX_DONE BIT(0) -#define SIRFUART_TX_DONE BIT(1) -#define SIRFUART_RX_OFLOW BIT(2) -#define SIRFUART_TX_ALL_EMPTY BIT(3) -#define SIRFUART_DMA_IO_RX_DONE BIT(4) -#define SIRFUART_DMA_IO_TX_DONE BIT(5) -#define SIRFUART_RXFIFO_FULL BIT(6) -#define SIRFUART_TXFIFO_EMPTY BIT(7) -#define SIRFUART_RXFIFO_THD_REACH BIT(8) -#define SIRFUART_TXFIFO_THD_REACH BIT(9) -#define SIRFUART_FRM_ERR BIT(10) -#define SIRFUART_RXD_BREAK BIT(11) -#define SIRFUART_RX_TIMEOUT BIT(12) -#define SIRFUART_PARITY_ERR BIT(13) -#define SIRFUART_CTS_CHANGE BIT(14) -#define SIRFUART_RTS_CHANGE BIT(15) -#define SIRFUART_PLUG_IN BIT(16) - -#define SIRFUART_ERR_INT_STAT \ - (SIRFUART_RX_OFLOW | \ - SIRFUART_FRM_ERR | \ - SIRFUART_RXD_BREAK | \ - SIRFUART_PARITY_ERR) -#define SIRFUART_ERR_INT_EN \ - (SIRFUART_RX_OFLOW_INT | \ - SIRFUART_FRM_ERR_INT | \ - SIRFUART_RXD_BREAK_INT | \ - SIRFUART_PARITY_ERR_INT) -#define SIRFUART_TX_INT_EN SIRFUART_TXFIFO_EMPTY_INT -#define SIRFUART_RX_IO_INT_EN \ - (SIRFUART_RX_TIMEOUT_INT | \ - SIRFUART_RXFIFO_THD_INT | \ - SIRFUART_RXFIFO_FULL_INT | \ - SIRFUART_ERR_INT_EN) - +#define SIRFUART_AFC_CTS_CTRL BIT(10) +#define SIRFUART_AFC_RTS_CTRL BIT(11) +#define SIRFUART_AFC_CTS_STATUS BIT(12) +#define SIRFUART_AFC_RTS_STATUS BIT(13) /* UART FIFO Register */ -#define SIRFUART_TX_FIFO_STOP 0x0 -#define SIRFUART_TX_FIFO_RESET 0x1 -#define SIRFUART_TX_FIFO_START 0x2 -#define SIRFUART_RX_FIFO_STOP 0x0 -#define SIRFUART_RX_FIFO_RESET 0x1 -#define SIRFUART_RX_FIFO_START 0x2 -#define SIRFUART_TX_MODE_DMA 0 -#define SIRFUART_TX_MODE_IO 1 -#define SIRFUART_RX_MODE_DMA 0 -#define SIRFUART_RX_MODE_IO 1 - -#define SIRFUART_RX_EN 0x1 -#define SIRFUART_TX_EN 0x2 +#define SIRFUART_FIFO_STOP 0x0 +#define SIRFUART_FIFO_RESET BIT(0) +#define SIRFUART_FIFO_START BIT(1) +#define SIRFUART_RX_EN BIT(0) +#define SIRFUART_TX_EN BIT(1) + +#define SIRFUART_IO_MODE BIT(0) +#define SIRFUART_DMA_MODE 0x0 + +/* Macro Specific*/ +#define SIRFUART_INT_EN_CLR 0x0060 +/* Baud Rate Calculation */ +#define SIRF_MIN_SAMPLE_DIV 0xf +#define SIRF_MAX_SAMPLE_DIV 0x3f +#define SIRF_IOCLK_DIV_MAX 0xffff +#define SIRF_SAMPLE_DIV_SHIFT 16 +#define SIRF_IOCLK_DIV_MASK 0xffff +#define SIRF_SAMPLE_DIV_MASK 0x3f0000 +#define SIRF_BAUD_RATE_SUPPORT_NR 18 + +/* USP SPEC */ +#define SIRFSOC_USP_ENDIAN_CTRL_LSBF BIT(4) +#define SIRFSOC_USP_EN BIT(5) +#define SIRFSOC_USP_MODE2_RXD_DELAY_OFFSET 0 +#define SIRFSOC_USP_MODE2_TXD_DELAY_OFFSET 8 +#define SIRFSOC_USP_MODE2_CLK_DIVISOR_MASK 0x3ff +#define SIRFSOC_USP_MODE2_CLK_DIVISOR_OFFSET 21 +#define SIRFSOC_USP_TX_DATA_LEN_OFFSET 0 +#define SIRFSOC_USP_TX_SYNC_LEN_OFFSET 8 +#define SIRFSOC_USP_TX_FRAME_LEN_OFFSET 16 +#define SIRFSOC_USP_TX_SHIFTER_LEN_OFFSET 24 +#define SIRFSOC_USP_TX_CLK_DIVISOR_OFFSET 30 +#define SIRFSOC_USP_RX_DATA_LEN_OFFSET 0 +#define SIRFSOC_USP_RX_FRAME_LEN_OFFSET 8 +#define SIRFSOC_USP_RX_SHIFTER_LEN_OFFSET 16 +#define SIRFSOC_USP_RX_CLK_DIVISOR_OFFSET 24 +#define SIRFSOC_USP_ASYNC_DIV2_MASK 0x3f +#define SIRFSOC_USP_ASYNC_DIV2_OFFSET 16 + +/* USP-UART Common */ +#define SIRFSOC_UART_RX_TIMEOUT(br, to) (((br) * (((to) + 999) / 1000)) / 1000) +#define SIRFUART_RECV_TIMEOUT_VALUE(x) \ + (((x) > 0xFFFF) ? 0xFFFF : ((x) & 0xFFFF)) +#define SIRFUART_RECV_TIMEOUT(port, x) \ + (((port)->line > 2) ? (x & 0xFFFF) : ((x) & 0xFFFF) << 16) + +#define SIRFUART_FIFO_THD(port) ((port->line) == 1 ? 16 : 64) +#define SIRFUART_ERR_INT_STAT(port, unit_st) \ + (uint_st->sirfsoc_rx_oflow | \ + uint_st->sirfsoc_frm_err | \ + uint_st->sirfsoc_rxd_brk | \ + ((port->line > 2) ? 0 : uint_st->sirfsoc_parity_err)) +#define SIRFUART_RX_IO_INT_EN(port, uint_en) \ + (uint_en->sirfsoc_rx_timeout_en |\ + uint_en->sirfsoc_rxfifo_thd_en |\ + uint_en->sirfsoc_rxfifo_full_en |\ + uint_en->sirfsoc_frm_err_en |\ + uint_en->sirfsoc_rx_oflow_en |\ + uint_en->sirfsoc_rxd_brk_en |\ + ((port->line > 2) ? 0 : uint_en->sirfsoc_parity_err_en)) +#define SIRFUART_RX_IO_INT_ST(uint_st) \ + (uint_st->sirfsoc_rx_timeout |\ + uint_st->sirfsoc_rxfifo_thd |\ + uint_st->sirfsoc_rxfifo_full) +#define SIRFUART_CTS_INT_ST(uint_st) (uint_st->sirfsoc_cts) +#define SIRFUART_RX_DMA_INT_EN(port, uint_en) \ + (uint_en->sirfsoc_rx_timeout_en |\ + uint_en->sirfsoc_frm_err_en |\ + uint_en->sirfsoc_rx_oflow_en |\ + uint_en->sirfsoc_rxd_brk_en |\ + ((port->line > 2) ? 0 : uint_en->sirfsoc_parity_err_en)) /* Generic Definitions */ #define SIRFSOC_UART_NAME "ttySiRF" #define SIRFSOC_UART_MAJOR 0 #define SIRFSOC_UART_MINOR 0 #define SIRFUART_PORT_NAME "sirfsoc-uart" #define SIRFUART_MAP_SIZE 0x200 -#define SIRFSOC_UART_NR 5 +#define SIRFSOC_UART_NR 6 #define SIRFSOC_PORT_TYPE 0xa5 /* Baud Rate Calculation */ @@ -151,19 +377,80 @@ #define SIRF_SAMPLE_DIV_MASK 0x3f0000 #define SIRF_BAUD_RATE_SUPPORT_NR 18 +/* Uart Common Use Macro*/ +#define SIRFSOC_RX_DMA_BUF_SIZE 256 +#define BYTES_TO_ALIGN(dma_addr) ((unsigned long)(dma_addr) & 0x3) +#define LOOP_DMA_BUFA_FILL 1 +#define LOOP_DMA_BUFB_FILL 2 +#define TX_TRAN_PIO 1 +#define TX_TRAN_DMA 2 +/* Uart Fifo Level Chk */ +#define SIRFUART_TX_FIFO_SC_OFFSET 0 +#define SIRFUART_TX_FIFO_LC_OFFSET 10 +#define SIRFUART_TX_FIFO_HC_OFFSET 20 +#define SIRFUART_TX_FIFO_CHK_SC(line, value) ((((line) == 1) ? (value & 0x3) :\ + (value & 0x1f)) << SIRFUART_TX_FIFO_SC_OFFSET) +#define SIRFUART_TX_FIFO_CHK_LC(line, value) ((((line) == 1) ? (value & 0x3) :\ + (value & 0x1f)) << SIRFUART_TX_FIFO_LC_OFFSET) +#define SIRFUART_TX_FIFO_CHK_HC(line, value) ((((line) == 1) ? (value & 0x3) :\ + (value & 0x1f)) << SIRFUART_TX_FIFO_HC_OFFSET) + +#define SIRFUART_RX_FIFO_CHK_SC SIRFUART_TX_FIFO_CHK_SC +#define SIRFUART_RX_FIFO_CHK_LC SIRFUART_TX_FIFO_CHK_LC +#define SIRFUART_RX_FIFO_CHK_HC SIRFUART_TX_FIFO_CHK_HC +/* Indicate how many buffers used */ +#define SIRFSOC_RX_LOOP_BUF_CNT 2 + +/* Indicate if DMA channel valid */ +#define IS_DMA_CHAN_VALID(x) ((x) != -1) +#define UNVALID_DMA_CHAN -1 /* For Fast Baud Rate Calculation */ struct sirfsoc_baudrate_to_regv { unsigned int baud_rate; unsigned int reg_val; }; +enum sirfsoc_tx_state { + TX_DMA_IDLE, + TX_DMA_RUNNING, + TX_DMA_PAUSE, +}; + +struct sirfsoc_loop_buffer { + struct circ_buf xmit; + dma_cookie_t cookie; + struct dma_async_tx_descriptor *desc; + dma_addr_t dma_addr; +}; + struct sirfsoc_uart_port { - unsigned char hw_flow_ctrl; - unsigned char ms_enabled; + bool hw_flow_ctrl; + bool ms_enabled; struct uart_port port; - struct pinctrl *p; struct clk *clk; + /* for SiRFmarco, there are SET/CLR for UART_INT_EN */ + bool is_marco; + struct sirfsoc_uart_register *uart_reg; + int rx_dma_no; + int tx_dma_no; + struct dma_chan *rx_dma_chan; + struct dma_chan *tx_dma_chan; + dma_addr_t tx_dma_addr; + struct dma_async_tx_descriptor *tx_dma_desc; + spinlock_t rx_lock; + spinlock_t tx_lock; + struct tasklet_struct rx_dma_complete_tasklet; + struct tasklet_struct rx_tmo_process_tasklet; + unsigned int rx_io_count; + unsigned long transfer_size; + enum sirfsoc_tx_state tx_dma_state; + unsigned int cts_gpio; + unsigned int rts_gpio; + + struct sirfsoc_loop_buffer rx_dma_items[SIRFSOC_RX_LOOP_BUF_CNT]; + int rx_completed; + int rx_issued; }; /* Hardware Flow Control */ diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c new file mode 100644 index 000000000000..21e6e84c0df8 --- /dev/null +++ b/drivers/tty/serial/st-asc.c @@ -0,0 +1,932 @@ +/* + * st-asc.c: ST Asynchronous serial controller (ASC) driver + * + * Copyright (C) 2003-2013 STMicroelectronics (R&D) Limited + * + * 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. + * + */ + +#if defined(CONFIG_SERIAL_ST_ASC_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/io.h> +#include <linux/irq.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/serial_core.h> +#include <linux/clk.h> + +#define DRIVER_NAME "st-asc" +#define ASC_SERIAL_NAME "ttyAS" +#define ASC_FIFO_SIZE 16 +#define ASC_MAX_PORTS 8 + +struct asc_port { + struct uart_port port; + struct clk *clk; + unsigned int hw_flow_control:1; + unsigned int force_m1:1; +}; + +static struct asc_port asc_ports[ASC_MAX_PORTS]; +static struct uart_driver asc_uart_driver; + +/*---- UART Register definitions ------------------------------*/ + +/* Register offsets */ + +#define ASC_BAUDRATE 0x00 +#define ASC_TXBUF 0x04 +#define ASC_RXBUF 0x08 +#define ASC_CTL 0x0C +#define ASC_INTEN 0x10 +#define ASC_STA 0x14 +#define ASC_GUARDTIME 0x18 +#define ASC_TIMEOUT 0x1C +#define ASC_TXRESET 0x20 +#define ASC_RXRESET 0x24 +#define ASC_RETRIES 0x28 + +/* ASC_RXBUF */ +#define ASC_RXBUF_PE 0x100 +#define ASC_RXBUF_FE 0x200 +/** + * Some of status comes from higher bits of the character and some come from + * the status register. Combining both of them in to single status using dummy + * bits. + */ +#define ASC_RXBUF_DUMMY_RX 0x10000 +#define ASC_RXBUF_DUMMY_BE 0x20000 +#define ASC_RXBUF_DUMMY_OE 0x40000 + +/* ASC_CTL */ + +#define ASC_CTL_MODE_MSK 0x0007 +#define ASC_CTL_MODE_8BIT 0x0001 +#define ASC_CTL_MODE_7BIT_PAR 0x0003 +#define ASC_CTL_MODE_9BIT 0x0004 +#define ASC_CTL_MODE_8BIT_WKUP 0x0005 +#define ASC_CTL_MODE_8BIT_PAR 0x0007 +#define ASC_CTL_STOP_MSK 0x0018 +#define ASC_CTL_STOP_HALFBIT 0x0000 +#define ASC_CTL_STOP_1BIT 0x0008 +#define ASC_CTL_STOP_1_HALFBIT 0x0010 +#define ASC_CTL_STOP_2BIT 0x0018 +#define ASC_CTL_PARITYODD 0x0020 +#define ASC_CTL_LOOPBACK 0x0040 +#define ASC_CTL_RUN 0x0080 +#define ASC_CTL_RXENABLE 0x0100 +#define ASC_CTL_SCENABLE 0x0200 +#define ASC_CTL_FIFOENABLE 0x0400 +#define ASC_CTL_CTSENABLE 0x0800 +#define ASC_CTL_BAUDMODE 0x1000 + +/* ASC_GUARDTIME */ + +#define ASC_GUARDTIME_MSK 0x00FF + +/* ASC_INTEN */ + +#define ASC_INTEN_RBE 0x0001 +#define ASC_INTEN_TE 0x0002 +#define ASC_INTEN_THE 0x0004 +#define ASC_INTEN_PE 0x0008 +#define ASC_INTEN_FE 0x0010 +#define ASC_INTEN_OE 0x0020 +#define ASC_INTEN_TNE 0x0040 +#define ASC_INTEN_TOI 0x0080 +#define ASC_INTEN_RHF 0x0100 + +/* ASC_RETRIES */ + +#define ASC_RETRIES_MSK 0x00FF + +/* ASC_RXBUF */ + +#define ASC_RXBUF_MSK 0x03FF + +/* ASC_STA */ + +#define ASC_STA_RBF 0x0001 +#define ASC_STA_TE 0x0002 +#define ASC_STA_THE 0x0004 +#define ASC_STA_PE 0x0008 +#define ASC_STA_FE 0x0010 +#define ASC_STA_OE 0x0020 +#define ASC_STA_TNE 0x0040 +#define ASC_STA_TOI 0x0080 +#define ASC_STA_RHF 0x0100 +#define ASC_STA_TF 0x0200 +#define ASC_STA_NKD 0x0400 + +/* ASC_TIMEOUT */ + +#define ASC_TIMEOUT_MSK 0x00FF + +/* ASC_TXBUF */ + +#define ASC_TXBUF_MSK 0x01FF + +/*---- Inline function definitions ---------------------------*/ + +static inline struct asc_port *to_asc_port(struct uart_port *port) +{ + return container_of(port, struct asc_port, port); +} + +static inline u32 asc_in(struct uart_port *port, u32 offset) +{ + return readl(port->membase + offset); +} + +static inline void asc_out(struct uart_port *port, u32 offset, u32 value) +{ + writel(value, port->membase + offset); +} + +/* + * Some simple utility functions to enable and disable interrupts. + * Note that these need to be called with interrupts disabled. + */ +static inline void asc_disable_tx_interrupts(struct uart_port *port) +{ + u32 intenable = asc_in(port, ASC_INTEN) & ~ASC_INTEN_THE; + asc_out(port, ASC_INTEN, intenable); + (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */ +} + +static inline void asc_enable_tx_interrupts(struct uart_port *port) +{ + u32 intenable = asc_in(port, ASC_INTEN) | ASC_INTEN_THE; + asc_out(port, ASC_INTEN, intenable); +} + +static inline void asc_disable_rx_interrupts(struct uart_port *port) +{ + u32 intenable = asc_in(port, ASC_INTEN) & ~ASC_INTEN_RBE; + asc_out(port, ASC_INTEN, intenable); + (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */ +} + +static inline void asc_enable_rx_interrupts(struct uart_port *port) +{ + u32 intenable = asc_in(port, ASC_INTEN) | ASC_INTEN_RBE; + asc_out(port, ASC_INTEN, intenable); +} + +static inline u32 asc_txfifo_is_empty(struct uart_port *port) +{ + return asc_in(port, ASC_STA) & ASC_STA_TE; +} + +static inline int asc_txfifo_is_full(struct uart_port *port) +{ + return asc_in(port, ASC_STA) & ASC_STA_TF; +} + +static inline const char *asc_port_name(struct uart_port *port) +{ + return to_platform_device(port->dev)->name; +} + +/*----------------------------------------------------------------------*/ + +/* + * This section contains code to support the use of the ASC as a + * generic serial port. + */ + +static inline unsigned asc_hw_txroom(struct uart_port *port) +{ + u32 status = asc_in(port, ASC_STA); + + if (status & ASC_STA_THE) + return port->fifosize / 2; + else if (!(status & ASC_STA_TF)) + return 1; + + return 0; +} + +/* + * Start transmitting chars. + * This is called from both interrupt and task level. + * Either way interrupts are disabled. + */ +static void asc_transmit_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + int txroom; + unsigned char c; + + txroom = asc_hw_txroom(port); + + if ((txroom != 0) && port->x_char) { + c = port->x_char; + port->x_char = 0; + asc_out(port, ASC_TXBUF, c); + port->icount.tx++; + txroom = asc_hw_txroom(port); + } + + if (uart_tx_stopped(port)) { + /* + * We should try and stop the hardware here, but I + * don't think the ASC has any way to do that. + */ + asc_disable_tx_interrupts(port); + return; + } + + if (uart_circ_empty(xmit)) { + asc_disable_tx_interrupts(port); + return; + } + + if (txroom == 0) + return; + + do { + c = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + asc_out(port, ASC_TXBUF, c); + port->icount.tx++; + txroom--; + } while ((txroom > 0) && (!uart_circ_empty(xmit))); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + asc_disable_tx_interrupts(port); +} + +static void asc_receive_chars(struct uart_port *port) +{ + struct tty_port *tport = &port->state->port; + unsigned long status; + unsigned long c = 0; + char flag; + + if (port->irq_wake) + pm_wakeup_event(tport->tty->dev, 0); + + while ((status = asc_in(port, ASC_STA)) & ASC_STA_RBF) { + c = asc_in(port, ASC_RXBUF) | ASC_RXBUF_DUMMY_RX; + flag = TTY_NORMAL; + port->icount.rx++; + + if ((c & (ASC_RXBUF_FE | ASC_RXBUF_PE)) || + status & ASC_STA_OE) { + + if (c & ASC_RXBUF_FE) { + if (c == ASC_RXBUF_FE) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + c |= ASC_RXBUF_DUMMY_BE; + } else { + port->icount.frame++; + } + } else if (c & ASC_RXBUF_PE) { + port->icount.parity++; + } + /* + * Reading any data from the RX FIFO clears the + * overflow error condition. + */ + if (status & ASC_STA_OE) { + port->icount.overrun++; + c |= ASC_RXBUF_DUMMY_OE; + } + + c &= port->read_status_mask; + + if (c & ASC_RXBUF_DUMMY_BE) + flag = TTY_BREAK; + else if (c & ASC_RXBUF_PE) + flag = TTY_PARITY; + else if (c & ASC_RXBUF_FE) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, c)) + continue; + + uart_insert_char(port, c, ASC_RXBUF_DUMMY_OE, c & 0xff, flag); + } + + /* Tell the rest of the system the news. New characters! */ + tty_flip_buffer_push(tport); +} + +static irqreturn_t asc_interrupt(int irq, void *ptr) +{ + struct uart_port *port = ptr; + u32 status; + + spin_lock(&port->lock); + + status = asc_in(port, ASC_STA); + + if (status & ASC_STA_RBF) { + /* Receive FIFO not empty */ + asc_receive_chars(port); + } + + if ((status & ASC_STA_THE) && + (asc_in(port, ASC_INTEN) & ASC_INTEN_THE)) { + /* Transmitter FIFO at least half empty */ + asc_transmit_chars(port); + } + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +/*----------------------------------------------------------------------*/ + +/* + * UART Functions + */ + +static unsigned int asc_tx_empty(struct uart_port *port) +{ + return asc_txfifo_is_empty(port) ? TIOCSER_TEMT : 0; +} + +static void asc_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* + * This routine is used for seting signals of: DTR, DCD, CTS/RTS + * We use ASC's hardware for CTS/RTS, so don't need any for that. + * Some boards have DTR and DCD implemented using PIO pins, + * code to do this should be hooked in here. + */ +} + +static unsigned int asc_get_mctrl(struct uart_port *port) +{ + /* + * This routine is used for geting signals of: DTR, DCD, DSR, RI, + * and CTS/RTS + */ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +/* There are probably characters waiting to be transmitted. */ +static void asc_start_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + if (!uart_circ_empty(xmit)) + asc_enable_tx_interrupts(port); +} + +/* Transmit stop */ +static void asc_stop_tx(struct uart_port *port) +{ + asc_disable_tx_interrupts(port); +} + +/* Receive stop */ +static void asc_stop_rx(struct uart_port *port) +{ + asc_disable_rx_interrupts(port); +} + +/* Force modem status interrupts on */ +static void asc_enable_ms(struct uart_port *port) +{ + /* Nothing here yet .. */ +} + +/* Handle breaks - ignored by us */ +static void asc_break_ctl(struct uart_port *port, int break_state) +{ + /* Nothing here yet .. */ +} + +/* + * Enable port for reception. + */ +static int asc_startup(struct uart_port *port) +{ + if (request_irq(port->irq, asc_interrupt, IRQF_NO_SUSPEND, + asc_port_name(port), port)) { + dev_err(port->dev, "cannot allocate irq.\n"); + return -ENODEV; + } + + asc_transmit_chars(port); + asc_enable_rx_interrupts(port); + + return 0; +} + +static void asc_shutdown(struct uart_port *port) +{ + asc_disable_tx_interrupts(port); + asc_disable_rx_interrupts(port); + free_irq(port->irq, port); +} + +static void asc_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct asc_port *ascport = to_asc_port(port); + unsigned long flags = 0; + u32 ctl; + + switch (state) { + case UART_PM_STATE_ON: + clk_prepare_enable(ascport->clk); + break; + case UART_PM_STATE_OFF: + /* + * Disable the ASC baud rate generator, which is as close as + * we can come to turning it off. Note this is not called with + * the port spinlock held. + */ + spin_lock_irqsave(&port->lock, flags); + ctl = asc_in(port, ASC_CTL) & ~ASC_CTL_RUN; + asc_out(port, ASC_CTL, ctl); + spin_unlock_irqrestore(&port->lock, flags); + clk_disable_unprepare(ascport->clk); + break; + } +} + +static void asc_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct asc_port *ascport = to_asc_port(port); + unsigned int baud; + u32 ctrl_val; + tcflag_t cflag; + unsigned long flags; + + /* Update termios to reflect hardware capabilities */ + termios->c_cflag &= ~(CMSPAR | + (ascport->hw_flow_control ? 0 : CRTSCTS)); + + port->uartclk = clk_get_rate(ascport->clk); + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + cflag = termios->c_cflag; + + spin_lock_irqsave(&port->lock, flags); + + /* read control register */ + ctrl_val = asc_in(port, ASC_CTL); + + /* stop serial port and reset value */ + asc_out(port, ASC_CTL, (ctrl_val & ~ASC_CTL_RUN)); + ctrl_val = ASC_CTL_RXENABLE | ASC_CTL_FIFOENABLE; + + /* reset fifo rx & tx */ + asc_out(port, ASC_TXRESET, 1); + asc_out(port, ASC_RXRESET, 1); + + /* set character length */ + if ((cflag & CSIZE) == CS7) { + ctrl_val |= ASC_CTL_MODE_7BIT_PAR; + } else { + ctrl_val |= (cflag & PARENB) ? ASC_CTL_MODE_8BIT_PAR : + ASC_CTL_MODE_8BIT; + } + + /* set stop bit */ + ctrl_val |= (cflag & CSTOPB) ? ASC_CTL_STOP_2BIT : ASC_CTL_STOP_1BIT; + + /* odd parity */ + if (cflag & PARODD) + ctrl_val |= ASC_CTL_PARITYODD; + + /* hardware flow control */ + if ((cflag & CRTSCTS)) + ctrl_val |= ASC_CTL_CTSENABLE; + + if ((baud < 19200) && !ascport->force_m1) { + asc_out(port, ASC_BAUDRATE, (port->uartclk / (16 * baud))); + } else { + /* + * MODE 1: recommended for high bit rates (above 19.2K) + * + * baudrate * 16 * 2^16 + * ASCBaudRate = ------------------------ + * inputclock + * + * However to keep the maths inside 32bits we divide top and + * bottom by 64. The +1 is to avoid a divide by zero if the + * input clock rate is something unexpected. + */ + u32 counter = (baud * 16384) / ((port->uartclk / 64) + 1); + asc_out(port, ASC_BAUDRATE, counter); + ctrl_val |= ASC_CTL_BAUDMODE; + } + + uart_update_timeout(port, cflag, baud); + + ascport->port.read_status_mask = ASC_RXBUF_DUMMY_OE; + if (termios->c_iflag & INPCK) + ascport->port.read_status_mask |= ASC_RXBUF_FE | ASC_RXBUF_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + ascport->port.read_status_mask |= ASC_RXBUF_DUMMY_BE; + + /* + * Characters to ignore + */ + ascport->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + ascport->port.ignore_status_mask |= ASC_RXBUF_FE | ASC_RXBUF_PE; + if (termios->c_iflag & IGNBRK) { + ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_BE; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_OE; + } + + /* + * Ignore all characters if CREAD is not set. + */ + if (!(termios->c_cflag & CREAD)) + ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_RX; + + /* Set the timeout */ + asc_out(port, ASC_TIMEOUT, 20); + + /* write final value and enable port */ + asc_out(port, ASC_CTL, (ctrl_val | ASC_CTL_RUN)); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *asc_type(struct uart_port *port) +{ + return (port->type == PORT_ASC) ? DRIVER_NAME : NULL; +} + +static void asc_release_port(struct uart_port *port) +{ +} + +static int asc_request_port(struct uart_port *port) +{ + return 0; +} + +/* + * Called when the port is opened, and UPF_BOOT_AUTOCONF flag is set + * Set type field if successful + */ +static void asc_config_port(struct uart_port *port, int flags) +{ + if ((flags & UART_CONFIG_TYPE)) + port->type = PORT_ASC; +} + +static int +asc_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + /* No user changeable parameters */ + return -EINVAL; +} + +#ifdef CONFIG_CONSOLE_POLL +/* + * Console polling routines for writing and reading from the uart while + * in an interrupt or debug context (i.e. kgdb). + */ + +static int asc_get_poll_char(struct uart_port *port) +{ + if (!(asc_in(port, ASC_STA) & ASC_STA_RBF)) + return NO_POLL_CHAR; + + return asc_in(port, ASC_RXBUF); +} + +static void asc_put_poll_char(struct uart_port *port, unsigned char c) +{ + while (asc_txfifo_is_full(port)) + cpu_relax(); + asc_out(port, ASC_TXBUF, c); +} + +#endif /* CONFIG_CONSOLE_POLL */ + +/*---------------------------------------------------------------------*/ + +static struct uart_ops asc_uart_ops = { + .tx_empty = asc_tx_empty, + .set_mctrl = asc_set_mctrl, + .get_mctrl = asc_get_mctrl, + .start_tx = asc_start_tx, + .stop_tx = asc_stop_tx, + .stop_rx = asc_stop_rx, + .enable_ms = asc_enable_ms, + .break_ctl = asc_break_ctl, + .startup = asc_startup, + .shutdown = asc_shutdown, + .set_termios = asc_set_termios, + .type = asc_type, + .release_port = asc_release_port, + .request_port = asc_request_port, + .config_port = asc_config_port, + .verify_port = asc_verify_port, + .pm = asc_pm, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = asc_get_poll_char, + .poll_put_char = asc_put_poll_char, +#endif /* CONFIG_CONSOLE_POLL */ +}; + +static int asc_init_port(struct asc_port *ascport, + struct platform_device *pdev) +{ + struct uart_port *port = &ascport->port; + struct resource *res; + + port->iotype = UPIO_MEM; + port->flags = UPF_BOOT_AUTOCONF; + port->ops = &asc_uart_ops; + port->fifosize = ASC_FIFO_SIZE; + port->dev = &pdev->dev; + port->irq = platform_get_irq(pdev, 0); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + port->membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(port->membase)) + return PTR_ERR(port->membase); + port->mapbase = res->start; + + spin_lock_init(&port->lock); + + ascport->clk = devm_clk_get(&pdev->dev, NULL); + + if (WARN_ON(IS_ERR(ascport->clk))) + return -EINVAL; + /* ensure that clk rate is correct by enabling the clk */ + clk_prepare_enable(ascport->clk); + ascport->port.uartclk = clk_get_rate(ascport->clk); + WARN_ON(ascport->port.uartclk == 0); + clk_disable_unprepare(ascport->clk); + + return 0; +} + +static struct asc_port *asc_of_get_asc_port(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + int id; + + if (!np) + return NULL; + + id = of_alias_get_id(np, ASC_SERIAL_NAME); + + if (id < 0) + id = 0; + + if (WARN_ON(id >= ASC_MAX_PORTS)) + return NULL; + + asc_ports[id].hw_flow_control = of_property_read_bool(np, + "st,hw-flow-control"); + asc_ports[id].force_m1 = of_property_read_bool(np, "st,force_m1"); + asc_ports[id].port.line = id; + return &asc_ports[id]; +} + +#ifdef CONFIG_OF +static struct of_device_id asc_match[] = { + { .compatible = "st,asc", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, asc_match); +#endif + +static int asc_serial_probe(struct platform_device *pdev) +{ + int ret; + struct asc_port *ascport; + + ascport = asc_of_get_asc_port(pdev); + if (!ascport) + return -ENODEV; + + ret = asc_init_port(ascport, pdev); + if (ret) + return ret; + + ret = uart_add_one_port(&asc_uart_driver, &ascport->port); + if (ret) + return ret; + + platform_set_drvdata(pdev, &ascport->port); + + return 0; +} + +static int asc_serial_remove(struct platform_device *pdev) +{ + struct uart_port *port = platform_get_drvdata(pdev); + + return uart_remove_one_port(&asc_uart_driver, port); +} + +#ifdef CONFIG_PM_SLEEP +static int asc_serial_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct uart_port *port = platform_get_drvdata(pdev); + + return uart_suspend_port(&asc_uart_driver, port); +} + +static int asc_serial_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct uart_port *port = platform_get_drvdata(pdev); + + return uart_resume_port(&asc_uart_driver, port); +} + +#endif /* CONFIG_PM_SLEEP */ + +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_SERIAL_ST_ASC_CONSOLE +static void asc_console_putchar(struct uart_port *port, int ch) +{ + unsigned int timeout = 1000000; + + /* Wait for upto 1 second in case flow control is stopping us. */ + while (--timeout && asc_txfifo_is_full(port)) + udelay(1); + + asc_out(port, ASC_TXBUF, ch); +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + */ + +static void asc_console_write(struct console *co, const char *s, unsigned count) +{ + struct uart_port *port = &asc_ports[co->index].port; + unsigned long flags; + unsigned long timeout = 1000000; + int locked = 1; + u32 intenable; + + local_irq_save(flags); + if (port->sysrq) + locked = 0; /* asc_interrupt has already claimed the lock */ + else if (oops_in_progress) + locked = spin_trylock(&port->lock); + else + spin_lock(&port->lock); + + /* + * Disable interrupts so we don't get the IRQ line bouncing + * up and down while interrupts are disabled. + */ + intenable = asc_in(port, ASC_INTEN); + asc_out(port, ASC_INTEN, 0); + (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */ + + uart_console_write(port, s, count, asc_console_putchar); + + while (--timeout && !asc_txfifo_is_empty(port)) + udelay(1); + + asc_out(port, ASC_INTEN, intenable); + + if (locked) + spin_unlock(&port->lock); + local_irq_restore(flags); +} + +static int asc_console_setup(struct console *co, char *options) +{ + struct asc_port *ascport; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index >= ASC_MAX_PORTS) + return -ENODEV; + + ascport = &asc_ports[co->index]; + + /* + * This driver does not support early console initialization + * (use ARM early printk support instead), so we only expect + * this to be called during the uart port registration when the + * driver gets probed and the port should be mapped at that point. + */ + BUG_ON(ascport->port.mapbase == 0 || ascport->port.membase == NULL); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&ascport->port, co, baud, parity, bits, flow); +} + +static struct console asc_console = { + .name = ASC_SERIAL_NAME, + .device = uart_console_device, + .write = asc_console_write, + .setup = asc_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &asc_uart_driver, +}; + +#define ASC_SERIAL_CONSOLE (&asc_console) + +#else +#define ASC_SERIAL_CONSOLE NULL +#endif /* CONFIG_SERIAL_ST_ASC_CONSOLE */ + +static struct uart_driver asc_uart_driver = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = ASC_SERIAL_NAME, + .major = 0, + .minor = 0, + .nr = ASC_MAX_PORTS, + .cons = ASC_SERIAL_CONSOLE, +}; + +static const struct dev_pm_ops asc_serial_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(asc_serial_suspend, asc_serial_resume) +}; + +static struct platform_driver asc_serial_driver = { + .probe = asc_serial_probe, + .remove = asc_serial_remove, + .driver = { + .name = DRIVER_NAME, + .pm = &asc_serial_pm_ops, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(asc_match), + }, +}; + +static int __init asc_init(void) +{ + int ret; + static char banner[] __initdata = + KERN_INFO "STMicroelectronics ASC driver initialized\n"; + + printk(banner); + + ret = uart_register_driver(&asc_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&asc_serial_driver); + if (ret) + uart_unregister_driver(&asc_uart_driver); + + return ret; +} + +static void __exit asc_exit(void) +{ + platform_driver_unregister(&asc_serial_driver); + uart_unregister_driver(&asc_uart_driver); +} + +module_init(asc_init); +module_exit(asc_exit); + +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_AUTHOR("STMicroelectronics (R&D) Limited"); +MODULE_DESCRIPTION("STMicroelectronics ASC serial port driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index ba60708053e0..cf86e729532b 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -577,7 +577,7 @@ static int hv_probe(struct platform_device *op) if (err) goto out_remove_port; - dev_set_drvdata(&op->dev, port); + platform_set_drvdata(op, port); return 0; @@ -601,7 +601,7 @@ out_free_port: static int hv_remove(struct platform_device *dev) { - struct uart_port *port = dev_get_drvdata(&dev->dev); + struct uart_port *port = platform_get_drvdata(dev); free_irq(port->irq, port); @@ -612,8 +612,6 @@ static int hv_remove(struct platform_device *dev) kfree(port); sunhv_port = NULL; - dev_set_drvdata(&dev->dev, NULL); - return 0; } diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index a422c8b55a47..5d6136b2a04a 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -1037,7 +1037,7 @@ static int sab_probe(struct platform_device *op) if (err) goto out3; - dev_set_drvdata(&op->dev, &up[0]); + platform_set_drvdata(op, &up[0]); inst++; @@ -1059,7 +1059,7 @@ out: static int sab_remove(struct platform_device *op) { - struct uart_sunsab_port *up = dev_get_drvdata(&op->dev); + struct uart_sunsab_port *up = platform_get_drvdata(op); uart_remove_one_port(&sunsab_reg, &up[1].port); uart_remove_one_port(&sunsab_reg, &up[0].port); @@ -1070,8 +1070,6 @@ static int sab_remove(struct platform_device *op) up[0].port.membase, sizeof(union sab82532_async_regs)); - dev_set_drvdata(&op->dev, NULL); - return 0; } diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index 0d8465728473..699cc1b5f6aa 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -1454,7 +1454,7 @@ static int su_probe(struct platform_device *op) kfree(up); return err; } - dev_set_drvdata(&op->dev, up); + platform_set_drvdata(op, up); nr_inst++; @@ -1483,7 +1483,7 @@ static int su_probe(struct platform_device *op) if (err) goto out_unmap; - dev_set_drvdata(&op->dev, up); + platform_set_drvdata(op, up); nr_inst++; @@ -1496,7 +1496,7 @@ out_unmap: static int su_remove(struct platform_device *op) { - struct uart_sunsu_port *up = dev_get_drvdata(&op->dev); + struct uart_sunsu_port *up = platform_get_drvdata(op); bool kbdms = false; if (up->su_type == SU_PORT_MS || @@ -1516,8 +1516,6 @@ static int su_remove(struct platform_device *op) if (kbdms) kfree(up); - dev_set_drvdata(&op->dev, NULL); - return 0; } diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c index 813ef8eb8eff..135a15203532 100644 --- a/drivers/tty/serial/sunzilog.c +++ b/drivers/tty/serial/sunzilog.c @@ -1495,7 +1495,7 @@ static int zs_probe(struct platform_device *op) kbm_inst++; } - dev_set_drvdata(&op->dev, &up[0]); + platform_set_drvdata(op, &up[0]); return 0; } @@ -1512,7 +1512,7 @@ static void zs_remove_one(struct uart_sunzilog_port *up) static int zs_remove(struct platform_device *op) { - struct uart_sunzilog_port *up = dev_get_drvdata(&op->dev); + struct uart_sunzilog_port *up = platform_get_drvdata(op); struct zilog_layout __iomem *regs; zs_remove_one(&up[0]); @@ -1521,8 +1521,6 @@ static int zs_remove(struct platform_device *op) regs = sunzilog_chip_regs[up[0].port.line / 2]; of_iounmap(&op->resource[0], regs, sizeof(struct zilog_layout)); - dev_set_drvdata(&op->dev, NULL); - return 0; } diff --git a/drivers/tty/serial/tilegx.c b/drivers/tty/serial/tilegx.c new file mode 100644 index 000000000000..f92d7e6bd876 --- /dev/null +++ b/drivers/tty/serial/tilegx.c @@ -0,0 +1,708 @@ +/* + * Copyright 2013 Tilera Corporation. All Rights Reserved. + * + * 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, version 2. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for + * more details. + * + * TILEGx UART driver. + */ + +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/serial_core.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> + +#include <gxio/common.h> +#include <gxio/iorpc_globals.h> +#include <gxio/iorpc_uart.h> +#include <gxio/kiorpc.h> + +#include <hv/drv_uart_intf.h> + +/* + * Use device name ttyS, major 4, minor 64-65. + * This is the usual serial port name, 8250 conventional range. + */ +#define TILEGX_UART_MAJOR TTY_MAJOR +#define TILEGX_UART_MINOR 64 +#define TILEGX_UART_NAME "ttyS" +#define DRIVER_NAME_STRING "TILEGx_Serial" +#define TILEGX_UART_REF_CLK 125000000; /* REF_CLK is always 125 MHz. */ + +struct tile_uart_port { + /* UART port. */ + struct uart_port uart; + + /* GXIO device context. */ + gxio_uart_context_t context; + + /* UART access mutex. */ + struct mutex mutex; + + /* CPU receiving interrupts. */ + int irq_cpu; +}; + +static struct tile_uart_port tile_uart_ports[TILEGX_UART_NR]; +static struct uart_driver tilegx_uart_driver; + + +/* + * Read UART rx fifo, and insert the chars into tty buffer. + */ +static void receive_chars(struct tile_uart_port *tile_uart, + struct tty_struct *tty) +{ + int i; + char c; + UART_FIFO_COUNT_t count; + gxio_uart_context_t *context = &tile_uart->context; + struct tty_port *port = tty->port; + + count.word = gxio_uart_read(context, UART_FIFO_COUNT); + for (i = 0; i < count.rfifo_count; i++) { + c = (char)gxio_uart_read(context, UART_RECEIVE_DATA); + tty_insert_flip_char(port, c, TTY_NORMAL); + } +} + + +/* + * Drain the Rx FIFO, called by interrupt handler. + */ +static void handle_receive(struct tile_uart_port *tile_uart) +{ + struct tty_port *port = &tile_uart->uart.state->port; + struct tty_struct *tty = tty_port_tty_get(port); + gxio_uart_context_t *context = &tile_uart->context; + + if (!tty) + return; + + /* First read UART rx fifo. */ + receive_chars(tile_uart, tty); + + /* Reset RFIFO_WE interrupt. */ + gxio_uart_write(context, UART_INTERRUPT_STATUS, + UART_INTERRUPT_MASK__RFIFO_WE_MASK); + + /* Final read, if any chars comes between the first read and + * the interrupt reset. + */ + receive_chars(tile_uart, tty); + + spin_unlock(&tile_uart->uart.lock); + tty_flip_buffer_push(port); + spin_lock(&tile_uart->uart.lock); + tty_kref_put(tty); +} + + +/* + * Push one char to UART Write FIFO. + * Return 0 on success, -1 if write filo is full. + */ +static int tilegx_putchar(gxio_uart_context_t *context, char c) +{ + UART_FLAG_t flag; + flag.word = gxio_uart_read(context, UART_FLAG); + if (flag.wfifo_full) + return -1; + + gxio_uart_write(context, UART_TRANSMIT_DATA, (unsigned long)c); + return 0; +} + + +/* + * Send chars to UART Write FIFO; called by interrupt handler. + */ +static void handle_transmit(struct tile_uart_port *tile_uart) +{ + unsigned char ch; + struct uart_port *port; + struct circ_buf *xmit; + gxio_uart_context_t *context = &tile_uart->context; + + /* First reset WFIFO_RE interrupt. */ + gxio_uart_write(context, UART_INTERRUPT_STATUS, + UART_INTERRUPT_MASK__WFIFO_RE_MASK); + + port = &tile_uart->uart; + xmit = &port->state->xmit; + if (port->x_char) { + if (tilegx_putchar(context, port->x_char)) + return; + port->x_char = 0; + port->icount.tx++; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + return; + + while (!uart_circ_empty(xmit)) { + ch = xmit->buf[xmit->tail]; + if (tilegx_putchar(context, ch)) + break; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + + /* Reset WFIFO_RE interrupt. */ + gxio_uart_write(context, UART_INTERRUPT_STATUS, + UART_INTERRUPT_MASK__WFIFO_RE_MASK); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + + +/* + * UART Interrupt handler. + */ +static irqreturn_t tilegx_interrupt(int irq, void *dev_id) +{ + unsigned long flags; + UART_INTERRUPT_STATUS_t intr_stat; + struct tile_uart_port *tile_uart; + gxio_uart_context_t *context; + struct uart_port *port = dev_id; + irqreturn_t ret = IRQ_NONE; + + spin_lock_irqsave(&port->lock, flags); + + tile_uart = container_of(port, struct tile_uart_port, uart); + context = &tile_uart->context; + intr_stat.word = gxio_uart_read(context, UART_INTERRUPT_STATUS); + + if (intr_stat.rfifo_we) { + handle_receive(tile_uart); + ret = IRQ_HANDLED; + } + if (intr_stat.wfifo_re) { + handle_transmit(tile_uart); + ret = IRQ_HANDLED; + } + + spin_unlock_irqrestore(&port->lock, flags); + return ret; +} + + +/* + * Return TIOCSER_TEMT when transmitter FIFO is empty. + */ +static u_int tilegx_tx_empty(struct uart_port *port) +{ + int ret; + UART_FLAG_t flag; + struct tile_uart_port *tile_uart; + gxio_uart_context_t *context; + + tile_uart = container_of(port, struct tile_uart_port, uart); + if (!mutex_trylock(&tile_uart->mutex)) + return 0; + context = &tile_uart->context; + + flag.word = gxio_uart_read(context, UART_FLAG); + ret = (flag.wfifo_empty) ? TIOCSER_TEMT : 0; + mutex_unlock(&tile_uart->mutex); + + return ret; +} + + +/* + * Set state of the modem control output lines. + */ +static void tilegx_set_mctrl(struct uart_port *port, u_int mctrl) +{ + /* N/A */ +} + + +/* + * Get state of the modem control input lines. + */ +static u_int tilegx_get_mctrl(struct uart_port *port) +{ + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; +} + + +/* + * Stop transmitting. + */ +static void tilegx_stop_tx(struct uart_port *port) +{ + /* N/A */ +} + + +/* + * Start transmitting. + */ +static void tilegx_start_tx(struct uart_port *port) +{ + unsigned char ch; + struct circ_buf *xmit; + struct tile_uart_port *tile_uart; + gxio_uart_context_t *context; + + tile_uart = container_of(port, struct tile_uart_port, uart); + if (!mutex_trylock(&tile_uart->mutex)) + return; + context = &tile_uart->context; + xmit = &port->state->xmit; + if (port->x_char) { + if (tilegx_putchar(context, port->x_char)) + return; + port->x_char = 0; + port->icount.tx++; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + mutex_unlock(&tile_uart->mutex); + return; + } + + while (!uart_circ_empty(xmit)) { + ch = xmit->buf[xmit->tail]; + if (tilegx_putchar(context, ch)) + break; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + mutex_unlock(&tile_uart->mutex); +} + + +/* + * Stop receiving - port is in process of being closed. + */ +static void tilegx_stop_rx(struct uart_port *port) +{ + int err; + struct tile_uart_port *tile_uart; + gxio_uart_context_t *context; + int cpu; + + tile_uart = container_of(port, struct tile_uart_port, uart); + if (!mutex_trylock(&tile_uart->mutex)) + return; + + context = &tile_uart->context; + cpu = tile_uart->irq_cpu; + err = gxio_uart_cfg_interrupt(context, cpu_x(cpu), cpu_y(cpu), + KERNEL_PL, -1); + mutex_unlock(&tile_uart->mutex); +} + + +/* + * Enable modem status interrupts. + */ +static void tilegx_enable_ms(struct uart_port *port) +{ + /* N/A */ +} + +/* + * Control the transmission of a break signal. + */ +static void tilegx_break_ctl(struct uart_port *port, int break_state) +{ + /* N/A */ +} + + +/* + * Perform initialization and enable port for reception. + */ +static int tilegx_startup(struct uart_port *port) +{ + struct tile_uart_port *tile_uart; + gxio_uart_context_t *context; + int ret = 0; + int cpu = raw_smp_processor_id(); /* pick an arbitrary cpu */ + + tile_uart = container_of(port, struct tile_uart_port, uart); + if (mutex_lock_interruptible(&tile_uart->mutex)) + return -EBUSY; + context = &tile_uart->context; + + /* Now open the hypervisor device if we haven't already. */ + if (context->fd < 0) { + UART_INTERRUPT_MASK_t intr_mask; + + /* Initialize UART device. */ + ret = gxio_uart_init(context, port->line); + if (ret) { + ret = -ENXIO; + goto err; + } + + /* Create our IRQs. */ + port->irq = create_irq(); + if (port->irq < 0) + goto err_uart_dest; + tile_irq_activate(port->irq, TILE_IRQ_PERCPU); + + /* Register our IRQs. */ + ret = request_irq(port->irq, tilegx_interrupt, 0, + tilegx_uart_driver.driver_name, port); + if (ret) + goto err_dest_irq; + + /* Request that the hardware start sending us interrupts. */ + tile_uart->irq_cpu = cpu; + ret = gxio_uart_cfg_interrupt(context, cpu_x(cpu), cpu_y(cpu), + KERNEL_PL, port->irq); + if (ret) + goto err_free_irq; + + /* Enable UART Tx/Rx Interrupt. */ + intr_mask.word = gxio_uart_read(context, UART_INTERRUPT_MASK); + intr_mask.wfifo_re = 0; + intr_mask.rfifo_we = 0; + gxio_uart_write(context, UART_INTERRUPT_MASK, intr_mask.word); + + /* Reset the Tx/Rx interrupt in case it's set. */ + gxio_uart_write(context, UART_INTERRUPT_STATUS, + UART_INTERRUPT_MASK__WFIFO_RE_MASK | + UART_INTERRUPT_MASK__RFIFO_WE_MASK); + } + + mutex_unlock(&tile_uart->mutex); + return ret; + +err_free_irq: + free_irq(port->irq, port); +err_dest_irq: + destroy_irq(port->irq); +err_uart_dest: + gxio_uart_destroy(context); + ret = -ENXIO; +err: + mutex_unlock(&tile_uart->mutex); + return ret; +} + + +/* + * Release kernel resources if it is the last close, disable the port, + * free IRQ and close the port. + */ +static void tilegx_shutdown(struct uart_port *port) +{ + int err; + UART_INTERRUPT_MASK_t intr_mask; + struct tile_uart_port *tile_uart; + gxio_uart_context_t *context; + int cpu; + + tile_uart = container_of(port, struct tile_uart_port, uart); + if (mutex_lock_interruptible(&tile_uart->mutex)) + return; + context = &tile_uart->context; + + /* Disable UART Tx/Rx Interrupt. */ + intr_mask.word = gxio_uart_read(context, UART_INTERRUPT_MASK); + intr_mask.wfifo_re = 1; + intr_mask.rfifo_we = 1; + gxio_uart_write(context, UART_INTERRUPT_MASK, intr_mask.word); + + /* Request that the hardware stop sending us interrupts. */ + cpu = tile_uart->irq_cpu; + err = gxio_uart_cfg_interrupt(context, cpu_x(cpu), cpu_y(cpu), + KERNEL_PL, -1); + + if (port->irq > 0) { + free_irq(port->irq, port); + destroy_irq(port->irq); + port->irq = 0; + } + + gxio_uart_destroy(context); + + mutex_unlock(&tile_uart->mutex); +} + + +/* + * Flush the buffer. + */ +static void tilegx_flush_buffer(struct uart_port *port) +{ + /* N/A */ +} + + +/* + * Change the port parameters. + */ +static void tilegx_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + int err; + UART_DIVISOR_t divisor; + UART_TYPE_t type; + unsigned int baud; + struct tile_uart_port *tile_uart; + gxio_uart_context_t *context; + + tile_uart = container_of(port, struct tile_uart_port, uart); + if (!mutex_trylock(&tile_uart->mutex)) + return; + context = &tile_uart->context; + + /* Open the hypervisor device if we haven't already. */ + if (context->fd < 0) { + err = gxio_uart_init(context, port->line); + if (err) { + mutex_unlock(&tile_uart->mutex); + return; + } + } + + divisor.word = gxio_uart_read(context, UART_DIVISOR); + type.word = gxio_uart_read(context, UART_TYPE); + + /* Divisor. */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); + divisor.divisor = uart_get_divisor(port, baud); + + /* Byte size. */ + if ((termios->c_cflag & CSIZE) == CS7) + type.dbits = UART_TYPE__DBITS_VAL_SEVEN_DBITS; + else + type.dbits = UART_TYPE__DBITS_VAL_EIGHT_DBITS; + + /* Parity. */ + if (termios->c_cflag & PARENB) { + /* Mark or Space parity. */ + if (termios->c_cflag & CMSPAR) + if (termios->c_cflag & PARODD) + type.ptype = UART_TYPE__PTYPE_VAL_MARK; + else + type.ptype = UART_TYPE__PTYPE_VAL_SPACE; + else if (termios->c_cflag & PARODD) + type.ptype = UART_TYPE__PTYPE_VAL_ODD; + else + type.ptype = UART_TYPE__PTYPE_VAL_EVEN; + } else + type.ptype = UART_TYPE__PTYPE_VAL_NONE; + + /* Stop bits. */ + if (termios->c_cflag & CSTOPB) + type.sbits = UART_TYPE__SBITS_VAL_TWO_SBITS; + else + type.sbits = UART_TYPE__SBITS_VAL_ONE_SBITS; + + /* Set the uart paramters. */ + gxio_uart_write(context, UART_DIVISOR, divisor.word); + gxio_uart_write(context, UART_TYPE, type.word); + + mutex_unlock(&tile_uart->mutex); +} + + +/* + * Return string describing the specified port. + */ +static const char *tilegx_type(struct uart_port *port) +{ + return port->type == PORT_TILEGX ? DRIVER_NAME_STRING : NULL; +} + + +/* + * Release the resources being used by 'port'. + */ +static void tilegx_release_port(struct uart_port *port) +{ + /* Nothing to release. */ +} + + +/* + * Request the resources being used by 'port'. + */ +static int tilegx_request_port(struct uart_port *port) +{ + /* Always present. */ + return 0; +} + + +/* + * Configure/autoconfigure the port. + */ +static void tilegx_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_TILEGX; +} + + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + */ +static int tilegx_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_TILEGX)) + return -EINVAL; + + return 0; +} + +#ifdef CONFIG_CONSOLE_POLL + +/* + * Console polling routines for writing and reading from the uart while + * in an interrupt or debug context. + */ + +static int tilegx_poll_get_char(struct uart_port *port) +{ + UART_FIFO_COUNT_t count; + gxio_uart_context_t *context; + struct tile_uart_port *tile_uart; + + tile_uart = container_of(port, struct tile_uart_port, uart); + context = &tile_uart->context; + count.word = gxio_uart_read(context, UART_FIFO_COUNT); + if (count.rfifo_count == 0) + return NO_POLL_CHAR; + return (char)gxio_uart_read(context, UART_RECEIVE_DATA); +} + +static void tilegx_poll_put_char(struct uart_port *port, unsigned char c) +{ + gxio_uart_context_t *context; + struct tile_uart_port *tile_uart; + + tile_uart = container_of(port, struct tile_uart_port, uart); + context = &tile_uart->context; + gxio_uart_write(context, UART_TRANSMIT_DATA, (unsigned long)c); +} + +#endif /* CONFIG_CONSOLE_POLL */ + + +static const struct uart_ops tilegx_ops = { + .tx_empty = tilegx_tx_empty, + .set_mctrl = tilegx_set_mctrl, + .get_mctrl = tilegx_get_mctrl, + .stop_tx = tilegx_stop_tx, + .start_tx = tilegx_start_tx, + .stop_rx = tilegx_stop_rx, + .enable_ms = tilegx_enable_ms, + .break_ctl = tilegx_break_ctl, + .startup = tilegx_startup, + .shutdown = tilegx_shutdown, + .flush_buffer = tilegx_flush_buffer, + .set_termios = tilegx_set_termios, + .type = tilegx_type, + .release_port = tilegx_release_port, + .request_port = tilegx_request_port, + .config_port = tilegx_config_port, + .verify_port = tilegx_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = tilegx_poll_get_char, + .poll_put_char = tilegx_poll_put_char, +#endif +}; + + +static void tilegx_init_ports(void) +{ + int i; + struct uart_port *port; + + for (i = 0; i < TILEGX_UART_NR; i++) { + port = &tile_uart_ports[i].uart; + port->ops = &tilegx_ops; + port->line = i; + port->type = PORT_TILEGX; + port->uartclk = TILEGX_UART_REF_CLK; + port->flags = UPF_BOOT_AUTOCONF; + + tile_uart_ports[i].context.fd = -1; + mutex_init(&tile_uart_ports[i].mutex); + } +} + + +static struct uart_driver tilegx_uart_driver = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME_STRING, + .dev_name = TILEGX_UART_NAME, + .major = TILEGX_UART_MAJOR, + .minor = TILEGX_UART_MINOR, + .nr = TILEGX_UART_NR, +}; + + +static int __init tilegx_init(void) +{ + int i; + int ret; + struct tty_driver *tty_drv; + + ret = uart_register_driver(&tilegx_uart_driver); + if (ret) + return ret; + tty_drv = tilegx_uart_driver.tty_driver; + tty_drv->init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; + tty_drv->init_termios.c_ispeed = 115200; + tty_drv->init_termios.c_ospeed = 115200; + + tilegx_init_ports(); + + for (i = 0; i < TILEGX_UART_NR; i++) { + struct uart_port *port = &tile_uart_ports[i].uart; + ret = uart_add_one_port(&tilegx_uart_driver, port); + } + + return 0; +} + + +static void __exit tilegx_exit(void) +{ + int i; + struct uart_port *port; + + for (i = 0; i < TILEGX_UART_NR; i++) { + port = &tile_uart_ports[i].uart; + uart_remove_one_port(&tilegx_uart_driver, port); + } + + uart_unregister_driver(&tilegx_uart_driver); +} + + +module_init(tilegx_init); +module_exit(tilegx_exit); + +MODULE_AUTHOR("Tilera Corporation"); +MODULE_DESCRIPTION("TILEGx serial port driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c index 6818410a2bea..f87097acd8ab 100644 --- a/drivers/tty/serial/timbuart.c +++ b/drivers/tty/serial/timbuart.c @@ -162,7 +162,7 @@ static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier) dev_dbg(port->dev, "%s - leaving\n", __func__); } -void timbuart_handle_rx_port(struct uart_port *port, u32 isr, u32 *ier) +static void timbuart_handle_rx_port(struct uart_port *port, u32 isr, u32 *ier) { if (isr & RXFLAGS) { /* Some RX status is set */ @@ -184,7 +184,7 @@ void timbuart_handle_rx_port(struct uart_port *port, u32 isr, u32 *ier) dev_dbg(port->dev, "%s - leaving\n", __func__); } -void timbuart_tasklet(unsigned long arg) +static void timbuart_tasklet(unsigned long arg) { struct timbuart_port *uart = (struct timbuart_port *)arg; u32 isr, ier = 0; diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index 7355303dad99..88317482b81f 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -1451,7 +1451,7 @@ static int ucc_uart_probe(struct platform_device *ofdev) goto out_np; } - dev_set_drvdata(&ofdev->dev, qe_port); + platform_set_drvdata(ofdev, qe_port); dev_info(&ofdev->dev, "UCC%u assigned to /dev/ttyQE%u\n", qe_port->ucc_num + 1, qe_port->port.line); @@ -1471,13 +1471,12 @@ out_free: static int ucc_uart_remove(struct platform_device *ofdev) { - struct uart_qe_port *qe_port = dev_get_drvdata(&ofdev->dev); + struct uart_qe_port *qe_port = platform_get_drvdata(ofdev); dev_info(&ofdev->dev, "removing /dev/ttyQE%u\n", qe_port->port.line); uart_remove_one_port(&ucc_uart_driver, &qe_port->port); - dev_set_drvdata(&ofdev->dev, NULL); kfree(qe_port); return 0; @@ -1518,9 +1517,11 @@ static int __init ucc_uart_init(void) } ret = platform_driver_register(&ucc_uart_of_driver); - if (ret) + if (ret) { printk(KERN_ERR "ucc-uart: could not register platform driver\n"); + uart_unregister_driver(&ucc_uart_driver); + } return ret; } diff --git a/drivers/tty/serial/vr41xx_siu.c b/drivers/tty/serial/vr41xx_siu.c index f655997f44af..a63c14bc9a24 100644 --- a/drivers/tty/serial/vr41xx_siu.c +++ b/drivers/tty/serial/vr41xx_siu.c @@ -705,7 +705,7 @@ static int siu_init_ports(struct platform_device *pdev) { struct uart_port *port; struct resource *res; - int *type = pdev->dev.platform_data; + int *type = dev_get_platdata(&pdev->dev); int i; if (!type) diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 1a8bc2275ea4..93b697a0de65 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -170,7 +170,9 @@ static void handle_rx(struct uart_port *port) tty_insert_flip_char(tport, c, flag); } + spin_unlock(&port->lock); tty_flip_buffer_push(tport); + spin_lock(&port->lock); } static void handle_tx(struct uart_port *port) @@ -630,7 +632,6 @@ static int vt8500_serial_remove(struct platform_device *pdev) { struct vt8500_port *vt8500_port = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); clk_disable_unprepare(vt8500_port->clk); uart_remove_one_port(&vt8500_uart_driver, &vt8500_port->uart); @@ -648,7 +649,7 @@ static struct platform_driver vt8500_platform_driver = { .driver = { .name = "vt8500_serial", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(wmt_dt_ids), + .of_match_table = wmt_dt_ids, }, }; diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 4e5c77834c50..7e4150aa69c6 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -14,6 +14,7 @@ #include <linux/platform_device.h> #include <linux/serial.h> #include <linux/serial_core.h> +#include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/console.h> @@ -139,6 +140,16 @@ #define XUARTPS_SR_RXTRIG 0x00000001 /* Rx Trigger */ /** + * struct xuartps - device data + * @refclk Reference clock + * @aperclk APB clock + */ +struct xuartps { + struct clk *refclk; + struct clk *aperclk; +}; + +/** * xuartps_isr - Interrupt handler * @irq: Irq number * @dev_id: Id of the port @@ -936,34 +947,55 @@ static int xuartps_probe(struct platform_device *pdev) int rc; struct uart_port *port; struct resource *res, *res2; - struct clk *clk; + struct xuartps *xuartps_data; - clk = of_clk_get(pdev->dev.of_node, 0); - if (IS_ERR(clk)) { - dev_err(&pdev->dev, "no clock specified\n"); - return PTR_ERR(clk); + xuartps_data = kzalloc(sizeof(*xuartps_data), GFP_KERNEL); + if (!xuartps_data) + return -ENOMEM; + + xuartps_data->aperclk = clk_get(&pdev->dev, "aper_clk"); + if (IS_ERR(xuartps_data->aperclk)) { + dev_err(&pdev->dev, "aper_clk clock not found.\n"); + rc = PTR_ERR(xuartps_data->aperclk); + goto err_out_free; + } + xuartps_data->refclk = clk_get(&pdev->dev, "ref_clk"); + if (IS_ERR(xuartps_data->refclk)) { + dev_err(&pdev->dev, "ref_clk clock not found.\n"); + rc = PTR_ERR(xuartps_data->refclk); + goto err_out_clk_put_aper; } - rc = clk_prepare_enable(clk); + rc = clk_prepare_enable(xuartps_data->aperclk); + if (rc) { + dev_err(&pdev->dev, "Unable to enable APER clock.\n"); + goto err_out_clk_put; + } + rc = clk_prepare_enable(xuartps_data->refclk); if (rc) { - dev_err(&pdev->dev, "could not enable clock\n"); - return -EBUSY; + dev_err(&pdev->dev, "Unable to enable device clock.\n"); + goto err_out_clk_dis_aper; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; + if (!res) { + rc = -ENODEV; + goto err_out_clk_disable; + } res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res2) - return -ENODEV; + if (!res2) { + rc = -ENODEV; + goto err_out_clk_disable; + } /* Initialize the port structure */ port = xuartps_get_port(); if (!port) { dev_err(&pdev->dev, "Cannot get uart_port structure\n"); - return -ENODEV; + rc = -ENODEV; + goto err_out_clk_disable; } else { /* Register the port. * This function also registers this device with the tty layer @@ -972,18 +1004,30 @@ static int xuartps_probe(struct platform_device *pdev) port->mapbase = res->start; port->irq = res2->start; port->dev = &pdev->dev; - port->uartclk = clk_get_rate(clk); - port->private_data = clk; - dev_set_drvdata(&pdev->dev, port); + port->uartclk = clk_get_rate(xuartps_data->refclk); + port->private_data = xuartps_data; + platform_set_drvdata(pdev, port); rc = uart_add_one_port(&xuartps_uart_driver, port); if (rc) { dev_err(&pdev->dev, "uart_add_one_port() failed; err=%i\n", rc); - dev_set_drvdata(&pdev->dev, NULL); - return rc; + goto err_out_clk_disable; } return 0; } + +err_out_clk_disable: + clk_disable_unprepare(xuartps_data->refclk); +err_out_clk_dis_aper: + clk_disable_unprepare(xuartps_data->aperclk); +err_out_clk_put: + clk_put(xuartps_data->refclk); +err_out_clk_put_aper: + clk_put(xuartps_data->aperclk); +err_out_free: + kfree(xuartps_data); + + return rc; } /** @@ -994,46 +1038,21 @@ static int xuartps_probe(struct platform_device *pdev) **/ static int xuartps_remove(struct platform_device *pdev) { - struct uart_port *port = dev_get_drvdata(&pdev->dev); - struct clk *clk = port->private_data; + struct uart_port *port = platform_get_drvdata(pdev); + struct xuartps *xuartps_data = port->private_data; int rc; /* Remove the xuartps port from the serial core */ rc = uart_remove_one_port(&xuartps_uart_driver, port); - dev_set_drvdata(&pdev->dev, NULL); port->mapbase = 0; - clk_disable_unprepare(clk); + clk_disable_unprepare(xuartps_data->refclk); + clk_disable_unprepare(xuartps_data->aperclk); + clk_put(xuartps_data->refclk); + clk_put(xuartps_data->aperclk); + kfree(xuartps_data); return rc; } -/** - * xuartps_suspend - suspend event - * @pdev: Pointer to the platform device structure - * @state: State of the device - * - * Returns 0 - **/ -static int xuartps_suspend(struct platform_device *pdev, pm_message_t state) -{ - /* Call the API provided in serial_core.c file which handles - * the suspend. - */ - uart_suspend_port(&xuartps_uart_driver, &xuartps_port[pdev->id]); - return 0; -} - -/** - * xuartps_resume - Resume after a previous suspend - * @pdev: Pointer to the platform device structure - * - * Returns 0 - **/ -static int xuartps_resume(struct platform_device *pdev) -{ - uart_resume_port(&xuartps_uart_driver, &xuartps_port[pdev->id]); - return 0; -} - /* Match table for of_platform binding */ static struct of_device_id xuartps_of_match[] = { { .compatible = "xlnx,xuartps", }, @@ -1044,8 +1063,6 @@ MODULE_DEVICE_TABLE(of, xuartps_of_match); static struct platform_driver xuartps_platform_driver = { .probe = xuartps_probe, /* Probe method */ .remove = xuartps_remove, /* Detach method */ - .suspend = xuartps_suspend, /* Suspend */ - .resume = xuartps_resume, /* Resume after a suspend */ .driver = { .owner = THIS_MODULE, .name = XUARTPS_NAME, /* Driver name */ diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 8eaf1ab8addb..e1ce141bad5e 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -577,22 +577,22 @@ struct mgsl_struct { #define SICR_RXC_ACTIVE BIT15 #define SICR_RXC_INACTIVE BIT14 -#define SICR_RXC (BIT15+BIT14) +#define SICR_RXC (BIT15|BIT14) #define SICR_TXC_ACTIVE BIT13 #define SICR_TXC_INACTIVE BIT12 -#define SICR_TXC (BIT13+BIT12) +#define SICR_TXC (BIT13|BIT12) #define SICR_RI_ACTIVE BIT11 #define SICR_RI_INACTIVE BIT10 -#define SICR_RI (BIT11+BIT10) +#define SICR_RI (BIT11|BIT10) #define SICR_DSR_ACTIVE BIT9 #define SICR_DSR_INACTIVE BIT8 -#define SICR_DSR (BIT9+BIT8) +#define SICR_DSR (BIT9|BIT8) #define SICR_DCD_ACTIVE BIT7 #define SICR_DCD_INACTIVE BIT6 -#define SICR_DCD (BIT7+BIT6) +#define SICR_DCD (BIT7|BIT6) #define SICR_CTS_ACTIVE BIT5 #define SICR_CTS_INACTIVE BIT4 -#define SICR_CTS (BIT5+BIT4) +#define SICR_CTS (BIT5|BIT4) #define SICR_RCC_UNDERFLOW BIT3 #define SICR_DPLL_NO_SYNC BIT2 #define SICR_BRG1_ZERO BIT1 @@ -1161,7 +1161,7 @@ static void mgsl_isr_receive_status( struct mgsl_struct *info ) { u16 status = usc_InReg( info, RCSR ); - if ( debug_level >= DEBUG_LEVEL_ISR ) + if ( debug_level >= DEBUG_LEVEL_ISR ) printk("%s(%d):mgsl_isr_receive_status status=%04X\n", __FILE__,__LINE__,status); @@ -1181,7 +1181,7 @@ static void mgsl_isr_receive_status( struct mgsl_struct *info ) (usc_InReg(info, RICR) & ~RXSTATUS_ABORT_RECEIVED)); } - if (status & (RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)) { + if (status & (RXSTATUS_EXITED_HUNT | RXSTATUS_IDLE_RECEIVED)) { if (status & RXSTATUS_EXITED_HUNT) info->icount.exithunt++; if (status & RXSTATUS_IDLE_RECEIVED) @@ -1463,21 +1463,21 @@ static void mgsl_isr_receive_data( struct mgsl_struct *info ) /* get the status of the received byte */ status = usc_InReg(info, RCSR); - if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR + - RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) ) + if ( status & (RXSTATUS_FRAMING_ERROR | RXSTATUS_PARITY_ERROR | + RXSTATUS_OVERRUN | RXSTATUS_BREAK_RECEIVED) ) usc_UnlatchRxstatusBits(info,RXSTATUS_ALL); icount->rx++; flag = 0; - if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR + - RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) ) { - printk("rxerr=%04X\n",status); + if ( status & (RXSTATUS_FRAMING_ERROR | RXSTATUS_PARITY_ERROR | + RXSTATUS_OVERRUN | RXSTATUS_BREAK_RECEIVED) ) { + printk("rxerr=%04X\n",status); /* update error statistics */ if ( status & RXSTATUS_BREAK_RECEIVED ) { - status &= ~(RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR); + status &= ~(RXSTATUS_FRAMING_ERROR | RXSTATUS_PARITY_ERROR); icount->brk++; - } else if (status & RXSTATUS_PARITY_ERROR) + } else if (status & RXSTATUS_PARITY_ERROR) icount->parity++; else if (status & RXSTATUS_FRAMING_ERROR) icount->frame++; @@ -1488,7 +1488,7 @@ static void mgsl_isr_receive_data( struct mgsl_struct *info ) icount->overrun++; } - /* discard char if tty control flags say so */ + /* discard char if tty control flags say so */ if (status & info->ignore_status_mask) continue; @@ -1545,8 +1545,8 @@ static void mgsl_isr_misc( struct mgsl_struct *info ) usc_EnableReceiver(info,DISABLE_UNCONDITIONAL); usc_DmaCmd(info, DmaCmd_ResetRxChannel); usc_UnlatchRxstatusBits(info, RXSTATUS_ALL); - usc_ClearIrqPendingBits(info, RECEIVE_DATA + RECEIVE_STATUS); - usc_DisableInterrupts(info, RECEIVE_DATA + RECEIVE_STATUS); + usc_ClearIrqPendingBits(info, RECEIVE_DATA | RECEIVE_STATUS); + usc_DisableInterrupts(info, RECEIVE_DATA | RECEIVE_STATUS); /* schedule BH handler to restart receiver */ info->pending_bh |= BH_RECEIVE; @@ -1595,7 +1595,7 @@ static void mgsl_isr_receive_dma( struct mgsl_struct *info ) u16 status; /* clear interrupt pending and IUS bit for Rx DMA IRQ */ - usc_OutDmaReg( info, CDIR, BIT9+BIT1 ); + usc_OutDmaReg( info, CDIR, BIT9 | BIT1 ); /* Read the receive DMA status to identify interrupt type. */ /* This also clears the status bits. */ @@ -1639,7 +1639,7 @@ static void mgsl_isr_transmit_dma( struct mgsl_struct *info ) u16 status; /* clear interrupt pending and IUS bit for Tx DMA IRQ */ - usc_OutDmaReg(info, CDIR, BIT8+BIT0 ); + usc_OutDmaReg(info, CDIR, BIT8 | BIT0 ); /* Read the transmit DMA status to identify interrupt type. */ /* This also clears the status bits. */ @@ -1832,8 +1832,8 @@ static void shutdown(struct mgsl_struct * info) usc_DisableMasterIrqBit(info); usc_stop_receiver(info); usc_stop_transmitter(info); - usc_DisableInterrupts(info,RECEIVE_DATA + RECEIVE_STATUS + - TRANSMIT_DATA + TRANSMIT_STATUS + IO_PIN + MISC ); + usc_DisableInterrupts(info,RECEIVE_DATA | RECEIVE_STATUS | + TRANSMIT_DATA | TRANSMIT_STATUS | IO_PIN | MISC ); usc_DisableDmaInterrupts(info,DICR_MASTER + DICR_TRANSMIT + DICR_RECEIVE); /* Disable DMAEN (Port 7, Bit 14) */ @@ -1886,7 +1886,7 @@ static void mgsl_program_hw(struct mgsl_struct *info) info->ri_chkcount = 0; info->dsr_chkcount = 0; - usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI); + usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI); usc_EnableInterrupts(info, IO_PIN); usc_get_serial_signals(info); @@ -2773,7 +2773,7 @@ static int mgsl_wait_event(struct mgsl_struct * info, int __user * mask_ptr) if (!waitqueue_active(&info->event_wait_q)) { /* disable enable exit hunt mode/idle rcvd IRQs */ usc_OutReg(info, RICR, usc_InReg(info,RICR) & - ~(RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)); + ~(RXSTATUS_EXITED_HUNT | RXSTATUS_IDLE_RECEIVED)); } spin_unlock_irqrestore(&info->irq_spinlock,flags); } @@ -3092,7 +3092,7 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp) printk("%s(%d):mgsl_close(%s) entry, count=%d\n", __FILE__,__LINE__, info->device_name, info->port.count); - if (tty_port_close_start(&info->port, tty, filp) == 0) + if (tty_port_close_start(&info->port, tty, filp) == 0) goto cleanup; mutex_lock(&info->port.mutex); @@ -4297,7 +4297,7 @@ static struct mgsl_struct* mgsl_allocate_device(void) spin_lock_init(&info->irq_spinlock); spin_lock_init(&info->netlock); memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); - info->idle_mode = HDLC_TXIDLE_FLAGS; + info->idle_mode = HDLC_TXIDLE_FLAGS; info->num_tx_dma_buffers = 1; info->num_tx_holding_buffers = 0; } @@ -4722,7 +4722,7 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info ) else if ( info->params.flags & HDLC_FLAG_UNDERRUN_FLAG ) RegValue |= BIT15; else if ( info->params.flags & HDLC_FLAG_UNDERRUN_CRC ) - RegValue |= BIT15 + BIT14; + RegValue |= BIT15 | BIT14; } if ( info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE ) @@ -4763,11 +4763,11 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info ) switch ( info->params.encoding ) { case HDLC_ENCODING_NRZB: RegValue |= BIT13; break; case HDLC_ENCODING_NRZI_MARK: RegValue |= BIT14; break; - case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT14 + BIT13; break; + case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT14 | BIT13; break; case HDLC_ENCODING_BIPHASE_MARK: RegValue |= BIT15; break; - case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT15 + BIT13; break; - case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14; break; - case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break; + case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT15 | BIT13; break; + case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT15 | BIT14; break; + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 | BIT14 | BIT13; break; } if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_16_CCITT ) @@ -4838,15 +4838,15 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info ) switch ( info->params.encoding ) { case HDLC_ENCODING_NRZB: RegValue |= BIT13; break; case HDLC_ENCODING_NRZI_MARK: RegValue |= BIT14; break; - case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT14 + BIT13; break; + case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT14 | BIT13; break; case HDLC_ENCODING_BIPHASE_MARK: RegValue |= BIT15; break; - case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT15 + BIT13; break; - case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14; break; - case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break; + case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT15 | BIT13; break; + case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT15 | BIT14; break; + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 | BIT14 | BIT13; break; } if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_16_CCITT ) - RegValue |= BIT9 + BIT8; + RegValue |= BIT9 | BIT8; else if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_32_CCITT ) RegValue |= ( BIT12 | BIT10 | BIT9 | BIT8); @@ -4957,7 +4957,7 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info ) RegValue = 0x0000; - if ( info->params.flags & (HDLC_FLAG_RXC_DPLL + HDLC_FLAG_TXC_DPLL) ) { + if ( info->params.flags & (HDLC_FLAG_RXC_DPLL | HDLC_FLAG_TXC_DPLL) ) { u32 XtalSpeed; u32 DpllDivisor; u16 Tc; @@ -5019,7 +5019,7 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info ) case HDLC_ENCODING_BIPHASE_MARK: case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT9; break; case HDLC_ENCODING_BIPHASE_LEVEL: - case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT9 + BIT8; break; + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT9 | BIT8; break; } } @@ -5056,8 +5056,8 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info ) /* enable Master Interrupt Enable bit (MIE) */ usc_EnableMasterIrqBit( info ); - usc_ClearIrqPendingBits( info, RECEIVE_STATUS + RECEIVE_DATA + - TRANSMIT_STATUS + TRANSMIT_DATA + MISC); + usc_ClearIrqPendingBits( info, RECEIVE_STATUS | RECEIVE_DATA | + TRANSMIT_STATUS | TRANSMIT_DATA | MISC); /* arm RCC underflow interrupt */ usc_OutReg(info, SICR, (u16)(usc_InReg(info,SICR) | BIT3)); @@ -5175,14 +5175,14 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info ) switch ( info->params.preamble_length ) { case HDLC_PREAMBLE_LENGTH_16BITS: RegValue |= BIT10; break; case HDLC_PREAMBLE_LENGTH_32BITS: RegValue |= BIT11; break; - case HDLC_PREAMBLE_LENGTH_64BITS: RegValue |= BIT11 + BIT10; break; + case HDLC_PREAMBLE_LENGTH_64BITS: RegValue |= BIT11 | BIT10; break; } switch ( info->params.preamble ) { - case HDLC_PREAMBLE_PATTERN_FLAGS: RegValue |= BIT8 + BIT12; break; + case HDLC_PREAMBLE_PATTERN_FLAGS: RegValue |= BIT8 | BIT12; break; case HDLC_PREAMBLE_PATTERN_ONES: RegValue |= BIT8; break; case HDLC_PREAMBLE_PATTERN_10: RegValue |= BIT9; break; - case HDLC_PREAMBLE_PATTERN_01: RegValue |= BIT9 + BIT8; break; + case HDLC_PREAMBLE_PATTERN_01: RegValue |= BIT9 | BIT8; break; } usc_OutReg( info, CCR, RegValue ); @@ -5221,7 +5221,7 @@ static void usc_enable_loopback(struct mgsl_struct *info, int enable) { if (enable) { /* blank external TXD output */ - usc_OutReg(info,IOCR,usc_InReg(info,IOCR) | (BIT7+BIT6)); + usc_OutReg(info,IOCR,usc_InReg(info,IOCR) | (BIT7 | BIT6)); /* Clock mode Control Register (CMCR) * @@ -5260,7 +5260,7 @@ static void usc_enable_loopback(struct mgsl_struct *info, int enable) outw( 0x0300, info->io_base + CCAR ); } else { /* enable external TXD output */ - usc_OutReg(info,IOCR,usc_InReg(info,IOCR) & ~(BIT7+BIT6)); + usc_OutReg(info,IOCR,usc_InReg(info,IOCR) & ~(BIT7 | BIT6)); /* clear Internal Data loopback mode */ info->loopback_bits = 0; @@ -5447,13 +5447,13 @@ static void usc_process_rxoverrun_sync( struct mgsl_struct *info ) usc_OutDmaReg( info, NRARU, (u16)(phys_addr >> 16) ); usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); - usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS ); + usc_ClearIrqPendingBits( info, RECEIVE_DATA | RECEIVE_STATUS ); usc_EnableInterrupts( info, RECEIVE_STATUS ); /* 1. Arm End of Buffer (EOB) Receive DMA Interrupt (BIT2 of RDIAR) */ /* 2. Enable Receive DMA Interrupts (BIT1 of DICR) */ - usc_OutDmaReg( info, RDIAR, BIT3 + BIT2 ); + usc_OutDmaReg( info, RDIAR, BIT3 | BIT2 ); usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT1) ); usc_DmaCmd( info, DmaCmd_InitRxChannel ); if ( info->params.flags & HDLC_FLAG_AUTO_DCD ) @@ -5488,8 +5488,8 @@ static void usc_stop_receiver( struct mgsl_struct *info ) usc_DmaCmd( info, DmaCmd_ResetRxChannel ); usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); - usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS ); - usc_DisableInterrupts( info, RECEIVE_DATA + RECEIVE_STATUS ); + usc_ClearIrqPendingBits( info, RECEIVE_DATA | RECEIVE_STATUS ); + usc_DisableInterrupts( info, RECEIVE_DATA | RECEIVE_STATUS ); usc_EnableReceiver(info,DISABLE_UNCONDITIONAL); @@ -5536,13 +5536,13 @@ static void usc_start_receiver( struct mgsl_struct *info ) usc_OutDmaReg( info, NRARU, (u16)(phys_addr >> 16) ); usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); - usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS ); + usc_ClearIrqPendingBits( info, RECEIVE_DATA | RECEIVE_STATUS ); usc_EnableInterrupts( info, RECEIVE_STATUS ); /* 1. Arm End of Buffer (EOB) Receive DMA Interrupt (BIT2 of RDIAR) */ /* 2. Enable Receive DMA Interrupts (BIT1 of DICR) */ - usc_OutDmaReg( info, RDIAR, BIT3 + BIT2 ); + usc_OutDmaReg( info, RDIAR, BIT3 | BIT2 ); usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT1) ); usc_DmaCmd( info, DmaCmd_InitRxChannel ); if ( info->params.flags & HDLC_FLAG_AUTO_DCD ) @@ -5551,7 +5551,7 @@ static void usc_start_receiver( struct mgsl_struct *info ) usc_EnableReceiver(info,ENABLE_UNCONDITIONAL); } else { usc_UnlatchRxstatusBits(info, RXSTATUS_ALL); - usc_ClearIrqPendingBits(info, RECEIVE_DATA + RECEIVE_STATUS); + usc_ClearIrqPendingBits(info, RECEIVE_DATA | RECEIVE_STATUS); usc_EnableInterrupts(info, RECEIVE_DATA); usc_RTCmd( info, RTCmd_PurgeRxFifo ); @@ -5925,7 +5925,7 @@ static void usc_set_async_mode( struct mgsl_struct *info ) RegValue = 0; if ( info->params.data_bits != 8 ) - RegValue |= BIT4+BIT3+BIT2; + RegValue |= BIT4 | BIT3 | BIT2; if ( info->params.parity != ASYNC_PARITY_NONE ) { RegValue |= BIT5; @@ -5982,7 +5982,7 @@ static void usc_set_async_mode( struct mgsl_struct *info ) RegValue = 0; if ( info->params.data_bits != 8 ) - RegValue |= BIT4+BIT3+BIT2; + RegValue |= BIT4 | BIT3 | BIT2; if ( info->params.parity != ASYNC_PARITY_NONE ) { RegValue |= BIT5; @@ -6129,7 +6129,7 @@ static void usc_loopback_frame( struct mgsl_struct *info ) /* WAIT FOR RECEIVE COMPLETE */ for (i=0 ; i<1000 ; i++) - if (usc_InReg( info, RCSR ) & (BIT8 + BIT4 + BIT3 + BIT1)) + if (usc_InReg( info, RCSR ) & (BIT8 | BIT4 | BIT3 | BIT1)) break; /* clear Internal Data loopback mode */ @@ -6579,8 +6579,8 @@ static bool mgsl_get_rx_frame(struct mgsl_struct *info) status = info->rx_buffer_list[EndIndex].status; - if ( status & (RXSTATUS_SHORT_FRAME + RXSTATUS_OVERRUN + - RXSTATUS_CRC_ERROR + RXSTATUS_ABORT) ) { + if ( status & (RXSTATUS_SHORT_FRAME | RXSTATUS_OVERRUN | + RXSTATUS_CRC_ERROR | RXSTATUS_ABORT) ) { if ( status & RXSTATUS_SHORT_FRAME ) info->icount.rxshort++; else if ( status & RXSTATUS_ABORT ) @@ -6762,8 +6762,8 @@ static bool mgsl_get_raw_rx_frame(struct mgsl_struct *info) status = info->rx_buffer_list[CurrentIndex].status; - if ( status & (RXSTATUS_SHORT_FRAME + RXSTATUS_OVERRUN + - RXSTATUS_CRC_ERROR + RXSTATUS_ABORT) ) { + if ( status & (RXSTATUS_SHORT_FRAME | RXSTATUS_OVERRUN | + RXSTATUS_CRC_ERROR | RXSTATUS_ABORT) ) { if ( status & RXSTATUS_SHORT_FRAME ) info->icount.rxshort++; else if ( status & RXSTATUS_ABORT ) @@ -6899,7 +6899,7 @@ static void mgsl_load_tx_dma_buffer(struct mgsl_struct *info, /* set CMR:13 to start transmit when * next GoAhead (abort) is received */ - info->cmr_value |= BIT13; + info->cmr_value |= BIT13; } /* begin loading the frame in the next available tx dma @@ -7278,7 +7278,7 @@ static bool mgsl_dma_test( struct mgsl_struct *info ) spin_unlock_irqrestore(&info->irq_spinlock,flags); - + /******************************/ /* WAIT FOR TRANSMIT COMPLETE */ /******************************/ @@ -7292,7 +7292,7 @@ static bool mgsl_dma_test( struct mgsl_struct *info ) status = usc_InReg( info, TCSR ); spin_unlock_irqrestore(&info->irq_spinlock,flags); - while ( !(status & (BIT6+BIT5+BIT4+BIT2+BIT1)) ) { + while ( !(status & (BIT6 | BIT5 | BIT4 | BIT2 | BIT1)) ) { if (time_after(jiffies, EndTime)) { rc = false; break; @@ -7307,7 +7307,7 @@ static bool mgsl_dma_test( struct mgsl_struct *info ) if ( rc ){ /* CHECK FOR TRANSMIT ERRORS */ - if ( status & (BIT5 + BIT1) ) + if ( status & (BIT5 | BIT1) ) rc = false; } @@ -7333,7 +7333,7 @@ static bool mgsl_dma_test( struct mgsl_struct *info ) /* CHECK FOR RECEIVE ERRORS */ status = info->rx_buffer_list[0].status; - if ( status & (BIT8 + BIT3 + BIT1) ) { + if ( status & (BIT8 | BIT3 | BIT1) ) { /* receive error has occurred */ rc = false; } else { @@ -7605,7 +7605,7 @@ static void usc_loopmode_send_done( struct mgsl_struct * info ) { info->loopmode_send_done_requested = false; /* clear CMR:13 to 0 to start echoing RxData to TxData */ - info->cmr_value &= ~BIT13; + info->cmr_value &= ~BIT13; usc_OutReg(info, CMR, info->cmr_value); } diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index ff171384ea52..dc6e96996ead 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -3478,7 +3478,7 @@ static int alloc_buf_list(SLMP_INFO *info) for ( i = 0; i < info->rx_buf_count; i++ ) { /* calculate and store physical address of this buffer entry */ info->rx_buf_list_ex[i].phys_entry = - info->buffer_list_phys + (i * sizeof(SCABUFSIZE)); + info->buffer_list_phys + (i * SCABUFSIZE); /* calculate and store physical address of */ /* next entry in cirular list of entries */ diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 749dae8c3489..40a9fe9d3b10 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -985,7 +985,7 @@ static int sysrq_reset_seq_param_set(const char *buffer, unsigned long val; int error; - error = strict_strtoul(buffer, 0, &val); + error = kstrtoul(buffer, 0, &val); if (error < 0) return error; diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 9121c1f7aeef..c043136fbe51 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -18,31 +18,118 @@ #include <linux/module.h> #include <linux/ratelimit.h> + +#define MIN_TTYB_SIZE 256 +#define TTYB_ALIGN_MASK 255 + +/* + * Byte threshold to limit memory consumption for flip buffers. + * The actual memory limit is > 2x this amount. + */ +#define TTYB_MEM_LIMIT 65536 + +/* + * We default to dicing tty buffer allocations to this many characters + * in order to avoid multiple page allocations. We know the size of + * tty_buffer itself but it must also be taken into account that the + * the buffer is 256 byte aligned. See tty_buffer_find for the allocation + * logic this must match + */ + +#define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF) + + +/** + * tty_buffer_lock_exclusive - gain exclusive access to buffer + * tty_buffer_unlock_exclusive - release exclusive access + * + * @port - tty_port owning the flip buffer + * + * Guarantees safe use of the line discipline's receive_buf() method by + * excluding the buffer work and any pending flush from using the flip + * buffer. Data can continue to be added concurrently to the flip buffer + * from the driver side. + * + * On release, the buffer work is restarted if there is data in the + * flip buffer + */ + +void tty_buffer_lock_exclusive(struct tty_port *port) +{ + struct tty_bufhead *buf = &port->buf; + + atomic_inc(&buf->priority); + mutex_lock(&buf->lock); +} + +void tty_buffer_unlock_exclusive(struct tty_port *port) +{ + struct tty_bufhead *buf = &port->buf; + int restart; + + restart = buf->head->commit != buf->head->read; + + atomic_dec(&buf->priority); + mutex_unlock(&buf->lock); + if (restart) + queue_work(system_unbound_wq, &buf->work); +} + +/** + * tty_buffer_space_avail - return unused buffer space + * @port - tty_port owning the flip buffer + * + * Returns the # of bytes which can be written by the driver without + * reaching the buffer limit. + * + * Note: this does not guarantee that memory is available to write + * the returned # of bytes (use tty_prepare_flip_string_xxx() to + * pre-allocate if memory guarantee is required). + */ + +int tty_buffer_space_avail(struct tty_port *port) +{ + int space = TTYB_MEM_LIMIT - atomic_read(&port->buf.memory_used); + return max(space, 0); +} + +static void tty_buffer_reset(struct tty_buffer *p, size_t size) +{ + p->used = 0; + p->size = size; + p->next = NULL; + p->commit = 0; + p->read = 0; +} + /** * tty_buffer_free_all - free buffers used by a tty * @tty: tty to free from * * Remove all the buffers pending on a tty whether queued with data * or in the free ring. Must be called when the tty is no longer in use - * - * Locking: none */ void tty_buffer_free_all(struct tty_port *port) { struct tty_bufhead *buf = &port->buf; - struct tty_buffer *thead; + struct tty_buffer *p, *next; + struct llist_node *llist; - while ((thead = buf->head) != NULL) { - buf->head = thead->next; - kfree(thead); - } - while ((thead = buf->free) != NULL) { - buf->free = thead->next; - kfree(thead); + while ((p = buf->head) != NULL) { + buf->head = p->next; + if (p->size > 0) + kfree(p); } - buf->tail = NULL; - buf->memory_used = 0; + llist = llist_del_all(&buf->free); + llist_for_each_entry_safe(p, next, llist, free) + kfree(p); + + tty_buffer_reset(&buf->sentinel, 0); + buf->head = &buf->sentinel; + buf->tail = &buf->sentinel; + + atomic_set(&buf->memory_used, 0); } /** @@ -51,29 +138,39 @@ void tty_buffer_free_all(struct tty_port *port) * @size: desired size (characters) * * Allocate a new tty buffer to hold the desired number of characters. + * We round our buffers off in 256 character chunks to get better + * allocation behaviour. * Return NULL if out of memory or the allocation would exceed the * per device queue - * - * Locking: Caller must hold tty->buf.lock */ static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) { + struct llist_node *free; struct tty_buffer *p; - if (port->buf.memory_used + size > 65536) + /* Round the buffer size out */ + size = __ALIGN_MASK(size, TTYB_ALIGN_MASK); + + if (size <= MIN_TTYB_SIZE) { + free = llist_del_first(&port->buf.free); + if (free) { + p = llist_entry(free, struct tty_buffer, free); + goto found; + } + } + + /* Should possibly check if this fails for the largest buffer we + have queued and recycle that ? */ + if (atomic_read(&port->buf.memory_used) > TTYB_MEM_LIMIT) return NULL; p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); if (p == NULL) return NULL; - p->used = 0; - p->size = size; - p->next = NULL; - p->commit = 0; - p->read = 0; - p->char_buf_ptr = (char *)(p->data); - p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; - port->buf.memory_used += size; + +found: + tty_buffer_reset(p, size); + atomic_add(size, &port->buf.memory_used); return p; } @@ -84,8 +181,6 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) * * Free a tty buffer, or add it to the free list according to our * internal strategy - * - * Locking: Caller must hold tty->buf.lock */ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) @@ -93,41 +188,12 @@ 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 */ - buf->memory_used -= b->size; - WARN_ON(buf->memory_used < 0); + WARN_ON(atomic_sub_return(b->size, &buf->memory_used) < 0); - if (b->size >= 512) + if (b->size > MIN_TTYB_SIZE) kfree(b); - else { - b->next = buf->free; - buf->free = b; - } -} - -/** - * __tty_buffer_flush - flush full tty buffers - * @tty: tty to flush - * - * flush all the buffers containing receive data. Caller must - * hold the buffer lock and must have ensured no parallel flush to - * ldisc is running. - * - * Locking: Caller must hold tty->buf.lock - */ - -static void __tty_buffer_flush(struct tty_port *port) -{ - struct tty_bufhead *buf = &port->buf; - struct tty_buffer *thead; - - if (unlikely(buf->head == NULL)) - return; - while ((thead = buf->head->next) != NULL) { - tty_buffer_free(port, buf->head); - buf->head = thead; - } - WARN_ON(buf->head != buf->tail); - buf->head->read = buf->head->commit; + else if (b->size > 0) + llist_add(&b->free, &buf->free); } /** @@ -138,65 +204,28 @@ static void __tty_buffer_flush(struct tty_port *port) * being processed by flush_to_ldisc then we defer the processing * to that function * - * Locking: none + * Locking: takes buffer lock to ensure single-threaded flip buffer + * 'consumer' */ 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(&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(TTYP_FLUSHING, &port->iflags)) { - set_bit(TTYP_FLUSHPENDING, &port->iflags); - spin_unlock_irqrestore(&buf->lock, flags); - wait_event(tty->read_wait, - test_bit(TTYP_FLUSHPENDING, &port->iflags) == 0); - return; - } else - __tty_buffer_flush(port); - spin_unlock_irqrestore(&buf->lock, flags); -} + struct tty_buffer *next; -/** - * tty_buffer_find - find a free tty buffer - * @tty: tty owning the buffer - * @size: characters wanted - * - * Locate an existing suitable tty buffer or if we are lacking one then - * allocate a new one. We round our buffers off in 256 character chunks - * to get better allocation behaviour. - * - * Locking: Caller must hold tty->buf.lock - */ + atomic_inc(&buf->priority); -static struct tty_buffer *tty_buffer_find(struct tty_port *port, size_t size) -{ - struct tty_buffer **tbh = &port->buf.free; - while ((*tbh) != NULL) { - struct tty_buffer *t = *tbh; - if (t->size >= size) { - *tbh = t->next; - t->next = NULL; - t->used = 0; - t->commit = 0; - t->read = 0; - port->buf.memory_used += t->size; - return t; - } - tbh = &((*tbh)->next); + mutex_lock(&buf->lock); + while ((next = buf->head->next) != NULL) { + tty_buffer_free(port, buf->head); + buf->head = next; } - /* Round the buffer size out */ - size = (size + 0xFF) & ~0xFF; - return tty_buffer_alloc(port, size); - /* Should possibly check if this fails for the largest buffer we - have queued and recycle that ? */ + buf->head->read = buf->head->commit; + atomic_dec(&buf->priority); + mutex_unlock(&buf->lock); } + /** * tty_buffer_request_room - grow tty buffer if needed * @tty: tty structure @@ -204,38 +233,26 @@ static struct tty_buffer *tty_buffer_find(struct tty_port *port, 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 port->buf.lock */ 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; - unsigned long flags; - spin_lock_irqsave(&buf->lock, flags); - /* 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 */ + b = buf->tail; - if (b != NULL) - left = b->size - b->used; - else - left = 0; + left = b->size - b->used; if (left < size) { /* This is the slow path - looking for new buffers to use */ - if ((n = tty_buffer_find(port, size)) != NULL) { - if (b != NULL) { - b->next = n; - b->commit = b->used; - } else - buf->head = n; + if ((n = tty_buffer_alloc(port, size)) != NULL) { buf->tail = n; + b->commit = b->used; + smp_mb(); + b->next = n; } else size = left; } - spin_unlock_irqrestore(&buf->lock, flags); return size; } EXPORT_SYMBOL_GPL(tty_buffer_request_room); @@ -249,8 +266,6 @@ 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 port->buf.lock */ int tty_insert_flip_string_fixed_flag(struct tty_port *port, @@ -261,12 +276,10 @@ int tty_insert_flip_string_fixed_flag(struct tty_port *port, int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); int space = tty_buffer_request_room(port, goal); struct tty_buffer *tb = port->buf.tail; - /* If there is no space then tb may be NULL */ - if (unlikely(space == 0)) { + if (unlikely(space == 0)) break; - } - memcpy(tb->char_buf_ptr + tb->used, chars, space); - memset(tb->flag_buf_ptr + tb->used, flag, space); + memcpy(char_buf_ptr(tb, tb->used), chars, space); + memset(flag_buf_ptr(tb, tb->used), flag, space); tb->used += space; copied += space; chars += space; @@ -287,8 +300,6 @@ EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag); * Queue a series of bytes to the tty buffering. For each character * the flags array indicates the status of the character. Returns the * number added. - * - * Locking: Called functions may take port->buf.lock */ int tty_insert_flip_string_flags(struct tty_port *port, @@ -299,12 +310,10 @@ int tty_insert_flip_string_flags(struct tty_port *port, int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); int space = tty_buffer_request_room(port, goal); struct tty_buffer *tb = port->buf.tail; - /* If there is no space then tb may be NULL */ - if (unlikely(space == 0)) { + if (unlikely(space == 0)) break; - } - memcpy(tb->char_buf_ptr + tb->used, chars, space); - memcpy(tb->flag_buf_ptr + tb->used, flags, space); + memcpy(char_buf_ptr(tb, tb->used), chars, space); + memcpy(flag_buf_ptr(tb, tb->used), flags, space); tb->used += space; copied += space; chars += space; @@ -325,20 +334,14 @@ EXPORT_SYMBOL(tty_insert_flip_string_flags); * 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 port->buf.lock */ void tty_schedule_flip(struct tty_port *port) { struct tty_bufhead *buf = &port->buf; - unsigned long flags; WARN_ON(port->low_latency); - spin_lock_irqsave(&buf->lock, flags); - if (buf->tail != NULL) - buf->tail->commit = buf->tail->used; - spin_unlock_irqrestore(&buf->lock, flags); + buf->tail->commit = buf->tail->used; schedule_work(&buf->work); } EXPORT_SYMBOL(tty_schedule_flip); @@ -354,8 +357,6 @@ EXPORT_SYMBOL(tty_schedule_flip); * accounted for as ready for normal characters. This is used for drivers * 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 port->buf.lock */ int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars, @@ -364,8 +365,8 @@ int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars, int space = tty_buffer_request_room(port, size); if (likely(space)) { struct tty_buffer *tb = port->buf.tail; - *chars = tb->char_buf_ptr + tb->used; - memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); + *chars = char_buf_ptr(tb, tb->used); + memset(flag_buf_ptr(tb, tb->used), TTY_NORMAL, space); tb->used += space; } return space; @@ -384,8 +385,6 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string); * accounted for as ready for characters. This is used for drivers * 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 port->buf.lock */ int tty_prepare_flip_string_flags(struct tty_port *port, @@ -394,8 +393,8 @@ int tty_prepare_flip_string_flags(struct tty_port *port, int space = tty_buffer_request_room(port, size); if (likely(space)) { struct tty_buffer *tb = port->buf.tail; - *chars = tb->char_buf_ptr + tb->used; - *flags = tb->flag_buf_ptr + tb->used; + *chars = char_buf_ptr(tb, tb->used); + *flags = flag_buf_ptr(tb, tb->used); tb->used += space; } return space; @@ -403,6 +402,23 @@ int tty_prepare_flip_string_flags(struct tty_port *port, EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); +static int +receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count) +{ + struct tty_ldisc *disc = tty->ldisc; + unsigned char *p = char_buf_ptr(head, head->read); + char *f = flag_buf_ptr(head, head->read); + + if (disc->ops->receive_buf2) + count = disc->ops->receive_buf2(tty, p, f, count); + else { + count = min_t(int, count, tty->receive_room); + if (count) + disc->ops->receive_buf(tty, p, f, count); + } + head->read += count; + return count; +} /** * flush_to_ldisc @@ -411,9 +427,10 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); * This routine is called out of the software interrupt to flush data * from the buffer chain to the line discipline. * - * Locking: holds tty->buf.lock to guard buffer list. Drops the lock - * while invoking the line discipline receive_buf method. The - * receive_buf method is single threaded for each tty instance. + * The receive_buf method is single threaded for each tty instance. + * + * Locking: takes buffer lock to ensure single-threaded flip buffer + * 'consumer' */ static void flush_to_ldisc(struct work_struct *work) @@ -421,7 +438,6 @@ static void flush_to_ldisc(struct work_struct *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; @@ -429,52 +445,34 @@ static void flush_to_ldisc(struct work_struct *work) return; disc = tty_ldisc_ref(tty); - if (disc == NULL) /* !TTY_LDISC */ + if (disc == NULL) return; - spin_lock_irqsave(&buf->lock, flags); - - if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) { - struct tty_buffer *head; - while ((head = buf->head) != NULL) { - int count; - char *char_buf; - unsigned char *flag_buf; - - count = head->commit - head->read; - if (!count) { - if (head->next == NULL) - break; - buf->head = head->next; - tty_buffer_free(port, head); - continue; - } - if (!tty->receive_room) - break; - if (count > tty->receive_room) - count = tty->receive_room; - char_buf = head->char_buf_ptr + head->read; - flag_buf = head->flag_buf_ptr + head->read; - head->read += count; - spin_unlock_irqrestore(&buf->lock, flags); - disc->ops->receive_buf(tty, char_buf, - flag_buf, count); - spin_lock_irqsave(&buf->lock, flags); - /* Ldisc or user is trying to flush the buffers. - 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(TTYP_FLUSHPENDING, &port->iflags)) { - __tty_buffer_flush(port); - clear_bit(TTYP_FLUSHPENDING, &port->iflags); - wake_up(&tty->read_wait); + mutex_lock(&buf->lock); + + while (1) { + struct tty_buffer *head = buf->head; + int count; + + /* Ldisc or user is trying to gain exclusive access */ + if (atomic_read(&buf->priority)) + break; + + count = head->commit - head->read; + if (!count) { + if (head->next == NULL) break; - } + buf->head = head->next; + tty_buffer_free(port, head); + continue; } - clear_bit(TTYP_FLUSHING, &port->iflags); + + count = receive_buf(tty, head, count); + if (!count) + break; } - spin_unlock_irqrestore(&buf->lock, flags); + mutex_unlock(&buf->lock); tty_ldisc_deref(disc); } @@ -503,19 +501,13 @@ void tty_flush_to_ldisc(struct tty_struct *tty) * * In the event of the queue being busy for flipping the work will be * held off and retried later. - * - * Locking: tty buffer lock. Driver locks in low latency mode. */ void tty_flip_buffer_push(struct tty_port *port) { struct tty_bufhead *buf = &port->buf; - unsigned long flags; - spin_lock_irqsave(&buf->lock, flags); - if (buf->tail != NULL) - buf->tail->commit = buf->tail->used; - spin_unlock_irqrestore(&buf->lock, flags); + buf->tail->commit = buf->tail->used; if (port->low_latency) flush_to_ldisc(&buf->work); @@ -530,19 +522,18 @@ EXPORT_SYMBOL(tty_flip_buffer_push); * * Set up the initial state of the buffer management for a tty device. * Must be called before the other tty buffer functions are used. - * - * Locking: none */ void tty_buffer_init(struct tty_port *port) { struct tty_bufhead *buf = &port->buf; - spin_lock_init(&buf->lock); - buf->head = NULL; - buf->tail = NULL; - buf->free = NULL; - buf->memory_used = 0; + mutex_init(&buf->lock); + tty_buffer_reset(&buf->sentinel, 0); + buf->head = &buf->sentinel; + buf->tail = &buf->sentinel; + init_llist_head(&buf->free); + atomic_set(&buf->memory_used, 0); + atomic_set(&buf->priority, 0); INIT_WORK(&buf->work, flush_to_ldisc); } - diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 6464029e4860..3a1a01af9a80 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -603,8 +603,8 @@ static int tty_signal_session_leader(struct tty_struct *tty, int exit_session) * BTM * redirect lock for undoing redirection * file list lock for manipulating list of ttys - * tty_ldisc_lock from called functions - * termios_mutex resetting termios data + * tty_ldiscs_lock from called functions + * termios_rwsem resetting termios data * tasklist_lock to walk task list for hangup event * ->siglock to protect ->signal/->sighand */ @@ -629,6 +629,11 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) tty_lock(tty); + if (test_bit(TTY_HUPPED, &tty->flags)) { + tty_unlock(tty); + return; + } + /* some functions below drop BTM, so we need this bit */ set_bit(TTY_HUPPING, &tty->flags); @@ -664,7 +669,6 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) spin_lock_irq(&tty->ctrl_lock); clear_bit(TTY_THROTTLED, &tty->flags); - clear_bit(TTY_PUSH, &tty->flags); clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); put_pid(tty->session); put_pid(tty->pgrp); @@ -850,7 +854,8 @@ void disassociate_ctty(int on_exit) struct pid *tty_pgrp = tty_get_pgrp(tty); if (tty_pgrp) { kill_pgrp(tty_pgrp, SIGHUP, on_exit); - kill_pgrp(tty_pgrp, SIGCONT, on_exit); + if (!on_exit) + kill_pgrp(tty_pgrp, SIGCONT, on_exit); put_pid(tty_pgrp); } } @@ -1388,8 +1393,7 @@ static int tty_reopen(struct tty_struct *tty) struct tty_driver *driver = tty->driver; if (test_bit(TTY_CLOSING, &tty->flags) || - test_bit(TTY_HUPPING, &tty->flags) || - test_bit(TTY_LDISC_CHANGING, &tty->flags)) + test_bit(TTY_HUPPING, &tty->flags)) return -EIO; if (driver->type == TTY_DRIVER_TYPE_PTY && @@ -1405,7 +1409,7 @@ static int tty_reopen(struct tty_struct *tty) } tty->count++; - WARN_ON(!test_bit(TTY_LDISC, &tty->flags)); + WARN_ON(!tty->ldisc); return 0; } @@ -1618,6 +1622,8 @@ static void release_tty(struct tty_struct *tty, int idx) tty_free_termios(tty); tty_driver_remove_tty(tty->driver, tty); tty->port->itty = NULL; + if (tty->link) + tty->link->port->itty = NULL; cancel_work_sync(&tty->port->buf.work); if (tty->link) @@ -2138,6 +2144,7 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait) static int __tty_fasync(int fd, struct file *filp, int on) { struct tty_struct *tty = file_tty(filp); + struct tty_ldisc *ldisc; unsigned long flags; int retval = 0; @@ -2148,11 +2155,17 @@ static int __tty_fasync(int fd, struct file *filp, int on) if (retval <= 0) goto out; + ldisc = tty_ldisc_ref(tty); + if (ldisc) { + if (ldisc->ops->fasync) + ldisc->ops->fasync(tty, on); + tty_ldisc_deref(ldisc); + } + if (on) { enum pid_type type; struct pid *pid; - if (!waitqueue_active(&tty->read_wait)) - tty->minimum_to_wake = 1; + spin_lock_irqsave(&tty->ctrl_lock, flags); if (tty->pgrp) { pid = tty->pgrp; @@ -2165,13 +2178,7 @@ static int __tty_fasync(int fd, struct file *filp, int on) spin_unlock_irqrestore(&tty->ctrl_lock, flags); retval = __f_setown(filp, pid, type, 0); put_pid(pid); - if (retval) - goto out; - } else { - if (!tty->fasync && !waitqueue_active(&tty->read_wait)) - tty->minimum_to_wake = N_TTY_BUF_SIZE; } - retval = 0; out: return retval; } @@ -2199,7 +2206,7 @@ static int tty_fasync(int fd, struct file *filp, int on) * FIXME: does not honour flow control ?? * * Locking: - * Called functions take tty_ldisc_lock + * Called functions take tty_ldiscs_lock * current->signal->tty check is safe without locks * * FIXME: may race normal receive processing @@ -2228,7 +2235,7 @@ static int tiocsti(struct tty_struct *tty, char __user *p) * * Copies the kernel idea of the window size into the user buffer. * - * Locking: tty->termios_mutex is taken to ensure the winsize data + * Locking: tty->winsize_mutex is taken to ensure the winsize data * is consistent. */ @@ -2236,9 +2243,9 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg) { int err; - mutex_lock(&tty->termios_mutex); + mutex_lock(&tty->winsize_mutex); err = copy_to_user(arg, &tty->winsize, sizeof(*arg)); - mutex_unlock(&tty->termios_mutex); + mutex_unlock(&tty->winsize_mutex); return err ? -EFAULT: 0; } @@ -2259,7 +2266,7 @@ int tty_do_resize(struct tty_struct *tty, struct winsize *ws) unsigned long flags; /* Lock the tty */ - mutex_lock(&tty->termios_mutex); + mutex_lock(&tty->winsize_mutex); if (!memcmp(ws, &tty->winsize, sizeof(*ws))) goto done; /* Get the PID values and reference them so we can @@ -2274,7 +2281,7 @@ int tty_do_resize(struct tty_struct *tty, struct winsize *ws) tty->winsize = *ws; done: - mutex_unlock(&tty->termios_mutex); + mutex_unlock(&tty->winsize_mutex); return 0; } EXPORT_SYMBOL(tty_do_resize); @@ -3013,8 +3020,10 @@ void initialize_tty_struct(struct tty_struct *tty, tty->session = NULL; tty->pgrp = NULL; mutex_init(&tty->legacy_mutex); - mutex_init(&tty->termios_mutex); - mutex_init(&tty->ldisc_mutex); + mutex_init(&tty->throttle_mutex); + init_rwsem(&tty->termios_rwsem); + mutex_init(&tty->winsize_mutex); + init_ldsem(&tty->ldisc_sem); init_waitqueue_head(&tty->write_wait); init_waitqueue_head(&tty->read_wait); INIT_WORK(&tty->hangup_work, do_tty_hangup); diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 3500d4114147..6fd60fece6b4 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -94,20 +94,20 @@ EXPORT_SYMBOL(tty_driver_flush_buffer); * @tty: terminal * * Indicate that a tty should stop transmitting data down the stack. - * Takes the termios mutex to protect against parallel throttle/unthrottle + * Takes the termios rwsem to protect against parallel throttle/unthrottle * and also to ensure the driver can consistently reference its own * termios data at this point when implementing software flow control. */ void tty_throttle(struct tty_struct *tty) { - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); /* check TTY_THROTTLED first so it indicates our state */ if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) && tty->ops->throttle) tty->ops->throttle(tty); tty->flow_change = 0; - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); } EXPORT_SYMBOL(tty_throttle); @@ -116,7 +116,7 @@ EXPORT_SYMBOL(tty_throttle); * @tty: terminal * * Indicate that a tty may continue transmitting data down the stack. - * Takes the termios mutex to protect against parallel throttle/unthrottle + * Takes the termios rwsem to protect against parallel throttle/unthrottle * and also to ensure the driver can consistently reference its own * termios data at this point when implementing software flow control. * @@ -126,12 +126,12 @@ EXPORT_SYMBOL(tty_throttle); void tty_unthrottle(struct tty_struct *tty) { - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && tty->ops->unthrottle) tty->ops->unthrottle(tty); tty->flow_change = 0; - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); } EXPORT_SYMBOL(tty_unthrottle); @@ -151,7 +151,7 @@ int tty_throttle_safe(struct tty_struct *tty) { int ret = 0; - mutex_lock(&tty->termios_mutex); + mutex_lock(&tty->throttle_mutex); if (!test_bit(TTY_THROTTLED, &tty->flags)) { if (tty->flow_change != TTY_THROTTLE_SAFE) ret = 1; @@ -161,7 +161,7 @@ int tty_throttle_safe(struct tty_struct *tty) tty->ops->throttle(tty); } } - mutex_unlock(&tty->termios_mutex); + mutex_unlock(&tty->throttle_mutex); return ret; } @@ -182,7 +182,7 @@ int tty_unthrottle_safe(struct tty_struct *tty) { int ret = 0; - mutex_lock(&tty->termios_mutex); + mutex_lock(&tty->throttle_mutex); if (test_bit(TTY_THROTTLED, &tty->flags)) { if (tty->flow_change != TTY_UNTHROTTLE_SAFE) ret = 1; @@ -192,7 +192,7 @@ int tty_unthrottle_safe(struct tty_struct *tty) tty->ops->unthrottle(tty); } } - mutex_unlock(&tty->termios_mutex); + mutex_unlock(&tty->throttle_mutex); return ret; } @@ -468,7 +468,7 @@ EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate); * @obad: output baud rate * * Update the current termios data for the tty with the new speed - * settings. The caller must hold the termios_mutex for the tty in + * settings. The caller must hold the termios_rwsem for the tty in * question. */ @@ -528,7 +528,7 @@ EXPORT_SYMBOL(tty_termios_hw_change); * is a bit of layering violation here with n_tty in terms of the * internal knowledge of this function. * - * Locking: termios_mutex + * Locking: termios_rwsem */ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios) @@ -544,7 +544,7 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios) /* FIXME: we need to decide on some locking/ordering semantics for the set_termios notification eventually */ - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); old_termios = tty->termios; tty->termios = *new_termios; unset_locked_termios(&tty->termios, &old_termios, &tty->termios_locked); @@ -586,7 +586,7 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios) (ld->ops->set_termios)(tty, &old_termios); tty_ldisc_deref(ld); } - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return 0; } EXPORT_SYMBOL_GPL(tty_set_termios); @@ -601,7 +601,7 @@ EXPORT_SYMBOL_GPL(tty_set_termios); * functions before using tty_set_termios to do the actual changes. * * Locking: - * Called functions take ldisc and termios_mutex locks + * Called functions take ldisc and termios_rwsem locks */ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) @@ -613,9 +613,9 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) if (retval) return retval; - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); tmp_termios = tty->termios; - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); if (opt & TERMIOS_TERMIO) { if (user_termio_to_kernel_termios(&tmp_termios, @@ -667,16 +667,16 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) static void copy_termios(struct tty_struct *tty, struct ktermios *kterm) { - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); *kterm = tty->termios; - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); } static void copy_termios_locked(struct tty_struct *tty, struct ktermios *kterm) { - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); *kterm = tty->termios_locked; - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); } static int get_termio(struct tty_struct *tty, struct termio __user *termio) @@ -723,10 +723,10 @@ static int set_termiox(struct tty_struct *tty, void __user *arg, int opt) return -ERESTARTSYS; } - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); if (tty->ops->set_termiox) tty->ops->set_termiox(tty, &tnew); - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return 0; } @@ -761,13 +761,13 @@ static int get_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) { struct sgttyb tmp; - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); tmp.sg_ispeed = tty->termios.c_ispeed; tmp.sg_ospeed = tty->termios.c_ospeed; tmp.sg_erase = tty->termios.c_cc[VERASE]; tmp.sg_kill = tty->termios.c_cc[VKILL]; tmp.sg_flags = get_sgflags(tty); - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0; } @@ -806,7 +806,7 @@ static void set_sgflags(struct ktermios *termios, int flags) * Updates a terminal from the legacy BSD style terminal information * structure. * - * Locking: termios_mutex + * Locking: termios_rwsem */ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) @@ -822,7 +822,7 @@ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) if (copy_from_user(&tmp, sgttyb, sizeof(tmp))) return -EFAULT; - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); termios = tty->termios; termios.c_cc[VERASE] = tmp.sg_erase; termios.c_cc[VKILL] = tmp.sg_kill; @@ -832,7 +832,7 @@ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) tty_termios_encode_baud_rate(&termios, termios.c_ispeed, termios.c_ospeed); #endif - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); tty_set_termios(tty, &termios); return 0; } @@ -843,14 +843,14 @@ static int get_tchars(struct tty_struct *tty, struct tchars __user *tchars) { struct tchars tmp; - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); tmp.t_intrc = tty->termios.c_cc[VINTR]; tmp.t_quitc = tty->termios.c_cc[VQUIT]; tmp.t_startc = tty->termios.c_cc[VSTART]; tmp.t_stopc = tty->termios.c_cc[VSTOP]; tmp.t_eofc = tty->termios.c_cc[VEOF]; tmp.t_brkc = tty->termios.c_cc[VEOL2]; /* what is brkc anyway? */ - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); return copy_to_user(tchars, &tmp, sizeof(tmp)) ? -EFAULT : 0; } @@ -860,14 +860,14 @@ static int set_tchars(struct tty_struct *tty, struct tchars __user *tchars) if (copy_from_user(&tmp, tchars, sizeof(tmp))) return -EFAULT; - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); tty->termios.c_cc[VINTR] = tmp.t_intrc; tty->termios.c_cc[VQUIT] = tmp.t_quitc; tty->termios.c_cc[VSTART] = tmp.t_startc; tty->termios.c_cc[VSTOP] = tmp.t_stopc; tty->termios.c_cc[VEOF] = tmp.t_eofc; tty->termios.c_cc[VEOL2] = tmp.t_brkc; /* what is brkc anyway? */ - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return 0; } #endif @@ -877,7 +877,7 @@ static int get_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) { struct ltchars tmp; - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); tmp.t_suspc = tty->termios.c_cc[VSUSP]; /* what is dsuspc anyway? */ tmp.t_dsuspc = tty->termios.c_cc[VSUSP]; @@ -886,7 +886,7 @@ static int get_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) tmp.t_flushc = tty->termios.c_cc[VEOL2]; tmp.t_werasc = tty->termios.c_cc[VWERASE]; tmp.t_lnextc = tty->termios.c_cc[VLNEXT]; - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); return copy_to_user(ltchars, &tmp, sizeof(tmp)) ? -EFAULT : 0; } @@ -897,7 +897,7 @@ static int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) if (copy_from_user(&tmp, ltchars, sizeof(tmp))) return -EFAULT; - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); tty->termios.c_cc[VSUSP] = tmp.t_suspc; /* what is dsuspc anyway? */ tty->termios.c_cc[VEOL2] = tmp.t_dsuspc; @@ -906,7 +906,7 @@ static int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) tty->termios.c_cc[VEOL2] = tmp.t_flushc; tty->termios.c_cc[VWERASE] = tmp.t_werasc; tty->termios.c_cc[VLNEXT] = tmp.t_lnextc; - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return 0; } #endif @@ -946,7 +946,7 @@ static int send_prio_char(struct tty_struct *tty, char ch) * @arg: enable/disable CLOCAL * * Perform a change to the CLOCAL state and call into the driver - * layer to make it visible. All done with the termios mutex + * layer to make it visible. All done with the termios rwsem */ static int tty_change_softcar(struct tty_struct *tty, int arg) @@ -955,7 +955,7 @@ static int tty_change_softcar(struct tty_struct *tty, int arg) int bit = arg ? CLOCAL : 0; struct ktermios old; - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); old = tty->termios; tty->termios.c_cflag &= ~CLOCAL; tty->termios.c_cflag |= bit; @@ -963,7 +963,7 @@ static int tty_change_softcar(struct tty_struct *tty, int arg) tty->ops->set_termios(tty, &old); if ((tty->termios.c_cflag & CLOCAL) != bit) ret = -EINVAL; - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return ret; } @@ -1066,9 +1066,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, if (user_termios_to_kernel_termios(&kterm, (struct termios __user *) arg)) return -EFAULT; - mutex_lock(&real_tty->termios_mutex); + down_write(&real_tty->termios_rwsem); real_tty->termios_locked = kterm; - mutex_unlock(&real_tty->termios_mutex); + up_write(&real_tty->termios_rwsem); return 0; #else case TIOCGLCKTRMIOS: @@ -1083,9 +1083,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, if (user_termios_to_kernel_termios_1(&kterm, (struct termios __user *) arg)) return -EFAULT; - mutex_lock(&real_tty->termios_mutex); + down_write(&real_tty->termios_rwsem); real_tty->termios_locked = kterm; - mutex_unlock(&real_tty->termios_mutex); + up_write(&real_tty->termios_rwsem); return ret; #endif #ifdef TCGETX @@ -1093,9 +1093,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, struct termiox ktermx; if (real_tty->termiox == NULL) return -EINVAL; - mutex_lock(&real_tty->termios_mutex); + down_read(&real_tty->termios_rwsem); memcpy(&ktermx, real_tty->termiox, sizeof(struct termiox)); - mutex_unlock(&real_tty->termios_mutex); + up_read(&real_tty->termios_rwsem); if (copy_to_user(p, &ktermx, sizeof(struct termiox))) ret = -EFAULT; return ret; @@ -1201,6 +1201,9 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, } return 0; case TCFLSH: + retval = tty_check_change(tty); + if (retval) + return retval; return __tty_perform_flush(tty, arg); default: /* Try the mode commands */ diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 1afe192bef6a..6458e11e8e9d 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -31,14 +31,20 @@ #define tty_ldisc_debug(tty, f, args...) #endif +/* lockdep nested classes for tty->ldisc_sem */ +enum { + LDISC_SEM_NORMAL, + LDISC_SEM_OTHER, +}; + + /* * This guards the refcounted line discipline lists. The lock * must be taken with irqs off because there are hangup path * callers who will do ldisc lookups and cannot sleep. */ -static DEFINE_RAW_SPINLOCK(tty_ldisc_lock); -static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); +static DEFINE_RAW_SPINLOCK(tty_ldiscs_lock); /* Line disc dispatch table */ static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; @@ -52,7 +58,7 @@ static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; * from this point onwards. * * Locking: - * takes tty_ldisc_lock to guard against ldisc races + * takes tty_ldiscs_lock to guard against ldisc races */ int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) @@ -63,11 +69,11 @@ int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) if (disc < N_TTY || disc >= NR_LDISCS) return -EINVAL; - raw_spin_lock_irqsave(&tty_ldisc_lock, flags); + raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); tty_ldiscs[disc] = new_ldisc; new_ldisc->num = disc; new_ldisc->refcount = 0; - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); return ret; } @@ -82,7 +88,7 @@ EXPORT_SYMBOL(tty_register_ldisc); * currently in use. * * Locking: - * takes tty_ldisc_lock to guard against ldisc races + * takes tty_ldiscs_lock to guard against ldisc races */ int tty_unregister_ldisc(int disc) @@ -93,12 +99,12 @@ int tty_unregister_ldisc(int disc) if (disc < N_TTY || disc >= NR_LDISCS) return -EINVAL; - raw_spin_lock_irqsave(&tty_ldisc_lock, flags); + raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); if (tty_ldiscs[disc]->refcount) ret = -EBUSY; else tty_ldiscs[disc] = NULL; - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); return ret; } @@ -109,7 +115,7 @@ static struct tty_ldisc_ops *get_ldops(int disc) unsigned long flags; struct tty_ldisc_ops *ldops, *ret; - raw_spin_lock_irqsave(&tty_ldisc_lock, flags); + raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); ret = ERR_PTR(-EINVAL); ldops = tty_ldiscs[disc]; if (ldops) { @@ -119,7 +125,7 @@ static struct tty_ldisc_ops *get_ldops(int disc) ret = ldops; } } - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); return ret; } @@ -127,10 +133,10 @@ static void put_ldops(struct tty_ldisc_ops *ldops) { unsigned long flags; - raw_spin_lock_irqsave(&tty_ldisc_lock, flags); + raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); ldops->refcount--; module_put(ldops->owner); - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); } /** @@ -143,10 +149,10 @@ static void put_ldops(struct tty_ldisc_ops *ldops) * available * * Locking: - * takes tty_ldisc_lock to guard against ldisc races + * takes tty_ldiscs_lock to guard against ldisc races */ -static struct tty_ldisc *tty_ldisc_get(int disc) +static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc) { struct tty_ldisc *ld; struct tty_ldisc_ops *ldops; @@ -173,8 +179,7 @@ static struct tty_ldisc *tty_ldisc_get(int disc) } ld->ops = ldops; - atomic_set(&ld->users, 1); - init_waitqueue_head(&ld->wq_idle); + ld->tty = tty; return ld; } @@ -186,20 +191,11 @@ static struct tty_ldisc *tty_ldisc_get(int disc) */ static inline void tty_ldisc_put(struct tty_ldisc *ld) { - unsigned long flags; - if (WARN_ON_ONCE(!ld)) return; - raw_spin_lock_irqsave(&tty_ldisc_lock, flags); - - /* unreleased reader reference(s) will cause this WARN */ - WARN_ON(!atomic_dec_and_test(&ld->users)); - - ld->ops->refcount--; - module_put(ld->ops->owner); + put_ldops(ld->ops); kfree(ld); - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); } static void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) @@ -251,34 +247,6 @@ const struct file_operations tty_ldiscs_proc_fops = { }; /** - * tty_ldisc_try - internal helper - * @tty: the tty - * - * Make a single attempt to grab and bump the refcount on - * the tty ldisc. Return 0 on failure or 1 on success. This is - * used to implement both the waiting and non waiting versions - * of tty_ldisc_ref - * - * Locking: takes tty_ldisc_lock - */ - -static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty) -{ - unsigned long flags; - struct tty_ldisc *ld; - - /* FIXME: this allows reference acquire after TTY_LDISC is cleared */ - raw_spin_lock_irqsave(&tty_ldisc_lock, flags); - ld = NULL; - if (test_bit(TTY_LDISC, &tty->flags) && tty->ldisc) { - ld = tty->ldisc; - atomic_inc(&ld->users); - } - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); - return ld; -} - -/** * tty_ldisc_ref_wait - wait for the tty ldisc * @tty: tty device * @@ -291,16 +259,15 @@ static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty) * against a discipline change, such as an existing ldisc reference * (which we check for) * - * Locking: call functions take tty_ldisc_lock + * Note: only callable from a file_operations routine (which + * guarantees tty->ldisc != NULL when the lock is acquired). */ struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) { - struct tty_ldisc *ld; - - /* wait_event is a macro */ - wait_event(tty_ldisc_wait, (ld = tty_ldisc_try(tty)) != NULL); - return ld; + ldsem_down_read(&tty->ldisc_sem, MAX_SCHEDULE_TIMEOUT); + WARN_ON(!tty->ldisc); + return tty->ldisc; } EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); @@ -311,13 +278,18 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); * Dereference the line discipline for the terminal and take a * reference to it. If the line discipline is in flux then * return NULL. Can be called from IRQ and timer functions. - * - * Locking: called functions take tty_ldisc_lock */ struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) { - return tty_ldisc_try(tty); + struct tty_ldisc *ld = NULL; + + if (ldsem_down_read_trylock(&tty->ldisc_sem)) { + ld = tty->ldisc; + if (!ld) + ldsem_up_read(&tty->ldisc_sem); + } + return ld; } EXPORT_SYMBOL_GPL(tty_ldisc_ref); @@ -327,48 +299,91 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref); * * Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May * be called in IRQ context. - * - * Locking: takes tty_ldisc_lock */ void tty_ldisc_deref(struct tty_ldisc *ld) { - unsigned long flags; + ldsem_up_read(&ld->tty->ldisc_sem); +} +EXPORT_SYMBOL_GPL(tty_ldisc_deref); - if (WARN_ON_ONCE(!ld)) - return; - raw_spin_lock_irqsave(&tty_ldisc_lock, flags); - /* - * WARNs if one-too-many reader references were released - * - the last reference must be released with tty_ldisc_put - */ - WARN_ON(atomic_dec_and_test(&ld->users)); - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); +static inline int __lockfunc +tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) +{ + return ldsem_down_write(&tty->ldisc_sem, timeout); +} - if (waitqueue_active(&ld->wq_idle)) - wake_up(&ld->wq_idle); +static inline int __lockfunc +tty_ldisc_lock_nested(struct tty_struct *tty, unsigned long timeout) +{ + return ldsem_down_write_nested(&tty->ldisc_sem, + LDISC_SEM_OTHER, timeout); } -EXPORT_SYMBOL_GPL(tty_ldisc_deref); -/** - * tty_ldisc_enable - allow ldisc use - * @tty: terminal to activate ldisc on - * - * Set the TTY_LDISC flag when the line discipline can be called - * again. Do necessary wakeups for existing sleepers. Clear the LDISC - * changing flag to indicate any ldisc change is now over. - * - * Note: nobody should set the TTY_LDISC bit except via this function. - * Clearing directly is allowed. - */ +static inline void tty_ldisc_unlock(struct tty_struct *tty) +{ + return ldsem_up_write(&tty->ldisc_sem); +} -static void tty_ldisc_enable(struct tty_struct *tty) +static int __lockfunc +tty_ldisc_lock_pair_timeout(struct tty_struct *tty, struct tty_struct *tty2, + unsigned long timeout) +{ + int ret; + + if (tty < tty2) { + ret = tty_ldisc_lock(tty, timeout); + if (ret) { + ret = tty_ldisc_lock_nested(tty2, timeout); + if (!ret) + tty_ldisc_unlock(tty); + } + } else { + /* if this is possible, it has lots of implications */ + WARN_ON_ONCE(tty == tty2); + if (tty2 && tty != tty2) { + ret = tty_ldisc_lock(tty2, timeout); + if (ret) { + ret = tty_ldisc_lock_nested(tty, timeout); + if (!ret) + tty_ldisc_unlock(tty2); + } + } else + ret = tty_ldisc_lock(tty, timeout); + } + + if (!ret) + return -EBUSY; + + set_bit(TTY_LDISC_HALTED, &tty->flags); + if (tty2) + set_bit(TTY_LDISC_HALTED, &tty2->flags); + return 0; +} + +static void __lockfunc +tty_ldisc_lock_pair(struct tty_struct *tty, struct tty_struct *tty2) +{ + tty_ldisc_lock_pair_timeout(tty, tty2, MAX_SCHEDULE_TIMEOUT); +} + +static void __lockfunc tty_ldisc_unlock_pair(struct tty_struct *tty, + struct tty_struct *tty2) +{ + tty_ldisc_unlock(tty); + if (tty2) + tty_ldisc_unlock(tty2); +} + +static void __lockfunc tty_ldisc_enable_pair(struct tty_struct *tty, + struct tty_struct *tty2) { clear_bit(TTY_LDISC_HALTED, &tty->flags); - set_bit(TTY_LDISC, &tty->flags); - clear_bit(TTY_LDISC_CHANGING, &tty->flags); - wake_up(&tty_ldisc_wait); + if (tty2) + clear_bit(TTY_LDISC_HALTED, &tty2->flags); + + tty_ldisc_unlock_pair(tty, tty2); } /** @@ -400,14 +415,14 @@ EXPORT_SYMBOL_GPL(tty_ldisc_flush); * they are not on hot paths so a little discipline won't do * any harm. * - * Locking: takes termios_mutex + * Locking: takes termios_rwsem */ static void tty_set_termios_ldisc(struct tty_struct *tty, int num) { - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); tty->termios.c_line = num; - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); } /** @@ -468,14 +483,14 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) int r; /* There is an outstanding reference here so this is safe */ - old = tty_ldisc_get(old->ops->num); + old = tty_ldisc_get(tty, old->ops->num); WARN_ON(IS_ERR(old)); tty->ldisc = old; tty_set_termios_ldisc(tty, old->ops->num); if (tty_ldisc_open(tty, old) < 0) { tty_ldisc_put(old); /* This driver is always present */ - new_ldisc = tty_ldisc_get(N_TTY); + new_ldisc = tty_ldisc_get(tty, N_TTY); if (IS_ERR(new_ldisc)) panic("n_tty: get"); tty->ldisc = new_ldisc; @@ -489,101 +504,6 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) } /** - * tty_ldisc_wait_idle - wait for the ldisc to become idle - * @tty: tty to wait for - * @timeout: for how long to wait at most - * - * Wait for the line discipline to become idle. The discipline must - * have been halted for this to guarantee it remains idle. - */ -static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout) -{ - long ret; - ret = wait_event_timeout(tty->ldisc->wq_idle, - atomic_read(&tty->ldisc->users) == 1, timeout); - return ret > 0 ? 0 : -EBUSY; -} - -/** - * tty_ldisc_halt - shut down the line discipline - * @tty: tty device - * @o_tty: paired pty device (can be NULL) - * @timeout: # of jiffies to wait for ldisc refs to be released - * - * Shut down the line discipline and work queue for this tty device and - * its paired pty (if exists). Clearing the TTY_LDISC flag ensures - * no further references can be obtained, while waiting for existing - * references to be released ensures no more data is fed to the ldisc. - * - * You need to do a 'flush_scheduled_work()' (outside the ldisc_mutex) - * in order to make sure any currently executing ldisc work is also - * flushed. - */ - -static int tty_ldisc_halt(struct tty_struct *tty, struct tty_struct *o_tty, - long timeout) -{ - int retval; - - clear_bit(TTY_LDISC, &tty->flags); - if (o_tty) - clear_bit(TTY_LDISC, &o_tty->flags); - - retval = tty_ldisc_wait_idle(tty, timeout); - if (!retval && o_tty) - retval = tty_ldisc_wait_idle(o_tty, timeout); - if (retval) - return retval; - - set_bit(TTY_LDISC_HALTED, &tty->flags); - if (o_tty) - set_bit(TTY_LDISC_HALTED, &o_tty->flags); - - return 0; -} - -/** - * tty_ldisc_hangup_halt - halt the line discipline for hangup - * @tty: tty being hung up - * - * Shut down the line discipline and work queue for the tty device - * being hungup. Clear the TTY_LDISC flag to ensure no further - * references can be obtained and wait for remaining references to be - * released to ensure no more data is fed to this ldisc. - * Caller must hold legacy and ->ldisc_mutex. - * - * NB: tty_set_ldisc() is prevented from changing the ldisc concurrently - * with this function by checking the TTY_HUPPING flag. - */ -static bool tty_ldisc_hangup_halt(struct tty_struct *tty) -{ - char cur_n[TASK_COMM_LEN], tty_n[64]; - long timeout = 3 * HZ; - - clear_bit(TTY_LDISC, &tty->flags); - - if (tty->ldisc) { /* Not yet closed */ - tty_unlock(tty); - - while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) { - timeout = MAX_SCHEDULE_TIMEOUT; - printk_ratelimited(KERN_WARNING - "%s: waiting (%s) for %s took too long, but we keep waiting...\n", - __func__, get_task_comm(cur_n, current), - tty_name(tty, tty_n)); - } - - set_bit(TTY_LDISC_HALTED, &tty->flags); - - /* must reacquire both locks and preserve lock order */ - mutex_unlock(&tty->ldisc_mutex); - tty_lock(tty); - mutex_lock(&tty->ldisc_mutex); - } - return !!tty->ldisc; -} - -/** * tty_set_ldisc - set line discipline * @tty: the terminal to set * @ldisc: the line discipline @@ -592,110 +512,49 @@ static bool tty_ldisc_hangup_halt(struct tty_struct *tty) * context. The ldisc change logic has to protect itself against any * overlapping ldisc change (including on the other end of pty pairs), * the close of one side of a tty/pty pair, and eventually hangup. - * - * Locking: takes tty_ldisc_lock, termios_mutex */ int tty_set_ldisc(struct tty_struct *tty, int ldisc) { int retval; - struct tty_ldisc *o_ldisc, *new_ldisc; - struct tty_struct *o_tty; + struct tty_ldisc *old_ldisc, *new_ldisc; + struct tty_struct *o_tty = tty->link; - new_ldisc = tty_ldisc_get(ldisc); + new_ldisc = tty_ldisc_get(tty, ldisc); if (IS_ERR(new_ldisc)) return PTR_ERR(new_ldisc); - tty_lock(tty); - /* - * We need to look at the tty locking here for pty/tty pairs - * when both sides try to change in parallel. - */ - - o_tty = tty->link; /* o_tty is the pty side or NULL */ - + retval = tty_ldisc_lock_pair_timeout(tty, o_tty, 5 * HZ); + if (retval) { + tty_ldisc_put(new_ldisc); + return retval; + } /* * Check the no-op case */ if (tty->ldisc->ops->num == ldisc) { - tty_unlock(tty); + tty_ldisc_enable_pair(tty, o_tty); tty_ldisc_put(new_ldisc); return 0; } - mutex_lock(&tty->ldisc_mutex); - - /* - * We could be midstream of another ldisc change which has - * dropped the lock during processing. If so we need to wait. - */ - - while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) { - mutex_unlock(&tty->ldisc_mutex); - tty_unlock(tty); - wait_event(tty_ldisc_wait, - test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0); - tty_lock(tty); - mutex_lock(&tty->ldisc_mutex); - } - - set_bit(TTY_LDISC_CHANGING, &tty->flags); - - /* - * No more input please, we are switching. The new ldisc - * will update this value in the ldisc open function - */ - - tty->receive_room = 0; - - o_ldisc = tty->ldisc; - - tty_unlock(tty); - /* - * Make sure we don't change while someone holds a - * reference to the line discipline. The TTY_LDISC bit - * prevents anyone taking a reference once it is clear. - * We need the lock to avoid racing reference takers. - * - * We must clear the TTY_LDISC bit here to avoid a livelock - * with a userspace app continually trying to use the tty in - * parallel to the change and re-referencing the tty. - */ - - retval = tty_ldisc_halt(tty, o_tty, 5 * HZ); - - /* - * Wait for hangup to complete, if pending. - * We must drop the mutex here in case a hangup is also in process. - */ - - mutex_unlock(&tty->ldisc_mutex); - - flush_work(&tty->hangup_work); - + old_ldisc = tty->ldisc; tty_lock(tty); - mutex_lock(&tty->ldisc_mutex); - - /* handle wait idle failure locked */ - if (retval) { - tty_ldisc_put(new_ldisc); - goto enable; - } - if (test_bit(TTY_HUPPING, &tty->flags)) { + if (test_bit(TTY_HUPPING, &tty->flags) || + test_bit(TTY_HUPPED, &tty->flags)) { /* We were raced by the hangup method. It will have stomped the ldisc data and closed the ldisc down */ - clear_bit(TTY_LDISC_CHANGING, &tty->flags); - mutex_unlock(&tty->ldisc_mutex); + tty_ldisc_enable_pair(tty, o_tty); tty_ldisc_put(new_ldisc); tty_unlock(tty); return -EIO; } - /* Shutdown the current discipline. */ - tty_ldisc_close(tty, o_ldisc); + /* Shutdown the old discipline. */ + tty_ldisc_close(tty, old_ldisc); /* Now set up the new line discipline. */ tty->ldisc = new_ldisc; @@ -705,26 +564,24 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) if (retval < 0) { /* Back to the old one or N_TTY if we can't */ tty_ldisc_put(new_ldisc); - tty_ldisc_restore(tty, o_ldisc); + tty_ldisc_restore(tty, old_ldisc); } - /* At this point we hold a reference to the new ldisc and a - a reference to the old ldisc. If we ended up flipping back - to the existing ldisc we have two references to it */ - - if (tty->ldisc->ops->num != o_ldisc->ops->num && tty->ops->set_ldisc) + if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) tty->ops->set_ldisc(tty); - tty_ldisc_put(o_ldisc); + /* At this point we hold a reference to the new ldisc and a + reference to the old ldisc, or we hold two references to + the old ldisc (if it was restored as part of error cleanup + above). In either case, releasing a single reference from + the old ldisc is correct. */ + + tty_ldisc_put(old_ldisc); -enable: /* * Allow ldisc referencing to occur again */ - - tty_ldisc_enable(tty); - if (o_tty) - tty_ldisc_enable(o_tty); + tty_ldisc_enable_pair(tty, o_tty); /* Restart the work queue in case no characters kick it off. Safe if already running */ @@ -732,7 +589,6 @@ enable: if (o_tty) schedule_work(&o_tty->port->buf.work); - mutex_unlock(&tty->ldisc_mutex); tty_unlock(tty); return retval; } @@ -746,11 +602,11 @@ enable: static void tty_reset_termios(struct tty_struct *tty) { - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); tty->termios = tty->driver->init_termios; tty->termios.c_ispeed = tty_termios_input_baud_rate(&tty->termios); tty->termios.c_ospeed = tty_termios_baud_rate(&tty->termios); - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); } @@ -765,7 +621,7 @@ static void tty_reset_termios(struct tty_struct *tty) static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc) { - struct tty_ldisc *ld = tty_ldisc_get(ldisc); + struct tty_ldisc *ld = tty_ldisc_get(tty, ldisc); if (IS_ERR(ld)) return -1; @@ -804,14 +660,8 @@ void tty_ldisc_hangup(struct tty_struct *tty) tty_ldisc_debug(tty, "closing ldisc: %p\n", tty->ldisc); - /* - * FIXME! What are the locking issues here? This may me overdoing - * things... This question is especially important now that we've - * removed the irqlock. - */ ld = tty_ldisc_ref(tty); if (ld != NULL) { - /* We may have no line discipline at this point */ if (ld->ops->flush_buffer) ld->ops->flush_buffer(tty); tty_driver_flush_buffer(tty); @@ -822,21 +672,22 @@ void tty_ldisc_hangup(struct tty_struct *tty) ld->ops->hangup(tty); tty_ldisc_deref(ld); } - /* - * FIXME: Once we trust the LDISC code better we can wait here for - * ldisc completion and fix the driver call race - */ + wake_up_interruptible_poll(&tty->write_wait, POLLOUT); wake_up_interruptible_poll(&tty->read_wait, POLLIN); + + tty_unlock(tty); + /* * Shutdown the current line discipline, and reset it to * N_TTY if need be. * * Avoid racing set_ldisc or tty_ldisc_release */ - mutex_lock(&tty->ldisc_mutex); + tty_ldisc_lock_pair(tty, tty->link); + tty_lock(tty); - if (tty_ldisc_hangup_halt(tty)) { + if (tty->ldisc) { /* At this point we have a halted ldisc; we want to close it and reopen a new ldisc. We could defer the reopen to the next @@ -855,9 +706,8 @@ void tty_ldisc_hangup(struct tty_struct *tty) BUG_ON(tty_ldisc_reinit(tty, N_TTY)); WARN_ON(tty_ldisc_open(tty, tty->ldisc)); } - tty_ldisc_enable(tty); } - mutex_unlock(&tty->ldisc_mutex); + tty_ldisc_enable_pair(tty, tty->link); if (reset) tty_reset_termios(tty); @@ -889,15 +739,12 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) tty_ldisc_close(tty, ld); return retval; } - tty_ldisc_enable(o_tty); } - tty_ldisc_enable(tty); return 0; } static void tty_ldisc_kill(struct tty_struct *tty) { - mutex_lock(&tty->ldisc_mutex); /* * Now kill off the ldisc */ @@ -908,7 +755,6 @@ static void tty_ldisc_kill(struct tty_struct *tty) /* Ensure the next open requests the N_TTY ldisc */ tty_set_termios_ldisc(tty, N_TTY); - mutex_unlock(&tty->ldisc_mutex); } /** @@ -930,15 +776,16 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) tty_ldisc_debug(tty, "closing ldisc: %p\n", tty->ldisc); - tty_ldisc_halt(tty, o_tty, MAX_SCHEDULE_TIMEOUT); - + tty_ldisc_lock_pair(tty, o_tty); tty_lock_pair(tty, o_tty); - /* This will need doing differently if we need to lock */ + tty_ldisc_kill(tty); if (o_tty) tty_ldisc_kill(o_tty); tty_unlock_pair(tty, o_tty); + tty_ldisc_unlock_pair(tty, o_tty); + /* And the memory resources remaining (buffers, termios) will be disposed of when the kref hits zero */ @@ -955,7 +802,7 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) void tty_ldisc_init(struct tty_struct *tty) { - struct tty_ldisc *ld = tty_ldisc_get(N_TTY); + struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY); if (IS_ERR(ld)) panic("n_tty: init_tty"); tty->ldisc = ld; diff --git a/drivers/tty/tty_ldsem.c b/drivers/tty/tty_ldsem.c new file mode 100644 index 000000000000..22fad8ad5ac2 --- /dev/null +++ b/drivers/tty/tty_ldsem.c @@ -0,0 +1,453 @@ +/* + * Ldisc rw semaphore + * + * The ldisc semaphore is semantically a rw_semaphore but which enforces + * an alternate policy, namely: + * 1) Supports lock wait timeouts + * 2) Write waiter has priority + * 3) Downgrading is not supported + * + * Implementation notes: + * 1) Upper half of semaphore count is a wait count (differs from rwsem + * in that rwsem normalizes the upper half to the wait bias) + * 2) Lacks overflow checking + * + * The generic counting was copied and modified from include/asm-generic/rwsem.h + * by Paul Mackerras <paulus@samba.org>. + * + * The scheduling policy was copied and modified from lib/rwsem.c + * Written by David Howells (dhowells@redhat.com). + * + * This implementation incorporates the write lock stealing work of + * Michel Lespinasse <walken@google.com>. + * + * Copyright (C) 2013 Peter Hurley <peter@hurleysoftware.com> + * + * This file may be redistributed under the terms of the GNU General Public + * License v2. + */ + +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/atomic.h> +#include <linux/tty.h> +#include <linux/sched.h> + + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +# define __acq(l, s, t, r, c, n, i) \ + lock_acquire(&(l)->dep_map, s, t, r, c, n, i) +# define __rel(l, n, i) \ + lock_release(&(l)->dep_map, n, i) +# ifdef CONFIG_PROVE_LOCKING +# define lockdep_acquire(l, s, t, i) __acq(l, s, t, 0, 2, NULL, i) +# define lockdep_acquire_nest(l, s, t, n, i) __acq(l, s, t, 0, 2, n, i) +# define lockdep_acquire_read(l, s, t, i) __acq(l, s, t, 1, 2, NULL, i) +# define lockdep_release(l, n, i) __rel(l, n, i) +# else +# define lockdep_acquire(l, s, t, i) __acq(l, s, t, 0, 1, NULL, i) +# define lockdep_acquire_nest(l, s, t, n, i) __acq(l, s, t, 0, 1, n, i) +# define lockdep_acquire_read(l, s, t, i) __acq(l, s, t, 1, 1, NULL, i) +# define lockdep_release(l, n, i) __rel(l, n, i) +# endif +#else +# define lockdep_acquire(l, s, t, i) do { } while (0) +# define lockdep_acquire_nest(l, s, t, n, i) do { } while (0) +# define lockdep_acquire_read(l, s, t, i) do { } while (0) +# define lockdep_release(l, n, i) do { } while (0) +#endif + +#ifdef CONFIG_LOCK_STAT +# define lock_stat(_lock, stat) lock_##stat(&(_lock)->dep_map, _RET_IP_) +#else +# define lock_stat(_lock, stat) do { } while (0) +#endif + + +#if BITS_PER_LONG == 64 +# define LDSEM_ACTIVE_MASK 0xffffffffL +#else +# define LDSEM_ACTIVE_MASK 0x0000ffffL +#endif + +#define LDSEM_UNLOCKED 0L +#define LDSEM_ACTIVE_BIAS 1L +#define LDSEM_WAIT_BIAS (-LDSEM_ACTIVE_MASK-1) +#define LDSEM_READ_BIAS LDSEM_ACTIVE_BIAS +#define LDSEM_WRITE_BIAS (LDSEM_WAIT_BIAS + LDSEM_ACTIVE_BIAS) + +struct ldsem_waiter { + struct list_head list; + struct task_struct *task; +}; + +static inline long ldsem_atomic_update(long delta, struct ld_semaphore *sem) +{ + return atomic_long_add_return(delta, (atomic_long_t *)&sem->count); +} + +static inline int ldsem_cmpxchg(long *old, long new, struct ld_semaphore *sem) +{ + long tmp = *old; + *old = atomic_long_cmpxchg(&sem->count, *old, new); + return *old == tmp; +} + +/* + * Initialize an ldsem: + */ +void __init_ldsem(struct ld_semaphore *sem, const char *name, + struct lock_class_key *key) +{ +#ifdef CONFIG_DEBUG_LOCK_ALLOC + /* + * Make sure we are not reinitializing a held semaphore: + */ + debug_check_no_locks_freed((void *)sem, sizeof(*sem)); + lockdep_init_map(&sem->dep_map, name, key, 0); +#endif + sem->count = LDSEM_UNLOCKED; + sem->wait_readers = 0; + raw_spin_lock_init(&sem->wait_lock); + INIT_LIST_HEAD(&sem->read_wait); + INIT_LIST_HEAD(&sem->write_wait); +} + +static void __ldsem_wake_readers(struct ld_semaphore *sem) +{ + struct ldsem_waiter *waiter, *next; + struct task_struct *tsk; + long adjust, count; + + /* Try to grant read locks to all readers on the read wait list. + * Note the 'active part' of the count is incremented by + * the number of readers before waking any processes up. + */ + adjust = sem->wait_readers * (LDSEM_ACTIVE_BIAS - LDSEM_WAIT_BIAS); + count = ldsem_atomic_update(adjust, sem); + do { + if (count > 0) + break; + if (ldsem_cmpxchg(&count, count - adjust, sem)) + return; + } while (1); + + list_for_each_entry_safe(waiter, next, &sem->read_wait, list) { + tsk = waiter->task; + smp_mb(); + waiter->task = NULL; + wake_up_process(tsk); + put_task_struct(tsk); + } + INIT_LIST_HEAD(&sem->read_wait); + sem->wait_readers = 0; +} + +static inline int writer_trylock(struct ld_semaphore *sem) +{ + /* only wake this writer if the active part of the count can be + * transitioned from 0 -> 1 + */ + long count = ldsem_atomic_update(LDSEM_ACTIVE_BIAS, sem); + do { + if ((count & LDSEM_ACTIVE_MASK) == LDSEM_ACTIVE_BIAS) + return 1; + if (ldsem_cmpxchg(&count, count - LDSEM_ACTIVE_BIAS, sem)) + return 0; + } while (1); +} + +static void __ldsem_wake_writer(struct ld_semaphore *sem) +{ + struct ldsem_waiter *waiter; + + waiter = list_entry(sem->write_wait.next, struct ldsem_waiter, list); + wake_up_process(waiter->task); +} + +/* + * handle the lock release when processes blocked on it that can now run + * - if we come here from up_xxxx(), then: + * - the 'active part' of count (&0x0000ffff) reached 0 (but may have changed) + * - the 'waiting part' of count (&0xffff0000) is -ve (and will still be so) + * - the spinlock must be held by the caller + * - woken process blocks are discarded from the list after having task zeroed + */ +static void __ldsem_wake(struct ld_semaphore *sem) +{ + if (!list_empty(&sem->write_wait)) + __ldsem_wake_writer(sem); + else if (!list_empty(&sem->read_wait)) + __ldsem_wake_readers(sem); +} + +static void ldsem_wake(struct ld_semaphore *sem) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&sem->wait_lock, flags); + __ldsem_wake(sem); + raw_spin_unlock_irqrestore(&sem->wait_lock, flags); +} + +/* + * wait for the read lock to be granted + */ +static struct ld_semaphore __sched * +down_read_failed(struct ld_semaphore *sem, long count, long timeout) +{ + struct ldsem_waiter waiter; + struct task_struct *tsk = current; + long adjust = -LDSEM_ACTIVE_BIAS + LDSEM_WAIT_BIAS; + + /* set up my own style of waitqueue */ + raw_spin_lock_irq(&sem->wait_lock); + + /* Try to reverse the lock attempt but if the count has changed + * so that reversing fails, check if there are are no waiters, + * and early-out if not */ + do { + if (ldsem_cmpxchg(&count, count + adjust, sem)) + break; + if (count > 0) { + raw_spin_unlock_irq(&sem->wait_lock); + return sem; + } + } while (1); + + list_add_tail(&waiter.list, &sem->read_wait); + sem->wait_readers++; + + waiter.task = tsk; + get_task_struct(tsk); + + /* if there are no active locks, wake the new lock owner(s) */ + if ((count & LDSEM_ACTIVE_MASK) == 0) + __ldsem_wake(sem); + + raw_spin_unlock_irq(&sem->wait_lock); + + /* wait to be given the lock */ + for (;;) { + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + + if (!waiter.task) + break; + if (!timeout) + break; + timeout = schedule_timeout(timeout); + } + + __set_task_state(tsk, TASK_RUNNING); + + if (!timeout) { + /* lock timed out but check if this task was just + * granted lock ownership - if so, pretend there + * was no timeout; otherwise, cleanup lock wait */ + raw_spin_lock_irq(&sem->wait_lock); + if (waiter.task) { + ldsem_atomic_update(-LDSEM_WAIT_BIAS, sem); + list_del(&waiter.list); + raw_spin_unlock_irq(&sem->wait_lock); + put_task_struct(waiter.task); + return NULL; + } + raw_spin_unlock_irq(&sem->wait_lock); + } + + return sem; +} + +/* + * wait for the write lock to be granted + */ +static struct ld_semaphore __sched * +down_write_failed(struct ld_semaphore *sem, long count, long timeout) +{ + struct ldsem_waiter waiter; + struct task_struct *tsk = current; + long adjust = -LDSEM_ACTIVE_BIAS; + int locked = 0; + + /* set up my own style of waitqueue */ + raw_spin_lock_irq(&sem->wait_lock); + + /* Try to reverse the lock attempt but if the count has changed + * so that reversing fails, check if the lock is now owned, + * and early-out if so */ + do { + if (ldsem_cmpxchg(&count, count + adjust, sem)) + break; + if ((count & LDSEM_ACTIVE_MASK) == LDSEM_ACTIVE_BIAS) { + raw_spin_unlock_irq(&sem->wait_lock); + return sem; + } + } while (1); + + list_add_tail(&waiter.list, &sem->write_wait); + + waiter.task = tsk; + + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + for (;;) { + if (!timeout) + break; + raw_spin_unlock_irq(&sem->wait_lock); + timeout = schedule_timeout(timeout); + raw_spin_lock_irq(&sem->wait_lock); + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if ((locked = writer_trylock(sem))) + break; + } + + if (!locked) + ldsem_atomic_update(-LDSEM_WAIT_BIAS, sem); + list_del(&waiter.list); + raw_spin_unlock_irq(&sem->wait_lock); + + __set_task_state(tsk, TASK_RUNNING); + + /* lock wait may have timed out */ + if (!locked) + return NULL; + return sem; +} + + + +static inline int __ldsem_down_read_nested(struct ld_semaphore *sem, + int subclass, long timeout) +{ + long count; + + lockdep_acquire_read(sem, subclass, 0, _RET_IP_); + + count = ldsem_atomic_update(LDSEM_READ_BIAS, sem); + if (count <= 0) { + lock_stat(sem, contended); + if (!down_read_failed(sem, count, timeout)) { + lockdep_release(sem, 1, _RET_IP_); + return 0; + } + } + lock_stat(sem, acquired); + return 1; +} + +static inline int __ldsem_down_write_nested(struct ld_semaphore *sem, + int subclass, long timeout) +{ + long count; + + lockdep_acquire(sem, subclass, 0, _RET_IP_); + + count = ldsem_atomic_update(LDSEM_WRITE_BIAS, sem); + if ((count & LDSEM_ACTIVE_MASK) != LDSEM_ACTIVE_BIAS) { + lock_stat(sem, contended); + if (!down_write_failed(sem, count, timeout)) { + lockdep_release(sem, 1, _RET_IP_); + return 0; + } + } + lock_stat(sem, acquired); + return 1; +} + + +/* + * lock for reading -- returns 1 if successful, 0 if timed out + */ +int __sched ldsem_down_read(struct ld_semaphore *sem, long timeout) +{ + might_sleep(); + return __ldsem_down_read_nested(sem, 0, timeout); +} + +/* + * trylock for reading -- returns 1 if successful, 0 if contention + */ +int ldsem_down_read_trylock(struct ld_semaphore *sem) +{ + long count = sem->count; + + while (count >= 0) { + if (ldsem_cmpxchg(&count, count + LDSEM_READ_BIAS, sem)) { + lockdep_acquire_read(sem, 0, 1, _RET_IP_); + lock_stat(sem, acquired); + return 1; + } + } + return 0; +} + +/* + * lock for writing -- returns 1 if successful, 0 if timed out + */ +int __sched ldsem_down_write(struct ld_semaphore *sem, long timeout) +{ + might_sleep(); + return __ldsem_down_write_nested(sem, 0, timeout); +} + +/* + * trylock for writing -- returns 1 if successful, 0 if contention + */ +int ldsem_down_write_trylock(struct ld_semaphore *sem) +{ + long count = sem->count; + + while ((count & LDSEM_ACTIVE_MASK) == 0) { + if (ldsem_cmpxchg(&count, count + LDSEM_WRITE_BIAS, sem)) { + lockdep_acquire(sem, 0, 1, _RET_IP_); + lock_stat(sem, acquired); + return 1; + } + } + return 0; +} + +/* + * release a read lock + */ +void ldsem_up_read(struct ld_semaphore *sem) +{ + long count; + + lockdep_release(sem, 1, _RET_IP_); + + count = ldsem_atomic_update(-LDSEM_READ_BIAS, sem); + if (count < 0 && (count & LDSEM_ACTIVE_MASK) == 0) + ldsem_wake(sem); +} + +/* + * release a write lock + */ +void ldsem_up_write(struct ld_semaphore *sem) +{ + long count; + + lockdep_release(sem, 1, _RET_IP_); + + count = ldsem_atomic_update(-LDSEM_WRITE_BIAS, sem); + if (count < 0) + ldsem_wake(sem); +} + + +#ifdef CONFIG_DEBUG_LOCK_ALLOC + +int ldsem_down_read_nested(struct ld_semaphore *sem, int subclass, long timeout) +{ + might_sleep(); + return __ldsem_down_read_nested(sem, subclass, timeout); +} + +int ldsem_down_write_nested(struct ld_semaphore *sem, int subclass, + long timeout) +{ + might_sleep(); + return __ldsem_down_write_nested(sem, subclass, timeout); +} + +#endif diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 121aeb9393e1..f597e88a705d 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -256,10 +256,9 @@ void tty_port_tty_hangup(struct tty_port *port, bool check_clocal) { struct tty_struct *tty = tty_port_tty_get(port); - if (tty && (!check_clocal || !C_CLOCAL(tty))) { + if (tty && (!check_clocal || !C_CLOCAL(tty))) tty_hangup(tty); - tty_kref_put(tty); - } + tty_kref_put(tty); } EXPORT_SYMBOL_GPL(tty_port_tty_hangup); diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index a9af1b9ae160..d0e3a4497707 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -132,12 +132,6 @@ static int shift_state = 0; static unsigned char ledstate = 0xff; /* undefined */ static unsigned char ledioctl; -static struct ledptr { - unsigned int *addr; - unsigned int mask; - unsigned char valid:1; -} ledptrs[3]; - /* * Notifier list for console keyboard events */ @@ -994,24 +988,11 @@ void setledstate(struct kbd_struct *kbd, unsigned int led) static inline unsigned char getleds(void) { struct kbd_struct *kbd = kbd_table + fg_console; - unsigned char leds; - int i; if (kbd->ledmode == LED_SHOW_IOCTL) return ledioctl; - leds = kbd->ledflagstate; - - if (kbd->ledmode == LED_SHOW_MEM) { - for (i = 0; i < 3; i++) - if (ledptrs[i].valid) { - if (*ledptrs[i].addr & ledptrs[i].mask) - leds |= (1 << i); - else - leds &= ~(1 << i); - } - } - return leds; + return kbd->ledflagstate; } static int kbd_update_leds_helper(struct input_handle *handle, void *data) diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 60b7b6926059..ea27804d87af 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -24,6 +24,7 @@ #include <linux/selection.h> #include <linux/tiocl.h> #include <linux/console.h> +#include <linux/tty_flip.h> /* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */ #define isspace(c) ((c) == ' ') @@ -346,8 +347,8 @@ int paste_selection(struct tty_struct *tty) console_unlock(); ld = tty_ldisc_ref_wait(tty); + tty_buffer_lock_exclusive(&vc->port); - /* FIXME: this is completely unsafe */ add_wait_queue(&vc->paste_wait, &wait); while (sel_buffer && sel_buffer_lth > pasted) { set_current_state(TASK_INTERRUPTIBLE); @@ -356,13 +357,14 @@ int paste_selection(struct tty_struct *tty) continue; } count = sel_buffer_lth - pasted; - count = min(count, tty->receive_room); - ld->ops->receive_buf(tty, sel_buffer + pasted, NULL, count); + count = tty_ldisc_receive_buf(ld, sel_buffer + pasted, NULL, + count); pasted += count; } remove_wait_queue(&vc->paste_wait, &wait); __set_current_state(TASK_RUNNING); + tty_buffer_unlock_exclusive(&vc->port); tty_ldisc_deref(ld); return 0; } diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index d7799deacb21..14a2b5f11bca 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -188,22 +188,7 @@ static loff_t vcs_lseek(struct file *file, loff_t offset, int orig) console_unlock(); if (size < 0) return size; - switch (orig) { - default: - return -EINVAL; - case 2: - offset += size; - break; - case 1: - offset += file->f_pos; - case 0: - break; - } - if (offset < 0 || offset > size) { - return -EINVAL; - } - file->f_pos = offset; - return file->f_pos; + return fixed_size_llseek(file, offset, orig, size); } diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 740202d8a5c4..9a8e8c5a0c73 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -828,7 +828,7 @@ static inline int resize_screen(struct vc_data *vc, int width, int height, * If the caller passes a tty structure then update the termios winsize * information and perform any necessary signal handling. * - * Caller must hold the console semaphore. Takes the termios mutex and + * Caller must hold the console semaphore. Takes the termios rwsem and * ctrl_lock of the tty IFF a tty is passed. */ @@ -972,7 +972,7 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows) * the actual work. * * Takes the console sem and the called methods then take the tty - * termios_mutex and the tty ctrl_lock in that order. + * termios_rwsem and the tty ctrl_lock in that order. */ static int vt_resize(struct tty_struct *tty, struct winsize *ws) { @@ -2809,8 +2809,10 @@ static void con_shutdown(struct tty_struct *tty) console_unlock(); } +static int default_color = 7; /* white */ static int default_italic_color = 2; // green (ASCII) static int default_underline_color = 3; // cyan (ASCII) +module_param_named(color, default_color, int, S_IRUGO | S_IWUSR); module_param_named(italic, default_italic_color, int, S_IRUGO | S_IWUSR); module_param_named(underline, default_underline_color, int, S_IRUGO | S_IWUSR); @@ -2832,7 +2834,7 @@ static void vc_init(struct vc_data *vc, unsigned int rows, vc->vc_palette[k++] = default_grn[j] ; vc->vc_palette[k++] = default_blu[j] ; } - vc->vc_def_color = 0x07; /* white */ + vc->vc_def_color = default_color; vc->vc_ulcolor = default_underline_color; vc->vc_itcolor = default_italic_color; vc->vc_halfcolor = 0x08; /* grey */ @@ -3086,17 +3088,6 @@ err: }; -static int bind_con_driver(const struct consw *csw, int first, int last, - int deflt) -{ - int ret; - - console_lock(); - ret = do_bind_con_driver(csw, first, last, deflt); - console_unlock(); - return ret; -} - #ifdef CONFIG_VT_HW_CONSOLE_BINDING static int con_is_graphics(const struct consw *csw, int first, int last) { @@ -3114,34 +3105,6 @@ static int con_is_graphics(const struct consw *csw, int first, int last) return retval; } -/** - * unbind_con_driver - unbind a console driver - * @csw: pointer to console driver to unregister - * @first: first in range of consoles that @csw should be unbound from - * @last: last in range of consoles that @csw should be unbound from - * @deflt: should next bound console driver be default after @csw is unbound? - * - * To unbind a driver from all possible consoles, pass 0 as @first and - * %MAX_NR_CONSOLES as @last. - * - * @deflt controls whether the console that ends up replacing @csw should be - * the default console. - * - * RETURNS: - * -ENODEV if @csw isn't a registered console driver or can't be unregistered - * or 0 on success. - */ -int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) -{ - int retval; - - console_lock(); - retval = do_unbind_con_driver(csw, first, last, deflt); - console_unlock(); - return retval; -} -EXPORT_SYMBOL(unbind_con_driver); - /* unlocked version of unbind_con_driver() */ int do_unbind_con_driver(const struct consw *csw, int first, int last, int deflt) { @@ -3262,8 +3225,11 @@ static int vt_bind(struct con_driver *con) if (first == 0 && last == MAX_NR_CONSOLES -1) deflt = 1; - if (first != -1) - bind_con_driver(csw, first, last, deflt); + if (first != -1) { + console_lock(); + do_bind_con_driver(csw, first, last, deflt); + console_unlock(); + } first = -1; last = -1; @@ -3301,8 +3267,11 @@ static int vt_unbind(struct con_driver *con) if (first == 0 && last == MAX_NR_CONSOLES -1) deflt = 1; - if (first != -1) - unbind_con_driver(csw, first, last, deflt); + if (first != -1) { + console_lock(); + do_unbind_con_driver(csw, first, last, deflt); + console_unlock(); + } first = -1; last = -1; @@ -3574,29 +3543,9 @@ err: return retval; } -/** - * register_con_driver - register console driver to console layer - * @csw: console driver - * @first: the first console to take over, minimum value is 0 - * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1 - * - * DESCRIPTION: This function registers a console driver which can later - * bind to a range of consoles specified by @first and @last. It will - * also initialize the console driver by calling con_startup(). - */ -int register_con_driver(const struct consw *csw, int first, int last) -{ - int retval; - - console_lock(); - retval = do_register_con_driver(csw, first, last); - console_unlock(); - return retval; -} -EXPORT_SYMBOL(register_con_driver); /** - * unregister_con_driver - unregister console driver from console layer + * do_unregister_con_driver - unregister console driver from console layer * @csw: console driver * * DESCRIPTION: All drivers that registers to the console layer must @@ -3606,17 +3555,6 @@ EXPORT_SYMBOL(register_con_driver); * * The driver must unbind first prior to unregistration. */ -int unregister_con_driver(const struct consw *csw) -{ - int retval; - - console_lock(); - retval = do_unregister_con_driver(csw); - console_unlock(); - return retval; -} -EXPORT_SYMBOL(unregister_con_driver); - int do_unregister_con_driver(const struct consw *csw) { int i, retval = -ENODEV; @@ -3654,7 +3592,7 @@ EXPORT_SYMBOL_GPL(do_unregister_con_driver); * when a driver wants to take over some existing consoles * and become default driver for newly opened ones. * - * take_over_console is basically a register followed by unbind + * do_take_over_console is basically a register followed by unbind */ int do_take_over_console(const struct consw *csw, int first, int last, int deflt) { @@ -3675,30 +3613,6 @@ int do_take_over_console(const struct consw *csw, int first, int last, int deflt } EXPORT_SYMBOL_GPL(do_take_over_console); -/* - * If we support more console drivers, this function is used - * when a driver wants to take over some existing consoles - * and become default driver for newly opened ones. - * - * take_over_console is basically a register followed by unbind - */ -int take_over_console(const struct consw *csw, int first, int last, int deflt) -{ - int err; - - err = register_con_driver(csw, first, last); - /* - * If we get an busy error we still want to bind the console driver - * and return success, as we may have unbound the console driver - * but not unregistered it. - */ - if (err == -EBUSY) - err = 0; - if (!err) - bind_con_driver(csw, first, last, deflt); - - return err; -} /* * give_up_console is a wrapper to unregister_con_driver. It will only @@ -3706,7 +3620,9 @@ int take_over_console(const struct consw *csw, int first, int last, int deflt) */ void give_up_console(const struct consw *csw) { - unregister_con_driver(csw); + console_lock(); + do_unregister_con_driver(csw); + console_unlock(); } static int __init vtconsole_class_init(void) @@ -4262,6 +4178,5 @@ EXPORT_SYMBOL(console_blanked); EXPORT_SYMBOL(vc_cons); EXPORT_SYMBOL(global_cursor_default); #ifndef VT_SINGLE_DRIVER -EXPORT_SYMBOL(take_over_console); EXPORT_SYMBOL(give_up_console); #endif diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index fc2c06c66e89..2bd78e2ac8ec 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -289,13 +289,10 @@ static int vt_disallocate(unsigned int vc_num) struct vc_data *vc = NULL; int ret = 0; - if (!vc_num) - return 0; - console_lock(); if (VT_BUSY(vc_num)) ret = -EBUSY; - else + else if (vc_num) vc = vc_deallocate(vc_num); console_unlock(); |