diff options
Diffstat (limited to 'drivers/tty')
66 files changed, 3044 insertions, 4536 deletions
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index b24aa010f68c..c01f45095877 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -419,4 +419,51 @@ config DA_CONSOLE help This enables a console on a Dash channel. +config MIPS_EJTAG_FDC_TTY + bool "MIPS EJTAG Fast Debug Channel TTY" + depends on MIPS_CDMM + help + This enables a TTY and console on the MIPS EJTAG Fast Debug Channels, + if they are present. This can be useful when working with an EJTAG + probe which supports it, to get console output and a login prompt via + EJTAG without needing to connect a serial cable. + + TTY devices are named e.g. ttyFDC3c2 (for FDC channel 2 of the FDC on + CPU3). + + The console can be enabled with console=fdc1 (for FDC channel 1 on all + CPUs). Do not use the console unless there is a debug probe attached + to drain the FDC TX FIFO. + + If unsure, say N. + +config MIPS_EJTAG_FDC_EARLYCON + bool "Early FDC console" + depends on MIPS_EJTAG_FDC_TTY + help + This registers a console on FDC channel 1 very early during boot (from + MIPS arch code). This is useful for bring-up and debugging early boot + issues. + + Do not enable unless there is a debug probe attached to drain the FDC + TX FIFO. + + If unsure, say N. + +config MIPS_EJTAG_FDC_KGDB + bool "Use KGDB over an FDC channel" + depends on MIPS_EJTAG_FDC_TTY && KGDB + default y + help + This enables the use of KGDB over an FDC channel, allowing KGDB to be + used remotely or when a serial port isn't available. + +config MIPS_EJTAG_FDC_KGDB_CHAN + int "KGDB FDC channel" + depends on MIPS_EJTAG_FDC_KGDB + range 2 15 + default 3 + help + FDC channel number to use for KGDB. + endif # TTY diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 58ad1c05b7f8..5817e2397463 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -29,5 +29,6 @@ obj-$(CONFIG_SYNCLINK) += synclink.o obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o obj-$(CONFIG_DA_TTY) += metag_da.o +obj-$(CONFIG_MIPS_EJTAG_FDC_TTY) += mips_ejtag_fdc.o obj-y += ipwireless/ diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index 967b2c2b7cf1..0655fecf8240 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -229,7 +229,6 @@ static int goldfish_tty_probe(struct platform_device *pdev) { struct goldfish_tty *qtty; int ret = -EINVAL; - int i; struct resource *r; struct device *ttydev; void __iomem *base; @@ -293,7 +292,6 @@ static int goldfish_tty_probe(struct platform_device *pdev) mutex_unlock(&goldfish_tty_lock); return 0; - tty_unregister_device(goldfish_tty_driver, i); err_tty_register_device_failed: free_irq(irq, pdev); err_request_irq_failed: diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c index 071551bf3e9a..47b54c6aefd2 100644 --- a/drivers/tty/hvc/hvc_opal.c +++ b/drivers/tty/hvc/hvc_opal.c @@ -29,6 +29,7 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/export.h> +#include <linux/interrupt.h> #include <asm/hvconsole.h> #include <asm/prom.h> @@ -41,7 +42,7 @@ static const char hvc_opal_name[] = "hvc_opal"; -static struct of_device_id hvc_opal_match[] = { +static const struct of_device_id hvc_opal_match[] = { { .name = "serial", .compatible = "ibm,opal-console-raw" }, { .name = "serial", .compatible = "ibm,opal-console-hvsi" }, { }, @@ -61,7 +62,6 @@ static struct hvc_opal_priv *hvc_opal_privs[MAX_NR_HVC_CONSOLES]; /* For early boot console */ static struct hvc_opal_priv hvc_opal_boot_priv; static u32 hvc_opal_boot_termno; -static bool hvc_opal_event_registered; static const struct hv_ops hvc_opal_raw_ops = { .get_chars = opal_get_chars, @@ -162,28 +162,15 @@ static const struct hv_ops hvc_opal_hvsi_ops = { .tiocmset = hvc_opal_hvsi_tiocmset, }; -static int hvc_opal_console_event(struct notifier_block *nb, - unsigned long events, void *change) -{ - if (events & OPAL_EVENT_CONSOLE_INPUT) - hvc_kick(); - return 0; -} - -static struct notifier_block hvc_opal_console_nb = { - .notifier_call = hvc_opal_console_event, -}; - static int hvc_opal_probe(struct platform_device *dev) { const struct hv_ops *ops; struct hvc_struct *hp; struct hvc_opal_priv *pv; hv_protocol_t proto; - unsigned int termno, boot = 0; + unsigned int termno, irq, boot = 0; const __be32 *reg; - if (of_device_is_compatible(dev->dev.of_node, "ibm,opal-console-raw")) { proto = HV_PROTOCOL_RAW; ops = &hvc_opal_raw_ops; @@ -227,18 +214,18 @@ static int hvc_opal_probe(struct platform_device *dev) dev->dev.of_node->full_name, boot ? " (boot console)" : ""); - /* We don't do IRQ ... */ - hp = hvc_alloc(termno, 0, ops, MAX_VIO_PUT_CHARS); + irq = opal_event_request(ilog2(OPAL_EVENT_CONSOLE_INPUT)); + if (!irq) { + pr_err("hvc_opal: Unable to map interrupt for device %s\n", + dev->dev.of_node->full_name); + return irq; + } + + hp = hvc_alloc(termno, irq, ops, MAX_VIO_PUT_CHARS); if (IS_ERR(hp)) return PTR_ERR(hp); dev_set_drvdata(&dev->dev, hp); - /* ... but we use OPAL event to kick the console */ - if (!hvc_opal_event_registered) { - opal_notifier_register(&hvc_opal_console_nb); - hvc_opal_event_registered = true; - } - return 0; } diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c index f1e57425e39f..7a3d146a5f0e 100644 --- a/drivers/tty/hvc/hvc_xen.c +++ b/drivers/tty/hvc/hvc_xen.c @@ -289,7 +289,7 @@ static int xen_initial_domain_console_init(void) return -ENOMEM; } - info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0); + info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0, false); info->vtermno = HVC_COOKIE; spin_lock(&xencons_lock); @@ -299,11 +299,27 @@ static int xen_initial_domain_console_init(void) return 0; } +static void xen_console_update_evtchn(struct xencons_info *info) +{ + if (xen_hvm_domain()) { + uint64_t v; + int err; + + err = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &v); + if (!err && v) + info->evtchn = v; + } else + info->evtchn = xen_start_info->console.domU.evtchn; +} + void xen_console_resume(void) { struct xencons_info *info = vtermno_to_xencons(HVC_COOKIE); - if (info != NULL && info->irq) + if (info != NULL && info->irq) { + if (!xen_initial_domain()) + xen_console_update_evtchn(info); rebind_evtchn_irq(info->evtchn, info->irq); + } } static void xencons_disconnect_backend(struct xencons_info *info) diff --git a/drivers/tty/ipwireless/hardware.c b/drivers/tty/ipwireless/hardware.c index 5c77e1eac4ee..ad7031a4f3c4 100644 --- a/drivers/tty/ipwireless/hardware.c +++ b/drivers/tty/ipwireless/hardware.c @@ -1455,7 +1455,7 @@ static void __handle_setup_get_version_rsp(struct ipw_hardware *hw) return; } - set_RTS(hw, PRIO_SETUP, channel_idx, + ret = set_RTS(hw, PRIO_SETUP, channel_idx, (hw->control_lines [channel_idx] & IPW_CONTROL_LINE_RTS) != 0); if (ret) { diff --git a/drivers/tty/mips_ejtag_fdc.c b/drivers/tty/mips_ejtag_fdc.c new file mode 100644 index 000000000000..358323c83b4f --- /dev/null +++ b/drivers/tty/mips_ejtag_fdc.c @@ -0,0 +1,1304 @@ +/* + * TTY driver for MIPS EJTAG Fast Debug Channels. + * + * Copyright (C) 2007-2015 Imagination Technologies Ltd + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for more + * details. + */ + +#include <linux/atomic.h> +#include <linux/bitops.h> +#include <linux/completion.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/kgdb.h> +#include <linux/kthread.h> +#include <linux/sched.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/uaccess.h> + +#include <asm/cdmm.h> +#include <asm/irq.h> + +/* Register offsets */ +#define REG_FDACSR 0x00 /* FDC Access Control and Status Register */ +#define REG_FDCFG 0x08 /* FDC Configuration Register */ +#define REG_FDSTAT 0x10 /* FDC Status Register */ +#define REG_FDRX 0x18 /* FDC Receive Register */ +#define REG_FDTX(N) (0x20+0x8*(N)) /* FDC Transmit Register n (0..15) */ + +/* Register fields */ + +#define REG_FDCFG_TXINTTHRES_SHIFT 18 +#define REG_FDCFG_TXINTTHRES (0x3 << REG_FDCFG_TXINTTHRES_SHIFT) +#define REG_FDCFG_TXINTTHRES_DISABLED (0x0 << REG_FDCFG_TXINTTHRES_SHIFT) +#define REG_FDCFG_TXINTTHRES_EMPTY (0x1 << REG_FDCFG_TXINTTHRES_SHIFT) +#define REG_FDCFG_TXINTTHRES_NOTFULL (0x2 << REG_FDCFG_TXINTTHRES_SHIFT) +#define REG_FDCFG_TXINTTHRES_NEAREMPTY (0x3 << REG_FDCFG_TXINTTHRES_SHIFT) +#define REG_FDCFG_RXINTTHRES_SHIFT 16 +#define REG_FDCFG_RXINTTHRES (0x3 << REG_FDCFG_RXINTTHRES_SHIFT) +#define REG_FDCFG_RXINTTHRES_DISABLED (0x0 << REG_FDCFG_RXINTTHRES_SHIFT) +#define REG_FDCFG_RXINTTHRES_FULL (0x1 << REG_FDCFG_RXINTTHRES_SHIFT) +#define REG_FDCFG_RXINTTHRES_NOTEMPTY (0x2 << REG_FDCFG_RXINTTHRES_SHIFT) +#define REG_FDCFG_RXINTTHRES_NEARFULL (0x3 << REG_FDCFG_RXINTTHRES_SHIFT) +#define REG_FDCFG_TXFIFOSIZE_SHIFT 8 +#define REG_FDCFG_TXFIFOSIZE (0xff << REG_FDCFG_TXFIFOSIZE_SHIFT) +#define REG_FDCFG_RXFIFOSIZE_SHIFT 0 +#define REG_FDCFG_RXFIFOSIZE (0xff << REG_FDCFG_RXFIFOSIZE_SHIFT) + +#define REG_FDSTAT_TXCOUNT_SHIFT 24 +#define REG_FDSTAT_TXCOUNT (0xff << REG_FDSTAT_TXCOUNT_SHIFT) +#define REG_FDSTAT_RXCOUNT_SHIFT 16 +#define REG_FDSTAT_RXCOUNT (0xff << REG_FDSTAT_RXCOUNT_SHIFT) +#define REG_FDSTAT_RXCHAN_SHIFT 4 +#define REG_FDSTAT_RXCHAN (0xf << REG_FDSTAT_RXCHAN_SHIFT) +#define REG_FDSTAT_RXE BIT(3) /* Rx Empty */ +#define REG_FDSTAT_RXF BIT(2) /* Rx Full */ +#define REG_FDSTAT_TXE BIT(1) /* Tx Empty */ +#define REG_FDSTAT_TXF BIT(0) /* Tx Full */ + +/* Default channel for the early console */ +#define CONSOLE_CHANNEL 1 + +#define NUM_TTY_CHANNELS 16 + +#define RX_BUF_SIZE 1024 + +/* + * When the IRQ is unavailable, the FDC state must be polled for incoming data + * and space becoming available in TX FIFO. + */ +#define FDC_TTY_POLL (HZ / 50) + +struct mips_ejtag_fdc_tty; + +/** + * struct mips_ejtag_fdc_tty_port - Wrapper struct for FDC tty_port. + * @port: TTY port data + * @driver: TTY driver. + * @rx_lock: Lock for rx_buf. + * This protects between the hard interrupt and user + * context. It's also held during read SWITCH operations. + * @rx_buf: Read buffer. + * @xmit_lock: Lock for xmit_*, and port.xmit_buf. + * This protects between user context and kernel thread. + * It is used from chars_in_buffer()/write_room() TTY + * callbacks which are used during wait operations, so a + * mutex is unsuitable. + * @xmit_cnt: Size of xmit buffer contents. + * @xmit_head: Head of xmit buffer where data is written. + * @xmit_tail: Tail of xmit buffer where data is read. + * @xmit_empty: Completion for xmit buffer being empty. + */ +struct mips_ejtag_fdc_tty_port { + struct tty_port port; + struct mips_ejtag_fdc_tty *driver; + raw_spinlock_t rx_lock; + void *rx_buf; + spinlock_t xmit_lock; + unsigned int xmit_cnt; + unsigned int xmit_head; + unsigned int xmit_tail; + struct completion xmit_empty; +}; + +/** + * struct mips_ejtag_fdc_tty - Driver data for FDC as a whole. + * @dev: FDC device (for dev_*() logging). + * @driver: TTY driver. + * @cpu: CPU number for this FDC. + * @fdc_name: FDC name (not for base of channel names). + * @driver_name: Base of driver name. + * @ports: Per-channel data. + * @waitqueue: Wait queue for waiting for TX data, or for space in TX + * FIFO. + * @lock: Lock to protect FDCFG (interrupt enable). + * @thread: KThread for writing out data to FDC. + * @reg: FDC registers. + * @tx_fifo: TX FIFO size. + * @xmit_size: Size of each port's xmit buffer. + * @xmit_total: Total number of bytes (from all ports) to transmit. + * @xmit_next: Next port number to transmit from (round robin). + * @xmit_full: Indicates TX FIFO is full, we're waiting for space. + * @irq: IRQ number (negative if no IRQ). + * @removing: Indicates the device is being removed and @poll_timer + * should not be restarted. + * @poll_timer: Timer for polling for interrupt events when @irq < 0. + * @sysrq_pressed: Whether the magic sysrq key combination has been + * detected. See mips_ejtag_fdc_handle(). + */ +struct mips_ejtag_fdc_tty { + struct device *dev; + struct tty_driver *driver; + unsigned int cpu; + char fdc_name[16]; + char driver_name[16]; + struct mips_ejtag_fdc_tty_port ports[NUM_TTY_CHANNELS]; + wait_queue_head_t waitqueue; + raw_spinlock_t lock; + struct task_struct *thread; + + void __iomem *reg; + u8 tx_fifo; + + unsigned int xmit_size; + atomic_t xmit_total; + unsigned int xmit_next; + bool xmit_full; + + int irq; + bool removing; + struct timer_list poll_timer; + +#ifdef CONFIG_MAGIC_SYSRQ + bool sysrq_pressed; +#endif +}; + +/* Hardware access */ + +static inline void mips_ejtag_fdc_write(struct mips_ejtag_fdc_tty *priv, + unsigned int offs, unsigned int data) +{ + __raw_writel(data, priv->reg + offs); +} + +static inline unsigned int mips_ejtag_fdc_read(struct mips_ejtag_fdc_tty *priv, + unsigned int offs) +{ + return __raw_readl(priv->reg + offs); +} + +/* Encoding of byte stream in FDC words */ + +/** + * struct fdc_word - FDC word encoding some number of bytes of data. + * @word: Raw FDC word. + * @bytes: Number of bytes encoded by @word. + */ +struct fdc_word { + u32 word; + unsigned int bytes; +}; + +/* + * This is a compact encoding which allows every 1 byte, 2 byte, and 3 byte + * sequence to be encoded in a single word, while allowing the majority of 4 + * byte sequences (including all ASCII and common binary data) to be encoded in + * a single word too. + * _______________________ _____________ + * | FDC Word | | + * |31-24|23-16|15-8 | 7-0 | Bytes | + * |_____|_____|_____|_____|_____________| + * | | | | | | + * |0x80 |0x80 |0x80 | WW | WW | + * |0x81 |0x81 | XX | WW | WW XX | + * |0x82 | YY | XX | WW | WW XX YY | + * | ZZ | YY | XX | WW | WW XX YY ZZ | + * |_____|_____|_____|_____|_____________| + * + * Note that the 4-byte encoding can only be used where none of the other 3 + * encodings match, otherwise it must fall back to the 3 byte encoding. + */ + +/* ranges >= 1 && sizes[0] >= 1 */ +static struct fdc_word mips_ejtag_fdc_encode(const char **ptrs, + unsigned int *sizes, + unsigned int ranges) +{ + struct fdc_word word = { 0, 0 }; + const char **ptrs_end = ptrs + ranges; + + for (; ptrs < ptrs_end; ++ptrs) { + const char *ptr = *(ptrs++); + const char *end = ptr + *(sizes++); + + for (; ptr < end; ++ptr) { + word.word |= (u8)*ptr << (8*word.bytes); + ++word.bytes; + if (word.bytes == 4) + goto done; + } + } +done: + /* Choose the appropriate encoding */ + switch (word.bytes) { + case 4: + /* 4 byte encoding, but don't match the 1-3 byte encodings */ + if ((word.word >> 8) != 0x808080 && + (word.word >> 16) != 0x8181 && + (word.word >> 24) != 0x82) + break; + /* Fall back to a 3 byte encoding */ + word.bytes = 3; + word.word &= 0x00ffffff; + case 3: + /* 3 byte encoding */ + word.word |= 0x82000000; + break; + case 2: + /* 2 byte encoding */ + word.word |= 0x81810000; + break; + case 1: + /* 1 byte encoding */ + word.word |= 0x80808000; + break; + } + return word; +} + +static unsigned int mips_ejtag_fdc_decode(u32 word, char *buf) +{ + buf[0] = (u8)word; + word >>= 8; + if (word == 0x808080) + return 1; + buf[1] = (u8)word; + word >>= 8; + if (word == 0x8181) + return 2; + buf[2] = (u8)word; + word >>= 8; + if (word == 0x82) + return 3; + buf[3] = (u8)word; + return 4; +} + +/* Console operations */ + +/** + * struct mips_ejtag_fdc_console - Wrapper struct for FDC consoles. + * @cons: Console object. + * @tty_drv: TTY driver associated with this console. + * @lock: Lock to protect concurrent access to other fields. + * This is raw because it may be used very early. + * @initialised: Whether the console is initialised. + * @regs: Registers base address for each CPU. + */ +struct mips_ejtag_fdc_console { + struct console cons; + struct tty_driver *tty_drv; + raw_spinlock_t lock; + bool initialised; + void __iomem |